Explorar el Código

增加与ai大模型对话

wwh hace 3 semanas
padre
commit
6614aa4623

+ 2 - 2
huimv-employment/fe-integration/src/main/java/com/huimv/employment/integration/kb/KbApiProperties.java

@@ -4,7 +4,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
4 4
 import org.springframework.http.HttpHeaders;
5 5
 
6 6
 /**
7
- * 大模型网关 HTTP 对接(知识库 {@code /api/v1/kb/**}、对话 {@code /v1/models}、{@code /v1/chat/completions}),绑定 {@code fe.kb.*}。
7
+ * 大模型网关 HTTP 对接(智能体 {@code /api/console/chat}、RAG {@code /api/rag/**}),绑定 {@code fe.kb.*}。
8 8
  */
9 9
 @ConfigurationProperties(prefix = "fe.kb")
10 10
 public class KbApiProperties {
@@ -16,7 +16,7 @@ public class KbApiProperties {
16 16
 
17 17
     private String host = "localhost";
18 18
 
19
-    private int port = 9107;
19
+    private int port = 9188;
20 20
 
21 21
     /** 访问令牌(暂不需要;外网远程访问时再配置 fe.kb.api-key) */
22 22
     private String apiKey = "";

+ 47 - 17
huimv-employment/fe-integration/src/main/java/com/huimv/employment/integration/kb/KnowledgeBaseClient.java

@@ -1,37 +1,67 @@
1 1
 package com.huimv.employment.integration.kb;
2 2
 
3
+import com.fasterxml.jackson.databind.JsonNode;
4
+import com.huimv.employment.integration.kb.rag.RagFileListQuery;
5
+import com.huimv.employment.integration.kb.rag.RagRetrieveRequest;
6
+
3 7
 import java.io.File;
4 8
 
5 9
 /**
6
- * 与大模型知识库后台的 HTTP 交互(上传文档、删除文档等),供各业务模块复用。
10
+ * RAG 知识库 HTTP 客户端(前缀 {@code /api/rag}),供各业务模块复用。
11
+ *
12
+ * @see <a href="doc/灵活用工项目后端与智能体接口文档.md">RAG 接口文档</a>
7 13
  */
8 14
 public interface KnowledgeBaseClient {
9 15
 
10 16
     /**
11
-     * 方式一:multipart/form-data 上传本地文件入库。
17
+     * {@code GET /api/rag/health} 健康检查。
18
+     */
19
+    JsonNode health();
20
+
21
+    /**
22
+     * {@code POST /api/rag/files} 上传文件(异步处理)。
12 23
      *
13
-     * @param file           本地文件
14
-     * @param uploadFilename multipart 中 file 部分的文件名
15
-     * @param knowledgeBase  知识库标识,默认 {@code default}
16
-     * @param category       分类
17
-     * @return 知识库返回的 file_id
24
+     * @return track_id,需后续调用 {@link #getFileStatus(String)} 轮询状态
18 25
      */
19
-    String uploadFile(File file, String uploadFilename, String knowledgeBase, String category);
26
+    String uploadFile(File file, String uploadFilename);
20 27
 
21 28
     /**
22
-     * 方式二:multipart/form-data 提交 url、category、description 上传文件入库
29
+     * {@code PUT /api/rag/files/{file_id}} 更新文件
23 30
      *
24
-     * @param fileUrl     知识库可 HTTP 拉取的正文地址
25
-     * @param category    分类
26
-     * @param description 描述
27
-     * @return 知识库返回的 file_id
31
+     * @return track_id
32
+     */
33
+    String updateFile(String fileId, File file, String uploadFilename);
34
+
35
+    /**
36
+     * {@code GET /api/rag/files} 分页列出文档。
37
+     */
38
+    JsonNode listFiles(RagFileListQuery query);
39
+
40
+    /**
41
+     * {@code GET /api/rag/files/{file_id}} 查询处理状态(track_id 或 doc_id)。
42
+     * <p>status:processing / processed / failed</p>
28 43
      */
29
-    String uploadFileByUrl(String fileUrl, String category, String description);
44
+    JsonNode getFileStatus(String fileId);
30 45
 
31 46
     /**
32
-     * 按知识库文档 id 移出条目。
47
+     * {@code DELETE /api/rag/files/{file_id}} 删除单个文档。
48
+     */
49
+    void deleteFile(String fileId);
50
+
51
+    /**
52
+     * {@code POST /api/rag/retrieve} 语义检索。
53
+     */
54
+    JsonNode retrieve(RagRetrieveRequest request);
55
+
56
+    /**
57
+     * {@code POST /api/rag/insert-text} 插入单条文本。
33 58
      *
34
-     * @param kbDocId {@code /api/v1/kb/files/{id}} 中的 id
59
+     * @return track_id
60
+     */
61
+    String insertText(String text, String fileSource);
62
+
63
+    /**
64
+     * {@code POST /api/rag/reprocess-failed} 重新处理失败文档。
35 65
      */
36
-    void deleteFile(String kbDocId);
66
+    JsonNode reprocessFailed();
37 67
 }

+ 258 - 123
huimv-employment/fe-integration/src/main/java/com/huimv/employment/integration/kb/KnowledgeBaseClientImpl.java

@@ -4,6 +4,9 @@ import com.fasterxml.jackson.databind.JsonNode;
4 4
 import com.fasterxml.jackson.databind.ObjectMapper;
5 5
 import com.huimv.employment.common.exception.BizException;
6 6
 import com.huimv.employment.common.exception.ErrorCode;
7
+import com.huimv.employment.integration.kb.rag.RagFileListQuery;
8
+import com.huimv.employment.integration.kb.rag.RagInsertTextRequest;
9
+import com.huimv.employment.integration.kb.rag.RagRetrieveRequest;
7 10
 import org.slf4j.Logger;
8 11
 import org.slf4j.LoggerFactory;
9 12
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -11,6 +14,7 @@ import org.springframework.core.io.FileSystemResource;
11 14
 import org.springframework.http.HttpEntity;
12 15
 import org.springframework.http.HttpHeaders;
13 16
 import org.springframework.http.HttpMethod;
17
+import org.springframework.http.MediaType;
14 18
 import org.springframework.http.ResponseEntity;
15 19
 import org.springframework.stereotype.Service;
16 20
 import org.springframework.util.LinkedMultiValueMap;
@@ -19,6 +23,7 @@ import org.springframework.util.StringUtils;
19 23
 import org.springframework.web.client.HttpStatusCodeException;
20 24
 import org.springframework.web.client.ResourceAccessException;
21 25
 import org.springframework.web.client.RestTemplate;
26
+import org.springframework.web.util.UriComponentsBuilder;
22 27
 
23 28
 import java.io.File;
24 29
 import java.io.UnsupportedEncodingException;
@@ -28,14 +33,18 @@ import java.nio.charset.StandardCharsets;
28 33
 import java.util.function.Supplier;
29 34
 
30 35
 /**
31
- * 调用大模型知识库开放 API(multipart 上传 / 删除文件等)
36
+ * RAG 知识库 {@code /api/rag/**} HTTP 客户端实现
32 37
  */
33 38
 @Service
34 39
 public class KnowledgeBaseClientImpl implements KnowledgeBaseClient {
35 40
 
36 41
     private static final Logger log = LoggerFactory.getLogger(KnowledgeBaseClientImpl.class);
37 42
 
38
-    private static final String[] FILE_ID_KEYS = { "file_id", "fileId", "kbDocId", "kb_doc_id", "docId", "id" };
43
+    private static final String RAG_PREFIX = "/api/rag";
44
+
45
+    private static final String[] TRACK_ID_KEYS = {
46
+            "track_id", "trackId", "file_id", "fileId", "kbDocId", "kb_doc_id", "docId", "id"
47
+    };
39 48
 
40 49
     private final KbApiProperties properties;
41 50
     private final RestTemplate restTemplate;
@@ -50,51 +59,188 @@ public class KnowledgeBaseClientImpl implements KnowledgeBaseClient {
50 59
     }
51 60
 
52 61
     @Override
53
-    public String uploadFile(File file, String uploadFilename, String knowledgeBase, String category) {
62
+    public JsonNode health() {
63
+        ensureReady();
64
+        return getJson("/health");
65
+    }
66
+
67
+    @Override
68
+    public String uploadFile(File file, String uploadFilename) {
54 69
         ensureReady();
55 70
         if (file == null || !file.isFile()) {
56 71
             throw new BizException(ErrorCode.BAD_REQUEST, "上传文件无效或不存在");
57 72
         }
58
-        String kb = StringUtils.hasText(knowledgeBase) ? knowledgeBase.trim() : properties.getDefaultKnowledgeBase();
59
-        if (!StringUtils.hasText(kb)) {
60
-            kb = "default";
61
-        }
62 73
         String name = StringUtils.hasText(uploadFilename) ? uploadFilename.trim() : file.getName();
74
+        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
75
+        body.add("file", multipartFileResource(file, name));
76
+        return parseTrackId(postMultipart("/files", body));
77
+    }
63 78
 
79
+    @Override
80
+    public String updateFile(String fileId, File file, String uploadFilename) {
81
+        ensureReady();
82
+        requireFileId(fileId);
83
+        if (file == null || !file.isFile()) {
84
+            throw new BizException(ErrorCode.BAD_REQUEST, "上传文件无效或不存在");
85
+        }
86
+        String name = StringUtils.hasText(uploadFilename) ? uploadFilename.trim() : file.getName();
64 87
         MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
65
-        body.add("knowledge_base", kb);
66
-        body.add("category", category);
67 88
         body.add("file", multipartFileResource(file, name));
89
+        return parseTrackId(putMultipart("/files/" + encodePathSegment(fileId), body));
90
+    }
68 91
 
69
-        return postMultipart(body);
92
+    @Override
93
+    public JsonNode listFiles(RagFileListQuery query) {
94
+        ensureReady();
95
+        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(ragUrl("/files"));
96
+        if (query != null) {
97
+            if (query.getPage() != null) {
98
+                builder.queryParam("page", query.getPage());
99
+            }
100
+            if (query.getPageSize() != null) {
101
+                builder.queryParam("page_size", query.getPageSize());
102
+            }
103
+            if (StringUtils.hasText(query.getSortField())) {
104
+                builder.queryParam("sort_field", query.getSortField());
105
+            }
106
+            if (StringUtils.hasText(query.getSortDirection())) {
107
+                builder.queryParam("sort_direction", query.getSortDirection());
108
+            }
109
+        }
110
+        return getJsonUri(builder.build(true).toUri());
70 111
     }
71 112
 
72 113
     @Override
73
-    public String uploadFileByUrl(String fileUrl, String category, String description) {
114
+    public JsonNode getFileStatus(String fileId) {
74 115
         ensureReady();
75
-        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
76
-        body.add("url", fileUrl);
77
-        body.add("category", category);
78
-        body.add("description", description);
79
-        return postMultipart(body);
116
+        requireFileId(fileId);
117
+        return getJson("/files/" + encodePathSegment(fileId));
80 118
     }
81 119
 
82
-    private String postMultipart(MultiValueMap<String, Object> body) {
83
-        String url = properties.baseUrl() + "/api/v1/kb/files";
84
-        HttpHeaders headers = new HttpHeaders();
85
-        properties.applyAuthHeader(headers);
86
-        HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
87
-        String raw = executeWithRetry("知识库上传", () -> {
120
+    @Override
121
+    public void deleteFile(String fileId) {
122
+        ensureReady();
123
+        requireFileId(fileId);
124
+        String path = "/files/" + encodePathSegment(fileId);
125
+        executeWithRetry("RAG 删除文档", () -> {
88 126
             try {
89
-                ResponseEntity<String> resp = restTemplate.exchange(URI.create(url), HttpMethod.POST, entity, String.class);
127
+                HttpEntity<Void> entity = new HttpEntity<>(authHeaders());
128
+                ResponseEntity<String> resp = restTemplate.exchange(
129
+                        URI.create(ragUrl(path)), HttpMethod.DELETE, entity, String.class);
130
+                if (!resp.getStatusCode().is2xxSuccessful()) {
131
+                    throw new BizException(ErrorCode.KB_SERVICE_FAILED,
132
+                            "RAG 删除失败 HTTP " + resp.getStatusCode().value());
133
+                }
134
+                return null;
135
+            } catch (HttpStatusCodeException e) {
136
+                if (e.getRawStatusCode() == 404) {
137
+                    return null;
138
+                }
139
+                throw httpError("RAG 删除失败", e);
140
+            }
141
+        });
142
+    }
143
+
144
+    @Override
145
+    public JsonNode retrieve(RagRetrieveRequest request) {
146
+        ensureReady();
147
+        if (request == null || !StringUtils.hasText(request.getQuery())) {
148
+            throw new BizException(ErrorCode.BAD_REQUEST, "检索 query 不能为空");
149
+        }
150
+        return postJson("/retrieve", request);
151
+    }
152
+
153
+    @Override
154
+    public String insertText(String text, String fileSource) {
155
+        ensureReady();
156
+        if (!StringUtils.hasText(text)) {
157
+            throw new BizException(ErrorCode.BAD_REQUEST, "text 不能为空");
158
+        }
159
+        if (!StringUtils.hasText(fileSource)) {
160
+            throw new BizException(ErrorCode.BAD_REQUEST, "file_source 不能为空");
161
+        }
162
+        RagInsertTextRequest body = new RagInsertTextRequest(text.trim(), fileSource.trim());
163
+        return parseTrackId(postJsonRaw("/insert-text", body));
164
+    }
165
+
166
+    @Override
167
+    public JsonNode reprocessFailed() {
168
+        ensureReady();
169
+        return postJson("/reprocess-failed", new Object());
170
+    }
171
+
172
+    private JsonNode getJson(String path) {
173
+        return getJsonUri(URI.create(ragUrl(path)));
174
+    }
175
+
176
+    private JsonNode getJsonUri(URI uri) {
177
+        String raw = executeWithRetry("RAG GET " + uri.getPath(), () -> {
178
+            try {
179
+                HttpEntity<Void> entity = new HttpEntity<>(authHeaders());
180
+                ResponseEntity<String> resp = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
181
+                return resp.getBody();
182
+            } catch (HttpStatusCodeException e) {
183
+                throw httpError("RAG 请求失败", e);
184
+            }
185
+        });
186
+        return parseJsonNode(raw);
187
+    }
188
+
189
+    private JsonNode postJson(String path, Object body) {
190
+        return parseJsonNode(postJsonRaw(path, body));
191
+    }
192
+
193
+    private String postJsonRaw(String path, Object body) {
194
+        return executeWithRetry("RAG POST " + path, () -> {
195
+            try {
196
+                HttpHeaders headers = authHeaders();
197
+                headers.setContentType(MediaType.APPLICATION_JSON);
198
+                HttpEntity<Object> entity = new HttpEntity<>(body, headers);
199
+                ResponseEntity<String> resp = restTemplate.exchange(
200
+                        URI.create(ragUrl(path)), HttpMethod.POST, entity, String.class);
90 201
                 return resp.getBody();
91 202
             } catch (HttpStatusCodeException e) {
92
-                String b = e.getResponseBodyAsString(StandardCharsets.UTF_8);
93
-                throw new BizException(ErrorCode.KB_SERVICE_FAILED,
94
-                        "知识库上传失败 HTTP " + e.getRawStatusCode() + ":" + abbreviate(b));
203
+                throw httpError("RAG 请求失败", e);
95 204
             }
96 205
         });
97
-        return parseUploadResponse(raw);
206
+    }
207
+
208
+    private String postMultipart(String path, MultiValueMap<String, Object> body) {
209
+        return executeWithRetry("RAG 上传", () -> {
210
+            try {
211
+                HttpHeaders headers = authHeaders();
212
+                HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
213
+                ResponseEntity<String> resp = restTemplate.exchange(
214
+                        URI.create(ragUrl(path)), HttpMethod.POST, entity, String.class);
215
+                return resp.getBody();
216
+            } catch (HttpStatusCodeException e) {
217
+                throw httpError("RAG 上传失败", e);
218
+            }
219
+        });
220
+    }
221
+
222
+    private String putMultipart(String path, MultiValueMap<String, Object> body) {
223
+        return executeWithRetry("RAG 更新文件", () -> {
224
+            try {
225
+                HttpHeaders headers = authHeaders();
226
+                HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
227
+                ResponseEntity<String> resp = restTemplate.exchange(
228
+                        URI.create(ragUrl(path)), HttpMethod.PUT, entity, String.class);
229
+                return resp.getBody();
230
+            } catch (HttpStatusCodeException e) {
231
+                throw httpError("RAG 更新失败", e);
232
+            }
233
+        });
234
+    }
235
+
236
+    private HttpHeaders authHeaders() {
237
+        HttpHeaders headers = new HttpHeaders();
238
+        properties.applyAuthHeader(headers);
239
+        return headers;
240
+    }
241
+
242
+    private String ragUrl(String path) {
243
+        return properties.baseUrl() + RAG_PREFIX + path;
98 244
     }
99 245
 
100 246
     private static FileSystemResource multipartFileResource(File file, String uploadName) {
@@ -106,33 +252,95 @@ public class KnowledgeBaseClientImpl implements KnowledgeBaseClient {
106 252
         };
107 253
     }
108 254
 
109
-    @Override
110
-    public void deleteFile(String kbDocId) {
111
-        ensureReady();
112
-        if (!StringUtils.hasText(kbDocId)) {
113
-            throw new BizException(ErrorCode.BAD_REQUEST, "知识库文档 id 为空");
255
+    private static void requireFileId(String fileId) {
256
+        if (!StringUtils.hasText(fileId)) {
257
+            throw new BizException(ErrorCode.BAD_REQUEST, "file_id 不能为空");
114 258
         }
115
-        String url = properties.baseUrl() + "/api/v1/kb/files/" + encodePathSegment(kbDocId);
116
-        HttpHeaders headers = new HttpHeaders();
117
-        properties.applyAuthHeader(headers);
118
-        HttpEntity<Void> entity = new HttpEntity<>(headers);
119
-        executeWithRetry("知识库删除", () -> {
120
-            try {
121
-                ResponseEntity<String> resp = restTemplate.exchange(URI.create(url), HttpMethod.DELETE, entity, String.class);
122
-                if (!resp.getStatusCode().is2xxSuccessful()) {
123
-                    throw new BizException(ErrorCode.KB_SERVICE_FAILED,
124
-                            "知识库删除失败 HTTP " + resp.getStatusCode().value());
259
+    }
260
+
261
+    private JsonNode parseJsonNode(String raw) {
262
+        if (!StringUtils.hasText(raw)) {
263
+            throw new BizException(ErrorCode.KB_SERVICE_FAILED, "RAG 返回空响应");
264
+        }
265
+        try {
266
+            JsonNode root = objectMapper.readTree(raw);
267
+            assertBusinessOk(root);
268
+            return root;
269
+        } catch (BizException ex) {
270
+            throw ex;
271
+        } catch (Exception ex) {
272
+            throw new BizException(ErrorCode.KB_SERVICE_FAILED, "RAG 响应非 JSON:" + abbreviate(raw));
273
+        }
274
+    }
275
+
276
+    private String parseTrackId(String raw) {
277
+        JsonNode root = parseJsonNode(raw);
278
+        String trackId = extractTrackId(root);
279
+        if (!StringUtils.hasText(trackId)) {
280
+            throw new BizException(ErrorCode.KB_SERVICE_FAILED,
281
+                    "RAG 成功但未解析到 track_id,响应:" + abbreviate(raw));
282
+        }
283
+        return trackId;
284
+    }
285
+
286
+    private void assertBusinessOk(JsonNode root) {
287
+        if (root == null || !root.has("code")) {
288
+            return;
289
+        }
290
+        JsonNode c = root.get("code");
291
+        boolean ok = false;
292
+        if (c.isNumber()) {
293
+            int v = c.intValue();
294
+            ok = (v == 0 || v == 200);
295
+        } else if (c.isTextual()) {
296
+            String s = c.asText();
297
+            ok = "0".equals(s) || "200".equals(s) || "success".equalsIgnoreCase(s);
298
+        }
299
+        if (!ok) {
300
+            String msg = root.has("message") ? root.get("message").asText(null) : null;
301
+            if (!StringUtils.hasText(msg) && root.has("msg")) {
302
+                msg = root.get("msg").asText(null);
303
+            }
304
+            if (!StringUtils.hasText(msg)) {
305
+                msg = "RAG 接口返回失败";
306
+            }
307
+            throw new BizException(ErrorCode.KB_SERVICE_FAILED, msg);
308
+        }
309
+    }
310
+
311
+    private static String extractTrackId(JsonNode root) {
312
+        JsonNode data = root.get("data");
313
+        if (data != null && data.isTextual()) {
314
+            String t = data.asText(null);
315
+            if (StringUtils.hasText(t)) {
316
+                return t;
317
+            }
318
+        }
319
+        if (data != null && data.isObject()) {
320
+            for (String key : TRACK_ID_KEYS) {
321
+                if (data.has(key) && !data.get(key).isNull()) {
322
+                    String v = data.get(key).asText(null);
323
+                    if (StringUtils.hasText(v)) {
324
+                        return v;
325
+                    }
125 326
                 }
126
-                return null;
127
-            } catch (HttpStatusCodeException e) {
128
-                if (e.getRawStatusCode() == 404) {
129
-                    return null;
327
+            }
328
+        }
329
+        for (String key : TRACK_ID_KEYS) {
330
+            if (root.has(key) && !root.get(key).isNull()) {
331
+                String v = root.get(key).asText(null);
332
+                if (StringUtils.hasText(v)) {
333
+                    return v;
130 334
                 }
131
-                String b = e.getResponseBodyAsString(StandardCharsets.UTF_8);
132
-                throw new BizException(ErrorCode.KB_SERVICE_FAILED,
133
-                        "知识库删除失败 HTTP " + e.getRawStatusCode() + ":" + abbreviate(b));
134 335
             }
135
-        });
336
+        }
337
+        return null;
338
+    }
339
+
340
+    private BizException httpError(String prefix, HttpStatusCodeException e) {
341
+        String b = e.getResponseBodyAsString(StandardCharsets.UTF_8);
342
+        return new BizException(ErrorCode.KB_SERVICE_FAILED,
343
+                prefix + " HTTP " + e.getRawStatusCode() + ":" + abbreviate(b));
136 344
     }
137 345
 
138 346
     private <T> T executeWithRetry(String operation, Supplier<T> action) {
@@ -236,79 +444,6 @@ public class KnowledgeBaseClientImpl implements KnowledgeBaseClient {
236 444
         }
237 445
     }
238 446
 
239
-    private String parseUploadResponse(String raw) {
240
-        if (!StringUtils.hasText(raw)) {
241
-            throw new BizException(ErrorCode.KB_SERVICE_FAILED, "知识库上传返回空响应");
242
-        }
243
-        JsonNode root;
244
-        try {
245
-            root = objectMapper.readTree(raw);
246
-        } catch (Exception e) {
247
-            throw new BizException(ErrorCode.KB_SERVICE_FAILED, "知识库上传响应非 JSON:" + abbreviate(raw));
248
-        }
249
-        assertBusinessOk(root);
250
-        String fileId = extractFileId(root);
251
-        if (!StringUtils.hasText(fileId)) {
252
-            throw new BizException(ErrorCode.KB_SERVICE_FAILED,
253
-                    "知识库上传成功但未解析到 file_id,响应:" + abbreviate(raw));
254
-        }
255
-        return fileId;
256
-    }
257
-
258
-    private void assertBusinessOk(JsonNode root) {
259
-        if (root == null || !root.has("code")) {
260
-            return;
261
-        }
262
-        JsonNode c = root.get("code");
263
-        boolean ok = false;
264
-        if (c.isNumber()) {
265
-            int v = c.intValue();
266
-            ok = (v == 0 || v == 200);
267
-        } else if (c.isTextual()) {
268
-            String s = c.asText();
269
-            ok = "0".equals(s) || "200".equals(s) || "success".equalsIgnoreCase(s);
270
-        }
271
-        if (!ok) {
272
-            String msg = root.has("message") ? root.get("message").asText(null) : null;
273
-            if (!StringUtils.hasText(msg) && root.has("msg")) {
274
-                msg = root.get("msg").asText(null);
275
-            }
276
-            if (!StringUtils.hasText(msg)) {
277
-                msg = "知识库接口返回失败";
278
-            }
279
-            throw new BizException(ErrorCode.KB_SERVICE_FAILED, msg);
280
-        }
281
-    }
282
-
283
-    private static String extractFileId(JsonNode root) {
284
-        JsonNode data = root.get("data");
285
-        if (data != null && data.isTextual()) {
286
-            String t = data.asText(null);
287
-            if (StringUtils.hasText(t)) {
288
-                return t;
289
-            }
290
-        }
291
-        if (data != null && data.isObject()) {
292
-            for (String key : FILE_ID_KEYS) {
293
-                if (data.has(key) && !data.get(key).isNull()) {
294
-                    String v = data.get(key).asText(null);
295
-                    if (StringUtils.hasText(v)) {
296
-                        return v;
297
-                    }
298
-                }
299
-            }
300
-        }
301
-        for (String key : FILE_ID_KEYS) {
302
-            if (root.has(key) && !root.get(key).isNull()) {
303
-                String v = root.get(key).asText(null);
304
-                if (StringUtils.hasText(v)) {
305
-                    return v;
306
-                }
307
-            }
308
-        }
309
-        return null;
310
-    }
311
-
312 447
     private static String abbreviate(String s) {
313 448
         if (s == null) {
314 449
             return "";

+ 54 - 0
huimv-employment/fe-integration/src/main/java/com/huimv/employment/integration/kb/rag/RagFileListQuery.java

@@ -0,0 +1,54 @@
1
+package com.huimv.employment.integration.kb.rag;
2
+
3
+import com.fasterxml.jackson.annotation.JsonInclude;
4
+import com.fasterxml.jackson.annotation.JsonProperty;
5
+
6
+/**
7
+ * {@code GET /api/rag/files} 分页查询参数。
8
+ */
9
+@JsonInclude(JsonInclude.Include.NON_NULL)
10
+public class RagFileListQuery {
11
+
12
+    private Integer page;
13
+
14
+    @JsonProperty("page_size")
15
+    private Integer pageSize;
16
+
17
+    @JsonProperty("sort_field")
18
+    private String sortField;
19
+
20
+    @JsonProperty("sort_direction")
21
+    private String sortDirection;
22
+
23
+    public Integer getPage() {
24
+        return page;
25
+    }
26
+
27
+    public void setPage(Integer page) {
28
+        this.page = page;
29
+    }
30
+
31
+    public Integer getPageSize() {
32
+        return pageSize;
33
+    }
34
+
35
+    public void setPageSize(Integer pageSize) {
36
+        this.pageSize = pageSize;
37
+    }
38
+
39
+    public String getSortField() {
40
+        return sortField;
41
+    }
42
+
43
+    public void setSortField(String sortField) {
44
+        this.sortField = sortField;
45
+    }
46
+
47
+    public String getSortDirection() {
48
+        return sortDirection;
49
+    }
50
+
51
+    public void setSortDirection(String sortDirection) {
52
+        this.sortDirection = sortDirection;
53
+    }
54
+}

+ 38 - 0
huimv-employment/fe-integration/src/main/java/com/huimv/employment/integration/kb/rag/RagInsertTextRequest.java

@@ -0,0 +1,38 @@
1
+package com.huimv.employment.integration.kb.rag;
2
+
3
+import com.fasterxml.jackson.annotation.JsonProperty;
4
+
5
+/**
6
+ * {@code POST /api/rag/insert-text} 请求体。
7
+ */
8
+public class RagInsertTextRequest {
9
+
10
+    private String text;
11
+
12
+    @JsonProperty("file_source")
13
+    private String fileSource;
14
+
15
+    public RagInsertTextRequest() {
16
+    }
17
+
18
+    public RagInsertTextRequest(String text, String fileSource) {
19
+        this.text = text;
20
+        this.fileSource = fileSource;
21
+    }
22
+
23
+    public String getText() {
24
+        return text;
25
+    }
26
+
27
+    public void setText(String text) {
28
+        this.text = text;
29
+    }
30
+
31
+    public String getFileSource() {
32
+        return fileSource;
33
+    }
34
+
35
+    public void setFileSource(String fileSource) {
36
+        this.fileSource = fileSource;
37
+    }
38
+}

+ 60 - 0
huimv-employment/fe-integration/src/main/java/com/huimv/employment/integration/kb/rag/RagRetrieveRequest.java

@@ -0,0 +1,60 @@
1
+package com.huimv.employment.integration.kb.rag;
2
+
3
+import com.fasterxml.jackson.annotation.JsonInclude;
4
+import com.fasterxml.jackson.annotation.JsonProperty;
5
+
6
+/**
7
+ * {@code POST /api/rag/retrieve} 请求体。
8
+ */
9
+@JsonInclude(JsonInclude.Include.NON_NULL)
10
+public class RagRetrieveRequest {
11
+
12
+    private String query;
13
+
14
+    @JsonProperty("top_k")
15
+    private Integer topK = 5;
16
+
17
+    private String mode = "mix";
18
+
19
+    @JsonProperty("include_references")
20
+    private Boolean includeReferences = true;
21
+
22
+    public RagRetrieveRequest() {
23
+    }
24
+
25
+    public RagRetrieveRequest(String query) {
26
+        this.query = query;
27
+    }
28
+
29
+    public String getQuery() {
30
+        return query;
31
+    }
32
+
33
+    public void setQuery(String query) {
34
+        this.query = query;
35
+    }
36
+
37
+    public Integer getTopK() {
38
+        return topK;
39
+    }
40
+
41
+    public void setTopK(Integer topK) {
42
+        this.topK = topK;
43
+    }
44
+
45
+    public String getMode() {
46
+        return mode;
47
+    }
48
+
49
+    public void setMode(String mode) {
50
+        this.mode = mode;
51
+    }
52
+
53
+    public Boolean getIncludeReferences() {
54
+        return includeReferences;
55
+    }
56
+
57
+    public void setIncludeReferences(Boolean includeReferences) {
58
+        this.includeReferences = includeReferences;
59
+    }
60
+}