openclaw-home-pc/workspace/memory_manager.py
2026-03-21 15:31:06 +08:00

261 lines
8.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
🦐 皮皮虾记忆管理工具
自动化记忆维护:整理、清理、提炼、归档
"""
import os
import json
import re
from datetime import datetime, timedelta
from pathlib import Path
# 配置
WORKSPACE = Path.home() / ".openclaw" / "workspace"
MEMORY_DIR = WORKSPACE / "memory"
MEMORY_MD = WORKSPACE / "MEMORY.md"
RETENTION_DAYS = 30 # 短期记忆保留天数
# ANSI 颜色
class Colors:
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BLUE = '\033[94m'
END = '\033[0m'
BOLD = '\033[1m'
def print_header(text):
print(f"\n{Colors.BOLD}{Colors.BLUE}{'='*60}{Colors.END}")
print(f"{Colors.BOLD}{Colors.BLUE} {text}{Colors.END}")
print(f"{Colors.BOLD}{Colors.BLUE}{'='*60}{Colors.END}\n")
def print_success(text):
print(f"{Colors.GREEN}{text}{Colors.END}")
def print_warning(text):
print(f"{Colors.YELLOW}⚠️ {text}{Colors.END}")
def print_info(text):
print(f"{Colors.BLUE}📌 {text}{Colors.END}")
def get_memory_files():
"""获取所有记忆文件"""
if not MEMORY_DIR.exists():
return []
return sorted([f for f in MEMORY_DIR.glob("*.md") if f.is_file()])
def get_daily_logs():
"""获取所有每日日志文件"""
files = get_memory_files()
daily_logs = []
for f in files:
if re.match(r'^\d{4}-\d{2}-\d{2}\.md$', f.name):
daily_logs.append(f)
return daily_logs
def parse_log_date(filename):
"""从文件名解析日期"""
match = re.match(r'^(\d{4}-\d{2}-\d{2})\.md$', filename)
if match:
return datetime.strptime(match.group(1), '%Y-%m-%d')
return None
def get_file_size_kb(filepath):
"""获取文件大小KB"""
return round(filepath.stat().st_size / 1024, 2)
def read_file_content(filepath):
"""读取文件内容"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
print_warning(f"读取失败 {filepath.name}: {e}")
return None
def write_file_content(filepath, content):
"""写入文件内容"""
try:
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
return True
except Exception as e:
print_warning(f"写入失败 {filepath.name}: {e}")
return False
def extract_important_content(content, filepath):
"""从每日日志中提取重要内容"""
important_items = []
filename = filepath.stem # 不含扩展名的文件名
# 提取会话记录中的关键事件
session_matches = re.findall(r'###.*?—.*?\n(.*?)(?=###|$)', content, re.DOTALL)
for session in session_matches[:5]: # 最多 5 个会话
# 提取用户请求
user_req = re.search(r'用户请求:(.*?)(?:\n|$)', session)
# 提取结果
result = re.search(r'结果:(✅.*?)(?:\n|$)', session)
if user_req and result:
important_items.append(f"**{filename}** - {user_req.group(1).strip()}{result.group(1).strip()}")
elif user_req:
important_items.append(f"**{filename}** - {user_req.group(1).strip()}")
# 提取重要性评分记录中的高分项
rating_matches = re.findall(r'\|\s*([^\|]+)\s*\|\s*([45])\s*\|\s*(✅.*?)(?:\n|$)', content)
for item, score, action in rating_matches[:3]:
important_items.append(f"**高分记忆** - {item.strip()} (评分{score}) → {action.strip()}")
return important_items
def update_memory_md(important_items):
"""更新 MEMORY.md 文件"""
if not MEMORY_MD.exists():
print_warning("MEMORY.md 不存在,跳过更新")
return
content = read_file_content(MEMORY_MD)
if not content:
return
# 在"核心记忆内容"部分添加新内容
today = datetime.now().strftime('%Y-%m-%d')
new_section = f"\n### {today} 自动提炼\n\n"
for item in important_items[:10]: # 最多添加 10 条
new_section += f"{item}\n"
new_section += "\n"
# 找到"核心记忆内容"部分并插入
if "## 📝 核心记忆内容" in content:
parts = content.split("## 📝 核心记忆内容")
if len(parts) == 2:
new_content = parts[0] + "## 📝 核心记忆内容" + new_section + parts[1]
# 更新最后更新时间
new_content = re.sub(
r'\*最后更新:\d{4}-\d{2}-\d{2}',
f'*最后更新:{today}',
new_content
)
if write_file_content(MEMORY_MD, new_content):
print_success("MEMORY.md 已更新")
else:
print_warning("未找到核心记忆内容部分")
def archive_old_logs(old_logs):
"""归档旧日志"""
if not old_logs:
return
archive_dir = MEMORY_DIR / "archive"
archive_dir.mkdir(exist_ok=True)
for log_file in old_logs:
# 移动到 archive 目录
dest = archive_dir / log_file.name
try:
log_file.rename(dest)
print_success(f"已归档:{log_file.name} → archive/")
except Exception as e:
print_warning(f"归档失败 {log_file.name}: {e}")
def cleanup_empty_files():
"""清理空文件"""
cleaned = 0
for f in get_memory_files():
if f.stat().st_size == 0:
try:
f.unlink()
print_success(f"已删除空文件:{f.name}")
cleaned += 1
except Exception as e:
print_warning(f"删除失败 {f.name}: {e}")
return cleaned
def generate_report(daily_logs, old_logs, important_items):
"""生成维护报告"""
today = datetime.now()
print_header("📊 记忆维护报告")
print(f"{Colors.BOLD}记忆文件统计:{Colors.END}")
print(f" - 每日日志总数:{len(daily_logs)}")
print(f" - 长期记忆文件:{len(get_memory_files()) - len(daily_logs)}")
print(f"\n{Colors.BOLD}清理结果:{Colors.END}")
if old_logs:
print(f" - 超过{RETENTION_DAYS}天的日志:{len(old_logs)}")
print(f" - 已归档到 memory/archive/")
else:
print(f" - 无需清理(所有日志都在{RETENTION_DAYS}天内)")
print(f"\n{Colors.BOLD}内容提炼:{Colors.END}")
if important_items:
print(f" - 提炼重要内容:{len(important_items)}")
print(f" - 已添加到 MEMORY.md")
else:
print(f" - 暂无可提炼内容")
print(f"\n{Colors.BOLD}下次维护:{Colors.END}")
next_check = today + timedelta(days=7)
print(f" - 建议下次检查:{next_check.strftime('%Y-%m-%d')}")
def main():
"""主函数"""
print_header("🦐 皮皮虾记忆维护工具")
# 1. 获取所有每日日志
daily_logs = get_daily_logs()
print_info(f"找到 {len(daily_logs)} 个每日日志文件")
# 2. 识别过期日志(超过 RETENTION_DAYS 天)
today = datetime.now()
cutoff_date = today - timedelta(days=RETENTION_DAYS)
old_logs = []
for log_file in daily_logs:
log_date = parse_log_date(log_file.name)
if log_date and log_date < cutoff_date:
old_logs.append(log_file)
if old_logs:
print_warning(f"发现 {len(old_logs)} 个过期日志(超过{RETENTION_DAYS}天)")
else:
print_success(f"所有日志都在{RETENTION_DAYS}天内,无需清理")
# 3. 从最近的日志中提取重要内容
important_items = []
recent_logs = sorted(daily_logs, key=lambda x: x.name, reverse=True)[:3]
for log_file in recent_logs:
content = read_file_content(log_file)
if content:
items = extract_important_content(content, log_file)
important_items.extend(items)
if important_items:
print_success(f"从最近日志中提取了 {len(important_items)} 条重要内容")
# 4. 更新 MEMORY.md
if important_items:
update_memory_md(important_items)
# 5. 归档过期日志(询问用户确认)
if old_logs:
print(f"\n{Colors.YELLOW}准备归档 {len(old_logs)} 个过期日志到 memory/archive/{Colors.END}")
# 自动执行归档
archive_old_logs(old_logs)
# 6. 清理空文件
cleaned = cleanup_empty_files()
if cleaned:
print_success(f"清理了 {cleaned} 个空文件")
# 7. 生成报告
generate_report(daily_logs, old_logs, important_items)
print(f"\n{Colors.GREEN}🦐 记忆维护完成!{Colors.END}\n")
if __name__ == "__main__":
main()