Browse Source

调整项目结构

lamphua 2 weeks ago
parent
commit
ceeff52856

+ 3
- 3
llm-back/pom.xml View File

@@ -220,8 +220,8 @@
220 220
 
221 221
             <!-- 智能体-->
222 222
             <dependency>
223
-                <groupId>com.cmc</groupId>
224
-                <artifactId>cmc-agent</artifactId>
223
+                <groupId>com.ruoyi</groupId>
224
+                <artifactId>ruoyi-agent</artifactId>
225 225
                 <version>${ruoyi.version}</version>
226 226
             </dependency>
227 227
 
@@ -229,8 +229,8 @@
229 229
     </dependencyManagement>
230 230
 
231 231
     <modules>
232
-        <module>cmc-agent</module>
233 232
         <module>ruoyi-admin</module>
233
+        <module>ruoyi-agent</module>
234 234
         <module>ruoyi-framework</module>
235 235
         <module>ruoyi-system</module>
236 236
         <module>ruoyi-quartz</module>

+ 9
- 2
llm-back/ruoyi-admin/pom.xml View File

@@ -39,8 +39,9 @@
39 39
 
40 40
          <!-- Mysql驱动包 -->
41 41
         <dependency>
42
-            <groupId>mysql</groupId>
43
-            <artifactId>mysql-connector-java</artifactId>
42
+            <groupId>com.mysql</groupId>
43
+            <artifactId>mysql-connector-j</artifactId>
44
+            <version>8.0.33</version>
44 45
         </dependency>
45 46
 
46 47
         <!-- 核心模块-->
@@ -61,6 +62,12 @@
61 62
             <artifactId>ruoyi-generator</artifactId>
62 63
         </dependency>
63 64
 
65
+        <!-- 智能体-->
66
+        <dependency>
67
+            <groupId>com.ruoyi</groupId>
68
+            <artifactId>ruoyi-agent</artifactId>
69
+        </dependency>
70
+
64 71
     </dependencies>
65 72
 
66 73
     <build>

llm-back/cmc-agent/pom.xml → llm-back/ruoyi-agent/pom.xml View File

@@ -8,8 +8,8 @@
8 8
         <version>3.8.9</version>
9 9
     </parent>
10 10
     <modelVersion>4.0.0</modelVersion>
11
-    <packaging>jar</packaging>
12
-    <artifactId>cmc-agent</artifactId>
11
+
12
+    <artifactId>ruoyi-agent</artifactId>
13 13
 
14 14
     <description>
15 15
         cmc智能体
@@ -74,34 +74,4 @@
74 74
 
75 75
     </dependencies>
76 76
 
77
-    <build>
78
-        <plugins>
79
-            <plugin>
80
-                <groupId>org.springframework.boot</groupId>
81
-                <artifactId>spring-boot-maven-plugin</artifactId>
82
-                <version>2.5.15</version>
83
-                <configuration>
84
-                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
85
-                </configuration>
86
-                <executions>
87
-                    <execution>
88
-                        <goals>
89
-                            <goal>repackage</goal>
90
-                        </goals>
91
-                    </execution>
92
-                </executions>
93
-            </plugin>
94
-            <plugin>   
95
-                <groupId>org.apache.maven.plugins</groupId>   
96
-                <artifactId>maven-war-plugin</artifactId>   
97
-                <version>3.1.0</version>   
98
-                <configuration>
99
-                    <failOnMissingWebXml>false</failOnMissingWebXml>
100
-                    <warName>${project.artifactId}</warName>
101
-                </configuration>   
102
-           </plugin>   
103
-        </plugins>
104
-        <finalName>${project.artifactId}</finalName>
105
-    </build>
106
-
107 77
 </project>

llm-back/cmc-agent/src/main/java/com/cmc/agent/controller/KnowLedgeController.java → llm-back/ruoyi-agent/src/main/java/com/cmc/agent/controller/KnowLedgeController.java View File


llm-back/cmc-agent/src/main/java/com/cmc/agent/controller/RagController.java → llm-back/ruoyi-agent/src/main/java/com/cmc/agent/controller/RagController.java View File


llm-back/cmc-agent/src/main/java/com/cmc/agent/controller/SessionController.java → llm-back/ruoyi-agent/src/main/java/com/cmc/agent/controller/SessionController.java View File


llm-back/cmc-agent/src/main/java/com/cmc/agent/service/LangChainMilvusService.java → llm-back/ruoyi-agent/src/main/java/com/cmc/agent/service/LangChainMilvusService.java View File


+ 63
- 0
llm-back/ruoyi-agent/src/main/java/com/ruoyi/agent/controller/KnowLedgeController.java View File

@@ -0,0 +1,63 @@
1
+package com.ruoyi.agent.controller;
2
+
3
+import com.ruoyi.common.config.RuoYiConfig;
4
+import com.ruoyi.common.core.controller.BaseController;
5
+import com.ruoyi.common.core.domain.AjaxResult;
6
+import com.ruoyi.agent.service.LangChainMilvusService;
7
+import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
8
+import dev.langchain4j.model.embedding.EmbeddingModel;
9
+import org.springframework.web.bind.annotation.*;
10
+import org.springframework.web.multipart.MultipartFile;
11
+
12
+import java.io.File;
13
+import java.io.IOException;
14
+
15
+/**
16
+ * cmc知识库Controller
17
+ * 
18
+ * @author cmc
19
+ * @date 2025-04-08
20
+ */
21
+@RestController
22
+@RequestMapping("/llm/knowledge")
23
+public class KnowLedgeController extends BaseController
24
+{
25
+    private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
26
+
27
+    /**
28
+     * 新建知识库
29
+     */
30
+    @PostMapping("/create")
31
+    public AjaxResult createKnowLedgeBase(String collectionName)
32
+    {
33
+        LangChainMilvusService langChainMilvusService = new LangChainMilvusService(
34
+                "192.168.28.188",
35
+                19530,
36
+                collectionName,
37
+                embeddingModel);
38
+        langChainMilvusService.createCollection(512);
39
+        return success();
40
+    }
41
+
42
+    /**
43
+     * 插入知识库文件
44
+     */
45
+    @PostMapping("/insert")
46
+    public AjaxResult insertKnowledge(MultipartFile file, String collectionName) throws IOException {
47
+        LangChainMilvusService langChainMilvusService = new LangChainMilvusService(
48
+                "192.168.28.188",
49
+                19530,
50
+                collectionName,
51
+                embeddingModel);
52
+
53
+        File profilePath = new File( RuoYiConfig.getProfile() + "/upload/knowledge");
54
+        if (!profilePath.exists())
55
+            profilePath.mkdirs();
56
+        File transferFile = new File( profilePath + "/" + file.getOriginalFilename());
57
+        if (!transferFile.exists())
58
+            file.transferTo(transferFile);
59
+        langChainMilvusService.insertLangchainEmbeddingDocument(transferFile);
60
+        return success();
61
+    }
62
+
63
+}

+ 45
- 0
llm-back/ruoyi-agent/src/main/java/com/ruoyi/agent/controller/RagController.java View File

@@ -0,0 +1,45 @@
1
+package com.ruoyi.agent.controller;
2
+
3
+import com.ruoyi.common.core.controller.BaseController;
4
+import com.ruoyi.common.core.domain.AjaxResult;
5
+import com.ruoyi.agent.service.LangChainMilvusService;
6
+import dev.langchain4j.model.embedding.EmbeddingModel;
7
+import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
8
+import org.springframework.web.bind.annotation.PostMapping;
9
+import org.springframework.web.bind.annotation.RequestMapping;
10
+import org.springframework.web.bind.annotation.RestController;
11
+
12
+import java.io.IOException;
13
+import java.util.List;
14
+
15
+/**
16
+ * cmc知识库Controller
17
+ * 
18
+ * @author cmc
19
+ * @date 2025-04-08
20
+ */
21
+@RestController
22
+@RequestMapping("/llm/rag")
23
+public class RagController extends BaseController
24
+{
25
+    private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
26
+
27
+    /**
28
+     * 增强检索生成回答
29
+     */
30
+    @PostMapping("/answer")
31
+    public AjaxResult answer(String question, String collectionName) throws IOException {
32
+        LangChainMilvusService langChainMilvusService = new LangChainMilvusService(
33
+                "192.168.28.188",
34
+                19530,
35
+                collectionName,
36
+                embeddingModel);
37
+
38
+        // 1. Milvus检索
39
+        List<String> contexts = langChainMilvusService.retrieveFromMilvus(question, 3);
40
+
41
+        // 2. 调用本地LLM或HTTP服务
42
+        return success(langChainMilvusService.generateAnswerWithRag(question, contexts));
43
+    }
44
+
45
+}

+ 57
- 0
llm-back/ruoyi-agent/src/main/java/com/ruoyi/agent/controller/SessionController.java View File

@@ -0,0 +1,57 @@
1
+package com.ruoyi.agent.controller;
2
+
3
+import com.alibaba.fastjson2.JSONObject;
4
+import com.ruoyi.common.core.controller.BaseController;
5
+import com.ruoyi.common.core.domain.AjaxResult;
6
+import okhttp3.*;
7
+import org.springframework.web.bind.annotation.GetMapping;
8
+import org.springframework.web.bind.annotation.RequestMapping;
9
+import org.springframework.web.bind.annotation.RestController;
10
+
11
+import java.io.IOException;
12
+import java.util.concurrent.TimeUnit;
13
+
14
+/**
15
+ * cmc知识库Controller
16
+ * 
17
+ * @author cmc
18
+ * @date 2025-04-08
19
+ */
20
+@RestController
21
+@RequestMapping("/llm/session")
22
+public class SessionController extends BaseController
23
+{
24
+    /**
25
+     * 生成回答
26
+     */
27
+    @GetMapping("/answer")
28
+    public AjaxResult answer(String question) throws IOException {
29
+
30
+        // 1. 调用本地LLM或HTTP服务
31
+        return success(generateAnswer(question));
32
+    }
33
+
34
+    // 调用LLM生成回答
35
+    public String generateAnswer(String question) throws IOException {
36
+        // 构建带动态参数的URL
37
+        HttpUrl url = HttpUrl.parse("http://192.168.28.188:8080/generate")
38
+                .newBuilder()
39
+                .addQueryParameter("prompt", question)
40
+                .build();
41
+
42
+        Request request = new Request.Builder()
43
+                .url(url)
44
+                .build();
45
+
46
+        OkHttpClient client = new OkHttpClient.Builder()
47
+                .readTimeout(60, TimeUnit.SECONDS)      // 读取响应超时
48
+                .build();
49
+
50
+        try (Response response = client.newCall(request).execute()) {
51
+            String responseBody = response.body().string();
52
+            String responseResult = JSONObject.parseObject(responseBody).getString("generated_text");
53
+            return responseResult;
54
+        }
55
+    }
56
+
57
+}

+ 208
- 0
llm-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/LangChainMilvusService.java View File

@@ -0,0 +1,208 @@
1
+package com.ruoyi.agent.service;
2
+
3
+import com.google.gson.Gson;
4
+import dev.langchain4j.data.document.Document;
5
+import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
6
+import dev.langchain4j.data.document.splitter.DocumentByParagraphSplitter;
7
+import dev.langchain4j.data.segment.TextSegment;
8
+import dev.langchain4j.model.embedding.EmbeddingModel;
9
+
10
+import io.milvus.client.MilvusServiceClient;
11
+import io.milvus.grpc.DataType;
12
+import io.milvus.grpc.MutationResult;
13
+import io.milvus.grpc.SearchResults;
14
+import io.milvus.param.ConnectParam;
15
+import io.milvus.param.IndexType;
16
+import io.milvus.param.MetricType;
17
+import io.milvus.param.R;
18
+import io.milvus.param.collection.CreateCollectionParam;
19
+import io.milvus.param.collection.FieldType;
20
+import io.milvus.param.dml.InsertParam;
21
+import io.milvus.param.dml.SearchParam;
22
+import io.milvus.param.index.CreateIndexParam;
23
+import io.milvus.response.SearchResultsWrapper;
24
+import okhttp3.*;
25
+
26
+import java.io.File;
27
+import java.io.FileInputStream;
28
+import java.io.IOException;
29
+import java.io.InputStream;
30
+import java.util.*;
31
+import java.util.stream.Collectors;
32
+
33
+public class LangChainMilvusService {
34
+    private String LLM_SERVICE_URL;
35
+    private MilvusServiceClient milvusClient;
36
+    private String collectionName;
37
+    private EmbeddingModel embeddingModel;
38
+
39
+    /**
40
+     * 连接milvus知识库,加入langchain自带emdding
41
+     */
42
+    public LangChainMilvusService(String host, int port, String collectionName, EmbeddingModel embeddingModel) {
43
+        this.milvusClient = new MilvusServiceClient(
44
+                ConnectParam.newBuilder()
45
+                        .withHost(host)
46
+                        .withPort(port)
47
+                        .build()
48
+        );
49
+        this.LLM_SERVICE_URL = "http://" + host + ":8000/generate";
50
+        this.collectionName = collectionName;
51
+        this.embeddingModel = embeddingModel;
52
+    }
53
+
54
+    /**
55
+     * 不使用向量数据库
56
+     */
57
+    public LangChainMilvusService(String host, int port) {
58
+        this.milvusClient = new MilvusServiceClient(
59
+                ConnectParam.newBuilder()
60
+                        .withHost(host)
61
+                        .withPort(port)
62
+                        .build()
63
+        );
64
+        this.LLM_SERVICE_URL = "http://" + host + ":8000/generate";
65
+    }
66
+
67
+    /**
68
+     * 新建知识库Collection(含Schema、Field、Index)
69
+     */
70
+    public void createCollection(int dimension) {
71
+        FieldType idField = FieldType.newBuilder()
72
+                .withName("id")
73
+                .withDataType(DataType.Int64)
74
+                .withPrimaryKey(true)
75
+                .withAutoID(true)
76
+                .build();
77
+
78
+        FieldType fileNameField = FieldType.newBuilder()
79
+                .withName("file_name")
80
+                .withDataType(DataType.VarChar)
81
+                .withMaxLength(256)
82
+                .build();
83
+
84
+        FieldType contentField = FieldType.newBuilder()
85
+                .withName("content")
86
+                .withDataType(DataType.VarChar)
87
+                .withMaxLength(65535)
88
+                .build();
89
+
90
+        FieldType vectorField = FieldType.newBuilder()
91
+                .withName("embedding")
92
+                .withDataType(DataType.Float16Vector)
93
+                .withDimension(dimension)
94
+                .build();
95
+
96
+        CreateCollectionParam createCollectionParam = CreateCollectionParam.newBuilder()
97
+                .withCollectionName(collectionName)
98
+                .addFieldType(idField)
99
+                .addFieldType(fileNameField)
100
+                .addFieldType(contentField)
101
+                .addFieldType(vectorField)
102
+                .build();
103
+
104
+        milvusClient.createCollection(createCollectionParam);
105
+
106
+        // 创建索引
107
+        CreateIndexParam createIndexParam = CreateIndexParam.newBuilder()
108
+                .withCollectionName(collectionName)
109
+                .withFieldName("embedding")
110
+                .withIndexType(IndexType.IVF_FLAT)
111
+                .withMetricType(MetricType.COSINE)
112
+                .withExtraParam("{\"nlist\": 64}")
113
+                .build();
114
+
115
+        milvusClient.createIndex(createIndexParam);
116
+    }
117
+
118
+    public void insertLangchainEmbeddingDocument(File file) throws IOException {
119
+        // 加载文档
120
+        InputStream fileInputStream = new FileInputStream(file);
121
+        Document document = new ApachePdfBoxDocumentParser().parse(fileInputStream);
122
+        DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(150,10);;
123
+        List<TextSegment> segments = splitter.split(document);
124
+
125
+        // 提取文本和生成嵌入
126
+        List<String> texts = new ArrayList<>();
127
+        List<List<Float>> embeddings = new ArrayList<>();
128
+
129
+        for (TextSegment segment : segments) {
130
+            String text = segment.text();
131
+            if (text.trim().isEmpty())
132
+                continue;
133
+            texts.add(text);
134
+            embeddings.add(embeddingModel.embed(text).content().vectorAsList());
135
+        }
136
+
137
+        // 准备插入数据
138
+        List<InsertParam.Field> fields = new ArrayList<>();
139
+        fields.add(new InsertParam.Field("file_name", Arrays.asList(file.getName())));
140
+        fields.add(new InsertParam.Field("content", Arrays.asList(texts)));
141
+        fields.add(new InsertParam.Field("embedding", Arrays.asList(embeddings)));
142
+
143
+        InsertParam insertParam = InsertParam.newBuilder()
144
+                .withCollectionName(collectionName)
145
+                .withFields(fields)
146
+                .build();
147
+
148
+        // 执行插入
149
+        R<MutationResult> insertResult = milvusClient.insert(insertParam);
150
+
151
+        if (insertResult.getStatus() != R.Status.Success.getCode()) {
152
+            throw new RuntimeException("Failed to insert document: " + insertResult.getMessage());
153
+        }
154
+    }
155
+
156
+    // 从Milvus检索相关文档
157
+    public List<String> retrieveFromMilvus(String query, int topK) throws IOException {
158
+        List<Float> queryVector = embeddingModel.embed(query).content().vectorAsList();
159
+
160
+        SearchParam searchParam = SearchParam.newBuilder()
161
+                .withCollectionName(collectionName)
162
+                .withVectors(queryVector)
163
+                .withTopK(topK)
164
+                .withOutFields(Arrays.asList("content"))
165
+                .build();
166
+
167
+        R<SearchResults> response = milvusClient.search(searchParam);
168
+        SearchResultsWrapper wrapper = new SearchResultsWrapper(response.getData().getResults());
169
+
170
+        return wrapper.getRowRecords(0).stream()
171
+                .map(record -> (String) record.get("content"))
172
+                .collect(Collectors.toList());
173
+    }
174
+
175
+    // 调用LLM+RAG生成回答
176
+    public String generateAnswerWithRag(String question, List<String> contexts) throws IOException {
177
+        String prompt = buildPrompt(question, contexts);
178
+        Gson gson = new Gson();
179
+        Map<String, String> hashMap = new HashMap<>();
180
+        hashMap.put("prompt", prompt);
181
+        hashMap.put("max_tokens", "512");
182
+        RequestBody body = RequestBody.create(
183
+                MediaType.parse("application/json"),
184
+                new Gson().toJson(hashMap));
185
+
186
+        Request request = new Request.Builder()
187
+                .url(LLM_SERVICE_URL)
188
+                .post(body)
189
+                .build();
190
+
191
+        try (Response response = new OkHttpClient().newCall(request).execute()) {
192
+            return gson.fromJson(response.body().string(), Map.class).get("generated_text").toString();
193
+        }
194
+    }
195
+
196
+    private String buildPrompt(String question, List<String> contexts) {
197
+        StringBuilder sb = new StringBuilder();
198
+        sb.append("根据以下上下文回答问题:\n\n");
199
+        for (int i = 0; i < contexts.size(); i++) {
200
+            sb.append("上下文").append(i+1).append(": ").append(contexts.get(i)).append("\n\n");
201
+        }
202
+        sb.append("问题: ").append(question).append("\n回答: ");
203
+        return sb.toString();
204
+    }
205
+    public void close() {
206
+        milvusClient.close();
207
+    }
208
+}

Loading…
Cancel
Save