青雲的博客
把 Orca 的 DeepSeek 缓存命中率打到 99%,我做了九轮优化

Article

把 Orca 的 DeepSeek 缓存命中率打到 99%,我做了九轮优化

· 33 分钟阅读

前言

这几天我在 Orca 里做了一轮比较硬的优化:DeepSeek 上下文缓存

这件事一开始看起来很简单:既然 DeepSeek 有 prefix cache,那就让每轮请求前缀尽量稳定。系统提示词别乱变,工具 schema 别乱变,对话历史 append-only,不要动老消息。

道理都对。

但真正做下来,我最大的感受是:缓存优化不是靠读代码“想明白”的,它必须拿真实 API 一轮一轮去撞。

因为这里面有太多看起来合理、实际没效果的判断:

  • 你以为改了,其实没改到真实 wire prompt。
  • 你以为只是本地 token 预算问题,实际 API 请求里还注入了 summary、tools、volatile overlay。
  • 你以为 remote summary 可以靠稳定 prompt 命中缓存,真实 API 返回还是 0%。
  • 你以为压缩是在省 token,结果一不小心变成压缩风暴,每轮都压。

这篇文章不是一个“胜利总结”。更准确地说,它是一次工程排雷记录。

Orca 最终在真实 API 评测里,压缩后的主链路能稳定到 99% 左右缓存命中。但这个结果不是线性推进出来的,中间有几轮其实是误判,我后来也重新认了账。

背景:Agent 为什么特别吃缓存

普通聊天应用里,缓存命中当然也有价值,但 Agent 对它更敏感。

因为 Agent 不是一问一答。一个稍微复杂点的任务,链路通常是:

用户任务
  -> 模型规划
  -> 工具调用
  -> 工具结果回灌
  -> 模型继续推理
  -> 再调用工具
  -> 再回灌
  -> ...

每一轮都会把历史上下文再发一遍。

如果 prefix cache 命中,旧历史的成本和延迟都会下降;如果 miss,Agent loop 跑得越久,浪费越大。

所以缓存优化的目标不是“某一轮少几个 token”,而是让多轮循环里的历史前缀尽可能稳定。真正影响成本的,不是第一轮,而是第十轮、第二十轮、压缩之后的下一轮。

先把评测口径说清楚

这次优化里,有一个口径问题非常重要。

Orca 的 usage.updated 事件只统计主链路里的 streaming 模型调用,不统计 compaction summary 那次非流式 API 调用。也就是说,如果只看 Orca 自己吐出来的 usage,压缩路径里最贵、最不命中的那条 summary 调用会被漏掉。

所以做基线时,我用本地 DeepSeek proxy 记录原始 response usage,再用 prompt_cache_hit_tokens / prompt_tokens 算缓存率。

最有代表性的基线样本是一个“完成且触发压缩”的任务:读 tests/workflow_script_contract.rs,触发 tool loop,然后发生 context.collapsed + context.summary

原始 API 调用缓存率是这样:

调用类型prompt tokenscached缓存率
1正常首轮4390243255.40%
2tool result 后6591243236.90%
3compaction summary227300.00%
4summary 后继续6748243236.04%
5后续稳定轮6871678498.73%

总计包含 summary 调用:14080 / 26873 = 52.39%

如果只看 Orca usage.updated 可见的 streaming 调用:14080 / 24600 = 57.24%

这两个数字差得不算离谱,但方向很关键:一旦进入压缩路径,summary 调用会把真实总缓存率往下拉。

我还跑过一个更重的压力样本,放大 tool output 到 20k tokens 后,模型没有按 prompt 收束,最后跑成 23 个 streaming 调用 + 12 个 summary 调用,我中止了。这个样本不适合作为“成功任务”的结论,但很能说明风险:

全部 API 调用:86272 / 182312 = 47.32%
仅 streaming 模型调用:86272 / 169586 = 50.87%
summary 调用:0 / 12726 = 0%
history 里记录了 19 次 context.collapsed,其中 12 次有 context.summary

所以后面所有优化,不能只问“普通 turn 是否命中”,还要问:

  • 压缩路径是否触发?
  • summary 调用算进来了没有?
  • 压缩后下一轮能不能恢复高命中?
  • 会不会进入 compaction storm?

第一处真正的问题:动态信息写进了 system

最早我盯上的问题是 budget hint。

Orca 当时会把剩余上下文预算提示 append 到 system message 的末尾,大概像这样:

<system prompt 静态正文>

[Context budget: ... remaining tokens ...]

这里有一个我一开始说错的点:它不是插在第一条 message 的开头,而是追加到 system message 的末尾。

但这并不改变结论,反而更说明问题。

DeepSeek 的 prefix cache 看的是请求前缀。system message 是整段请求最前面的部分。只要 budget hint 每轮变化,system message 从 hint 位置开始就不一样了,后面整段多轮历史也跟着 miss。

这是真正的大头。

不是“一个小提示本身浪费了多少 token”,而是它把后面所有历史的缓存资格都弄没了。

所以第一轮真正的优化很明确:不要把 volatile 信息放进 system prompt。

这一步是实打实的生产代码优化。

后来我看了 Reasonix 的架构文档,它的原则更激进:immutable prefix 一旦建立就不要动,append-only log 只能追加,volatile scratch 不进入缓存前缀。这个思路和我们后面一路撞出来的结论基本一致。

九轮优化:不是一条直线

这里我先把九轮摊开说。因为如果只写最后结果,会把过程写得太顺,反而不真实。

轮次当时做的事后来怎么看
第 1 轮去掉 system 里的动态 budget hint,改成本地展示 context usage真优化,压缩/多轮样本从约 50% 拉到约 75%
第 2 轮把 plan/skill/goal 这类 volatile 信息从历史 message 中剥离普通 agent loop 有效,非压缩样本到 92.18%;但压缩样本仍被 summary 0% 命中拉垮
第 3 轮Rolling Summary:remote summary 只吃 previous summary + 新 delta,不再吃全量 collapsed 历史有效,summary 请求明显变小
第 4 轮SummaryState:baseline + deltas,渲染成 append-only summary 区部分有效,但真实 resume 暴露“summary + 原文重复”问题
第 5 轮Local Extractive Compaction:remote summary 前先对中等 tool output 做确定性 head/tail 摘要有效但范围比预想窄,超大输出被 micro compact 先处理了
第 6 轮Summary Hash Cache:对 previous summary + delta 做内容寻址,本地命中则跳过 remote summary有效,重复同一段 summary 第二次不打 API
第 7 轮单一 deterministic summary renderer + telemetry,试图统一 micro/extractive 两条路径方向对,但第一版策略太保守,真实 API 下 huge 场景反而更贵
第 8 轮收紧 summary renderer:每工具硬字节预算,更激进 tier;同时优化 pipe-eval-stdin 做真实碰撞renderer 达标,但真实碰撞又打出 compaction storm 和 stdin framing 问题
第 9 轮wire-equivalent compaction 判断、60% hysteresis、user/stdin extractive render、resume 持久化 inherited summary_state真正把压缩风暴和 summary_state 漂移压住,最后主链路稳定下来

这里面最重要的教训是:不是每一轮“看起来合理”的改动都会在真实 API 上变好。

第 7 轮就是一个典型例子。它解决了结构问题:summary renderer 不再被 micro compaction 遮蔽,也有了完整 telemetry。但真实 API 一测,huge 场景比旧路径更贵。因为旧路径虽然“不优雅”,但 micro compact 已经把输入压得很短;新 renderer 从原始内容渲染,证据保留更多,成本也上去了。

所以第 8 轮不是继续讲架构,而是加硬预算:medium 最多约 900 bytes,huge 最多约 700 bytes,行/字符提取之后再做字符边界安全裁剪。真实 API 下:

summary_huge_old_micro_masked          prompt=235
summary_huge_current_original_renderer prompt=235
summary_mid_old_without_renderer       prompt=2100
summary_mid_current_renderer           prompt=307
normal_turn_2                          hit=99.2%

这才算把第 7 轮的方向补成了可落地的优化。

第二处问题:动态状态不能混进历史消息

budget hint 之后,另一个问题藏在会话结构里。

Orca 有一些运行时状态,比如:

  • 当前 plan
  • 当前 goal
  • skill context

这些东西对模型有用,但它们会变化。

早期做法是把它们作为特殊 message 放进 conversation.messages,更新时先删除旧块,再 push 新块。

问题就在这里。

假设某一轮历史是:

system
history A
plan_v1
user N
assistant N
tool result N

下一次 plan 更新后,如果把 plan_v1 从中间删掉,再把 plan_v2 push 到末尾,就会变成:

system
history A
user N
assistant N
tool result N
plan_v2

plan_v1 原来的位置开始,wire prompt 已经完全不一样了。

这不是“plan 自己 miss 一点”的问题,而是它后面的整段历史都被重新排列了。

最后的处理是:这些动态状态不再作为历史 message 的一部分保存。

它们进入 volatile state,在真正发请求时,渲染成一个 overlay,挂到当前轮尾部。这样旧历史保持 append-only,动态状态只影响靠后的部分。

这轮真实评测里,普通 agent loop 覆盖了 update_plan + read Cargo.toml,没有触发 summary:

calls: 3
prompt_tokens: 14,441
cache_hit: 13,312
cache_miss: 1,129
hit_rate: 92.18%

但压缩样本仍然很差:

summary call:            prompt=18,980 hit=0     hit_rate=0%
main call after summary: prompt=4,478  hit=2,432 hit_rate=54.31%
total including summary: prompt=23,458 hit=2,432 hit_rate=10.37%

这说明第二轮方向是对的,但它只解决普通 turn 的历史污染。只要进入 remote summary,真实总缓存率还是会被 0% 命中的 summary 调用拉垮。

这一步让我重新确认了一件事:conversation 的内存结构不是缓存真相,发给模型的 wire prompt 才是缓存真相。

你可以在内存里怎么组织都行,但只要最终 wire 顺序动了,缓存就会碎。

第三处问题:本地 token 预算不等于真实请求预算

后面评测时又撞到一个问题:明明本地估算没有超上下文,真实 API 调用却还是触发压缩,甚至出现压缩后马上又压缩的情况。

原因是本地预算一开始算得不够“像真实请求”。

真实发给 DeepSeek 的不只是 conversation.messages,还包括:

  • system prompt
  • injected summary messages
  • volatile overlay
  • tools schema
  • message 包装开销

如果本地只数 conversation messages,就会低估请求体。

低估的结果很糟糕:系统以为“不需要压缩”,但真实请求已经接近上限;或者压缩后以为“已经安全”,下一轮又被真实 wire prompt 打爆,于是继续压缩。

这就是压缩风暴。

第 8 轮真实碰撞把这个问题打得很明显。低阈值触发 compaction 后,后续几轮是这样:

turn2-resume-compact  input=20713 cache=12416 hit=59.9%
turn3-after-compact   input=20787 cache=2432  hit=11.7%
turn4-continue        input=20881 cache=2432  hit=11.6%
turn5-continue        input=20998 cache=2432  hit=11.6%

同时 remote summary 每轮都是 miss:

orca.remote_summary requested=1 purpose=delta cache_hit=0 cache_miss=1

这组数据的关键不是“某一轮命中率低”,而是:压缩后仍然发出 20k+ input,超过我设置的 auto_compact_token_limit=18000。也就是说,本地以为压完了,真实 wire prompt 仍然超阈值,于是下一轮继续压。

这就是 compaction storm。

所以后面加了 needs_compaction_wire 这类 wire 等价判断:按真实请求形态估 token,而不是按内存结构估 token。

这一步不是为了追求 token 计算绝对精准,而是为了让本地判断和真实 API 行为同向。

Agent 系统里这种差一点的估算很危险。因为它不是单次误差,而是会进入 loop,变成持续错误。

第 9 轮还加了一个很重要的东西:hysteresis。压缩不能只压到“刚好低于阈值”,否则一两条新消息又把它顶回去。后面改成压到目标窗口的 60% 左右,让压缩后留出足够缓冲。

第四处问题:压缩不是删历史,而是维护协议

一开始说“上下文压缩”,很容易想到把旧消息总结一下,然后替换掉。

但对 prefix cache 来说,压缩不是简单的“减少 token”。它其实是在改变后续所有请求的前缀结构。

如果每次压缩都重写历史、重排消息、让 summary 位置漂移,那缓存还是会碎。

后面 Orca 改成了更明确的 summary state:

  • baseline summary:长期稳定的摘要基线
  • delta summaries:后续压缩产生的增量摘要
  • kept messages:最近需要保留的原始消息

这样压缩之后的请求结构更像:

system
summary baseline
summary delta 1
summary delta 2
kept recent messages
current turn

压缩不再是“随手把历史改短”,而是维护一个可恢复、可持久化、可继续追加的 summary 状态。

这里还修了一个很现实的问题:resume / continue 后必须把继承来的 summary state 写回新 transcript。

否则这次进程里看起来 summary 还在,下一次从历史恢复时 summary shape 丢了,又会重新触发压缩。缓存问题就会从“上下文策略问题”变成“会话持久化问题”。

第三轮和第四轮一起评测时,暴露过一个很典型的问题。

当时 C 场景里第二次压缩后,主请求确实渲染成了:

[Summary baseline]
[Summary update 1]
kept tail
current user

history 里也写出了 summary_state:baseline 长度 620,delta 数量 1,delta 长度 392。

但 D 场景 resume 后继续请求时,虽然恢复了 baseline + delta,原始被压缩的大段历史也从 history message log 里回来了。也就是说,请求里出现了“summary + 原文重复”。

这件事提醒我:summary_state 持久化对了,不代表 resume 语义就对了。

压缩不是只在当前进程里成立,它必须跨 transcript、跨 --continue、跨 TUI resume 都成立。否则长期任务会把已经压缩过的原文再带回来,既拖垮上下文窗口,也拖垮缓存前缀。

最麻烦的一条支路:remote summary

主链路逐渐稳定之后,remote summary 成了新的问题。

所谓 remote summary,就是把要折叠的历史片段交给模型总结。这个动作本身也是一次 API 调用,也有 input tokens,也可能命中缓存。

我一开始的直觉是:那就让 summary prompt 也稳定一点,争取它也命中缓存。

真实 API 给了很直接的反馈:没用。

优化前,remote summary telemetry 是这样:

orca.remote_summary_usage purpose=delta input_tokens=257 output_tokens=192 cache_tokens=0 cache_hit_ratio=0.0000
orca.remote_summary_usage purpose=delta input_tokens=581 output_tokens=422 cache_tokens=0 cache_hit_ratio=0.0000

后来尝试把 summary system prompt 做得更长、更稳定,结果是:

orca.remote_summary_usage purpose=delta input_tokens=462 output_tokens=211 cache_tokens=0 cache_hit_ratio=0.0000
orca.remote_summary_usage purpose=delta input_tokens=849 output_tokens=471 cache_tokens=0 cache_hit_ratio=0.0000

token 更多了,命中还是 0。

这就是典型的“看起来像优化,真实 API 不认”。

所以最后的方案不是继续调 remote summary prompt,而是换一个问题问法:

有没有必要每次都 remote summary?

答案是:不一定。

这条路中间经历了几次拆分。

第一步是 Rolling Summary。以前 remote summary 更像是“把当前要折叠的历史重新总结一遍”,后来改成只喂 previous summary + 新折叠 delta。这样第一次 summary 可能还大,但第二次以后只处理新增片段。真实评测里,第一次 summary call 是 16,272 prompt tokens;第二次已有 baseline 后降到 11,764,而且没有把 previous summary 再塞进去。

第二步是 SummaryState。把 summary 拆成 baseline + deltas 后,主上下文里的 summary 区域可以 append-only。baseline 稳定,delta 追加,只有 delta 太多或太长时才低频 rebuild baseline。

第三步是 Local Extractive Compaction + Summary Hash Cache。

Local Extractive Compaction 先对 collapsed delta 里的 tool output 做确定性压缩:大输出保留 head/tail 和尺寸标记,自然语言对话原样保留。这样 remote summary 看到的 delta 更小,而且同一段输入渲染出来永远一样。

Summary Hash Cache 则对 previous_summary + delta_text 做内容寻址。第一次 miss 才打 remote summary,第二次同输入直接从本地 cache 读。

这轮验证里,我第一次让 compact_with_summary(DeepSeek) 真打远程 summary,然后把 cache 文件内容改成 sentinel,第二次同输入返回了 sentinel:

first_kind=remote
second_kind=remote
cache_files_after_first=1
cache_files_after_second=1
second_summary=SENTINEL_REAL_API_CACHE

这说明产品代码里本地 summary cache 确实能跳过重复远程 summary。

对于中等大小的 delta,我们可以做确定性的 extractive summary delta:

  • 保留消息角色和关键边界
  • 大块 stdin / tool output / assistant content 做 head-tail 提取
  • 记录 omitted bytes / chars 等元信息
  • 去掉 assistant reasoning content
  • 输出稳定、可测试、可缓存

这不是让本地 summary 取代模型理解一切。它只是处理一类很常见的场景:旧内容太长,但并不需要模型重新“理解成文学摘要”,只需要把证据压到一个稳定、可继续携带的形态。

这里还有一个弯路。

第 7 轮我把 renderer 统一成单入口后,离线 harness 很好看:mid-sized tool output 有 76% token 降幅,huge output 有 96.3% 降幅。但真实 API 评测发现,它相对“原始 huge output”是省了,可相对旧的 micro-compacted 路径反而更贵。

当时真实 API 是这样:

summary_mid_old_without_renderer       prompt=3932
summary_mid_current_renderer           prompt=3848

summary_huge_old_micro_masked          prompt=2252
summary_huge_current_original_renderer prompt=5644

所以第 8 轮才加了硬预算。目标不是“相对原文省”,而是“不能输给旧路径”。这点很重要,优化不能只找一个更容易赢的参照物。

第九轮之后,最终评测里 remote summary telemetry 为空。

也就是说,那两次中等 delta 压缩没有再调用 remote summary。

这个结果比“remote summary 命中缓存”更好:直接没有这次调用。

最终真实 API 评测

最后一轮评测不是 mock,也不是只跑单轮。它用真实 DeepSeek API,构造了三段较大的输入,通过 stdin 喂给 Orca,并且强制进入压缩场景。

这组结果读的时候也要注意口径:它来自第九轮后的 pipe-eval-stdin 多进程 --continue 场景,主调用读 Orca 的 usage.updated;同时开了 ORCA_SUMMARY_DEBUG 看 remote summary telemetry。最终 telemetry 为空,说明这组中等 delta 压缩没有再产生 remote summary 调用,所以这里不存在“summary 调用被 usage.updated 漏算”的问题。

评测流程大概是:

turn1: 注入 alpha 大块数据
turn2: 注入 beta 大块数据,触发第一次压缩
turn3: 压缩后继续对话
turn4: 稳定性检查
turn5: 注入 gamma 大块数据,触发第二次压缩
turn6: 第二次压缩后继续对话

最后一次有效评测结果:

回合input tokenscache tokens命中率说明
turn1-seed1839800.0%冷启动,预期为 0
turn2-trigger-compact1858500.0%新增大块输入并触发首次压缩,预期较低
turn3-after-compact186121856099.7%第一次压缩后,主链路基本全命中
turn4-stability186871856099.3%稳定续跑,命中保持
turn5-trigger-second-compact18906243212.9%第二次注入大块输入并触发压缩,只有较短稳定前缀命中
turn6-after-second-compact189331881699.4%第二次压缩后,主链路再次恢复高命中

原始输出里是这样:

turn1-seed                   input=18398 cache=0     hit=0.0%
turn2-trigger-compact        input=18585 cache=0     hit=0.0%
turn3-after-compact          input=18612 cache=18560 hit=99.7%
turn4-stability              input=18687 cache=18560 hit=99.3%
turn5-trigger-second-compact input=18906 cache=2432  hit=12.9%
turn6-after-second-compact   input=18933 cache=18816 hit=99.4%

这里不要误读 turn2 和 turn5。

它们是主动注入新大块数据、触发压缩的回合,本来就不是最适合命中的回合。真正要看的是压缩之后的下一轮:turn3 和 turn6。

这两个回合都到了 99% 左右。

这说明压缩后的新上下文结构稳定了,后续主链路不再因为 summary、volatile 状态、预算 hint 或历史重排而整体 miss。

这组数据还有一个隐含结论:真正稳定的不是“永远 99%”。触发压缩、注入新大块数据的那一轮,命中率本来就会低。关键是压缩后的下一轮能不能恢复。如果恢复不了,就说明 summary 区、tail、volatile overlay 或预算判断仍然在扰动前缀。

这次优化真正得到的原则

如果把这九轮收敛成几条原则,我会这么写。

1. system prompt 只能放真正不变的东西

system prompt 是缓存前缀的地基。

剩余预算、当前 plan、临时状态、动态提示,不管看起来多小,都不应该写进去。

小的动态文本会破坏大的历史缓存。

2. append-only 比“整理得更干净”重要

对话历史不要为了看起来整齐而重排。

删掉中间一块再 push 到末尾,在数据结构里可能很自然,但在 prefix cache 眼里就是灾难。

Agent 的 conversation log 应该更像事件流:旧内容一旦进入历史,就尽量不要原地变形。

3. 本地结构不重要,wire prompt 才重要

缓存命中看的是发给模型的字节序列,不是你内存里的抽象对象。

所以预算、测试、snapshot 都要尽量对齐 wire prompt。

这也是为什么后面要把 summary messages、volatile overlay、tools schema 都算进预算里。

4. 压缩是协议,不是字符串处理

压缩后的上下文形态必须稳定、可恢复、可持久化。

如果压缩只是“这次临时把历史截一下”,那 resume、continue、第二次 compact 都会把问题重新引出来。

summary state 其实就是一份协议:旧历史如何被代表,新 delta 如何追加,什么时候需要重建 baseline。

5. 不要执着于优化一条不值得优化的调用

remote summary 一直 0% cache 命中时,最容易犯的错是继续调 prompt。

但真实评测已经说明这条路不划算。

最后真正有效的是减少 remote summary 发生的次数:中等 delta 本地确定性渲染,巨大 delta 才交给模型。

有时候最好的缓存优化,是让那次请求根本不要发生。

6. 真实 API 是唯一裁判

这次最重要的变化不是某个函数,而是评测方式。

后来我专门做了 pipe-eval-stdin,让真实碰撞变简单:可以把大块输入直接 pipe 给 orca exec,再用脚本连续跑 resume / compact / stability 场景。

这让优化从“我觉得应该会命中”变成了:

你说得对,但 cache_tokens 是多少?

这句话很朴素,但救了很多时间。

这里还有一个细节:stdin 评测也必须 byte-stable。

第 8 轮做过一个单变量实验:

A-repeat-base-2 input=12521 cache=12416 hit=99.2%
B-tail-diff     input=12521 cache=12416 hit=99.2%
C-head-diff     input=12494 cache=4224  hit=33.8%
D-prompt-diff   input=12526 cache=4224  hit=33.7%
E-full-stdin    input=12515 cache=4224  hit=33.8%

尾部变化很安全;只要 user/prompt/stdin framing 的前部变了,基本只剩 system + tools 命中。

所以评测工具本身也会影响缓存结论。pipe-eval-stdin 的价值不只是“喂大输入方便”,还在于它能把 prompt 参数和 stdin framing 固定下来,让后续碰撞更像真实、也更可比。

现在能不能先告一段落?

我觉得可以。

不是说缓存这件事已经到终点,而是这条主链路已经从“玄学”变成了“可解释、可复现、可继续优化”的状态。

现在的结论是:

  • 普通多轮续跑的历史前缀已经稳定。
  • 压缩后的下一轮能回到 99% 左右命中。
  • 中等 delta 不再触发 remote summary。
  • remote summary 的 0% cache 问题不再是主链路上的持续成本。
  • 压缩风暴和 summary state 继承丢失已经被真实场景打过。

后面当然还有可以做的事,比如更细的线上分布统计、更复杂的工具调用场景、更极端的大文件输入、更严格的 wire snapshot 回归测试。但这些已经不是“主链路还没站稳”的问题,而是继续扩大覆盖面的工程工作。

对我来说,这次优化真正改变的不是某个命中率数字,而是 Orca 的开发方法。

以前我会先从代码里推理:这里应该稳定,那里应该 append-only,这样应该命中。

现在我更愿意反过来:

先设计一个真实碰撞场景。 用 API 跑。 拿 cache_tokens 说话。 再回代码里找为什么。

Agent 系统很多问题都是这样。你可以从架构上建立方向感,但最后一定要回到真实调用。因为模型服务、上下文序列化、工具注入、压缩策略、会话恢复,这些东西合在一起之后,行为往往会比单看代码复杂得多。

缓存优化尤其如此。

它不是“少发一点 token”的小修小补,而是一件关于上下文结构、运行时状态、历史协议和真实 API 行为的系统工程。

但缓存也只是 Agent 优化里的一个点。

它解决的是“同样的上下文,能不能更稳定、更便宜、更快地被模型复用”。这很重要,尤其对长任务和高频 Agent loop 来说,缓存命中率就是成本、延迟和可持续性的地基。

可 Agent 的最终效果不只由缓存决定。缓存打稳之后,后面还有更长的路:工具调用是否准确,压缩后的 summary 是否真的保留了关键事实,多轮计划会不会漂移,目标状态能不能正确继承,验证器、测试、diff、审批这些 harness 能不能把“看起来完成”变成“可信完成”。

很多效果问题不像缓存这么诚实,它不会给你一个清清楚楚的 hit rate。它可能表现成工具选错、总结漏事实、计划走偏、测试没覆盖、失败语义不清。那些问题同样需要真实任务去碰撞,只是 metric 会更难设计。

所以这轮缓存优化可以先告一段落,但 Orca 的效果优化远没结束。

这九轮做完,我对这件事的判断反而更简单了:

最大强度的缓存优化,不是不断猜哪里能省,而是不断拿真实调用去碰撞,直到系统暴露出它真正的边界。

而最大强度的 Agent 优化,也差不多是同一件事:不断拿真实任务去碰撞,把“我觉得它会做得更好”变成可观察、可复现、可回归的数据。

这大概是这次优化最值钱的收获。不是某个聪明的改动,而是一套能让假设变成数据的工作流。

Keep Reading

相关文章

评论