DeepSeek Harness Windows 每次 spawn 多 4 秒:分段计时定位 pwsh 包装

故障排查发布于 2026-10-30作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSHWindows性能pwshhooksspawn
DeepSeek Harness 在 Windows 上每次调用子进程都多花 4~6 秒,连不 spawn 的 read 也慢。用 echo 1 基线加四段计时,把工具本身、钩子、pwsh 包装层与进程创建分开量,再决定改哪里。

echo 1 在终端里是毫秒级,在 DSH 里端到端要 10 秒:4.4 秒是钩子,另外约 6 秒是从钩子结束到工具结果。 这条基线一旦建立,问题范围就被锁死在「每次 spawn 都要付一笔固定成本」,而不是模型或网络(#10608)。下面按四段计时逐步逼近。

DeepSeek Harness 实验准备:用 echo 1 做基线

选一条最不可能慢的命令,量它的端到端时间,再拆开看时间花在哪一段。

  1. 在会话里让 Agent 执行 echo 1,记录 tool/call 到 tool/result 的墙上时间。
  2. 社区结果:约 10 秒,其中 PreToolUse 钩子 4.4 秒,钩子结束到结果约 6 秒。
  3. 同一条命令在终端里跑,几乎立刻返回。
  4. 结论:慢的是调用链路,不是命令内容。

DSH插件 第一段:先量「不 spawn 的工具」

read 和 grep 不启动子进程,如果它们也慢,说明开销在调用前后,而不是在工具内部。

  1. 统计一段时间的工具耗时:社区样本里 read 49 次、均值 4,860 ms;grep 11 次、均值 4,309 ms。
  2. 对照钩子均值:PreToolUse 324 次、均值 4,046 ms。
  3. 两者几乎重合,说明 read/grep 的时间基本就是钩子的时间。
  4. pwsh 均值 10,621 ms,多出来的部分是 pwsh 进程自身启动。
  5. mcp__…save 均值 4,251 ms,MCP 子进程也吃到同一笔固定成本。
工具是否 spawn均值
read否4,860 ms
grep否4,309 ms
pwsh是10,621 ms
mcp__…save是4,251 ms

这一段就把「工具实现慢」排除掉了:不 spawn 的工具一样慢。

DeepSeek Harness 第二段:把钩子摘出来单独量

把钩子脚本改成第一行就退出,如果耗时不变,就证明成本不在脚本里。

  1. 用 hooks.json 里的四条钩子做统计:SessionStart、UserPromptSubmit、PreToolUse、PostToolUse。
  2. 记录 DSH 自己报的 hook/result.durationMs:PreToolUse 均值 4,046 ms,PostToolUse 4,024 ms,UserPromptSubmit 4,310 ms。
  3. 同样的脚本在终端手工跑:206 ms、51 ms、51 ms。
  4. 再做一个对照实验:让 block_delete.js 第一行 process.exit(0),什么都不做。
  5. DSH 记录的耗时仍是 4,298~4,429 ms,8 次连续调用波动只有 131 ms。
  6. 结论:这是固定成本,与脚本工作量无关。

DeepSeek插件 第三段:量 shell 本身的启动时间

如果 shell 启动只要 0.2 秒,而调用要 4 秒,那么多出来的部分在 shell 之外的包装与进程创建里。

  1. 在终端里分别计时:pwsh -NoProfile -Command exit 约 190 ms,pwsh -Command exit 约 198 ms。
  2. powershell.exe 5.1 约 132149 ms,cmd /c exit 约 31 ms,node -e 0 约 3848 ms。
  3. -NoProfile 只省 8 ms,所以「profile 或模块自动加载拖慢」这个假设被排除。
  4. 也就是说:没有任何一次单纯 shell 启动接近 4 秒。
  5. 成本 = 那次启动 + runner 在它周围做的所有事,每次都付。

DeepSeek Harness 第四段:看进程树,找中间层

用任务管理器或进程查看器打开命令行列,看 node.exe 的父进程是谁——这一步直接暴露包装层。

  1. 触发一次钩子调用,同时观察进程树。
  2. 社区发现:node.exe 不是 DSH 的直接子进程,而是 pwsh 包装层下的孙进程。
  3. 结合源码形状:DSH 把命令统一包成 pwsh -NoLogo -NoProfile -NonInteractive -Command "<command>"。
  4. 对「绝对路径 + 参数」这类命令,包装层是纯开销。
  5. 至此四段计时闭合:工具本身没问题,钩子脚本没问题,shell 启动也没问题,问题在包装层。

DSH插件 修法:命令形状简单时直接 CreateProcessW

社区补丁让执行器的 argv() 先尝试简单拆分,能直接调 CreateProcessW 就跳过 pwsh;其余情况回退原路径。

  1. 只有「绝对路径 + 参数」形状的命令走直连。
  2. 含管道、重定向、$、反引号、#、换行、智能引号、不配对引号、相对首 token、.cmd/.bat/.ps1、--% 的,一律回退。
  3. 超时、缓冲与退出码处理都不动。
  4. 补丁后同机同版本实测:PreToolUse 从 4,046 ms 降到 240255 ms;read 降到 144177 ms;grep 362~395 ms;一个 python MCP 调用 151 ms。
  5. 注意 240~255 ms 只是 spawn 加脚本执行,不含等待输出流关闭的约 200 ms,整个钩子约 440 ms。
  6. 仍有约 1.3 秒的等待停在 NtCreateUserProcess,属于 JS 层以下,这个补丁够不到。

版本状态与你能做的事

rc.3 上包装层仍在,所以走 shell 语法的命令依旧 4.2~4.7 秒;你只能减少触发它的次数。

  1. 0.2.1-rc.3 上 argv() 仍无条件返回 pwsh 包装,社区实测直接 spawn 路径 0.40.6 秒、走 shell 的 4.24.7 秒。
  2. 含引号参数的 python.exe -c "…" 会被扫描器判回 shell,实测仍是 4,506 ms。
  3. 能做的:减少钩子数量与触发频率,尤其是 PreToolUse。
  4. 钩子命令尽量写成「绝对路径 + 参数」,避免管道与重定向。
  5. 别去调 Defender 排除项——社区没试过也已经不需要,真正的原因是包装层。
  6. 想彻底解决,需要上游让钩子走 ctx.subprocess,或在命令已是 argv 时跳过 shell。

注意事项

  1. 别把固定延迟当网络抖动。8 次调用只波动 131 ms,这是固定成本。
  2. 别先怀疑 profile。-NoProfile 只省 8 ms。
  3. 别只看钩子。pwsh 工具本身还叠加了自己的启动。
  4. 补丁不是发布版。报问题时标清版本与是否打了补丁。
  5. 记录进程树。它是区分「工具慢」与「包装慢」最快的一张图。

如果你的机器上是「偶发几秒」而不是「每次都几秒」,那更可能是沙箱或 ACL 相关,换个方向查。相关排查见 dsh 启动很慢、启动卡住怎么办?DeepSeek Harness 启动耗时的原因与加速排查。

常见问题

DeepSeek Harness 在 Windows 上每条命令都慢 4 秒,是模型慢还是工具慢?

是 DeepSeek Harness 的工具层慢,不是模型。社区用 echo 1 做基线,端到端花了 10 秒,其中约 4.4 秒是 PreToolUse 钩子,另外约 6 秒是从钩子结束到工具结果。echo 1 本身是毫秒级。

为什么 read、grep 这种不走子进程的工具,在 DeepSeek Harness 里也慢?

因为 DeepSeek Harness 在每次调用前都要过一遍 PreToolUse 钩子,而钩子本身要 spawn 进程。社区测到 read 平均 4,860 ms、grep 4,309 ms,与钩子的 4,046 ms 几乎重合。

怎么证明拖慢 DeepSeek Harness 的不是 PreToolUse 钩子脚本本身?

把 DeepSeek Harness 的钩子脚本改成第一行就 process.exit(0)、什么都不做,DSH 记录到的耗时仍是 4,298~4,429 ms;而同样的脚本在终端手工跑只要 51~206 ms。差值在包装层,不在脚本。

社区在 DeepSeek Harness 的启动链里找到的中间层,到底是什么?

是 pwsh 包装层,这是 DeepSeek Harness 在 Windows 上额外加的一层。DSH 把每条命令包成 pwsh -NoLogo -NoProfile -NonInteractive -Command "<command>",而 node.exe 实际是 pwsh 的孙进程。去掉这层包装后,钩子降到 240~255 ms。

这个 spawn 变慢的问题,DeepSeek Harness 官方版本修了吗?

官方版本没修,社区补丁只在 DeepSeek Harness 的自有构建上验证过。0.2.1-rc.3 上 argv() 仍然无条件返回 pwsh 包装,走 shell 语法的命令依旧 4.2~4.7 秒,而直接 spawn 的路径是 0.4~0.6 秒。

相关术语

PreToolUse 钩子
PreToolUse 钩子是兼容 Claude Code 风格的钩子类型,在每次工具调用前执行。它由 DSH 通过 shell 运行,因此会引入额外的进程启动开销。— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
pwsh 包装层
pwsh 包装层是 DSH 在 Windows 上把命令统一包成 pwsh -Command 的形式再交给执行器的中间层。对已经是「绝对路径 + 参数」的命令,这一层是纯开销。— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
CreateProcessW
CreateProcessW 是 Windows 创建进程的底层 API。社区补丁在命令形状简单时直接调用它,绕过 shell;否则回退到原有包装路径。— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
NtCreateUserProcess
NtCreateUserProcess 是 CreateProcessW 内部实际执行进程创建的系统调用。社区观察到一个仍有约 1.3 秒的等待停在这里,属于 JS 层以下。— https://github.com/deepseek-ai/deepseek-harness/discussions/10608

来源