From 1b46b60b3f7f4454731c1eeda312e72f0879096d Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Wed, 10 Dec 2025 11:57:25 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ai):=20=E9=87=8D=E6=9E=84AI=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E6=9C=8D=E5=8A=A1=E5=AE=9E=E7=8E=B0=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重命名变量名 log 为 commandLog 以提高代码可读性 --- .../service/impl/AiCommandLogServiceImpl.java | 8 +- .../ai/service/impl/AiCommandServiceImpl.java | 530 +++++++++--------- 2 files changed, 269 insertions(+), 269 deletions(-) diff --git a/src/main/java/com/youlai/boot/platform/ai/service/impl/AiCommandLogServiceImpl.java b/src/main/java/com/youlai/boot/platform/ai/service/impl/AiCommandLogServiceImpl.java index 05f1143a..fb0eea05 100644 --- a/src/main/java/com/youlai/boot/platform/ai/service/impl/AiCommandLogServiceImpl.java +++ b/src/main/java/com/youlai/boot/platform/ai/service/impl/AiCommandLogServiceImpl.java @@ -32,17 +32,17 @@ public class AiCommandLogServiceImpl extends ServiceImpl result.getOutput().getText()) + .orElse(""); + + ParseResult parseResult = parseAiResponse(rawContent); + + commandLog.setAiProvider(StrUtil.emptyToDefault(parseResult.provider(), "spring-ai")); + commandLog.setAiModel(StrUtil.emptyToDefault(parseResult.model(), "auto")); + commandLog.setParseStatus(parseResult.success() ? 1 : 0); + commandLog.setExplanation(parseResult.explanation()); + commandLog.setFunctionCalls(JSONUtil.toJsonStr(parseResult.functionCalls())); + commandLog.setConfidence(parseResult.confidence() != null ? BigDecimal.valueOf(parseResult.confidence()) : null); + commandLog.setParseErrorMessage(parseResult.success() ? null : StrUtil.emptyToDefault(parseResult.error(), "解析失败")); + long duration = System.currentTimeMillis() - startTime; + commandLog.setParseDurationMs((int) duration); + + logService.save(commandLog); + + AiParseResponseDTO response = AiParseResponseDTO.builder() + .parseLogId(commandLog.getId()) + .success(parseResult.success()) + .functionCalls(parseResult.functionCalls()) + .explanation(parseResult.explanation()) + .confidence(parseResult.confidence()) + .error(parseResult.error()) + .rawResponse(rawContent) + .build(); + + if (!parseResult.success()) { + log.warn("❗️ AI 未能解析命令: {}", parseResult.error()); + } else { + log.info("✅ 解析成功,审计记录ID: {}", commandLog.getId()); + } + + return response; + } catch (Exception e) { + long duration = System.currentTimeMillis() - startTime; + commandLog.setParseStatus(0); + commandLog.setFunctionCalls(JSONUtil.toJsonStr(Collections.emptyList())); + commandLog.setParseErrorMessage(e.getMessage()); + commandLog.setParseDurationMs((int) duration); + logService.save(commandLog); + + log.error("❌ 解析命令失败: {}", e.getMessage(), e); + throw new RuntimeException("解析命令失败: " + e.getMessage(), e); + } } - Long userId = SecurityUtils.getUserId(); - String username = SecurityUtils.getUsername(); - String ipAddress = JakartaServletUtil.getClientIP(httpRequest); - - AiCommandLog log = new AiCommandLog(); - log.setUserId(userId); - log.setUsername(username); - log.setOriginalCommand(command); - log.setIpAddress(ipAddress); - log.setAiProvider("spring-ai"); - log.setAiModel("auto"); - - String systemPrompt = buildSystemPrompt(); - String userPrompt = buildUserPrompt(request); - - try { - log.info("📤 发送命令至 AI 模型: {}", command); - ChatResponse chatResponse = chatClient.prompt() - .system(systemPrompt) - .user(userPrompt) - .call().chatResponse(); - - String rawContent = Optional.ofNullable(chatResponse.getResult()) - .map(result -> result.getOutput().getText()) - .orElse(""); - - ParseResult parseResult = parseAiResponse(rawContent); - - log.setAiProvider(StrUtil.emptyToDefault(parseResult.provider(), "spring-ai")); - log.setAiModel(StrUtil.emptyToDefault(parseResult.model(), "auto")); - log.setParseStatus(parseResult.success() ? 1 : 0); - log.setExplanation(parseResult.explanation()); - log.setFunctionCalls(JSONUtil.toJsonStr(parseResult.functionCalls())); - log.setConfidence(parseResult.confidence() != null ? BigDecimal.valueOf(parseResult.confidence()) : null); - log.setParseErrorMessage(parseResult.success() ? null : StrUtil.emptyToDefault(parseResult.error(), "解析失败")); - long duration = System.currentTimeMillis() - startTime; - log.setParseDurationMs((int) duration); - - logService.save(log); - - AiParseResponseDTO response = AiParseResponseDTO.builder() - .parseLogId(log.getId()) - .success(parseResult.success()) - .functionCalls(parseResult.functionCalls()) - .explanation(parseResult.explanation()) - .confidence(parseResult.confidence()) - .error(parseResult.error()) - .rawResponse(rawContent) - .build(); - - if (!parseResult.success()) { - log.warn("❗️ AI 未能解析命令: {}", parseResult.error()); - } else { - log.info("✅ 解析成功,审计记录ID: {}", log.getId()); - } - - return response; - } catch (Exception e) { - long duration = System.currentTimeMillis() - startTime; - log.setParseStatus(0); - log.setFunctionCalls(JSONUtil.toJsonStr(Collections.emptyList())); - log.setParseErrorMessage(e.getMessage()); - log.setParseDurationMs((int) duration); - logService.save(log); - - log.error("❌ 解析命令失败: {}", e.getMessage(), e); - throw new RuntimeException("解析命令失败: " + e.getMessage(), e); - } - } - - private String buildSystemPrompt() { - return SYSTEM_PROMPT; - } - - private String buildUserPrompt(AiParseRequestDTO request) { - JSONObject payload = JSONUtil.createObj() - .set("command", request.getCommand()) - .set("currentRoute", request.getCurrentRoute()) - .set("currentComponent", request.getCurrentComponent()) - .set("context", Optional.ofNullable(request.getContext()).orElse(Collections.emptyMap())) - .set("availableFunctions", availableFunctions()); - - return StrUtil.format(""" - 请根据以下上下文识别用户意图,并输出符合系统提示要求的 JSON: - {} - """, JSONUtil.toJsonPrettyStr(payload)); - } - - private List> availableFunctions() { - return List.of( - Map.of( - "name", "updateUserNickname", - "description", "根据用户名更新用户昵称", - "requiredParameters", List.of("username", "nickname") - ) - ); - } - - private ParseResult parseAiResponse(String rawContent) { - if (StrUtil.isBlank(rawContent)) { - throw new IllegalStateException("AI 返回内容为空"); + private String buildSystemPrompt() { + return SYSTEM_PROMPT; } - try { - JSONObject jsonObject = JSONUtil.parseObj(rawContent); - boolean success = jsonObject.getBool("success", false); - String explanation = jsonObject.getStr("explanation"); - Double confidence = jsonObject.containsKey("confidence") ? jsonObject.getDouble("confidence") : null; - String error = jsonObject.getStr("error"); - String provider = jsonObject.getStr("provider"); - String model = jsonObject.getStr("model"); + private String buildUserPrompt(AiParseRequestDTO request) { + JSONObject payload = JSONUtil.createObj() + .set("command", request.getCommand()) + .set("currentRoute", request.getCurrentRoute()) + .set("currentComponent", request.getCurrentComponent()) + .set("context", Optional.ofNullable(request.getContext()).orElse(Collections.emptyMap())) + .set("availableFunctions", availableFunctions()); - List functionCalls = toFunctionCallList(jsonObject.getJSONArray("functionCalls")); - - return new ParseResult(success, explanation, confidence, error, provider, model, functionCalls); - } catch (Exception ex) { - throw new IllegalStateException("无法解析 AI 响应: " + ex.getMessage(), ex); - } - } - - private List toFunctionCallList(JSONArray array) { - if (array == null || array.isEmpty()) { - return Collections.emptyList(); + return StrUtil.format(""" + 请根据以下上下文识别用户意图,并输出符合系统提示要求的 JSON: + {} + """, JSONUtil.toJsonPrettyStr(payload)); } - List result = new ArrayList<>(); - for (Object element : array) { - JSONObject functionJson = JSONUtil.parseObj(element); - Map arguments = Optional.ofNullable(functionJson.getJSONObject("arguments")) - .map(obj -> obj.toBean(new TypeReference>() { - })) - .orElse(Collections.emptyMap()); - - result.add(AiFunctionCallDTO.builder() - .name(functionJson.getStr("name")) - .description(functionJson.getStr("description")) - .arguments(arguments) - .build()); - } - return result; - } - - private record ParseResult( - boolean success, - String explanation, - Double confidence, - String error, - String provider, - String model, - List functionCalls - ) { - } - - @Override - public Object executeCommand(AiExecuteRequestDTO request, HttpServletRequest httpRequest) throws Exception { - long startTime = System.currentTimeMillis(); - - // 获取用户信息 - Long userId = SecurityUtils.getUserId(); - String username = SecurityUtils.getUsername(); - String ipAddress = JakartaServletUtil.getClientIP(httpRequest); - - AiFunctionCallDTO functionCall = request.getFunctionCall(); - - // 根据解析日志ID获取审计记录,如果不存在则创建新记录 - AiCommandLog log; - if (StrUtil.isNotBlank(request.getParseLogId())) { - // 更新已存在的审计记录(解析阶段已创建) - log = logService.getById(request.getParseLogId()); - if (log == null) { - throw new IllegalStateException("未找到对应的解析记录,ID: " + request.getParseLogId()); - } - } else { - // 如果没有解析日志ID,创建新记录(兼容直接执行的情况) - log = new AiCommandLog(); - log.setUserId(userId); - log.setUsername(username); - log.setOriginalCommand(request.getOriginalCommand()); - log.setIpAddress(ipAddress); - logService.save(log); + private List> availableFunctions() { + return List.of( + Map.of( + "name", "updateUserNickname", + "description", "根据用户名更新用户昵称", + "requiredParameters", List.of("username", "nickname") + ) + ); } - // 更新执行相关字段 - log.setFunctionName(functionCall.getName()); - log.setFunctionArguments(JSONUtil.toJsonStr(functionCall.getArguments())); - log.setExecuteStatus(0); // 0-待执行 + private ParseResult parseAiResponse(String rawContent) { + if (StrUtil.isBlank(rawContent)) { + throw new IllegalStateException("AI 返回内容为空"); + } - try { - // 🎯 执行具体的函数调用 - Object result = executeFunctionCall(functionCall); + try { + JSONObject jsonObject = JSONUtil.parseObj(rawContent); + boolean success = jsonObject.getBool("success", false); + String explanation = jsonObject.getStr("explanation"); + Double confidence = jsonObject.containsKey("confidence") ? jsonObject.getDouble("confidence") : null; + String error = jsonObject.getStr("error"); + String provider = jsonObject.getStr("provider"); + String model = jsonObject.getStr("model"); - // 更新执行成功 - log.setExecuteStatus(1); // 1-成功 - log.setExecuteErrorMessage(null); + List functionCalls = toFunctionCallList(jsonObject.getJSONArray("functionCalls")); - // 更新审计记录 - logService.updateById(log); - - log.info("✅ 命令执行成功,审计记录ID: {}", log.getId()); - - return result; - - } catch (Exception e) { - // 更新执行失败 - log.setExecuteStatus(-1); // -1-失败 - log.setExecuteErrorMessage(e.getMessage()); - - // 更新审计记录 - logService.updateById(log); - - log.error("❌ 命令执行失败,审计记录ID: {}", log.getId(), e); - - // 抛出异常,由 Controller 统一处理 - throw e; - } - } - - /** - * 执行具体的函数调用 - */ - private Object executeFunctionCall(AiFunctionCallDTO functionCall) { - String functionName = functionCall.getName(); - Map arguments = functionCall.getArguments(); - - log.info("🎯 执行函数: {}, 参数: {}", functionName, arguments); - - // 根据函数名称路由到不同的处理器 - switch (functionName) { - case "updateUserNickname": - return executeUpdateUserNickname(arguments); - default: - throw new UnsupportedOperationException("不支持的函数: " + functionName); - } - } - - /** - * 使用 Tool: 根据用户名更新用户昵称 - */ - private Object executeUpdateUserNickname(Map arguments) { - String username = (String) arguments.get("username"); - String nickname = (String) arguments.get("nickname"); - - log.info("🔧 [Tool] 更新用户昵称: username={}, nickname={}", username, nickname); - String resultMsg = userTools.updateUserNickname(username, nickname); - - boolean success = resultMsg != null && resultMsg.contains("成功"); - if (!success) { - throw new RuntimeException(resultMsg != null ? resultMsg : "更新用户昵称失败"); + return new ParseResult(success, explanation, confidence, error, provider, model, functionCalls); + } catch (Exception ex) { + throw new IllegalStateException("无法解析 AI 响应: " + ex.getMessage(), ex); + } } - return Map.of("username", username, "nickname", nickname, "message", resultMsg); - } + private List toFunctionCallList(JSONArray array) { + if (array == null || array.isEmpty()) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(); + for (Object element : array) { + JSONObject functionJson = JSONUtil.parseObj(element); + Map arguments = Optional.ofNullable(functionJson.getJSONObject("arguments")) + .map(obj -> obj.toBean(new TypeReference>() { + })) + .orElse(Collections.emptyMap()); + + result.add(AiFunctionCallDTO.builder() + .name(functionJson.getStr("name")) + .description(functionJson.getStr("description")) + .arguments(arguments) + .build()); + } + return result; + } + + private record ParseResult( + boolean success, + String explanation, + Double confidence, + String error, + String provider, + String model, + List functionCalls + ) { + } + + @Override + public Object executeCommand(AiExecuteRequestDTO request, HttpServletRequest httpRequest) throws Exception { + long startTime = System.currentTimeMillis(); + + // 获取用户信息 + Long userId = SecurityUtils.getUserId(); + String username = SecurityUtils.getUsername(); + String ipAddress = JakartaServletUtil.getClientIP(httpRequest); + + AiFunctionCallDTO functionCall = request.getFunctionCall(); + + // 根据解析日志ID获取审计记录,如果不存在则创建新记录 + AiCommandLog commandLog; + if (StrUtil.isNotBlank(request.getParseLogId())) { + // 更新已存在的审计记录(解析阶段已创建) + commandLog = logService.getById(request.getParseLogId()); + if (commandLog == null) { + throw new IllegalStateException("未找到对应的解析记录,ID: " + request.getParseLogId()); + } + } else { + // 如果没有解析日志ID,创建新记录(兼容直接执行的情况) + commandLog = new AiCommandLog(); + commandLog.setUserId(userId); + commandLog.setUsername(username); + commandLog.setOriginalCommand(request.getOriginalCommand()); + commandLog.setIpAddress(ipAddress); + logService.save(commandLog); + } + + // 更新执行相关字段 + commandLog.setFunctionName(functionCall.getName()); + commandLog.setFunctionArguments(JSONUtil.toJsonStr(functionCall.getArguments())); + commandLog.setExecuteStatus(0); // 0-待执行 + + try { + // 🎯 执行具体的函数调用 + Object result = executeFunctionCall(functionCall); + + // 更新执行成功 + commandLog.setExecuteStatus(1); // 1-成功 + commandLog.setExecuteErrorMessage(null); + + // 更新审计记录 + logService.updateById(commandLog); + + log.info("✅ 命令执行成功,审计记录ID: {}", commandLog.getId()); + + return result; + + } catch (Exception e) { + // 更新执行失败 + commandLog.setExecuteStatus(-1); // -1-失败 + commandLog.setExecuteErrorMessage(e.getMessage()); + + // 更新审计记录 + logService.updateById(commandLog); + + log.error("❌ 命令执行失败,审计记录ID: {}", commandLog.getId(), e); + + // 抛出异常,由 Controller 统一处理 + throw e; + } + } + + /** + * 执行具体的函数调用 + */ + private Object executeFunctionCall(AiFunctionCallDTO functionCall) { + String functionName = functionCall.getName(); + Map arguments = functionCall.getArguments(); + + log.info("🎯 执行函数: {}, 参数: {}", functionName, arguments); + + // 根据函数名称路由到不同的处理器 + switch (functionName) { + case "updateUserNickname": + return executeUpdateUserNickname(arguments); + default: + throw new UnsupportedOperationException("不支持的函数: " + functionName); + } + } + + /** + * 使用 Tool: 根据用户名更新用户昵称 + */ + private Object executeUpdateUserNickname(Map arguments) { + String username = (String) arguments.get("username"); + String nickname = (String) arguments.get("nickname"); + + log.info("🔧 [Tool] 更新用户昵称: username={}, nickname={}", username, nickname); + String resultMsg = userTools.updateUserNickname(username, nickname); + + boolean success = resultMsg != null && resultMsg.contains("成功"); + if (!success) { + throw new RuntimeException(resultMsg != null ? resultMsg : "更新用户昵称失败"); + } + + return Map.of("username", username, "nickname", nickname, "message", resultMsg); + } }