上下文工程:优化系统提示词,利用 KV Cache 降低 Token 成本

在开发智能差旅助手时,我遇到过一个容易被忽略的问题:系统提示词越来越长,但每轮对话都要重复发送和计算。

差旅助手中的每个子 Agent 都有自己的角色定义、业务规则、工具使用规范和输出格式。随着功能增加,单个系统提示词可能达到几千 Token。用户每多说一句话,这些稳定内容就会被重复处理一次。

后来在阅读 Manus 的上下文工程实践时,我重点关注了 KV Cache。结合项目实际情况,我对系统提示词中的动态内容做了拆分,让稳定的提示词前缀可以在多轮对话中持续命中缓存。

这篇文章记录这个问题是怎么出现的,以及我在 差旅助手中采用的处理方式。

KV Cache 为什么依赖稳定前缀

大模型生成文本时采用自回归方式。生成后面的 Token 时,模型需要参考前面已经出现的内容。

Transformer 会为历史 Token 计算 Key 和 Value,也就是常说的 KV。后续生成过程可以复用已经计算过的 KV,避免每次都从头计算。

因此,当两次请求的开头内容完全相同时,模型可以复用这部分计算结果。这类缓存也被称为前缀缓存。

前缀缓存有一个约束:

缓存内容必须从开头开始连续一致。前缀中间一旦发生变化,后面的缓存也会失效。

例如,两次请求只有当前时间不同:

Plain 复制代码
你是差旅助手。当前时间:14:32:07。请遵守以下业务规则......
你是差旅助手。当前时间:14:32:08。请遵守以下业务规则......

从语义上看,两段文字几乎没有区别。但从 Token 序列看,前缀已经在时间部分发生变化。时间后面的业务规则也就无法继续复用之前的缓存。

这也是 KV Cache 优化中最容易忽略的一点:动态内容放在长前缀中,会影响它后面的所有内容。

对话应用中,系统提示词是最值得优化的部分

对话类 Agent 的消息通常包含几部分:

Plain 复制代码
系统提示词
当前时间
历史消息
用户问题
工具调用结果

其中系统提示词通常最长,也最稳定。

以我开发的 智能差旅助手为例,行程规划、差旅管理、酒店预订和通用信息查询等子 Agent,都包含较长的系统提示词。这些提示词描述了:

  • Agent 的职责和边界;
  • 差旅业务规则;
  • 工具调用顺序;
  • 多轮对话处理方式;
  • 结果输出格式;
  • 异常和确认流程。

这些内容在同一个 Agent 的多轮会话中很少变化。如果每轮都能命中缓存,节省的是:

Plain 复制代码
系统提示词长度 × 对话轮数 × 用户数量

百炼提供了隐式缓存能力,平台会自动识别请求之间相同的前缀并复用。项目中的相关配置和注释按缓存命中部分约 20% 的价格进行估算。

因此,系统提示词成为上下文工程的主要优化对象。

问题从哪里开始:动态时间写进了系统提示词

差旅助手需要知道当前时间。

用户说"明天去杭州"或者"下周三返回"时,模型需要根据当前日期计算具体日期。时间信息不能直接删除。

最自然的做法,是在加载系统提示词时,把当前日期、星期和时间都填入模板:

Plain 复制代码
当前日期:2026-07-27
当前星期:星期一
当前时间:14:32:07

这种方式使用起来简单,但秒级时间会不断变化。

差旅助手早期的 Prompt 加载方式就支持直接注入当前时间。对于不需要缓存优化的场景,这种方式没有问题;但对于需要频繁多轮对话的 Agent,它会破坏系统提示词的稳定前缀。

时间位于提示词前部时,影响范围如下:

Plain 复制代码
当前时间发生变化
        ↓
系统提示词前缀发生变化
        ↓
时间后面的业务规则无法复用缓存
        ↓
每轮重新计算大量稳定内容

模型仍然需要时间,但时间不应该放在长系统提示词的前面。

我的处理方式:把稳定内容和动态内容分开

我最后采用的方案是:

  • 系统提示词只保留当天内稳定的内容;
  • 当前日期和星期继续写入系统提示词;
  • 秒级变化的当前时间单独作为一条消息注入;
  • 动态时间紧跟静态系统提示词之后。

消息结构变成:

Plain 复制代码
[静态系统提示词]
[动态时间消息]
[历史对话和用户消息]
[工具调用消息]

这样,第一条系统消息在一天内保持不变。动态时间虽然每秒变化,但它位于缓存前缀之后,不会影响前面的系统提示词。

系统提示词中会明确告诉模型:

Plain 复制代码
当前时间见消息列表中的动态时间注入。

模型仍然可以读取当前时间,只是时间不再参与静态提示词的构建。

日期和星期为什么仍然放在系统提示词中

日期和星期也属于动态信息,但它们一天只变化一次。

如果把它们也放到独立消息中,当然可以获得更长的缓存稳定时间。不过在差旅场景中,日期是系统提示词中比较重要的上下文,直接放在角色规则附近更容易让模型理解。

因此我采用了一个折中方案:

Plain 复制代码
当前日期:一天变化一次
当前星期:一天变化一次
当前时间:每秒变化,独立注入

这样同一个自然日内,系统提示词保持稳定;跨过零点后,日期发生变化,重新建立缓存。

这个取舍适合差旅助手。相比每秒破坏缓存,每天重新建立一次缓存的代价更容易接受。

动态时间应该在什么时候注入

动态时间需要在每轮模型推理前加入消息列表。

在 差旅助手中,我把这部分处理放在 Agent Hook 中。Hook 会在模型推理开始前检查当前消息:

  • 消息列表不能为空;
  • 第一条消息必须是系统消息;
  • 动态时间插入到第一条系统消息之后;
  • 后面的历史消息和工具消息保持原有顺序。

这样每轮消息的开头都保持一致:

Plain 复制代码
第一条:静态系统提示词
第二条:当前时间
后续:对话和工具消息

Hook 的执行优先级也需要靠前。项目中动态时间注入发生在上下文压缩等处理之前,这样后续处理拿到的是完整消息列表,动态时间的位置也不会被其他逻辑改变。

这里有一个边界条件:如果第一条消息不是系统消息,Hook 会跳过注入。这个判断用于保护异常消息结构,避免把时间插入到错误位置。

哪些 Agent 使用了这套方式

我在 差旅助手中逐步将需要高频多轮交互的 Agent 切换到静态系统提示词模式,包括:

  • 行程规划 Agent;
  • 差旅管理 Agent;
  • 预订 Agent;
  • 信息查询 Agent。

这些 Agent 的系统提示词较长,且会在一次会话中多次调用。缓存优化的收益比较明显。

项目中仍然存在一些旧的 Prompt 加载方式,用于兼容不需要缓存优化的场景。这里没有强行统一,因为不同 Agent 的调用频率、提示词长度和动态信息需求并不相同。

上下文工程需要结合调用场景处理,不是把所有消息都塞进同一种模板。

这套方案解决了什么问题

降低重复计算和输入成本

系统提示词不再因为秒级时间变化而频繁失去缓存。

长提示词在多轮对话中可以持续复用,尤其适合调用次数较多的行程规划和差旅管理场景。

降低首 Token 延迟

命中静态前缀后,模型不需要重新计算完整的系统提示词,首 Token 延迟也会相应降低。

保留时间理解能力

当前时间仍然会发送给模型。

用户提出下面这些问题时,模型仍然可以正常处理:

  • 明天从上海去杭州;
  • 后天返回;
  • 下周五提交申请;
  • 三天后入住酒店;
  • 本周最后一个工作日。

变化的只是时间在消息中的位置。

减少提示词维护时的隐性成本

系统提示词中不再混入频繁变化的内容,排查缓存命中率时也更容易定位问题。

如果缓存命中率突然下降,可以优先检查:

  • 系统提示词是否新增动态字段;
  • 工具定义是否每轮变化;
  • 消息顺序是否发生改变;
  • 是否有请求级内容被拼接到了系统提示词前部。

还需要注意哪些问题

随机内容也会破坏前缀

除了时间,以下内容也不适合放在静态系统提示词中:

  • 请求 ID;
  • 随机字符串;
  • 当前用户动态信息;
  • 每轮变化的工具列表;
  • 动态生成的调试信息;
  • 不稳定顺序的 JSON 内容。

只要它们位于长提示词前部,就可能影响后续缓存。

消息顺序需要保持稳定

即使文本内容相同,消息顺序发生变化,也可能导致 Token 前缀不同。

因此,消息构建逻辑需要保持稳定:

Plain 复制代码
静态系统提示词
→ 动态时间
→ 历史消息
→ 当前问题
→ 工具调用结果

缓存数据需要结合指标判断

缓存优化不能只看设计是否合理,还要观察实际调用数据。

我会重点关注:

  • 缓存命中的 Token 数;
  • 总输入 Token;
  • 首 Token 延迟;
  • 单轮调用成本;
  • 不同 Agent 的缓存命中情况。

在 差旅助手的调用观测中,隐式缓存命中的 Token 总量基本保持在输入 Token 的 50% 左右。这个数据会随着 Prompt 长度、对话轮次和平台缓存策略变化,不能脱离测试条件单独解读。

总结

在 Agent 系统中,系统提示词往往是最稳定、也最容易重复计算的一部分。

时间、请求 ID、用户临时状态等动态信息如果直接写进长系统提示词,会破坏 KV Cache 的前缀匹配。

我在 差旅助手中的处理方式是:

Plain 复制代码
稳定规则放在系统提示词中
动态时间放在独立消息中
消息位置固定
同一天内保持静态前缀不变

这套方案没有减少模型需要知道的信息,只调整了信息的组织方式。

上下文工程的一个实用判断是:

让稳定内容尽可能早出现,让动态内容尽可能晚出现。

延伸阅读:Manus 的上下文工程实践:构建 Manus 的经验

相关推荐
感谢地心引力1 天前
我用 Doubao-Seed-2.1-pro 做了一个深度融入 AI 功能的本地知识库软件
ai·开源·seed·markdown·豆包
阿昌喜欢吃黄桃1 天前
提示词工程:User Prompt 与 System Prompt
ai·prompt·提示词·提示词工程
AI产品测评官1 天前
海内外AI招聘工具分赛道横向对比:五大品类的技术路线与选型参考
人工智能·ai·求职招聘
俊哥V1 天前
每日 AI 研究简报 · 2026-09-21
人工智能·ai
代码方舟1 天前
零信任架构实战:基于天远手机在网状态V即时版构建自动化通信分发网关
人工智能·ai·工具分享
MicrosoftReactor1 天前
技术速递|GitHub Copilot App 入门指南:使用 Diff、终端和浏览器
ai·copilot·agent
TechEdu2026061 天前
[人工智能]人工智能时代的零日漏洞与零日攻击
人工智能·网络安全·ai·信息安全
程序员无隅1 天前
Pi Agent Loop 完整流程源码解析:从 Agent.prompt() 到 agent_end
ai