deepfish-ai 1.0.22 → 1.0.24

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.
Files changed (29) hide show
  1. package/README_CN.md +2 -2
  2. package/package.json +2 -2
  3. package/src/AgentRobot/AgentRobotFactory/MainAgentRobot.js +4 -7
  4. package/src/AgentRobot/AgentRobotFactory/SubAgentRobot.js +3 -8
  5. package/src/AgentRobot/AgentRobotFactory/SubSkillAgentRobot.js +3 -8
  6. package/src/AgentRobot/BaseAgentRobot/Brain.js +8 -8
  7. package/src/AgentRobot/BaseAgentRobot/Hand.js +3 -3
  8. package/src/AgentRobot/BaseAgentRobot/index.js +25 -89
  9. package/src/AgentRobot/BaseAgentRobot/lazy-tools/doc-transform.js +1 -0
  10. package/src/AgentRobot/BaseAgentRobot/lazy-tools/docx.js +1 -0
  11. package/src/AgentRobot/BaseAgentRobot/lazy-tools/embedding.js +1 -0
  12. package/src/AgentRobot/BaseAgentRobot/lazy-tools/img.js +1 -0
  13. package/src/AgentRobot/BaseAgentRobot/lazy-tools/pdf.js +2 -0
  14. package/src/AgentRobot/BaseAgentRobot/lazy-tools/pptx.js +1 -0
  15. package/src/AgentRobot/BaseAgentRobot/lazy-tools/xlsx.js +1 -0
  16. package/src/AgentRobot/BaseAgentRobot/tools/BaseTools.js +1 -0
  17. package/src/AgentRobot/BaseAgentRobot/tools/CreateAgentTools.js +3 -2
  18. package/src/AgentRobot/BaseAgentRobot/tools/FileTools.js +1 -0
  19. package/src/AgentRobot/BaseAgentRobot/tools/GenerateTools.js +32 -112
  20. package/src/AgentRobot/BaseAgentRobot/tools/InquirerTools.js +1 -0
  21. package/src/AgentRobot/BaseAgentRobot/tools/SystemTools.js +5 -6
  22. package/src/AgentRobot/BaseAgentRobot/tools/TaskTools.js +1 -0
  23. package/src/AgentRobot/BaseAgentRobot/tools/TestTools.js +1 -0
  24. package/src/AgentRobot/BaseAgentRobot/tools/UserTool.js +87 -0
  25. package/src/AgentRobot/BaseAgentRobot/tools/WebTools.js +1 -0
  26. package/src/AgentRobot/BaseAgentRobot/utils/AIRequest.js +9 -2
  27. package/src/AgentRobot/BaseAgentRobot/utils/AIToolManager.js +129 -0
  28. package/src/AgentRobot/BaseAgentRobot/utils/AttachmentToolScanner.js +1 -1
  29. package/src/cli/DefaultConfig.js +1 -0
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.22",
3
+ "version": "1.0.24",
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",
@@ -56,7 +56,7 @@
56
56
  "openai": "^6.18.0",
57
57
  "pdf-lib": "^1.17.1",
58
58
  "pdf-parse": "^2.4.5",
59
- "pdf-to-img": "^6.0.0",
59
+ "pdf-to-img": "5.0.0",
60
60
  "pdfkit": "^0.18.0",
61
61
  "pizzip": "^3.2.0",
62
62
  "pptxgenjs": "^4.0.1",
@@ -18,9 +18,12 @@ class MainAgentRobot extends BaseAgentRobot {
18
18
  _initFiles(opt) {
19
19
  this.workspace = opt.workspace || process.cwd() // 工作空间,目录
20
20
  this.basespace = opt.basespace || path.join(os.homedir(), '.deepfish-ai') // 记忆空间,目录
21
+ this.userspace = path.join(this.basespace, 'user-info') // 用户空间,目录
22
+ this.userInfoFilePath = path.join(this.userspace, 'user.md')
21
23
  this.memorySpace = path.join(this.basespace, 'memory') // 记忆空间,目录
22
24
  this.agentRecordFilePath = path.join(this.memorySpace, 'agentRecord.json')
23
25
  fs.ensureDirSync(this.memorySpace)
26
+ fs.ensureDirSync(this.userspace)
24
27
  // 查看agentRecord.json文件是否存在,不存在则创建
25
28
  if (!fs.pathExistsSync(this.agentRecordFilePath)) {
26
29
  fs.writeJsonSync(this.agentRecordFilePath, [], { spaces: 2 })
@@ -68,12 +71,6 @@ class MainAgentRobot extends BaseAgentRobot {
68
71
  fs.writeJsonSync(this.agentRecordFilePath, agentRecord, { spaces: 2 })
69
72
  this.logger = new Logger(this) // 初始化日志系统
70
73
  this.logger.clearAllLogs()
71
- this.toolCollection = AttachmentToolScanner.getToolCollection(
72
- this.workspace,
73
- ) // 加载工具集合
74
- this.clawSkillCollection = AttachmentToolScanner.getClawSkillCollection(
75
- this.basespace,
76
- ) // 加载Claw技能集合
77
74
  }
78
75
 
79
76
  _getDefaultSystemPrompt(opt) {
@@ -82,7 +79,7 @@ class MainAgentRobot extends BaseAgentRobot {
82
79
  ${systemPrompt}
83
80
  ### 工具调用
84
81
  对于复杂的任务,先从可以使用的Skills中查找并使用合适的Skill,如果没有合适的Skill,再使用内置工具函数,使用时请严格按照工具函数的调用方式进行调用。
85
- ${AttachmentToolScanner.getAttachToolPrompt(this.toolCollection, this.clawSkillCollection)}
82
+ ${AttachmentToolScanner.getAttachToolPrompt(this.toolManager.toolCollection, this.toolManager.clawSkillCollection)}
86
83
  `
87
84
  }
88
85
  }
@@ -1,6 +1,5 @@
1
1
  const path = require('path')
2
2
  const os = require('os')
3
- const fs = require('fs-extra')
4
3
  const BaseAgentRobot = require('../BaseAgentRobot/index.js')
5
4
  const Logger = require('../BaseAgentRobot/Logger.js')
6
5
  const {AttachmentToolScanner} = require('../BaseAgentRobot/utils/AttachmentToolScanner.js')
@@ -20,6 +19,8 @@ class SubAgentRobot extends BaseAgentRobot {
20
19
  this.attachTools = opt.attachTools || []
21
20
  this.workspace = opt.workspace || process.cwd() // 工作空间,目录
22
21
  this.basespace = opt.basespace || path.join(os.homedir(), '.deepfish-ai') // 记忆空间,目录
22
+ this.userspace = path.join(this.basespace, 'user-info') // 用户空间,目录
23
+ this.userInfoFilePath = path.join(this.userspace, 'user.md')
23
24
  this.memorySpace = path.join(this.basespace, 'memory') // 记忆空间,目录
24
25
  this.agentRecordFilePath = path.join(this.memorySpace, 'agentRecord.json')
25
26
  this.agentSpace = path.join(this.memorySpace, this.root.id) // Agent空间,目录
@@ -28,12 +29,6 @@ class SubAgentRobot extends BaseAgentRobot {
28
29
  this.logger = new Logger(this) // 初始化日志系统
29
30
  this.agentTree = new AgentTree(this)
30
31
  this.agentTree.init()
31
- this.toolCollection = AttachmentToolScanner.getToolCollection(
32
- this.workspace,
33
- ) // 加载工具集合
34
- this.clawSkillCollection = AttachmentToolScanner.getClawSkillCollection(
35
- this.basespace,
36
- ) // 加载Claw技能集合
37
32
  }
38
33
 
39
34
  _getDefaultSystemPrompt(opt) {
@@ -42,7 +37,7 @@ class SubAgentRobot extends BaseAgentRobot {
42
37
  ${systemPrompt}
43
38
  ### 工具调用
44
39
  对于复杂的任务,先从可以使用的Skills中查找并使用合适的Skill,如果没有合适的Skill,再使用内置工具函数,使用时请严格按照工具函数的调用方式进行调用。
45
- ${AttachmentToolScanner.getAttachToolPrompt(this.toolCollection, this.clawSkillCollection)}
40
+ ${AttachmentToolScanner.getAttachToolPrompt(this.toolManager.toolCollection, this.toolManager.clawSkillCollection)}
46
41
  `
47
42
  }
48
43
  }
@@ -20,6 +20,8 @@ class SubSkillAgentRobot extends BaseAgentRobot {
20
20
  this.attachTools = opt.attachTools || []
21
21
  this.workspace = opt.workspace || process.cwd() // 工作空间,目录
22
22
  this.basespace = opt.basespace || path.join(os.homedir(), '.deepfish-ai') // 记忆空间,目录
23
+ this.userspace = path.join(this.basespace, 'user-info') // 用户空间,目录
24
+ this.userInfoFilePath = path.join(this.userspace, 'user.md')
23
25
  this.memorySpace = path.join(this.basespace, 'memory') // 记忆空间,目录
24
26
  this.agentRecordFilePath = path.join(this.memorySpace, 'agentRecord.json')
25
27
  this.agentSpace = path.join(this.memorySpace, this.root.id) // Agent空间,目录
@@ -29,20 +31,13 @@ class SubSkillAgentRobot extends BaseAgentRobot {
29
31
  this.logger = new Logger(this) // 初始化日志系统
30
32
  this.agentTree = new AgentTree(this)
31
33
  this.agentTree.init()
32
-
33
- this.toolCollection = AttachmentToolScanner.getToolCollection(
34
- this.workspace,
35
- ) // 加载工具集合
36
- this.clawSkillCollection = AttachmentToolScanner.getClawSkillCollection(
37
- this.basespace,
38
- ) // 加载Claw技能集合
39
34
  }
40
35
 
41
36
  _getDefaultSystemPrompt(opt) {
42
37
  const clawSkills = opt.clawSkills || []
43
38
  let systemPrompt = super._getDefaultSystemPrompt(opt)
44
39
  systemPrompt =
45
- systemPrompt + '\n' + AttachmentToolScanner.getClawSkillPrompt(clawSkills, this.toolCollection, this.clawSkillCollection)
40
+ systemPrompt + '\n' + AttachmentToolScanner.getClawSkillPrompt(clawSkills, this.toolManager.toolCollection, this.toolManager.clawSkillCollection)
46
41
  return systemPrompt
47
42
  }
48
43
  }
@@ -70,10 +70,17 @@ class Brain extends EventEmitterSuper {
70
70
  if (maxIterations === -1) {
71
71
  maxIterations = Infinity
72
72
  }
73
- const skillDescriptions = this.agentRobot.getToolDescriptions()
73
+ const skillDescriptions = this.agentRobot.toolManager.descriptions
74
74
  this.emit(BrainEvent.THINK_BEFORE, messages)
75
75
  while (maxIterations-- > 0) {
76
76
  try {
77
+ // 更新系统提示词
78
+ if (messages[0].role === 'system') {
79
+ messages[0] = {
80
+ role: 'system',
81
+ content: this.agentRobot.systemPrompt,
82
+ }
83
+ }
77
84
  // 压缩上下文
78
85
  await this.messageCompresser.compress(messages)
79
86
  const { message, content, tool_calls } = await thinkByTool(
@@ -180,13 +187,6 @@ class Brain extends EventEmitterSuper {
180
187
  }
181
188
 
182
189
  _initMessages(messages) {
183
- let firstMessage = messages[0]
184
- if (firstMessage.role === 'system') {
185
- messages[0] = {
186
- role: 'system',
187
- content: this.agentRobot.systemPrompt,
188
- }
189
- }
190
190
  let lastMessage = messages[messages.length - 1]
191
191
  while (
192
192
  messages.length > 1 &&
@@ -12,7 +12,7 @@ class Hand extends EventEmitterSuper {
12
12
  super()
13
13
  this.agentRobot = agentRobot
14
14
  this.maxBlockFileSize = agentRobot.opt.maxBlockFileSize || 20 // KB
15
- this.tools = agentRobot.getTools()
15
+ this.tools = agentRobot.toolManager.functions
16
16
  }
17
17
 
18
18
  _parseToolCalls(tool_call) {
@@ -57,7 +57,7 @@ class Hand extends EventEmitterSuper {
57
57
  }
58
58
 
59
59
  _getRequiredParamNames(funcName) {
60
- const descriptions = this.agentRobot.getToolDescriptions()
60
+ const descriptions = this.agentRobot.toolManager.descriptions
61
61
  const current = descriptions.find((item) => item?.function?.name === funcName)
62
62
  return current?.function?.parameters?.required || []
63
63
  }
@@ -89,7 +89,7 @@ class Hand extends EventEmitterSuper {
89
89
  toolContent = {
90
90
  truncated: true,
91
91
  message:
92
- '文件内容过大,请使用executeJSCode工具编写脚本分块读取和处理文件,避免一次性读取整个文件内容到对话中。',
92
+ '文件内容过大,请使用executeJSCode工具编写脚本分块读取和处理文件,避免一次性读取整个文件内容到对话中。如果不是本地文件,建议创建或下载成本地文件后再进行分块读取。',
93
93
  preview: toolContent.substring(0, 1000) + '...',
94
94
  }
95
95
  } else {
@@ -1,17 +1,10 @@
1
1
  const path = require('path')
2
2
  const os = require('os')
3
- const fs = require('fs-extra')
4
- const lodash = require('lodash')
5
3
  const { Brain } = require('./Brain.js')
6
4
  const BrainEvent = require('./BrainEvent.js')
7
5
  const ScreenPrinter = require('./ScreenPrinter.js')
8
6
  const { HandEvent, Hand } = require('./Hand.js')
9
- const dayjs = require('dayjs')
10
- const axios = require('axios')
11
- const echarts = require('echarts')
12
- const canvas = require('canvas')
13
- const cheerio = require('cheerio')
14
- const puppeteer = require('puppeteer')
7
+ const AIToolManager = require('./utils/AIToolManager.js')
15
8
 
16
9
  class BaseAgentRobot {
17
10
  id = '' // Agentid
@@ -19,8 +12,7 @@ class BaseAgentRobot {
19
12
 
20
13
  brain = null // 大脑,负责思考、记忆、决策
21
14
  hand = null // 手,负责使用工具
22
- originalTools = null // 原装工具
23
- attachTools = null // 附加工具, Agent后续安装的工具函数
15
+
24
16
  heart = null // 心脏,负责心跳、连接
25
17
  sender = null // 发送器,负责发送消息
26
18
  receiver = null // 接收器,负责接收消息
@@ -34,12 +26,14 @@ class BaseAgentRobot {
34
26
 
35
27
  workspace = null
36
28
  basespace = null
29
+ userspace = null
37
30
  memorySpace = null
38
31
  agentRecordFilePath = null
39
32
  agentSpace = null
40
33
  agentTree = null
41
34
  memoryFilePath = null
42
35
  logDirPath = null
36
+ toolManager = null
43
37
 
44
38
  constructor(
45
39
  opt = {
@@ -54,6 +48,7 @@ class BaseAgentRobot {
54
48
  maxBlockFileSize: 20, // 大文件分块阈值,单位KB;超过该大小的文件需要分块处理
55
49
  systemPrompt: '', // 系统提示语
56
50
  encoding: 'auto', // 命令行编码格式, 可设置为utf-8、gbk等, 也可以设置成auto或空值自动判断
51
+ isThinkPrint: true, // 是否打印思考过程
57
52
  aiConfig: {
58
53
  name: 'deepseek',
59
54
  type: 'deepseek',
@@ -73,9 +68,7 @@ class BaseAgentRobot {
73
68
  this.name = opt.name || 'AgentRobot'
74
69
  this.screenPrinter = new ScreenPrinter() // 屏幕打印机
75
70
  this._initFiles(opt) // 初始化文件
76
-
77
- this.originalTools = this._getOriginalTools() // 天赋技能
78
- this.attachTools = opt.attachTools || [] // 附加工具, Agent后续安装的工具函数
71
+ this.toolManager = new AIToolManager(this)
79
72
  this.systemPrompt = opt.systemPrompt || this._getDefaultSystemPrompt(opt) // 系统提示语
80
73
  this.brain = new Brain(this) // 初始化大脑
81
74
  this.hand = new Hand(this) // 初始化手
@@ -87,10 +80,11 @@ class BaseAgentRobot {
87
80
 
88
81
  _initEvents() {
89
82
  const aiConfig = this.opt.aiConfig
83
+ const isThinkPrint = this.opt.isThinkPrint
90
84
  let stopLoading = null
91
85
  this.brain.on(BrainEvent.THINK_BEFORE, () => {})
92
86
  this.brain.on(BrainEvent.SUB_THINK_BEFORE, (messages) => {
93
- if (!aiConfig.stream) {
87
+ if (!aiConfig.stream || !isThinkPrint) {
94
88
  if (stopLoading) {
95
89
  stopLoading('I have finished thinking.')
96
90
  }
@@ -98,27 +92,27 @@ class BaseAgentRobot {
98
92
  }
99
93
  })
100
94
  this.brain.on(BrainEvent.SUB_THINK_AFTER, (messages) => {
101
- if (!aiConfig.stream && stopLoading) {
95
+ if ((!aiConfig.stream && stopLoading) || !isThinkPrint) {
102
96
  stopLoading('I have finished thinking.')
103
97
  stopLoading = null
104
- const lastMessage = messages[messages.length - 1]
105
- this.screenPrinter.logInfo(lastMessage?.content)
98
+ // const lastMessage = messages[messages.length - 1]
99
+ // this.screenPrinter.logInfo(lastMessage?.content)
106
100
  }
107
101
  })
108
102
  this.brain.on(BrainEvent.SUB_STREAM_THINK_OUTPUT, (messages, output) => {
109
- this.screenPrinter.streamOutput(output, '#47854a')
103
+ isThinkPrint && this.screenPrinter.streamOutput(output, '#47854a')
110
104
  })
111
105
  this.brain.on(BrainEvent.SUB_STREAM_CONTENT_OUTPUT, (messages, content) => {
112
- this.screenPrinter.streamOutput(content, '#c2a654')
106
+ isThinkPrint && this.screenPrinter.streamOutput(content, '#c2a654')
113
107
  })
114
108
  this.brain.on(
115
109
  BrainEvent.SUB_STREAM_TOOL_CALLS_OUTPUT,
116
110
  (messages, toolCalls) => {
117
- this.screenPrinter.streamOutput(toolCalls, '#47854a')
111
+ isThinkPrint && this.screenPrinter.streamOutput(toolCalls, '#47854a')
118
112
  },
119
113
  )
120
114
  this.brain.on(BrainEvent.SUB_STREAM_END, () => {
121
- this.screenPrinter.streamLineBreak()
115
+ isThinkPrint && this.screenPrinter.streamLineBreak()
122
116
  })
123
117
  this.brain.on(BrainEvent.SUB_USE_TOOL, async (toolCalls) => {
124
118
  await this.hand.useTools(toolCalls)
@@ -181,73 +175,6 @@ class BaseAgentRobot {
181
175
  })
182
176
  }
183
177
 
184
- loadAttachTool(toolName) {
185
- let tool = this.attachTools.find((t) => t.name === toolName)
186
- if (!tool) {
187
- tool = this.toolCollection.find((t) => t.name === toolName)
188
- this.attachTools.push(tool)
189
- }
190
- }
191
-
192
- getTools() {
193
- const tools = [...this.originalTools, ...this.attachTools]
194
- const toolFunctions = {
195
- fs,
196
- axios,
197
- dayjs,
198
- lodash,
199
- canvas,
200
- echarts,
201
- cheerio,
202
- puppeteer
203
- }
204
-
205
- tools.forEach((tool) => {
206
- Object.assign(toolFunctions, tool.functions)
207
- })
208
- toolFunctions.agentRobot = this
209
- toolFunctions.Tools = toolFunctions
210
- // 兼容老版本
211
- toolFunctions.aiCli = {
212
- Tools: toolFunctions,
213
- }
214
- return toolFunctions
215
- }
216
-
217
- getToolDescriptions() {
218
- const tools = [...this.originalTools, ...this.attachTools]
219
- const toolDescriptions = []
220
- tools.forEach((tool) => {
221
- const descriptions = tool.descriptions.map((item) => {
222
- if (!item.type) {
223
- return {
224
- type: 'function',
225
- function: item,
226
- }
227
- } else {
228
- return item
229
- }
230
- })
231
- toolDescriptions.push(...descriptions)
232
- })
233
- return toolDescriptions
234
- }
235
-
236
- // 获取原装工具
237
- _getOriginalTools() {
238
- // 自动扫描tools目录
239
- const toolsPath = path.join(__dirname, './tools')
240
- const toolFiles = fs.readdirSync(toolsPath).filter((file) => {
241
- return file.endsWith('.js') || file.endsWith('.mjs')
242
- })
243
- const tools = []
244
- toolFiles.forEach((file) => {
245
- const tool = require(path.join(toolsPath, file))
246
- tools.push(tool)
247
- })
248
- return tools
249
- }
250
-
251
178
  _getDefaultSystemPrompt(opt) {
252
179
  const osType = process.platform
253
180
  const workspace = this.workspace
@@ -262,7 +189,7 @@ class BaseAgentRobot {
262
189
  语言类型: 与用户输入语言一致
263
190
 
264
191
  ### 工具使用
265
- 执行任务前,应仔细阅读工具描述以及可以使用的Skills的描述内容,选择最合适的工具或技能来完成任务。
192
+ 执行任务前,应仔细阅读工具描述以及可以使用的Skills的描述内容,优先使用匹配到的工具或技能,避免自己发挥。
266
193
 
267
194
  ### 大文本文件处理规则(分步执行)
268
195
  处理长文档等大文件(单文件>${maxBlockFileSize}KB)时,必须按以下步骤分块处理:
@@ -277,6 +204,15 @@ class BaseAgentRobot {
277
204
  3. 结果校验:任务完成后,需简单校验结果是否符合用户目标(如文件是否生成、内容是否完整),并向用户反馈校验结果。
278
205
  4. 如果执行任务过程中需要安装依赖、软件或工具,必须通过调用用户交互函数与用户交互,等待用户确认后再执行安装,除非用户明确说明执行过程中使用静默模式。
279
206
  5. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"tmp_"为前缀命名,如"tmp_block_filename.txt、tmp_test_filename.txt、tmp_bak_filename.txt",并在任务完成后删除这些临时文件,确保工作目录整洁。
207
+
208
+ ### 用户信息
209
+ #### 用户信息记录规则
210
+ 当对话中出现用户信息时,如个人基础信息(如姓名、年龄、职业、兴趣、性格特征)、操作习惯、代码习惯、阅读习惯、常用目录、文档收藏夹目录等,必须使用用户信息读写函数进行记录。
211
+
212
+ #### 当前用户信息
213
+ ----user info start----
214
+ ${this.toolManager.functions.readUserInfo()}
215
+ ----user info end----
280
216
  `
281
217
  }
282
218
 
@@ -198,6 +198,7 @@ const DocTransformTool = {
198
198
  platform: 'all',
199
199
  descriptions,
200
200
  functions,
201
+ isSystem: true
201
202
  }
202
203
 
203
204
  module.exports = DocTransformTool
@@ -1253,6 +1253,7 @@ const DocxTool = {
1253
1253
  platform: 'all',
1254
1254
  descriptions,
1255
1255
  functions,
1256
+ isSystem: true
1256
1257
  }
1257
1258
 
1258
1259
  module.exports = DocxTool
@@ -754,6 +754,7 @@ const EmbeddingTool = {
754
754
  platform: 'all',
755
755
  descriptions,
756
756
  functions,
757
+ isSystem: true
757
758
  }
758
759
 
759
760
  module.exports = EmbeddingTool
@@ -732,6 +732,7 @@ const ImgTool = {
732
732
  platform: 'all',
733
733
  descriptions,
734
734
  functions,
735
+ isSystem: true
735
736
  }
736
737
 
737
738
  module.exports = ImgTool
@@ -455,6 +455,7 @@ async function pdfToLongImage(pdfPath, outputImagePath) {
455
455
 
456
456
  return ok({ pdfPath, outputImagePath, pageCount: pages.length, width: maxWidth, height: totalHeight })
457
457
  } catch (error) {
458
+ console.error(error)
458
459
  return fail(error, { pdfPath, outputImagePath })
459
460
  }
460
461
  }
@@ -729,6 +730,7 @@ const PdfTool = {
729
730
  platform: 'all',
730
731
  descriptions,
731
732
  functions,
733
+ isSystem: true
732
734
  }
733
735
 
734
736
  module.exports = PdfTool
@@ -534,6 +534,7 @@ const PptxTool = {
534
534
  platform: 'all',
535
535
  descriptions,
536
536
  functions,
537
+ isSystem: true
537
538
  }
538
539
 
539
540
  module.exports = PptxTool
@@ -581,6 +581,7 @@ const XlsxTool = {
581
581
  platform: 'all',
582
582
  descriptions,
583
583
  functions,
584
+ isSystem: true
584
585
  }
585
586
 
586
587
  module.exports = XlsxTool
@@ -7,6 +7,7 @@ const BaseTool = {
7
7
  platform: 'all', // 扩展支持的平台(process.platform),all或空表示所有平台, win32表示仅支持Windows, darwin表示仅支持MacOS, linux表示仅支持Linux
8
8
  descriptions,
9
9
  functions,
10
+ isSystem: true
10
11
  }
11
12
 
12
13
  module.exports = BaseTool
@@ -67,7 +67,7 @@ async function createSubAgent(workGoal) {
67
67
 
68
68
  function loadAttachTool(toolName) {
69
69
  try {
70
- this.agentRobot.loadAttachTool(toolName)
70
+ this.agentRobot.toolManager.addTool(toolName)
71
71
  return `Tool ${toolName} loaded successfully`
72
72
  } catch (error) {
73
73
  return `Failed to load tool ${toolName}: ${error.message}`
@@ -107,7 +107,7 @@ const descriptions = [
107
107
  function: {
108
108
  name: 'createSubAgent',
109
109
  description:
110
- '创建一个携带所有技能包说明的子agent(能够根据需要创建加载技能包的子agent),并让其执行给定工作目标。返回执行结果与状态信息。',
110
+ '创建一个子agent,并让其执行给定工作目标。可以进行复杂的任务分配,如知识库查询、多源搜索结果整合、多步骤任务处理等,返回执行结果与状态信息。',
111
111
  parameters: {
112
112
  type: 'object',
113
113
  properties: {
@@ -151,6 +151,7 @@ const CreateAgentTool = {
151
151
  description: '提供子agent创建与任务分发能力,可按技能选择子agent执行目标任务',
152
152
  descriptions,
153
153
  functions,
154
+ isSystem: true
154
155
  }
155
156
 
156
157
  module.exports = CreateAgentTool
@@ -670,6 +670,7 @@ const FileTool = {
670
670
  '提供文件和目录的创建、读取、修改、删除、移动、重命名、信息获取等文件系统操作功能',
671
671
  descriptions,
672
672
  functions,
673
+ isSystem: true
673
674
  }
674
675
 
675
676
  module.exports = FileTool
@@ -64,14 +64,13 @@ const descriptions = [
64
64
  ]
65
65
 
66
66
  async function getGenerateSkillRules(goal) {
67
- const packagePath = path.resolve(__filename, '../../../index.js')
68
67
  const newGoal = `
69
- ### 任务目标
68
+ ## 任务目标
70
69
  基于指定规则创建一个标准化的Node.js NPM项目,实现用户目标:${goal},最终输出符合AI工作流调用规范的函数模块,并配套中英文说明文档。
71
70
 
72
- ### 任务步骤
71
+ ## 任务步骤
73
72
 
74
- #### 第一步:项目初始化
73
+ ### 第一步:项目初始化
75
74
  1. 目录创建:新建目录,目录名称以"deepfish-ai-"开头,如"deepfish-ai-「项目功能名称」",作为NPM项目根目录,并作为当前项目的名称
76
75
  2. package.json配置:
77
76
  - name字段值:与项目名称一致,即"deepfish-ai-「项目功能名称」"
@@ -86,10 +85,9 @@ async function getGenerateSkillRules(goal) {
86
85
  - 文档文件:项目根目录需新增2个文档文件:
87
86
  - README_CN.md(中文说明文档)
88
87
  - README.md(英文说明文档)
89
- - 主测试文件:test.js
90
88
 
91
- #### 第二步:index.js 完整开发规范
92
- ##### 2.1 核心输出要求
89
+ ### 第二步:核心代码开发
90
+ #### 2.1 核心输出要求
93
91
  文件需输出四个核心字段,且代码逻辑清晰、可运行:
94
92
  - name:字符串类型,扩展的名称标识
95
93
  - description:字符串类型,扩展功能的简要描述,说明该扩展提供的核心能力
@@ -97,7 +95,7 @@ async function getGenerateSkillRules(goal) {
97
95
  - platform:字符串类型,扩展支持的平台(process.platform),all或空表示所有平台, win32表示仅支持Windows, darwin表示仅支持MacOS, linux表示仅支持Linux:
98
96
  - functions:对象类型,key为函数名称,value为函数方法体
99
97
 
100
- ##### 2.2 开发强制规则
98
+ #### 2.2 开发强制规则
101
99
  1. 参数一致性:functions中函数的入参,必须与descriptions中对应函数声明的parameters完全一致
102
100
  2. 命名规范:
103
101
  - 函数名称前缀:「领域用途+分隔符」(如systemFileManagement_)
@@ -108,8 +106,9 @@ async function getGenerateSkillRules(goal) {
108
106
  4. 函数数量:至少包含1个可被AI工作流调用的函数
109
107
  5. 拆分成多个文件,保持文件结构清晰
110
108
  6. 对于大于5个的扩展功能,需要在functions中输出一个说明函数,只需返回一个markdown类型的英文字符串,专门用于解释当前扩展工具的使用方法、参数说明、示例等内容,函数名称为「readme」,如「systemFileManagement_readme」;函数描述需要强调调用该扩展模块前必须先阅读该规则文档。
109
+ 7. 仅进行简单逻辑性检查,不需要测试。
111
110
 
112
- ##### 2.3 基础代码模板(必须遵循)
111
+ #### 2.3 基础代码模板(必须遵循)
113
112
  const descriptions = []
114
113
  const functions = {}
115
114
  module.exports = {
@@ -120,9 +119,9 @@ module.exports = {
120
119
  functions,
121
120
  }
122
121
 
123
- ##### 2.4 参考示例(可参考格式,展示多文件拆分结构)
122
+ #### 2.4 参考示例(可参考格式,展示多文件拆分结构)
124
123
 
125
- ###### index.js(主文件)
124
+ ##### index.js(主文件)
126
125
  const descriptions = require('./descriptions')
127
126
  const functions = require('./functions')
128
127
  module.exports = {
@@ -133,7 +132,7 @@ module.exports = {
133
132
  functions,
134
133
  }
135
134
 
136
- ###### descriptions.js(描述子文件)
135
+ ##### descriptions.js(描述子文件)
137
136
  const descriptions = [
138
137
  {
139
138
  name: 'systemFileManagement_renameFile',
@@ -160,7 +159,7 @@ const descriptions = [
160
159
  ]
161
160
  module.exports = descriptions
162
161
 
163
- ###### functions.js(函数子文件)
162
+ ##### functions.js(函数子文件)
164
163
  const functions = {
165
164
  systemFileManagement_renameFile: function(oldPath, newPath) {
166
165
  try {
@@ -196,26 +195,14 @@ const functions = {
196
195
  }
197
196
  module.exports = functions
198
197
 
199
- #### 第三步:测试规则
200
- 1. 测试目标:至少覆盖扩展中的核心函数(建议覆盖每个对外函数),验证“正常输入可用、关键边界可处理、异常输入有明确反馈”。
201
- 2. 测试文件:统一在 test.js 编写可直接运行的测试脚本,结构清晰,包含“准备数据 → 执行函数 → 断言结果 → 输出结论”。
202
- 3. 测试文件:如果引入了requestAI,必须确保函数可正确使用 this.Tools 上下文。
203
- - 环境创建方式:
204
- "const { DeepFishAI } = require('${packagePath}')\nconst deepfishAI = new DeepFishAI();"
205
- - 调用方式:为模块导出的functions绑定Tools上下文,示例:functions.Tools = deepfishAI.agentRobot.getTools();
206
- 4. 断言与输出规范:每个用例需打印“用例名称、输入、期望、实际、是否通过(PASS/FAIL)”;全部执行后输出汇总(总数、通过数、失败数)。
207
- 5. 失败处理:出现异常时不得静默吞错,需捕获并输出可定位信息(错误消息、对应用例、关键参数)。
208
- 6. 副作用控制:测试过程中创建的临时文件必须使用 tmp_test_ 前缀,并在测试结束后清理。
209
- 7. 执行方式:test.js 不需要导出模块,支持通过 node test.js 直接运行并看到结果。
210
-
211
- #### 第四步:README文档规范
212
- ##### 4.1 通用要求
198
+ ### 第三步:输出README文档
199
+ #### 3.1 通用要求
213
200
  - 两个文档需在标题下方包含「中英文切换标签」(如文档顶部标注「English | 中文」/「中文 | English」)
214
201
  - 结构保持一致,仅语言不同,核心模块顺序不可调整
215
202
  - 文件名称README_CN.md(中文)、README.md(英文)
216
203
  - 链接使用相对路径,如[中文](./README_CN.md)
217
204
 
218
- ##### 4.2 核心模块
205
+ #### 3.2 核心模块
219
206
  1. 总体功能描述:
220
207
  - 清晰说明当前NPM包的核心定位、整体功能价值、适用场景
221
208
  - 语言简洁易懂,无需技术细节,聚焦「做什么」而非「怎么做」
@@ -240,12 +227,12 @@ async function generateSkill(rules) {
240
227
 
241
228
  async function getGenerateClawSkillRules(goal) {
242
229
  const newGoal = `
243
- ### 任务目标
230
+ ## 任务目标
244
231
  基于OpenClaw Skill规范创建一个标准化的Skill工具包,实现用户目标:${goal},最终输出可被你直接加载使用。
245
232
 
246
- ### 任务步骤
233
+ ## 任务步骤
247
234
 
248
- #### 第一步:项目初始化
235
+ ### 第一步:项目初始化
249
236
  1. 目录创建:在当前工作目录下新建一个子目录,目录名称应简洁明了地反映Skill功能(如"web-scraper"、"code-reviewer"、"image-optimizer"等)
250
237
  2. 核心文件:目录中必须包含 SKILL.md 文件(文件名大小写敏感,必须为 SKILL.md)
251
238
  3. 文档文件:目录中需新增2个说明文档:
@@ -253,7 +240,7 @@ async function getGenerateClawSkillRules(goal) {
253
240
  - README.md(英文说明文档)
254
241
  4. 辅助文件:如果Skill涉及复杂逻辑,可在目录中创建辅助脚本文件(如 .js、.sh、.py 等),并在 SKILL.md 中说明其用途和调用方式
255
242
 
256
- ##### 标准目录结构
243
+ #### 标准目录结构
257
244
  \`\`\`
258
245
  <skill-name>/
259
246
  ├── SKILL.md # [必需] 核心文件:YAML元数据 + AI执行指令
@@ -268,13 +255,13 @@ async function getGenerateClawSkillRules(goal) {
268
255
  └── ...
269
256
  \`\`\`
270
257
 
271
- #### 第二步:SKILL.md 完整开发规范
272
- ##### 2.1 文件结构
258
+ ### 第二步:SKILL.md 完整开发规范
259
+ #### 2.1 文件结构
273
260
  SKILL.md 由两部分组成:
274
261
  - **YAML Frontmatter**(元数据区):位于文件顶部,用 \`---\` 包裹
275
262
  - **Markdown Body**(指令正文区):Frontmatter之后的所有内容,是AI执行Skill的核心指令
276
263
 
277
- ##### 2.2 YAML Frontmatter 规范(必须遵循)
264
+ #### 2.2 YAML Frontmatter 规范(必须遵循)
278
265
  \`\`\`yaml
279
266
  ---
280
267
  name: "skill-name"
@@ -288,7 +275,7 @@ homepage: "https://github.com/your-repo/skill-name"
288
275
  - description:Skill功能的简要描述,用于在Skill列表中展示,帮助AI判断何时匹配使用该Skill
289
276
  - homepage:Skill的主页或仓库地址,可留空
290
277
 
291
- ##### 2.3 Markdown Body 指令正文规范
278
+ #### 2.3 Markdown Body 指令正文规范
292
279
  指令正文是Skill的核心,AI在加载Skill后会仔细阅读此部分内容来学习如何使用该Skill。正文需包含以下模块:
293
280
 
294
281
  **模块一:概述**
@@ -318,90 +305,22 @@ homepage: "https://github.com/your-repo/skill-name"
318
305
  - 列出已知限制和边界条件
319
306
  - 提供常见问题的处理建议
320
307
 
321
- ##### 2.4 SKILL.md 参考示例
322
- \`\`\`markdown
323
- ---
324
- name: "file-translator"
325
- description: "将指定文件翻译为目标语言,支持多种文件格式的智能翻译"
326
- homepage: "https://github.com/example/file-translator"
327
- ---
328
-
329
- # File Translator Skill
330
-
331
- ## 概述
332
- 本Skill提供文件翻译能力,能够将指定文件内容翻译为目标语言。支持 .txt、.md、.json 等文本格式文件。
333
-
334
- #### 能力范围
335
- - ✅ 支持文本文件的全文翻译
336
- - ✅ 支持保留原文件格式结构
337
- - ✅ 支持中、英、日、韩等主流语言互译
338
- - ❌ 不支持二进制文件(如图片、视频)
339
- - ❌ 不支持超过100MB的大文件
340
-
341
- ## 使用指令
342
-
343
- #### 步骤1:读取源文件
344
- 通过 readFile 工具函数读取需要翻译的源文件内容。
345
-
346
- #### 步骤2:执行翻译
347
- 调用 requestAI 工具函数,将文件内容和目标语言作为参数,获取翻译结果。
348
-
349
- 提示词模板:
350
- "请将以下内容翻译为{目标语言},保持原有格式不变:\\n{文件内容}"
351
-
352
- #### 步骤3:写入结果
353
- 将翻译结果写入目标文件。目标文件命名规则:{原文件名}_{语言代码}.{扩展名}
354
-
355
- ## 输入输出
356
- - 输入:源文件路径、目标语言
357
- - 输出:翻译后的文件路径
358
-
359
- ## 注意事项
360
- - 大文件需按分块规则处理,避免超出上下文长度限制
361
- - 翻译完成后需校验文件完整性
362
- \`\`\`
363
-
364
- ##### 2.5 开发强制规则
365
- 1. SKILL.md 的指令正文必须足够详细,使AI仅通过阅读该文件即可完全理解并执行Skill的所有功能
366
- 2. 避免使用模糊描述(如"适当处理"、"合理配置"),所有操作步骤必须明确、具体、可执行
367
- 3. 如果Skill依赖辅助脚本文件,必须在指令正文中说明文件路径和调用方式
368
- 4. Skill的指令中可以引导AI使用内置工具函数完成任务,如:
369
- - executeCommand(command):执行系统命令
370
- - executeJSCode(code):执行Node.js代码
371
- - requestAI(systemDescription, prompt, temperature):调用AI进行文本处理
372
- 5. description字段的描述至关重要,它决定了AI何时选择加载该Skill,必须准确反映Skill的核心能力
373
- 6. 工具包创建在当前工作目录下,完成后不需要自动安装到全局
374
-
375
- #### 第三步:辅助脚本文件规范(如需要)
376
- 1. 如果Skill的功能较复杂,无法仅通过纯文本指令描述完成,可以创建辅助脚本文件
377
- 2. 辅助脚本可以是任意可执行格式(.js、.sh、.py、.bat等),需确保与目标操作系统兼容
378
- 3. 脚本文件放置在Skill目录中,SKILL.md中通过相对路径引用
379
- 4. 脚本需要有良好的注释和错误处理
380
- 5. 如果脚本依赖第三方库,需要在SKILL.md的环境依赖模块中明确说明安装方式
381
-
382
- #### 第四步:README文档规范
383
- ##### 4.1 通用要求
308
+ ### 第三步:输出README文档
309
+ #### 3.1 通用要求
384
310
  - 两个文档需在标题下方包含「中英文切换标签」(如文档顶部标注「English | 中文」/「中文 | English」)
385
311
  - 结构保持一致,仅语言不同,核心模块顺序不可调整
386
312
  - 文件名称README_CN.md(中文)、README.md(英文)
387
313
  - 链接使用相对路径,如[中文](./README_CN.md)
388
314
 
389
- ##### 4.2 核心模块
315
+ #### 3.2 核心模块
390
316
  1. 总体功能描述:
391
- - 清晰说明当前Skill工具包的核心定位、整体功能价值、适用场景
317
+ - 清晰说明当前SKILL包的核心定位、整体功能价值、适用场景
392
318
  - 语言简洁易懂,无需技术细节,聚焦「做什么」而非「怎么做」
393
319
  2. 快速开始:
394
- - 明确说明安装步骤,顺序不可颠倒:
395
- 先安装deepfish-ai全局库:npm install deepfish-ai -g
396
- 将Skill目录拷贝到当前工作目录,执行:ai skill add <skill-name>
397
- 启用Skill:ai skill enable <skill-name>
398
- - 也可以从ClawHub安装(如已发布):ai skill install <clawhub-url>
399
- 3. Skill能力说明:
400
- - 列出该Skill提供的所有能力
401
- - 对应说明每项能力的使用场景和效果
402
- 4. 使用示例:
403
- - 提供1-3个典型的使用场景示例
404
- - 说明用户输入和预期输出
320
+ - 明确说明安装步骤:
321
+ 全局安装deepfish-ainpm install deepfish-ai -g
322
+ 全局安装当前项目:ai skill add <skill-name>;ai skill ls;ai skill enable <skill-name|skill-index>
323
+ 在命令行中输入:ai skill功能」。如:添加了一个查询天气的扩展。则输入:ai 查询一下今天的天气
405
324
  `
406
325
  return newGoal
407
326
  }
@@ -423,6 +342,7 @@ const GenerateTools = {
423
342
  description: '提供扩展工具与Skill工具包生成规则能力,用于辅助AI构建标准化扩展项目模板',
424
343
  descriptions,
425
344
  functions,
345
+ isSystem: true
426
346
  }
427
347
 
428
348
  module.exports = GenerateTools
@@ -230,6 +230,7 @@ const InquirerTool = {
230
230
  '提供用户交互功能,支持确认、列表选择、文本输入、数字输入等多种交互方式',
231
231
  descriptions,
232
232
  functions,
233
+ isSystem: true
233
234
  }
234
235
 
235
236
  module.exports = InquirerTool
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @Author: Roman 306863030@qq.com
3
3
  * @Date: 2026-03-17 11:59:19
4
- * @LastEditors: Roman 306863030@qq.com
5
- * @LastEditTime: 2026-04-21 21:08:34
4
+ * @LastEditors: roman_123 306863030@qq.com
5
+ * @LastEditTime: 2026-04-24 00:07:22
6
6
  * @FilePath: \deepfish\src\AgentRobot\BaseAgentRobot\tools\SystemTools.js
7
7
  * @Description: 默认扩展函数
8
8
  * @
@@ -65,8 +65,6 @@ async function requestAI(
65
65
  systemDescription = systemDescription.systemDescription || ''
66
66
  }
67
67
  try {
68
- aiConsole.logInfo(`aiSystem: ${systemDescription}`)
69
- aiConsole.logInfo(`aiPrompt: ${prompt}`)
70
68
  const response = await this.agentRobot.brain.think(
71
69
  systemDescription,
72
70
  prompt,
@@ -91,7 +89,7 @@ async function executeJSCode(code) {
91
89
  throw error
92
90
  }
93
91
  try {
94
- const functions = this.agentRobot.getTools()
92
+ const functions = this.agentRobot.toolManager.functions
95
93
  const Func = new Function(
96
94
  'Tools',
97
95
  'require',
@@ -103,7 +101,7 @@ async function executeJSCode(code) {
103
101
  this.logMessages.push(Array.from(arguments).join(' '))
104
102
  }
105
103
  console.log = newLog.bind(this)
106
- function __main() {
104
+ async function __main() {
107
105
  ${code}
108
106
  }
109
107
  const result = await __main()
@@ -237,6 +235,7 @@ const SystemTool = {
237
235
  '提供系统命令执行、AI请求、JS代码执行、扩展文件生成规则、AI配置管理、Tool加载执行等核心系统功能',
238
236
  descriptions,
239
237
  functions,
238
+ isSystem: true
240
239
  }
241
240
 
242
241
  module.exports = SystemTool
@@ -205,6 +205,7 @@ const TaskTools = {
205
205
  '提供任务列表创建规则、执行规则、任务读写与子任务执行能力,支持基于tasklist的可追踪拆分执行流程',
206
206
  descriptions,
207
207
  functions,
208
+ isSystem: true
208
209
  }
209
210
 
210
211
  module.exports = TaskTools
@@ -94,6 +94,7 @@ const TestTools = {
94
94
  extensionDescription: "提供程序功能测试任务的生成和执行功能,支持自动化测试工作流",
95
95
  descriptions,
96
96
  functions,
97
+ isSystem: true
97
98
  }
98
99
 
99
100
  module.exports = TestTools
@@ -0,0 +1,87 @@
1
+ const fs = require('fs-extra')
2
+
3
+ function readUserInfo() {
4
+ const userInfoFilePath = this.agentRobot.userInfoFilePath
5
+ if (!fs.existsSync(userInfoFilePath)) {
6
+ return '暂无用户信息记录';
7
+ }
8
+ return fs.readFileSync(userInfoFilePath, 'utf-8') || '暂无用户信息记录'
9
+ }
10
+
11
+ async function writeUserInfo(info) {
12
+ const userInfoFilePath = this.agentRobot.userInfoFilePath
13
+ const oldUserInfo = this.Tools.readUserInfo()
14
+ const normalizedOldUserInfo = oldUserInfo === '暂无用户信息记录' ? '' : oldUserInfo
15
+ let mergedInfo = (info || '').trim()
16
+
17
+ const systemDescription = `你是用户信息整理助手。请将已有用户信息与新增用户信息合并为一份Markdown文本,只保留非敏感信息并去重,优先保留更新、更准确的信息。记录内容应简洁、可检索、可复用。`
18
+ const prompt = `请整合以下两部分用户信息,输出最终Markdown内容(仅输出正文,不要解释):\n\n【已有用户信息】\n${normalizedOldUserInfo || '(空)'}\n\n【新增用户信息】\n${mergedInfo || '(空)'}\n\n要求:\n1. 仅保留非敏感信息。\n2. 相同信息去重,冲突时以新增信息为准。\n3. 不确定内容标注“待确认”。\n4. 按“信息类型: 信息内容”格式整理。`
19
+
20
+ if (this.Tools.requestAI && mergedInfo) {
21
+ try {
22
+ const aiMerged = await this.Tools.requestAI(systemDescription, prompt, 0.2)
23
+ if (typeof aiMerged === 'string' && aiMerged.trim()) {
24
+ mergedInfo = aiMerged.trim()
25
+ }
26
+ } catch (error) {
27
+ // AI合并失败时回退为直接写入新增信息,保证写入流程可用
28
+ mergedInfo = normalizedOldUserInfo + '\n' + info
29
+ }
30
+ }
31
+ fs.writeFileSync(userInfoFilePath, mergedInfo, 'utf-8')
32
+ // 更新系统提示词中的用户信息区块
33
+ this.agentRobot.systemPrompt = this.agentRobot.systemPrompt.replace(
34
+ /----user info start----([\s\S]*?)----user info end----/,
35
+ `----user info start----\n${mergedInfo}\n----user info end----`
36
+ )
37
+ return true
38
+ }
39
+
40
+ const descriptions = [
41
+ {
42
+ type: 'function',
43
+ function: {
44
+ name: 'writeUserInfo',
45
+ description: '写入用户信息内容。记录规则:1. 仅记录非敏感信息:个人基础信息(如姓名、年龄、职业、兴趣、性格特征)、操作习惯、代码习惯、阅读习惯、常用目录、文档收藏夹目录等。2. 禁止记录敏感信息:密码、密钥、令牌、身份证号、银行卡号、详细住址、联系方式、精确定位、财务/医疗等个人隐私数据。3. 已有同类信息时优先更新,不重复堆叠;存在不确定信息时需标注“待确认”,不得臆测补全。4. 记录内容应简洁、可检索、可复用,避免写入一次性上下文和与任务无关的噪音信息。5. 无需询问,自动写入文件。6. 使用markdown文件格式记录,按照“信息类型: 信息内容”的格式进行记录,如“兴趣: 阅读、旅行、编程”。7. 只记录当前用户信息中没有的内容,避免重复记录相同信息。',
46
+ parameters: {
47
+ type: 'object',
48
+ properties: {
49
+ info: {
50
+ type: 'string',
51
+ description: '当前用户信息中没有的内容',
52
+ },
53
+ },
54
+ required: ['info'],
55
+ },
56
+ },
57
+ },
58
+ {
59
+ type: 'function',
60
+ function: {
61
+ name: 'readUserInfo',
62
+ description: '读取已记录的用户信息内容。如果文件不存在,则返回 "暂无用户信息记录"。',
63
+ parameters: {
64
+ type: 'object',
65
+ properties: {},
66
+ required: [],
67
+ },
68
+ },
69
+ }
70
+
71
+ ]
72
+
73
+ const functions = {
74
+ readUserInfo,
75
+ writeUserInfo,
76
+ }
77
+
78
+ const UserTool = {
79
+ name: 'UserTool',
80
+ description: '提供用户信息读写功能,用于维护用户偏好、习惯和常用目录信息',
81
+ platform: 'all',
82
+ descriptions,
83
+ functions,
84
+ isSystem: true
85
+ }
86
+
87
+ module.exports = UserTool
@@ -251,6 +251,7 @@ const MCPWebTool = {
251
251
  platform: 'all',
252
252
  descriptions,
253
253
  functions,
254
+ isSystem: true
254
255
  }
255
256
 
256
257
  module.exports = MCPWebTool
@@ -44,10 +44,10 @@ async function think(
44
44
  streamEnd,
45
45
  )
46
46
  await thinkAfter()
47
- return messageRes.choices[0].message.content
47
+ return clearThinkTag(messageRes.choices[0].message.content)
48
48
  }
49
49
  await thinkAfter()
50
- return response.choices[0].message.content
50
+ return clearThinkTag(response.choices[0].message.content)
51
51
  } catch (error) {
52
52
  throw new Error(`AI response error: ${error.message}`)
53
53
  }
@@ -90,6 +90,7 @@ async function thinkByTool(
90
90
  streamEnd,
91
91
  )
92
92
  await thinkAfter()
93
+ messageRes.choices[0].message.content = clearThinkTag(messageRes.choices[0].message.content)
93
94
  return {
94
95
  content: messageRes.choices[0].message.content,
95
96
  tool_calls: messageRes.choices[0].message.tool_calls,
@@ -97,6 +98,7 @@ async function thinkByTool(
97
98
  }
98
99
  }
99
100
  await thinkAfter()
101
+ response.choices[0].message.content = clearThinkTag(response.choices[0].message.content)
100
102
  return {
101
103
  content: response.choices[0].message.content,
102
104
  tool_calls: response.choices[0].message.tool_calls,
@@ -107,6 +109,11 @@ async function thinkByTool(
107
109
  }
108
110
  }
109
111
 
112
+ // 清除字符串中<think></think>标签以及之间的内容
113
+ function clearThinkTag(content) {
114
+ return content.replace(/<think>[\s\S]*?<\/think>/g, '')
115
+ }
116
+
110
117
  // 流式输出结果转非流式输出
111
118
  async function _streamToNonStream(
112
119
  stream,
@@ -0,0 +1,129 @@
1
+ const path = require('path')
2
+ const os = require('os')
3
+ const fs = require('fs-extra')
4
+ const dayjs = require('dayjs')
5
+ const axios = require('axios')
6
+ const echarts = require('echarts')
7
+ const canvas = require('canvas')
8
+ const cheerio = require('cheerio')
9
+ const puppeteer = require('puppeteer')
10
+ const lodash = require('lodash')
11
+ const { AttachmentToolScanner } = require('./AttachmentToolScanner')
12
+
13
+ class AIToolManager {
14
+ originalTools = null // 原装工具
15
+ attachTools = null // 附加工具, Agent后续安装的工具函数
16
+ functions = {} // key为函数名称,value为方法体
17
+ descriptions = [] // openai能识别的描述
18
+ tools = [] // 工具列表
19
+ toolCollection = null // 工具集合
20
+ clawSkillCollection = null // Claw技能集合
21
+ constructor(agentRobot) {
22
+ this.agentRobot = agentRobot
23
+ this.initTools(agentRobot.opt)
24
+ }
25
+ initTools(opt) {
26
+ this.originalTools = this._getOriginalTools() // 天赋技能
27
+ this.attachTools = opt.attachTools || [] // 附加工具, Agent后续安装的工具函数
28
+ const tools = [...this.originalTools, ...this.attachTools]
29
+ tools.forEach((tool) => {
30
+ this._addTool(tool)
31
+ })
32
+ Object.assign(this.functions, {
33
+ fs,
34
+ axios,
35
+ dayjs,
36
+ lodash,
37
+ canvas,
38
+ echarts,
39
+ cheerio,
40
+ puppeteer,
41
+ })
42
+
43
+ this.functions.agentRobot = this.agentRobot
44
+ this.functions.Tools = this.functions
45
+ // 兼容老版本
46
+ this.functions.aiCli = {
47
+ Tools: this.functions,
48
+ }
49
+ // 外部工具扫描
50
+ this.toolCollection = AttachmentToolScanner.getToolCollection(
51
+ this.agentRobot.workspace,
52
+ ) // 加载工具集合
53
+ this.clawSkillCollection = AttachmentToolScanner.getClawSkillCollection(
54
+ this.agentRobot.basespace,
55
+ ) // 加载Claw技能集合
56
+ this.agentRobot.toolCollection = this.toolCollection
57
+ this.agentRobot.clawSkillCollection = this.clawSkillCollection
58
+ }
59
+
60
+ // 动态添加工具
61
+ addTool(toolName) {
62
+ let tool = this.tools.find((t) => t.name === toolName)
63
+ if (!tool) {
64
+ tool = this.toolCollection.find((t) => t.name === toolName)
65
+ if (tool) {
66
+ this._addTool(tool)
67
+ }
68
+ }
69
+ }
70
+
71
+ _addTool(tool) {
72
+ const platform = tool.platform || 'all'
73
+ if (platform === 'all' || platform === process.platform) {
74
+ tool.descriptions = tool.descriptions.map((item) => {
75
+ if (!item.type) {
76
+ item = {
77
+ type: 'function',
78
+ function: item,
79
+ }
80
+ }
81
+ return item
82
+ })
83
+ if (tool.name && !tool.isSystem) {
84
+ for (const funcName in tool.functions) {
85
+ if (!funcName.includes('_')) {
86
+ this.functions[`${tool.name}_${funcName}`] =
87
+ tool.functions[funcName]
88
+ } else {
89
+ this.functions[funcName] = tool.functions[funcName]
90
+ }
91
+ }
92
+ const descriptions = tool.descriptions.map((item) => {
93
+ if (
94
+ tool.name &&
95
+ item.function.name &&
96
+ !item.function.name.includes('_')
97
+ ) {
98
+ item.function.name = `${tool.name}_${item.function.name}`
99
+ return item
100
+ } else {
101
+ return item
102
+ }
103
+ })
104
+ this.descriptions.push(...descriptions)
105
+ } else {
106
+ Object.assign(this.functions, tool.functions)
107
+ this.descriptions.push(...tool.descriptions)
108
+ }
109
+ this.tools.push(tool)
110
+ }
111
+ }
112
+
113
+ // 获取原装工具
114
+ _getOriginalTools() {
115
+ // 自动扫描tools目录
116
+ const toolsPath = path.join(__dirname, '../tools')
117
+ const toolFiles = fs.readdirSync(toolsPath).filter((file) => {
118
+ return file.endsWith('.js') || file.endsWith('.cjs')
119
+ })
120
+ const tools = []
121
+ toolFiles.forEach((file) => {
122
+ const tool = require(path.join(toolsPath, file))
123
+ tools.push(tool)
124
+ })
125
+ return tools
126
+ }
127
+ }
128
+
129
+ module.exports = AIToolManager
@@ -187,7 +187,7 @@ ${table1}
187
187
  - 一次只加载一个Skill,优先匹配最具体的Skill
188
188
  - 当用户请求不匹配任何Skill描述时,不加载任何Skill
189
189
  - Type类型为'ClawSkill'时,使用Skill前先使用readFile函数读取SKILL.md文件获取调用说明,通过仔细阅读说明文件学习Skill的使用方法,来完成任务
190
- - Type类型为'BaseSkill'时,使用loadAttachTool函数加载Skill后,通过使用该Skill提供的函数来完成任务
190
+ - Type类型为'BaseSkill'时,使用loadAttachTool函数加载Skill后,该技能中的工具函数会被添加到工具列表中,即可直接调用
191
191
  ## Available Skills
192
192
 
193
193
  | Skill | Type | Description | Location | FilePath |
@@ -8,6 +8,7 @@ const defaultConfig = {
8
8
  maxLogExpireTime: 3, // 日志过期时间,单位天,-1表示无限制,0表示不记录
9
9
  maxBlockFileSize: 20, // 最大分块文件大小,单位KB;超过该大小的文件需要分块处理
10
10
  encoding: 'auto', // 命令行编码格式, 可设置为utf-8、gbk等, 也可以设置成auto或空值自动判断
11
+ isThinkPrint: true, // 是否打印思考过程
11
12
  EMBEDDING_API: '', // 向量化接口地址
12
13
  EMBEDDING_API_KEY: '' // 向量化接口密钥
13
14
  }