ChatGPTClaudeGrok / X / CursorGemini

DeepSeek Harness 每轮结束多转 28 秒:turn/end 被 git 快照阻塞的链路

故障排查发布于 2026-10-30作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH性能turn/endgit快照workspace-changes
DeepSeek Harness 模型 1.9 秒就说完了,界面却继续转 28 秒。按时间线拆开:turn/end 要等串行的收尾钩子,而钩子里做了一次完整 git 快照,慢在未跟踪文件上,补 .gitignore 即可。

一次「你好」对话,模型 1.9 秒就说完了,界面却继续转圈到 30.6 秒——多出来的 28.7 秒不在模型,也不在网络,而在回合收尾的那条串行钩子里。 钩子做了一次完整的 git 工作区快照,而工作区里有 12 万个未被忽略的未跟踪文件(#10601)。按三段拆开看。

DeepSeek Harness 发生前:模型其实已经说完了

日志里的两个时间点把责任划得很清楚:内容在 1.939 秒就落盘了。

  1. 0.000s turn/start。
  2. 1.939s assistant/message + step/end——模型输出完成,这一步结束。
  3. 30.607s turn/end——回合才正式结束。
  4. 中间 28.7 秒没有任何模型输出,纯粹是收尾等待。
  5. 这就是「界面还在转、但其实早就答完了」的来源。

DSH插件 发生时:串行钩子把 turn/end 挡在后面

agent/turn-stopping 是 serial 且 await 的分发,所以 turn/end 一定在它之后才写入。

  1. agent.ts 里在回合结束时判断没有下一步,然后 await this.dispatch.serial('agent/turn-stopping', ...)。
  2. 因为是串行等待,后续的 turn/end 写入被排在这之后。
  3. 钩子落点是 workspace-changes/src/index.ts,里面 await recorders.get(...).stopping(turn)。
  4. stopping() 再落到 recorder.ts 的 await snapshotTree(...)。
  5. 快照最终执行 git add --all --ignore-errors,写一个私有 index。
  6. 整条链上没有任何一步是后台异步的,所以耗时直接叠加到用户等待上。

DeepSeek Harness 发生后:慢在未跟踪文件,不在受跟踪文件

git add --all 会为每个未跟踪文件计算 blob 哈希并写进 index,12 万个文件就是 12 万次计算。

  1. 社区在 Windows 11 + git 2.52.0.windows.1、1.6 万个受跟踪文件的仓库上做了对照测量。
  2. 受跟踪文件本身很快:git ls-files 只要 0.065 秒,git status --porcelain 0.11 秒。
  3. 但 git ls-files --others --exclude-standard 列出了 121,182 个未跟踪且未被忽略的文件。
  4. 其中一个临时目录贡献了 115,776 个、约 4.98 GB。
  5. git add --all 因此要 66.03 秒;git write-tree 还要 1.65 秒。
  6. 单次快照合计 67.7 秒——这解释了你看到的几十秒卡顿。

DeepSeek插件 一张表看清慢在哪一步

同一组命令,补 .gitignore 前后对比,差距全在未跟踪文件那一行。

操作修复前修复后说明
git ls-files0.065s0.065s受跟踪文件很快
git status --porcelain0.11s0.11s只做 stat,不动 index
git ls-files --others121,182 个67 个未跟踪文件数
git add --all66.03s0.13s差距来源
git write-tree1.65s0.04s随 index 变小
单次快照合计67.7s0.17s卡顿消失

DeepSeek Harness 减负清单:先补 .gitignore

把构建产物、临时目录、缓存目录补进 .gitignore,是成本最低、收益最大的一步。

  1. 用 git ls-files --others --exclude-standard | wc -l 数一下当前未跟踪文件。
  2. 按目录聚合,找出贡献最多的几个:git ls-files --others --exclude-standard | cut -d/ -f1 | sort | uniq -c | sort -rn | head。
  3. 把 .dsh-build-tmp/ 这类构建临时目录、下载缓存、模型权重目录补进 .gitignore。
  4. 重新数一遍,确认数量降到几十的量级。
  5. 手动跑一次 git add --all 计时,确认从几十秒降到一秒以内。
  6. 再回 DSH 发一条消息,观察 turn/end 是否紧跟 step/end。

社区就是靠这一步把单次快照从 67.7 秒降到 0.17 秒的。

DSH插件 为什么「加缓存」「换硬盘」都不解决

成本与文件数量线性相关,不降数量就只能等。

  1. 慢的动作是为每个新文件算哈希,不是读磁盘缓存命中率。
  2. 换更快的盘能改善一部分,但 4.98 GB 的临时目录照样要遍历。
  3. 加大内存不改变 git add --all 的算法复杂度。
  4. 结论:先减文件数,再谈硬件。

修复方向:把快照挪出阻塞路径

插件已经在监听 turn/end,说明「回合结束后再记」这条路本来就有。

  1. 在 turn-stopping 里只记录需要的坐标(HEAD、基线 tree)。
  2. 把 snapshotTree 改成回合结束后的异步补记,让 UI 的 running 立刻翻。
  3. 风险要认:这中间工作树可能又变,快照不再精确对应「回合结束那一刻」。
  4. 另一条路线是保住精确性、只把等待变可见:先发一个「正在生成变更摘要」的状态。
  5. 后者不改耗时,只是别让它看起来像模型还在思考。

注意事项

  1. 别只看总耗时。拆成 step/end 与 turn/end 两个时间点,责任立刻清楚。
  2. 别先怀疑模型。这段等待里没有任何生成。
  3. .gitignore 要覆盖临时目录,不只是源码构建产物。
  4. 注意 --ignore-errors 会让超长路径被静默跳过,快照可能不完整。
  5. 长会话里这是常态开销,每轮都要付一次,值得一次性治理。

如果你的 turn/end 延迟只有一两秒,那可能只是正常的收尾开销;几十秒才说明工作区里有大量未跟踪文件。相关排查见 dsh 启动很慢、启动卡住怎么办?DeepSeek Harness 启动耗时的原因与加速排查。

ChatGPTClaudeGrok / X / CursorGemini

常见问题

DeepSeek Harness 模型已经回复完了,界面还在转圈十几秒,是模型没结束吗?

不是,这是 DeepSeek Harness 的回合收尾钩子拖住了界面。会话日志里 assistant/message 与 step/end 早在 1.939 秒就写入了,turn/end 到 30.607 秒才落,中间 28.7 秒都花在收尾钩子上。

每轮结束多出来的这 28 秒,在 DeepSeek Harness 里具体花在哪?

这 28 秒花在 DeepSeek Harness 的一次完整 git 工作区快照上。agent/turn-stopping 是串行且 await 的钩子,workspace-changes 在里面调 snapshotTree(),后者执行 git add --all,会把所有未跟踪文件也算一遍 blob 哈希。

为什么受跟踪的文件不多,DeepSeek Harness 的 turn/end 还是这么慢?

因为慢的不是受跟踪文件,而是 DeepSeek Harness 快照要扫的未跟踪文件。社区实测仓库只有 1.6 万个受跟踪文件,但工作区里有 12 万个未被 .gitignore 覆盖的未跟踪文件,其中一个临时目录就贡献了 11.5 万个、4.98 GB。

给 DeepSeek Harness 补 .gitignore,真的能减轻这 28 秒吗?

有效,社区把相关目录补进 DeepSeek Harness 仓库的 .gitignore 后,未跟踪文件从 121,182 降到 67,单次快照从 67.7 秒降到 0.17 秒,卡顿随之消失。

为什么加大缓存或换机器,都解决不了 DeepSeek Harness 的这个问题?

因为 DeepSeek Harness 的成本来自要为每个未跟踪文件计算 blob 哈希并写入 index,属于工作量问题,不是缓存命中问题。文件数量不降,单次快照就是那么久。

相关术语

agent/turn-stopping
agent/turn-stopping 是回合结束前分发的串行钩子,必须在 turn/end 写入之前完成。任何在它里面同步等待的工作都会直接拖长整轮收尾时间。— https://github.com/deepseek-ai/deepseek-harness/discussions/10601
snapshotTree
snapshotTree 是 workspace-changes 用来记录工作区快照的函数,内部通过 git add 与 write-tree 生成一次完整树状态。它的耗时与工作区文件数量正相关。— https://github.com/deepseek-ai/deepseek-harness/discussions/10601
git add --all
git add --all 是把工作区所有变更(含未跟踪文件)加入索引的 git 命令。它会为每个新文件计算 blob 哈希,因此未跟踪文件数量是主要成本来源。— https://github.com/deepseek-ai/deepseek-harness/discussions/10601
turn/end
turn/end 是标记一个回合正式结束的会话事件。前端据此关闭运行状态,因此它被阻塞时,界面会一直显示模型仍在工作。— https://github.com/deepseek-ai/deepseek-harness/discussions/10601

来源