上下文 token 预算是什么
上下文预算等于模型窗口减去系统提示、输出预留与会话历史,剩下的才是能放检索片段的空间。中文不能按字数粗算 token:不同模型的 tokenizer 对中文、标点、表格与代码的分词差异很大,长表格与代码块尤其容易超预估。稳妥做法是用目标模型的 tokenizer 实测片段长度,按顺序累加直到触及预算停止,而不是先按条数挑片段再祈祷不超限。
基本信息
| 预算公式 | 窗口 − 系统提示 − 输出预留 − 会话历史 |
|---|---|
| 计算方式 | 用目标模型的 tokenizer 实测,不按字数折算 |
| 顺序 | 按重排结果从高到低填充,超限即停 |
| 输出预留 | 按期望回答长度留足,避免尾部被截断 |
| 边界情况 | 长表格与代码块的 token 密度明显更高 |
典型使用场景
- 多片段问答:按预算决定能放几条候选,兼顾覆盖与成本
- 输出被截断排查:先算系统提示与历史占了多少,再看输出预留是否不足
- 模型切换:换 tokenizer 后重新核算每条的预期长度
- 成本控制:把上下文长度写进监控,观察单价与长度的关系
- 长历史会话:为历史设置上限,超出部分按摘要或截断处理
预算换算示例
预算换算示例
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained('Qwen/Qwen2.5-7B-Instruct')
budget = 8192 - len(tok('系统提示……').input_ids) - 1024 # 预留输出
used, selected = 0, []
for c in ranked: # 已按重排顺序排好
n = len(tok(c.text).input_ids)
if used + n > budget:
break # 放不下就停,不做半截截断
used += n
selected.append(c)
print(used, len(selected))预算构成
| 组成部分 | 典型占用量级 | 控制手段 |
|---|---|---|
| 系统提示 | 数百 token | 精简模板,去掉重复说明 |
| 会话历史 | 随轮次增长 | 限定轮数或做摘要 |
| 检索片段 | 占大头 | 按 token 累加填充 |
| 输出预留 | 按回答长度定 | 预留不足会导致回答被截断 |
预算要在服务端统一计算并记录到日志:出现“回答突然变短”或“引用缺号”时,先看这次实际用了多少 token、被截掉了哪几条片段。