deepfish-ai 1.0.24 → 1.0.25
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_CN.md +2 -2
- package/package.json +1 -1
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/docx.js +27 -0
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/embedding.js +211 -515
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/img.js +27 -0
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/pptx.js +27 -0
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/video.js +36 -0
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/xlsx.js +24 -0
- package/src/AgentRobot/BaseAgentRobot/tools/GenerateTools.js +2 -4
- package/src/cli/DefaultConfig.js +0 -2
package/README_CN.md
CHANGED
|
@@ -378,8 +378,8 @@ AI始终使用相对于当前工作目录的相对路径。
|
|
|
378
378
|
|
|
379
379
|
### 对话历史
|
|
380
380
|
|
|
381
|
-
对话历史是以程序执行目录为单位创建的,每个程序的执行目录会对应一个独立的 Agent 上下文。这意味着在不同目录下启动的对话是相互独立的。
|
|
382
|
-
|
|
381
|
+
对话历史是以程序执行目录为单位创建的,每个程序的执行目录会对应一个独立的 Agent 上下文。这意味着在不同目录下启动的对话是相互独立的。
|
|
382
|
+
|
|
383
383
|
> **⚠️ 重要提示:** AI上下文是以目录为单位的,一个目录对应一个上下文。**请不要在同一个目录下开启两个命令行对话框**,以免造成上下文冲突和意外行为。
|
|
384
384
|
|
|
385
385
|
对话历史会在一定时间内自动清除(通过配置文件中的 `maxMemoryExpireTime` 字段控制,默认为 30 天)。您也可以手动管理对话历史:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepfish-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"description": "This is an AI-driven command-line tool built on Node.js, equipped with AI agent and workflow capabilities. It is compatible with a wide range of AI models, can convert natural language into cross-system terminal and file operation commands, and features high extensibility. It supports complex tasks such as translation, content creation, and format conversion, while allowing custom extensions to be automatically generated via AI.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -876,7 +876,33 @@ async function patchDocxText(filePath, replacements) {
|
|
|
876
876
|
|
|
877
877
|
// ─── 工具描述 ─────────────────────────────────────────────────────────────────
|
|
878
878
|
|
|
879
|
+
|
|
880
|
+
// ─── 使用说明 ─────────────────────────────────────────────────────────────────
|
|
881
|
+
|
|
882
|
+
function docxReadme() {
|
|
883
|
+
return `【DOCX 工具使用说明】
|
|
884
|
+
1. 优先使用本工具内置函数完成任务(读取、创建、搜索、替换、模板填充、格式转换、合并等)。
|
|
885
|
+
2. 如果内置函数无法满足需求(如复杂排版保真、特殊宏处理、跨格式高级转换),再尝试使用 LibreOffice 命令行。
|
|
886
|
+
3. 在调用 LibreOffice 前,先检测系统是否已安装 LibreOffice:
|
|
887
|
+
- 已安装:直接使用 LibreOffice 命令行继续处理。
|
|
888
|
+
- 未安装:询问用户是否允许安装。
|
|
889
|
+
4. 若用户同意安装:引导完成安装后继续执行原任务。
|
|
890
|
+
5. 若用户拒绝安装:明确告知当前能力限制,并终止该操作。
|
|
891
|
+
|
|
892
|
+
建议:
|
|
893
|
+
- 常规文档处理优先使用内置函数,速度更快且依赖更少。
|
|
894
|
+
- 仅在确实需要高保真格式转换或复杂排版时才启用 LibreOffice 路径。`
|
|
895
|
+
}
|
|
896
|
+
|
|
879
897
|
const descriptions = [
|
|
898
|
+
{
|
|
899
|
+
type: 'function',
|
|
900
|
+
function: {
|
|
901
|
+
name: 'docxReadme',
|
|
902
|
+
description: '获取 DOCX 工具集的使用说明, 调用函数前必须先查看本说明。',
|
|
903
|
+
parameters: {},
|
|
904
|
+
}
|
|
905
|
+
},
|
|
880
906
|
{
|
|
881
907
|
type: 'function',
|
|
882
908
|
function: {
|
|
@@ -1224,6 +1250,7 @@ const descriptions = [
|
|
|
1224
1250
|
// ─── 导出 ──────────────────────────────────────────────────────────────────────
|
|
1225
1251
|
|
|
1226
1252
|
const functions = {
|
|
1253
|
+
docxReadme,
|
|
1227
1254
|
readDocxText,
|
|
1228
1255
|
readDocxHtml,
|
|
1229
1256
|
getDocxInfo,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
-
const os = require('os')
|
|
3
2
|
const fs = require('fs-extra')
|
|
4
3
|
const crypto = require('crypto')
|
|
5
4
|
const mammoth = require('mammoth')
|
|
@@ -15,25 +14,8 @@ function fail(error, data = null) {
|
|
|
15
14
|
return { success: false, error: error?.message || String(error), data }
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const configDir = path.join(os.homedir(), './.deepfish-ai')
|
|
21
|
-
const configPath = path.join(configDir, './config.js')
|
|
22
|
-
return configPath
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function _loadConfig(configPath) {
|
|
26
|
-
fs.ensureDirSync(path.dirname(configPath))
|
|
27
|
-
if (!fs.existsSync(configPath)) {
|
|
28
|
-
fs.writeFileSync(configPath, 'module.exports = {}')
|
|
29
|
-
}
|
|
30
|
-
const resolved = require.resolve(configPath)
|
|
31
|
-
delete require.cache[resolved]
|
|
32
|
-
return require(configPath)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function _getKbRootPath(knowledgeBasePath = '') {
|
|
36
|
-
return path.resolve(process.cwd(), knowledgeBasePath || '.deepfish-rag')
|
|
17
|
+
function _getKbRootPath() {
|
|
18
|
+
return path.resolve(process.cwd(), '.deepfish-rag')
|
|
37
19
|
}
|
|
38
20
|
|
|
39
21
|
function _getKbIndexPath(kbRootPath) {
|
|
@@ -95,10 +77,49 @@ async function _readDocumentContent(filePath) {
|
|
|
95
77
|
return fs.readFileSync(filePath, 'utf8')
|
|
96
78
|
}
|
|
97
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
|
+
|
|
98
119
|
function _getEmptyKnowledgeBase(kbRootPath) {
|
|
99
120
|
const now = new Date().toISOString()
|
|
100
121
|
return {
|
|
101
|
-
version:
|
|
122
|
+
version: 2,
|
|
102
123
|
name: 'deepfish-rag',
|
|
103
124
|
kbRootPath,
|
|
104
125
|
createdAt: now,
|
|
@@ -116,13 +137,30 @@ function _loadKnowledgeBase(kbRootPath) {
|
|
|
116
137
|
fs.writeFileSync(indexPath, JSON.stringify(emptyKb, null, 2), 'utf8')
|
|
117
138
|
return emptyKb
|
|
118
139
|
}
|
|
140
|
+
|
|
119
141
|
const content = fs.readFileSync(indexPath, 'utf8')
|
|
120
142
|
const parsed = JSON.parse(content)
|
|
121
|
-
|
|
143
|
+
const base = {
|
|
122
144
|
..._getEmptyKnowledgeBase(kbRootPath),
|
|
123
145
|
...parsed,
|
|
124
146
|
kbRootPath,
|
|
125
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
|
|
126
164
|
}
|
|
127
165
|
|
|
128
166
|
function _saveKnowledgeBase(kbRootPath, knowledgeBase) {
|
|
@@ -131,243 +169,170 @@ function _saveKnowledgeBase(kbRootPath, knowledgeBase) {
|
|
|
131
169
|
fs.writeFileSync(indexPath, JSON.stringify(knowledgeBase, null, 2), 'utf8')
|
|
132
170
|
}
|
|
133
171
|
|
|
134
|
-
function
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const step = Math.max(1, size - overlapSize)
|
|
139
|
-
const chunks = []
|
|
140
|
-
for (let i = 0; i < text.length; i += step) {
|
|
141
|
-
const chunk = text.slice(i, i + size)
|
|
142
|
-
if (!chunk.trim()) continue
|
|
143
|
-
chunks.push({
|
|
144
|
-
offsetStart: i,
|
|
145
|
-
offsetEnd: i + chunk.length,
|
|
146
|
-
content: chunk,
|
|
147
|
-
})
|
|
148
|
-
if (i + size >= text.length) {
|
|
149
|
-
break
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return chunks
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function _calcKeywordScore(content = '', keyword = '') {
|
|
156
|
-
if (!keyword) return 0
|
|
157
|
-
const src = String(content || '').toLowerCase()
|
|
158
|
-
const kw = String(keyword || '').toLowerCase()
|
|
159
|
-
let count = 0
|
|
160
|
-
let fromIndex = 0
|
|
161
|
-
while (true) {
|
|
162
|
-
const idx = src.indexOf(kw, fromIndex)
|
|
163
|
-
if (idx < 0) break
|
|
164
|
-
count += 1
|
|
165
|
-
fromIndex = idx + kw.length
|
|
172
|
+
async function _upsertKnowledgeBase(sourcePath = '', knowledgeBasePath = '', reset = false) {
|
|
173
|
+
const inputSourcePath = sourcePath || (await aiInquirer.askInput('请输入源文件目录或文件路径', '', {}))
|
|
174
|
+
if (!inputSourcePath) {
|
|
175
|
+
return fail('未提供源文件目录或文件路径')
|
|
166
176
|
}
|
|
167
|
-
return count
|
|
168
|
-
}
|
|
169
177
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// 提示用户输入
|
|
176
|
-
const res = await aiInquirer.askInput('请输入向量化接口地址', '', {})
|
|
177
|
-
if (res) {
|
|
178
|
-
config.EMBEDDING_API = res
|
|
179
|
-
setEmbeddingConfig(config.EMBEDDING_API, config.EMBEDDING_API_KEY)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
if (!config.EMBEDDING_API_KEY) {
|
|
183
|
-
// 提示用户输入
|
|
184
|
-
const res = await aiInquirer.askInput('请输入向量化接口密钥', '', {})
|
|
185
|
-
if (res) {
|
|
186
|
-
config.EMBEDDING_API_KEY = res
|
|
187
|
-
setEmbeddingConfig(config.EMBEDDING_API, config.EMBEDDING_API_KEY)
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return {
|
|
191
|
-
EMBEDDING_API: config.EMBEDDING_API || '',
|
|
192
|
-
EMBEDDING_API_KEY: config.EMBEDDING_API_KEY || '',
|
|
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
|
+
})
|
|
193
183
|
}
|
|
194
|
-
}
|
|
195
184
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const config = _loadConfig(configPath)
|
|
200
|
-
const newConfig = {
|
|
201
|
-
...config,
|
|
202
|
-
EMBEDDING_API: embeddingApi,
|
|
203
|
-
EMBEDDING_API_KEY: embeddingApiKey,
|
|
185
|
+
const kbRootPath = _getKbRootPath(knowledgeBasePath)
|
|
186
|
+
if (reset && fs.existsSync(kbRootPath)) {
|
|
187
|
+
fs.removeSync(kbRootPath)
|
|
204
188
|
}
|
|
205
|
-
fs.writeFileSync(configPath, `module.exports = ${JSON.stringify(newConfig, null, 2)}`)
|
|
206
|
-
return ok({
|
|
207
|
-
configPath,
|
|
208
|
-
EMBEDDING_API: embeddingApi || '',
|
|
209
|
-
EMBEDDING_API_KEY: embeddingApiKey || '',
|
|
210
|
-
})
|
|
211
|
-
}
|
|
212
189
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const inputSourcePath = sourcePath || (await aiInquirer.askInput('请输入源文件目录或文件路径', '', {}))
|
|
217
|
-
if (!inputSourcePath) {
|
|
218
|
-
return fail('未提供源文件目录或文件路径')
|
|
219
|
-
}
|
|
190
|
+
const knowledgeBase = _loadKnowledgeBase(kbRootPath)
|
|
191
|
+
const sourceFiles = _collectSourceFiles(resolvedSourcePath)
|
|
192
|
+
const supportedFiles = sourceFiles.filter((filePath) => _isSupportedFile(filePath))
|
|
220
193
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
sourcePath: resolvedSourcePath,
|
|
225
|
-
})
|
|
226
|
-
}
|
|
194
|
+
let addedCount = 0
|
|
195
|
+
let updatedCount = 0
|
|
196
|
+
let skippedCount = 0
|
|
227
197
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
+
}
|
|
231
206
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
let updatedCount = 0
|
|
235
|
-
let skippedCount = 0
|
|
207
|
+
const sourceHash = _sha256(content)
|
|
208
|
+
const existingIndex = knowledgeBase.documents.findIndex((item) => item.absolutePath === absolutePath)
|
|
236
209
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const content = await _readDocumentContent(filePath)
|
|
240
|
-
if (!content || !content.trim()) {
|
|
210
|
+
if (existingIndex >= 0) {
|
|
211
|
+
if (knowledgeBase.documents[existingIndex].sourceHash === sourceHash) {
|
|
241
212
|
skippedCount += 1
|
|
242
213
|
continue
|
|
243
214
|
}
|
|
244
215
|
|
|
245
|
-
const
|
|
246
|
-
const existingIndex = knowledgeBase.documents.findIndex((item) => item.sourcePath === filePath)
|
|
247
|
-
if (existingIndex >= 0) {
|
|
248
|
-
if (knowledgeBase.documents[existingIndex].sourceHash === sourceHash) {
|
|
249
|
-
skippedCount += 1
|
|
250
|
-
continue
|
|
251
|
-
}
|
|
252
|
-
knowledgeBase.documents[existingIndex] = {
|
|
253
|
-
...knowledgeBase.documents[existingIndex],
|
|
254
|
-
sourceHash,
|
|
255
|
-
size: Buffer.byteLength(content, 'utf8'),
|
|
256
|
-
content,
|
|
257
|
-
updatedAt: new Date().toISOString(),
|
|
258
|
-
}
|
|
259
|
-
updatedCount += 1
|
|
260
|
-
continue
|
|
261
|
-
}
|
|
216
|
+
const summary = await _extractSummary.call(this, content, 320, absolutePath)
|
|
262
217
|
|
|
263
|
-
knowledgeBase.documents
|
|
264
|
-
|
|
265
|
-
sourcePath: filePath,
|
|
218
|
+
knowledgeBase.documents[existingIndex] = {
|
|
219
|
+
...knowledgeBase.documents[existingIndex],
|
|
266
220
|
sourceHash,
|
|
267
221
|
size: Buffer.byteLength(content, 'utf8'),
|
|
268
|
-
|
|
269
|
-
createdAt: new Date().toISOString(),
|
|
222
|
+
summary,
|
|
270
223
|
updatedAt: new Date().toISOString(),
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
224
|
+
}
|
|
225
|
+
_saveKnowledgeBase(kbRootPath, knowledgeBase)
|
|
226
|
+
updatedCount += 1
|
|
227
|
+
continue
|
|
275
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
|
|
276
245
|
}
|
|
246
|
+
}
|
|
277
247
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
+
})
|
|
287
258
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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)
|
|
299
276
|
} catch (error) {
|
|
300
277
|
return fail(error, { sourcePath, knowledgeBasePath })
|
|
301
278
|
}
|
|
302
279
|
}
|
|
303
280
|
|
|
304
|
-
|
|
305
|
-
function readKnowledgeBase(keyword = '', knowledgeBasePath = '', limit = 10) {
|
|
281
|
+
async function appendKnowledgeBase(sourcePath = '', knowledgeBasePath = '') {
|
|
306
282
|
try {
|
|
307
|
-
|
|
308
|
-
const knowledgeBase = _loadKnowledgeBase(kbRootPath)
|
|
309
|
-
const normalizedKeyword = (keyword || '').trim().toLowerCase()
|
|
310
|
-
const maxResult = Number(limit) > 0 ? Number(limit) : 10
|
|
311
|
-
|
|
312
|
-
const filtered = knowledgeBase.documents.filter((item) => {
|
|
313
|
-
if (!normalizedKeyword) return true
|
|
314
|
-
return (
|
|
315
|
-
item.sourcePath.toLowerCase().includes(normalizedKeyword) ||
|
|
316
|
-
item.content.toLowerCase().includes(normalizedKeyword)
|
|
317
|
-
)
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
const result = filtered.slice(0, maxResult).map((item) => ({
|
|
321
|
-
id: item.id,
|
|
322
|
-
sourcePath: item.sourcePath,
|
|
323
|
-
size: item.size,
|
|
324
|
-
updatedAt: item.updatedAt,
|
|
325
|
-
preview: (item.content || '').slice(0, 240),
|
|
326
|
-
}))
|
|
327
|
-
|
|
328
|
-
return ok({
|
|
329
|
-
knowledgeBasePath: kbRootPath,
|
|
330
|
-
totalDocuments: knowledgeBase.documents.length,
|
|
331
|
-
matchedDocuments: filtered.length,
|
|
332
|
-
items: result,
|
|
333
|
-
})
|
|
283
|
+
return await _upsertKnowledgeBase.call(this, sourcePath, knowledgeBasePath, false)
|
|
334
284
|
} catch (error) {
|
|
335
|
-
return fail(error, {
|
|
285
|
+
return fail(error, { sourcePath, knowledgeBasePath })
|
|
336
286
|
}
|
|
337
287
|
}
|
|
338
288
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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',
|
|
353
311
|
}
|
|
354
|
-
return ok({
|
|
355
|
-
id: doc.id,
|
|
356
|
-
sourcePath: doc.sourcePath,
|
|
357
|
-
size: doc.size,
|
|
358
|
-
createdAt: doc.createdAt,
|
|
359
|
-
updatedAt: doc.updatedAt,
|
|
360
|
-
content: doc.content,
|
|
361
|
-
})
|
|
362
|
-
} catch (error) {
|
|
363
|
-
return fail(error, { documentId, knowledgeBasePath })
|
|
364
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)
|
|
365
331
|
}
|
|
366
332
|
|
|
367
|
-
|
|
368
|
-
function searchKnowledgeBaseChunks(keyword = '', knowledgeBasePath = '', chunkSize = 800, overlap = 120, limit = 10) {
|
|
333
|
+
async function queryKnowledgeBase(keyword = '', knowledgeBasePath = '', limit = 10, includeFullDocument = false) {
|
|
369
334
|
try {
|
|
370
|
-
const normalizedKeyword = (keyword || '').trim()
|
|
335
|
+
const normalizedKeyword = String(keyword || '').trim().toLowerCase()
|
|
371
336
|
if (!normalizedKeyword) {
|
|
372
337
|
return fail('keyword is required')
|
|
373
338
|
}
|
|
@@ -375,61 +340,26 @@ function searchKnowledgeBaseChunks(keyword = '', knowledgeBasePath = '', chunkSi
|
|
|
375
340
|
const kbRootPath = _getKbRootPath(knowledgeBasePath)
|
|
376
341
|
const knowledgeBase = _loadKnowledgeBase(kbRootPath)
|
|
377
342
|
const maxResult = Number(limit) > 0 ? Number(limit) : 10
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
for (const doc of knowledgeBase.documents) {
|
|
381
|
-
const chunks = _chunkText(doc.content || '', chunkSize, overlap)
|
|
382
|
-
chunks.forEach((chunk, index) => {
|
|
383
|
-
const score = _calcKeywordScore(chunk.content, normalizedKeyword)
|
|
384
|
-
if (score > 0) {
|
|
385
|
-
allChunks.push({
|
|
386
|
-
documentId: doc.id,
|
|
387
|
-
sourcePath: doc.sourcePath,
|
|
388
|
-
chunkIndex: index,
|
|
389
|
-
offsetStart: chunk.offsetStart,
|
|
390
|
-
offsetEnd: chunk.offsetEnd,
|
|
391
|
-
score,
|
|
392
|
-
content: chunk.content,
|
|
393
|
-
})
|
|
394
|
-
}
|
|
395
|
-
})
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
const items = allChunks
|
|
399
|
-
.sort((a, b) => b.score - a.score || a.sourcePath.localeCompare(b.sourcePath) || a.chunkIndex - b.chunkIndex)
|
|
400
|
-
.slice(0, maxResult)
|
|
343
|
+
const summaryMatches = _matchSummary(knowledgeBase, normalizedKeyword, maxResult)
|
|
401
344
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
keyword
|
|
405
|
-
|
|
406
|
-
items,
|
|
407
|
-
})
|
|
408
|
-
} catch (error) {
|
|
409
|
-
return fail(error, { keyword, knowledgeBasePath, chunkSize, overlap, limit })
|
|
410
|
-
}
|
|
411
|
-
}
|
|
345
|
+
let subAgentResult = null
|
|
346
|
+
if (summaryMatches.length > 0) {
|
|
347
|
+
subAgentResult = await _queryBySubAgent.call(this, keyword, summaryMatches, includeFullDocument)
|
|
348
|
+
}
|
|
412
349
|
|
|
413
|
-
// 读取知识库统计信息
|
|
414
|
-
function getKnowledgeBaseInfo(knowledgeBasePath = '') {
|
|
415
|
-
try {
|
|
416
|
-
const kbRootPath = _getKbRootPath(knowledgeBasePath)
|
|
417
|
-
const knowledgeBase = _loadKnowledgeBase(kbRootPath)
|
|
418
350
|
return ok({
|
|
419
351
|
knowledgeBasePath: kbRootPath,
|
|
420
|
-
|
|
421
|
-
name: knowledgeBase.name,
|
|
422
|
-
createdAt: knowledgeBase.createdAt,
|
|
423
|
-
updatedAt: knowledgeBase.updatedAt,
|
|
352
|
+
keyword,
|
|
424
353
|
totalDocuments: knowledgeBase.documents.length,
|
|
425
|
-
|
|
354
|
+
matchedDocuments: summaryMatches.length,
|
|
355
|
+
items: summaryMatches,
|
|
356
|
+
subAgentResult,
|
|
426
357
|
})
|
|
427
358
|
} catch (error) {
|
|
428
|
-
return fail(error, { knowledgeBasePath })
|
|
359
|
+
return fail(error, { keyword, knowledgeBasePath, limit, includeFullDocument })
|
|
429
360
|
}
|
|
430
361
|
}
|
|
431
362
|
|
|
432
|
-
// 删除知识库目录
|
|
433
363
|
function deleteKnowledgeBase(knowledgeBasePath = '') {
|
|
434
364
|
try {
|
|
435
365
|
const kbRootPath = _getKbRootPath(knowledgeBasePath)
|
|
@@ -440,6 +370,7 @@ function deleteKnowledgeBase(knowledgeBasePath = '') {
|
|
|
440
370
|
message: 'knowledge base path not found',
|
|
441
371
|
})
|
|
442
372
|
}
|
|
373
|
+
|
|
443
374
|
fs.removeSync(kbRootPath)
|
|
444
375
|
return ok({
|
|
445
376
|
knowledgeBasePath: kbRootPath,
|
|
@@ -450,209 +381,16 @@ function deleteKnowledgeBase(knowledgeBasePath = '') {
|
|
|
450
381
|
}
|
|
451
382
|
}
|
|
452
383
|
|
|
453
|
-
// 先删除再重建知识库
|
|
454
|
-
async function rebuildKnowledgeBase(sourcePath = '', knowledgeBasePath = '') {
|
|
455
|
-
try {
|
|
456
|
-
const deleteResult = deleteKnowledgeBase(knowledgeBasePath)
|
|
457
|
-
if (!deleteResult.success) {
|
|
458
|
-
return deleteResult
|
|
459
|
-
}
|
|
460
|
-
return await buildKnowledgeBase(sourcePath, knowledgeBasePath)
|
|
461
|
-
} catch (error) {
|
|
462
|
-
return fail(error, { sourcePath, knowledgeBasePath })
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// 知识库创建描述
|
|
467
|
-
function getKnowledgeBaseCreationDescription() {
|
|
468
|
-
return `# Knowledge Base Creation Guide
|
|
469
|
-
|
|
470
|
-
## 目标
|
|
471
|
-
使用本工具在命令执行目录下创建或维护本地知识库(默认目录为 .deepfish-rag),并支持后续持续增量更新。
|
|
472
|
-
|
|
473
|
-
## 能完成的任务
|
|
474
|
-
1. 从用户给定的目录或文件加载文档内容,构建本地知识库。
|
|
475
|
-
2. 自动识别常见文本与文档格式(如 md、txt、json、js、pdf、docx、xlsx 等)。
|
|
476
|
-
3. 在重复导入时执行增量更新:
|
|
477
|
-
- 内容未变化:跳过
|
|
478
|
-
- 内容已变化:更新
|
|
479
|
-
- 新文件:新增
|
|
480
|
-
4. 记录每次构建来源和统计信息(sourceHistory),方便审计和复盘。
|
|
481
|
-
5. 支持删除后重建,快速恢复知识库状态。
|
|
482
|
-
|
|
483
|
-
## 关键函数与协同关系
|
|
484
|
-
|
|
485
|
-
### 1) 构建层
|
|
486
|
-
- buildKnowledgeBase(sourcePath, knowledgeBasePath)
|
|
487
|
-
- 创建或续加知识库的主入口。
|
|
488
|
-
- sourcePath 为空时会交互式要求用户输入文件或目录。
|
|
489
|
-
- 默认知识库路径为 process.cwd()/.deepfish-rag。
|
|
490
|
-
|
|
491
|
-
它在内部协同调用:
|
|
492
|
-
- _getKbRootPath:统一知识库目录解析。
|
|
493
|
-
- _loadKnowledgeBase:读取或初始化 index.json。
|
|
494
|
-
- _collectSourceFiles:展开目录得到文件集合。
|
|
495
|
-
- _isSupportedFile:过滤可处理文件类型。
|
|
496
|
-
- _readDocumentContent:按不同文件类型提取文本。
|
|
497
|
-
- _sha256:计算内容哈希,用于增量判断。
|
|
498
|
-
- _saveKnowledgeBase:保存最终索引。
|
|
499
|
-
|
|
500
|
-
### 2) 重建层
|
|
501
|
-
- deleteKnowledgeBase(knowledgeBasePath)
|
|
502
|
-
- 删除现有知识库目录。
|
|
503
|
-
- rebuildKnowledgeBase(sourcePath, knowledgeBasePath)
|
|
504
|
-
- 先删后建。
|
|
505
|
-
- 典型用于“索引异常修复”或“结构升级后重建”。
|
|
506
|
-
|
|
507
|
-
## 推荐执行流程
|
|
508
|
-
1. 调用 buildKnowledgeBase 进行首次构建。
|
|
509
|
-
2. 后续补充文档时再次调用 buildKnowledgeBase(续加)。
|
|
510
|
-
3. 如需彻底刷新:调用 rebuildKnowledgeBase。
|
|
511
|
-
4. 构建完成后再进入检索阶段(read/search 系列函数)。
|
|
512
|
-
|
|
513
|
-
## 面向用户任务的协同策略
|
|
514
|
-
- 用户说“请把这个目录做成知识库”:
|
|
515
|
-
- buildKnowledgeBase(目录路径)
|
|
516
|
-
- 用户说“继续把新资料加进去”:
|
|
517
|
-
- buildKnowledgeBase(新目录路径)
|
|
518
|
-
- 用户说“从头重建”:
|
|
519
|
-
- rebuildKnowledgeBase(目录路径)
|
|
520
|
-
|
|
521
|
-
## 结果校验建议
|
|
522
|
-
构建后建议检查:
|
|
523
|
-
1. getKnowledgeBaseInfo 的 totalDocuments 是否大于 0。
|
|
524
|
-
2. sourceHistory 是否新增一条构建记录。
|
|
525
|
-
3. addedCount / updatedCount / skippedCount 是否符合预期。
|
|
526
|
-
`
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// 知识库检索描述
|
|
530
|
-
function getKnowledgeBaseRetrievalDescription() {
|
|
531
|
-
return `# Knowledge Base Retrieval Guide
|
|
532
|
-
|
|
533
|
-
## 目标
|
|
534
|
-
从本地知识库中高效找到“相关文档”与“关键片段”,用于问答、总结、比对和后续 RAG 召回。
|
|
535
|
-
|
|
536
|
-
## 能完成的任务
|
|
537
|
-
1. 查看知识库总体状态与构建历史。
|
|
538
|
-
2. 按关键词筛选文档摘要,快速定位候选文档。
|
|
539
|
-
3. 按文档 ID 读取全文,进行精读分析。
|
|
540
|
-
4. 按分块检索返回命中片段,适合长文档场景。
|
|
541
|
-
|
|
542
|
-
## 关键函数与协同关系
|
|
543
|
-
|
|
544
|
-
### 1) 元信息确认
|
|
545
|
-
- getKnowledgeBaseInfo(knowledgeBasePath)
|
|
546
|
-
- 获取总文档数、创建时间、更新时间、构建历史。
|
|
547
|
-
- 作用:先判断库是否可用,再决定检索策略。
|
|
548
|
-
|
|
549
|
-
### 2) 粗粒度召回(文档级)
|
|
550
|
-
- readKnowledgeBase(keyword, knowledgeBasePath, limit)
|
|
551
|
-
- 返回文档摘要(id、sourcePath、preview)。
|
|
552
|
-
- 作用:先召回候选文档,缩小范围。
|
|
553
|
-
|
|
554
|
-
### 3) 细粒度阅读(全文级)
|
|
555
|
-
- readKnowledgeBaseDocument(documentId, knowledgeBasePath)
|
|
556
|
-
- 读取指定文档全文。
|
|
557
|
-
- 作用:对高价值候选文档做精读与引用。
|
|
558
|
-
|
|
559
|
-
### 4) 片段级召回(chunk级)
|
|
560
|
-
- searchKnowledgeBaseChunks(keyword, knowledgeBasePath, chunkSize, overlap, limit)
|
|
561
|
-
- 将文档切块并按关键词命中分数排序。
|
|
562
|
-
- 作用:在超长文档里快速找到最相关上下文。
|
|
563
|
-
|
|
564
|
-
它在内部协同调用:
|
|
565
|
-
- _chunkText:按 chunkSize + overlap 生成可检索片段。
|
|
566
|
-
- _calcKeywordScore:计算关键词命中次数并排序。
|
|
567
|
-
|
|
568
|
-
## 推荐检索流程
|
|
569
|
-
1. 调用 getKnowledgeBaseInfo,确认知识库可用。
|
|
570
|
-
2. 调用 readKnowledgeBase(keyword),拿到候选文档列表。
|
|
571
|
-
3. 对重点文档调用 readKnowledgeBaseDocument 进行精读。
|
|
572
|
-
4. 若候选文档过大或命中不精确,调用 searchKnowledgeBaseChunks 做片段召回。
|
|
573
|
-
5. 将片段结果组织为回答证据,必要时回看全文补全上下文。
|
|
574
|
-
|
|
575
|
-
## 典型用户任务协同方案
|
|
576
|
-
|
|
577
|
-
### 场景 A:用户问“知识库里有没有某主题”
|
|
578
|
-
1. readKnowledgeBase(主题词)
|
|
579
|
-
2. 返回候选文档与预览
|
|
580
|
-
|
|
581
|
-
### 场景 B:用户问“请给出该主题的依据段落”
|
|
582
|
-
1. readKnowledgeBase(主题词)
|
|
583
|
-
2. searchKnowledgeBaseChunks(主题词)
|
|
584
|
-
3. 输出高分片段 + 源文件路径
|
|
585
|
-
|
|
586
|
-
### 场景 C:用户问“请基于某篇文档做总结”
|
|
587
|
-
1. readKnowledgeBase(文档名关键词)
|
|
588
|
-
2. readKnowledgeBaseDocument(documentId)
|
|
589
|
-
3. 对全文执行总结
|
|
590
|
-
|
|
591
|
-
## 检索参数建议
|
|
592
|
-
1. limit
|
|
593
|
-
- 初筛推荐 5-20
|
|
594
|
-
2. chunkSize
|
|
595
|
-
- 一般 600-1200
|
|
596
|
-
3. overlap
|
|
597
|
-
- 一般 80-200
|
|
598
|
-
- 过小可能断句,过大可能冗余
|
|
599
|
-
|
|
600
|
-
## 结果质量建议
|
|
601
|
-
1. 优先返回包含 sourcePath 与文档 ID 的证据。
|
|
602
|
-
2. 先文档级筛选,再 chunk 级定位,避免全库全文扫描输出过大。
|
|
603
|
-
3. 对高分片段做二次核对,防止关键词误命中。
|
|
604
|
-
`
|
|
605
|
-
}
|
|
606
|
-
|
|
607
384
|
const descriptions = [
|
|
608
385
|
{
|
|
609
386
|
type: 'function',
|
|
610
387
|
function: {
|
|
611
|
-
name: '
|
|
612
|
-
description: '
|
|
613
|
-
parameters: {
|
|
614
|
-
type: 'object',
|
|
615
|
-
properties: {},
|
|
616
|
-
},
|
|
617
|
-
},
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
type: 'function',
|
|
621
|
-
function: {
|
|
622
|
-
name: 'getKnowledgeBaseRetrievalDescription',
|
|
623
|
-
description: '知识库检索的完整说明文档,包含检索策略、函数协同关系与典型任务执行方案。在执行知识库检索前,建议先阅读此文档以明确使用方法与注意事项。',
|
|
624
|
-
parameters: {
|
|
625
|
-
type: 'object',
|
|
626
|
-
properties: {},
|
|
627
|
-
},
|
|
628
|
-
},
|
|
629
|
-
},
|
|
630
|
-
{
|
|
631
|
-
type: 'function',
|
|
632
|
-
function: {
|
|
633
|
-
name: 'buildKnowledgeBase',
|
|
634
|
-
description: '创建或续加知识库。默认知识库路径为命令执行目录下的 .deepfish-rag。sourcePath 为空时会提示用户输入源文件目录或文件路径。',
|
|
388
|
+
name: 'createKnowledgeBase',
|
|
389
|
+
description: '创建知识库(先删除旧库再重建),存储文档绝对路径和约300字摘要。',
|
|
635
390
|
parameters: {
|
|
636
391
|
type: 'object',
|
|
637
392
|
properties: {
|
|
638
393
|
sourcePath: { type: 'string', description: '源文件目录或文件路径。为空时会交互输入。' },
|
|
639
|
-
knowledgeBasePath: { type: 'string', description: '知识库目录路径,默认 .deepfish-rag(相对命令执行目录)。' },
|
|
640
|
-
},
|
|
641
|
-
required: [],
|
|
642
|
-
},
|
|
643
|
-
},
|
|
644
|
-
},
|
|
645
|
-
{
|
|
646
|
-
type: 'function',
|
|
647
|
-
function: {
|
|
648
|
-
name: 'readKnowledgeBase',
|
|
649
|
-
description: '读取知识库中的文档摘要,支持关键词过滤。可用于快速检索知识库内容。',
|
|
650
|
-
parameters: {
|
|
651
|
-
type: 'object',
|
|
652
|
-
properties: {
|
|
653
|
-
keyword: { type: 'string', description: '关键词,不传表示返回全部文档摘要。' },
|
|
654
|
-
knowledgeBasePath: { type: 'string', description: '知识库目录路径,默认 .deepfish-rag。' },
|
|
655
|
-
limit: { type: 'number', description: '返回条数上限,默认 10。' },
|
|
656
394
|
},
|
|
657
395
|
required: [],
|
|
658
396
|
},
|
|
@@ -661,27 +399,12 @@ const descriptions = [
|
|
|
661
399
|
{
|
|
662
400
|
type: 'function',
|
|
663
401
|
function: {
|
|
664
|
-
name: '
|
|
665
|
-
description: '
|
|
402
|
+
name: 'appendKnowledgeBase',
|
|
403
|
+
description: '续加知识库(增量导入),存储文档绝对路径和约300字摘要。',
|
|
666
404
|
parameters: {
|
|
667
405
|
type: 'object',
|
|
668
406
|
properties: {
|
|
669
|
-
|
|
670
|
-
knowledgeBasePath: { type: 'string', description: '知识库目录路径,默认 .deepfish-rag。' },
|
|
671
|
-
},
|
|
672
|
-
required: ['documentId'],
|
|
673
|
-
},
|
|
674
|
-
},
|
|
675
|
-
},
|
|
676
|
-
{
|
|
677
|
-
type: 'function',
|
|
678
|
-
function: {
|
|
679
|
-
name: 'getKnowledgeBaseInfo',
|
|
680
|
-
description: '读取知识库元信息与历史加载记录(sourceHistory)。',
|
|
681
|
-
parameters: {
|
|
682
|
-
type: 'object',
|
|
683
|
-
properties: {
|
|
684
|
-
knowledgeBasePath: { type: 'string', description: '知识库目录路径,默认 .deepfish-rag。' },
|
|
407
|
+
sourcePath: { type: 'string', description: '源文件目录或文件路径。为空时会交互输入。' },
|
|
685
408
|
},
|
|
686
409
|
required: [],
|
|
687
410
|
},
|
|
@@ -690,16 +413,14 @@ const descriptions = [
|
|
|
690
413
|
{
|
|
691
414
|
type: 'function',
|
|
692
415
|
function: {
|
|
693
|
-
name: '
|
|
694
|
-
description: '
|
|
416
|
+
name: 'queryKnowledgeBase',
|
|
417
|
+
description: '查询知识库:先按摘要匹配关键词,再由子agent在必要时读取命中文档的完整内容。',
|
|
695
418
|
parameters: {
|
|
696
419
|
type: 'object',
|
|
697
420
|
properties: {
|
|
698
421
|
keyword: { type: 'string', description: '检索关键词。' },
|
|
699
|
-
knowledgeBasePath: { type: 'string', description: '知识库目录路径,默认 .deepfish-rag。' },
|
|
700
|
-
chunkSize: { type: 'number', description: '分块长度,默认 800。' },
|
|
701
|
-
overlap: { type: 'number', description: '分块重叠长度,默认 120。' },
|
|
702
422
|
limit: { type: 'number', description: '返回数量上限,默认 10。' },
|
|
423
|
+
includeFullDocument: { type: 'boolean', description: '是否允许子agent读取全文,默认 false。' },
|
|
703
424
|
},
|
|
704
425
|
required: ['keyword'],
|
|
705
426
|
},
|
|
@@ -712,24 +433,7 @@ const descriptions = [
|
|
|
712
433
|
description: '删除知识库目录(默认删除命令执行目录下的 .deepfish-rag)。',
|
|
713
434
|
parameters: {
|
|
714
435
|
type: 'object',
|
|
715
|
-
properties: {
|
|
716
|
-
knowledgeBasePath: { type: 'string', description: '知识库目录路径,默认 .deepfish-rag。' },
|
|
717
|
-
},
|
|
718
|
-
required: [],
|
|
719
|
-
},
|
|
720
|
-
},
|
|
721
|
-
},
|
|
722
|
-
{
|
|
723
|
-
type: 'function',
|
|
724
|
-
function: {
|
|
725
|
-
name: 'rebuildKnowledgeBase',
|
|
726
|
-
description: '重建知识库:先删除原知识库,再从源目录/文件重新构建。',
|
|
727
|
-
parameters: {
|
|
728
|
-
type: 'object',
|
|
729
|
-
properties: {
|
|
730
|
-
sourcePath: { type: 'string', description: '源文件目录或文件路径。为空时会交互输入。' },
|
|
731
|
-
knowledgeBasePath: { type: 'string', description: '知识库目录路径,默认 .deepfish-rag。' },
|
|
732
|
-
},
|
|
436
|
+
properties: {},
|
|
733
437
|
required: [],
|
|
734
438
|
},
|
|
735
439
|
},
|
|
@@ -737,20 +441,15 @@ const descriptions = [
|
|
|
737
441
|
]
|
|
738
442
|
|
|
739
443
|
const functions = {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
readKnowledgeBase,
|
|
744
|
-
readKnowledgeBaseDocument,
|
|
745
|
-
getKnowledgeBaseInfo,
|
|
746
|
-
searchKnowledgeBaseChunks,
|
|
444
|
+
createKnowledgeBase,
|
|
445
|
+
appendKnowledgeBase,
|
|
446
|
+
queryKnowledgeBase,
|
|
747
447
|
deleteKnowledgeBase,
|
|
748
|
-
rebuildKnowledgeBase,
|
|
749
448
|
}
|
|
750
449
|
|
|
751
450
|
const EmbeddingTool = {
|
|
752
451
|
name: 'EmbeddingTool',
|
|
753
|
-
description: '
|
|
452
|
+
description: '提供本地知识库创建、续加、查询、删除能力(索引仅存摘要和绝对路径)',
|
|
754
453
|
platform: 'all',
|
|
755
454
|
descriptions,
|
|
756
455
|
functions,
|
|
@@ -758,6 +457,3 @@ const EmbeddingTool = {
|
|
|
758
457
|
}
|
|
759
458
|
|
|
760
459
|
module.exports = EmbeddingTool
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
@@ -420,7 +420,33 @@ async function imagesToPdf(imagePaths, outputPath, options = {}) {
|
|
|
420
420
|
|
|
421
421
|
// ─── 工具描述 ─────────────────────────────────────────────────────────────────
|
|
422
422
|
|
|
423
|
+
|
|
424
|
+
// ─── 使用说明 ─────────────────────────────────────────────────────────────────
|
|
425
|
+
|
|
426
|
+
function imgReadme() {
|
|
427
|
+
return `【IMG 工具使用说明】
|
|
428
|
+
1. 优先使用本工具内置函数完成任务(元数据读取、缩放、裁剪、旋转、格式转换、压缩、水印等)。
|
|
429
|
+
2. 如果内置函数无法满足需求(如复杂滤镜处理、特殊格式转换、高级图像合成),再尝试使用 ImageMagick 命令行。
|
|
430
|
+
3. 在调用 ImageMagick 前,先检测系统是否已安装 ImageMagick:
|
|
431
|
+
- 已安装:直接使用 ImageMagick 命令行继续处理。
|
|
432
|
+
- 未安装:询问用户是否允许安装。
|
|
433
|
+
4. 若用户同意安装:引导完成安装后继续执行原任务。
|
|
434
|
+
5. 若用户拒绝安装:明确告知当前能力限制,并终止该操作。
|
|
435
|
+
|
|
436
|
+
建议:
|
|
437
|
+
- 常规图像处理优先使用内置函数,速度更快且依赖更少。
|
|
438
|
+
- 仅在确实需要高级图像处理能力时才启用 ImageMagick 路径。`
|
|
439
|
+
}
|
|
440
|
+
|
|
423
441
|
const descriptions = [
|
|
442
|
+
{
|
|
443
|
+
type: 'function',
|
|
444
|
+
function: {
|
|
445
|
+
name: 'imgReadme',
|
|
446
|
+
description: '获取 IMG 工具集的使用说明, 调用函数前必须先查看本说明。',
|
|
447
|
+
parameters: {},
|
|
448
|
+
}
|
|
449
|
+
},
|
|
424
450
|
{
|
|
425
451
|
type: 'function',
|
|
426
452
|
function: {
|
|
@@ -707,6 +733,7 @@ const descriptions = [
|
|
|
707
733
|
// ─── 导出 ──────────────────────────────────────────────────────────────────────
|
|
708
734
|
|
|
709
735
|
const functions = {
|
|
736
|
+
imgReadme,
|
|
710
737
|
getImageInfo,
|
|
711
738
|
resizeImage,
|
|
712
739
|
cropImage,
|
|
@@ -372,7 +372,33 @@ async function getPptxTextStats(filePath) {
|
|
|
372
372
|
|
|
373
373
|
// ─── 工具描述 ─────────────────────────────────────────────────────────────────
|
|
374
374
|
|
|
375
|
+
|
|
376
|
+
// ─── 使用说明 ─────────────────────────────────────────────────────────────────
|
|
377
|
+
|
|
378
|
+
function pptxReadme() {
|
|
379
|
+
return `【PPTX 工具使用说明】
|
|
380
|
+
1. 优先使用本工具内置函数完成任务(信息读取、文本提取、搜索、创建演示文稿、追加幻灯片、文本替换、合并、图片提取等)。
|
|
381
|
+
2. 如果内置函数无法满足需求(如复杂动画保真、特殊版式处理、跨格式高级转换),再尝试使用 LibreOffice 命令行。
|
|
382
|
+
3. 在调用 LibreOffice 前,先检测系统是否已安装 LibreOffice:
|
|
383
|
+
- 已安装:直接使用 LibreOffice 命令行继续处理。
|
|
384
|
+
- 未安装:询问用户是否允许安装。
|
|
385
|
+
4. 若用户同意安装:引导完成安装后继续执行原任务。
|
|
386
|
+
5. 若用户拒绝安装:明确告知当前能力限制,并终止该操作。
|
|
387
|
+
|
|
388
|
+
建议:
|
|
389
|
+
- 常规演示文稿处理优先使用内置函数,速度更快且依赖更少。
|
|
390
|
+
- 仅在确实需要高保真格式转换或复杂版式时才启用 LibreOffice 路径。`
|
|
391
|
+
}
|
|
392
|
+
|
|
375
393
|
const descriptions = [
|
|
394
|
+
{
|
|
395
|
+
type: 'function',
|
|
396
|
+
function: {
|
|
397
|
+
name: 'pptxReadme',
|
|
398
|
+
description: '获取 PPTX 工具集的使用说明, 调用函数前必须先查看本说明。',
|
|
399
|
+
parameters: {},
|
|
400
|
+
}
|
|
401
|
+
},
|
|
376
402
|
{
|
|
377
403
|
type: 'function',
|
|
378
404
|
function: {
|
|
@@ -517,6 +543,7 @@ options 可选:{ title, subject, author, company, layout }。
|
|
|
517
543
|
// ─── 导出 ──────────────────────────────────────────────────────────────────────
|
|
518
544
|
|
|
519
545
|
const functions = {
|
|
546
|
+
pptxReadme,
|
|
520
547
|
getPptxInfo,
|
|
521
548
|
readPptxText,
|
|
522
549
|
searchPptxText,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function videoReadme() {
|
|
2
|
+
return `【VIDEO 工具使用说明】
|
|
3
|
+
1. 优先尝试使用 FFmpeg 命令行。
|
|
4
|
+
3. 在调用 FFmpeg 前,先检测系统是否已安装 FFmpeg:
|
|
5
|
+
- 已安装:直接使用 FFmpeg 命令行继续处理。
|
|
6
|
+
- 未安装:询问用户是否允许安装。
|
|
7
|
+
4. 若用户同意安装:引导完成安装后继续执行原任务。
|
|
8
|
+
5. 若用户拒绝安装:明确告知当前能力限制,并终止该操作。`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const descriptions = [
|
|
12
|
+
{
|
|
13
|
+
type: 'function',
|
|
14
|
+
function: {
|
|
15
|
+
name: 'videoReadme',
|
|
16
|
+
description: '获取 VIDEO 工具集的使用说明, 在处理音频、视频文件前必须先查看本说明。',
|
|
17
|
+
parameters: {},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const functions = {
|
|
23
|
+
videoReadme,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const VideoTool = {
|
|
27
|
+
name: 'VideoTool',
|
|
28
|
+
description: '提供音频、视频处理能力说明',
|
|
29
|
+
platform: 'all',
|
|
30
|
+
descriptions,
|
|
31
|
+
functions,
|
|
32
|
+
isSystem: true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = VideoTool
|
|
36
|
+
|
|
@@ -22,6 +22,21 @@ function resolvePath(filePath) {
|
|
|
22
22
|
|
|
23
23
|
// ─── 工具函数 ─────────────────────────────────────────────────────────────────
|
|
24
24
|
|
|
25
|
+
function xlsxReadme() {
|
|
26
|
+
return `【XLSX 工具使用说明】
|
|
27
|
+
1. 优先使用本工具内置函数完成任务(读取、写入、搜索、合并、CSV 互转等)。
|
|
28
|
+
2. 如果内置函数无法满足需求(如复杂格式保真、特殊公式处理、跨格式高级转换),再尝试使用 LibreOffice 命令行。
|
|
29
|
+
3. 在调用 LibreOffice 前,先检测系统是否已安装 LibreOffice:
|
|
30
|
+
- 已安装:直接使用 LibreOffice 命令行继续处理。
|
|
31
|
+
- 未安装:询问用户是否允许安装。
|
|
32
|
+
4. 若用户同意安装:引导完成安装后继续执行原任务。
|
|
33
|
+
5. 若用户拒绝安装:明确告知当前能力限制,并终止该操作。
|
|
34
|
+
|
|
35
|
+
建议:
|
|
36
|
+
- 简单与结构化数据处理优先使用内置函数,速度更快且依赖更少。
|
|
37
|
+
- 仅在确实需要高保真格式转换时才启用 LibreOffice 路径。`
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
/**
|
|
26
41
|
* 获取 XLSX 文件基本信息(工作表名称、行列数等)
|
|
27
42
|
*/
|
|
@@ -337,6 +352,14 @@ async function getSheetStats(filePath, sheetName) {
|
|
|
337
352
|
// ─── 工具描述 ─────────────────────────────────────────────────────────────────
|
|
338
353
|
|
|
339
354
|
const descriptions = [
|
|
355
|
+
{
|
|
356
|
+
type: 'function',
|
|
357
|
+
function: {
|
|
358
|
+
name: 'xlsxReadme',
|
|
359
|
+
description: '获取 XLSX 工具集的使用说明, 调用函数前必须先查看本说明。',
|
|
360
|
+
parameters: {},
|
|
361
|
+
}
|
|
362
|
+
},
|
|
340
363
|
{
|
|
341
364
|
type: 'function',
|
|
342
365
|
function: {
|
|
@@ -559,6 +582,7 @@ const descriptions = [
|
|
|
559
582
|
// ─── 导出 ──────────────────────────────────────────────────────────────────────
|
|
560
583
|
|
|
561
584
|
const functions = {
|
|
585
|
+
xlsxReadme,
|
|
562
586
|
getXlsxInfo,
|
|
563
587
|
getSheetNames,
|
|
564
588
|
readSheet,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
|
|
3
1
|
const descriptions = [
|
|
4
2
|
{
|
|
5
3
|
type: 'function',
|
|
@@ -176,8 +174,8 @@ const functions = {
|
|
|
176
174
|
},
|
|
177
175
|
systemFileManagement_copyFile: function(srcPath, destPath) {
|
|
178
176
|
try {
|
|
179
|
-
const fullSourcePath = path.resolve(process.cwd(),
|
|
180
|
-
const fullDestPath = path.resolve(process.cwd(),
|
|
177
|
+
const fullSourcePath = path.resolve(process.cwd(), srcPath)
|
|
178
|
+
const fullDestPath = path.resolve(process.cwd(), destPath)
|
|
181
179
|
const destDirPath = path.dirname(fullDestPath)
|
|
182
180
|
|
|
183
181
|
fs.ensureDirSync(destDirPath)
|
package/src/cli/DefaultConfig.js
CHANGED
|
@@ -2,8 +2,6 @@ const defaultConfig = {
|
|
|
2
2
|
ai: [],
|
|
3
3
|
currentAi: '',
|
|
4
4
|
maxIterations: -1, // ai完成工作流的最大迭代次数,-1表示无限制
|
|
5
|
-
maxMessagesLength: 150000, // 最大压缩长度,-1表示无限制
|
|
6
|
-
maxMessagesCount: 100, // 最大压缩数量,-1表示无限制
|
|
7
5
|
maxMemoryExpireTime: 30, // 整个会话的最大过期时间,单位天,-1表示无限制, 0表示不记录
|
|
8
6
|
maxLogExpireTime: 3, // 日志过期时间,单位天,-1表示无限制,0表示不记录
|
|
9
7
|
maxBlockFileSize: 20, // 最大分块文件大小,单位KB;超过该大小的文件需要分块处理
|