foliko 1.1.20 → 1.1.21
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/.agent/agents/ui-designer.md +1 -1
- package/.agent/data/ambient/goals.json +1 -47
- package/.agent/data/scheduler/tasks.json +86 -0
- package/.agent/memory/project/mo0y04d0-bmaefl.md +9 -0
- package/.agent/memory/user/mnzvewyj-jl67cq.md +9 -0
- package/.agent/memory/user/mnzwh9xo-43ys3f.md +9 -0
- package/.agent/memory/user/mo0ycvpn-eebsxc.md +9 -0
- package/.agent/sessions/cli_default.json +2231 -98
- package/nul +3 -0
- package/package.json +1 -1
- package/plugins/ambient-agent/ExplorerLoop.js +121 -11
- package/plugins/ambient-agent/GoalManager.js +1 -1
- package/plugins/extension-executor-plugin.js +1 -1
- package/plugins/weixin-plugin.js +5 -2
- package/src/core/agent-chat.js +22 -9
- package/src/core/chat-session.js +7 -2
- package/src/core/context-compressor.js +34 -13
- package/src/core/subagent.js +23 -12
- package/src/utils/chat-queue.js +16 -3
- package/system.md +79 -0
- package/system.md.bak +1978 -0
- package/undefined.svg +8 -0
- package/weixin-bot-poster-v2.png +0 -0
- package/weixin-bot-poster.png +0 -0
- package/.agent/sessions/default.json +0 -111
- package/audio/bed_moonlight.mp3 +0 -0
package/nul
ADDED
package/package.json
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
const { Plugin } = require('../../src/core/plugin-base')
|
|
6
6
|
const { logger } = require('../../src/utils/logger')
|
|
7
|
-
const { Agent } = require('../../src/core/agent')
|
|
8
7
|
const { StepExecutor } = require('../../src/capabilities/workflow-engine')
|
|
9
8
|
const { SystemPrompts, ThinkModePrompts, FreeThinkPrompts } = require('./constants')
|
|
10
9
|
|
|
@@ -37,13 +36,19 @@ class ExplorerLoop {
|
|
|
37
36
|
const aiPlugin = this._framework.pluginManager?.get('ai')
|
|
38
37
|
const llmConfig = aiPlugin ? aiPlugin.getConfig() : {}
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
// 使用 Subagent 代替 Agent,轻量化处理后台任务
|
|
40
|
+
this._ambientAgent = this._framework.createSubAgent({
|
|
41
41
|
name: 'ambient-worker',
|
|
42
|
+
role: '后台任务执行助手',
|
|
43
|
+
description: '执行需要 AI 能力的后台任务',
|
|
42
44
|
systemPrompt: SystemPrompts.ambientWorker,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
isAll:true,
|
|
46
|
+
llmConfig: {
|
|
47
|
+
model: llmConfig.model,
|
|
48
|
+
provider: llmConfig.provider,
|
|
49
|
+
apiKey: llmConfig.apiKey,
|
|
50
|
+
baseURL: llmConfig.baseURL
|
|
51
|
+
}
|
|
47
52
|
})
|
|
48
53
|
|
|
49
54
|
// 注册到主 agent(如果主 agent 已存在)
|
|
@@ -451,10 +456,22 @@ class ExplorerLoop {
|
|
|
451
456
|
if (typeof value === 'string') {
|
|
452
457
|
// 处理 ${actionId.output} 和 ${actionId.output.field} 格式
|
|
453
458
|
const result = value.replace(/\$\{([^}]+)\}/g, (match, path) => {
|
|
459
|
+
// 特殊处理:${result.xxx} 引用上一步结果中的字段
|
|
460
|
+
if (path.startsWith('result.')) {
|
|
461
|
+
const fieldPath = path.replace(/^result\./, '')
|
|
462
|
+
const lastResult = stepOutputs._lastResult
|
|
463
|
+
if (lastResult) {
|
|
464
|
+
const val = this._getNestedValue(lastResult, fieldPath)
|
|
465
|
+
if (val !== undefined) return val
|
|
466
|
+
}
|
|
467
|
+
return match
|
|
468
|
+
}
|
|
469
|
+
|
|
454
470
|
if (path === 'output' || path === 'result') {
|
|
455
|
-
// {
|
|
471
|
+
// ${result} 引用上一步结果
|
|
456
472
|
return stepOutputs._lastResult ?? match
|
|
457
473
|
}
|
|
474
|
+
|
|
458
475
|
if (path.startsWith('event.')) {
|
|
459
476
|
// ${event.xxx} 引用事件数据
|
|
460
477
|
let eventPath = path.replace(/^event\./, '')
|
|
@@ -478,10 +495,12 @@ class ExplorerLoop {
|
|
|
478
495
|
}
|
|
479
496
|
return match
|
|
480
497
|
}
|
|
498
|
+
|
|
481
499
|
if (path === 'event') {
|
|
482
500
|
// ${event} 引用整个事件数据对象
|
|
483
501
|
return eventData ?? match
|
|
484
502
|
}
|
|
503
|
+
|
|
485
504
|
if (path.includes('.')) {
|
|
486
505
|
// ${actionId.output.field} 格式
|
|
487
506
|
const [actionId, ...fieldParts] = path.split('.')
|
|
@@ -526,6 +545,66 @@ class ExplorerLoop {
|
|
|
526
545
|
return current
|
|
527
546
|
}
|
|
528
547
|
|
|
548
|
+
/**
|
|
549
|
+
* 判断 LLM 回复是否应该发送通知
|
|
550
|
+
* 识别模式:
|
|
551
|
+
* - 包含 "【通知】" 或 "[通知]" 开头的回复
|
|
552
|
+
* - JSON 中包含 notification_send 调用
|
|
553
|
+
* - 包含 "已发送通知"、"发送通知" 等关键词
|
|
554
|
+
*/
|
|
555
|
+
_shouldSendNotification(resultText) {
|
|
556
|
+
if (!resultText) return false
|
|
557
|
+
|
|
558
|
+
// 检查通知标记
|
|
559
|
+
const notifyPatterns = [
|
|
560
|
+
/【通知】/,
|
|
561
|
+
/\[通知\]/,
|
|
562
|
+
/notification_send/,
|
|
563
|
+
/已发送通知/,
|
|
564
|
+
/发送通知成功/,
|
|
565
|
+
/通知已发送/,
|
|
566
|
+
/【提醒】/,
|
|
567
|
+
/\[提醒\]/,
|
|
568
|
+
/⚠️|🚨|🔔|📢/ // 常见通知 emoji
|
|
569
|
+
]
|
|
570
|
+
|
|
571
|
+
return notifyPatterns.some(pattern => pattern.test(resultText))
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* 从 LLM 回复中提取通知内容
|
|
576
|
+
*/
|
|
577
|
+
_extractNotification(resultText) {
|
|
578
|
+
if (!resultText) return null
|
|
579
|
+
|
|
580
|
+
// 尝试提取 【通知】... 内容
|
|
581
|
+
const zhMatch = resultText.match(/【通知】([\s\S]*?)(?:```|$)/)
|
|
582
|
+
if (zhMatch) {
|
|
583
|
+
const content = zhMatch[1].trim()
|
|
584
|
+
// 尝试分离标题和内容
|
|
585
|
+
const lines = content.split('\n').filter(l => l.trim())
|
|
586
|
+
if (lines.length >= 2) {
|
|
587
|
+
return { title: lines[0], message: lines.slice(1).join('\n') }
|
|
588
|
+
} else if (lines.length === 1) {
|
|
589
|
+
return { title: '通知提醒', message: lines[0] }
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// 尝试提取 [通知]... 内容
|
|
594
|
+
const enMatch = resultText.match(/\[通知\]([\s\S]*?)(?:```|$)/)
|
|
595
|
+
if (enMatch) {
|
|
596
|
+
const content = enMatch[1].trim()
|
|
597
|
+
const lines = content.split('\n').filter(l => l.trim())
|
|
598
|
+
if (lines.length >= 1) {
|
|
599
|
+
return { title: lines[0], message: lines.slice(1).join('\n') || lines[0] }
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// 默认:从结果中提取前100字符作为消息
|
|
604
|
+
const shortContent = resultText.substring(0, 200).replace(/```[\s\S]*?```/g, '').trim()
|
|
605
|
+
return { title: 'Ambient Agent 通知', message: shortContent }
|
|
606
|
+
}
|
|
607
|
+
|
|
529
608
|
/**
|
|
530
609
|
* 按顺序执行所有 actions
|
|
531
610
|
* @param {Array} actions - action 数组
|
|
@@ -607,9 +686,36 @@ class ExplorerLoop {
|
|
|
607
686
|
content = `${content}\n\n[事件上下文: ${JSON.stringify(context.variables._event)}]`
|
|
608
687
|
}
|
|
609
688
|
|
|
689
|
+
// 添加通知能力提示
|
|
690
|
+
const notifyPrompt = `\n\n## 通知能力\n如果你需要发送通知,可以使用 notification_send 工具:\n- plugin: "notification"\n- tool: "notification_send" \n- args: { "title": "通知标题", "message": "通知内容" }\n\n**重要**:当判断结果满足条件时,你应该调用 notification_send 发送通知。不满足条件时,静默结束即可。`
|
|
691
|
+
|
|
610
692
|
try {
|
|
611
|
-
const result = await this._ambientAgent.
|
|
612
|
-
|
|
693
|
+
const result = await this._ambientAgent.chat(content + notifyPrompt, { maxRetries: 2 })
|
|
694
|
+
|
|
695
|
+
// Subagent 返回 { success, message, steps, error }
|
|
696
|
+
if (!result.success) {
|
|
697
|
+
return { success: false, error: result.error }
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const resultText = result.message || ''
|
|
701
|
+
|
|
702
|
+
// 如果 result 中包含通知相关的内容,自动发送通知
|
|
703
|
+
if (this._shouldSendNotification(resultText)) {
|
|
704
|
+
const notification = this._extractNotification(resultText)
|
|
705
|
+
if (notification) {
|
|
706
|
+
// 通过事件系统发送通知
|
|
707
|
+
this._framework.emit('notification', {
|
|
708
|
+
title: notification.title || 'Ambient Agent 通知',
|
|
709
|
+
message: notification.message,
|
|
710
|
+
source: 'ambient',
|
|
711
|
+
level: 'info',
|
|
712
|
+
timestamp: new Date()
|
|
713
|
+
})
|
|
714
|
+
return { success: true, result: resultText, notified: true, notification }
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return { success: true, result: resultText }
|
|
613
719
|
} catch (err) {
|
|
614
720
|
return { success: false, error: err.message }
|
|
615
721
|
}
|
|
@@ -701,8 +807,12 @@ ${extensionsInfo}
|
|
|
701
807
|
请直接返回 JSON 代码块。`
|
|
702
808
|
|
|
703
809
|
try {
|
|
704
|
-
const
|
|
705
|
-
|
|
810
|
+
const result = await this._ambientAgent.chat(prompt, { maxRetries: 2 })
|
|
811
|
+
if (!result.success) {
|
|
812
|
+
log.warn(`[ExplorerLoop] Subagent 调用失败: ${result.error}`)
|
|
813
|
+
return { success: false, error: result.error }
|
|
814
|
+
}
|
|
815
|
+
const responseText = result.message || ''
|
|
706
816
|
|
|
707
817
|
// 尝试解析 LLM 返回的 JSON
|
|
708
818
|
let toolChoice = null
|
|
@@ -136,7 +136,7 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
136
136
|
}),
|
|
137
137
|
execute: async (args) => {
|
|
138
138
|
const { plugin, tool, args: toolArgs = {} } = args;
|
|
139
|
-
log.info(` ext_call: plugin=${plugin}, tool=${tool}
|
|
139
|
+
log.info(`[Extension] ext_call: plugin=${plugin}, tool=${tool}`);
|
|
140
140
|
|
|
141
141
|
// MCP 服务器工具(已注册为 server_toolname 格式,如 github_search)
|
|
142
142
|
if (plugin === 'mcp' && this._mcpExecutor) {
|
package/plugins/weixin-plugin.js
CHANGED
|
@@ -463,8 +463,11 @@ class WeixinPlugin extends Plugin {
|
|
|
463
463
|
[用户发送了${type_dict[msg.type]}消息]\n
|
|
464
464
|
文件列表:\n
|
|
465
465
|
${JSON.stringify(files,null,2)} \n
|
|
466
|
-
|
|
467
|
-
|
|
466
|
+
|
|
467
|
+
**重要提醒:**
|
|
468
|
+
- 禁止读取任何文件内容!
|
|
469
|
+
- 禁止调用任何读取工具!
|
|
470
|
+
- 只告知用户文件保存路径
|
|
468
471
|
`
|
|
469
472
|
, msg)
|
|
470
473
|
break
|
package/src/core/agent-chat.js
CHANGED
|
@@ -606,6 +606,8 @@ class AgentChatHandler extends EventEmitter {
|
|
|
606
606
|
*/
|
|
607
607
|
async _prepareSession(message, sessionId) {
|
|
608
608
|
const messageStore = this._getSessionMessageStore(sessionId);
|
|
609
|
+
// 先加载 session 文件中的历史消息
|
|
610
|
+
this._chatSession.loadHistory(sessionId);
|
|
609
611
|
const messages = messageStore.messages;
|
|
610
612
|
|
|
611
613
|
// 刷新系统提示词
|
|
@@ -621,11 +623,17 @@ class AgentChatHandler extends EventEmitter {
|
|
|
621
623
|
const totalTokens = messagesTokens + toolsTokens + systemPromptTokens;
|
|
622
624
|
const limit = this._maxContextTokens * 0.5;
|
|
623
625
|
|
|
626
|
+
console.log(
|
|
627
|
+
`[_prepareSession] BEFORE: messages=${messages.length}, tokens=${totalTokens}/${limit}`
|
|
628
|
+
);
|
|
629
|
+
|
|
624
630
|
if (totalTokens > limit) {
|
|
631
|
+
console.log(`[_prepareSession] Compressing context...`);
|
|
625
632
|
logger.info(
|
|
626
633
|
`Context large (${totalTokens}/${this._maxContextTokens} tokens), compressing...`
|
|
627
634
|
);
|
|
628
635
|
await this._compressContext(sessionId, messages, messageStore);
|
|
636
|
+
console.log(`[_prepareSession] AFTER compress: messages=${messages.length}`);
|
|
629
637
|
}
|
|
630
638
|
|
|
631
639
|
// 最终 token 检查
|
|
@@ -743,26 +751,31 @@ class AgentChatHandler extends EventEmitter {
|
|
|
743
751
|
try {
|
|
744
752
|
const tokenCount = this._countMessagesTokens(inputMessages);
|
|
745
753
|
const tokenLimit = this._maxContextTokens * 0.75;
|
|
746
|
-
logger.debug(`prepareStep: messages=${inputMessages.length}, tokens=${tokenCount}`);
|
|
747
754
|
|
|
755
|
+
// 超过限制时,保留三分之一的消息
|
|
748
756
|
if (tokenCount > tokenLimit) {
|
|
749
|
-
|
|
750
|
-
|
|
757
|
+
const keepCount = Math.max(10, Math.floor(inputMessages.length / 3));
|
|
758
|
+
console.log(
|
|
759
|
+
`[PrepareStep] Trimming: ${inputMessages.length} msgs -> ${keepCount}, tokens=${tokenCount}`
|
|
751
760
|
);
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
inputMessages.length = 0;
|
|
756
|
-
inputMessages.push(...pairedMessages);
|
|
757
|
-
}
|
|
761
|
+
const trimmed = inputMessages.slice(-keepCount);
|
|
762
|
+
inputMessages.length = 0;
|
|
763
|
+
inputMessages.push(...trimmed);
|
|
758
764
|
}
|
|
759
765
|
|
|
766
|
+
// 验证 tool-call 和 tool-result 配对
|
|
760
767
|
const validated = this._validateMessagesPairing(inputMessages);
|
|
761
768
|
if (validated.length !== inputMessages.length) {
|
|
769
|
+
console.log(
|
|
770
|
+
`[PrepareStep] After pairing validation: ${inputMessages.length} -> ${validated.length}`
|
|
771
|
+
);
|
|
762
772
|
inputMessages.length = 0;
|
|
763
773
|
inputMessages.push(...validated);
|
|
764
774
|
}
|
|
775
|
+
|
|
776
|
+
// 验证 tool-call 格式
|
|
765
777
|
this._validateToolCalls(inputMessages);
|
|
778
|
+
|
|
766
779
|
return { messages: inputMessages };
|
|
767
780
|
} catch (err) {
|
|
768
781
|
logger.error('prepareStep error:', err.message, err.stack);
|
package/src/core/chat-session.js
CHANGED
|
@@ -212,9 +212,14 @@ class ChatSession extends EventEmitter {
|
|
|
212
212
|
return messageStore.messages;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
//
|
|
215
|
+
// 确保 SessionContext 已初始化(会从文件加载或创建新的)
|
|
216
216
|
if (this.agent?.framework) {
|
|
217
|
-
|
|
217
|
+
// 先尝试获取已存在的 SessionContext
|
|
218
|
+
let sessionCtx = this.agent.framework._sessionContexts?.get(sessionId);
|
|
219
|
+
// 如果不存在,调用 getOrCreateSessionContext 来加载或创建
|
|
220
|
+
if (!sessionCtx) {
|
|
221
|
+
sessionCtx = this.agent.framework.getOrCreateSessionContext(sessionId);
|
|
222
|
+
}
|
|
218
223
|
if (sessionCtx) {
|
|
219
224
|
const messages = sessionCtx.getMessages();
|
|
220
225
|
if (messages && messages.length > 0) {
|
|
@@ -15,7 +15,7 @@ const MODEL_CONTEXT_LIMITS = {
|
|
|
15
15
|
'deepseek-chat': 128000,
|
|
16
16
|
'deepseek-coder': 128000,
|
|
17
17
|
'deepseek-reasoner': 128000,
|
|
18
|
-
'MiniMax-M2.7':
|
|
18
|
+
'MiniMax-M2.7': 128000,
|
|
19
19
|
'gpt-4': 100000,
|
|
20
20
|
'gpt-4o': 100000,
|
|
21
21
|
'gpt-4o-mini': 100000,
|
|
@@ -136,14 +136,14 @@ class ContextCompressor {
|
|
|
136
136
|
const messagesToSummarize = otherMessages.slice(0, -this._keepRecentMessages);
|
|
137
137
|
|
|
138
138
|
const compressedCount = messagesToSummarize.length;
|
|
139
|
+
|
|
139
140
|
let summaryContent = '';
|
|
140
141
|
|
|
141
142
|
// 使用 AI 生成摘要
|
|
142
143
|
if (this._enableSmartCompress && this.agent?._chatHandler?._aiClient) {
|
|
143
144
|
try {
|
|
144
145
|
const summaryText = await this._summarizeMessages(messagesToSummarize);
|
|
145
|
-
summaryContent = `[早期对话摘要]: ${summaryText}`;
|
|
146
|
-
logger.info(`AI summary generated (${summaryText.length} chars)`);
|
|
146
|
+
summaryContent = `[早期对话摘要]: ${summaryText || '(无内容)'}`;
|
|
147
147
|
} catch (err) {
|
|
148
148
|
logger.warn('AI summary failed, using simple compression:', err.message);
|
|
149
149
|
summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`;
|
|
@@ -409,23 +409,36 @@ class ContextCompressor {
|
|
|
409
409
|
* @private
|
|
410
410
|
*/
|
|
411
411
|
async _summarizeMessages(messages) {
|
|
412
|
-
|
|
412
|
+
const chatHandler = this.agent?._chatHandler;
|
|
413
|
+
if (!chatHandler?._aiClient) {
|
|
413
414
|
throw new Error('AI client not available');
|
|
414
415
|
}
|
|
415
416
|
|
|
416
417
|
const { generateText } = await import('ai');
|
|
417
418
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
419
|
+
// 使用 AI SDK 消息格式
|
|
420
|
+
const chatMessages = [
|
|
421
|
+
{
|
|
422
|
+
role: 'user',
|
|
423
|
+
content:
|
|
424
|
+
'请简要总结以下对话的主要内容,用1000字以内描述:\n' +
|
|
425
|
+
messages
|
|
426
|
+
.map((m) => {
|
|
427
|
+
const content =
|
|
428
|
+
typeof m.content === 'string' ? m.content : JSON.stringify(m.content || '');
|
|
429
|
+
return `${m.role}: ${content}`;
|
|
430
|
+
})
|
|
431
|
+
.join('\n'),
|
|
432
|
+
},
|
|
433
|
+
];
|
|
423
434
|
|
|
424
435
|
try {
|
|
436
|
+
// _aiClient 已经是 model 对象,直接使用
|
|
437
|
+
const model = chatHandler._aiClient;
|
|
425
438
|
const result = await generateText({
|
|
426
|
-
model:
|
|
427
|
-
|
|
428
|
-
maxTokens:
|
|
439
|
+
model: model,
|
|
440
|
+
messages: chatMessages,
|
|
441
|
+
maxTokens: 1000,
|
|
429
442
|
});
|
|
430
443
|
return result.text || '';
|
|
431
444
|
} catch (err) {
|
|
@@ -458,6 +471,10 @@ ${messages.map((m) => `${m.role}: ${typeof m.content === 'string' ? m.content :
|
|
|
458
471
|
|
|
459
472
|
let total = 4; // 角色开销
|
|
460
473
|
|
|
474
|
+
if (!msg.content) {
|
|
475
|
+
return total;
|
|
476
|
+
}
|
|
477
|
+
|
|
461
478
|
if (typeof msg.content === 'string') {
|
|
462
479
|
total += Math.ceil(Buffer.byteLength(msg.content, 'utf8') / 4);
|
|
463
480
|
} else if (Array.isArray(msg.content)) {
|
|
@@ -470,7 +487,11 @@ ${messages.map((m) => `${m.role}: ${typeof m.content === 'string' ? m.content :
|
|
|
470
487
|
}
|
|
471
488
|
} else if (block.type === 'tool-result' || block.type === 'tool_result') {
|
|
472
489
|
const content =
|
|
473
|
-
|
|
490
|
+
block.content == null
|
|
491
|
+
? ''
|
|
492
|
+
: typeof block.content === 'string'
|
|
493
|
+
? block.content
|
|
494
|
+
: JSON.stringify(block.content);
|
|
474
495
|
total += Math.ceil(Buffer.byteLength(content, 'utf8') / 4);
|
|
475
496
|
}
|
|
476
497
|
}
|
package/src/core/subagent.js
CHANGED
|
@@ -50,7 +50,7 @@ class Subagent extends EventEmitter {
|
|
|
50
50
|
};
|
|
51
51
|
// 如果提供了 systemPrompt 则使用,否则标记为需要动态构建
|
|
52
52
|
this._customSystemPrompt = config.systemPrompt || null;
|
|
53
|
-
this.parentTools = config?.parentTools
|
|
53
|
+
this.parentTools = config?.parentTools;
|
|
54
54
|
// 工具管理
|
|
55
55
|
this._tools = new Map();
|
|
56
56
|
this._registerTools(config.tools || []);
|
|
@@ -121,20 +121,31 @@ class Subagent extends EventEmitter {
|
|
|
121
121
|
const tools = {};
|
|
122
122
|
// 从父Agent继承工具
|
|
123
123
|
const all_tools = this.framework.getTools();
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
124
|
+
let parentTools = [];
|
|
125
|
+
if (Array.isArray(this.parentTools)) {
|
|
126
|
+
parentTools = this.parentTools.map((key) => {
|
|
127
|
+
return this.bindTools[key.toLocaleLowerCase()] || key;
|
|
128
|
+
});
|
|
129
|
+
for (const toolName of parentTools) {
|
|
130
|
+
const toolDef = all_tools.find((t) => t.name === toolName);
|
|
131
|
+
if (toolDef) {
|
|
132
|
+
tools[toolDef.name] = toolDef;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const defaulTools = all_tools.filter((a) => this.defaulTools.includes(a.name));
|
|
136
|
+
|
|
137
|
+
defaulTools.map((tool) => {
|
|
138
|
+
tools[tool.name] = tool;
|
|
139
|
+
});
|
|
140
|
+
} else {
|
|
141
|
+
for (const toolName of all_tools) {
|
|
142
|
+
const toolDef = all_tools.find((t) => t.name === toolName);
|
|
143
|
+
if (toolDef) {
|
|
144
|
+
tools[toolDef.name] = toolDef;
|
|
145
|
+
}
|
|
131
146
|
}
|
|
132
147
|
}
|
|
133
|
-
const defaulTools = all_tools.filter((a) => this.defaulTools.includes(a.name));
|
|
134
148
|
|
|
135
|
-
defaulTools.map((tool) => {
|
|
136
|
-
tools[tool.name] = tool;
|
|
137
|
-
});
|
|
138
149
|
return { ...tools, ...this._tools };
|
|
139
150
|
}
|
|
140
151
|
|
package/src/utils/chat-queue.js
CHANGED
|
@@ -119,7 +119,12 @@ class ChatQueueManager extends EventEmitter {
|
|
|
119
119
|
|
|
120
120
|
// 检查是否有错误(通过返回的 result.error)
|
|
121
121
|
if (result.error) {
|
|
122
|
-
console.log(
|
|
122
|
+
console.log(
|
|
123
|
+
'[ChatQueue] executeWithRetry: attempt',
|
|
124
|
+
attempt,
|
|
125
|
+
'got error:',
|
|
126
|
+
result.error.message
|
|
127
|
+
);
|
|
123
128
|
lastError = result.error;
|
|
124
129
|
if (attempt < this.retryAttempts && this.isRetryableError(lastError)) {
|
|
125
130
|
await this.sleep(this.retryDelay * Math.pow(2, attempt - 1));
|
|
@@ -132,7 +137,12 @@ class ChatQueueManager extends EventEmitter {
|
|
|
132
137
|
|
|
133
138
|
return result;
|
|
134
139
|
} catch (error) {
|
|
135
|
-
console.log(
|
|
140
|
+
console.log(
|
|
141
|
+
'[ChatQueue] executeWithRetry: attempt',
|
|
142
|
+
attempt,
|
|
143
|
+
'threw error:',
|
|
144
|
+
error.message
|
|
145
|
+
);
|
|
136
146
|
lastError = error;
|
|
137
147
|
if (attempt < this.retryAttempts && this.isRetryableError(error)) {
|
|
138
148
|
await this.sleep(this.retryDelay * Math.pow(2, attempt - 1));
|
|
@@ -186,7 +196,10 @@ class ChatQueueManager extends EventEmitter {
|
|
|
186
196
|
? 'AI 服务暂时不可用,请稍后重试'
|
|
187
197
|
: (err.message || err.toString()).split('\n')[0];
|
|
188
198
|
|
|
189
|
-
console.log(
|
|
199
|
+
console.log(
|
|
200
|
+
'[ChatQueue] executeStream caught error, converting to friendly:',
|
|
201
|
+
friendlyMessage
|
|
202
|
+
);
|
|
190
203
|
chunks.push({ type: 'error', error: friendlyMessage });
|
|
191
204
|
this.emit('stream:chunk', {
|
|
192
205
|
requestId: item.id,
|
package/system.md
CHANGED
|
@@ -5,6 +5,85 @@
|
|
|
5
5
|
【元数据】
|
|
6
6
|
- WORK_DIR: D:\code\vb-agent
|
|
7
7
|
|
|
8
|
+
## 【Ambient Agent 监控任务指南】
|
|
9
|
+
|
|
10
|
+
当用户请求创建条件监控任务(如"价格高于X时通知我"、"收到某邮件时自动回复")时,使用以下模式:
|
|
11
|
+
|
|
12
|
+
### 标准条件监控配置
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
{
|
|
16
|
+
"title": "监控任务标题",
|
|
17
|
+
"description": "监控条件描述(包含阈值等信息)",
|
|
18
|
+
"conditions": { "events": ["scheduler:reminder"] },
|
|
19
|
+
"persistent": true,
|
|
20
|
+
"actions": [
|
|
21
|
+
{
|
|
22
|
+
"id": "get_data",
|
|
23
|
+
"type": "tool",
|
|
24
|
+
"plugin": "gate-trading", // 或 email, web 等
|
|
25
|
+
"name": "gate_get_market_info",
|
|
26
|
+
"args": { "currency_pair": "BTC_USDT", "info_type": "tickers" }
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "judge",
|
|
30
|
+
"type": "message",
|
|
31
|
+
"content": "你是一个条件判断助手。\n\n上一步获取到了数据:${result.last}\n\n判断规则:\n- 如果数据满足条件,调用 notification_send 发送通知\n- 如果不满足条件,静默结束(不要发送任何通知)\n\n你的任务是分析数据,判断是否需要通知用户。"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 关键要点
|
|
38
|
+
|
|
39
|
+
1. **actions 必须包含两个步骤**:
|
|
40
|
+
- 第一步:用 `type: "tool"` 获取数据
|
|
41
|
+
- 第二步:用 `type: "message"` 让 LLM 判断是否发送通知
|
|
42
|
+
|
|
43
|
+
2. **message content 中的判断提示词**:
|
|
44
|
+
- 明确告诉 LLM 阈值是多少
|
|
45
|
+
- 说明满足条件时应该做什么
|
|
46
|
+
- 说明不满足条件时应该静默
|
|
47
|
+
|
|
48
|
+
3. **使用 ${result.xxx} 引用上一步结果**:
|
|
49
|
+
- `${result.last}` - 获取价格数据
|
|
50
|
+
- `${result.from}` - 获取发件人
|
|
51
|
+
- `${result.subject}` - 获取邮件主题
|
|
52
|
+
|
|
53
|
+
### 示例:比特币价格监控
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
{
|
|
57
|
+
"title": "比特币价格监控 (BTC > $73000)",
|
|
58
|
+
"description": "每2分钟检查BTC价格,高于73000美元时通知",
|
|
59
|
+
"conditions": { "events": ["scheduler:reminder"] },
|
|
60
|
+
"persistent": true,
|
|
61
|
+
"actions": [
|
|
62
|
+
{
|
|
63
|
+
"type": "tool",
|
|
64
|
+
"plugin": "gate-trading",
|
|
65
|
+
"name": "gate_get_market_info",
|
|
66
|
+
"args": { "currency_pair": "BTC_USDT", "info_type": "tickers" }
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"type": "message",
|
|
70
|
+
"content": "你是条件判断助手。\n\n上一步获取到 BTC 价格数据:${result.last} USDT\n\n监控条件:价格 > 73000 USDT\n\n判断规则:\n- 如果 ${result.last} > 73000,调用 notification_send 发送通知\n- 通知内容:当前 BTC 价格 ${result.last} USDT,已超过 $73000 阈值\n- 如果价格 <= 73000,静默结束,不要发送任何通知"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 常见监控场景配置
|
|
77
|
+
|
|
78
|
+
| 场景 | 工具 | 判断逻辑 |
|
|
79
|
+
|------|------|----------|
|
|
80
|
+
| 价格监控 | gate_get_market_info | 价格 > 阈值 |
|
|
81
|
+
| 邮件监控 | email_read | 主题/发件人匹配 |
|
|
82
|
+
| 余额监控 | gate_get_account_balance | 余额变化 |
|
|
83
|
+
| 网页监控 | web_request | 内容包含某字符串 |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
8
87
|
## 【记忆上下文】
|
|
9
88
|
### 【用户偏好】
|
|
10
89
|
- 用户海报风格偏好: ## 用户海报设计风格偏好
|