传统的 AI 交互遵循一种常见模式:你给出提示词,AI 自行做出各种假设,然后生成回复。当这些假设与你的实际需求不符时,你就需要反复进行修正。每一个假设都会带来额外的返工,浪费时间与上下文。
如果 AI 智能体能够在给出答案之前先向你提出澄清问题,会怎么样?
AskUserQuestionTool 正是为此而生。它让 AI 智能体在回答之前先提出澄清问题,以交互式方式收集需求,并从一开始就创建与你的实际需求完全匹配的结果。
Spring AI 的这一实现将“先澄清再处理”这种交互模式带入 Java 生态系统,并确保大模型可移植性 —— 你只需定义一次问题处理器,即可在 OpenAI、Anthropic、Google Gemini 或任何其他受支持的模型中使用。
AskUserQuestionTool 可以将 AI 智能体转变为能够以交互式方式收集需求的协作伙伴。
AskUserQuestionTool 工作原理
AskUserQuestionTool 是 spring-ai-agent-utils 工具包的一部分,是 Claude Code 的 AskUserQuestion 工具在 Spring AI 中的可移植实现,让 AI 智能体在执行过程中可以向用户提出选择题式问题。

该工具遵循问答工作流:
- AI 生成问题: 智能体判断需要用户输入,并构造问题(每个问题包含问题文本、标题、2–4 个选项,以及多选标识),然后调用
askUserQuestion工具函数。 - 用户提供答案: 你的自定义处理器接收这些问题,通过你的界面展示给用户,收集答案并返回给 AI。
- 追加提问: 如有必要,重复步骤 1 和 2,收集更多用户反馈。
- AI 基于上下文继续执行: 智能体使用收集到的答案提供定制化解决方案。
每个问题支持:
- 单选或多选
- 自由文本输入: 用户始终可以提供预定义选项之外的自定义内容
- 丰富上下文: 每个选项都包含说明,解释其含义与权衡
可移植性与模型无关,无厂商锁定
与绑定特定大模型平台的实现不同,Spring AI 的这一实现可跨多家大模型提供商使用,让你无需重写代码或问题处理器即可切换模型。
与 MCP Elicitation 的关系
AskUserQuestionTool 是智能体本地的交互式用户输入方案,在概念上与 MCP Elicitation 能力类似。MCP Elicitation 让 MCP 服务器通过 JSON 模式请求结构化用户输入,而 AskUserQuestionTool 无需依赖 MCP 服务器,即可在你的智能体内部直接提供相同的交互式模式。Spring AI 还通过 @McpElicitation 注解为服务端驱动场景提供完整的 MCP Elicitation 支持。
示例:旅行推荐助手
以下是旅行推荐场景中的实际应用(来自 ask-user-question-demo 示例):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
用户:下次去欧洲哪个国家玩? 兴趣偏好:你旅行时最关注什么? 1. 历史与文化 — 博物馆、历史古迹、建筑 2. 自然与户外 — 徒步、海滩、山脉、国家公园 3. 美食与饮品 — 美食体验、葡萄酒产区、美食之旅 4. 城市与都市 — 城市探索、购物、夜生活 (输入数字用逗号分隔,或输入自定义内容) 1,2 出行季节:你计划什么时候出行? 1. 春季 — 3–5月 — 气温温和、游客较少 2. 夏季 — 6–8月 — 天气温暖、旺季 3. 秋季 — 9–11月 — 气温凉爽、秋色宜人 4. 冬季 — 12–2月 — 圣诞市集、冬季运动 (输入一个数字,或输入自定义内容) 3 预算:这次旅行的大致预算水平? 1. … … 助手:根据你偏好秋季出行、喜欢历史文化与自然、中等预算,同时希望热门景点与小众体验结合,以下是值得推荐的欧洲国家: 1. 葡萄牙 — 里斯本历史城区、杜罗河谷葡萄酒丰收、阿尔加维海岸线。物价友好,秋季气温舒适。 2. 希腊 — 古迹众多、美丽岛屿,秋季仍适合游泳,游客更少。 3. 斯洛文尼亚 — 布莱德湖、朱利安阿尔卑斯、秋色壮观。比邻国奥地利、意大利性价比更高。 |
智能体通过交互式问题完整收集需求,然后提供个性化推荐 —— 无假设、无反复修正。
快速开始
1. 添加依赖:
|
1 2 3 4 5 |
<dependency> <groupId>org.springaicommunity</groupId> <artifactId>spring-ai-agent-utils</artifactId> <version>0.3.0</version> </dependency> |
2. 配置智能体:
|
1 2 3 4 5 6 |
AskUserQuestionTool questionTool = AskUserQuestionTool.builder() .questionHandler(this::handleQuestions) .build() ChatClient chatClient = chatClientBuilder.defaultTools(questionTool) .build(); |
3. 实现 QuestionHandler
使用下面的控制台或 Web 示例实现 QuestionHandler。
当智能体需要澄清信息时,会自动调用该工具,并使用答案提供定制化解决方案。
示例代码: ask-user-question-demo
QuestionHandler 示例
1. 基于控制台的 QuestionHandler
代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
private static Map<String, String> handleQuestions(List<Question> questions) { Map<String, String> answers = new HashMap<>(); Scanner scanner = new Scanner(System.in); for (Question q : questions) { System.out.println("\n" + q.header() + ": " + q.question()); for (int i = 0; i < q.options().size(); i++) { Option opt = q.options().get(i); System.out.printf(" %d. %s - %s%n", i + 1, opt.label(), opt.description()); } System.out.println(q.multiSelect() ? "(输入数字用逗号分隔,或输入自定义内容)" : "(输入一个数字,或输入自定义内容)"); String response = scanner.nextLine().trim(); // 解析数字选择或直接作为自由文本 try { String[] parts = response.split(","); List<String> labels = new ArrayList<>(); for (String part : parts) { int index = Integer.parseInt(part.trim()) - 1; if (index >= 0 && index < q.options().size()) { labels.add(q.options().get(index).label()); } } answers.put(q.question(), labels.isEmpty() ? response : String.join(", ", labels)); } catch (NumberFormatException e) { answers.put(q.question(), response); } } return answers; } |
代码中的 Handler 实现会展示问题选项,接受数字选择(如 “1,2”)或自由文本(如 “中等预算”),并将答案返回给智能体。
2. 基于 Web 的 QuestionHandler
对于 Web 应用,使用 CompletableFuture 桥接异步 UI 交互与同步 QuestionHandler API 来获取问题。
而后通过 WebSocket/SSE 将问题发送到前端,并在 future.get() 处阻塞;当用户通过 REST 接口提交答案时,完成该 future。
这里没有示例代码了。
结论
AskUserQuestionTool 将 AI 智能体从 “基于假设的应答者” 转变为 “先收集需求再行动的协作者”,从而一次就能给出符合需求的答案。