一、为什么需要 Context Engineering?
以前我们写大模型应用时,经常会这样想:
我把用户问题、系统提示词、历史对话、检索结果、工具结果全部塞给大模型,大模型应该就能回答好了吧?
但实际情况并不是这样。
因为大模型的上下文窗口虽然越来越大,但它依然是有限资源。上下文越长,不代表效果越好,反而可能出现几个问题:
1. 信息太多,模型抓不住重点
比如用户只是问:
"帮我分析这个告警是什么原因?"
如果我们把所有历史对话、所有日志、所有知识库内容、所有工具返回结果都塞进去,模型可能会被无关信息干扰。
它不是不知道答案,而是在太多信息里迷路了。
2. Token 成本变高
上下文越长,消耗的 token 越多。
对于一个真实 Agent 系统来说,尤其是多轮对话、多工具调用、多 Agent 协作场景,如果每一步都带上全部上下文,成本会越来越高。
3. 容易引入错误信息
上下文中如果包含过期信息、无关检索结果、错误工具结果,模型可能会把这些内容当成依据,从而产生错误回答。
也就是说:
Agent 幻觉不一定只是模型本身的问题,也可能是上下文组织得不好。
二、什么是 Context Engineering?
我现在对 Context Engineering 的理解是:
Context Engineering 不是简单地写 Prompt,而是设计一套机制,决定 Agent 在每一步推理、调用工具、生成回答时,应该拿到哪些上下文信息。
LangChain 对 Agent 上下文工程的总结比较清晰:常见策略可以分为 Write、Select、Compress、Isolate 四类,也就是写入、选择、压缩和隔离。
换成更容易理解的话就是:
| 能力 | 作用 |
|---|---|
| Write | 把有价值的信息写入状态或记忆 |
| Select | 从大量信息中选择当前最相关的内容 |
| Compress | 把长历史、长日志、长文档压缩成摘要 |
| Isolate | 把不同任务、不同 Agent、不同会话的上下文隔离开 |
这四个词其实可以串起我们之前学过的大部分 Agent 技术。
三、Context Engineering 和 Prompt Engineering 的区别
很多人刚开始会把 Context Engineering 和 Prompt Engineering 混在一起。
但它们不是一个层级的东西。
Prompt Engineering 更像是"怎么说"
比如:
你是一个专业的智能运维助手。
请根据告警信息、日志内容和历史排障经验,分析可能原因,并给出解决建议。
这属于 Prompt。
它主要解决的是:
我要让模型以什么身份、什么格式、什么思路回答?
Context Engineering 更像是"给它看什么"
比如在一次 AIOps Agent 调用中,我们要决定:
1. 当前用户问题是什么?
2. 当前 session 的最近几轮对话是什么?
3. 是否需要加载历史摘要?
4. 是否需要检索 RAG 知识库?
5. 是否需要调用 Prometheus 查询告警?
6. 是否需要调用 CLS 查询日志?
7. 工具返回结果是否需要压缩?
8. 哪些信息应该交给 Planner?
9. 哪些信息应该交给 Executor?
10. 哪些信息应该从上下文中移除?
这就是 Context Engineering。
所以我觉得可以这样理解:
Prompt Engineering 是一句话怎么写。
Context Engineering 是整个 Agent 的信息流怎么设计。
四、一个完整 Agent 上下文里通常包含什么?
结合我目前学习的 Agent 项目,一个比较完整的上下文通常不是只有用户输入,而是由很多部分组成。
可以简单拆成下面几类:
1. System Prompt:系统身份和规则
这是最基础的一层。
比如:
你是一个智能运维 Agent。
你需要根据告警、日志、知识库和工具结果进行分析。
如果信息不足,需要说明缺少哪些信息。
不要编造不存在的日志和指标。
它决定 Agent 的角色、边界和输出规范。
2. User Input:用户当前问题
比如:
线上订单服务响应变慢,帮我分析一下原因。
这是 Agent 当前要解决的问题。
但只靠这个问题往往不够,因为它缺少环境信息,比如服务名、时间范围、错误日志、指标变化等。
3. Conversation History:短期对话历史
比如用户前面已经说过:
服务名是 order-service。
时间大概是晚上 8 点到 8 点半。
最近刚发布过一个新版本。
这些信息在后续分析中非常重要。
如果 Agent 不记得,就会反复追问用户。
这就是短期记忆的作用。
4. Long-term Memory:长期记忆
长期记忆更适合保存跨会话仍然有价值的信息。
比如:
用户的项目主要是基于 Spring Boot + Spring AI Alibaba。
用户当前关注 AIOps Agent、RAG、Memory、Tool Calling。
用户希望回答偏向实习面试和项目表达。
长期记忆不是每次都全部塞给模型,而是需要根据当前任务选择性召回。
这就涉及 Context Engineering 里的 Select。
5. RAG Retrieved Context:知识库检索结果
比如从知识库中检索到:
order-service 响应变慢可能与数据库慢查询、Redis 缓存击穿、下游接口超时有关。
RAG 提供的是外部知识。
但 RAG 也不能无限塞,通常要控制 TopK,并且要过滤无关内容。
6. Tool Results:工具调用结果
比如 Agent 调用了 Prometheus:
CPU 使用率正常。
接口 P99 延迟从 80ms 上升到 1200ms。
数据库连接池等待时间明显升高。
又调用了日志查询工具:
大量出现 SQL timeout。
这些工具结果往往比模型自己的猜测更可靠。
Anthropic 在工具使用相关内容中也强调,工具定义和工具返回结果会直接影响 Agent 的实际表现,工具本身也需要像 Prompt 一样认真设计。
7. Agent State:当前任务状态
在多 Agent 或 LangGraph 这类编排框架里,通常会维护一个状态对象。
比如:
class AgentState {
String userQuestion;
List<String> plan;
List<String> executedSteps;
List<String> observations;
String finalAnswer;
}
这个 State 不是单纯给用户看的,而是 Agent 工作流内部用来记录任务进度的。
比如:
- Planner 生成了哪些步骤
- Executor 已经执行了哪些工具
- Replanner 判断是否需要重新规划
- Supervisor 决定下一步交给谁
这就是上下文工程里的 "Write to State"。
LangChain 的文档里也提到,工具结果既可以直接返回给模型,也可以更新 Agent 的状态或记忆,使后续步骤可以继续使用这些上下文。
五、Context Engineering 的四个核心动作
下面用更接近项目实现的方式理解这四个动作。
1. Write:把有价值的信息写进去
Agent 在执行过程中会不断产生新信息。
比如:
用户补充了服务名。
工具返回了日志结果。
Planner 生成了执行计划。
Executor 执行了第一步。
Replanner 判断需要重新查询数据库。
这些信息不能丢。
所以需要写入:
- 短期上下文
- Agent State
- 长期 Memory
- 工具执行记录
- 任务中间结果
但是这里有一个关键点:
不是所有信息都值得写入长期记忆。
比如:
用户今天问了一个临时问题
某一次工具调用失败
某一次测试日志
这些一般不适合长期保存。
真正适合写入长期记忆的是:
用户的长期偏好
项目固定技术栈
常见排障经验
稳定的业务规则
重复出现的问题处理方案
2. Select:从大量信息中选出当前需要的
这是 Context Engineering 里非常重要的一步。
因为上下文窗口有限,所以 Agent 每一步都应该选择最相关的信息。
比如用户问:
Memory0 是怎么存储数据的?
这时候最相关的上下文应该是:
Memory0
长期记忆
向量数据库
存储后端
RAG 区别
而不是把之前所有关于 Docker、Kafka、软件工程考试的内容都塞进去。
所以 Select 的本质是:
当前任务需要什么,就取什么。
在项目里可以对应这些实现:
根据 sessionId 取最近 N 轮对话
根据用户问题检索长期记忆
根据问题向量检索 RAG 文档
根据任务类型选择工具说明
根据 Agent 角色选择不同上下文
3. Compress:把长内容压缩成可用摘要
多轮对话很容易变长。
比如一个用户连续问了 50 轮,如果每轮都带上,token 会爆炸。
这时候就需要摘要压缩。
常见做法是:
最近 5 轮对话:原文保留
更早的历史:压缩成摘要
关键事实:写入长期记忆
无关内容:丢弃
OpenAI 的 Agents SDK Cookbook 中也提到,短期记忆管理可以使用 trimming 和 compression 等方式来保持 Agent 快速、可靠和更节省成本。
用在项目中,可以这样设计:
public String buildContext(String sessionId, String userInput) {
String summary = memoryService.getHistorySummary(sessionId);
List<Message> recentMessages = memoryService.getRecentMessages(sessionId, 5);
List<Document> ragDocs = ragService.retrieve(userInput, 5);
return """
历史摘要:
%s
最近对话:
%s
检索资料:
%s
用户当前问题:
%s
""".formatted(summary, recentMessages, ragDocs, userInput);
}
这个逻辑其实就是上下文工程。
4. Isolate:隔离不同任务和不同 Agent 的上下文
这点在多 Agent 系统中特别重要。
比如一个 AIOps 系统里有:
SupervisorAgent
PlannerAgent
ExecutorAgent
ReplannerAgent
ReportAgent
它们不一定都需要看到同样的信息。
Planner 需要看到:
用户问题
可用工具列表
任务目标
历史摘要
Executor 需要看到:
当前执行步骤
工具参数
工具调用规范
Replanner 需要看到:
原计划
已执行步骤
执行结果
失败原因
ReportAgent 需要看到:
最终结论
证据链
处理建议
风险提示
如果所有 Agent 都看到全部上下文,就会混乱。
所以 Isolate 的核心就是:
不同角色,只看自己需要的信息。
这和我们之前学的多 Agent 协作是能接上的。
Supervisor 不是简单地把所有内容扔给每个 Agent,而是根据任务阶段分发不同上下文。
六、结合 AIOps Agent 项目理解
假设用户输入:
order-service 晚上 8 点之后响应很慢,帮我分析原因。
一个比较合理的上下文工程流程可能是:
第一步:解析用户问题
Agent 先从用户输入中抽取关键信息:
服务名:order-service
时间范围:晚上 8 点之后
问题类型:接口响应慢
目标:定位可能原因并给出建议
第二步:选择需要的工具
根据问题类型选择工具:
Prometheus:查询服务指标
CLS / 日志系统:查询错误日志
数据库工具:查询慢 SQL 或连接池状态
RAG:检索历史排障经验
第三步:构造 Planner 上下文
给 Planner 的上下文可以是:
用户问题
已知条件
可用工具
输出要求
Planner 不一定需要看到大量日志原文。
第四步:Executor 调用工具
Executor 根据计划调用工具。
比如:
查询 order-service 在 20:00-20:30 的 P99 延迟
查询同时间段错误日志
查询数据库连接池状态
第五步:压缩工具结果
工具返回的日志可能很多,不能全部塞给模型。
需要压缩成:
1. P99 延迟从 80ms 上升到 1200ms
2. CPU 和内存正常
3. 数据库连接池等待时间升高
4. 日志中大量 SQL timeout
5. 没有明显下游接口错误
第六步:Replanner 判断是否需要补充查询
如果当前证据还不够,Replanner 可以继续规划:
需要进一步查询慢 SQL 列表
需要确认是否有版本发布
需要查询数据库 QPS 和锁等待情况
第七步:生成最终回答
最终回答不能只给结论,还要给证据链:
初步判断 order-service 响应慢主要与数据库访问异常有关。
依据:
1. 接口 P99 延迟明显升高;
2. 应用 CPU、内存没有明显异常;
3. 数据库连接池等待时间升高;
4. 日志中出现大量 SQL timeout。
建议:
1. 优先排查慢 SQL;
2. 检查数据库连接池配置;
3. 回看 20:00 前后的版本发布记录;
4. 如有必要,临时扩容数据库连接池或回滚相关 SQL 改动。
这才是一个比较完整的 Agent 回答。
七、Context Engineering 和 RAG、Memory、Tool 的关系
我觉得可以用一句话概括:
RAG、Memory、Tool 都是在提供上下文;
Context Engineering 是决定这些上下文怎么被组织和使用。
具体来说:
| 模块 | 提供什么 | Context Engineering 负责什么 |
|---|---|---|
| Prompt | 角色和规则 | 决定不同阶段用什么 Prompt |
| Memory | 历史和偏好 | 决定哪些记忆要召回 |
| RAG | 外部知识 | 决定检索什么、取多少、怎么排序 |
| Tool | 实时数据 | 决定何时调用、结果如何压缩 |
| State | 中间过程 | 决定任务进度如何保存 |
| Multi-Agent | 分工协作 | 决定不同 Agent 看哪些上下文 |
所以它不是一个单独的新技术,而是一种架构能力。
八、面试中怎么表达这个点?
如果面试官问:
你这个 Agent 项目里是怎么管理上下文的?
可以这样回答:
我不是简单把所有历史对话和工具结果都拼到 Prompt 里,而是做了分层上下文管理。
首先通过 sessionId 做会话隔离,避免不同用户或不同任务之间的上下文串扰。
其次,对于短期对话,我保留最近几轮原始消息;对于更早的历史,会压缩成摘要,避免 token 过长。
然后在需要外部知识时,通过 RAG 检索相关文档片段,而不是把整个知识库塞给模型。
对于工具调用结果,比如日志、指标、数据库查询结果,我会先结构化和摘要化,再交给模型做分析。
如果是多 Agent 流程,不同 Agent 看到的上下文也不一样。Planner 更关注任务目标和工具列表,Executor 更关注当前步骤和工具参数,Replanner 更关注执行结果和失败原因。
这样做的目的主要是降低 token 成本,减少无关信息干扰,同时提升 Agent 执行复杂任务时的稳定性。
这段话就比单纯说"我用了 Memory 和 RAG"更高级。
因为它体现的是工程设计能力。
九、今天的理解总结
今天学习完 Context Engineering 后,我感觉 Agent 的核心已经不只是"会不会调用工具",而是:
能不能在正确的时间,把正确的信息,交给正确的 Agent 或模型。
以前我对上下文的理解比较简单,觉得上下文就是用户输入加历史对话。
现在发现,一个真正的 Agent 系统里,上下文其实包括:
系统提示词
用户当前问题
短期对话历史
长期记忆
RAG 检索结果
工具返回结果
Agent State
执行计划
中间观察结果
错误信息
最终输出约束
而 Context Engineering 要做的,就是对这些信息进行:
写入
选择
压缩
隔离
这也是为什么很多 Agent Demo 看起来能跑,但一到复杂任务、多轮任务、真实项目里就不稳定。
因为 Demo 只解决了"能回答"的问题。
而 Context Engineering 解决的是:
Agent 如何持续、稳定、低成本地完成任务。
十、下一步可以继续学习什么?
后面可以继续往这个方向展开:
1. Agent State 是怎么设计的?
2. LangGraph / Spring AI 中的状态流转怎么理解?
3. 多 Agent 之间如何传递上下文?
4. Tool Result 应该怎么结构化?
5. Memory 和 RAG 如何避免互相污染?
6. 如何设计一个适合面试表达的 AIOps Agent 上下文架构?
如果把这些内容串起来,就能从"会用大模型 API"逐渐过渡到"能设计 Agent 系统"。
这也是我现在学习 Agent 的一个重要转折点:
不只是学单个技术点,而是开始理解 Agent 背后的工程组织方式。