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.

163 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'
});