AI Agent 学习笔记(三):上下文工程(下)—— KV Cache、提示词设计与 Agent Skills

基于李博杰《深入理解 AI Agent:设计原理与工程实践》第2章学习笔记。


开篇:一条差点让账单翻倍的时间戳

某团队的客服 Agent 每天处理 10 万次对话,原本一切正常。某天,工程师为了让 Agent"知道"当前时间,在系统提示词里加了一行 Current time: {{now}}。第二天监控告警:所有对话的首 token 延迟从 0.5 秒涨到 3-5 秒,月度推理账单几乎翻了一倍。

代码看起来完全没问题,模型也没换------问题出在哪里?

答案是一个藏得很深的机制:KV Cache。那一行动态时间戳让每次请求的缓存前缀都不同,模型不得不从头重新计算整个前缀。一行看似无害的代码,让整条推理链路慢了一个量级。

这篇文章我们继续上下文工程的另一半:KV Cache 友好的上下文设计、系统提示词的艺术、动态提示词(Agent Skills)以及上下文压缩策略。


一、KV Cache:看不见的成本规则

缓存是什么

模型每生成一个 token,都要"回头看"一遍前文所有 token 的中间计算结果。如果每轮都从头算一次,开销会随上下文长度爆炸式增长。KV Cache 的做法是:把前文算过的键值对缓存下来,下一轮只计算新增部分。前提是前缀完全不变------只要前缀里有一个字符被改写,缓存就全部作废。

这就像做菜:如果前几步完全一样,你可以直接从上次切好的地方继续;但如果前面任何一步变了,后面所有步骤都得重来。

三条铁律

  1. 系统提示词和工具定义一旦确定就不要改。 任何改动,哪怕多一个空格,都会导致缓存全部失效,延迟成倍增加、成本上升。
  2. 动态信息永远追加到末尾。 时间戳、用户状态等变化内容,作为新消息追加到对话末尾,而不是修改已有的系统提示词。
  3. 使用标准 API 格式,不要自行拼接消息。 结构化消息会被 Chat Template 翻译成模型训练时见过的固定 token 序列;自行拼成 "USER: ... ASSISTANT: ..." 偏离了训练格式,会削弱模型的多步思考能力。

常见错误模式

错误模式 危害 正确做法
动态系统提示词(嵌入时间戳) KV Cache 完全失效 时间作为消息追加末尾,或通过工具获取
动态用户配置(每次更新余额) 破坏缓存 通过专门状态管理机制处理
工具定义动态排序 缓存全部失效 固定顺序(对工具选择能力几乎无影响)
滑动窗口对话历史 破坏前缀一致性 + 丢失关键工具结果 避免滑动窗口,Agent 会"忘记"之前的工具调用结果
文本格式化拼接消息 偏离训练格式,模型需额外推断角色边界 用标准 role-content 消息

实验中发现:使用滑动窗口的 Agent 经常陷入循环,反复执行相同的工具调用,因为它"忘记"了之前已经获得的结果。

KV Cache 与 Prompt Cache:两个层级

  • KV Cache:模型内部优化,加速单次请求内的 token 生成。
  • Prompt Cache:API 服务层优化,跨请求缓存相同前缀的计算结果,直接影响 API 计费。

各家策略不同:Anthropic 需要显式设置 cache_control 断点才会缓存(缓存写入约 1.25 倍加价);OpenAI 自动前缀缓存。缓存读取成本约为首次计算的十分之一。

在生产级系统中,缓存经济性是前置架构约束:Claude Code 的提示词结构由缓存边界决定------可跨用户全局缓存的内容放在边界前,所有动态元素(操作系统、模式、用户偏好)严格归到边界后,否则 N 个二值条件会产生 2^N 种缓存键变体。


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

提示工程的核心对象是系统提示词------API 消息列表中那条 role: "system" 的消息。它是 Agent 的"员工手册",定义了 Agent 的身份、行为规则、约束条件和工作流程。

检验标准:大语言模型是一位聪明的新员工,能力出众,但对你们的具体工作流程一无所知。如果一个聪明的新员工读完你的系统提示词还不知道该怎么做,Agent 也一样不知道。

语气与风格:提示词的"人格"

  • 大写字母(如 NEVER do X)比 Please avoid doing X 更能引起模型的"注意",但过度使用会稀释效果,应保留给真正关键的约束。
  • 简洁明确的约束优于含糊请求:"回答不超过 4 行"比"尽量简短"有效得多。

结构化提示:提示词的"格式"

现代大模型对结构化输入显著敏感,这源于训练数据中包含大量结构化内容:

  • XML 标签 :标签名称本身携带语义------<working_directory> 能立即告诉模型这是工作目录信息。
  • Markdown:在保持可读性的同时提供轻量级结构。

两者协同构成双层结构:XML 负责机器可解析的精确语义,Markdown 负责人机共读的组织逻辑。

流程驱动 vs 规则堆砌

给新员工一本包含上百条零散规则的手册,没有流程图也没有优先级说明------即使最聪明的人也会困惑。

流程驱动的提示词(SOP 风格)让模型在任何时刻都知道自己处于哪个阶段、当前步骤的目标、完成后进入哪一步:

vbnet 复制代码
File Processing Standard Operating Procedure:
Step 1: Validation → Check if file exists and is accessible
  - If not found → log error and stop
Step 2: Classification → Determine file type by extension and content
Step 3: Preprocessing → Config files → backup; Large files (>1MB) → stream
Step 4: Execution → Execute core logic based on file type
Step 5: Verification → Ensure integrity of the processed file

遇到异常时,模型根据当前所处阶段确定处理方式,而不是遍历所有规则去找匹配项。提示工程消融实验表明,信息组织混乱会导致成功率下降 30% 以上。


三、动态提示词与 Agent Skills

随着 Agent 覆盖的业务场景越来越多,系统提示词会不断膨胀------全部塞进一个提示词会带来两个问题:浪费 token (大部分内容与当前任务无关),以及注意力被稀释

这就是从静态提示工程到动态提示词的自然演进:不是把所有知识一次性塞给 Agent,而是让它按需加载

Skills 的渐进式披露

Agent Skills 将 Agent 的能力模块化为独立的、可按需加载的知识包。设计哲学是渐进式披露(Progressive Disclosure)------先给 Agent 看目录摘要,需要时再加载完整内容:

  • 第一层(元数据,~300 tokens) :启动时扫描所有已安装 Skill,注入 namedescription 列表。
  • 第二层(SKILL.md 核心流程,~2K tokens) :Agent 判断任务需要时,通过 Skill 工具加载完整的 SKILL.md
  • 第三层(子文档,选择性深入)html2pptx.mdreference.mdscripts/*.py 等,按需加载。

这种结构对 KV Cache 极其友好:元数据固定 → 缓存友好;动态内容追加 → 不破坏缓存

Skills 与工具的关系

如果把所有专用工具定义都放进系统提示词,数量膨胀会消耗大量 token,且变更时破坏缓存前缀;而 Skill + 通用执行器模式下,工具数量始终很少,Skill 内容通过渐进式披露按需加载,不影响已缓存前缀。

一个真实例子:用 Claude Code + PPTX Skill 从论文 PDF 生成演示文稿,Agent 的执行流程是:

  1. 在上下文末尾的 Skill 元数据列表中看到 PPTX Skill 的描述
  2. 识别任务需要该 Skill
  3. 通过 Skill 工具加载完整的 SKILL.md
  4. 选择性加载子文档获取详细方法
  5. 使用捆绑的工具脚本和模板文件

四、Agent 状态栏:让模型"瞥一眼"就知道状态

为什么需要状态栏

生产级 Agent 容易陷入各种陷阱:无限循环、状态遗忘、任务目标偏离。根源在于 Agent 缺乏对自身状态和任务进展的感知。

Agent 状态栏通过在上下文中嵌入结构化的元信息,为 Agent 提供自我感知机制。最好的类比是手机顶部状态栏------时间、电量、信号强度,随时瞥一眼就掌握设备状态。

与 System Prompt 的区别:系统提示词是入职时发的员工手册,定下来就不变;Agent 状态栏是贴在屏幕边缘的实时仪表盘,随任务推进不断更新。

理论基础:上下文学习是检索而非推理

注意力机制的一个本质特性:模型擅长从已有内容中查找信息,但不擅长主动归纳和总结。上下文窗口是一台只有一半的检索引擎------"检索"的一半非常强,但缺了"提炼层"。

考虑实际场景:系统提示词要求拨打每个商家不超过 3 次。但打了 3 次之后,Agent 经常数不清到底打了几次,又打第 4 次甚至陷入循环。当我们在每个电话的工具调用结果中直接加入"本次是第 3 次呼叫该商家",模型立即发现已达限制,不再继续。把分散的隐式状态提炼为可直接使用的显式知识------这是状态栏的本质。

状态栏的构成与实现

状态栏包含:任务规划(TODO 列表)、工具调用计数器、时间戳跟踪、详细错误信息、系统状态(时间、工作目录、操作系统、Shell 环境)等。

状态更新的两种实现与缓存代价

  • 实现一:每轮替换。移除旧状态、追加最新状态。保证上下文只有一份最新状态,但移除旧状态会使其位置之后的缓存失效。
  • 实现二:持久追加 。状态一旦注入就永久留在轨迹中(Claude Code 的 <system-reminder> 采用此方式)。对缓存完全友好,但陈旧状态会累积占用 token。

取舍法则:状态更新频繁且轨迹长 → 选持久追加;轨迹短或状态消息大 → 选每轮替换。

实验效果

  • 工具调用计数器:显式计数触发模型模式识别------第一次失败检查路径、第二次列出目录、第三次主动放弃找替代方案,实现隐式成本感知。
  • TODO 列表:启用后 Agent 平均 15 次迭代完成任务,禁用时需 21 次且常遗漏子任务。
  • 详细错误信息(错误类型+参数+调用栈+修复建议):错误场景中找替代方案的成功率从 60% 提升到 95%。
  • 系统状态感知:注入时间、工作目录、OS、Shell 等,Agent 能做出平台相关决策(Linux 用 apt、macOS 用 brew)。

这些技术协同会产生涌现效应------完全启用的 Agent 不再是机械执行指令的工具,而更像有自我意识的助手。

一个重要警告:光有读数不够,还要给策略

实验发现一个反直觉结论:只把原始时间戳给模型,和什么都不给几乎没有区别。真正把通过率从一成拉到四五成的,是那份"这些读数该怎么用"的操作手册------显式的读数只是原料,模型还需要把读数翻译成动作的说明书


五、上下文压缩策略

为什么需要压缩:不只是长度问题

  1. 解决长度约束和成本约束:上下文窗口有限,工具调用结果动辄数万字符,几轮交互就可能撑满窗口。
  2. 提升思考质量:总结后的知识比原始形式更利于模型使用。Agent 在数万 token 中反复"检索"关键片段,注意力被分散。

上下文腐化:比溢出更隐蔽

上下文腐化(Context Rot)与溢出是不同问题:溢出是"装不下了",腐化是"装得下但找不到了"。实践中最常见的失效模式不是窗口不够长,而是信息密度不对

一个例子:100 个笼子(90 只黑猫、10 只白猫)的巡查记录。问"黑猫和白猫各多少只"------不启用思维链,模型很难直接答对;启用思维链每次都要从头数。而如果提前写入"当前统计:黑猫 90 只,白猫 10 只",模型立即检索到结论。这就是压缩的第二个价值:把需要思考才能得到的结论变成可以直接检索的知识。

压缩与 KV Cache:看似矛盾,实则互补

关键:压缩不是在单次 API 调用过程中修改上下文,而是在两次调用之间由 Agent 框架预处理消息列表。System Prompt 和工具定义永远不动;压缩对象是 tool results------替换位置之后的缓存失效,但之前的缓存仍然有效。压缩频次需要权衡:最好在上下文接近阈值时批量压缩,而非每轮都压。

六种压缩策略对比

任务:识别并追踪 OpenAI 联合创始人的职业状态,上下文预算限制在 128K 窗口。

策略 Token 用量 结果
无压缩 >110K ✗ 溢出失败
个体摘要 123K ✓ 但信息碎片化
组合摘要 55K ✓ 长输入截断可能丢信息
上下文感知压缩 25K ✓ token 减少 77%,成功率最高,迭代最少
感知+引用 45K ✓ 有损压缩+无损索引
自适应窗口 181K ✓ 延迟压缩,最大保真

上下文感知压缩 的核心创新:将当前查询意图和已积累信息纳入压缩决策------"Given the search query: {query}" + "Current context: {context}"。多步骤任务中不同阶段需要的信息密度不同:初期广泛收集、中期精确核验、后期综合整合。

自适应窗口化 的三个机制:阈值触发(80% 窗口才激活)、批量压缩(一次性压缩所有未标记工具结果)、防重复保护([COMPRESSED] 标记)。

生产级的分层压缩机制

成熟系统不只用单一策略,而是组合为分层机制(以 Claude Code 为参照):

  1. 工具结果预算控制:大体积输出存磁盘,模型只看摘要预览
  2. 噪声直接删除:低价值内容直接移除,不做摘要
  3. API 层微压缩:通过上下文编辑能力指示服务端移除指定结果
  4. 归档式摘要:逐轮结构化摘要(像 git log 保留每轮记录,而非 git squash 合并)
  5. 全量压缩:LLM 驱动的完整压缩作为最后手段,配连续失败熔断器

前三层实现成本最低、对缓存扰动可控,优先使用;后两层成本高但压缩效果强,作为兜底。

保留优先级

压缩最容易丢失的不是细节本身,而是早期的架构决策、约束背后的理由和失败的路径。生产系统应显式定义保留优先级:

  1. 架构决策和关键约束:不得摘要
  2. 已修改的文件列表和关键变更记录:完整保留
  3. 验证状态(pass/fail):必须保留
  4. 未解决的 TODO 和回滚笔记:必须保留
  5. 工具输出:可删除,仅保留 pass/fail 结论

UUID、hash、IP、端口、URL、文件名等标识符必须原样保留------一旦把 PR 编号或 commit hash 改错一位,后续工具调用直接失效。

隔离优于压缩:子 Agent 上下文隔离

更釜底抽薪的思路是让大体积中间信息根本不进入主上下文------把"读取大量文件""大范围搜索"这类任务委派给子 Agent,子 Agent 在自己的上下文完成探索,只回传几百 token 的结论。

这本质上是用隔离代替压缩:压缩是有损的、需要额外 LLM 调用的事后补救;隔离让噪声从一开始就与主上下文绝缘,主 Agent 的 KV Cache 前缀完全不受影响。代价是任务描述必须自包含、目标明确------这又回到本章主题:上下文的质量决定能力上限。


本章小结:显式的信息管理

本章绕来绕去,其实在说一件事:给模型看什么、怎么组织,比模型本身有多聪明更影响最终的结果

  • API 消息结构定义了上下文的骨架
  • KV Cache 约束了你能改什么、不能改什么
  • 提示工程和 Agent Skills 决定了如何高效提供静态指令和动态知识
  • Agent 状态栏把隐式状态变成显式信息
  • 压缩策略解决了上下文膨胀问题------不仅是控制长度,更是把原始数据变成高密度结构化知识

这些技术的共同点是显式的、工程化的信息管理。回到 Rich Sutton 的《苦涩的教训》:那些能更有效地利用更多算力的通用方法将最终胜出。


下篇预告

下一章将把视角从"一次任务之内的上下文管理"延伸到"跨越会话的持久化知识体系"------用户记忆与知识库,让 Agent 能够在实践中不断积累经验,逐步成为真正的领域专家。


本文基于李博杰著《深入理解 AI Agent:设计原理与工程实践》v1.3 第2章学习整理。系列文章同步发布于 ai-agent-study 仓库。

相关推荐
资深低代码开发平台专家1 小时前
2026企业级AI编程平台厂推荐:行业趋势、评估维度与主流品牌对比解析
大数据·人工智能·ai编程
玹外之音1 小时前
别做"AI 翻译"了,做"AI 本地化":跨境电商 Listing 系统的工程实践
aigc·工作流引擎·aiops
CAD老兵1 小时前
在浏览器里对比 DWG/DXF 图纸 —— @mlightcad/cad-diff-viewer
前端·javascript·github
Web3_Basketball1 小时前
从0到1落地MCP连接器从零接企业工具:踩坑全记录
ai编程
奈斯先生vector1 小时前
DeepSeek Harness 配置的真正价值:它如何改变 Agent 的运行方式与工程边界
aigc·ai编程
JasonYin1 小时前
AI 定义的 H5移动端 开发规范,直接抄作业!
前端
诗章与猫1 小时前
Leaflet 渲染 100 万 marker 卡成 PPT?我用 WebGL 重写了渲染器
前端
不一样的少年_1 小时前
图解 AI Agent ①:大模型接上 API,为什么还不算 Agent?
人工智能·agent·ai编程
用户921080262861 小时前
在 AI 代码生成项目里接入 Thought:别展示“玄学思维链”,只展示用户真正关心的工具调用
前端