Repository navigation
[bug] compaction-basic 摘要请求漏传 reasoningEffort,导致 provider 前缀缓存整体 miss #11816
NothingToSay0031
started this conversation in
General
Replies: 1 comment 1 reply
|
补充一个可能需要排除的因素。我最近在实现外部压缩插件 dsh-smart-compaction,正好看了已发布版本这部分源码: 如果裁剪改写了历史中较早的 tool result,后续摘要请求回放的消息虽然与“裁剪后的 session”一致,却已经不再与上一次真实模型请求的消息前缀一致。缓存可能从第一个被改写的位置开始失效。因此,“只命中 system + 工具 schema 附近”以及历史摘要总体命中率偏低,也可能包含提前裁剪造成的影响,不一定都能归因于 漏传 可以做一个 2×2 对照:摘要前“原样保留/先裁剪工具结果”,各自再比较“保持 effort/省略 effort”,同时核对实际请求前缀和 usage。 我们插件目前采用的规避思路是:先试算裁剪是否足以降压,足够才提交;如果仍然必须生成摘要,就先用未改写的历史前缀生成摘要,之后才按需裁剪保留尾部。这是减少前缀变化的措施,并不保证缓存命中。供排查参考。 |
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
现象
@deepseek-ai/dsh-compaction-basic的辅助摘要请求没有转发reasoningEffort,导致它不再落在会话最后一个已路由请求的缓存命名空间里,回放前缀几乎全部按未命中计费。这不是配置问题,是
summarizeWithLlm()少带了一个字段。定位
packages/compaction/compaction-basic/src/summarizer.ts里summarizeWithLlm()构造的调用参数:provider/model/maxTokens都转发了,唯独没有reasoningEffort。已发布的0.2.1-rc.5与当前master源码一致。为什么算 bug
reasoningEffort属于调用配置的一等维度。packages/llm/llm/src/call-config.ts把它列在LlmCallControls里,并且同文件的配置相等性比较把它与provider/model/maxTokens并列:提供方按
reasoning_effort分缓存命名空间(max是独立的一份,high与「不传」共用另一份),所以省略它等于把请求搬到了另一个命名空间。packages/compaction/compaction-basic/README.zh.md「辅助摘要器请求 / KV Cache 影响」写着:并且把「放弃复用」的原因只归为两类:路由到不同 provider/model、压缩非头部范围。漏传
reasoningEffort是第三种、且未被声明的复用失效来源。latest就是requestHeader()?.config,provider/model 从它取,reasoningEffort也在同一个对象上。实测证据
同一个自建网关、同样的消息前缀做 A/B,只看摘要请求的
usage.cacheReadTokens:maxmaxmaxhighhigh与不传共用命名空间,所以侥幸命中)也就是说只有会话本身跑在
high(或默认档)时才看不出问题;一旦用到独立命名空间的档位,摘要调用就整体退化成「只有头部命中」。历史 109 次真实摘要调用合计 2,538 万输入 token,仅 134 万命中,整体命中率 5.0%;命中的块大小全是 10,368 / 6,272 这种「只有 system + 工具 schema」的量级,与「消息一个都没复用」的判断吻合。
建议改法
在
summarizeWithLlm()里按 provider/model 的同一来源转发 effort,约三行:latest(会话路由)时,把latest.reasoningEffort一并放进 options;config.summarizationProvider/summarizationModel时不要硬套会话的 effort——不同模型的reasoning.efforts取值集合不同,硬塞会撞上UNSUPPORTED_REASONING_EFFORT;resolveCallWithInfo的校验,所以只在与会话路由同源时转发是安全的。如果希望更省事,也可以让压缩走一遍
agent/request扩展点(现在为了跳过 agent-loop 专用扩展点而直连ctx.llm.stream(),代价正是路由参数会漏)。社区侧规避(供参考)
在上游修好之前,可以在更外层包一次
llm.stream():只挑purpose === 'compaction'且自身未指定reasoningEffort的调用,从该sessionId的requestHeader().config补上路由用的值,其余调用原样透传。补充一点给实现者参考:
llm/streamwaterfall 做不到这件事。它是框架正式给出的拦截点,但监听器签名只有(options, next),options是闭包里同一个对象,next(...)不会把新参数带给下游或内置实现——监听器只能整体替换返回的流,于是会跳过下游监听器和内置的失败包装。要「只补一个字段、其余全不动」,只能选更外层的入口。环境
@deepseek-ai/dsh/dsh-compaction-basic:0.2.1-rc.5(master源码同样存在)/compact两条路径都能复现All reactions