从 80% 到 99.5%:DeepSeek Harness 缓存优化指南
DeepSeek Harness 是 DeepSeek AI 于 2026 年 8 月开源的 Agent Harness,缓存命中指后续模型请求的提示词前缀被提供方缓存读取,从而减少重复的输入处理;官方将命中率定义为 cacheRead / (input + cacheRead + cacheWrite),并在 Web 统计行与 TUI 页脚显示这一指标。本文解释缓存命中的计算口径、请求前缀为何会失效、自动压缩如何复用 KV Cache,以及如何用日志和测试确认命中,而不是只看一个漂亮的百分比。
DeepSeek Harness 的“缓存命中”是什么
DeepSeek Harness(dsh)中的缓存命中不是本地文件缓存,也不是把最终回答直接存起来,而是模型提供方对重复提示词前缀的 token 复用。一次请求的输入计量被拆成互不重叠的 input、cacheRead 和 cacheWrite 三个桶;其中只有 cacheRead 表示本轮从既有前缀缓存中读到的 token。
官方仓库将 Harness 定义为开源 Agent 运行框架,采用“一切皆插件”的 Cordis 架构。缓存指标由 token meter 从会话日志中的 assistant/message usage 折叠得出,不依赖某个临时 UI 状态,因此刷新、回放和重连可以重新得到同一组计数。
命中率怎么算
缓存命中率的正确公式是:
cache hit rate = cacheRead / (input + cacheRead + cacheWrite) × 100%例如一轮请求有 input=200、cacheRead=800、cacheWrite=0,命中率是 800 / 1000 = 80%。如果有 100 个新 token 被写入缓存,分母应增加这 100 个 token;把 cacheWrite 排除会高估命中率。
官方 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 移到消息末尾,并复现最近一次已路由请求的 system、tools 和历史消息,再追加一条尾部 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 和两轮以上的连续任务。验证时至少检查三层信号:
UI 的 cache 百分比是否在第一轮之后出现,并随热请求上升。
会话事件中的
assistant/message是否携带 usage,而不是只看assistant/chunk。后续 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% 是展示结果,成本核算应使用完整的 input、cacheRead、cacheWrite 和输出 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,升级前应重新核对接口与展示行为。
参考资料: