feat: 自愈循环系统 + xiaoguo信号桥
- 新增 docs/self-healing-loop.md: 自愈循环设计文档 - 修改 morning_health_check.py: 发现异常后自动写入TODO系统 - severity=critical→priority=high, error→medium, warn→low - 相同问题已completed→重新打开并升一级 - 已有pending→不重复 - 新增 scripts/xiaoguo_signal_consumer.py: 知微消费小果扫描信号 - 每30分钟盘中运行 - 读signal_news未处理xiaoguo信号 - 五维快速评估→加自选/关注/跳过 - 标记processed=1 - 新增 cron: 小果信号消费-盘中 (15,45 9-15) - 更新health_checklist自维护机制 自愈循环: 体检→TODO→修复→验证
This commit is contained in:
@@ -37,6 +37,67 @@ CHECKLIST_PATH = DATA / "health_checklist.json"
|
||||
HISTORY_PATH = DATA / "health_check_history.json"
|
||||
DB_PATH = DATA / "mofin.db"
|
||||
HERMES_CRON_DIR = Path("/home/hmo/.hermes/profiles/position-analyst/cron")
|
||||
TODO_PATH = Path("/home/hmo/.hermes/profiles/position-analyst/todo.json")
|
||||
|
||||
|
||||
def write_todos_for_issues():
|
||||
"""将体检发现的异常写入 TODO 系统(去重、升级)"""
|
||||
try:
|
||||
if not ctx["report"]:
|
||||
return
|
||||
|
||||
# 只有 error/critical/warn 才写 TODO
|
||||
issues = [e for e in ctx["report"] if e["level"] in ("critical", "error", "warn")]
|
||||
if not issues:
|
||||
return
|
||||
|
||||
# 读现有 TODO
|
||||
existing = []
|
||||
if TODO_PATH.exists():
|
||||
try:
|
||||
existing = json.loads(TODO_PATH.read_text())
|
||||
except:
|
||||
existing = []
|
||||
|
||||
existing_titles = {t.get("title", "") for t in existing}
|
||||
todo_priority = {"critical": "high", "error": "medium", "warn": "low"}
|
||||
|
||||
new_items = []
|
||||
for issue in issues:
|
||||
title = f"[体检发现] {issue['msg']}"
|
||||
# 去重:检查是否已有相同 title 的 TODO
|
||||
if title in existing_titles:
|
||||
# 已有相同 TODO -> 升级
|
||||
for t in existing:
|
||||
if t.get("title") == title:
|
||||
if t.get("status") == "completed":
|
||||
# 昨天修了但今天还有问题 -> 重新打开并升级
|
||||
t["status"] = "pending"
|
||||
t["priority"] = todo_priority.get(issue["level"], "medium")
|
||||
t["note"] = f"重新打开: {ctx['started_at'].isoformat()}"
|
||||
elif t.get("status") == "pending":
|
||||
# 还是 pending -> 不重复写入
|
||||
pass
|
||||
elif t.get("status") == "in_progress":
|
||||
# 正在处理中 -> 不干扰
|
||||
pass
|
||||
continue
|
||||
|
||||
existing_titles.add(title)
|
||||
new_items.append({
|
||||
"title": title,
|
||||
"desc": f"体检发现于 {ctx['started_at'].strftime('%Y-%m-%d %H:%M')},分类: {issue['category']},详情: {issue.get('detail', '')}",
|
||||
"status": "pending",
|
||||
"priority": todo_priority.get(issue["level"], "medium"),
|
||||
"created": ctx["started_at"].isoformat(),
|
||||
"target": "health_check_fix",
|
||||
})
|
||||
|
||||
if new_items:
|
||||
existing.extend(new_items)
|
||||
TODO_PATH.write_text(json.dumps(existing, ensure_ascii=False, indent=2))
|
||||
except Exception as e:
|
||||
pass # TODO 写入失败不阻碍体检主流程
|
||||
|
||||
# 异常缓存(同一问题24h内不重复推)
|
||||
KNOWN_ISSUES_PATH = DATA / "health_known_issues.json"
|
||||
@@ -519,6 +580,9 @@ def main():
|
||||
for entry in ctx["report"]:
|
||||
if entry["level"] in ("critical", "error"):
|
||||
print(f" [{entry['level'].upper()}] {entry['category']}: {entry['msg']}")
|
||||
|
||||
# 将异常写入 TODO 系统
|
||||
write_todos_for_issues()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user