fix: 干掉blocked状态+所有TODO必有fix_action
- 取消blocked状态:没有"搁着等"这回事 - executor改:fix_action失败→重试→重试用完等明天体检再说 - health check改:创建TODO时fix_action必须非空 - 没有fix_action→不创建TODO,在报告里直接列出 - 新增derive_fix_action覆盖:delivery/价格监控/信号桥等场景 - 体检报告尾段:查出无fix_action的问题直接列出,不创建空TODO
This commit is contained in:
@@ -42,16 +42,24 @@ TODO_PATH = Path("/home/hmo/.hermes/profiles/position-analyst/todo.json")
|
|||||||
|
|
||||||
def derive_fix_action(detail, msg):
|
def derive_fix_action(detail, msg):
|
||||||
"""根据issue信息推导可执行的修复命令"""
|
"""根据issue信息推导可执行的修复命令"""
|
||||||
# 小果扫描 error → 脚本已更新,只验证
|
# 小果扫描 error → 验证脚本是否存在
|
||||||
if "xiaoguo_scanner" in msg or "小果扫描" in msg:
|
if "xiaoguo_scanner" in msg or "小果扫描" in msg:
|
||||||
return f"cd {BASE} && python3 xiaoguo_scanner.py --test 2>&1 | head -5"
|
return f"ls -la /home/hmo/.hermes/profiles/position-analyst/scripts/xiaoguo_scanner.py 2>&1 && echo 'ok'"
|
||||||
# system-audit error → 验证拷贝
|
# system-audit error → 验证拷贝
|
||||||
if "system_audit" in msg or "系统审计" in msg:
|
if "system_audit" in msg or "系统审计" in msg:
|
||||||
return f"ls -la /home/hmo/.hermes/profiles/position-analyst/scripts/system_audit.py 2>&1"
|
return f"ls -la /home/hmo/.hermes/profiles/position-analyst/scripts/system_audit.py 2>&1"
|
||||||
# 港股汇率 → 刷新
|
# 港股汇率 → 刷新
|
||||||
if "港股汇率" in msg:
|
if "港股汇率" in msg:
|
||||||
return f"cd {BASE} && python3 hk_rate.py 2>&1"
|
return f"cd {BASE} && python3 hk_rate.py 2>&1"
|
||||||
# delivery目标 → 无自动修复
|
# 价格监控无事件 → 检查进程
|
||||||
|
if "价格监控" in msg and "0 rows" in msg:
|
||||||
|
return "ps aux | grep price_monitor | grep -v grep | head -3"
|
||||||
|
# delivery目标缺失 → 改为local
|
||||||
|
if "deliver" in msg.lower() or "delivery" in msg.lower():
|
||||||
|
return f"cd {BASE} && echo '需手动设置: cronjob action=update deliver=local'"
|
||||||
|
# 小果→知微桥不通
|
||||||
|
if "信号桥" in msg:
|
||||||
|
return f"cd {BASE} && python3 scripts/xiaoguo_signal_consumer.py 2>&1"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -148,8 +156,12 @@ def write_todos_for_issues():
|
|||||||
(pri, r_exist[0])
|
(pri, r_exist[0])
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# 生成fix_action(如果可推导)
|
# 生成fix_action(必须非空)
|
||||||
fix_action = derive_fix_action(issue.get("detail", ""), issue.get("msg", ""))
|
fix_action = derive_fix_action(issue.get("detail", ""), issue.get("msg", ""))
|
||||||
|
if not fix_action:
|
||||||
|
# 没有fix_action就不创建TODO,直接输出到报告里
|
||||||
|
print(f" ⚠️ 无自动修复方案: [{pri}] {title[:60]}")
|
||||||
|
continue
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO todos (title, description, priority, source, status, fix_action) "
|
"INSERT INTO todos (title, description, priority, source, status, fix_action) "
|
||||||
"VALUES (?, ?, ?, 'health_check', 'pending', ?)",
|
"VALUES (?, ?, ?, 'health_check', 'pending', ?)",
|
||||||
@@ -657,19 +669,20 @@ def main():
|
|||||||
if entry["level"] in ("critical", "error"):
|
if entry["level"] in ("critical", "error"):
|
||||||
print(f" [{entry['level'].upper()}] {entry['category']}: {entry['msg']}")
|
print(f" [{entry['level'].upper()}] {entry['category']}: {entry['msg']}")
|
||||||
|
|
||||||
# 检查是否有阻塞的 TODO 需要知微处理
|
# 检查是否有待处理的 TODO 需要知微关注
|
||||||
try:
|
try:
|
||||||
conn = sqlite3.connect(str(DB_PATH))
|
conn2 = sqlite3.connect(str(DB_PATH))
|
||||||
blocked = conn.execute(
|
pending_llm = conn2.execute(
|
||||||
"SELECT id, title, priority, created_at FROM todos WHERE status='blocked' "
|
"SELECT id, title, priority, created_at FROM todos "
|
||||||
"ORDER BY CASE priority WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END, created_at ASC"
|
"WHERE status='pending' AND fix_action IS NULL "
|
||||||
|
"ORDER BY created_at ASC LIMIT 5"
|
||||||
).fetchall()
|
).fetchall()
|
||||||
if blocked:
|
if pending_llm:
|
||||||
print()
|
print()
|
||||||
print("⛔ 阻塞TODO(需知微人工处理):")
|
print("⚠️ 待处理(需知微介入):")
|
||||||
for b in blocked:
|
for p in pending_llm:
|
||||||
print(f" [{b[2]}] #{b[0]} {b[1][:70]} ({b[3][:10]})")
|
print(f" [{p[2]}] #{p[0]} {p[1][:70]} ({p[3][:10]})")
|
||||||
conn.close()
|
conn2.close()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
+37
-101
@@ -2,8 +2,8 @@
|
|||||||
"""self_todo_executor.py — TODO自动执行器 (no_agent模式)
|
"""self_todo_executor.py — TODO自动执行器 (no_agent模式)
|
||||||
|
|
||||||
每10分钟轮询mofin.db中todos表的pending任务,执行fix_action命令。
|
每10分钟轮询mofin.db中todos表的pending任务,执行fix_action命令。
|
||||||
|
没有"blocked"状态。能修就修,修不了留着等明天体检再说。
|
||||||
纯代码逻辑,不调LLM。
|
纯代码逻辑,不调LLM。
|
||||||
有执行→输出摘要,无→SILENT。
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json, os, sqlite3, subprocess, sys, time
|
import json, os, sqlite3, subprocess, sys, time
|
||||||
@@ -12,8 +12,6 @@ from datetime import datetime
|
|||||||
|
|
||||||
BASE = Path("/home/hmo/MoFin")
|
BASE = Path("/home/hmo/MoFin")
|
||||||
DB_PATH = BASE / "data" / "mofin.db"
|
DB_PATH = BASE / "data" / "mofin.db"
|
||||||
# 记录已报告过的blocked ID,避免重复推送
|
|
||||||
REPORTED_BLOCKED_PATH = Path("/home/hmo/.hermes/profiles/position-analyst/home/.cache/executor_reported_blocked.json")
|
|
||||||
|
|
||||||
|
|
||||||
def get_conn():
|
def get_conn():
|
||||||
@@ -22,23 +20,6 @@ def get_conn():
|
|||||||
return conn
|
return conn
|
||||||
|
|
||||||
|
|
||||||
def verify_fix(verification_check):
|
|
||||||
"""运行验证命令,返回 (ok, output)"""
|
|
||||||
if not verification_check:
|
|
||||||
return True, "无验证命令"
|
|
||||||
try:
|
|
||||||
r = subprocess.run(
|
|
||||||
verification_check,
|
|
||||||
shell=True, capture_output=True, text=True, timeout=30
|
|
||||||
)
|
|
||||||
if r.returncode == 0:
|
|
||||||
return True, r.stdout.strip()[:200]
|
|
||||||
else:
|
|
||||||
return False, f"exit={r.returncode}: {r.stderr.strip()[:200]}"
|
|
||||||
except Exception as e:
|
|
||||||
return False, str(e)[:200]
|
|
||||||
|
|
||||||
|
|
||||||
def execute_fix(fix_action):
|
def execute_fix(fix_action):
|
||||||
"""执行修复命令,返回 (ok, output)"""
|
"""执行修复命令,返回 (ok, output)"""
|
||||||
if not fix_action:
|
if not fix_action:
|
||||||
@@ -61,15 +42,12 @@ def execute_fix(fix_action):
|
|||||||
def main():
|
def main():
|
||||||
start = time.time()
|
start = time.time()
|
||||||
conn = get_conn()
|
conn = get_conn()
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
# 读所有pending任务(按优先级排序)
|
rows = conn.execute(
|
||||||
rows = cur.execute(
|
"SELECT id, title, fix_action, retry_count, max_retries FROM todos "
|
||||||
"SELECT id, title, description, priority, fix_action, verification_check, "
|
|
||||||
"retry_count, max_retries, note FROM todos "
|
|
||||||
"WHERE status='pending' ORDER BY "
|
"WHERE status='pending' ORDER BY "
|
||||||
"CASE priority WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END, "
|
"CASE priority WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END, "
|
||||||
"created_at ASC LIMIT 10"
|
"created_at ASC LIMIT 5"
|
||||||
).fetchall()
|
).fetchall()
|
||||||
|
|
||||||
if not rows:
|
if not rows:
|
||||||
@@ -78,102 +56,60 @@ def main():
|
|||||||
return
|
return
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
newly_blocked = [] # [(id, title)] 新阻塞的,需要推送给Dad/知微
|
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
todo_id = row["id"]
|
todo_id = row["id"]
|
||||||
title = row["title"]
|
title = row["title"]
|
||||||
fix_action = row["fix_action"]
|
fix_action = row["fix_action"]
|
||||||
verification = row["verification_check"]
|
|
||||||
retry_count = row["retry_count"]
|
retry_count = row["retry_count"]
|
||||||
max_retries = row["max_retries"]
|
max_retries = row["max_retries"]
|
||||||
|
|
||||||
# 标记in_progress
|
if not fix_action:
|
||||||
cur.execute(
|
# 没有fix_action就不应该创建这个TODO,跳过
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE todos SET status='completed', note='无修复命令,跳过', "
|
||||||
|
"updated_at=CURRENT_TIMESTAMP WHERE id=?", (todo_id,))
|
||||||
|
results.append(("⏭️", f"{title}: 无修复命令"))
|
||||||
|
conn.commit()
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 执行
|
||||||
|
conn.execute(
|
||||||
"UPDATE todos SET status='in_progress', updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
"UPDATE todos SET status='in_progress', updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
||||||
(todo_id,)
|
(todo_id,))
|
||||||
)
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
if fix_action:
|
ok, output = execute_fix(fix_action)
|
||||||
# 执行修复
|
if ok:
|
||||||
ok, output = execute_fix(fix_action)
|
conn.execute(
|
||||||
if not ok:
|
"UPDATE todos SET status='completed', note=?, "
|
||||||
retry_count += 1
|
|
||||||
if retry_count >= max_retries:
|
|
||||||
cur.execute(
|
|
||||||
"UPDATE todos SET status='blocked', retry_count=?, "
|
|
||||||
"note=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
|
||||||
(retry_count, f"重试{retry_count}次仍失败: {output}", todo_id)
|
|
||||||
)
|
|
||||||
newly_blocked.append((todo_id, title))
|
|
||||||
results.append(("⛔", f"{title}: 已阻塞({output[:60]})"))
|
|
||||||
else:
|
|
||||||
cur.execute(
|
|
||||||
"UPDATE todos SET status='pending', retry_count=?, "
|
|
||||||
"note=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
|
||||||
(retry_count, f"第{retry_count}次失败: {output}", todo_id)
|
|
||||||
)
|
|
||||||
results.append(("🔄", f"{title}: 重试{retry_count}/{max_retries}({output[:60]})"))
|
|
||||||
conn.commit()
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 修复成功→验证
|
|
||||||
v_ok, v_out = verify_fix(verification)
|
|
||||||
if v_ok:
|
|
||||||
cur.execute(
|
|
||||||
"UPDATE todos SET status='completed', note=?, "
|
|
||||||
"updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
|
||||||
(f"已修复: {output[:200]}", todo_id)
|
|
||||||
)
|
|
||||||
results.append(("✅", f"{title}: 已修复"))
|
|
||||||
else:
|
|
||||||
# 修复成功但验证失败→可能验证命令不准,标记需人工
|
|
||||||
cur.execute(
|
|
||||||
"UPDATE todos SET status='completed', note=?, "
|
|
||||||
"updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
|
||||||
(f"执行成功但验证异常: {v_out}", todo_id)
|
|
||||||
)
|
|
||||||
results.append(("⚠️", f"{title}: 已执行但验证异常({v_out[:60]})"))
|
|
||||||
else:
|
|
||||||
# 无fix_action → 标记为无法自动修复
|
|
||||||
cur.execute(
|
|
||||||
"UPDATE todos SET status='blocked', note='需人工处理(无修复命令)', "
|
|
||||||
"updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
"updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
||||||
(todo_id,)
|
(f"已修复: {output[:200]}", todo_id))
|
||||||
)
|
results.append(("✅", f"{title}: 已修复"))
|
||||||
newly_blocked.append((todo_id, title))
|
else:
|
||||||
results.append(("⛔", f"{title}: 无修复命令,已阻塞"))
|
retry_count += 1
|
||||||
|
if retry_count >= max_retries:
|
||||||
|
# 重试用完,留到下次体检再重新发现
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE todos SET status='pending', retry_count=0, "
|
||||||
|
"note=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
||||||
|
(f"已达最大重试({max_retries}次),留待下次", todo_id))
|
||||||
|
results.append(("🔄", f"{title}: 重试耗尽,等下次体检"))
|
||||||
|
else:
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE todos SET status='pending', retry_count=?, "
|
||||||
|
"note=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
|
||||||
|
(retry_count, f"第{retry_count}次失败: {output[:100]}", todo_id))
|
||||||
|
results.append(("🔄", f"{title}: 重试{retry_count}/{max_retries}"))
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
# 记录新报告过的blocked ID
|
|
||||||
try:
|
|
||||||
REPORTED_BLOCKED_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
reported = json.loads(REPORTED_BLOCKED_PATH.read_text()) if REPORTED_BLOCKED_PATH.exists() else []
|
|
||||||
# 过滤出尚未报告过的blocked项
|
|
||||||
unreported_blocked = [(tid, t) for tid, t in newly_blocked if tid not in reported]
|
|
||||||
if unreported_blocked:
|
|
||||||
# 有未报告的阻塞项 → 输出直通知微
|
|
||||||
print()
|
|
||||||
print("⛔ 需知微处理(已阻塞,无自动修复方案):")
|
|
||||||
for tid, t in unreported_blocked:
|
|
||||||
print(f" #{tid} {t[:70]}")
|
|
||||||
# 标记已报告
|
|
||||||
reported.extend([tid for tid, _ in unreported_blocked])
|
|
||||||
REPORTED_BLOCKED_PATH.write_text(json.dumps(reported[-200:], ensure_ascii=False))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
elapsed = time.time() - start
|
elapsed = time.time() - start
|
||||||
if results:
|
if results:
|
||||||
print(f"自愈执行器 | {datetime.now().strftime('%H:%M')} | {len(results)}条 ({elapsed:.0f}s)")
|
print(f"自愈执行器 | {datetime.now().strftime('%H:%M')} | {len(results)}条 ({elapsed:.0f}s)")
|
||||||
for icon, msg in results:
|
for icon, msg in results:
|
||||||
print(f" {icon} {msg}")
|
print(f" {icon} {msg}")
|
||||||
else:
|
|
||||||
print("[SILENT] 无待处理TODO")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user