Agent上下文工程构建

上下文工程的核心不只是"怎么写 Prompt",而是:把上下文当成一套持续运行的信息供给系统,在每次模型决策前,决定该让模型看到什么、以什么结构看到、哪些内容保持稳定、哪些内容按需加载、哪些历史需要提炼或隔离。

Ct=Pstable+Tt+St C_t = P_{\text{stable}} + T_t + S_t Ct=Pstable+Tt+St

  • PstableP_{\text{stable}}Pstable:稳定前缀,包括系统提示词、核心工具定义。
  • TtT_tTt:不断增长的任务轨迹,包括用户消息、模型回复、工具调用和工具结果。
  • StS_tSt:当前状态摘要,包括 TODO、环境状态、调用计数、约束是否触发等。

一条主线

#mermaid-svg-rEaLHUU1GOx5R2iQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rEaLHUU1GOx5R2iQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rEaLHUU1GOx5R2iQ .error-icon{fill:#552222;}#mermaid-svg-rEaLHUU1GOx5R2iQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rEaLHUU1GOx5R2iQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rEaLHUU1GOx5R2iQ .marker.cross{stroke:#333333;}#mermaid-svg-rEaLHUU1GOx5R2iQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rEaLHUU1GOx5R2iQ p{margin:0;}#mermaid-svg-rEaLHUU1GOx5R2iQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rEaLHUU1GOx5R2iQ .cluster-label text{fill:#333;}#mermaid-svg-rEaLHUU1GOx5R2iQ .cluster-label span{color:#333;}#mermaid-svg-rEaLHUU1GOx5R2iQ .cluster-label span p{background-color:transparent;}#mermaid-svg-rEaLHUU1GOx5R2iQ .label text,#mermaid-svg-rEaLHUU1GOx5R2iQ span{fill:#333;color:#333;}#mermaid-svg-rEaLHUU1GOx5R2iQ .node rect,#mermaid-svg-rEaLHUU1GOx5R2iQ .node circle,#mermaid-svg-rEaLHUU1GOx5R2iQ .node ellipse,#mermaid-svg-rEaLHUU1GOx5R2iQ .node polygon,#mermaid-svg-rEaLHUU1GOx5R2iQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rEaLHUU1GOx5R2iQ .rough-node .label text,#mermaid-svg-rEaLHUU1GOx5R2iQ .node .label text,#mermaid-svg-rEaLHUU1GOx5R2iQ .image-shape .label,#mermaid-svg-rEaLHUU1GOx5R2iQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-rEaLHUU1GOx5R2iQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rEaLHUU1GOx5R2iQ .rough-node .label,#mermaid-svg-rEaLHUU1GOx5R2iQ .node .label,#mermaid-svg-rEaLHUU1GOx5R2iQ .image-shape .label,#mermaid-svg-rEaLHUU1GOx5R2iQ .icon-shape .label{text-align:center;}#mermaid-svg-rEaLHUU1GOx5R2iQ .node.clickable{cursor:pointer;}#mermaid-svg-rEaLHUU1GOx5R2iQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rEaLHUU1GOx5R2iQ .arrowheadPath{fill:#333333;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rEaLHUU1GOx5R2iQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rEaLHUU1GOx5R2iQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rEaLHUU1GOx5R2iQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rEaLHUU1GOx5R2iQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rEaLHUU1GOx5R2iQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rEaLHUU1GOx5R2iQ .cluster text{fill:#333;}#mermaid-svg-rEaLHUU1GOx5R2iQ .cluster span{color:#333;}#mermaid-svg-rEaLHUU1GOx5R2iQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rEaLHUU1GOx5R2iQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rEaLHUU1GOx5R2iQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-rEaLHUU1GOx5R2iQ .icon-shape,#mermaid-svg-rEaLHUU1GOx5R2iQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rEaLHUU1GOx5R2iQ .icon-shape p,#mermaid-svg-rEaLHUU1GOx5R2iQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rEaLHUU1GOx5R2iQ .icon-shape .label rect,#mermaid-svg-rEaLHUU1GOx5R2iQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rEaLHUU1GOx5R2iQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rEaLHUU1GOx5R2iQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rEaLHUU1GOx5R2iQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否

任务需要什么信息
按角色和来源组织
稳定前缀:规则与核心工具
按需加载:Skills 与延迟工具
显式状态:TODO、计数、环境
持续累积交互轨迹
信息是否过多?
调用模型决策
删除噪声、摘要旧证据、保留约束与来源
必要时交给子 Agent 隔离中间过程

1. 上下文决定的是 Agent 的能力上限

模型本身只有通用能力,不知道具体项目里的:

  • 代码结构和业务规则;
  • 团队流程和权限边界;
  • 当前环境、任务进度和历史决策;
  • 哪些信息是用户指令,哪些只是网页、文档或工具返回的数据。

因此,上下文工程的目标不是"尽量塞更多信息",而是:

在每个决策点,以最低成本给模型提供足够、准确、结构清晰的信息。

一个中等模型配上高质量上下文,可能胜过一个更强模型在信息不足时的盲目探索。它也是组织问题:文档、决策记录和规则越透明,Agent 越容易工作。

2. API 是无状态的,Harness 负责重建状态

每次模型调用本质上都是一次独立请求。模型不会自动记住上一轮发生了什么,Harness 必须重新提交:

  • system:开发者规则;
  • user:用户输入;
  • assistant:模型之前的回复和工具调用;
  • tool:工具执行结果;
  • tools:模型可调用的工具定义。

典型循环是:

复制代码
用户请求
→ 模型决定调用工具
→ Harness 执行工具
→ 把 assistant tool_calls 和 tool results 放回历史
→ 再次调用模型
→ 得到最终回答或继续调用工具

这里最重要的思想是:模型负责决策,Harness 负责执行和上下文管理。消息角色、tool_call_id 和工具结果的来源不能混淆,否则模型会错误理解历史。

3. 上下文首先要分成"稳定前缀"和"动态轨迹"

这是整章的架构地基:

复制代码
稳定前缀
├── System Prompt
└── 核心 Tool Definitions

动态轨迹
├── 用户消息
├── 模型回复
├── 工具调用
├── 工具结果
├── 按需加载的 Skill/工具定义
└── 当前状态栏

稳定前缀尽量不动,动态信息尽量追加到后面。原因既包括语义清晰,也包括 KV Cache 和 Prompt Cache 的经济性。

三条硬规则

  1. 系统提示词和核心工具定义确定后尽量保持字节级稳定。
  2. 时间、工作目录、用户状态等动态信息追加到末尾,不要修改系统提示词。
  3. 使用标准消息角色和 Chat Template,不要手工拼成一大段 "USER: ... TOOL: ..." 字符串。

例如把 Current time: {``{now}} 写进系统提示词,会让每次请求的前缀不同,导致后续缓存大面积失效。缓存因此不只是性能优化,而会反过来影响 Agent 的架构设计。

4. 静态规则应该流程化,而不是无限堆砌

系统提示词是 Agent 的"员工手册"。好的提示词应让一个聪明的新员工读完后知道:

  • 自己的角色和目标;
  • 按什么步骤工作;
  • 每一步的验证条件;
  • 哪些边界不能越过;
  • 遇到异常时什么时候停止或请求确认。

本章主张优先使用:

复制代码
验证 → 分类 → 执行 → 检查 → 完成

而不是几十条没有优先级的零散规则。

提示词设计的几个重点:

  • 用 Markdown/XML 明确层次和来源;
  • 把业务规则写成可执行判断,不把关键决策留给模型自由发挥;
  • 只有难以抽象描述的风格和格式才使用 few-shot;
  • 两三个覆盖边界的高质量示例,通常胜过十个相似示例;
  • 工具描述需要包含用途、参数语义、使用边界、示例和工具间关系。

本章实验发现,规则内容不变、只把结构打乱,也会显著降低任务成功率;说明"信息如何组织"与"信息是否存在"同样重要。

5. 不要把全部知识常驻:用 Skills 做渐进式披露

随着业务增加,所有领域规则都塞进系统提示词会产生两个问题:

  • 浪费 token;
  • 无关内容稀释注意力,造成上下文腐化。

Skills 的解决方案是"少量目录常驻,完整知识按需加载":

复制代码
第一层:Skill 元数据
name + description
↓ 路由判断
第二层:SKILL.md 核心流程
↓ 根据任务继续深入
第三层:参考文档、模板、脚本、示例

这里的关键不是 Skill 文件格式本身,而是信息生命周期:

  • 可发现性信息常驻;
  • 完整流程只有被选中时才加入轨迹;
  • 更详细资料再按需读取;
  • 已经加载的内容追加进入历史,而不是回头重写前缀。

同样的思想也适用于大量工具:启动时只提供工具名称和简述,模型需要时再加载完整 schema。

6. 模型擅长检索,不擅长自动归纳状态

这是状态栏和压缩机制共同的理论基础。

假如三次电话调用分散在几千个 token 的轨迹中,模型虽然能看到它们,却不一定能稳定地计算出"已经调用三次,达到上限"。它每一轮都需要重新扫描、计数和推理。

因此 Harness 应提前把隐式状态提炼成显式知识:

复制代码
<agent_status>
- Goal: cancel subscription
- TODO: identity verification completed
- phone_call: 3/3, limit reached
- Current working directory: /project
- Last validation: failed
</agent_status>

状态栏通常包含:

  • 任务目标和 TODO;
  • 已完成、进行中、取消的步骤;
  • 工具调用次数和重复失败;
  • 当前时间、工作目录、操作系统;
  • 关键约束是否达到阈值;
  • 最近一次验证结果。

核心原则是:

  • 能由代码确定性计算的状态,就不要让另一个 LLM 批量总结;
  • 状态栏只做投影,不能无条件替代原始证据;
  • 模型会高度信任状态栏,所以其准确率本身是生产指标;
  • 动态状态放在上下文末尾,而不是修改 system 前缀。

7. 压缩不只是为了"装得下"

上下文过长有两个不同问题:

  • 上下文溢出:窗口真的装不下;
  • 上下文腐化:虽然装得下,但关键信息被大量噪声淹没,模型找不到或用不好。

所以压缩有两个目标:

  1. 控制 token、成本和延迟;
  2. 把需要反复思考才能得到的结论,变成可直接检索的高密度知识。

例如,不要让模型每轮从十几次搜索结果中重新推导人员名单,而应生成:

复制代码
已确认:
- A:当前职位......
- B:于某年离职......
仍缺:
- C 的最新状态
来源:
- URL 1
- URL 2

生产级分层处理

按照信息价值,可以逐层处理:

  1. 大型工具输出写入磁盘,上下文只放摘要和路径;
  2. 导航栏、重复结果等纯噪声直接删除;
  3. 接近预算时批量移除低价值工具结果;
  4. 用结构化的逐轮摘要保留逻辑脉络;
  5. 最后才进行全量 LLM 压缩,并设置失败熔断器。

压缩时应明确保留优先级:

  1. 架构决策和关键约束;
  2. 已修改文件及关键变更;
  3. 验证的 pass/fail 状态;
  4. 未完成 TODO 和回滚信息;
  5. 事实来源与引用;
  6. 原始工具输出可以删除或外置。

8. 隔离通常比事后压缩更好

如果某个探索会产生海量中间信息,最好从一开始就不要把它放进主 Agent 上下文。

例如:

复制代码
主 Agent
  → 委派:"找到支付回调函数和所有调用点"
      → 搜索子 Agent 阅读几十个文件
      → 只返回位置、调用关系和关键证据
  → 主上下文只增加几百 token

这相当于:

  • 压缩:噪声先进来,再有损清理;
  • 隔离:噪声从未进入主上下文。

隔离还能保护主 Agent 的缓存和注意力,但要求委派任务足够自包含、目标和输出格式明确。

9. 上下文安全:必须区分"指令"和"数据"

网页、邮件、PDF、检索结果和第三方 Skill 都可能包含伪装指令。

上下文层面的第一道防线包括:

  • <external_content source="..."> 标注不可信内容;
  • 严格使用 system/user/assistant/tool 角色;
  • 不把工具结果伪装成用户指令;
  • 清洗明显的注入模式;
  • 审查第三方 Skill;
  • 不把未经验证的外部内容直接写进高可信状态栏。

但上下文防御不能代替权限、沙盒和高风险操作确认。提示注入一旦结合工具执行能力,问题就从"回答错误"升级为"执行危险动作"。

最后压缩成六句话

  1. 上下文管理的本质是管理 Agent 每一步的观察空间。
  2. 每次模型调用都无状态,Harness 必须重建必要历史。
  3. 静态规则保持稳定,动态信息只增不改地放到后面。
  4. 长期领域能力用 Skills 按需加载,运行时状态由代码提炼成状态栏。
  5. 当轨迹变长时,删除噪声、外置原文、压缩旧证据,但必须保留决策、约束、失败、验证和来源。
  6. 能通过子 Agent 隔离的中间过程,就不要先污染主上下文再压缩。

最终要建立的不是一组 Prompt 技巧,而是一个完整的上下文生命周期:

复制代码
分类 → 组织 → 注入 → 缓存 → 更新 → 提炼 → 压缩/隔离 → 验证
相关推荐
段一凡-华北理工大学1 小时前
AI推动工业智能化转型~系列文章24:钢铁工业 AI 全景图:从原料场到轧机的场景地图
人工智能·深度学习·模型生命周期·钢铁工业·ai全景
styshoo1 小时前
[NVSentinel] gpu-health-monitor模块调研
人工智能·云原生·nvsentinel
小玮看世界1 小时前
基于AI的活动预期流量统计分析:共享单车精准数治与精准派单的工程可行性研究
人工智能
iori97king2 小时前
织信开发日志 11:AI 智能体如何从一句话生成应用
人工智能
Jiamiren2 小时前
WEEX:长鑫科技上市大涨,宇树科技合约同步走高,传统资产交易迎来新路径
人工智能·科技·区块链
海兰2 小时前
mcporter — 安装部署及使用完全指南(二)
人工智能·agent
一拳不是超人3 小时前
LangChain 们的丧钟?DeepSeek 把 Agent 开发变成了拼乐高
人工智能·agent
麻雀飞吧3 小时前
零基础选量化工具,先把问题说清楚
人工智能·python
向成科技3 小时前
新品亮相|向成电子IPCA_3588H AI边缘网关重磅发布,算力下沉,自主可控
人工智能·边缘计算·国产化·硬件·边缘网关·主板·端侧ai