§09造、修、续:自改进的三台引擎
Build, Repair, Continue: Three Engines of Self-Improvement
第一版书写的是「缰绳会自己长」。后来读源码,我能指着说这根缰绳是谁在长、长歪了谁来剪、剪错了怎么救。它从一句理念,变成了三台跑在后台、各管一摊的引擎。
先说第一版欠下的债
上一版Hermes橙皮书写学习循环的时候,对应版本是v0.7.0。我当时的措辞挺虚的:一个「会写记忆、会建skill的agent」。意思是它能自我进化,但你要问「具体哪个文件、哪个触发条件、跑在哪个线程」,我答不上来。那时候它本质上还是个理念,加一个被prompt鼓励着去存东西的主agent。
两个月后它跳了八个版本,到v0.16.0。我把源码逐行读了一遍,发现那套理念已经被工程化成了实打实的基础设施。到这一版的v0.22.2,骨架没变,细节又改了不少,下面遇到会单独标出来。最干净的拆法是三个动词:
造、修、续。
造修续
Background Review Curator Goals / Ralph Loop
造,是每轮对话后悄悄fork一个子agent去复盘,决定这次学到了什么、写进哪个skill。修,是一个每七天自己醒一次的维护者,给agent造出来的skill库做剪枝和归档(合并相似skill这一步现在默认是关的)。续,是当一个目标没完成时,agent给自己续命、连着多跑几轮,而不是答一句就停。
三台引擎跑在三种完全不同的时间尺度上。这一节先把三台机器各自的位置摆清楚,后面两节再单独深挖最有意思的两台。
一个能自我进化的东西,必须也能自我清理
在拆引擎之前,我想先给你一个看待这套机制的角度,不然容易把它当成一堆零散的后台任务。
会自己长skill的agent,有个绕不开的副作用:它解决一个新问题就想存一条经验,跑上几个月,攒出几十上百个又窄又重复的skill。每个都只对当时那一个bug有用,合在一起就是一团污染catalog、白白烧token的垃圾。
只会增不会减的系统,最后一定被自己撑死。
所以Hermes这套东西真正成立的地方,不在于它会造,而在于它造的同时配了一套清理。我读源码时脑子里冒出来的类比是生物的细胞,既要增殖,也要凋亡。增殖让组织生长,凋亡把老化、错误、用不上的细胞清掉。
一个只增不亡的组织有个名字,叫肿瘤。Hermes的background review负责增殖,Curator负责凋亡,这俩是一组对仗,配套出现的。
带着这个角度,再看三台引擎就顺了。
第一台·造:每轮对话后的后台复盘
这台引擎对应源码里的agent/background_review.py 。它的工作方式,文件头注释写得很直白:每一轮对话结束后,主agent可能会fork一个守护线程,把刚才那段对话的快照拿去重放一遍,然后问自己一句:这次有没有什么skill或记忆值得存下来、改一改?
关键是它跑在主回复发出去之后。v0.18之后它又省了一层钱:复盘改用辅助模型来做,看的是摘要过的上下文,频率也会根据情况自己调。源码注释专门解释了为什么:复盘绝不能和用户的任务抢模型的注意力。所以它是best-effort的,失败了就静默跳过,绝不拖垮主流程。你跟它对话时根本感觉不到背后有个分身在写笔记。
触发它的不是一个计数器,是两个,而且这俩错开了。这个细节我觉得特别能体现设计者想清楚了「知识有两种」。
| 计数器 | 按什么算 | 默认阈值 | 管的是哪种知识 |
|---|---|---|---|
| 记忆nudge | 对话轮数 | 每10轮 | 你是谁、当前在干什么 |
| skill nudge | 本轮工具迭代次数 | 每10次 | 这类活该怎么干 |
为什么记忆按「轮」、skill按「工具迭代次数」?我琢磨了一下,逻辑很微妙。一次复杂任务,对话上可能只来回一两句,但内部跑了几十次工具调用。这种活更可能沉淀出一条「怎么做这类事」的程序性知识,所以拿工具迭代数当尺子才合理。记忆是「你是谁」,skill是「怎么做事」,两种知识用两把尺子量。这俩阈值都能在配置里改(记忆那个是memory.nudge_interval ,默认还是10)。v0.22又放宽了一步:后台复盘现在可以修改任何一个skill,学到的东西会写进跟这个任务相关的那个skill里。
这台引擎里最值得引用的,是它复盘skill时用的那段prompt。我把核心几条挑出来,这几乎就是一份「AI该怎么给自己写规则」的工程方法论。
默认就该学:prompt原话是「Be ACTIVE,大多数session都该产出至少一次skill更新……一次什么都不做的复盘,是错过了学习机会,不是中性结果」。它把「不学」定义成失职,而不是默认安全选项。
更有意思的是它对学习信号的定义。Mitchell Hashimoto用Claude Code时有个习惯:agent每犯一个错,他就往CLAUDE.md里加一条规则。Hermes把这件事自动化了,而且把「犯错」具体成了用户的不爽语气,「stop doing X」「这太啰嗦了」「别这么排版」「你怎么又解释」「直接给答案」,这些挫败信号被它列为一等学习信号,要求当场编码成skill里的坑位或步骤。
它改skill还有个优先级梯度,不是一遇到事就新建:先改本轮刚加载过的那个skill,再改一个已有的大类,再往大类下面加子文件,实在没对应大类才新建。这条梯度就是为了防止skill无限膨胀,能在旧的上面补,就别造新的。新建的skill名字还必须是「类级」的,禁止用某个PR号、某串报错、某个今天才有的代号命名。源码原话很狠:如果这名字只对今天这个任务有意义,那它就是错的。
第二台·修:Curator,七天醒一次的维护者
第二台引擎是Curator,源码在agent/curator.py 。这玩意儿在旧书里压根不存在,它是v0.12.0才诞生的,那个版本官方直接命名为The Curator release,发布说明第一句就是:Hermes现在能自己维护自己了。
它干的事,正是上面说的「凋亡」那一半:给agent自创的skill库做剪枝、归档,纯维护,不学新东西。
background review在复盘时如果发现两个skill重叠,它不会自己去合并,只会「记一笔,交给curator大规模处理」。两台引擎的职责在源码里被划得很清。
Curator的触发节奏跟第一台完全不同。它不靠cron守护进程,靠的是idle检测:默认每7天最多跑一次,而且要等agent闲置满2小时才动手。还有一道很贴心的闸门:全新安装或刚update完,第一次tick它不会立刻乱动你的skill库,而是把「上次运行时间」种成「现在」,等满一整个周期才真跑。设计者的考虑是:刚更新完就大改用户的库,太唐突了。
它干活分两段,第一段完全不用大模型。一个纯函数状态机,按每个skill的「最后活动时间」机械地推进生命周期:
active stale archived
活跃
→ 14天没用→
陈旧
→ 30天没用→
归档陈旧的skill一旦被重新用到,会自动复活回active。被pin住的skill永远不参与自动转移。这一段不烧一分钱token,纯靠时间戳推。(这两个阈值在6月的v0.16还是30天和90天,后来收紧成了14天和30天,§06里hermes curator status的截图就是这个数。)第二段是大模型复盘,fork一个agent真正去判断哪些该合并。
但从v0.17开始,这第二段默认是关的,理由是省token;想打开要在配置里设curator.consolidate:
true ,或者手动跑hermes curator run --consolidate 。
Curator这台引擎细节最多、刹车做得最重,本书下一节专门拆它,包括它从「找重复」进化到「建伞」的哲学转向,以及那套永不删除、跑前打tar.gz快照、可以整轮回滚的安全设计。这里你先记住它在三台引擎里的位置:负责修剪的那一台。
第三台·续:Goals的Ralph Loop
前两台改的是agent跨会话的能力。第三台不一样,它改的是agent在一次会话里的坚持度。源码在hermes_cli/goals.py ,注释里管它叫「Hermes的Ralph loop」。
机制不复杂:一个goal是用户给的、跨多轮一直生效的目标。每一轮结束后,一个很小的judge调用会问辅助模型一句:这个目标,被刚才这条回复满足了吗?没满足的话,Hermes就把一段「继续干」的提示喂回同一个会话,接着跑,直到目标完成、轮次预算耗尽、用户喊停、或者用户发来一条新消息为止。
它有几个我挺欣赏的克制设计。续命用的提示就是一条普通的用户消息追加进去,不动system prompt、不换工具集,这样prefix cache不会失效,省钱。judge万一坏了,它选择fail-open,也就是「继续」,因为一个坏掉的判官不能把进度卡死,反正有轮次预算兜底。你随时发一条真实消息,就能抢占掉续命提示。
v0.18给这台引擎加了一个「完成契约」:你在设/goal的时候可以写清楚「什么叫做完」,judge按证据来判,而不是凭感觉说「看起来差不多了」。默认的轮次预算仍是20轮。
核心建议
前两台引擎让agent「越用越懂你」,第三台让它「盯着一个目标不松手」。造能力、修能力、续动力,三件事合起来,才是完整的「agent自己给自己造缰绳」。
三台引擎,一张全景
把三台机器并在一起看,差异一目了然。它们跑在三种时间尺度上,各管一件事,谁也不抢谁的活。
| 动 | ||||
|---|---|---|---|---|
| 引擎 | 触发节奏 | 干什么 | 跑在哪 | |
| 词 | ||||
| Background | 造 | 每对话10轮 / 10次工 | 决定这次学到什么→写记忆、 | 主回复后fork的后台线程 |
| Review | 具迭代 | 建改skill | ||
| Curator | 修 | 默认每7天 + idle 2小 | 剪枝、归档skill库(合并默认 | 独立fork的agent,用辅 |
| 时 | 关) | 助模型 | ||
| Goals / Ralph | 续 | 每轮结束后判一次 | 目标没完成就续命,连跑多轮 | 同一会话内,追加提示 |
| Loop |
这就是这条闭环最大的变化。第一版书写的是一个「应该会自我进化」的agent;到v0.16.0它已经是一套有触发器、有状态机、有边界、有刹车的工程系统。它从一个含混的承诺,变成了你能在源码里指着每一行追问的基础设施。
三台引擎里,造和续相对好懂,最值得展开的是「修」这一台,它身上藏着这套自改进系统最克制、也最反直觉的两笔设计。下一节我们就钻进Curator,看看一个会自我清理的agent,刹车到底踩在哪里。