@cicctencent/agent-midway 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +280 -0
- package/dist/adapters/express.d.ts +8 -0
- package/dist/adapters/express.js +91 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.js +21 -0
- package/dist/adapters/koa.d.ts +3 -0
- package/dist/adapters/koa.js +75 -0
- package/dist/adapters/midway.d.ts +5 -0
- package/dist/adapters/midway.js +11 -0
- package/dist/adapters/next.d.ts +12 -0
- package/dist/adapters/next.js +89 -0
- package/dist/adapters/shared.d.ts +4 -0
- package/dist/adapters/shared.js +31 -0
- package/dist/channel/dingtalk.d.ts +18 -0
- package/dist/channel/dingtalk.js +68 -0
- package/dist/channel/feishu.d.ts +20 -0
- package/dist/channel/feishu.js +96 -0
- package/dist/channel/index.d.ts +46 -0
- package/dist/channel/index.js +311 -0
- package/dist/channel/types.d.ts +77 -0
- package/dist/channel/types.js +7 -0
- package/dist/channel/wecom.d.ts +22 -0
- package/dist/channel/wecom.js +106 -0
- package/dist/component.d.ts +49 -0
- package/dist/component.js +129 -0
- package/dist/connector/calendar-adapter.d.ts +19 -0
- package/dist/connector/calendar-adapter.js +236 -0
- package/dist/connector/db-adapter.d.ts +28 -0
- package/dist/connector/db-adapter.js +193 -0
- package/dist/connector/email-adapter.d.ts +23 -0
- package/dist/connector/email-adapter.js +192 -0
- package/dist/connector/fs-adapter.d.ts +15 -0
- package/dist/connector/fs-adapter.js +199 -0
- package/dist/connector/http-adapter.d.ts +29 -0
- package/dist/connector/http-adapter.js +181 -0
- package/dist/connector/index.d.ts +24 -0
- package/dist/connector/index.js +454 -0
- package/dist/connector/mcp-adapter.d.ts +27 -0
- package/dist/connector/mcp-adapter.js +156 -0
- package/dist/connector/mq-adapter.d.ts +25 -0
- package/dist/connector/mq-adapter.js +181 -0
- package/dist/connector/types.d.ts +205 -0
- package/dist/connector/types.js +9 -0
- package/dist/controller/a2a.controller.d.ts +41 -0
- package/dist/controller/a2a.controller.js +150 -0
- package/dist/controller/agent-profile.controller.d.ts +97 -0
- package/dist/controller/agent-profile.controller.js +200 -0
- package/dist/controller/agent.controller.d.ts +199 -0
- package/dist/controller/agent.controller.js +414 -0
- package/dist/controller/application.controller.d.ts +113 -0
- package/dist/controller/application.controller.js +217 -0
- package/dist/controller/automation.controller.d.ts +113 -0
- package/dist/controller/automation.controller.js +246 -0
- package/dist/controller/channel.controller.d.ts +73 -0
- package/dist/controller/channel.controller.js +183 -0
- package/dist/controller/chat.controller.d.ts +188 -0
- package/dist/controller/chat.controller.js +375 -0
- package/dist/controller/connector.controller.d.ts +134 -0
- package/dist/controller/connector.controller.js +257 -0
- package/dist/controller/knowledge-base.controller.d.ts +157 -0
- package/dist/controller/knowledge-base.controller.js +278 -0
- package/dist/controller/mcp-server.controller.d.ts +115 -0
- package/dist/controller/mcp-server.controller.js +236 -0
- package/dist/controller/model-config.controller.d.ts +139 -0
- package/dist/controller/model-config.controller.js +274 -0
- package/dist/controller/observability.controller.d.ts +124 -0
- package/dist/controller/observability.controller.js +142 -0
- package/dist/controller/security.controller.d.ts +91 -0
- package/dist/controller/security.controller.js +172 -0
- package/dist/controller/settings.controller.d.ts +83 -0
- package/dist/controller/settings.controller.js +280 -0
- package/dist/core/ai-workstation.d.ts +17 -0
- package/dist/core/ai-workstation.js +129 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +20 -0
- package/dist/core/service-container.d.ts +12 -0
- package/dist/core/service-container.js +54 -0
- package/dist/core/sse.d.ts +6 -0
- package/dist/core/sse.js +56 -0
- package/dist/core/types.d.ts +72 -0
- package/dist/core/types.js +2 -0
- package/dist/dto/agent.dto.d.ts +21 -0
- package/dist/dto/agent.dto.js +79 -0
- package/dist/dto/ai-config.dto.d.ts +67 -0
- package/dist/dto/ai-config.dto.js +249 -0
- package/dist/dto/chat.dto.d.ts +40 -0
- package/dist/dto/chat.dto.js +122 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +195 -0
- package/dist/memory/db-store.d.ts +33 -0
- package/dist/memory/db-store.js +143 -0
- package/dist/memory/index.d.ts +187 -0
- package/dist/memory/index.js +443 -0
- package/dist/model/ai-agent-profile.entity.d.ts +32 -0
- package/dist/model/ai-agent-profile.entity.js +289 -0
- package/dist/model/ai-application.entity.d.ts +20 -0
- package/dist/model/ai-application.entity.js +166 -0
- package/dist/model/ai-chat-memory.entity.d.ts +16 -0
- package/dist/model/ai-chat-memory.entity.js +123 -0
- package/dist/model/ai-chat-message.entity.d.ts +16 -0
- package/dist/model/ai-chat-message.entity.js +122 -0
- package/dist/model/ai-chat-skill.entity.d.ts +19 -0
- package/dist/model/ai-chat-skill.entity.js +155 -0
- package/dist/model/ai-chat-thread.entity.d.ts +15 -0
- package/dist/model/ai-chat-thread.entity.js +113 -0
- package/dist/model/ai-chat-workspace.entity.d.ts +17 -0
- package/dist/model/ai-chat-workspace.entity.js +136 -0
- package/dist/model/ai-kb-document.entity.d.ts +16 -0
- package/dist/model/ai-kb-document.entity.js +122 -0
- package/dist/model/ai-knowledge-base.entity.d.ts +22 -0
- package/dist/model/ai-knowledge-base.entity.js +185 -0
- package/dist/model/ai-mcp-server.entity.d.ts +23 -0
- package/dist/model/ai-mcp-server.entity.js +198 -0
- package/dist/model/ai-model-config.entity.d.ts +24 -0
- package/dist/model/ai-model-config.entity.js +200 -0
- package/dist/service/a2a.service.d.ts +142 -0
- package/dist/service/a2a.service.js +537 -0
- package/dist/service/agent-profile.service.d.ts +34 -0
- package/dist/service/agent-profile.service.js +110 -0
- package/dist/service/agent-server.service.d.ts +91 -0
- package/dist/service/agent-server.service.js +634 -0
- package/dist/service/agent-task-queue.service.d.ts +98 -0
- package/dist/service/agent-task-queue.service.js +283 -0
- package/dist/service/ai-chat.service.d.ts +103 -0
- package/dist/service/ai-chat.service.js +431 -0
- package/dist/service/ai-skill.service.d.ts +116 -0
- package/dist/service/ai-skill.service.js +457 -0
- package/dist/service/application.service.d.ts +42 -0
- package/dist/service/application.service.js +139 -0
- package/dist/service/automation.service.d.ts +37 -0
- package/dist/service/automation.service.js +196 -0
- package/dist/service/connector.service.d.ts +136 -0
- package/dist/service/connector.service.js +524 -0
- package/dist/service/knowledge-base.service.d.ts +138 -0
- package/dist/service/knowledge-base.service.js +528 -0
- package/dist/service/mcp-server.service.d.ts +39 -0
- package/dist/service/mcp-server.service.js +143 -0
- package/dist/service/model-config.service.d.ts +57 -0
- package/dist/service/model-config.service.js +168 -0
- package/dist/service/observability.service.d.ts +145 -0
- package/dist/service/observability.service.js +281 -0
- package/dist/service/openai.service.d.ts +88 -0
- package/dist/service/openai.service.js +406 -0
- package/dist/service/prompt-builder.service.d.ts +50 -0
- package/dist/service/prompt-builder.service.js +246 -0
- package/dist/tools/code-exec.tool.d.ts +37 -0
- package/dist/tools/code-exec.tool.js +162 -0
- package/dist/tools/datetime.tool.d.ts +21 -0
- package/dist/tools/datetime.tool.js +379 -0
- package/dist/tools/http-request.tool.d.ts +43 -0
- package/dist/tools/http-request.tool.js +455 -0
- package/dist/tools/registry.d.ts +71 -0
- package/dist/tools/registry.js +77 -0
- package/dist/tools/text-process.tool.d.ts +7 -0
- package/dist/tools/text-process.tool.js +366 -0
- package/dist/tools/web-search.tool.d.ts +28 -0
- package/dist/tools/web-search.tool.js +304 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.js +7 -0
- package/package.json +69 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.KnowledgeBaseService = void 0;
|
|
16
|
+
const core_1 = require("@midwayjs/core");
|
|
17
|
+
const midwayjs_base_1 = require("@cicctencent/midwayjs-base");
|
|
18
|
+
const typeorm_1 = require("@midwayjs/typeorm");
|
|
19
|
+
const typeorm_2 = require("typeorm");
|
|
20
|
+
const openai_service_1 = require("./openai.service");
|
|
21
|
+
const ai_knowledge_base_entity_1 = __importDefault(require("../model/ai-knowledge-base.entity"));
|
|
22
|
+
const ai_kb_document_entity_1 = __importDefault(require("../model/ai-kb-document.entity"));
|
|
23
|
+
const agent_server_1 = require("@cicctencent/agent-server");
|
|
24
|
+
/**
|
|
25
|
+
* 知识库服务
|
|
26
|
+
* 管理知识库的创建、文档索引、向量搜索
|
|
27
|
+
*
|
|
28
|
+
* 向量存储委托给 agent-server 的 FileKnowledgeBase,
|
|
29
|
+
* 元数据(KnowledgeBase / KBDocument)仍由 TypeORM 实体管理。
|
|
30
|
+
*/
|
|
31
|
+
let KnowledgeBaseService = class KnowledgeBaseService extends midwayjs_base_1.BaseService {
|
|
32
|
+
constructor() {
|
|
33
|
+
super(...arguments);
|
|
34
|
+
/** FileKnowledgeBase 实例缓存: Map<knowledgeBaseId, FileKnowledgeBase> */
|
|
35
|
+
this.kbInstances = new Map();
|
|
36
|
+
}
|
|
37
|
+
get kbModel() {
|
|
38
|
+
if (this.kbRepo)
|
|
39
|
+
return this.kbRepo;
|
|
40
|
+
return (this.kbRepo = this.dataSource.getRepository(ai_knowledge_base_entity_1.default));
|
|
41
|
+
}
|
|
42
|
+
get docModel() {
|
|
43
|
+
if (this.docRepo)
|
|
44
|
+
return this.docRepo;
|
|
45
|
+
return (this.docRepo =
|
|
46
|
+
this.dataSource.getRepository(ai_kb_document_entity_1.default));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 获取 EmbeddingProvider,使用 OpenAIService 生成 embedding
|
|
50
|
+
*/
|
|
51
|
+
getEmbeddingProvider() {
|
|
52
|
+
if (this._embeddingProvider)
|
|
53
|
+
return this._embeddingProvider;
|
|
54
|
+
this._embeddingProvider = {
|
|
55
|
+
dimensions: 1536,
|
|
56
|
+
embed: async (texts) => {
|
|
57
|
+
if (this.openaiService.isAvailable()) {
|
|
58
|
+
try {
|
|
59
|
+
const client = this.openaiService.client;
|
|
60
|
+
const model = this.openaiConfig?.embeddingModel ||
|
|
61
|
+
'text-embedding-3-small';
|
|
62
|
+
const response = await client.embeddings.create({
|
|
63
|
+
model,
|
|
64
|
+
input: texts,
|
|
65
|
+
});
|
|
66
|
+
return response.data.map((item) => item.embedding);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.warn('[KnowledgeBase] 向量生成失败,使用哈希降级:', error.message);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// 降级:使用简单哈希向量
|
|
73
|
+
return texts.map(text => this.hashEmbedding(text, 1536));
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
return this._embeddingProvider;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 获取或创建 FileKnowledgeBase 实例
|
|
80
|
+
* 每个 KnowledgeBase 对应一个 FileKnowledgeBase(通过 docId 前缀隔离)
|
|
81
|
+
*/
|
|
82
|
+
getFileKB(kbId) {
|
|
83
|
+
let fkb = this.kbInstances.get(kbId);
|
|
84
|
+
if (!fkb) {
|
|
85
|
+
fkb = new agent_server_1.FileKnowledgeBase({
|
|
86
|
+
embeddingProvider: this.getEmbeddingProvider(),
|
|
87
|
+
});
|
|
88
|
+
this.kbInstances.set(kbId, fkb);
|
|
89
|
+
}
|
|
90
|
+
return fkb;
|
|
91
|
+
}
|
|
92
|
+
// ===================== 知识库 CRUD =====================
|
|
93
|
+
/** 列出知识库 */
|
|
94
|
+
async list(applicationId, options) {
|
|
95
|
+
const where = { applicationId };
|
|
96
|
+
if (options?.enabled !== undefined) {
|
|
97
|
+
where.enabled = options.enabled ? 1 : 0;
|
|
98
|
+
}
|
|
99
|
+
if (options?.status) {
|
|
100
|
+
where.status = options.status;
|
|
101
|
+
}
|
|
102
|
+
return this.kbModel.find({
|
|
103
|
+
where,
|
|
104
|
+
order: { createTime: 'DESC' },
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** 获取单个知识库 */
|
|
108
|
+
async get(id, applicationId) {
|
|
109
|
+
const where = { id };
|
|
110
|
+
if (applicationId)
|
|
111
|
+
where.applicationId = applicationId;
|
|
112
|
+
return this.kbModel.findOne({ where });
|
|
113
|
+
}
|
|
114
|
+
/** 创建知识库 */
|
|
115
|
+
async create(applicationId, data) {
|
|
116
|
+
const kb = this.kbModel.create({
|
|
117
|
+
...data,
|
|
118
|
+
applicationId,
|
|
119
|
+
status: 'pending',
|
|
120
|
+
documentCount: 0,
|
|
121
|
+
chunkCount: 0,
|
|
122
|
+
});
|
|
123
|
+
return this.kbModel.save(kb);
|
|
124
|
+
}
|
|
125
|
+
/** 更新知识库 */
|
|
126
|
+
async update(id, applicationId, data) {
|
|
127
|
+
await this.kbModel.update({ id, applicationId }, data);
|
|
128
|
+
return this.get(id, applicationId);
|
|
129
|
+
}
|
|
130
|
+
/** 删除知识库 */
|
|
131
|
+
async delete(id, applicationId) {
|
|
132
|
+
// 删除关联文档
|
|
133
|
+
await this.docModel.delete({ knowledgeBaseId: id });
|
|
134
|
+
// 清除 FileKnowledgeBase 实例缓存
|
|
135
|
+
this.kbInstances.delete(id);
|
|
136
|
+
// 删除知识库
|
|
137
|
+
await this.kbModel.delete({ id, applicationId });
|
|
138
|
+
}
|
|
139
|
+
/** 启用知识库 */
|
|
140
|
+
async enable(id, applicationId) {
|
|
141
|
+
await this.kbModel.update({ id, applicationId }, { enabled: 1 });
|
|
142
|
+
return this.get(id, applicationId);
|
|
143
|
+
}
|
|
144
|
+
/** 禁用知识库 */
|
|
145
|
+
async disable(id, applicationId) {
|
|
146
|
+
await this.kbModel.update({ id, applicationId }, { enabled: 0 });
|
|
147
|
+
return this.get(id, applicationId);
|
|
148
|
+
}
|
|
149
|
+
// ===================== 文档管理 =====================
|
|
150
|
+
/** 获取文档列表 */
|
|
151
|
+
async getDocuments(knowledgeBaseId) {
|
|
152
|
+
return this.docModel.find({
|
|
153
|
+
where: { knowledgeBaseId },
|
|
154
|
+
order: { createTime: 'DESC' },
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/** 删除文档 */
|
|
158
|
+
async deleteDocument(documentId) {
|
|
159
|
+
const doc = await this.docModel.findOne({
|
|
160
|
+
where: { id: documentId },
|
|
161
|
+
});
|
|
162
|
+
if (!doc)
|
|
163
|
+
return;
|
|
164
|
+
const kbId = doc.knowledgeBaseId;
|
|
165
|
+
const fkb = this.getFileKB(kbId);
|
|
166
|
+
// 从 FileKnowledgeBase 中移除文档
|
|
167
|
+
await fkb.remove(`doc_${documentId}`);
|
|
168
|
+
// 更新知识库分块计数
|
|
169
|
+
const docs = await this.docModel.find({
|
|
170
|
+
where: { knowledgeBaseId: kbId },
|
|
171
|
+
});
|
|
172
|
+
const remainingDocCount = docs.filter(d => d.id !== documentId).length;
|
|
173
|
+
// 获取剩余分块数
|
|
174
|
+
const docList = await fkb.list();
|
|
175
|
+
const remainingChunks = docList.reduce((sum, d) => sum + d.chunkCount, 0);
|
|
176
|
+
await this.kbModel.update({ id: kbId }, {
|
|
177
|
+
chunkCount: remainingChunks,
|
|
178
|
+
documentCount: remainingDocCount,
|
|
179
|
+
});
|
|
180
|
+
await this.docModel.delete({ id: documentId });
|
|
181
|
+
}
|
|
182
|
+
// ===================== 文档索引 =====================
|
|
183
|
+
/**
|
|
184
|
+
* 从文件索引文档
|
|
185
|
+
* 使用 agent-server 的 parseDocument() 解析文件 + FileKnowledgeBase.ingest() 存储
|
|
186
|
+
*
|
|
187
|
+
* @param id 知识库 ID
|
|
188
|
+
* @param filePath 文件路径
|
|
189
|
+
* @param title 文档标题(可选,默认使用文件名)
|
|
190
|
+
*/
|
|
191
|
+
async indexFromFile(id, filePath, title) {
|
|
192
|
+
const kb = await this.get(id);
|
|
193
|
+
if (!kb) {
|
|
194
|
+
throw new Error('知识库不存在');
|
|
195
|
+
}
|
|
196
|
+
await this.kbModel.update({ id }, { status: 'indexing', lastError: null });
|
|
197
|
+
try {
|
|
198
|
+
const fkb = this.getFileKB(id);
|
|
199
|
+
// 使用 parseDocument() 解析文档文件
|
|
200
|
+
const parsed = await (0, agent_server_1.parseDocument)(filePath, `doc_file_${Date.now()}`);
|
|
201
|
+
const filename = parsed.filename;
|
|
202
|
+
const docTitle = title || filename;
|
|
203
|
+
// 创建文档记录
|
|
204
|
+
const docEntity = this.docModel.create({
|
|
205
|
+
knowledgeBaseId: id,
|
|
206
|
+
title: docTitle,
|
|
207
|
+
content: parsed.text,
|
|
208
|
+
sourcePath: filePath,
|
|
209
|
+
sourceType: 'file',
|
|
210
|
+
status: 'pending',
|
|
211
|
+
meta: { filename, originalChunkCount: parsed.chunks.length },
|
|
212
|
+
});
|
|
213
|
+
const savedDoc = await this.docModel.save(docEntity);
|
|
214
|
+
const documentId = savedDoc.id;
|
|
215
|
+
const docIdStr = `doc_${documentId}`;
|
|
216
|
+
// 构建 TextChunk 数组
|
|
217
|
+
const textChunks = parsed.chunks.map((c, i) => ({
|
|
218
|
+
id: `${docIdStr}_chunk_${i}`,
|
|
219
|
+
docId: docIdStr,
|
|
220
|
+
content: c.content,
|
|
221
|
+
metadata: {
|
|
222
|
+
documentId,
|
|
223
|
+
title: docTitle,
|
|
224
|
+
sourcePath: filePath,
|
|
225
|
+
filename,
|
|
226
|
+
chunkIndex: i,
|
|
227
|
+
totalChunks: parsed.chunks.length,
|
|
228
|
+
...c.metadata,
|
|
229
|
+
},
|
|
230
|
+
}));
|
|
231
|
+
// 使用 FileKnowledgeBase.ingest() 存储
|
|
232
|
+
await fkb.ingest(docIdStr, textChunks);
|
|
233
|
+
// 更新文档状态
|
|
234
|
+
await this.docModel.update({ id: documentId }, { status: 'indexed', chunkCount: parsed.chunks.length });
|
|
235
|
+
// 更新知识库状态
|
|
236
|
+
const docCount = await this.docModel.count({
|
|
237
|
+
where: { knowledgeBaseId: id },
|
|
238
|
+
});
|
|
239
|
+
const docList = await fkb.list();
|
|
240
|
+
const totalChunks = docList.reduce((sum, d) => sum + d.chunkCount, 0);
|
|
241
|
+
await this.kbModel.update({ id }, {
|
|
242
|
+
status: 'ready',
|
|
243
|
+
documentCount: docCount,
|
|
244
|
+
chunkCount: totalChunks,
|
|
245
|
+
});
|
|
246
|
+
return { documentId, chunkCount: parsed.chunks.length };
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
await this.kbModel.update({ id }, { status: 'error', lastError: error.message });
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* 索引文档
|
|
255
|
+
* 对于有 sourcePath 的文档使用 parseDocument() 解析,否则使用内置分块逻辑
|
|
256
|
+
* 所有分块通过 FileKnowledgeBase.ingest() 存储
|
|
257
|
+
*/
|
|
258
|
+
async indexDocuments(id, documents) {
|
|
259
|
+
const kb = await this.get(id);
|
|
260
|
+
if (!kb) {
|
|
261
|
+
throw new Error('知识库不存在');
|
|
262
|
+
}
|
|
263
|
+
// 更新状态为索引中
|
|
264
|
+
await this.kbModel.update({ id }, { status: 'indexing', lastError: null });
|
|
265
|
+
try {
|
|
266
|
+
const fkb = this.getFileKB(id);
|
|
267
|
+
let totalChunks = 0;
|
|
268
|
+
for (const doc of documents) {
|
|
269
|
+
// 创建文档记录
|
|
270
|
+
const docEntity = this.docModel.create({
|
|
271
|
+
knowledgeBaseId: id,
|
|
272
|
+
title: doc.title,
|
|
273
|
+
content: doc.content,
|
|
274
|
+
sourcePath: doc.sourcePath || '',
|
|
275
|
+
sourceType: doc.sourceType || kb.sourceType,
|
|
276
|
+
status: 'pending',
|
|
277
|
+
meta: doc.meta || null,
|
|
278
|
+
});
|
|
279
|
+
const savedDoc = await this.docModel.save(docEntity);
|
|
280
|
+
const documentId = savedDoc.id;
|
|
281
|
+
const docIdStr = `doc_${documentId}`;
|
|
282
|
+
let chunks;
|
|
283
|
+
// 如果有 sourcePath 且是文件类型,使用 parseDocument() 解析
|
|
284
|
+
if (doc.sourcePath &&
|
|
285
|
+
(doc.sourceType === 'file' || !doc.content)) {
|
|
286
|
+
const parsed = await (0, agent_server_1.parseDocument)(doc.sourcePath, docIdStr);
|
|
287
|
+
chunks = parsed.chunks.map(c => ({
|
|
288
|
+
content: c.content,
|
|
289
|
+
metadata: c.metadata || {},
|
|
290
|
+
}));
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
// 使用内置分块逻辑处理文本内容
|
|
294
|
+
chunks = this.chunkTextWithConfig(doc.content, docIdStr, kb.chunkSize, kb.chunkOverlap);
|
|
295
|
+
}
|
|
296
|
+
// 构建 TextChunk 数组
|
|
297
|
+
const textChunks = chunks.map((c, i) => ({
|
|
298
|
+
id: `${docIdStr}_chunk_${i}`,
|
|
299
|
+
docId: docIdStr,
|
|
300
|
+
content: c.content,
|
|
301
|
+
metadata: {
|
|
302
|
+
documentId,
|
|
303
|
+
title: doc.title,
|
|
304
|
+
sourcePath: doc.sourcePath || '',
|
|
305
|
+
chunkIndex: i,
|
|
306
|
+
totalChunks: chunks.length,
|
|
307
|
+
...c.metadata,
|
|
308
|
+
},
|
|
309
|
+
}));
|
|
310
|
+
// 使用 FileKnowledgeBase.ingest() 存储(内部会自动生成 embedding)
|
|
311
|
+
await fkb.ingest(docIdStr, textChunks);
|
|
312
|
+
totalChunks += chunks.length;
|
|
313
|
+
// 更新文档状态
|
|
314
|
+
await this.docModel.update({ id: documentId }, { status: 'indexed', chunkCount: chunks.length });
|
|
315
|
+
}
|
|
316
|
+
// 更新知识库状态
|
|
317
|
+
const docCount = await this.docModel.count({
|
|
318
|
+
where: { knowledgeBaseId: id },
|
|
319
|
+
});
|
|
320
|
+
await this.kbModel.update({ id }, {
|
|
321
|
+
status: 'ready',
|
|
322
|
+
documentCount: docCount,
|
|
323
|
+
chunkCount: totalChunks,
|
|
324
|
+
});
|
|
325
|
+
return {
|
|
326
|
+
documentCount: docCount,
|
|
327
|
+
chunkCount: totalChunks,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
await this.kbModel.update({ id }, { status: 'error', lastError: error.message });
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* 重新索引
|
|
337
|
+
*/
|
|
338
|
+
async reindex(id) {
|
|
339
|
+
const kb = await this.get(id);
|
|
340
|
+
if (!kb) {
|
|
341
|
+
throw new Error('知识库不存在');
|
|
342
|
+
}
|
|
343
|
+
// 清除 FileKnowledgeBase 实例缓存(重新创建)
|
|
344
|
+
this.kbInstances.delete(id);
|
|
345
|
+
// 获取所有文档
|
|
346
|
+
const docs = await this.docModel.find({
|
|
347
|
+
where: { knowledgeBaseId: id },
|
|
348
|
+
});
|
|
349
|
+
if (docs.length === 0) {
|
|
350
|
+
await this.kbModel.update({ id }, { status: 'ready', documentCount: 0, chunkCount: 0 });
|
|
351
|
+
return { documentCount: 0, chunkCount: 0 };
|
|
352
|
+
}
|
|
353
|
+
// 重置文档状态
|
|
354
|
+
await this.docModel.update({ knowledgeBaseId: id }, { status: 'pending', chunkCount: 0 });
|
|
355
|
+
// 重新索引
|
|
356
|
+
return this.indexDocuments(id, docs.map(d => ({
|
|
357
|
+
title: d.title,
|
|
358
|
+
content: d.content || '',
|
|
359
|
+
sourcePath: d.sourcePath,
|
|
360
|
+
sourceType: d.sourceType,
|
|
361
|
+
meta: d.meta || undefined,
|
|
362
|
+
})));
|
|
363
|
+
}
|
|
364
|
+
// ===================== 向量搜索 =====================
|
|
365
|
+
/**
|
|
366
|
+
* 向量搜索
|
|
367
|
+
* 使用 FileKnowledgeBase.search(query, topK) 进行语义检索
|
|
368
|
+
*/
|
|
369
|
+
async search(id, query, options = {}) {
|
|
370
|
+
const kb = await this.get(id);
|
|
371
|
+
if (!kb) {
|
|
372
|
+
throw new Error('知识库不存在');
|
|
373
|
+
}
|
|
374
|
+
const fkb = this.getFileKB(id);
|
|
375
|
+
const topK = options.topK || 5;
|
|
376
|
+
const scoreThreshold = options.scoreThreshold || 0;
|
|
377
|
+
// 使用 FileKnowledgeBase.search() 进行向量搜索
|
|
378
|
+
const results = await fkb.search(query, topK);
|
|
379
|
+
return results
|
|
380
|
+
.filter(r => r.score >= scoreThreshold)
|
|
381
|
+
.map(r => ({
|
|
382
|
+
content: r.chunk.content,
|
|
383
|
+
score: r.score,
|
|
384
|
+
metadata: r.chunk.metadata || {},
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* 使用 EmbeddingVectorIndex 进行向量搜索
|
|
389
|
+
* 适用于需要自定义向量索引的场景
|
|
390
|
+
*
|
|
391
|
+
* @param id 知识库 ID
|
|
392
|
+
* @param query 查询文本
|
|
393
|
+
* @param topK 返回结果数
|
|
394
|
+
*/
|
|
395
|
+
async searchWithVectorIndex(id, query, topK = 5) {
|
|
396
|
+
const fkb = this.getFileKB(id);
|
|
397
|
+
const docList = await fkb.list();
|
|
398
|
+
// 获取所有文档的分块
|
|
399
|
+
const allChunks = [];
|
|
400
|
+
for (const doc of docList) {
|
|
401
|
+
// 通过 FileKnowledgeBase 的 search 获取分块内容
|
|
402
|
+
const results = await fkb.search('', 9999);
|
|
403
|
+
for (const r of results) {
|
|
404
|
+
if (r.chunk.docId === doc.id) {
|
|
405
|
+
allChunks.push(r.chunk);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// 使用 EmbeddingVectorIndex 构建向量索引
|
|
410
|
+
const embeddingProvider = this.getEmbeddingProvider();
|
|
411
|
+
const vectorIndex = new agent_server_1.EmbeddingVectorIndex(embeddingProvider, {
|
|
412
|
+
textExtractor: (item) => item.metadata?.['content'] || '',
|
|
413
|
+
});
|
|
414
|
+
// 构建 VectorItem 数组
|
|
415
|
+
const items = allChunks.map(chunk => ({
|
|
416
|
+
id: chunk.id,
|
|
417
|
+
metadata: { content: chunk.content, ...chunk.metadata },
|
|
418
|
+
vector: chunk.embedding,
|
|
419
|
+
}));
|
|
420
|
+
await vectorIndex.build(items);
|
|
421
|
+
// 执行搜索
|
|
422
|
+
const searchResults = await vectorIndex.searchText(query, topK);
|
|
423
|
+
return searchResults.map(r => ({
|
|
424
|
+
content: r.item.metadata?.['content'] || '',
|
|
425
|
+
score: r.score,
|
|
426
|
+
metadata: r.item.metadata || {},
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
// ===================== 私有方法 =====================
|
|
430
|
+
/**
|
|
431
|
+
* 使用配置参数对文本进行分块
|
|
432
|
+
* 参考 agent-server parseDocument 的 chunkText 逻辑
|
|
433
|
+
*/
|
|
434
|
+
chunkTextWithConfig(text, docId, chunkSize = 500, chunkOverlap = 50) {
|
|
435
|
+
if (!text || !text.trim())
|
|
436
|
+
return [];
|
|
437
|
+
const chunks = [];
|
|
438
|
+
// 先按双换行分段(段落级)
|
|
439
|
+
const paragraphs = text.split(/\n\s*\n/).filter(p => p.trim());
|
|
440
|
+
let currentChunk = '';
|
|
441
|
+
let chunkIndex = 0;
|
|
442
|
+
for (const para of paragraphs) {
|
|
443
|
+
// 如果当前段落本身超过 chunkSize,按固定大小切分
|
|
444
|
+
if (para.length > chunkSize) {
|
|
445
|
+
// 先保存当前累积的内容
|
|
446
|
+
if (currentChunk.trim()) {
|
|
447
|
+
chunks.push({
|
|
448
|
+
content: currentChunk.trim(),
|
|
449
|
+
metadata: { index: chunkIndex++, docId },
|
|
450
|
+
});
|
|
451
|
+
currentChunk = '';
|
|
452
|
+
}
|
|
453
|
+
// 固定大小切分长段落
|
|
454
|
+
for (let i = 0; i < para.length; i += chunkSize - chunkOverlap) {
|
|
455
|
+
const slice = para.slice(i, i + chunkSize);
|
|
456
|
+
chunks.push({
|
|
457
|
+
content: slice.trim(),
|
|
458
|
+
metadata: { index: chunkIndex++, docId },
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
else if (currentChunk.length + para.length + 2 >
|
|
463
|
+
chunkSize) {
|
|
464
|
+
// 当前块已满,保存并开始新块
|
|
465
|
+
if (currentChunk.trim()) {
|
|
466
|
+
chunks.push({
|
|
467
|
+
content: currentChunk.trim(),
|
|
468
|
+
metadata: { index: chunkIndex++, docId },
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
currentChunk = para;
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
// 追加到当前块
|
|
475
|
+
currentChunk = currentChunk
|
|
476
|
+
? `${currentChunk}\n\n${para}`
|
|
477
|
+
: para;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
// 保存最后一个块
|
|
481
|
+
if (currentChunk.trim()) {
|
|
482
|
+
chunks.push({
|
|
483
|
+
content: currentChunk.trim(),
|
|
484
|
+
metadata: { index: chunkIndex++, docId },
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
return chunks;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* 哈希向量(降级方案)
|
|
491
|
+
*/
|
|
492
|
+
hashEmbedding(text, dimensions) {
|
|
493
|
+
const vec = new Array(dimensions).fill(0);
|
|
494
|
+
const words = text.toLowerCase().split(/\s+/);
|
|
495
|
+
for (const word of words) {
|
|
496
|
+
let hash = 0;
|
|
497
|
+
for (let i = 0; i < word.length; i++) {
|
|
498
|
+
hash = ((hash << 5) - hash + word.charCodeAt(i)) | 0;
|
|
499
|
+
}
|
|
500
|
+
const idx = Math.abs(hash) % dimensions;
|
|
501
|
+
vec[idx] += 1;
|
|
502
|
+
}
|
|
503
|
+
// 归一化
|
|
504
|
+
const norm = Math.sqrt(vec.reduce((s, v) => s + v * v, 0));
|
|
505
|
+
if (norm > 0) {
|
|
506
|
+
for (let i = 0; i < dimensions; i++) {
|
|
507
|
+
vec[i] /= norm;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return vec;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
exports.KnowledgeBaseService = KnowledgeBaseService;
|
|
514
|
+
__decorate([
|
|
515
|
+
(0, typeorm_1.InjectDataSource)('default'),
|
|
516
|
+
__metadata("design:type", typeorm_2.DataSource)
|
|
517
|
+
], KnowledgeBaseService.prototype, "dataSource", void 0);
|
|
518
|
+
__decorate([
|
|
519
|
+
(0, core_1.Inject)(),
|
|
520
|
+
__metadata("design:type", openai_service_1.OpenAIService)
|
|
521
|
+
], KnowledgeBaseService.prototype, "openaiService", void 0);
|
|
522
|
+
__decorate([
|
|
523
|
+
(0, core_1.Config)('openai'),
|
|
524
|
+
__metadata("design:type", Object)
|
|
525
|
+
], KnowledgeBaseService.prototype, "openaiConfig", void 0);
|
|
526
|
+
exports.KnowledgeBaseService = KnowledgeBaseService = __decorate([
|
|
527
|
+
(0, core_1.Provide)()
|
|
528
|
+
], KnowledgeBaseService);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BaseService } from '@cicctencent/midwayjs-base';
|
|
2
|
+
import { DataSource, Repository } from 'typeorm';
|
|
3
|
+
import AIMCPServerEntity from '../model/ai-mcp-server.entity';
|
|
4
|
+
/**
|
|
5
|
+
* MCP Server 服务
|
|
6
|
+
* 管理 MCP Server 配置、状态、工具定义等
|
|
7
|
+
*/
|
|
8
|
+
export declare class MCPServerService extends BaseService {
|
|
9
|
+
dataSource: DataSource;
|
|
10
|
+
private repo?;
|
|
11
|
+
get model(): Repository<AIMCPServerEntity>;
|
|
12
|
+
/** 获取所有 MCP Server */
|
|
13
|
+
list(applicationId: number, options?: {
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
status?: string;
|
|
16
|
+
}): Promise<AIMCPServerEntity[]>;
|
|
17
|
+
/** 获取单个 MCP Server */
|
|
18
|
+
get(id: number, applicationId?: number): Promise<AIMCPServerEntity>;
|
|
19
|
+
/** 获取启用的 MCP Server */
|
|
20
|
+
getEnabled(applicationId: number): Promise<AIMCPServerEntity[]>;
|
|
21
|
+
/** 创建 MCP Server */
|
|
22
|
+
create(applicationId: number, data: Partial<AIMCPServerEntity>): Promise<AIMCPServerEntity>;
|
|
23
|
+
/** 更新 MCP Server */
|
|
24
|
+
update(id: number, applicationId: number, data: Partial<AIMCPServerEntity>): Promise<AIMCPServerEntity>;
|
|
25
|
+
/** 删除 MCP Server */
|
|
26
|
+
delete(id: number, applicationId: number): Promise<void>;
|
|
27
|
+
/** 启用 MCP Server */
|
|
28
|
+
enable(id: number, applicationId: number): Promise<AIMCPServerEntity>;
|
|
29
|
+
/** 禁用 MCP Server */
|
|
30
|
+
disable(id: number, applicationId: number): Promise<AIMCPServerEntity>;
|
|
31
|
+
/** 更新连接状态 */
|
|
32
|
+
updateStatus(id: number, applicationId: number, status: 'connected' | 'disconnected' | 'error', error?: string): Promise<AIMCPServerEntity>;
|
|
33
|
+
/** 更新工具定义 */
|
|
34
|
+
updateToolDefinitions(id: number, applicationId: number, tools: any[]): Promise<AIMCPServerEntity>;
|
|
35
|
+
/** 获取 MCP Server 的工具列表 */
|
|
36
|
+
getTools(id: number, applicationId: number): Promise<any[]>;
|
|
37
|
+
/** 批量获取工具定义 */
|
|
38
|
+
getToolsByServerIds(applicationId: number, ids: (number | string)[]): Promise<any[]>;
|
|
39
|
+
}
|