ARTICLE / AI Agent · Context Engineering
为什么 System Prompt 越稳定越好?从 KV Cache 理解 Agent 性能优化
System Prompt 里放一个当前时间戳,就可能让整段 Context 的缓存全部失效。本文从 KV Cache 的前缀复用原理出发,解释为什么越靠近开头的信息越要保持稳定、为什么 Tool Definitions 不该无意义重排,并给出 Minimal Stable Prefix 与「越稳定 → 越动态」的 Context 排列原则。
- 发布
- 阅读
- 约 6 分钟
- 作者
- Nicolas Leigh
上一篇我们提到,一个比较合理的 Agent Context 可以抽象成:
Context = Stable Prefix + Dynamic Trajectory + Current State
其中 System Prompt 和 Tool Definitions 应该尽量稳定,而用户消息、工具调用结果和运行状态会不断变化。
但这里有一个很自然的问题:
为什么一定要强调"稳定"?只是为了 Context 结构更整齐吗?
并不是。它还直接关系到 Agent 的两个核心指标:成本和延迟。
理解这一点,需要先认识一个非常重要的概念:KV Cache。
为什么大模型每次读取长 Context 都很贵?
假设一个 Agent 已经执行了几十轮任务,现在 Context 有 50,000 个 Token。下一轮用户只新增了一句话:
继续执行测试。
直觉上可能会觉得:只新增了几个 Token,模型应该只需要处理这几个 Token。
但实际并没有这么简单。Transformer 在处理 Context 时,需要对前面的 Token 进行计算,并保存中间结果。如果每次调用都从头重新计算整个 50,000 Token Context:
50K + 50K + 50K + ...
Agent 的成本和响应时间都会迅速上升,于是就需要缓存。
什么是 KV Cache?
Transformer 中的 Attention 会为 Token 计算一系列中间结果,其中包括 Key 和 Value,因此通常称为 KV Cache。
这里不需要深入数学公式。从 Agent 工程的角度,可以简单理解成:
模型把已经读过的前缀保存下来,下次遇到完全相同的前缀时,就不需要重新处理。
例如第一次请求 A B C D E F,模型把这段全算一遍:
A B C D E F
████████████
第二次请求变成 A B C D E F G H。因为前面的 A B C D E F 完全没有变化,所以理论上可以复用之前的计算,只处理新增的 G H:
A B C D E F | G H
████████████ | ████
cache | new
这就是前缀缓存最直观的价值。
问题在于:缓存要求前缀相同
缓存有一个很重要的前提:前面的 Token 必须保持一致。
假设原来的 Context 是 A B C D E F G H,下一轮却变成 A B X D E F G H。由于前缀在 X 这里发生了变化,后面的内容即使完全相同,也很难继续直接复用原来的前缀缓存。
这带来了一个非常重要的 Agent 设计原则:
越靠近 Context 开头的信息,越应该保持稳定。
因为前面一旦变化,影响的可能不是几个 Token,而是后面整段 Context 的缓存复用。
为什么 System Prompt 不应该放动态信息?
假设你写了这样一个 System Prompt:
You are a coding agent.
Current time:
2026-09-15 12:30:01
Current working directory:
/workspace/project
Please follow the rules below...
下一轮时间变成 2026-09-15 12:30:08,再下一轮变成 2026-09-15 12:30:15。表面看只是时间改变了几个字符。
但问题在于:它出现在 Context 最前面。于是本来应该稳定的 Prefix 每一轮都在变化。
更合理的设计是把高度动态的信息放到末尾:
System Prompt
Tool Definitions
Conversation History
Tool Results
...
Current Status
例如把状态信息放到最后:
<agent_status>
<current_time>2026-09-15 12:30</current_time>
<cwd>/workspace/project</cwd>
<todo>run tests</todo>
</agent_status>
这样前面的 System Prompt 和 Tool Definitions 仍然可以保持长期稳定。
Tool Definitions 也应该保持稳定
很多 Agent Framework 会动态提供工具。假设某一轮提供的工具列表是:
1. read_file
2. write_file
3. search_code
4. run_shell
下一轮却变成:
1. search_code
2. run_shell
3. read_file
4. write_file
虽然工具完全相同,只是顺序发生了变化,但从 Token 序列来看,它们已经不是相同的 Prefix。
所以如果没有特殊原因,Tool Definitions 的内容和顺序都应该尽量稳定。甚至不要为了所谓的"智能排序",每轮根据模型预测动态调整工具顺序——这种优化可能省不了多少模型推理,却破坏了更有价值的 Prefix Cache。
当然,如果工具集合本身确实需要动态变化(例如进入数据库阶段后才开放数据库写工具),那么动态 Tool Set 是合理的。关键不是"永远不能变化",而是不要无意义地变化。
Prompt Cache 和 KV Cache 是一回事吗?
在实际使用各种大模型 API 时,你还会经常看到另一个词:Prompt Caching。
它和 KV Cache 密切相关,但从工程视角上可以稍微区分:
- KV Cache —— 模型底层如何复用 Attention 中间结果。
- Prompt Cache —— 服务商如何把"相同 Prompt 前缀可复用"暴露成工程能力和计费优化。
对于 Agent 开发者来说,不一定需要关心底层具体如何实现。真正重要的是:相同前缀通常更容易被缓存和复用。
因此无论底层具体采用哪种缓存机制,稳定前缀都是一个非常有价值的设计原则。
这为什么对 Agent 特别重要?
普通 Chatbot 一次对话可能只有几轮。Agent 却可能运行 20 轮、50 轮甚至 100 轮,而每一轮都需要重新把大量 Context 提供给模型。
例如:
System Prompt 5K
Tool Definitions 4K
User Task 1K
Trajectory 30K
Current State 1K
--------------------------
Total 41K
如果前面的 System Prompt + Tools 每轮都能稳定复用,大量输入 Token 的处理成本就有机会被减少。但如果每轮都因为一个时间戳、随机 ID 或 Tool 顺序改变导致 Prefix changed,原本可以复用的大量 Context 就可能失去缓存价值。
所以对于 Agent 来说:Prompt Stability 本身就是一种性能优化。
哪些信息不应该放进 Stable Prefix?
一个非常实用的判断方法是:这个信息每轮会不会发生变化? 如果答案是"经常会",它通常不应该出现在 Stable Prefix。
例如:
- 当前时间
- 当前目录
- 剩余 Token
- 当前 TODO
- 工具调用次数
- 最近错误
- Git 状态
- 测试结果
- 当前文件
- 任务进度
这些更适合放到 Current State,或者最新的 Tool Result 中。
相反,以下信息通常比较适合作为 Stable Prefix:
- Agent 的身份
- 核心行为规范
- 安全规则
- 输出约束
- 固定 Tool Schema
- 通用工作流程
可以简单记成:Stable Prefix = 长期规则,Dynamic Context = 当前事实。
不要为了"个性化"频繁改 System Prompt
还有一种常见写法:
System Prompt:
You are helping user Nicolas.
Today is Monday.
The user is currently working on Go.
The last task was...
The current repository is...
乍看之下信息非常完整。但很多内容其实属于 Session State,而不是 System Prompt。
更好的做法是:
System Prompt: 固定身份、规则、行为边界
User / Memory Context: 用户长期偏好
Current State: 当前项目、当前目录、当前任务
这样不仅结构更清晰,也能够减少 System Prompt 的频繁变化。
这体现了一个很重要的原则:不要把所有"重要信息"都塞进 System Prompt。重要,不代表稳定。
稳定前缀并不是越大越好
说到这里,还要避免另一个误区:既然 Stable Prefix 可以缓存,是不是应该把大量信息全部放进去?
也不是。
假设一个 Agent 有 200 个工具。如果把所有工具永久放进 Context:
200 Tool Schemas
即使它们都很稳定,也会产生两个问题:
- Context 太大;
- 大量无关工具干扰模型。
所以真正理想的策略不是"尽可能大的 Stable Prefix",而是尽可能小 + 尽可能稳定 + 足够完成任务——也就是 Minimal Stable Prefix。
这也是为什么后面会出现 Agent Skills 和 Progressive Disclosure:不需要的能力,不要提前加载。
一个更合理的 Context 排列方式
综合起来,可以把 Agent Context 设计成:
System Prompt ← Stable Prefix(固定身份 / 规则 / SOP)
Tool Definitions ← Stable Prefix(固定工具 / 固定顺序)
─────────
User Task
Dynamic Trajectory: Assistant → Tool Call → Tool Result → ...
─────────
Current State ← Time / TODO / Environment / Recent Error
核心规律其实只有一句话:越稳定 → 越动态。这不仅是一种结构设计,也是一种性能设计。
结语
Agent 的性能优化并不只发生在模型选择、并发调用、网络请求、推理参数这些层面。Context 本身的组织方式,也会直接影响成本和延迟。
其中一个非常实用的原则就是:让 Context 的前缀尽可能稳定。
具体来说:
- System Prompt 不要塞入频繁变化的信息;
- Tool Definitions 不要无意义地重新排序;
- 时间、任务进度、错误状态等动态数据尽量放在后面。
最终可以把这个原则概括成:
Stable Prefix + Append-only Trajectory + Dynamic Tail
这样做的意义不只是让 Context 更整洁,更重要的是它能够让 Agent 更充分地利用缓存,从而在长任务中降低重复计算。