admin
2025-06-06 9cd825aea53fa5ba0cda1485464af027e27f0ce4
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiBotController.java
@@ -12,24 +12,35 @@
import com.agentsflex.core.message.HumanMessage;
import com.agentsflex.core.message.SystemMessage;
import com.agentsflex.core.prompt.HistoriesPrompt;
import com.agentsflex.core.prompt.ToolPrompt;
import com.agentsflex.core.util.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import tech.aiflowy.ai.config.DifyStreamClient;
import tech.aiflowy.ai.entity.*;
import tech.aiflowy.ai.mapper.AiBotConversationMessageMapper;
import tech.aiflowy.ai.service.*;
import tech.aiflowy.common.ai.ChatManager;
import tech.aiflowy.common.ai.MySseEmitter;
import tech.aiflowy.common.domain.Result;
import tech.aiflowy.common.entity.LoginAccount;
import tech.aiflowy.common.satoken.util.SaTokenUtil;
import tech.aiflowy.common.util.StringUtil;
import tech.aiflowy.common.web.controller.BaseCurdController;
@@ -41,10 +52,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
 * 控制层。
@@ -66,6 +74,10 @@
    private AiBotConversationMessageService aiBotConversationMessageService;
    @Resource
    private AiBotConversationMessageMapper aiBotConversationMessageMapper;
    private static final Logger logger = LoggerFactory.getLogger(AiBotController.class);
    @Autowired
    private RestTemplate restTemplate;
    public AiBotController(AiBotService service, AiLlmService aiLlmService, AiBotWorkflowService aiBotWorkflowService, AiBotKnowledgeService aiBotKnowledgeService, AiBotMessageService aiBotMessageService) {
        super(service);
@@ -121,6 +133,11 @@
     * @param response
     * @return
     */
    @Autowired
    private ObjectMapper objectMapper;
    @PostMapping("chat")
    public SseEmitter chat(@JsonBody(value = "prompt", required = true) String prompt,
                           @JsonBody(value = "botId", required = true) BigInteger botId,
@@ -132,92 +149,183 @@
        if (aiBot == null) {
            return ChatManager.getInstance().sseEmitterForContent("机器人不存在");
        }
        Map<String, Object> llmOptions = aiBot.getLlmOptions();
        String systemPrompt = llmOptions != null ? (String) llmOptions.get("systemPrompt") : null;
        AiLlm aiLlm = aiLlmService.getById(aiBot.getLlmId());
        if (StringUtil.hasText(aiBot.getModelAPI())){
            String apiUrl = aiBot.getModelAPI(); // 替换为实际API URL
            String apiKey = aiBot.getModelKEY(); // 替换为实际API Key
        if (aiLlm == null) {
            return ChatManager.getInstance().sseEmitterForContent("LLM不存在");
            DifyStreamClient client = new DifyStreamClient(apiUrl, apiKey, aiBotMessageService);
            AiBotMessageMemory memory = new AiBotMessageMemory(botId, SaTokenUtil.getLoginAccount().getId(),
                    sessionId, isExternalMsg, aiBotMessageService, aiBotConversationMessageMapper,
                    aiBotConversationMessageService);
            final HistoriesPrompt historiesPrompt = new HistoriesPrompt();
            if (systemPrompt != null) {
                historiesPrompt.setSystemMessage(SystemMessage.of(systemPrompt));
            }
            historiesPrompt.setMemory(memory);
            HumanMessage humanMessage = new HumanMessage(prompt);
            // 添加插件相关的function calling
            appendPluginToolFunction(botId, humanMessage);
            //添加工作流相关的 Function Calling
            appendWorkflowFunctions(botId, humanMessage);
            //添加知识库相关的 Function Calling
            appendKnowledgeFunctions(botId, humanMessage);
            historiesPrompt.addMessage(humanMessage);
            final Boolean[] needClose = {true};
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            MySseEmitter emitter = new MySseEmitter(1000L * 60 * 2); // 2分钟超时
            try {
                String userId = SaTokenUtil.getLoginAccount().getId() + "";
                client.chatStream(prompt, userId, emitter, sessionId, botId);
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
            System.out.println(emitter.toString());
            return emitter;
        }
        else{
            AiLlm aiLlm = aiLlmService.getById(aiBot.getLlmId());
        Llm llm = aiLlm.toLlm();
            if (aiLlm == null) {
                return ChatManager.getInstance().sseEmitterForContent("LLM不存在");
            }
        if (llm == null) {
            return ChatManager.getInstance().sseEmitterForContent("LLM获取为空");
        }
            Llm llm = aiLlm.toLlm();
            if (llm == null) {
                return ChatManager.getInstance().sseEmitterForContent("LLM获取为空");
            }
        AiBotMessageMemory memory = new AiBotMessageMemory(botId, SaTokenUtil.getLoginAccount().getId(),
                sessionId, isExternalMsg, aiBotMessageService, aiBotConversationMessageMapper,
                aiBotConversationMessageService);
            AiBotMessageMemory memory = new AiBotMessageMemory(botId, SaTokenUtil.getLoginAccount().getId(),
                    sessionId, isExternalMsg, aiBotMessageService, aiBotConversationMessageMapper,
                    aiBotConversationMessageService);
        final HistoriesPrompt historiesPrompt = new HistoriesPrompt();
        if (systemPrompt != null) {
            historiesPrompt.setSystemMessage(SystemMessage.of(systemPrompt));
        }
            final HistoriesPrompt historiesPrompt = new HistoriesPrompt();
            if (systemPrompt != null) {
                historiesPrompt.setSystemMessage(SystemMessage.of(systemPrompt));
            }
        historiesPrompt.setMemory(memory);
            historiesPrompt.setMemory(memory);
        HumanMessage humanMessage = new HumanMessage(prompt);
            HumanMessage humanMessage = new HumanMessage(prompt);
        // 添加插件相关的function calling
        appendPluginToolFunction(botId, humanMessage);
            // 添加插件相关的function calling
            appendPluginToolFunction(botId, humanMessage);
        //添加工作流相关的 Function Calling
        appendWorkflowFunctions(botId, humanMessage);
            //添加工作流相关的 Function Calling
            appendWorkflowFunctions(botId, humanMessage);
        //添加知识库相关的 Function Calling
        appendKnowledgeFunctions(botId, humanMessage);
            //添加知识库相关的 Function Calling
            appendKnowledgeFunctions(botId, humanMessage);
        historiesPrompt.addMessage(humanMessage);
            historiesPrompt.addMessage(humanMessage);
        MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2));
            MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2));
        final Boolean[] needClose = {true};
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 统一使用流式处理,无论是否有 Function Calling
        llm.chatStream(historiesPrompt, new StreamResponseListener() {
            @Override
            public void onMessage(ChatContext context, AiMessageResponse response) {
                try {
                    RequestContextHolder.setRequestAttributes(sra, true);
                    if (response != null){
                        // 检查是否需要触发 Function Calling
                        if (response.getFunctionCallers() != null && CollectionUtil.hasItems(response.getFunctionCallers())) {
                            needClose[0] = false;
                            function_call(response, emitter, needClose, historiesPrompt, llm, prompt, false);
                        } else {
                            // 强制流式返回,即使有 Function Calling 也先返回部分结果
                            if (response.getMessage() != null) {
                                String content = response.getMessage().getContent();
                                if (StringUtil.hasText(content)) {
                                    emitter.send(JSON.toJSONString(response.getMessage()));
            final Boolean[] needClose = {true};
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            // 统一使用流式处理,无论是否有 Function Calling
            llm.chatStream(historiesPrompt, new StreamResponseListener() {
                @Override
                public void onMessage(ChatContext context, AiMessageResponse response) {
                    try {
                        RequestContextHolder.setRequestAttributes(sra, true);
                        if (response != null) {
                            // 检查是否需要触发 Function Calling
                            if (response.getFunctionCallers() != null && CollectionUtil.hasItems(response.getFunctionCallers())) {
                                needClose[0] = false;
                                function_call(response, emitter, needClose, historiesPrompt, llm, prompt, false);
                            } else {
                                // 强制流式返回,即使有 Function Calling 也先返回部分结果
                                if (response.getMessage() != null) {
                                    String content = response.getMessage().getContent();
                                    if (StringUtil.hasText(content)) {
                                        System.out.println(response);
                                        emitter.send(JSON.toJSONString(response.getMessage()));
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        emitter.completeWithError(e);
                    }
                } catch (Exception e) {
                    emitter.completeWithError(e);
                }
            }
            @Override
            public void onStop(ChatContext context) {
                if (needClose[0]) {
                    emitter.complete();
                @Override
                public void onStop(ChatContext context) {
                    if (needClose[0]) {
                        emitter.complete();
                    }
                }
            }
            @Override
            public void onFailure(ChatContext context, Throwable throwable) {
                emitter.completeWithError(throwable);
            }
        });
                @Override
                public void onFailure(ChatContext context, Throwable throwable) {
                    emitter.completeWithError(throwable);
                }
            });
        return emitter;
            System.out.println(emitter.toString());
            return emitter;
        }
    }
    public Result save(@RequestBody String jsonStr) {
        // 解析JSON
        JSONObject json = JSONObject.parseObject(jsonStr);
        // 合并所有secondMenuId*字段
        List<Integer> menuIds = new ArrayList<>();
        for (String key : json.keySet()) {
            if (key.startsWith("secondMenuId")) {
                Object value = json.get(key);
                if (value instanceof Integer) {
                    menuIds.add((Integer) value);
                }
            }
        }
        // 保留第一个ID(根据需要调整)
        if (!menuIds.isEmpty()) {
            json.put("secondMenuId", menuIds.get(0));
        }
        // 转换为实体类
        AiBot entity = json.toJavaObject(AiBot.class);
        // 后续处理保持不变
        Result result = onSaveOrUpdateBefore(entity, true);
        if (result != null) return result;
        if (entity == null) {
            throw new NullPointerException("entity is null");
        }
        LoginAccount loginAccount = SaTokenUtil.getLoginAccount();
        commonFiled(entity,loginAccount.getId(),loginAccount.getTenantId(),loginAccount.getDeptId());
        boolean success = service.save(entity);
        onSaveOrUpdateAfter(entity, true);
        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entity.getClass());
        Object[] pkArgs = tableInfo.buildPkSqlArgs(entity);
        return Result.create(success).set("id", pkArgs);
    }
    /**
     * 外部用户调用智能体进行对话
@@ -422,45 +530,34 @@
            }
        }
        List<FunctionCaller> functionCallers = aiMessageResponse.getFunctionCallers();
        if (CollectionUtil.hasItems(functionCallers)) {
            needClose[0] = false;
            for (FunctionCaller functionCaller : functionCallers) {
                Object result = functionCaller.call();
                if (ObjectUtil.isNotEmpty(result)) {
                    String newPrompt = "请根据以下内容回答用户,内容是:\n" + result + "\n 用户的问题是:" + prompt;
                    historiesPrompt.addMessageTemporary(new HumanMessage(newPrompt));
                    llm.chatStream(historiesPrompt, new StreamResponseListener() {
                        @Override
                        public void onMessage(ChatContext context, AiMessageResponse response) {
                            needClose[0] = true;
                            String content = response.getMessage().getContent();
                            Object messageContent = response.getMessage();
                            if (StringUtil.hasText(content)) {
                                String jsonResult = JSON.toJSONString(messageContent);
                                emitter.send(jsonResult);
                            }
                        }
                        @Override
                        public void onStop(ChatContext context) {
                            if (needClose[0]) {
                                System.out.println("function chat complete");
                                emitter.complete();
                            }
                            historiesPrompt.clearTemporaryMessages();
                        }
                        @Override
                        public void onFailure(ChatContext context, Throwable throwable) {
                            emitter.completeWithError(throwable);
                        }
                    });
        System.out.println("function call 接收到的参数message:" + aiMessageResponse);
        llm.chatStream(ToolPrompt.of(aiMessageResponse), new StreamResponseListener() {
            @Override
            public void onMessage(ChatContext context, AiMessageResponse response) {
                System.out.println("function call <UNK>message<UNK>" + aiMessageResponse);
                String content = response.getMessage().getContent();
                if (StringUtil.hasText(content)) {
                    System.out.println("if content"  + content);
                    emitter.send(JSON.toJSONString(response.getMessage()));
                }
            }
        }
            @Override
            public void onStop(ChatContext context) {
                System.out.println("function call complete");
                emitter.complete();
            }
            @Override
            public void onFailure(ChatContext context, Throwable throwable) {
                logger.error("function_call报错:",throwable);
                AiMessage aiMessage = new AiMessage();
                aiMessage.setContent("未查询到相关信息...");
                emitter.send(JSON.toJSONString(aiMessage));
                System.out.println("function call complete with error");
            }
        });
        return JSON.toJSONString(messageContent);
    }
@@ -551,4 +648,4 @@
    }
}
}