ARTICLE / 开源项目
补充AIDE自进化评估特性的研究
PR 地址:NousResearch/hermes-agent#77236 · AIDE² 式三循环自进化引擎:信号检测 → 账本记录 → LLM 验证 → LLM 变异
研究摘要
Hermes 的技能库靠人维护:技能写得不好、用户反复纠正、流程反复返工,这些信号散落在会话里,技能文件本身却纹丝不动。本 PR 为 Hermes 实现了 AIDE² 风格的自改进引擎(+8107 行,30 个文件):从交互中检测"用户纠正、返工、复用"三类信号并记账,LLM 据此提出技能改进提案,经真实执行评估与盲审打分后,把通过的改进原子地写回 SKILL.md——带备份与回滚。5 个阶段、8 个 commit、64 个测试全过、CI 全绿。
这是一个默认关闭的实验性能力:不配置就不运行,普通用户感知不到任何变化。引擎全链路在 7 个自测 bug 修复后才达到可合入状态,其中最关键的一个是"验证被架空"——提案应用阶段重新调用变异器而不是使用已验证的内容,导致验证环节形同虚设。这个特性把"技能会自己变好"从口号变成了可审计、可回滚的机制。
一、问题背景:技能改进的信号一直在产生,却从未被利用
1.1 技能质量的三个信号
Hermes 运行中,技能质量的负面信号其实每时每刻都在产生:
- 用户纠正(correction):用户对技能产出不满意,当场纠正——这是最直接的质量反馈;
- 返工(rework):同一任务在短时间内反复折腾,说明技能流程有问题;
- 死胡同(dead-end):技能把用户带进走不通的路径。
这些信号分布在会话记录里,而技能的改进依赖人工发现与手动编辑 SKILL.md。
1.2 三个缺失
一是信号没有收集器:没有人把"这次被纠正了"记下来;二是改进没有评估器:即使有人改了技能,也没有客观手段验证改完是否真的更好;三是应用没有保险:技能文件被改坏后没有可靠的恢复机制。结果是技能库的质量完全依赖维护者的勤勉。
二、特性设计:三循环引擎与五个阶段
2.1 总体架构(3 个循环)
引擎由三个循环组成:信号检测循环(发现并记录问题信号)、LLM 驱动验证循环(提出改进并用真实执行评估)、永久应用循环(把验证过的改进原子写回技能文件)。
2.2 五个阶段的落地
Phase 1 — Aide2Metrics 指标结构。新增指标数据类,镜像协议定义,提供统一的指标转换入口,为后续阶段提供数据结构底座。
Phase 2 — 信号检测与经验账本。SkillEvalProducer.record_turn() 在每一轮交互中检测三类信号:用户纠正检测器(覆盖 5 种语言的正则匹配)、返工检测器(10 分钟滑动窗口内反复执行同一技能)、复用追踪器(技能复用与结果的映射)。record_batch() 批量落账;ExperienceLedger.record_eval() 把结构化评估记录写入经验账本,并附私有质量评分估计。私有评分的计算方式是把公开评分按信号加权扣减:max(0, public - 0.4×纠正 - 0.15×返工 - 0.2×复用失败)——被纠正得越狠,私有评分越低,越值得改进。
Phase 3 — 执行评估与盲审裁判。EvalRunner.execute_prompt() 在辅助客户端上真实执行技能代码,执行前用 12 个危险 token 模式阻断注入向量;LLMJudge.judge() 对执行结果打分,评分提示词只含正确性、效率、清晰度这类通用质量维度,不含私有评分标准——裁判不知道内部评估口径,防止引擎"教裁判作弊"。
Phase 4 — 技能变异与应用。SkillMuter.mutate() 调用 LLM 重写 SKILL.md;DefaultSkillMuterApplier.apply() 以"临时文件 + 原子替换"的方式写回,每次应用生成带时间戳的唯一备份,成功后删除备份;rollback() 带 mtime 守卫——当前文件比备份新时拒绝恢复,防止陈旧备份覆盖人工编辑。
Phase 5 — 引擎主循环。HermesSquaredEngine 把四个环节串成循环:_run_cycle() → _generate_proposals()(生成改进提案)→ _validate_proposal()(真实执行 + 盲审)→ _apply_proposal()(原子写回)。提案队列支持可配置并行度;并发验证用 return_exceptions=True 隔离单个失败,不让一个坏提案拖垮整轮循环。
2.3 七个自测 bug 修复:从"看似能用"到"真的能用"
引擎在自测中暴露并修复了 7 个 bug,其中 4 个直接决定引擎可信度:
| 级别 | 问题 | 修复 |
|---|---|---|
| CRITICAL | _apply_proposal 重新调用变异器而不是用已验证的内容,验证沦为空操作 | 提案增加 new_content 字段,验证阶段缓存 LLM 输出,应用阶段直接使用缓存 |
| HIGH | 多个应用共用同一个备份路径,并发写互相覆盖 | 备份名带时间戳 + PID + 序号 |
| HIGH | 应用成功后不删备份,人工编辑后回滚会恢复陈旧备份 | 成功后删除备份,回滚校验 mtime |
| HIGH | 账本读写无锁,并发写损坏 JSON;读取时还错读了锁文件 | fcntl.flock 独占锁 + 临时文件原子替换,读用共享锁,修正路径混淆 |
| HIGH | 解析变异响应时 MULTILINE $ 在 <reasoning> 标签前误匹配,静默丢弃正文 | 改用 re.DOTALL,纯推理响应要求匹配到内容末尾 |
| HIGH | 单行前言(如"以下是新的 SKILL.md")无换行无标题却通过内容校验 | 无换行且无标题/围栏标记的响应按空内容处理 |
| MEDIUM | isinstance(raw_score, int) 把布尔值 True 当作合法分数 | 改为 type(raw_score) is int |
前两个 bug 尤其值得注意:验证与应用脱节意味着"验证通过"是假象;备份互相覆盖意味着回滚保护本身不可靠。这两个问题不修,自进化引擎就是一台会自己损坏技能库的机器。
三、实测结果
3.1 测试套件
| 测试组 | 结果 |
|---|---|
tests/agent/test_aide2_phase5.py | 11 passed |
tests/agent/test_skill_muter.py | 19 passed |
tests/agent/test_llm_judge.py | 17 passed |
tests/agent/test_experience_ledger.py | 17 passed |
| 合计 | 64 passed, 0 failed |
3.2 工程检查
- ruff check 干净、ruff format 已应用;
- Windows footgun 扫描 0 发现(
fcntl属 POSIX 限定,锁定路径优雅降级); - CI 各测试切片全部通过,PR 状态 MERGEABLE。
四、平台兼容性与能力边界
| 平台 | 兼容性 |
|---|---|
| macOS | 全兼容(纯 Python,fcntl 可用) |
| Linux | 全兼容 |
| Windows | 部分兼容——fcntl 仅 POSIX 可用,锁定路径降级,非主目标平台 |
能力边界与已知约束:
- 默认关闭,配置启用:引擎在未配置时完全休眠,需要按文档配置信号检测、账本记录、LLM 验证与变异环节后才会运行;
- LLM 变异是核心依赖:技能重写质量取决于所用 LLM,低质量模型可能产出低质量改进,盲审环节只能过滤明显的劣化;
- 危险 token 阻断是防御不是保险:12 个正则模式覆盖常见注入向量,极端输入仍需审计;
- Windows 上并发写保护降级:无 fcntl 时锁定能力受限,多进程并发写场景下建议在 POSIX 主机运行引擎;
- 信号检测基于启发式:5 语言正则、滑动窗口等规则对非常规表达可能漏检或误检;
- PR 状态为 open,64 测试与 CI 结果均为开发记录,未含长期运行下"引擎持续改进技能"的纵向数据。
五、PR 信息
- PR 地址:https://github.com/NousResearch/hermes-agent/pull/77236
- 改动规模:+8107 / -0,30 个文件
- PR 状态:open(5 阶段 + 7 bug 修复,8 个 commit,MERGEABLE)
- 提交时间:2026-08-03
本文记录 x7peeps 向 Hermes Agent 上游贡献的特性研究,所有数据来自 PR 实测记录。