foliko 1.0.75 → 1.0.76
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/.claude/settings.local.json +159 -157
- package/cli/bin/foliko.js +12 -12
- package/cli/src/commands/chat.js +143 -143
- package/cli/src/commands/list.js +93 -93
- package/cli/src/index.js +75 -75
- package/cli/src/ui/chat-ui.js +201 -201
- package/cli/src/utils/ansi.js +40 -40
- package/cli/src/utils/markdown.js +292 -292
- package/examples/ambient-example.js +194 -194
- package/examples/basic.js +115 -115
- package/examples/bootstrap.js +121 -121
- package/examples/mcp-example.js +56 -56
- package/examples/skill-example.js +49 -49
- package/examples/test-chat.js +137 -137
- package/examples/test-mcp.js +85 -85
- package/examples/test-reload.js +59 -59
- package/examples/test-telegram.js +50 -50
- package/examples/test-tg-bot.js +45 -45
- package/examples/test-tg-simple.js +47 -47
- package/examples/test-tg.js +62 -62
- package/examples/test-think.js +43 -43
- package/examples/test-web-plugin.js +103 -103
- package/examples/test-weixin-feishu.js +103 -103
- package/examples/workflow.js +158 -158
- package/package.json +1 -1
- package/plugins/ai-plugin.js +102 -102
- package/plugins/ambient-agent/EventWatcher.js +113 -113
- package/plugins/ambient-agent/ExplorerLoop.js +640 -640
- package/plugins/ambient-agent/GoalManager.js +197 -197
- package/plugins/ambient-agent/Reflector.js +95 -95
- package/plugins/ambient-agent/StateStore.js +90 -90
- package/plugins/ambient-agent/constants.js +101 -101
- package/plugins/ambient-agent/index.js +579 -579
- package/plugins/audit-plugin.js +187 -187
- package/plugins/default-plugins.js +662 -662
- package/plugins/email/constants.js +64 -64
- package/plugins/email/handlers.js +461 -461
- package/plugins/email/index.js +278 -278
- package/plugins/email/monitor.js +269 -269
- package/plugins/email/parser.js +138 -138
- package/plugins/email/reply.js +151 -151
- package/plugins/email/utils.js +124 -124
- package/plugins/feishu-plugin.js +481 -481
- package/plugins/file-system-plugin.js +826 -826
- package/plugins/install-plugin.js +199 -199
- package/plugins/python-executor-plugin.js +367 -367
- package/plugins/python-plugin-loader.js +481 -481
- package/plugins/rules-plugin.js +294 -294
- package/plugins/scheduler-plugin.js +691 -691
- package/plugins/session-plugin.js +369 -369
- package/plugins/shell-executor-plugin.js +197 -197
- package/plugins/storage-plugin.js +240 -240
- package/plugins/subagent-plugin.js +845 -845
- package/plugins/telegram-plugin.js +482 -482
- package/plugins/think-plugin.js +345 -345
- package/plugins/tools-plugin.js +196 -196
- package/plugins/web-plugin.js +606 -606
- package/plugins/weixin-plugin.js +545 -545
- package/src/capabilities/index.js +11 -11
- package/src/capabilities/skill-manager.js +609 -609
- package/src/capabilities/workflow-engine.js +1109 -1109
- package/src/core/agent-chat.js +882 -882
- package/src/core/agent.js +892 -892
- package/src/core/framework.js +465 -465
- package/src/core/index.js +19 -19
- package/src/core/plugin-base.js +219 -219
- package/src/core/plugin-manager.js +863 -863
- package/src/core/provider.js +114 -114
- package/src/core/sub-agent-config.js +264 -264
- package/src/core/system-prompt-builder.js +120 -120
- package/src/core/tool-registry.js +517 -517
- package/src/core/tool-router.js +297 -297
- package/src/executors/executor-base.js +58 -58
- package/src/executors/mcp-executor.js +741 -741
- package/src/index.js +25 -25
- package/src/utils/circuit-breaker.js +301 -301
- package/src/utils/error-boundary.js +363 -363
- package/src/utils/error.js +374 -374
- package/src/utils/event-emitter.js +97 -97
- package/src/utils/id.js +133 -133
- package/src/utils/index.js +217 -217
- package/src/utils/logger.js +181 -181
- package/src/utils/plugin-helpers.js +90 -90
- package/src/utils/retry.js +122 -122
- package/src/utils/sandbox.js +292 -292
- package/test/tool-registry-validation.test.js +218 -218
- package/website/script.js +136 -136
- package/foliko-1.0.75.tgz +0 -0
|
@@ -1,640 +1,640 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ExplorerLoop - 主自主循环,决定并执行操作
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const { Plugin } = require('../../src/core/plugin-base')
|
|
6
|
-
const { logger } = require('../../src/utils/logger')
|
|
7
|
-
const { Agent } = require('../../src/core/agent')
|
|
8
|
-
const { StepExecutor } = require('../../src/capabilities/workflow-engine')
|
|
9
|
-
const { SystemPrompts, ThinkModePrompts, FreeThinkPrompts } = require('./constants')
|
|
10
|
-
|
|
11
|
-
const log = logger.child('Ambient:ExplorerLoop')
|
|
12
|
-
|
|
13
|
-
class ExplorerLoop {
|
|
14
|
-
constructor(goalManager, reflector, framework, config) {
|
|
15
|
-
this._goalManager = goalManager
|
|
16
|
-
this._reflector = reflector
|
|
17
|
-
this._framework = framework
|
|
18
|
-
this._config = config
|
|
19
|
-
this._running = false
|
|
20
|
-
this._timer = null
|
|
21
|
-
this._lastActionTime = 0
|
|
22
|
-
this._tickCount = 0
|
|
23
|
-
this._recentActivities = []
|
|
24
|
-
this._stepExecutor = new StepExecutor(framework)
|
|
25
|
-
this._ambientAgent = null
|
|
26
|
-
this._initAmbientAgent()
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// ============================================================================
|
|
30
|
-
// 生命周期
|
|
31
|
-
// ============================================================================
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 初始化 Ambient 专用子 Agent
|
|
35
|
-
*/
|
|
36
|
-
_initAmbientAgent() {
|
|
37
|
-
const aiPlugin = this._framework.pluginManager?.get('ai')
|
|
38
|
-
const llmConfig = aiPlugin ? aiPlugin.getConfig() : {}
|
|
39
|
-
|
|
40
|
-
this._ambientAgent = new Agent(this._framework, {
|
|
41
|
-
name: 'ambient-worker',
|
|
42
|
-
systemPrompt: SystemPrompts.ambientWorker,
|
|
43
|
-
model: llmConfig.model,
|
|
44
|
-
provider: llmConfig.provider,
|
|
45
|
-
apiKey: llmConfig.apiKey,
|
|
46
|
-
baseURL: llmConfig.baseURL
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
// 注册到主 agent(如果主 agent 已存在)
|
|
50
|
-
if (this._framework._mainAgent) {
|
|
51
|
-
this._registerAmbientWorker(this._framework._mainAgent)
|
|
52
|
-
} else {
|
|
53
|
-
// 等待主 agent 创建
|
|
54
|
-
this._framework.once('agent:created', (agent) => {
|
|
55
|
-
if (this._framework._mainAgent && agent === this._framework._mainAgent) {
|
|
56
|
-
this._registerAmbientWorker(agent)
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* 注册 ambient-worker 到主 agent
|
|
64
|
-
*/
|
|
65
|
-
_registerAmbientWorker(agent) {
|
|
66
|
-
if (!this._ambientAgent) return
|
|
67
|
-
agent.registerSubAgent(
|
|
68
|
-
'ambient-worker',
|
|
69
|
-
this._ambientAgent,
|
|
70
|
-
'后台任务执行器',
|
|
71
|
-
'执行需要 AI 能力的后台任务'
|
|
72
|
-
)
|
|
73
|
-
log.info('ambient-worker subagent registered')
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* 启动探索循环
|
|
78
|
-
*/
|
|
79
|
-
start() {
|
|
80
|
-
if (this._running) return
|
|
81
|
-
this._running = true
|
|
82
|
-
this._scheduleNext()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 停止探索循环
|
|
87
|
-
*/
|
|
88
|
-
stop() {
|
|
89
|
-
this._running = false
|
|
90
|
-
if (this._timer) {
|
|
91
|
-
clearTimeout(this._timer)
|
|
92
|
-
this._timer = null
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* 暂停探索循环
|
|
98
|
-
*/
|
|
99
|
-
pause() {
|
|
100
|
-
this._running = false
|
|
101
|
-
if (this._timer) {
|
|
102
|
-
clearTimeout(this._timer)
|
|
103
|
-
this._timer = null
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* 恢复探索循环
|
|
109
|
-
*/
|
|
110
|
-
resume() {
|
|
111
|
-
if (!this._running) {
|
|
112
|
-
this._running = true
|
|
113
|
-
this._scheduleNext()
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* 检查是否运行中
|
|
119
|
-
*/
|
|
120
|
-
isRunning() {
|
|
121
|
-
return this._running
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 获取状态
|
|
126
|
-
*/
|
|
127
|
-
getStatus() {
|
|
128
|
-
return {
|
|
129
|
-
running: this._running,
|
|
130
|
-
tickCount: this._tickCount,
|
|
131
|
-
lastTick: this._lastActionTime ? new Date(this._lastActionTime) : null,
|
|
132
|
-
recentActivities: this._recentActivities.slice(-10),
|
|
133
|
-
activeGoals: this._goalManager.getActiveGoals().length,
|
|
134
|
-
pendingGoals: this._goalManager.getPendingGoals().length
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// ============================================================================
|
|
139
|
-
// 主循环
|
|
140
|
-
// ============================================================================
|
|
141
|
-
|
|
142
|
-
_scheduleNext() {
|
|
143
|
-
if (!this._running) return
|
|
144
|
-
this._timer = setTimeout(() => this._tick(), this._config.tickInterval)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async _tick() {
|
|
148
|
-
if (!this._running) return
|
|
149
|
-
|
|
150
|
-
// 如果处于嵌套执行上下文中,跳过此tick
|
|
151
|
-
try {
|
|
152
|
-
if (this._framework.getExecutionContext) {
|
|
153
|
-
const ctx = this._framework.getExecutionContext()
|
|
154
|
-
if (ctx && ctx.id) {
|
|
155
|
-
// 跳过此tick,重新调度
|
|
156
|
-
this._scheduleNext()
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
} catch (e) {
|
|
161
|
-
// getExecutionContext可能不存在,继续执行
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
this._tickCount++
|
|
165
|
-
|
|
166
|
-
try {
|
|
167
|
-
await this._processGoals()
|
|
168
|
-
} catch (err) {
|
|
169
|
-
log.error('Tick错误:', err.message)
|
|
170
|
-
this._addActivity('error', { message: err.message })
|
|
171
|
-
}
|
|
172
|
-
this._lastActionTime = Date.now()
|
|
173
|
-
// 调度下一次tick
|
|
174
|
-
this._scheduleNext()
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
async _processGoals() {
|
|
178
|
-
const activeGoals = this._goalManager.getActiveGoals()
|
|
179
|
-
|
|
180
|
-
for (const goal of activeGoals) {
|
|
181
|
-
// 检查冷却时间
|
|
182
|
-
if (Date.now() - this._lastActionTime < this._config.cooldownPeriod) {
|
|
183
|
-
continue
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// 检查目标是否有未处理的事件
|
|
187
|
-
const hasNewEvents = goal.eventsReceived && goal.eventsReceived.length > 0
|
|
188
|
-
|
|
189
|
-
// 检查是否是事件驱动目标
|
|
190
|
-
const isEventDriven = goal.conditions?.events && goal.conditions.events.length > 0
|
|
191
|
-
|
|
192
|
-
// 事件驱动目标:只有在新事件时才执行 actions
|
|
193
|
-
if (isEventDriven && !hasNewEvents) {
|
|
194
|
-
continue
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// 获取下一个操作
|
|
198
|
-
if (goal.actions && goal.actions.length > 0) {
|
|
199
|
-
// 如果有多个 action,按顺序执行所有 action
|
|
200
|
-
if (goal.actions.length > 1) {
|
|
201
|
-
const eventData = hasNewEvents ? goal.eventsReceived[0] : null
|
|
202
|
-
const results = await this._executeAllActions(goal.actions, eventData)
|
|
203
|
-
|
|
204
|
-
// 最后一个结果用于评估
|
|
205
|
-
const lastResult = results.length > 0 ? results[results.length - 1].result : null
|
|
206
|
-
|
|
207
|
-
// 评估结果
|
|
208
|
-
const outcome = this._reflector.evaluateOutcome(goal, lastResult)
|
|
209
|
-
|
|
210
|
-
if (outcome.goalComplete && !isEventDriven) {
|
|
211
|
-
// 非事件驱动目标可以完成
|
|
212
|
-
this._goalManager.completeGoal(goal.id)
|
|
213
|
-
this._addActivity('goal_completed', { goalId: goal.id, attempts: goal.attempts })
|
|
214
|
-
this._notifyUser('目标完成', `目标 "${goal.title}" 已完成(尝试次数:${goal.attempts})`, 'success')
|
|
215
|
-
} else if (!outcome.shouldContinue && !isEventDriven) {
|
|
216
|
-
// 非事件驱动目标可以失败
|
|
217
|
-
this._goalManager.failGoal(goal.id, '达到最大尝试次数')
|
|
218
|
-
this._addActivity('goal_failed', { goalId: goal.id, reason: '达到最大尝试次数' })
|
|
219
|
-
this._notifyUser('目标失败', `目标 "${goal.title}" 失败:达到最大尝试次数`, 'error')
|
|
220
|
-
} else {
|
|
221
|
-
// 事件驱动目标:不论结果如何,都重置并继续等待下一事件
|
|
222
|
-
this._addActivity('actions_completed', { goalId: goal.id, actionCount: results.length })
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// 执行操作后清除已处理的事件,并重置 actions 以处理下一事件
|
|
226
|
-
if (hasNewEvents) {
|
|
227
|
-
this._resetGoalForNextEvent(goal)
|
|
228
|
-
}
|
|
229
|
-
continue
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// 单个 action,使用原有逻辑
|
|
233
|
-
const action = goal.actions[0]
|
|
234
|
-
|
|
235
|
-
// 循环检测(仅用于非事件驱动的操作)
|
|
236
|
-
if (!hasNewEvents && this._reflector.checkLoopDetection(goal, action.id)) {
|
|
237
|
-
this._addActivity('goal_failed', { goalId: goal.id, reason: '检测到循环' })
|
|
238
|
-
// 通知用户目标失败
|
|
239
|
-
this._notifyUser('目标失败', `目标 "${goal.title}" 失败:检测到循环(同一操作连续重复)`, 'error')
|
|
240
|
-
continue
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// 执行操作
|
|
244
|
-
const result = await this._executeAction(action, hasNewEvents ? goal.eventsReceived[0] : null)
|
|
245
|
-
|
|
246
|
-
// 评估结果
|
|
247
|
-
const outcome = this._reflector.evaluateOutcome(goal, result)
|
|
248
|
-
|
|
249
|
-
if (outcome.goalComplete) {
|
|
250
|
-
this._goalManager.completeGoal(goal.id)
|
|
251
|
-
this._addActivity('goal_completed', { goalId: goal.id, attempts: goal.attempts })
|
|
252
|
-
// 通知用户目标完成
|
|
253
|
-
this._notifyUser('目标完成', `目标 "${goal.title}" 已完成(尝试次数:${goal.attempts})`, 'success')
|
|
254
|
-
} else if (!outcome.shouldContinue) {
|
|
255
|
-
this._goalManager.failGoal(goal.id, '达到最大尝试次数')
|
|
256
|
-
this._addActivity('goal_failed', { goalId: goal.id, reason: '达到最大尝试次数' })
|
|
257
|
-
// 通知用户目标失败
|
|
258
|
-
this._notifyUser('目标失败', `目标 "${goal.title}" 失败:达到最大尝试次数`, 'error')
|
|
259
|
-
} else if (outcome.actionTaken) {
|
|
260
|
-
// 从队列中移除已完成的操作
|
|
261
|
-
if (!hasNewEvents) {
|
|
262
|
-
goal.actions.shift()
|
|
263
|
-
}
|
|
264
|
-
this._addActivity('action_executed', { goalId: goal.id, action: action.id, hasEvents: hasNewEvents })
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// 执行操作后清除已处理的事件,并重置 actions 以处理下一事件
|
|
268
|
-
if (hasNewEvents) {
|
|
269
|
-
this._resetGoalForNextEvent(goal)
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// 激活满足条件的待处理目标
|
|
275
|
-
const pendingGoals = this._goalManager.getPendingGoals()
|
|
276
|
-
for (const goal of pendingGoals) {
|
|
277
|
-
// 自动激活没有条件的目标
|
|
278
|
-
// 有条件的目标会在事件触发时被 EventWatcher 自动激活
|
|
279
|
-
if (!goal.conditions || Object.keys(goal.conditions).length === 0) {
|
|
280
|
-
this._goalManager.activateGoal(goal.id)
|
|
281
|
-
this._addActivity('goal_activated', { goalId: goal.id })
|
|
282
|
-
} else {
|
|
283
|
-
// 有条件的目标,立即激活以便开始监听事件
|
|
284
|
-
this._goalManager.activateGoal(goal.id)
|
|
285
|
-
this._addActivity('goal_activated', { goalId: goal.id, reason: '事件监听目标已激活' })
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* 重置目标以处理下一事件
|
|
292
|
-
*/
|
|
293
|
-
_resetGoalForNextEvent(goal) {
|
|
294
|
-
goal.eventsReceived = []
|
|
295
|
-
// 重置 actions 为原始 actions,以便处理下一事件
|
|
296
|
-
if (goal._originalActions) {
|
|
297
|
-
goal.actions = goal._originalActions.slice()
|
|
298
|
-
}
|
|
299
|
-
this._goalManager.updateGoal(goal.id, { eventsReceived: [], actions: goal.actions })
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// ============================================================================
|
|
303
|
-
// Action 执行
|
|
304
|
-
// ============================================================================
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* 执行单个 action
|
|
308
|
-
* @param {Object} action - action 配置
|
|
309
|
-
* @param {Object} eventData - 事件数据
|
|
310
|
-
* @param {Object} context - 执行上下文(包含 stepOutputs 等)
|
|
311
|
-
* @returns {Promise<Object>} 执行结果
|
|
312
|
-
*/
|
|
313
|
-
async _executeAction(action, eventData = null, context = null) {
|
|
314
|
-
if (!this._framework) {
|
|
315
|
-
return { success: false, error: '框架不可用' }
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
try {
|
|
319
|
-
// 如果没有提供 context,创建一个新的
|
|
320
|
-
if (!context) {
|
|
321
|
-
context = {
|
|
322
|
-
input: {},
|
|
323
|
-
variables: {
|
|
324
|
-
_event: eventData?.data || eventData || null,
|
|
325
|
-
_eventRaw: eventData || null,
|
|
326
|
-
_action: action
|
|
327
|
-
},
|
|
328
|
-
lastResult: null
|
|
329
|
-
}
|
|
330
|
-
} else {
|
|
331
|
-
// 更新 context 中的 _action
|
|
332
|
-
context.variables._action = action
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// 将 action 转换为 step 配置
|
|
336
|
-
const step = this._actionToStep(action)
|
|
337
|
-
|
|
338
|
-
// 解析 step.args 中的变量引用(支持 ${actionId.output} 格式)
|
|
339
|
-
if (step.args) {
|
|
340
|
-
step.args = this._resolveActionArgs(step.args, context)
|
|
341
|
-
}
|
|
342
|
-
if (step.input) {
|
|
343
|
-
step.input = this._resolveActionArgs(step.input, context)
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// message 和 think 类型使用专用子 agent 执行
|
|
347
|
-
if (step.type === 'message' || step.type === 'think') {
|
|
348
|
-
return await this._executeWithAmbientAgent(step, context)
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// 其他类型使用 StepExecutor 直接执行
|
|
352
|
-
const result = await this._stepExecutor.executeStep(step, context)
|
|
353
|
-
return result
|
|
354
|
-
} catch (err) {
|
|
355
|
-
return { success: false, error: err.message }
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* 解析 action 参数中的变量引用
|
|
361
|
-
* 支持 ${actionId.output}、${actionId.output.field} 和 ${event.xxx} 格式
|
|
362
|
-
*/
|
|
363
|
-
_resolveActionArgs(args, context) {
|
|
364
|
-
if (!args || typeof args !== 'object') return args
|
|
365
|
-
|
|
366
|
-
const stepOutputs = context.variables._stepOutputs || {}
|
|
367
|
-
const eventData = context.variables._event || {}
|
|
368
|
-
|
|
369
|
-
const resolved = {}
|
|
370
|
-
for (const [key, value] of Object.entries(args)) {
|
|
371
|
-
resolved[key] = this._resolveActionValue(value, stepOutputs, eventData)
|
|
372
|
-
}
|
|
373
|
-
return resolved
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* 解析单个值中的 ${actionId.output} 引用和 ${event.xxx} 引用
|
|
378
|
-
*/
|
|
379
|
-
_resolveActionValue(value, stepOutputs, eventData) {
|
|
380
|
-
if (typeof value === 'string') {
|
|
381
|
-
// 处理 ${actionId.output} 和 ${actionId.output.field} 格式
|
|
382
|
-
const result = value.replace(/\$\{([^}]+)\}/g, (match, path) => {
|
|
383
|
-
if (path === 'output' || path === 'result') {
|
|
384
|
-
// {{result}} 引用上一步结果
|
|
385
|
-
return stepOutputs._lastResult ?? match
|
|
386
|
-
}
|
|
387
|
-
if (path.startsWith('event.')) {
|
|
388
|
-
// ${event.xxx} 引用事件数据
|
|
389
|
-
let eventPath = path.replace(/^event\./, '')
|
|
390
|
-
// 优先在 eventData.data 中查找(因为事件数据通常存储在 data 字段)
|
|
391
|
-
if (eventData?.data) {
|
|
392
|
-
let val = this._getNestedValue(eventData.data, eventPath)
|
|
393
|
-
if (val !== undefined) return val
|
|
394
|
-
}
|
|
395
|
-
// 再尝试直接在 eventData 中查找
|
|
396
|
-
let val = this._getNestedValue(eventData, eventPath)
|
|
397
|
-
if (val !== undefined) return val
|
|
398
|
-
// 兼容:如果 path 是 event.data.xxx 但 eventData 没有 data,尝试跳过一层
|
|
399
|
-
if (eventPath.startsWith('data.')) {
|
|
400
|
-
eventPath = eventPath.replace(/^data\./, '')
|
|
401
|
-
val = this._getNestedValue(eventData, eventPath)
|
|
402
|
-
if (val !== undefined) return val
|
|
403
|
-
}
|
|
404
|
-
// 特殊回退:如果查找 body 但找不到,尝试 text(邮件数据中用 text)
|
|
405
|
-
if (eventPath === 'body' || eventPath === 'text') {
|
|
406
|
-
const textVal = this._getNestedValue(eventData?.data, 'text')
|
|
407
|
-
if (textVal !== undefined) return textVal
|
|
408
|
-
}
|
|
409
|
-
return match
|
|
410
|
-
}
|
|
411
|
-
if (path === 'event') {
|
|
412
|
-
// ${event} 引用整个事件数据对象
|
|
413
|
-
return eventData ?? match
|
|
414
|
-
}
|
|
415
|
-
if (path.includes('.')) {
|
|
416
|
-
// ${actionId.output.field} 格式
|
|
417
|
-
const [actionId, ...fieldParts] = path.split('.')
|
|
418
|
-
if (actionId === 'result' || actionId === 'output') {
|
|
419
|
-
// {{result.field}} 格式
|
|
420
|
-
const val = this._getNestedValue(stepOutputs._lastResult, fieldParts.join('.'))
|
|
421
|
-
return val !== undefined ? val : match
|
|
422
|
-
}
|
|
423
|
-
const actionOutput = stepOutputs[actionId]
|
|
424
|
-
if (actionOutput !== undefined) {
|
|
425
|
-
const val = this._getNestedValue(actionOutput, fieldParts.join('.'))
|
|
426
|
-
return val !== undefined ? val : match
|
|
427
|
-
}
|
|
428
|
-
} else {
|
|
429
|
-
// ${actionId.output} 格式
|
|
430
|
-
const actionOutput = stepOutputs[path]
|
|
431
|
-
if (actionOutput !== undefined) return actionOutput
|
|
432
|
-
}
|
|
433
|
-
return match
|
|
434
|
-
})
|
|
435
|
-
return result
|
|
436
|
-
} else if (Array.isArray(value)) {
|
|
437
|
-
return value.map(v => this._resolveActionValue(v, stepOutputs, eventData))
|
|
438
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
439
|
-
const resolved = {}
|
|
440
|
-
for (const [k, v] of Object.entries(value)) {
|
|
441
|
-
resolved[k] = this._resolveActionValue(v, stepOutputs, eventData)
|
|
442
|
-
}
|
|
443
|
-
return resolved
|
|
444
|
-
}
|
|
445
|
-
return value
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
_getNestedValue(obj, path) {
|
|
449
|
-
if (!obj) return undefined
|
|
450
|
-
const parts = path.split('.')
|
|
451
|
-
let current = obj
|
|
452
|
-
for (const part of parts) {
|
|
453
|
-
if (current === null || current === undefined) return undefined
|
|
454
|
-
current = current[part]
|
|
455
|
-
}
|
|
456
|
-
return current
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* 按顺序执行所有 actions
|
|
461
|
-
* @param {Array} actions - action 数组
|
|
462
|
-
* @param {Object} eventData - 事件数据
|
|
463
|
-
* @returns {Promise<Array>} 所有执行结果
|
|
464
|
-
*/
|
|
465
|
-
async _executeAllActions(actions, eventData = null) {
|
|
466
|
-
const results = []
|
|
467
|
-
const stepOutputs = {}
|
|
468
|
-
|
|
469
|
-
const context = {
|
|
470
|
-
input: {},
|
|
471
|
-
variables: {
|
|
472
|
-
_event: eventData?.data || eventData || null,
|
|
473
|
-
_eventRaw: eventData || null,
|
|
474
|
-
_stepOutputs: stepOutputs
|
|
475
|
-
},
|
|
476
|
-
lastResult: null
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
for (const action of actions) {
|
|
480
|
-
// 执行前更新 stepOutputs
|
|
481
|
-
context.variables._stepOutputs = stepOutputs
|
|
482
|
-
|
|
483
|
-
const result = await this._executeAction(action, eventData, context)
|
|
484
|
-
|
|
485
|
-
// 保存结果:使用 action id 作为键
|
|
486
|
-
if (action.id) {
|
|
487
|
-
stepOutputs[action.id] = result
|
|
488
|
-
}
|
|
489
|
-
// 同时保存为 _lastResult 供 {{result}} 引用
|
|
490
|
-
stepOutputs._lastResult = result
|
|
491
|
-
context.lastResult = result
|
|
492
|
-
|
|
493
|
-
results.push({ actionId: action.id, result })
|
|
494
|
-
|
|
495
|
-
// 如果执行失败,停止执行
|
|
496
|
-
if (result && result.success === false) {
|
|
497
|
-
break
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
return results
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* 使用 ambient 子 agent 执行 AI 相关操作
|
|
506
|
-
*/
|
|
507
|
-
async _executeWithAmbientAgent(step, context) {
|
|
508
|
-
if (!this._ambientAgent) {
|
|
509
|
-
return { success: false, error: 'Ambient agent 未初始化' }
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (step.type === 'message') {
|
|
513
|
-
let content = step.content || ''
|
|
514
|
-
content = this._stepExecutor._resolveValue(content, context)
|
|
515
|
-
|
|
516
|
-
if (context.variables._event) {
|
|
517
|
-
content = `${content}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
try {
|
|
521
|
-
const result = await this._ambientAgent.pushMessage(content)
|
|
522
|
-
return { success: true, result }
|
|
523
|
-
} catch (err) {
|
|
524
|
-
return { success: false, error: err.message }
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (step.type === 'think') {
|
|
529
|
-
const thinkPlugin = this._framework.pluginManager?.get('think')
|
|
530
|
-
if (!thinkPlugin) {
|
|
531
|
-
return { success: false, error: '思考插件不可用' }
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
let topic = step.topic || 'Ambient代理反思'
|
|
535
|
-
topic = this._stepExecutor._resolveValue(topic, context)
|
|
536
|
-
|
|
537
|
-
if (context.variables._event) {
|
|
538
|
-
topic = `${topic}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
try {
|
|
542
|
-
const result = await thinkPlugin._triggerThinking({
|
|
543
|
-
topic,
|
|
544
|
-
mode: step.mode || 'reflect',
|
|
545
|
-
depth: step.depth || 2
|
|
546
|
-
})
|
|
547
|
-
return result
|
|
548
|
-
} catch (err) {
|
|
549
|
-
return { success: false, error: err.message }
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
return { success: false, error: `未知 AI 操作类型: ${step.type}` }
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/**
|
|
557
|
-
* 将 action 转换为 step 配置
|
|
558
|
-
*/
|
|
559
|
-
_actionToStep(action) {
|
|
560
|
-
const step = {
|
|
561
|
-
id: action.id || `action_${Date.now()}`,
|
|
562
|
-
type: action.type,
|
|
563
|
-
name: action.name || action.id
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
switch (action.type) {
|
|
567
|
-
case 'tool':
|
|
568
|
-
step.tool = action.name
|
|
569
|
-
step.args = action.args || {}
|
|
570
|
-
break
|
|
571
|
-
case 'message':
|
|
572
|
-
step.content = action.content || ''
|
|
573
|
-
break
|
|
574
|
-
case 'think':
|
|
575
|
-
step.topic = action.topic || 'Ambient代理反思'
|
|
576
|
-
step.mode = action.mode || 'reflect'
|
|
577
|
-
step.depth = action.depth || 2
|
|
578
|
-
break
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return step
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// ============================================================================
|
|
585
|
-
// 辅助方法
|
|
586
|
-
// ============================================================================
|
|
587
|
-
|
|
588
|
-
_getActiveAgent() {
|
|
589
|
-
if (this._framework._mainAgent) {
|
|
590
|
-
return this._framework._mainAgent
|
|
591
|
-
}
|
|
592
|
-
const agents = this._framework._agents || []
|
|
593
|
-
return agents.length > 0 ? agents[agents.length - 1] : null
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
_addActivity(type, data) {
|
|
597
|
-
this._recentActivities.push({
|
|
598
|
-
type,
|
|
599
|
-
data,
|
|
600
|
-
timestamp: new Date()
|
|
601
|
-
})
|
|
602
|
-
// 只保留最近50条活动记录
|
|
603
|
-
if (this._recentActivities.length > 50) {
|
|
604
|
-
this._recentActivities = this._recentActivities.slice(-50)
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
/**
|
|
609
|
-
* 通知用户(通过事件发送给消息插件)
|
|
610
|
-
*/
|
|
611
|
-
_notifyUser(title, message, level = 'info') {
|
|
612
|
-
if (!this._framework) return
|
|
613
|
-
|
|
614
|
-
// 获取目标相关的sessionId(如果有)
|
|
615
|
-
let sessionId = null
|
|
616
|
-
const sessionPlugin = this._framework.pluginManager.get('session')
|
|
617
|
-
if (sessionPlugin) {
|
|
618
|
-
const sessions = sessionPlugin.listSessions()
|
|
619
|
-
if (sessions.length > 0) {
|
|
620
|
-
// 取最新的会话
|
|
621
|
-
sessions.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
622
|
-
sessionId = sessions[0].id
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// 发送统一的通知事件
|
|
627
|
-
this._framework.emit('notification', {
|
|
628
|
-
title,
|
|
629
|
-
message,
|
|
630
|
-
source: 'ambient',
|
|
631
|
-
level, // 'info' | 'warning' | 'success' | 'error'
|
|
632
|
-
sessionId,
|
|
633
|
-
timestamp: new Date()
|
|
634
|
-
})
|
|
635
|
-
|
|
636
|
-
log.info(`${title}: ${message}`)
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
module.exports = { ExplorerLoop }
|
|
1
|
+
/**
|
|
2
|
+
* ExplorerLoop - 主自主循环,决定并执行操作
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { Plugin } = require('../../src/core/plugin-base')
|
|
6
|
+
const { logger } = require('../../src/utils/logger')
|
|
7
|
+
const { Agent } = require('../../src/core/agent')
|
|
8
|
+
const { StepExecutor } = require('../../src/capabilities/workflow-engine')
|
|
9
|
+
const { SystemPrompts, ThinkModePrompts, FreeThinkPrompts } = require('./constants')
|
|
10
|
+
|
|
11
|
+
const log = logger.child('Ambient:ExplorerLoop')
|
|
12
|
+
|
|
13
|
+
class ExplorerLoop {
|
|
14
|
+
constructor(goalManager, reflector, framework, config) {
|
|
15
|
+
this._goalManager = goalManager
|
|
16
|
+
this._reflector = reflector
|
|
17
|
+
this._framework = framework
|
|
18
|
+
this._config = config
|
|
19
|
+
this._running = false
|
|
20
|
+
this._timer = null
|
|
21
|
+
this._lastActionTime = 0
|
|
22
|
+
this._tickCount = 0
|
|
23
|
+
this._recentActivities = []
|
|
24
|
+
this._stepExecutor = new StepExecutor(framework)
|
|
25
|
+
this._ambientAgent = null
|
|
26
|
+
this._initAmbientAgent()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// 生命周期
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 初始化 Ambient 专用子 Agent
|
|
35
|
+
*/
|
|
36
|
+
_initAmbientAgent() {
|
|
37
|
+
const aiPlugin = this._framework.pluginManager?.get('ai')
|
|
38
|
+
const llmConfig = aiPlugin ? aiPlugin.getConfig() : {}
|
|
39
|
+
|
|
40
|
+
this._ambientAgent = new Agent(this._framework, {
|
|
41
|
+
name: 'ambient-worker',
|
|
42
|
+
systemPrompt: SystemPrompts.ambientWorker,
|
|
43
|
+
model: llmConfig.model,
|
|
44
|
+
provider: llmConfig.provider,
|
|
45
|
+
apiKey: llmConfig.apiKey,
|
|
46
|
+
baseURL: llmConfig.baseURL
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// 注册到主 agent(如果主 agent 已存在)
|
|
50
|
+
if (this._framework._mainAgent) {
|
|
51
|
+
this._registerAmbientWorker(this._framework._mainAgent)
|
|
52
|
+
} else {
|
|
53
|
+
// 等待主 agent 创建
|
|
54
|
+
this._framework.once('agent:created', (agent) => {
|
|
55
|
+
if (this._framework._mainAgent && agent === this._framework._mainAgent) {
|
|
56
|
+
this._registerAmbientWorker(agent)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 注册 ambient-worker 到主 agent
|
|
64
|
+
*/
|
|
65
|
+
_registerAmbientWorker(agent) {
|
|
66
|
+
if (!this._ambientAgent) return
|
|
67
|
+
agent.registerSubAgent(
|
|
68
|
+
'ambient-worker',
|
|
69
|
+
this._ambientAgent,
|
|
70
|
+
'后台任务执行器',
|
|
71
|
+
'执行需要 AI 能力的后台任务'
|
|
72
|
+
)
|
|
73
|
+
log.info('ambient-worker subagent registered')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 启动探索循环
|
|
78
|
+
*/
|
|
79
|
+
start() {
|
|
80
|
+
if (this._running) return
|
|
81
|
+
this._running = true
|
|
82
|
+
this._scheduleNext()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 停止探索循环
|
|
87
|
+
*/
|
|
88
|
+
stop() {
|
|
89
|
+
this._running = false
|
|
90
|
+
if (this._timer) {
|
|
91
|
+
clearTimeout(this._timer)
|
|
92
|
+
this._timer = null
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 暂停探索循环
|
|
98
|
+
*/
|
|
99
|
+
pause() {
|
|
100
|
+
this._running = false
|
|
101
|
+
if (this._timer) {
|
|
102
|
+
clearTimeout(this._timer)
|
|
103
|
+
this._timer = null
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 恢复探索循环
|
|
109
|
+
*/
|
|
110
|
+
resume() {
|
|
111
|
+
if (!this._running) {
|
|
112
|
+
this._running = true
|
|
113
|
+
this._scheduleNext()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 检查是否运行中
|
|
119
|
+
*/
|
|
120
|
+
isRunning() {
|
|
121
|
+
return this._running
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 获取状态
|
|
126
|
+
*/
|
|
127
|
+
getStatus() {
|
|
128
|
+
return {
|
|
129
|
+
running: this._running,
|
|
130
|
+
tickCount: this._tickCount,
|
|
131
|
+
lastTick: this._lastActionTime ? new Date(this._lastActionTime) : null,
|
|
132
|
+
recentActivities: this._recentActivities.slice(-10),
|
|
133
|
+
activeGoals: this._goalManager.getActiveGoals().length,
|
|
134
|
+
pendingGoals: this._goalManager.getPendingGoals().length
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// 主循环
|
|
140
|
+
// ============================================================================
|
|
141
|
+
|
|
142
|
+
_scheduleNext() {
|
|
143
|
+
if (!this._running) return
|
|
144
|
+
this._timer = setTimeout(() => this._tick(), this._config.tickInterval)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async _tick() {
|
|
148
|
+
if (!this._running) return
|
|
149
|
+
|
|
150
|
+
// 如果处于嵌套执行上下文中,跳过此tick
|
|
151
|
+
try {
|
|
152
|
+
if (this._framework.getExecutionContext) {
|
|
153
|
+
const ctx = this._framework.getExecutionContext()
|
|
154
|
+
if (ctx && ctx.id) {
|
|
155
|
+
// 跳过此tick,重新调度
|
|
156
|
+
this._scheduleNext()
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch (e) {
|
|
161
|
+
// getExecutionContext可能不存在,继续执行
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this._tickCount++
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
await this._processGoals()
|
|
168
|
+
} catch (err) {
|
|
169
|
+
log.error('Tick错误:', err.message)
|
|
170
|
+
this._addActivity('error', { message: err.message })
|
|
171
|
+
}
|
|
172
|
+
this._lastActionTime = Date.now()
|
|
173
|
+
// 调度下一次tick
|
|
174
|
+
this._scheduleNext()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async _processGoals() {
|
|
178
|
+
const activeGoals = this._goalManager.getActiveGoals()
|
|
179
|
+
|
|
180
|
+
for (const goal of activeGoals) {
|
|
181
|
+
// 检查冷却时间
|
|
182
|
+
if (Date.now() - this._lastActionTime < this._config.cooldownPeriod) {
|
|
183
|
+
continue
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 检查目标是否有未处理的事件
|
|
187
|
+
const hasNewEvents = goal.eventsReceived && goal.eventsReceived.length > 0
|
|
188
|
+
|
|
189
|
+
// 检查是否是事件驱动目标
|
|
190
|
+
const isEventDriven = goal.conditions?.events && goal.conditions.events.length > 0
|
|
191
|
+
|
|
192
|
+
// 事件驱动目标:只有在新事件时才执行 actions
|
|
193
|
+
if (isEventDriven && !hasNewEvents) {
|
|
194
|
+
continue
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 获取下一个操作
|
|
198
|
+
if (goal.actions && goal.actions.length > 0) {
|
|
199
|
+
// 如果有多个 action,按顺序执行所有 action
|
|
200
|
+
if (goal.actions.length > 1) {
|
|
201
|
+
const eventData = hasNewEvents ? goal.eventsReceived[0] : null
|
|
202
|
+
const results = await this._executeAllActions(goal.actions, eventData)
|
|
203
|
+
|
|
204
|
+
// 最后一个结果用于评估
|
|
205
|
+
const lastResult = results.length > 0 ? results[results.length - 1].result : null
|
|
206
|
+
|
|
207
|
+
// 评估结果
|
|
208
|
+
const outcome = this._reflector.evaluateOutcome(goal, lastResult)
|
|
209
|
+
|
|
210
|
+
if (outcome.goalComplete && !isEventDriven) {
|
|
211
|
+
// 非事件驱动目标可以完成
|
|
212
|
+
this._goalManager.completeGoal(goal.id)
|
|
213
|
+
this._addActivity('goal_completed', { goalId: goal.id, attempts: goal.attempts })
|
|
214
|
+
this._notifyUser('目标完成', `目标 "${goal.title}" 已完成(尝试次数:${goal.attempts})`, 'success')
|
|
215
|
+
} else if (!outcome.shouldContinue && !isEventDriven) {
|
|
216
|
+
// 非事件驱动目标可以失败
|
|
217
|
+
this._goalManager.failGoal(goal.id, '达到最大尝试次数')
|
|
218
|
+
this._addActivity('goal_failed', { goalId: goal.id, reason: '达到最大尝试次数' })
|
|
219
|
+
this._notifyUser('目标失败', `目标 "${goal.title}" 失败:达到最大尝试次数`, 'error')
|
|
220
|
+
} else {
|
|
221
|
+
// 事件驱动目标:不论结果如何,都重置并继续等待下一事件
|
|
222
|
+
this._addActivity('actions_completed', { goalId: goal.id, actionCount: results.length })
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// 执行操作后清除已处理的事件,并重置 actions 以处理下一事件
|
|
226
|
+
if (hasNewEvents) {
|
|
227
|
+
this._resetGoalForNextEvent(goal)
|
|
228
|
+
}
|
|
229
|
+
continue
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// 单个 action,使用原有逻辑
|
|
233
|
+
const action = goal.actions[0]
|
|
234
|
+
|
|
235
|
+
// 循环检测(仅用于非事件驱动的操作)
|
|
236
|
+
if (!hasNewEvents && this._reflector.checkLoopDetection(goal, action.id)) {
|
|
237
|
+
this._addActivity('goal_failed', { goalId: goal.id, reason: '检测到循环' })
|
|
238
|
+
// 通知用户目标失败
|
|
239
|
+
this._notifyUser('目标失败', `目标 "${goal.title}" 失败:检测到循环(同一操作连续重复)`, 'error')
|
|
240
|
+
continue
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 执行操作
|
|
244
|
+
const result = await this._executeAction(action, hasNewEvents ? goal.eventsReceived[0] : null)
|
|
245
|
+
|
|
246
|
+
// 评估结果
|
|
247
|
+
const outcome = this._reflector.evaluateOutcome(goal, result)
|
|
248
|
+
|
|
249
|
+
if (outcome.goalComplete) {
|
|
250
|
+
this._goalManager.completeGoal(goal.id)
|
|
251
|
+
this._addActivity('goal_completed', { goalId: goal.id, attempts: goal.attempts })
|
|
252
|
+
// 通知用户目标完成
|
|
253
|
+
this._notifyUser('目标完成', `目标 "${goal.title}" 已完成(尝试次数:${goal.attempts})`, 'success')
|
|
254
|
+
} else if (!outcome.shouldContinue) {
|
|
255
|
+
this._goalManager.failGoal(goal.id, '达到最大尝试次数')
|
|
256
|
+
this._addActivity('goal_failed', { goalId: goal.id, reason: '达到最大尝试次数' })
|
|
257
|
+
// 通知用户目标失败
|
|
258
|
+
this._notifyUser('目标失败', `目标 "${goal.title}" 失败:达到最大尝试次数`, 'error')
|
|
259
|
+
} else if (outcome.actionTaken) {
|
|
260
|
+
// 从队列中移除已完成的操作
|
|
261
|
+
if (!hasNewEvents) {
|
|
262
|
+
goal.actions.shift()
|
|
263
|
+
}
|
|
264
|
+
this._addActivity('action_executed', { goalId: goal.id, action: action.id, hasEvents: hasNewEvents })
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 执行操作后清除已处理的事件,并重置 actions 以处理下一事件
|
|
268
|
+
if (hasNewEvents) {
|
|
269
|
+
this._resetGoalForNextEvent(goal)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 激活满足条件的待处理目标
|
|
275
|
+
const pendingGoals = this._goalManager.getPendingGoals()
|
|
276
|
+
for (const goal of pendingGoals) {
|
|
277
|
+
// 自动激活没有条件的目标
|
|
278
|
+
// 有条件的目标会在事件触发时被 EventWatcher 自动激活
|
|
279
|
+
if (!goal.conditions || Object.keys(goal.conditions).length === 0) {
|
|
280
|
+
this._goalManager.activateGoal(goal.id)
|
|
281
|
+
this._addActivity('goal_activated', { goalId: goal.id })
|
|
282
|
+
} else {
|
|
283
|
+
// 有条件的目标,立即激活以便开始监听事件
|
|
284
|
+
this._goalManager.activateGoal(goal.id)
|
|
285
|
+
this._addActivity('goal_activated', { goalId: goal.id, reason: '事件监听目标已激活' })
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 重置目标以处理下一事件
|
|
292
|
+
*/
|
|
293
|
+
_resetGoalForNextEvent(goal) {
|
|
294
|
+
goal.eventsReceived = []
|
|
295
|
+
// 重置 actions 为原始 actions,以便处理下一事件
|
|
296
|
+
if (goal._originalActions) {
|
|
297
|
+
goal.actions = goal._originalActions.slice()
|
|
298
|
+
}
|
|
299
|
+
this._goalManager.updateGoal(goal.id, { eventsReceived: [], actions: goal.actions })
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ============================================================================
|
|
303
|
+
// Action 执行
|
|
304
|
+
// ============================================================================
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 执行单个 action
|
|
308
|
+
* @param {Object} action - action 配置
|
|
309
|
+
* @param {Object} eventData - 事件数据
|
|
310
|
+
* @param {Object} context - 执行上下文(包含 stepOutputs 等)
|
|
311
|
+
* @returns {Promise<Object>} 执行结果
|
|
312
|
+
*/
|
|
313
|
+
async _executeAction(action, eventData = null, context = null) {
|
|
314
|
+
if (!this._framework) {
|
|
315
|
+
return { success: false, error: '框架不可用' }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
// 如果没有提供 context,创建一个新的
|
|
320
|
+
if (!context) {
|
|
321
|
+
context = {
|
|
322
|
+
input: {},
|
|
323
|
+
variables: {
|
|
324
|
+
_event: eventData?.data || eventData || null,
|
|
325
|
+
_eventRaw: eventData || null,
|
|
326
|
+
_action: action
|
|
327
|
+
},
|
|
328
|
+
lastResult: null
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
// 更新 context 中的 _action
|
|
332
|
+
context.variables._action = action
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 将 action 转换为 step 配置
|
|
336
|
+
const step = this._actionToStep(action)
|
|
337
|
+
|
|
338
|
+
// 解析 step.args 中的变量引用(支持 ${actionId.output} 格式)
|
|
339
|
+
if (step.args) {
|
|
340
|
+
step.args = this._resolveActionArgs(step.args, context)
|
|
341
|
+
}
|
|
342
|
+
if (step.input) {
|
|
343
|
+
step.input = this._resolveActionArgs(step.input, context)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// message 和 think 类型使用专用子 agent 执行
|
|
347
|
+
if (step.type === 'message' || step.type === 'think') {
|
|
348
|
+
return await this._executeWithAmbientAgent(step, context)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// 其他类型使用 StepExecutor 直接执行
|
|
352
|
+
const result = await this._stepExecutor.executeStep(step, context)
|
|
353
|
+
return result
|
|
354
|
+
} catch (err) {
|
|
355
|
+
return { success: false, error: err.message }
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* 解析 action 参数中的变量引用
|
|
361
|
+
* 支持 ${actionId.output}、${actionId.output.field} 和 ${event.xxx} 格式
|
|
362
|
+
*/
|
|
363
|
+
_resolveActionArgs(args, context) {
|
|
364
|
+
if (!args || typeof args !== 'object') return args
|
|
365
|
+
|
|
366
|
+
const stepOutputs = context.variables._stepOutputs || {}
|
|
367
|
+
const eventData = context.variables._event || {}
|
|
368
|
+
|
|
369
|
+
const resolved = {}
|
|
370
|
+
for (const [key, value] of Object.entries(args)) {
|
|
371
|
+
resolved[key] = this._resolveActionValue(value, stepOutputs, eventData)
|
|
372
|
+
}
|
|
373
|
+
return resolved
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* 解析单个值中的 ${actionId.output} 引用和 ${event.xxx} 引用
|
|
378
|
+
*/
|
|
379
|
+
_resolveActionValue(value, stepOutputs, eventData) {
|
|
380
|
+
if (typeof value === 'string') {
|
|
381
|
+
// 处理 ${actionId.output} 和 ${actionId.output.field} 格式
|
|
382
|
+
const result = value.replace(/\$\{([^}]+)\}/g, (match, path) => {
|
|
383
|
+
if (path === 'output' || path === 'result') {
|
|
384
|
+
// {{result}} 引用上一步结果
|
|
385
|
+
return stepOutputs._lastResult ?? match
|
|
386
|
+
}
|
|
387
|
+
if (path.startsWith('event.')) {
|
|
388
|
+
// ${event.xxx} 引用事件数据
|
|
389
|
+
let eventPath = path.replace(/^event\./, '')
|
|
390
|
+
// 优先在 eventData.data 中查找(因为事件数据通常存储在 data 字段)
|
|
391
|
+
if (eventData?.data) {
|
|
392
|
+
let val = this._getNestedValue(eventData.data, eventPath)
|
|
393
|
+
if (val !== undefined) return val
|
|
394
|
+
}
|
|
395
|
+
// 再尝试直接在 eventData 中查找
|
|
396
|
+
let val = this._getNestedValue(eventData, eventPath)
|
|
397
|
+
if (val !== undefined) return val
|
|
398
|
+
// 兼容:如果 path 是 event.data.xxx 但 eventData 没有 data,尝试跳过一层
|
|
399
|
+
if (eventPath.startsWith('data.')) {
|
|
400
|
+
eventPath = eventPath.replace(/^data\./, '')
|
|
401
|
+
val = this._getNestedValue(eventData, eventPath)
|
|
402
|
+
if (val !== undefined) return val
|
|
403
|
+
}
|
|
404
|
+
// 特殊回退:如果查找 body 但找不到,尝试 text(邮件数据中用 text)
|
|
405
|
+
if (eventPath === 'body' || eventPath === 'text') {
|
|
406
|
+
const textVal = this._getNestedValue(eventData?.data, 'text')
|
|
407
|
+
if (textVal !== undefined) return textVal
|
|
408
|
+
}
|
|
409
|
+
return match
|
|
410
|
+
}
|
|
411
|
+
if (path === 'event') {
|
|
412
|
+
// ${event} 引用整个事件数据对象
|
|
413
|
+
return eventData ?? match
|
|
414
|
+
}
|
|
415
|
+
if (path.includes('.')) {
|
|
416
|
+
// ${actionId.output.field} 格式
|
|
417
|
+
const [actionId, ...fieldParts] = path.split('.')
|
|
418
|
+
if (actionId === 'result' || actionId === 'output') {
|
|
419
|
+
// {{result.field}} 格式
|
|
420
|
+
const val = this._getNestedValue(stepOutputs._lastResult, fieldParts.join('.'))
|
|
421
|
+
return val !== undefined ? val : match
|
|
422
|
+
}
|
|
423
|
+
const actionOutput = stepOutputs[actionId]
|
|
424
|
+
if (actionOutput !== undefined) {
|
|
425
|
+
const val = this._getNestedValue(actionOutput, fieldParts.join('.'))
|
|
426
|
+
return val !== undefined ? val : match
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
// ${actionId.output} 格式
|
|
430
|
+
const actionOutput = stepOutputs[path]
|
|
431
|
+
if (actionOutput !== undefined) return actionOutput
|
|
432
|
+
}
|
|
433
|
+
return match
|
|
434
|
+
})
|
|
435
|
+
return result
|
|
436
|
+
} else if (Array.isArray(value)) {
|
|
437
|
+
return value.map(v => this._resolveActionValue(v, stepOutputs, eventData))
|
|
438
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
439
|
+
const resolved = {}
|
|
440
|
+
for (const [k, v] of Object.entries(value)) {
|
|
441
|
+
resolved[k] = this._resolveActionValue(v, stepOutputs, eventData)
|
|
442
|
+
}
|
|
443
|
+
return resolved
|
|
444
|
+
}
|
|
445
|
+
return value
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
_getNestedValue(obj, path) {
|
|
449
|
+
if (!obj) return undefined
|
|
450
|
+
const parts = path.split('.')
|
|
451
|
+
let current = obj
|
|
452
|
+
for (const part of parts) {
|
|
453
|
+
if (current === null || current === undefined) return undefined
|
|
454
|
+
current = current[part]
|
|
455
|
+
}
|
|
456
|
+
return current
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* 按顺序执行所有 actions
|
|
461
|
+
* @param {Array} actions - action 数组
|
|
462
|
+
* @param {Object} eventData - 事件数据
|
|
463
|
+
* @returns {Promise<Array>} 所有执行结果
|
|
464
|
+
*/
|
|
465
|
+
async _executeAllActions(actions, eventData = null) {
|
|
466
|
+
const results = []
|
|
467
|
+
const stepOutputs = {}
|
|
468
|
+
|
|
469
|
+
const context = {
|
|
470
|
+
input: {},
|
|
471
|
+
variables: {
|
|
472
|
+
_event: eventData?.data || eventData || null,
|
|
473
|
+
_eventRaw: eventData || null,
|
|
474
|
+
_stepOutputs: stepOutputs
|
|
475
|
+
},
|
|
476
|
+
lastResult: null
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
for (const action of actions) {
|
|
480
|
+
// 执行前更新 stepOutputs
|
|
481
|
+
context.variables._stepOutputs = stepOutputs
|
|
482
|
+
|
|
483
|
+
const result = await this._executeAction(action, eventData, context)
|
|
484
|
+
|
|
485
|
+
// 保存结果:使用 action id 作为键
|
|
486
|
+
if (action.id) {
|
|
487
|
+
stepOutputs[action.id] = result
|
|
488
|
+
}
|
|
489
|
+
// 同时保存为 _lastResult 供 {{result}} 引用
|
|
490
|
+
stepOutputs._lastResult = result
|
|
491
|
+
context.lastResult = result
|
|
492
|
+
|
|
493
|
+
results.push({ actionId: action.id, result })
|
|
494
|
+
|
|
495
|
+
// 如果执行失败,停止执行
|
|
496
|
+
if (result && result.success === false) {
|
|
497
|
+
break
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return results
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* 使用 ambient 子 agent 执行 AI 相关操作
|
|
506
|
+
*/
|
|
507
|
+
async _executeWithAmbientAgent(step, context) {
|
|
508
|
+
if (!this._ambientAgent) {
|
|
509
|
+
return { success: false, error: 'Ambient agent 未初始化' }
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (step.type === 'message') {
|
|
513
|
+
let content = step.content || ''
|
|
514
|
+
content = this._stepExecutor._resolveValue(content, context)
|
|
515
|
+
|
|
516
|
+
if (context.variables._event) {
|
|
517
|
+
content = `${content}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
try {
|
|
521
|
+
const result = await this._ambientAgent.pushMessage(content)
|
|
522
|
+
return { success: true, result }
|
|
523
|
+
} catch (err) {
|
|
524
|
+
return { success: false, error: err.message }
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (step.type === 'think') {
|
|
529
|
+
const thinkPlugin = this._framework.pluginManager?.get('think')
|
|
530
|
+
if (!thinkPlugin) {
|
|
531
|
+
return { success: false, error: '思考插件不可用' }
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
let topic = step.topic || 'Ambient代理反思'
|
|
535
|
+
topic = this._stepExecutor._resolveValue(topic, context)
|
|
536
|
+
|
|
537
|
+
if (context.variables._event) {
|
|
538
|
+
topic = `${topic}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
const result = await thinkPlugin._triggerThinking({
|
|
543
|
+
topic,
|
|
544
|
+
mode: step.mode || 'reflect',
|
|
545
|
+
depth: step.depth || 2
|
|
546
|
+
})
|
|
547
|
+
return result
|
|
548
|
+
} catch (err) {
|
|
549
|
+
return { success: false, error: err.message }
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return { success: false, error: `未知 AI 操作类型: ${step.type}` }
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* 将 action 转换为 step 配置
|
|
558
|
+
*/
|
|
559
|
+
_actionToStep(action) {
|
|
560
|
+
const step = {
|
|
561
|
+
id: action.id || `action_${Date.now()}`,
|
|
562
|
+
type: action.type,
|
|
563
|
+
name: action.name || action.id
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
switch (action.type) {
|
|
567
|
+
case 'tool':
|
|
568
|
+
step.tool = action.name
|
|
569
|
+
step.args = action.args || {}
|
|
570
|
+
break
|
|
571
|
+
case 'message':
|
|
572
|
+
step.content = action.content || ''
|
|
573
|
+
break
|
|
574
|
+
case 'think':
|
|
575
|
+
step.topic = action.topic || 'Ambient代理反思'
|
|
576
|
+
step.mode = action.mode || 'reflect'
|
|
577
|
+
step.depth = action.depth || 2
|
|
578
|
+
break
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return step
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ============================================================================
|
|
585
|
+
// 辅助方法
|
|
586
|
+
// ============================================================================
|
|
587
|
+
|
|
588
|
+
_getActiveAgent() {
|
|
589
|
+
if (this._framework._mainAgent) {
|
|
590
|
+
return this._framework._mainAgent
|
|
591
|
+
}
|
|
592
|
+
const agents = this._framework._agents || []
|
|
593
|
+
return agents.length > 0 ? agents[agents.length - 1] : null
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
_addActivity(type, data) {
|
|
597
|
+
this._recentActivities.push({
|
|
598
|
+
type,
|
|
599
|
+
data,
|
|
600
|
+
timestamp: new Date()
|
|
601
|
+
})
|
|
602
|
+
// 只保留最近50条活动记录
|
|
603
|
+
if (this._recentActivities.length > 50) {
|
|
604
|
+
this._recentActivities = this._recentActivities.slice(-50)
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* 通知用户(通过事件发送给消息插件)
|
|
610
|
+
*/
|
|
611
|
+
_notifyUser(title, message, level = 'info') {
|
|
612
|
+
if (!this._framework) return
|
|
613
|
+
|
|
614
|
+
// 获取目标相关的sessionId(如果有)
|
|
615
|
+
let sessionId = null
|
|
616
|
+
const sessionPlugin = this._framework.pluginManager.get('session')
|
|
617
|
+
if (sessionPlugin) {
|
|
618
|
+
const sessions = sessionPlugin.listSessions()
|
|
619
|
+
if (sessions.length > 0) {
|
|
620
|
+
// 取最新的会话
|
|
621
|
+
sessions.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
622
|
+
sessionId = sessions[0].id
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// 发送统一的通知事件
|
|
627
|
+
this._framework.emit('notification', {
|
|
628
|
+
title,
|
|
629
|
+
message,
|
|
630
|
+
source: 'ambient',
|
|
631
|
+
level, // 'info' | 'warning' | 'success' | 'error'
|
|
632
|
+
sessionId,
|
|
633
|
+
timestamp: new Date()
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
log.info(`${title}: ${message}`)
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
module.exports = { ExplorerLoop }
|