很多人认为 Agent 开发最重要的是接模型、调工具。错。业内共识是:
Context Engineering(上下文工程)是 Agent 开发最重要的工程能力,没有之一。
本文讲清楚它的方法论:五个维度、提示词怎么设计、上下文怎么压缩、缓存怎么做。
一、为什么说它最重要?
理由很简单:模型大家都用同一批(GPT、Deepseek...),工具系统也大同小异。真正拉开差距的是上下文管理——它直接决定了你的 Agent 在第 50 轮之后还能不能做出正确决策。
我们无法决定模型的能力,只能决定喂给模型的上下文长什么样。
还记得"上下文腐烂"吗——上下文越长,模型对每一条信息的注意力越弱。而 Agent 恰恰是个"跑 50 轮、读 50 个文件"的长跑选手——上下文工程就是它的供氧系统。
以下两种方式,确实也可以提升Agent的能力,但是:
- "Agent 不够聪明,我训一个自己的模型吧"——微调(Fine-tuning)解决不了上下文管理问题,别动不动就想训模型
- "等产品稳定了我用 RL 优化 Agent 行为"——方向没错,但不要试图重建基础模型,工程手段优先
二、上下文工程的五个维度
整个上下文工程可以归纳为五个字:卸、缓、压、隔、检。
| 维度 | 解决什么问题 | 手段 |
|---|---|---|
| Offload(卸载) | 上下文太挤 | 把信息搬到上下文之外(数据库、文件系统),只留引用 |
| Cache(缓存) | 重复计算 | 复用已有计算结果(KV Cache、Prompt Cache) |
| Reduce(压缩) | 上下文太长 | 压缩成更短的形式(摘要、紧凑化) |
| Isolate(隔离) | 一个上下文装不下所有任务 | 拆成多个独立上下文(Multi-Agent) |
| Retrieve(检索) | 需要的信息不在上下文中 | 从外部源检索(RAG、文件读取) |
五个维度有优先级(重要!):
Offload > Cache > Reduce > Isolate > Retrieve
先卸载(文件系统是廉价的,上下文窗口是昂贵的);能缓存就缓存(别重复算);再压缩(先温和后暴力);实在不行拆上下文(多 Agent);最后才是检索。
2.1 Offload(卸载)
上下文窗口是昂贵的,但文件系统是廉价的。所以可以把信息卸载到上下文之外,仅在上下文中保留一个引用(比如文件路径)。需要时再读回来。
2.2 Cache(缓存)
缓存的方式有很多种,KV Cache、Prompt Cache,这两种都是大模型厂商内置的,我们的Agent中无需再做,但还有一种 Context Collapse(上下文折叠):把上下文中的重复信息折叠起来,只保留一次
2.3 Reduce(压缩)的两个层次
- Compaction(紧凑化):不改变对话结构,把老旧的工具结果替换成占位符
[old_tool result cleared],或缩短工具参数。信息损失小,回收空间也小。 - Summarization(摘要化):用一段 LLM 生成的摘要替换整段对话。消息结构被破坏,信息损失大,但 token 回收多。
2.4 Isolate(隔离)的两种方式
- By communicating(消息传递):主 Agent 给子 Agent 发一段任务消息,子 Agent 处理完返回结果。主 Agent 的上下文里只多出"发出的消息 + 返回的结果",完全不知道子 Agent 的内部细节。
- By sharing context(共享上下文):子 Agent 直接复制主 Agent 的完整上下文,在此基础上继续工作。主 Agent 上下文只多出"返回的结果",但子 Agent 的 token 消耗更大。
Claude Code 两种都支持,按需选择。
2.5 Retrieve(检索)
需要的信息不在上下文里,就从外部源检索——RAG 是其中的代表。
三、提示词工程:不只是"写得好"
很多人以为提示词就是打磨一句话。但 Agent 的提示词是一套行为控制系统,追求的不是单轮回答质量,而是稳定性——模型可能连跑 50 轮,每一轮都要做出正确决策。
- Chat prompt 追求高质量:一问一答,答得漂亮就行
- Agent prompt 追求稳定:连续 50 轮不出昏招
Agent 的提示词系统要回答四个问题。
问题一:Prompt 怎么分模块?
把一大段话拆成职责清晰的模块:
const systemPrompt = [
identitySection(), // 身份:"你是 XX,负责 YY"
systemRulesSection(), // 系统规则:环境约束
taskGuidelinesSection(), // 做事方式
riskGuidelinesSection(), // 行动准则:危险命令要确认
toolUsageGuide(tools), // 工具指南:根据实际工具列表动态生成
outputStyle() // 输出风格:简洁,不要……
]
模块化的好处:
- 独立修改:改输出风格不会碰坏身份和规则
- 条件组装:不同场景选择性拼装不同模块
- 缓存友好:稳定的前缀可以在多次请求间复用缓存,显著降低延迟和成本
问题二:哪些能够缓存?
System prompt 有静态/动态分界线:
const systemPrompt = [
// ---- 静态部分:全局可缓存,所有用户共享 ----
identitySection(),
systemRulesSection(),
taskGuidelines(),
riskGuidelines(),
toolUsageGuide(tools),
outputStyle(),
// ============ 分界线 ============
// ---- 动态部分:每个会话都不同 ----
envInfo(cwd, gitStatus), // 工作目录、Git 状态
userConfig(claudeMd), // 用户自定义规则
languagePref(lang), // 语言偏好
memoryContext(memories), // Memory 内容
]
静态在前,动态在后——因为缓存是按前缀匹配的,前缀越稳定,缓存命中越多。
问题三:设置了系统提示词后,用户还怎么自定义 Agent 角色?
两条规则:
- 越通用的配置,优先级越低:全局系统提示词的默认规则会被用户自定义规则覆盖
- 低优先级的先加载,高优先级的后加载:因为transformer的注意力机制,模型对 prompt 末尾的内容更敏感
问题四:高频变化的信息放哪?
放对话消息里,别放 system prompt 里。
比如用户在 IDE 里打开了一个文件,这个信息每小时都在变。正确做法是在用户消息前注入:
<system-context>
当前 IDE 打开的文件:src/auth.ts (第 42 行)
相关 Skill:@security-review(安全审查最佳实践)
今天日期:2026-04-07
</system-context>
帮我看看 auth.ts 有什么问题
这样 system prompt 保持稳定、缓存不被击穿。
四、压缩:三层策略
不管提示词管理做得多好,Agent 跑完 50 轮后,上下文大概率会爆。Claude Code 的策略是三层处理,先试轻手段,不行再上重手段:
第 1 层:Microcompact(微压缩)
不删消息、不改对话结构,只把老旧的工具结果缩小。比如第 3 轮读文件得到 3000 token 的结果,到第 30 轮时这个结果再也用不上了,就替换成一句话 [Old tool result content cleared]。省了 token,对话结构完好。
第 2 层:Snip(裁剪)
直接砍掉最老的消息。从历史对话头部开始删,删了就永久丢失。
第 3 层:Auto-compact(自动摘要)
用 LLM 给整个对话生成摘要。流程:
- 剥离图片:上下文中的图片替换成
[Image]标记 - Fork 一个子 Agent 专门生成摘要(绝对不能在主Agent干)
- 生成严格九段结构的摘要:用户意图、技术概念、文件改动、错误修复、问题解决、用户消息、待办任务、当前工作、下一步
- 恢复关键文件:压缩后附加最近读过的 5 个文件内容(防止丢失当前任务的关键信息)
- 替换消息:旧消息 →
[压缩边界标记] + [摘要] + [恢复的文件] + [最近对话]
什么时候触发压缩?
触发阈值 = 有效上下文窗口 - 13K(留出安全余量)
五、缓存:三级缓存体系
5.1 KV Cache(模型推理层,不受 Agent 管控)
模型推理时会把已计算的 K/V 缓存起来,避免重复计算。它按 prompt 前缀 匹配——前缀一变,缓存全失效。这也是为什么提示词要"静态在前、动态在后"、工具要延迟加载。
5.2 Prompt Cache(API 层,开发者可控制)
模型厂商提供的 API 级缓存:相同的 prompt 前缀直接复用,计费打折。用法是在请求里标记缓存断点:
{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "你是一个代码助手。以下是行为规则……(几千 token 的规则)",
"cache_control": { "type": "ephemeral" }
}
],
"messages": [
{ "role": "user", "content": "帮我看看 auth.ts" }
]
}
那个 cache_control 标记就是关键:它告诉服务端"这一段帮我缓存住,下次别重算"。
5.3 Context Collapse(上下文折叠,开发者控制)
与其把老消息删掉(信息永久丢失),不如把它们折叠——老消息存入外部存储,上下文里只保留一个折叠标记。需要时可以再展开恢复。
附:模型路由——变相的"缓存"
省钱和省上下文的另一个思路:让不同任务用不同模型。
- 静态路由:按任务类型,XX 类任务固定交给 XX 模型
- 动态路由:用一个分类器分析当前 prompt 的难度——简单任务给便宜模型,复杂任务给贵模型
小结
本文是上下文工程的"方法论"部分:
- 五个维度:卸(Offload)> 缓(Cache)> 压(Reduce)> 隔(Isolate)> 检(Retrieve),优先级依次递减
- 提示词是行为控制系统:模块化、静态前缀缓存、动态信息注入到消息里
- 压缩三十六计:先 Microcompact(温和替换),再 Snip(直接删),最后 Auto-compact(LLM 摘要 + 恢复关键文件)
- 三级缓存:KV Cache(推理层)→ Prompt Cache(API 层)→ Context Collapse(应用层)
方法论之外,它还有两个明星应用:RAG(让模型开卷考试)和记忆系统(让 Agent 记住你)——它们共同回答一个关键问题:信息到底应该在什么时候进入上下文?