在早期的大语言模型应用里,很多工作都围绕 Prompt 展开:怎样描述任务、怎样约束输出、怎样给几个示例让模型模仿。这个阶段的问题比较明确,用户输入一段话,模型返回一段话,提示词写得越清楚,结果通常越稳定。
但 AI Agent 出现后,情况变了。
Agent 不只是回答问题,它要读文件、调用工具、搜索资料、执行代码、分解任务、保存状态,还要在几十轮甚至上百轮交互中保持目标不变。单靠一段写得漂亮的 Prompt,很难支撑这种复杂系统。真正决定 Agent 表现的,往往是模型在每一步推理前看到了什么信息、能使用什么工具、历史状态怎么保留、无关内容怎么移除。
这就是上下文工程要解决的问题。
什么是上下文工程
上下文工程是一套为大语言模型(LLM,Large Language Model)动态准备输入环境的工程方法。它不只关心用户发给模型的那一句话,而是关心模型在生成答案之前能看到的全部内容。
一次模型调用里的上下文通常包括:
- 系统指令:约束模型角色、行为边界、输出要求。
- 用户请求:用户当前提出的问题或任务。
- 对话历史:当前会话中已经发生过的交互。
- 长期记忆:跨会话保留下来的用户偏好、项目约定、历史任务摘要。
- 检索结果:从文档、代码库、数据库、搜索引擎或 API 中找到的相关信息。
- 工具定义:模型可以调用哪些函数、命令、插件或外部系统。
- 当前状态:任务进度、待办事项、已失败的尝试、执行环境状态。
- 输出格式:要求模型返回 JSON、Markdown、代码补丁还是结构化字段。
可以把上下文工程理解成一个“上下文装配系统”:它在每次调用模型前,从不同来源收集信息,筛选出真正相关的部分,再以模型容易理解、容易执行的格式放进上下文窗口。
flowchart LR
U[用户请求] --> B[上下文构建器]
S[系统指令] --> B
H[短期对话历史] --> B
M[长期记忆] --> B
R[RAG 检索结果] --> B
T[工具定义] --> B
ST[任务状态] --> B
O[输出格式约束] --> B
B --> LLM[大语言模型]
LLM --> A[回答 / 工具调用 / 代码修改]
这里的关键不是“把所有东西都塞给模型”,而是“把正确的信息以正确的形式放到正确的位置”。上下文窗口再大也是有限资源,信息越多不一定越好,混乱的信息还会让模型更容易犯错。
提示词工程和上下文工程的区别
提示词工程关注的是“怎么说”,上下文工程关注的是“让模型看见什么、能做什么、记住什么”。
| 维度 | 提示词工程 | 上下文工程 |
|---|---|---|
| 核心问题 | 如何写出更清晰的 Prompt | 如何动态组织模型执行任务所需的信息 |
| 作用范围 | 通常围绕单次输入输出 | 覆盖指令、记忆、检索、工具、状态、输出格式 |
| 典型手段 | 角色设定、Few-shot 示例、格式约束 | RAG、记忆系统、工具管理、上下文压缩、多 Agent 隔离 |
| 适合场景 | 简单问答、文本生成、单步任务 | 编程 Agent、研究 Agent、自动化办公、多步骤任务 |
| 主要风险 | Prompt 不清楚导致输出偏差 | 信息缺失、上下文污染、工具误用、长上下文退化 |
| 类比 | 给模型贴一张任务便签 | 给模型准备一整套工作台、资料库和操作规范 |
一个简单例子能说明差异。
用户说:
明天方便快速碰一下吗?
如果模型只看到这句话,它最多能礼貌回复:
可以,请问你想约几点?
但如果系统同时提供了日历、联系人关系、历史邮件语气和发送邀请工具,模型就能做出更接近真实助理的回应:
Jim,明天我这边从早到晚都有会议。周四上午还有空,我已经发了邀请,你确认下是否合适。
两次回答差异并不来自更复杂的措辞,而是来自上下文质量。模型知道了“明天没空”“对方是谁”“该用什么语气”“可以直接发邀请”,才能完成更完整的任务。
为什么 Agent 更需要上下文工程
普通聊天机器人通常是一问一答,任务边界比较短。Agent 的难点在于它要不断做决策:下一步查什么、改哪个文件、调用哪个工具、是否需要回退、是否已经满足目标。
这类系统失败时,问题不一定出在模型能力上,更常见的是上下文没有设计好。
信息不全会导致错误决策
让模型修复一个代码缺陷,如果只告诉它错误现象,却不给相关文件、依赖关系、测试结果和项目规范,它只能猜。猜对了像是智能,猜错了就是幻觉。
信息太多会稀释注意力
把整个代码仓库、所有历史对话、所有工具说明都塞进上下文,模型未必能更准确。长上下文中间的信息容易被忽略,重复内容还会让模型陷入犹豫,甚至反复选择错误路径。
工具太多会增加误用概率
Agent 能调用的工具越多,动作空间越大。没有工具筛选、工具遮蔽或状态机约束时,模型可能在不该搜索时搜索,在不该执行命令时执行命令,或者调用已经不适合当前阶段的工具。
状态丢失会破坏长任务
多步骤任务里,模型需要知道已经完成了什么、失败过什么、当前目标是什么。如果状态没有被稳定保存,Agent 很容易重复劳动,或者在长循环后偏离原始目标。
长上下文的 Context Rot 问题
上下文窗口越来越长,并不意味着可以无限堆信息。长上下文会带来一种常见退化:Context Rot,也可以称为上下文腐蚀。
它表现为:
- 早期目标被后续细节淹没,模型忘记最初要完成什么。
- 无关内容占据注意力,关键证据被稀释。
- 历史中的错误结论被继续沿用,后续步骤被带偏。
- 多个相似但冲突的信息同时存在,模型行为变得不稳定。
- 大量重复日志、网页内容或工具返回结果让模型难以决定下一步。
Context Rot 的根源不是单一问题,而是模型注意力机制、训练分布、信息密度和自然语言模糊性共同造成的结果。工程上不能只依赖“更长的上下文窗口”,还需要主动管理上下文。
flowchart TD
A[任务执行时间变长] --> B[上下文不断累积]
B --> C[无关信息增加]
B --> D[重复内容增加]
B --> E[冲突信息增加]
C --> F[关键信息被稀释]
D --> G[模型行动变慢或犹豫]
E --> H[决策不稳定]
F --> I[Agent 失败率上升]
G --> I
H --> I
四类上下文管理方法
上下文工程的核心动作可以归纳为四类:写入、检索、压缩、隔离。它们分别解决“信息放哪”“取什么”“删什么”“怎么拆”的问题。
| 方法 | 英文常用说法 | 解决的问题 | 典型做法 |
|---|---|---|---|
| 写入 | Offload | 上下文窗口装不下所有信息 | 把大内容放到文件、数据库、记忆系统,只给模型引用 |
| 检索 | Retrieve | 模型只需要相关片段 | RAG、关键词搜索、代码搜索、GraphRAG、rerank |
| 压缩 | Reduce / Compress | 历史内容太长 | 摘要、裁剪、语义压缩、去重 |
| 隔离 | Isolate | 单个 Agent 上下文压力太大 | SubAgent、沙盒环境、只读分析任务拆分 |
写入:把大内容放到上下文窗口之外
工具返回的结果可能非常大,例如网页全文、数据库查询结果、测试日志、代码搜索结果。直接把这些内容全部塞给模型,会快速消耗上下文窗口,也会增加噪音。
更好的做法是把原始内容写入外部存储,只给模型一个引用:
{
"observation": "搜索完成,找到 18 个相关文件。",
"artifacts": [
{
"name": "search-result-auth-login",
"path": "/workspace/.agent/search/auth-login.json",
"summary": "包含登录鉴权相关的文件路径、函数名和匹配行号"
}
]
}
模型需要详细内容时,可以再通过工具读取。这样既保留了信息,又避免每轮调用都携带完整数据。
常见外部存储包括:
- 文件系统:适合保存日志、网页、计划、代码分析结果。
- 数据库:适合保存结构化状态和任务记录。
- 长期记忆系统:适合保存用户偏好、项目约定、常用命令。
- 对象存储:适合保存大文件、报告、运行产物。
检索:只取当前任务相关的信息
RAG(Retrieval-Augmented Generation,检索增强生成)是上下文工程里最常见的检索方式。它的基本流程是:把文档或代码切分、索引,用户提出问题后先检索相关片段,再把片段放进上下文。
flowchart LR
Q[用户问题] --> E[生成查询]
E --> V[向量检索 / 关键词检索]
V --> RR[重排序 rerank]
RR --> C[选择相关片段]
C --> LLM[放入上下文调用 LLM]
在代码 Agent 里,检索不一定必须依赖复杂向量库。很多时候,grep、find、语言服务器、AST 分析和依赖图就足够有效。代码天然有文件名、函数名、调用关系和类型信息,Agent 可以通过多轮工具调用逐步定位上下文。
检索系统要避免两个极端:
- 召回太少:模型缺少关键证据,只能猜。
- 召回太多:噪音进入上下文,模型更难判断。
压缩:保留语义,减少 token
长对话、多轮工具调用、网页搜索和测试日志都会产生大量上下文。压缩的目标不是简单截断,而是在降低 token 的同时保留决策所需的信息。
常见压缩方式:
| 压缩方式 | 适合内容 | 风险 |
|---|---|---|
| 摘要 | 多轮对话、任务进展 | 摘要遗漏关键细节 |
| 去重 | 重复日志、重复搜索结果 | 去重规则过粗会删掉差异 |
| rerank | RAG 候选片段 | 排序模型可能误判相关性 |
| 结构化提取 | 错误日志、接口文档 | 抽取 schema 不完整会丢字段 |
| 分层摘要 | 长任务历史 | 上层摘要可能逐轮失真 |
压缩后的内容最好保持结构化,例如:
## 当前任务状态
目标:修复登录接口在 token 过期时返回 500 的问题。
已确认:
- `AuthMiddleware` 会捕获 `ExpiredTokenError`
- `TokenService.verify()` 抛出的异常没有被转换为业务错误
- 失败测试:`tests/auth/test_login.py::test_expired_token_returns_401`
下一步:
- 修改异常映射逻辑
- 补充 401 响应断言
这种格式比一段自然语言摘要更适合 Agent 继续执行,因为目标、证据和下一步动作被明确分开了。
隔离:把复杂任务拆给不同上下文
当一个任务需要搜索、分析、编码、测试、写文档时,让同一个 Agent 在同一个上下文里处理全部内容,容易出现上下文污染。隔离的办法是让不同 SubAgent 处理不同子任务,每个 SubAgent 只拿到自己需要的信息。
flowchart TD
M[主 Agent:规划和调度] --> A[代码搜索 SubAgent]
M --> B[实现 SubAgent]
M --> C[测试 SubAgent]
M --> D[文档 SubAgent]
A --> M
B --> M
C --> M
D --> M
这种结构适合只读分析、并行搜索、测试诊断等任务。需要注意的是,多 Agent 协作会增加调度复杂度,如果子任务之间强依赖很多,主 Agent 必须维护清晰的共享状态,否则不同 Agent 的结论会互相冲突。
Claude Code 的上下文工程实践
Claude Code 代表了编码 Agent 的一种成熟形态。它的能力不只来自模型本身,也来自围绕代码任务设计的上下文系统。
三层记忆:短期、中期、长期分工
编码任务往往跨越很多轮交互。Claude Code 使用类似三层记忆的结构来处理不同生命周期的信息:
| 记忆层级 | 保存内容 | 生命周期 | 作用 |
|---|---|---|---|
| 短期记忆 | 当前对话、最近工具结果 | 当前上下文窗口内 | 保持即时连续性 |
| 中期记忆 | 压缩后的任务摘要、关键决策 | 长对话期间 | 在上下文接近上限时保留主线 |
| 长期记忆 | 项目约定、命令、架构说明、用户偏好 | 跨会话 | 恢复项目背景,减少重复说明 |
典型的长期记忆文件类似 CLAUDE.md,里面可以记录项目构建命令、测试方式、代码风格、目录结构和注意事项。例如:
# Project Guide
## Commands
- Install: pnpm install
- Test: pnpm test
- Lint: pnpm lint
## Conventions
- API errors must use `AppError`
- Do not access database directly in controllers
- Add tests for every bug fix
## Architecture
- `src/modules/auth` handles login and token refresh
- `src/shared/errors` defines common error types
这种项目知识不应该每次都靠用户重新输入。它适合进入长期记忆,在需要时自动注入上下文。
flowchart TD
A[当前对话] --> S[短期记忆]
S -->|接近上下文上限| C[智能压缩]
C --> M[中期记忆摘要]
P[项目知识 CLAUDE.md] --> L[长期记忆]
M --> B[上下文构建]
L --> B
S --> B
B --> LLM[模型调用]
实时 Steering:执行中允许用户改变方向
传统 Agent 经常像一个黑盒任务执行器:用户发出任务后,只能等它跑完。如果它中途理解错了,后面的搜索、修改和测试都会浪费。
实时 Steering 的核心是让 Agent 在执行过程中持续输出进展,并允许用户中断、补充约束或修改目标。工程上通常需要:
- 流式输出:让用户看到当前计划和执行状态。
- 异步消息队列:接收用户在执行中的新指令。
- 可恢复主循环:中断后保留状态,再从新目标继续。
- 工具调用边界:在安全点处理中断,避免半写入状态。
sequenceDiagram
participant U as 用户
participant A as Agent 主循环
participant Q as 异步消息队列
participant T as 工具执行器
U->>A: 发起修复任务
A->>T: 搜索相关文件
T-->>A: 返回搜索结果
A-->>U: 流式汇报计划
U->>Q: 补充约束:不要改公共接口
A->>Q: 检查是否有新消息
Q-->>A: 返回新约束
A->>A: 更新计划和上下文
A->>T: 按新约束继续执行
这类机制让 Agent 更像协作开发者,而不是一次性代码生成器。
分层多 Agent:主控调度,子任务隔离
复杂编码任务通常包含多个并行分支,例如搜索调用链、检查测试失败、分析历史实现、生成修改方案。把这些都放在一个上下文里,会让模型同时处理太多无关细节。
分层架构里,主 Agent 负责任务拆解和最终决策,SubAgent 负责局部分析或执行。每个 SubAgent 有独立上下文,完成后只把结论、证据和产物返回给主 Agent。
这种设计的好处很明确:
- 搜索类任务可以并行执行。
- 子任务上下文更短、更聚焦。
- 一个 SubAgent 的失败不一定污染主任务。
- 主 Agent 可以只接收结构化结论,而不是所有中间日志。
但它也有代价:调度器需要控制并发数量、工具权限和结果合并方式,否则系统复杂度会快速上升。
动态文件注入:从用户意图定位代码上下文
编码 Agent 必须能理解用户提到的文件、函数、模块和错误信息。例如用户说“登录中间件这里有问题”,系统应该自动关联:
- 登录相关目录。
- 中间件文件。
- token 校验函数。
- 相关测试。
- 最近失败日志。
动态上下文注入一般包含几个步骤:
flowchart LR
U[用户描述] --> P[解析文件名 / 函数名 / 错误信息]
P --> S[代码搜索和依赖分析]
S --> R[筛选相关文件]
R --> C[容量控制和格式化]
C --> LLM[注入上下文]
容量控制很重要。一次注入太多文件,会挤掉任务状态和指令;注入太少,模型又缺少证据。更稳妥的方式是先注入摘要、符号和关键片段,必要时再读取完整文件。
Manus 的上下文工程实践
Manus 更偏向通用 Agent 系统,它的任务链条长、工具调用多、上下文增长快,因此它在成本、缓存、工具空间和长期任务稳定性上做了很多工程优化。
围绕 KV 缓存设计上下文
KV 缓存(Key-Value Cache)是 Transformer 推理中的缓存机制。对于相同的上下文前缀,模型可以复用已经计算过的注意力键值,从而减少首 token 延迟和推理成本。
Agent 的输入输出比例很特殊:每一步都会带上很长的历史上下文,但模型输出可能只是一个短动作或一次工具调用。也就是说,大量成本花在“重新读前缀”上。只要保持上下文前缀稳定,KV 缓存就能节省大量计算。
工程上要注意:
| 做法 | 原因 |
|---|---|
| 保持系统提示词和工具定义稳定 | 前缀变化会导致缓存失效 |
| 避免在前缀中放动态时间戳 | 时间每轮变化,缓存无法命中 |
| 使用只追加的上下文结构 | 旧前缀不变,后续内容追加 |
| 确保 JSON 序列化确定性 | 字段顺序变化也会破坏前缀一致性 |
| 必要时显式设置缓存断点 | 某些推理框架不会自动识别可缓存前缀 |
一个常见反例是工具定义每轮动态重排:
{
"tools": {
"search": {},
"browser": {},
"shell": {}
}
}
如果下一轮变成:
{
"tools": {
"shell": {},
"search": {},
"browser": {}
}
}
语义上工具集合没有变化,但 token 序列已经变了,缓存前缀可能失效。Agent 系统里的“确定性序列化”不是代码洁癖,而是直接影响成本和延迟。
遮蔽工具,而不是频繁移除工具
工具太多会让模型更容易选错,但动态添加、删除工具也会带来问题:
- 工具定义变化会破坏 KV 缓存。
- 模型可能引用已经从上下文中删除的工具。
- 工具列表变化会让动作空间不稳定。
更稳的方式是保持工具定义不变,通过状态机或解码约束控制当前可用工具。也就是“工具还在上下文里,但当前阶段不允许选”。
stateDiagram-v2
[*] --> Planning
Planning --> Searching: 需要信息
Searching --> Coding: 找到相关文件
Coding --> Testing: 完成修改
Testing --> Reporting: 测试通过
Testing --> Coding: 测试失败
Planning: 允许 plan/search
Searching: 允许 search/read
Coding: 允许 read/write/shell
Testing: 允许 shell/read
Reporting: 允许 final_report
这种做法可以通过 response prefill、logits mask、函数调用约束或工具路由层实现。核心目标是:工具定义稳定,动作空间受控。
文件系统作为外部记忆
上下文窗口有限,文件系统几乎不受这个限制,而且天然持久化。Agent 可以把大段网页、搜索结果、计划、运行日志和中间报告写入文件,再在需要时读取。
flowchart LR
A[Agent] --> W[写入文件系统]
W --> F[(workspace / artifacts)]
F --> R[按需读取]
R --> A
A --> LLM[每轮只携带摘要和路径]
例如网页抓取结果不必一直放在上下文里,只需要保留 URL、文件路径和摘要:
已保存资料:
- `/workspace/research/oauth-rfc.md`
- 来源:https://example.com/oauth-spec
- 摘要:包含 token 过期、refresh token、错误码定义
后续如果模型需要细节,再读取对应文件。这种模式让文件系统成为“可操作的外部上下文”。
通过复述任务计划保持注意力
长任务中,Agent 很容易在几十次工具调用后偏离目标。一个低成本但有效的办法是反复维护待办事项列表,并把当前目标放在上下文较新的位置。
## Active Plan
Goal: 修复 token 过期时登录接口返回 500 的问题。
TODO:
- [x] 定位失败测试
- [x] 找到 token 校验逻辑
- [ ] 修改异常映射为 401
- [ ] 补充测试
- [ ] 运行 auth 测试集
这不是单纯的任务管理 UI,而是在操控模型注意力。模型通常更容易关注上下文靠近末尾的信息,把目标和计划持续刷新到近期上下文,有助于缓解“关键信息丢在中间”的问题。
保留错误,让模型知道哪些路走不通
多步骤任务里,错误并不总是垃圾信息。失败命令、异常栈、错误补丁和测试输出都能帮助模型调整判断。
如果系统把错误全部清理掉,模型可能会重复尝试同一条错误路径。保留失败证据,可以让模型知道:
- 哪个命令执行失败。
- 哪个假设已经被证伪。
- 哪个文件修改没有解决问题。
- 哪个测试仍然不通过。
比较好的格式是保留错误摘要,而不是完整堆积全部日志:
## Failed Attempts
1. 尝试只修改 `AuthMiddleware`
- 结果:`test_expired_token_returns_401` 仍然失败
- 原因:异常在 `TokenService.verify()` 内部被包装成通用 `RuntimeError`
2. 尝试捕获所有 `RuntimeError`
- 结果:影响其他错误分支,导致 `test_invalid_token_returns_403` 失败
- 结论:需要只处理 `ExpiredTokenError`
这样既提供负面样本,又不会让上下文被长日志淹没。
避免 Few-shot 示例把 Agent 带偏
Few-shot 示例能帮助模型学习格式,但在 Agent 系统里也可能产生副作用。模型会模仿上下文里的行为模式,如果示例过于单一,它可能在不适合的场景也套用同样动作。
解决办法不是完全不用示例,而是增加受控多样性:
- 示例中使用不同任务类型。
- 工具调用顺序不要完全一致。
- 输出格式保持规范,但措辞可以有变化。
- 展示成功路径,也展示失败后的修正路径。
这样可以降低模型对某一种轨迹的过拟合,让 Agent 在复杂任务中更灵活。
从 Vibe Coding 到 Spec-Driven Development
AI 编程早期常见模式是 Vibe Coding:用户给一个大概想法,模型直接生成代码。
flowchart LR
P[Prompt] --> C[Code]
这种方式适合小脚本、原型验证和一次性任务,但在真实项目中会遇到几个问题:
- 用户很难一次性写出完整、无歧义的需求。
- 生成代码可能缺少测试、设计约束和边界条件。
- 开发者不一定理解生成结果,后续维护困难。
- 大项目多人协作时,隐含假设很容易失控。
Spec-Driven Development(规范驱动开发)把流程拆开:先把模糊需求变成规范,再进入设计和任务拆解,最后才生成代码。
flowchart LR
P[Prompt] --> R[Requirements]
R --> D[Design]
D --> T[Tasks]
T --> C[Code]
C --> V[Tests / Review]
它的核心变化是:上下文不再是临时拼出来的一段 Prompt,而是一组可检查、可修改、可复用的工程文档。
requirements.md:把需求写成可验证规则
需求文档可以使用 EARS(Easy Approach to Requirements Syntax,简易需求语法)格式描述:
WHEN the token is expired
THE SYSTEM SHALL return HTTP 401 with error code TOKEN_EXPIRED
这种写法的好处是条件和期望行为非常清楚,适合模型生成测试,也适合人类审查。
design.md:把实现约束写清楚
设计文档应该包含架构、模块边界、接口、数据结构和异常处理方式。例如:
## Auth Error Handling Design
- `TokenService.verify()` should throw domain-specific errors.
- `AuthMiddleware` maps `ExpiredTokenError` to HTTP 401.
- Controllers must not catch token verification errors directly.
- Error responses follow `ApiErrorResponse` schema.
有了设计文档,模型不会只追求“让代码跑起来”,而会更容易遵守项目结构。
tasks.md:把开发拆成可执行步骤
任务文档把实现过程拆成小块,每一块都能独立执行和验证:
- [ ] Add `ExpiredTokenError` type
- [ ] Update `TokenService.verify()` error mapping
- [ ] Update `AuthMiddleware` response handling
- [ ] Add unit test for expired token
- [ ] Run auth test suite
这种结构对 Agent 很友好。每个任务都是明确的上下文单元,执行状态也能被持续维护。
规范驱动为什么适合上下文工程
Spec-Driven Development 和上下文工程天然匹配:
| 工程产物 | 在上下文工程中的作用 |
|---|---|
| requirements.md | 提供业务目标和验收条件 |
| design.md | 提供架构边界和实现约束 |
| tasks.md | 提供执行计划和进度状态 |
| tests | 提供可验证反馈 |
| 项目记忆 | 提供长期约定和团队规范 |
Prompt 直接生成代码时,很多约束藏在用户脑子里;规范驱动会把这些约束显式写下来,变成 Agent 可以检索、引用和验证的上下文。
从上下文工程到环境工程
上下文工程主要关注“模型输入里放什么”。但更长期的方向可能是环境工程:为 Agent 构建一个可感知、可交互、可持续演化的工作环境。
| 阶段 | 关注点 | 特点 | 局限 |
|---|---|---|---|
| 提示词工程 | 单条 Prompt | 简单、低成本、适合单步任务 | 静态,依赖人类表达能力 |
| 上下文工程 | 动态输入组织 | 管理记忆、检索、工具、状态 | 仍然以模型调用为中心 |
| 环境工程 | Agent 所处的世界 | 有状态、可交互、可反馈、可演化 | 系统设计和安全控制更复杂 |
环境不只是上下文的集合,还包括:
- 当前世界状态。
- 可执行动作。
- 权限和规则。
- 反馈机制。
- 长期影响。
- 多 Agent 协作关系。
- 可观察的外部系统变化。
在这种模式下,Agent 不只是被动接收上下文,而是主动探索环境、修改环境、从反馈中调整策略。代码仓库、测试系统、浏览器、数据库、任务队列、监控平台,都可以成为环境的一部分。
落地上下文工程的检查清单
设计 Agent 系统时,可以用这组问题检查上下文是否足够工程化:
| 问题 | 需要关注的设计 |
|---|---|
| 模型每一步必须知道什么? | 系统指令、任务状态、关键证据 |
| 哪些信息不该长期放在上下文里? | 大日志、网页全文、完整搜索结果 |
| 信息应该保存在哪里? | 文件系统、数据库、长期记忆、对象存储 |
| 什么时候检索? | 用户提到实体、文件、错误、外部知识时 |
| 什么时候压缩? | 上下文接近上限、历史重复、任务阶段切换时 |
| 工具太多怎么办? | 工具分组、状态机、遮蔽、权限控制 |
| 长任务如何不跑偏? | 待办事项、目标复述、阶段性验收 |
| 失败信息如何处理? | 保留错误摘要和关键日志 |
| 多 Agent 是否必要? | 任务能否隔离、是否适合并行、结果如何合并 |
| 成本如何控制? | KV 缓存、稳定前缀、只追加上下文、外部存储 |
上下文工程的重点不是把 Prompt 写得更长,而是把 Agent 运行所需的信息系统化。一个可靠的 Agent,需要知道目标、拥有工具、理解约束、保存记忆、检索证据、处理失败,并且在长任务中持续保持方向。
当这些能力被工程化地组织起来,模型才不只是一个会生成文本的接口,而能成为真正可协作、可验证、可持续运行的智能执行单元。