| | |
| | | package tech.aiflowy.ai.controller; |
| | | |
| | | import cn.dev33.satoken.annotation.SaIgnore; |
| | | import com.agentsflex.core.message.AiMessage; |
| | | import com.alibaba.fastjson.serializer.SerializeConfig; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.http.ResponseEntity; |
| | | import tech.aiflowy.ai.entity.*; |
| | | import tech.aiflowy.ai.mapper.AiBotConversationMessageMapper; |
| | | import tech.aiflowy.ai.service.*; |
| | |
| | | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.math.BigInteger; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * 控制层。 |
| | |
| | | return Result.success(); |
| | | } |
| | | |
| | | /** |
| | | * 当前系统用户调用对话 |
| | | * @param prompt |
| | | * @param botId |
| | | * @param sessionId |
| | | * @param isExternalMsg |
| | | * @param response |
| | | * @return |
| | | */ |
| | | @PostMapping("chat") |
| | | public SseEmitter chat(@JsonBody(value = "prompt", required = true) String prompt, |
| | | @JsonBody(value = "botId", required = true) BigInteger botId, |
| | |
| | | if (humanMessage.getFunctions() != null && !humanMessage.getFunctions().isEmpty()) { |
| | | try { |
| | | AiMessageResponse aiMessageResponse = llm.chat(historiesPrompt); |
| | | function_call(aiMessageResponse, emitter, needClose, historiesPrompt, llm, prompt); |
| | | function_call(aiMessageResponse, emitter, needClose, historiesPrompt, llm, prompt, false); |
| | | } catch (Exception e) { |
| | | emitter.completeWithError(e); |
| | | } |
| | |
| | | public void onMessage(ChatContext context, AiMessageResponse response) { |
| | | try { |
| | | |
| | | function_call(response, emitter, needClose, historiesPrompt, llm, prompt); |
| | | function_call(response, emitter, needClose, historiesPrompt, llm, prompt, false); |
| | | } catch (Exception e) { |
| | | emitter.completeWithError(e); |
| | | } |
| | |
| | | return emitter; |
| | | } |
| | | |
| | | /** |
| | | * 外部用户调用智能体进行对话 |
| | | * 需要用户传 apiKey 对用户进行身份验证 |
| | | * @return |
| | | * @param stream [true: 返回sse false: 返回json |
| | | */ |
| | | @SaIgnore |
| | | @PostMapping("externalChat") |
| | | public Object externalChat( |
| | | @JsonBody(value = "messages", required = true) List<AiBotMessage> messages, |
| | | @JsonBody(value = "botId", required = true) BigInteger botId, |
| | | @JsonBody(value = "stream", required = false) boolean stream, |
| | | HttpServletResponse response, |
| | | HttpServletRequest request |
| | | ) { |
| | | // 设置响应类型 |
| | | if (stream) { |
| | | response.setContentType("text/event-stream"); |
| | | } else { |
| | | response.setContentType("application/json"); |
| | | } |
| | | |
| | | private void function_call(AiMessageResponse aiMessageResponse, MySseEmitter emitter, Boolean[] needClose, HistoriesPrompt historiesPrompt, Llm llm, String prompt) { |
| | | // 获取 API Key 和 Bot 信息 |
| | | String apiKey = request.getHeader("Authorization"); |
| | | AiBot aiBot = service.getById(botId); |
| | | if (aiBot == null) { |
| | | return createResponse(stream, "机器人不存在"); |
| | | } |
| | | |
| | | Map<String, Object> llmOptions = aiBot.getLlmOptions(); |
| | | AiLlm aiLlm = aiLlmService.getById(aiBot.getLlmId()); |
| | | if (aiLlm == null) { |
| | | return createResponse(stream, "LLM不存在"); |
| | | } |
| | | |
| | | Llm llm = aiLlm.toLlm(); |
| | | AiBotExternalMessageMemory messageMemory = new AiBotExternalMessageMemory(messages); |
| | | HistoriesPrompt historiesPrompt = new HistoriesPrompt(); |
| | | historiesPrompt.setSystemMessage(SystemMessage.of((String) llmOptions.get("systemPrompt"))); |
| | | historiesPrompt.setMemory(messageMemory); |
| | | |
| | | String prompt = messages.get(messages.size() - 1).getContent(); |
| | | HumanMessage humanMessage = new HumanMessage(); |
| | | |
| | | // 添加插件、工作流、知识库相关的 Function Calling |
| | | appendPluginFunctions(botId, humanMessage); |
| | | appendWorkflowFunctions(botId, humanMessage); |
| | | appendKnowledgeFunctions(botId, humanMessage); |
| | | |
| | | historiesPrompt.addMessage(humanMessage); |
| | | |
| | | // 根据 responseType 返回不同的响应 |
| | | if (stream) { |
| | | MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2)); |
| | | final Boolean[] needClose = {true}; |
| | | |
| | | if (humanMessage.getFunctions() != null && !humanMessage.getFunctions().isEmpty()) { |
| | | try { |
| | | AiMessageResponse aiMessageResponse = llm.chat(historiesPrompt); |
| | | function_call(aiMessageResponse, emitter, needClose, historiesPrompt, llm, prompt, true); |
| | | } catch (Exception e) { |
| | | emitter.completeWithError(e); |
| | | } |
| | | |
| | | if (needClose[0]) { |
| | | System.out.println("function chat complete"); |
| | | emitter.complete(); |
| | | } |
| | | } else { |
| | | llm.chatStream(historiesPrompt, new StreamResponseListener() { |
| | | @Override |
| | | public void onMessage(ChatContext context, AiMessageResponse response) { |
| | | try { |
| | | function_call(response, emitter, needClose, historiesPrompt, llm, prompt, true); |
| | | } catch (Exception e) { |
| | | emitter.completeWithError(e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onStop(ChatContext context) { |
| | | |
| | | if (needClose[0]) { |
| | | System.out.println("normal chat complete"); |
| | | emitter.complete(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onFailure(ChatContext context, Throwable throwable) { |
| | | emitter.completeWithError(throwable); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | return emitter; |
| | | } else { |
| | | AiMessageResponse resultFunctionCall; |
| | | if (humanMessage.getFunctions() != null && !humanMessage.getFunctions().isEmpty()) { |
| | | try { |
| | | AiMessageResponse aiMessageResponse = llm.chat(historiesPrompt); |
| | | resultFunctionCall = jsonResultJsonFunctionCall(aiMessageResponse, historiesPrompt, llm, prompt); |
| | | return JSON.toJSONString(resultFunctionCall.getMessage(), new SerializeConfig()); |
| | | } catch (Exception e) { |
| | | return createErrorResponse(e); |
| | | } |
| | | } else { |
| | | AiMessageResponse messageResponse = llm.chat(historiesPrompt); |
| | | resultFunctionCall = jsonResultJsonFunctionCall(messageResponse, historiesPrompt, llm, prompt); |
| | | AiBotExternalMsgJsonResult result = handleMessageResult(resultFunctionCall.getMessage()); |
| | | return JSON.toJSONString(result, new SerializeConfig()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private AiBotExternalMsgJsonResult handleMessageResult(AiMessage aiMessage) { |
| | | AiBotExternalMsgJsonResult messageResult = new AiBotExternalMsgJsonResult(); |
| | | messageResult.setCreated(new Date().getTime()); |
| | | AiBotExternalMsgJsonResult.Usage usage = new AiBotExternalMsgJsonResult.Usage(); |
| | | usage.setTotalTokens(aiMessage.getTotalTokens()); |
| | | usage.setCompletionTokens(aiMessage.getCompletionTokens()); |
| | | usage.setPromptTokens(aiMessage.getPromptTokens()); |
| | | messageResult.setUsage(usage); |
| | | AiBotExternalMsgJsonResult.Choice choice = new AiBotExternalMsgJsonResult.Choice(); |
| | | AiBotExternalMsgJsonResult.Message message = new AiBotExternalMsgJsonResult.Message(); |
| | | message.setContent(aiMessage.getContent()); |
| | | message.setRole("assistant"); |
| | | choice.setMessage(message); |
| | | messageResult.setChoices(choice); |
| | | messageResult.setStatus(aiMessage.getStatus().name()); |
| | | return messageResult; |
| | | } |
| | | |
| | | // 辅助方法:创建响应 |
| | | private Object createResponse(boolean stream, String content) { |
| | | if (stream) { |
| | | MySseEmitter emitter = new MySseEmitter((long) (1000 * 60 * 2)); |
| | | emitter.send(content); |
| | | emitter.complete(); |
| | | return emitter; |
| | | } else { |
| | | return ResponseEntity.ok(content); |
| | | } |
| | | } |
| | | |
| | | // 辅助方法:创建错误响应 |
| | | private Object createErrorResponse(Exception e) { |
| | | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); |
| | | } |
| | | /** |
| | | * |
| | | * @param aiMessageResponse 大模型返回的消息 |
| | | * @param emitter |
| | | * @param needClose 是否需要关闭流 |
| | | * @param historiesPrompt 消息历史记录 |
| | | * @param llm 大模型 |
| | | * @param prompt 提示词 |
| | | * @param isExternalChatApi true 外部系统调用bot false 内部系统调用bot |
| | | */ |
| | | private String function_call(AiMessageResponse aiMessageResponse, MySseEmitter emitter, Boolean[] needClose, HistoriesPrompt historiesPrompt, Llm llm, String prompt, boolean isExternalChatApi) { |
| | | ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); |
| | | RequestContextHolder.setRequestAttributes(sra, true); |
| | | String content = aiMessageResponse.getMessage().getContent(); |
| | | Object messageContent = aiMessageResponse.getMessage(); |
| | | if (StringUtil.hasText(content)) { |
| | | String jsonResult = JSON.toJSONString(messageContent); |
| | | emitter.send(jsonResult); |
| | | // 如果是外部系统调用chat |
| | | if (isExternalChatApi){ |
| | | AiBotExternalMsgJsonResult result = handleMessageStreamJsonResult(aiMessageResponse.getMessage()); |
| | | System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); |
| | | System.out.println(JSON.toJSONString(result, new SerializeConfig())); |
| | | System.out.println("未完测试"); |
| | | |
| | | emitter.send(JSON.toJSONString(result, new SerializeConfig())); |
| | | } else{ |
| | | emitter.send(messageContent.toString()); |
| | | } |
| | | |
| | | } |
| | | List<FunctionCaller> functionCallers = aiMessageResponse.getFunctionCallers(); |
| | | if (CollectionUtil.hasItems(functionCallers)) { |
| | |
| | | } |
| | | } |
| | | } |
| | | return JSON.toJSONString(messageContent); |
| | | } |
| | | |
| | | private AiBotExternalMsgJsonResult handleMessageStreamJsonResult(AiMessage message) { |
| | | AiBotExternalMsgJsonResult result = new AiBotExternalMsgJsonResult(); |
| | | AiBotExternalMsgJsonResult.Choice choice = new AiBotExternalMsgJsonResult.Choice(); |
| | | AiBotExternalMsgJsonResult.Delta delta = new AiBotExternalMsgJsonResult.Delta(); |
| | | delta.setRole("assistant"); |
| | | delta.setContent(message.getContent()); |
| | | choice.setDelta(delta); |
| | | result.setCreated(new Date().getTime()); |
| | | result.setChoices(choice); |
| | | result.setStatus(message.getStatus().name()); |
| | | |
| | | return result; |
| | | } |
| | | |
| | | private AiMessageResponse jsonResultJsonFunctionCall(AiMessageResponse aiMessageResponse , HistoriesPrompt historiesPrompt, Llm llm, String prompt) { |
| | | List<FunctionCaller> functionCallers = aiMessageResponse.getFunctionCallers(); |
| | | if (CollectionUtil.hasItems(functionCallers)) { |
| | | for (FunctionCaller functionCaller : functionCallers) { |
| | | Object result = functionCaller.call(); |
| | | if (ObjectUtil.isNotEmpty(result)) { |
| | | String newPrompt = "请根据以下内容回答用户,内容是:\n" + result + "\n 用户的问题是:" + prompt; |
| | | historiesPrompt.addMessageTemporary(new HumanMessage(newPrompt)); |
| | | return llm.chat(historiesPrompt); |
| | | } |
| | | } |
| | | } |
| | | return aiMessageResponse; |
| | | } |
| | | |
| | | private void appendWorkflowFunctions(BigInteger botId, HumanMessage humanMessage) { |