上下文管理
为什么长会话的费用会非线性增长,以及如何将历史记录控制在有限范围内。
API 是无状态的。每轮都会重新发送整个对话,因此输入 token 会随着对话记录增长 — 此后每次调用的成本也会随之增加。
问题的增长规律
一个包含 N 轮、每轮大约新增 T 个 token 的对话,总计消耗的输入 token 量约为 N²T/2,而不是 NT。会话长度翻倍,输入费用就大约变为四倍。这就是为什么一个前十分钟感觉便宜的智能体,运行一小时后会让人对费用感到吃惊。
四种有效模式
| 模式 | 方法 | 权衡 |
|---|---|---|
| 滑动窗口 | 保留最近 K 轮,丢弃其余内容 | 完全丢失早期上下文 |
| 持续更新的摘要 | 定期用模型生成的摘要替换较早的对话轮次 | 需要额外调用一次;保留要点 |
| 每个任务开启新会话 | 话题变化时重新开始 | 无需额外成本,而且通常是正确的做法 |
| 外部化 | 将状态保存在文件或数据库中,仅加载当前步骤所需的内容 | 工作量最大,效果最好 |
总结较早的对话轮次
def compact(history, keep=6):
class="s">""class="s">"Replace everything but the last `keep` turns with one summary turn."class="s">""
if len(history) <= keep:
return history
old, recent = history[:-keep], history[-keep:]
summary = summarise(old) class=class="s">"c"># one cheap Haiku call
return [{class="s">"role": class="s">"user", class="s">"content": fclass="s">"Summary of earlier discussion: {summary}"}] + recent
使用低成本模型进行摘要。这是压缩任务,不是推理任务。
工具结果通常是问题根源
- 工具返回 500 行,就会让之后的每个请求都包含这 500 行。限制返回行数,或返回摘要和一个可按需获取详细信息的句柄。
- 智能体读取的完整文件内容会一直留在上下文中。只读取所需部分,不要读取整个文件。
- 截图和图像会像其他内容一样被重复发送——描述完毕后就将其移除。
- 构建日志和堆栈跟踪:保留开头和结尾的 50 行,丢弃中间部分。
在智能体工具中,为互不相关的任务开启新会话,是成本最低的一项优化。在 Claude Code 中,对应的操作是
/clear.