DSH plugin 子代理用错模型?subagent 模型继承、推理档配置与排查方法完整解析

故障排查发布于 2026-09-12作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH pluginsubagent 模型继承意外计费reasoningEffort
DeepSeek Harness 子代理跑在别的模型上、意外计费甚至委派被拒?根因是子代理继承会话创建时的模型快照,UI 切模型与推理档都不传导。v0.1.2-alpha.1 已原生修复。

DeepSeek Harness 的子代理跑在与界面显示不同的模型上、悄悄产生计费,或一委派就被端点拒绝,根因是同一条:子代理继承的是父会话创建时刻options 快照(provider / model / maxTokens),而不是父会话当前实际使用的实时路由,reasoningEffort 这类新字段更是完全不传导。 已在 v0.1.2-alpha.1 原生修复(按 request-time selection 解析),在此之前可用「切模型后开新会话」「workflow 显式指定模型」等办法规避。

DSH plugin 子代理的两种表现:意外计费与委派被拒

同一个继承缺口在计费与可用性两个方向上都能造成事故,而且都以「父会话看起来一切正常」为掩护。 社区实测:

  1. 主模型切了,子代理没切:主对话切到 deepseek-v4-flash 后(日志显示 21:00 之后主会话 0 条 v4-pro 请求),23 个子代理会话全部走 deepseek-v4-pro,累计 550+ 条 pro 请求;子代理工作期间 pro 计费持续增长(10.36 CNY → 14.44 CNY),而对话框指示器显示的是 flash(#1472)。
  2. 免费/收费混淆最危险:混合部署(官方 API + 本地 llama.cpp)里,用户以为子代理在本地免费跑,实际每个子代理请求都命中官方计费端点——实测子代理请求头是 deepseek-official / deepseek-v4-flash / maxTokens 256000,本地模型应为 local-aeonb / 27B-AEON-Q5 / 65536#1581)。
  3. 委派被端点硬拒spawn 出来的子代理会静默丢弃 reasoningEffort,请求不带思考参数;对「模型总是思考、不允许关闭」的端点(如 z.ai glm-5.3-flash)直接返回 400 {"code":"1210", ...},而父工具只看到一句 Error: subagent run failed,没有任何诊断信息。同为进程内运行的 subagent_fork 因为克隆了完整会话头(含持久化 effort)而正常完成(#4666)。
  4. 故障扩散:父会话切到可用模型逃生后,新派出的子代理仍从旧快照继承「已停掉的本地路由」,继续报 Connection error. / code TRANSPORT;同一家族还有 401、429 周配额被烧、空响应与流中断等记录。
  5. 方向可以双向复现#1581):创建时是本地模型 → 切到 flash → 子代理仍是本地;创建时是 flash → 切到本地模型 → 子代理仍是 flash。两种方向都说明子代理看的不是「现在用什么」。

DeepSeek Harness 机制:parent.options 快照与字段缺失

结构性原因在于同一套代码里并存了两个「当前模型」的概念,而子代理读的是那个永不更新的。 逐层拆解:

  1. 继承读的是快照resolveChildAgentOptions()packages/subagent/subagent/src/child-agent.ts:68-83,对应发布包 dsh-subagent/lib/index.js:501-512)只把 parent.options.provider / parent.options.model / parent.options.maxTokens 复制给子代理,最后再展开调用方传入的 requested
  2. 快照只在构造时写一次parent.options 在 Agent 构造函数里赋值(agent-loop/lib/index.js:354),此后没有任何代码更新它;它的内容来自会话创建/恢复时的全局默认(api-proxyselectionFordefaults.defaultModelSelection(),读 settings.yamlagent-default-model)。
  3. 实时路由在另一处:当前轮真正发出去的 provider/model/maxTokens 存在会话的最新 request/header 事件里(packages/core/session/src/index.ts:670),Web 层的 selectionFor() 与系统提示词变量都从这里解析;selectionFor 的文档注释明确写着优先级「每次读取时解析,而不是只播种一次」。
  4. selectModel 不回头补快照:切换模型时只更新了两处——selectionFor(...).current 与写入 settings.yaml 的全局默认——parent.options 保持旧值。这还带来一个副作用:每次切模型都会改全局默认,于是「新会话跟随新模型、子代理仍跟旧快照」的错位更难理解。
  5. 断链的续传路径也走同一函数continuation.ts 既把同一对值快照进 descriptor,又在真正启动时调用 resolveChildAgentOptions;因此一次性 spawn 与可续传子代理一起中;改的时候两份拷贝(源码与打包产物)都要改。
  6. 理由字段根本不存在:v0.1.1-rc.2 的 AgentOptionspackages/core/agent/src/runtime-types.ts)没有 reasoningEffort 字段,新 loop 的 buildRequest 也只播种 { provider, model }spawn 子代理不安装 installModelSelection,没有持久化 header 可以复原 effort,适配器便把「缺失」翻译为 provider 默认 → 强制思考端点拒绝。
  7. 正确的优先级写起来很短:以 parent.session.requestHeader()?.config 为准,parent.options 只在该会话尚未发出任何请求时兜底;maxTokens 同样跟随 header,避免 flash 的 256000 与本地模型的 65536 相互串味。社区补丁还补了一个细节:request/header 中由适配器默认提供(而非显式指定)的 maxTokens 不应被提升为子代理的显式上限,否则会把旧路由的输出预算冻结进新路由;可续传创建时应在首个 await 之前一次性解析,让初始物化与持久化 descriptor 拿到同一份值,避免冷恢复时漂回创建时路由。

DeepSeek Harness 已修版本、规避与自查做法

v0.1.2-alpha.1 已原生修复;在此之前按所处版本选择规避,并用「三个 header 对比」自查。 具体如下:

  1. 修复内容child-agent.ts 改为按 request-time selection 解析子代理的 provider / model / effort,创建时 options 只作为「父会话尚未发出首个请求」之前的兜底;AgentOptions 新增 reasoningEffort?: ReasoningEffortId,请求构建器按 this.options.reasoningEffort ?? persistedReasoningEffort 取值;dsh-tool-subagent 的 schema 支持 per-call agentOptions,并暴露模型可见的 reasoning_effort 参数。
  2. rc 线规避(不改源码):① 切完模型后开一个新会话再派子代理——新会话会重新写入创建快照,resume 旧会话不会;② 多子代理任务改用 workflowagent() 显式指定 model;③ 把角色定义钉死到完整 provider/model,父会话切模型就不会把旧快照带进子代理;④ 需要子代理跟随当前模型时,可用第三方子代理运行时(如 pi2dsh + @tintinweb/pi-subagents,默认策略是 inherit parent,按创建当刻的 ctx.model 解析路由)。
  3. 自查用三 header 对比:抓①父会话当前 request/header、②同一有界任务新 spawn 子代理的首条 request/header、③fork 子代理的 header。若 provider/model 一致而 reasoningEffort 只在①③出现,就命中了路由保真缺口。注意 fork 跑通只是对照组,不能证明 spawn 已修好。
  4. 确认版本dsh --version 落在包含补丁的 alpha 线之后即可;此前为 ≤0.1.1-rc.2 用户提供的社区 DeepSeek插件(补 effort 传导 + 增加 subagent_effort 工具)在 ≥ 0.1.2-alpha.1 已被官方实现取代,无需再装。
  5. 运维建议:不要把 agent-default-model 指向一个可以被单独停掉的本地服务——那样整条通道都变成单点故障;默认用常在线路由,需要本地模型时按会话切换。子代理连接类报错的其它形态见 模型连接排查
  6. 排查 DSH插件 类问题时,用 DSH Plugin Hub 的「设置 → 插件市场」装卸插件,比手工改 profile 更安全(失败会回滚 manifest)。

DSH plugin 排查注意事项

别只盯 UI 显示——模型指示器展示的是父会话当前选择,不代表子代理实际用的路由,判断一律以会话日志里的 request/header 为准。 六条要点:

  1. 别只盯 UI 显示:模型指示器展示的是父会话当前选择,不代表子代理实际用的路由;判断要用会话日志里的 request/header
  2. 计费差异是第一个信号:本地模型免费、官方 API 计量,一旦两者的请求落到同一批子代理上,账单会先于报错暴露问题。
  3. settings.yamlagent-default-model 管不到已存在的会话:它只在没有会话级选择时生效。
  4. selectModel 会改全局默认:排查时先确认这条链,避免把「全局默认被改」误判成「子代理乱选模型」。
  5. reasoningEffort 属于路由的一部分:以后遇到「同一个模型,主会话能跑、子代理被拒」这类现象,先按路由保真来查。
  6. 可观测性仍待补:子代理终态的错误详情过去没有回传给父工具,只有一句 subagent run failed;遇到这类报错请直接查子会话日志。
DSH Plugin Hub 插件市场:装卸子代理相关插件、查看版本与更新

来源:Discussion #1472Discussion #1581Discussion #4666Discussion #2006dsh-v0.1.2-alpha.1 release notes

常见问题

DeepSeek Harness 子代理为什么用了另一个模型,还产生意外计费?

DeepSeek Harness 子代理的模型不是从父会话当前实际使用的模型继承的,而是从父会话创建时刻的 parent.options 快照继承的——resolveChildAgentOptions 只读 parent.options.provider/model/maxTokens,而这份快照在 Agent 构造时写入一次、之后永不更新。实测一个会话在主对话切到 flash 后仍派出 23 个会话、550+ 次请求全走 v4-pro,费用从 10.36 CNY 涨到 14.44 CNY(来源:Discussion #1472)。

为什么 DeepSeek Harness 里 spawn 出来的子代理会被强制思考的端点拒绝(400 / code 1210),而 subagent_fork 却正常?

DeepSeek Harness 的 spawn 子代理会静默丢弃 reasoningEffort:v0.1.1-rc.2 的 AgentOptions 根本没有 reasoningEffort 字段、resolveChildAgentOptions 只转发 provider/model/maxTokens,进程内子代理又不安装 installModelSelection、新 loop 没有持久化 request/header 可恢复该值,适配器在 effort 缺失时省略思考参数,于是强制思考的端点(如 z.ai glm-5.3-flash)返回 400/1210。fork 之所以正常,是因为它克隆了包含持久化 effort 的完整会话头(来源:Discussion #4666)。

切换模型后 DeepSeek Harness 子代理还是不跟随,有没有立刻可用的规避办法?

DeepSeek Harness 子代理想立刻跟随新模型,可先用三条规避:① 切完模型后开一个新会话再派发子代理(新会话会重新写入创建快照,resume 旧会话不会);② 用 workflow 的 agent() 显式指定 model(普通 subagent 工具在旧版本没有 per-call 覆盖);③ 需要精确固定路由时,把角色定义钉死到完整 provider/model。混合部署里还要注意 agent-default-model 不要指向可被单独停掉的本地服务(来源:Discussion #1581)。

这个 DeepSeek Harness 子代理模型继承问题修好了吗?怎么自查我的部署是否命中?

DeepSeek Harness 已在 v0.1.2-alpha.1 原生修复这个问题:child-agent.ts 改为按 request-time selection 解析子代理的 provider/model/effort,创建时 options 只作首个请求之前的 fallback;AgentOptions 新增 reasoningEffort,tool-subagent 也支持 per-call agentOptions 与模型可见的 reasoning_effort 参数。自查用「三个 header 对比」:抓父会话 request/header、同一个有界任务的新 spawn 子代理首条 request/header、以及 fork 子代理 header——若 provider/model 一致但 effort 只在父与 fork 上出现,就命中了路由保真缺口;fork 跑通只是对照,不能证明 spawn 已修(来源:Discussion #4666)。

相关术语

creation-time snapshot(创建时快照)
creation-time snapshot 是 Agent 在构造时写入的 options(provider/model/maxTokens),在父会话运行期间从不被更新;子代理继承逻辑读的就是这份快照,因此与 UI 当前所选模型可能长期不一致。https://github.com/deepseek-ai/deepseek-harness/discussions/1581
request header(请求头事件 / 实时路由)
request header 是会话在当前轮实际发往 provider 的配置记录(session.requestHeader().config),是模型选择机制认定的权威来源;Web 层的 selectionFor 与系统提示词变量都从这里解析「当前模型」。https://github.com/deepseek-ai/deepseek-harness/discussions/1581
reasoningEffort(推理力度)
reasoningEffort 是控制模型思考档位(off/low/high/max)的请求参数。它属于「路由」的一部分,但旧版 AgentOptions 没有该字段,spawn 子代理因此发出不带思考参数的请求。https://github.com/deepseek-ai/deepseek-harness/discussions/4666

来源