DeepSeek Harness 是 DeepSeek AI 于 2026 年 8 月开源的 Agent Harness,缓存命中指后续模型请求的提示词前缀被提供方缓存读取,从而减少重复的输入处理;官方将命中率定义为 cacheRead / (input + cacheRead + cacheWrite),并在 Web 统计行与 TUI 页脚显示这一指标。本文解释缓存命中的计算口径、请求前缀为何会失效、自动压缩如何复用 KV Cache,以及如何用日志和测试确认命中,而不是只看一个漂亮的百分比。

DeepSeek Harness 的“缓存命中”是什么

DeepSeek Harness(dsh)中的缓存命中不是本地文件缓存,也不是把最终回答直接存起来,而是模型提供方对重复提示词前缀的 token 复用。一次请求的输入计量被拆成互不重叠的 inputcacheReadcacheWrite 三个桶;其中只有 cacheRead 表示本轮从既有前缀缓存中读到的 token。

官方仓库将 Harness 定义为开源 Agent 运行框架,采用“一切皆插件”的 Cordis 架构。缓存指标由 token meter 从会话日志中的 assistant/message usage 折叠得出,不依赖某个临时 UI 状态,因此刷新、回放和重连可以重新得到同一组计数。

命中率怎么算

缓存命中率的正确公式是:

cache hit rate = cacheRead / (input + cacheRead + cacheWrite) × 100%

例如一轮请求有 input=200cacheRead=800cacheWrite=0,命中率是 800 / 1000 = 80%。如果有 100 个新 token 被写入缓存,分母应增加这 100 个 token;把 cacheWrite 排除会高估命中率。

情况

Web/TUI 表现

含义

尚无计费输入

隐藏缓存分组

没有分母,不显示虚假的 0%

普通命中率

显示四舍五入整数

例如 49% 或 80%

99.5% 等接近满命中

增加必要的小数位

防止非满命中被显示成 100%

真实 100%

显示 100%

不附带多余小数

官方 2026 年 8 月 19 日的实现说明,99.1% 与 99.49% 仍可显示为 99%,而 99.5%、99.95% 和 99.995% 分别保留一位、两位和三位小数。这不是缓存突然变精确,而是展示层避免整数舍入误导用户。

为什么下一轮可能命不中

缓存通常按请求开头的 token 序列匹配,因此只要前缀发生变化,后续内容即使相同也可能无法复用。最常见的破坏因素包括:

  • system prompt 在每轮动态插入时间、随机数或用户状态;

  • tools schema、工具顺序或参数描述被无关改动;

  • 历史消息被重新渲染成不同空格、标记或序列化格式;

  • 中途切换 provider、model 或 profile,导致请求头部不同;

  • 把相同的上下文放到不同消息角色,改变 token 边界。

因此“同一个问题再次提问”并不等于命中;只有前缀从字节和顺序上足够稳定,提供方才可能复用。

自动压缩如何保住前缀缓存

DeepSeek Harness 的自动压缩会把摘要指令从新的 system prompt 移到消息末尾,并复现最近一次已路由请求的 systemtools 和历史消息,再追加一条尾部 user 指令。这样摘要请求变成已预热请求的“前缀扩展”,而不是从不同 system prompt 开始的新请求。

这个设计解决了一个高成本场景:长对话触发压缩时,如果摘要器使用完全不同的 system prompt,整段历史会再次被完整处理;复用原始前缀后,摘要调用至少保留了可命中的共享部分。官方文档同时强调,手动压缩中段区域或切换摘要模型时,缓存复用可能放弃,但正确性仍然优先保证。

多轮工具调用怎样形成命中链

工具调用天然会产生多个相邻请求:模型先决定调用工具,工具返回结果后模型再生成回答。只要每一步都在前一请求基础上追加工具结果,后续请求就有机会复用前缀。

官方的带密钥端到端测试使用一个足够长的 system prompt,先运行包含工具调用的回合,再发送后续回合;测试检查第一条请求之后的每个 assistant/message usage 都有 cacheReadTokens > 0。这类测试比只观察 UI 百分比更可靠,因为它直接检查模型提供方回传的命中 token 数。

如何在本地复现与验证

先按官方 README 启动 Web UI:

npx @deepseek-ai/dsh web

然后准备一个稳定的 system prompt、固定工具 schema 和两轮以上的连续任务。验证时至少检查三层信号:

  1. UI 的 cache 百分比是否在第一轮之后出现,并随热请求上升。

  2. 会话事件中的 assistant/message 是否携带 usage,而不是只看 assistant/chunk

  3. 后续 usage 的 cacheReadTokens 是否大于 0,并用完整公式重新计算比例。

下面是一个简化的日志检查示例,session-events.json 是脱敏后的事件导出文件:

jq '[.[] | select(.type == "assistant/message") | .data.usage | select(. != null)]
  | map({input: (.inputTokens // 0), read: (.cacheReadTokens // 0), write: (.cacheWriteTokens // 0)})
  | map(. + {rate: (100 * .read / ((.input + .read + .write) | if . == 0 then 1 else . end))})' \
  session-events.json

生产环境不要把原始 prompt、客户资料或 API 凭证写入共享日志;只保留计数、请求 ID、模型路由和脱敏后的稳定性摘要。

提高命中率的工程清单

先稳定,再追求高比例

把长期不变的 system prompt、工具 schema 和基础规则放在前缀,把用户问题、时间、随机实验参数和实时数据放在尾部。这样做的目标不是让每次都达到 100%,而是让真正重复的部分保持可复用。

不要用显示值做成本核算

UI 里的 80% 或 99.5% 是展示结果,成本核算应使用完整的 inputcacheReadcacheWrite 和输出 token。尤其是接近满命中时,整数显示可能隐藏差异;接近 100% 时的小数位只是纠错信号,不是额外计费字段。

把切换路由视为实验变量

如果 provider、model、system prompt 或工具集合发生变化,应在实验记录中单独标记。混合不同路由的 usage 会让命中率趋势失去可解释性。

通过回放测试防止回归

为固定会话准备一份脱敏 fixture,断言第二个及后续请求拥有 cacheReadTokens > 0。当插件、Profile 或压缩器改动后,先跑回放,再看真实 API 的小规模验证。

常见问题

Q:缓存命中等于模型回答被缓存了吗?
不等于。命中的是提示词前缀的输入 token,模型仍会根据最新消息继续推理并生成新的输出。命中率高也不能证明回答正确。

Q:为什么空会话不显示 cache 0%?
因为尚未发生计费输入,分母为零。官方选择隐藏缓存分组,避免给一个不存在的比值赋予误导性。

Q:99.5% 为什么不直接显示 100%?
整数四舍五入会把 99.5% 误报为 100%。DeepSeek Harness 会增加最少的小数位,用来区分接近满命中和真实满命中。

Q:自动压缩一定能缓存命中吗?
不能保证。它会复现已路由前缀并把指令放到末尾,创造命中条件;如果手动压缩的是中段区域,或摘要模型与对话路由不同,复用可能放弃。

Q:多模型服务能直接解决命中率问题吗?
不能把平台接入本身当作命中保证。若用七牛云 AI 做多模型原型,应固定路由、记录完整 token usage,并以实际 provider 返回的 cacheReadTokens 验证,而不是只比较界面数字。

结论与参考资料

DeepSeek Harness 的缓存命中,本质是稳定请求前缀与提供方 KV Cache 的协作结果。理解 cacheRead / (input + cacheRead + cacheWrite)、保留完整 usage、让压缩请求复用原始前缀,是从“看到百分比”走向“验证成本收益”的关键。本文基于 2026 年 8 月 26 日官方仓库、实现说明和测试资料整理;项目仍处于 developer preview,升级前应重新核对接口与展示行为。

参考资料: