对话太长了怎么办:Claude 的压缩和上下文编辑,各自什么时候用
长对话和跑很久的智能体迟早要处理上下文:要么让服务端把旧的轮次写成摘要(压缩),要么按规则清掉旧的工具结果和思考(上下文编辑)。本文整理两类办法的用法、限制和对缓存的影响。通过中转站实测:上下文编辑能用;两种压缩都用不了,其中一种是参数被悄悄忽略。
本站已实测,测试日期:2026-10-13。
这篇教程解决一个问题:自己写的智能体或聊天程序,对话越来越长,快要撑满上下文窗口了,或者越长回答越差,该怎么办?
Claude Code 里这件事是自动的(它会自己压缩,你也可以用 /compact)。这篇讲的是自己直接调接口时,官方提供的两类办法。
| 实测环境 | |
|---|---|
| 日期 | 2026-10-13 |
| 方式 | 用 Python 直接发 HTTP 请求 |
| 模型 | Sonnet 5.5、Opus 5.5 |
| 连接方式 | 通过酷站AI 中转站,不是直连官方 |
先说实测的结论: 通过这家中转站,上下文编辑能用,两种压缩都用不了。所以压缩那几节是按文档整理的,没有跑通过。
先分清两类办法
| 压缩 | 上下文编辑 | |
|---|---|---|
| 做什么 | 把旧的轮次写成一段摘要,用摘要代替它们 | 按规则直接清掉旧的工具结果或旧的思考 |
| 丢了什么 | 细节。摘要里没写的就没了 | 被清掉的那些内容,原地留一句“已清除” |
| 你的历史记录 | 要换成摘要 | 不用动。清理发生在服务端,你照常保存完整的历史 |
| 适合 | 长聊天、长任务,旧的内容整体不再需要逐字保留 | 工具调用很多的智能体,旧的工具结果(文件内容、搜索结果)用过就没用了 |
| 状态 | 测试版 | 测试版 |
文档的建议是:多数情况用压缩,上下文编辑留给需要更细控制的场合。
为什么不等窗口满了再说:文档反复强调,内容越多,回答的质量越差。压缩不只是为了不超限,也是为了让模型眼前的东西少一些。
一、压缩:三种做法
| 按需压缩 | 到阈值自动压缩 | 自己写摘要 | |
|---|---|---|---|
| 谁决定什么时候压 | 你,发一个专门的请求 | 接口,输入到了你设的 token 数就压 | 你 |
| 你要写的代码 | 一个循环:要摘要,换进历史 | 普通请求上多一个参数 | 全部:摘要的请求、提示词、改历史 |
| 最近几轮能不能逐字保留 | 能 | 能,要让接口压完先停一下 | 能 |
| 能不能在后台压 | 能 | 不能,就在触发的那个请求里做 | 能 |
| 保留下来的轮次,思考还有效吗 | 有效,满足几个条件的话 | 无效 | 无效 |
文档的建议:能用按需压缩就用它。
按需压缩
发一个带 compaction 参数的请求,把到目前为止的对话原样带上。接口不生成回复,只返回一个压缩块:
{
"model": "claude-opus-5-5",
"max_tokens": 4096,
"messages": [ ……到目前为止的对话…… ],
"compaction": {"type": "summarize"}
}
请求要带 beta 头 compact-2026-09-04。响应的 stop_reason 是 compaction,内容里只有一个 compaction 块,里面是可读的摘要文字和一个签名。
接着用的规则:
- 压缩块放在
messages的最前面,代替它概括的那些消息,后面跟你的下一轮。 - 被概括的消息要删掉。 留在块前面会报 400;留在块后面不报错,但等于把它们又发了一遍。
- 块要原样发回,签名不能动,一个请求里只能有一个。
- 之后每个带着这个块的请求都要带 beta 头。
max_tokens要给够几千,它管的是整个摘要过程。
什么时候压,自己定。常见的做法是每轮结束后把上一个响应的输入和输出 token 加起来(开了缓存的话,读缓存和写缓存的也要加上),超过你定的一个数就压。
没拿到摘要的情况要处理:响应仍然是 200,但内容是空的,这时看 stop_reason。是 max_tokens 就调大重发;是 tool_use 说明模型去调工具了,在自定义的摘要提示里写明不要调工具。无论哪种,都可以先不压、接着聊,过后再压。
两个进一步的做法:
- 保留最近几轮。 没有专门的参数。你在历史里选一个切点:切点之前的发去压缩,切点之后的原样留着,接在压缩块后面。切点不能落在一次工具调用和它的结果之间。
- 在后台压。 压缩的请求发出去之后不等它,对话照常在完整的历史上继续;摘要回来了,再把当时发出去的那一段换成压缩块,这期间新增的轮次留在后面。
摘要带不走的东西:图片、文档、抓取的网页。被概括的消息里有这些,换成压缩块之后就没有了,后面还要用就得重新给。
到阈值自动压缩
在普通请求上加一个策略,其余不用管:
"context_management": {
"edits": [
{"type": "compact_20260112", "trigger": {"type": "input_tokens", "value": 150000}}
]
}
beta 头是 compact-2026-01-12。输入到了阈值,接口先写一段摘要,再接着回答。你把响应照常追加到历史里,下一个请求时接口自动丢掉压缩块之前的内容。
- 阈值默认 15 万 token,最低 5 万。
- 摘要用的就是请求里的那个模型,不能换一个便宜的。
- 请求里有工具时,模型偶尔会在写摘要的那一步去调工具,结果压缩块的内容是空的。办法同样是自定义摘要提示,写明不要调工具。
压缩怎么计费
写摘要是额外的一次模型调用,照常收费、照常算进限流。要注意的是顶层的 input_tokens 和 output_tokens 不包含它。开了压缩之后,响应的 usage 里多一个 iterations 数组,每次调用一项,算总用量要把这个数组里的全加起来。
压缩和思考
这是最绕的一处,只有同时满足“模型会保留以前的思考”“你把 thinking 块发回去”“压缩后还留着几轮”这三条才需要管。5.5 这一代的模型会核对以前的 thinking 块和产生它的那段对话是否一致,压缩把一部分对话换成了摘要,本来对不上;但摘要是接口自己写的,所以按需压缩在几个条件下能让保留下来的思考继续有效:每次压缩都用同一个会保留思考的模型;保留的轮次紧跟在被概括的消息后面、原样发送;system 和 tools 不变。
条件不满足时,压缩本身不报错,问题出在后面第一个带着那些思考的请求上:默认是 400。到阈值自动压缩和自己写摘要没有这个待遇,保留的轮次里的思考要去掉。
二、上下文编辑:清掉旧的工具结果
智能体读过的文件、搜过的结果,模型处理完之后多半不再需要,却一直占着上下文。这个策略到了阈值就把最旧的工具结果清掉,原地换成一句占位的话,告诉模型这里的内容被清除了。
"context_management": {
"edits": [
{
"type": "clear_tool_uses_20250919",
"trigger": {"type": "input_tokens", "value": 30000},
"keep": {"type": "tool_uses", "value": 3}
}
]
}
beta 头是 context-management-2025-06-27。
| 配置项 | 默认 | 作用 |
|---|---|---|
trigger |
10 万输入 token | 到什么程度开始清。可以按输入 token 数,也可以按工具调用的次数 |
keep |
3 次 | 清完之后保留最近几次工具调用 |
clear_at_least |
无 | 每次至少清掉多少 token,清不到这个数就不清 |
exclude_tools |
无 | 这些工具的结果永远不清 |
clear_tool_inputs |
否 | 是否连工具调用的参数一起清。默认只清结果 |
响应里有一项 context_management,告诉你这次清了什么。
实测:六次工具调用,清掉旧的
本站造了一段历史:模型依次读了 a.txt 到 f.txt 六个文件,每个文件的内容几百个 token。然后问它“你现在还看得到哪几个文件的内容”。
| 设置 | 清掉了几次 | 它的回答 |
|---|---|---|
| 只写策略名,全用默认值 | 0(没到默认的 10 万 token) | 六个文件都看得到 |
| 按次数触发,阈值 3,保留 1 | 3 次,1,506 个 token | 看得到 d、e、f,“a、b、c 的读取结果已被清除” |
| 按次数触发,阈值 1,保留 1 | 5 次,2,665 个 token | 只看得到 f |
| 按输入 token 触发,阈值 1,000,保留 2 | 4 次,2,644 个 token | 看得到 e、f |
| 按次数触发,阈值 3,保留 5 | 400 | 报错:保留的次数不能大于触发的次数 |
响应里对应的那一项长这样:
"context_management": {
"applied_edits": [
{"type": "clear_tool_uses_20250919", "cleared_tool_uses": 3, "cleared_input_tokens": 1506}
]
}
三点观察:
- 模型知道东西被清了。 它没有编造 a.txt 的内容,而是说看不到了。需要的话它可以再读一遍。
- 第二行不是“只留 1 次”。 阈值是 3 次、保留 1 次的时候,实际清到剩 3 次就停了。从这几次看,它清到不超过阈值为止,
keep是个下限。要清到只剩一次,阈值也得设成 1。 - 你的历史不用改。 每次都发的是完整的六次调用,清理是服务端在把提示交给模型之前做的。
它和缓存
清掉内容就改了开头,缓存从清的那个位置起作废。实测清理发生的那一次,usage 里是一次全新的缓存写入;同样的请求再发一次,就全部从缓存读了。
所以不要每次只清一点点。用 clear_at_least 保证每次清得够多,值得为它重写一次缓存。
清理旧的思考
另一个策略 clear_thinking_20251015 管的是以前几轮的 thinking 块。各模型默认的做法不一样:Opus 4.5 及之后、Sonnet 4.6 及之后、Haiku 5.5、Fable 默认全留着;更早的只留最近一轮。用这个策略可以改掉默认值。代码要跑在好几档模型上的话,文档建议明确写 keep,不要依赖各模型的默认。
留着思考,缓存不受影响;清掉思考,缓存从清的位置起作废。
三、通过中转站实测到的
| 功能 | 结果 |
|---|---|
| 上下文编辑:清工具结果 | 能用,表现和文档一致。不带 beta 头也能用 |
| 上下文编辑:清思考 | 请求被接受。测试的对话里没有思考,没看到效果 |
| 到阈值自动压缩 | 400:context_management.edits[0] has an unsupported strategy |
| 按需压缩 | 参数被忽略。返回 200,模型把对话当成普通请求接着回答了 |
最后一条要特别留意。本站带着 compaction 参数和 beta 头发了请求,拿到的不是压缩块,是一段一两千 token 的普通回复,stop_reason 是 end_turn,usage 里也没有 iterations:
请求摘要:HTTP 200 stop_reason=end_turn 块=[text('以下是基于 SQLite + 整数自增主键的主要实体设计…')] output=1421
没有报错,还花了一笔输出的钱。如果你的代码不检查 stop_reason 是不是 compaction,就会把这段回复当成摘要塞回历史里。照着文档写压缩的循环时,一定先检查 stop_reason,这在官方接口上也是必须的。
通过这家中转站要控制长对话的上下文,目前可行的是:用上下文编辑清工具结果;或者自己写摘要,也就是自己发一个普通请求让模型总结,再用总结替换历史。
和 Claude Code、Agent SDK 的关系
- Claude Code 自己管上下文:快满时自动压缩,也可以手动
/compact。见《Claude Code 会话和上下文》。 - Agent SDK 底下跑的是 Claude Code,同样是自动的。
- 官方 SDK 里还有一个客户端压缩(配合 tool runner 用)。文档说它已经不推荐,Python SDK 从 1.0 起去掉了,TypeScript 和 Ruby 里标了弃用。用服务端的压缩代替。
花费
这一轮十五次请求,按官方价估算约 0.15 美元,其中大半花在按需压缩那四次上:参数被忽略,模型各写了一两千 token 的普通回复。到阈值自动压缩原本要一段五万多 token 的对话才能触发,请求直接被拒了,没有产生费用。
这次实测没有覆盖到的
- 官方接口没有测。
- 两种压缩都没有跑通,这一家中转站不支持。压缩块的样子、放置的规则、各种报错、计费方式、和思考的配合,全部是按文档整理的。
- 保留最近几轮、后台压缩没有试。
- 上下文编辑只测了按规则清工具结果,用的是造出来的历史,不是真的工具循环。
clear_at_least、exclude_tools、clear_tool_inputs没有试,清思考的策略没有看到实际效果。 - 只测了一家中转站、一天。