第二章 上下文工程

一、上下文工程

上下文:

Agent的"眼睛",AI实际看得到的信息。

上下文内容:

  • 系统提示词 System Prompt
  • 工具定义 Tool Definitions
  • 对话历史 Conversation History
  • 本轮思考 reasoning
  • 当前生成位置

上下文工程:

上下文的设计与管理

与harness的关系:

决定Agent在每个决策点看到的信息(一个高效的信息供给系统)。

二、上下文:决定Agent能力上限的关键

人与agent一样,拥有足够的Context才能把特定的活干好。
该Context包含完成该任务所需的足够的背景知识。
该知识需要系统性的设计、组织和提供,而不是直接硬塞。

三、Agent 如何调用大模型:理解 API 的上下文结构

3.1、消息角色

一句话理解:

消息角色用于标记信息的来源和用途,帮助模型区分规则、请求、历史输出与环境反馈。

四种基本角色:

  • **system**:定义Agent的身份、规则和行为边界
  • **user**:提供用户请求和任务信息
  • **assistant**:记录模型之前的回复和工具调用
  • **tool**:返回工具执行结果,并关联对应的工具调用

工具定义:

tools通常是独立于消息列表的字段,用于声明工具的名称、用途和参数格式。

两者共同构成模型的基础输入:

上下文=消息历史+工具定义\text{上下文} = \text{消息历史} + \text{工具定义}注意:

  • 历史消息不等于模型记忆,而是系统在每次调用时重新传入的上下文
  • 工具结果属于外部数据,不应被当作高优先级指令
  • 工具定义只表示模型知道该工具,不代表模型拥有实际执行权限
  • 不同模型API的角色名称和结构可能有所不同

核心结论:

消息角色告诉模型"信息来自哪里",工具定义告诉模型"可以使用什么能力"。

3.2、模型API交互模式

a. 单轮对话

一句话理解:

模型根据本次请求中的上下文生成一次回复

基本流程:

system规则 + user规则 → assistant回复

模型API本身通常不维护会话状态。多轮对话所需的历史信息,需要由Agent框架在后续调用中重新提供。

b. 工具调用循环

当模型缺少实时信息或需要执行外部操作时,会返回工具调用请求,而不是直接回答。

基本流程:

发送上下文和工具定义 → 模型生成工具调用 → 框架执行工具 → 结果写回上下文 → 模型继续决策

职责划分:

  • 模型:判断是否调用工具、选择工具并生成参数
  • Agent框架:校验并执行工具,将结果返回给模型
  • 工具:读取或改变外部环境
  • 模型:根据结果继续调用工具或生成最终回复

关键机制:

  • tool_calls表示模型提出的调用请求,不代表工具已经执行
  • tool_call_id用于关联工具请求与执行结果
  • 相互独立的工具可以并行执行
  • 存在数据依赖的工具必须分轮串行执行
  • 模型不再返回工具调用时,通常表示本轮任务可以结束

注意:

后续请求不一定必须携带未经处理的完整历史,而应提供完成当前决策所需的有效上下文。长任务中通常需要进行历史压缩、状态提取或持久化。

核心结论:

Agent的核心不是一次模型调用,而是模型决策、工具执行和环境反馈组成的持续闭环。这个闭环就是ReAct在API层面的基本实现。

3.3、API视角下的上下文

一句话理解:

每次模型调用的上下文,由相对稳定的基础配置和持续变化的任务轨迹共同构成。

基本结构:

上下文 = 稳定前缀 + 交互轨迹 + 当前状态

其中:

  • 稳定前缀:系统提示词和核心工具定义
  • 交互轨迹:用户消息、模型回复和工具执行结果
  • 当前状态:任务进度、关键结论、失败记录和下一步目标

a. 上下文构造原则

  • 尽量保持系统提示词和核心工具定义稳定,以提高前缀缓存命中率
  • 对话过长时压缩较早的历史,而不是简单截断
  • 压缩时保留目标、约束、决策、失败原因和证据来源
  • 将当前任务状态放在上下文末尾,降低模型重新推导状态的成本
  • 只提供当前决策所需的信息,避免无关历史形成噪声

核心流程:

加载历史 → 提取状态 → 检查Token预算 → 压缩旧信息 → 构造请求

b. KV Cache的意义

稳定前缀可以复用之前计算的KV Cache,从而降低首Token延迟和重复计算成本。

注意:

"前缀不能修改"并非协议限制,而是性能优化原则。修改前缀仍然可以正常调用模型,但可能导致缓存失效。

c. 本地小模型实验的启示

  • 工具调用能力不完全由模型参数规模决定
  • 合理的上下文和工具定义可以显著改善小模型表现
  • 独立的工具调用可以并行执行
  • 模型根据工具结果决定继续调用还是结束任务
  • 流式输出可以降低用户感知延迟

注意:

单次实验只能证明模型具备工具调用能力,不能证明其在复杂任务中足够可靠。实际部署仍需评估调用准确率、参数正确率、循环终止率和异常恢复能力。

核心结论:

上下文工程不是保存全部历史,而是在有限预算内,稳定地保留当前决策最需要的信息。

四、KVCache友好的上下文设计

一句话理解:

保持上下文前缀稳定,让模型复用已计算的中间状态,从而降低延迟和推理成本。

4.1、两种缓存

  • KV Cache:在一次推理过程中缓存历史Token的Key和Value,避免生成每个新Token时重复计算
  • Prompt Cache:在多次请求之间复用相同前缀对应的KV Cache

两者都依赖同一个条件:

可复用部分的Token前缀必须保持一致;从首个变化位置开始,后续缓存通常需要重新计算。

4.2、上下文结构

推荐将上下文组织为:

稳定前缀+增长轨迹+动态状态

  • 稳定前缀:系统提示词、核心工具定义
  • 增长轨迹:用户消息、模型回复、工具结果
  • 动态状态:时间、任务进度、用户状态等易变信息

核心原则:

  • 保持系统提示词和工具定义的内容、顺序及序列化方式稳定
  • 将动态信息追加到上下文末尾,不要频繁修改前缀
  • 使用标准消息格式和模型对应的Chat Template
  • 上下文过长时进行语义压缩,不要简单截断历史

4.3、Chat Template

一句话理解:

Chat Template负责把结构化API消息转换成模型实际处理的Token序列。

不同模型使用不同的角色标记和工具调用格式。错误地把工具结果当作用户消息,或者手动拼接纯文本对话,可能破坏角色语义、工具调用和多轮推理。

因此:

使用模型官方支持的消息协议,不要自行假设不同模型的内部格式兼容。

4.4、常见错误

  • 在系统提示词中动态注入时间或用户状态
  • 每轮重新排序工具定义
  • 使用滑动窗口直接删除早期消息
  • 将结构化消息拼接成普通文本
  • 无差别保留全部历史,导致上下文持续膨胀

这些做法可能同时造成缓存失效和关键信息丢失。

4.5、架构原则

  • 将全局稳定信息放在前部
  • 将用户、会话和环境信息放在后部
  • 对旧历史进行批量压缩并保留目标、约束、决策、失败和证据
  • 用状态栏呈现最新任务状态,避免模型反复从历史中推导
  • 通过实际监控评估缓存命中率、首Token延迟和成本

注意:

缓存是重要的性能约束,但不能凌驾于正确性和安全性之上。系统提示词或工具定义需要修正时,应正常更新并接受缓存失效,而不是为了缓存继续使用错误配置。
研究中的可编辑、可组合KV Cache有望降低动态上下文的重算成本,但尚未成为通用生产能力。

核心结论:

KV Cache友好的设计,本质上是"稳定前缀、追加变化、规范格式、按需压缩"

五、提示工程:优化系统提示词

一句话理解:

提示工程是通过明确、结构化的指令,让模型理解自己的职责、执行方法和行为边界。

系统提示词主要回答四个问题:

  • 你是谁:身份与职责
  • 要完成什么:目标与成功标准
  • 应该怎么做:流程、工具和异常处理
  • 不能做什么:权限、约束与安全边界

5.1、系统提示词设计

核心原则:

  • 使用清晰、具体且可执行的语言
  • 使目标、流程、规则、异常和输出要求组织内容
  • 为冲突规则明确优先级
  • 将复杂任务组织称流程,而不是堆砌零散规则
  • 把模糊业务要求细化为可判断的条件
  • 使用Markdown或XML划分语义区块

检验标准:

如果一个具备专业能力的新员工读完后仍不知道如何执行,模型通常也无法稳定执行。

注意:

MUST、NEVER等强调词可以突出关键约束,但不能替代明确规则,更不能形成真正的安全保障。

5.2、规则、示例与工具定义

规则适合描述:

  • 可以明确判断的业务条件
  • 必须遵循的处理流程
  • 输出格式和验收标准

Few-shot示例适合描述:

  • 难以用规则定义的风格
  • 特定的输出结构
  • 边界场景和例外情况

示例应少而精,优先覆盖典型情况和关键边界。

工具定义则应说明:

  • 工具解决什么问题
  • 什么时候应该或不应该使用
  • 参数的含义和取值范围
  • 返回结果与失败方式
  • 与其它工具的依赖关系

当工具数量过多时,可以采用按需发现和渐进式加载,避免一次性把全部定义放入上下文。

5.3、业务规则的工程边界

模型适合执行需要语言理解和语义判断的规则,但不应承担必须确定成立的系统约束。

例如以下规则不应只写在提示词中:

  • 身份与权限校验
  • 金额和计费计算
  • 支付、删除等高风险审批
  • 事务一致性
  • 法律与合规限制

核心原则:

提示词负责指导模型,Harness负责强制执行边界。

5.4、提示注入

一句话理解:

提示注入是把恶意指令伪装成普通数据,诱导Agent偏离原有目标或越权行动。

防御原则:

  • 明确区分可信指令与外部数据
  • 标记网页、邮件、文档等内容的来源
  • 使用标准消息角色和Chat Template
  • 对外部内容进行清洗和风险检测
  • 不将未经验证的信息直接写入记忆、Skill或状态栏
  • 对高风险工具实施独立权限检查和人工确认

注意:

上下文层防御只能降低提示注入成功率,无法彻底消除风险。真正的安全边界必须建立在模型之外。

5.5、评估与迭代

提示词质量应通过实验验证,而不是依赖主观感觉:

  • 任务完成率
  • 工具选择和参数准确率
  • 业务规则违反率
  • 输出一致性
  • 提示注入成功率
  • 合法请求误拒绝率

核心结论:

  • 好的提示词不是写的更长,而是让目标更明确、流程更清晰、规则可执行;需要确定性保障的部分,则必须交给Harness和程序实现。

六、动态提示词与Agents Skills

一句话理解:

  • Agent Skills将领域知识、操作流程和配套资源封装成按需加载的能力模块,避免把所有指令长期塞进系统提示词。

6.1、渐进式披露

Skills通常分为三个层次:

  • 元数据目录:常驻少量名称、描述和触发条件,帮助Agent判断是否需要加载
  • 核心指令:选中Skill后加载完整流程、规则和验收标准
  • 扩展资源:根据任务继续读取参考文档、模版、脚本和示例

基本流程:

发现Skill → 判断是否适用 → 加载核心指令 → 按需读取资源 → 执行并验证

核心价值:

让少量目录常驻,让完整能力按需进入上下文,从而降低Token消耗和无关信息干扰。

6.2、Skill的设计

一份可执行的Skill应包含:

  • 适用范围:什么任务应该使用,什么情况不应使用
  • 执行流程:按照什么顺序完成任务
  • 核心原则:最重要的判断规则和边界
  • 异常处理:何时重试、停止或询问用户
  • 验收标准:什么结果才算完成
  • 配套资源:模板、示例、参考资料和脚本

其中,description的核心作用是路由:

与其描述"我能做什么",不如明确"什么时候应该使用我"。

6.3、skill与工具的区别

  • Skill提供领域知识和执行方法,解决"应该怎样做"
  • 工具提供外部操作能力,解决"实际用什么做"
  • Skill可以指导Agent组合多个工具,也可以附带脚本和模版

例如,制作演示文稿的Skill描述内容提取、页面设计和质量检查流程;文件读取、图片处理和PPT生成工具负责实际执行。

6.4、上下文与缓存

Skill对上下文的优化方式是:

  • 启动时只加载简短目录
  • 选中后将正文追加到当前轨迹
  • 详细资料继续按需加载
  • 已加载内容在后续轮次中作为历史上下文复用

注意:

渐进式加载不是零成本。目录和Skill正文首次出现时仍需要计算,只是避免了每次启动都加载全部能力。

不同Agent运行时对Skill的消息角色、加载方式和缓存策略可能不同,真正稳定的原则是:

少量目录常驻,完整指令按需加载。

6.5、工程边界

以下内容不适合只放在Skill中:

  • 全局身份和基础行为准则
  • 所有任务都必须遵守的安全规则
  • 权限、支付和删除等强制约束
  • 必须由系统保证的事务与合规要求

这些内容应保留在系统指令或Harness中。Skill适合承载特定领域的知识和流程,而不是充当安全边界。

Skill本身也可能包含恶意指令或不安全代码,因此需要:

  • 审查来源和内容
  • 限制脚本执行权限
  • 进行版本控制和变更审核
  • 在真实任务上测试路由与执行效果

核心结论:

Agent Skills 的本质不是增加更多提示词,而是把专业经验模块化,并在正确的任务中、正确的时间、只加载必要的部分。

七、Agents 状态栏

一句话理解:

Agent状态栏是由Harness维护的结构化运行时摘要,让模型直接看到任务当前处于什么状态。

它相当于Agent轨迹的"物化视图":

完整执行轨迹 → 状态提取与聚合 → 当前状态栏

7.1、主要作用

  • 显示任务目标、计划和完成度
  • 记录工具调用次数、重试次数和资源预算
  • 提供当前时间、工作目录和运行环境
  • 汇总最近的错误及其处理状态
  • 提醒模型已触发的约束和停止条件
  • 减少模型反复扫描、统计完整历史的成本

状态栏把分散在长轨迹中的隐式状态,转换成模型可以直接使用的显式信息。

7.2、典型内容

  • 任务状态:当前目标、TODO、已完成和待处理事项
  • 执行状态:当前阶段、最近动作和下一步候选
  • 工具状态:调用次数、失败次数和调用上限
  • 环境状态:时间、目录、操作系统和资源状态
  • 约束状态:预算、权限和停止条件是否触发
  • 错误摘要:错误类型、关键参数和修复建议

状态栏应该短小、结构稳定,只保留当前决策需要的信息。

7.3、上下文位置与更新

状态栏通常放在上下文靠近末尾的位置,使模型在下一次决策前能够直接看到最新状态,同时避免频繁修改稳定前缀。

两种更新方式:

  • 末尾替换:删除上一版状态栏,再写入最新状态;信息干净,但会使短后缀缓存失效。
  • 持续追加:保留历史状态,每轮追加新快照;缓存友好,但会增加Token和陈旧状态。

实际系统可以定期生成检查点,在缓存效率、上下文长度和状态一致性之间取舍。

注意:

状态栏使用什么消息角色取决于具体API和Harness。重点不是固定使用user角色,而是明确标记它由运行时生成,避免与真实用户输入混淆。

7.4、设计原则

  • 尽量由确定性代码计算状态
  • 每项状态标明来源、时间和适用范围
  • 区分事实状态、模型推测和待确认事项
  • 关键计数和权限状态必须以系统数据为准
  • 外部网页、文档等不可信内容不得直接进入状态栏
  • 状态栏只保存摘要,必要时仍能追溯原始证据

7.5、风险与边界

模型通常会高度信任状态栏,因此错误状态可能比缺少状态更加危险。

主要风险包括:

  • 统计错误导致错误决策
  • 陈旧状态与真实环境不一致
  • 外部数据污染引发状态栏投毒
  • 摘要遗漏重要细节
  • 删除原始轨迹后无法重新验证

状态栏是对完整轨迹的有损投影,不应天然被视为事实源。

核心结论:

Agent状态栏不是让模型获得"自我意识",而是由Harness提前计算并呈现关键状态,用少量Token换取更稳定的规划、约束遵循和错误恢复能力。

八、实验

实验教程

九、参考

教程参考

相关推荐
知无不研1 小时前
调用模型的API接口与Token详谈
ai·agent·token·api接口
Code_流苏2 小时前
AI 知识库和数据库有什么区别?从“查订单”到“问规则”讲清楚
数据库·ai·agent·知识库
AAIshangyanxiu2 小时前
智能气候前沿:AI Agent结合机器学习与深度学习在全球气候变化驱动因素预测中的应用
人工智能·agent·气候变化·智能气候前沿
User_芊芊君子2 小时前
让 Claude Code 换上国产大脑:蓝耘元生代上 GLM-5.2 / DeepSeek / Qwen 模型横评实测
人工智能·ai·大模型
xiezhr2 小时前
我用豆包 Seed-2.1-pro-0915 做了个「今天吃啥」,中午点菜这事终于不用纠结了
agent·ai编程
寻道码路2 小时前
大模型工程化实战(十二):RAG 数据工程底座——采集到入库流水线(离线+在线双链路)
大模型·知识库·rag·ai工程化·llmops`·数据工程底座·文档采集
Code_流苏2 小时前
如何写好一个 Skills?
ai·agent·技能·skills
吴佳浩7 小时前
Skill 为什么不同于 Tool?Agent 技能库的自演进与动态加载机制
人工智能·agent·ai编程
吴佳浩7 小时前
Tool 的安全性与执行沙箱:从 Docker 到 gVisor 的防御架构
agent·ai编程·mcp