deepfish-ai 1.0.26 → 1.0.28
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/package.json +1 -1
- package/src/AgentRobot/AgentRobotFactory/MainAgentRobot.js +1 -0
- package/src/AgentRobot/BaseAgentRobot/Logger.js +9 -3
- package/src/AgentRobot/BaseAgentRobot/index.js +1 -0
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/AliBailian.js +997 -0
- package/src/AgentRobot/BaseAgentRobot/tools/GenerateTools.js +100 -5
- package/src/AgentRobot/BaseAgentRobot/tools/InquirerTools.js +8 -0
- package/src/AgentRobot/BaseAgentRobot/tools/SystemTools.js +8 -8
- package/src/AgentRobot/BaseAgentRobot/utils/AIRequest.js +59 -10
- package/src/AgentRobot/BaseAgentRobot/utils/AIToolManager.js +1 -0
- package/src/AgentRobot/BaseAgentRobot/utils/AttachmentToolScanner.js +6 -4
- package/src/AgentRobot/BaseAgentRobot/utils/analyzeReturn.test.js +104 -0
- package/src/AgentRobot/BaseAgentRobot/utils/copilot.js +117 -0
- package/src/AgentRobot/BaseAgentRobot/utils/normal.js +288 -0
- package/src/cli/ConfigManager.js +25 -1
- package/src/cli/DefaultConfig.js +22 -6
- package/src/cli/SkillConfigManager.js +3 -2
- package/src/cli/ai-auth.js +231 -0
- package/src/cli/ai-config.js +21 -3
- package/src/cli/index.js +4 -2
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/embedding.js +0 -459
package/src/cli/ai-config.js
CHANGED
|
@@ -11,6 +11,8 @@ const { program } = require('commander')
|
|
|
11
11
|
const { aiCliConfig } = require('./DefaultConfig.js')
|
|
12
12
|
const ConfigManager = require('./ConfigManager.js')
|
|
13
13
|
const aiInquirer = require('../AgentRobot/BaseAgentRobot/utils/aiInquirer.js')
|
|
14
|
+
const aiConsole = require('../AgentRobot/BaseAgentRobot/utils/aiConsole.js')
|
|
15
|
+
const { githubDeviceLogin } = require('./ai-auth.js')
|
|
14
16
|
|
|
15
17
|
const configManager = new ConfigManager()
|
|
16
18
|
const configCommand = program
|
|
@@ -87,6 +89,7 @@ configCommand
|
|
|
87
89
|
type: 'input',
|
|
88
90
|
name: 'baseUrl',
|
|
89
91
|
message: 'Enter API base URL:',
|
|
92
|
+
when: (answers) => answers.Type !== 'Copilot',
|
|
90
93
|
default: (answers) => {
|
|
91
94
|
return aiCliConfig[answers.Type].baseUrl
|
|
92
95
|
},
|
|
@@ -122,6 +125,7 @@ configCommand
|
|
|
122
125
|
type: 'input',
|
|
123
126
|
name: 'apiKey',
|
|
124
127
|
message: 'Enter API key:',
|
|
128
|
+
when: (answers) => answers.Type !== 'Copilot',
|
|
125
129
|
default: (answers) => {
|
|
126
130
|
return aiCliConfig[answers.Type].apiKey
|
|
127
131
|
},
|
|
@@ -143,7 +147,6 @@ configCommand
|
|
|
143
147
|
default: (answers) => {
|
|
144
148
|
return aiCliConfig[answers.Type].maxTokens
|
|
145
149
|
},
|
|
146
|
-
validate: (value) => value > 0 || 'Max tokens must be greater than 0',
|
|
147
150
|
},
|
|
148
151
|
{
|
|
149
152
|
type: 'number',
|
|
@@ -167,14 +170,29 @@ configCommand
|
|
|
167
170
|
const aiConfig = {
|
|
168
171
|
name: answers.name,
|
|
169
172
|
type: aiCliConfig[answers.Type].type,
|
|
170
|
-
baseUrl: answers.baseUrl,
|
|
173
|
+
baseUrl: answers.baseUrl || aiCliConfig[answers.Type].baseUrl,
|
|
171
174
|
model: answers.model,
|
|
172
|
-
apiKey: answers.apiKey,
|
|
175
|
+
apiKey: answers.apiKey || aiCliConfig[answers.Type].apiKey,
|
|
173
176
|
temperature: answers.temperature,
|
|
174
177
|
maxTokens: answers.maxTokens,
|
|
175
178
|
maxContextLength: answers.maxContextLength,
|
|
176
179
|
stream: answers.stream,
|
|
177
180
|
}
|
|
181
|
+
|
|
182
|
+
if (answers.Type === 'Copilot') {
|
|
183
|
+
const loginRes = await githubDeviceLogin({
|
|
184
|
+
configManager,
|
|
185
|
+
targetName: answers.name,
|
|
186
|
+
saveToConfig: false,
|
|
187
|
+
})
|
|
188
|
+
if (!loginRes) {
|
|
189
|
+
aiConsole.logError('Copilot login failed. Configuration was not added.')
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
aiConfig.apiKey = loginRes.accessToken
|
|
193
|
+
aiConfig.githubAuth = loginRes.githubAuth
|
|
194
|
+
}
|
|
195
|
+
|
|
178
196
|
return configManager.addAiConfig(aiConfig)
|
|
179
197
|
})
|
|
180
198
|
|
package/src/cli/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
const { program } = require('commander')
|
|
3
3
|
const { DeepFishAI } = require('../index.js')
|
|
4
4
|
const { GlobalVariable } = require('./GlobalVariable.js')
|
|
5
5
|
require('./ai-config.js')
|
|
6
6
|
require('./ai-skill.js')
|
|
7
7
|
require('./ai-memory.js')
|
|
8
|
+
require('./ai-auth.js')
|
|
8
9
|
const aiConsole = require('../AgentRobot/BaseAgentRobot/utils/aiConsole.js')
|
|
9
10
|
program
|
|
10
11
|
.version('1.0.0')
|
|
@@ -23,7 +24,8 @@ async function main() {
|
|
|
23
24
|
if (
|
|
24
25
|
(program.args &&
|
|
25
26
|
(program.args[0] === 'config' ||
|
|
26
|
-
program.args[0] === 'skill'
|
|
27
|
+
program.args[0] === 'skill' ||
|
|
28
|
+
program.args[0] === 'auth')) ||
|
|
27
29
|
program.args[0] === 'memory'
|
|
28
30
|
) {
|
|
29
31
|
return
|
|
@@ -1,459 +0,0 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const fs = require('fs-extra')
|
|
3
|
-
const crypto = require('crypto')
|
|
4
|
-
const mammoth = require('mammoth')
|
|
5
|
-
const pdfParse = require('pdf-parse')
|
|
6
|
-
const XLSX = require('xlsx')
|
|
7
|
-
const aiInquirer = require('../utils/aiInquirer')
|
|
8
|
-
|
|
9
|
-
function ok(data = null) {
|
|
10
|
-
return { success: true, data }
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function fail(error, data = null) {
|
|
14
|
-
return { success: false, error: error?.message || String(error), data }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function _getKbRootPath() {
|
|
18
|
-
return path.resolve(process.cwd(), '.deepfish-rag')
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function _getKbIndexPath(kbRootPath) {
|
|
22
|
-
return path.join(kbRootPath, 'index.json')
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function _sha256(content) {
|
|
26
|
-
return crypto.createHash('sha256').update(content).digest('hex')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function _isSupportedFile(filePath) {
|
|
30
|
-
const ext = path.extname(filePath).toLowerCase()
|
|
31
|
-
const supportedExts = new Set([
|
|
32
|
-
'.md', '.txt', '.json', '.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx', '.html', '.htm', '.css', '.scss', '.less', '.xml', '.yaml', '.yml', '.csv', '.log', '.sql', '.py', '.java', '.go', '.rs', '.cpp', '.c', '.h', '.docx', '.pdf', '.xlsx', '.xls',
|
|
33
|
-
])
|
|
34
|
-
return supportedExts.has(ext)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function _collectSourceFiles(sourcePath) {
|
|
38
|
-
const stat = fs.statSync(sourcePath)
|
|
39
|
-
if (stat.isFile()) {
|
|
40
|
-
return [sourcePath]
|
|
41
|
-
}
|
|
42
|
-
const files = []
|
|
43
|
-
const walk = (current) => {
|
|
44
|
-
const children = fs.readdirSync(current)
|
|
45
|
-
for (const child of children) {
|
|
46
|
-
const fullPath = path.join(current, child)
|
|
47
|
-
const childStat = fs.statSync(fullPath)
|
|
48
|
-
if (childStat.isDirectory()) {
|
|
49
|
-
walk(fullPath)
|
|
50
|
-
} else if (childStat.isFile()) {
|
|
51
|
-
files.push(fullPath)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
walk(sourcePath)
|
|
56
|
-
return files
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function _readDocumentContent(filePath) {
|
|
60
|
-
const ext = path.extname(filePath).toLowerCase()
|
|
61
|
-
if (ext === '.docx') {
|
|
62
|
-
const result = await mammoth.extractRawText({ path: filePath })
|
|
63
|
-
return result.value || ''
|
|
64
|
-
}
|
|
65
|
-
if (ext === '.pdf') {
|
|
66
|
-
const buffer = fs.readFileSync(filePath)
|
|
67
|
-
const result = await pdfParse(buffer)
|
|
68
|
-
return result.text || ''
|
|
69
|
-
}
|
|
70
|
-
if (ext === '.xlsx' || ext === '.xls') {
|
|
71
|
-
const workbook = XLSX.readFile(filePath)
|
|
72
|
-
return workbook.SheetNames.map((sheetName) => {
|
|
73
|
-
const rows = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], { header: 1 })
|
|
74
|
-
return [`# ${sheetName}`, ...rows.map((row) => row.join(' | '))].join('\n')
|
|
75
|
-
}).join('\n\n')
|
|
76
|
-
}
|
|
77
|
-
return fs.readFileSync(filePath, 'utf8')
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function _normalizeText(content = '') {
|
|
81
|
-
return String(content || '').replace(/\s+/g, ' ').trim()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function _buildSummary(content = '', maxLen = 320) {
|
|
85
|
-
const normalized = _normalizeText(content)
|
|
86
|
-
if (!normalized) return ''
|
|
87
|
-
if (normalized.length <= maxLen) return normalized
|
|
88
|
-
return `${normalized.slice(0, maxLen)}...`
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async function _extractSummary(content = '', maxLen = 320, absolutePath = '') {
|
|
92
|
-
const fallbackSummary = _buildSummary(content, maxLen)
|
|
93
|
-
if (!fallbackSummary) return ''
|
|
94
|
-
|
|
95
|
-
if (!this?.Tools?.requestAI) {
|
|
96
|
-
return fallbackSummary
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const systemDescription = '你是文档摘要助手,只输出简洁摘要正文,不要解释。'
|
|
100
|
-
const prompt = `请为下面文档提取摘要:\n\n文档路径:${absolutePath || '未知'}\n文档内容:\n${content}\n\n要求:\n1. 输出中文摘要,保留关键事实与结论。\n2. 摘要长度不超过${maxLen}个字符。\n3. 不要输出标题、前后缀或解释,只输出摘要正文。`
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
const aiSummary = await this.Tools.requestAI(systemDescription, prompt, 0.2)
|
|
104
|
-
if (typeof aiSummary !== 'string') {
|
|
105
|
-
return fallbackSummary
|
|
106
|
-
}
|
|
107
|
-
const normalizedSummary = _normalizeText(aiSummary)
|
|
108
|
-
if (!normalizedSummary) {
|
|
109
|
-
return fallbackSummary
|
|
110
|
-
}
|
|
111
|
-
return normalizedSummary.length > maxLen
|
|
112
|
-
? `${normalizedSummary.slice(0, maxLen)}...`
|
|
113
|
-
: normalizedSummary
|
|
114
|
-
} catch {
|
|
115
|
-
return fallbackSummary
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function _getEmptyKnowledgeBase(kbRootPath) {
|
|
120
|
-
const now = new Date().toISOString()
|
|
121
|
-
return {
|
|
122
|
-
version: 2,
|
|
123
|
-
name: 'deepfish-rag',
|
|
124
|
-
kbRootPath,
|
|
125
|
-
createdAt: now,
|
|
126
|
-
updatedAt: now,
|
|
127
|
-
sourceHistory: [],
|
|
128
|
-
documents: [],
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function _loadKnowledgeBase(kbRootPath) {
|
|
133
|
-
const indexPath = _getKbIndexPath(kbRootPath)
|
|
134
|
-
fs.ensureDirSync(kbRootPath)
|
|
135
|
-
if (!fs.existsSync(indexPath)) {
|
|
136
|
-
const emptyKb = _getEmptyKnowledgeBase(kbRootPath)
|
|
137
|
-
fs.writeFileSync(indexPath, JSON.stringify(emptyKb, null, 2), 'utf8')
|
|
138
|
-
return emptyKb
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const content = fs.readFileSync(indexPath, 'utf8')
|
|
142
|
-
const parsed = JSON.parse(content)
|
|
143
|
-
const base = {
|
|
144
|
-
..._getEmptyKnowledgeBase(kbRootPath),
|
|
145
|
-
...parsed,
|
|
146
|
-
kbRootPath,
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// 向后兼容旧结构:若旧数据里有content,则在加载时迁移为summary。
|
|
150
|
-
base.documents = (base.documents || []).map((doc) => {
|
|
151
|
-
const absolutePath = path.resolve(doc.absolutePath || doc.sourcePath || '')
|
|
152
|
-
return {
|
|
153
|
-
id: doc.id || _sha256(absolutePath).slice(0, 16),
|
|
154
|
-
absolutePath,
|
|
155
|
-
sourceHash: doc.sourceHash || '',
|
|
156
|
-
size: doc.size || 0,
|
|
157
|
-
summary: doc.summary || _buildSummary(doc.content || ''),
|
|
158
|
-
createdAt: doc.createdAt || base.createdAt,
|
|
159
|
-
updatedAt: doc.updatedAt || base.updatedAt,
|
|
160
|
-
}
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
return base
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function _saveKnowledgeBase(kbRootPath, knowledgeBase) {
|
|
167
|
-
const indexPath = _getKbIndexPath(kbRootPath)
|
|
168
|
-
knowledgeBase.updatedAt = new Date().toISOString()
|
|
169
|
-
fs.writeFileSync(indexPath, JSON.stringify(knowledgeBase, null, 2), 'utf8')
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async function _upsertKnowledgeBase(sourcePath = '', knowledgeBasePath = '', reset = false) {
|
|
173
|
-
const inputSourcePath = sourcePath || (await aiInquirer.askInput('请输入源文件目录或文件路径', '', {}))
|
|
174
|
-
if (!inputSourcePath) {
|
|
175
|
-
return fail('未提供源文件目录或文件路径')
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const resolvedSourcePath = path.resolve(process.cwd(), inputSourcePath)
|
|
179
|
-
if (!fs.existsSync(resolvedSourcePath)) {
|
|
180
|
-
return fail(`Source path does not exist: ${resolvedSourcePath}`, {
|
|
181
|
-
sourcePath: resolvedSourcePath,
|
|
182
|
-
})
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const kbRootPath = _getKbRootPath(knowledgeBasePath)
|
|
186
|
-
if (reset && fs.existsSync(kbRootPath)) {
|
|
187
|
-
fs.removeSync(kbRootPath)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const knowledgeBase = _loadKnowledgeBase(kbRootPath)
|
|
191
|
-
const sourceFiles = _collectSourceFiles(resolvedSourcePath)
|
|
192
|
-
const supportedFiles = sourceFiles.filter((filePath) => _isSupportedFile(filePath))
|
|
193
|
-
|
|
194
|
-
let addedCount = 0
|
|
195
|
-
let updatedCount = 0
|
|
196
|
-
let skippedCount = 0
|
|
197
|
-
|
|
198
|
-
for (const filePath of supportedFiles) {
|
|
199
|
-
try {
|
|
200
|
-
const absolutePath = path.resolve(filePath)
|
|
201
|
-
const content = await _readDocumentContent(absolutePath)
|
|
202
|
-
if (!content || !content.trim()) {
|
|
203
|
-
skippedCount += 1
|
|
204
|
-
continue
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const sourceHash = _sha256(content)
|
|
208
|
-
const existingIndex = knowledgeBase.documents.findIndex((item) => item.absolutePath === absolutePath)
|
|
209
|
-
|
|
210
|
-
if (existingIndex >= 0) {
|
|
211
|
-
if (knowledgeBase.documents[existingIndex].sourceHash === sourceHash) {
|
|
212
|
-
skippedCount += 1
|
|
213
|
-
continue
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const summary = await _extractSummary.call(this, content, 320, absolutePath)
|
|
217
|
-
|
|
218
|
-
knowledgeBase.documents[existingIndex] = {
|
|
219
|
-
...knowledgeBase.documents[existingIndex],
|
|
220
|
-
sourceHash,
|
|
221
|
-
size: Buffer.byteLength(content, 'utf8'),
|
|
222
|
-
summary,
|
|
223
|
-
updatedAt: new Date().toISOString(),
|
|
224
|
-
}
|
|
225
|
-
_saveKnowledgeBase(kbRootPath, knowledgeBase)
|
|
226
|
-
updatedCount += 1
|
|
227
|
-
continue
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const summary = await _extractSummary.call(this, content, 320, absolutePath)
|
|
231
|
-
|
|
232
|
-
knowledgeBase.documents.push({
|
|
233
|
-
id: _sha256(absolutePath).slice(0, 16),
|
|
234
|
-
absolutePath,
|
|
235
|
-
sourceHash,
|
|
236
|
-
size: Buffer.byteLength(content, 'utf8'),
|
|
237
|
-
summary,
|
|
238
|
-
createdAt: new Date().toISOString(),
|
|
239
|
-
updatedAt: new Date().toISOString(),
|
|
240
|
-
})
|
|
241
|
-
_saveKnowledgeBase(kbRootPath, knowledgeBase)
|
|
242
|
-
addedCount += 1
|
|
243
|
-
} catch {
|
|
244
|
-
skippedCount += 1
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
knowledgeBase.sourceHistory.push({
|
|
249
|
-
sourcePath: resolvedSourcePath,
|
|
250
|
-
loadedAt: new Date().toISOString(),
|
|
251
|
-
scannedFiles: sourceFiles.length,
|
|
252
|
-
supportedFiles: supportedFiles.length,
|
|
253
|
-
addedCount,
|
|
254
|
-
updatedCount,
|
|
255
|
-
skippedCount,
|
|
256
|
-
mode: reset ? 'create' : 'append',
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
_saveKnowledgeBase(kbRootPath, knowledgeBase)
|
|
260
|
-
|
|
261
|
-
return ok({
|
|
262
|
-
knowledgeBasePath: kbRootPath,
|
|
263
|
-
sourcePath: resolvedSourcePath,
|
|
264
|
-
scannedFiles: sourceFiles.length,
|
|
265
|
-
supportedFiles: supportedFiles.length,
|
|
266
|
-
addedCount,
|
|
267
|
-
updatedCount,
|
|
268
|
-
skippedCount,
|
|
269
|
-
totalDocuments: knowledgeBase.documents.length,
|
|
270
|
-
})
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
async function createKnowledgeBase(sourcePath = '', knowledgeBasePath = '') {
|
|
274
|
-
try {
|
|
275
|
-
return await _upsertKnowledgeBase.call(this, sourcePath, knowledgeBasePath, true)
|
|
276
|
-
} catch (error) {
|
|
277
|
-
return fail(error, { sourcePath, knowledgeBasePath })
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
async function appendKnowledgeBase(sourcePath = '', knowledgeBasePath = '') {
|
|
282
|
-
try {
|
|
283
|
-
return await _upsertKnowledgeBase.call(this, sourcePath, knowledgeBasePath, false)
|
|
284
|
-
} catch (error) {
|
|
285
|
-
return fail(error, { sourcePath, knowledgeBasePath })
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function _matchSummary(knowledgeBase, normalizedKeyword = '', maxResult = 10) {
|
|
290
|
-
const filtered = knowledgeBase.documents.filter((item) => {
|
|
291
|
-
if (!normalizedKeyword) return true
|
|
292
|
-
const haystack = `${item.absolutePath || ''} ${item.summary || ''}`.toLowerCase()
|
|
293
|
-
return haystack.includes(normalizedKeyword)
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
return filtered.slice(0, maxResult).map((item) => ({
|
|
297
|
-
id: item.id,
|
|
298
|
-
absolutePath: item.absolutePath,
|
|
299
|
-
size: item.size,
|
|
300
|
-
updatedAt: item.updatedAt,
|
|
301
|
-
summary: item.summary,
|
|
302
|
-
}))
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
async function _queryBySubAgent(keyword, summaryMatches, includeFullDocument = false) {
|
|
306
|
-
if (!this?.Tools?.createSubAgent) {
|
|
307
|
-
return {
|
|
308
|
-
success: false,
|
|
309
|
-
skipped: true,
|
|
310
|
-
reason: 'createSubAgent tool is unavailable in current context',
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const prompt = `你是知识库检索子agent,请完成文档检索。
|
|
315
|
-
|
|
316
|
-
用户关键词:${keyword}
|
|
317
|
-
是否允许读取全文:${includeFullDocument ? '是' : '否(仅在必要时)'}
|
|
318
|
-
候选文档(按摘要初筛后)如下:
|
|
319
|
-
${JSON.stringify(summaryMatches, null, 2)}
|
|
320
|
-
|
|
321
|
-
执行要求:
|
|
322
|
-
1. 先使用候选文档的summary进行关键词匹配和排序。
|
|
323
|
-
2. 当summary不足以回答问题时,再读取对应absolutePath的完整文档内容进行补充。
|
|
324
|
-
3. 输出结构化结果,必须包含:
|
|
325
|
-
- 命中文档列表(id、absolutePath、匹配原因)
|
|
326
|
-
- 最终结论
|
|
327
|
-
- 若读取全文,列出已读取的absolutePath。
|
|
328
|
-
`
|
|
329
|
-
|
|
330
|
-
return this.Tools.createSubAgent(prompt)
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
async function queryKnowledgeBase(keyword = '', knowledgeBasePath = '', limit = 10, includeFullDocument = false) {
|
|
334
|
-
try {
|
|
335
|
-
const normalizedKeyword = String(keyword || '').trim().toLowerCase()
|
|
336
|
-
if (!normalizedKeyword) {
|
|
337
|
-
return fail('keyword is required')
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const kbRootPath = _getKbRootPath(knowledgeBasePath)
|
|
341
|
-
const knowledgeBase = _loadKnowledgeBase(kbRootPath)
|
|
342
|
-
const maxResult = Number(limit) > 0 ? Number(limit) : 10
|
|
343
|
-
const summaryMatches = _matchSummary(knowledgeBase, normalizedKeyword, maxResult)
|
|
344
|
-
|
|
345
|
-
let subAgentResult = null
|
|
346
|
-
if (summaryMatches.length > 0) {
|
|
347
|
-
subAgentResult = await _queryBySubAgent.call(this, keyword, summaryMatches, includeFullDocument)
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return ok({
|
|
351
|
-
knowledgeBasePath: kbRootPath,
|
|
352
|
-
keyword,
|
|
353
|
-
totalDocuments: knowledgeBase.documents.length,
|
|
354
|
-
matchedDocuments: summaryMatches.length,
|
|
355
|
-
items: summaryMatches,
|
|
356
|
-
subAgentResult,
|
|
357
|
-
})
|
|
358
|
-
} catch (error) {
|
|
359
|
-
return fail(error, { keyword, knowledgeBasePath, limit, includeFullDocument })
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function deleteKnowledgeBase(knowledgeBasePath = '') {
|
|
364
|
-
try {
|
|
365
|
-
const kbRootPath = _getKbRootPath(knowledgeBasePath)
|
|
366
|
-
if (!fs.existsSync(kbRootPath)) {
|
|
367
|
-
return ok({
|
|
368
|
-
knowledgeBasePath: kbRootPath,
|
|
369
|
-
deleted: false,
|
|
370
|
-
message: 'knowledge base path not found',
|
|
371
|
-
})
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
fs.removeSync(kbRootPath)
|
|
375
|
-
return ok({
|
|
376
|
-
knowledgeBasePath: kbRootPath,
|
|
377
|
-
deleted: true,
|
|
378
|
-
})
|
|
379
|
-
} catch (error) {
|
|
380
|
-
return fail(error, { knowledgeBasePath })
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const descriptions = [
|
|
385
|
-
{
|
|
386
|
-
type: 'function',
|
|
387
|
-
function: {
|
|
388
|
-
name: 'createKnowledgeBase',
|
|
389
|
-
description: '创建知识库(先删除旧库再重建),存储文档绝对路径和约300字摘要。',
|
|
390
|
-
parameters: {
|
|
391
|
-
type: 'object',
|
|
392
|
-
properties: {
|
|
393
|
-
sourcePath: { type: 'string', description: '源文件目录或文件路径。为空时会交互输入。' },
|
|
394
|
-
},
|
|
395
|
-
required: [],
|
|
396
|
-
},
|
|
397
|
-
},
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
type: 'function',
|
|
401
|
-
function: {
|
|
402
|
-
name: 'appendKnowledgeBase',
|
|
403
|
-
description: '续加知识库(增量导入),存储文档绝对路径和约300字摘要。',
|
|
404
|
-
parameters: {
|
|
405
|
-
type: 'object',
|
|
406
|
-
properties: {
|
|
407
|
-
sourcePath: { type: 'string', description: '源文件目录或文件路径。为空时会交互输入。' },
|
|
408
|
-
},
|
|
409
|
-
required: [],
|
|
410
|
-
},
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
type: 'function',
|
|
415
|
-
function: {
|
|
416
|
-
name: 'queryKnowledgeBase',
|
|
417
|
-
description: '查询知识库:先按摘要匹配关键词,再由子agent在必要时读取命中文档的完整内容。',
|
|
418
|
-
parameters: {
|
|
419
|
-
type: 'object',
|
|
420
|
-
properties: {
|
|
421
|
-
keyword: { type: 'string', description: '检索关键词。' },
|
|
422
|
-
limit: { type: 'number', description: '返回数量上限,默认 10。' },
|
|
423
|
-
includeFullDocument: { type: 'boolean', description: '是否允许子agent读取全文,默认 false。' },
|
|
424
|
-
},
|
|
425
|
-
required: ['keyword'],
|
|
426
|
-
},
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
|
-
{
|
|
430
|
-
type: 'function',
|
|
431
|
-
function: {
|
|
432
|
-
name: 'deleteKnowledgeBase',
|
|
433
|
-
description: '删除知识库目录(默认删除命令执行目录下的 .deepfish-rag)。',
|
|
434
|
-
parameters: {
|
|
435
|
-
type: 'object',
|
|
436
|
-
properties: {},
|
|
437
|
-
required: [],
|
|
438
|
-
},
|
|
439
|
-
},
|
|
440
|
-
},
|
|
441
|
-
]
|
|
442
|
-
|
|
443
|
-
const functions = {
|
|
444
|
-
createKnowledgeBase,
|
|
445
|
-
appendKnowledgeBase,
|
|
446
|
-
queryKnowledgeBase,
|
|
447
|
-
deleteKnowledgeBase,
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
const EmbeddingTool = {
|
|
451
|
-
name: 'EmbeddingTool',
|
|
452
|
-
description: '提供本地知识库创建、续加、查询、删除能力(索引仅存摘要和绝对路径)',
|
|
453
|
-
platform: 'all',
|
|
454
|
-
descriptions,
|
|
455
|
-
functions,
|
|
456
|
-
isSystem: true
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
module.exports = EmbeddingTool
|