已经是最新一篇文章了!
已经是最后一篇文章了!

agent runtime lab · 2026 · 技术研究笔记

02. Prompt 长度、上下文增长与缓存成本

第二轮请求不是只发送新增内容;历史如何保留,决定了延迟、成本和可恢复性

从 Codex 与 Kimi 的两轮请求观察上下文怎样增长,以及缓存、裁剪、摘要和服务端状态引用分别解决什么问题

研究版 · 14 个章节 · 约 21 分钟 · 更新于 2026-07-27

版本 A · 中文 B · English
SCROLL

这次抓包里,Kimi 在第一次模型响应中发起两个 Bash 调用。CLI 执行命令后,又向模型发送了第二个请求。第二个请求包含原系统消息、环境、任务、模型给出的工具调用以及两条工具结果。

这条轨迹让我看到一个容易被最终回答掩盖的问题:Agent 每多运行一轮,上下文就会继续增长。

先区分四个经常混用的量

上下文窗口是单次模型调用能够处理的 Token 上限;Token 是模型处理文本时使用的离散单元,并不等于字符。输入 Token是某次调用实际送入模型的数量,累计输入量则是一次任务中所有模型调用的输入之和。

若固定规则和工具定义占 S + T,第 i 轮新增消息占 M_i,完整历史模式下,第 n 轮输入近似为:

input_n = S + T + M_1 + M_2 + ... + M_n

累计输入不是最后一轮的长度。早期内容会在后续请求中再次出现,所以长会话的累计量增长得更快。

本次抓包能确认什么

Codex 的成功轨迹记录了以下用量:

指标Token
输入33,348
其中缓存输入16,128
输出156
其中推理输出36

缓存输入约占本次输入的 48.4%。这个比例只表示服务端把多少输入计入缓存命中,不能直接解释成费用下降 48.4%,因为缓存 Token 的价格由服务端和具体模型决定。

Kimi 的两次 HTTP 请求正文分别约为 94 KB 和 96 KB。字节数可以证明第二次请求更大,却不能换算成 Token:JSON 转义、中文编码、字段名和分词方式都会改变换算关系。因此这里保留字节级观察,不把它包装成 Token 统计。

Claude Code 返回 403,Grok 返回 402。两者都没有进入第二轮,无法从本次数据判断其成功会话怎样增长。

四种控制方法解决的是不同问题

方法保留什么主要收益主要风险
前缀缓存不变的请求前缀减少重复计算前部动态字段会破坏命中
历史裁剪最近且仍相关的消息直接减少输入可能删掉约束或失败原因
会话摘要目标、事实、未完成项用较短文本代替历史摘要可能写错或遗漏
状态引用服务端保存的前序响应客户端少传历史调试依赖服务端状态

Prompt 缓存是对相同前缀的复用。它不会扩大上下文窗口,也不会阻止历史继续变长。摘要和裁剪会改变模型能看到的内容,属于信息管理;缓存不改变语义,属于计算复用。

Anthropic 的缓存文档把请求前缀按 toolssystemmessages 的顺序处理。稳定内容放在前面、每轮变化的工具结果放在后面,较容易保留相同前缀。Claude Prompt Caching

动态字段放在哪里很重要

日期、随机标识、工作目录和实时状态如果出现在请求开头,缓存会在较早位置分叉。后面的长系统规则即使没有变化,也可能无法沿用同一前缀。

我会把上下文按变化频率排列:

顺序内容更新频率
1平台规则、工具定义客户端升级时
2项目规则、技能说明切换项目时
3会话摘要、历史消息多轮对话中
4最新工具结果、当前输入每一轮

这不是指令优先级表。优先级描述冲突时听谁的,排列顺序描述怎样形成稳定前缀,两者要分别维护。

为什么累计成本会出现二次增长项

把每轮新增消息都记作固定长度 m,固定前缀记作 p。若客户端每轮重发完整历史,第 i 次模型调用的输入近似为 p + i × m。运行 n 轮之后,累计输入为:

total(n) = Σ(p + i × m)
         = n × p + m × n × (n + 1) / 2

式子中的第二项随 增长。它不表示模型的单次上下文窗口按平方增长,而是表示同一段早期历史在后续调用中被重复计入。十轮会话里,第一轮消息最多会进入十次请求,第十轮消息只进入一次。

这个模型还没有计入工具结果变长、模型输出长度变化和摘要操作,因此只能作为成本下界。它已经足以解释一个现象:减少一轮没有必要的模型往返,节省的不只是该轮输出,还会减少后续每一轮携带的历史。

前缀缓存改变的是重复前缀的计算价格和首 Token 延迟,并不删除上式中的 Token。Anthropic 当前文档明确区分了四种手段:工具搜索减少预先加载的工具定义,程序化工具调用减少中间往返,Prompt 缓存复用稳定前缀的计算,Context Editing 删除已经失去用途的旧工具结果。四者作用在不同位置,不能互相替代。Anthropic:Manage tool context

KV Cache 与跨请求 Prompt Cache 不是同一个缓存

KV Cache是模型推理时保存注意力层 Key、Value 张量的内存结构;它避免自回归生成每个新 Token 时重新计算此前 Token。Prompt Cache是服务端在请求之间复用某段相同前缀的机制。前者存在于一次推理过程内部,后者解决多次请求拥有相同前缀时的重复计算。

两者都依赖前缀一致性,但生命周期和可见指标不同。客户端一般无法直接读取模型实例里的 KV Cache,只能从服务端返回的缓存 Token、首 Token 延迟和计费字段判断跨请求缓存是否命中。把两者统称为“缓存”会导致两个错误判断:

错误判断实际情况
命中 Prompt Cache 后不再占用上下文窗口缓存前缀仍是本次上下文的一部分
裁剪历史会提高已有缓存的命中率裁剪改变了消息序列,可能形成新的前缀
请求 JSON 相同就一定命中缓存还受模型、工具配置、缓存时效和服务端策略影响

Anthropic 的当前缓存顺序是 tools → system → messages。修改工具定义会使后续系统与消息缓存一并失效;改变 tool_choice 只影响消息层;默认缓存有效期为五分钟,也可选择一小时。文档还给出一个容易遗漏的并发条件:缓存条目要到首个响应开始后才可用于其他请求,因此同时发出的第一批请求不能假定共享刚创建的缓存。Anthropic:Prompt caching

缓存命中率不能单独回答成本问题

设普通输入单价为 C_in,缓存写入单价为 C_write,缓存读取单价为 C_read。一次调用的输入成本应按三部分计算:

cost = uncached_tokens × C_in
     + cache_write_tokens × C_write
     + cache_read_tokens × C_read

缓存命中率相同的两条轨迹,成本仍可能不同。第一条轨迹可能反复读取一个短前缀,第二条轨迹可能读取一个很长的工具定义区;两者的缓存 Token 绝对量不同。缓存命中率也不能表示延迟改善,因为首 Token 延迟还包含排队、网络、服务端调度和未缓存后缀的预填充时间。

因此十轮实验至少同时报告四个量:缓存读取 Token 的绝对值、未缓存输入 Token、首 Token 延迟和累计费用。只报告“命中 80%”没有说明基数,也没有说明写入成本。

压缩本质上是一笔信息债务

摘要不是把 20 KB 文本无损编码成 2 KB,而是由模型选择哪些事实继续保留。这里的信息债务指被删除内容在当前轮看似无用,却可能在后续验证、恢复或审计时重新需要。

可以把轨迹中的内容按可压缩性分成四类:

内容默认处理原因
用户目标、禁止事项、批准范围原文保留一个否定词被改写就可能改变权限
外部系统回执、文件位置、对象标识结构化保留后续动作需要精确引用
大段搜索结果与文件正文外置并保存引用正文占用大,但仍需回查证据
已完成的中间解释可摘要后续主要需要结论与证据指针

摘要正确性不能只让生成摘要的同一个模型自评。更直接的办法是从压缩前轨迹抽取一组必须回答的问题,例如“用户是否允许联网”“已经修改了哪些文件”“最后一次工具错误是什么”,再让压缩后的上下文回答。若任一答案改变,摘要不能替换原轨迹。

这是一种保真度测试:保真度表示压缩后仍能恢复多少任务关键事实。它与语言是否通顺无关。一段读起来完整的摘要,仍可能遗漏一个路径、一个否定条件或一次尚未确认的写操作。

摘要至少要保留哪些事实

摘要如果只写“用户正在分析项目”,恢复后会缺少继续执行所需的目标、约束和进度。一个可用的会话摘要至少要包含:

  • 当前目标与明确禁止事项。
  • 已读取或修改的资源。
  • 已完成步骤及其证据。
  • 工具失败、错误类型与重试次数。
  • 尚未完成的步骤。
  • 需要用户确认的操作。

还要保存摘要基于哪一段历史生成。否则出现错误时,无法回到摘要前定位被遗漏的信息。

我会怎样验证缓存设计

同一任务连续执行五轮,每轮只改变末尾的一小段用户输入。日志同时记录输入 Token、缓存 Token、首 Token 延迟和总延迟。随后把当前时间移到系统块最前部,再运行同一组任务。

如果第二组的缓存命中位置提前失效,就能证明问题来自上下文排列,而不是模型回答内容。最终答案相同,并不代表运行成本相同。

十轮实验应该怎样记录

每轮至少记录请求序号、输入 Token、缓存读取 Token、输出 Token、首 Token 延迟、总延迟和本轮新增事件数。只比较最后一轮会漏掉前九轮重复发送的成本;只比较累计 Token 又无法定位从哪一轮开始失控。

实验还需要保存缓存边界之前的内容指纹和变化位置。这里的指纹用于比较同一次实验中的文本是否相同,不保存敏感正文。若第三轮在平台规则前加入时间戳,而缓存命中从第三轮开始下降,日志应能把这两个事件连接起来。

更完整的实验使用四组配置,每组重复相同的十轮任务:

组别历史策略缓存策略要隔离的变量
A完整历史关闭建立累计输入基线
B完整历史稳定前缀缓存测量计算复用
C删除已消费的工具结果稳定前缀缓存测量上下文编辑
D关键事实原文 + 其余内容摘要稳定前缀缓存测量有损压缩

四组必须使用相同模型版本、工具定义、任务输入和请求间隔。B 组若超过缓存有效期再运行,结果不能与有效期内的 A 组直接比较。D 组除了 Token 与延迟,还要运行前述保真度问题集;否则它可能以丢失约束为代价得到最低输入量。

实验判定也不能只看最终回答。若 Agent 在第六轮忘记已读取某文件,于第七轮重复调用工具,最后仍给出正确答案,结果正确性通过,过程效率和状态保持已经失败。轨迹需要分别标记这两类结果。

裁剪与摘要需要可逆的审计路径

裁剪策略不能只按“保留最近 N 条消息”工作。权限批准、失败原因和仍未完成的约束即使较早出现,也可能继续影响当前动作。每次删除历史时,应记录被删消息的标识、删除原因以及替代它的摘要版本。

摘要也不能直接覆盖上一版。一个可审计的实现会保留摘要输入范围、生成模型、生成时间和验证结果。若后续发现摘要把“不得联网”写成“可以联网”,运行时需要回到生成该摘要的历史,而不是只剩一段错误文字。

何时应该开始压缩

压缩阈值不宜只使用上下文窗口百分比。工具结果很大但后续不再使用时,可以提前外置;约束密集的短会话即使 Token 不多,也不适合机械摘要。更合适的触发条件同时考虑剩余窗口、预计下一轮输出、未完成工具数量和必须原样保留的证据量。

验收时至少设置两类任务:一类需要引用第一轮出现的约束,另一类只依赖最近结果。若策略只在第二类任务上表现正常,不能说明它适合长期 Agent 会话。

一个可以落地的触发器会先预留下一轮预算,再判断是否压缩:

usable = context_limit
       - reserved_model_output
       - reserved_tool_results
       - safety_margin

当当前输入接近 usable 时,运行时先外置体积大且已消费的工具结果,再删除可重新获取的内容,最后才摘要含有决策历史的消息。这个顺序体现的是可恢复性:能够从文件或对象存储重新读取的内容,比只存在于对话中的批准理由更适合先移出窗口。

压缩后还要记录“上下文世代”。上下文世代是一次压缩产生的新版本号。后续轨迹若出现行为偏移,可以比较压缩前后的事实集合,而不是面对一份已经被覆盖的聊天记录。

从当前资料还能得到一个新的工程结论

截至 2026 年 7 月,厂商已经把上下文压力拆成多个独立产品能力。Anthropic 同时提供 Prompt Caching、Context Editing、Tool Search 和 Programmatic Tool Calling;这说明“扩大上下文窗口”并不是长任务的唯一方向。窗口扩大只能延后容量耗尽,不能消除重复预填充、低价值工具结果、无关工具定义和错误摘要带来的问题。

对 Agent 运行时而言,更稳定的设计不是选择一种统一压缩算法,而是为每类内容保存三个属性:重新获取成本、丢失后的风险和预计复用距离。只有重新获取成本低、丢失风险低、短期内不再使用的内容,才适合优先移出上下文。

本次抓包可以证明 Kimi 的第二轮请求重发了历史,也可以证明 Codex 的一次成功请求含有缓存输入。它不能证明某个客户端在更长会话中采用哪种压缩策略。缓存有效期、服务端内部状态和模型实例上的 KV Cache 也无法通过单次 HTTPS 正文直接观察。这些结论必须依靠十轮用量数据、延迟分布和压缩前后问答测试继续验证。

版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:02. Prompt 长度、上下文增长与缓存成本

本文链接:https://www.sshipanoo.com/blog/ai/agent-runtime-lab/02-上下文增长与缓存成本/