InteractionTool.java 2.21 KB
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)——消歧 / 澄清用的小问题:一句问题 + 若干可点选项(也允许自由输入)。
 *
 * <p>工具返回结构化 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<String> opts = new ArrayList<>();
        if (options != null && !options.isBlank()) {
            for (String o : options.split("[、,,]")) {
                String t = o.trim();
                if (!t.isEmpty()) {
                    opts.add(t);
                }
            }
        }
        Map<String, Object> 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\":[]}";
        }
    }
}