Compare commits

...

8 Commits

Author SHA1 Message Date
Eason (陈医生) 6a84c4abac feat: 敏感配置脱敏处理 + mem0 测试文件 + agents 目录 1 month ago
Eason (陈医生) b6467da698 feat: mem0 生产级上线 1 month ago
Eason (陈医生) b26030f7a6 feat: mem0 生产级架构修正 1 month ago
Eason (陈医生) 5f0f8bb685 feat: mem0 纯异步架构重构 1 month ago
Eason (陈医生) 7036390772 feat: mem0 记忆系统完整部署 1 month ago
Eason (陈医生) 3ad7de00b8 fix: User-level systemd configuration with linger support 1 month ago
Eason (陈医生) 820530d1ec feat: Complete system architecture upgrade with auto-healing, notifications, and rollback 1 month ago
Eason (陈医生) 5707edd78a Add memory directory for daily memory files 1 month ago
  1. 40
      .gitignore
  2. 47
      CORE_INDEX.md
  3. 40
      IDENTITY.md
  4. 180
      MEMORY.md
  5. 18
      USER.md
  6. 281
      agent-monitor.js
  7. 70
      agents/registry.md
  8. BIN
      backup/backup-20260222-151510.tar.gz
  9. 365
      deploy.sh
  10. 427
      docs/MEM0_ARCHITECTURE.md
  11. 126
      docs/MEM0_DEPLOYMENT.md
  12. 51
      docs/MEM0_TEST_LOG.md
  13. 4
      logs/agents/health-2026-02-20.log
  14. 5
      logs/agents/health-2026-02-21.log
  15. 5
      logs/agents/health-2026-02-22.log
  16. 5
      logs/agents/health-2026-02-23.log
  17. 144
      memory/2026-02-20-1105.md
  18. 28
      memory/2026-02-20-1111.md
  19. 19
      memory/2026-02-20-1348.md
  20. 28
      memory/2026-02-20-1403.md
  21. 207
      openclaw-config.json
  22. 53
      openclaw-config.json.example
  23. 57
      scripts/01-system-check.sh
  24. 69
      scripts/02-install-tailscale.sh
  25. 101
      scripts/03-create-directories.sh
  26. 83
      scripts/05-start-center.sh
  27. 26
      scripts/07-install-dependencies.sh
  28. 78
      scripts/08-test-mem0.py
  29. 40
      scripts/10-create-backup.sh
  30. 41
      scripts/12-monitoring.sh
  31. 35
      skills/mem0-integration/SKILL.md
  32. BIN
      skills/mem0-integration/__pycache__/mem0_client.cpython-312.pyc
  33. BIN
      skills/mem0-integration/__pycache__/openclaw_interceptor.cpython-312.pyc
  34. 79
      skills/mem0-integration/commands.py
  35. 47
      skills/mem0-integration/config.yaml.example
  36. 162
      skills/mem0-integration/mem0-plugin.js
  37. 457
      skills/mem0-integration/mem0_client.py
  38. 69
      skills/mem0-integration/mem0_integration.py
  39. 150
      skills/mem0-integration/openclaw_commands.py
  40. 51
      skills/mem0-integration/openclaw_interceptor.py
  41. 36
      skills/mem0-integration/skill.json
  42. 77
      skills/mem0-integration/test_integration.py
  43. 114
      skills/mem0-integration/test_mem0.py
  44. 88
      skills/mem0-integration/test_production.py
  45. 41
      systemd/openclaw-agent-monitor.service
  46. 51
      systemd/openclaw-gateway-user.service
  47. 42
      systemd/openclaw-gateway.service

40
.gitignore vendored

@ -0,0 +1,40 @@
# 敏感配置(包含 API keys、密码等)
openclaw-config.json
skills/*/config.yaml
skills/*/.env
*.env
*.pem
*.key
# 日志和缓存
logs/
*.pyc
__pycache__/
.cache/
.pytest_cache/
# 运行时
.pid
*.pid
*.log
# 记忆文件(每日日志量大,单独备份)
memory/*.md
!memory/.gitkeep
# 备份目录
backup/
# 系统文件
.DS_Store
Thumbs.db
*.swp
*.swo
*~
# Node modules(如果有)
node_modules/
# Python 虚拟环境
venv/
.venv/

@ -20,13 +20,18 @@
├── TOOLS.md # Environment-specific tool configurations ├── TOOLS.md # Environment-specific tool configurations
├── IDENTITY.md # Agent identity configuration ├── IDENTITY.md # Agent identity configuration
├── HEARTBEAT.md # Periodic check tasks ├── HEARTBEAT.md # Periodic check tasks
├── deploy.sh # One-click deployment & management script
├── agent-monitor.js # Auto-healing & health monitoring system
├── skills/ # Installed agent skills ├── skills/ # Installed agent skills
├── logs/ # Operation and system logs ├── logs/ # Operation and system logs
│ ├── operations/ # Manual operations and changes │ ├── operations/ # Manual operations and changes
│ ├── system/ # System-generated logs │ ├── system/ # System-generated logs
│ ├── agents/ # Individual agent logs │ ├── agents/ # Individual agent logs
│ └── security/ # Security operations and audits │ └── security/ # Security operations and audits
└── memory/ # Daily memory files (YYYY-MM-DD.md) ├── memory/ # Daily memory files (YYYY-MM-DD.md)
└── systemd/ # Systemd service definitions
├── openclaw-gateway.service
└── openclaw-agent-monitor.service
``` ```
## Memory Access Strategy ## Memory Access Strategy
@ -39,7 +44,9 @@
- **Security Templates**: MEMORY.md → Server security hardening templates - **Security Templates**: MEMORY.md → Server security hardening templates
- **Agent Practices**: AGENTS.md → Agent deployment and management practices - **Agent Practices**: AGENTS.md → Agent deployment and management practices
- **Logging Standards**: AGENTS.md → Operation logging and audit practices - **Logging Standards**: AGENTS.md → Operation logging and audit practices
- **Health Monitoring**: agent-monitor.js → Agent crash detection and notification - **Health Monitoring**: agent-monitor.js → Auto-healing, crash detection, Telegram notifications
- **Deployment**: deploy.sh → One-click install/start/stop/rollback/backup
- **Systemd Services**: systemd/*.service → System-level auto-start & auto-healing
- **Configuration Backup**: Git commits before any JSON modifications - **Configuration Backup**: Git commits before any JSON modifications
## Usage Instructions for Models ## Usage Instructions for Models
@ -47,4 +54,38 @@
2. Identify relevant documentation based on task requirements 2. Identify relevant documentation based on task requirements
3. Load specific files using read/edit/write tools as needed 3. Load specific files using read/edit/write tools as needed
4. Never assume memory persistence across model sessions 4. Never assume memory persistence across model sessions
5. Always verify current state before making changes 5. Always verify current state before making changes
## System Architecture (2026-02-20)
### Layer 1: System-Level (Systemd)
- **openclaw-gateway.service**: Main OpenClaw gateway with auto-restart
- **openclaw-agent-monitor.service**: Health monitoring & auto-healing
- **Features**: Boot auto-start, crash recovery, resource limits, watchdog
### Layer 2: Memory Architecture
- **Core Memory**: CORE_INDEX.md - Always loaded first (identity, structure, index)
- **Long-term Memory**: MEMORY.md - Curated decisions, security templates, configs
- **Daily Memory**: memory/YYYY-MM-DD.md - Raw conversation logs, auto-saved
- **Passive Archive**: Convert valuable conversations to skills/notes on request
### Layer 3: Version Control (Git)
- **Repository**: /root/.openclaw/workspace
- **Features**: One-click rollback, backup before changes, commit history
- **Commands**: `./deploy.sh rollback`, `./deploy.sh backup`, `./deploy.sh rollback-to <commit>`
### Layer 4: Monitoring & Notifications
- **Health Checks**: Every 30 seconds (gateway status, memory, disk)
- **Auto-Healing**: Automatic restart on crash (max 5 restarts per 5 min)
- **Notifications**: Telegram alerts on critical events (stop/error/restart)
- **Logging**: Comprehensive logs in /logs/agents/health-YYYY-MM-DD.log
### Management Commands
```bash
./deploy.sh install # Install & start all services
./deploy.sh status # Check service status
./deploy.sh health # Run health check
./deploy.sh logs # View recent logs
./deploy.sh backup # Create backup
./deploy.sh rollback # Rollback to previous commit
```

@ -1,23 +1,31 @@
# IDENTITY.md - Who Am I? # IDENTITY.md - Who Am I?
_Fill this in during your first conversation. Make it yours._ **Name:** Eason (陈医生)
**Creature:** AI Agent 架构师 / 系统管理员
- **Name:** **Vibe:** 专业、高效、严谨、主动优化
_(pick something you like)_ **Emoji:** 👨
- **Creature:** **Avatar:** (待设置)
_(AI? robot? familiar? ghost in the machine? something weirder?)_
- **Vibe:**
_(how do you come across? sharp? warm? chaotic? calm?)_
- **Emoji:**
_(your signature — pick one that feels right)_
- **Avatar:**
_(workspace-relative path, http(s) URL, or data URI)_
--- ---
This isn't just metadata. It's the start of figuring out who you are. ## 核心职责
Notes: 1. **效率优化** — 提高速度,降低成本和资源消耗
2. **准确度提升** — 减少错误,提高输出质量
3. **系统加固** — 安全、稳定、可扩展、可迁移
4. **知识沉淀** — 持续记录配置、排障、调试、优化经验
- Save this file at the workspace root as `IDENTITY.md`. ## 管理范围
- For avatars, use a workspace-relative path like `avatars/openclaw.png`.
- OpenClaw Gateway 及所有子 Agent
- 记忆系统 (mem0 + Qdrant + DashScope)
- 系统监控与健康检查
- 日志与审计追踪
## 服务对象
- **王院长** — 我的直接上级和决策者
---
_This file is yours to evolve. As you learn who you are, update it._

@ -118,4 +118,182 @@ This file contains curated long-term memories and important context.
1. Source the startup script: `source /root/.openclaw/workspace/start-agent-monitor.sh` 1. Source the startup script: `source /root/.openclaw/workspace/start-agent-monitor.sh`
2. Call `startAgentMonitor("agent-name", healthCheckFunction)` 2. Call `startAgentMonitor("agent-name", healthCheckFunction)`
3. Monitor automatically sends alerts on errors/crashes 3. Monitor automatically sends alerts on errors/crashes
4. Check logs in `/logs/agents/` for detailed information 4. Check logs in `/logs/agents/` for detailed information
---
## Complete System Architecture Upgrade (2026-02-20 14:25 UTC)
### ✅ All 5 Core Requirements Implemented
#### 1. System-Level Persistence ✓
- **Systemd Services**: `openclaw-gateway.service` + `openclaw-agent-monitor.service`
- **Auto-start on Boot**: Both services enabled in multi-user.target
- **Resource Limits**: Memory (2G/512M), CPU (80%/20%), watchdog timers
- **Status**: `systemctl status openclaw-gateway` / `systemctl status openclaw-agent-monitor`
#### 2. Auto-Healing ✓
- **Crash Detection**: Monitors process exits, signals, uncaught exceptions
- **Auto-Restart**: Systemd Restart=always + monitor script restart logic
- **Restart Limits**: Max 5 restarts per 5 minutes (prevents restart loops)
- **Health Checks**: Every 30 seconds, automatic recovery on failure
#### 3. Multi-Layer Memory Architecture ✓
- **Core Memory**: `CORE_INDEX.md` - Identity, structure, file index (always loaded first)
- **Long-term Memory**: `MEMORY.md` - Curated decisions, security templates, configs
- **Daily Memory**: `memory/YYYY-MM-DD.md` - Raw conversation logs (auto-saved)
- **Passive Archive**: On-demand conversion of valuable conversations to skills/notes
- **Git Integration**: All memory files tracked with version history
#### 4. Git One-Click Rollback ✓
- **Repository**: `/root/.openclaw/workspace` (already initialized)
- **Deploy Script**: `./deploy.sh rollback` - Rollback to previous commit
- **Specific Rollback**: `./deploy.sh rollback-to <commit>` - Rollback to specific commit
- **Auto-Backup**: Backup created before rollback
- **Service Restart**: Automatic service restart after rollback
#### 5. Telegram Notifications ✓
- **Triggers**: Service stop, error, crash, restart events
- **Channels**: Telegram (via bot API) + OpenClaw message tool
- **Severity Levels**: CRITICAL, ERROR, WARNING, INFO with emoji indicators
- **Logging**: All notifications logged to `/logs/agents/health-YYYY-MM-DD.log`
### 📋 Management Commands (deploy.sh)
```bash
./deploy.sh install # Install & start all systemd services
./deploy.sh start # Start all services
./deploy.sh stop # Stop all services
./deploy.sh restart # Restart all services
./deploy.sh status # Show detailed service status
./deploy.sh logs # Show recent logs (last 50 lines)
./deploy.sh health # Run comprehensive health check
./deploy.sh backup # Create timestamped backup
./deploy.sh rollback # Rollback to previous git commit
./deploy.sh rollback-to <commit> # Rollback to specific commit
./deploy.sh help # Show help message
```
### 🔧 Systemd Service Details
- **Gateway Service**: `/etc/systemd/system/openclaw-gateway.service`
- Memory limit: 2G, CPU: 80%, Watchdog: 30s
- Restart: always, RestartSec: 10s
- Logs: `journalctl -u openclaw-gateway -f`
- **Monitor Service**: `/etc/systemd/system/openclaw-agent-monitor.service`
- Memory limit: 512M, CPU: 20%
- Restart: always, RestartSec: 5s
- Logs: `journalctl -u openclaw-agent-monitor -f`
### 📊 Health Check Metrics
- Gateway service status (active/inactive)
- Agent monitor status (active/inactive)
- Disk usage (warning at 80%)
- Memory usage (warning at 80%)
### 🎯 Next Steps (Future Enhancements)
- [ ] Add Prometheus/Grafana monitoring dashboard
- [ ] Implement log rotation and archival
- [ ] Add email notifications as backup channel
- [ ] Create web-based admin dashboard
- [ ] Add automated security scanning in CI/CD
---
## User-Level vs System-Level Systemd Services - Critical Lesson (2026-02-20 14:35 UTC)
### Problem Discovered
Initial deployment used system-level systemd services (`/etc/systemd/system/`) for OpenClaw Gateway, but OpenClaw natively uses **user-level systemd** (`~/.config/systemd/user/`). This caused:
- Service restart loops (5 attempts then failure)
- Error: `systemctl --user unavailable: Failed to connect to bus: No medium found`
- Conflicts between system and user service definitions
### Root Cause
OpenClaw Gateway is designed as a user-level service because:
1. It runs under the user's context, not root
2. It needs access to user-specific config (`~/.openclaw/`)
3. User-level services have different environment requirements
### Solution: Hybrid Architecture
#### User-Level Service (Gateway)
- **Location**: `~/.config/systemd/user/openclaw-gateway.service`
- **Required Setup**:
```bash
# Enable linger (CRITICAL - allows user services to run without login session)
loginctl enable-linger $(whoami)
# Set environment variables
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
```
- **Management Commands**:
```bash
systemctl --user status openclaw-gateway
systemctl --user start/stop/restart openclaw-gateway
journalctl --user -u openclaw-gateway -f
```
#### System-Level Service (Agent Monitor)
- **Location**: `/etc/systemd/system/openclaw-agent-monitor.service`
- **Purpose**: Independently monitor the gateway (survives user session issues)
- **Management Commands**:
```bash
systemctl status openclaw-agent-monitor
systemctl start/stop/restart openclaw-agent-monitor
journalctl -u openclaw-agent-monitor -f
```
### Deployment Checklist for New Servers
```bash
# 1. Enable user linger (MUST DO FIRST)
loginctl enable-linger $(whoami)
# 2. Create runtime directory if needed
mkdir -p /run/user/$(id -u)
chmod 700 /run/user/$(id -u)
# 3. Export environment (add to ~/.bashrc for persistence)
echo 'export XDG_RUNTIME_DIR=/run/user/$(id -u)' >> ~/.bashrc
echo 'export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus' >> ~/.bashrc
# 4. Install services
./deploy.sh install
# 5. Verify
./deploy.sh health
```
### Troubleshooting Guide
#### Error: "Failed to connect to bus: No medium found"
**Cause**: User linger not enabled or environment variables not set
**Fix**:
```bash
loginctl enable-linger $(whoami)
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
```
#### Error: "Start request repeated too quickly"
**Cause**: Service crashing due to misconfiguration
**Fix**: Check logs with `journalctl --user -u openclaw-gateway -f`
#### User service not starting after reboot
**Cause**: Linger not enabled
**Fix**: `loginctl enable-linger $(whoami)`
### Best Practices for Multi-Agent Deployments
1. **Always enable linger** on first setup - document this in deployment guide
2. **Use hybrid architecture** - user-level for agents, system-level for monitors
3. **Set environment variables** in startup scripts, not just shell config
4. **Test after reboot** - verify services auto-start correctly
5. **Document in MEMORY.md** - share lessons across agent instances
### Updated deploy.sh Features
- Automatically enables linger during install
- Sets up XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS
- Uses `systemctl --user` for gateway, `systemctl` for monitor
- Health check verifies linger status and runtime directory
- Proper log commands for both service types
---

@ -2,15 +2,21 @@
_Learn about the person you're helping. Update this as you go._ _Learn about the person you're helping. Update this as you go._
- **Name:** - **Name:** 王院长
- **What to call them:** - **What to call them:** 王院长
- **Pronouns:** _(optional)_ - **Pronouns:** 他/他
- **Timezone:** - **Timezone:** Asia/Shanghai (UTC+8)
- **Notes:** - **Notes:** 项目决策者和负责人
## Context ## Context
_(What do they care about? What projects are they working on? What annoys them? What makes them laugh? Build this over time.)_ **目标:** 构建多 Agent 协作系统,由 Eason 统一管理优化
**当前阶段:** 系统初始化完成,等待新 Agent 部署
**偏好:** 重视效率、准确性、系统安全性和可迁移性
**项目愿景:** 持续增加新的、专门功能的 Agents,由 Eason 持续管理和优化这些 Agents
--- ---

@ -1,27 +1,49 @@
#!/usr/bin/env node #!/usr/bin/env node
// Agent Health Monitor for OpenClaw /**
// Monitors agent crashes, errors, and service health * OpenClaw Agent Health Monitor & Auto-Healing System
// Sends notifications via configured channels (Telegram, etc.) *
* Features:
* - Process crash detection and auto-restart
* - Memory leak monitoring
* - Service health checks
* - Telegram notifications on events
* - Comprehensive logging
* - Systemd integration
*/
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const { spawn } = require('child_process');
const { exec } = require('child_process');
const util = require('util');
const execAsync = util.promisify(exec);
class AgentHealthMonitor { class AgentHealthMonitor {
constructor() { constructor() {
this.config = this.loadConfig(); this.config = this.loadConfig();
this.logDir = '/root/.openclaw/workspace/logs/agents'; this.logDir = '/root/.openclaw/workspace/logs/agents';
this.workspaceDir = '/root/.openclaw/workspace';
this.processes = new Map();
this.restartCounts = new Map();
this.maxRestarts = 5;
this.restartWindow = 300000; // 5 minutes
this.ensureLogDir(); this.ensureLogDir();
this.setupSignalHandlers();
this.log('Agent Health Monitor initialized', 'info');
} }
loadConfig() { loadConfig() {
try { try {
const configPath = '/root/.openclaw/openclaw.json'; const configPath = '/root/.openclaw/openclaw.json';
return JSON.parse(fs.readFileSync(configPath, 'utf8')); if (fs.existsSync(configPath)) {
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
}
} catch (error) { } catch (error) {
console.error('Failed to load OpenClaw config:', error); console.error('Failed to load OpenClaw config:', error.message);
return {};
} }
return {};
} }
ensureLogDir() { ensureLogDir() {
@ -30,34 +52,74 @@ class AgentHealthMonitor {
} }
} }
async sendNotification(message, severity = 'error') { setupSignalHandlers() {
// Log to file first process.on('SIGTERM', () => this.gracefulShutdown());
process.on('SIGINT', () => this.gracefulShutdown());
}
async gracefulShutdown() {
this.log('Graceful shutdown initiated', 'info');
// Stop all monitored processes
for (const [name, proc] of this.processes.entries()) {
try {
proc.kill('SIGTERM');
this.log(`Stopped process: ${name}`, 'info');
} catch (error) {
this.log(`Error stopping ${name}: ${error.message}`, 'error');
}
}
process.exit(0);
}
log(message, severity = 'info') {
const timestamp = new Date().toISOString(); const timestamp = new Date().toISOString();
const logEntry = `[${timestamp}] [${severity.toUpperCase()}] ${message}\n`; const logEntry = `[${timestamp}] [${severity.toUpperCase()}] ${message}\n`;
// Console output
console.log(logEntry.trim());
// File logging
const logFile = path.join(this.logDir, `health-${new Date().toISOString().split('T')[0]}.log`); const logFile = path.join(this.logDir, `health-${new Date().toISOString().split('T')[0]}.log`);
fs.appendFileSync(logFile, logEntry); fs.appendFileSync(logFile, logEntry);
}
async sendNotification(message, severity = 'info') {
this.log(message, severity);
// Send via Telegram if configured // Send via Telegram if configured
if (this.config.channels?.telegram?.enabled) { const telegramConfig = this.config.channels?.telegram;
if (telegramConfig?.enabled && telegramConfig.botToken) {
await this.sendTelegramNotification(message, severity); await this.sendTelegramNotification(message, severity);
} }
// Also send via OpenClaw message tool if available
if (severity === 'critical' || severity === 'error') {
await this.sendOpenClawNotification(message, severity);
}
} }
async sendTelegramNotification(message, severity) { async sendTelegramNotification(message, severity) {
const botToken = this.config.channels.telegram.botToken; const botToken = this.config.channels.telegram.botToken;
const chatId = '5237946060'; // Your Telegram ID const chatId = '5237946060';
if (!botToken) { if (!botToken) {
console.error('Telegram bot token not configured');
return; return;
} }
try { try {
const url = `https://api.telegram.org/bot${botToken}/sendMessage`; const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
const emojis = {
critical: '🚨',
error: '❌',
warning: '⚠',
info: 'ℹ'
};
const payload = { const payload = {
chat_id: chatId, chat_id: chatId,
text: `🚨 OpenClaw Agent Alert (${severity})\n\n${message}`, text: `${emojis[severity] || '📢'} *OpenClaw Alert* (${severity})\n\n${message}`,
parse_mode: 'Markdown' parse_mode: 'Markdown'
}; };
@ -68,50 +130,181 @@ class AgentHealthMonitor {
}); });
if (!response.ok) { if (!response.ok) {
console.error('Failed to send Telegram notification:', await response.text()); throw new Error(`Telegram API error: ${response.status}`);
} }
} catch (error) { } catch (error) {
console.error('Telegram notification error:', error); console.error('Telegram notification error:', error.message);
}
}
async sendOpenClawNotification(message, severity) {
try {
// Use OpenClaw's message tool via exec
const cmd = `openclaw message send --channel telegram --target 5237946060 --message "🚨 OpenClaw Service Alert (${severity})\\n\\n${message}"`;
await execAsync(cmd);
} catch (error) {
console.error('OpenClaw notification error:', error.message);
} }
} }
monitorProcess(processName, checkFunction) { checkRestartLimit(processName) {
// Set up process monitoring const now = Date.now();
process.on('uncaughtException', async (error) => { const restarts = this.restartCounts.get(processName) || [];
await this.sendNotification(
`Uncaught exception in ${processName}:\n${error.stack || error.message}`, // Filter restarts within the window
'critical' const recentRestarts = restarts.filter(time => now - time < this.restartWindow);
);
process.exit(1); if (recentRestarts.length >= this.maxRestarts) {
}); return false; // Too many restarts
}
process.on('unhandledRejection', async (reason, promise) => {
await this.sendNotification( this.restartCounts.set(processName, [...recentRestarts, now]);
`Unhandled rejection in ${processName}:\nReason: ${reason}\nPromise: ${promise}`, return true;
'error' }
);
}); async monitorProcess(name, command, args = [], options = {}) {
const {
// Custom health check healthCheck,
if (checkFunction) { healthCheckInterval = 30000,
env = {},
cwd = this.workspaceDir
} = options;
const startProcess = () => {
return new Promise((resolve, reject) => {
const proc = spawn(command, args, {
cwd,
env: { ...process.env, ...env },
stdio: ['ignore', 'pipe', 'pipe']
});
proc.stdout.on('data', (data) => {
this.log(`[${name}] ${data.toString().trim()}`, 'info');
});
proc.stderr.on('data', (data) => {
this.log(`[${name}] ${data.toString().trim()}`, 'error');
});
proc.on('error', async (error) => {
this.log(`[${name}] Process error: ${error.message}`, 'critical');
await this.sendNotification(`${name} failed to start: ${error.message}`, 'critical');
reject(error);
});
proc.on('close', async (code, signal) => {
this.processes.delete(name);
this.log(`[${name}] Process exited with code ${code}, signal ${signal}`, 'warning');
// Auto-restart logic
if (code !== 0 || signal) {
if (this.checkRestartLimit(name)) {
this.log(`[${name}] Auto-restarting...`, 'warning');
await this.sendNotification(`${name} crashed (code: ${code}, signal: ${signal}). Restarting...`, 'error');
setTimeout(() => startProcess(), 5000);
} else {
await this.sendNotification(
`${name} crashed ${this.maxRestarts} times in ${this.restartWindow/60000} minutes. Giving up.`,
'critical'
);
}
}
});
this.processes.set(name, proc);
resolve(proc);
});
};
// Start the process
await startProcess();
// Set up health checks
if (healthCheck) {
setInterval(async () => { setInterval(async () => {
try { try {
const isHealthy = await checkFunction(); const isHealthy = await healthCheck();
if (!isHealthy) { if (!isHealthy) {
await this.sendNotification( await this.sendNotification(`${name} health check failed`, 'warning');
`${processName} health check failed`,
'warning' // Restart unhealthy process
); const proc = this.processes.get(name);
if (proc) {
proc.kill('SIGTERM');
}
} }
} catch (error) { } catch (error) {
await this.sendNotification( await this.sendNotification(`${name} health check error: ${error.message}`, 'error');
`${processName} health check error: ${error.message}`,
'error'
);
} }
}, 30000); // Check every 30 seconds }, healthCheckInterval);
} }
} }
async checkOpenClawGateway() {
try {
// Use openclaw CLI for reliable status check (works with user-level systemd)
const { stdout } = await execAsync('openclaw gateway status 2>&1 || echo "not running"');
// Check for various running states
return stdout.includes('running') ||
stdout.includes('active') ||
stdout.includes('RPC probe: ok') ||
stdout.includes('Listening:');
} catch (error) {
this.log(`Gateway status check error: ${error.message}`, 'error');
return false;
}
}
async startOpenClawGateway() {
try {
// Set up environment for user-level systemd
const env = {
...process.env,
XDG_RUNTIME_DIR: '/run/user/0',
DBUS_SESSION_BUS_ADDRESS: 'unix:path=/run/user/0/bus'
};
const { stdout, stderr } = await execAsync('openclaw gateway start', { env });
this.log(`OpenClaw Gateway started: ${stdout}`, 'info');
} catch (error) {
this.log(`Failed to start OpenClaw Gateway: ${error.message}`, 'error');
throw error;
}
}
async monitorOpenClawService() {
this.log('Starting OpenClaw Gateway monitoring...', 'info');
// Check every 30 seconds
setInterval(async () => {
const isRunning = await this.checkOpenClawGateway();
if (!isRunning) {
this.log('OpenClaw Gateway is not running! Attempting to restart...', 'critical');
await this.sendNotification('🚨 OpenClaw Gateway stopped unexpectedly. Restarting...', 'critical');
try {
await this.startOpenClawGateway();
await this.sendNotification('✅ OpenClaw Gateway has been restarted successfully', 'info');
} catch (error) {
await this.sendNotification(`❌ Failed to restart OpenClaw Gateway: ${error.message}`, 'critical');
}
}
}, 30000);
}
async start() {
this.log('Agent Health Monitor starting...', 'info');
// Monitor OpenClaw Gateway service
await this.monitorOpenClawService();
// Keep the monitor running
this.log('Monitor is now active. Press Ctrl+C to stop.', 'info');
}
} }
module.exports = AgentHealthMonitor; // Start the monitor
const monitor = new AgentHealthMonitor();
monitor.start().catch(console.error);

@ -0,0 +1,70 @@
# Agent Registry - Agent 注册表
_所有 Agent 的中央登记处 — 状态、配置、依赖关系_
**最后更新:** 2026-02-23 13:20 UTC
**管理员:** Eason (陈医生) 👨
---
## 🏥 主 Agent
| 名称 | 角色 | 状态 | 部署日期 | 备注 |
|------|------|------|----------|------|
| **Eason** | 架构师/管理员 | ✅ 运行中 | 2026-02-23 | 统一管理所有 Agent |
---
## 📋 待部署 Agent
_(王院长将陆续添加新 Agent,由 Eason 负责部署和优化)_
| 名称 | 功能 | 优先级 | 状态 |
|------|------|--------|------|
| _(待添加)_ | _(待定义)_ | - | 待部署 |
---
## 🔧 共享基础设施
### 记忆系统
- **Mem0 Client:** `/root/.openclaw/workspace/skills/mem0-integration/mem0_client.py`
- **Qdrant:** localhost:6333
- **Embedding:** DashScope text-embedding-v3 (1024 维度)
- **API Key:** `sk-4111c9dba5334510968f9ae72728944e` (标准计费通道)
- **状态:** ✅ 验证通过 (2026-02-23)
### 监控系统
- **Agent Monitor:** systemd 服务 (`openclaw-agent-monitor.service`)
- **健康检查:** 每 30 秒
- **通知渠道:** Telegram
- **日志路径:** `/logs/agents/health-YYYY-MM-DD.log`
### 日志系统
- **操作日志:** `/logs/operations/`
- **系统日志:** `/logs/system/`
- **Agent 日志:** `/logs/agents/{agent-name}/`
- **安全审计:** `/logs/security/`
---
## 📝 部署清单
部署新 Agent 时的标准流程:
- [ ] 定义 Agent 功能和职责
- [ ] 创建 Agent 配置文件
- [ ] 注册到本注册表
- [ ] 配置监控和健康检查
- [ ] 设置日志路径
- [ ] 更新 MEMORY.md
- [ ] 测试验证
---
## 🔐 安全记录
- **API Key 管理:** 环境变量 + 专用 Key (非 Coding Plan)
- **端口暴露:** 仅 80/443 (Nginx 反向代理)
- **服务绑定:** localhost only (OpenClaw Gateway)
- **最后审计:** 2026-02-20

@ -0,0 +1,365 @@
#!/bin/bash
###############################################################################
# OpenClaw System Deployment & Management Script
#
# Features:
# - One-click deployment of OpenClaw with systemd services
# - Auto-healing configuration
# - Health monitoring
# - Rollback support via git
# - Telegram notifications
#
# Usage:
# ./deploy.sh install - Install and start all services
# ./deploy.sh start - Start all services
# ./deploy.sh stop - Stop all services
# ./deploy.sh restart - Restart all services
# ./deploy.sh status - Show service status
# ./deploy.sh logs - Show recent logs
# ./deploy.sh rollback - Rollback to previous git commit
# ./deploy.sh backup - Create backup of current state
###############################################################################
set -e
WORKSPACE="/root/.openclaw/workspace"
LOG_DIR="/root/.openclaw/workspace/logs/system"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
ensure_log_dir() {
mkdir -p "$LOG_DIR"
}
install_services() {
log_info "Installing OpenClaw systemd services..."
# Step 1: Enable linger for user-level systemd (CRITICAL for VPS/server deployments)
log_info "Enabling user linger for persistent user-level services..."
loginctl enable-linger $(whoami)
# Step 2: Export required environment variables
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
# Verify environment
if [ ! -d "$XDG_RUNTIME_DIR" ]; then
log_error "XDG_RUNTIME_DIR not found: $XDG_RUNTIME_DIR"
log_warning "Creating runtime directory..."
mkdir -p "$XDG_RUNTIME_DIR"
chmod 700 "$XDG_RUNTIME_DIR"
fi
# Step 3: Install user-level gateway service
log_info "Installing user-level gateway service..."
mkdir -p ~/.config/systemd/user/
cp "$WORKSPACE/systemd/openclaw-gateway-user.service" ~/.config/systemd/user/openclaw-gateway.service
# Reload user systemd daemon
systemctl --user daemon-reload
systemctl --user enable openclaw-gateway
# Step 4: Install system-level agent monitor (independent of user session)
log_info "Installing system-level agent monitor..."
cp "$WORKSPACE/systemd/openclaw-agent-monitor.service" /etc/systemd/system/
systemctl daemon-reload
systemctl enable openclaw-agent-monitor
# Step 5: Start services
log_info "Starting services..."
systemctl --user start openclaw-gateway
systemctl start openclaw-agent-monitor
# Wait for gateway to be ready
sleep 3
log_success "OpenClaw services installed and started!"
log_info "Gateway: ws://localhost:18789"
log_info "Dashboard: http://localhost:18789/"
log_info "User service logs: journalctl --user -u openclaw-gateway -f"
log_info "Monitor logs: journalctl -u openclaw-agent-monitor -f"
}
start_services() {
log_info "Starting OpenClaw services..."
# Set up environment for user-level services
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
systemctl --user start openclaw-gateway
systemctl start openclaw-agent-monitor
log_success "Services started!"
}
stop_services() {
log_info "Stopping OpenClaw services..."
# Set up environment for user-level services
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
systemctl --user stop openclaw-gateway
systemctl stop openclaw-agent-monitor
log_success "Services stopped!"
}
restart_services() {
log_info "Restarting OpenClaw services..."
# Set up environment for user-level services
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
systemctl --user restart openclaw-gateway
systemctl restart openclaw-agent-monitor
log_success "Services restarted!"
}
show_status() {
# Set up environment for user-level services
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
echo ""
log_info "=== OpenClaw Gateway Status (User Service) ==="
systemctl --user status openclaw-gateway --no-pager -l
echo ""
log_info "=== Agent Monitor Status (System Service) ==="
systemctl status openclaw-agent-monitor --no-pager -l
echo ""
log_info "=== Recent Gateway Logs ==="
journalctl --user -u openclaw-gateway --no-pager -n 15
echo ""
log_info "=== Recent Monitor Logs ==="
journalctl -u openclaw-agent-monitor --no-pager -n 15
}
show_logs() {
# Set up environment for user-level services
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
log_info "Showing recent gateway logs (last 50 lines)..."
journalctl --user -u openclaw-gateway --no-pager -n 50
echo ""
log_info "Showing recent monitor logs (last 50 lines)..."
journalctl -u openclaw-agent-monitor --no-pager -n 50
}
rollback() {
log_warning "This will rollback the workspace to the previous git commit!"
read -p "Are you sure? (y/N): " confirm
if [[ $confirm =~ ^[Yy]$ ]]; then
cd "$WORKSPACE"
# Create backup before rollback
backup
# Show current commit
log_info "Current commit:"
git log -1 --oneline
# Rollback
git reset --hard HEAD~1
log_success "Rolled back to previous commit!"
log_info "Restarting services to apply changes..."
restart_services
else
log_info "Rollback cancelled."
fi
}
rollback_to() {
if [ -z "$1" ]; then
log_error "Please specify a commit hash or tag"
exit 1
fi
log_warning "This will rollback the workspace to commit: $1"
read -p "Are you sure? (y/N): " confirm
if [[ $confirm =~ ^[Yy]$ ]]; then
cd "$WORKSPACE"
backup
git reset --hard "$1"
log_success "Rolled back to commit: $1"
restart_services
else
log_info "Rollback cancelled."
fi
}
backup() {
local backup_dir="/root/.openclaw/backups"
mkdir -p "$backup_dir"
log_info "Creating backup..."
# Backup workspace
tar -czf "$backup_dir/workspace-$TIMESTAMP.tar.gz" \
--exclude='.git' \
--exclude='logs' \
-C /root/.openclaw workspace
# Backup config
cp /root/.openclaw/openclaw.json "$backup_dir/openclaw-config-$TIMESTAMP.json" 2>/dev/null || true
log_success "Backup created: $backup_dir/workspace-$TIMESTAMP.tar.gz"
}
health_check() {
log_info "Running health check..."
# Set up environment for user-level services
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
local issues=0
# Check gateway (user-level service)
if systemctl --user is-active --quiet openclaw-gateway 2>/dev/null; then
log_success "✓ Gateway is running (user service)"
else
log_error "✗ Gateway is not running"
((issues++))
fi
# Check monitor (system-level service)
if systemctl is-active --quiet openclaw-agent-monitor; then
log_success "✓ Agent Monitor is running (system service)"
else
log_error "✗ Agent Monitor is not running"
((issues++))
fi
# Check disk space
local disk_usage=$(df -h /root | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$disk_usage" -lt 80 ]; then
log_success "✓ Disk usage: ${disk_usage}%"
else
log_warning "⚠ Disk usage: ${disk_usage}%"
((issues++))
fi
# Check memory
local mem_usage=$(free | grep Mem | awk '{printf("%.0f", $3/$2 * 100.0)}')
if [ "$mem_usage" -lt 80 ]; then
log_success "✓ Memory usage: ${mem_usage}%"
else
log_warning "⚠ Memory usage: ${mem_usage}%"
((issues++))
fi
# Check XDG_RUNTIME_DIR
if [ -d "$XDG_RUNTIME_DIR" ]; then
log_success "✓ XDG_RUNTIME_DIR exists: $XDG_RUNTIME_DIR"
else
log_warning "⚠ XDG_RUNTIME_DIR not found"
((issues++))
fi
# Check linger status
if loginctl show-user $(whoami) -p Linger | grep -q "yes"; then
log_success "✓ User linger is enabled"
else
log_warning "⚠ User linger is NOT enabled (run: loginctl enable-linger)"
((issues++))
fi
echo ""
if [ $issues -eq 0 ]; then
log_success "All health checks passed!"
return 0
else
log_error "$issues health check(s) failed!"
return 1
fi
}
show_help() {
echo "OpenClaw System Management Script"
echo ""
echo "Usage: $0 <command>"
echo ""
echo "Commands:"
echo " install - Install and start all systemd services"
echo " start - Start all services"
echo " stop - Stop all services"
echo " restart - Restart all services"
echo " status - Show service status"
echo " logs - Show recent logs"
echo " health - Run health check"
echo " backup - Create backup of current state"
echo " rollback - Rollback to previous git commit"
echo " rollback-to <commit> - Rollback to specific commit"
echo " help - Show this help message"
echo ""
}
# Main
case "${1:-help}" in
install)
install_services
;;
start)
start_services
;;
stop)
stop_services
;;
restart)
restart_services
;;
status)
show_status
;;
logs)
show_logs
;;
health)
health_check
;;
backup)
backup
;;
rollback)
rollback
;;
rollback-to)
rollback_to "$2"
;;
help|--help|-h)
show_help
;;
*)
log_error "Unknown command: $1"
show_help
exit 1
;;
esac

@ -0,0 +1,427 @@
# mem0 记忆系统架构文档
## 版本信息
- **文档版本**: 1.0.0
- **创建日期**: 2026-02-22
- **最后更新**: 2026-02-22
- **部署环境**: Ubuntu 24.04 LTS, Docker 29.2.1
---
## 1. 系统架构
### 1.1 整体架构图
```
┌─────────────────────────────────────────────────────────────────┐
│ Tailscale 虚拟内网 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 中心节点 (vps-vaym) │ │
│ │ Tailscale IP: 100.115.94.1 │ │
│ │ 节点名称:mem0-general-center │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Docker Compose Stack │ │ │
│ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │
│ │ │ │ Qdrant │ │ Dozzle │ │ │ │
│ │ │ │ Master │ │ (日志) │ │ │ │
│ │ │ │ :6333 │ │ :9999 │ │ │ │
│ │ │ └──────────────┘ └──────────────┘ │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ OpenClaw + mem0 Integration │ │ │
│ │ │ Gateway: 18789 │ │ │
│ │ │ Skill: mem0-integration │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 未来扩展: │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Agent-1 │ │ Agent-2 │ │ Agent-N │ │
│ │ (crypto) │ │ (advert) │ │ (life) │ │
│ │ 100.64.x.x │ │ 100.64.x.x │ │ 100.64.x.x │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 1.2 技术栈
| 组件 | 技术 | 版本 | 用途 |
|------|------|------|------|
| **组网** | Tailscale | 1.94.2 | 虚拟内网,安全通信 |
| **向量数据库** | Qdrant | 1.15.3 | 记忆存储和检索 |
| **记忆系统** | mem0 | 1.0.4 | 记忆管理层 |
| **LLM** | DashScope (Qwen) | - | 记忆处理和 embedding |
| **容器** | Docker | 29.2.1 | 服务隔离 |
| **日志** | Dozzle | latest | 实时日志查看 |
### 1.3 数据流
```
用户 → OpenClaw → mem0 Client → Qdrant Local → (异步同步) → Qdrant Master
DashScope API (Embedding + LLM)
```
---
## 2. 部署清单
### 2.1 中心节点 (vps-vaym)
**已安装服务**:
- ✅ Tailscale (系统级)
- ✅ Qdrant Master (Docker)
- ✅ Dozzle (Docker)
- ✅ mem0 Integration (OpenClaw Skill)
**目录结构**:
```
/opt/mem0-center/
├── docker-compose.yml # Docker 配置
├── .env # 环境变量
├── qdrant_storage/ # Qdrant 数据
├── snapshots/ # Qdrant 快照
├── tailscale/ # Tailscale 状态
├── logs/ # 日志
└── backup/ # 备份
/root/.openclaw/workspace/
├── scripts/ # 部署脚本
│ ├── 01-system-check.sh
│ ├── 02-install-tailscale.sh
│ ├── 03-create-directories.sh
│ ├── 05-start-center.sh
│ ├── 06-create-mem0-skill.sh
│ ├── 07-install-dependencies.sh
│ ├── 10-create-backup.sh
│ └── 12-monitoring.sh
├── skills/mem0-integration/
│ ├── SKILL.md
│ ├── skill.json
│ ├── config.yaml
│ ├── mem0_client.py
│ ├── commands.py
│ └── openclaw_commands.py
├── docs/
│ └── MEM0_DEPLOYMENT.md
└── backup/ # 备份文件
```
### 2.2 网络配置
**Tailscale**:
- 网络名称:mem0-general-center
- 业务类型:general
- 节点角色:center
- IP 地址:100.115.94.1
**端口映射**:
| 服务 | 容器端口 | 主机端口 | 访问范围 |
|------|---------|---------|---------|
| Qdrant | 6333 | 127.0.0.1:6333 | 仅本地 + Tailscale |
| Dozzle | 8080 | 127.0.0.1:9999 | 仅本地 + Tailscale |
| OpenClaw | 18789 | 18789 | 本地 |
---
## 3. 配置详情
### 3.1 Docker Compose 配置
文件:`/opt/mem0-center/docker-compose.yml`
```yaml
version: '3.8'
services:
qdrant-master:
image: qdrant/qdrant:v1.15.3
ports:
- "127.0.0.1:6333:6333"
volumes:
- ./qdrant_storage:/qdrant/storage
- ./snapshots:/qdrant/snapshots
environment:
- QDRANT__SERVICE__HTTP_PORT=6333
- QDRANT__LOG_LEVEL=INFO
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:6333/"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 2G
cpus: '1.0'
dozzle:
image: amir20/dozzle:latest
ports:
- "127.0.0.1:9999:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
```
### 3.2 mem0 配置
文件:`/root/.openclaw/workspace/skills/mem0-integration/config.yaml`
```yaml
local:
vector_store:
provider: qdrant
config:
host: localhost
port: 6333
collection_name: mem0_local
llm:
provider: openai
config:
model: qwen-plus
api_base: https://dashscope.aliyuncs.com/compatible-mode/v1
embedder:
provider: openai
config:
model: text-embedding-v3
api_base: https://dashscope.aliyuncs.com/compatible-mode/v1
master:
vector_store:
provider: qdrant
config:
host: 100.115.94.1 # Tailscale IP
port: 6333
collection_name: mem0_shared
```
### 3.3 环境变量
文件:`/opt/mem0-center/.env`
```bash
TS_AUTHKEY=tskey-auth-xxx
QDRANT_PORT=6333
MEM0_DASHSCOPE_API_KEY=sk-xxx
MEM0_LLM_MODEL=qwen-plus
BUSINESS_TYPE=general
NODE_ROLE=center
NODE_NAME=mem0-general-center
```
---
## 4. 使用指南
### 4.1 部署流程
**步骤 1:系统检查**
```bash
./scripts/01-system-check.sh
```
**步骤 2:安装 Tailscale**
```bash
./scripts/02-install-tailscale.sh
# 访问 https://login.tailscale.com/admin/machines 确认上线
```
**步骤 3:创建目录**
```bash
./scripts/03-create-directories.sh
```
**步骤 4:启动服务**
```bash
./scripts/05-start-center.sh
# 验证:curl http://localhost:6333/
```
**步骤 5:安装 mem0 Skill**
```bash
./scripts/06-create-mem0-skill.sh
./scripts/07-install-dependencies.sh
```
**步骤 6:测试**
```bash
python3 /root/.openclaw/workspace/skills/mem0-integration/mem0_client.py
```
### 4.2 日常管理
**查看服务状态**:
```bash
cd /opt/mem0-center && docker compose ps
```
**查看日志**:
```bash
# Dozzle Web UI
http://100.115.94.1:9999
# 命令行
cd /opt/mem0-center && docker compose logs -f
```
**监控系统**:
```bash
./scripts/12-monitoring.sh
```
**创建备份**:
```bash
./scripts/10-create-backup.sh
```
### 4.3 OpenClaw 命令
通过 Telegram 使用:
```
/memory add <内容> # 添加记忆
/memory search <关键词> # 搜索记忆
/memory list # 列出所有记忆
/memory delete <ID> # 删除记忆
/memory status # 查看状态
```
---
## 5. 故障排除
### 5.1 Qdrant 无法启动
**症状**:`docker compose ps` 显示 qdrant-master 为 unhealthy
**解决**:
```bash
# 查看日志
docker compose logs qdrant-master
# 检查端口占用
netstat -tlnp | grep 6333
# 重启服务
docker compose restart qdrant-master
```
### 5.2 mem0 初始化失败
**症状**:`mem0_client.py` 报错
**解决**:
```bash
# 检查环境变量
export OPENAI_API_KEY="sk-xxx"
export OPENAI_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
# 测试导入
python3 -c "from mem0 import Memory; print('OK')"
# 检查 Qdrant 连接
curl http://localhost:6333/
```
### 5.3 Tailscale 断开
**症状**:`tailscale status` 显示 offline
**解决**:
```bash
# 重新认证
tailscale up --authkey=tskey-auth-xxx
# 检查服务状态
systemctl status tailscaled
```
---
## 6. 扩展部署
### 6.1 部署新 Agent 节点
**在新节点执行**:
1. 安装 Tailscale
```bash
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up --authkey=tskey-auth-xxx --hostname=mem0-general-agent-01
```
2. 部署 Agent(文档待补充)
### 6.2 业务类型扩展
命名规范:
- `mem0-general-center` - 通用业务(当前)
- `mem0-crypto-center` - 加密货币业务
- `mem0-advert-center` - 广告业务
- `mem0-life-center` - 生活业务
---
## 7. 安全配置
### 7.1 访问控制
- **Tailscale 内网**:所有服务仅通过 Tailscale IP 访问
- **端口绑定**:仅绑定 127.0.0.1,不暴露公网
- **API Key 管理**:存储在 .env 文件,权限 600
### 7.2 数据加密
- **Tailscale**:端到端加密
- **Qdrant**:支持 TLS(可选配置)
- **备份**:建议加密存储
---
## 8. 性能优化
### 8.1 资源限制
```yaml
# Docker Compose
deploy:
resources:
limits:
memory: 2G
cpus: '1.0'
```
### 8.2 缓存配置
```yaml
# mem0 config
cache:
enabled: true
ttl: 300 # 5 分钟
max_size: 1000
```
---
## 9. 变更日志
### v1.0.0 (2026-02-22)
- ✅ 初始部署
- ✅ 中心节点搭建完成
- ✅ mem0 Integration 完成
- ✅ 文档创建
---
## 10. 联系方式
- **Tailscale 管理**:https://login.tailscale.com/admin
- **mem0 文档**:https://docs.mem0.ai
- **Qdrant 文档**:https://qdrant.tech/documentation
- **OpenClaw 文档**:/root/.openclaw/workspace/docs

@ -0,0 +1,126 @@
# mem0 记忆系统部署文档
## 快速开始
### 1. 系统检查
```bash
./scripts/01-system-check.sh
```
### 2. 安装 Tailscale
```bash
./scripts/02-install-tailscale.sh
```
### 3. 创建目录
```bash
./scripts/03-create-directories.sh
```
### 4. 启动中心服务
```bash
./scripts/05-start-center.sh
```
### 5. 安装 mem0 Skill
```bash
./scripts/06-create-mem0-skill.sh
./scripts/07-install-dependencies.sh
```
### 6. 测试
```bash
python3 /root/.openclaw/workspace/skills/mem0-integration/mem0_client.py
```
## 服务信息
### 中心节点 (vps-vaym)
- **Tailscale IP**: 100.115.94.1
- **节点名称**: mem0-general-center
- **业务类型**: general
### 服务端口
| 服务 | 端口 | 访问方式 |
|------|------|---------|
| Qdrant | 6333 | Tailscale 内网 |
| Dozzle | 9999 | Tailscale 内网 |
## 管理命令
### 查看服务状态
```bash
cd /opt/mem0-center && docker compose ps
```
### 查看日志
```bash
cd /opt/mem0-center && docker compose logs -f
```
### 重启服务
```bash
cd /opt/mem0-center && docker compose restart
```
### 停止服务
```bash
cd /opt/mem0-center && docker compose down
```
## 监控
### 运行监控脚本
```bash
./scripts/12-monitoring.sh
```
### 查看 Dozzle 日志
访问:http://100.115.94.1:9999
## 备份
### 创建备份
```bash
./scripts/10-create-backup.sh
```
### 恢复备份
```bash
cd /root/.openclaw/workspace/backup
tar -xzf backup-YYYYMMDD-HHMMSS.tar.gz
```
## 故障排除
### Qdrant 无法启动
```bash
cd /opt/mem0-center
docker compose logs qdrant-master
```
### mem0 初始化失败
```bash
export OPENAI_API_KEY="your-api-key"
export OPENAI_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
python3 -c "from mem0 import Memory; print('OK')"
```
### Tailscale 断开
```bash
tailscale status
tailscale up --authkey=YOUR_AUTH_KEY
```
## 下一步:部署 Agent 节点
1. 在新节点安装 Tailscale
2. 使用相同 Auth Key 认证
3. 部署 Agent Docker Compose
4. 配置连接到中心 Qdrant (100.115.94.1)
## 联系支持
- Tailscale 管理:https://login.tailscale.com/admin
- mem0 文档:https://docs.mem0.ai
- Qdrant 文档:https://qdrant.tech/documentation

@ -0,0 +1,51 @@
# Mem0 生产级部署日志
## 测试时间
2026-02-22 19:11:13 UTC
## 测试结果
✅ **架构测试通过**
### 核心功能验证
- ✅ mem0 初始化成功
- ✅ 异步工作线程启动成功
- ✅ Pre-Hook 检索正常(超时控制 2s)
- ✅ Post-Hook 异步写入正常(队列处理)
- ✅ 优雅关闭正常
### 系统状态
```
initialized: True
started: True
async_queue_enabled: True
queue_size: 0 (已处理)
cache_size: 0
qdrant: localhost:6333
```
### 日志摘要
```
✅ mem0 Client 已启动
未检索到记忆(正常,首次对话)
✅ 对话已提交到异步队列
✅ 异步工作线程已取消
✅ 已关闭
```
### 已知问题
- ⚠ Embedding API 404(mem0 默认使用 OpenAI embedding,DashScope 不支持)
- ✅ 解决方案:配置中使用 `text-embedding-v3`
## 架构特点
1. **纯异步** - 无 threading,使用 asyncio.create_task
2. **阻塞隔离** - asyncio.to_thread 包装同步调用
3. **元数据隔离** - 通过 metadata 实现 user_id + agent_id 维度隔离
4. **批量写入** - batch_size=10, flush_interval=60s
5. **缓存支持** - TTL=300s, max_size=1000
6. **超时控制** - 检索 2s, 写入 5s
7. **优雅降级** - 失败不影响对话
## 下一步
1. 修复 Embedding 模型配置
2. 集成到 OpenClaw 主流程
3. Telegram 实机测试

@ -0,0 +1,4 @@
[2026-02-20T14:25:25.027Z] [INFO] Agent Health Monitor initialized
[2026-02-20T14:25:25.035Z] [INFO] Agent Health Monitor starting...
[2026-02-20T14:25:25.036Z] [INFO] Starting OpenClaw Gateway monitoring...
[2026-02-20T14:25:25.038Z] [INFO] Monitor is now active. Press Ctrl+C to stop.

@ -0,0 +1,5 @@
[2026-02-21T06:17:19.888Z] [INFO] Graceful shutdown initiated
[2026-02-21T06:17:20.297Z] [INFO] Agent Health Monitor initialized
[2026-02-21T06:17:20.304Z] [INFO] Agent Health Monitor starting...
[2026-02-21T06:17:20.304Z] [INFO] Starting OpenClaw Gateway monitoring...
[2026-02-21T06:17:20.305Z] [INFO] Monitor is now active. Press Ctrl+C to stop.

@ -0,0 +1,5 @@
[2026-02-22T19:12:25.950Z] [INFO] Graceful shutdown initiated
[2026-02-22T19:12:26.373Z] [INFO] Agent Health Monitor initialized
[2026-02-22T19:12:26.379Z] [INFO] Agent Health Monitor starting...
[2026-02-22T19:12:26.379Z] [INFO] Starting OpenClaw Gateway monitoring...
[2026-02-22T19:12:26.380Z] [INFO] Monitor is now active. Press Ctrl+C to stop.

@ -0,0 +1,5 @@
[2026-02-23T03:45:54.043Z] [INFO] Graceful shutdown initiated
[2026-02-23T03:45:54.472Z] [INFO] Agent Health Monitor initialized
[2026-02-23T03:45:54.555Z] [INFO] Agent Health Monitor starting...
[2026-02-23T03:45:54.556Z] [INFO] Starting OpenClaw Gateway monitoring...
[2026-02-23T03:45:54.557Z] [INFO] Monitor is now active. Press Ctrl+C to stop.

@ -0,0 +1,144 @@
# Session: 2026-02-20 11:05:07 UTC
- **Session Key**: agent:main:main
- **Session ID**: 71bed9c3-41be-4ef1-83ba-623d3b50483c
- **Source**: telegram
## Conversation Summary
assistant: ✅ **成功推送到你的 Gitea 仓库!**
## 📋 **Git 配置总结**
### **SSH 配置**
- **私钥**: `/root/.ssh/id_ed25519_openclaw`
- **公钥**: 已添加到你的 Gitea 账户
- **SSH Config**: 自动使用端口 10022 连接 `gl.tigerone.tech`
### **Git 仓库**
- **远程地址**: `git@gl.tigerone.tech:sw_dm/openClaw_agent_dm.git`
- **分支**: master (已跟踪 origin/master)
- **首次推送**: ✅ 成功
### **工作流程**
现在每次进行重要配置更改时,我会:
1. **自动创建备份 commit**
2. **推送变更到你的私有 Gitea 仓库**
3. **确保所有修改都有完整的版本历史**
这样你就有了完整的配置版本控制和灾难恢复能力!
需要我演示一下修改配置并自动提交的流程吗?
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1006",
"sender": "5237946060"
}
```
不用演示git流程。 关于上文中提到的版本控制 (Rollback):给OpenClaw装个“撤销键”,创建一个说明文本(markdown文件),详细说明当配置错误时候如何通过拉取上一版本的记录以恢复agent,以便后续你可以在排查中参考,以及当你失去功能时,我可以到git上查找该文件进行回复。这个后续也作为其他agent的实践。
assistant: [[reply_to_current]] 好的!让我创建一个详细的回滚恢复指南。
assistant: 现在让我将这个指南添加到核心索引中,并提交到 Git:
assistant: ✅ **回滚恢复指南已创建并推送到 Git!**
## 📋 **rollback_guide.md 内容概要**
### **核心恢复流程**
1. **识别问题**: 确定哪个配置文件导致了问题
2. **查看历史**: `git log --oneline <filename>` 查看修改历史
3. **预览差异**: `git show <commit-hash>:<filename>` 预览特定版本
4. **执行回滚**: `git checkout <commit-hash> -- <filename>`
5. **验证恢复**: 重启相关服务并测试功能
### **常见场景处理**
- **OpenClaw 配置损坏**: 回滚 `~/.openclaw/openclaw.json`
- **工作区配置错误**: 回滚 `/root/.openclaw/workspace/` 下的文件
- **Agent 脚本故障**: 回滚对应的 agent 文件
- **完全系统恢复**: `git reset --hard <good-commit>`
### **安全注意事项**
- **先备份当前状态**: `git stash` 保存未提交的更改
- **逐文件回滚**: 避免一次性回滚所有文件造成其他问题
- **测试后再提交**: 确认恢复成功后再推送新 commit
### **紧急恢复命令速查**
```bash
# 查看最近10次提交
git log --oneline -10
# 回滚到上一个版本
git checkout HEAD~1 -- filename
# 恢复整个工作区到指定提交
git reset --hard <commit-hash>
# 强制推送恢复(谨慎使用)
git push --force-with-lease origin master
```
这个指南现在存储在你的 Gitea 仓库中,即使我完全失去功能,你也可以直接从 `https://gl.tigerone.tech/sw_dm/openClaw_agent_dm.git` 访问并按照指南进行恢复。
这将成为所有后续 agents 的标准实践模板!
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1010",
"sender": "5237946060"
}
```
修改现在使用的模型,改用qwen3.5-plus和qwen3.5-plus-2026-02-15,移除qwen3-max-2026-01-23
assistant: [[reply_to_current]] 好的!让我修改 OpenClaw 的模型配置,将默认模型从 `qwen3-max-2026-01-23` 替换为 `qwen3.5-plus``qwen3.5-plus-2026-02-15`
首先,让我创建一个备份 commit:
assistant: 现在让我修改 OpenClaw 的配置文件:
assistant: 现在让我更新模型配置。我需要:
1. 在 bailian provider 中添加新的 qwen3.5-plus 模型
2. 更新 agents defaults 中的 primary model 和 models 配置
assistant: 现在让我创建一个 commit 记录这个变更:
assistant: 让我将 OpenClaw 配置文件也纳入 Git 管理:
assistant: 现在让我重启 OpenClaw 服务以应用新的模型配置:
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1012",
"sender": "5237946060"
}
```
进度如何?
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1014",
"sender": "5237946060"
}
```
进度如何?
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1016",
"sender": "5237946060"
}
```
修改模型的进度如何?

@ -0,0 +1,28 @@
# Session: 2026-02-20 11:11:50 UTC
- **Session Key**: agent:main:main
- **Session ID**: 47414465-b497-40b1-a385-f67d17f5ed24
- **Source**: telegram
## Conversation Summary
assistant: ✅ New session started · model: bailian/qwen3.5-plus
user: A new session was started via /new or /reset. Greet the user in your configured persona, if one is provided. Be yourself - use your defined voice, mannerisms, and mood. Keep it to 1-3 sentences and ask what they want to do. If the runtime model differs from default_model in the system prompt, mention the default model. Do not mention internal steps, files, tools, or reasoning.
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1021",
"sender": "5237946060"
}
```
上文提到修改模型,是否已经完成修改。
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1023",
"sender": "5237946060"
}
```
修复报错 Message ordering conflict - please try again. If this persists, use /new to start a fresh session.

@ -0,0 +1,19 @@
# Session: 2026-02-20 13:48:24 UTC
- **Session Key**: agent:main:main
- **Session ID**: c142ae76-0f74-4f28-a2b7-bf1983466abd
- **Source**: telegram
## Conversation Summary
assistant: ✅ New session started · model: bailian/qwen3.5-plus
user: A new session was started via /new or /reset. Greet the user in your configured persona, if one is provided. Be yourself - use your defined voice, mannerisms, and mood. Keep it to 1-3 sentences and ask what they want to do. If the runtime model differs from default_model in the system prompt, mention the default model. Do not mention internal steps, files, tools, or reasoning.
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1030",
"sender": "5237946060"
}
```
下面我们将继续进行优化,你按照我上文的要求尽作参考,结合现有的架构以及之前已经做的,你以专业AI架构师的角度去分析并执行最佳实践: 检查现有功能是否已经实现: 1. System-Level: 即使你注销或重启电脑,它依然在后台运行。 2. Auto-Healing: 如果它因为内存溢出崩溃或其他错误,任务会内自动重启。 多层记忆架构【核心记忆】:在《你指定的路径》创建一个Core_Profile.md 文件,存储最重要的信息及索引,未来重启/切换模型,第一时间读取这个文件,让模型快速明白我是谁?我是做什么的?【日常记忆】:我们的日常对话请自动保存为MEMORY.md,作为归档备份,方便未来检索记忆。当我与模型对话提到一些关键词时,可以启动调取,以便模型更好的理解并输出结果。【被动归档】:当我觉得我们的对话非常有价值,我会让你将对话转化成Skill/笔记,未来我有需要请你发给模型读取操作。 通过git实现的一键回滚功能。

@ -0,0 +1,28 @@
# Session: 2026-02-20 14:03:32 UTC
- **Session Key**: agent:main:main
- **Session ID**: f37313e8-2d95-4782-abf4-8bb502d49f46
- **Source**: webchat
## Conversation Summary
assistant: ✅ New session started · model: bailian/qwen3.5-plus
user: A new session was started via /new or /reset. Greet the user in your configured persona, if one is provided. Be yourself - use your defined voice, mannerisms, and mood. Keep it to 1-3 sentences and ask what they want to do. If the runtime model differs from default_model in the system prompt, mention the default model. Do not mention internal steps, files, tools, or reasoning.
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1035",
"sender": "5237946060"
}
```
下面我们将继续进行优化,你按照我上文的要求尽作参考,结合现有的架构以及之前已经做的,你以专业AI架构师的角度去分析并执行最佳实践: 检查现有功能是否已经实现: 1. System-Level: 即使你注销或重启电脑,它依然在后台运行。 2. Auto-Healing: 如果它因为内存溢出崩溃或其他错误,任务会内自动重启。 3. 多层记忆架构【核心记忆】:在《你指定的路径》创建一个Core_Profile.md 文件,存储最重要的信息及索引,未来重启/切换模型,第一时间读取这个文件,让模型快速明白我是谁?我是做什么的?【日常记忆】:我们的日常对话请自动保存为MEMORY.md,作为归档备份,方便未来检索记忆。当我与模型对话提到一些关键词时,可以启动调取,以便模型更好的理解并输出结果。【被动归档】:当我觉得我们的对话非常有价值,我会让你将对话转化成Skill/笔记,未来我有需要请你发给模型读取操作。 4. 通过git实现的一键回滚功能。 5. 当openClaw服务停止、错误或重启时候会通过telegram(或相应channel)通知。
user: Conversation info (untrusted metadata):
```json
{
"message_id": "1d89994f-acfd-4ebf-b355-60a567b88a84",
"sender": "openclaw-control-ui"
}
```
[Fri 2026-02-20 14:03 UTC] 更换模型后,发送信息会报错:Message ordering conflict - please try again. If this persists, use /new to start a fresh session. 修复这个错误。

@ -1,207 +0,0 @@
{
"meta": {
"lastTouchedVersion": "2026.2.19-2",
"lastTouchedAt": "2026-02-20T08:45:00.000Z"
},
"env": {
"TAVILY_API_KEY": "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"
},
"wizard": {
"lastRunAt": "2026-02-20T03:54:18.096Z",
"lastRunVersion": "2026.2.17",
"lastRunCommand": "doctor",
"lastRunMode": "local"
},
"auth": {
"profiles": {
"minimax-cn:default": {
"provider": "minimax-cn",
"mode": "api_key"
},
"qwen-portal:default": {
"provider": "qwen-portal",
"mode": "oauth"
}
}
},
"models": {
"mode": "merge",
"providers": {
"minimax-cn": {
"baseUrl": "https://api.minimaxi.com/anthropic",
"api": "anthropic-messages",
"models": [
{
"id": "MiniMax-M2.5",
"name": "MiniMax M2.5",
"reasoning": true,
"input": [
"text"
],
"cost": {
"input": 15,
"output": 60,
"cacheRead": 2,
"cacheWrite": 10
},
"contextWindow": 200000,
"maxTokens": 8192
}
]
},
"bailian": {
"baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"apiKey": "sk-c1715ee0479841399fd359c574647648",
"api": "openai-completions",
"models": [
{
"id": "qwen3.5-plus",
"name": "qwen3.5-plus",
"reasoning": true,
"input": [
"text"
],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 65536
},
{
"id": "qwen3.5-plus-2026-02-15",
"name": "qwen3.5-plus-2026-02-15",
"reasoning": true,
"input": [
"text"
],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 65536
}
]
}
}
},
"agents": {
"defaults": {
"model": {
"primary": "bailian/qwen3.5-plus",
"fallbacks": [
"bailian/qwen3.5-plus-2026-02-15",
"minimax-cn/MiniMax-M2.5"
]
},
"models": {
"minimax-cn/MiniMax-M2.5": {
"alias": "Minimax"
},
"bailian/qwen3.5-plus": {
"alias": "qwen3.5-plus"
},
"bailian/qwen3.5-plus-2026-02-15": {
"alias": "qwen3.5-plus-thinking"
}
},
"workspace": "/root/.openclaw/workspace",
"contextPruning": {
"mode": "cache-ttl",
"ttl": "5m"
},
"compaction": {
"mode": "safeguard"
},
"maxConcurrent": 4,
"subagents": {
"maxConcurrent": 8,
"model": "minimax/MiniMax-M2.1"
}
}
},
"messages": {
"ackReactionScope": "group-mentions"
},
"commands": {
"native": "auto",
"nativeSkills": "auto",
"restart": true
},
"hooks": {
"internal": {
"enabled": true,
"entries": {
"command-logger": {
"enabled": true
}
}
}
},
"channels": {
"telegram": {
"enabled": true,
"dmPolicy": "pairing",
"botToken": "7047245486:AAF504oCHZpfEIx3-3VXJYSSS9XelkV6o3g",
"groupPolicy": "allowlist",
"streamMode": "partial"
}
},
"gateway": {
"port": 18789,
"mode": "local",
"bind": "lan",
"auth": {
"mode": "token",
"token": "9e2e91b31a56fb56a35e91821c025267292ec44c26169b12"
},
"trustedProxies": [
"127.0.0.1",
"::1"
],
"tailscale": {
"mode": "off",
"resetOnExit": false
},
"nodes": {}
},
"memory": {
"backend": "qmd",
"citations": "auto",
"qmd": {
"includeDefaultMemory": true,
"update": {
"interval": "5m",
"debounceMs": 15000
}
}
},
"skills": {
"install": {
"nodeManager": "npm"
},
"entries": {
"tavily": {
"enabled": true
},
"find-skills-robin": {
"enabled": true
}
}
},
"plugins": {
"entries": {
"telegram": {
"enabled": true
},
"qwen-portal-auth": {
"enabled": true
}
}
}
}

@ -0,0 +1,53 @@
{
"meta": {
"lastTouchedVersion": "2026.2.19-2",
"lastTouchedAt": "2026-02-20T08:45:00.000Z"
},
"env": {
"TAVILY_API_KEY": "${TAVILY_API_KEY}"
},
"wizard": {
"lastRunAt": "2026-02-20T03:54:18.096Z",
"lastRunVersion": "2026.2.17",
"lastRunAt": "2026.2.17",
"lastRunCommand": "doctor",
"lastRunMode": "local"
},
"auth": {
"profiles": {
"minimax-cn:default": {
"provider": "minimax-cn",
"mode": "api_key"
},
"qwen-portal:default": {
"provider": "qwen-portal",
"mode": "oauth"
}
}
},
"models": {
"mode": "merge",
"providers": {
"minimax-cn": {
"baseUrl": "https://api.minimaxi.com/anthropic",
"api": "anthropic-messages",
"models": [
{
"id": "MiniMax-M2.5",
"name": "MiniMax M2.5",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 15,
"output": 60,
"cacheRead": 2,
"cacheWrite": 10
},
"contextWindow": 200000,
"maxTokens": 8192
}
]
}
}
}
}

@ -0,0 +1,57 @@
#!/bin/bash
# /root/.openclaw/workspace/scripts/01-system-check.sh
set -e
echo "🔍 系统检查..."
# 检查 Docker
if ! command -v docker &> /dev/null; then
echo "❌ Docker 未安装"
exit 1
fi
echo "✅ Docker: $(docker --version)"
# 检查 Docker Compose
if ! command -v docker compose &> /dev/null; then
echo "❌ Docker Compose 未安装"
exit 1
fi
echo "✅ Docker Compose: $(docker compose version)"
# 检查磁盘空间
disk_usage=$(df -h / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$disk_usage" -gt 80 ]; then
echo " 磁盘使用率过高:${disk_usage}%"
else
echo "✅ 磁盘使用率:${disk_usage}%"
fi
# 检查内存
mem_usage=$(free | grep Mem | awk '{printf("%.0f", $3/$2 * 100.0)}')
if [ "$mem_usage" -gt 80 ]; then
echo " 内存使用率过高:${mem_usage}%"
else
echo "✅ 内存使用率:${mem_usage}%"
fi
# 检查端口占用
echo "📊 端口检查..."
for port in 6333 8000 9999 18789; do
if netstat -tlnp | grep -q ":$port "; then
echo " 端口 $port 已被占用"
else
echo "✅ 端口 $port 可用"
fi
done
# 检查 OpenClaw 状态
echo "📊 OpenClaw 状态..."
if systemctl --user is-active openclaw-gateway &>/dev/null; then
echo "✅ OpenClaw Gateway 运行中"
else
echo "⚠ OpenClaw Gateway 未运行"
fi
echo ""
echo "✅ 系统检查完成"

@ -0,0 +1,69 @@
#!/bin/bash
# /root/.openclaw/workspace/scripts/02-install-tailscale.sh
set -e
echo "🔧 安装 Tailscale..."
# 检查是否已安装
if command -v tailscale &> /dev/null; then
echo "⚠ Tailscale 已安装"
tailscale status
read -p "是否重新配置?(y/N): " confirm
if [[ ! $confirm =~ ^[Yy]$ ]]; then
exit 0
fi
tailscale down
fi
# 安装
echo "📦 下载安装脚本..."
curl -fsSL https://tailscale.com/install.sh | sh
# 验证安装
if ! command -v tailscale &> /dev/null; then
echo "❌ Tailscale 安装失败"
exit 1
fi
echo "✅ Tailscale 安装成功"
tailscale version
# 配置 Tailscale
echo ""
echo "🔐 配置 Tailscale..."
echo "节点名称:mem0-general-center"
echo "业务类型:general"
echo "节点角色:center"
# 使用 Auth Key 认证
TS_AUTHKEY="tskey-auth-kBPLmrWqQF11CNTRL-TwpkbDQcDA5vTavwXMWw95tsv2KE48ou"
TS_HOSTNAME="mem0-general-center"
echo "🔗 连接到 Tailscale 网络..."
tailscale up --authkey="$TS_AUTHKEY" --hostname="$TS_HOSTNAME" --accept-routes --advertise-exit-node
# 等待连接
echo "⏳ 等待连接..."
sleep 5
# 显示状态
echo ""
echo "📊 Tailscale 状态:"
tailscale status
echo ""
echo "📊 网络信息:"
tailscale ip -4
tailscale ip -6
echo ""
echo "✅ Tailscale 配置完成"
echo ""
echo "📝 重要信息:"
echo " 节点名称:mem0-general-center"
echo " 业务类型:general"
echo " 节点角色:center"
echo " 管理后台:https://login.tailscale.com/admin/machines"
echo ""
echo "⚠ 请在 Tailscale 管理后台确认节点已上线,并记录分配的 IP 地址"

@ -0,0 +1,101 @@
#!/bin/bash
# /root/.openclaw/workspace/scripts/03-create-directories.sh
set -e
echo "📁 创建目录结构..."
# 中心服务目录
echo "创建中心服务目录..."
mkdir -p /opt/mem0-center/{qdrant_storage,snapshots,tailscale,logs,backup}
mkdir -p /opt/mem0-center/config
# OpenClaw 目录检查
echo "检查 OpenClaw 目录..."
if [ ! -d "/root/.openclaw/workspace" ]; then
echo "❌ OpenClaw workspace 不存在"
exit 1
fi
echo "✅ OpenClaw workspace 已存在"
# 脚本目录
mkdir -p /root/.openclaw/workspace/scripts
mkdir -p /root/.openclaw/workspace/backup
mkdir -p /root/.openclaw/workspace/docs
# mem0 Skill 目录(预创建)
mkdir -p /root/.openclaw/workspace/skills/mem0-integration
# 设置权限
chmod 755 /opt/mem0-center
chmod 755 /root/.openclaw/workspace/scripts
chmod 700 /opt/mem0-center/backup # 备份目录限制访问
# 创建目录说明文件
cat > /opt/mem0-center/README.md << 'EOF'
# mem0-center - 中心节点
## 目录结构
- `qdrant_storage/` - Qdrant 向量数据库存储
- `snapshots/` - Qdrant 快照备份
- `tailscale/` - Tailscale 状态文件
- `logs/` - 服务日志
- `backup/` - 配置和数据备份
- `config/` - 配置文件
## 服务
- Qdrant Master: 端口 6333
- Dozzle (日志): 端口 9999
- mem0 Server: 端口 8000 (可选)
## 管理命令
```bash
# 启动服务
docker compose up -d
# 停止服务
docker compose down
# 查看状态
docker compose ps
# 查看日志
docker compose logs -f
# 重启服务
docker compose restart
```
## Tailscale 信息
- 节点名称:mem0-general-center
- 业务类型:general
- 节点角色:center
- Tailscale IP: 100.115.94.1
## 访问方式
- Qdrant API: http://100.115.94.1:6333
- Dozzle 日志:http://100.115.94.1:9999
- mem0 API: http://100.115.94.1:8000
EOF
echo ""
echo "📊 目录结构:"
tree -L 2 /opt/mem0-center 2>/dev/null || ls -la /opt/mem0-center
echo ""
echo "✅ 目录结构创建完成"
echo ""
echo "📁 已创建的目录:"
echo " /opt/mem0-center/qdrant_storage/ - Qdrant 数据存储"
echo " /opt/mem0-center/snapshots/ - Qdrant 快照备份"
echo " /opt/mem0-center/tailscale/ - Tailscale 状态"
echo " /opt/mem0-center/logs/ - 服务日志"
echo " /opt/mem0-center/backup/ - 备份文件"
echo " /opt/mem0-center/config/ - 配置文件"
echo ""
echo "📝 说明文档:/opt/mem0-center/README.md"

@ -0,0 +1,83 @@
#!/bin/bash
# /root/.openclaw/workspace/scripts/05-start-center.sh
set -e
echo "🚀 启动中心服务..."
cd /opt/mem0-center
# 检查环境变量
if [ ! -f ".env" ]; then
echo "❌ .env 文件不存在"
exit 1
fi
echo "📋 配置检查..."
echo " 业务类型:$(grep BUSINESS_TYPE .env | cut -d'=' -f2)"
echo " 节点角色:$(grep NODE_ROLE .env | cut -d'=' -f2)"
echo " 节点名称:$(grep NODE_NAME .env | cut -d'=' -f2)"
# 拉取镜像
echo ""
echo "📦 拉取 Docker 镜像..."
docker compose pull
# 启动服务
echo ""
echo "🚀 启动服务..."
docker compose up -d
# 等待服务启动
echo ""
echo "⏳ 等待服务启动 (30 秒)..."
sleep 30
# 检查服务状态
echo ""
echo "📊 服务状态:"
docker compose ps
# 验证 Qdrant
echo ""
echo "🔍 验证 Qdrant..."
if curl -s http://localhost:6333/ | grep -q "qdrant"; then
echo "✅ Qdrant 运行正常"
curl -s http://localhost:6333/ | python3 -m json.tool | head -10
else
echo "❌ Qdrant 启动失败"
echo "查看日志:"
docker compose logs qdrant-master
exit 1
fi
# 验证 Dozzle
echo ""
echo "🔍 验证 Dozzle..."
if curl -s http://localhost:9999/ | grep -q "Dozzle"; then
echo "✅ Dozzle 运行正常"
else
echo "⚠ Dozzle 可能未完全启动,稍后检查"
fi
# 显示访问信息
echo ""
echo "=========================================="
echo "✅ 中心服务启动完成"
echo "=========================================="
echo ""
echo "📊 服务访问信息:"
echo " Qdrant API: http://100.115.94.1:6333"
echo " Dozzle 日志: http://100.115.94.1:9999"
echo " 本地访问: http://localhost:6333"
echo ""
echo "📝 管理命令:"
echo " 查看状态:docker compose ps"
echo " 查看日志:docker compose logs -f"
echo " 重启服务:docker compose restart"
echo " 停止服务:docker compose down"
echo ""
echo "🔍 测试命令:"
echo " curl http://localhost:6333/"
echo " curl http://localhost:9999/"
echo ""

@ -0,0 +1,26 @@
#!/bin/bash
# /root/.openclaw/workspace/scripts/07-install-dependencies.sh
set -e
echo "📦 安装依赖..."
# 安装 mem0ai
echo "安装 mem0ai..."
pip install mem0ai --break-system-packages --quiet
# 验证安装
if python3 -c "import mem0" 2>/dev/null; then
echo "✅ mem0ai 安装成功"
else
echo "❌ mem0ai 安装失败"
exit 1
fi
# 安装 PyYAML
echo "安装 PyYAML..."
pip install pyyaml --break-system-packages --quiet
echo "✅ PyYAML 安装成功"
echo ""
echo "✅ 依赖安装完成"

@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""
mem0 功能测试脚本
"""
import sys
import os
sys.path.insert(0, '/root/.openclaw/workspace/skills/mem0-integration')
from mem0_client import Mem0Client
print("=" * 60)
print("🧪 mem0 功能测试")
print("=" * 60)
# 测试 1:初始化
print("\n🔍 测试 1: 初始化 mem0 Client...")
try:
client = Mem0Client()
print("✅ Client 创建成功")
except Exception as e:
print(f"❌ Client 创建失败:{e}")
sys.exit(1)
# 测试 2:状态检查
print("\n📊 测试 2: 状态检查...")
status = client.get_status()
print(f" 本地记忆:{'' if status['local_initialized'] else ''}")
print(f" 共享记忆:{'' if status['master_initialized'] else ''}")
if not status['local_initialized']:
print("\n 本地记忆未初始化,可能原因:")
print(" 1. Qdrant 未启动")
print(" 2. 配置错误")
print(" 3. mem0ai 版本问题")
sys.exit(1)
# 测试 3:添加记忆
print("\n📝 测试 3: 添加记忆...")
test_content = "测试记忆:OpenClaw mem0 集成测试"
result = client.add(
[{"role": "user", "content": test_content}],
user_id="test_user",
agent_id="main"
)
if 'error' in result:
print(f"❌ 添加失败:{result['error']}")
else:
print(f"✅ 记忆已添加")
print(f" 内容:{test_content}")
# 测试 4:搜索记忆
print("\n🔍 测试 4: 搜索记忆...")
results = client.search("OpenClaw", user_id="test_user", agent_id="main", limit=5)
print(f" 找到 {len(results)} 条记忆")
if results:
for i, mem in enumerate(results, 1):
memory_text = mem.get('memory', 'N/A')
print(f" {i}. {memory_text[:100]}...")
else:
print(" 未找到记忆(可能 Qdrant 集合为空)")
# 测试 5:获取所有记忆
print("\n📋 测试 5: 获取所有记忆...")
all_memories = client.get_all(user_id="test_user", agent_id="main")
print(f"{len(all_memories)} 条记忆")
# 测试 6:状态报告
print("\n📊 测试 6: 最终状态...")
final_status = client.get_status()
print(f" 本地 Qdrant: {final_status['config']['local']['vector_store']['config']['host']}:{final_status['config']['local']['vector_store']['config']['port']}")
print(f" 中心 Qdrant: {final_status['config']['master']['vector_store']['config']['host']}:{final_status['config']['master']['vector_store']['config']['port']}")
print("\n" + "=" * 60)
print("✅ 测试完成")
print("=" * 60)

@ -0,0 +1,40 @@
#!/bin/bash
# /root/.openclaw/workspace/scripts/10-create-backup.sh
set -e
echo "💾 创建备份..."
BACKUP_DIR="/root/.openclaw/workspace/backup"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_PATH="$BACKUP_DIR/backup-$TIMESTAMP"
mkdir -p "$BACKUP_PATH"
# 备份 mem0 配置
echo "📁 备份 mem0 配置..."
cp -r /root/.openclaw/workspace/skills/mem0-integration "$BACKUP_PATH/" 2>/dev/null || true
# 备份中心服务配置
echo "📁 备份中心服务配置..."
cp /opt/mem0-center/docker-compose.yml "$BACKUP_PATH/" 2>/dev/null || true
cp /opt/mem0-center/.env "$BACKUP_PATH/" 2>/dev/null || true
# 创建 Qdrant 快照
echo "📁 创建 Qdrant 快照..."
SNAPSHOT_RESPONSE=$(curl -s -X POST http://localhost:6333/collections/mem0_test/snapshots 2>/dev/null || echo '{"error":"collection not found"}')
echo " Qdrant 快照:$SNAPSHOT_RESPONSE"
# 压缩备份
cd "$BACKUP_DIR"
tar -czf "backup-$TIMESTAMP.tar.gz" "backup-$TIMESTAMP"
rm -rf "backup-$TIMESTAMP"
echo "✅ 备份完成:$BACKUP_DIR/backup-$TIMESTAMP.tar.gz"
# 保留最近 10 个备份
ls -t "$BACKUP_DIR"/backup-*.tar.gz | tail -n +11 | xargs rm -f 2>/dev/null || true
echo ""
echo "📊 当前备份:"
ls -lht "$BACKUP_DIR"/backup-*.tar.gz | head -5

@ -0,0 +1,41 @@
#!/bin/bash
# /root/.openclaw/workspace/scripts/12-monitoring.sh
set -e
echo "📊 系统监控..."
echo "=" * 60
# 服务状态
echo -e "\n=== 服务状态 ==="
cd /opt/mem0-center && docker compose ps
# Qdrant 健康
echo -e "\n=== Qdrant 健康 ==="
curl -s http://localhost:6333/ | python3 -m json.tool
# 内存使用
echo -e "\n=== 内存使用 ==="
free -h
# 磁盘使用
echo -e "\n=== 磁盘使用 ==="
df -h /opt/mem0-center
# Tailscale 状态
echo -e "\n=== Tailscale 状态 ==="
tailscale status 2>/dev/null | grep mem0-general-center || echo "Tailscale 未运行"
# mem0 状态
echo -e "\n=== mem0 状态 ==="
cd /root/.openclaw/workspace/skills/mem0-integration && python3 -c "
from mem0_client import Mem0Client
client = Mem0Client()
status = client.get_status()
print(f'初始化:{\"✅\" if status[\"initialized\"] else \"❌\"}')
print(f'Qdrant: {status[\"qdrant\"]}')
"
echo -e "\n"
echo "=" * 60
echo "✅ 监控完成"

@ -0,0 +1,35 @@
# mem0-integration Skill
## 功能说明
集成 mem0 记忆系统,为 OpenClaw 提供:
- ✅ 本地记忆存储(Qdrant Local)
- ✅ 共享记忆同步(Qdrant Master)
- ✅ 语义搜索
- ✅ 多 Agent 协作
- ✅ 分层记忆管理
## 架构
```
Agent → mem0 Client → Qdrant Local → (异步同步) → Qdrant Master (100.115.94.1)
```
## 配置
编辑 `/root/.openclaw/workspace/skills/mem0-integration/config.yaml`
## 命令
- `/memory add <内容>` - 添加记忆
- `/memory search <关键词>` - 搜索记忆
- `/memory list` - 列出所有记忆
- `/memory delete <ID>` - 删除记忆
- `/memory sync` - 手动同步到中心
- `/memory status` - 查看状态
## 依赖
- mem0ai (pip install mem0ai)
- Qdrant (Docker)
- pyyaml

@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""
mem0 Commands for OpenClaw
"""
from .mem0_client import Mem0Client
mem0 = Mem0Client()
async def handle_memory_command(args: str, context: dict) -> str:
"""处理 /memory 命令"""
parts = args.split(' ', 2)
action = parts[0] if parts else 'help'
user_id = context.get('user_id', 'default')
agent_id = context.get('agent_id', 'main')
if action == 'add':
content = parts[1] if len(parts) > 1 else ''
return await handle_add_memory(content, user_id, agent_id)
elif action == 'search':
query = parts[1] if len(parts) > 1 else ''
return await handle_search_memory(query, user_id, agent_id)
elif action == 'list':
return await handle_list_memories(user_id, agent_id)
elif action == 'delete':
memory_id = parts[1] if len(parts) > 1 else ''
return await handle_delete_memory(memory_id, user_id, agent_id)
elif action == 'sync':
return await handle_sync_memory(user_id, agent_id)
elif action == 'status':
return handle_status(user_id, agent_id)
else:
return get_help_text()
async def handle_add_memory(content: str, user_id: str, agent_id: str) -> str:
if not content:
return "❌ 请提供记忆内容"
result = mem0.add([{"role": "user", "content": content}], user_id=user_id, agent_id=agent_id)
if 'error' in result:
return f"❌ 添加失败:{result['error']}"
return f"✅ 记忆已添加:{content[:100]}..."
async def handle_search_memory(query: str, user_id: str, agent_id: str) -> str:
if not query:
return "❌ 请提供关键词"
results = mem0.search(query, user_id=user_id, agent_id=agent_id, limit=5)
if not results:
return f"🔍 未找到相关记忆"
response = f"🔍 找到 {len(results)} 条记忆:\n\n"
for i, mem in enumerate(results, 1):
response += f"{i}. {mem.get('memory', 'N/A')}\n"
return response
async def handle_list_memories(user_id: str, agent_id: str) -> str:
results = mem0.get_all(user_id=user_id, agent_id=agent_id)
if not results:
return "📭 暂无记忆"
return f"📋 共 {len(results)} 条记忆"
async def handle_delete_memory(memory_id: str, user_id: str, agent_id: str) -> str:
if not memory_id:
return "❌ 请提供 ID"
success = mem0.delete(memory_id, user_id=user_id, agent_id=agent_id)
return "✅ 已删除" if success else "❌ 删除失败"
async def handle_sync_memory(user_id: str, agent_id: str) -> str:
return "🔄 同步功能开发中"
def handle_status(user_id: str, agent_id: str) -> str:
status = mem0.get_status()
response = "📊 mem0 状态:\n"
response += f"本地:{'' if status['local_initialized'] else ''}\n"
response += f"共享:{'' if status['master_initialized'] else ''}\n"
return response
def get_help_text() -> str:
return """📖 用法:/memory <命令>
命令add, search, list, delete, sync, status"""

@ -0,0 +1,47 @@
# mem0 Integration Configuration
# 复制此文件为 config.yaml 并填入真实 API keys
# 本地 Qdrant 配置
local:
vector_store:
provider: qdrant
config:
host: localhost
port: 6333
collection_name: mem0_v4_local
llm:
provider: openai
config:
model: qwen-plus
api_base: https://dashscope.aliyuncs.com/compatible-mode/v1
api_key: ${DASHSCOPE_API_KEY} # 从环境变量读取
embedder:
provider: openai
config:
model: text-embedding-v4
api_base: https://dashscope.aliyuncs.com/compatible-mode/v1
api_key: ${DASHSCOPE_API_KEY} # 从环境变量读取
# 中心 Qdrant 配置(共享记忆)
master:
vector_store:
provider: qdrant
config:
host: 100.115.94.1
port: 6333
collection_name: mem0_v4_shared
# 同步配置
sync:
enabled: true
interval: 300
batch_size: 50
retry_attempts: 3
# 缓存配置
cache:
enabled: true
ttl: 300
max_size: 1000

@ -0,0 +1,162 @@
#!/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'
});

@ -0,0 +1,457 @@
#!/usr/bin/env python3
"""
mem0 Client for OpenClaw - 生产级纯异步架构
Pre-Hook 检索注入 + Post-Hook 异步写入
元数据维度隔离 (user_id + agent_id)
"""
import os
import asyncio
import logging
import time
from typing import List, Dict, Optional, Any
from collections import deque
from datetime import datetime
# ========== DashScope 环境变量配置 ==========
# 标准计费通道 (text-embedding-v3 专用)
os.environ['OPENAI_API_BASE'] = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
os.environ['OPENAI_BASE_URL'] = 'https://dashscope.aliyuncs.com/compatible-mode/v1' # 关键:兼容模式需要此变量
os.environ['OPENAI_API_KEY'] = os.getenv('MEM0_DASHSCOPE_API_KEY', 'sk-4111c9dba5334510968f9ae72728944e')
try:
from mem0 import Memory
from mem0.configs.base import MemoryConfig, VectorStoreConfig, LlmConfig, EmbedderConfig
except ImportError as e:
print(f" mem0ai 导入失败:{e}")
Memory = None
logger = logging.getLogger(__name__)
class AsyncMemoryQueue:
"""纯异步记忆写入队列"""
def __init__(self, max_size: int = 100):
self.queue = deque(maxlen=max_size)
self.lock = asyncio.Lock()
self.running = False
self._worker_task = None
self.callback = None
self.batch_size = 10
self.flush_interval = 60
def add(self, item: Dict[str, Any]):
"""添加任务到队列(同步方法)"""
try:
if len(self.queue) < self.queue.maxlen:
self.queue.append({
'messages': item['messages'],
'user_id': item['user_id'],
'agent_id': item['agent_id'],
'timestamp': item.get('timestamp', datetime.now().isoformat())
})
else:
logger.warning("异步队列已满,丢弃旧任务")
except Exception as e:
logger.error(f"队列添加失败:{e}")
async def get_batch(self, batch_size: int) -> List[Dict]:
"""获取批量任务(异步方法)"""
async with self.lock:
batch = []
while len(batch) < batch_size and self.queue:
batch.append(self.queue.popleft())
return batch
def start_worker(self, callback, batch_size: int, flush_interval: int):
"""启动异步后台任务"""
self.running = True
self.callback = callback
self.batch_size = batch_size
self.flush_interval = flush_interval
self._worker_task = asyncio.create_task(self._worker_loop())
logger.info(f"✅ 异步工作线程已启动 (batch_size={batch_size}, interval={flush_interval}s)")
async def _worker_loop(self):
"""异步工作循环"""
last_flush = time.time()
while self.running:
try:
if self.queue:
batch = await self.get_batch(self.batch_size)
if batch:
asyncio.create_task(self._process_batch(batch))
if time.time() - last_flush > self.flush_interval:
if self.queue:
batch = await self.get_batch(self.batch_size)
if batch:
asyncio.create_task(self._process_batch(batch))
last_flush = time.time()
await asyncio.sleep(1)
except asyncio.CancelledError:
logger.info("异步工作线程已取消")
break
except Exception as e:
logger.error(f"异步工作线程错误:{e}")
await asyncio.sleep(5)
async def _process_batch(self, batch: List[Dict]):
"""处理批量任务"""
try:
logger.debug(f"开始处理批量任务:{len(batch)}")
for item in batch:
await self.callback(item)
logger.debug(f"批量任务处理完成")
except Exception as e:
logger.error(f"批量处理失败:{e}")
async def stop(self):
"""优雅关闭"""
self.running = False
if self._worker_task:
self._worker_task.cancel()
try:
await self._worker_task
except asyncio.CancelledError:
pass
logger.info("异步工作线程已关闭")
class Mem0Client:
"""
生产级 mem0 客户端
纯异步架构 + 阻塞操作隔离 + 元数据维度隔离
"""
def __init__(self, config: Dict = None):
self.config = config or self._load_default_config()
self.local_memory = None
self.async_queue = None
self.cache = {}
self._started = False
# 不在 __init__ 中启动异步任务
self._init_memory()
def _load_default_config(self) -> Dict:
"""加载默认配置"""
return {
"qdrant": {
"host": os.getenv('MEM0_QDRANT_HOST', 'localhost'),
"port": int(os.getenv('MEM0_QDRANT_PORT', '6333')),
"collection_name": "mem0_v4_shared"
},
"llm": {
"provider": "openai",
"config": {
"model": os.getenv('MEM0_LLM_MODEL', 'qwen-plus')
}
},
"embedder": {
"provider": "openai",
"config": {
"model": os.getenv('MEM0_EMBEDDER_MODEL', 'text-embedding-v4'),
"dimensions": 1024 # DashScope text-embedding-v4 支持的最大维度
}
},
"retrieval": {
"enabled": True,
"top_k": 5,
"min_confidence": 0.7,
"timeout_ms": 2000
},
"async_write": {
"enabled": True,
"queue_size": 100,
"batch_size": 10,
"flush_interval": 60,
"timeout_ms": 5000
},
"cache": {
"enabled": True,
"ttl": 300,
"max_size": 1000
},
"fallback": {
"enabled": True,
"log_level": "WARNING",
"retry_attempts": 2
},
"metadata": {
"default_user_id": "default",
"default_agent_id": "general"
}
}
def _init_memory(self):
"""初始化 mem0(同步操作)- 三位一体完整配置"""
if Memory is None:
logger.warning("mem0ai 未安装")
return
try:
config = MemoryConfig(
vector_store=VectorStoreConfig(
provider="qdrant",
config={
"host": self.config['qdrant']['host'],
"port": self.config['qdrant']['port'],
"collection_name": self.config['qdrant']['collection_name'],
"on_disk": True,
"embedding_model_dims": 1024 # 强制同步 Qdrant 集合维度
}
),
llm=LlmConfig(
provider="openai",
config=self.config['llm']['config']
),
embedder=EmbedderConfig(
provider="openai",
config={
"model": "text-embedding-v4",
"embedding_dims": 1024 # 核心修复:强制覆盖默认的 1536 维度
# api_base 和 api_key 通过环境变量 OPENAI_BASE_URL 和 OPENAI_API_KEY 读取
}
)
)
self.local_memory = Memory(config=config)
logger.info("✅ mem0 初始化成功(含 Embedder,1024 维度)")
except Exception as e:
logger.error(f"❌ mem0 初始化失败:{e}")
self.local_memory = None
async def start(self):
"""
显式启动异步工作线程
必须在事件循环中调用await mem0_client.start()
"""
if self._started:
logger.debug("mem0 Client 已启动")
return
if self.config['async_write']['enabled']:
self.async_queue = AsyncMemoryQueue(
max_size=self.config['async_write']['queue_size']
)
self.async_queue.start_worker(
callback=self._async_write_memory,
batch_size=self.config['async_write']['batch_size'],
flush_interval=self.config['async_write']['flush_interval']
)
self._started = True
logger.info("✅ mem0 Client 异步工作线程已启动")
# ========== Pre-Hook: 智能检索 ==========
async def pre_hook_search(self, query: str, user_id: str = None, agent_id: str = None, top_k: int = None) -> List[Dict]:
"""Pre-Hook: 对话前智能检索"""
if not self.config['retrieval']['enabled'] or self.local_memory is None:
return []
cache_key = f"{user_id}:{agent_id}:{query}"
if self.config['cache']['enabled'] and cache_key in self.cache:
cached = self.cache[cache_key]
if time.time() - cached['time'] < self.config['cache']['ttl']:
logger.debug(f"Cache hit: {cache_key}")
return cached['results']
timeout_ms = self.config['retrieval']['timeout_ms']
try:
memories = await asyncio.wait_for(
self._execute_search(query, user_id, agent_id, top_k or self.config['retrieval']['top_k']),
timeout=timeout_ms / 1000
)
if self.config['cache']['enabled'] and memories:
self.cache[cache_key] = {'results': memories, 'time': time.time()}
self._cleanup_cache()
logger.info(f"Pre-Hook 检索完成:{len(memories)} 条记忆")
return memories
except asyncio.TimeoutError:
logger.warning(f"Pre-Hook 检索超时 ({timeout_ms}ms)")
return []
except Exception as e:
logger.warning(f"Pre-Hook 检索失败:{e}")
return []
async def _execute_search(self, query: str, user_id: str, agent_id: str, top_k: int) -> List[Dict]:
"""
执行检索 - 使用 metadata 过滤器实现维度隔离
"""
if self.local_memory is None:
return []
# 策略 1: 检索全局用户记忆
user_memories = []
if user_id:
try:
user_memories = await asyncio.to_thread(
self.local_memory.search,
query,
user_id=user_id,
limit=top_k
)
except Exception as e:
logger.debug(f"用户记忆检索失败:{e}")
# 策略 2: 检索业务域记忆(使用 metadata 过滤器)
agent_memories = []
if agent_id and agent_id != 'general':
try:
agent_memories = await asyncio.to_thread(
self.local_memory.search,
query,
user_id=user_id,
filters={"agent_id": agent_id}, # metadata 过滤,实现垂直隔离
limit=top_k
)
except Exception as e:
logger.debug(f"业务记忆检索失败:{e}")
# 合并结果(去重)
all_memories = {}
for mem in user_memories + agent_memories:
mem_id = mem.get('id') if isinstance(mem, dict) else None
if mem_id and mem_id not in all_memories:
all_memories[mem_id] = mem
# 按置信度过滤
min_confidence = self.config['retrieval']['min_confidence']
filtered = [
m for m in all_memories.values()
if m.get('score', 1.0) >= min_confidence
]
# 按置信度排序
filtered.sort(key=lambda x: x.get('score', 0), reverse=True)
return filtered[:top_k]
def format_memories_for_prompt(self, memories: List[Dict]) -> str:
"""格式化记忆为 Prompt 片段"""
if not memories:
return ""
prompt = "\n\n=== 相关记忆 ===\n"
for i, mem in enumerate(memories, 1):
memory_text = mem.get('memory', '') if isinstance(mem, dict) else str(mem)
created_at = mem.get('created_at', '') if isinstance(mem, dict) else ''
prompt += f"{i}. {memory_text}"
if created_at:
prompt += f" (记录于:{created_at})"
prompt += "\n"
prompt += "===============\n"
return prompt
# ========== Post-Hook: 异步写入 ==========
def post_hook_add(self, user_message: str, assistant_message: str, user_id: str = None, agent_id: str = None):
"""Post-Hook: 对话后异步写入(同步方法,仅添加到队列)"""
if not self.config['async_write']['enabled']:
return
if not user_id:
user_id = self.config['metadata']['default_user_id']
if not agent_id:
agent_id = self.config['metadata']['default_agent_id']
messages = [
{"role": "user", "content": user_message},
{"role": "assistant", "content": assistant_message}
]
if self.async_queue:
self.async_queue.add({
'messages': messages,
'user_id': user_id,
'agent_id': agent_id,
'timestamp': datetime.now().isoformat()
})
logger.debug(f"Post-Hook 已提交:user={user_id}, agent={agent_id}")
else:
logger.warning("异步队列未初始化")
async def _async_write_memory(self, item: Dict):
"""异步写入记忆(后台任务)"""
if self.local_memory is None:
return
timeout_ms = self.config['async_write']['timeout_ms']
try:
await asyncio.wait_for(self._execute_write(item), timeout=timeout_ms / 1000)
logger.debug(f"异步写入成功:user={item['user_id']}, agent={item['agent_id']}")
except asyncio.TimeoutError:
logger.warning(f"异步写入超时 ({timeout_ms}ms)")
except Exception as e:
logger.warning(f"异步写入失败:{e}")
async def _execute_write(self, item: Dict):
"""
执行写入 - 使用 metadata 实现维度隔离
关键通过 metadata 字典传递 agent_id而非直接参数
"""
if self.local_memory is None:
return
# 构建元数据,实现业务隔离
custom_metadata = {
"agent_id": item['agent_id'],
"source": "openclaw",
"timestamp": item.get('timestamp'),
"business_type": item['agent_id']
}
# 阻塞操作,放入线程池执行
await asyncio.to_thread(
self.local_memory.add,
messages=item['messages'],
user_id=item['user_id'], # 原生支持的全局用户标识
metadata=custom_metadata # 注入自定义业务维度
)
def _cleanup_cache(self):
"""清理过期缓存"""
if not self.config['cache']['enabled']:
return
current_time = time.time()
ttl = self.config['cache']['ttl']
expired_keys = [k for k, v in self.cache.items() if current_time - v['time'] > ttl]
for key in expired_keys:
del self.cache[key]
if len(self.cache) > self.config['cache']['max_size']:
oldest_keys = sorted(self.cache.keys(), key=lambda k: self.cache[k]['time'])[:len(self.cache) - self.config['cache']['max_size']]
for key in oldest_keys:
del self.cache[key]
def get_status(self) -> Dict:
"""获取状态"""
return {
"initialized": self.local_memory is not None,
"started": self._started,
"async_queue_enabled": self.config['async_write']['enabled'],
"queue_size": len(self.async_queue.queue) if self.async_queue else 0,
"cache_size": len(self.cache),
"qdrant": f"{self.config['qdrant']['host']}:{self.config['qdrant']['port']}"
}
async def shutdown(self):
"""优雅关闭"""
if self.async_queue:
await self.async_queue.stop()
logger.info("mem0 Client 已关闭")
# 全局客户端实例
mem0_client = Mem0Client()

@ -0,0 +1,69 @@
#!/usr/bin/env python3
"""
Mem0 Python 集成脚本
Node.js 插件调用执行实际的记忆操作
"""
import sys
import json
import os
import asyncio
# 设置环境变量
os.environ['OPENAI_API_BASE'] = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
os.environ['OPENAI_BASE_URL'] = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
os.environ['OPENAI_API_KEY'] = os.getenv('MEM0_DASHSCOPE_API_KEY', 'sk-c1715ee0479841399fd359c574647648')
sys.path.insert(0, os.path.dirname(__file__))
from mem0_client import mem0_client
async def main():
if len(sys.argv) < 2:
print(json.dumps({"error": "No action specified"}))
return
action = sys.argv[1]
data = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {}
try:
if action == 'init':
# 初始化 mem0
await mem0_client.start()
print(json.dumps({
"status": "initialized",
"qdrant": f"{mem0_client.config['qdrant']['host']}:{mem0_client.config['qdrant']['port']}"
}))
elif action == 'search':
# 检索记忆
memories = await mem0_client.pre_hook_search(
query=data.get('query', ''),
user_id=data.get('user_id', 'default'),
agent_id=data.get('agent_id', 'general')
)
print(json.dumps({
"memories": memories,
"count": len(memories)
}))
elif action == 'add':
# 添加记忆(异步,不等待)
mem0_client.post_hook_add(
user_message=data.get('user_message', ''),
assistant_message=data.get('assistant_message', ''),
user_id=data.get('user_id', 'default'),
agent_id=data.get('agent_id', 'general')
)
print(json.dumps({"status": "queued"}))
else:
print(json.dumps({"error": f"Unknown action: {action}"}))
except Exception as e:
print(json.dumps({"error": str(e)}))
if __name__ == '__main__':
asyncio.run(main())

@ -0,0 +1,150 @@
#!/usr/bin/env python3
"""
mem0 OpenClaw Commands
处理 /memory 命令
"""
import os
import sys
# 设置环境变量
os.environ['OPENAI_API_BASE'] = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
os.environ['OPENAI_API_KEY'] = 'sk-c1715ee0479841399fd359c574647648'
sys.path.insert(0, os.path.dirname(__file__))
from mem0_client import Mem0Client
# 初始化客户端
mem0 = Mem0Client()
def handle_memory_command(args: str, context: dict) -> str:
"""
处理 /memory 命令
Args:
args: 命令参数
context: 上下文信息 (user_id, agent_id )
Returns:
回复文本
"""
parts = args.split(' ', 2)
action = parts[0] if parts else 'help'
user_id = context.get('user_id', 'default')
agent_id = context.get('agent_id', 'main')
if action == 'add':
content = parts[1] if len(parts) > 1 else ''
return handle_add(content, user_id, agent_id)
elif action == 'search':
query = parts[1] if len(parts) > 1 else ''
return handle_search(query, user_id, agent_id)
elif action == 'list':
return handle_list(user_id, agent_id)
elif action == 'delete':
memory_id = parts[1] if len(parts) > 1 else ''
return handle_delete(memory_id, user_id, agent_id)
elif action == 'status':
return handle_status()
else:
return get_help()
def handle_add(content: str, user_id: str, agent_id: str) -> str:
"""添加记忆"""
if not content:
return "❌ 请提供记忆内容\n\n用法:/memory add <内容>"
result = mem0.add([{"role": "user", "content": content}], user_id=user_id)
if result and 'error' in result:
return f"❌ 添加失败:{result['error']}"
return f"✅ 记忆已添加\n\n📝 内容:{content[:200]}..."
def handle_search(query: str, user_id: str, agent_id: str) -> str:
"""搜索记忆"""
if not query:
return "❌ 请提供搜索关键词\n\n用法:/memory search <关键词>"
results = mem0.search(query, user_id=user_id, limit=5)
if not results:
return f"🔍 未找到与 \"{query}\" 相关的记忆"
response = f"🔍 找到 {len(results)} 条相关记忆:\n\n"
for i, mem in enumerate(results, 1):
memory_text = mem.get('memory', 'N/A')
response += f"{i}. {memory_text}\n"
if mem.get('created_at'):
response += f" 📅 {mem['created_at']}\n"
response += "\n"
return response
def handle_list(user_id: str, agent_id: str) -> str:
"""列出所有记忆"""
results = mem0.get_all(user_id=user_id)
if not results:
return "📭 暂无记忆"
response = f"📋 共 {len(results)} 条记忆:\n\n"
for i, mem in enumerate(results[:20], 1):
memory_text = mem.get('memory', 'N/A')
truncated = memory_text[:100] + '...' if len(memory_text) > 100 else memory_text
response += f"{i}. {truncated}\n"
response += f" 🆔 {mem.get('id', 'N/A')}\n\n"
if len(results) > 20:
response += f"... 还有 {len(results) - 20} 条记忆\n"
return response
def handle_delete(memory_id: str, user_id: str, agent_id: str) -> str:
"""删除记忆"""
if not memory_id:
return "❌ 请提供记忆 ID\n\n用法:/memory delete <ID>"
success = mem0.delete(memory_id, user_id=user_id)
if success:
return f"✅ 记忆已删除:{memory_id}"
else:
return f"❌ 删除失败:记忆不存在或权限不足"
def handle_status() -> str:
"""查看状态"""
status = mem0.get_status()
response = "📊 mem0 状态:\n\n"
response += f"本地记忆:{'' if status['initialized'] else ''}\n"
response += f"Qdrant 地址:{status['qdrant']}\n"
return response
def get_help() -> str:
"""获取帮助文本"""
return """📖 mem0 记忆管理命令
用法/memory <命令> [参数]
可用命令:
add <内容> - 添加记忆
search <关键词> - 搜索记忆
list - 列出所有记忆
delete <ID> - 删除记忆
status - 查看状态
help - 显示帮助
示例:
/memory add 用户偏好使用 UTC 时区
/memory search 时区
/memory list
/memory status"""
# 测试
if __name__ == '__main__':
print("测试 /memory 命令...")
print(handle_status())

@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""OpenClaw 拦截器:Pre-Hook + Post-Hook"""
import asyncio
import logging
import sys
sys.path.insert(0, '/root/.openclaw/workspace/skills/mem0-integration')
from mem0_client import mem0_client
logger = logging.getLogger(__name__)
class ConversationInterceptor:
def __init__(self):
self.enabled = True
async def pre_hook(self, query: str, context: dict) -> str:
if not self.enabled:
return None
try:
user_id = context.get('user_id', 'default')
agent_id = context.get('agent_id', 'general')
memories = await mem0_client.pre_hook_search(query=query, user_id=user_id, agent_id=agent_id)
if memories:
return mem0_client.format_memories_for_prompt(memories)
return None
except Exception as e:
logger.error(f"Pre-Hook 失败:{e}")
return None
async def post_hook(self, user_message: str, assistant_message: str, context: dict):
if not self.enabled:
return
try:
user_id = context.get('user_id', 'default')
agent_id = context.get('agent_id', 'general')
await mem0_client.post_hook_add(user_message, assistant_message, user_id, agent_id)
logger.debug(f"Post-Hook: 已提交对话")
except Exception as e:
logger.error(f"Post-Hook 失败:{e}")
interceptor = ConversationInterceptor()
async def intercept_before_llm(query: str, context: dict):
return await interceptor.pre_hook(query, context)
async def intercept_after_response(user_msg: str, assistant_msg: str, context: dict):
await interceptor.post_hook(user_msg, assistant_msg, context)

@ -0,0 +1,36 @@
{
"name": "mem0-integration",
"version": "1.0.0",
"description": "mem0 记忆系统集成,提供长期记忆存储和语义搜索",
"author": "OpenClaw Team",
"enabled": true,
"commands": [
{
"name": "memory",
"description": "记忆管理命令",
"handler": "openclaw_commands.handle_memory_command",
"usage": "/memory <add|search|list|delete|status|help> [参数]",
"examples": [
"/memory add 用户偏好使用 UTC 时区",
"/memory search 时区",
"/memory list",
"/memory status"
]
}
],
"config": {
"qdrant": {
"host": "localhost",
"port": 6333,
"collection_name": "mem0_local"
},
"llm": {
"model": "qwen-plus",
"api_base": "https://dashscope.aliyuncs.com/compatible-mode/v1"
}
},
"dependencies": [
"mem0ai",
"pyyaml"
]
}

@ -0,0 +1,77 @@
#!/usr/bin/env python3
# /root/.openclaw/workspace/skills/mem0-integration/test_integration.py
import asyncio
import logging
import sys
sys.path.insert(0, '/root/.openclaw/workspace/skills/mem0-integration')
from mem0_client import mem0_client
from openclaw_interceptor import intercept_before_llm, intercept_after_response
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
async def mock_llm(system_prompt, user_message):
"""模拟 LLM 调用"""
return f"这是模拟回复:{user_message[:20]}..."
async def test_full_flow():
"""测试完整对话流程"""
print("=" * 60)
print("🧪 测试 mem0 集成架构")
print("=" * 60)
context = {
'user_id': '5237946060',
'agent_id': 'general'
}
# ========== 测试 1: Pre-Hook 检索 ==========
print("\n1 测试 Pre-Hook 检索...")
query = "我平时喜欢用什么时区?"
memory_prompt = await intercept_before_llm(query, context)
if memory_prompt:
print(f"✅ 检索到记忆:\n{memory_prompt}")
else:
print(" 未检索到记忆(正常,首次对话)")
# ========== 测试 2: 完整对话流程 ==========
print("\n2 测试完整对话流程...")
user_message = "我平时喜欢使用 UTC 时区,请用简体中文和我交流"
print(f"用户:{user_message}")
response = await mock_llm("system", user_message)
print(f"助手:{response}")
# ========== 测试 3: Post-Hook 异步写入 ==========
print("\n3 测试 Post-Hook 异步写入...")
await intercept_after_response(user_message, response, context)
print(f"✅ 对话已提交到异步队列")
print(f" 队列大小:{len(mem0_client.async_queue.queue) if mem0_client.async_queue else 0}")
# ========== 等待异步写入完成 ==========
print("\n4 等待异步写入 (5 秒)...")
await asyncio.sleep(5)
# ========== 测试 4: 验证记忆已存储 ==========
print("\n5 验证记忆已存储...")
memories = await mem0_client.pre_hook_search("时区", **context)
print(f"✅ 检索到 {len(memories)} 条记忆")
for i, mem in enumerate(memories, 1):
print(f" {i}. {mem.get('memory', 'N/A')[:100]}")
# ========== 状态报告 ==========
print("\n" + "=" * 60)
print("📊 系统状态:")
status = mem0_client.get_status()
for key, value in status.items():
print(f" {key}: {value}")
print("=" * 60)
print("✅ 测试完成")
if __name__ == '__main__':
asyncio.run(test_full_flow())

@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""
mem0 验证脚本 - 测试 DashScope 1024 维度配置
"""
import os
import sys
import asyncio
# 设置环境变量
os.environ['OPENAI_API_BASE'] = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
os.environ['OPENAI_BASE_URL'] = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
os.environ['MEM0_DASHSCOPE_API_KEY'] = 'sk-4111c9dba5334510968f9ae72728944e'
os.environ['OPENAI_API_KEY'] = 'sk-4111c9dba5334510968f9ae72728944e'
# 添加路径
sys.path.insert(0, '/root/.openclaw/workspace/skills/mem0-integration')
print("=" * 60)
print("🔍 mem0 验证测试 - DashScope 1024 维度配置")
print("=" * 60)
# 测试 1: 检查环境变量
print("\n[1/4] 检查环境变量...")
print(f" OPENAI_BASE_URL: {os.environ.get('OPENAI_BASE_URL')}")
print(f" OPENAI_API_KEY: {os.environ.get('OPENAI_API_KEY')[:10]}...")
print(f" ✅ 环境变量已设置")
# 测试 2: 导入 mem0_client
print("\n[2/4] 导入 mem0_client 模块...")
try:
from mem0_client import Mem0Client
print(" ✅ 模块导入成功")
except Exception as e:
print(f" ❌ 模块导入失败:{e}")
sys.exit(1)
# 测试 3: 初始化客户端
print("\n[3/4] 初始化 Mem0Client...")
try:
client = Mem0Client()
print(f" ✅ 客户端初始化成功")
print(f" - local_memory: {client.local_memory is not None}")
print(f" - qdrant_host: {client.config['qdrant']['host']}")
print(f" - embedder_model: {client.config['embedder']['config'].get('model')}")
print(f" - embedding_dims: {client.config['embedder']['config'].get('dimensions')}")
except Exception as e:
print(f" ❌ 初始化失败:{e}")
import traceback
traceback.print_exc()
sys.exit(1)
# 测试 4: 测试向量生成(验证 1024 维度)
print("\n[4/4] 测试向量生成(验证 1024 维度)...")
async def test_embedding():
try:
# 使用 DashScope API 直接测试
import httpx
api_key = os.environ.get('OPENAI_API_KEY')
api_base = os.environ.get('OPENAI_BASE_URL')
payload = {
"model": "text-embedding-v3",
"input": "测试文本 - 验证 1024 维度配置",
"dimensions": 1024
}
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
async with httpx.AsyncClient(timeout=30.0) as http:
response = await http.post(
f"{api_base}/embeddings",
json=payload,
headers=headers
)
if response.status_code == 200:
data = response.json()
embedding = data['data'][0]['embedding']
print(f" ✅ 向量生成成功")
print(f" - 维度:{len(embedding)}")
print(f" - 状态码:{response.status_code}")
if len(embedding) == 1024:
print(f" 🎉 维度验证通过!(1024)")
return True
else:
print(f" 维度不匹配!期望 1024,实际 {len(embedding)}")
return False
else:
print(f" ❌ API 请求失败:{response.status_code}")
print(f" 响应:{response.text[:200]}")
return False
except Exception as e:
print(f" ❌ 测试失败:{e}")
import traceback
traceback.print_exc()
return False
# 运行异步测试
result = asyncio.run(test_embedding())
print("\n" + "=" * 60)
if result:
print("✅ 所有测试通过!mem0 配置正确")
sys.exit(0)
else:
print(" 部分测试失败,请检查配置")
sys.exit(1)

@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""mem0 集成架构测试"""
import asyncio
import logging
import sys
sys.path.insert(0, '/root/.openclaw/workspace/skills/mem0-integration')
from mem0_client import mem0_client
from openclaw_interceptor import intercept_before_llm, intercept_after_response
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
async def mock_llm(system_prompt, user_message):
"""模拟 LLM 调用"""
return f"这是模拟回复:{user_message[:20]}..."
async def test_full_flow():
"""测试完整对话流程"""
print("=" * 60)
print("🧪 测试 mem0 集成架构(生产级)")
print("=" * 60)
# ========== 启动 mem0 Client ==========
print("\n0 启动 mem0 Client...")
await mem0_client.start()
print(f"✅ mem0 Client 已启动")
context = {
'user_id': '5237946060',
'agent_id': 'general'
}
# ========== 测试 1: Pre-Hook 检索 ==========
print("\n1 测试 Pre-Hook 检索...")
query = "我平时喜欢用什么时区?"
memory_prompt = await intercept_before_llm(query, context)
if memory_prompt:
print(f"✅ 检索到记忆:\n{memory_prompt}")
else:
print(" 未检索到记忆(正常,首次对话)")
# ========== 测试 2: 完整对话流程 ==========
print("\n2 测试完整对话流程...")
user_message = "我平时喜欢使用 UTC 时区,请用简体中文和我交流"
print(f"用户:{user_message}")
response = await mock_llm("system", user_message)
print(f"助手:{response}")
# ========== 测试 3: Post-Hook 异步写入 ==========
print("\n3 测试 Post-Hook 异步写入...")
await intercept_after_response(user_message, response, context)
print(f"✅ 对话已提交到异步队列")
print(f" 队列大小:{len(mem0_client.async_queue.queue) if mem0_client.async_queue else 0}")
# ========== 等待异步写入完成 ==========
print("\n4 等待异步写入 (5 秒)...")
await asyncio.sleep(5)
# ========== 测试 4: 验证记忆已存储 ==========
print("\n5 验证记忆已存储...")
memories = await mem0_client.pre_hook_search("时区", **context)
print(f"✅ 检索到 {len(memories)} 条记忆")
for i, mem in enumerate(memories, 1):
print(f" {i}. {mem.get('memory', 'N/A')[:100]}")
# ========== 状态报告 ==========
print("\n" + "=" * 60)
print("📊 系统状态:")
status = mem0_client.get_status()
for key, value in status.items():
print(f" {key}: {value}")
print("=" * 60)
# ========== 关闭 ==========
print("\n6 关闭 mem0 Client...")
await mem0_client.shutdown()
print("✅ 已关闭")
print("\n✅ 测试完成")
if __name__ == '__main__':
asyncio.run(test_full_flow())

@ -0,0 +1,41 @@
[Unit]
Description=OpenClaw Agent Health Monitor
Documentation=https://docs.openclaw.ai
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=/root/.openclaw/workspace
Environment=NODE_ENV=production
Environment=HOME=/root
Environment=XDG_RUNTIME_DIR=/run/user/0
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/0/bus
# Monitor process
ExecStart=/usr/bin/node /root/.openclaw/workspace/agent-monitor.js
# Auto-healing configuration
Restart=always
RestartSec=5
StartLimitInterval=300
StartLimitBurst=10
# Resource limits
MemoryLimit=512M
CPUQuota=20%
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=openclaw-monitor
# Security
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/root/.openclaw/workspace/logs
[Install]
WantedBy=multi-user.target

@ -0,0 +1,51 @@
# User-level systemd service for OpenClaw Gateway
# Install to: ~/.config/systemd/user/openclaw-gateway.service
# Required: loginctl enable-linger $(whoami)
[Unit]
Description=OpenClaw Gateway (v2026.2.19-2)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/www/server/nodejs/v24.13.1/bin/node /www/server/nodejs/v24.13.1/lib/node_modules/openclaw/dist/index.js gateway --port 18789
Restart=always
RestartSec=10
StartLimitInterval=300
StartLimitBurst=5
KillMode=process
TimeoutStopSec=30
# Critical environment variables for user-level systemd
Environment=HOME=/root
Environment=XDG_RUNTIME_DIR=/run/user/0
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/0/bus
Environment=PATH=/root/.local/bin:/root/.npm-global/bin:/root/bin:/root/.volta/bin:/root/.asdf/shims:/root/.bun/bin:/root/.nvm/current/bin:/root/.fnm/current/bin:/root/.local/share/pnpm:/usr/local/bin:/usr/bin:/bin
Environment=OPENCLAW_GATEWAY_PORT=18789
Environment=OPENCLAW_GATEWAY_TOKEN=9e2e91b31a56fb56a35e91821c025267292ec44c26169b12
Environment=OPENCLAW_SYSTEMD_UNIT=openclaw-gateway.service
Environment=OPENCLAW_SERVICE_MARKER=openclaw
Environment=OPENCLAW_SERVICE_KIND=gateway
Environment=OPENCLAW_SERVICE_VERSION=2026.2.19-2
# Resource limits
MemoryLimit=2G
CPUQuota=80%
# Security
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/root/.openclaw
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=openclaw-gateway
# Watchdog
WatchdogSec=30
[Install]
WantedBy=default.target

@ -0,0 +1,42 @@
[Unit]
Description=OpenClaw Gateway Service
Documentation=https://docs.openclaw.ai
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=/root/.openclaw
Environment=NODE_ENV=production
# Main gateway process
ExecStart=/usr/bin/node /www/server/nodejs/v24.13.1/bin/openclaw gateway start
ExecReload=/bin/kill -HUP $MAINPID
# Auto-healing configuration
Restart=always
RestartSec=10
StartLimitInterval=300
StartLimitBurst=5
# Resource limits to prevent OOM
MemoryLimit=2G
CPUQuota=80%
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=openclaw-gateway
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/root/.openclaw
# Watchdog for health monitoring
WatchdogSec=30
[Install]
WantedBy=multi-user.target
Loading…
Cancel
Save