foliko 1.1.19 → 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 -0
- package/.agent/data/plugins-state.json +1 -1
- package/.agent/data/scheduler/tasks.json +86 -0
- package/.agent/memory/feedback/mnzugpej-esdwsr.md +9 -0
- package/.agent/memory/project/mnztftxj-af82rh.md +9 -0
- package/.agent/memory/project/mo0y04d0-bmaefl.md +9 -0
- package/.agent/memory/user/mnzuh4cw-zvee8w.md +13 -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 +2377 -3
- package/.claude/settings.local.json +3 -1
- package/cli/src/ui/chat-ui.js +8 -2
- 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/scheduler-plugin.js +3 -2
- package/plugins/weixin-plugin.js +6 -3
- package/src/core/agent-chat.js +26 -13
- package/src/core/chat-session.js +7 -2
- package/src/core/context-compressor.js +34 -13
- package/src/core/subagent.js +99 -44
- package/src/utils/chat-queue.js +93 -12
- 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
|
@@ -202,7 +202,9 @@
|
|
|
202
202
|
"Bash(node -e \"\nconst { createTools } = require\\('ai'\\);\nconsole.log\\('createTools exists:', typeof createTools\\);\n\" 2>&1)",
|
|
203
203
|
"Bash(node -e \"\nconst poster = require\\('/d/code/foliko-plugins/poster-plugin'\\);\nconsole.log\\('poster.tools:', typeof poster.tools\\);\nconsole.log\\('poster.tools keys:', Object.keys\\(poster.tools || {}\\).slice\\(0, 5\\)\\);\n\" 2>&1)",
|
|
204
204
|
"Bash(node -e \"require\\('./src/core/agent-chat.js'\\); console.log\\('OK'\\)\" 2>&1)",
|
|
205
|
-
"Bash(node -e \"const ai = require\\('ai'\\); console.log\\('tool type:', typeof ai.tool\\)\")"
|
|
205
|
+
"Bash(node -e \"const ai = require\\('ai'\\); console.log\\('tool type:', typeof ai.tool\\)\")",
|
|
206
|
+
"Bash(taskkill //PID 26152 //F)",
|
|
207
|
+
"Bash(netstat -ano | grep 3000)"
|
|
206
208
|
]
|
|
207
209
|
}
|
|
208
210
|
}
|
package/cli/src/ui/chat-ui.js
CHANGED
|
@@ -198,9 +198,15 @@ class ChatUI {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
} catch (err) {
|
|
201
|
-
|
|
201
|
+
// 只打印简洁错误消息,不打印堆栈
|
|
202
|
+
const errName = err?.name || '';
|
|
203
|
+
const isRetryError = errName === 'AI_RetryError' || errName === 'RetryError';
|
|
204
|
+
const friendlyMessage = isRetryError
|
|
205
|
+
? 'AI 服务暂时不可用,请稍后重试'
|
|
206
|
+
: (err.message || '未知错误').split('\n')[0];
|
|
207
|
+
|
|
202
208
|
if (!interrupted) {
|
|
203
|
-
console.error(`\n${colored('[错误]', RED)} ${
|
|
209
|
+
console.error(`\n${colored('[错误]', RED)} ${friendlyMessage}\n`);
|
|
204
210
|
}
|
|
205
211
|
} finally {
|
|
206
212
|
process.removeListener('SIGINT', interruptHandler);
|
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) {
|
|
@@ -581,11 +581,12 @@ class SchedulerPlugin extends Plugin {
|
|
|
581
581
|
// }
|
|
582
582
|
|
|
583
583
|
// 根据 notify 参数决定是否发送通知
|
|
584
|
-
|
|
584
|
+
// 只有当 LLM 返回了有效消息时才发送通知
|
|
585
|
+
if (task.notify !== false && responseText && responseText.trim()) {
|
|
585
586
|
// 发送统一的通知事件
|
|
586
587
|
this._framework.emit('notification', {
|
|
587
588
|
title: task.name,
|
|
588
|
-
message: responseText
|
|
589
|
+
message: responseText,
|
|
589
590
|
source: 'scheduler',
|
|
590
591
|
level: 'info',
|
|
591
592
|
sessionId: task.sessionId,
|
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
|
|
@@ -556,7 +559,7 @@ class WeixinPlugin extends Plugin {
|
|
|
556
559
|
}else{
|
|
557
560
|
const msg = `继续进行下一步`
|
|
558
561
|
await this._sendMessageBatch(originalMsg, userId, msg, true)
|
|
559
|
-
agent.sendMessage(msg, { sessionId, priority: 1 })
|
|
562
|
+
await agent.sendMessage(msg, { sessionId, priority: 1 })
|
|
560
563
|
}
|
|
561
564
|
lineBuffer = ''
|
|
562
565
|
message=''
|
package/src/core/agent-chat.js
CHANGED
|
@@ -324,11 +324,10 @@ class AgentChatHandler extends EventEmitter {
|
|
|
324
324
|
executeFunction: this.chatStream.bind(this),
|
|
325
325
|
})
|
|
326
326
|
.catch((err) => {
|
|
327
|
-
|
|
327
|
+
// 只设置 streamError,不打印日志(避免重复)
|
|
328
328
|
if (!streamError) {
|
|
329
329
|
streamError = err;
|
|
330
330
|
}
|
|
331
|
-
throw err; // 重新抛出,让 await queuePromise 能捕获
|
|
332
331
|
});
|
|
333
332
|
|
|
334
333
|
try {
|
|
@@ -489,6 +488,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
489
488
|
AI_RetryError: 'AI 服务暂时不可用,请稍后重试',
|
|
490
489
|
AI_APICallError: err?.isRetryable ? 'AI 服务暂时不可用,请稍后重试' : simpleMessage,
|
|
491
490
|
AI_NoContentGeneratedError: 'AI 未生成有效内容,请重试',
|
|
491
|
+
AI_NoOutputGeneratedError: 'AI 未生成有效内容,请重试',
|
|
492
492
|
AI_NoSuchModelError: '指定的 AI 模型不存在',
|
|
493
493
|
AI_NoSuchProviderError: 'AI 提供商配置错误',
|
|
494
494
|
AI_LoadAPIKeyError: 'AI API 密钥配置错误',
|
|
@@ -499,7 +499,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
499
499
|
const friendlyMessage =
|
|
500
500
|
errorMessages[errName] || (isRetryError ? 'AI 服务暂时不可用,请稍后重试' : simpleMessage);
|
|
501
501
|
|
|
502
|
-
logger.error(`[AgentChat] Error [${errName}]: ${simpleMessage}`);
|
|
502
|
+
//logger.error(`[AgentChat] Error [${errName}]: ${simpleMessage}`);
|
|
503
503
|
this.emit('message:error', { sessionId, error: friendlyMessage });
|
|
504
504
|
yield { type: 'error', error: friendlyMessage };
|
|
505
505
|
} finally {
|
|
@@ -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 检查
|
|
@@ -681,7 +689,7 @@ class AgentChatHandler extends EventEmitter {
|
|
|
681
689
|
|
|
682
690
|
// 执行工具
|
|
683
691
|
this.emit('tool-call', { name: toolName, args: cleanedArgs });
|
|
684
|
-
logger.info(`[
|
|
692
|
+
logger.info(`[Tool] Call: ${toolName}`);
|
|
685
693
|
try {
|
|
686
694
|
const result = await toolDef.execute(cleanedArgs, this.agent.framework);
|
|
687
695
|
this.emit('tool-result', { name: toolName, args: cleanedArgs, result });
|
|
@@ -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
|
}
|