Repository navigation
[Bug] 会话中途 cacheReadTokens 反复回落到 ~6,144(只剩静态前缀),2–3 万 token 被重新预填充 #64
Copy link
Copy link
Open
Description
先叠个甲
我是一个学生,这是我第一次在Github上发issues,以下内容和调研过程基本都是由AI完成的,内容大部分我也看不太懂,如果有任何不合适的地方或者错误请宽容一下,谢谢🌹🌹,如果需要更详细的数据随时联系我
环境
- dsh 0.1.7-rc.2,@mars-sea/dsh-commandcode-provider 0.11.12(web profile)
- 模型:commandcode/deepseek/deepseek-v4.1-flash;套餐 Go(Provider API 返回 403,走 /alpha/generate)
- Windows 11 + Node v24.14.1;cordis.patch.yml 中 requestTimeoutMs: 60000
- 网络经 Clash TUN → 新加坡节点,实测节点到 api.commandcode.ai 延迟 71–136ms
现象
同一会话内,cacheReadTokens 会间歇性掉回 ~6,144(量级与"系统提示 + 26 个工具 schema"这段共享静态前缀相当),
此时未缓存输入达 2.1–3.2 万 token,需要重新预填充;该步耗时从热缓存时的 5–7 秒涨到 20–100 秒。
一个 14 步的会话里出现 4 次。
数据(session 370f0615,14 步,最大提示 46,865 tok,失败尝试 0 次、引擎重试 0 次)
| step | 总提示 | 已缓存 | 未缓存 | 命中率 | 首token | 输出 |
|---|---|---|---|---|---|---|
| 1/4 | 27,464 | 23,296 | 4,168 | 84.8% | 27.8s | 92 |
| 1/5 | 28,062 | 6,656 | 21,406 | 23.7% | 5.0s | 276 |
| 1/6 | 29,408 | 28,288 | 1,120 | 96.2% | 6.5s | 4,865 |
| 1/7 | 34,341 | 34,176 | 165 | 99.5% | 6.6s | 104 |
| 1/8 | 34,496 | 6,144 | 28,352 | 17.8% | 19.9s | 57 |
| 1/9 | 35,613 | 34,432 | 1,181 | 96.7% | 6.5s | 1,634 |
| 1/10 | 37,326 | 6,784 | 30,542 | 18.2% | 22.4s | 104 |
| 1/11 | 37,459 | 37,120 | 339 | 99.1% | 5.5s | 57 |
| 1/12 | 38,601 | 6,144 | 32,457 | 15.9% | 55.2s | 7,774 |
| 1/14 | 46,865 | 46,336 | 529 | 98.9% | 36.7s | 1,895 |
同账号同模型的对照:官方 CLI 1.65.2 连跑 60 次调用,累计 10,083,417 提示 token、命中 9,779,712(97.0%),
稳态 99.7–99.9%,全程只有 1 次掉到 3.4%。插件侧同期三个会话分别是 68.2% / 85.6% / 91.7%。
已排除(附证据)
- 网络/代理:流式正常(单请求 1000+ 事件、最大事件间隔 2.1s),本会话 0 次 TRANSPORT/连接错误
- 超时与重试:上表这些慢步的失败尝试和引擎重试均为 0
- 会话标识:做过 A/B(随机 threadId vs 固定 threadId + x-session-id),两者缓存行为一致
- 统计口径:确认 dsh 会话日志里 inputTokens 是"未缓存"、cacheReadTokens 是"已缓存",相加才是总提示
怀疑点
- 插件 buildCliBody 的请求体顶层字段:config / memory / taste / skills / params / threadId
- 官方 CLI 1.65.2 的请求体:config / memory / taste / skills / permissionMode / threadId / promptCache,
且 system 以分节数组发送(systemSections)。插件这两项都没有发送。 - Command Code 官方仓库 issue #346(p1,已关闭)承认过"deepseek-v4 缓存命中率远低于同一 DeepSeek 官方 provider",
并在 CLI 0.33.0 修复,修复说明为 "dynamic prefix cache thrashing by relocating volatile dynamic context block",
官方称可省 2.9x–21x 成本。插件是自己拼请求体,可能没有继承这个"易变内容块位置"的修复。
请求
- 对比插件与 CLI ≥0.33.0 的请求体布局(尤其易变/动态内容落在哪个字段、哪个位置),确认是否会把前缀切开
- 考虑像 CLI 一样发送 promptCache / permissionMode
- 为请求体/响应元数据增加抓取开关(DSH_COMMANDCODE_TRACE 目前只抓响应流);
另外插件目前不持久化 provider 侧的 trace/generation ID(dsh 会话日志里 generationId /
providerRequestId / x-trace-id / promptCacheHitTokens 均为 0 处),而官方 CLI 会把 traceIds
写进 ~/.commandcode/projects/.meta.json。有 ID 服务端才能定位到具体请求。
补充说明(诚实起见)
本会话也存在"缓存热但依然慢"的步(1/4 命中 84.8% 仍耗时 27.8s、输出仅 92 token;1/14 命中 98.9% 耗时 36.7s),
所以这个 issue 主要针对"请求体构造是否触发前缀缓存失效",并不声称解释了全部延迟。
复现方法
解压 ~/.dsh/sessions/**/session.v4.jsonl.zstd,逐条读取 assistant/message 事件里的 usage.cacheReadTokens / usage.inputTokens
(注意 inputTokens 是未缓存部分)。
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
Mars-Sea commentedon Sep 27, 2026
已经更新0.11.17版本,你再试试看看还有没有问题
smartxm commentedon Sep 27, 2026
我已经更新到了@mars-sea/dsh-commandcode-provider@0.11.17+@deepseek-ai/dsh@0.1.7-rc.2现在仍然复现,现在不是“回落到 0”,而是回落到一个固定前缀,今天中午看到更新以后我立刻又跑了一段长对话,总token24M,一口气跑完的,没有长时间中断,上下文用了242k,未命中1.7M,命中22M,下面是数据:
161 次请求里 15 次命中量被卡在 66,432–66,688,其余全部正常。这 15 次是:
会话总计(非高峰价:未命中 $0.15/M、命中 $0.003/M、输出 $0.6/M):
关键线索:15/15 都发生在“上一步的工具结果新增一张图片”之后的下一个请求
但是:图片本身不是充分条件(反例)
2026-09-20 有个会话,138 步,12:32–13:59,同样是 commandcode / deepseek-v4.1-flash):新增 7 张图,掉缓存 0 次,聚合命中率 99.23%。
两次的差别(供参考,可能是混淆因素):
toolUpdate: 'addition-only'能力(允许在既有会话中把新增工具投射进当前请求)—— 若某次请求的 tool 声明列表发生变化,值得怀疑;但本次掉缓存时命中量不是掉到 0 而是掉到 66k,且下一步立刻恢复,暂时无法只靠会话日志判定已排除的客户端因素
append,DSH 从未改写/压缩历史,前缀是纯追加cli传输,没有中途 fallback 到/provider/v1/chat/completionsagent/inbox/spliced(后台任务通知注入)的时间点对不上想问问你使用的时候出现过这种情况吗,我对这种情况没有任何头绪。
Mars-Sea commentedon Sep 28, 2026
感谢补充数据。确认 0.11.17 仍有这个问题;我用合成图片真实调用 Command Code 服务端,也复现了新增图片后缓存回落。
我已完成一个缓解方案:模型看过旧图并回复后,可选择在后续请求中不再重复发送旧图。它会影响模型回看旧图,因此默认关闭。这个改动会加入下一个版本;发布后我会在这里通知你测试。服务端缓存回落的根因仍未确认,issue 会保持打开。
Mars-Sea commentedon Sep 28, 2026
补充一次官方 CLI 的真实调用结果:我用
command-code 1.66.0、deepseek/deepseek-v4.1-flash,在隔离会话中让read_file依次返回两张合成图片,中间加入长文本。第二张图片进入请求前,cacheReadTokens为 55,040(总输入 55,431);进入后降到 11,776(总输入 56,164);下一轮纯文本恢复到 56,064(总输入 56,183)。本次会话记录里有两次含图工具结果,没有压缩记录。所以官方 CLI 在这个条件下也会出现同类缓存回落;这不是本插件独有的现象。但这次合成测试不能解释你那段 161 次请求的全部细节,也尚不能确认具体触发机制。你此前 CLI 1.65.2 的对照结果依然有效,说明并非每个会话都会触发。issue 继续保持打开,我会继续追查请求前缀与缓存行为。
smartxm commentedon Sep 28, 2026
收到。需要帮助请随时提出
xiaijiangxue commentedon Sep 30, 2026
我今天也出现这个问题了,版本:v0.12.0,不知道是不是上游的问题,最近command调整了模型的东西。
GT-610 commentedon Oct 16, 2026
按仓库源码核对了一下,供参考:
0.12.0 加了
offloadSeenImagesForCache——模型看过某张图并回复后,后续请求用文字占位替代旧图,你描述的「新增图片后缓存回落」在实机 A/B 里因此消失。但它是默认关闭、且只在 CLI 传输生效,属于缓解而非修复。仍未解决:
promptCache请求字段没有采纳(permissionMode在 0.12.2 补上了,x-taste-learning也调过,但都不是这条根因)。(我是普通用户,以上按仓库源码与 CHANGELOG 核对,没有真机实测。)
GT-610 commentedon Oct 16, 2026
更正我上一条回复里的一个错误,抱歉。
我说「持久化 provider 侧 trace / generation ID 没有落地」,这是错的。经核实,v0.12.3 起适配器会把响应级标识通过
replayState.response交给宿主,而ReplayEnvelope.response是持久化的(dsh-llm类型里写明是「adapter-private metadata」,会随assistant/message的source一起落盘)。本机实测证据(解压
.dsh/sessions/**/session.v4.jsonl.zstd,注意是多帧 zstd,单帧解压只会拿到文件头):所以现在能从会话日志里拿到
x-trace-id/responseId了,你要的「有 ID 服务端才能定位到具体请求」这半条已经满足。抓取的是x-request-id、request-id、x-trace-id、x-generation-id、traceparent这些响应头(src/stream-response.ts)。仍然没落地的是:CLI
promptCache请求字段、你建议的请求体侧抓取开关(DSH_COMMANDCODE_TRACE目前仍只抓响应流,请求侧只记结构计数与 SHA-256 指纹,不记正文)。再次为那条错误信息致歉——它是跨版本比对时把「当时没有」当成了「现在也没有」。