97 lines
3.8 KiB
Python
97 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
memory-governance.py — 记忆治理脚本(2026-08-12)
|
||
触发:每周日 03:00 cron(memory-governance cron)
|
||
功能:
|
||
1. 织忆低价值记忆标记(7天未recall + 重要性<0.3 → tombstone 候选)
|
||
2. 织忆去重(复用 /api/v1/consolidate/memory P2)
|
||
3. 生成治理报告(清理了什么,透明可审计)
|
||
4. 推送飞书报告
|
||
设计原则:只标记/报告,不硬删(防止误删重要记忆);MEMORY.md 压缩由会话内批量操作完成。
|
||
"""
|
||
import json
|
||
import urllib.request
|
||
import subprocess
|
||
import datetime
|
||
import os
|
||
import sys
|
||
|
||
ZHIYI_URL = "http://localhost:7821"
|
||
ZHIYI_KEY = "zhiyi-dev-key-2026"
|
||
FEISHU_HOME = "oc_81f6df701c872a1122f32080e366543f" # AI创业核心群
|
||
|
||
def api_get(path):
|
||
req = urllib.request.Request(
|
||
f"{ZHIYI_URL}{path}",
|
||
headers={"X-API-Key": ZHIYI_KEY},
|
||
)
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||
return json.loads(resp.read())
|
||
except Exception as e:
|
||
return {"error": str(e)}
|
||
|
||
def api_post(path, body):
|
||
data = json.dumps(body).encode()
|
||
req = urllib.request.Request(
|
||
f"{ZHIYI_URL}{path}",
|
||
data=data,
|
||
headers={"X-API-Key": ZHIYI_KEY, "Content-Type": "application/json"},
|
||
)
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||
return json.loads(resp.read())
|
||
except Exception as e:
|
||
return {"error": str(e)}
|
||
|
||
def main():
|
||
report = []
|
||
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
|
||
|
||
# 1. 织忆健康画像
|
||
stats = api_get("/api/v1/stats")
|
||
if not isinstance(stats, dict) or "error" in stats:
|
||
report.append(f"❌ 织忆 stats 拉取失败: {stats if isinstance(stats, str) else stats.get('error')}")
|
||
else:
|
||
total = stats.get("total_memories", "?")
|
||
report.append(f"📊 织忆现状: {total} 条记忆")
|
||
|
||
# 2. 离线整合(去重 + 更新),limit 控制成本
|
||
cons = api_post("/api/v1/consolidate/memory", {"namespace": "hermes-main", "limit": 10})
|
||
if not isinstance(cons, dict) or "error" in cons:
|
||
report.append(f"⚠️ consolidate 异常: {cons if isinstance(cons, str) else cons.get('error')}")
|
||
else:
|
||
report.append(f"🧹 去重整合: processed={cons.get('processed', 0)} updated={cons.get('updated', 0)} deleted={cons.get('deleted', 0)}")
|
||
|
||
# 3. 淘汰候选(7天未recall + 低重要性)— 通过 recall 抽样判断
|
||
# 简化策略:拉最近记忆,标记低价值候选(数据层支持时启用)
|
||
try:
|
||
# 尝试拉记忆列表(如果 API 支持)
|
||
mems = api_get("/api/v1/memories?limit=100")
|
||
if isinstance(mems, dict) and "items" in mems:
|
||
candidates = []
|
||
for m in mems.get("items", []):
|
||
imp = m.get("computed_importance", 1.0)
|
||
recall = m.get("recall_count", 0)
|
||
if imp < 0.3 and recall == 0:
|
||
candidates.append(m.get("id", "")[:12])
|
||
if candidates:
|
||
report.append(f"🗑️ 淘汰候选(低价值未recall): {len(candidates)} 条 -> {candidates[:5]}")
|
||
else:
|
||
report.append("🗑️ 淘汰候选: 0 条(暂无低价值记忆)")
|
||
else:
|
||
report.append("🗑️ 淘汰候选: API 不支持列表查询,跳过(织忆自动管理)")
|
||
except Exception as e:
|
||
report.append(f"🗑️ 淘汰候选跳过: {e}")
|
||
|
||
# 4. 输出报告
|
||
body = "\n".join(report)
|
||
print(f"🧠 记忆治理报告 {now}\n" + body)
|
||
|
||
# 5. 推送飞书(通过 hermes send_message 或直接脚本)
|
||
# 用 feishu webhook 简单推送(如果配置了)或由 cron 自动投递
|
||
# cron no_agent 模式下 stdout 即投递内容
|
||
|
||
if __name__ == "__main__":
|
||
main()
|