跳到主要内容

Token 计算与成本

做 AI 功能开发,迟早要回答两个问题:这次调用花了多少钱? 以及 要不要向用户收 Token 费? 本篇按两个维度展开——内部成本核算与对外计费——共用同一套计量基础,但目标、精度和工程要求不同。

维度目标精度要求典型读者
内部成本测试、评测、上线后知道项目真实花费以厂商账单为准,可接受 ±5% 预估误差开发、测试、FinOps
对外计费按 Token 或积分向用户收费必须可审计、可对账、防重复扣费产品、后端、财务

Agent / RAG 的上下文拼装见 上下文工程;记忆分层见 Agent 记忆分层实践;框架选型见 RAG 与框架选型。

共同语言:Token 是什么​

Token 是模型处理文本的最小计费单位,不等于「一个字」或「一个单词」。中文通常 1 个汉字 ≈ 1–2 token;英文约 4 字符 ≈ 1 token。同一句话,不同模型、不同 tokenizer 结果可能不同。

厂商账单一般拆成:

字段含义计费
prompt_tokens / input_tokens发给模型的输入(system、history、RAG 片段、tool schema)输入单价
completion_tokens / output_tokens模型生成的输出输出单价(通常更高)
reasoning_tokenso 系列 / 推理模型的「思考」token(部分厂商单独列)可能按输出价或专属价
cached_tokensPrompt Cache 命中的输入 token通常按折扣价
total_tokens上述合计(各厂商定义略有差异)—

记忆口诀:输入看 Prompt 有多胖,输出看模型说多长;Agent 多轮 = 每轮输入都会重新计费。

两种计量方式​

请求前(预估) 请求后(实计)
│ │
├─ tiktoken / 厂商 count API ├─ response.usage(最准)
├─ 拼 Prompt 前算预算 ├─ 流式:最后一个 chunk 的 usage
└─ 截断 RAG / 窗口裁剪 └─ 对账:与账单 CSV 核对
方式适用局限
预估发请求前控成本、裁剪上下文、余额预检与实计可能有偏差(特殊 token、tool 序列化)
实计内部报表、对外扣费、财务对账只有请求完成后才知道;失败/重试要单独处理

原则:内部成本以 实计 为准;对外计费在 实计 基础上扣用户积分,预估只用于 前置拦截(余额不足则拒绝)。


维度一:内部成本核算​

目标:开发、测试、评测、生产环境都能回答「这个功能 / 这次实验花了多少钱」。

1.1 单次 LLM 调用的成本​

# cost.py — 最小成本计算
def llm_cost(
model: str,
prompt_tokens: int,
completion_tokens: int,
*,
cached_tokens: int = 0,
price: dict | None = None,
) -> float:
"""price 示例:{"input": 2.5, "output": 10.0, "cached": 1.25} # USD / 1M tokens"""
p = price or PRICES[model]
billable_input = prompt_tokens - cached_tokens
return (
billable_input * p["input"]
+ cached_tokens * p.get("cached", p["input"])
+ completion_tokens * p["output"]
) / 1_000_000

OpenAI Chat Completions 返回 response.usage:

from openai import OpenAI

client = OpenAI()
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "你好"}],
)
u = resp.usage
# u.prompt_tokens, u.completion_tokens, u.total_tokens
cost = llm_cost("gpt-4o", u.prompt_tokens, u.completion_tokens)

流式场景:开启 stream_options={"include_usage": True}(OpenAI),在 最后一个 chunk 取 usage;不要按 chunk 数估算。

1.2 发请求前预估(tiktoken)​

import tiktoken

def count_tokens(text: str, model: str) -> int:
enc = tiktoken.encoding_for_model(model)
return len(enc.encode(text))

def count_messages(messages: list[dict], model: str) -> int:
"""Chat 格式有固定 overhead,生产环境建议用厂商 token counting API"""
enc = tiktoken.encoding_for_model(model)
total = 0
for m in messages:
total += 4 # 每条消息的格式开销(近似)
total += len(enc.encode(m.get("content") or ""))
total += 2 # 回复 priming
return total

注意:tool call 的 JSON、多模态图片 token、reasoning token 用 tiktoken 估不准,只能作上限参考;财务口径仍靠 usage。

1.3 整条链路的成本,不只 Chat​

环节计量对象常见遗漏
Embedding索引文档 + 每次 query 向量化重建索引的批量费用
Rerank按 query + 候选条数与 LLM 分开计价
LLM 生成多轮 Agent 每轮重算输入只统计最后一轮
摘要 / 抽取GraphRAG 建索引时的批量 LLM离线 job 未纳入
评测jsonl 全量跑用例 × 多模型CI nightly 账单爆炸

Agent 循环示例:

用户问题
→ RAG 检索(embedding query)
→ LLM 第 1 轮(system + tools + 问题 + 检索片段) ← prompt 大
→ tool 结果回灌
→ LLM 第 2 轮(history 变长,prompt 更大) ← 重复计费
→ … 直到结束

内部报表应记录:request_id、trace_id、feature(chat/rag/agent/eval)、model、各阶段 token、美元成本、环境(dev/staging/prod)。

1.4 推荐日志结构​

{
"request_id": "req_abc",
"trace_id": "trace_xyz",
"user_id": "internal_tester_01",
"feature": "agent_support",
"stage": "llm_turn_2",
"model": "gpt-4o",
"usage": {
"prompt_tokens": 4200,
"completion_tokens": 180,
"cached_tokens": 3000,
"total_tokens": 4380
},
"cost_usd": 0.0021,
"latency_ms": 1200,
"env": "staging"
}

实现路径(按投入从低到高):

方案适合
应用层 middleware 写 DB / 日志PoC、小团队
LangSmith、Helicone、Langfuse已有 LangChain / 需要 trace 可视化
统一 LLM Gateway(自建或 LiteLLM)多模型、多环境、要集中计价

1.5 测试与评测的成本控制​

做法说明
用例分级smoke 用便宜模型;全量 eval 放 nightly
固定 seed + 缓存相同 Prompt 命中 Prompt Cache
预算上限CI job 超 $X 自动 fail
Mock LLM单测不调真 API;集成测才打真模型
采样1000 条用例先跑 50 条估单价 × 总量

维度二:对外 Token 计费​

目标:用户消耗可计量、可充值、可退款、可与厂商账单对账。

2.1 产品形态选型​

形态用户体验工程复杂度适用
积分 / Token 包买 100 万 token,用完再买中AI 写作、API 平台
订阅 + 额度月费含 N token,超出按量中SaaS Copilot
纯订阅不限量不暴露 token低(但成本不可控)早期补贴期
按次一次对话 = 1 次低简单场景,难反映真实成本

对外通常 不直接暴露厂商 raw token,而是 平台积分(如 1 积分 = 1000 token),便于调价而不改 UI。

2.2 定价与加价​

用户支付单价 ≥ 厂商成本 × (1 + 毛利率) + 分摊固定成本
策略做法
输入/输出分开定价输出价更高,与厂商一致
按模型分档gpt-4o 积分 ≠ gpt-4o-mini
功能溢价Agent(多轮)比单次问答贵
缓存让利命中缓存的输入给用户折扣,鼓励长 system prompt

维护一张 价目表(版本化),扣费时 快照 当时单价,避免改价影响历史订单。

2.3 扣费流程​

┌─────────────────┐
用户请求 ────────►│ 鉴权 + 余额预检 │─── 不足 ──► 402 / 引导充值
└────────┬────────┘
│ 通过
┌────────▼────────┐
│ 调用 LLM │
└────────┬────────┘
│
┌──────────────▼──────────────┐
│ 读 usage,按价目表算扣减量 │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ 事务:扣余额 + 写流水 │ idempotency_key = request_id
└─────────────────────────────┘

关键设计:

点建议
幂等同一 request_id 只扣一次;重试带相同 key
预扣 vs 后扣预扣(冻结估算上限)适合长 Agent;后扣(按实计)更简单
失败API 5xx 不扣用户;4xx 视业务(用户输入过长可扣或不扣)
流式结束时一次性扣;或按 chunk 累计(复杂,一般不必)
并发余额更新用 DB 行锁或 Redis DECR

2.4 最小扣费实现(后扣 + 实计)​

from dataclasses import dataclass

@dataclass
class UsageRecord:
request_id: str
user_id: str
model: str
prompt_tokens: int
completion_tokens: int

def tokens_to_credits(usage: UsageRecord, price_table: dict) -> int:
p = price_table[usage.model]
raw = (
usage.prompt_tokens * p["input_weight"]
+ usage.completion_tokens * p["output_weight"]
)
return max(1, int(raw)) # 最少扣 1 积分,避免零成本刷接口

def settle_usage(db, usage: UsageRecord, price_table: dict) -> None:
credits = tokens_to_credits(usage, price_table)
with db.transaction():
if db.ledger_exists(usage.request_id):
return # 幂等
if db.get_balance(usage.user_id) < credits:
raise InsufficientCredits()
db.debit(usage.user_id, credits, idempotency_key=usage.request_id)
db.insert_ledger(
request_id=usage.request_id,
user_id=usage.user_id,
credits=credits,
usage=usage,
price_version=price_table["version"],
)

2.5 对账​

每月:

Σ 用户扣费积分 × 积分单价 ≈ 收入
Σ 厂商账单 USD ≈ 成本

差异来源:预估误差、缓存折扣未传递、免费额度、内部测试账号、汇率。保留 usage 原始字段 便于与 OpenAI Usage Dashboard / 账单 CSV 逐条核对。

2.6 合规与体验​

项说明
用量透明设置页展示本月消耗、剩余积分
软/硬限流80% 预警,100% 拒绝或降级模型
退款按流水回滚积分,标记 refund_of
滥用同一 IP / 账号异常高频 → 限流

常见坑​

坑现象处理
只统计最后一轮Agent 成本低估 5–10 倍每轮 LLM 调用都打点
tiktoken 当财务口径与账单差 1–5%对外扣费只用 usage
忽略 embeddingRAG 问答「很便宜」直到索引重建embedding 单独记账
重试双扣用户被扣两次request_id 幂等
中英文混排估错预算截断过早/过晚按语言分桶统计校准
Tool schema 膨胀system 越来越大按需加载工具;监控 prompt 增长
测试打生产 Key账单惊现环境隔离 Key + 预算告警

快速对照​

你要干什么?
│
├─ 知道测试/评测花了多少钱
│ → 实计 usage 写日志 + 按模型单价汇总 + CI 预算
│
├─ 上线后看各功能成本
│ → trace 按 feature 聚合 + 告警
│
└─ 向用户收 Token 费
→ 平台积分 + 后扣实计 + 幂等流水 + 月对账

一句话:内部求 准(实计 + 全链路);对外求 稳(实计扣费 + 幂等 + 可审计)。预估只负责 别超支,不负责 结账。