ByteNoteByteNote
Agent 干到一半要问人:拆解 dsh 的人机问答接缝
字

字节笔记本

2026年10月6日 · 约 5 分钟读完

Agent 干到一半要问人:拆解 dsh 的人机问答接缝

API中转
¥120

Agent 越自主,越需要一个可靠的停下来问人的机制:问得太随意会不停打断任务,问得不清楚,人给出的回答模型又接不住。DeepSeek 开源的 agent 框架 DeepSeek Harness(简称 dsh,MIT 协议,目前处于 developer preview 阶段)把这件事做成了一个独立接缝:user-questions 包。它定义了一套与提供方无关的问答词汇,工具或权限插件在需要人类回答才能继续时只依赖这套词汇;真正的界面由 UI 侧提供的 UserQuestionProvider 实现,宿主运行时负责把请求转发给已连接的客户端。

user-questions 接缝的五步问答流程

为什么问答要做成接缝

同样是向用户提一个问题,命令行、Web 控制台、桌面端的呈现方式完全不同,但调用方关心的事情是一样的:问题怎么描述、答案怎么拿回来。dsh 的做法是让工具只认识词汇表(选项、意图、答案),不认识任何具体界面;UI 侧注册一个激活的 provider,运行时居中转发。同一套工具今天跑在终端里,明天换上图形界面,问答协议一字不改,接缝两侧各自演化。这是整套设计里最值得借鉴的一刀。

一个问题由什么组成

最小单位是选项 AskUserQuestionOption:label 既是用户看到的文字,也是回传给模型的选中值,一个字段两种身份,省掉了显示文本与取值之间的映射层;description 是可选的辅助说明。往上一层是问题条目 AskUserQuestionItem:调用方提供稳定 id,答案原样带回这个 id,因此多个问题可以打包进一次请求批量呈现,答案仍各归各位。条目还能携带 detail(随问题渲染、但绝不混进选项标签的辅助文本)、header(分组小标题)和 multiSelect(是否多选,默认单选)。请求对象 AskUserQuestionRequest 里的 questions 是数组,正是为了让相关追问合并成一次流畅的交互。

批准要按名字,不按位置

plan-review 意图、答案编码与错误码速览

最有意思的是呈现意图 AskUserQuestionIntent,目前定义了 plan-review 一种:问题本身就是一次计划评审,detail 字段必须携带计划正文,决定就是批准或否决。关键规则是 approve 指名肯定选项的 label,而不是约定第一个选项就是同意。UI 不认识这个意图标签,就退化成普通选项列表;认识了,就可以画出真正的评审界面。无论走哪条路,答案字段完全一致,意图只改变呈现,从不改变协议。为了堵住含糊地带,ask() 在运行时拒绝两种类型系统拦不住的情况:approve 没有指向该问题自己的任何选项,以及给没有 detail 的问题贴上意图标签。

答案的编码规则

Provider 对每个问题返回一条答案:selected 装选中的 label,custom 装用户手输的其他回答。单选时 custom 覆盖选中项,selected 置空;多选时 custom 是补充,与 selected 并存。还有一种容易被忽略的情况:用户跳过了某个问题,UI 用一条 selected 为空且无 custom 的答案占位,整批答案仍算完成,批量问答的路由不会被跳过打乱。

单一 provider 与生命周期

一个上下文里只允许存在一个激活的 provider。注册 provider 会返回一个 disposer,注册行为与效果绑定,热更新或组件释放时自动摘除激活状态。这保证了「当前谁在替人回答」永远明确,不会出现两个界面同时抢答的混乱局面。

谁有资格问人

ask() 允许携带 agent 字段标明精确的活跃调用方,但人类交互只对运行时注册表认定的 runtime root 有效:被其他 agent 拥有的子代理没有人类回答者,问下去只会永远阻塞;而带着会话血统恢复成新 runtime root 的会话,可以正常发问。边界守不住就报错:CALLER_NOT_LIVE 表示调用方不是注册表里的活跃实例,DELEGATED_CALLER 表示这个活跃 agent 本身被别的 agent 拥有。

失败也要有名字

UserQuestionError 继承自框架的 HarnessError,经 ctx.tools.execute() 抛出时会保留 name 与 code:空问题列表是 EMPTY_QUESTIONS,没有激活界面是 NO_PROVIDER,请求中止是 ASK_ABORTED,另有 UI 侧取消。模型拿到的失败带着稳定错误码,可以据此决定重试、放弃,还是换一条路推进。

可以抄的作业

对想自己造 harness 的开发者,这套词汇给出了一份现成答卷:协议先行,让工具与界面解耦;批准按名字不按位置,杜绝顺序歧义;批量答案靠稳定 id 路由;错误带码,模型可反应;生命周期可退订,热更新不留脏状态。项目仍在快速迭代,接口可能有破坏性变更,仓库地址是 github.com/deepseek-ai/deepseek-harness,源码见 packages/interaction/user-questions。

相关文章

分享: