Skip to content

功能规格说明:AskUserQuestion 工具 ​

创建日期:2026-01-19

用户场景与测试 (必填) ​

用户故事:澄清模糊指令(优先级:P1) ​

作为 AI agent,当我收到用户的模糊指令时,我希望使用结构化的多选界面提出澄清问题,以便我可以在正确理解用户意图的情况下继续。

为什么是这个优先级:这是工具的核心价值。它防止 agent 做出错误假设并提高交互质量。

独立测试:可以通过给 agent 一个模糊任务(如"重构这个函数")并验证它使用 AskUserQuestion 工具询问期望的重构模式而不是猜测来测试。

验收场景:

  1. 假设 agent 正在处理任务,当遇到模糊性时,则它必须调用 AskUserQuestion 工具并提供相关选项。
  2. 假设用户看到问题,当用户选择选项时,则 agent 必须收到答案并基于该选择继续任务。

用户故事:在实现方案之间选择(优先级:P2) ​

作为 AI agent,当有多种有效方式实现功能时,我希望向用户展示这些选项,以便他们可以决定哪种方案最适合其需求。

为什么是这个优先级:使用户能够在不必自己编写代码的情况下做出架构或设计决策。

独立测试:可以通过要求 agent "添加认证" 并验证它询问是使用 JWT、OAuth2 还是基于 Session 的认证来测试。

验收场景:

  1. 假设 agent 处于规划或实施阶段,当识别出多种方案时,则它应该使用 AskUserQuestion 让用户选择。
  2. 假设选项中包含推荐项,则 agent 应该在标签中标记为 "(Recommended)"。

用户故事:在计划模式中收集需求(优先级:P2) ​

作为计划模式中的 AI agent,我希望在最终确定计划之前向用户询问缺失的需求,确保计划准确并获得批准。

为什么是这个优先级:确保规划阶段是交互式的并产生高质量计划。

独立测试:可以通过带着模糊请求进入计划模式并验证 agent 在调用 ExitPlanMode 之前提出问题来测试。

验收场景:

  1. 假设 agent 处于计划模式,当需求缺失时,则它必须使用 AskUserQuestion 来收集。
  2. 假设 agent 准备最终确定计划,则它不得将 AskUserQuestion 用于计划批准(必须使用 ExitPlanMode 代替)。

用户故事:选项组键盘可达(优先级:P1) ​

作为用户,当回答 agent 的提问时,我希望选项(单选/多选)逐个成为 Tab 可达的焦点目标、用空格选中、全部答完后用 Enter 提交,以便纯键盘用户无需鼠标即可选择任意选项并提交回答。

为什么是这个优先级:对齐 Claude IDE/桌面端的 AskUserQuestion 实际交互(permissionRequestContainer 选项每个 tabIndex={0},选项只响应 Enter/Space,方向键不参与选项导航,输入框内按键 stopPropagation 保持文本编辑语义)。曾实现 roving tabindex(组内单一 Tab stop + 方向键移动),与 CC 不一致,且方向键导航与「其他」输入框内左右键编辑光标冲突(输入框里按左右键会跳走焦点)。改后选项间用 Tab 遍历,输入框内方向键回归光标语义。Enter 键分工按交互设计师裁决(2026-08-27 a4466e21):Enter 仅用于全部题目答完后的提交、由空格负责选中选项——焦点在任意选项上时同样生效(答完按 Enter 即提交、未答完无动作),不把 Enter 当作选中键,避免与提交语义冲突(单选答完后重复按 Enter 若只做「选中」会变成无意义的 no-op,见场景 8/10 与「多问题循环轮播」)。

独立测试:触发 AskUserQuestion 弹窗,Tab 依次经过每个选项(含「其他」)、空格选中;在「其他」输入框内按方向键/空格确认光标移动与文本输入不受影响、不改变选中状态;Enter 仅用于提交(全部题目答完时提交,未答完无动作,见「多问题循环轮播」)。

验收场景:

  1. 假设 选项组渲染(单选或多选),当 Tab 遍历弹窗时,则 每个选项(含「其他」)都是独立 Tab stop(tabIndex={0}),Tab 依次经过全部选项,不跳过任何一项。
  2. 假设 焦点在某个选项上,当 用户按方向键(↑/↓/←/→)时,则 焦点不移动(方向键不参与选项导航)。
  3. 假设 焦点在某个选项上,当 用户按空格时,则 单选=替换为该选项;多选=切换该选项的勾选状态;当 用户按 Enter 时,则 Enter 仅用于提交(全部题目已答完时提交、未答完无动作),不改变选中状态(见场景 8/10 与「多问题循环轮播」)。
  4. 假设 「其他」选项被空格选中,则 输入框自动聚焦可直接输入(保留现有自动聚焦行为);未选中时提示「输入自定义回答...」照常显示。
  5. 假设 焦点在「其他」输入框内,当 用户按方向键或空格时,则 均为输入框自身的文本编辑行为(光标移动/输入空格),不触发选项选择或任何弹窗级快捷键(输入框内按键不冒泡到弹窗,对齐 CC 的 stopPropagation)。
  6. 假设 焦点在选项或「其他」输入框上,当 用户按 Enter(非 Shift)且非输入法合成中时,则 Enter 仅用于提交——全部题目已答完时提交,未答完时无动作,不负责切换题目、不改变选中状态(见「多问题循环轮播」场景 8/10)。
  7. 假设 用户回答完当前问题后切换到下一题(点击「下一个」或按 →),当 题目切换完成时,则 焦点落在新题目第一个选项(或当前已选中的选项)上,焦点不会因按钮状态变化而丢失到弹窗外。

用户故事:多问题循环轮播(优先级:P1) ​

作为用户,当 agent 在一次 AskUserQuestion 中提出多个问题时,我希望弹窗底部固定三个按钮([上一个] [下一个] [提交回答]),题目可循环轮播切换,以便我随时前后浏览、修改任意一题答案,清楚还剩多少问题要回答,不会因未答完而困惑为什么无法提交。

为什么是这个优先级:固定三按钮 + 循环轮播让多问题问卷的浏览路径不受边界限制(首页可「上一个」回看末题、末题可「下一个」回到首题),进度与答题状态一目了然;替换之前「向导式」(第一题仅「下一个」、中间「上一个/下一个」、最后一题才出现「提交回答」)的阶段性按钮组合——向导式在中间题不显示「提交回答」,用户想提交时还得先走到最后一题,循环轮播固定三按钮把「提交」入口始终可见。

独立测试:触发包含 3 个问题的 AskUserQuestion,逐题观察底部三按钮与启用状态;首页点「上一个」验证跳到末题、末题点「下一个」验证跳回首题;答完部分题目验证进度条对应段着色、提交按钮保持禁用;全部答完后验证「提交回答」可用并回车提交。

验收场景:

  1. 假设 弹窗包含多个问题,当 展示任意一题时,则 底部导航固定显示「上一个」「下一个」「提交回答」三个按钮(「提交回答」为主按钮、最右侧),不随题目位置隐藏或新增按钮。
  2. 假设 用户在第一题,当 点击「上一个」时,则 循环跳转到最后一题,且各题已填答案保留。
  3. 假设 用户在最后一题,当 点击「下一个」时,则 循环跳转到第一题,且各题已填答案保留。
  4. 假设 用户处于任意一题,当 按 ← 或 → 方向键时,则 循环切换上一题 / 下一题(与按钮行为一致)。
  5. 假设 题目进度条显示(每段代表一题),当 用户点击某一段时,则 直接定位到对应题目,不要求逐题翻页。
  6. 假设 用户已答完部分题目,当 查看进度条时,则 已答完的题目段标记为完成状态(着色),与当前浏览到第几题无关;回看已答题目时已答段保持完成色。
  7. 假设 弹窗存在未作答的题目,当 用户点击「提交回答」时,则 按钮保持禁用(不可点击);「上一个」「下一个」不受作答状态影响,始终可点。
  8. 假设 全部题目均已回答,当 用户点击「提交回答」或按 Enter 时,则 一次性提交所有问题的答案并关闭弹窗。
  9. 假设 弹窗只有一个问题,当 查看底部导航时,则 只显示「提交回答」,无「上一个/下一个」按钮。
  10. 假设 用户未答完所有题目,当 按 Enter 时,则 无动作(Enter 仅用于全部答完后的提交,不负责切换题目)。
  11. 假设 主按钮「提交回答」可提交时,则 按钮上显示回车键提示标注(⏎),提示可用回车提交。
  12. 假设 弹窗显示底部导航按钮,当 用户按 Tab 遍历按钮,则 焦点按视觉从左到右顺序遍历:「上一个」→「下一个」→ 主按钮「提交回答」(主按钮最后);Shift+Tab 反向(主按钮最先),Tab 陷阱仍限于弹窗内(与确认操作面板按钮布局一致,见 confirm-ui.md)。

用户故事:提问弹窗选项区可折叠(优先级:P1) ​

作为用户,当 AskUserQuestion 弹窗在窄窗口(小屏笔记本)里占住面板高度、把消息列表挤到几乎看不见时,我希望把选项区折叠起来、只留题干与底部按钮栏,以便我先回看消息列表再展开作答,而不必关闭弹窗(关闭等于跳过这次提问)。

为什么是这个优先级:桌面端弹窗高度上限为 min(70vh, 560px),768px 高的窗口上可占面板高度约 70%,消息列表被挤到几乎不可见——这是用户实际反馈的问题;折叠是不动高度上限、不动布局的逃生口。范围只限 AskUserQuestion:折叠收起的是「选项交互区」,判断所需的上下文(题干 chip、题号进度、底部三按钮)留在原位,因此不会造成盲判;其余确认类型(Bash / Write / Edit / MCP / 计划审批)的卡体就是审批依据(命令行、diff、参数),收起它等于让用户在看不到内容的情况下批准,故不给折叠键。此范围与 Claude 一致——claude.ai 的确认卡是同一个壳(.epitaxy-approval-card),但折叠(data-collapsed + caret)只挂在 AskUserQuestion 卡上,通用工具审批卡没有折叠键。

独立测试:在窄窗口触发多题 AskUserQuestion,点卡头折叠键,验证选项区收起、题干与进度条、底部三按钮均在原位可点、消息列表完整可见;再点一次展开,验证已勾选选项、其他 输入内容、当前题号与选项区滚动位置均未丢失;对 Bash/Write/Edit 确认验证头部不出现折叠键。

验收场景:

  1. 假设 AskUserQuestion 弹窗显示,当 用户查看弹窗头部尾部按钮组(与关闭键同一组),则 其中有一个折叠键,展开态下图标与 aria-label 表示「折叠」语义;当 确认类型为 Bash / Write / Edit / MCP / 计划审批时,则 头部不渲染该键。
  2. 假设 弹窗处于展开态,当 用户点击折叠键,则 选项区(全部选项与 其他 输入框)整体收起,题干 chip 与题号进度条仍可见,底部按钮栏(「上一个 / 下一个 / 提交回答」)保持原位可点,弹窗高度随之收缩、消息列表随之拿回空间。
  3. 假设 弹窗处于折叠态,当 用户再次点击折叠键(或 Tab 聚焦后按 Enter/空格),则 选项区恢复展开,折叠前已作出的选择不丢失:选项勾选、其他 输入内容、当前题号、以及折叠前选项区的滚动位置。
  4. 假设 弹窗处于折叠态,当 用户查看折叠键,则 其图标与 aria-label 表示「展开」语义,且该键在弹窗焦点圈循环覆盖范围内(见 confirm-ui.md「确认弹窗焦点圈」场景 2)。
  5. 假设 当前提问被解决后队列中的下一个确认出现,或弹窗因切换会话/会话重挂载,当 新确认显示时,则 折叠态重置为展开——折叠态随确认实例生灭,不跨确认、不跨会话记忆。
  6. 假设 弹窗处于折叠态,当 用户按 Esc 或点关闭键,则 行为与展开态完全一致(拒绝关闭)。
  7. 假设 弹窗处于折叠态,当 用户按 Tab 遍历,则 已收起的选项与 其他 输入框不在循环内,循环只覆盖标题行按钮与底部三按钮(与 confirm-ui.md「确认弹窗焦点圈」的「被折叠收起的元素不在循环内」一致)。

用户故事:题干超长时在题干区域内滚动(webview)(优先级:P2) ​

作为用户,当 agent 的问题描述(题干)字数很多、几乎占满确认弹窗时,我希望题干在它自己的区域内滚动,以便选项区仍然留在屏上、能直接看到并点选,而不必先滚过整段题干、或面对一片空的选项区。

为什么是这个优先级:题干此前没有高度上限,而弹窗的高度配额又全部由选项区承担 ⇒ 题干越长选项区越矮;字数极多时选项区被压成零高度,选项虽仍在 DOM 里,但既看不见也点不到(零高度的滚动容器无法交互),滚弹窗主体也救不回来。这直接违背下方边界情况的「选项列表固定在确认 UI 底部始终可见」。另两个备选口径已评估并否掉:让题干与选项整体一起滚(滚动容器上移到两者外层)只是把「看不见选项」换成「要滚过整段题干才见到第一个选项」,首屏仍然只有题干;截断/省略题干则让用户读不全判断依据。

独立测试:触发一条字数极多的题干 + 多个选项的 AskUserQuestion,在窄栏宽度、小屏笔记本高度、大窗口三档视口下验证:题干停在高度上限内并可滚到全文末尾、选项区至少露出一整行且可点选;多题场景切题后题干滚动位置回到顶部。

验收场景:

  1. 假设 题干内容不超过高度上限,当 弹窗显示时,则 题干不出现滚动条,弹窗布局与改动前一致。
  2. 假设 题干内容超过高度上限,当 弹窗显示时,则 题干在自身区域内纵向滚动(可滚到全文最后一行,不截断、不出现省略号),选项区不被压缩掉、仍至少露出一整行可点选的选项。
  3. 假设 题干与选项区都处于可滚动状态,当 用户在题干上滚动时,则 只有题干滚动、选项区滚动位置不变;反之滚选项区时题干不动。
  4. 假设 一次提问包含多个问题,当 用户切换到另一题(「上一个/下一个」或点进度条)时,则 新题目的题干从顶部开始显示,不沿用上一题的滚动位置。
  5. 假设 视口极小(题干上限与选项区所需高度之和超过弹窗可用高度),当 弹窗显示时,则 题干可被继续压缩以让出高度、选项区仍保证一行可见,超出部分由弹窗主体滚动承担——降级路径,任何情况下都不出现零高度、不可交互的选项区。

用户故事:选项缺 label 时返回输入错误而不是弹窗(优先级:P1) ​

作为用户,当模型漏写某个选项的 label(只写了 description)时,我希望这次调用被直接退回去让模型重试,以便我看到的是正常的提问卡片,而不是一个只有描述文字、点不出所以然的空白选项。

为什么是这个优先级:label 在声明里是必填,但模型仍会漏写;确认卡直接输出 option.label,漏字段就是一个没有标题的空白选项,用户既读不懂也没法判断该选什么,错误发生在离原因最远的一层。

独立测试:调用工具传 options: [{ label: "A" }, { description: "只有描述" }],确认返回失败结果、错误文案点明 questions[0].options[1],且确认弹窗未出现。

验收场景:

  1. 假设 某个选项缺 label 或 label 是空白字符串,当 工具执行时,则 返回输入校验错误(文案点明 questions[i].options[j] 与实收值,多个不合格项一次列全)且不弹出确认卡片,模型据该错误重试。

边界情况 ​

  • 用户提供自定义输入:"Other" 选项的自定义输入框在用户选中 "Other" 后才出现;未选中时不显示输入框。
    • 用户选中 "Other" 后,输入框出现,输入的文本作为该问题的答案返回。
    • 用户选中 "Other"(键盘空格/Enter 或鼠标点击)后,输入框自动聚焦,可直接开始输入;切换题目时,若新题目此前已选中 "Other",不抢占输入框焦点(焦点落回选项组的第一个/已选中选项,用户需要时再 Tab 进入输入框)。
    • 未选中 "Other" 时,选项下方以灰色显示「输入自定义回答...」提示(复用普通选项的 description 样式),说明该选项可输入自定义内容;选中后提示由输入框替代。
    • 用户取消选择 "Other"(单选改选其他选项 / 多选取消勾选)时,输入框隐藏,但已输入的内容保留;再次选中 "Other" 时内容恢复,不丢失。
    • 选中 "Other" 但未输入任何内容时,不算有效答案,提交/下一步仍被禁用。
    • 多选模式行为相同:勾选 "Other" 显示输入框,取消勾选隐藏输入框(内容保留)。
  • 多个问题:一次最多 4 个问题。弹窗一次只显示一个问题,底部固定「上一个/下一个/提交回答」三按钮(循环轮播:首页「上一个」跳末题、末题「下一个」回首题,←/→ 方向键等效),顶部显示可点击的分段进度条(每段代表一题,已答段着色为完成状态,点击直接定位到对应题);所有问题都有有效答案后「提交回答」才可用(按钮带 ⏎ 提示),一次性提交全部答案。单问题(仅 1 题)只显示「提交回答」。
  • 键盘导航不改变答案:Tab 聚焦弹窗内任意元素(包括自定义输入框)只应移动焦点,不得改变已选答案;选中/取消选项必须来自明确的用户操作(点击或空格),Enter 只负责全部题目答完后的提交。
  • 多选答案:多个选中选项如何返回?应该以逗号分隔的字符串或类似的结构化格式返回。
  • 工具拒绝:用户关掉提问弹窗(点右上角关闭键、按 Esc)时,提问工具收到拒绝且本轮立即中止——模型不会拿「用户拒绝了操作」当输入继续往下做事,会话回到空闲等用户重新发话。这条与 confirm-ui.md「基本工具确认」场景 2 / 5 是同一条语义,四端(桌面端 / VS Code / JetBrains / 远控页)必须一致:远控页曾经只回一条 deny 不中断,于是手机上关掉弹窗后模型继续、桌面端关掉就停。只有带反馈的拒绝(先给出说明再拒绝,例如计划模式的「不,现在开始实现」)才是「只拒绝该工具、模型按反馈继续或再次提问」。
  • 选项与内容的关系:选项列表固定在确认 UI 底部始终可见,方向键不参与选项导航(选项间用 Tab 遍历),与上方内容区的滚动完全无关(内容区超高时的 PgUp/PgDn 滚动见 confirm-ui.md「确认详情超高时滚动」)。题干本身超长时由题干区域自己滚动,选项区不被压缩到不可见(见「题干超长时在题干区域内滚动」)。
  • 选项 Tab 遍历与选中状态:所有选项(含「其他」)均为独立 Tab stop,方向键不改变焦点也不改变已选答案;「其他」选中后输入框自动聚焦;输入框内按键(方向键/空格/Enter 等,除 Meta/Ctrl 组合)不冒泡到弹窗,保持文本编辑语义。
  • 折叠后还能作答吗? 不能——折叠是「先回看消息列表」的暂存态,不是提交态:选项与 其他 输入框一并收起,作答前必须展开。折叠不改变任何提交校验(未答完的题目在展开后仍不可提交,见场景 7),也不代表跳过或拒绝。
  • 为什么只有 AskUserQuestion 能折叠? 折叠收起的是选项交互区,上下文(题干、题号进度)留在原位,不产生盲判风险;其余确认类型的卡体就是审批依据(命令行、diff、参数),收起它会让用户在看不到内容的情况下批准。所以折叠键只挂在提问弹窗上,不追求「所有确认类型都有」的统一。
  • 折叠态会不会被误当成新确认? 不会:折叠态下标题、题干与底部三按钮仍在原位,只有选项区收起;新确认(队列下一项)总是以展开态出现。

假设 ​

  • 渲染这些问题的 UI 组件已存在或将作为 code 包的一部分实现。
  • AskUserQuestion 工具将被视为需要用户交互的"受限"工具,类似于 Bash 或 Write。
  • "Other" 选项由 UI 自动处理,agent 不需要在工具的输入模式中明确定义。