package com.xly.tool; import com.fasterxml.jackson.databind.ObjectMapper; import dev.langchain4j.agent.tool.P; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * AskUser 工具(架构 §5 #4)——消歧 / 澄清用的小问题:一句问题 + 若干可点选项(也允许自由输入)。 * *

工具返回结构化 JSON;{@code AgentChatController} 侦测到 {@code type=question} 后推一条 SSE * {@code question} 事件,前端渲染可点选项片。用户点选/输入的答复作为下一条消息进入对话,凭 ChatMemory * 自然接续(无需额外的 pending 状态机即可完成一轮澄清)。工具无状态、单例。 */ @Component public class InteractionTool { private final ObjectMapper mapper; public InteractionTool(ObjectMapper mapper) { this.mapper = mapper; } @Tool("向用户提出一个澄清/消歧的小问题并给出可选项。当用户意图不明确、或需要在几个候选中二选一时使用;" + "问完即结束本轮、等待用户回答。options 用中文顿号或逗号分隔(如「本月、上月、本季度」),可留空表示自由回答。") public String askUser( @P("要问用户的问题") String question, @P(value = "候选选项,用、或,分隔;没有明确候选时留空", required = false) String options) { List opts = new ArrayList<>(); if (options != null && !options.isBlank()) { for (String o : options.split("[、,,]")) { String t = o.trim(); if (!t.isEmpty()) { opts.add(t); } } } Map out = new LinkedHashMap<>(); out.put("type", "question"); out.put("question", question == null ? "" : question.trim()); out.put("options", opts); try { return mapper.writeValueAsString(out); } catch (Exception e) { return "{\"type\":\"question\",\"question\":\"" + (question == null ? "" : question) + "\",\"options\":[]}"; } } }