dsh-better-choice
给 DeepSeek Harness(DSH)的 ask_user_question
问题卡片加两个能力:
- 右键选项写补充:右键任意选项,就在该选项下方展开一个补充说明输入框; 提交时说明与所选项一起交给模型,不用把一整段解释塞进「其他」。
- 先问一句再选:卡片底部有一个旁问框,基于当前会话的真实上下文回答你的疑问; 答案只显示在卡片里,不提交、不污染主对话,也不替你做出选择。
左键选择、跳过、上一题 / 下一题、倒计时与「慢慢回答」等官方行为全部保留。 计划审查(plan-review)、审批等非通用问题卡片仍由官方组件处理。
功能
| 能力 | 行为 |
|---|---|
| 右键展开补充框 | 右键选项即选中该项(若尚未选中)并展开输入框;切到下一题时自动收起,文字保留 |
| 每个选项独立记忆 | 单选、多选都按选项分别保存补充内容,切换选项不会串稿 |
| 提交不改变协议 | selected 仍是原始选项文字;补充说明合并进 custom,无补充时与官方请求完全一致 |
| 未选也可备注 | 多选时给未勾选的选项写了备注,提交文本会标注 (未选),模型能分清语气 |
| 旁问当前会话 | 以 fork 子代理复用当前会话已完成的上下文、模型与工具声明,回答「这个选项到底什么意思」这类问题 |
| 旁问不打断作答 | 每题独立线程、可连续追问;聚焦旁问框时卡片倒计时暂停,失焦恢复;点「提问」后卡片进入等待,不再超时 |
| 旁问可取消 | 回答中显示「取消旁问」,取消后线程里留下已取消提示,可以换个问法再问 |
| 旁问只读 | 执行期拦截一切工具调用,不继续原任务、不替用户选择;输出上限 32K tokens(不超过父会话上限) |
| 官方交互保留 | 左键选择、自由输入行、跳过、翻页、倒计时、超时后续答、已结算回看均不受影响 |
右键补充
单选时右键某个选项,会先选中它,再在选项下方展开输入框。填写的内容不会替换选项, 而是作为该选项的附注:
{
"answers": [
{
"id": "question-id",
"selected": ["方案 B"],
"custom": "方案 B: 先灰度 5%,观察一小时再全量"
}
]
}
多选时每个选项各有一条附注,提交时一行一个:
方案 A: 需要保留旧接口
方案 C: 下个迭代再删
没有填写任何附注时,请求与官方完全一致,不额外生成 custom。
旁问
问题卡片底部有「先问一句」输入区。Enter 发送,Shift+Enter 换行;回答完成后写入卡片, 可以继续追问。旁问使用当前会话的模型与上下文,但:
- 会启动一个 fork 子代理;它通过
delivery: 'caller'挂在当前命令调用上,不排队等父回合, 所以父会话正卡在ask_user_question时也能回答; - 不把问答写回父会话的消息流;子代理有独立的子会话日志,父会话只记录
command/run、subagent/catalog/command/done(不记录问题正文); - 执行期 guard 拒绝一切工具调用,不改文件,也不替你提交答案。
倒计时开着也来得及想:把光标放进旁问框,卡片会显示「已暂停」(倒计时停住),失焦后 自动恢复;点「提问」后卡片进入「会一直等你回答」,不会再因为等回答而超时。旁问线程按题 分开保存,切题再回来仍能看到原来的问答;回答中可点「取消旁问」中止,每个线程最多保留 20 轮,避免长会话无限增长。
适合问:「这两个选项在生产环境下的差别是什么?」「选 B 的话需要改哪些文件?」 「刚才那个结论的依据在哪?」——问完再点选项。
安装
要求 DSH 0.2.0-rc 族(engines.dsh: >=0.2.0-rc.1 <0.3.0-0,客户端内置包
^0.2.0-rc.1),Node.js 20+;已在 DSH 0.2.2-rc.1 上验证。
从 npm 安装(推荐)
打开侧边栏 插件 页 → 安装插件;
输入包名并确认:
dsh-better-choice安装完成后硬刷新(
Ctrl+Shift+R);若宿主半边未生效,重启桌面端。
固定版本:
dsh-better-choice@1.0.1
命令行等价写法:
dsh plugin --profile desktop add dsh-better-choice@1.0.1
从 GitHub 安装
仓库的 lib/ 已是可直接运行的产物,安装时不需要本地构建:
https://github.com/TLEWA/dsh-better-choice
固定到标签:
https://github.com/TLEWA/dsh-better-choice#v1.0.3
或直接安装 Release 附件:
https://github.com/TLEWA/dsh-better-choice/releases/latest/download/dsh-better-choice.tgz
命令行等价写法:
dsh plugin --profile desktop add github:TLEWA/dsh-better-choice#v1.0.3
从源码安装
git clone https://github.com/TLEWA/dsh-better-choice.git
cd dsh-better-choice
npm install
npm run build
npm test
dsh plugin --profile desktop add .
安装或升级后硬刷新页面;宿主命令 bc-ask 未注册时,旁问框会提示失败,重启桌面端即可。
使用
- 等模型发起一个普通的选择题;
- 想补充约束:右键选项,写完后正常提交;
- 想先确认信息:在底部旁问框提问,看完回答再选;
- 两者可以同时使用,旁问内容不会进入最终答案。
右键在触控板上通常是双指点按;没有右键的设备仍可正常左键作答,只是用不到补充框。
实现
客户端:接在官方 composer 链上
浏览器半边向 conversation.composer 链式槽注册,priority: -10(官方默认 0),
因此先于官方组件被询问。选择器以结构化方式识别通用 PendingQuestion 载体;
识别失败或不是通用问题卡时返回 null,自动落回官方组件。
组件本身是官方通用问题流程的移植:保留卡片结构、推荐标记解析、倒计时、
自由输入行与提交逻辑,只增加「按选项的补充框」与「底部旁问线程」。
样式由官方问题卡 CSS 派生并加 bc_ 前缀,避免污染其他插件。
宿主:一个只读的旁问命令
旁问通过本插件自己的命令 bc-ask 完成,客户端把问题、全部问题卡片、已写备注和本线程
之前的旁问序列化后交给宿主。宿主:
- 用
ctx.subagents.startActivation({ provider: 'fork', delivery: 'caller' })启动一个 fork 子代理;fork provider 会把父会话最后一个turn/end之前的已完成回合作为 seed, 当前尚未完成的 tool-call 回合不会进入 seed; - 只追加一条尾部 user 消息(旁问模式 + 问题卡片 + 之前的旁问 + 本次问题),不改 system、 不改工具声明、不重写历史,因此前缀与父会话已完成回合保持一致;
- 子代理继承父会话的 provider / model / reasoningEffort,
maxTokens取min(32000, parent.maxTokens);父调用的 signal 与 30 分钟 watchdog 一起透传; - 子代理执行期挂
tools.guard,任何工具调用都会被拒绝;回答只取文本,并清理模型误发的<tool_call>伪影; - 子代理不把问答写回父 session;父会话只落
command/run、subagent/catalog与command/done,且recordInput: false不记录问题正文。
fork 子代理复用父会话已完成回合的前缀,provider 可以命中已有 prompt cache。实测主请求与
紧随其后的 fork 子请求都会报告非零 cacheReadTokens,并随父会话已完成回合增长(本机
DeepSeek 路由为自动前缀缓存)。
目录
src/index.ts 宿主入口:bc-ask 命令、fork 子代理、旁问提示词
src/client/index.ts 客户端入口:样式、词典、slot 注册、旁问桥
src/client/composer.tsx 增强问题卡:右键补充、旁问线程、提交组装
src/client/carrier.ts 结构化识别通用 PendingQuestion
src/client/draft-store.ts 草稿与备注的跨渲染存储
src/client/side.ts 旁问服务接口
src/client/locales.ts 中英文案
src/client/styles.ts 由 scripts/gen-css.mjs 生成的样式
scripts/build.mjs esbuild 构建(客户端 CJS 信封 + 宿主 ESM)
scripts/gen-css.mjs 从参考样式生成带前缀的 CSS
scripts/upstream-composer.module.css 上游 MIT 样式(已内置,构建无需外部目录)
lib/ 构建产物(已提交,可直接安装)
cordis.patch.yml bundle patch:把宿主行插入 profile 插件树
test/package.test.mjs 包元数据、产物与清单校验
开发
npm install
npm run build # 产出 lib/index.js 与 lib/client.js
npm run check # 产物语法检查
npm test # node:test
npm pack # 生成可安装的 .tgz
npm pack 的 prepack 会重新构建、检查并跑测试。客户端产物是带
window.__ModuleLoader__ 信封的 CJS,宿主产物是不打包的 ESM。
兼容性
按 DSH 客户端 0.2.2-rc.1 的 composer 槽与 PendingQuestion 契约做类型校验,
manifest 的 engines.dsh 声明为 >=0.2.0-rc.1 <0.3.0-0,peer 依赖声明为
^0.2.0-rc.1;已在 0.2.2-rc.1 Web 宿主真机验证。DSH 升级若改动
问题卡内部结构:
- 识别失败时增强卡片不接管,官方问题卡仍可用;
- 旁问依赖宿主命令、
ctx.subagents.startActivation与 fork provider;接口变化时旁问会报错, 但不影响正常作答。
已知限制
- 只接管通用选择题;计划审查、审批等卡片保持官方实现。
- 旁问固定在当前会话的模型上,不提供单独选择模型或推理强度的入口。
- 旁问是只读的:需要读文件、跑命令才能回答的问题,应直接在对话里问。
- fork seed 只包含父会话最后一个已完成回合;当前未完成的回合(例如正在等待回答的这一轮) 不会进入 seed,旁问需要从问题卡片和之前的旁问里了解它。
- 旁问回答目前是完成后整段出现,不是逐字流式显示;超长回答受 32K 输出上限约束。
- 没有右键输入的设备无法使用补充框,但左键作答完全不受影响。
许可与来源
本项目以 MIT 发布。问题卡的组件结构与样式派生自
dsh-question-note
(MIT,NedFlanders);旁问交互参考了
dsh-btw(Apache-2.0)的设计。
具体复用范围与许可证全文见 NOTICE.md;
AI 参与范围与验证边界见 AI_USAGE.md。
DSH 本身及其内置包的版权归 DeepSeek 所有,不因本项目的 MIT 许可而改变。
No comments yet. Be the first to write one.