#!/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()
|