☰
深入gnhf编排器架构:状态机如何让AI代理整夜循环不丢一行代码
2026/9/25 4:25:38 网站建设 项目流程

深入gnhf编排器架构:状态机如何让AI代理整夜循环不丢一行代码

【免费下载链接】gnhfBefore I go to bed, I tell my agents: good night, have fun项目地址: https://gitcode.com/gh_mirrors/gn/gnhf

gnhf(good night, have fun)是一个 AI 代理过夜运行编排器:睡前一条命令,它就让编码代理(Claude Code、Codex、Copilot 等)朝着你的目标整夜循环工作——每一轮迭代完成一小步可验证的改动,成功即自动提交,失败即自动回滚。你醒来时,得到的是一条装满干净提交的分支和一份完整日志。它的核心秘密,就是 orchestrator.ts 里那台精心设计的状态机。

🌙 gnhf 编排器:AI 代理的“过夜模式”

传统用法是:你在终端里盯着 AI 写代码,它一卡壳、一出错就得你接手。gnhf 换了一个思路——把“盯着”这件事交给状态机。

你只需在一个 Git 仓库里执行一条命令:

gnhf "在不改变功能的前提下降低代码库的复杂度"

然后安心睡觉。gnhf 会自动:

  • 校验工作区是否干净,并创建独立的gnhf/分支
  • 每轮迭代生成一份提示词,调用你配置的代理(默认claude)
  • 解析代理返回的结构化结果(agents/types.ts),决定提交还是回滚
  • 直到达到迭代上限、token 上限、你设定的停止条件,或你按下停止

整个运行过程由Orchestrator类驱动,它继承自 Node.js 的EventEmitter,通过事件把状态推送给终端界面(renderer.ts),所以你能实时看到迭代数、token 消耗和提交计数。

🔁 核心状态机:驱动整夜循环的 4 个状态

gnhf 的状态机定义在 orchestrator.ts 的OrchestratorState接口中,只有 4 个主状态:

状态含义触发条件
running正在执行某轮迭代每次迭代开始时
waiting指数退避中,暂停等待代理连续硬错误后
aborted运行已中止(达到上限或连续失败)连续 3 次失败、token 超限、永久错误
stopped优雅停止完成两次 Ctrl+C 或 SIGTERM

主循环的逻辑非常直白(orchestrator.ts):

  1. 迭代前检查:是否达到--max-iterations或--max-tokens?是则中止
  2. 执行迭代:构建提示词 → 调用代理 → 等待结构化结果
  3. 迭代后检查:记录成败 → 判断是否触发--stop-when停止条件
  4. 失败熔断:连续失败达到maxConsecutiveFailures(默认 3 次)立即中止
  5. 退避等待:有硬错误时按60秒 × 2^(n-1)指数等待,然后回到第 1 步

这个“检查—执行—检查”的节奏,正是状态机不丢工作的第一个原因:任何异常都只会把循环推回安全点,而不是带着脏状态继续跑。

✅ 每轮提交与回滚:“不丢一行代码”是如何保证的

这是 gnhf 最巧妙的部分。每轮迭代结束时的处置逻辑:

  • 成功→ 立即git add -A并创建独立提交(git.ts),并把本轮摘要、关键改动、关键学习追加到notes.md
  • 代理自报失败→git reset --hard硬回滚(git.ts),工作区恢复原样
  • 代理崩溃(硬错误)→ 同样回滚,并进入退避等待
  • 提交本身失败→ 唯一不立即回滚的情况:gnhf 保留未提交的工作,并在下一轮提示词里注入“修复指令”,让下一轮代理先修好提交再继续(orchestrator.ts)

也就是说,代理的任何胡作非为都只存活到那一轮迭代结束。成功的工作被固化为提交,失败的工作被彻底清除。你早上醒来时,分支上的每一个提交都是“成功过验证的工作”,可以逐个 cherry-pick 或 revert,互不牵连。

还有一个细节:提交时强制关闭 GPG 签名(-c commit.gpgsign=false),避免自动循环被密码弹窗卡死——这是为“无人值守”场景专门做的防御。

⚠️ 故障处理:三级故障分级与指数退避

整夜运行最怕的不是单次失败,而是“雪崩式失败”。gnhf 的解法是把故障分成三类,各给不同的待遇(orchestrator.ts):

  1. 代理自报失败(success=false):说明循环本身健康,只是这一步走不通 → 直接进下一轮,不等待
  2. 硬错误(代理进程崩溃、网络超时等):环境可能暂时性出问题 → 触发指数退避,第 1 次等 60 秒、第 2 次 2 分钟、第 3 次 4 分钟……(orchestrator.ts)
  3. 永久错误(如 Claude 额度用尽):重试毫无意义 → 立即中止,并打印运行日志路径

另外两个保命设计:

  • 零进展熔断:一轮既没改文件、也没产生新学习 = 自报失败,计入连续失败,防止代理在原地打转烧 token
  • token 上限中途拦截:--max-tokens达到时立刻中止当前迭代,账单不会失控

同时 gnhf 会默认开启防休眠(sleep.ts):macOS 用caffeinate、Linux 用systemd-inhibit、Windows 用SetThreadExecutionState,保证机器不会在你睡着时自己“下班”。

🛑 优雅中断:两次 Ctrl+C,两种结局

过夜运行必然要处理“我想停”这个场景。gnhf 的中断本身也是一台状态机(interrupt-state.ts):

你的操作状态机处置结果
第 1 次 Ctrl+Crequest-graceful-stop让当前迭代跑完(或提前结束退避等待),再干净退出
第 2 次 Ctrl+Cforce-stop立即中止,回滚未提交内容
SIGTERMforce-stop立即强停
已中止状态下再按exit直接退出界面

优雅停止的关键在于:它只标记、不抢跑(orchestrator.ts)。状态机先记下gracefulStopRequested,等主循环在迭代间隙自然收敛后才真正停机。这样即使你半夜被闹钟吵醒按了一下 Ctrl+C,也不会出现“提交提交到一半”的半成品状态。

🧠 notes.md:跨迭代的共享记忆

状态机保证“不丢代码”,那“不丢上下文”呢?答案是一个纯文本文件notes.md:

  • 每轮迭代前,代理被要求先读notes.md了解历史(iteration-prompt.ts)
  • 每轮结束后,编排器自动把摘要、关键改动、关键学习追加进去(run.ts)
  • 所有提示词、停止条件、提交规范等元数据都保存在.gnhf/runs/<runId>/下,并且被本地忽略,不会污染你的分支

这让每一轮代理都像是“从昨晚接着干的人”,而不是失忆的陌生人。

📁 关键源码路径速查

想深挖实现?从这几个文件入手:

  • 状态机主循环:src/core/orchestrator.ts
  • 运行元数据与 notes.md:src/core/run.ts
  • 中断状态机:src/core/interrupt-state.ts
  • Git 提交与回滚:src/core/git.ts
  • 迭代提示词模板:src/templates/iteration-prompt.ts
  • 代理输出 schema:src/core/agents/types.ts
  • CLI 入口:src/cli.ts
  • 代理技能说明:skills/gnhf/SKILL.md

🚀 快速上手 gnhf 过夜运行

  1. 安装:npm install -g gnhf
  2. 准备:在 Git 仓库中确保工作区干净(空目录请先git init)
  3. 启动:执行gnhf "<你的目标>",建议加上限更安心:

gnhf "reduce complexity of the codebase" --max-iterations 10 --max-tokens 5000000

  1. 进阶:--worktree可让多个代理在隔离 worktree 中同时干活;--stop-when支持用自然语言设定停止条件;在已有gnhf/分支上重跑gnhf即可断点续跑

一次运行结束后,gnhf 会打印退出摘要:分支、耗时、迭代数、token 总量、diff 统计和复盘命令。从此,你只需要在睡前说一句:good night, have fun。

【免费下载链接】gnhfBefore I go to bed, I tell my agents: good night, have fun项目地址: https://gitcode.com/gh_mirrors/gn/gnhf

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询