Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第八章:背压机制——让 AI 学会自我纠错

📌 本章李明轩在做什么:他给自己的发布流程立了三条铁律——错别字零容忍、敏感词必须过审、4 个平台的配图必须齐全。这一章的背压机制就是把这些铁律"硬塞"进 AI 的工作流里,不合格就打回重做。

一个关于关卡的比喻

想象一个工厂的流水线,生产汽车零件。每个工序之间,有一个质检员:

  • 零件到达质检站
  • 质检员检查是否符合规格
  • 符合:零件继续进入下一道工序
  • 不符合:零件被打回,重新加工,不合格的零件永远不会流向下一工序

这就是"背压"(Backpressure)的物理含义:上游的压力被限制,不符合质量的东西不会向下游传递

在 Ralph 里,背压机制确保:不合格的代码不会被当成"任务完成"接受

⚙️ AI 产出 写代码 / 改文稿 🚦 质量关卡(背压) ☑️ 跑测试(pytest) ☑️ 跑 lint / 格式检查 ☑️ 跑类型检查 ☑️ 跑项目自定义门禁 ✅ 放行 进入下一顶帽子 ❌ 打回 附上失败原因 下一步 (审查 / 提交) 完成 全过 任一项不过 同一顶帽子再做一轮,直到通过

关卡的设计哲学是——Ralph 不告诉 AI "怎么做对",只告诉它 "什么算做对"。AI 有完全自由去决定怎么实现,但交付物必须能过关。过不了的那一刻,压力传回给 AI,它必须自己修。


背压的实现方式:通过帽子指令,而非自动执行

Ralph 的背压机制不是由 Ralph 框架直接拦截并执行命令行检查——而是通过帽子指令告诉 AI:在发出"完成"信号之前,必须自己运行验证命令,并把命令的实际输出结果附在事件载荷里作为证据。

这种设计哲学正是"背压优于规定"原则的体现:

  • 规定式(脆弱):Ralph 框架在特定节点运行固定命令
  • 背压式(健壮):AI 自己负责验证,把证据附在事件里,审查者帽子再核实证据

在帽子指令里定义验证要求

hats:
  builder:
    name: "⚙️ 构建者"
    triggers: ["tasks.ready"]
    publishes: ["review.ready"]
    instructions: |
      实现分配的任务。

      ## 背压要求

      在发出 review.ready 之前,你必须已完成:
      - tests: 通过(运行 python -m pytest)
      - lint: 通过(运行 flake8 .)
      - typecheck: 通过(运行 mypy src/)

      在事件载荷里附上证据:
      "tests: pass, lint: pass, typecheck: pass"
      
      不要仅声称"测试通过",要提供实际的命令输出摘要。

在事件载荷里携带证据

AI 在完成工作后,用 ralph emit 发出完成信号,并附上验证证据:

# AI 运行这条命令来完成背压验证并发出事件
ralph emit "review.ready" "tests: pass (5/5), lint: pass, typecheck: pass"

审查者帽子核实证据

审查者帽子(Critic)被设计为不信任构建者的自我声明——它会重新运行验证命令来独立确认:

hats:
  critic:
    name: "🧪 审查者"
    triggers: ["review.ready"]
    instructions: |
      不要相信构建者说"测试通过了"。
      重新运行测试,自己验证。
      如果测试失败,发出 review.rejected 并说明原因。

一个实际的背压流程

让我们用例子看看背压是如何工作的:

场景:AI 实现了一个功能,但测试实际上没有全部通过。

[Iteration 6/150]

⚙️ 构建者 完成工作:
   → 实现了 validate_email() 函数
   → 运行 pytest,声称"通过"(实际边界测试失败了)
   → 发出 review.ready 事件,载荷:"tests: pass"

[Iteration 7/150]

🧪 审查者 独立检查:
   → 自己运行 python -m pytest
   → 发现:
     FAILED tests/test_validate.py::test_empty_email
     FAILED tests/test_validate.py::test_none_input
   → 发出 review.rejected
     原因:"声称测试全部通过,但边界情况测试失败"

[Iteration 8/150]

⚙️ 构建者 重新工作:
   → 读取 review.rejected 的具体原因
   → 修复 validate_email() 的边界情况处理
   → 重新运行测试,实际全部通过(5/5)
   → 发出 review.ready,载荷:"tests: pass (5/5)"

[Iteration 9/150]

🧪 审查者 再次独立验证:
   → 运行 pytest:5 passed
   → 发出 review.passed ✅

整个背压循环完全自动,不需要你介入。


背压验证的常见类型

在帽子指令里,你可以要求 AI 运行任何类型的验证命令:

测试类

hats:
  builder:
    instructions: |
      完成实现后,在发出完成事件前必须通过:
      - Python 项目:python -m pytest -v
      - Node 项目:npm test
      - Rust 项目:cargo test --all
      把实际输出结果附在事件载荷里。

格式与 Lint 类

instructions: |
  代码格式必须通过检查:
  - Python:black --check . && isort --check-only .
  - JavaScript:npm run lint
  - Rust:cargo fmt --check && cargo clippy -- -D warnings

类型检查类

instructions: |
  类型检查必须通过:
  - Python:mypy src/
  - TypeScript:tsc --noEmit
  - Rust:cargo check(无 warnings)

综合验证示例(Rust 项目)

下面是 ralph-orchestrator 项目本身的实际使用方式——注意背压验证要求写在 Builder 帽子的 instructions 里:

hats:
  builder:
    name: "⚡ Builder"
    instructions: |
      Before emitting subtask.done, ALL must pass:
      1. cargo fmt --all -- --check — formatting
      2. cargo clippy --all-targets --all-features -- -D warnings — lints
      3. cargo test -p <target-crate> — targeted tests

      Include actual pass/fail output in scratchpad Notes.
      Do not claim "pass" without running.

好的验证指令 vs. 坏的

好的(要求实际输出):

instructions: |
  验证要求(发出 review.ready 之前):
  - tests: 运行 pytest,把通过数/总数附在载荷里
  - lint: 运行 flake8,确认 0 个错误
  
  如果任何一项失败,先修复,再发出事件。
  不要在测试失败的情况下发出 review.ready。

坏的(只是声明,没有要求证据):

instructions: |
  完成后发出 review.ready。
  (AI 可能直接发出而不实际运行测试)

什么情况下背压会无法自动修复

有些问题 AI 无法自动修复,背压会一直失败,直到:

  1. 达到最大迭代次数(默认 150 次):循环会停止,并告诉你卡在了哪里

  2. AI 主动发布 build.blocked:AI 自己判断无法继续,需要人工介入

  3. 循环超时(默认 4 小时):超过配置的最长运行时间

这时你需要:

  1. 查看最后一次失败的具体信息
  2. 手动修复无法自动解决的问题(比如安装缺少的依赖)
  3. 重新启动循环,从断点继续
# 查看为什么卡住了
ralph events --topic blocked
cat .ralph/agent/memories.md  # AI 可能记录了遇到的问题

# 手动修复后重新运行
ralph run -p "继续上次的任务,已经修复了 xxx 问题"

LLM-as-Judge:用 AI 评判主观质量

有些质量标准很难用命令行工具检查,比如:

  • 代码的可读性
  • 错误消息是否对用户友好
  • 文档是否清晰

这类主观标准可以交给审查者帽子来判断,把它设计成专门针对这类问题的角色:

hats:
  ux_reviewer:
    name: "💬 UX 审查者"
    triggers: ["review.ready"]
    publishes: ["ux.passed", "ux.rejected"]
    instructions: |
      你是用户体验审查者。检查代码里的错误消息是否对普通用户友好:
      
      判断标准:
      - 不包含技术栈信息(如 'NullPointerException'、堆栈跟踪)
      - 告诉用户如何解决问题,而不只是说"出错了"
      - 语言清晰,不使用编程行话
      
      如果全部通过,发出 ux.passed。
      如果有不符合的,发出 ux.rejected 并列出具体的问题消息。

为什么背压比详细指令更有效

还记得第三章的"背压优于规定"原则吗?这里是一个具体的对比:

规定式(容易失效)

instructions: |
  第一步,写测试文件
  第二步,运行测试,确认它失败
  第三步,写实现代码
  第四步,运行测试,确认它通过
  第五步,运行 flake8 检查格式
  第六步,如果格式不通过,运行 black 修复
  第七步,再次运行 flake8...

(如果 AI 跳过了某一步,或者文件路径不同,整个指令就失效了)

背压式(更健壮)

hats:
  builder:
    instructions: |
      实现功能。
      发出 review.ready 之前,必须已通过:
      tests: pytest 全部通过
      lint: flake8 0 个错误
      
      把实际命令输出附在载荷里作为证据。

(不管 AI 怎么实现,最终都要提供可核实的验证证据)

背压机制关注结果,而不是过程,这让它对各种实现方式都有效。


本章小结

  • 背压关卡是自动运行的质量检查点,不合格的代码不会被"通过"
  • 可以配置测试、格式检查、类型检查、安全扫描等各类关卡
  • 好的 on_fail 消息能大大提高 AI 自动修复的成功率
  • 关卡按顺序执行,建议快速检查放前面
  • 背压比详细规定更健壮,关注结果而不是过程
  • 支持 LLM-as-Judge 检查主观质量标准

质量门禁搞定了,但李明轩遇到新情况:付费社群周报今晚没写完,明晚接着写时 AI 已经把上下文全忘光了。他需要一个跨会话的"AI 笔记本"。下一章讲记忆与任务系统——让 AI 把学到的东西留在磁盘上。