Bladeren bron

结合mcp工具返回值流式输出

lamphua 2 dagen geleden
bovenliggende
commit
9c401b261b

+ 16
- 0
llm-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/impl/McpServiceImpl.java Bestand weergeven

@@ -1,6 +1,8 @@
1 1
 package com.ruoyi.agent.service.impl;
2 2
 
3 3
 import com.ruoyi.agent.service.IMcpService;
4
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
5
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
4 6
 import org.noear.solon.ai.annotation.PromptMapping;
5 7
 import org.noear.solon.ai.annotation.ResourceMapping;
6 8
 import org.noear.solon.ai.annotation.ToolMapping;
@@ -9,6 +11,8 @@ import org.noear.solon.ai.mcp.server.annotation.McpServerEndpoint;
9 11
 import org.noear.solon.annotation.Param;
10 12
 import org.springframework.stereotype.Service;
11 13
 
14
+import java.io.FileInputStream;
15
+import java.io.IOException;
12 16
 import java.util.Arrays;
13 17
 import java.util.Collection;
14 18
 
@@ -21,6 +25,18 @@ public class McpServiceImpl implements IMcpService {
21 25
         return "晴,14度";
22 26
     }
23 27
 
28
+    @ToolMapping(description = "查看word文档")
29
+    public String openDocument() throws IOException {
30
+        String filePath = "C:\\Users\\lamphua\\Desktop\\智慧水利\\李家岩水库数字孪生招标文件.DOCX";
31
+        try (XWPFDocument doc = new XWPFDocument(new FileInputStream(filePath))) {
32
+            StringBuilder content = new StringBuilder();
33
+            for (XWPFParagraph paragraph : doc.getParagraphs()) {
34
+                content.append(paragraph.getText()).append("\n");
35
+            }
36
+            return content.toString();
37
+        }
38
+    }
39
+
24 40
     @ResourceMapping(uri = "config://app-version", description = "获取应用版本号")
25 41
     public String getAppVersion() {
26 42
         return "v3.2.0";

+ 6
- 5
llm-back/ruoyi-llm/pom.xml Bestand weergeven

@@ -67,11 +67,12 @@
67 67
             <groupId>org.noear</groupId>
68 68
             <artifactId>solon-ai-mcp</artifactId>
69 69
             <version>3.3.1</version>
70
-        </dependency>
71
-
72
-        <dependency>
73
-            <groupId>com.squareup.okhttp3</groupId>
74
-            <artifactId>okhttp</artifactId>
70
+            <exclusions>
71
+                <exclusion>
72
+                    <groupId>org.slf4j</groupId>
73
+                    <artifactId>*</artifactId>
74
+                </exclusion>
75
+            </exclusions>
75 76
         </dependency>
76 77
 
77 78
     </dependencies>

+ 27
- 11
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/McpController.java Bestand weergeven

@@ -1,14 +1,18 @@
1 1
 package com.ruoyi.web.llm.controller;
2 2
 
3 3
 import com.ruoyi.common.core.controller.BaseController;
4
-import com.ruoyi.common.core.domain.AjaxResult;
4
+import com.ruoyi.web.llm.service.ILangChainMilvusService;
5
+import org.noear.solon.ai.chat.ChatModel;
6
+import org.noear.solon.ai.chat.ChatResponse;
7
+import org.noear.solon.ai.chat.message.AssistantMessage;
5 8
 import org.noear.solon.ai.mcp.client.McpClientProvider;
9
+import org.reactivestreams.Publisher;
10
+import org.springframework.beans.factory.annotation.Autowired;
6 11
 import org.springframework.web.bind.annotation.GetMapping;
7 12
 import org.springframework.web.bind.annotation.RequestMapping;
8 13
 import org.springframework.web.bind.annotation.RestController;
14
+import reactor.core.publisher.Flux;
9 15
 
10
-import java.util.Collections;
11
-import java.util.Map;
12 16
 
13 17
 /**
14 18
  * mcp模型上下文协议Controller
@@ -20,20 +24,32 @@ import java.util.Map;
20 24
 @RequestMapping("/llm/mcp")
21 25
 public class McpController extends BaseController
22 26
 {
27
+    @Autowired
28
+    private ILangChainMilvusService langChainMilvusService;
29
+
23 30
     /**
24
-     * 生成回答
31
+     * 打开word文档
32
+     * @return
25 33
      */
26 34
     @GetMapping("/answer")
27
-    public AjaxResult answer() {
35
+    public Flux<AssistantMessage> answer(String question)
36
+    {
28 37
         McpClientProvider clientProvider = McpClientProvider.builder()
29
-                .apiUrl("http://localhost:52548/llm/mcp/sse")
38
+                    .apiUrl("http://localhost:8080/llm/mcp/sse")
39
+                    .build();
40
+
41
+        ChatModel chatModel = ChatModel.of("http://192.168.28.188:8000/v1/chat/completions")
42
+                .provider("openai")
43
+                .model("DeepSeek-R1-Distill-Qwen-1.5B")
44
+                .apiKey("1")
45
+//                .defaultToolsAdd(clientProvider)
30 46
                 .build();
47
+        Publisher<ChatResponse> publisher = chatModel.prompt(question).stream();
31 48
 
32
-        Map<String, Object> map = Collections.singletonMap("location", "杭州");
33
-        String rst = clientProvider.callToolAsText("getWeather", map).getContent();
34
-        // 1. 调用本地LLM或HTTP服务
35
-        return success(rst);
49
+        return Flux.from(publisher)
50
+                .map(response -> {
51
+                    return response.getChoices().get(0).getMessage();
52
+                });
36 53
     }
37 54
 
38
-
39 55
 }

+ 4
- 3
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/RagController.java Bestand weergeven

@@ -13,10 +13,12 @@ import dev.langchain4j.model.embedding.EmbeddingModel;
13 13
 import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
14 14
 import io.milvus.client.MilvusServiceClient;
15 15
 import io.milvus.param.ConnectParam;
16
+import org.noear.solon.ai.chat.message.AssistantMessage;
16 17
 import org.springframework.beans.factory.annotation.Autowired;
17 18
 import org.springframework.web.bind.annotation.GetMapping;
18 19
 import org.springframework.web.bind.annotation.RequestMapping;
19 20
 import org.springframework.web.bind.annotation.RestController;
21
+import reactor.core.publisher.Flux;
20 22
 
21 23
 import java.io.IOException;
22 24
 import java.util.List;
@@ -46,11 +48,10 @@ public class RagController extends BaseController
46 48
      * 增强检索生成回答
47 49
      */
48 50
     @GetMapping("/answer")
49
-    public AjaxResult answer(String question, String collectionName) throws IOException {
51
+    public Flux<AssistantMessage> answer(String question, String collectionName) throws IOException {
50 52
 
51 53
         List<String> contexts = langChainMilvusService.retrieveFromMilvus(milvusClient, embeddingModel, collectionName, question, 1);
52
-        String result = langChainMilvusService.generateAnswerWithRag(question, contexts, "http://192.168.28.188:8000/generate");
53
-        return success(result);
54
+        return langChainMilvusService.generateAnswerWithRag(question, contexts, "http://192.168.28.188:8000/v1/chat/completions");
54 55
     }
55 56
 
56 57
 }

+ 4
- 2
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/SessionController.java Bestand weergeven

@@ -3,10 +3,12 @@ package com.ruoyi.web.llm.controller;
3 3
 import com.ruoyi.common.core.controller.BaseController;
4 4
 import com.ruoyi.common.core.domain.AjaxResult;
5 5
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
6
+import org.noear.solon.ai.chat.message.AssistantMessage;
6 7
 import org.springframework.beans.factory.annotation.Autowired;
7 8
 import org.springframework.web.bind.annotation.GetMapping;
8 9
 import org.springframework.web.bind.annotation.RequestMapping;
9 10
 import org.springframework.web.bind.annotation.RestController;
11
+import reactor.core.publisher.Flux;
10 12
 
11 13
 import java.io.IOException;
12 14
 
@@ -27,8 +29,8 @@ public class SessionController extends BaseController
27 29
      * 生成回答
28 30
      */
29 31
     @GetMapping("/answer")
30
-    public AjaxResult answer(String question) throws IOException {
31
-        return success(langChainMilvusService.generateAnswer(question, "http://192.168.28.188:8000/generate"));
32
+    public Flux<AssistantMessage> answer(String question) {
33
+        return langChainMilvusService.generateAnswer(question, "http://192.168.28.188:8000/v1/chat/completions");
32 34
     }
33 35
 
34 36
 }

+ 6
- 2
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/ILangChainMilvusService.java Bestand weergeven

@@ -4,7 +4,9 @@ import dev.langchain4j.model.embedding.EmbeddingModel;
4 4
 import io.milvus.client.MilvusClient;
5 5
 import io.milvus.grpc.MutationResult;
6 6
 import io.milvus.param.R;
7
+import org.noear.solon.ai.chat.message.AssistantMessage;
7 8
 import org.springframework.web.multipart.MultipartFile;
9
+import reactor.core.publisher.Flux;
8 10
 
9 11
 import java.io.IOException;
10 12
 import java.util.List;
@@ -23,11 +25,13 @@ public interface ILangChainMilvusService {
23 25
 
24 26
     /**
25 27
      * 调用LLM+RAG生成回答
28
+     * @return
26 29
      */
27
-    public String generateAnswerWithRag(String question, List<String> contexts, String llmServiceUrl) throws IOException;
30
+    public Flux<AssistantMessage> generateAnswerWithRag(String question, List<String> contexts, String llmServiceUrl);
28 31
 
29 32
     /**
30 33
      * 调用LLM生成回答
34
+     * @return
31 35
      */
32
-    public String generateAnswer(String question, String llmServiceUrl) throws IOException;
36
+    public Flux<AssistantMessage> generateAnswer(String question, String llmServiceUrl);
33 37
 }

+ 18
- 24
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/LangChainMilvusServiceImpl.java Bestand weergeven

@@ -6,7 +6,6 @@
6 6
  */
7 7
 package com.ruoyi.web.llm.service.impl;
8 8
 
9
-import com.alibaba.fastjson2.JSONObject;
10 9
 import com.ruoyi.common.config.RuoYiConfig;
11 10
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
12 11
 import dev.langchain4j.data.document.Document;
@@ -26,18 +25,21 @@ import io.milvus.param.collection.ReleaseCollectionParam;
26 25
 import io.milvus.param.dml.InsertParam;
27 26
 import io.milvus.param.dml.SearchParam;
28 27
 import io.milvus.response.SearchResultsWrapper;
29
-import okhttp3.*;
30 28
 import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
31 29
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
30
+import org.noear.solon.ai.chat.ChatModel;
31
+import org.noear.solon.ai.chat.ChatResponse;
32
+import org.noear.solon.ai.chat.message.AssistantMessage;
33
+import org.reactivestreams.Publisher;
32 34
 import org.springframework.stereotype.Service;
33 35
 import org.springframework.web.multipart.MultipartFile;
36
+import reactor.core.publisher.Flux;
34 37
 
35 38
 import java.io.File;
36 39
 import java.io.FileInputStream;
37 40
 import java.io.IOException;
38 41
 import java.io.InputStream;
39 42
 import java.util.*;
40
-import java.util.concurrent.TimeUnit;
41 43
 import java.util.stream.Collectors;
42 44
 
43 45
 @Service
@@ -153,9 +155,10 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
153 155
 
154 156
     /**
155 157
      * 调用LLM+RAG生成回答
158
+     * @return
156 159
      */
157 160
     @Override
158
-    public String generateAnswerWithRag(String question, List<String> contexts, String llmServiceUrl) throws IOException {
161
+    public Flux<AssistantMessage> generateAnswerWithRag(String question, List<String> contexts, String llmServiceUrl) {
159 162
         StringBuilder sb = new StringBuilder();
160 163
         sb.append("根据以下上下文回答问题:\n\n");
161 164
         for (int i = 0; i < contexts.size(); i++) {
@@ -168,30 +171,21 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
168 171
 
169 172
     /**
170 173
      * 调用LLM生成回答
174
+     * @return
171 175
      */
172 176
     @Override
173
-    public String generateAnswer(String prompt, String llmServiceUrl) throws IOException {
174
-        HttpUrl url = HttpUrl.parse(llmServiceUrl)
175
-                .newBuilder()
176
-                .addQueryParameter("prompt", prompt)
177
-//                .addQueryParameter("max_token", String.valueOf(1024))
177
+    public Flux<AssistantMessage> generateAnswer(String prompt, String llmServiceUrl) {
178
+        ChatModel chatModel = ChatModel.of(llmServiceUrl)
179
+                .provider("openai")
180
+                .model("DeepSeek-R1-Distill-Qwen-1.5B")
181
+                .apiKey("1")
178 182
                 .build();
183
+        Publisher<ChatResponse> publisher = chatModel.prompt(prompt).stream();
179 184
 
180
-        Request request = new Request.Builder()
181
-                .url(url)
182
-                .build();
183
-
184
-        OkHttpClient client = new OkHttpClient.Builder()
185
-                .readTimeout(60, TimeUnit.SECONDS)      // 读取响应超时
186
-                .build();
187
-
188
-        try (Response response = client.newCall(request).execute()) {
189
-            if (response.body() != null) {
190
-                String responseBody = response.body().string();
191
-                return JSONObject.parseObject(responseBody).getString("generated_text");
192
-            }
193
-            else return null;
194
-        }
185
+        return Flux.from(publisher)
186
+                .map(response -> {
187
+                    return response.getChoices().get(0).getMessage();
188
+                });
195 189
     }
196 190
 
197 191
 }

+ 1
- 1
llm-ui/src/views/llm/chat/index.vue Bestand weergeven

@@ -426,7 +426,7 @@ const sendMessage = async () => {
426 426
       // 使用Vue.set或直接赋值来确保响应式更新
427 427
       chatMessages.value[messageIndex] = {
428 428
         ...chatMessages.value[messageIndex],
429
-        output: answer.msg,
429
+        output: answer.resultContent,
430 430
         outputTime: proxy.parseTime(new Date(), '{y}-{m}-{d}')
431 431
       };
432 432
     }

+ 1
- 1
llm-ui/src/views/llm/knowledge/index.vue Bestand weergeven

@@ -440,7 +440,7 @@ function sendMessage() {
440 440
       console.log(response);
441 441
       const aiMessage = {
442 442
         type: 'ai',
443
-        content: response.msg,
443
+        content: response.resultContent,
444 444
         time: new Date()
445 445
       };
446 446
       chatMessages.value.push(aiMessage);

Laden…
Annuleren
Opslaan