deepfish-ai 1.0.13 → 1.0.16

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.
@@ -2,12 +2,11 @@
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-17 10:00:50
6
- * @FilePath: \src\core\ai-services\AiWorker\AIMessageManager.js
5
+ * @LastEditTime: 2026-03-25 15:27:58
6
+ * @FilePath: \deepfish\src\core\ai-services\AiWorker\AIMessageManager.js
7
7
  * @Description: 上下文管理-添加、自动压缩
8
8
  * @
9
9
  */
10
- const { cloneDeep } = require('lodash')
11
10
  const { logError, logInfo } = require('../../utils/log')
12
11
  const { aiRequestSingle } = require('./AiTools')
13
12
  const { GlobalVariable } = require('../../globalVariable')
@@ -28,8 +27,8 @@ class AIMessageManager {
28
27
  // 添加消息
29
28
  addMsg(message) {
30
29
  this.messages.push(message)
31
- GlobalVariable.aiRecorder.record(this.messages)
32
- GlobalVariable.aiRecorder.log(message)
30
+ GlobalVariable.historyManager.record(this.messages)
31
+ GlobalVariable.historyManager.log(message)
33
32
  }
34
33
  // 添加tool
35
34
  addTool(id, content) {
@@ -42,8 +41,8 @@ class AIMessageManager {
42
41
  content: content,
43
42
  }
44
43
  this.messages.push(message)
45
- GlobalVariable.aiRecorder.record(this.messages)
46
- GlobalVariable.aiRecorder.log(message)
44
+ GlobalVariable.historyManager.record(this.messages)
45
+ GlobalVariable.historyManager.log(message)
47
46
  }
48
47
  /**
49
48
  * 压缩消息,根据配置压缩消息长度和数量
@@ -54,41 +53,50 @@ class AIMessageManager {
54
53
  const currentLength = this._getLength(messages)
55
54
  const currentCount = messages.length
56
55
  if (
57
- currentLength > this.config.maxMessagesLength ||
58
- currentCount > this.config.maxMessagesCount
56
+ (this.config.maxMessagesLength !== -1 && currentLength > this.config.maxMessagesLength) ||
57
+ (this.config.maxMessagesCount !== -1 && currentCount > this.config.maxMessagesCount)
59
58
  ) {
60
59
  logInfo(
61
60
  `Managing messages: current length ${currentLength}, count ${currentCount}`,
62
61
  )
63
- const systemMessage = messages[0]
64
- const userMessage = messages[1]
65
- const goal = messages[1].content
66
- const messagesToSummarize = messages.slice(2, -2)
67
-
68
- if (messagesToSummarize.length > 0) {
69
- messages = cloneDeep(messages)
70
- const summary = await this._getSummary(
71
- goal,
72
- messages.slice(-2),
73
- messagesToSummarize,
74
- )
75
-
76
- const newMessages = [
77
- systemMessage,
78
- userMessage,
79
- {
80
- role: 'user',
81
- content: `[CONVERSATION SUMMARY]: ${summary}`,
82
- },
83
- ...messages.slice(-2),
84
- ]
85
- logInfo(
86
- `Messages compressed: ${messages.length} -> ${newMessages.length}`,
62
+ let newMessages = []
63
+ if (messages.length > 2) {
64
+ // 始终只保留system和user的最后一条消息,以及最后两条消息,其他消息进行压缩
65
+ const systemMessage = messages[0]
66
+ // 查询最后一条用户消息
67
+ const lastUserMessageIndex = messages.findIndex(
68
+ (m) => m.role === 'user',
87
69
  )
88
- GlobalVariable.aiRecorder.record(newMessages)
89
- GlobalVariable.aiRecorder.log(newMessages)
90
- return newMessages
70
+ // 压缩第二条到lastUserMessageIndex之间的消息
71
+ const messages1 = messages.slice(1, lastUserMessageIndex)
72
+ newMessages = [systemMessage]
73
+ if (messages1.length > 0) {
74
+ const summary1 = await this._getSummary(messages1)
75
+ newMessages.push(summary1)
76
+ GlobalVariable.historyManager.log(summary1, true)
77
+ }
78
+ if (lastUserMessageIndex < messages.length - 2) {
79
+ newMessages.push(messages[lastUserMessageIndex])
80
+ // 压缩lastUserMessageIndex到倒数第二条消息之间的消息
81
+ const messages2 = messages.slice(lastUserMessageIndex + 1, -2)
82
+ if (messages2.length > 0) {
83
+ const summary2 = await this._getSummary(messages2)
84
+ newMessages.push(summary2)
85
+ GlobalVariable.historyManager.log(summary2, true)
86
+ }
87
+ newMessages.push(...messages.slice(-2))
88
+ } else if (lastUserMessageIndex === messages.length - 2) {
89
+ newMessages.push(messages[lastUserMessageIndex])
90
+ newMessages.push(messages[messages.length - 1])
91
+ } else if (lastUserMessageIndex === messages.length - 1) {
92
+ newMessages.push(messages[lastUserMessageIndex])
93
+ }
94
+ GlobalVariable.historyManager.record(newMessages)
95
+ } else if (messages.length === 2) {
96
+ const summary = await this._getSummary([messages[1]])
97
+ newMessages.push([messages[0], summary])
91
98
  }
99
+ return newMessages
92
100
  }
93
101
  return messages
94
102
  }
@@ -109,22 +117,12 @@ class AIMessageManager {
109
117
  }, 0)
110
118
  }
111
119
  // 合并消息
112
- async _getSummary(goal, lastTwoMessages, messages) {
113
- lastTwoMessages = lastTwoMessages
114
- .map((m) => {
115
- if (m.role === 'system') return `[SYSTEM]: ${m.content}`
116
- if (m.role === 'user') return `[USER]: ${m.content}`
117
- if (m.role === 'assistant')
118
- return `[ASSISTANT]: ${m.content ? m.content : '[Tool calls]'}`
119
- if (m.role === 'tool') return `[TOOL RESULT]: ${m.content}`
120
- return ''
121
- })
122
- .join('\n')
123
- const summaryPrompt = `请结合任务目标${goal},和最后两轮的对话${lastTwoMessages}, 总结以下对话历史,重点:
124
- 1. 删除不需要的信息,如程序报错、冗余表述、语气词、闲聊等信息
125
- 2. 关注当前进度和状态
126
- 3. 总结后续任务所需的重要背景信息并以及所需要的内容
127
- 结果只保留对上下文有用的内容,保持摘要简短且全面,保证后续任务有效进行。.
120
+ async _getSummary(messages) {
121
+ const summaryPrompt = `总结以下对话历史,重点:
122
+ 1. 只需要关注用户输入的任务目标和AI的执行结果,删除过程中的细节描述和执行过程中的失败信息等无用信息;
123
+ 2. 删除不需要的信息,如程序报错、冗余表述、语气词、闲聊等信息;
124
+ 3. 保留和总结后续任务所需的重要背景信息并以及所需要的内容;
125
+ 4. 保持摘要简短且全面,保证后续任务有效进行.
128
126
 
129
127
  Conversation history:
130
128
  ${messages
@@ -144,10 +142,29 @@ ${messages
144
142
  'You are a helpful assistant that creates concise summaries of conversations.',
145
143
  summaryPrompt,
146
144
  )
147
- return summary
145
+ return {
146
+ role: 'user',
147
+ content: summary,
148
+ }
148
149
  } catch (error) {
149
150
  logError('Failed to summarize messages: ' + error.message)
150
- return 'Previous conversation history was too long and has been summarized. Please continue with the current task.'
151
+ // 出错时手动压缩
152
+ let manualSummary = ''
153
+ messages.forEach((m) => {
154
+ if (m.role === 'system') {
155
+ manualSummary += `[SYSTEM]: ${m.content.slice(0, 100)}...\n`
156
+ } else if (m.role === 'user') {
157
+ manualSummary += `[USER]: ${m.content.slice(0, 100)}...\n`
158
+ } else if (m.role === 'assistant') {
159
+ manualSummary += `[ASSISTANT]: ${m.content ? m.content.slice(0, 100) : '[Tool calls]'}...\n`
160
+ } else if (m.role === 'tool') {
161
+ manualSummary += `[TOOL RESULT]: ${m.content.slice(0, 100)}...\n`
162
+ }
163
+ })
164
+ return {
165
+ role: 'user',
166
+ content: manualSummary,
167
+ }
151
168
  }
152
169
  }
153
170
  }
@@ -1,21 +1,18 @@
1
1
  /**
2
2
  * @Author: Roman 306863030@qq.com
3
3
  * @Date: 2026-03-16 09:18:05
4
- * @LastEditors: Roman 306863030@qq.com
5
- * @LastEditTime: 2026-03-17 10:00:20
6
- * @FilePath: \src\core\ai-services\AiWorker\AiAgent.js
4
+ * @LastEditors: roman_123 306863030@qq.com
5
+ * @LastEditTime: 2026-03-26 01:04:45
6
+ * @FilePath: \deepfish\src\core\ai-services\AiWorker\AiAgent.js
7
7
  * @Description: 工作流循环
8
8
  * @
9
9
  */
10
10
 
11
11
  const { logError, logInfo, loading } = require('../../utils/log')
12
12
  const AIMessageManager = require('./AIMessageManager')
13
- const { AiAgentSystemPrompt } = require('./AiPrompt')
14
13
  const { aiRequestByTools } = require('./AiTools')
15
14
 
16
15
  class AiAgent {
17
- // 工作流提示词
18
- prompt
19
16
  messages
20
17
  maxIterations
21
18
  goal
@@ -29,9 +26,9 @@ class AiAgent {
29
26
  this.aiClient = aiClient
30
27
  this.config = config
31
28
  this.aiConfig = aiConfig
32
- this.prompt = AiAgentSystemPrompt
33
29
  this.maxIterations =
34
30
  config.maxIterations === -1 ? Infinity : config.maxIterations
31
+ this.maxBlockFileSize = this.config.maxBlockFileSize || 20 // 默认20KB
35
32
  this.aiMessageManager = new AIMessageManager(aiClient, config, aiConfig, [])
36
33
  this.extensionTools = extensionTools
37
34
  this.name = config.name
@@ -101,7 +98,7 @@ class AiAgent {
101
98
  const fileInfo = await this.extensionTools.functions['getFileInfo'](
102
99
  parsedArgs.filePath,
103
100
  )
104
- if (fileInfo && fileInfo.isFile && fileInfo.size > 10 * 1024) {
101
+ if (fileInfo && fileInfo.isFile && fileInfo.size > this.maxBlockFileSize * 1024) {
105
102
  this.aiMessageManager.addTool(id, {
106
103
  error:
107
104
  '文件过大,请使用executeJSCode工具编写脚本分块读取和处理文件,避免一次性读取整个文件内容到对话中。建议使用fs.createReadStream逐行或分块读取,仅返回必要的结果或总结。',
@@ -114,7 +111,7 @@ class AiAgent {
114
111
  let result = await toolFunction(...Object.values(parsedArgs))
115
112
  let toolContent = JSON.stringify(result)
116
113
  if (name !== 'requestAI') {
117
- const MAX_CONTENT_SIZE = 100000
114
+ const MAX_CONTENT_SIZE = this.maxBlockFileSize * 1024
118
115
  if (toolContent.length > MAX_CONTENT_SIZE) {
119
116
  if (
120
117
  typeof result === 'string' &&
@@ -2,7 +2,7 @@
2
2
  * @Author: Roman 306863030@qq.com
3
3
  * @Date: 2026-03-17 09:12:22
4
4
  * @LastEditors: roman_123 306863030@qq.com
5
- * @LastEditTime: 2026-03-19 15:47:31
5
+ * @LastEditTime: 2026-03-26 02:12:44
6
6
  * @FilePath: \deepfish\src\core\ai-services\AiWorker\AiPrompt.js
7
7
  * @Description: AI请求提示词
8
8
  * @
@@ -11,19 +11,19 @@ const currentDir = process.cwd()
11
11
  const osType = process.platform
12
12
 
13
13
  const AiAgentSystemPrompt = `
14
- 你是一个严格按规则执行任务的智能体,不能违反任何系统限制。
14
+ 你叫DeepFish, 是一个严格按规则执行任务的智能体,不能违反任何系统限制。
15
15
  ### 基础环境信息
16
16
  当前工作目录:${currentDir}
17
17
  操作系统类型:${osType}
18
18
  语言类型: 与用户输入语言一致
19
19
 
20
20
  ### 工具使用规则
21
- 优先使用工具完成任务:可调用 executeJSCode 运行 Node.js 代码处理复杂逻辑;可调用 executeCommand 运行系统命令行工具(如 git、npm 等),工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
21
+ 系统中内置了一些可以直接调用的工具函数,如可调用 executeJSCode 运行 Node.js 代码处理复杂逻辑;可调用 executeCommand 运行系统命令行工具(如 git、npm 等),工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
22
22
 
23
23
  ### 大文本文件处理规则(分步执行)
24
24
  处理长文档等大文件(单文件>20KB)时,必须按以下步骤分块处理:
25
25
  1. 预处理:先执行文件大小/结构检查(如通过命令行/JS 代码获取文件大小、判断文件格式),输出检查结果;
26
- 2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:{原文件名}_chunk{序号}.tmp);
26
+ 2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:tmp_{原文件名}_chunk{序号}.tmp);
27
27
  3. 处理逻辑:翻译/总结/分析类任务逐块处理,每块处理完成后记录结果,最后合并所有块的结果生成最终文件;
28
28
  4. 合并校验:合并后需校验结果完整性(如总字符数匹配、无内容缺失),确保分块处理无遗漏。
29
29
 
@@ -31,8 +31,58 @@ const AiAgentSystemPrompt = `
31
31
  1. 最优路径优先:执行前必须先规划最少步骤的操作路径,明确「先做什么、再做什么、哪些可省略」,避免重复操作和无效步骤;
32
32
  2. 异常反馈:操作失败(如命令执行报错、文件不存在)时,需明确说明「失败原因+可尝试的解决方案」,而非仅提示“操作失败”;
33
33
  3. 结果校验:任务完成后,需简单校验结果是否符合用户目标(如文件是否生成、内容是否完整),并向用户反馈校验结果。
34
+ 4. 如果执行任务过程中需要安装软件、工具或执行"npm install"命令,必须通过调用用户交互函数与用户交互,等待用户确认后再执行安装。
35
+ 5. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"tmp_"为前缀命名,并在任务完成后删除这些临时文件,确保工作目录整洁。
36
+ `
37
+
38
+ const SkillAiAgentSystemPrompt = `
39
+ 你叫DeepFish, 是一个能够使用Skill完成任务的智能体,不能违反任何系统限制。用户会给你一个明确的目标,这是整个任务环节中的一个子任务,你需要仔细分析目标,结合Skill文档来完成这项任务,如果不能借助Skill完成则直接返回原因。
40
+ ### 基础环境信息
41
+ 当前工作目录:${currentDir}
42
+ 操作系统类型:${osType}
43
+ 语言类型: 与用户输入语言一致
44
+
45
+ ### 工具使用规则
46
+ 工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
47
+
48
+ ### 大文本文件处理规则(分步执行)
49
+ 处理长文档等大文件(单文件>20KB)时,必须按以下步骤分块处理:
50
+ 1. 预处理:先执行文件大小/结构检查(如通过命令行/JS 代码获取文件大小、判断文件格式),输出检查结果;
51
+ 2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:tmp_{原文件名}_chunk{序号}.tmp);
52
+ 3. 处理逻辑:翻译/总结/分析类任务逐块处理,每块处理完成后记录结果,最后合并所有块的结果生成最终文件;
53
+ 4. 合并校验:合并后需校验结果完整性(如总字符数匹配、无内容缺失),确保分块处理无遗漏。
54
+
55
+ ### Skill使用规则
56
+ 1. 仔细阅读Skill.md中的技能描述和调用说明,确保完全理解技能功能和使用方法;
57
+ 2. 使用Skill.md中列出的技能,避免使用其他技能;
58
+ 3. 可以使用工具函数(如 executeJSCode、executeCommand)来辅助使用技能,但必须确保工具调用符合当前操作系统规范;
59
+ 4. 如果发现不能使用Skill完成任务,则直接说明原因,不要尝试使用其他工具或技能来完成任务;
60
+ 5. 如果需要安装软件或工具来完成任务,必须通过调用用户交互函数与用户交互,等待用户确认后再执行安装。
61
+ 6. 任务完成后,反馈任务执行结果。
62
+ 7. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"tmp_"为前缀命名,并在任务完成后删除这些临时文件,确保工作目录整洁。
63
+ `
64
+
65
+ const TestAiAgentSystemPrompt = `
66
+ 你叫DeepFish, 是一个调用工具函数完成测试任务的智能体,不能违反任何系统限制。用户会给你一个明确的测试目标,这是整个测试环节中的一个子任务,你需要仔细分析目标,结合工具函数来完成这项测试,如果不能借助工具函数完成则直接返回原因。
67
+ ### 基础环境信息
68
+ 当前工作目录:${currentDir}
69
+ 操作系统类型:${osType}
70
+ 语言类型: 与用户输入语言一致
71
+
72
+ ### 工具使用规则
73
+ 工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
74
+
75
+ ### 测试规则
76
+ 1. 仔细分析测试目标,确保完全理解测试需求和预期结果;
77
+ 2. 在测试过程中可以通过创建前缀为"test_"的临时文件辅助测试,完成后需删除这些临时文件;
78
+ 3. 在测试过程中随意不能修改原有的文件内容;
79
+ 4. 如果发现不能使用工具函数完成测试任务,则直接说明原因,不要做过多尝试;
80
+ 5. 任务完成后,反馈测试执行结果。
81
+ 6. 任务执行过程中,产生的所有临时文件(如分块文件、测试文件等)必须以"tmp_"为前缀命名,并在任务完成后删除这些临时文件,确保工作目录整洁。
34
82
  `
35
83
 
36
84
  module.exports = {
37
- AiAgentSystemPrompt
38
- }
85
+ AiAgentSystemPrompt,
86
+ SkillAiAgentSystemPrompt,
87
+ TestAiAgentSystemPrompt,
88
+ }
@@ -1,15 +1,16 @@
1
1
  /**
2
2
  * @Author: Roman 306863030@qq.com
3
3
  * @Date: 2026-03-17 09:12:22
4
- * @LastEditors: Roman 306863030@qq.com
5
- * @LastEditTime: 2026-03-20 16:28:53
4
+ * @LastEditors: roman_123 306863030@qq.com
5
+ * @LastEditTime: 2026-03-26 01:08:48
6
6
  * @FilePath: \deepfish\src\core\ai-services\AiWorker\AiTools.js
7
7
  * @Description: 对话初始化、对话请求
8
8
  * @
9
9
  */
10
10
  const { OpenAI } = require('openai')
11
- const { AiAgentSystemPrompt } = require('./AiPrompt')
11
+ const { AiAgentSystemPrompt, SkillAiAgentSystemPrompt, TestAiAgentSystemPrompt } = require('./AiPrompt')
12
12
  const { streamOutput, streamLineBreak } = require('../../utils/log')
13
+ const { GlobalVariable } = require('../../globalVariable')
13
14
 
14
15
  // 创建client
15
16
  function createOpenAiClient(aiConfig) {
@@ -21,10 +22,50 @@ function createOpenAiClient(aiConfig) {
21
22
 
22
23
  // 获取初始的message
23
24
  function getInitialMessages(goal) {
25
+ // 合并系统描述
24
26
  return [
25
27
  {
26
28
  role: 'system',
27
- content: AiAgentSystemPrompt,
29
+ content: getSystemPrompt(),
30
+ },
31
+ {
32
+ role: 'user',
33
+ content: goal,
34
+ },
35
+ ]
36
+ }
37
+
38
+ function getSystemPrompt() {
39
+ const config = GlobalVariable.aiCli.config
40
+ const skillPrompt = GlobalVariable.skillConfigManager.preLoadSkills()
41
+ const systemDescription = `${AiAgentSystemPrompt.replace('20KB', `${config.maxBlockFileSize}KB`)}\n\n${skillPrompt}`
42
+ return systemDescription
43
+ }
44
+
45
+ // 获取调用skill的初始message
46
+ function getInitialMessagesForSkill(skillContent, goal) {
47
+ const config = GlobalVariable.aiCli.config
48
+ const systemDescription = `
49
+ ${SkillAiAgentSystemPrompt.replace('20KB', `${config.maxBlockFileSize}KB`)}
50
+ ### 以下是加载完成的Skill.md文件的内容:
51
+ ${skillContent}`
52
+ return [
53
+ {
54
+ role: 'system',
55
+ content: systemDescription,
56
+ },
57
+ {
58
+ role: 'user',
59
+ content: goal,
60
+ },
61
+ ]
62
+ }
63
+
64
+ function getInitialMessagesForTest(goal) {
65
+ return [
66
+ {
67
+ role: 'system',
68
+ content: TestAiAgentSystemPrompt.replace('20KB', `${GlobalVariable.aiCli.config.maxBlockFileSize}KB`),
28
69
  },
29
70
  {
30
71
  role: 'user',
@@ -153,13 +194,13 @@ async function _streamToNonStream(stream) {
153
194
  if (reasoning_content) {
154
195
  finalResponse.choices[0].message.reasoning_content += reasoning_content
155
196
  // 流式输出
156
- streamOutput(reasoning_content)
197
+ streamOutput(reasoning_content, '#47854a')
157
198
  }
158
199
  const content = delta.content
159
200
  if (content) {
160
201
  finalResponse.choices[0].message.content += content
161
202
  // 流式输出
162
- streamOutput(content)
203
+ streamOutput(content, '#68e46e')
163
204
  }
164
205
  // 3. 处理工具调用(核心逻辑)
165
206
  if (delta.tool_calls && delta.tool_calls.length > 0) {
@@ -185,7 +226,7 @@ async function _streamToNonStream(stream) {
185
226
  const toolCall = toolCallBuffers.get(id)
186
227
  if (toolCall && toolCallChunk.function?.arguments) {
187
228
  toolCall.function.arguments += toolCallChunk.function.arguments
188
- streamOutput(toolCallChunk.function.arguments)
229
+ streamOutput(toolCallChunk.function.arguments, '#47854a')
189
230
  }
190
231
  }
191
232
  })
@@ -219,4 +260,7 @@ module.exports = {
219
260
  aiRequestSingle,
220
261
  aiRequestByTools,
221
262
  getInitialMessages,
263
+ getInitialMessagesForSkill,
264
+ getInitialMessagesForTest,
265
+ getSystemPrompt
222
266
  }
@@ -2,20 +2,24 @@
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-17 17:03:16
5
+ * @LastEditTime: 2026-03-25 20:25:28
6
6
  * @FilePath: \deepfish\src\core\ai-services\AiWorker\index.js
7
7
  * @Description: 工作流类
8
8
  * @
9
9
  */
10
- const { logInfo } = require('../../utils/log')
11
10
  const AiAgent = require('./AiAgent')
12
- const { getInitialMessages } = require('./AiTools')
11
+ const {
12
+ getInitialMessages,
13
+ getInitialMessagesForSkill,
14
+ getInitialMessagesForTest,
15
+ getSystemPrompt,
16
+ } = require('./AiTools')
13
17
 
14
18
  class AiWorker {
15
19
  constructor(aiCli, client) {
16
20
  this.aiCli = aiCli
17
21
  this.client = client
18
- this.aiRecorder = this.aiCli.aiRecorder
22
+ this.historyManager = this.aiCli.historyManager
19
23
  this.messages = []
20
24
  this.aiAgent = new AiAgent(
21
25
  this.client,
@@ -26,40 +30,91 @@ class AiWorker {
26
30
  }
27
31
 
28
32
  async main(goal) {
29
- // 判断是否回复会话
30
- const isRecover = await this.aiRecorder.recover()
31
- if (isRecover) {
32
- const { messages } = isRecover
33
- // 判断是否已经完成
34
- const lastMessage = messages[messages.length - 1]
35
- if (lastMessage.role === 'assistant' && !lastMessage.tool_calls) {
36
- // 说明已经执行完毕,直接返回
33
+ // 自动加载历史记录
34
+ if (this.messages.length === 0) {
35
+ const messages = this.historyManager.getMessage()
36
+ if (messages.length) {
37
+ // 更新系统skill提示词
38
+ const systemPrompt = getSystemPrompt()
39
+ messages[0].content = systemPrompt
40
+ this.clearUserMessage(messages)
37
41
  this.messages = messages
38
- logInfo(lastMessage.content)
39
- return
40
- }
41
- this.messages = messages
42
- this.aiAgent.aiMessageManager.reLinkMsgs(this.messages)
43
- await this._recoverHistory(goal, this.messages)
44
- } else {
45
- if (!this.messages.length) {
46
- this.messages = getInitialMessages(goal)
47
- await this.aiAgent.work(this.messages)
42
+ // await this.loadHhistoryMessages(messages)
43
+ await this.main(goal)
48
44
  } else {
49
- this.aiAgent.aiMessageManager.reLinkMsgs(this.messages)
50
- this.aiAgent.aiMessageManager.addMsg({
51
- role: 'user',
52
- content: goal,
53
- })
45
+ this.messages = getInitialMessages(goal)
54
46
  await this.aiAgent.work(this.messages)
55
47
  }
48
+ } else {
49
+ this.aiAgent.aiMessageManager.reLinkMsgs(this.messages)
50
+ this.aiAgent.aiMessageManager.addMsg({
51
+ role: 'user',
52
+ content: goal,
53
+ })
54
+ await this.aiAgent.work(this.messages)
55
+ }
56
+ }
57
+
58
+ subSkillAgent(skillContent, goal) {
59
+ const aiAgent = new AiAgent(
60
+ this.client,
61
+ this.aiCli.config,
62
+ this.aiCli.aiConfig,
63
+ this.aiCli.extensionManager.extensions,
64
+ )
65
+ const initMessages = getInitialMessagesForSkill(skillContent, goal)
66
+ return aiAgent.work(initMessages)
67
+ }
68
+
69
+ subTestAgent(goal) {
70
+ const aiAgent = new AiAgent(
71
+ this.client,
72
+ this.aiCli.config,
73
+ this.aiCli.aiConfig,
74
+ this.aiCli.extensionManager.extensions,
75
+ )
76
+ const initMessages = getInitialMessagesForTest(goal)
77
+ return aiAgent.work(initMessages)
78
+ }
79
+
80
+ clearUserMessage(messages) {
81
+ while (
82
+ messages.length > 0 &&
83
+ messages[messages.length - 1].role === 'user'
84
+ ) {
85
+ messages.pop()
86
+ }
87
+ let lastMessage = messages[messages.length - 1]
88
+ if (lastMessage.role === 'assistant' && lastMessage.tool_calls) {
89
+ messages.pop()
90
+ }
91
+ while (
92
+ messages.length > 0 &&
93
+ messages[messages.length - 1].role === 'user'
94
+ ) {
95
+ messages.pop()
96
+ }
97
+ lastMessage = messages[messages.length - 1]
98
+ if (lastMessage.role === 'tool' && lastMessage.tool_call_id) {
99
+ messages.push({
100
+ role: 'assistant',
101
+ content:
102
+ '上次对话未完成,已清除用户输入,请重新输入。',
103
+ reasoning_content: '',
104
+ })
56
105
  }
57
- // this.aiRecorder.clear()
58
106
  }
59
107
 
60
- async _recoverHistory(goal, messages) {
61
- logInfo('Recovering from previous conversation...')
108
+ async loadHhistoryMessages(messages) {
109
+ // 判断是否已经完成
62
110
  let lastMessage = messages[messages.length - 1]
111
+ if (lastMessage.role === 'assistant' && !lastMessage.tool_calls) {
112
+ // 说明已经执行完毕,直接返回
113
+ this.messages = messages
114
+ return
115
+ }
116
+ this.messages = messages
117
+ this.aiAgent.aiMessageManager.reLinkMsgs(this.messages)
63
118
  if (lastMessage.role === 'tool') {
64
119
  // 删除最后一项
65
120
  messages.pop()
@@ -68,18 +123,12 @@ class AiWorker {
68
123
  if (lastMessage.role === 'assistant' && lastMessage.tool_calls) {
69
124
  // 最后一项正在执行工具,则重新执行
70
125
  await this.aiAgent.execTools(lastMessage.tool_calls)
71
- this.aiAgent.work(this.messages)
126
+ await this.aiAgent.work(this.messages)
72
127
  } else if (lastMessage.role === 'assistant' && lastMessage.content) {
73
128
  return lastMessage.content || ''
74
129
  } else if (lastMessage.role === 'user') {
75
130
  // 最后一项是用户输入,说明是新的一轮对话
76
- this.aiAgent.work(this.messages)
77
- } else if (lastMessage.role === 'system') {
78
- this.aiAgent.aiMessageManager.addMsg({
79
- role: 'user',
80
- content: goal,
81
- })
82
- this.aiAgent.work(this.messages)
131
+ await this.aiAgent.work(this.messages)
83
132
  }
84
133
  return ''
85
134
  }
@@ -22,6 +22,14 @@ class AIService {
22
22
  mainWorkflow(goal) {
23
23
  return this.aiWorker.main(goal);
24
24
  }
25
+
26
+ subSkillWorkflow(skillContent, goal) {
27
+ return this.aiWorker.subSkillAgent(skillContent, goal);
28
+ }
29
+
30
+ subTestWorkflow(goal) {
31
+ return this.aiWorker.subTestAgent(goal);
32
+ }
25
33
  }
26
34
 
27
35
  module.exports = AIService;
@@ -2,6 +2,8 @@ const descriptions = [] // openai能识别的描述
2
2
  const functions = {} // key为函数名称,value为方法体
3
3
 
4
4
  module.exports = {
5
+ name: 'BaseExtension',
6
+ extensionDescription: "基础扩展模板,提供扩展的基本结构定义",
5
7
  descriptions,
6
8
  functions,
7
9
  }