Agent Basic / 13
Context Engineering:系统化设计模型输入
前面几篇已经分别讲了:
- 05:API 协议层面的消息结构
- 06:Context、State、Memory 的概念区分和压缩策略
这一篇把视角拉高一层:当你面对一个完整的 Agent 系统时,怎样系统化地设计模型每一轮看到的 Context?
这就是 Context Engineering——不只是”写好 Prompt”,而是设计整个信息供给策略。
为什么单独谈 Context Engineering
2025-2026 年,行业从 Prompt Engineering 过渡到 Context Engineering,背后有一个核心观察:
Agent 的能力天花板不是模型参数量,而是模型每一步决策时能看到什么。
同一个模型,在以下两种情况下表现差距巨大:
- 情况 A:只给一句”帮我分析这个项目的风险”
- 情况 B:给出任务目标、已完成的步骤、待检查的工具列表、相关历史结论、当前约束
差别不在模型能力,而在 Context 质量。
Context Engineering 的范围比 Prompt Engineering 大得多:
| 层次 | 关注什么 | 典型产出 |
|---|---|---|
| Prompt Engineering | 单次调用的指令质量 | 模板、结构化指令、Few-shot |
| Context Engineering | 整个信息供给链的设计 | Skills 加载策略、状态栏、动态注入、压缩管道 |
Prompt Engineering 是 Context Engineering 的子集。
Context 的五个组成部分
一次 Agent 调用中,模型可见的 Context 通常由五部分组成:
┌─────────────────────────────────────────┐
│ 1. System Prompt(系统指令) │ ← 身份、规则、约束
│ 2. Tool Definitions(工具定义) │ ← 能力列表、参数 Schema
│ 3. Conversation Trajectory(执行轨迹) │ ← 历史消息、工具调用/结果
│ 4. Dynamic Metadata(动态元数据) │ ← 状态栏、时间、环境信息
│ 5. Retrieved Content(检索内容) │ ← RAG 结果、Skills 文档
└─────────────────────────────────────────┘
Context Engineering 的目标是:让这五部分在每一轮都恰好包含当前决策所需的信息——不多(浪费 token、引入噪声),不少(关键信息缺失导致错误决策)。
Prompt Engineering 的五个维度
System Prompt 虽然只是 Context 的一部分,但它决定了模型的基础行为模式。一个好的 System Prompt 通常覆盖五个维度:
1. 语气与风格
定义模型应该怎样表达:
- 用大写或 加粗 标记硬约束(
NEVER、MUST) - 明确输出的详细程度和格式偏好
- 声明角色边界(”你是……,你不能……”)
2. 结构化组织
用 XML 标签或 Markdown 分区组织指令,而不是写一大段连续文字:
<role>你是一个代码审查助手。</role>
<constraints>
- 不修改代码,只指出问题
- 每个问题必须引用具体行号
</constraints>
<output_format>
## 问题列表
- [文件:行号] 问题描述
</output_format>
结构化的好处:模型更容易定位和遵守不同类别的指令,调试时也更容易判断哪部分没生效。
3. 流程驱动而非规则堆砌
与其堆一大堆规则,不如给出清晰的执行流程(SOP):
收到用户请求后:
1. 先判断请求类型(查询 / 操作 / 闲聊)
2. 如果是查询,先检索知识库
3. 如果是操作,先验证权限
4. 最后生成回复
流程化指令比散列规则更容易被模型稳定遵守。
4. 业务规则精炼
从产品需求中提取出模型需要遵守的核心规则,删掉废话:
- 原始需求:”在用户询问退款相关问题时,系统应该首先查询订单状态,如果订单已完成且在7天内,可以自动处理退款……”
- 精炼版:
退款条件:订单状态=已完成 AND 下单≤7天 → 自动退款;否则转人工
规则越精炼,模型遵守率越高。
5. Few-shot 示例
给 2-3 个精选示例比给 10 个冗余示例效果更好。
好的 Few-shot 应覆盖:
- 一个标准场景
- 一个边界场景
- 一个应该拒绝的场景
示例的格式应和真实交互格式一致,不要用虚构的格式。
Agent Skills:按需加载能力
当 Agent 具备几十甚至上百种能力时,不可能把所有指令都塞进 System Prompt。这时需要 Skills 架构——一种按需加载的能力管理系统。
三层加载架构
┌────────────────────────────────────────────┐
│ 第一层:Metadata(始终在 Context 中) │
│ · 每个 Skill 的名称和一句话描述 │
│ · 总共几百 token │
│ · 让模型知道"有哪些能力可以用" │
├────────────────────────────────────────────┤
│ 第二层:Core Workflow(按需加载) │
│ · 当模型决定使用某个 Skill 时加载 │
│ · 包含完整的执行指令和约束 │
│ · 通常几千 token │
├────────────────────────────────────────────┤
│ 第三层:Detailed Docs(深度按需) │
│ · Core Workflow 中引用的详细文档 │
│ · 只在模型需要时通过工具读取 │
│ · 可能是完整的 API 文档或参考资料 │
└────────────────────────────────────────────┘
为什么不把所有 Skills 直接注入
| 方案 | 优势 | 问题 |
|---|---|---|
| 全部注入 System Prompt | 模型看到完整信息 | token 爆炸、指令冲突、注意力分散 |
| 全部通过工具读取 | 极省 token | 模型不知道有什么能力,无法主动选择 |
| 三层架构 | 平衡 token 和能力发现 | 需要设计加载逻辑 |
Skills 的 cache-friendly 设计
第一层 Metadata 放在 Context 的稳定前缀中,这样跨请求时更容易命中 Provider 的前缀缓存。第二层和第三层是动态内容,放在 Context 的后部。
稳定区域:System Prompt + Tool Definitions + Skills Metadata
动态区域:执行轨迹 + 当前加载的 Skill Workflow + 检索内容
实际例子
以 Claude Code 为例,它的 Skills 就是这种架构:
- Metadata:每个 skill 有 name 和一句话触发描述,始终可见
- Core Workflow:用户触发特定 skill 后加载完整指令
- Detailed Docs:skill 内部需要时再读取参考文件
这让系统能管理几十种能力而不爆 Context。
Agent Status Bar:结构化的执行元数据
传统的 Agent 只在消息历史中保留执行轨迹。Status Bar 是一种补充机制——用结构化字段把当前执行状态显性地注入 Context。
Status Bar 包含什么
<status_bar>
<task_plan>
- [x] 查询用户订单状态
- [x] 验证退款条件
- [ ] 执行退款操作
- [ ] 发送确认通知
</task_plan>
<environment>
<current_time>2026-07-22T14:30:00+08:00</current_time>
<session_duration>4m 32s</session_duration>
<tool_calls_remaining>5</tool_calls_remaining>
<token_budget_remaining>45000</token_budget_remaining>
</environment>
<warnings>
- 上一次 payment_api 调用延迟 >3s,可能不稳定
</warnings>
</status_bar>
为什么 Status Bar 有效
模型在上下文学习中的行为更接近”检索”而非”推理”——它擅长从已有内容中提取和遵守信息,但不擅长从散乱的历史消息中自行统计和推断状态。
Status Bar 把分散在历史中的状态信息 显性化,减少模型需要自己推断的工作量。
具体好处:
| 没有 Status Bar | 有 Status Bar |
|---|---|
| 模型需要从长历史中推断已完成哪些步骤 | 直接读取 task_plan 的 checkbox |
| 模型不知道还能调几次工具 | 直接看 tool_calls_remaining |
| 模型不知道当前时间 | 直接读 current_time |
| 模型可能重复已做过的事 | 已完成步骤有明确标记 |
Status Bar 的风险
Status Bar 中的信息会被模型无条件信任。如果 Status Bar 写入了错误状态,模型会基于错误信息继续决策。
因此:
- Status Bar 只能从可靠的程序状态派生,不能从未验证的模型输出生成
- 更新 Status Bar 的逻辑必须有明确的触发条件和验证
- 不要把未确认的猜测写进 Status Bar
上下文的三个设计原则
原则 1:稳定内容在前,动态内容在后
较稳定:System Prompt → Tool Schema → Skills Metadata → 固定约束
较动态:历史轨迹 → Status Bar → 检索结果 → 当前用户输入
好处:
- Provider 前缀缓存更容易命中(token 不白算)
- 模型注意力的位置偏差倾向于更关注开头和结尾的内容
原则 2:不在 Context 中重复信息
如果一条约束已经在 System Prompt 中声明过,不需要在每轮历史中重复。
如果一个工具的返回结果已经被提取到 State 中,历史中的原始结果可以压缩或删除。
重复信息不仅浪费 token,还可能在版本不一致时造成冲突。
原则 3:正确性优先于缓存
不能为了保持前缀稳定而保留过时信息。如果约束变了、权限变了、环境变了,必须更新 Context,即使这意味着缓存失效。
缓存是优化,不是约束。
Context Engineering 的核心思维模式
回顾一下,Context Engineering 解决的核心问题是:
在有限的 token 预算内,让模型每一步都能看到当前决策所需的最相关信息。
这意味着需要同时管理:
- 信息供给:什么时候注入什么(Skills 加载、RAG 检索、Status Bar 更新)
- 信息压缩:什么时候删掉什么(窗口裁剪、摘要、归档)
- 信息质量:注入的信息是否准确、相关、不矛盾
如果把 Agent 比作一个人在做决策,Context Engineering 就是”决定这个人每一步能看到哪些资料”。资料太少会做错决策,资料太多会找不到重点,资料有误会被误导。
和其他工程层次的关系
| 层次 | 关注点 | Context Engineering 的角色 |
|---|---|---|
| Prompt Engineering | 单次调用的指令 | Context Engineering 的子集 |
| Harness Engineering | 工具、权限、验证 | Context Engineering 决定 Harness 送什么给模型 |
| Loop Engineering | 循环策略和退出 | Loop 的每一轮都需要 Context Engineering 组装输入 |
| Agent Infra | 运行时和持久化 | Infra 负责存储和恢复 Context 需要的数据 |
它们不是互斥的,而是同一个系统的不同观察角度。Context Engineering 贯穿所有层次——因为不管系统怎么设计,最终都要回到”这一轮模型看到什么”。
小结
Context Engineering 是 Agent 工程中从”写好 Prompt”到”设计信息系统”的跃升。
核心要点:
- Context 由五部分组成:指令、工具定义、执行轨迹、动态元数据、检索内容
- Prompt Engineering 是五个维度的系统化方法,不是”写一段话”
- Agent Skills 三层架构解决”能力太多怎么加载”的问题
- Status Bar 把执行状态显性化,减少模型需要推断的负担
- 三个设计原则:稳定在前、不重复、正确性优先于缓存
如果你的 Agent 表现不稳定,很多时候不是模型不行,而是 Context 组织得不好——关键信息没送进去,或者垃圾信息太多。
参考资料
- 李博杰《深入理解 AI Agent:设计原理与工程实践》,第二章:上下文工程,固定提交
e3883f8c。本文按本仓库基础教程定位重新组织结构和表述,聚焦方法论层面。