Claude Code 输出被上下文截断

agent 长回复半句话被切掉,是上下文压力惹的祸。多半是 compaction、max_tokens 上限、或者某次工具输出把预算吃光了。

你让 Claude Code 写一份详尽的总结、一个长计划、或者一份多章节报告。它写到一半就停了——没报错、没提示继续、看不出原因。或者下一轮带着一个「context compacted」的标记出现、原来的细节没了。Claude Code 工作在一个固定大小的 context window 里。工具调用、文件读取、之前的 assistant 轮、system prompt 全都和你这次回复挤在同一个 window 里。预算紧了,模型可能被 max_tokens 卡住、对话可能被自动 compact、单次工具输出可能把这一轮的预算吃光。修复方法看你撞上了哪种压力点。

常见原因

按命中率从高到低。

1. Assistant 回复撞 max_tokens 上限

每一轮 assistant 回复都有 token 数上限。你的 prompt 邀请的是 20k token 的回答、上限是 8k,那回复就在 8k 处断掉。

怎么判断:数一下被截断回复的 token 数。卡在 8192 或 16384 这种整数附近,就是撞上限了。

2. 单次工具结果把窗口吃光

一次 Bash 调用吐出 20 万字符、或者 Read 一个超大文件,留给 assistant 思考和回复的空间就没了。

怎么判断:看截断之前最近那次 tool_result,如果体积巨大,就是它。

3. 对话 compaction 中途启动了

用量上去之后,Claude Code 可能把旧轮自动 compact 成总结。compact 之后那一轮新的 assistant 回复可能丢了它原本依赖的细节。

怎么判断:transcript 里截断那一轮的前面找找有没有「compacted」或「summarized」标记。

4. System prompt + skills + tools 不知不觉膨胀了

每个插件、Skill、MCP server 都给 system block 加 token。原本不大的用户 prompt 现在前面顶着 40k token 的 overhead,留给回复的就不多了。

怎么判断:跑 claude --debug 看报出来的 system token 数。意外高的话能解释很多事。

5. 之前的 assistant 轮太啰嗦、一直占着 context

早先几轮要是吐了大段代码,那些都还留在 context window 里。几轮下来就把你预算占完了。

怎么判断:往上翻。会话里全是几千行的代码 dump,预算就没了。

6. 模型确实没更多东西要说

有时候看着像被截、其实模型本来就想停在那。「半句话」的感觉可能是个该有却没产生的句号。

怎么判断:看 API 响应里的 stop reason。stop_reason: end_turn 是模型主动停;max_tokens 是被卡住。

开始前

  • 大致记下被截断回复的长度(行数或字符数)。
  • 能访问 ~/.claude/projects/ 下最近的 transcript 文件。
  • 想清楚你是只要一次完整输出、还是反复都要长输出。
  • 必要时准备把 prompt 拆小。

需要收集的信息

  • Claude Code 版本:claude --version
  • 触发截断的那条 prompt 原文。
  • 这一轮结束时显示的 token 使用量(如果看得到)。
  • 截断点前最后几条 tool result。
  • transcript JSON 里的 compaction 标记。
  • claude --debug 报出来的 system prompt 大小。

一步一步修复

Step 1:确认 stop reason

打开最近的 session transcript:

ls -lt ~/.claude/projects/*/sessions/ | head -3

找到被截断那一轮,看 stop_reasonmax_tokens 就是撞上限;end_turn 就是模型自己停的。

Step 2:缩小前面那次工具输出

截断前的 Bash 或 Read 结果太大,就改用更窄的范围重跑:

# 不要 dump 整个文件,改成:
sed -n '100,200p' big-file.log

或者写到磁盘、让模型读一份摘要。

Step 3:让模型从断点继续

简单一句「从最后一句话继续」通常就行,因为之前的 context 还留着半截答案。别让它从头重来,那是双倍 token 开销。

Step 4:把任务拆章节

要长结构化输出,每次只要一段:

只写第 1 节:架构概览。这一节写完就停。

下一轮再要第 2 节。每一轮都舒舒服服地装在上限以内。

Step 5:在长回复之前主动 compact

会话里堆了一堆无关的工具调用,跑一下 /compact 把它们总结掉、给接下来的回复腾出 token。注意可能会丢细节。

Step 6:关掉用不到的 MCP server 和 skill

每个 MCP server 和 skill 都往 system prompt 里加 token。这次会话用不到的就关掉:

claude --no-mcp

或者临时从 settings.json 里挪掉。

Step 7:反复要长输出,就写到文件

让 agent 把长输出用 Write 写进 markdown 文件,对话里只回路径加摘要:

把完整报告写到 /tmp/report.md,回复时只给路径加 5 条 bullet 的总结。

这样根本不会撞对话里的 token 上限。

怎么验证修好了

  • 用修好的方式再发一次同样的 prompt,能拿到完整回复。
  • transcript 里 stop reason 从 max_tokens 变成 end_turn
  • 后面几轮不再触发意外 compaction。
  • 文件输出方式下,磁盘文件内容是完整的、就算对话里回复短。

长期预防

  • 工具输出尽量小,多用 head、tail、sed range、grep,少用全量 dump。
  • 长结果一律写盘、用路径引用。
  • 会话用到窗口 50% 以上时主动 /compact
  • 审一下当前激活的 skill 和 MCP server,平时不用的关掉。
  • 反复要的报告,prompt 模板里固定拆章节。

容易踩的坑

  • 截断后让模型「从头再生成一遍」——token 直接翻倍、上限照样撞。
  • 读整个 log 文件、而不是 grep 出相关行。
  • 让 compaction 静默丢掉之前重要的决策;compaction 摘要要看一眼。
  • 五个 MCP server 都连着、其实只用一个;system prompt 巨大。
  • 把每个短回复都当成截断,其实模型只是 end_turn。

常见问答

  • Claude Code 的 context window 多大? 看模型。Sonnet 和 Opus 是 200k token,但扣掉 system prompt、tools、history 之后,实际回复预算小得多。
  • max_tokens 到底是啥? assistant 回复 token 数的上限。和总 context 大小是独立的。
  • 能调大 max_tokens 吗? 有的版本能在 settings 里设、或者换模型,但更稳的办法是拆 prompt。
  • 为啥对话自己 compact 了我没让它做? Claude Code 在用量接近上限时会自动 compact。也能手动 /compact
  • 写文件算 context 用量吗? Write 调用本身占一些 token,但文件内容不占——这正是它的优势。
  • 要不要总把没用的 MCP 关掉? 长会话该关、短会话无所谓。每个连着的 server 都耗 token。

相关

标签: #Claude Code #排查 #agent #排查