为什么 Agent 比聊天更烧 token
一句话答案:API 是无状态的,每一轮都要把整段对话重发一次。N 轮对话每轮约 T 个 token,累计输入 token 约为 N²T/2 而不是 NT——会话长度翻倍,输入账单约翻四倍。这就是「十分钟很便宜、一小时后吓人」的原因。
如果你在用按 token 计费的方式,聊天时几乎感觉不到成本,Claude Code 跑半小时就心疼,问题通常不在模型单价,而在上下文是怎么长的。
成本是怎么长成平方的
结论:因为每一轮都重发历史。第 1 轮发 1 份,第 2 轮发 2 份,第 3 轮发 3 份……N 轮累计发送 1+2+…+N ≈ N²/2 份。
假设每轮新增 2,000 token:
| 轮次 | 这一轮的输入 token | 累计输入 |
|---|---|---|
| 10 | 2,000 | 约 110,000 |
| 20 | 2,000 | 约 420,000 |
| 40 | 2,000 | 约 1,640,000 |
轮次翻倍,累计输入约翻四倍。这就是为什么「一个下午顺手用一下」和「挂着一整天跑」是两种完全不同的账单。
最占 token 的不是对话,是工具结果
结论:对话本身通常不大,真正撑大上下文的是工具返回的内容。一次 cat 一个 4MB 日志、一次打包后的压缩文件、一次在 node_modules 里做的全量搜索,这些输出会进入之后每一次请求。
实用的做法:
- 先看规模再取内容:
wc -l、ls -la、取首尾各 50 行; - 明确告诉它读哪个文件,而不是让它扫整个目录;
- 大输出让它自己过滤后只回结论。
四种控制历史的模式
结论:这四种模式可以叠加,选哪种取决于你更能接受哪种损失。
| 模式 | 做法 | 代价 |
|---|---|---|
| 滑动窗口 | 只保留最近 K 轮,其余丢掉 | 早期上下文完全丢失 |
| 滚动摘要 | 定期用便宜模型把旧轮次压成一段摘要 | 多一次调用;保留大意 |
| 每任务新会话 | 话题一变就 /clear | 免费,而且通常最正确 |
| 状态外部化 | 状态写进文件或数据库,按需读回 | 工作量最大,效果最好 |
在 Claude Code 里对应 /compact(滚动摘要)与 /clear(新会话)。把状态写进文件对应 CLAUDE.md 的用法,见 The.md 怎么写。
摘要压缩要用便宜的档位
结论:摘要是一次压缩任务,不是推理任务。用便宜的档位做,保留「事实、ID、结论」而不是「讲得通顺」。
一份有用的摘要至少要保留:
- 任务目标与已经确定的约束;
- 改过哪些文件,以及为什么;
- 失败过的尝试和报错原文;
- 还不知道的事(open questions)。
丢掉精确值是最常见的失误:「用户有个发票问题」几乎没用,「2025-01-08 重复扣款两笔,单号 inv_1042 和 inv_1077」才是能继续工作的信息。
怎么看上下文被什么占满了
结论:会话里输入 /context 会按类别列出窗口构成:系统提示、工具定义、MCP 工具、历史对话、文件内容各占多少。
看到「固定成本」占比高时,压缩对话没用,应该去清理 MCP 服务器和技能;看到「历史对话」占比高时才用 /compact。具体处置见 prompt too long 怎么处理。
长上下文窗口不等于免账单
结论:更大的窗口只是把天花板抬高。你付的是实际发送的 token,不是窗口大小。
1M 窗口的价值在于「需要时可以真的塞进去」,而不是「每次都塞满」。每次把同一份大文档重发一遍,账单照常增长;对稳定前缀开提示缓存才是省这类重复的办法,见提示缓存原理。
常见问题
会话时间长了 token 为什么会突然变多?
因为每一轮都要重发整段历史。N 轮、每轮 T 个 token,累计输入约为 N²T/2;会话长度翻倍,输入账单约翻四倍。
最占 token 的是什么?
工具结果。一次 cat 一个 4MB 日志、或让 agent 读整文件,这段输出会进入之后每一次请求。只取首尾各几十行往往就够。
摘要压缩用什么模型做?
用便宜的档位(如 Haiku)。摘要是压缩任务不是推理任务,质量标准是「保留事实与 ID」而不是「讲得通顺」。
长上下文窗口能不能解决这个问题?
不能省,只是把上限抬高。上下文窗口是能力,不是默认架构;该不该把大段材料发进去,仍取决于任务是否真的需要。
相关阅读
- prompt too long 报错怎么处理:窗口满了怎么释放
- 降低 Claude 用量的工程方法:更系统的降本做法
- 提示缓存原理:稳定前缀的复用
- Claude Code 启动时那 33k token 去了哪:固定成本的构成