大模型应用的 Token 成本怎么算与控制
大模型应用的成本不只是"每次调用多少钱",而是输入、输出、重试、上下文膨胀共同决定的。这篇文章拆解成本的构成,并给出工程上真正有效的六个控制手段。
一、一个容易被低估的成本项
很多团队估算大模型成本时会这样算:
单次调用 0.001 元 × 每天 1 万次 = 每天 10 元
看起来很便宜。但上线一个月后账单往往是估算的好几倍。原因在于这个公式漏掉了四个变量。
二、成本的完整构成
真实成本 = (输入 token + 输出 token) × 单价 × 调用次数
× (1 + 重试率)
× (1 + 上下文膨胀系数)
1. 输入和输出是分开计价的
绝大多数模型厂商对输入和输出分别定价,而且输出通常贵 2~4 倍:
| 典型相对价格 | 说明 | |
|---|---|---|
| 输入 token | 1x | 你发给模型的 prompt + 上下文 |
| 输出 token | 2~4x | 模型生成的内容 |
这个差异有重要的实践含义:让模型少说废话,比让它少读资料更省钱。
2. RAG 场景下,输入是大头
在 RAG(检索增强生成)场景里,输入 token 会被显著放大:
用户问题: "这个产品的保修期多久?" ≈ 15 token
+ 检索到的文档片段(5 个 × 500 token) ≈ 2500 token
+ 系统提示词 ≈ 300 token
+ 对话历史(5 轮) ≈ 800 token
─────────────────────────────────────────────
实际输入 ≈ 3615 token
原始问题只占输入的 0.4%。 这就是为什么 RAG 应用的优化重点几乎都在"怎么少塞文档"上。
3. 上下文膨胀:多轮对话的隐性成本
多轮对话要带上历史,而历史是累积的:
| 轮次 | 本轮输入 | 累计成本倍数 |
|---|---|---|
| 第 1 轮 | 500 token | 1x |
| 第 5 轮 | 2500 token | 5x |
| 第 10 轮 | 5000 token | 10x |
| 第 20 轮 | 10000 token | 20x |
成本随轮次线性增长,但用户感知到的价值并没有同步增长——他问第 20 个问题时,前 19 轮的细节大多已经不重要了。
4. 重试和 Agent 循环
| 场景 | 成本倍数 |
|---|---|
| 调用失败重试(重试 2 次) | 3x |
| Agent 多轮工具调用(平均 5 轮) | 5x |
| 检索失败后改写查询重试 | 2x |
| 自我校验(生成 + 检查) | 2x |
Agent 类应用的成本往往比单次问答高一个数量级,因为它天然是多轮调用。
三、六个有效的控制手段
手段一:精确控制检索片段数量
这是 RAG 场景收益最大的优化。
| 做法 | 效果 |
|---|---|
调小 topK(从 10 降到 5) |
输入直接减半 |
| 重排后截断 | 用 Reranker 排序,只留最相关的 3~5 条 |
| 上下文压缩 | 只抽取片段中与问题相关的句子 |
| 相似度阈值过滤 | 低相关度的片段直接不塞 |
注意副作用:减少上下文会降低召回,可能让答案质量下降。所以这不是无脑调小,而是在质量可接受的前提下找最小值——需要 A/B 测试。
一个实用的做法:先在评测集上测不同 topK 下的答案质量,找到质量开始明显下降的拐点,取拐点略高一点的值。
手段二:滑动窗口 + 滚动摘要
针对多轮对话的上下文膨胀:
❌ 全量历史: 第 1~20 轮全部带上 → 成本 20x
✅ 滑动窗口: 只带最近 5 轮 → 成本 5x
✅✅ 窗口 + 摘要: 最近 5 轮原文 + 更早内容的摘要 → 成本 5x 且不丢关键信息
滚动摘要的做法:当历史超过窗口时,把溢出的部分交给模型压缩成一段摘要,摘要随对话滚动更新。
[早期摘要] 用户在咨询 X 产品的保修和退换政策,已确认购买日期为 3 月。
[最近对话]
用户: 那保修期内维修收费吗?
助手: ...
用户: 如果是我自己弄坏的呢?
摘要通常能压缩到原文的 10%~20%,而关键信息基本保留。
手段三:给输出加长度约束
输出 token 单价更高,控制输出收益明显:
| 做法 | 说明 |
|---|---|
| prompt 里明确要求 | "用不超过 100 字回答" |
设置 max_tokens |
硬上限,防止异常长输出 |
| 结构化输出 | 要求返回 JSON,比自然语言更紧凑 |
| 分场景控制 | 客服问答要短,文案生成可以长 |
max_tokens 是必须设置的——它不仅是成本控制,也是防止模型陷入重复循环时产生巨额账单的保险。
手段四:选对模型
不是所有任务都需要最强的模型:
| 任务类型 | 适合的模型档次 |
|---|---|
| 意图分类、实体抽取 | 小模型足够 |
| 简单问答、格式转换 | 小模型 / 中等模型 |
| 复杂推理、长文写作 | 强模型 |
| 代码生成 | 强模型 |
常见的优化是分级路由:先用小模型判断任务难度,简单的自己处理,复杂的转给强模型。成本能降一半以上。
手段五:缓存
| 缓存类型 | 做法 | 收益 |
|---|---|---|
| 精确缓存 | 相同问题直接返回缓存答案 | 高频重复问题收益极大 |
| 语义缓存 | 语义相似的问题复用答案 | 收益更大,但要控制误判 |
| Prompt 缓存 | 复用固定的系统提示词前缀 | 部分厂商支持,直接降价 |
| 检索结果缓存 | 热门查询的检索结果缓存 | 省向量库查询(也省一点 token) |
Prompt 缓存值得特别关注:很多场景下系统提示词很长(几百到几千 token)且固定不变。部分厂商对重复的 prompt 前缀提供折扣,能直接降低成本。
手段六:流式输出 + 提前终止
| 做法 | 效果 |
|---|---|
| 流式输出(SSE) | 用户能提前看到内容,体验好;但不省成本 |
| 提前终止 | 检测到答案已完整就停止生成,能省钱 |
| 限制最大轮次 | Agent 循环设上限,防止无限调用 |
注意区分:流式输出优化的是体验(首字延迟),不是成本。真正省钱的是提前终止——但这需要额外的判断逻辑,实现复杂度较高。
四、监控指标
成本控制的前提是能看见成本。至少要监控:
| 指标 | 用途 |
|---|---|
| 每请求输入/输出 token | 定位异常请求 |
| 每用户日均 token | 发现滥用 |
| 按接口/场景的 token 消耗 | 找到成本大头 |
| 重试率和 Agent 平均轮次 | 发现异常循环 |
| 单次请求成本分布 | 找出长尾(少数请求吃掉大部分成本) |
长尾往往是最值得优化的地方:如果 5% 的请求消耗了 50% 的成本,优化这 5% 比优化平均值有效得多。
常见的长尾来源:超长对话历史、检索返回了过多片段、Agent 陷入循环、异常输入(用户粘贴了整篇文档)。
五、一个实用的优化顺序
按"收益/成本比"排序:
| 优先级 | 动作 | 预期收益 | 实现成本 |
|---|---|---|---|
| 1 | 设置 max_tokens |
防巨额账单 | 极低 |
| 2 | 调小 topK + 重排截断 | 输入减半 | 低 |
| 3 | 滑动窗口 + 滚动摘要 | 多轮成本大幅下降 | 中 |
| 4 | 加精确缓存 | 高频场景收益大 | 低 |
| 5 | 分级路由到小模型 | 成本降 50%+ | 中 |
| 6 | 语义缓存 + 上下文压缩 | 进一步压缩 | 高 |
先做便宜且安全的事(1、2、4),再做需要调优的(3、5),最后做复杂度高的(6)。
小结
- 真实成本 ≠ 单价 × 次数,还要算上重试率和上下文膨胀
- 输入和输出分开计价,输出通常贵 2~4 倍——让模型少说废话更省钱
- RAG 场景下输入是大头,原始问题可能只占 0.4%
- 多轮对话成本随轮次线性增长,20 轮就是 20 倍
- Agent 类应用因多轮工具调用,成本常高一个数量级
- 六个控制手段:精确控制片段数量、滑动窗口+摘要、输出长度约束、选对模型、缓存、流式+提前终止
- 流式输出优化的是体验,不是成本——别混淆
max_tokens必须设,它是防止异常循环产生巨额账单的保险- 监控要关注长尾:5% 的请求可能吃掉 50% 的成本
- 优化顺序按收益/成本比:先做便宜安全的,再做需要调优的
大模型应用的成本优化,本质上是在信息量和开销之间找平衡。塞得越多答案越准,但成本越高——找到那个"质量还够用"的最小值,就是优化的目标。
留下点什么
评论基于 GitHub Discussions(giscus)。在项目根目录配置.env.local填入 NEXT_PUBLIC_GISCUS_* 四个变量后自动开启。