前言¶
DSH 的 ask_user_question 工具是阻塞的:一旦调用,回合就停在等待,人不在电脑前就等于卡死。这不适合需要长时间运行或多人协作的场景。dsh-question-bridge 把“提问”和“等待”拆开,让智能体在回合中间向人发起问题,拿到一个批次号后立刻返回,继续处理后续逻辑;人稍后在原生问题卡片或右下角的待答面板里回答;答案以一条普通的 user/message 回到会话里,智能体再接着干。
插件简介¶
dsh-question-bridge 是为 DSH Agent 提供非阻塞交互式提问能力的插件。它通过工具 ask_interactive_question 立即返回 { status: "pending", batchId },不阻塞回合;答案以 kind: "answer_to_question_bridge" 的普通用户消息投递到会话,由智能体自行消费。批次状态持久化在插件自己的 JSON 文件里,重启和刷新都不会丢失。
核心功能¶
- 工具
ask_interactive_question:立即返回pending和batchId,不视为答案或授权。 - 批次持久化:状态存于
~/.dsh/question-bridge/state/<sessionId>.json,原子写入。 - 普通消息投递:答案以
user/message形式通过agent.steer()投递,只有日志中出现该消息才算delivered。 - 双入口:原生问题卡片与右下角待答面板;关闭卡片、通知、超时都不算已读或已答。
- 文本识别:回合结束时,助手最后一段话若明显在向人提问,可按规则切出并生成批次。
- 通知控制:页面可见时不弹通知,只显示卡片;系统通知依赖浏览器权限。
安装与启用¶
目标基线:DSH Desktop 0.2.0-rc.2(宿主 tag dsh-v0.2.0-rc.2)。
- 从 Releases 下载
dsh-question-bridge.tgz,放进~/.dsh/plugins/packages/。 - 在
~/.dsh/profiles/desktop/package.json的dependencies和dsh.profile.bundles里各加一条dsh-question-bridge。 - 执行
pnpm install,然后完全退出并重启 DSH。
安装细节、自检和排错见 docs/installation.md,兼容性与取舍见 docs/compatibility.md。
典型用法¶
安装后,Agent 会在需要人做决定的地方调用:
{
"questions": [
{
"question": "通知走哪条链路?",
"header": "通知",
"detail": "两条都能离线工作,区别在多窗口时谁来弹。",
"options": [
{ "label": "WebSocket 推送", "recommended": true },
{ "label": "两秒轮询" }
]
}
]
}
工具返回 pending 后回合继续。人回答之后,会话里多一条用户消息:
{
"kind": "answer_to_question_bridge",
"batchId": "…",
"revision": 2,
"questions": [ … ],
"answers": [{ "questionId": "…:q1", "choiceIds": ["…:q1:c1"], "skipped": false }]
}
模型只需认这个 kind;问题文本、推荐项、自动生成的选项都不是用户的决定。
另外两个入口:
- 原生卡片:插件把批次转成原生
AskUserQuestionItem调userQuestions.ask(),但不传wait.callId,所以卡片只是渲染器,答案由插件自己收。 - 普通文本识别:回合结束时,如果助手最后一段话里明显在向人提问,插件按规则切出来(跳过代码块、引用、行内代码),生成同一套批次。默认只跑规则;
classifier: "model"才会在规则没命中时调用模型判定,且有每小时 30 次的额度。
配置¶
放在 profile 的插件配置里(字段都有默认值):
| 字段 | 默认 | 说明 |
|---|---|---|
enabled |
true |
总开关 |
detectPlainText |
true |
是否在回合结束时识别普通文本里的提问 |
classifier |
"off" |
"model" 时才调用模型补判 |
classifierTimeoutMs |
12000 |
单次判定超时 |
maxClassifierCallsPerTurn |
1 |
每个回合最多判定一次 |
maxClassifierCallsPerSessionPerHour |
30 |
额度在发起前预占,失败也计数 |
maxInputChars |
6000 |
送进判定的文本上限 |
maxOpenBatchesPerSession |
5 |
同会话同时开着的批次数上限 |
notifications |
true |
是否允许系统通知入口 |
notifyOnlyWhenHidden |
true |
页面可见时不弹通知,只显示卡片 |
组成¶
lib/index.js 宿主端:工具、presenter、批次存储、投递、两条 HTTP 路由、提示词
lib/client.js 浏览器端:shell.overlay 里的待答面板、轮询、系统通知
cordis.patch.yml 把插件插进 DSH 组合
docs/ 安装、兼容性、验收
tests/ 宿主端纯函数的单元测试(node --test)
状态文件在 ~/.dsh/question-bridge/state/<sessionId>.json,一会话一个文件,原子写。没有待答问题时删掉整个目录是安全的。
已知边界¶
- 系统通知依赖网页的 Notification 权限,必须由用户点“启用系统通知”触发申请;
window.focus()不保证把窗口从托盘里拉出来——这是浏览器/Electron 的限制,不是插件能补的。 - 每会话同一时刻只有一张插件卡片在展示,其余排在待答面板里,避免多张卡互相抢焦点。
- 规则识别、答案映射、投递与持久化有单元测试和假 ctx 的集成测试(
npm test,15 项);classifier: "model"那条模型判定路径没有在本机真实调用过模型,默认也是关的。验收清单见 docs/acceptance.md。
适用场景与注意¶
适合需要“人机协同做决策”的场景:例如路由选择、策略配置、多窗口协作时的权限分配等。插件以当前 dsh 进程权限运行,安装前应检查源码与许可证(MIT)。
短结尾¶
dsh-question-bridge 让 DSH 智能体可以在回合中间发起非阻塞提问,答案以普通消息流回到会话,适合需要长时间运行或多人协作的流程。更多细节见 GitHub。