前言¶
如果你在 DeepSeek Harness(DSH)里用过 ask_user_question,可能遇到过这样的场景:agent 提了一个问题,问题卡片通过 mux WebSocket 单次推送到浏览器。一旦页面被切到后台、笔记本休眠,或者连接进入半开状态,这一帧就丢了——客户端没有心跳,感知不到问题存在,工具就一直等下去。实测观察到过 6 小时以上的挂起,唯一的恢复手段是手动按 Stop。上游反馈见 deepseek-ai/deepseek-harness#1554(discussion)。
dsh-ask-guard 就是针对这个问题的一个插件:丢失或未回答的问题会以结构化的 ASK_TIMEOUT 结束,而不是让回合永远挂起。下面介绍它的原理、安装和配置。
这是什么¶
dsh-ask-guard 是 DSH 中 ask_user_question 的超时守卫插件,由 Q1hangL 维护,当前版本 0.1.0,许可证为 MIT(README 与 package.json 均有标明)。它体现的是 DSH「一切皆插件」的思路:不改 harness 本体,通过一个工具执行包装器补上官方 dsh-tool-call-timeout-policy 覆盖不到的空档——后者只强制执行工具插件声明的预算(budget),而 ask_user_question 没有声明任何预算,所以此前始终无人兜底。
工作机制¶
插件做的事可以拆成三步:
1、注册一个 tools/execute 包装器,为每次 ask_user_question 调度设置协作式截止时间(deadline)。
2、截止时间触发时,等待中的 provider 中止处理器被触发:web provider 会广播 question/resolved cancelled,把卡住的输入框(composer)清理掉。
3、包装器把归一化后的中止替换为结构化错误结果(code: ASK_TIMEOUT,name: AskTimeoutError),并附带一条面向模型的消息,agent 因此可以优雅地结束回合,而不是阻塞在那里。
这里有两个值得注意的设计:
- 超时是协作式的。由
@deepseek-ai/dsh-timeout通过exec.signal发出通知,web user-questions provider 响应这个信号并真正终止,而不是只在工具层假超时。 - 其他工具原样通过。包装器只针对
ask_user_question,不影响其余工具的执行路径。
安装与启用¶
官方安装命令:
dsh plugin --profile web add dsh-ask-guard
装完后重启 dsh web。插件是一个 dsh.bundle 包,reconcile 时补丁行会自动加入 profile 组合,不需要手动改组合文件。
如果想从 git checkout 安装:
dsh plugin --profile web add github:Q1hangL/dsh-ask-guard
依赖方面,插件的 peerDependencies 为 @deepseek-ai/dsh-timeout ^0.1.0-rc.6、@deepseek-ai/dsh-tools ^0.1.0-rc.6、@deepseek-ai/schemastery ^3.18.1,安装前可以确认一下环境版本。
配置超时时间¶
只有一个配置项 timeoutMs,即单次 ask_user_question 调用的截止时间,默认 300000 ms(5 分钟)。
要调整的话,在 profile 的 cordis.patch.yml 里加一段,例如改成 10 分钟:
- id: ask-guard
config:
timeoutMs: 600000
数值按你的实际交互节奏定:问题通常几分钟内会被回答,默认值就够;如果提问后经常要离开一段时间再回来,可以适当调大。
关于恢复¶
先说明一下现状:页面刷新(F5)本来就能重新同步待回答问题——主机向重连的客户端重放未回答的问题。装了这个插件之后,即使不刷新页面,回合也不会永远挂起,超时后会以 ASK_TIMEOUT 收场。两者解决的是同一个问题的不同层面:刷新恢复的是「问题还在等回答」的场景,插件兜住的是「问题根本没送达」的场景。
运行测试¶
如果你要改源码或验证行为,仓库里带了测试,跑法是标准两步:
npm install
node --test
适用场景与注意¶
适合谁:在 web 界面使用 DSH、并且依赖 ask_user_question 做人机交互的开发者。只要你有过「agent 卡在等回答上、只能按 Stop」的经历,这个插件就是对症的。
两点提醒:
1、插件以当前 dsh 进程的权限运行,安装前建议先看一遍源码和许可证。代码在 GitHub 上可以完整审阅,许可证为 MIT。
2、超时值不要设得过短,否则正常节奏下还没来得及回答的问题会被误判为超时结束。
结尾¶
dsh-ask-guard 解决的是一个很具体的问题:让丢失或未回答的提问以结构化的 ASK_TIMEOUT 收场,agent 能优雅结束回合,而不是无限等待。实现不复杂,机制也克制——只包 ask_user_question,其余工具不碰。如果你的工作流里有类似的挂起问题,值得一试。