From 319da488ef058274f8bc6af77a3b398efacdd32b Mon Sep 17 00:00:00 2001
From: Junjie <DELL@qq.com>
Date: 星期四, 11 十二月 2025 16:51:16 +0800
Subject: [PATCH] #AI
---
src/main/java/com/zy/ai/entity/WcsDiagnosisResponse.java | 21 ++
src/main/java/com/zy/ai/entity/DeviceRealTimeData.java | 14 +
src/main/java/com/zy/ai/entity/ChatCompletionRequest.java | 21 ++
src/main/java/com/zy/ai/controller/WcsDiagnosisController.java | 118 +++++++++++
src/main/resources/logback-spring.xml | 3
pom.xml | 7
src/main/java/com/zy/ai/entity/DeviceConfigsData.java | 14 +
src/main/java/com/zy/ai/config/LlmConfig.java | 20 ++
src/main/java/com/zy/ai/service/LlmChatService.java | 63 ++++++
src/main/java/com/zy/asrs/utils/Utils.java | 25 +
src/main/java/com/zy/ai/entity/ChatCompletionResponse.java | 45 ++++
src/main/java/com/zy/ai/log/AiLogAppender.java | 53 +++++
src/main/java/com/zy/ai/service/WcsDiagnosisService.java | 117 +++++++++++
src/main/resources/application.yml | 5
src/main/java/com/zy/ai/entity/WcsDiagnosisRequest.java | 57 +++++
15 files changed, 575 insertions(+), 8 deletions(-)
diff --git a/pom.xml b/pom.xml
index 49d2ff1..38b5e6a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
- <version>2.1.3.RELEASE</version>
+ <version>2.5.14</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zy</groupId>
@@ -126,6 +126,11 @@
<artifactId>truelicense-core</artifactId>
<version>1.33</version>
</dependency>
+ <!-- WebClient -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-webflux</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git a/src/main/java/com/zy/ai/config/LlmConfig.java b/src/main/java/com/zy/ai/config/LlmConfig.java
new file mode 100644
index 0000000..385286d
--- /dev/null
+++ b/src/main/java/com/zy/ai/config/LlmConfig.java
@@ -0,0 +1,20 @@
+package com.zy.ai.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@Configuration
+public class LlmConfig {
+
+ @Value("${llm.base-url}")
+ private String baseUrl;
+
+ @Bean
+ public WebClient llmWebClient() {
+ return WebClient.builder()
+ .baseUrl(baseUrl)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/ai/controller/WcsDiagnosisController.java b/src/main/java/com/zy/ai/controller/WcsDiagnosisController.java
new file mode 100644
index 0000000..34167fa
--- /dev/null
+++ b/src/main/java/com/zy/ai/controller/WcsDiagnosisController.java
@@ -0,0 +1,118 @@
+package com.zy.ai.controller;
+
+import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.zy.ai.entity.DeviceConfigsData;
+import com.zy.ai.entity.DeviceRealTimeData;
+import com.zy.ai.entity.WcsDiagnosisRequest;
+import com.zy.ai.entity.WcsDiagnosisResponse;
+import com.zy.ai.log.AiLogAppender;
+import com.zy.ai.service.WcsDiagnosisService;
+import com.zy.asrs.entity.BasCrnp;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.BasCrnpService;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.core.cache.SlaveConnection;
+import com.zy.core.enums.SlaveType;
+import com.zy.core.model.StationObjModel;
+import com.zy.core.model.protocol.CrnProtocol;
+import com.zy.core.model.protocol.StationProtocol;
+import com.zy.core.thread.CrnThread;
+import com.zy.core.thread.StationThread;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@RestController
+@RequestMapping("/ai/diagnose")
+@RequiredArgsConstructor
+public class WcsDiagnosisController {
+
+ @Autowired
+ private WcsDiagnosisService wcsDiagnosisService;
+ @Autowired
+ private WrkMastService wrkMastService;
+ @Autowired
+ private BasCrnpService basCrnpService;
+
+ @GetMapping("/runAi")
+ public WcsDiagnosisResponse runAi() {
+ WcsDiagnosisRequest request = new WcsDiagnosisRequest();
+
+ request.setAlarmMessage("绯荤粺涓嶆墽琛屼换鍔�");
+
+ List<String> logs = AiLogAppender.getRecentLogs(300);
+ request.setLogs(logs);
+
+ List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<>());
+ request.setTasks(wrkMasts);
+
+ List<DeviceRealTimeData> deviceRealTimeDataList = new ArrayList<>();
+ List<DeviceConfigsData> deviceConfigsDataList = new ArrayList<>();
+
+ List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>());
+ for (BasCrnp basCrnp : basCrnps) {
+ CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo());
+ if (crnThread == null) {
+ continue;
+ }
+
+ CrnProtocol protocol = crnThread.getStatus();
+
+ for (StationObjModel stationObjModel : basCrnp.getInStationList$()) {
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
+ if (stationThread == null) {
+ continue;
+ }
+
+ Map<Integer, StationProtocol> map = stationThread.getStatusMap();
+ StationProtocol stationProtocol = map.get(stationObjModel.getStationId());
+ if (stationProtocol == null) {
+ continue;
+ }
+
+ DeviceRealTimeData stationData = new DeviceRealTimeData();
+ stationData.setDeviceNo(stationObjModel.getDeviceNo());
+ stationData.setDeviceType(String.valueOf(SlaveType.Devp));
+ stationData.setDeviceData(stationProtocol);
+ deviceRealTimeDataList.add(stationData);
+ }
+
+
+ DeviceRealTimeData deviceRealTimeData = new DeviceRealTimeData();
+ deviceRealTimeData.setDeviceNo(basCrnp.getCrnNo());
+ deviceRealTimeData.setDeviceType(String.valueOf(SlaveType.Crn));
+ deviceRealTimeData.setDeviceData(protocol);
+ deviceRealTimeDataList.add(deviceRealTimeData);
+
+ DeviceConfigsData deviceConfigsData = new DeviceConfigsData();
+ deviceConfigsData.setDeviceNo(basCrnp.getCrnNo());
+ deviceConfigsData.setDeviceType(String.valueOf(SlaveType.Crn));
+ deviceConfigsData.setDeviceData(basCrnp);
+ deviceConfigsDataList.add(deviceConfigsData);
+ }
+
+ request.setDeviceRealtimeData(deviceRealTimeDataList);
+ WcsDiagnosisResponse response = diagnose(request);
+ return response;
+ }
+
+ /**
+ * POST /api/ai/diagnose/wcs
+ */
+ @PostMapping("/wcs")
+ public WcsDiagnosisResponse diagnose(@RequestBody WcsDiagnosisRequest request) {
+ String analysis = wcsDiagnosisService.diagnose(request);
+
+ WcsDiagnosisResponse resp = new WcsDiagnosisResponse();
+ resp.setAnalysis(analysis);
+ resp.setOriginalRequest(request);
+ return resp;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/ai/entity/ChatCompletionRequest.java b/src/main/java/com/zy/ai/entity/ChatCompletionRequest.java
new file mode 100644
index 0000000..599bee0
--- /dev/null
+++ b/src/main/java/com/zy/ai/entity/ChatCompletionRequest.java
@@ -0,0 +1,21 @@
+package com.zy.ai.entity;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ChatCompletionRequest {
+
+ private String model;
+ private List<Message> messages;
+ // 鍙�夊弬鏁�
+ private Double temperature;
+ private Integer max_tokens;
+
+ @Data
+ public static class Message {
+ private String role; // "user" / "assistant" / "system"
+ private String content;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/ai/entity/ChatCompletionResponse.java b/src/main/java/com/zy/ai/entity/ChatCompletionResponse.java
new file mode 100644
index 0000000..8f2c167
--- /dev/null
+++ b/src/main/java/com/zy/ai/entity/ChatCompletionResponse.java
@@ -0,0 +1,45 @@
+package com.zy.ai.entity;
+
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ChatCompletionResponse {
+
+ private String id;
+
+ @JsonProperty("object")
+ private String objectName;
+
+ private Long created;
+ private List<Choice> choices;
+ private Usage usage;
+
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Choice {
+ private Integer index;
+ private ChatCompletionRequest.Message message;
+
+ @JsonProperty("finish_reason")
+ private String finishReason;
+ }
+
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Usage {
+ @JsonProperty("prompt_tokens")
+ private Integer promptTokens;
+
+ @JsonProperty("completion_tokens")
+ private Integer completionTokens;
+
+ @JsonProperty("total_tokens")
+ private Integer totalTokens;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/ai/entity/DeviceConfigsData.java b/src/main/java/com/zy/ai/entity/DeviceConfigsData.java
new file mode 100644
index 0000000..e802b0d
--- /dev/null
+++ b/src/main/java/com/zy/ai/entity/DeviceConfigsData.java
@@ -0,0 +1,14 @@
+package com.zy.ai.entity;
+
+import lombok.Data;
+
+@Data
+public class DeviceConfigsData {
+
+ private Integer deviceNo;
+
+ private String deviceType;
+
+ private Object deviceData;
+
+}
diff --git a/src/main/java/com/zy/ai/entity/DeviceRealTimeData.java b/src/main/java/com/zy/ai/entity/DeviceRealTimeData.java
new file mode 100644
index 0000000..2de1824
--- /dev/null
+++ b/src/main/java/com/zy/ai/entity/DeviceRealTimeData.java
@@ -0,0 +1,14 @@
+package com.zy.ai.entity;
+
+import lombok.Data;
+
+@Data
+public class DeviceRealTimeData {
+
+ private Integer deviceNo;
+
+ private String deviceType;
+
+ private Object deviceData;
+
+}
diff --git a/src/main/java/com/zy/ai/entity/WcsDiagnosisRequest.java b/src/main/java/com/zy/ai/entity/WcsDiagnosisRequest.java
new file mode 100644
index 0000000..b49f84b
--- /dev/null
+++ b/src/main/java/com/zy/ai/entity/WcsDiagnosisRequest.java
@@ -0,0 +1,57 @@
+package com.zy.ai.entity;
+
+import com.zy.asrs.entity.WrkMast;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * WCS AI 璇婃柇璇锋眰
+ * 鏀寔锛�
+ * - 浠诲姟淇℃伅
+ * - 璁惧瀹炴椂鏁版嵁
+ * - 璁惧閰嶇疆淇℃伅
+ * - 绯荤粺鏃ュ織
+ * - 棰濆涓婁笅鏂�
+ */
+@Data
+public class WcsDiagnosisRequest {
+
+ /**
+ * 褰撳墠鍏虫敞鐨勮澶囧彿锛堝彲閫夛紝渚嬪鍫嗗灈鏈哄彿=1锛夛紝濡傛灉鏄暣浣撶郴缁熻瘖鏂彲浠ヤ笉濉�
+ */
+ private Integer craneNo;
+
+ /**
+ * 褰撳墠浣犺瀵熷埌鐨勭幇璞�/闂鎻忚堪锛堝彲閫夛級
+ * 渚嬪锛氱郴缁熶笉鎵ц浠诲姟锛屼笉鐭ラ亾鍝釜璁惧娌″湪杩愯
+ */
+ private String alarmMessage;
+
+ /**
+ * 绯荤粺鏃ュ織锛堟寜鏃堕棿椤哄簭锛�
+ */
+ private List<String> logs;
+
+ /**
+ * 浠诲姟淇℃伅鍒楄〃锛堝綋鍓嶅緟鎵ц/鍦ㄦ墽琛�/鎸傝捣浠诲姟锛�
+ */
+ private List<WrkMast> tasks;
+
+ /**
+ * 璁惧褰撳墠瀹炴椂鏁版嵁锛堢姸鎬佷綅銆佽繍琛屾ā寮忋�佸績璺虫椂闂寸瓑锛�
+ */
+ private List<DeviceRealTimeData> deviceRealtimeData;
+
+ /**
+ * 璁惧閰嶇疆淇℃伅
+ */
+ private List<DeviceConfigsData> deviceConfigs;
+
+ /**
+ * 棰濆涓婁笅鏂囷紝渚嬪锛�
+ * warehouseCode, shift, wcsVersion, plcVersion 绛�
+ */
+ private Map<String, Object> extraContext;
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/ai/entity/WcsDiagnosisResponse.java b/src/main/java/com/zy/ai/entity/WcsDiagnosisResponse.java
new file mode 100644
index 0000000..817ceab
--- /dev/null
+++ b/src/main/java/com/zy/ai/entity/WcsDiagnosisResponse.java
@@ -0,0 +1,21 @@
+package com.zy.ai.entity;
+
+
+import lombok.Data;
+
+/**
+ * WCS AI 璇婃柇鍝嶅簲
+ */
+@Data
+public class WcsDiagnosisResponse {
+
+ /**
+ * AI 璇婃柇瀹屾暣鏂囨湰锛圡arkdown锛�
+ */
+ private String analysis;
+
+ /**
+ * 鍙�夛細淇濈暀鍘熷璇锋眰锛屽墠绔皟璇曠敤
+ */
+ private Object originalRequest;
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/ai/log/AiLogAppender.java b/src/main/java/com/zy/ai/log/AiLogAppender.java
new file mode 100644
index 0000000..9a25289
--- /dev/null
+++ b/src/main/java/com/zy/ai/log/AiLogAppender.java
@@ -0,0 +1,53 @@
+package com.zy.ai.log;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.stream.Collectors;
+
+public class AiLogAppender extends AppenderBase<ILoggingEvent> {
+
+ // 淇濆瓨鏈�杩� 2000 鏉℃棩蹇�
+ private static final LinkedBlockingDeque<String> LOG_BUFFER = new LinkedBlockingDeque<>(2000);
+
+ private static final DateTimeFormatter TIME_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
+ .withZone(ZoneId.systemDefault());
+
+ @Override
+ protected void append(ILoggingEvent event) {
+ String time = TIME_FORMATTER.format(Instant.ofEpochMilli(event.getTimeStamp()));
+ String thread = event.getThreadName();
+ String level = event.getLevel().toString();
+ String loggerName = event.getLoggerName();
+ String message = event.getFormattedMessage();
+
+ String logLine = String.format(
+ "%s [%s] %-5s %s - %s",
+ time,
+ thread,
+ level,
+ loggerName,
+ message
+ );
+
+ // 鏀捐繘鐜舰缂撳啿鍖�
+ if (LOG_BUFFER.remainingCapacity() == 0) {
+ LOG_BUFFER.pollFirst(); // 绉婚櫎鏈�鏃х殑
+ }
+ LOG_BUFFER.offerLast(logLine);
+ }
+
+ public static List<String> getRecentLogs(int limit) {
+ int size = LOG_BUFFER.size();
+ int skip = Math.max(0, size - limit);
+ return LOG_BUFFER.stream()
+ .skip(skip)
+ .collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/ai/service/LlmChatService.java b/src/main/java/com/zy/ai/service/LlmChatService.java
new file mode 100644
index 0000000..b001d0e
--- /dev/null
+++ b/src/main/java/com/zy/ai/service/LlmChatService.java
@@ -0,0 +1,63 @@
+package com.zy.ai.service;
+
+import com.zy.ai.entity.ChatCompletionRequest;
+import com.zy.ai.entity.ChatCompletionResponse;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LlmChatService {
+
+ private final WebClient llmWebClient;
+
+ @Value("${llm.api-key}")
+ private String apiKey;
+
+ @Value("${llm.model}")
+ private String model;
+
+ /**
+ * 閫氱敤瀵硅瘽鏂规硶锛氫紶鍏� messages锛岃繑鍥炲ぇ妯″瀷鏂囨湰鍥炲
+ */
+ public String chat(List<ChatCompletionRequest.Message> messages,
+ Double temperature,
+ Integer maxTokens) {
+
+ ChatCompletionRequest req = new ChatCompletionRequest();
+ req.setModel(model);
+ req.setMessages(messages);
+ req.setTemperature(temperature != null ? temperature : 0.3);
+ req.setMax_tokens(maxTokens != null ? maxTokens : 1024);
+
+ ChatCompletionResponse response = llmWebClient.post()
+ .uri("/chat/completions")
+ .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey)
+ .contentType(MediaType.APPLICATION_JSON)
+ .bodyValue(req) // 2.5.14 宸叉敮鎸� bodyValue
+ .retrieve()
+ .bodyToMono(ChatCompletionResponse.class)
+ .doOnError(ex -> log.error("璋冪敤 LLM 澶辫触", ex))
+ .onErrorResume(ex -> Mono.empty())
+ .block();
+
+ if (response == null ||
+ response.getChoices() == null ||
+ response.getChoices().isEmpty() ||
+ response.getChoices().get(0).getMessage() == null) {
+
+ return "AI 璇婃柇澶辫触锛氭湭鑾峰彇鍒版湁鏁堝洖澶嶃��";
+ }
+
+ return response.getChoices().get(0).getMessage().getContent();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/ai/service/WcsDiagnosisService.java b/src/main/java/com/zy/ai/service/WcsDiagnosisService.java
new file mode 100644
index 0000000..9f90c1d
--- /dev/null
+++ b/src/main/java/com/zy/ai/service/WcsDiagnosisService.java
@@ -0,0 +1,117 @@
+package com.zy.ai.service;
+
+import com.alibaba.fastjson.JSON;
+import com.zy.ai.entity.ChatCompletionRequest;
+import com.zy.ai.entity.WcsDiagnosisRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class WcsDiagnosisService {
+
+ private final LlmChatService llmChatService;
+
+ /**
+ * 閽堝鈥滅郴缁熶笉鎵ц浠诲姟 / 涓嶇煡閬撳摢涓澶囨病鍦ㄨ繍琛屸�濈殑閫氱敤 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锛夐澶栦笂涓嬫枃锛坋xtraContext锛夛細濡備粨搴撲唬鐮併�乄CS 鐗堟湰绛塡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);
+
+ // 2. user锛氭妸鍏蜂綋鐨勬暟鎹粍缁囨垚鏂囨湰锛圝SON 褰㈠紡鏂逛究妯″瀷鐪嬬粨鏋勶級
+ ChatCompletionRequest.Message user = new ChatCompletionRequest.Message();
+ user.setRole("user");
+ user.setContent(buildUserContent(request));
+ messages.add(user);
+
+ // 璋冪敤澶фā鍨�
+ return llmChatService.chat(messages, 0.2, 2048);
+ }
+
+ private String buildUserContent(WcsDiagnosisRequest request) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("銆愰棶棰樻弿杩般�慭n");
+ if (request.getAlarmMessage() != null && !request.getAlarmMessage().isEmpty()) {
+ sb.append(request.getAlarmMessage()).append("\n\n");
+ } else {
+ sb.append("绯荤粺褰撳墠涓嶆墽琛屼换鍔★紝浣嗗叿浣撳師鍥犱笉鏄庯紝璇锋牴鎹互涓嬩俊鎭府鍔╁垽鏂�俓n\n");
+ }
+
+ sb.append("銆愯澶囦俊鎭�慭n");
+ sb.append("鍏虫敞璁惧锛堝鏋滄湁鎸囧畾锛�: ")
+ .append(request.getCraneNo() != null ? request.getCraneNo() : "鏈寚瀹氾紝闇�鏁翠綋鍒嗘瀽")
+ .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();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/asrs/utils/Utils.java b/src/main/java/com/zy/asrs/utils/Utils.java
index f84672c..185f27f 100644
--- a/src/main/java/com/zy/asrs/utils/Utils.java
+++ b/src/main/java/com/zy/asrs/utils/Utils.java
@@ -20,14 +20,9 @@
import com.zy.core.model.protocol.CrnProtocol;
import com.zy.core.thread.CrnThread;
+import java.lang.reflect.Field;
import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.*;
public class Utils {
@@ -212,4 +207,20 @@
}
return list;
}
+
+
+ public static Map<String, Object> convertObjectToMap(Object obj) {
+ Map<String, Object> map = new HashMap<>();
+ Class<?> clazz = obj.getClass();
+ Field[] fields = clazz.getDeclaredFields();
+ for (Field field : fields) {
+ field.setAccessible(true);
+ try {
+ map.put(field.getName(), field.get(obj));
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+ return map;
+ }
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 52a49a3..da30edb 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -65,3 +65,8 @@
loggingPath: ./stock/out/@pom.build.finalName@/deviceLogs
# 鏃ュ織杩囨湡鏃堕棿 鍗曚綅澶�
expireDays: 7
+
+llm:
+ base-url: https://api.siliconflow.cn/v1
+ api-key: sk-sxdtebtquwrugzrmaqqqkzdzmrgzhzmplwwuowysdasccent
+ model: Qwen/Qwen3-VL-32B-Instruct
\ No newline at end of file
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml
index f72c762..6fa80d8 100644
--- a/src/main/resources/logback-spring.xml
+++ b/src/main/resources/logback-spring.xml
@@ -11,6 +11,8 @@
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(-%5p) ${PID:-} [%15.15t] %-40.40logger{39} : %m%n">
</property>
+ <appender name="AI_LOG" class="com.zy.ai.log.AiLogAppender"/>
+
<!--鎺у埗鍙�-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
@@ -55,6 +57,7 @@
</appender>
<root level="INFO">
+ <appender-ref ref="AI_LOG"/>
<appender-ref ref="CONSOLE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
--
Gitblit v1.9.1