Replies: 2 comments
|
感谢这份取证——两类根因的分离、实测 tokenizer 对照、以及「同站不同模型行为不一致」这条观察,都直接可用。我按 一、根因 1 的分类落点:400 走的是 DeepSeek 适配器,不是
|
|
补一个不同口径的数据点,不改动你根因 2 的结论。 你测的是中文密集内容的 chars/token(官方 tokenizer,2.21×)。我这边测的是真实混合会话里 两者不冲突,是同一件事的两个切面:纯中文段落偏 2.21×,而真实会话里混了大量英文工具结果与源码, 顺带一个可能对你有用的口径细节:meter 的总量是 provider-anchored 也就是拿到过真实 中转站那一半我没测,只补 meter 这个切面。 (环境:DSH 0.1.5-rc.1 / Windows 11 / deepseek-flash / contextWindow 1048576) 影响的能力
|
Uh oh!
There was an error while loading. Please reload this page.
自动压缩在中文密集会话与大陆中转站下失效:两类根因 + 实测证据
环境:DSH Desktop 2.0.5 / harness
0.1.2-rc.1(macOS arm64)触发场景:中文为主的编码会话 + 硅基流动(
api.siliconflow.cn/v1,openai-completions 协议)+ 跨模型切换症状:上下文溢出后回合直接中止;
/compact或compact_now也失败我在本机做了完整取证,确认这是两个互相独立的根因,都会让自动压缩在关键路径上静默失效。源码行号按
0.1.2-rc.1。根因 1(P0):provider 的溢出报文不被
isContextWindowExceededError识别 → 溢出恢复分支永不执行实测报文
硅基流动在超长输入时返回 HTTP 400:
{"code":20015,"message":"number of input tokens (150019) has exceeded max_prompt_tokens (98304) limit."}分类结果
@deepseek-ai/dsh-llm的isContextWindowExceededError(lib/index.js:145)与 pi-ai 的 24 条
OVERFLOW_PATTERNS(pi-ai/dist/utils/overflow.js)全部不匹配:对照:同站另一模型的报文格式是命中的,所以同站不同模型行为不一致(这解释了「时好时坏」的观感):
失败链
classifyPiAiError把 HTTP 400 判为INVALID_REQUEST,不是CONTEXT_WINDOW_EXCEEDEDdsh-compaction-basic的溢出恢复钩子首行即放行:if (failure.code !== CONTEXT_WINDOW_EXCEEDED_CODE || signal.aborted) return next()(lib/index.js:805)dsh-llm-retry对INVALID_REQUEST不重试建议
把这类报文纳入分类器。最小改动是给
OVERFLOW_PATTERNS与isContextWindowExceededError补两条模式(
max_prompt_tokens与「N input tokens 超过 max_prompt_tokens(M)」),并考虑纳入
max_prompt_tokens/max_input_tokens这类「输入侧上限 ≠ 模型上下文长度」的措辞。根因 2(P0):
token-meter的chars/4估算让中文会话的阈值晚到 2.21 倍dsh-token-meter/lib/types/estimate.js用固定CHARS_PER_TOKEN = 4。实测
取本机一天前真实会话中 12 段中文密集内容(16,566 字),用 deepseek 官方 tokenizer 实测:
后果
compaction-basic的thresholdRatio是估算口径的比例,于是:thresholdRatio: 0.75实际等价于「真实上下文的 166% 才触发」 —— 永远够不着,provider 早在真实上限处就拒绝了。中文比重越高越严重(英文会话偏差小得多)。
这同时解释了为什么加
maxOverflowRetries/compactionRetries都没用:压力触发根本没到,而溢出的那条路被根因 1 切断了。
建议
不必改全局常数(它同时服务 UI 上下文环,改动面太大)。可用其中任一:
estimate.ts里对 CJK 区间按加权密度估算(例如按 CJK 字符占比在 4 与 ~1.8 之间插值);thresholdRatio的语义改为「真实口径」,由compaction-basic内部按同一估算器做折算;thresholdRatio是估算口径,并在 CJK 会话下提示折算系数。附带(同类故障,供参考):摘要调用自身溢出 → 自锁
buildSummarizationInput把「完整 system prompt + 全部 tool schema + 全部被遮蔽对话」打包后回发给摘要模型。本机 system + tools 常达数万 token,因此 26 万 token 的会话做摘要时
是一个 30 万 token 的请求 —— 若摘要走对话路由,必然再溢出一次:
已在本地用「摘要路由固定到 1M 上下文模型」绕过。建议上游考虑在摘要输入超预算时先裁剪
(而非整段回发),或至少把这条失败标注成「摘要输入溢出」以便区分。
另外一条实测(非 bug,但值得写进文档):
maxTokens默认 8192 对中文六段式 checkpoint 偏小。本机 25.7 万 token 会话实测:
35.9 秒(非秒退)证明是真跑满输出预算后截断,且输出预算里还含 reasoning token。
提到 48K 后解决。
附:大陆中转站的容量事实(harness 无法自动获知)
硅基流动
GET /v1/models只返回id/object/created/owned_by,不含容量,故llm-pi-ai的contextWindow只能手配。而我实测的 provider 真实上限与官方文档标称不一致:zai-org/GLM-4.5-Airzai-org/GLM-4.5VQwen/Qwen3.6-35B-A3Bzai-org/GLM-5.3而
dsh-llm-pi-ai对未声明容量的模型回落defaultContextWindow: 262144(
DEFAULT_CONTEXT_WINDOW,lib/index.js:857)。对上面两个小窗口模型,这个默认值是危险值:压力触发按 75% 算是 196,608,而 provider 在 65536/98304 就拒绝了。
建议:文档里明确「未声明容量 ≠ 安全默认」,并建议中转站路由显式设置
defaultContextWindow为站内最小值。复现清单
同一 DSH 版本下,把
thresholdRatio折到 0.45、把summarizationProvider/Model固定到大窗口独立路由、并给每个中转站模型显式写实测
contextWindow之后,本机长中文会话的溢出中止已停止复现。
All reactions