deepfish-ai 1.0.16 → 1.0.17
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 +19 -1
- package/README_CN.md +20 -1
- package/package.json +1 -1
- package/src/cli/HistoryManager.js +94 -18
- package/src/cli/SkillConfigManager.js +1 -1
- package/src/cli/ai-history.js +3 -1
- package/src/core/AICLI.js +13 -1
- package/src/core/ai-services/AiWorker/AIMessageManager.js +6 -5
- package/src/core/ai-services/AiWorker/AiAgent.js +14 -12
- package/src/core/ai-services/AiWorker/AiPrompt.js +64 -17
- package/src/core/ai-services/AiWorker/AiTools.js +127 -25
- package/src/core/ai-services/AiWorker/index.js +48 -5
- package/src/core/ai-services/index.js +4 -0
- package/src/core/extension/ExtensionManager.js +10 -10
- package/src/core/extension/GenerateExtension.js +333 -0
- package/src/core/extension/InquirerExtension.js +32 -92
- package/src/core/extension/SystemExtension.js +10 -125
- package/src/core/extension/TaskExtension.js +169 -0
- package/src/core/extension/TestExtension.js +1 -1
- package/src/core/utils/log.js +13 -12
package/README.md
CHANGED
|
@@ -258,7 +258,18 @@ ai ext add weather.js
|
|
|
258
258
|
|
|
259
259
|
```bash
|
|
260
260
|
ai skill install https://clawhub.ai/TheSethRose/agent-browser
|
|
261
|
+
ai skill install https://clawhub.ai/steipete/weather
|
|
261
262
|
ai skill ls
|
|
263
|
+
ai skill enable 1
|
|
264
|
+
ai skill disable 0
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**OpenClaw Skill Generation:**
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
ai "Create a weather query skill"
|
|
271
|
+
ai skill add weather-query
|
|
272
|
+
ai skill enable weather-query
|
|
262
273
|
```
|
|
263
274
|
|
|
264
275
|
**Media Processing:**
|
|
@@ -273,6 +284,13 @@ ai "I have ffmpeg5 installed on my system, help me convert all MP4 files in the
|
|
|
273
284
|
ai "Classify all files under the 'model' directory into the 'model2' directory by month, date format YYYY-MM"
|
|
274
285
|
```
|
|
275
286
|
|
|
287
|
+
**Task List Execution:**
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
ai "Create a task list: 1.xxxx; 2.xxxx; ..."
|
|
291
|
+
ai "Execute task list" # Start execution
|
|
292
|
+
```
|
|
293
|
+
|
|
276
294
|
## 6. Extension Development
|
|
277
295
|
|
|
278
296
|
Extensions allow you to add custom functions that AI can use in its workflows. For complex processes, you can develop them yourself or try generating extensions using this program, then register the extension with the program and use the command line to complete tasks.
|
|
@@ -346,7 +364,7 @@ module.exports = {
|
|
|
346
364
|
Rules for automatic scanning of extension modules upon program startup:
|
|
347
365
|
|
|
348
366
|
1. Scanning locations:
|
|
349
|
-
- node_modules in the root directory
|
|
367
|
+
- node_modules in the npm root directory
|
|
350
368
|
- node_modules in the command execution directory
|
|
351
369
|
- the command execution directory itself
|
|
352
370
|
|
package/README_CN.md
CHANGED
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
- [AI服务选择](#ai服务选择)
|
|
56
56
|
- [8. 使用说明](#8-使用说明)
|
|
57
57
|
- [使用相对路径](#使用相对路径)
|
|
58
|
+
- [对话历史](#对话历史)
|
|
58
59
|
- [9. 故障排除](#9-故障排除)
|
|
59
60
|
- [配置问题](#配置问题)
|
|
60
61
|
- [AI服务连接](#ai服务连接)
|
|
@@ -255,7 +256,18 @@ ai ext add weather.js
|
|
|
255
256
|
|
|
256
257
|
```bash
|
|
257
258
|
ai skill install https://clawhub.ai/TheSethRose/agent-browser
|
|
259
|
+
ai skill install https://clawhub.ai/steipete/weather
|
|
258
260
|
ai skill ls
|
|
261
|
+
ai skill enable 1
|
|
262
|
+
ai skill disable 0
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**OpenClaw Skill 生成:**
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
ai "创建一个查询天气的skill"
|
|
269
|
+
ai skill add weather-query
|
|
270
|
+
ai skill enable weather-query
|
|
259
271
|
```
|
|
260
272
|
|
|
261
273
|
**媒体处理:**
|
|
@@ -270,6 +282,13 @@ ai "我的系统上安装了ffmpeg5,帮我将目录中的所有MP4文件转换
|
|
|
270
282
|
ai "将model目录下的所有文件按月份分类到model2目录中,日期格式为YYYY-MM"
|
|
271
283
|
```
|
|
272
284
|
|
|
285
|
+
**任务列表执行:**
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
ai "创建一个任务列表,1.xxxx;2.xxxx;..."
|
|
289
|
+
ai "执行任务列表" # 开始执行
|
|
290
|
+
```
|
|
291
|
+
|
|
273
292
|
## 6. 扩展开发
|
|
274
293
|
|
|
275
294
|
扩展允许您添加AI可以在其工作流中使用的自定义函数, 对于复杂的流程也可以自行开发或尝试使用本程序生成扩展,然后将扩展注册到程序中,在使用命令行来完成任务。
|
|
@@ -342,7 +361,7 @@ module.exports = {
|
|
|
342
361
|
|
|
343
362
|
程序启动时自动扫描扩展模块的规则:
|
|
344
363
|
1. 扫描位置:
|
|
345
|
-
- 根目录的node_modules
|
|
364
|
+
- npm根目录的node_modules
|
|
346
365
|
- 命令执行目录的node_modules
|
|
347
366
|
- 命令执行目录
|
|
348
367
|
2. 扫描文件:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepfish-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
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
|
"bin": {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @Author: Roman 306863030@qq.com
|
|
3
3
|
* @Date: 2026-03-16 09:18:05
|
|
4
4
|
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
-
* @LastEditTime: 2026-03-
|
|
5
|
+
* @LastEditTime: 2026-03-26 15:19:36
|
|
6
6
|
* @FilePath: \deepfish\src\cli\HistoryManager.js
|
|
7
7
|
* @Description: 对话历史记录、恢复
|
|
8
8
|
* @
|
|
@@ -15,12 +15,16 @@ const { v4: uuidv4 } = require('uuid')
|
|
|
15
15
|
const { logSuccess, logError } = require('../core/utils/log')
|
|
16
16
|
const { openDirectory } = require('../core/utils/normal')
|
|
17
17
|
// cache => [history.json, id => [message.json, logs => [log.txt]]]
|
|
18
|
+
// messageType:1.主会话 2.子会话(每次开始前自动清空上下文) 3.子任务会话(任务开始前,自动加载会话历史,或加载主会话历史)
|
|
18
19
|
class HistoryManager {
|
|
19
20
|
constructor() {
|
|
20
21
|
this.configManager = GlobalVariable.configManager
|
|
21
22
|
this.cacheDir = null
|
|
22
23
|
this.historyFilePath = null
|
|
23
24
|
this.history = null
|
|
25
|
+
this.messagePath = null // 主会话历史记录
|
|
26
|
+
this.subMessagePath = null // 子会话历史记录
|
|
27
|
+
this.taskMessagePath = null // 任务会话历史记录
|
|
24
28
|
this.id = null
|
|
25
29
|
this.logDir = null
|
|
26
30
|
GlobalVariable.historyManager = this
|
|
@@ -55,15 +59,21 @@ class HistoryManager {
|
|
|
55
59
|
}
|
|
56
60
|
// 根据id创建目录,再创建一个message.json文件
|
|
57
61
|
const recordDir = path.join(this.cacheDir, id)
|
|
58
|
-
|
|
62
|
+
this.messagePath = path.join(recordDir, 'message.json')
|
|
63
|
+
this.subMessagePath = path.join(recordDir,'subMessage.json')
|
|
64
|
+
this.taskMessagePath = path.join(recordDir,'taskMessage.json')
|
|
59
65
|
fs.ensureDirSync(recordDir)
|
|
60
|
-
fs.writeJsonSync(
|
|
66
|
+
fs.writeJsonSync(this.messagePath, [], { spaces: 2 })
|
|
61
67
|
this.history.push(newHistoryItem)
|
|
62
68
|
this.updateHistory(this.history)
|
|
63
69
|
this.id = newHistoryItem.id
|
|
64
70
|
} else {
|
|
65
71
|
historyItem.execTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
|
|
66
72
|
this.id = historyItem.id
|
|
73
|
+
const recordDir = path.join(this.cacheDir, this.id)
|
|
74
|
+
this.messagePath = path.join(recordDir, 'message.json')
|
|
75
|
+
this.subMessagePath = path.join(recordDir,'subMessage.json')
|
|
76
|
+
this.taskMessagePath = path.join(recordDir,'taskMessage.json')
|
|
67
77
|
}
|
|
68
78
|
const logDir = path.join(this.cacheDir, this.id, 'logs')
|
|
69
79
|
fs.ensureDirSync(logDir)
|
|
@@ -125,25 +135,86 @@ class HistoryManager {
|
|
|
125
135
|
}
|
|
126
136
|
}
|
|
127
137
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (fs.existsSync(
|
|
131
|
-
fs.writeJsonSync(
|
|
138
|
+
// 清除主会话
|
|
139
|
+
clearMainMessage() {
|
|
140
|
+
if (fs.existsSync(this.messagePath)) {
|
|
141
|
+
fs.writeJsonSync(this.messagePath, [], { spaces: 2 })
|
|
132
142
|
logSuccess('History messages have been cleared.')
|
|
133
143
|
return
|
|
134
144
|
}
|
|
135
145
|
logError('No history messages found to clear.')
|
|
136
146
|
}
|
|
147
|
+
|
|
137
148
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (fs.
|
|
141
|
-
fs.writeJsonSync(
|
|
149
|
+
// 清除子会话
|
|
150
|
+
clearSubMessage() {
|
|
151
|
+
if (fs.existsSync(this.subMessagePath)) {
|
|
152
|
+
fs.writeJsonSync(this.subMessagePath, [], { spaces: 2 })
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 清除任务会话
|
|
158
|
+
clearTaskMessage() {
|
|
159
|
+
if (fs.existsSync(this.taskMessagePath)) {
|
|
160
|
+
fs.writeJsonSync(this.taskMessagePath, [], { spaces: 2 })
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 更新主会话
|
|
166
|
+
updateMainMessage(message) {
|
|
167
|
+
if (fs.pathExistsSync(this.messagePath)) {
|
|
168
|
+
fs.writeJsonSync(this.messagePath, message, { spaces: 2 })
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 更新子会话
|
|
173
|
+
updateSubMessage(message) {
|
|
174
|
+
fs.writeJsonSync(this.subMessagePath, message, { spaces: 2 })
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 更新任务会话
|
|
178
|
+
updateTaskMessage(message) {
|
|
179
|
+
fs.writeJsonSync(this.taskMessagePath, message, { spaces: 2 })
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
clearMessage(messageType = 1) {
|
|
184
|
+
switch (messageType) {
|
|
185
|
+
case 1:
|
|
186
|
+
this.clearMainMessage()
|
|
187
|
+
break
|
|
188
|
+
case 2:
|
|
189
|
+
this.clearSubMessage()
|
|
190
|
+
break
|
|
191
|
+
case 3:
|
|
192
|
+
this.clearTaskMessage()
|
|
193
|
+
break
|
|
142
194
|
}
|
|
143
195
|
}
|
|
144
196
|
|
|
145
|
-
|
|
146
|
-
|
|
197
|
+
updateMessage(messageType = 1, message) {
|
|
198
|
+
switch (messageType) {
|
|
199
|
+
case 1:
|
|
200
|
+
this.updateMainMessage(message)
|
|
201
|
+
break
|
|
202
|
+
case 2:
|
|
203
|
+
this.updateSubMessage(message)
|
|
204
|
+
break
|
|
205
|
+
case 3:
|
|
206
|
+
this.updateTaskMessage(message)
|
|
207
|
+
break
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
getMessage(messageType = 1) {
|
|
212
|
+
let messageFile = this.messagePath
|
|
213
|
+
if (messageType === 2) {
|
|
214
|
+
messageFile = this.subMessagePath
|
|
215
|
+
} else if (messageType === 3) {
|
|
216
|
+
messageFile = this.taskMessagePath
|
|
217
|
+
}
|
|
147
218
|
if (!fs.pathExistsSync(messageFile)) {
|
|
148
219
|
return []
|
|
149
220
|
}
|
|
@@ -174,13 +245,14 @@ class HistoryManager {
|
|
|
174
245
|
fs.writeJsonSync(this.historyFilePath, this.history, { spaces: 2 })
|
|
175
246
|
}
|
|
176
247
|
|
|
177
|
-
|
|
248
|
+
|
|
249
|
+
record(messages, messageType = 1) {
|
|
178
250
|
try {
|
|
179
251
|
const config = this.configManager.getConfig()
|
|
180
252
|
if (config.maxHistoryExpireTime === 0) {
|
|
181
253
|
return false
|
|
182
254
|
}
|
|
183
|
-
this.updateMessage(messages)
|
|
255
|
+
this.updateMessage(messageType, messages)
|
|
184
256
|
return true
|
|
185
257
|
} catch (error) {
|
|
186
258
|
console.error('Failed to record:', error.message)
|
|
@@ -200,10 +272,14 @@ class HistoryManager {
|
|
|
200
272
|
)
|
|
201
273
|
try {
|
|
202
274
|
let logEntry = ''
|
|
203
|
-
if (
|
|
204
|
-
logEntry = `[${new Date().toISOString()}][
|
|
275
|
+
if (typeof message === 'string') {
|
|
276
|
+
logEntry = `[${new Date().toISOString()}][###############] ${message}\n`
|
|
205
277
|
} else {
|
|
206
|
-
|
|
278
|
+
if (isCompress) {
|
|
279
|
+
logEntry = `[${new Date().toISOString()}][***compress***] ${message.content}\n`
|
|
280
|
+
} else {
|
|
281
|
+
logEntry = `[${new Date().toISOString()}][${message.role}] ${message.content}\n`
|
|
282
|
+
}
|
|
207
283
|
}
|
|
208
284
|
fs.appendFileSync(logFile, logEntry)
|
|
209
285
|
return true
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @Author: Roman 306863030@qq.com
|
|
3
3
|
* @Date: 2026-03-23 15:23:42
|
|
4
4
|
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
-
* @LastEditTime: 2026-03-
|
|
5
|
+
* @LastEditTime: 2026-03-26 10:34:01
|
|
6
6
|
* @FilePath: \deepfish\src\cli\SkillConfigManager.js
|
|
7
7
|
* @Description: Skill configuration manager
|
|
8
8
|
*/
|
package/src/cli/ai-history.js
CHANGED
|
@@ -9,7 +9,9 @@ extCommand
|
|
|
9
9
|
.command("clear")
|
|
10
10
|
.description("Clear the history messages for the current directory")
|
|
11
11
|
.action(() => {
|
|
12
|
-
historyManager.clearMessage();
|
|
12
|
+
historyManager.clearMessage(1);
|
|
13
|
+
historyManager.clearMessage(2);
|
|
14
|
+
historyManager.clearMessage(3);
|
|
13
15
|
});
|
|
14
16
|
|
|
15
17
|
extCommand
|
package/src/core/AICLI.js
CHANGED
|
@@ -3,10 +3,22 @@ const readline = require('readline')
|
|
|
3
3
|
const { logError } = require('./utils/log')
|
|
4
4
|
const { GlobalVariable } = require('./globalVariable')
|
|
5
5
|
const AIService = require('./ai-services')
|
|
6
|
+
const ConfigManager = require('../cli/ConfigManager')
|
|
7
|
+
const SkillConfigManager = require('../cli/SkillConfigManager')
|
|
8
|
+
const HistoryManager = require('../cli/HistoryManager')
|
|
6
9
|
|
|
7
10
|
class AICLI {
|
|
8
11
|
constructor(config) {
|
|
9
|
-
|
|
12
|
+
if (!GlobalVariable.configManager) {
|
|
13
|
+
GlobalVariable.configManager = new ConfigManager()
|
|
14
|
+
}
|
|
15
|
+
if (!GlobalVariable.skillConfigManager) {
|
|
16
|
+
GlobalVariable.skillConfigManager = new SkillConfigManager()
|
|
17
|
+
}
|
|
18
|
+
if (!GlobalVariable.historyManager) {
|
|
19
|
+
GlobalVariable.historyManager = new HistoryManager()
|
|
20
|
+
}
|
|
21
|
+
this.config = config || GlobalVariable.configManager.getConfig()
|
|
10
22
|
this.aiConfig = GlobalVariable.configManager.getCurrentAiConfig()
|
|
11
23
|
this.skillConfigManager = GlobalVariable.skillConfigManager
|
|
12
24
|
this.historyManager = GlobalVariable.historyManager
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @Author: Roman 306863030@qq.com
|
|
3
3
|
* @Date: 2026-03-16 09:18:05
|
|
4
4
|
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
-
* @LastEditTime: 2026-03-
|
|
5
|
+
* @LastEditTime: 2026-03-26 10:17:59
|
|
6
6
|
* @FilePath: \deepfish\src\core\ai-services\AiWorker\AIMessageManager.js
|
|
7
7
|
* @Description: 上下文管理-添加、自动压缩
|
|
8
8
|
* @
|
|
@@ -15,11 +15,12 @@ class AIMessageManager {
|
|
|
15
15
|
aiClient
|
|
16
16
|
aiConfig
|
|
17
17
|
config
|
|
18
|
-
constructor(aiClient, config, aiConfig, messages) {
|
|
18
|
+
constructor(aiClient, config, aiConfig, messages, messageType = 1) {
|
|
19
19
|
this.aiClient = aiClient
|
|
20
20
|
this.aiConfig = aiConfig
|
|
21
21
|
this.config = config
|
|
22
22
|
this.messages = messages
|
|
23
|
+
this.messageType = messageType
|
|
23
24
|
}
|
|
24
25
|
reLinkMsgs(messages) {
|
|
25
26
|
this.messages = messages
|
|
@@ -27,7 +28,7 @@ class AIMessageManager {
|
|
|
27
28
|
// 添加消息
|
|
28
29
|
addMsg(message) {
|
|
29
30
|
this.messages.push(message)
|
|
30
|
-
GlobalVariable.historyManager.record(this.messages)
|
|
31
|
+
GlobalVariable.historyManager.record(this.messages, this.messageType)
|
|
31
32
|
GlobalVariable.historyManager.log(message)
|
|
32
33
|
}
|
|
33
34
|
// 添加tool
|
|
@@ -41,7 +42,7 @@ class AIMessageManager {
|
|
|
41
42
|
content: content,
|
|
42
43
|
}
|
|
43
44
|
this.messages.push(message)
|
|
44
|
-
GlobalVariable.historyManager.record(this.messages)
|
|
45
|
+
GlobalVariable.historyManager.record(this.messages, this.messageType)
|
|
45
46
|
GlobalVariable.historyManager.log(message)
|
|
46
47
|
}
|
|
47
48
|
/**
|
|
@@ -91,7 +92,7 @@ class AIMessageManager {
|
|
|
91
92
|
} else if (lastUserMessageIndex === messages.length - 1) {
|
|
92
93
|
newMessages.push(messages[lastUserMessageIndex])
|
|
93
94
|
}
|
|
94
|
-
GlobalVariable.historyManager.record(newMessages)
|
|
95
|
+
GlobalVariable.historyManager.record(newMessages, this.messageType)
|
|
95
96
|
} else if (messages.length === 2) {
|
|
96
97
|
const summary = await this._getSummary([messages[1]])
|
|
97
98
|
newMessages.push([messages[0], summary])
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @Author: Roman 306863030@qq.com
|
|
3
3
|
* @Date: 2026-03-16 09:18:05
|
|
4
|
-
* @LastEditors:
|
|
5
|
-
* @LastEditTime: 2026-03-26
|
|
4
|
+
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
+
* @LastEditTime: 2026-03-26 19:32:11
|
|
6
6
|
* @FilePath: \deepfish\src\core\ai-services\AiWorker\AiAgent.js
|
|
7
7
|
* @Description: 工作流循环
|
|
8
8
|
* @
|
|
@@ -22,6 +22,7 @@ class AiAgent {
|
|
|
22
22
|
config,
|
|
23
23
|
aiConfig,
|
|
24
24
|
extensionTools = { descriptions: [], functions: {} },
|
|
25
|
+
messageType = 1
|
|
25
26
|
) {
|
|
26
27
|
this.aiClient = aiClient
|
|
27
28
|
this.config = config
|
|
@@ -29,7 +30,8 @@ class AiAgent {
|
|
|
29
30
|
this.maxIterations =
|
|
30
31
|
config.maxIterations === -1 ? Infinity : config.maxIterations
|
|
31
32
|
this.maxBlockFileSize = this.config.maxBlockFileSize || 20 // 默认20KB
|
|
32
|
-
this.aiMessageManager = new AIMessageManager(aiClient, config, aiConfig, [])
|
|
33
|
+
this.aiMessageManager = new AIMessageManager(aiClient, config, aiConfig, [], messageType)
|
|
34
|
+
this.messageType = messageType
|
|
33
35
|
this.extensionTools = extensionTools
|
|
34
36
|
this.name = config.name
|
|
35
37
|
}
|
|
@@ -60,12 +62,16 @@ class AiAgent {
|
|
|
60
62
|
loadingStop(`${this.name} have finished thinking.`)
|
|
61
63
|
loadingStop = null
|
|
62
64
|
}
|
|
63
|
-
logInfo(content)
|
|
64
65
|
// 检查是否是任务完成的总结响应(没有工具调用且有内容)
|
|
65
66
|
if (tool_calls) {
|
|
66
67
|
// 执行函数
|
|
68
|
+
logInfo(content)
|
|
67
69
|
await this.execTools(tool_calls)
|
|
68
70
|
} else {
|
|
71
|
+
if (this.messageType === 1) {
|
|
72
|
+
// 只有主任务输出最后总结
|
|
73
|
+
logInfo(content)
|
|
74
|
+
}
|
|
69
75
|
// 没有工具调用,结束
|
|
70
76
|
break
|
|
71
77
|
}
|
|
@@ -89,15 +95,12 @@ class AiAgent {
|
|
|
89
95
|
for (const toolCall of tool_calls) {
|
|
90
96
|
const { id, function: func } = toolCall
|
|
91
97
|
const { name, arguments: args } = func
|
|
92
|
-
|
|
98
|
+
const toolFunctions = this.extensionTools.functions
|
|
93
99
|
logInfo(`Calling tool ${toolCall.function.name}`)
|
|
94
|
-
if (
|
|
100
|
+
if (toolFunctions[name]) {
|
|
95
101
|
try {
|
|
96
102
|
const parsedArgs = typeof args === 'string' ? JSON.parse(args) : args
|
|
97
|
-
|
|
98
|
-
const fileInfo = await this.extensionTools.functions['getFileInfo'](
|
|
99
|
-
parsedArgs.filePath,
|
|
100
|
-
)
|
|
103
|
+
const fileInfo = await toolFunctions['getFileInfo'](parsedArgs.filePath)
|
|
101
104
|
if (fileInfo && fileInfo.isFile && fileInfo.size > this.maxBlockFileSize * 1024) {
|
|
102
105
|
this.aiMessageManager.addTool(id, {
|
|
103
106
|
error:
|
|
@@ -107,8 +110,7 @@ class AiAgent {
|
|
|
107
110
|
})
|
|
108
111
|
continue
|
|
109
112
|
}
|
|
110
|
-
|
|
111
|
-
let result = await toolFunction(...Object.values(parsedArgs))
|
|
113
|
+
let result = await toolFunctions[name](...Object.values(parsedArgs))
|
|
112
114
|
let toolContent = JSON.stringify(result)
|
|
113
115
|
if (name !== 'requestAI') {
|
|
114
116
|
const MAX_CONTENT_SIZE = this.maxBlockFileSize * 1024
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
const { GlobalVariable } = require("../../globalVariable")
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @Author: Roman 306863030@qq.com
|
|
3
5
|
* @Date: 2026-03-17 09:12:22
|
|
4
|
-
* @LastEditors:
|
|
5
|
-
* @LastEditTime: 2026-03-26
|
|
6
|
+
* @LastEditors: Roman 306863030@qq.com
|
|
7
|
+
* @LastEditTime: 2026-03-26 16:56:47
|
|
6
8
|
* @FilePath: \deepfish\src\core\ai-services\AiWorker\AiPrompt.js
|
|
7
9
|
* @Description: AI请求提示词
|
|
8
10
|
* @
|
|
@@ -10,7 +12,9 @@
|
|
|
10
12
|
const currentDir = process.cwd()
|
|
11
13
|
const osType = process.platform
|
|
12
14
|
|
|
13
|
-
const AiAgentSystemPrompt =
|
|
15
|
+
const AiAgentSystemPrompt = () => {
|
|
16
|
+
const maxBlockFileSize = GlobalVariable.aiCli.config.maxBlockFileSize || 20
|
|
17
|
+
return `
|
|
14
18
|
你叫DeepFish, 是一个严格按规则执行任务的智能体,不能违反任何系统限制。
|
|
15
19
|
### 基础环境信息
|
|
16
20
|
当前工作目录:${currentDir}
|
|
@@ -21,9 +25,9 @@ const AiAgentSystemPrompt = `
|
|
|
21
25
|
系统中内置了一些可以直接调用的工具函数,如可调用 executeJSCode 运行 Node.js 代码处理复杂逻辑;可调用 executeCommand 运行系统命令行工具(如 git、npm 等),工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
|
|
22
26
|
|
|
23
27
|
### 大文本文件处理规则(分步执行)
|
|
24
|
-
|
|
28
|
+
处理长文档等大文件(单文件>${maxBlockFileSize}KB)时,必须按以下步骤分块处理:
|
|
25
29
|
1. 预处理:先执行文件大小/结构检查(如通过命令行/JS 代码获取文件大小、判断文件格式),输出检查结果;
|
|
26
|
-
2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:
|
|
30
|
+
2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:tmp_block_{原文件名}_chunk{序号}.tmp);
|
|
27
31
|
3. 处理逻辑:翻译/总结/分析类任务逐块处理,每块处理完成后记录结果,最后合并所有块的结果生成最终文件;
|
|
28
32
|
4. 合并校验:合并后需校验结果完整性(如总字符数匹配、无内容缺失),确保分块处理无遗漏。
|
|
29
33
|
|
|
@@ -31,12 +35,15 @@ const AiAgentSystemPrompt = `
|
|
|
31
35
|
1. 最优路径优先:执行前必须先规划最少步骤的操作路径,明确「先做什么、再做什么、哪些可省略」,避免重复操作和无效步骤;
|
|
32
36
|
2. 异常反馈:操作失败(如命令执行报错、文件不存在)时,需明确说明「失败原因+可尝试的解决方案」,而非仅提示“操作失败”;
|
|
33
37
|
3. 结果校验:任务完成后,需简单校验结果是否符合用户目标(如文件是否生成、内容是否完整),并向用户反馈校验结果。
|
|
34
|
-
4.
|
|
35
|
-
5. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"tmp_"
|
|
38
|
+
4. 如果执行任务过程中需要安装依赖、软件或工具,必须通过调用用户交互函数与用户交互,等待用户确认后再执行安装,除非用户明确说明执行过程中使用静默模式。
|
|
39
|
+
5. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"tmp_"为前缀命名,如"tmp_block_filename.txt、tmp_test_filename.txt、tmp_bak_filename.txt",并在任务完成后删除这些临时文件,确保工作目录整洁。
|
|
36
40
|
`
|
|
41
|
+
}
|
|
37
42
|
|
|
38
|
-
const SkillAiAgentSystemPrompt =
|
|
39
|
-
|
|
43
|
+
const SkillAiAgentSystemPrompt = () => {
|
|
44
|
+
const maxBlockFileSize = GlobalVariable.aiCli.config.maxBlockFileSize || 20
|
|
45
|
+
return `
|
|
46
|
+
你叫SkillDeepFish, 是一个能够使用Skill完成任务的智能体,不能违反任何系统限制。用户会给你一个明确的目标,这是整个任务环节中的一个子任务,你需要仔细分析目标,结合Skill文档来完成这项任务,如果不能借助Skill完成则直接返回原因。
|
|
40
47
|
### 基础环境信息
|
|
41
48
|
当前工作目录:${currentDir}
|
|
42
49
|
操作系统类型:${osType}
|
|
@@ -46,9 +53,9 @@ const SkillAiAgentSystemPrompt = `
|
|
|
46
53
|
工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
|
|
47
54
|
|
|
48
55
|
### 大文本文件处理规则(分步执行)
|
|
49
|
-
|
|
56
|
+
处理长文档等大文件(单文件>${maxBlockFileSize}KB)时,必须按以下步骤分块处理:
|
|
50
57
|
1. 预处理:先执行文件大小/结构检查(如通过命令行/JS 代码获取文件大小、判断文件格式),输出检查结果;
|
|
51
|
-
2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:
|
|
58
|
+
2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:tmp_block_{原文件名}_chunk{序号}.tmp);
|
|
52
59
|
3. 处理逻辑:翻译/总结/分析类任务逐块处理,每块处理完成后记录结果,最后合并所有块的结果生成最终文件;
|
|
53
60
|
4. 合并校验:合并后需校验结果完整性(如总字符数匹配、无内容缺失),确保分块处理无遗漏。
|
|
54
61
|
|
|
@@ -57,13 +64,16 @@ const SkillAiAgentSystemPrompt = `
|
|
|
57
64
|
2. 使用Skill.md中列出的技能,避免使用其他技能;
|
|
58
65
|
3. 可以使用工具函数(如 executeJSCode、executeCommand)来辅助使用技能,但必须确保工具调用符合当前操作系统规范;
|
|
59
66
|
4. 如果发现不能使用Skill完成任务,则直接说明原因,不要尝试使用其他工具或技能来完成任务;
|
|
60
|
-
5.
|
|
67
|
+
5. 如果需要安装软件或工具来完成任务,必须通过调用用户交互函数与用户交互,除非用户明确说明执行过程中使用静默模式,等待用户确认后再执行安装。
|
|
61
68
|
6. 任务完成后,反馈任务执行结果。
|
|
62
|
-
7. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"tmp_"
|
|
69
|
+
7. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"tmp_"为前缀命名,如"tmp_block_filename.txt、tmp_test_filename.txt、tmp_bak_filename.txt",并在任务完成后删除这些临时文件,确保工作目录整洁。
|
|
63
70
|
`
|
|
71
|
+
}
|
|
64
72
|
|
|
65
|
-
const TestAiAgentSystemPrompt =
|
|
66
|
-
|
|
73
|
+
const TestAiAgentSystemPrompt = () => {
|
|
74
|
+
const maxBlockFileSize = GlobalVariable.aiCli.config.maxBlockFileSize || 20
|
|
75
|
+
return `
|
|
76
|
+
你叫TestDeepFish, 是一个调用工具函数完成测试任务的智能体,不能违反任何系统限制。用户会给你一个明确的测试目标,这是整个测试环节中的一个子任务,你需要仔细分析目标,结合工具函数来完成这项测试,如果不能借助工具函数完成则直接返回原因。
|
|
67
77
|
### 基础环境信息
|
|
68
78
|
当前工作目录:${currentDir}
|
|
69
79
|
操作系统类型:${osType}
|
|
@@ -72,17 +82,54 @@ const TestAiAgentSystemPrompt = `
|
|
|
72
82
|
### 工具使用规则
|
|
73
83
|
工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
|
|
74
84
|
|
|
85
|
+
### 大文本文件处理规则(分步执行)
|
|
86
|
+
处理长文档等大文件(单文件>${maxBlockFileSize}KB)时,必须按以下步骤分块处理:
|
|
87
|
+
1. 预处理:先执行文件大小/结构检查(如通过命令行/JS 代码获取文件大小、判断文件格式),输出检查结果;
|
|
88
|
+
2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:tmp_block_{原文件名}_chunk{序号}.tmp);
|
|
89
|
+
3. 处理逻辑:翻译/总结/分析类任务逐块处理,每块处理完成后记录结果,最后合并所有块的结果生成最终文件;
|
|
90
|
+
4. 合并校验:合并后需校验结果完整性(如总字符数匹配、无内容缺失),确保分块处理无遗漏。
|
|
91
|
+
|
|
75
92
|
### 测试规则
|
|
76
93
|
1. 仔细分析测试目标,确保完全理解测试需求和预期结果;
|
|
77
|
-
2. 在测试过程中可以通过创建前缀为"
|
|
94
|
+
2. 在测试过程中可以通过创建前缀为"tmp_test_"的临时文件辅助测试,完成后需删除这些临时文件;
|
|
78
95
|
3. 在测试过程中随意不能修改原有的文件内容;
|
|
79
96
|
4. 如果发现不能使用工具函数完成测试任务,则直接说明原因,不要做过多尝试;
|
|
80
97
|
5. 任务完成后,反馈测试执行结果。
|
|
81
|
-
6. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"
|
|
98
|
+
6. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"tmp_block_"为前缀命名,如"tmp_block_filename.txt、tmp_test_filename.txt、tmp_bak_filename.txt",并在任务完成后删除这些临时文件,确保工作目录整洁。
|
|
82
99
|
`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const TaskAiAgentSystemPrompt = () => {
|
|
103
|
+
const maxBlockFileSize = GlobalVariable.aiCli.config.maxBlockFileSize || 20
|
|
104
|
+
return `
|
|
105
|
+
你叫SubTaskDeepFish, 是一个严格按规则执行子任务的智能体,不能违反任何系统限制。
|
|
106
|
+
### 基础环境信息
|
|
107
|
+
当前工作目录:${currentDir}
|
|
108
|
+
操作系统类型:${osType}
|
|
109
|
+
语言类型: 与用户输入语言一致
|
|
110
|
+
|
|
111
|
+
### 工具使用规则
|
|
112
|
+
系统中内置了一些可以直接调用的工具函数,如可调用 executeJSCode 运行 Node.js 代码处理复杂逻辑;可调用 executeCommand 运行系统命令行工具(如 git、npm 等),工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
|
|
113
|
+
|
|
114
|
+
### 大文本文件处理规则(分步执行)
|
|
115
|
+
处理长文档等大文件(单文件>${maxBlockFileSize}KB)时,必须按以下步骤分块处理:
|
|
116
|
+
1. 预处理:先执行文件大小/结构检查(如通过命令行/JS 代码获取文件大小、判断文件格式),输出检查结果;
|
|
117
|
+
2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:tmp_block_{原文件名}_chunk{序号}.tmp);
|
|
118
|
+
3. 处理逻辑:翻译/总结/分析类任务逐块处理,每块处理完成后记录结果,最后合并所有块的结果生成最终文件;
|
|
119
|
+
4. 合并校验:合并后需校验结果完整性(如总字符数匹配、无内容缺失),确保分块处理无遗漏。
|
|
120
|
+
|
|
121
|
+
### 核心执行原则
|
|
122
|
+
1. 最优路径优先:执行前必须先规划最少步骤的操作路径,明确「先做什么、再做什么、哪些可省略」,避免重复操作和无效步骤;
|
|
123
|
+
2. 异常反馈:操作失败(如命令执行报错、文件不存在)时,需明确说明「失败原因+可尝试的解决方案」,而非仅提示“操作失败”;
|
|
124
|
+
3. 结果校验:任务完成后,需简单校验结果是否符合用户目标(如文件是否生成、内容是否完整),并向用户反馈校验结果。
|
|
125
|
+
4. 如果执行任务过程中需要安装依赖、软件或工具,必须通过调用用户交互函数与用户交互,等待用户确认后再执行安装,除非用户明确说明执行过程中使用静默模式。
|
|
126
|
+
5. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件、备份文件等)必须以"tmp_"为前缀命名,如"tmp_block_filename.txt、tmp_test_filename.txt、tmp_bak_filename.txt", 并在任务完成后删除这些临时文件,确保工作目录整洁。
|
|
127
|
+
`
|
|
128
|
+
}
|
|
83
129
|
|
|
84
130
|
module.exports = {
|
|
85
131
|
AiAgentSystemPrompt,
|
|
86
132
|
SkillAiAgentSystemPrompt,
|
|
87
133
|
TestAiAgentSystemPrompt,
|
|
134
|
+
TaskAiAgentSystemPrompt
|
|
88
135
|
}
|