摘要

很多团队做AI Agent,陷入了“效果差就换更大模型”的误区,却忽略了一个核心事实:模型能力是天花板,上下文质量才是决定Agent实际表现的地板。同一个模型,上下文组织得好与坏,任务完成率可以差出一个量级。
本文基于一线工程实践,系统拆解上下文工程的7个核心层级:从底层最容易被忽略的KV Cache性能约束,到静态提示工程、Skills动态能力加载、Agent状态栏、上下文压缩,再到子Agent隔离架构。每个模块讲清原理、真实踩坑、生产级最佳实践,帮你用现有模型把Agent能力拉满,同时控制延迟与推理成本。
关键词:AI Agent;上下文工程;KV Cache;提示工程;Agent状态栏;上下文压缩;子Agent隔离

目录

0 前言:别再只靠换模型提升Agent效果
1 底层约束:KV Cache——90%的人都踩过的性能暗坑
2 静态底座:提示工程,首先是产品设计问题
3 动态扩展:Agent Skills,按需加载的能力模块
4 状态感知:Agent状态栏,把隐式状态变成显式知识
5 密度提升:上下文压缩,解决的不只是长度问题
6 架构优化:子Agent隔离,用隔离代替压缩
7 落地路线:从入门到高阶的7步优化清单
8 结语:上下文工程的核心哲学


0 前言:别再只靠换模型提升Agent效果

做Coding Agent的同学大概率有过这种体验:
同样是修复一个bug,给Agent完整的目录结构、代码规范、分支策略,它能输出符合项目规范的代码;只丢一句“帮我修bug”,它写出来的东西往往语法正确但完全不符合项目架构,甚至直接往主分支瞎提交。

这就是上下文的价值。模型就像一个能力很强的新员工,你不给它业务规则、流程规范、环境信息,它再聪明也发挥不出来。
很多团队做Agent效果不好,第一反应是换更大的模型,却忽略了上下文的组织质量。上下文工程不是“往提示词里塞更多信息”,而是系统性地设计:模型每次决策时能看到什么、信息怎么组织、哪些静态哪些动态、膨胀了怎么处理。

这篇文章我们从底层约束到顶层架构,逐层拆解上下文工程的完整方法论,每一层都配真实踩坑和生产级实践。

1 底层约束:KV Cache——90%的人都踩过的性能暗坑

1.1 一个真实事故

某团队的客服Agent上线后一直很稳定,某天工程师为了让Agent知道当前时间,在系统提示词里加了一行Current time: {{now}},每次请求动态替换时间戳。
结果第二天直接告警:首token延迟从0.5秒涨到3-5秒,月度推理账单几乎翻了一倍。
代码看起来完全没错,问题就出在KV Cache上——那一行动态时间戳,让所有请求的前缀缓存全部失效,模型每次都要从头计算所有token的键值对。

1.2 KV Cache到底是什么

简单说,模型生成每个token时,都需要回看前文所有token的中间计算结果。如果每轮都从头算,开销会随上下文长度爆炸式增长。
KV Cache就是把前文的中间计算结果缓存下来,下一轮只算新增token的部分。前提是前缀必须字节级完全一致,哪怕多一个空格、改一个标点,缓存都会全部失效。

类比:前面切菜、调酱汁的步骤每次都一样,就可以提前备好直接用;如果前面换了食材,后面所有步骤都得重来。
系统提示词、工具定义就是“前面的步骤”,定了就别改;时间、状态这类动态内容,应该追加到末尾,不能改前面的前缀。

1.3 三条必须刻进脑子里的铁律

  1. 系统提示词和工具定义一旦确定就不要改。任何改动都会导致缓存全失效,延迟成倍增加,成本直接翻倍。
  2. 动态信息永远追加到末尾。时间戳、用户状态、进度信息,全部作为新消息追加到对话末尾,绝对不要修改已有的system消息。
  3. 用标准API消息格式,不要自己拼字符串。每个模型都有官方训练时的聊天模板,自己拼USER:... ASSISTANT:...等于发明一套模型没见过的格式,模型表现会打折。

1.4 KV Cache vs Prompt Cache,别搞混

很多人把这两个概念混为一谈,其实完全不在一个层面:

  • KV Cache:推理引擎层面的缓存,存在于vLLM、SGLang的内存里,是单次会话内的加速,会话结束就清空。
  • Prompt Cache:API服务商的跨请求缓存,在KV Cache之上实现,允许相同前缀在不同用户、不同请求之间复用。OpenAI、Anthropic的提示词缓存都属于这一类。

【实战避坑】
不要为了“个性化”给每个用户改系统提示词,不要动态往system里塞few-shot示例。这些做法都会让缓存彻底失效,推理成本涨几倍都找不到原因。

2 静态底座:提示工程,首先是产品设计问题

很多人觉得提示工程就是“把指令写清楚”,其实远不止如此。对Agent来说,系统提示词+工具定义是整个上下文的静态前缀,是Agent行为的规则底座。

2.1 案例:电商退货审核Agent

假设要做一个自动审核退货的Agent,目标是正常订单快速批,恶意订单防薅羊毛。
如果只写一句“对合理的退货请求予以批准”,Agent的批准率会忽高忽低:超过7天的VIP订单批不批?拆封的数码产品退不退?生鲜食品退不退?全靠模型自由发挥。

正确的做法是把规则写死、边界划清:

NEVER auto_approve returns for fresh food or activated digital products. Use human_review instead.
VIP orders: extend the return window to 30 days.

这个案例背后有一个核心认知:提示工程首先是产品设计问题,其次才是技术问题
成熟的Agent团队里,提示词是产品经理基于业务规则、用户反馈来设计迭代的,工程师负责把规则准确落地,而不是自己拍脑袋定业务逻辑。

2.2 四个提示工程最佳实践

  1. 关键约束用大写强调NEVERMUST比“请不要”“建议”的约束力强很多,但别滥用,只留给真正的红线规则。
  2. XML + Markdown双层结构化。XML标签自带语义(比如<working_directory>),模型能快速识别;Markdown用来组织层级,兼顾可读性。两者配合效果远好于纯文本堆砌。
  3. 用SOP流程代替规则堆砌。就像新员工培训给流程图比给一百条零散规则有用,给Agent写标准操作流程,一步一步告诉它先做什么、再做什么、异常怎么处理,模型的稳定性会大幅提升。
  4. few-shot示例宁少勿滥。两三个覆盖边界场景的高质量示例,远好于十个大同小异的例子。而且示例一旦放进系统提示词,就要保持固定,不要动态检索替换,否则缓存会失效。

2.3 不能不提的安全风险:提示注入

提示工程设计得再完善,只要攻击者能往上下文里注入恶意指令,所有规则都可能被绕过。
尤其是Agent有工具调用能力,被注入后可能执行删文件、泄露数据等危险操作,比普通聊天机器人风险高得多。

上下文层面的基础防御手段:

  • 所有外部内容用<external_content>标签包裹,帮模型区分“指令”和“数据”
  • 过滤外部内容里的“忽略之前所有指令”这类常见注入模式
  • 永远不要把上下文层防御当成唯一防线,执行层的权限控制、沙盒隔离、高危操作人工审核必须配齐

3 动态扩展:Agent Skills,按需加载的能力模块

3.1 为什么不能把所有规则都塞进系统提示词

随着Agent覆盖的场景越来越多,客服退款规则、代码规范、文档格式要求……全塞进系统提示词会出两个问题:

  1. 浪费token:大部分内容和当前任务完全无关,白白占用上下文和成本
  2. 注意力稀释:无关信息太多,模型会漏掉真正关键的规则

于是就有了Skills机制:不是把所有手册一次性塞给Agent,而是先给一份能力目录,需要哪个领域的知识,再加载对应的完整内容。这就是渐进式披露的设计思想。

3.2 Skills的三层架构

层级 内容 位置 特点
第一层:元数据 技能名称、适用场景、不适用场景 常驻上下文前缀 只有几百token,用于路由判断
第二层:核心流程 完整的操作规范、SOP 按需加载,追加到对话末尾 任务需要时才加载
第三层:细则 技术细节、子文档 按需深入读取 只有需要细节时才展开

类比:就像你不会把公司所有部门的手册都堆在新员工桌上,而是先给总目录,需要哪个部门的规则再去取。

3.3 三种实现方式的权衡

Skills内容放在上下文的什么位置,直接决定了缓存效率和指令遵循效果:

实现方式 做法 优点 缺点
注入system消息 加载时改写系统提示词 指令遵循最强 每次加载都破坏前缀缓存,成本暴涨
作为tool result追加 完整内容作为工具返回结果放在对话中 完全不影响前缀缓存 模型对中间位置的指令遵循度会打折扣
路由+执行分离(生产推荐) 元数据常驻前缀用于判断,完整内容按需追加 兼顾缓存效率和指令遵循 实现复杂度最高

目前Claude Code等成熟产品都采用第三种方案,这是在性能和效果之间找到的最优平衡。

【实战提醒】
Skill的描述里一定要写清楚“Don’t use when”(什么场景别用),只写适用场景不写反例,路由准确率会明显下降,经常在不相关的任务上误触发。

4 状态感知:Agent状态栏,把隐式状态变成显式知识

4.1 为什么模型总“数不清”

一个非常经典的问题:你告诉Agent“同一个商家最多打3次电话”,结果它经常打第4次、第5次,陷入循环。
是模型笨吗?不是。本质原因是:注意力机制擅长检索,不擅长提炼
上下文里有原始的通话记录,但“已经打了几次”这个结论,模型每次都要从头扫一遍上下文去数,效率极低还容易数错。就像你书桌上堆了一堆单据,你不整理出一个总数,每次都得从头翻一遍数。

状态栏解决的就是这个问题:框架提前把状态算好,以非常低的token成本,直接把结论摆在模型面前,不用它自己去数。

4.2 状态栏的量化效果

在专门的基准测试ContextDistill-Bench上,状态栏的效果非常显著:

  • 对小模型:计数、规则归纳、状态跟踪类任务,准确率能提升40-54个百分点,2B的小模型甚至能追平不带状态栏的前沿大模型
  • 对大模型:准确率提升不明显,但思考token、延迟、成本能降一个数量级
  • 最本质的变化:不带状态栏,思考量随上下文变长而增长;带上状态栏,思考量基本恒定

4.3 三条工程经验,踩过坑才懂

  1. 状态栏用代码维护,绝对不要用LLM去总结。一个20行的正则函数就能统计出准确的调用次数;让大模型去读历史统计,等于把“扫上下文”的难题原封不动搬了个家,反而更不准。
  2. 状态栏覆盖的维度要提前想全。状态栏是有损投影,只覆盖了你预设的维度。如果后面业务问了状态栏没统计的问题,就会出问题。准备压缩原始上下文之前,先确认状态栏能覆盖所有后续会用到的信息。
  3. 把状态栏的准确率当成生产指标来盯。模型几乎会无条件相信状态栏里的内容,你写“打了3次”它就当真的是3次。状态栏写错了,最终结果一定错。状态信息越来自真实的外部观测,价值越高。

4.4 放对位置很重要

状态栏在API层面是作为user角色的消息插到上下文末尾的,绝对不能去改开头的system消息。
原因还是KV Cache:改system会让整个前缀缓存失效;追加到末尾,前面的缓存完全不受影响。而且放在末尾紧邻生成位置,模型的注意力权重最高,效果最好。

5 密度提升:上下文压缩,解决的不只是长度问题

很多人对压缩的理解停留在“上下文满了装不下,压一压”,其实压缩更深层的价值是提升信息密度,解决上下文腐化

5.1 上下文腐化:装得下,但找不到

长上下文有一个非常隐蔽的问题:明明窗口还没满,但模型突然找不到关键信息了,或者反复纠结已经解决的问题。
这就是Lost in the Middle等研究证实的现象:信息越在上下文中间,模型检索到的概率越低。上下文越长,中间的信息越容易被淹没。
就像你的书桌越堆越乱,虽然还有空位,但你想找的东西已经找不到了。这时候换更大的桌子(更大的上下文窗口)没用,定期整理才有用。

5.2 六种压缩策略对比

策略 做法 评价
无压缩 完整保留所有原始结果 窗口溢出直接失败,不推荐
个体摘要 每个结果单独压缩 信息碎片化,整体逻辑差
组合摘要 所有结果合并生成综合摘要 信息密度高,但超长输入需截断
上下文感知压缩 结合查询意图和已有信息做压缩 效果最佳,保留关键信息比例最高
带引用的感知压缩 压缩同时保留溯源链接 可验证,但token开销高
自适应窗口化 接近阈值才触发压缩 保留初期完整性,适合短任务

生产环境首推上下文感知压缩:同样压缩到1.3%的体积,它能精准保留人名、职位变动等核心决策信息,不会把关键内容压丢。

5.3 生产级分层压缩机制

成熟的Agent系统不会只用一种压缩方式,而是像垃圾分类一样分层处理,优先用成本最低的手段:

  1. 工具结果预算控制:大体积输出存磁盘,模型只看摘要
  2. 噪声直接删除:低价值、只看过一眼的内容直接移除,不做摘要
  3. API层微压缩:调用服务端接口移除指定的历史工具结果
  4. 归档式摘要:逐轮做结构化摘要,像git log一样保留每轮脉络
  5. 全量压缩:LLM驱动的完整压缩,作为最后手段,配熔断器避免反复失败死循环

5.4 压缩保留优先级(生产必配)

压缩最容易丢的不是细节,是早期的架构决策、约束的原因、失败的路径。一定要显式定义保留优先级:
✅ 必须完整保留:架构决策与关键约束、文件修改记录、验证结果、待办与回滚笔记
⚠️ 可保留结论:工具输出只保留pass/fail结果,删除详情
❌ 必须原样保留:UUID、hash、文件名、IP、端口号等标识符,错一位后续工具调用就会失效

6 架构优化:子Agent隔离,用隔离代替压缩

压缩是信息已经进了上下文之后做减法,还有一种更彻底的思路:让大体积的中间信息根本不进入主上下文,这就是子Agent隔离。

6.1 一个直观的对比

任务:在代码库里找处理支付回调的函数

  • 主Agent亲自搜:十几个文件、数万token的原始代码全部进入主上下文,找到目标后,这些代码就成了永久占着窗口的噪声,后面还要压缩清理
  • 委派给子Agent:子Agent在自己的上下文里读文件、搜关键词、分析候选函数,最后只把“函数位置+调用点”几百token的结论回传给主Agent。中间过程的数万token随子Agent一起被丢弃。

主上下文始终干干净净,没有噪声,KV缓存也完全不受影响。

6.2 隔离 vs 压缩 完整对比

维度 压缩 隔离
时机 事后补救,信息已经进入上下文 事前预防,信息根本不进入
有损性 有损,LLM蒸馏会丢信息 无损,原始信息在子Agent里完整保留
额外成本 需要额外LLM调用做压缩 需要子Agent的推理成本
缓存影响 替换点之后缓存失效 主上下文缓存完全不受影响
适用场景 信息已进入,必须精简 可预判会产生大量中间内容的任务

一句话总结:能用隔离解决的,就不要等到需要压缩的时候
现在主流的深度研究Agent、代码库分析Agent,基本都是这个架构:主Agent负责规划和汇总,子Agent负责具体的检索、阅读、分析,只把结论传回来。

7 落地路线:从入门到高阶的7步优化清单

很多人看完一堆技术点不知道从哪下手,这里给一个可直接执行的落地优先级:

入门级(先做这两步,见效最快)

  1. 固定静态前缀:把系统提示词、工具定义彻底固定下来,所有动态内容全部追加到对话末尾,先把KV Cache的性能红利吃满
  2. SOP化提示词:把零散规则改成流程化指令,结构化组织,先把Agent的稳定性提上来

进阶级

  1. 加上基础状态栏:先做工具调用计数、任务进度、当前时间这三个最基础的状态,解决循环、忘事的问题
  2. 能力模块化:把不同领域的规则拆成Skills,按需加载,不要全堆在系统提示词里
  3. 加上下文压缩:配置分层压缩机制和保留优先级,控制上下文长度和推理成本

高阶级

  1. 复杂任务子Agent化:大体积检索、深度分析类任务,拆成子Agent隔离执行,主Agent只做规划和汇总
  2. 建立监控指标:把缓存命中率、上下文膨胀速度、状态栏准确率当成核心生产指标来监控

8 结语:上下文工程的核心哲学

整篇文章看下来,你会发现所有技术都在围绕同一个核心原则:

静态的归静态,动态的归动态;主动提炼结构化信息,而不是让模型被动在海量内容里检索。

模型在变强,上下文窗口在变大,但这个原则不会变。
未来模型能力会越来越强,但上下文工程永远有价值——它本质上是用工程手段,在当前模型能力边界下,把信息利用效率最大化。
与其一味追更大的模型、更长的窗口,不如先把上下文组织好。很多时候,不是模型不够强,是你给它看的信息太乱了。

你们做Agent的时候遇到过最头疼的上下文问题是什么?是无限循环、任务跑偏还是推理成本太高?欢迎评论区交流。

#AI Agent #上下文工程 #KV Cache #提示工程 #Agent开发 #大模型工程化

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐