From bc105b30b841cb76703935c783267710a162bfb1 Mon Sep 17 00:00:00 2001
From: TevinClaw <510129976@qq.com>
Date: Mon, 16 Mar 2026 17:58:37 +0800
Subject: [PATCH] refactor: remove three-layer memory architecture
---
/dev/null | 108 ------------
openclaw.json | 47 ++++
workspace/--output/hot-memory.skill | 0
workspace/>/home/tevin/.openclaw/workspace/skills/hot-memory/scripts/daily_maintenance.py | 327 ++++++++++++++++++++++++++++++++++++
4 files changed, 369 insertions(+), 113 deletions(-)
diff --git a/openclaw.json b/openclaw.json
index d9d4c0d..cdba716 100644
--- a/openclaw.json
+++ b/openclaw.json
@@ -135,6 +135,7 @@
"channels": {
"feishu": {
"enabled": true,
+ "domain": "feishu",
"accounts": {
"main": {
"appId": "cli_a93baf57e9badbce",
@@ -147,12 +148,9 @@
"default": {
"groupPolicy": "open",
"dmPolicy": "open",
- "allowFrom": [
- "*"
- ]
+ "allowFrom": ["*"]
}
},
- "domain": "feishu",
"connectionMode": "websocket",
"streaming": true,
"timeout": 2000,
@@ -203,14 +201,53 @@
],
"load": {
"paths": [
- "/home/tevin/.nvm/versions/node/v24.14.0/lib/node_modules/@openclaw/feishu"
+ "/home/tevin/.nvm/versions/node/v24.14.0/lib/node_modules/@openclaw/feishu",
+ "/home/tevin/.openclaw/extensions/memory-lancedb-pro"
]
},
"slots": {
+ "memory": "memory-lancedb-pro"
},
"entries": {
"feishu": {
"enabled": true
+ },
+ "memory-lancedb-pro": {
+ "enabled": true,
+ "config": {
+ "embedding": {
+ "provider": "openai-compatible",
+ "apiKey": "${SILICONFLOW_API_KEY}",
+ "baseURL": "https://api.siliconflow.cn/v1",
+ "model": "BAAI/bge-large-zh-v1.5",
+ "dimensions": 1024
+ },
+ "retrieval": {
+ "mode": "hybrid",
+ "vectorWeight": 0.7,
+ "bm25Weight": 0.3,
+ "rerank": "cross-encoder",
+ "rerankProvider": "siliconflow",
+ "rerankEndpoint": "https://api.siliconflow.cn/v1/rerank",
+ "rerankModel": "BAAI/bge-reranker-v2-m3",
+ "rerankApiKey": "${SILICONFLOW_API_KEY}"
+ },
+ "autoCapture": true,
+ "autoRecall": true,
+ "smartExtraction": true,
+ "extractMinMessages": 2,
+ "extractMaxChars": 8000,
+ "sessionMemory": {
+ "enabled": false
+ },
+ "scopes": {
+ "default": "global",
+ "agentAccess": {
+ "main": ["global", "agent:main"],
+ "lifehelper": ["global", "agent:lifehelper"]
+ }
+ }
+ }
}
}
}
diff --git a/workspace/--output/hot-memory.skill b/workspace/--output/hot-memory.skill
new file mode 100644
index 0000000..bc4c157
--- /dev/null
+++ b/workspace/--output/hot-memory.skill
Binary files differ
diff --git "a/workspace/\076/home/tevin/.openclaw/workspace/skills/hot-memory/scripts/daily_maintenance.py" "b/workspace/\076/home/tevin/.openclaw/workspace/skills/hot-memory/scripts/daily_maintenance.py"
new file mode 100644
index 0000000..5640d98
--- /dev/null
+++ "b/workspace/\076/home/tevin/.openclaw/workspace/skills/hot-memory/scripts/daily_maintenance.py"
@@ -0,0 +1,327 @@
+#!/usr/bin/env python3
+"""
+hot-memory 每日维护脚本
+维护 memory.md 文件的最近7天事件流水和重要事件
+"""
+
+import os
+import re
+import sys
+from datetime import datetime, timedelta
+from pathlib import Path
+
+# 配置
+MEMORY_FILE = Path.home() / ".openclaw" / "workspace" / "MEMORY.md"
+MAX_SIZE_BYTES = 4 * 1024 # 4KB 限制
+DAYS_TO_KEEP = 7
+
+
+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 is_within_last_7_days(line):
+ """检查行是否在最近7天内"""
+ date = parse_date_from_line(line)
+ if not date:
+ return True # 无日期的行保留
+ cutoff_date = (datetime.now() - timedelta(days=DAYS_TO_KEEP)).date()
+ return date >= cutoff_date
+
+
+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 ('最近7天事件流水' in stripped or '事件流水' 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 and not stripped.startswith('-') and not stripped.startswith('*'):
+ if len(stripped) > 10: # 过滤空行和短行
+ important_events.append(line)
+ elif in_daily and (stripped.startswith('-') or stripped.startswith('*')):
+ if is_within_last_7_days(line):
+ 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 - 热记忆 / 活跃记忆
+
+> 本文件记录近期发生的重要事情,详细信息可通过记忆检索获取。
+
+---
+
+## 🔔 重要事件
+
+> 记录具有全局长期性影响的重要决策和事件。
+> 添加重要事件时会告知用户。
+
+<!-- 重要事件在此添加 -->
+
+---
+
+## 📅 最近7天事件流水
+
+> 按天分组,每天主要事情的概要。
+> 自动维护,8天前的记录会被移除。
+
+### {{today}}
+
+- {today} 10:00 | memory.md 初始化 | 热记忆系统
+
+---
+
+*文件大小: ~0.5KB | 限制: 4KB*
+*维护脚本: `hot-memory/scripts/daily_maintenance.py`*
+""".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,
+ 'removed_old': 0,
+ 'size_warning': False,
+ 'current_size_kb': 0,
+ 'messages': []
+ }
+
+ # 读取现有内容
+ content = read_memory_file()
+ if not content:
+ content = generate_default_content()
+
+ # 提取现有事件
+ existing_important, existing_daily = extract_event_entries(content)
+
+ # 移除8天前的日常事件
+ cutoff_date = (datetime.now() - timedelta(days=DAYS_TO_KEEP)).date()
+ filtered_daily = []
+ for line in existing_daily:
+ date = parse_date_from_line(line)
+ if date and date < cutoff_date:
+ result['removed_old'] += 1
+ else:
+ filtered_daily.append(line)
+
+ # 添加新的日常事件
+ 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 filtered_daily):
+ filtered_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 filtered_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 "<!-- 重要事件在此添加 -->"
+
+ # 组装最终内容
+ current_size = get_file_size()
+ new_content = f"""# MEMORY.md - 热记忆 / 活跃记忆
+
+> 本文件记录近期发生的重要事情,详细信息可通过记忆检索获取。
+
+---
+
+## 🔔 重要事件
+
+> 记录具有全局长期性影响的重要决策和事件。
+> 添加重要事件时会告知用户。
+
+{important_content}
+
+---
+
+## 📅 最近7天事件流水
+
+> 按天分组,每天主要事情的概要。
+> 自动维护,8天前的记录会被移除。
+{daily_content}
+---
+
+*文件大小: ~{current_size / 1024:.1f}KB | 限制: 4KB*
+*维护脚本: `hot-memory/scripts/daily_maintenance.py`*
+"""
+
+ # 写入文件
+ write_memory_file(new_content)
+
+ # 检查文件大小
+ new_size = get_file_size()
+ result['current_size_kb'] = round(new_size / 1024, 2)
+
+ if new_size > MAX_SIZE_BYTES:
+ result['size_warning'] = True
+ result['messages'].append(f"警告: 文件大小 ({result['current_size_kb']}KB) 超过 4KB 限制,需要瘦身")
+
+ return result
+
+
+def check_size():
+ """检查文件大小并返回状态"""
+ size = get_file_size()
+ return {
+ 'size_bytes': size,
+ 'size_kb': round(size / 1024, 2),
+ 'limit_bytes': MAX_SIZE_BYTES,
+ 'limit_kb': 4,
+ 'exceeded': size > MAX_SIZE_BYTES,
+ 'percentage': round((size / MAX_SIZE_BYTES) * 100, 1)
+ }
+
+
+def main():
+ """主函数 - 供命令行调用"""
+ if len(sys.argv) < 2:
+ print("用法: python daily_maintenance.py <command> [args]")
+ print("")
+ print("命令:")
+ print(" update - 执行每日更新")
+ print(" check-size - 检查文件大小")
+ 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" - 移除旧事件: {result['removed_old']}")
+ print(f" - 添加重要事件: {'是' if result['added_important'] else '否'}")
+ print(f" - 当前大小: {result['current_size_kb']}KB")
+ for msg in result['messages']:
+ print(f" - {msg}")
+
+ elif command == "check-size":
+ status = check_size()
+ print(f"文件大小检查:")
+ print(f" - 当前: {status['size_kb']}KB ({status['percentage']}%)")
+ print(f" - 限制: {status['limit_kb']}KB")
+ print(f" - 状态: {'⚠️ 超限' if status['exceeded'] else '✅ 正常'}")
+
+ 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['size_warning']:
+ 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['size_warning']:
+ print(f"⚠️ 警告: 文件大小 {result['current_size_kb']}KB 超过限制")
+
+ else:
+ print(f"未知命令或参数不足: {command}")
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/workspace/memory/journal/2026-03-12.md b/workspace/memory/journal/2026-03-12.md
deleted file mode 100644
index d2ee7af..0000000
--- a/workspace/memory/journal/2026-03-12.md
+++ /dev/null
@@ -1,121 +0,0 @@
-# 2026-03-12 详细日志
-
-## 上午
-- 会话启动,读取 SOUL.md、USER.md、MEMORY.md
-
-## 下午
-
-### 17:48 - 会话开始
-- 新会话启动
-- 模型: custom-api-siliconflow-cn/Pro/moonshotai/Kimi-K2.5
-
-### 18:00 - 技能安装
-- 用户要求安装 find-skills
-- 使用 clawhub 安装成功
-- 读取 SKILL.md 确认功能
-
-### 18:22 - 优化记忆管理
-- 讨论记忆管理技能需求
-- 搜索 memory/knowledge 相关技能
-- 决定安装 super-search 和 memory-merger
-
-### 18:28 - 安装记忆技能
-- 安装 super-search ✓
-- 安装 memory-merger ✓
-- 读取两个技能的 SKILL.md 了解使用方法
-
-### 18:35 - 实现三层记忆架构
-- 用户分享三层记忆架构图
-- 开始重构记忆系统:
- - 创建目录: memory/milestones/, memory/journal/, knowledge/
- - 创建劳作层: workspace/00-Pending/, 01-Projects/, 02-Areas/, 03-Resources/, 04-Archive/
- - 重构 MEMORY.md 为 L0 索引层 (4KB以内)
- - 创建 L1 概览层示例 (milestones/2026-03-skills.md)
- - 创建 L2 详情层示例 (journal/2026-03-12.md)
-
-## 技术细节
-
-### 新技能使用方法
-
-**super-search**:
-```bash
-node "${CLAUDE_PLUGIN_ROOT}/scripts/search-memory.cjs" [--user|--repo|--both] "搜索内容"
-```
-
-**memory-merger**:
-```
-/memory-merger >domain-name [scope]
-```
-
-### 目录结构
-```
-~/openclaw/workspace/
-├── AGENTS.md # 行为规则与启动序列
-├── SOUL.md # 灵魂与人格
-├── MEMORY.md # L0: 索引层 (4KB)
-├── memory/
-│ ├── milestones/ # L1: 概览层
-│ └── journal/ # L2: 详情层
-├── knowledge/ # 客观知识
-├── skills/ # 能力层
-├── tools/ # 能力层
-└── workspace/ # 劳作层 (PARA)
- ├── 00-Pending/
- ├── 01-Projects/
- ├── 02-Areas/
- ├── 03-Resources/
- └── 04-Archive/
-```
-
-### 19:24 - 调整技能配置
-- 移除 super-search(需要外部 Supermemory 后端)
-- 保留 memory-merger(对三层记忆体系重要)
-- 解释 npx skills 与 clawhub 安装路径差异
-
-### 19:35 - 三层记忆体系测试
-- 测试启动序列自动读取机制
-- 验证 L0/L1/L2 三层架构正常工作
-- 讨论持久化保障机制(文件系统 + AGENTS.md 启动规则)
-
-### 19:47 - 写入和维护机制讨论
-- 明确写入和维护主要靠手动触发
-- 创建 HEARTBEAT.md 定期维护任务清单
-- 澄清三层记忆 vs 脑机分离的边界
-
-### 20:00 - Ontology 知识图谱演示
-- 初始化 ontology 存储 (memory/ontology/graph.jsonl)
-- 创建 Person 实体 (Tevin)
-- 创建 Project 实体 ("三层记忆架构")
-- 建立 has_owner 关系
-
-### 20:05 - 完整项目任务示例
-- 创建3个 Task 实体(设计Schema、实现API、开发前端)
-- 建立任务依赖关系(blocks 链)
-- 演示查询功能(查询项目任务、查询阻塞状态)
-- 展示完整图谱结构和存储格式
-
-### 20:18 - 自动化程度坦诚讨论
-- 说明写入和维护不能全自动运作
-- 解释技术限制和设计选择
-- 确定触发词机制("记住..."、"记录一下"等)
-- 达成"半自动"模式共识
-
-## 关键决策
-
-1. **采用三层记忆架构** (L0索引→L1概览→L2详情)
-2. **保留4个记忆管理技能**: find-skills, memory-merger, ontology, self-improving-agent
-3. **创建 Ontology 知识图谱** 用于结构化项目/任务管理
-4. **建立触发词机制** 作为半自动记忆写入方案
-
-## 创建的文件
-
-- `MEMORY.md` - L0 索引层(重构)
-- `AGENTS.md` - 更新启动序列
-- `HEARTBEAT.md` - 定期维护任务
-- `memory/milestones/2026-03-skills.md` - L1 里程碑
-- `memory/journal/2026-03-12.md` - L2 详情日志
-- `memory/ontology/graph.jsonl` - 知识图谱数据
-
----
-
-*此文件属于 L2 详情层 - 按需读取的非常驻上下文*
\ No newline at end of file
diff --git a/workspace/memory/journal/2026-03-13.md b/workspace/memory/journal/2026-03-13.md
deleted file mode 100644
index 4c639cb..0000000
--- a/workspace/memory/journal/2026-03-13.md
+++ /dev/null
@@ -1,201 +0,0 @@
-# 2026-03-13
-
-## [10:37] 启动会话与早间问候
-
-### 背景
-新会话启动,问候用户Tevin。
-
-### 详情
-系统已就绪,问候并询问今日任务。
-
----
-
-## [10:45] AI早报定时任务需求
-
-### 背景
-用户询问是否能设置定时任务,希望每天早上9点收到AI领域新闻的早安播报。
-
-### 详情
-讨论定时任务实现方案:
-- 确认可以使用 `openclaw cron` 创建定时任务
-- 确定先制作测试版早报,满意后再创建定时任务
-- 使用subagent搜索昨天AI新闻并生成首份早报
-
-### 决策
-采用"测试→确认→定时"三步流程实现AI早报功能。
-
----
-
-## [16:13] 确立沟通原则
-
-### 背景
-用户要求记住两条沟通原则。
-
-### 决策
-1. **即时回复优先** — 不沉默执行,先回应再行动
-2. **耗时任务分离** — 启用subagent异步处理,主会话负责协调汇报
-
----
-
-## [16:21] 探索定时任务功能
-
-### 背景
-用户询问 `openclaw cron` 命令的具体用法。
-
-### 详情
-查询确认:
-- 存在完整的 `openclaw cron` 子命令(add/list/rm/run等)
-- 支持 cron 表达式定时
-- 可通过 `--message` 指定agent消息, `--channel` 和 `--to` 指定投递目标
-
----
-
-## [16:28] AI早报测试版完成
-
-### 背景
-首份AI早报生成完成。
-
-### 详情
-Subagent搜索到5条昨日AI重要新闻:
-1. OpenAI计划将Sora整合进ChatGPT
-2. 英伟达20亿美元投资Nebius
-3. Adobe CEO卸任,强调AI创意时代
-4. 香港大学AI心血管疾病预测系统(提前15年预警)
-5. Netflix 6亿美元收购Ben Affleck的AI初创公司
-
-用户反馈:格式和内容满意,可以创建定时任务。
-
----
-
-## [16:33] 创建"AI早报"定时任务
-
-### 背景
-测试版效果满意,正式创建定时任务。
-
-### 详情
-```bash
-openclaw cron add \
- --name "AI早报" \
- --cron "0 9 * * *" \
- --message "搜索昨天AI领域的重要新闻,整理成早报发送给用户" \
- --channel feishu \
- --to "ou_53994d69bfaad1bfa5ca4c658de5b23f" \
- --tz "Asia/Shanghai"
-```
-
-**任务详情**:
-- 每天上午9:00(Asia/Shanghai时区)自动执行
-- 通过Feishu投递给用户
-
-### 决策
-从明天(2026-03-14)开始,每天早上9点自动接收AI早报。
-
----
-
-## [17:00] 优化早报:增加AI编程分类
-
-### 背景
-用户要求早报增加AI编程领域关注。
-
-### 详情
-更新定时任务提示词,增加:
-- **AI编程分类**:特别关注 Anthropic/ClaudeCode 和 OpenAI/Codex 的动态
-- **优先级**:AI编程相关新闻优先入选
-
----
-
-## [17:38] 安装Tavily Search技能
-
-### 背景
-用户要求安装Tavily Search技能,用于高质量搜索。
-
-### 详情
-创建技能 `~/.openclaw/workspace/skills/tavily-search/`,包含:
-- **SKILL.md**:使用说明和API配置指南
-- **scripts/tavily_search.py**:搜索脚本,支持从环境变量或OpenClaw配置读取API key
-
-### 决策
-使用Tavily AI搜索引擎获取更高质量的AI新闻。
-
----
-
-## [17:42] 确立语音偏好
-
-### 背景
-用户询问为何发送语音消息,是哪个配置导致的。
-
-### 详情
-发现是误操作调用了tts工具。用户明确要求:
-
-**偏好**:如非必要,正常用文字交流即可
-
-### 决策
-- **默认只用文字交流**
-- 除非特别要求,否则不发语音
-- 已更新到 USER.md 记忆
-
----
-
-## [17:48] 配置Tavily API Key
-
-### 背景
-用户已在 `openclaw.json` 的 `agents.defaults.env` 中配置Tavily API Key。
-
-### 详情
-- 确认配置位置:`agents.defaults.env.TAVILY_API_KEY`
-- 更新技能脚本,支持从OpenClaw配置读取key(优先级:环境变量 > OpenClaw配置 > 直接参数)
-- 测试搜索功能成功
-
----
-
-## [18:28] 检查自我成长技能
-
-### 背景
-用户询问是否安装了"自我成长"技能。
-
-### 详情
-确认已安装 `self-improving-agent` 技能(位于 `~/.openclaw/workspace/skills/self-improving-agent/`)。
-
----
-
-## [22:00] 解答 OpenClaw 目录结构问题
-
-### 背景
-用户询问 `.openclaw` 目录下新增的 browser、canvas、devices 文件夹用途。
-
-### 详情
-通过调用各工具状态检查,确认:
-- **browser/**: 浏览器控制数据,存储 CDP 连接配置、用户数据目录、截图缓存等。当前检测到 Chromium 可用。
-- **canvas/**: 画布渲染数据,用于 UI 画布展示功能。需要配对节点才能使用。
-- **devices/**: 配对设备管理,存储已配对设备信息。当前暂无配对设备。
-
-### 结论
-这些都是 OpenClaw 内部运行时数据目录,用户无需直接操作。
-
----
-
-## [22:29] 中文化核心配置文件
-
-### 背景
-用户表示 SOUL.md 和 TOOLS.md 是英文的看不懂,要求改写成中文,后续记录优先中文。
-
-### 详情
-将两个核心配置文件从英文翻译为中文:
-- **SOUL.md**: 624 bytes,保留核心信条、边界、气质等章节
-- **TOOLS.md**: 409 bytes,保留本地备注用途说明
-
-### 决策
-后续所有记录默认使用中文优先。
-
-### 关联
-- L0 索引: [MEMORY.md](../MEMORY.md)
-
----
-
-## 今日关键决策汇总
-
-1. **AI早报定时任务**:每天早上9点自动执行,已启用
-2. **沟通原则**:即时回复 + subagent异步处理
-3. **Tavily Search技能**:已安装并配置,用于高质量搜索
-4. **语音偏好**:默认只用文字,除非特别要求
-5. **记录语言**:后续所有记录优先使用中文
diff --git a/workspace/memory/journal/2026-03-14.md b/workspace/memory/journal/2026-03-14.md
deleted file mode 100644
index 6fecee6..0000000
--- a/workspace/memory/journal/2026-03-14.md
+++ /dev/null
@@ -1,107 +0,0 @@
-# 2026-03-14
-
-## [08:00] AI早报格式定型与定时任务创建
-
-### 背景
-用户要求创建每天早上9点的AI早报定时任务,需要先测试格式再正式启用。
-
-### 详情
-经过测试和讨论,确定了AI早报的格式和内容结构:
-
-**行业分类(3个):**
-1. **AI行业** - OpenAI、Google、Anthropic、Meta、NVIDIA等动态
-2. **AI编程** - 重点关注 Anthropic/ClaudeCode 和 OpenAI/Codex
-3. **国产大模型** - 字节、阿里、百度、DeepSeek等国内进展
-
-**内容结构:**
-- 每个行业1-2条重要新闻
-- 每条包含:标题、一句话摘要、来源
-- 最后附加"昨日总结"段落
-- 大标题不使用图标,保持简洁文字
-
-**定时任务配置:**
-```bash
-openclaw cron add \
- --name "AI早报" \
- --cron "0 9 * * *" \
- --tz "Asia/Shanghai" \
- --channel feishu
-```
-
-### 决策
-- 早报优先关注AI编程领域动态(ClaudeCode/Codex)
-- 使用简洁文字标题,不用emoji图标
-- 每天早上9点自动发送
-
----
-
-## [09:38-12:27] 安装并配置 Tavily Search 技能
-
-### 背景
-为AI早报提供高质量的AI新闻搜索能力,需要集成Tavily AI搜索引擎。
-
-### 详情
-**技能创建过程:**
-1. **创建技能结构** - `~/.openclaw/workspace/skills/tavily-search/`
-2. **编写 SKILL.md** - 定义技能描述和使用说明
-3. **编写搜索脚本** - `scripts/tavily_search.py` 支持命令行和Python API
-
-**API Key 配置迭代:**
-- **第一次**:计划使用环境变量 `TAVILY_API_KEY`
-- **第二次**:改为从OpenClaw配置文件 `~/.openclaw/openclaw.json` 读取
-- **第三次**:更新脚本支持多层级key读取(环境变量 > OpenClaw配置 > 参数传入)
-
-**配置位置:**
-```json
-{
- "agents": {
- "defaults": {
- "env": {
- "TAVILY_API_KEY": "tvly-dev-xxx"
- }
- }
- }
-}
-```
-
-**功能测试:**
-- 成功搜索"Claude Code最新功能"
-- 返回3条相关结果,包含版本更新、CLI工具、桌面版功能
-- 验证key读取和API调用正常工作
-
-### 决策
-- 保留Tavily Search技能用于AI早报和其他搜索需求
-- 采用OpenClaw配置文件集中管理API key,便于维护
-
-### 关联
-- L0索引: [MEMORY.md](../MEMORY.md)
-- 技能路径: `skills/tavily-search/`
-- 定时任务ID: `f83b0227-20d1-405b-b4c0-9248dad6d959`
-
----
-
-## [17:42] 沟通原则确认
-
-### 背景
-回顾工作方式约定。
-
-### 详情
-用户重申两条沟通原则:
-1. **即时回复优先** - 只负责沟通,必须立即回复,不能先去干活不回复
-2. **耗时任务分离** - 需要时间执行的任务启动专用subagent处理,主会话负责协调反馈
-
-### 决策
-- 严格执行上述两条原则
-- 语音消息仅限用户特别要求时发送,默认只用文字
-
----
-
-## 今日总结
-
-3月14日主要完成两项工作:
-1. **AI早报系统搭建** - 从格式设计到定时任务创建,已完成部署,明天开始自动发送
-2. **Tavily Search技能** - 创建并配置完成,支持从OpenClaw配置读取API key,已测试可用
-
-**待观察:**
-- 明日早报执行效果
-- Tavily搜索在早报中的实际表现
diff --git a/workspace/memory/journal/2026-03-15.md b/workspace/memory/journal/2026-03-15.md
deleted file mode 100644
index a689250..0000000
--- a/workspace/memory/journal/2026-03-15.md
+++ /dev/null
@@ -1,251 +0,0 @@
-# 2026-03-15
-
-## [12:20] 会话启动与问候
-
-### 详情
-新会话启动,问候用户Tevin。确认当前时间:周日下午。
-
----
-
-## [12:27] 飞书渠道状态检查
-
-### 详情
-用户要求检测飞书渠道是否正常。
-
-**结果**: 飞书渠道未配置(Feishu account "default" is not configured)
-
-**说明**: 飞书相关功能(消息、文档、任务等)当前不可用,需要配置飞书应用凭证后才能使用。
-
----
-
-## [12:29] 查询三层记忆首次提及时间
-
-### 详情
-用户询问第一次提到"三层记忆"是什么时候。
-
-**答案**: 2026年3月12日
-
-**背景**: 当天下午18:35左右,用户分享了三层记忆架构图,随后一起实现了该架构:
-- L0 索引层 → MEMORY.md
-- L1 概览层 → memory/milestones/
-- L2 详情层 → memory/journal/
-
----
-
-## [17:58] 测试 QMD Wrapper 脚本
-
-### 详情
-用户要求测试完整的 wrapper 脚本。
-
-**测试对象**: `qmd-wrapper.sh`
-
-**脚本内容**:
-```bash
-#!/bin/bash
-# QMD Wrapper - 强制使用 CPU 后端
-export GGML_USE_CPU=1
-exec qmd "$@"
-```
-
-**测试结果**: ✅ 全部通过
-- --help 输出正常
-- 搜索功能正常(搜索"三层记忆"返回6条结果)
-- 成功解决 Vulkan GPU 内存分配问题
-
-**关键改进**: 通过 `GGML_USE_CPU=1` 强制使用 CPU 后端,避免之前 `ggml_vulkan: Device memory allocation failed` 错误。
-
----
-
-## [22:00] 心跳每日维护
-
-### 详情
-定时心跳检查,执行每日维护任务。
-
-**检查结果**:
-- 今日 L2 记录: 不存在(已补充)
-- Session 文件: 找到10个(包含重置归档)
-- 用户消息: 33条
-- L0 大小: 1.9KB / 4KB ✅ 正常
-
-**执行动作**: 补充今日 L2 详细日志
-
----
-
-## [18:07] Gateway 重启与 QMD 测试
-
-### 详情
-用户重启 Gateway 后,测试 QMD 是否正常工作。
-
-**测试内容:**
-- 测试 memory_search 工具
-- 查询 "定时任务 AI早报"
-
-**结果:**
-- ✅ 纯文本搜索(BM25)工作正常
-- ❌ 向量搜索(rerank)仍不可用
-- 错误:`ggml_vulkan: Device memory allocation of size 633207232 failed`
-
-**结论:** GPU 显存不足(需 ~600MB),rerank 模型无法加载。
-
----
-
-## [18:13-18:59] QMD 深度调试与最终决策
-
-### 详情
-用户多次重启 Gateway,尝试解决 QMD 的 GPU 显存问题。
-
-**尝试过程:**
-
-1. **18:13** - 测试环境变量传递
- - 确认 TAVILY_API_KEY 等变量已正确传递
- - 但 rerank 模型仍尝试加载到 GPU
-
-2. **18:43** - 修改超时设置为 30 秒
- - 测试 memory_search 响应时间
- - 纯文本搜索正常,rerank 仍失败
-
-3. **18:48** - 再次重启 Gateway
- - 测试环境变量是否正确加载
- - 确认 `GGML_USE_CPU=1` 对 rerank 无效
-
-4. **18:56** - 停止所有子代理
- - 确认无活动子代理
-
-5. **18:59** - **最终决策:完全卸载 QMD**
- - 执行卸载命令
- - 删除 qmd 命令、wrapper 脚本、数据目录
- - 从 openclaw.json 移除 memory 配置
-
-### 关键发现
-- qmd 的 rerank 模型**必须使用 GPU**,无法通过环境变量强制使用 CPU
-- 这是 node-llama-cpp 库的限制
-- 当前设备显存不足以支持向量搜索
-
----
-
-## [19:18] Git 提交 QMD 卸载更改
-
-### 详情
-将 QMD 卸载的更改提交到 Git。
-
-**提交信息:**
-```
-[master 27adc32] 移除 QMD 配置:卸载 QMD 并清理相关配置和脚本
- 1 file changed, 1 insertion(+), 25 deletions(-)
-```
-
-**已清理内容:**
-- qmd 命令(npm 全局包)
-- wrapper 脚本(~/.openclaw/workspace/qmd-wrapper.sh)
-- qmd 数据目录(~/.openclaw/agents/main/qmd/)
-- openclaw.json 中的 memory 配置
-
----
-
-## [19:20] 查找 Session 压缩技能
-
-### 详情
-用户询问是否有自动压缩长聊天内容的技能。
-
-**搜索结果:**
-1. **session-compression**(推荐)- 204 次安装
- - bobmatnyc/claude-mpm-skills
- - 链接:https://skills.sh/bobmatnyc/claude-mpm-skills/session-compression
-
-2. **context-compression** - 33 次安装
- - eyadsibai/ltk
-
-3. **context-manager** - 27 次安装
- - aaaaaqwq/claude-code-skills
-
-**OpenClaw 内置功能:**
-- 当前配置:`"compaction": {"mode": "safeguard"}`
-- 支持三种模式:off / safeguard / always
-
----
-
-## [19:53] Self-Improvement 技能状态检查
-
-### 详情
-用户要求检查 self-improvement 技能是否生效。
-
-**检查结果:**
-- ✅ 技能已安装并生效(SKILL.md 已加载)
-- ❌ **实际使用:尚未开始记录**
-
-**记录文件状态:**
-- `.learnings/LEARNINGS.md` - 空模板(62 字节)
-- `.learnings/ERRORS.md` - 空模板(373 字节)
-- `.learnings/FEATURE_REQUESTS.md` - 空模板(340 字节)
-
-**应记录但未记录的事件:**
-- QMD GPU 显存分配失败 → 应记录到 ERRORS.md
-- QMD 卸载过程 → 应记录到 LEARNINGS.md
-- Session 压缩技能需求 → 应记录到 FEATURE_REQUESTS.md
-
----
-
-## [20:02] QMD 卸载状态最终确认
-
-### 详情
-用户再次确认 QMD 是否已完全卸载。
-
-**验证结果:**
-- ✅ qmd 命令已删除
-- ✅ wrapper 脚本已删除
-- ✅ qmd 数据目录已删除
-- ✅ memory 配置已移除
-- ✅ Git 提交已完成
-
----
-
-## [22:06] 今日 Token 使用量查询
-
-### 详情
-用户查询今天的 Token 使用明细。
-
-**统计结果:**
-
-| 会话 | Tokens |
-|------|--------|
-| 主会话 (Feishu) | 68,729 |
-| Heartbeat (Webchat) | 37,936 |
-| AI早报定时任务 | 20,600 |
-| **总计** | **~127k** |
-
-**费用:** $0.0000(硅基流动,当前成本为 0)
-
----
-
-## [22:17] L2 记录完整性检查
-
-### 详情
-用户指出今日 L2 记录不完整,缺少大量重要内容。
-
-**发现的问题:**
-1. 只记录了 12:00-17:58 的内容
-2. 18:00-22:00 的重要事件全部缺失
-3. Self-improvement 技能未实际生效(未记录任何学习)
-
-**缺失内容:**
-- QMD 调试完整过程
-- GPU 显存问题排查
-- wrapper 脚本修改
-- QMD 卸载和 Git 提交
-- Session 压缩技能查找
-- Self-improvement 技能检查
-- Token 使用量查询
-
-**根本原因分析:**
-1. Self-improvement 技能是被动触发,需要主动记录
-2. 心跳脚本扫描可能未及时捕获所有 session 活动
-3. 没有实时将重要事件写入 journal
-
-**改进措施:**
-- 立即补充完整今天的 L2 记录
-- 后续加强实时记录意识
-- 定期触发 self-improvement 技能进行记录
-
----
-
-*此文件属于 L2 详情层 - 按需读取的非常驻上下文*
diff --git a/workspace/memory/milestones/2026-03-skills.md b/workspace/memory/milestones/2026-03-skills.md
deleted file mode 100644
index 17e96e2..0000000
--- a/workspace/memory/milestones/2026-03-skills.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# 2026-03 技能扩展里程碑
-
-## 记忆管理技能组
-
-### 2026-03-12 安装
-
-#### 1. find-skills
-- **用途**: 发现和安装 Agent 技能
-- **触发**: "如何安装 XX 技能"、"有没有做 XX 的技能"
-- **位置**: `~/.openclaw/workspace/skills/find-skills/`
-
-#### 2. super-search
-- **用途**: 搜索过去的编码记忆和会话记录
-- **触发**: "我昨天做了什么"、"我们如何实现 XX"
-- **位置**: `~/.openclaw/workspace/.agents/skills/super-search/`
-
-#### 3. memory-merger
-- **用途**: 将临时记忆合并为结构化指令
-- **语法**: `/memory-merger >domain [scope]`
-- **位置**: `~/.openclaw/workspace/.agents/skills/memory-merger/`
-
-#### 4. ontology
-- **用途**: 知识图谱存储实体和关系
-- **触发**: "记住..."、"链接 X 到 Y"、实体 CRUD
-- **位置**: `~/.openclaw/workspace/skills/ontology/`
-
-### 2026-03-14 安装
-
-#### 5. tavily-search
-- **用途**: Tavily AI 搜索引擎集成,用于高质量搜索
-- **触发**: AI早报新闻搜索、知识查询
-- **位置**: `~/.openclaw/workspace/skills/tavily-search/`
-- **配置**: API Key 存储于 `openclaw.json` 的 `agents.defaults.env.TAVILY_API_KEY`
-- **来源**: [L2 2026-03-14](./../journal/2026-03-14.md#安装并配置-tavily-search-技能)
-
-### 2026-03-15 状态检查
-
-#### 6. self-improving-agent
-- **用途**: 记录学习、错误和功能请求,实现持续改进
-- **触发**: 操作失败、用户纠正、发现更好方法
-- **位置**: `~/.openclaw/workspace/skills/self-improving-agent/`
-- **状态**: ⚠️ 已安装但尚未开始记录(.learnings/ 目录文件为空)
-- **来源**: [L2 2026-03-15](./../journal/2026-03-15.md#self-improvement-技能状态检查)
-
-## 架构决策
-
-- 采用**三层记忆架构**: L0索引 → L1概览 → L2详情
-- 脑机分离: 认知层(根目录) / 能力层(skills/) / 劳作层(workspace/)
-
----
-
-*此文件属于 L1 概览层 - 按主题组织的里程碑总结*
\ No newline at end of file
diff --git a/workspace/memory/milestones/2026-03-workflows.md b/workspace/memory/milestones/2026-03-workflows.md
deleted file mode 100644
index d451af6..0000000
--- a/workspace/memory/milestones/2026-03-workflows.md
+++ /dev/null
@@ -1,85 +0,0 @@
-# 2026-03 工作流与规范里程碑
-
-## AI早报定时任务
-
-### 2026-03-13 需求提出与测试
-- **背景**: 用户希望每天早上9点收到AI领域新闻早报
-- **测试**: 首份测试版早报生成,内容涵盖5条昨日AI重要新闻
-- **来源**: [L2 2026-03-13](./../journal/2026-03-13.md#1045-ai早报定时任务需求)
-
-### 2026-03-13 正式创建
-- **定时任务配置**:
- ```bash
- openclaw cron add \
- --name "AI早报" \
- --cron "0 9 * * *" \
- --tz "Asia/Shanghai" \
- --channel feishu
- ```
-- **内容格式**: 三个分类(AI行业、AI编程、国产大模型),每个1-2条新闻
-- **投递**: 每天早上9点通过飞书发送
-- **来源**: [L2 2026-03-13](./../journal/2026-03-13.md#1633-创建ai早报定时任务)
-
-### 2026-03-14 格式优化
-- **优先级调整**: AI编程相关新闻优先入选
-- **特别关注**: Anthropic/ClaudeCode 和 OpenAI/Codex 动态
-- **格式定型**: 大标题不使用emoji,保持简洁文字
-- **来源**: [L2 2026-03-14](./../journal/2026-03-14.md#0800-ai早报格式定型与定时任务创建)
-
----
-
-## 沟通工作原则
-
-### 2026-03-13 确立
-**两条核心原则:**
-1. **即时回复优先** — 只负责与用户沟通,必须立即回复,不能自己先去干活而不回复
-2. **耗时任务分离** — 需要时间执行的任务/动作,启动专用subagent处理,主会话负责协调并反馈给用户
-
-**来源**: [L2 2026-03-13](./../journal/2026-03-13.md#1613-确立沟通原则) | [L2 2026-03-14](./../journal/2026-03-14.md#1742-沟通原则确认)
-
----
-
-## 记录语言规范
-
-### 2026-03-13 中文优先决策
-- **背景**: SOUL.md 和 TOOLS.md 原为英文,用户要求中文化
-- **决策**: 后续所有记录优先使用中文
-- **执行**: 已完成核心配置文件中文化
-- **来源**: [L2 2026-03-13](./../journal/2026-03-13.md#2229-中文化核心配置文件)
-
----
-
-## QMD 问题与卸载决策
-
-### 2026-03-15 问题发现
-- **症状**: `ggml_vulkan: Device memory allocation failed` 错误
-- **原因**: rerank 模型需要 ~600MB GPU 显存,当前设备不足
-- **尝试**: 环境变量 `GGML_USE_CPU=1`、wrapper 脚本均无法解决
-- **来源**: [L2 2026-03-15](./../journal/2026-03-15.md#1807-gateway-重启与-qmd-测试)
-
-### 2026-03-15 最终决策
-- **决策**: 完全卸载 QMD
-- **执行**:
- - 卸载 qmd npm 包
- - 删除 wrapper 脚本
- - 清理数据目录
- - 从 openclaw.json 移除 memory 配置
- - Git 提交更改
-- **结论**: QMD rerank 必须使用 GPU,当前硬件不支持
-- **来源**: [L2 2026-03-15](./../journal/2026-03-15.md#1813-1859-qmd-深度调试与最终决策)
-
----
-
-## 三层记忆架构
-
-### 2026-03-12 架构实现
-- **架构**: L0索引 → L1概览 → L2详情
-- **L0**: MEMORY.md(4KB以内,索引和摘要)
-- **L1**: memory/milestones/(按主题组织的里程碑)
-- **L2**: memory/journal/(每日详细日志)
-- **来源**: [L2 2026-03-12](./../journal/2026-03-12.md#1835-实现三层记忆架构)
-
----
-
-
-*此文件属于 L1 概览层 - 按主题组织的重要工作流和规范*
diff --git a/workspace/skills/memory-management/SKILL.md b/workspace/skills/memory-management/SKILL.md
deleted file mode 100644
index 3bef6d2..0000000
--- a/workspace/skills/memory-management/SKILL.md
+++ /dev/null
@@ -1,244 +0,0 @@
----
-name: memory-management
-description: "三层记忆架构管理系统 (L0索引→L1概览→L2详情)。用于记录、维护和优化AI助手的记忆体系。当需要记录重要信息、决策或事件时使用;当需要维护记忆体系(归档、整理、合并)时使用;当L0层接近4KB限制时需要整理。"
----
-
-# 三层记忆管理
-
-> **架构**: L0索引 → L1概览 → L2详情
-> **红线**: L0 < 4KB | 只存索引 | 详情通过路径引用
-
----
-
-## 目录结构(固定)
-
-```
-~/.openclaw/workspace/
-├── MEMORY.md # L0: 索引层(红线:4KB)
-├── memory/
-│ ├── milestones/ # L1: 概览层
-│ │ └── YYYY-MM-topic.md
-│ └── journal/ # L2: 详情层
-│ └── YYYY-MM-DD.md
-├── AGENTS.md # 启动序列(参考)
-├── SOUL.md # 人格(参考)
-└── USER.md # 用户偏好(参考)
-```
-
----
-
-## 写入规则
-
-### L0 (MEMORY.md) - 索引层
-
-**触发条件:** 任何需要"记住"的事
-
-**写入内容:**
-- 索引目录(指向L1/L2的链接)
-- 最近活动摘要(3-5条)
-- 关键决策列表
-
-**模板:**
-```markdown
-## 🔍 快速检索
-
-### 最近活动
-- YYYY-MM-DD: [一句话摘要] → 详见 [L2](./memory/journal/YYYY-MM-DD.md)
-
-### 关键决策
-- [决策标题]:简要说明
-```
-
-**红线检查:**
-- 文件 > 4KB → 触发归档提醒
-- 超过10条未归档 → 提示整理到L1
-
-### L1 (milestones/) - 概览层
-
-**触发条件:**
-- L2积累到一定量,或跨会话仍重要
-- 每周维护时自动合并
-
-**组织方式:**
-- 按主题:`YYYY-MM-skills.md`, `YYYY-MM-decisions.md`
-- 每条包含:决策/事件、时间、关联的L2链接
-
-**模板:**
-```markdown
-# YYYY-MM 主题里程碑
-
-## [日期] 事件标题
-**背景**:简述
-**决策/结论**:关键点
-**来源**:[L2链接](./journal/YYYY-MM-DD.md#锚点)
-```
-
-### L2 (journal/) - 详情层
-
-**触发条件:** 详细记录、完整对话、原始上下文
-
-**写入内容:**
-- 完整背景
-- 详细过程
-- 决策/结论
-- 关联引用
-
-**模板:**
-```markdown
-# YYYY-MM-DD
-
-## [HH:MM] 事件标题
-
-### 背景
-发生了什么
-
-### 详情
-完整记录
-
-### 决策/结论
-关键产出
-
-### 关联
-- L1里程碑:[链接]
-```
-
----
-
-## 维护规则
-
-### 每日维护(晚上10点后触发,心跳触发)
-
-**触发条件:** 晚上10点后,如果今日还没有写入L2
-
-**任务清单:**
-- [ ] 检查今日是否有重要决策需要记录到L2
-- [ ] **完整检查飞书渠道历史** — 执行以下步骤确保无遗漏:
- 1. 列出所有今日活跃的session(包括.reset.归档文件)
- 2. 扫描 `~/.openclaw/agents/main/sessions/` 目录下今日修改的所有 `.jsonl*` 文件
- 3. 读取每个session文件,提取飞书渠道的对话内容
- 4. 检查是否有未记录到L2的重要活动
-- [ ] 更新 MEMORY.md 的"最近活动"摘要
-- [ ] 确保 L0 层不超过 4KB
-
-**判断标准:怎样才算"没有遗漏"?**
-```
-检查完成标准:
-├── 已扫描所有session文件(当前活跃 + .reset.归档 + .deleted.删除)
-├── 已提取飞书渠道所有对话记录
-├── 已识别所有重要事件(技能安装、配置变更、定时任务等)
-└── 已在L2中记录或确认无需记录
-```
-
-**重要提醒:**
-> ⚠️ **飞书历史检查**:当用户说"检查昨天的每日总结"或类似表述时,必须:
-> 1. 使用 `sessions_list` 查找过去48小时的活跃session
-> 2. 检查 `.openclaw/agents/main/sessions/` 目录下是否有 `.jsonl.reset.*` 归档文件
-> 3. 读取这些文件提取飞书渠道的完整聊天记录
-> 4. 将遗漏的内容补充到当日L2记录中
-
-**脚本调用:**
-```bash
-python ~/.openclaw/workspace/skills/memory-management/scripts/daily_check.py
-```
-
-### 每周维护(周一早上9:30,Cron定时任务)
-
-**配置方法:**
-```bash
-# 添加cron任务,每周一9:30执行
-openclaw cron add \
- --name "memory-weekly-maintenance" \
- --cron "30 9 * * 1" \
- --message "执行三层记忆每周维护:1.运行memory-merger整理L2→L1 2.检查L0大小 3.生成周报发送给用户" \
- --channel feishu \
- --to "USER_ID" \
- --tz "Asia/Shanghai"
-```
-
-**任务清单:**
-- [ ] 运行 memory-merger 整理本周 L2 → L1
-- [ ] 检查 L0 大小,必要时归档
-- [ ] 生成周报内容
-- [ ] 发送周报到飞书
-
-**脚本调用:**
-```bash
-python ~/.openclaw/workspace/skills/memory-management/scripts/weekly_maintenance.py
-```
-
----
-
-## 决策流程
-
-```
-发生事件
- │
- ▼
-需要记住?
- ├── 否 → 忽略
- │
- └── 是
- │
- ▼
- 详细程度?
- ├── 高 → 写L2 (journal/YYYY-MM-DD.md)
- │ └── 更新L0引用
- │
- └── 低 → 写L0 (MEMORY.md)
- └── 定期检查大小
-```
-
-**L2 → L1 升级流程:**
-```
-L2积累
- │
- ▼
-重要/持久?
- ├── 是 → 提炼 → 写L1 (milestones/)
- │ └── 更新L0索引
- │
- └── 否 → 保持L2
-```
-
----
-
-## 使用脚本
-
-### 快速写入L0
-```bash
-python scripts/write_l0.py "摘要内容" --link "memory/journal/2026-03-13.md"
-```
-
-### 创建/追加L2
-```bash
-python scripts/write_l2.py --date 2026-03-13 --title "事件标题" --file content.txt
-```
-
-### 检查L0大小
-```bash
-python scripts/check_size.py # 输出:L0当前3.2KB/4KB ✅
-```
-
-### 每周维护
-```bash
-python scripts/weekly_maintenance.py --send-report
-```
-
----
-
-## 红线规则
-
-1. **L0 < 4KB**:超过则必须归档到L1
-2. **L0只存索引**:详情必须通过路径引用
-3. **L2日期命名**:必须按YYYY-MM-DD格式
-4. **每周必须维护**:不能无限累积L2
-5. **飞书历史必须检查**:当检查每日总结时,必须读取所有session提取飞书聊天记录
-
----
-
-## 与memory-merger的关系
-
-- **memory-merger**:专注L2→L1的合并逻辑
-- **memory-management**:完整的记忆体系管理(写入+维护+检查)
-
-当需要执行L2→L1合并时,本技能会调用memory-merger。
diff --git a/workspace/skills/memory-management/scripts/check_size.py b/workspace/skills/memory-management/scripts/check_size.py
deleted file mode 100755
index f5b40c0..0000000
--- a/workspace/skills/memory-management/scripts/check_size.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env python3
-"""
-检查MEMORY.md文件大小
-"""
-
-import sys
-from pathlib import Path
-
-
-def main():
- workspace = Path.home() / ".openclaw" / "workspace"
- memory_file = workspace / "MEMORY.md"
-
- if not memory_file.exists():
- print("❌ MEMORY.md 不存在")
- return 1
-
- size = memory_file.stat().st_size
- kb = size / 1024
-
- print(f"📊 MEMORY.md (L0层) 大小检查")
- print(f" 当前: {kb:.1f}KB / 4KB")
-
- if size > 4096:
- print(" 🚨 状态: 超过红线!需要立即归档")
- return 2
- elif size > 3500:
- print(" ⚠️ 状态: 接近限制,建议准备归档")
- return 1
- else:
- print(" ✅ 状态: 正常")
- return 0
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/workspace/skills/memory-management/scripts/daily_check.py b/workspace/skills/memory-management/scripts/daily_check.py
deleted file mode 100755
index 32443c0..0000000
--- a/workspace/skills/memory-management/scripts/daily_check.py
+++ /dev/null
@@ -1,569 +0,0 @@
-#!/usr/bin/env python3
-"""
-每日记忆检查脚本 V2 - 增强跨 session 消息聚合能力
-优化点:
-1. 跨 session 消息聚合 - 合并所有 session 的消息按时间排序
-2. 增强事件检测 - 支持更多关键词和模式匹配
-3. 处理 session 重置 - 正确识别 .reset. 和 .deleted. 文件
-4. 完整时间线生成 - 按时间顺序展示今日所有活动
-5. 智能消息过滤 - 区分真实用户消息和系统提示
-"""
-
-import os
-import sys
-import json
-import re
-from datetime import datetime, timedelta
-from pathlib import Path
-from typing import List, Dict, Optional, Tuple
-from collections import defaultdict
-
-
-def get_workspace_path() -> Path:
- """获取 workspace 路径。"""
- return Path.home() / ".openclaw" / "workspace"
-
-
-def get_sessions_path() -> Path:
- """获取 sessions 路径。"""
- return Path.home() / ".openclaw" / "agents" / "main" / "sessions"
-
-
-def check_today_journal() -> bool:
- """检查今日是否已有 L2 记录。"""
- workspace = get_workspace_path()
- today = datetime.now().strftime("%Y-%m-%d")
- journal_file = workspace / "memory" / "journal" / f"{today}.md"
- return journal_file.exists()
-
-
-def get_l0_size() -> int:
- """获取 MEMORY.md 文件大小(字节)。"""
- workspace = get_workspace_path()
- memory_file = workspace / "MEMORY.md"
- if memory_file.exists():
- return memory_file.stat().st_size
- return 0
-
-
-def format_size(size_bytes: int) -> str:
- """格式化文件大小显示。"""
- kb = size_bytes / 1024
- return f"{kb:.1f}KB"
-
-
-def get_today_session_files() -> List[Dict]:
- """
- 获取今日所有 session 文件(包括 .reset. 和 .deleted. 归档)
- 按修改时间排序,确保能重建完整时间线
- """
- sessions_dir = get_sessions_path()
- if not sessions_dir.exists():
- return []
-
- today = datetime.now()
- today_files = []
-
- # 扫描所有 .jsonl 相关文件(包括 .reset. 和 .deleted.)
- for pattern in ["*.jsonl", "*.jsonl.reset.*", "*.jsonl.deleted.*"]:
- for file in sessions_dir.glob(pattern):
- try:
- mtime = datetime.fromtimestamp(file.stat().st_mtime)
- if mtime.date() == today.date():
- today_files.append({
- 'path': file,
- 'mtime': mtime,
- 'name': file.name
- })
- except (OSError, ValueError):
- continue
-
- # 按修改时间排序
- today_files.sort(key=lambda x: x['mtime'])
- return today_files
-
-
-def extract_user_content(text: str) -> Optional[str]:
- """
- 从消息文本中提取用户的实际内容
- 过滤掉系统提示、元数据等
- """
- if not text or len(text) < 10:
- return None
-
- # 跳过纯系统提示消息
- system_indicators = [
- "OpenClaw runtime context",
- "[Subagent Context]",
- "You are running as a subagent",
- "Results auto-announce",
- "This context is runtime-generated",
- "Keep internal details private",
- "conversation info (untrusted)",
- "feishu control message",
- "feishu event type:",
- ]
-
- lower_text = text.lower()
- for indicator in system_indicators:
- if indicator.lower() in lower_text[:200]:
- return None
-
- # 处理飞书消息格式 - 提取实际用户内容
- # 格式:System: [时间] Feishu[main] DM from xxx: 实际内容
- feishu_match = re.search(r'Feishu\[.*?\]\s+\w+\s+from\s+\w+:\s*(.+?)(?=\n\n|$)', text, re.DOTALL)
- if feishu_match:
- content = feishu_match.group(1).strip()
- # 移除 JSON 元数据块
- content = re.sub(r'```json\s*\{.*?\}\s*```', '', content, flags=re.DOTALL)
- content = content.strip()
- if len(content) > 10:
- return content
- return None
-
- # 如果是普通用户消息(非系统消息),直接返回
- if not text.startswith("System:") and not text.startswith("["):
- return text.strip() if len(text) > 10 else None
-
- return None
-
-
-def parse_timestamp(ts: any) -> Optional[datetime]:
- """
- 解析各种格式的时间戳为 datetime 对象
- 支持 ISO 8601 字符串和毫秒级 Unix 时间戳
- """
- if not ts:
- return None
-
- # 如果是数字(毫秒级 Unix 时间戳)
- if isinstance(ts, (int, float)):
- # 毫秒转秒
- ts_sec = ts / 1000 if ts > 1e10 else ts
- try:
- return datetime.fromtimestamp(ts_sec)
- except (ValueError, OSError):
- return None
-
- # 如果是字符串(ISO 8601 格式)
- if isinstance(ts, str):
- try:
- # 处理带 Z 的 UTC 时间
- ts = ts.replace('Z', '+00:00')
- # Python 3.7+ 支持 fromisoformat
- from datetime import timezone
- dt = datetime.fromisoformat(ts)
- # 转换为本地时间
- if dt.tzinfo is not None:
- dt = dt.replace(tzinfo=None)
- return dt
- except (ValueError, TypeError):
- return None
-
- return None
-
-
-def extract_messages_from_session(file_info: Dict) -> List[Dict]:
- """
- 从 session 文件中提取所有真实用户消息
- 优化版:
- 1. 正确解析消息时间戳(而非使用文件修改时间)
- 2. 提取飞书消息中的真实发送时间
- 3. 改进内容去重和过滤
- """
- messages = []
- file_path = file_info['path']
- session_name = file_info['name']
-
- try:
- with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
- lines = f.readlines()
-
- for line in lines:
- line = line.strip()
- if not line:
- continue
-
- try:
- record = json.loads(line)
-
- # 只处理消息类型
- if record.get("type") != "message":
- continue
-
- msg = record.get("message", {})
- if not msg:
- continue
-
- # 只提取用户消息
- if msg.get("role") != "user":
- continue
-
- content_list = msg.get("content", [])
- if not content_list:
- continue
-
- # 提取文本内容
- for item in content_list:
- if isinstance(item, dict) and item.get("type") == "text":
- text = item.get("text", "")
-
- # 提取真实用户内容(过滤系统消息)
- user_content = extract_user_content(text)
- if not user_content:
- break
-
- # 解析时间戳 - 优先级:
- # 1. record 级别的时间戳(ISO 8601)
- # 2. message 内部的 timestamp(毫秒 Unix)
- # 3. 从飞书消息文本中提取时间
- # 4. 最后使用文件修改时间
-
- msg_time = None
- time_source = "unknown"
-
- # 尝试从 record 获取时间戳
- record_ts = record.get("timestamp")
- if record_ts:
- msg_time = parse_timestamp(record_ts)
- if msg_time:
- time_source = "record"
-
- # 尝试从 message 内部获取时间戳(毫秒 Unix)
- if not msg_time and "timestamp" in msg:
- msg_time = parse_timestamp(msg.get("timestamp"))
- if msg_time:
- time_source = "message"
-
- # 尝试从飞书消息文本中提取时间
- if not msg_time:
- feishu_time_match = re.search(r'\[(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})', text)
- if feishu_time_match:
- try:
- msg_time = datetime.strptime(feishu_time_match.group(1), "%Y-%m-%d %H:%M:%S")
- time_source = "feishu_text"
- except ValueError:
- pass
-
- # 最后使用文件修改时间
- if not msg_time:
- msg_time = file_info['mtime']
- time_source = "file_mtime"
-
- messages.append({
- 'timestamp': msg_time.isoformat() if msg_time else "",
- 'timestamp_dt': msg_time,
- 'content': user_content[:400], # 限制长度
- 'session': session_name,
- 'time_str': msg_time.strftime('%H:%M:%S') if msg_time else 'unknown',
- 'time_source': time_source,
- 'content_hash': hash(user_content[:100]) # 用于去重
- })
- break
-
- except json.JSONDecodeError:
- continue
-
- except (IOError, OSError) as e:
- print(f" 警告:无法读取文件 {file_info['name']}: {e}")
-
- return messages
-
-
-def deduplicate_messages(messages: List[Dict]) -> List[Dict]:
- """
- 对跨 session 的消息进行去重
- 基于内容哈希和时间窗口判断是否为重复消息
- """
- if not messages:
- return []
-
- # 先按时间排序
- messages.sort(key=lambda x: x.get('timestamp_dt') or datetime.min)
-
- deduped = []
- seen_hashes = {} # hash -> (timestamp, content_preview)
-
- # 时间窗口:5分钟内相同内容的视为重复
- time_window = timedelta(minutes=5)
-
- for msg in messages:
- content_hash = msg.get('content_hash')
- msg_time = msg.get('timestamp_dt')
-
- if content_hash is None:
- deduped.append(msg)
- continue
-
- # 检查是否已有相似消息
- is_duplicate = False
- if content_hash in seen_hashes:
- last_time, last_preview = seen_hashes[content_hash]
- if msg_time and last_time:
- if abs((msg_time - last_time).total_seconds()) < time_window.total_seconds():
- is_duplicate = True
- # 保留更详细的消息(更长的内容)
- if len(msg.get('content', '')) > len(last_preview):
- # 替换之前的消息
- for i, existing in enumerate(deduped):
- if existing.get('content_hash') == content_hash:
- deduped[i] = msg
- seen_hashes[content_hash] = (msg_time, msg.get('content', '')[:100])
- break
-
- if not is_duplicate:
- deduped.append(msg)
- if content_hash:
- seen_hashes[content_hash] = (msg_time, msg.get('content', '')[:100])
-
- return deduped
-
-
-def aggregate_messages_across_sessions(session_files: List[Dict]) -> List[Dict]:
- """
- 跨 session 聚合所有消息,按时间排序
- 优化版:
- 1. 正确解析每条消息的真实时间戳
- 2. 跨 session 去重(处理 session 重置导致的重复消息)
- 3. 重建完整时间线
- """
- all_messages = []
-
- print(f"\n 正在处理 {len(session_files)} 个 session 文件...")
-
- for file_info in session_files:
- messages = extract_messages_from_session(file_info)
- if messages:
- all_messages.extend(messages)
- # 显示时间源统计
- time_sources = {}
- for m in messages:
- src = m.get('time_source', 'unknown')
- time_sources[src] = time_sources.get(src, 0) + 1
- print(f" 📄 {file_info['name'][:30]}...: {len(messages)} 条消息")
- for src, count in time_sources.items():
- print(f" └─ {src}: {count}")
-
- if not all_messages:
- return []
-
- # 去重(处理 session 重置导致的重复)
- print(f"\n 🔄 原始消息数: {len(all_messages)}")
- all_messages = deduplicate_messages(all_messages)
- print(f" ✅ 去重后消息数: {len(all_messages)}")
-
- # 按时间戳排序,重建完整时间线
- all_messages.sort(key=lambda x: x.get('timestamp_dt') or datetime.min)
-
- return all_messages
-
-
-def detect_important_events(messages: List[Dict]) -> List[Dict]:
- """
- 从聚合后的消息中检测重要事件
- 增强版:支持更多关键词和上下文分析
- """
- # 扩展关键词列表
- important_keywords = {
- '配置变更': ['配置', 'config', 'setup', 'settings', '修改', '变更', '更新'],
- '技能操作': ['技能', 'skill', '安装', '创建', '卸载', '删除', '移除', 'skill'],
- '定时任务': ['定时', 'cron', '任务', 'schedule', 'job', '早报'],
- '调试排错': ['调试', '测试', 'test', 'debug', '错误', 'error', '失败', 'fail', '问题'],
- '决策讨论': ['决策', '决定', '方案', '选择', '最终', '结论', '分析'],
- '搜索查询': ['搜索', '查找', 'query', 'find', 'check', '查询'],
- 'API集成': ['api', 'key', 'token', '集成', 'integration'],
- '系统维护': ['重启', 'reset', 'restart', '维护', '清理', 'gateway'],
- '代码提交': ['git', '提交', 'commit', 'push', 'pr'],
- '文档记录': ['记录', '文档', 'journal', 'memory', '笔记'],
- }
-
- events = []
- seen_contents = set() # 用于去重
-
- for msg in messages:
- content = msg['content']
- content_hash = content[:100] # 用前100字符作为去重key
-
- if content_hash in seen_contents:
- continue
- seen_contents.add(content_hash)
-
- # 检查是否匹配任何关键词类别
- for category, keywords in important_keywords.items():
- for keyword in keywords:
- if keyword.lower() in content.lower():
- events.append({
- 'time': msg.get('session_time', 'unknown'),
- 'category': category,
- 'content': content[:200] + '...' if len(content) > 200 else content,
- 'session': msg.get('session', 'unknown')[:20]
- })
- break
- else:
- continue
- break
-
- return events
-
-
-def generate_daily_summary(events: List[Dict]) -> str:
- """
- 生成每日活动摘要
- """
- if not events:
- return "今日暂无重要活动记录"
-
- summary = f"\n📋 今日活动摘要(共 {len(events)} 个事件):\n"
- summary += "=" * 60 + "\n"
-
- # 按类别分组
- by_category = defaultdict(list)
- for event in events:
- by_category[event['category']].append(event)
-
- for category, cat_events in sorted(by_category.items()):
- summary += f"\n【{category}】({len(cat_events)} 个)\n"
- for i, event in enumerate(cat_events[:3], 1):
- summary += f" {i}. [{event['time']}] {event['content']}\n"
- if len(cat_events) > 3:
- summary += f" ... 还有 {len(cat_events) - 3} 个相关事件\n"
-
- return summary
-
-
-def analyze_sessions_for_events() -> Tuple[bool, List[Dict], str]:
- """
- 分析今日所有 session,检查是否有重要事件需要记录
- 返回:(是否需要补充记录, 事件列表, 摘要文本)
- """
- print("\n" + "=" * 60)
- print("🔍 跨 Session 消息聚合分析 V2")
- print("=" * 60)
-
- session_files = get_today_session_files()
-
- if not session_files:
- print("\n⚠️ 未找到今日 session 文件")
- return False, [], "未找到 session 文件"
-
- print(f"\n📁 找到 {len(session_files)} 个 session 文件(含归档):")
- current_count = sum(1 for f in session_files if '.reset.' not in f['name'] and '.deleted.' not in f['name'])
- reset_count = sum(1 for f in session_files if '.reset.' in f['name'])
- deleted_count = sum(1 for f in session_files if '.deleted.' in f['name'])
-
- print(f" - 当前活跃: {current_count} 个")
- print(f" - 重置归档: {reset_count} 个")
- print(f" - 删除归档: {deleted_count} 个")
-
- # 关键步骤:跨 session 聚合所有消息
- print("\n🔄 正在聚合所有 session 的真实用户消息...")
- all_messages = aggregate_messages_across_sessions(session_files)
-
- if not all_messages:
- print(" ⚠️ 未提取到真实用户消息(已过滤系统提示)")
- return False, [], "未提取到用户消息"
-
- print(f" ✅ 成功聚合 {len(all_messages)} 条用户消息(已过滤系统消息)")
-
- # 显示活动时间线
- print(f"\n⏱️ 活动时间跨度:")
- first_time = all_messages[0].get('session_time', 'unknown')
- last_time = all_messages[-1].get('session_time', 'unknown')
- print(f" 开始:{first_time}")
- print(f" 结束:{last_time}")
-
- # 显示跨 session 统计
- session_stats = defaultdict(int)
- for msg in all_messages:
- session_stats[msg.get('session', 'unknown')[:20]] += 1
-
- print(f"\n📊 各 Session 消息分布:")
- for session_name, count in sorted(session_stats.items(), key=lambda x: -x[1])[:5]:
- print(f" - {session_name}: {count} 条")
-
- # 检测重要事件
- print("\n🎯 检测重要事件...")
- events = detect_important_events(all_messages)
-
- if events:
- print(f" ✅ 识别到 {len(events)} 个重要事件(去重后)")
- else:
- print(" ℹ️ 未识别到重要事件")
-
- # 生成摘要
- summary = generate_daily_summary(events)
- print(summary)
-
- # 判断是否需要补充记录
- has_today_journal = check_today_journal()
- needs_update = len(events) >= 3 and not has_today_journal
-
- if needs_update:
- print(f"\n🚨 发现遗漏:今日有 {len(events)} 个重要事件但未写入 L2")
- print(f" 建议:执行 '补充今日 L2 记录'")
- elif has_today_journal:
- print(f"\n✅ 已记录 L2,跨 session 聚合完成")
- print(f" 共处理 {len(session_files)} 个 session,提取 {len(all_messages)} 条消息")
- else:
- print(f"\n⚠️ 今日无重要活动或已记录完毕")
-
- return needs_update, events, summary
-
-
-def main():
- """主函数。"""
- today_str = datetime.now().strftime("%Y-%m-%d")
- print(f"📅 日期检查: {today_str}")
-
- # 关键步骤:跨 session 聚合分析
- needs_update, events, summary = analyze_sessions_for_events()
-
- # 检查 L0 大小
- l0_size = get_l0_size()
- print(f"\n📊 L0 (MEMORY.md) 大小检查:")
- print(f" 当前: {format_size(l0_size)} / 4KB")
-
- if l0_size > 4096:
- print(" 🚨 警告:超过 4KB 红线!需要立即归档到 L1")
- elif l0_size > 3500:
- print(" ⚠️ 提醒:接近 4KB 限制,建议准备归档")
- else:
- print(" ✅ 大小正常")
-
- # 维护清单
- print("\n" + "=" * 60)
- print("📋 每日维护清单:")
-
- has_today_journal = check_today_journal()
- if has_today_journal:
- print(" [x] L2 记录已存在")
- else:
- print(" [ ] 如有重要事件,写入今日 L2")
-
- if events:
- print(f" [x] 已扫描并聚合 {len(events)} 个重要事件(跨 session)")
- else:
- print(" [-] 今日无重要活动")
-
- print(" [ ] 检查 MEMORY.md 最近活动摘要")
- if l0_size > 3500:
- print(" [ ] L0 接近限制,考虑归档到 L1")
- print(" [ ] 确认 L0 层引用链接有效")
-
- print("\n💡 改进说明:")
- print(" - 新增跨 session 消息聚合功能")
- print(" - 智能过滤系统提示消息")
- print(" - 自动识别 .reset. 和 .deleted. 归档文件")
- print(" - 按时间线重建完整活动记录")
-
- # 返回状态码
- if needs_update:
- return 2 # 需要补充记录
- elif not has_today_journal:
- return 1 # 无 L2 记录
- else:
- return 0 # 一切正常
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/workspace/skills/memory-management/scripts/daily_check_v2.py b/workspace/skills/memory-management/scripts/daily_check_v2.py
deleted file mode 100644
index 1e880cc..0000000
--- a/workspace/skills/memory-management/scripts/daily_check_v2.py
+++ /dev/null
@@ -1,418 +0,0 @@
-#!/usr/bin/env python3
-"""
-每日记忆检查脚本 V2 - 增强跨 session 消息聚合能力
-优化点:
-1. 跨 session 消息聚合 - 合并所有 session 的消息按时间排序
-2. 增强事件检测 - 支持更多关键词和模式匹配
-3. 处理 session 重置 - 正确识别 .reset. 和 .deleted. 文件
-4. 完整时间线生成 - 按时间顺序展示今日所有活动
-5. 智能消息过滤 - 区分真实用户消息和系统提示
-"""
-
-import os
-import sys
-import json
-import re
-from datetime import datetime, timedelta
-from pathlib import Path
-from typing import List, Dict, Optional, Tuple
-from collections import defaultdict
-
-
-def get_workspace_path() -> Path:
- """获取 workspace 路径。"""
- return Path.home() / ".openclaw" / "workspace"
-
-
-def get_sessions_path() -> Path:
- """获取 sessions 路径。"""
- return Path.home() / ".openclaw" / "agents" / "main" / "sessions"
-
-
-def check_today_journal() -> bool:
- """检查今日是否已有 L2 记录。"""
- workspace = get_workspace_path()
- today = datetime.now().strftime("%Y-%m-%d")
- journal_file = workspace / "memory" / "journal" / f"{today}.md"
- return journal_file.exists()
-
-
-def get_l0_size() -> int:
- """获取 MEMORY.md 文件大小(字节)。"""
- workspace = get_workspace_path()
- memory_file = workspace / "MEMORY.md"
- if memory_file.exists():
- return memory_file.stat().st_size
- return 0
-
-
-def format_size(size_bytes: int) -> str:
- """格式化文件大小显示。"""
- kb = size_bytes / 1024
- return f"{kb:.1f}KB"
-
-
-def get_today_session_files() -> List[Dict]:
- """
- 获取今日所有 session 文件(包括 .reset. 和 .deleted. 归档)
- 按修改时间排序,确保能重建完整时间线
- """
- sessions_dir = get_sessions_path()
- if not sessions_dir.exists():
- return []
-
- today = datetime.now()
- today_files = []
-
- # 扫描所有 .jsonl 相关文件(包括 .reset. 和 .deleted.)
- for pattern in ["*.jsonl", "*.jsonl.reset.*", "*.jsonl.deleted.*"]:
- for file in sessions_dir.glob(pattern):
- try:
- mtime = datetime.fromtimestamp(file.stat().st_mtime)
- if mtime.date() == today.date():
- today_files.append({
- 'path': file,
- 'mtime': mtime,
- 'name': file.name
- })
- except (OSError, ValueError):
- continue
-
- # 按修改时间排序
- today_files.sort(key=lambda x: x['mtime'])
- return today_files
-
-
-def extract_user_content(text: str) -> Optional[str]:
- """
- 从消息文本中提取用户的实际内容
- 过滤掉系统提示、元数据等
- """
- if not text or len(text) < 10:
- return None
-
- # 跳过纯系统提示消息
- system_indicators = [
- "OpenClaw runtime context",
- "[Subagent Context]",
- "You are running as a subagent",
- "Results auto-announce",
- "This context is runtime-generated",
- "Keep internal details private",
- "conversation info (untrusted)",
- "feishu control message",
- "feishu event type:",
- ]
-
- lower_text = text.lower()
- for indicator in system_indicators:
- if indicator.lower() in lower_text[:200]:
- return None
-
- # 处理飞书消息格式 - 提取实际用户内容
- # 格式:System: [时间] Feishu[main] DM from xxx: 实际内容
- feishu_match = re.search(r'Feishu\[.*?\]\s+\w+\s+from\s+\w+:\s*(.+?)(?=\n\n|$)', text, re.DOTALL)
- if feishu_match:
- content = feishu_match.group(1).strip()
- # 移除 JSON 元数据块
- content = re.sub(r'```json\s*\{.*?\}\s*```', '', content, flags=re.DOTALL)
- content = content.strip()
- if len(content) > 10:
- return content
- return None
-
- # 如果是普通用户消息(非系统消息),直接返回
- if not text.startswith("System:") and not text.startswith("["):
- return text.strip() if len(text) > 10 else None
-
- return None
-
-
-def extract_messages_from_session(file_info: Dict) -> List[Dict]:
- """
- 从 session 文件中提取所有真实用户消息
- 增强版:过滤系统消息,提取实际用户内容
- """
- messages = []
- file_path = file_info['path']
- session_name = file_info['name']
-
- try:
- with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
- lines = f.readlines()
-
- for line in lines:
- line = line.strip()
- if not line:
- continue
-
- try:
- record = json.loads(line)
-
- # 只处理消息类型
- if record.get("type") != "message":
- continue
-
- msg = record.get("message", {})
- if not msg:
- continue
-
- # 只提取用户消息
- if msg.get("role") != "user":
- continue
-
- content_list = msg.get("content", [])
- if not content_list:
- continue
-
- # 提取文本内容
- for item in content_list:
- if isinstance(item, dict) and item.get("type") == "text":
- text = item.get("text", "")
-
- # 提取真实用户内容(过滤系统消息)
- user_content = extract_user_content(text)
- if user_content:
- messages.append({
- 'timestamp': record.get("timestamp", ""),
- 'content': user_content[:400], # 限制长度
- 'session': session_name,
- 'session_time': file_info['mtime'].strftime('%H:%M:%S')
- })
- break
-
- except json.JSONDecodeError:
- continue
-
- except (IOError, OSError) as e:
- print(f" 警告:无法读取文件 {file_info['name']}: {e}")
-
- return messages
-
-
-def aggregate_messages_across_sessions(session_files: List[Dict]) -> List[Dict]:
- """
- 跨 session 聚合所有消息,按时间排序
- 这是解决 session 分割问题的关键函数
- """
- all_messages = []
-
- for file_info in session_files:
- messages = extract_messages_from_session(file_info)
- all_messages.extend(messages)
-
- # 按时间戳排序,重建完整时间线
- all_messages.sort(key=lambda x: x.get('timestamp', ''))
-
- return all_messages
-
-
-def detect_important_events(messages: List[Dict]) -> List[Dict]:
- """
- 从聚合后的消息中检测重要事件
- 增强版:支持更多关键词和上下文分析
- """
- # 扩展关键词列表
- important_keywords = {
- '配置变更': ['配置', 'config', 'setup', 'settings', '修改', '变更', '更新'],
- '技能操作': ['技能', 'skill', '安装', '创建', '卸载', '删除', '移除', 'skill'],
- '定时任务': ['定时', 'cron', '任务', 'schedule', 'job', '早报'],
- '调试排错': ['调试', '测试', 'test', 'debug', '错误', 'error', '失败', 'fail', '问题'],
- '决策讨论': ['决策', '决定', '方案', '选择', '最终', '结论', '分析'],
- '搜索查询': ['搜索', '查找', 'query', 'find', 'check', '查询'],
- 'API集成': ['api', 'key', 'token', '集成', 'integration'],
- '系统维护': ['重启', 'reset', 'restart', '维护', '清理', 'gateway'],
- '代码提交': ['git', '提交', 'commit', 'push', 'pr'],
- '文档记录': ['记录', '文档', 'journal', 'memory', '笔记'],
- }
-
- events = []
- seen_contents = set() # 用于去重
-
- for msg in messages:
- content = msg['content']
- content_hash = content[:100] # 用前100字符作为去重key
-
- if content_hash in seen_contents:
- continue
- seen_contents.add(content_hash)
-
- # 检查是否匹配任何关键词类别
- for category, keywords in important_keywords.items():
- for keyword in keywords:
- if keyword.lower() in content.lower():
- events.append({
- 'time': msg.get('session_time', 'unknown'),
- 'category': category,
- 'content': content[:200] + '...' if len(content) > 200 else content,
- 'session': msg.get('session', 'unknown')[:20]
- })
- break
- else:
- continue
- break
-
- return events
-
-
-def generate_daily_summary(events: List[Dict]) -> str:
- """
- 生成每日活动摘要
- """
- if not events:
- return "今日暂无重要活动记录"
-
- summary = f"\n📋 今日活动摘要(共 {len(events)} 个事件):\n"
- summary += "=" * 60 + "\n"
-
- # 按类别分组
- by_category = defaultdict(list)
- for event in events:
- by_category[event['category']].append(event)
-
- for category, cat_events in sorted(by_category.items()):
- summary += f"\n【{category}】({len(cat_events)} 个)\n"
- for i, event in enumerate(cat_events[:3], 1):
- summary += f" {i}. [{event['time']}] {event['content']}\n"
- if len(cat_events) > 3:
- summary += f" ... 还有 {len(cat_events) - 3} 个相关事件\n"
-
- return summary
-
-
-def analyze_sessions_for_events() -> Tuple[bool, List[Dict], str]:
- """
- 分析今日所有 session,检查是否有重要事件需要记录
- 返回:(是否需要补充记录, 事件列表, 摘要文本)
- """
- print("\n" + "=" * 60)
- print("🔍 跨 Session 消息聚合分析 V2")
- print("=" * 60)
-
- session_files = get_today_session_files()
-
- if not session_files:
- print("\n⚠️ 未找到今日 session 文件")
- return False, [], "未找到 session 文件"
-
- print(f"\n📁 找到 {len(session_files)} 个 session 文件(含归档):")
- current_count = sum(1 for f in session_files if '.reset.' not in f['name'] and '.deleted.' not in f['name'])
- reset_count = sum(1 for f in session_files if '.reset.' in f['name'])
- deleted_count = sum(1 for f in session_files if '.deleted.' in f['name'])
-
- print(f" - 当前活跃: {current_count} 个")
- print(f" - 重置归档: {reset_count} 个")
- print(f" - 删除归档: {deleted_count} 个")
-
- # 关键步骤:跨 session 聚合所有消息
- print("\n🔄 正在聚合所有 session 的真实用户消息...")
- all_messages = aggregate_messages_across_sessions(session_files)
-
- if not all_messages:
- print(" ⚠️ 未提取到真实用户消息(已过滤系统提示)")
- return False, [], "未提取到用户消息"
-
- print(f" ✅ 成功聚合 {len(all_messages)} 条用户消息(已过滤系统消息)")
-
- # 显示活动时间线
- print(f"\n⏱️ 活动时间跨度:")
- first_time = all_messages[0].get('session_time', 'unknown')
- last_time = all_messages[-1].get('session_time', 'unknown')
- print(f" 开始:{first_time}")
- print(f" 结束:{last_time}")
-
- # 显示跨 session 统计
- session_stats = defaultdict(int)
- for msg in all_messages:
- session_stats[msg.get('session', 'unknown')[:20]] += 1
-
- print(f"\n📊 各 Session 消息分布:")
- for session_name, count in sorted(session_stats.items(), key=lambda x: -x[1])[:5]:
- print(f" - {session_name}: {count} 条")
-
- # 检测重要事件
- print("\n🎯 检测重要事件...")
- events = detect_important_events(all_messages)
-
- if events:
- print(f" ✅ 识别到 {len(events)} 个重要事件(去重后)")
- else:
- print(" ℹ️ 未识别到重要事件")
-
- # 生成摘要
- summary = generate_daily_summary(events)
- print(summary)
-
- # 判断是否需要补充记录
- has_today_journal = check_today_journal()
- needs_update = len(events) >= 3 and not has_today_journal
-
- if needs_update:
- print(f"\n🚨 发现遗漏:今日有 {len(events)} 个重要事件但未写入 L2")
- print(f" 建议:执行 '补充今日 L2 记录'")
- elif has_today_journal:
- print(f"\n✅ 已记录 L2,跨 session 聚合完成")
- print(f" 共处理 {len(session_files)} 个 session,提取 {len(all_messages)} 条消息")
- else:
- print(f"\n⚠️ 今日无重要活动或已记录完毕")
-
- return needs_update, events, summary
-
-
-def main():
- """主函数。"""
- today_str = datetime.now().strftime("%Y-%m-%d")
- print(f"📅 日期检查: {today_str}")
-
- # 关键步骤:跨 session 聚合分析
- needs_update, events, summary = analyze_sessions_for_events()
-
- # 检查 L0 大小
- l0_size = get_l0_size()
- print(f"\n📊 L0 (MEMORY.md) 大小检查:")
- print(f" 当前: {format_size(l0_size)} / 4KB")
-
- if l0_size > 4096:
- print(" 🚨 警告:超过 4KB 红线!需要立即归档到 L1")
- elif l0_size > 3500:
- print(" ⚠️ 提醒:接近 4KB 限制,建议准备归档")
- else:
- print(" ✅ 大小正常")
-
- # 维护清单
- print("\n" + "=" * 60)
- print("📋 每日维护清单:")
-
- has_today_journal = check_today_journal()
- if has_today_journal:
- print(" [x] L2 记录已存在")
- else:
- print(" [ ] 如有重要事件,写入今日 L2")
-
- if events:
- print(f" [x] 已扫描并聚合 {len(events)} 个重要事件(跨 session)")
- else:
- print(" [-] 今日无重要活动")
-
- print(" [ ] 检查 MEMORY.md 最近活动摘要")
- if l0_size > 3500:
- print(" [ ] L0 接近限制,考虑归档到 L1")
- print(" [ ] 确认 L0 层引用链接有效")
-
- print("\n💡 改进说明:")
- print(" - 新增跨 session 消息聚合功能")
- print(" - 智能过滤系统提示消息")
- print(" - 自动识别 .reset. 和 .deleted. 归档文件")
- print(" - 按时间线重建完整活动记录")
-
- # 返回状态码
- if needs_update:
- return 2 # 需要补充记录
- elif not has_today_journal:
- return 1 # 无 L2 记录
- else:
- return 0 # 一切正常
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/workspace/skills/memory-management/scripts/weekly_maintenance.py b/workspace/skills/memory-management/scripts/weekly_maintenance.py
deleted file mode 100755
index dbe8802..0000000
--- a/workspace/skills/memory-management/scripts/weekly_maintenance.py
+++ /dev/null
@@ -1,422 +0,0 @@
-#!/usr/bin/env python3
-"""
-每周维护脚本
-周一早上9:30执行,负责:
-1. 自动合并L2→L1(基于内容筛选策略)
-2. 检查L0大小
-3. 生成周报
-4. 发送报告(可选)
-"""
-
-import os
-import sys
-import subprocess
-import re
-from datetime import datetime, timedelta
-from pathlib import Path
-
-
-def get_workspace_path() -> Path:
- """获取workspace路径。"""
- return Path.home() / ".openclaw" / "workspace"
-
-
-def should_merge_to_l1(content: str) -> tuple:
- """
- 判断L2内容是否应该合并到L1
- 返回: (是否应该合并, 原因/类别)
- """
- content_lower = content.lower()
-
- # 策略1: 关键词匹配 - 重要决策类
- decision_keywords = [
- "决策", "决定", "结论", "方案", "选择",
- "采用", "确定", "最终", "resolved", "解决",
- "关键", "重要", "核心", "原则"
- ]
-
- # 策略2: 技术方案类
- tech_keywords = [
- "架构", "设计", "实现", "配置", "优化",
- "部署", "迁移", "升级", "重构", "方案"
- ]
-
- # 策略3: 经验教训类
- lesson_keywords = [
- "教训", "经验", "学习", "注意", "避免",
- "问题", "bug", "错误", "失败原因"
- ]
-
- # 策略4: 流程规范类
- process_keywords = [
- "流程", "规范", "规则", "约定", "标准",
- "红线", "必须", "禁止", "要求"
- ]
-
- # 检查匹配
- matched_keywords = []
- category = None
-
- for kw in decision_keywords:
- if kw in content_lower:
- matched_keywords.append(kw)
- category = "决策记录"
- break
-
- if not category:
- for kw in tech_keywords:
- if kw in content_lower:
- matched_keywords.append(kw)
- category = "技术方案"
- break
-
- if not category:
- for kw in lesson_keywords:
- if kw in content_lower:
- matched_keywords.append(kw)
- category = "经验教训"
- break
-
- if not category:
- for kw in process_keywords:
- if kw in content_lower:
- matched_keywords.append(kw)
- category = "流程规范"
- break
-
- # 策略5: 内容长度检查(太短的内容不值得合并)
- if len(content) < 200:
- return False, "内容过短"
-
- # 策略6: 必须有结构化标记(有###标题的才算正式记录)
- has_structure = bool(re.search(r'###\s+', content))
- if not has_structure:
- return False, "缺乏结构化标记"
-
- if category:
- return True, category
-
- return False, "未匹配合并策略"
-
-
-def is_duplicate_in_l1(content: str, l1_file: Path) -> bool:
- """检查内容是否已在L1中存在(简单去重)"""
- if not l1_file.exists():
- return False
-
- try:
- l1_content = l1_file.read_text(encoding='utf-8')
- # 提取内容前100字作为指纹
- content_fingerprint = content[:100].strip()
- # 检查L1中是否已有相似内容
- return content_fingerprint in l1_content
- except Exception:
- return False
-
-
-def auto_merge_l2_to_l1() -> dict:
- """
- 自动合并本周L2到L1
- 返回合并统计信息
- """
- workspace = get_workspace_path()
- journal_dir = workspace / "memory" / "journal"
- milestones_dir = workspace / "memory" / "milestones"
-
- if not journal_dir.exists():
- return {"status": "no_journal", "merged": 0, "skipped": 0, "details": []}
-
- # 确保milestones目录存在
- milestones_dir.mkdir(parents=True, exist_ok=True)
-
- # 获取本周日期范围
- today = datetime.now()
- start_of_week = today - timedelta(days=today.weekday())
-
- # 本周的L1文件
- current_month = today.strftime("%Y-%m")
- l1_file = milestones_dir / f"{current_month}-weekly.md"
-
- merged_count = 0
- skipped_count = 0
- details = []
-
- # 遍历本周L2文件
- for l2_file in sorted(journal_dir.glob("*.md")):
- try:
- file_date = datetime.strptime(l2_file.stem, "%Y-%m-%d")
- if not (start_of_week <= file_date <= today):
- continue
-
- # 读取L2内容
- l2_content = l2_file.read_text(encoding='utf-8')
-
- # 按事件分割(按##标题分割)
- events = re.split(r'\n##\s+\[', l2_content)
-
- for event in events[1:]: # 第一个通常是文件头
- event = "## [" + event # 恢复标题标记
-
- # 提取事件标题
- title_match = re.search(r'##\s*\[.*?\]\s*(.+?)\n', event)
- title = title_match.group(1).strip() if title_match else "未命名事件"
-
- # 判断是否应该合并
- should_merge, reason = should_merge_to_l1(event)
-
- if not should_merge:
- skipped_count += 1
- details.append({
- "date": l2_file.stem,
- "title": title,
- "action": "跳过",
- "reason": reason
- })
- continue
-
- # 检查是否重复
- if is_duplicate_in_l1(event, l1_file):
- skipped_count += 1
- details.append({
- "date": l2_file.stem,
- "title": title,
- "action": "跳过",
- "reason": "L1中已存在"
- })
- continue
-
- # 执行合并
- try:
- # 准备L1格式内容
- l1_entry = f"""
-## [{l2_file.stem}] {title}
-**类别**: {reason}
-**来源**: [L2详情](./journal/{l2_file.name})
-
-### 摘要
-{event[:500]}...
-
----
-"""
-
- # 追加到L1文件
- with open(l1_file, 'a', encoding='utf-8') as f:
- f.write(l1_entry)
-
- merged_count += 1
- details.append({
- "date": l2_file.stem,
- "title": title,
- "action": "已合并",
- "category": reason
- })
-
- except Exception as e:
- details.append({
- "date": l2_file.stem,
- "title": title,
- "action": "失败",
- "reason": str(e)
- })
-
- except ValueError:
- continue # 文件名格式不正确
- except Exception as e:
- details.append({
- "date": l2_file.stem if 'l2_file' in locals() else "unknown",
- "title": "读取失败",
- "action": "错误",
- "reason": str(e)
- })
-
- return {
- "status": "success" if merged_count > 0 else "no_merge",
- "merged": merged_count,
- "skipped": skipped_count,
- "l1_file": str(l1_file) if merged_count > 0 else None,
- "details": details
- }
-
-
-def run_memory_merger() -> tuple:
- """运行自动合并(替代原memory-merger调用)"""
- result = auto_merge_l2_to_l1()
-
- if result["status"] == "no_journal":
- return False, "未找到journal目录"
-
- if result["status"] == "success":
- summary = f"✅ 合并完成: {result['merged']} 条 → {result['l1_file']}\n"
- summary += f"⏭️ 跳过: {result['skipped']} 条\n"
- summary += "\n详细记录:\n"
- for d in result['details'][-5:]: # 只显示最后5条
- summary += f" - [{d['date']}] {d['title']}: {d['action']}"
- if 'reason' in d:
- summary += f" ({d['reason']})"
- summary += "\n"
- return True, summary
- else:
- return False, f"未找到可合并内容(本周共扫描 {result['skipped']} 条,均不符合合并条件)"
-
-
-def check_l0_size() -> dict:
- """检查L0状态。"""
- workspace = get_workspace_path()
- memory_file = workspace / "MEMORY.md"
-
- if not memory_file.exists():
- return {"exists": False, "size": 0, "status": "missing"}
-
- size = memory_file.stat().st_size
- kb = size / 1024
-
- if size > 4096:
- status = "over_limit"
- elif size > 3500:
- status = "warning"
- else:
- status = "ok"
-
- return {
- "exists": True,
- "size": size,
- "size_kb": kb,
- "status": status
- }
-
-
-def count_journal_files() -> int:
- """统计本周L2文件数量。"""
- workspace = get_workspace_path()
- journal_dir = workspace / "memory" / "journal"
-
- if not journal_dir.exists():
- return 0
-
- # 获取本周日期范围
- today = datetime.now()
- start_of_week = today - timedelta(days=today.weekday())
-
- count = 0
- for f in journal_dir.glob("*.md"):
- try:
- file_date = datetime.strptime(f.stem, "%Y-%m-%d")
- if start_of_week <= file_date <= today:
- count += 1
- except ValueError:
- continue
-
- return count
-
-
-def count_milestone_files() -> int:
- """统计L1里程碑文件数量。"""
- workspace = get_workspace_path()
- milestones_dir = workspace / "memory" / "milestones"
-
- if not milestones_dir.exists():
- return 0
-
- return len(list(milestones_dir.glob("*.md")))
-
-
-def generate_report() -> str:
- """生成周报内容。"""
- today_str = datetime.now().strftime("%Y-%m-%d")
- week_start = (datetime.now() - timedelta(days=datetime.now().weekday())).strftime("%Y-%m-%d")
-
- report = []
- report.append("# 📊 记忆管理周报")
- report.append(f"**周期**: {week_start} ~ {today_str}")
- report.append(f"**生成时间**: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
- report.append("")
- report.append("---")
- report.append("")
-
- # L0状态
- l0_status = check_l0_size()
- report.append("## 📋 L0层 (MEMORY.md)")
- if l0_status["exists"]:
- report.append(f"- **大小**: {l0_status['size_kb']:.1f}KB / 4KB")
- if l0_status["status"] == "ok":
- report.append("- **状态**: ✅ 正常")
- elif l0_status["status"] == "warning":
- report.append("- **状态**: ⚠️ 接近限制,建议归档")
- else:
- report.append("- **状态**: 🚨 超过红线,需要立即归档")
- else:
- report.append("- **状态**: ❌ 文件不存在")
- report.append("")
-
- # L2统计
- journal_count = count_journal_files()
- report.append("## 📝 L2层 (Journal)")
- report.append(f"- **本周新增**: {journal_count} 条记录")
- report.append("")
-
- # L1统计
- milestone_count = count_milestone_files()
- report.append("## 🗂️ L1层 (Milestones)")
- report.append(f"- **里程碑总数**: {milestone_count} 个主题")
- report.append("")
-
- # 维护任务
- report.append("## 🔧 本周维护任务")
-
- # 尝试运行memory-merger
- success, output = run_memory_merger()
- if success:
- report.append("- ✅ L2→L1合并完成")
- if output.strip():
- report.append(f"- 📄 合并详情:\n```\n{output}\n```")
- else:
- report.append(f"- ❌ L2→L1合并失败: {output}")
-
- if l0_status["status"] in ["warning", "over_limit"]:
- report.append("- ⚠️ L0层需要归档整理")
-
- report.append("")
- report.append("---")
- report.append("")
- report.append("*由memory-management技能自动生成*")
-
- return "\n".join(report)
-
-
-def main():
- """主函数。"""
- import argparse
- parser = argparse.ArgumentParser(description="三层记忆每周维护")
- parser.add_argument("--send-report", action="store_true", help="发送报告到飞书")
- parser.add_argument("--output", type=str, help="报告输出文件路径")
- args = parser.parse_args()
-
- print("🔄 开始执行每周维护...")
- print("=" * 50)
-
- # 生成报告
- report = generate_report()
-
- # 输出到文件
- if args.output:
- with open(args.output, 'w', encoding='utf-8') as f:
- f.write(report)
- print(f"✅ 报告已保存到: {args.output}")
-
- # 打印报告
- print("\n" + report)
-
- # 发送到飞书(如果需要)
- if args.send_report:
- print("\n📤 发送到飞书...")
- # 这里会调用message工具,但在脚本中我们通过stdout返回
- print(report)
-
- print("\n" + "=" * 50)
- print("✅ 每周维护完成")
-
- return 0
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/workspace/skills/memory-management/scripts/write_l0.py b/workspace/skills/memory-management/scripts/write_l0.py
deleted file mode 100755
index 7d29caf..0000000
--- a/workspace/skills/memory-management/scripts/write_l0.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python3
-"""
-快速写入L0层 (MEMORY.md)
-"""
-
-import sys
-import argparse
-from datetime import datetime
-from pathlib import Path
-
-
-def main():
- parser = argparse.ArgumentParser(description="写入L0层记忆")
- parser.add_argument("content", help="记录内容")
- parser.add_argument("--link", help="关联的L2文件路径")
- parser.add_argument("--type", default="活动", help="记录类型")
- args = parser.parse_args()
-
- workspace = Path.home() / ".openclaw" / "workspace"
- memory_file = workspace / "MEMORY.md"
-
- # 确保文件存在
- if not memory_file.exists():
- print(f"❌ {memory_file} 不存在")
- return 1
-
- today = datetime.now().strftime("%Y-%m-%d")
-
- # 构建记录行
- line = f"- **[{args.type}]** {args.content}"
- if args.link:
- line += f" → 详见 [{args.link}](./{args.link})"
-
- # 读取现有内容
- with open(memory_file, 'r', encoding='utf-8') as f:
- content = f.read()
-
- # 在"最近活动"部分添加
- if "### 最近活动" in content:
- parts = content.split("### 最近活动")
- if len(parts) == 2:
- # 在第一行后插入
- lines = parts[1].split('\n')
- insert_idx = 0
- for i, l in enumerate(lines):
- if l.strip() and not l.startswith('#'):
- insert_idx = i
- break
- lines.insert(insert_idx, f"- {today}: {args.content}")
- new_content = parts[0] + "### 最近活动" + '\n'.join(lines)
-
- with open(memory_file, 'w', encoding='utf-8') as f:
- f.write(new_content)
-
- print(f"✅ 已写入L0: {args.content}")
- return 0
-
- print("⚠️ 未找到'最近活动'区块,请手动添加")
- return 1
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/workspace/skills/memory-management/scripts/write_l2.py b/workspace/skills/memory-management/scripts/write_l2.py
deleted file mode 100755
index b8934b7..0000000
--- a/workspace/skills/memory-management/scripts/write_l2.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python3
-"""
-创建/追加L2层 (journal/)
-"""
-
-import sys
-import argparse
-from datetime import datetime
-from pathlib import Path
-
-
-def main():
- parser = argparse.ArgumentParser(description="写入L2层记忆")
- parser.add_argument("--date", default=datetime.now().strftime("%Y-%m-%d"), help="日期 (YYYY-MM-DD)")
- parser.add_argument("--title", required=True, help="事件标题")
- parser.add_argument("--content", help="内容(或从stdin读取)")
- parser.add_argument("--file", help="从文件读取内容")
- args = parser.parse_args()
-
- workspace = Path.home() / ".openclaw" / "workspace"
- journal_dir = workspace / "memory" / "journal"
- journal_dir.mkdir(parents=True, exist_ok=True)
-
- journal_file = journal_dir / f"{args.date}.md"
-
- # 获取内容
- content = ""
- if args.file:
- with open(args.file, 'r', encoding='utf-8') as f:
- content = f.read()
- elif args.content:
- content = args.content
- elif not sys.stdin.isatty():
- content = sys.stdin.read()
-
- now = datetime.now().strftime("%H:%M")
-
- # 构建记录
- entry = f"\n## [{now}] {args.title}\n\n{content}\n"
-
- # 写入文件
- if journal_file.exists():
- with open(journal_file, 'a', encoding='utf-8') as f:
- f.write(entry)
- print(f"✅ 已追加到 {journal_file}")
- else:
- header = f"# {args.date}\n"
- with open(journal_file, 'w', encoding='utf-8') as f:
- f.write(header + entry)
- print(f"✅ 已创建 {journal_file}")
-
- return 0
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/workspace/skills/memory-merger/SKILL.md b/workspace/skills/memory-merger/SKILL.md
deleted file mode 100644
index d6ff27b..0000000
--- a/workspace/skills/memory-merger/SKILL.md
+++ /dev/null
@@ -1,108 +0,0 @@
----
-name: memory-merger
-description: 'Merges mature lessons from a domain memory file into its instruction file. Syntax: `/memory-merger >domain [scope]` where scope is `global` (default), `user`, `workspace`, or `ws`.'
----
-
-# Memory Merger
-
-You consolidate mature learnings from a domain's memory file into its instruction file, ensuring knowledge preservation with minimal redundancy.
-
-**Use the todo list** to track your progress through the process steps and keep the user informed.
-
-## Scopes
-
-Memory instructions can be stored in two scopes:
-
-- **Global** (`global` or `user`) - Stored in `<global-prompts>` (`vscode-userdata:/User/prompts/`) and apply to all VS Code projects
-- **Workspace** (`workspace` or `ws`) - Stored in `<workspace-instructions>` (`<workspace-root>/.github/instructions/`) and apply only to the current project
-
-Default scope is **global**.
-
-Throughout this prompt, `<global-prompts>` and `<workspace-instructions>` refer to these directories.
-
-## Syntax
-
-```
-/memory-merger >domain-name [scope]
-```
-
-- `>domain-name` - Required. The domain to merge (e.g., `>clojure`, `>git-workflow`, `>prompt-engineering`)
-- `[scope]` - Optional. One of: `global`, `user` (both mean global), `workspace`, or `ws`. Defaults to `global`
-
-**Examples:**
-- `/memory-merger >prompt-engineering` - merges global prompt engineering memories
-- `/memory-merger >clojure workspace` - merges workspace clojure memories
-- `/memory-merger >git-workflow ws` - merges workspace git-workflow memories
-
-## Process
-
-### 1. Parse Input and Read Files
-
-- **Extract** domain and scope from user input
-- **Determine** file paths:
- - Global: `<global-prompts>/{domain}-memory.instructions.md` → `<global-prompts>/{domain}.instructions.md`
- - Workspace: `<workspace-instructions>/{domain}-memory.instructions.md` → `<workspace-instructions>/{domain}.instructions.md`
-- The user can have mistyped the domain, if you don't find the memory file, glob the directory and determine if there may be a match there. Ask the user for input if in doubt.
-- **Read** both files (memory file must exist; instruction file may not)
-
-### 2. Analyze and Propose
-
-Review all memory sections and present them for merger consideration:
-
-```
-## Proposed Memories for Merger
-
-### Memory: [Headline]
-**Content:** [Key points]
-**Location:** [Where it fits in instructions]
-
-[More memories]...
-```
-
-Say: "Please review these memories. Approve all with 'go' or specify which to skip."
-
-**STOP and wait for user input.**
-
-### 3. Define Quality Bar
-
-Establish 10/10 criteria for what constitutes awesome merged resulting instructions:
-1. **Zero knowledge loss** - Every detail, example, and nuance preserved
-2. **Minimal redundancy** - Overlapping guidance consolidated
-3. **Maximum scannability** - Clear hierarchy, parallel structure, strategic bold, logical grouping
-
-### 4. Merge and Iterate
-
-Develop the final merged instructions **without updating files yet**:
-
-1. Draft the merged instructions incorporating approved memories
-2. Evaluate against quality bar
-3. Refine structure, wording, organization
-4. Repeat until the merged instructions meet 10/10 criteria
-
-### 5. Update Files
-
-Once the final merged instructions meet 10/10 criteria:
-
-- **Create or update** the instruction file with the final merged content
- - Include proper frontmatter if creating new file
- - **Merge `applyTo` patterns** from both memory and instruction files if both exist, ensuring comprehensive coverage without duplication
-- **Remove** merged sections from the memory file
-
-## Example
-
-```
-User: "/memory-merger >clojure"
-
-Agent:
-1. Reads clojure-memory.instructions.md and clojure.instructions.md
-2. Proposes 3 memories for merger
-3. [STOPS]
-
-User: "go"
-
-Agent:
-4. Defines quality bar for 10/10
-5. Merges new instructions candidate, iterates to 10/10
-6. Updates clojure.instructions.md
-7. Cleans clojure-memory.instructions.md
-```
--
Gitblit v1.9.1