| New file |
| | |
| | | #!/usr/bin/env python3 |
| | | """ |
| | | memory-md-hot 每日维护脚本 |
| | | 维护 memory.md 文件的事件流水和重要事件 |
| | | 保留所有历史记录,需配合 memory-md-archive 技能归档 |
| | | """ |
| | | |
| | | import os |
| | | import re |
| | | import sys |
| | | from datetime import datetime |
| | | from pathlib import Path |
| | | |
| | | # 配置 |
| | | MEMORY_FILE = Path.home() / ".openclaw" / "workspace" / "MEMORY.md" |
| | | ARCHIVE_THRESHOLD_KB = 50 # 建议归档的阈值(仅提示,不强制) |
| | | |
| | | |
| | | def read_memory_file(): |
| | | """读取 memory.md 文件内容""" |
| | | if not MEMORY_FILE.exists(): |
| | | return "" |
| | | return MEMORY_FILE.read_text(encoding='utf-8') |
| | | |
| | | |
| | | def write_memory_file(content): |
| | | """写入 memory.md 文件""" |
| | | MEMORY_FILE.parent.mkdir(parents=True, exist_ok=True) |
| | | MEMORY_FILE.write_text(content, encoding='utf-8') |
| | | |
| | | |
| | | def get_file_size(): |
| | | """获取文件大小(字节)""" |
| | | if not MEMORY_FILE.exists(): |
| | | return 0 |
| | | return MEMORY_FILE.stat().st_size |
| | | |
| | | |
| | | def parse_date_from_line(line): |
| | | """从行中提取日期(YYYY-MM-DD 格式)""" |
| | | match = re.search(r'(\d{4}-\d{2}-\d{2})', line) |
| | | if match: |
| | | try: |
| | | return datetime.strptime(match.group(1), '%Y-%m-%d').date() |
| | | except ValueError: |
| | | return None |
| | | return None |
| | | |
| | | |
| | | def extract_event_entries(content): |
| | | """ |
| | | 从内容中提取事件条目 |
| | | 返回: (important_events, daily_events) 元组 |
| | | """ |
| | | important_events = [] |
| | | daily_events = [] |
| | | |
| | | lines = content.split('\n') |
| | | in_important = False |
| | | in_daily = False |
| | | |
| | | for line in lines: |
| | | stripped = line.strip() |
| | | |
| | | # 检测区块 |
| | | if '重要事件' in stripped and stripped.startswith('#'): |
| | | in_important = True |
| | | in_daily = False |
| | | continue |
| | | elif ('事件流水' in stripped) and stripped.startswith('#'): |
| | | in_important = False |
| | | in_daily = True |
| | | continue |
| | | elif stripped.startswith('#') and in_important: |
| | | in_important = False |
| | | elif stripped.startswith('#') and in_daily: |
| | | in_daily = False |
| | | |
| | | # 收集事件 |
| | | if in_important and stripped.startswith('-'): |
| | | if len(stripped) > 10: # 过滤空行和短行 |
| | | important_events.append(line) |
| | | elif in_daily and (stripped.startswith('-') or stripped.startswith('*')): |
| | | daily_events.append(line) |
| | | |
| | | return important_events, daily_events |
| | | |
| | | |
| | | def generate_default_content(): |
| | | """生成默认的 memory.md 内容""" |
| | | today = datetime.now().strftime('%Y-%m-%d') |
| | | return f"""# MEMORY.md - 热记忆 / 活跃记忆 |
| | | |
| | | > 本文件记录近期发生的重要事情,详细信息可通过记忆检索获取。 |
| | | |
| | | --- |
| | | |
| | | ## 🔔 重要事件 |
| | | |
| | | > 记录具有全局长期性影响的重要决策和事件。 |
| | | > 添加重要事件时会告知用户。 |
| | | |
| | | <!-- 重要事件在此添加 --> |
| | | |
| | | --- |
| | | |
| | | ## 📅 事件流水 |
| | | |
| | | > 按天分组,每天主要事情的概要。 |
| | | > 所有记录永久保留,可使用 memory-md-archive 技能归档瘦身。 |
| | | |
| | | ### {{today}} |
| | | |
| | | - {today} 10:00 | memory.md 初始化 | 热记忆系统 |
| | | |
| | | --- |
| | | |
| | | *文件大小: ~0.5KB | 事件数: 1* |
| | | *维护脚本: `memory-md-hot/scripts/daily_maintenance.py`* |
| | | *归档提示: 文件较大时请使用 memory-md-archive 技能归档* |
| | | """.format(today=today) |
| | | |
| | | |
| | | def update_memory_file(new_daily_events=None, important_event=None): |
| | | """ |
| | | 更新 memory.md 文件 |
| | | |
| | | Args: |
| | | new_daily_events: 新的每日事件列表 [(date, time, summary, keywords), ...] |
| | | important_event: 重要事件元组 (date, time, summary, keywords) 或 None |
| | | |
| | | Returns: |
| | | dict: 操作结果 |
| | | """ |
| | | result = { |
| | | 'success': True, |
| | | 'added_daily': 0, |
| | | 'added_important': False, |
| | | 'current_size_kb': 0, |
| | | 'total_events': 0, |
| | | 'archive_suggested': False, |
| | | 'messages': [] |
| | | } |
| | | |
| | | # 读取现有内容 |
| | | content = read_memory_file() |
| | | if not content: |
| | | content = generate_default_content() |
| | | |
| | | # 提取现有事件 |
| | | existing_important, existing_daily = extract_event_entries(content) |
| | | |
| | | # 保留所有现有日常事件(不再移除旧记录) |
| | | all_daily = list(existing_daily) |
| | | |
| | | # 添加新的日常事件 |
| | | if new_daily_events: |
| | | for event in new_daily_events: |
| | | date, time, summary, keywords = event |
| | | entry = f"- {date} {time} | {summary} | {keywords}" |
| | | # 检查是否已存在 |
| | | if not any(entry.strip() in existing.strip() for existing in all_daily): |
| | | all_daily.append(entry) |
| | | result['added_daily'] += 1 |
| | | |
| | | # 添加重要事件 |
| | | new_important = list(existing_important) |
| | | if important_event: |
| | | date, time, summary, keywords = important_event |
| | | entry = f"- {date} {time} | {summary} | {keywords}" |
| | | new_important.append(entry) |
| | | result['added_important'] = True |
| | | result['messages'].append(f"重要事件已记录: {summary}") |
| | | |
| | | # 重新构建文件内容 |
| | | today = datetime.now().strftime('%Y-%m-%d') |
| | | |
| | | # 按日期分组日常事件 |
| | | daily_by_date = {} |
| | | for line in all_daily: |
| | | date = parse_date_from_line(line) |
| | | if date: |
| | | date_str = date.strftime('%Y-%m-%d') |
| | | if date_str not in daily_by_date: |
| | | daily_by_date[date_str] = [] |
| | | daily_by_date[date_str].append(line) |
| | | |
| | | # 构建日常事件区块 |
| | | daily_sections = [] |
| | | for date_str in sorted(daily_by_date.keys(), reverse=True): |
| | | daily_sections.append(f"\n### {date_str}\n") |
| | | daily_sections.extend([f"{line}\n" for line in daily_by_date[date_str]]) |
| | | |
| | | daily_content = ''.join(daily_sections) if daily_sections else f"\n### {today}\n\n- {today} --:-- | 暂无记录 | --\n" |
| | | |
| | | # 构建重要事件区块 |
| | | important_content = '\n'.join(new_important) if new_important else "<!-- 重要事件在此添加 -->" |
| | | |
| | | # 计算统计 |
| | | total_events = len(all_daily) + len(new_important) |
| | | result['total_events'] = total_events |
| | | |
| | | # 组装最终内容 |
| | | current_size = get_file_size() |
| | | result['current_size_kb'] = round(current_size / 1024, 2) |
| | | |
| | | new_content = f"""# MEMORY.md - 热记忆 / 活跃记忆 |
| | | |
| | | > 本文件记录近期发生的重要事情,详细信息可通过记忆检索获取。 |
| | | |
| | | --- |
| | | |
| | | ## 🔔 重要事件 |
| | | |
| | | > 记录具有全局长期性影响的重要决策和事件。 |
| | | > 添加重要事件时会告知用户。 |
| | | |
| | | {important_content} |
| | | |
| | | --- |
| | | |
| | | ## 📅 事件流水 |
| | | |
| | | > 按天分组,每天主要事情的概要。 |
| | | > 所有记录永久保留,可使用 memory-md-archive 技能归档瘦身。 |
| | | {daily_content} |
| | | --- |
| | | |
| | | *文件大小: ~{result['current_size_kb']:.1f}KB | 事件数: {total_events}* |
| | | *维护脚本: `memory-md-hot/scripts/daily_maintenance.py`* |
| | | *归档提示: 文件较大时请使用 memory-md-archive 技能归档* |
| | | """ |
| | | |
| | | # 写入文件 |
| | | write_memory_file(new_content) |
| | | |
| | | # 检查是否需要建议归档 |
| | | new_size = get_file_size() |
| | | result['current_size_kb'] = round(new_size / 1024, 2) |
| | | |
| | | if result['current_size_kb'] > ARCHIVE_THRESHOLD_KB: |
| | | result['archive_suggested'] = True |
| | | result['messages'].append(f"提示: 文件大小 ({result['current_size_kb']}KB) 超过 {ARCHIVE_THRESHOLD_KB}KB,建议使用 memory-md-archive 技能归档") |
| | | |
| | | return result |
| | | |
| | | |
| | | def get_stats(): |
| | | """获取文件统计信息""" |
| | | content = read_memory_file() |
| | | if not content: |
| | | return { |
| | | 'size_bytes': 0, |
| | | 'size_kb': 0, |
| | | 'total_events': 0, |
| | | 'important_events': 0, |
| | | 'daily_events': 0, |
| | | 'archive_suggested': False |
| | | } |
| | | |
| | | important, daily = extract_event_entries(content) |
| | | size = get_file_size() |
| | | size_kb = round(size / 1024, 2) |
| | | |
| | | return { |
| | | 'size_bytes': size, |
| | | 'size_kb': size_kb, |
| | | 'total_events': len(important) + len(daily), |
| | | 'important_events': len(important), |
| | | 'daily_events': len(daily), |
| | | 'archive_suggested': size_kb > ARCHIVE_THRESHOLD_KB |
| | | } |
| | | |
| | | |
| | | def main(): |
| | | """主函数 - 供命令行调用""" |
| | | if len(sys.argv) < 2: |
| | | print("用法: python daily_maintenance.py <command> [args]") |
| | | print("") |
| | | print("命令:") |
| | | print(" update - 执行每日更新") |
| | | print(" stats - 查看文件统计") |
| | | print(" add-daily <date> <time> <summary> <keywords>") |
| | | print(" - 添加日常事件") |
| | | print(" add-important <date> <time> <summary> <keywords>") |
| | | print(" - 添加重要事件") |
| | | print("") |
| | | print("示例:") |
| | | print(' python daily_maintenance.py add-daily 2026-03-16 "10:30" "测试脚本" "开发,测试"') |
| | | sys.exit(1) |
| | | |
| | | command = sys.argv[1] |
| | | |
| | | if command == "update": |
| | | result = update_memory_file() |
| | | print(f"更新完成:") |
| | | print(f" - 添加日常事件: {result['added_daily']}") |
| | | print(f" - 添加重要事件: {'是' if result['added_important'] else '否'}") |
| | | print(f" - 当前大小: {result['current_size_kb']}KB") |
| | | print(f" - 总事件数: {result['total_events']}") |
| | | for msg in result['messages']: |
| | | print(f" - {msg}") |
| | | |
| | | elif command == "stats": |
| | | stats = get_stats() |
| | | print(f"文件统计:") |
| | | print(f" - 文件大小: {stats['size_kb']}KB ({stats['size_bytes']} 字节)") |
| | | print(f" - 总事件数: {stats['total_events']}") |
| | | print(f" - 重要事件: {stats['important_events']}") |
| | | print(f" - 日常事件: {stats['daily_events']}") |
| | | if stats['archive_suggested']: |
| | | print(f" - 归档建议: 文件较大,建议使用 memory-md-archive 技能归档") |
| | | else: |
| | | print(f" - 归档建议: 暂无需要") |
| | | |
| | | elif command == "add-daily" and len(sys.argv) >= 6: |
| | | event = (sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]) |
| | | result = update_memory_file(new_daily_events=[event]) |
| | | print(f"已添加日常事件: {event}") |
| | | if result['archive_suggested']: |
| | | print(f"提示: 文件大小 {result['current_size_kb']}KB,建议归档") |
| | | |
| | | elif command == "add-important" and len(sys.argv) >= 6: |
| | | event = (sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]) |
| | | result = update_memory_file(important_event=event) |
| | | print(f"已添加重要事件: {event}") |
| | | print(f"ℹ️ 请知悉用户: 已记录重要事件 '{sys.argv[4]}'") |
| | | if result['archive_suggested']: |
| | | print(f"提示: 文件大小 {result['current_size_kb']}KB,建议归档") |
| | | |
| | | else: |
| | | print(f"未知命令或参数不足: {command}") |
| | | sys.exit(1) |
| | | |
| | | |
| | | if __name__ == "__main__": |
| | | main() |