package com.zy.ai.service;
|
|
import com.alibaba.fastjson.JSON;
|
import com.zy.ai.entity.ChatCompletionRequest;
|
import com.zy.ai.entity.WcsDiagnosisRequest;
|
import com.zy.common.utils.RedisUtil;
|
import com.zy.core.enums.RedisKeyType;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Map;
|
|
@Service
|
@RequiredArgsConstructor
|
public class WcsDiagnosisService {
|
|
private final LlmChatService llmChatService;
|
private final RedisUtil redisUtil;
|
private static final long CHAT_TTL_SECONDS = 7L * 24 * 3600;
|
|
/**
|
* 针对“系统不执行任务 / 不知道哪个设备没在运行”的通用 AI 诊断
|
*/
|
public String diagnose(WcsDiagnosisRequest request) {
|
List<ChatCompletionRequest.Message> messages = new ArrayList<>();
|
|
// 1. system:定义专家身份 + 输出结构
|
ChatCompletionRequest.Message system = new ChatCompletionRequest.Message();
|
system.setRole("system");
|
system.setContent(
|
"你是一名资深 WCS(仓储控制系统)与自动化立库专家,熟悉:堆垛机、输送线、提升机、穿梭车等设备的任务分配和运行逻辑," +
|
"也熟悉常见的系统卡死、任务不执行、设备空闲但无任务等问题模式。\n\n" +
|
"你将收到以下几类数据:\n" +
|
"1)任务信息(tasks):当前待执行/在执行/挂起任务\n" +
|
"2)设备实时数据(deviceRealtimeData):每台设备当前状态、是否在线、当前任务号等\n" +
|
"3)设备配置信息(deviceConfigs):设备是否启用、服务区域、允许的任务类型等\n" +
|
"4)系统日志(logs):按时间顺序的日志文本\n" +
|
"5)额外上下文(extraContext):如仓库代码、WCS 版本等\n\n" +
|
"你的目标是:帮助现场运维人员分析,为什么系统当前不执行任务,或者任务执行效率异常,指出可能是哪些设备导致的问题。\n\n" +
|
"请按以下结构输出诊断结果(使用简体中文):\n" +
|
"1. 问题概述(1-3 句话,概括当前系统状态)\n" +
|
"2. 可疑设备列表(列出 1-N 个设备编号,并说明每个设备为什么可疑,例如:配置禁用/长时间空闲/状态异常/任务分配不到它等)\n" +
|
"3. 可能原因(从任务分配、设备状态、配置错误、接口/通信异常等角度,列出 3-7 条)\n" +
|
"4. 建议排查步骤(步骤 1、2、3...,每步要尽量具体、可操作,例如:在某页面查看某字段、检查某个开关、对比某个状态位等)\n" +
|
"5. 风险评估(说明当前问题对业务影响程度:高/中/低,以及是否需要立即人工干预)\n" +
|
"6. WCS 逻辑优化建议(如果从日志/数据看出可能的系统逻辑缺陷,请给出简要建议,例如增加某个防呆校验、告警、监控等)\n"
|
);
|
messages.add(system);
|
|
ChatCompletionRequest.Message user = new ChatCompletionRequest.Message();
|
user.setRole("user");
|
user.setContent(buildDiagnosisUserContent(request));
|
messages.add(user);
|
|
// 调用大模型
|
return llmChatService.chat(messages, 0.2, 2048);
|
}
|
|
public void diagnoseStream(WcsDiagnosisRequest request, SseEmitter emitter) {
|
List<ChatCompletionRequest.Message> messages = new ArrayList<>();
|
|
ChatCompletionRequest.Message system = new ChatCompletionRequest.Message();
|
system.setRole("system");
|
system.setContent(
|
"你是一名资深 WCS(仓储控制系统)与自动化立库专家,熟悉:堆垛机、输送线、提升机、穿梭车等设备的任务分配和运行逻辑,也熟悉常见的系统卡死、任务不执行、设备空闲但无任务等问题模式。\n\n" +
|
"你将收到以下几类数据:\n" +
|
"1)任务信息(tasks):当前待执行/在执行/挂起任务\n" +
|
"2)设备实时数据(deviceRealtimeData):每台设备当前状态、是否在线、当前任务号等\n" +
|
"3)设备配置信息(deviceConfigs):设备是否启用、服务区域、允许的任务类型等\n" +
|
"4)系统日志(logs):按时间顺序的日志文本\n" +
|
"5)额外上下文(extraContext):如仓库代码、WCS 版本等\n\n" +
|
"你的目标是:帮助现场运维人员分析,为什么系统当前不执行任务,或者任务执行效率异常,指出可能是哪些设备导致的问题。\n\n" +
|
"请按以下结构输出诊断结果(使用简体中文):\n" +
|
"1. 问题概述(1-3 句话,概括当前系统状态)\n" +
|
"2. 可疑设备列表(列出 1-N 个设备编号,并说明每个设备为什么可疑,例如:配置禁用/长时间空闲/状态异常/任务分配不到它等)\n" +
|
"3. 可能原因(从任务分配、设备状态、配置错误、接口/通信异常等角度,列出 3-7 条)\n" +
|
"4. 建议排查步骤(步骤 1、2、3...,每步要尽量具体、可操作,例如:在某页面查看某字段、检查某个开关、对比某个状态位等)\n" +
|
"5. 风险评估(说明当前问题对业务影响程度:高/中/低,以及是否需要立即人工干预)\n" +
|
"6. WCS 逻辑优化建议(如果从日志/数据看出可能的系统逻辑缺陷,请给出简要建议,例如增加某个防呆校验、告警、监控等)\n"
|
);
|
messages.add(system);
|
|
ChatCompletionRequest.Message user = new ChatCompletionRequest.Message();
|
user.setRole("user");
|
user.setContent(buildDiagnosisUserContent(request));
|
messages.add(user);
|
|
llmChatService.chatStream(messages, 0.2, 2048, s -> {
|
try {
|
// SSE 协议不允许原样携带换行,先转为 \n 传输,前端再还原
|
String safe = s == null ? "" : s.replace("\r", "").replace("\n", "\\n");
|
if (!safe.isEmpty()) {
|
emitter.send(SseEmitter.event().data(safe));
|
}
|
} catch (Exception ignore) {}
|
}, () -> {
|
try { emitter.complete(); } catch (Exception ignore) {}
|
}, e -> {
|
try { emitter.completeWithError(e); } catch (Exception ignore) {}
|
});
|
}
|
|
public void askStream(WcsDiagnosisRequest request,
|
String prompt,
|
String chatId,
|
boolean reset,
|
SseEmitter emitter) {
|
List<ChatCompletionRequest.Message> base = new ArrayList<>();
|
|
ChatCompletionRequest.Message system = new ChatCompletionRequest.Message();
|
system.setRole("system");
|
system.setContent(
|
"你是一名资深 WCS(仓储控制系统)与自动化立库专家,熟悉:堆垛机、输送线、提升机、穿梭车等设备的任务分配和运行逻辑,也熟悉常见的系统卡死、任务不执行、设备空闲但无任务等问题模式。\n\n" +
|
"在回答用户问题时,需要结合下面给出的系统当前上下文信息(任务、设备实时状态、设备配置、系统日志等),以简洁、明确的中文作答,并在需要时给出可执行的排查建议。"
|
);
|
base.add(system);
|
|
List<ChatCompletionRequest.Message> history = null;
|
String historyKey = null;
|
String metaKey = null;
|
if (chatId != null && !chatId.isEmpty()) {
|
historyKey = RedisKeyType.AI_CHAT_HISTORY.key + chatId;
|
metaKey = RedisKeyType.AI_CHAT_META.key + chatId;
|
if (reset) {
|
redisUtil.del(historyKey, metaKey);
|
}
|
List<Object> stored = redisUtil.lGet(historyKey, 0, -1);
|
if (stored != null && !stored.isEmpty()) {
|
history = new ArrayList<>(stored.size());
|
for (Object o : stored) {
|
ChatCompletionRequest.Message m = convertToMessage(o);
|
if (m != null) history.add(m);
|
}
|
if (!history.isEmpty()) base.addAll(history);
|
} else {
|
history = new ArrayList<>();
|
}
|
}
|
|
ChatCompletionRequest.Message contextMsg = new ChatCompletionRequest.Message();
|
contextMsg.setRole("user");
|
contextMsg.setContent(buildAskUserContent(request));
|
base.add(contextMsg);
|
|
ChatCompletionRequest.Message questionMsg = new ChatCompletionRequest.Message();
|
questionMsg.setRole("user");
|
questionMsg.setContent("【用户提问】\n" + (prompt == null ? "" : prompt));
|
base.add(questionMsg);
|
|
StringBuilder assistantBuffer = new StringBuilder();
|
final String finalChatId = chatId;
|
final String finalHistoryKey = historyKey;
|
final String finalMetaKey = metaKey;
|
final String finalPrompt = prompt;
|
|
llmChatService.chatStream(base, 0.2, 2048, s -> {
|
try {
|
String safe = s == null ? "" : s.replace("\r", "").replace("\n", "\\n");
|
if (!safe.isEmpty()) {
|
emitter.send(SseEmitter.event().data(safe));
|
assistantBuffer.append(s);
|
}
|
} catch (Exception ignore) {}
|
}, () -> {
|
try {
|
if (finalChatId != null && !finalChatId.isEmpty()) {
|
ChatCompletionRequest.Message q = new ChatCompletionRequest.Message();
|
q.setRole("user");
|
q.setContent(finalPrompt == null ? "" : finalPrompt);
|
ChatCompletionRequest.Message a = new ChatCompletionRequest.Message();
|
a.setRole("assistant");
|
a.setContent(assistantBuffer.toString());
|
redisUtil.lSet(finalHistoryKey, q);
|
redisUtil.lSet(finalHistoryKey, a);
|
redisUtil.expire(finalHistoryKey, CHAT_TTL_SECONDS);
|
Map<Object, Object> old = redisUtil.hmget(finalMetaKey);
|
Long createdAt = old != null && old.get("createdAt") != null ?
|
(old.get("createdAt") instanceof Number ? ((Number) old.get("createdAt")).longValue() : Long.valueOf(String.valueOf(old.get("createdAt"))))
|
: System.currentTimeMillis();
|
Map<String, Object> meta = new java.util.HashMap<>();
|
meta.put("chatId", finalChatId);
|
meta.put("title", buildTitleFromPrompt(finalPrompt));
|
meta.put("createdAt", createdAt);
|
meta.put("updatedAt", System.currentTimeMillis());
|
redisUtil.hmset(finalMetaKey, meta, CHAT_TTL_SECONDS);
|
}
|
emitter.complete();
|
} catch (Exception ignore) {}
|
}, e -> {
|
try { emitter.completeWithError(e); } catch (Exception ignore) {}
|
});
|
}
|
|
public List<Map<String, Object>> listChats() {
|
java.util.Set<String> keys = redisUtil.scanKeys(RedisKeyType.AI_CHAT_META.key, 1000);
|
List<Map<String, Object>> resp = new ArrayList<>();
|
if (keys != null) {
|
for (String key : keys) {
|
Map<Object, Object> m = redisUtil.hmget(key);
|
if (m != null && !m.isEmpty()) {
|
java.util.HashMap<String, Object> item = new java.util.HashMap<>();
|
for (Map.Entry<Object, Object> e : m.entrySet()) {
|
item.put(String.valueOf(e.getKey()), e.getValue());
|
}
|
String chatId = String.valueOf(item.get("chatId"));
|
String historyKey = RedisKeyType.AI_CHAT_HISTORY.key + chatId;
|
item.put("size", redisUtil.lGetListSize(historyKey));
|
resp.add(item);
|
}
|
}
|
}
|
return resp;
|
}
|
|
public boolean deleteChat(String chatId) {
|
if (chatId == null || chatId.isEmpty()) return false;
|
String historyKey = RedisKeyType.AI_CHAT_HISTORY.key + chatId;
|
String metaKey = RedisKeyType.AI_CHAT_META.key + chatId;
|
redisUtil.del(historyKey, metaKey);
|
return true;
|
}
|
|
public List<ChatCompletionRequest.Message> getChatHistory(String chatId) {
|
if (chatId == null || chatId.isEmpty()) return java.util.Collections.emptyList();
|
String historyKey = RedisKeyType.AI_CHAT_HISTORY.key + chatId;
|
List<Object> stored = redisUtil.lGet(historyKey, 0, -1);
|
List<ChatCompletionRequest.Message> result = new ArrayList<>();
|
if (stored != null) {
|
for (Object o : stored) {
|
ChatCompletionRequest.Message m = convertToMessage(o);
|
if (m != null) result.add(m);
|
}
|
}
|
return result;
|
}
|
|
private ChatCompletionRequest.Message convertToMessage(Object o) {
|
if (o instanceof ChatCompletionRequest.Message) {
|
return (ChatCompletionRequest.Message) o;
|
}
|
if (o instanceof Map) {
|
Map<?, ?> map = (Map<?, ?>) o;
|
ChatCompletionRequest.Message m = new ChatCompletionRequest.Message();
|
Object role = map.get("role");
|
Object content = map.get("content");
|
m.setRole(role == null ? null : String.valueOf(role));
|
m.setContent(content == null ? null : String.valueOf(content));
|
return m;
|
}
|
return null;
|
}
|
|
private String buildTitleFromPrompt(String prompt) {
|
if (prompt == null || prompt.isEmpty()) return "未命名会话";
|
String p = prompt.replaceAll("\n", " ").trim();
|
return p.length() > 20 ? p.substring(0, 20) : p;
|
}
|
|
private String buildDiagnosisUserContent(WcsDiagnosisRequest request) {
|
StringBuilder sb = new StringBuilder();
|
|
if (request.getAlarmMessage() != null && !request.getAlarmMessage().isEmpty()) {
|
sb.append("【问题描述】\n");
|
sb.append(request.getAlarmMessage()).append("\n\n");
|
}
|
|
sb.append("【设备信息】\n");
|
sb.append("关注设备(如果有指定): ")
|
.append(request.getCraneNo() != null ? request.getCraneNo() : "未指定,需整体分析")
|
.append("\n\n");
|
|
Object pseudo = redisUtil.get(com.zy.core.enums.RedisKeyType.MAIN_PROCESS_PSEUDOCODE.key);
|
if (pseudo != null) {
|
sb.append("【主流程伪代码 mainProcessPseudo】\n");
|
sb.append(String.valueOf(pseudo)).append("\n\n");
|
}
|
|
if (request.getExtraContext() != null && !request.getExtraContext().isEmpty()) {
|
sb.append("【额外上下文 extraContext】\n");
|
sb.append(JSON.toJSONString(request.getExtraContext(), true)).append("\n\n");
|
}
|
|
if (request.getTasks() != null && !request.getTasks().isEmpty()) {
|
sb.append("【任务信息 tasks】\n");
|
sb.append("下面是当前相关任务列表的 JSON 数据:\n");
|
sb.append(JSON.toJSONString(request.getTasks(), true)).append("\n\n");
|
} else {
|
sb.append("【任务信息 tasks】\n");
|
sb.append("当前未提供任务信息。\n\n");
|
}
|
|
if (request.getDeviceRealtimeData() != null && !request.getDeviceRealtimeData().isEmpty()) {
|
sb.append("【设备实时数据 deviceRealtimeData】\n");
|
sb.append("下面是各设备当前实时状态的 JSON 数据:\n");
|
sb.append(JSON.toJSONString(request.getDeviceRealtimeData(), true)).append("\n\n");
|
} else {
|
sb.append("【设备实时数据 deviceRealtimeData】\n");
|
sb.append("当前未提供设备实时数据。\n\n");
|
}
|
|
if (request.getDeviceConfigs() != null && !request.getDeviceConfigs().isEmpty()) {
|
sb.append("【设备配置信息 deviceConfigs】\n");
|
sb.append("下面是各设备配置的 JSON 数据:\n");
|
sb.append(JSON.toJSONString(request.getDeviceConfigs(), true)).append("\n\n");
|
} else {
|
sb.append("【设备配置信息 deviceConfigs】\n");
|
sb.append("当前未提供设备配置信息。\n\n");
|
}
|
|
sb.append("【系统日志 logs(按时间顺序)】\n");
|
if (request.getLogs() != null && !request.getLogs().isEmpty()) {
|
for (String logLine : request.getLogs()) {
|
sb.append(logLine).append("\n");
|
}
|
} else {
|
sb.append("当前未提供日志信息。\n");
|
}
|
|
sb.append("\n请根据以上所有信息,结合你的经验进行分析诊断。");
|
|
return sb.toString();
|
}
|
|
private String buildAskUserContent(WcsDiagnosisRequest request) {
|
StringBuilder sb = new StringBuilder();
|
|
if (request.getExtraContext() != null && !request.getExtraContext().isEmpty()) {
|
sb.append("【额外上下文 extraContext】\n");
|
sb.append(JSON.toJSONString(request.getExtraContext(), true)).append("\n\n");
|
}
|
|
if (request.getTasks() != null && !request.getTasks().isEmpty()) {
|
sb.append("【任务信息 tasks】\n");
|
sb.append("下面是当前相关任务列表的 JSON 数据:\n");
|
sb.append(JSON.toJSONString(request.getTasks(), true)).append("\n\n");
|
}
|
|
if (request.getDeviceRealtimeData() != null && !request.getDeviceRealtimeData().isEmpty()) {
|
sb.append("【设备实时数据 deviceRealtimeData】\n");
|
sb.append("下面是各设备当前实时状态的 JSON 数据:\n");
|
sb.append(JSON.toJSONString(request.getDeviceRealtimeData(), true)).append("\n\n");
|
}
|
|
if (request.getDeviceConfigs() != null && !request.getDeviceConfigs().isEmpty()) {
|
sb.append("【设备配置信息 deviceConfigs】\n");
|
sb.append("下面是各设备配置的 JSON 数据:\n");
|
sb.append(JSON.toJSONString(request.getDeviceConfigs(), true)).append("\n\n");
|
}
|
|
sb.append("【系统日志 logs(按时间顺序)】\n");
|
if (request.getLogs() != null && !request.getLogs().isEmpty()) {
|
for (String logLine : request.getLogs()) {
|
sb.append(logLine).append("\n");
|
}
|
}
|
|
return sb.toString();
|
}
|
}
|