【Claude Code-上下文实测】撑爆 Coding Agent 上下文的,往往不是聊天历史,而是工具输出
非线智能API经验 [Claude Code-上下文实测] 第18篇
摘要
长会话 agent 的上下文压力,常常不是用户和模型聊太久造成的,而是文件读取、搜索结果、测试日志、构建输出、MCP 工具结果、工具定义和错误信息造成的。一次失败测试、一段大 JSON、一份长文件,可能比几十轮自然语言对话更占 token。
截断机制可以防止单次输出无限膨胀,但它也会引入新的风险:模型可能基于不完整文件或不完整工具结果继续推理,形成“我已经读完了”的错误信念。因此,上下文治理不能只做历史消息摘要,必须把工具输出当成一等对象来预算、压缩、标记完整性和归档。
数据来源:非线智能Nonlinear 官网
证据层级
Claude Code MCP 文档可以支撑 MCP 输出告警和默认最大输出限制等机制事实。AkitaOnRails 的源码逆向文章可作为社区观察,用于说明文件读取和工具结果截断的可能实现细节,但不能写成官方保证。Claude Code GitHub issue 适合支撑 Read tool 截断、文件 token 超限、工具 / MCP 描述启动开销等真实用户问题。Start Debugging 和 dev.to 文章适合作为实践补充。
本文把“截断”视为必要保护机制,而不是简单 bug。真正的问题是截断是否显式、是否可继续读取、是否被模型误解为完整事实。
从聊天历史太长到工具输出太胖
传统聊天系统里,上下文增长主要来自用户和助手的自然语言轮次。coding agent 的上下文增长则更像一个运行日志:
| 来源 | 典型内容 | 特点 |
|---|---|---|
| 文件读取 | 源码、配置、文档、日志 | 体积大,局部相关性强 |
| 搜索结果 | rg 命中、路径、上下文片段 |
重复多,噪声高 |
| 测试输出 | 失败断言、stack trace、覆盖率 | 单次可能数万字符 |
| 构建输出 | 编译错误、依赖日志、warning | 关键错误常埋在噪声中 |
| MCP 工具结果 | 外部系统数据、JSON、表格 | schema 和数据都可能很大 |
| 浏览器结果 | DOM、console、截图描述 | 页面状态容易过期 |
| 错误信息 | API 400、权限失败、网络错误 | 容易污染后续推理 |
这些内容进入上下文后,不只是增加 token,还会改变模型注意力分布。旧错误日志、过期文件片段、重复搜索结果会和当前事实竞争注意力,让模型更容易围绕已经解决的问题继续推理。
MCP 是高价值入口,也是高风险入口
MCP 的价值是让 agent 连接外部工具和数据源。但 MCP 也会放大上下文体积,原因有三类:
1、工具定义和描述本身会进入上下文预算。
2、工具数量增加后,模型每轮都要在更多能力之间选择。
3、MCP tool output 可能返回大量结构化数据。
Claude Code MCP 文档提到,MCP tool output 超过一定 token 会告警,并有默认最大输出限制。这说明 MCP 输出已经被视为上下文风险源。问题不只是“返回太多会被截断”,而是工具设计者是否能让返回结果天然适合 agent 使用。
一个不适合 agent 的 MCP 工具通常这样返回:
{
"rows": [
{"hundreds": "of", "fields": "..." }
],
"debug": "...",
"raw_response": "..."
}
更适合 agent 的工具应返回:
{
"status": "ok",
"summary": "查询到 3 个失败任务,最早失败发生在 deploy step。",
"items": [
{
"id": "job_123",
"state": "failed",
"reason": "missing env var API_KEY",
"url": "..."
}
],
"truncated": false,
"next_page": null
}
上下文友好的工具结果应当默认摘要、分页、字段过滤,并显式标注是否完整。
截断不是纯收益
为了保护上下文,工具宿主通常会截断超大输出。截断可以避免单次工具调用耗尽上下文,但它也有副作用。
| 截断风险 | 表现 |
|---|---|
| 完整性误判 | 模型以为已经读完整文件或完整日志 |
| 关键片段丢失 | 错误根因刚好在截断后半段 |
| 计划错误 | 基于不完整依赖关系做修改 |
| 重复调用 | 模型反复读取同一大文件,浪费 token |
| 摘要污染 | 截断结果被写入压缩摘要,变成长期错误事实 |
GitHub issue 中关于 Read tool truncation 的反馈,正好说明这个问题:如果工具只给预览而没有强烈标注完整性,agent 可能把预览当成完整文件。对代码修改任务来说,这会造成非常具体的风险,比如遗漏后半段函数、忽略同名实现、误删未看到的配置。
工具结果完整性 metadata
每个可能截断的工具结果都应该带完整性信息:
{
"tool": "read_file",
"path": "src/example.ts",
"status": "ok",
"content": "...",
"completeness": {
"is_complete": false,
"original_size_bytes": 184320,
"returned_size_bytes": 32000,
"truncation_reason": "token_budget",
"range": {
"start_line": 1,
"end_line": 800
},
"continue_options": [
{"start_line": 801, "end_line": 1600},
{"query": "function targetName"}
]
}
}
不要只把截断写进工具宿主日志。模型上下文里也要出现“未完整读取”的事实,否则模型没有理由主动怀疑自己看到的是片段。
测试日志和构建日志应结构化摘要
测试输出是最常见的上下文膨胀源。完整日志通常包含大量重复 stack trace、warning、依赖输出和无关测试。但修复 bug 所需的信息往往可以压成很小:
{
"command": "npm test",
"exit_code": 1,
"failed_tests": [
{
"name": "maps tool_use to tool_calls",
"file": "tests/tool_mapper.test.ts",
"error": "expected tool_call_id to equal toolu_123",
"first_relevant_stack": "src/mapper.ts:84"
}
],
"full_log_ref": "artifacts/test-runs/2026-06-18/npm-test.log",
"truncated": false
}
这种摘要比完整日志更适合模型继续推理,也更适合压缩后保留。完整日志仍然要归档,以便复现和审计。
文件读取应定位式,而不是全文式
文件读取也要从“先读完整文件”变成“先定位再读取”。推荐流程:
1、用 rg 找符号、错误字符串、测试名。
2、读取命中附近的函数或配置块。
3、如果需要跨文件理解,再读取导入链和调用点。
4、只有在文件本身很短或结构未知时,才读取全文。
5、对长文件读取必须显示行号范围和完整性状态。
这并不是为了节省一点 token,而是为了减少模型接收低相关上下文的概率。代码理解任务里,高信号局部片段通常比低信号全文更可靠。
工具输出生命周期
工具输出不应该永久以原文形式留在模型上下文里。更合理的生命周期是:
| 阶段 | 上下文形态 |
|---|---|
| 刚返回 | 原始关键片段 + 完整性 metadata |
| 被模型使用后 | 结构化结论 + 原始引用 |
| 阶段结束 | 压缩进任务摘要 |
| 任务完成或过期 | 移出 live context,只留 audit ref |
这要求工具层、记忆层和审计层协同,而不是把工具返回字符串直接 append 到 messages。
工程策略
| 问题 | 策略 |
|---|---|
| MCP 工具太多 | 按任务暴露工具子集,懒加载 server |
| MCP 输出太大 | 分页、字段过滤、返回摘要优先 |
| 文件读取太大 | 定位式读取、行号范围、完整性标记 |
| 测试日志太长 | 提取失败测试、退出码、关键 stack |
| 构建日志噪声 | 保留首个有效错误和依赖版本 |
| 错误日志过期 | 标注 resolved / unresolved / stale |
| 截断误导模型 | is_complete=false 必须进入上下文 |
| 原文无法复盘 | 外置 artifact,摘要记录 hash/ref |
失败模式
| 失败模式 | 触发原因 | 修复策略 |
|---|---|---|
| 模型基于半个文件改代码 | Read result 被截断但未显式标记 | 强制完整性 metadata |
| 上下文突然爆掉 | 测试 / MCP 一次返回巨大输出 | 工具层分页和摘要 |
| 已解决错误反复出现 | 旧日志长期留在上下文 | 工具结果生命周期管理 |
| prompt cache 下降 | 动态工具结果进入稳定前缀 | 工具结果放入 dynamic tail |
| MCP 启动成本过高 | 所有 server/tool schema 全量暴露 | 工具能力索引和按需暴露 |
| 摘要无法审计 | 原始输出被丢弃 | raw_ref + hash + retention policy |
验证清单
• 构造超过工具输出限制的大 MCP 结果,确认告警、截断和分页行为。
• 构造长文件读取,检查模型上下文里是否明确出现 is_complete=false。
• 跑一次失败测试,确认上下文只保留失败摘要,完整日志进入 artifact。
• 构造已解决错误,确认后续摘要标注 resolved,不再污染下一阶段。
• 比较“全文读取”和“定位式读取”的 token 消耗与修复成功率。
• 启用多个 MCP server,统计 tool schema、tool count、MCP output 对输入 token 的贡献。
工程结论
coding agent 的上下文治理不能只盯着聊天历史。真正高频、高体积、高噪声的入口是工具输出、文件读取和 MCP。截断只是保护机制,不是治理策略;可靠的做法是让每个工具结果可预算、可摘要、可分页、可标记完整性、可复盘。只有这样,compact 才不会把不完整事实压缩成长期记忆。
参考链接
• Claude Code MCP Docs
• AkitaOnRails: Claude Code's Source Code Leaked. Here's What We Found Inside
• anthropics/claude-code #28783
• anthropics/claude-code #4002
• anthropics/claude-code #3406
• Start Debugging: How to Reduce the Number of MCP Tools Claude Loads
• dev.to: Tool-Result Truncation: The Silent Bug That Makes Agents Lie
本文由非线智能API Claude Code 行业专家整理编写