大模型应用如何管理 Token 预算?
简化版
Token 预算管理,就是在模型上下文上限内,为系统指令、历史对话、检索证据、工具结果、当前问题和输出预留分别设额度,并按业务价值决定超限时先删什么。核心约束是 输入 token + 最大输出 token ≤ 上下文窗口;生产环境还要同时控制单请求成本和延迟,因此不能只做字符截断,而要用 tokenizer 精确计数、分区预算、相关性排序、摘要压缩和溢出降级。
详细版
先把一次请求拆成可观测的预算项:
T_total = T_system + T_tools + T_history + T_retrieval + T_query + T_output_reserve
T_total <= T_context_limit - T_safety_margin
常见分配原则是:系统指令和当前问题属于不可丢区;工具定义按本轮可用工具裁剪;历史对话保留最近轮次并维护摘要;RAG 片段先重排、去重,再按边际收益装入;输出则根据任务类型预留,而不是等输入塞满后才发现没有生成空间。
超限时应按明确策略降级:先去掉低相关检索片段和重复工具描述,再压缩较早历史,最后才缩短输出上限;关键证据不能被无声截断。预算还要纳入成本与性能指标,例如记录每个分区的 token、截断原因、完成长度、TTFT、总延迟和单请求费用,用离线评测确定不同任务的最小有效预算。
完整版教学
一、先建立正确的预算模型
模型供应商标出的 8K、32K 或 128K 上下文,不是全部都能拿来放用户材料。系统提示、工具 Schema、对话历史和最终输出共享同一个窗口。若窗口是 8192,输入已经占 7600 token,却希望最多生成 1000 token,请求要么被接口拒绝,要么输入被框架截断,要么答案在半途因长度上限停止。
一个更可执行的公式是:
可装载输入 = 窗口上限 - 输出预留 - 安全余量
例:8192 - 1200 - 200 = 6792 token
这里的 200 token 安全余量用于吸收聊天模板、特殊 token 和不同 tokenizer 计数差异。预算不是“尽量塞到上限”,而是事先给输出和协议开销留座位。
二、必须按模型的 tokenizer 计数
字符数不能可靠换算成 token。英文单词可能被拆成一个或多个子词,中文通常接近一字一 token 但并不恒定,代码、JSON、长数字和罕见符号的比例又不同。同一段文本换一个模型族,token 数也可能变化。
因此预算器必须使用目标模型对应的 tokenizer;如果请求会被路由到多个 tokenizer 不同的模型,就要在路由确定后重新计数,或者按最保守模型预估。客户端估算只能用于提前提示,服务端在真正发出请求前仍应做一次最终校验。
def fits_window(parts, encode, context_limit, output_reserve, margin=200):
used = sum(len(encode(part.text)) for part in parts)
return used <= context_limit - output_reserve - margin
三、把上下文分成“不可丢、可压缩、可淘汰”
所有内容同等截断会造成隐蔽错误。例如从尾部硬截断可能删掉用户当前问题,从头部截断可能删掉系统安全约束。工程上应给每个分区标注优先级和最小额度。
| 分区 | 典型内容 | 超限策略 | 原因 |
|---|---|---|---|
| 不可丢 | 系统规则、当前问题、关键输出格式 | 保留完整,失败则拒绝请求 | 缺失会改变任务和安全边界 |
| 有损压缩 | 较早对话、长工具结果 | 摘要、结构化提取 | 需要语义,但不必逐字保留 |
| 可淘汰 | 低分检索片段、未使用工具定义 | 按相关性删除 | 边际价值最低 |
| 动态预留 | 模型输出 | 按任务设上下限 | 防止答案被截断 |
记忆钩子:预算不足时先删“低价值证据”,再压“旧状态”,绝不能让字符串切片替你决定业务语义。
四、RAG 预算看的是边际收益
检索返回十个片段,不代表十个都应该进入 Prompt。相邻分块经常有大段重叠,同一事实也可能被多个文档重复描述;盲目增加 top_k 会同时推高成本、首 token 延迟,并让关键证据在噪声中失焦。
假设输入可用 6000 token,固定部分占 2200,历史占 1200,留给检索的只有 2600。重排后的候选长度分别为 900、800、700、650、600,前三段总计 2400,可以装入;第四段会溢出。此时应该评估第四段是否提供新证据,而不是机械地截取它的前 200 token。若第二、第三段高度重复,先去重可能腾出完整第四段。
实际装箱可综合相关性、来源权威性、信息新颖度和 token 长度,近似使用“单位 token 的证据收益”排序。对必须同时出现的多跳证据,还要以证据组为单位保留,避免只留下推理链的一半。
五、多轮对话需要状态压缩而非无限滚动
保留最近 N 轮简单,但一轮可能只有十几个 token,也可能包含数千 token 的日志,所以 N 不是稳定预算。更可靠的方式是同时维护近期原文、滚动摘要和结构化状态:近期原文保留表达细节;摘要保存已经确认的事实与决策;结构化状态保存用户偏好、任务约束和未完成事项。
较早消息 ──摘要──> conversation_summary
关键事实 ──提取──> {目标, 约束, 已确认项, 待办}
最近 3~5 轮 ─────> 原文保留
摘要本身也会丢信息,所以要让摘要携带版本和来源消息范围,并定期从原始记录重新生成。涉及金额、日期、代码参数等精确信息时,应保存结构化原值,不能只相信自然语言摘要。
六、输出预算必须跟任务类型走
分类任务只需要几十 token,JSON 抽取可能需要几百 token,代码生成和长报告可能需要数千 token。统一设置一个很大的 max_output_tokens 会抬高最坏成本和资源占用;设置过小又会让 JSON 缺右括号、代码缺结尾、解释答到一半。
可以根据任务建立输出档位,例如分类 64、结构化抽取 512、普通问答 1000、报告生成 4000。若模型支持停止原因,应区分 stop 和 length:后者说明答案因预算耗尽而截断,需要续写、缩小任务或重新分配,而不能当作成功响应。
流式输出也不等于没有预算。服务端仍需累计已生成 token,在达到业务上限时给出可识别的截断状态,避免前端只收到一段看似正常但语义不完整的内容。
七、把 token 换算成钱和容量
设某模型输入价格为每百万 token 10 元、输出价格为每百万 token 30 元。一天 10 万次请求,平均输入 3000、输出 800 token,则:
输入成本 = 100000 × 3000 / 1000000 × 10 = 3000 元
输出成本 = 100000 × 800 / 1000000 × 30 = 2400 元
日成本 = 5400 元
如果通过检索去重把输入从 3000 降到 2200,每天可节省 800 元。但压缩不能只看费用:若事实正确率下降两个百分点,业务损失可能远大于 token 成本。正确的优化目标是在质量下限约束下减少 token,而不是单独最小化输入长度。
预算还影响容量。更长输入增加 prefill 计算,更多输出占用更久的解码槽位;在相同 GPU 下,请求变长通常会降低可承载并发。因此容量规划至少要看输入和输出长度的 P50、P95、P99,而不是只看平均值。
八、溢出策略与观测指标要在上线前定义
超限时可以选择裁剪、摘要、分阶段调用、换长窗口模型,或者明确拒绝。哪一种合适取决于任务:合同审查不能悄悄漏掉附件尾部,聊天应用可以压缩早期寒暄,超长报告则适合“分块生成—章节汇总”。
每次调用建议记录这些字段:各分区 token 数、窗口利用率、被移除片段及原因、输出预留与实际输出、停止原因、模型版本、费用、TTFT 和总延迟。线上告警可以关注 length 截断率、预算溢出率和答案质量回归。
评测时做预算曲线而非只测一个点。例如分别给 RAG 证据 1000/2000/4000 token,比较事实正确率、引用命中率、P95 延迟和成本。若 2000 到 4000 只提升 0.2% 正确率却增加 35% 延迟,2000 往往是更合理的生产配置。
九、常见误区与追问
- 误区:上下文窗口有 128K,就应该尽量装满。 标称容量不是免费资源;长输入会增加 prefill、KV Cache、成本和噪声。
- 误区:按字符数除以四就足够准确。 这只是某些英文文本的粗略经验,中文、代码和不同 tokenizer 都会产生明显误差。
- 误区:超限时从最早消息开始截断即可。 早期消息可能包含仍然生效的约束,应先抽取状态并做语义压缩。
- 误区:只限制输入 token 就控制住成本了。 输出 token 往往单价更高,还会占用更长的逐 token 解码时间。
- 误区:摘要一定比原文更省且无损。 摘要会遗漏细节,还额外消耗一次模型调用,应对关键字段保留结构化原值。
- 追问:工具 Schema 很大怎么办? 先按本轮意图筛选工具,只注入候选工具;再精简重复描述,但不能删掉参数约束。
- 追问:如何确定 RAG 应分多少 token? 用固定评测集绘制质量—token—延迟曲线,在质量下限之上选择边际收益开始变小的位置。
- 追问:输出被
length截断如何处理? 结构化任务应判失败并修复或重试;长文可带已完成状态续写,但要防止重复和上下文继续膨胀。
十、加强记忆
把 Token 预算记成一次装箱:先用 窗口上限 - 输出预留 - 安全余量 算出真正可用的输入空间,再把内容分为不可丢、可压缩和可淘汰三类。计数必须跟模型 tokenizer 一致;RAG 按证据边际收益装箱,多轮历史用“摘要 + 结构化状态 + 近期原文”收敛;输出按任务分档,并监控 length 停止。最后把 token 同时换算成质量、费用、TTFT 和并发容量,才能做出生产级取舍。