You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
4.8 KiB
162 lines
4.8 KiB
#!/usr/bin/env node |
|
/** |
|
* OpenClaw Mem0 Integration Plugin |
|
* 将 mem0 拦截器挂载到 OpenClaw 主对话生命周期 |
|
*/ |
|
|
|
const fs = require('fs'); |
|
const path = require('path'); |
|
|
|
// Python 子进程执行器 |
|
const { spawn } = require('child_process'); |
|
|
|
class Mem0Plugin { |
|
constructor(config) { |
|
this.config = config; |
|
this.enabled = true; |
|
this.pythonPath = config.pythonPath || 'python3'; |
|
this.scriptPath = path.join(__dirname, 'mem0_integration.py'); |
|
this.initialized = false; |
|
} |
|
|
|
/** |
|
* 初始化 mem0(在 OpenClaw 启动时调用) |
|
*/ |
|
async onLoad() { |
|
if (!this.enabled) return; |
|
|
|
console.log('[Mem0] 初始化记忆系统...'); |
|
|
|
try { |
|
// 调用 Python 脚本初始化 mem0 |
|
const result = await this._executePython('init'); |
|
|
|
if (result.success) { |
|
this.initialized = true; |
|
console.log('🟢 Mem0 生产环境集成完毕,等待 Telegram 消息注入'); |
|
} else { |
|
console.error('[Mem0] 初始化失败:', result.error); |
|
} |
|
} catch (error) { |
|
console.error('[Mem0] 初始化异常:', error.message); |
|
} |
|
} |
|
|
|
/** |
|
* Pre-Hook: 在 LLM 生成前检索记忆 |
|
*/ |
|
async preLLM(userMessage, context) { |
|
if (!this.enabled || !this.initialized) return null; |
|
|
|
try { |
|
const result = await this._executePython('search', { |
|
query: userMessage, |
|
user_id: context.user_id || 'default', |
|
agent_id: context.agent_id || 'general' |
|
}); |
|
|
|
if (result.success && result.memories && result.memories.length > 0) { |
|
console.log(`[Mem0] Pre-Hook: 检索到 ${result.memories.length} 条记忆`); |
|
return this._formatMemories(result.memories); |
|
} |
|
|
|
return null; |
|
} catch (error) { |
|
console.error('[Mem0] Pre-Hook 失败:', error.message); |
|
return null; // 静默失败,不影响对话 |
|
} |
|
} |
|
|
|
/** |
|
* Post-Hook: 在响应后异步写入记忆 |
|
*/ |
|
async postResponse(userMessage, assistantMessage, context) { |
|
if (!this.enabled || !this.initialized) return; |
|
|
|
try { |
|
await this._executePython('add', { |
|
user_message: userMessage, |
|
assistant_message: assistantMessage, |
|
user_id: context.user_id || 'default', |
|
agent_id: context.agent_id || 'general' |
|
}, false); // 不等待结果(异步) |
|
|
|
console.log('[Mem0] Post-Hook: 已提交对话到记忆队列'); |
|
} catch (error) { |
|
console.error('[Mem0] Post-Hook 失败:', error.message); |
|
// 静默失败 |
|
} |
|
} |
|
|
|
/** |
|
* 执行 Python 脚本 |
|
*/ |
|
_executePython(action, data = {}, waitForResult = true) { |
|
return new Promise((resolve, reject) => { |
|
const args = [this.scriptPath, action]; |
|
if (data) { |
|
args.push(JSON.stringify(data)); |
|
} |
|
|
|
const proc = spawn(this.pythonPath, args, { |
|
cwd: __dirname, |
|
env: process.env |
|
}); |
|
|
|
let stdout = ''; |
|
let stderr = ''; |
|
|
|
proc.stdout.on('data', (data) => { |
|
stdout += data.toString(); |
|
}); |
|
|
|
proc.stderr.on('data', (data) => { |
|
stderr += data.toString(); |
|
}); |
|
|
|
proc.on('close', (code) => { |
|
if (code === 0) { |
|
try { |
|
const result = JSON.parse(stdout); |
|
resolve({ success: true, ...result }); |
|
} catch { |
|
resolve({ success: true, raw: stdout }); |
|
} |
|
} else { |
|
resolve({ success: false, error: stderr || `Exit code: ${code}` }); |
|
} |
|
}); |
|
|
|
proc.on('error', reject); |
|
|
|
// 如果不等待结果,立即返回 |
|
if (!waitForResult) { |
|
resolve({ success: true, async: true }); |
|
} |
|
}); |
|
} |
|
|
|
/** |
|
* 格式化记忆为 Prompt |
|
*/ |
|
_formatMemories(memories) { |
|
if (!memories || memories.length === 0) return ''; |
|
|
|
let prompt = '\n\n=== 相关记忆 ===\n'; |
|
memories.forEach((mem, i) => { |
|
prompt += `${i + 1}. ${mem.memory}`; |
|
if (mem.created_at) { |
|
prompt += ` (记录于:${mem.created_at})`; |
|
} |
|
prompt += '\n'; |
|
}); |
|
prompt += '===============\n'; |
|
|
|
return prompt; |
|
} |
|
} |
|
|
|
// 导出插件实例 |
|
module.exports = new Mem0Plugin({ |
|
pythonPath: process.env.MEM0_PYTHON_PATH || 'python3' |
|
});
|
|
|