From 4b1fcf453f731c4984781923aa125cb621d46ba5 Mon Sep 17 00:00:00 2001 From: MerCry Date: Tue, 24 Feb 2026 10:27:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(MCA):=20TASK-033=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=97=A7=20AiService=20=E5=92=8C=20AiConfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除旧的 AiService 类 - 删除旧的 AiConfig 类 - 更新 MessageRouterServiceImpl 使用 AiServiceClient - 更新 DebugController 移除 AiService 引用 - 无编译错误 --- .../java/com/wecom/robot/config/AiConfig.java | 30 ---- .../robot/controller/DebugController.java | 42 +---- .../com/wecom/robot/service/AiService.java | 155 ------------------ .../robot/service/MessageProcessService.java | 1 - .../impl/MessageRouterServiceImpl.java | 52 +++--- 5 files changed, 32 insertions(+), 248 deletions(-) delete mode 100644 src/main/java/com/wecom/robot/config/AiConfig.java delete mode 100644 src/main/java/com/wecom/robot/service/AiService.java diff --git a/src/main/java/com/wecom/robot/config/AiConfig.java b/src/main/java/com/wecom/robot/config/AiConfig.java deleted file mode 100644 index ada95c4..0000000 --- a/src/main/java/com/wecom/robot/config/AiConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.wecom.robot.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -@Data -@Component -@ConfigurationProperties(prefix = "ai") -public class AiConfig { - - private boolean enabled; - private String provider; - private DeepSeekConfig deepseek; - private OpenAiConfig openai; - - @Data - public static class DeepSeekConfig { - private String apiKey; - private String baseUrl; - private String model; - } - - @Data - public static class OpenAiConfig { - private String apiKey; - private String baseUrl; - private String model; - } -} diff --git a/src/main/java/com/wecom/robot/controller/DebugController.java b/src/main/java/com/wecom/robot/controller/DebugController.java index 70da513..7c5535c 100644 --- a/src/main/java/com/wecom/robot/controller/DebugController.java +++ b/src/main/java/com/wecom/robot/controller/DebugController.java @@ -2,9 +2,7 @@ package com.wecom.robot.controller; import com.wecom.robot.config.WecomConfig; import com.wecom.robot.dto.ApiResponse; -import com.wecom.robot.dto.ChatCompletionRequest; import com.wecom.robot.entity.Message; -import com.wecom.robot.service.AiService; import com.wecom.robot.service.SessionManagerService; import com.wecom.robot.util.WXBizMsgCrypt; import lombok.RequiredArgsConstructor; @@ -23,7 +21,6 @@ import java.util.Map; public class DebugController { private final WecomConfig wecomConfig; - private final AiService aiService; private final SessionManagerService sessionManagerService; @GetMapping("/config") @@ -108,38 +105,13 @@ public class DebugController { return ApiResponse.success(result); } - @GetMapping("/ai/context") - public ApiResponse> getLastAiContext() { - Map result = new HashMap<>(); - - result.put("systemPrompt", aiService.getSystemPrompt()); - result.put("lastRequestJson", aiService.getLastRequestJson()); - result.put("lastConfidence", aiService.getLastConfidence()); - - List messages = aiService.getLastRequestMessages(); - result.put("messageCount", messages.size()); - - List> messageList = new java.util.ArrayList<>(); - for (ChatCompletionRequest.Message msg : messages) { - Map msgMap = new HashMap<>(); - msgMap.put("role", msg.getRole()); - msgMap.put("content", msg.getContent()); - messageList.add(msgMap); - } - result.put("messages", messageList); - - return ApiResponse.success(result); - } - @GetMapping("/ai/session/{sessionId}/context") public ApiResponse> getSessionAiContext( - @PathVariable String sessionId, - @RequestParam(required = false, defaultValue = "测试消息") String testMessage) { + @PathVariable String sessionId) { Map result = new HashMap<>(); result.put("sessionId", sessionId); - result.put("systemPrompt", aiService.getSystemPrompt()); List history = sessionManagerService.getSessionMessages(sessionId); result.put("historyCount", history.size()); @@ -156,18 +128,6 @@ public class DebugController { } result.put("history", historyList); - List contextMessages = aiService.buildMessagesForTest(history, testMessage); - result.put("contextMessageCount", contextMessages.size()); - - List> contextList = new java.util.ArrayList<>(); - for (ChatCompletionRequest.Message msg : contextMessages) { - Map msgMap = new HashMap<>(); - msgMap.put("role", msg.getRole()); - msgMap.put("content", msg.getContent()); - contextList.add(msgMap); - } - result.put("contextMessages", contextList); - return ApiResponse.success(result); } diff --git a/src/main/java/com/wecom/robot/service/AiService.java b/src/main/java/com/wecom/robot/service/AiService.java deleted file mode 100644 index cbab38b..0000000 --- a/src/main/java/com/wecom/robot/service/AiService.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.wecom.robot.service; - -import com.alibaba.fastjson.JSON; -import com.wecom.robot.config.AiConfig; -import com.wecom.robot.dto.ChatCompletionRequest; -import com.wecom.robot.dto.ChatCompletionResponse; -import com.wecom.robot.entity.Message; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; - -import java.util.ArrayList; -import java.util.List; - -@Slf4j -@Service -@RequiredArgsConstructor -public class AiService { - - private static final String SYSTEM_PROMPT = "你是ash超脑的客服,请用简洁、友好的语言回答客户的问题。" + - "如果不确定答案,请诚实告知客户。" + - "回答要准确、专业,避免过于冗长,要尽量拟人化口语化,自然回答问题。"; - - private final AiConfig aiConfig; - private final RestTemplate restTemplate = new RestTemplate(); - - private double lastConfidence = 1.0; - private List lastRequestMessages = new ArrayList<>(); - private String lastRequestJson = ""; - - public String generateReply(String userMessage, List history) { - if (!aiConfig.isEnabled()) { - return "AI服务暂未开启,请联系人工客服。"; - } - - try { - String provider = aiConfig.getProvider(); - String apiUrl; - String apiKey; - String model; - - if ("deepseek".equalsIgnoreCase(provider)) { - apiUrl = aiConfig.getDeepseek().getBaseUrl() + "/chat/completions"; - apiKey = aiConfig.getDeepseek().getApiKey(); - model = aiConfig.getDeepseek().getModel(); - } else { - apiUrl = aiConfig.getOpenai().getBaseUrl() + "/chat/completions"; - apiKey = aiConfig.getOpenai().getApiKey(); - model = aiConfig.getOpenai().getModel(); - } - - List messages = buildMessages(history, userMessage); - ChatCompletionRequest request = ChatCompletionRequest.create(model, messages); - - lastRequestMessages = messages; - lastRequestJson = JSON.toJSONString(request, true); - - log.info("===== AI请求上下文 ====="); - log.info("Provider: {}, Model: {}", provider, model); - log.info("API URL: {}", apiUrl); - log.info("消息数量: {}", messages.size()); - for (int i = 0; i < messages.size(); i++) { - ChatCompletionRequest.Message msg = messages.get(i); - log.info("[{}] {}: {}", i, msg.getRole(), msg.getContent()); - } - log.info("===== 完整请求JSON ====="); - log.info("\n{}", lastRequestJson); - log.info("========================"); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setBearerAuth(apiKey); - - HttpEntity entity = new HttpEntity<>(JSON.toJSONString(request), headers); - ResponseEntity response = restTemplate.postForEntity(apiUrl, entity, String.class); - - log.info("===== AI响应 ====="); - log.info("Status: {}", response.getStatusCode()); - log.info("Body: {}", response.getBody()); - log.info("=================="); - - ChatCompletionResponse completionResponse = JSON.parseObject(response.getBody(), ChatCompletionResponse.class); - String reply = completionResponse.getContent(); - - lastConfidence = calculateConfidence(reply); - - log.info("AI回复生成成功: confidence={}, reply={}", lastConfidence, reply); - return reply; - - } catch (Exception e) { - log.error("AI回复生成失败", e); - lastConfidence = 0.0; - return "抱歉,我暂时无法回答您的问题,正在为您转接人工客服..."; - } - } - - public double getLastConfidence() { - return lastConfidence; - } - - public List getLastRequestMessages() { - return lastRequestMessages; - } - - public String getLastRequestJson() { - return lastRequestJson; - } - - public String getSystemPrompt() { - return SYSTEM_PROMPT; - } - - public List buildMessagesForTest(List history, String currentMessage) { - return buildMessages(history, currentMessage); - } - - private List buildMessages(List history, String currentMessage) { - List messages = new ArrayList<>(); - - messages.add(new ChatCompletionRequest.Message("system", SYSTEM_PROMPT)); - - int startIndex = Math.max(0, history.size() - 10); - for (int i = startIndex; i < history.size(); i++) { - Message msg = history.get(i); - String role = Message.SENDER_TYPE_CUSTOMER.equals(msg.getSenderType()) ? "user" : "assistant"; - messages.add(new ChatCompletionRequest.Message(role, msg.getContent())); - } - - messages.add(new ChatCompletionRequest.Message("user", currentMessage)); - - return messages; - } - - private double calculateConfidence(String reply) { - if (reply == null || reply.isEmpty()) { - return 0.0; - } - - if (reply.contains("不确定") || reply.contains("不清楚") || reply.contains("无法回答")) { - return 0.4; - } - - if (reply.contains("转接人工") || reply.contains("人工客服")) { - return 0.5; - } - - if (reply.length() < 10) { - return 0.6; - } - - return 0.85; - } -} diff --git a/src/main/java/com/wecom/robot/service/MessageProcessService.java b/src/main/java/com/wecom/robot/service/MessageProcessService.java index 76c6158..34530d8 100644 --- a/src/main/java/com/wecom/robot/service/MessageProcessService.java +++ b/src/main/java/com/wecom/robot/service/MessageProcessService.java @@ -31,7 +31,6 @@ public class MessageProcessService { private static final String CHANNEL_TYPE = "wechat"; private final SessionManagerService sessionManagerService; - private final AiService aiService; private final TransferService transferService; private final WecomApiService wecomApiService; private final WebSocketService webSocketService; diff --git a/src/main/java/com/wecom/robot/service/impl/MessageRouterServiceImpl.java b/src/main/java/com/wecom/robot/service/impl/MessageRouterServiceImpl.java index c444dad..2a75e1e 100644 --- a/src/main/java/com/wecom/robot/service/impl/MessageRouterServiceImpl.java +++ b/src/main/java/com/wecom/robot/service/impl/MessageRouterServiceImpl.java @@ -4,6 +4,8 @@ import com.wecom.robot.adapter.ChannelAdapter; import com.wecom.robot.adapter.TransferCapable; import com.wecom.robot.dto.InboundMessage; import com.wecom.robot.dto.OutboundMessage; +import com.wecom.robot.dto.ai.ChatRequest; +import com.wecom.robot.dto.ai.ChatResponse; import com.wecom.robot.entity.Message; import com.wecom.robot.entity.Session; import com.wecom.robot.service.*; @@ -17,14 +19,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -/** - * 消息路由服务实现 - 渠道无关的消息路由核心逻辑 - * - *

关联 AC: [AC-MCA-08] 统一消息路由, [AC-MCA-09] 状态路由, [AC-MCA-10] 人工客服分发 - * - * @see MessageRouterService - * @see InboundMessage - */ @Slf4j @Service @RequiredArgsConstructor @@ -34,7 +28,7 @@ public class MessageRouterServiceImpl implements MessageRouterService { private static final long IDEMPOTENT_TTL_HOURS = 1; private final SessionManagerService sessionManagerService; - private final AiService aiService; + private final AiServiceClient aiServiceClient; private final TransferService transferService; private final WebSocketService webSocketService; private final Map channelAdapters; @@ -95,20 +89,36 @@ public class MessageRouterServiceImpl implements MessageRouterService { session.getSessionId(), truncateContent(message.getContent())); List history = sessionManagerService.getSessionMessages(session.getSessionId()); - String reply = aiService.generateReply(message.getContent(), history); - double confidence = aiService.getLastConfidence(); + ChatRequest chatRequest = ChatRequest.fromInboundMessage(message); + ChatResponse chatResponse; + try { + chatResponse = aiServiceClient.generateReply(chatRequest).get(); + } catch (Exception e) { + log.error("[AC-MCA-06] AI服务调用失败: {}", e.getMessage()); + chatResponse = ChatResponse.fallbackWithTransfer( + "抱歉,我暂时无法回答您的问题,正在为您转接人工客服...", + e.getMessage() + ); + } + + String reply = chatResponse.getReply(); + double confidence = chatResponse.getConfidence() != null ? chatResponse.getConfidence() : 0.0; int messageCount = sessionManagerService.getMessageCount(session.getSessionId()); - boolean shouldTransfer = transferService.shouldTransferToManual( - message.getContent(), - confidence, - messageCount, - session.getCreatedAt() - ); + boolean shouldTransfer = chatResponse.getShouldTransfer() != null && chatResponse.getShouldTransfer(); + + if (!shouldTransfer) { + shouldTransfer = transferService.shouldTransferToManual( + message.getContent(), + confidence, + messageCount, + session.getCreatedAt() + ); + } if (shouldTransfer) { - handleTransferToManual(session, message, reply); + handleTransferToManual(session, message, reply, chatResponse.getTransferReason()); } else { sendReplyToUser(session, message, reply); } @@ -177,10 +187,10 @@ public class MessageRouterServiceImpl implements MessageRouterService { ); } - private void handleTransferToManual(Session session, InboundMessage message, String reply) { - String reason = transferService.getTransferReason( + private void handleTransferToManual(Session session, InboundMessage message, String reply, String transferReason) { + String reason = transferReason != null ? transferReason : transferService.getTransferReason( message.getContent(), - aiService.getLastConfidence(), + 0.0, sessionManagerService.getMessageCount(session.getSessionId()) );