Claude Code是如何做上下文工程的?
Claude Code是如何做上下文工程的?
从提示词工程到上下文工程,一个新的名词的出现往往是思维范式上的转变,一个工程的出现往往是为了解决某些问题产生的一种方法论。
最近的很多实践里都发现Agent能不能有满意的交付的关键就在于能不能理解上下文,这考验模型的能力,以及Agent系统提供准确充足上下文的能力。
不可否认,Claude Code的强大源于Claude系列模型的强大,但有力的上下文支持也是Claude Code成功的关键。
接下来让我们以Claude Code逆向工程仓库为基础一点一点探索Claude Code是如何做上下文工程的?
1.上下文,一种支撑prompt的语境
上下文这个词还是太过于技术表达,咱们换个更接地气的表达——语境。
如果咱们把prompt当成一种指令,一次对话,那么上下文就是为每次对话提供足够的语境支持。就像两个人聊天,如果对方不知道你刚才在做什么、在想什么、有什么背景,就很难理解你的话。
比如你说"帮我看看这个",如果对方不知道你正在写代码、不知道你遇到了什么错误、不知道你的项目结构,他就无法给出真正有用的帮助。
2.Claude Code的上下文的难题
最近上下文工程这个词很火,很多朋友可能会问不就把上下文信息塞到prompt里吗,为什么还需要一个新的上下文工程?
在回答这个问题之前,咱们先来看上一篇提到的Claude Code逆向工程研究仓库,仓库里有一段对ClaudeCode运行log的分析。
一次重构任务ClaudeCode需要给出系统提示,运行大量工具调用,从而产生了执行结果、错误信息、状态反馈等海量的细节。从运行日志来看一轮Agent loop 产生了84247字节的上下文,这还只是一轮Agent Loop,一次重构任务经常面临着多轮AgentLoop,如此庞杂的上下文信息该如何管理呢?
除此之外ClaudeCode还有分层多Agent架构(主Agent + SubAgent),每个Agent有自己的上下文信息,如何传递协调不同Agent之间的上下文也是个大问题。
3.上下文的四大痛点
Drew Breunig把这些上下文难题总结成四大痛点:
1.上下文中毒
比如当一次模型虚构了工具调用状态信息,被写入上下文时,在多轮循环中,模型可能会把错误信息误认为是事实反复引用,并执着于实现不可能或无关的目标,以至于后续每一步决策都在错误基础上前行,越走越偏。
2.上下文过长,情境干扰
当上下文过长,会导致模型注意力资源被稀释,过度关注上下文,忽略了原本通过训练获得的内容,输出质量下降。
虽然现在的大模型都已经支持百万级别的 token 上下文输入,但是 Gemini 2.5 的一份技术报告中指出,随着上下文超过 10 万个 token,大模型倾向于多步骤生成推理的长上下文,而非用于检索的长上下文。
3.多余信息过多,语境混淆
上下文中多余的内容也会干扰大模型的响应。以MCP工具调用为例,用过的朋友都知道,如果配置了数十个五花八门的MCP函数给大模型,每次调用时大模型要对数十个MCP做判断,调用工具决策的准确性会大幅下降。
伯克利今年6月发布的报告,显示大模型在引入函数调用或使用工具的时候,几乎所有大语言模型的性能都会比原始文本生成任务中有所下降,在多函数调用场景中,准确率显著低于单函数调用。
4.多个信息源,语境冲突
语境冲突常出现在多个Agent协作时,比如导航subAgentA说中山路往东,导航subAgentB说中山路要往西,大模型做决策时到底听谁的?
4.ClaudeCode的上下文结构六大模块
为了解决这些痛点,在工程上,咱们就需要解决上下文结构是什么样的?什么时候增减?什么时候压缩?如何压缩?是否需要隔离?谁来写?谁来拼接?
解答这些问题这需要一套系统的工程方法论,这就是上下文工程的由来。
咱们还是从Claude Code逆向工程研究仓库的这份context_dump.log来看,这个690行的日志文件包含了一次逆向分析ClaudeCode代码会话的完整上下文信息,按照日志原文结构分为以下六个部分:
1. 系统信息内容
- 包含系统身份定位,安全策略,交互规范,代码风格,工具使用指南,任务管理,工作流程以及示例展示的系统提示词。
- 第二部分包含工作目录路径,近期git状态,文件列表。
2. 会话延续上下文
-
主要记录之前的会话完整历史,包含初始背景,主要请求,关键行动,用户反馈,修正措施,最终任务,最新请求
-
第二块是会话中断的原因,中断状态,当时的工作进度
-
这块是之前提到过的八段式压缩上下文,将超过阈值的上下文总结成:
- 用户的主要请求和意图、关键技术概念、相关的文件位置、出现的问题及其解决方案、问题解决的思路方法结果、所有用户消息的完整记录和时间线、 待完成的任务(包含挂起的任务)和当前工作及下一步的结构化摘要。
- 日志里面发现其实九段,其中对挂起的任务做了单独的总结,这在压缩提示词模板里是可选项。
3. 会话中的工具的调用和反馈
- 这块包含工具的调用参数,反馈。需要注意的是,Claude Code的subAgent,todo也是一种工具,所以你在这里也会看到TaskAgent相关的信息和todolist的任务状态。
4. 已验证的关键任务节点
- 基于工具调用,日志等信息汇总而成的关键任务结论。
5. 用户请求
- 这块主要按时间顺序,结构化用户最新请求。
6. 系统信息
- 包含用户近期操作,打开或点击文件,有意思的是ClaudeCode在备注用户操作文件时给出提示词不要告诉用户,不需要因为状态变化而重读文件。
【注】提示词里会用标记system-reminder 用户消息,工具调用信息 ,用于与其他信息区分,并用ismeta是用户消息,用于与系统消息区分。
5.Claude Code的上下文更新流程图
从上下文的结构上,咱们就能大致猜测出什么时机注入什么上下文,接下来咱们拆解一下,Claude Code的上下文更新机制:
6.Claude Code的上下文更新流程
从前面流程图不难看出来,整个机制就是以事件触发为起点,经过消息队列,上下文处理等流程到最后API调用响应为终点。
整个流程大体可以总结为的
- 用户操作/系统事件 → 事件分类 → WD5分发处理
→ K2创建消息 → 消息队列 → nO主循环 → wU2压缩
→ Ie1上下文注入 → wu API调用 → 响应处理
Claude Code的上下文更新机制咱们可以总结成三个原则
- 事件驱动
- 实时响应
- 按需注入
7.Claude Code上下文更新的三个原则
1).事件驱动
-
Claude Code上下文更新触发机制,采用事件驱动架构,当产生文件操作,工具执行,用户输入,目录变更时等操作时就会触发事件,由WD5函数作为事件分发中心。
-
每一个事件对应着相应的提示词模板,比如plan模式激活会触发创建限制写文件操作的提示词,打开文件触发提示用户打开文件的提示词。
-
除此之外比较特别的是,ClaudeCode将对内存的访问事件,也会触发事件处理机制,比如内存紧张时触发紧张压缩机制,传递提示词给压缩大模型进行紧急压缩。
2).实时响应
-
当这些事件触发之后,K2函数会创建对应的提示词,事件提示词会立马被塞到一个消息队列里。
-
每次调用API前,如果消息队列里有新的内容,都会对其重新做上下文的注入,过期之前的会话的用户信息和工具信息,从而保证上下文的时效性。
-
第二层实时响应,是在主循环多个yield _处设置中断点,支持用户实时的进行交互。
3).按需注入
- 由于在触发事件时,才生成提示词,这种机制能够让ClaudeCode,每次只注入需要的提示词,比如只需要某些工具的调用,就只注入某些工具调用的提示词,减少多工具调用的语境混淆,避免多余的信息干扰。
- 消息队列会将带有前文提到的system-reminder用户消息,工具调用信息总是放在消息队列最前面,确保AI最先看到最新的状态信息。
- 以及对ismate用户消息特殊处理,确保用户信息不丢失。
- Ie1函数通过条件检查机制,只有在存在相关事件队列时才进行注入,从而才做API调用,避免了无效上下文注入和不必要的API的调用。
- 可以通过多种方式触发,对上下文内容进行结构化压缩。
8.Claude Code的上下文压缩触发节点
Claude Code上下文压缩机制的核心是qH1函数,有三种触发方式:
- 1.自动触发通过主循环调用wU2压缩判断达到token使用阈值之后自动触发qH1函数执行压缩。
- 2.通过用户输入/compact 命令手段执行压缩,
- 用户可以通过输入"/compact"命令跳过是否压缩判断,自主选择时机压缩上下文。
- 用户还可以通过输入"/compact 请重点关注TypeScript代码变更",传递给压缩提示词模板,要求他除了原有八段式要点之外增加"请重点关注TypeScript代码变更"当作压缩要点。
- 3.系统检测到内存压力时触发,系统会持续监控内存和性能指标,当指标超过阈值时,也会触发事件生成提示词,通过提示词传递给qH1函数执行更为激进的紧急压缩策略。
9.上下文压缩流程图:
10.ClaudeCode的上下文压缩流程:
ClaudeCode压缩流程里,最核心的无疑就是调用专用压缩大模型进行总结抽象式压缩,当然前期消息准备,和后期上下文重建也起到重要作用。整个压缩流程可以总结为三个阶段:分析,执行,上下文重建
-
1.分析阶段,
- 主要做消息的分类整理,工具消息,用户消息,以及其他类型的区分处理
- 第二层是从多维度对上下文进行分析包含:消息类型的统计,工具使用情况的统计,时间序列分析,文件操作的报告,以及内容复杂度分析
- 第三是对token的计算分析,token用量,各消息token使用占比等,从而计算可能压缩的空间,生成一份分析报告。
-
2.执行阶段,
- 生成压缩提示词,如果有外部传递过来的压缩要点提示,这个阶段也会进行整合。
- 将压缩提示词,上下文,分析报告向特定上下压缩大模型上报等待响应
-
3.上下文重建阶段,
- 在这个阶段会将在token预算内,根据时间频次等指标尽可能的恢复重要文件,写回上下文
- 此外Agent的状态进行恢复,比如todo工具信息,会话延续上下文,任务进度等
- 为了避免核心提示词被压缩,还会重新注入系统提示词,与压缩后的上下文一起组成新的上下文
- 另外这个阶段还会对UI状态做恢复,对存储的信息进行更新。
11.Claude Code的如何协调多Agent结构
我们在上篇文章中提到过Claude Code采用Task工具作为一种子Agent协作模式,整体运行流程是这样的:
12.多Agent避免上下文冲突的机制
在Agent的运行方式上,通常可以划分为串行的,并行的,有上下文共享的,上下文不共享的。如果说todo+Agent Loop的回调算一种串行的,有上下文共享的Agent。那么Task工具可以说实现一种并行的,上下文不共享的Agent。
Task Agent在避免上下文冲突和混乱上做了许多工作:
1. 隔离机制
- 完全隔离的执行环境:独立会话ID、空消息历史
- 选择性上下文继承:只继承必要环境信息,不继承对话历史
- 工具权限白名单:由于TaskAgent是通过工具实现的,所以限制可用工具,不继承Task等工具,防止递归调用,不允许再次拆分任务
- 关键任务注入:将拆解后的任务注入到不同的TaskAgent里
2. 状态管理
- 无状态设计:每次调用完全独立
- 单向通信:TaskAgent只向主Agent传递工具调用报告,不传递上下文信息
- 并发协调:UH1函数管理多Agent并发
3. 结果处理
- 独立结果收集:每个Agent结果独立存储
- 智能排序:由于TaskAgent是并发执行的,但任务拆解时之间本身可能有顺序关系,在整合结果的时候按Agent索引确保结果顺序一致
- 智能结果合成:KN5函数本质上是一个新的TaskAgent专门用于合并多Agent结果,合成结果时会按照以下要求大模型来合成结果:
- 阅读所有Agent的响应
- 识别每个响应的核心观点
- 检测观点之间的冲突
- 基于任务目标进行优先级排序
- 生成能够协调所有观点的统一方案
- 保留重要的技术细节和代码示例
- 组织成结构化的输出格式
- 错误隔离:由于单个Agent是独立环境运行的,单个Agent的错误不影响其他Agent