提示缓存对工具调用 Agent 有用吗:多步循环中的缓存行为
本文解释提示缓存(prompt caching)在工具调用型 Agent 多步循环中的实际行为。重点分析当每一轮都把工具结果追加到消息历史时,缓存前缀是否还能命中、什么情况下会失效,并给出提升缓存命中率的工程实践。
工具调用型 Agent 通常在一个循环里反复调用模型:模型输出工具调用请求,你执行工具并把结果追加到消息历史,然后再次请求模型。这个过程中,提示词会随着每一轮不断增长。提示缓存是否还有用,取决于缓存机制如何识别「可复用的前缀」。
提示缓存的基本机制
主流 LLM API 的提示缓存通常基于前缀匹配:服务端把请求中开头一段连续不变的 token 序列缓存起来,下次请求如果前缀相同,就直接复用缓存中的计算结果,从而减少计算量、降低延迟和费用。
关键点在于「前缀」是严格按顺序匹配的。只要在某个位置插入了新内容,从该位置往后的所有 token 都不再属于原来的前缀。
Agent 循环中提示词如何增长
一个典型的多步 Agent 循环,消息序列大致是:
- 系统提示(system)
- 用户任务(user)
- 助手消息:工具调用请求(assistant, tool_calls)
- 工具结果(tool)
- 助手消息:下一轮工具调用请求
- 工具结果
- ……直到模型给出最终回答
每一轮你都是在末尾追加新的助手消息和工具结果。前几轮的内容完全不变,只有尾部在增长。
什么时候缓存仍然命中
如果缓存实现支持「按前缀分段」或「增量缓存」,那么在末尾追加内容不会破坏已有前缀。具体来说:
- 第 N 轮请求的前缀 = 第 N-1 轮请求的全部内容 + 新增部分。
- 只要服务端把第 N-1 轮那一段缓存下来,第 N 轮就能命中这段前缀。
- 新增的工具结果本身没有被缓存,但前面的 system、user、历史轮次都可以复用。
因此,在纯追加的循环里,缓存通常仍然有效,而且随着轮次增加,可复用的前缀越来越长,节省的绝对量可能更明显。
什么时候追加内容会破坏前缀
缓存失效往往不是因为追加,而是因为在中间插入或修改。常见情况:
- 修改系统提示:如果在某一轮临时往 system 里加一段指令,system 之后的全部内容前缀都变了,缓存大面积失效。
- 在历史中间插入消息:例如把工具结果重新排序、把摘要插到较早位置,都会破坏前缀。
- 改写较早的消息:对早期助手消息做格式化、裁剪或替换,同样会让后续 token 序列变化。
- 动态变化的前缀字段:时间戳、随机 ID、不断变化的「当前状态」如果放在消息开头,每轮都会让前缀不同。
- 工具结果的序列化不稳定:同一个工具返回的 JSON 如果键顺序、空格、换行每次不同,也会导致前缀 token 不一致。
对工具调用 Agent 的实践建议
- 保持前缀稳定:把不变的 system、工具定义、任务描述放在最前面,避免每轮改动。
- 只在末尾追加:新增的助手消息和工具结果一律追加到历史末尾,不要回插。
- 工具结果格式规范化:对 JSON 做稳定序列化(固定键顺序、统一缩进),减少无意义的 token 差异。
- 把动态内容后置:时间戳、轮次计数、临时状态等放在消息序列靠后的位置,减少对前缀的影响。
- 控制历史长度:当上下文接近上限需要裁剪时,优先从中间或较早位置移除,但这会破坏前缀;可以权衡是接受一次缓存重建,还是保留更长的稳定前缀。
- 关注缓存粒度:不同服务的缓存命中条件和最小缓存长度可能不同,实际效果以你所用的 API 文档和实测为准。
小结
在工具调用 Agent 的多步循环里,追加本身通常不会破坏缓存,因为缓存按前缀匹配,历史部分可以持续复用。真正让缓存失效的是对前缀区域的修改、插入和不稳定序列化。把系统提示、工具定义和早期历史保持稳定,把变化内容尽量后置并只在末尾追加,就能在长循环中更充分地利用提示缓存。