#!/usr/bin/env python3 """ 自进化管线 — 定期运行,检查差距,执行升级,验证回滚 运行流程: 1. 快照当前配置 2. 检查差距(模型健康、技能使用、系统资源) 3. 生成升级提案 4. 逐个执行(带快照+验证) 5. 标记稳定 由每日复盘后面的 cron 触发 """ import json import os import subprocess import time from datetime import datetime HOME = os.path.expanduser("~") HERMES = os.path.join(HOME, ".hermes") SCRIPTS = os.path.join(HERMES, "scripts") WATCHDOG = os.path.join(HERMES, "watchdog") LOG_FILE = os.path.join(WATCHDOG, "self-evolve.log") def log(msg): ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") line = f"[{ts}] {msg}" print(line) os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True) with open(LOG_FILE, "a") as f: f.write(line + "\n") def shell(cmd, timeout=30): try: r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout) return r.returncode, r.stdout.strip(), r.stderr.strip() except subprocess.TimeoutExpired: return -1, "", "timeout" def snapshot(): """执行配置快照""" rc, out, err = shell(f"bash {SCRIPTS}/config-protector.sh snapshot") return rc == 0 def mark_stable(): """标记当前为稳定""" rc, out, err = shell(f"bash {SCRIPTS}/config-protector.sh mark-stable") return rc == 0 # === 差距检查 === def check_model_health(): """检查模型健康报告""" path = os.path.join(HERMES, "model-health.json") if not os.path.exists(path): return {"status": "no_report", "recommendations": []} with open(path) as f: data = json.load(f) stable = data.get("stable", 0) total = data.get("total_models", 0) fastest = data.get("fastest_stable", []) issues = [] if stable < 2: issues.append("可用模型不足,需要扩大测试范围") if total > 0 and stable / total < 0.2: issues.append(f"模型稳定率过低 ({stable}/{total})") return { "status": "ok" if not issues else "warning", "stable": stable, "total": total, "fastest": fastest, "issues": issues, } def check_skills_health(): """检查技能库状态""" skills_dir = os.path.join(HERMES, "skills") count = 0 categories = set() for root, dirs, files in os.walk(skills_dir): if "SKILL.md" in files: count += 1 cat = root.split("/")[-2] if "/" in root else "uncategorized" categories.add(cat) return { "total_skills": count, "categories": sorted(categories), } def check_disk(): """检查磁盘空间""" rc, out, err = shell("df / | awk 'NR==2 {print $5}' | sed 's/%//'") pct = int(out) if out else 0 return {"disk_pct": pct, "critical": pct > 90} def check_gaps(): """综合差距分析""" health = check_model_health() skills = check_skills_health() disk = check_disk() gaps = [] upgrades = [] # 模型层 if health.get("issues"): for issue in health["issues"]: gaps.append(f"模型: {issue}") upgrades.append({ "id": "expand-model-test", "priority": "medium", "desc": "扩大模型测试范围(增加测试队列中的模型数)", "action": '修改 model-health.py 的 MODELS 列表,增加候选模型', "verify": "下一轮巡检后至少有5个稳定模型", "rollback": "git checkout MODELS 原始列表", }) # 磁盘层 if disk["disk_pct"] > 85: gaps.append(f"磁盘: 使用率 {disk['disk_pct']}%") upgrades.append({ "id": "disk-cleanup", "priority": "high", "desc": "清理磁盘空间", "action": "apt autoremove && pip cache purge && npm cache clean", "verify": "磁盘使用率下降>5%", "rollback": "N/A(清理不可逆)", }) # 能力缺口(检查关键工具是否缺失) missing_tools = [] for tool, cmd in [("ComfyUI", "which comfy"), ("llama.cpp", "which llama-cli"), ("frpc", "which frpc")]: rc, _, _ = shell(f"which {tool.split('/')[-1]} 2>/dev/null || command -v {cmd.split()[0]} 2>/dev/null") if rc != 0: missing_tools.append(tool) if missing_tools: upgrades.append({ "id": "install-tools", "priority": "low", "desc": f"安装缺失工具: {', '.join(missing_tools)}", "action": f"按需安装 {', '.join(missing_tools)}", "verify": "工具可用", "rollback": "卸载已安装的工具", }) return { "gaps": gaps, "upgrades": upgrades, "health": health, "skills": skills, "disk": disk, } # === 执行升级 === def execute_upgrade(upgrade): """执行单个升级项,带快照+验证""" log(f"🔧 执行升级: {upgrade['id']} — {upgrade['desc']}") # 1. 快照 if not snapshot(): log(" ❌ 快照失败,中止升级") return False # 2. 执行 log(f" 📋 动作: {upgrade['action']}") # 根据不同 upgrade id 执行不同动作 success = True if upgrade["id"] == "disk-cleanup": for cmd in ["apt-get autoremove -y", "pip cache purge", "npm cache clean --force"]: rc, out, err = shell(f"sudo {cmd}" if cmd.startswith("apt") else cmd, timeout=120) if rc != 0 and "cache" not in cmd: log(f" ⚠️ 命令失败: {cmd} ({err})") elif upgrade["id"] == "expand-model-test": log(" ℹ️ 需人工或下次 session 修改 model-health.py") success = True # 不阻塞管线 elif upgrade["id"] == "install-tools": log(" ℹ️ 需人工决策安装哪些工具") success = True # 3. 验证 if success: mark_stable() log(f" ✅ 升级完成,已标记稳定") else: log(f" ❌ 升级失败,需要人工介入") return success # === 主流程 === def main(): log(f"\n{'='*50}") log(f"自进化管线启动") log(f"{'='*50}") # 差距分析 log("\n📊 差距分析...") report = check_gaps() log(f" 技能: {report['skills']['total_skills']} 个 ({len(report['skills']['categories'])} 类)") log(f" 模型: {report['health'].get('stable', 'N/A')}/{report['health'].get('total', 'N/A')} 稳定") log(f" 磁盘: {report['disk']['disk_pct']}%") if report["gaps"]: log(f"\n⚠️ 发现 {len(report['gaps'])} 个差距:") for g in report["gaps"]: log(f" - {g}") else: log("\n✅ 无差距") if report["upgrades"]: log(f"\n🔧 建议 {len(report['upgrades'])} 个升级:") for u in report["upgrades"]: log(f" [{u['priority']}] {u['id']}: {u['desc']}") # 执行高优先级升级 high = [u for u in report["upgrades"] if u["priority"] == "high"] for u in high: execute_upgrade(u) else: log("\n✅ 无需升级") # 输出摘要供 cron 交付 log(f"\n{'='*50}") log(f"自进化管线完成") log(f"差距: {len(report['gaps'])} | 升级: {len(report['upgrades'])}") if report["gaps"]: print(f"\n⚠️ 差距:") for g in report["gaps"]: print(f" {g}") if report["upgrades"]: print(f"\n🔧 升级执行:") for u in report["upgrades"]: print(f" [{u['priority']}] {u['desc']}") if __name__ == "__main__": main()