Claude Code 上下文太长怎么办:Prompt is too long 会自动压缩,中转站 / DeepSeek 的 maximum context length 却不会(实测)
先回答你搜的问题
- 看到
Prompt is too long · …:Claude Code 认出了这是上下文超长。多轮对话里它会自动压缩旧对话再重发,通常你什么都不用做;如果提示说 single exchange cannot be compacted,说明是系统提示、工具定义、附件本身太大,要减少 MCP 工具或附件,或者/clear重开。- 看到
API Error: 400 This model's maximum context length is …(用 DeepSeek 或中转站时最常见):Claude Code 不认识这句话,不会自动压缩,之后每一轮都会报同样的错。手动输入/compact,实测能恢复。- 想一劳永逸:告诉 Claude Code 模型的真实上限,让它在撞墙之前就压缩:
- 模型 ID 不以
claude-开头(如glm-4.6、deepseek-…):export CLAUDE_CODE_MAX_CONTEXT_TOKENS=131072(换成你的模型的真实上限)- 中转站给的模型 ID 以
claude-开头:export CLAUDE_CODE_AUTO_COMPACT_WINDOW=131072(最小 100000)- 看到
max_tokens相关的超限:不用管,Claude Code 会自动把max_tokens调小后重发。
问题背景
对话越聊越长,或者一次塞进很多文件,迟早会碰到上下文上限。在 Claude Code 里,这件事有两种完全不同的表现:
Prompt is too long · the request is ~210000 tokens (limit 200000) but this conversation is only ~12807 tokens — …
API Error: 400 This model's maximum context length is 1048576 tokens. However, you requested 1053707 tokens (1045707 in the messages, 8000 in the completion).
第一种有些人从来没见过,因为 Claude Code 在后台自动压缩掉了;第二种用 DeepSeek 或中转站的人会反复看到,而且 /clear 之前每轮都在报。区别不在模型,而在后端返回的报错文案。
问题分析
官方 错误文档 提到了一个细节:Bedrock 的超长报错是 Input is too long for requested model.,Before v2.1.217, Claude Code didn't recognize the Bedrock wording, so auto-compact never triggered on it。也就是说,自动压缩是按文案识别触发的。
在 2.1.285 的二进制里能找到识别函数,原样摘录:
function BWn(e){let n=e.toLowerCase();return n.includes("prompt is too long")||n.includes("input is too long for requested model")}
function jWn(e){return e.toLowerCase().includes("context window")} // 只用于 413
function M0r(e){return e.toLowerCase().includes("input length and `max_tokens` exceed context limit")}
function kA(e){ … return BWn(e.message)||bI(e.message,"prompt_too_long")}
所以 Claude Code 只认这几种说法:
| 后端文案(不区分大小写) | Claude Code 怎么处理 |
|---|---|
包含 prompt is too long |
当作上下文超长 → 自动压缩 |
包含 input is too long for requested model(Bedrock) |
同上 |
HTTP 413 且包含 context window |
同上 |
input length and \max_tokens` exceed context limit: A + B > C` |
解析出数字,把 max_tokens 调小后重发 |
| 其他文案 | 普通的 400 错误,原样显示 |
而 DeepSeek 的超长报错是 This model's maximum context length is 1048576 tokens. However, you requested … tokens (… in the messages, … in the completion)(站内 DeepSeek 1M 实测 中从 DeepSeek 的 Anthropic 兼容端点拿到的原文),OpenAI 风格的中转站透传的也是 maximum context length 这一类说法,都不在识别范围内。
技术方案与选型
| 方案 | 结论 | 理由 |
|---|---|---|
| 本地 stub 返回各种超长文案 | ✅ 采用 | 文案可以逐字控制;能看到 Claude Code 是否发出压缩请求(debug 日志 source=compact)、重发时带了几条消息、max_tokens 改成了多少 |
| 真的塞满 100 万 token 去撞 DeepSeek | ❌ 排除 | 每次都要花真钱;而且只能测一种文案 |
| 只读二进制推断 | ❌ 不能单独用 | 识别函数说明了「认不认」,说明不了认出来之后的完整流程 |
隔离做法同 429 篇:env -i、全新的空 CLAUDE_CONFIG_DIR、假 key、ANTHROPIC_BASE_URL 指向本机 stub、HTTPS_PROXY 也指向 stub 以拦截一切外连。
多轮对话怎么测:同一个隔离配置目录里,先用全部正常响应的 stub 跑 3 轮(claude -p → claude -p -c → claude -p -c)攒出历史,再换成「第一次报超长、之后正常」的 stub 跑第 4 轮。单轮对话没有旧历史可压缩,测不出自动压缩。
关于文案来源:Anthropic 格式按二进制里解析 token 数的正则 prompt is too long[^0-9]*(\d+)\s*tokens?\s*>\s*(\d+) 构造;DeepSeek 文案由上述实测里的两段原文拼接(中间是否还有别的句子未核实,但不影响结论:两段都不含识别关键词);OpenAI 风格中转按 code: context_length_exceeded 的常见格式构造。
实测过程
1. 单轮:认得出 vs 认不出
每种文案跑 2 次,结果一致(claude -p,exit 和 stdout 原文):
| 后端返回 | exit | Claude Code 输出 |
|---|---|---|
Anthropic prompt is too long: 210000 tokens > 200000 maximum |
1 | Prompt is too long · the request is ~210000 tokens (limit 200000) but this conversation is only ~12539 tokens — the rest is system prompt, tool definitions, and attachment content. A single-exchange … |
Bedrock Input is too long for requested model. |
1 | Prompt is too long · this conversation is a single exchange and cannot be compacted — the request size comes mostly from system prompt, tool definitions, or attachments. |
HTTP 413 Request exceeds the model's context window |
1 | Prompt is too long |
input length and \max_tokens` exceed context limit: 190000 + 32000 > 200000` |
0 | OK(自动重发,见第 3 节) |
DeepSeek This model's maximum context length is … |
1 | API Error: 400 This model's maximum context length is 1048576 tokens. However, you requested 1053707 tokens (1045707 in the messages, 8000 in the completion). |
OpenAI 风格中转 context_length_exceeded |
1 | API Error: 400 This model's maximum context length is 131072 tokens. … |
认出来的会被改写成 Prompt is too long · …,还会解析出 token 数给你算账(「请求 21 万,但对话只有 1.25 万,其余是系统提示、工具定义和附件」);认不出来的就是原样一句 API Error: 400。
交互模式下的显示:
❯ reply with exactly OK
⎿ Prompt is too long · the request is ~210000 tokens (limit 200000) but this conversation is only ~12807 tokens — the
rest is system prompt, tool definitions, and attachment content. A single-exchange conversation cannot be
compacted; reduce attached files/tools or start with less context. · /clear to start fresh
❯ reply with exactly OK
⏺ API Error: 400 This model's maximum context length is 1048576 tokens. However, you requested 1053707 tokens
(1045707 in the messages, 8000 in the completion).
2. 多轮:认得出的自动压缩,认不出的直接失败
前 3 轮正常,第 4 轮第一次请求返回超长报错,之后全部正常:
| 后端文案 | 第 4 轮 exit | stub 收到的请求 | debug 日志 |
|---|---|---|---|
| Anthropic | 0,输出 OK |
400(7 条消息)→ 压缩请求 → 重发(5 条消息)→ 成功 | source=compact、Forked agent [reactive-compact] |
| Bedrock | 0 | 400 → 压缩 → 重发 → 成功 | source=compact |
| 413 + context window | 0 | 400 → 压缩 → 重发 → 成功 | source=compact |
| DeepSeek | 1,API Error: 400 … |
只有 1 个请求 | 没有 source=compact |
| OpenAI 风格中转 | 1 | 只有 1 个请求 | 没有 source=compact |
认得出的文案,自动压缩完全无感:-p 的输出只有 OK,exit 0。认不出的文案,Claude Code 连压缩都不会尝试,下一轮带着同样长的历史再发,再报同样的错。
(脚注:模型 ID 换成 glm-4.6 这类非 claude- 开头的 ID 时,认不出的 400 会被原样重发 1 次。debug 日志写明了原因:an unrecognised HTTP 400 arrived while the dangerous-tool-use beta was on the request; retrying once without it,是去掉一个 auto mode 相关的请求头再试一次,和上下文无关,重发后照样失败,也不会压缩。)
3. max_tokens 超限:自动调小
后端返回 input length and \max_tokens` exceed context limit: 190000 + 32000 > 200000时,Claude Code 第二次请求的max_tokens` 从 32000 变成了 9000:上限 200000 − 输入 190000 = 10000,再留 1000 余量。单轮、多轮都是一次重发就成功,exit 0。
4. 认不出时手动 /compact:有效
DeepSeek 文案下第 4 轮失败(exit 1)后,第 5 轮发 claude -p -c '/compact':stub 收到 1 个压缩请求(source=compact),exit 0。第 6 轮正常对话,请求里只剩 1 条消息(压缩后的摘要),exit 0。
5. 提前压缩:告诉它真实上限
最好的办法是不等后端报错。stub 在前 3 轮报告每轮输入 128000 token(模拟一个快满的对话),第 4 轮全部正常响应,看 Claude Code 会不会在发主请求之前先压缩:
| 模型 ID | 设置 | 第 4 轮 | 结论 |
|---|---|---|---|
glm-4.6 |
不设 | 直接发主请求(11 条消息) | 对照组:它以为窗口还够 |
glm-4.6 |
CLAUDE_CODE_MAX_CONTEXT_TOKENS=131072 |
先发压缩请求,主请求只剩 2 条消息,exit 0 | ✅ 生效 |
claude-sonnet-4-5 |
CLAUDE_CODE_MAX_CONTEXT_TOKENS=131072 |
直接发主请求,没压缩 | ❌ 对 claude- ID 不生效(和文档一致) |
claude-sonnet-4-5 |
CLAUDE_CODE_AUTO_COMPACT_WINDOW=131072 |
先压缩,exit 0 | ✅ 生效 |
claude-sonnet-4-5 |
同上,但每轮只用 60000 token | 不压缩 | 对照组:没到阈值就不压 |
官方文档 对 CLAUDE_CODE_MAX_CONTEXT_TOKENS 的说明是:模型 ID 不以 claude- 开头时直接生效;能解析成已知 Claude 模型的 ID,只有同时设了 DISABLE_COMPACT(会关掉一切压缩)才生效。所以中转站给 claude- 开头 ID 的情况,用 CLAUDE_CODE_AUTO_COMPACT_WINDOW 更合适(文档:取值 100000~1000000,只接受纯数字,500k 这种写法会被读成 500 再被抬到最小值)。
实践效果
1. 先看报错是哪一种。Prompt is too long · … 是 Claude Code 认出来的,多轮里会自己处理;API Error: 400 This model's maximum context length … 是它认不出来的,要你出手。
**2. 认不出时:输入 /compact。**实测有效,压缩后下一轮恢复正常。不想保留历史就 /clear。
3. 用 DeepSeek / 国产模型 / 中转站,长期使用建议加上(数值换成你的模型的真实上下文上限):
# 模型 ID 不以 claude- 开头(glm-4.6、deepseek-…、kimi-… 等)
export CLAUDE_CODE_MAX_CONTEXT_TOKENS=131072
# 中转站给的模型 ID 以 claude- 开头,但背后的真实上限更小
export CLAUDE_CODE_AUTO_COMPACT_WINDOW=131072
这样它会在快到上限时主动压缩,根本不会撞到后端那句它认不出来的报错。上限数值请以模型官方文档为准;注意很多后端的上限是「输入 + 输出」之和,留一点余量更稳。
**4. 单轮就超长(提示 single exchange cannot be compacted):**压缩救不了,因为大头是系统提示、工具定义和附件。减少启用的 MCP 服务器、少 @ 几个大文件,或者把大文件拆开读。
**5. 网关开发者:**如果你的网关在做协议转换,把上下文超长的报错文案里带上 prompt is too long,Claude Code 用户就能自动压缩,体验会好很多。
本轮全部请求都打在本地 stub 上;为确认 DeepSeek 对过大 max_tokens 的处理,另外直接调用了 2 次 DeepSeek(它会静默截断 max_tokens 并正常回答,共约 190 token),没有经过 Claude Code。
踩坑
- **单轮对话测不出自动压缩。**第一轮实验全部是单轮,认得出的文案也只是报错退出,差点得出「自动压缩不存在」的结论。提示里那句 single exchange cannot be compacted 才点醒:没有旧历史可压。
- **交互模式下报错被侧请求吃掉了。**录交互界面时,stub 的「第一次报错、之后正常」被每句话附带的第二个请求先领走,主请求拿到的是正常结果。改成「一直报错」才录到报错画面。
- **一次「意外成功」差点误导结论。**用
glm-4.6测手动/compact时第 4 轮居然成功了,查日志才发现是去掉 beta 头后的那次重发恰好拿到了 stub 的正常响应,和压缩无关。随后用「一直报错」的 stub 复测确认:认不出的文案在任何模型 ID 下都不会压缩。
未验证清单:真实 DeepSeek 超长报错的完整原文(本文使用两段实测原文的拼接);真实中转站的报错原文(各家可能不同);压缩摘要的质量(stub 的摘要只是 OK);DISABLE_COMPACT 搭配 CLAUDE_CODE_MAX_CONTEXT_TOKENS 的行为(会关掉所有压缩,不推荐,未测);OAuth 订阅登录下的行为;Claude Code 在本地预估超长而直接拒绝的路径(见 DeepSeek 1M 实测)。