Command Palette

Search for a command to run...

0

Trace → Fix → PR:构建 Agent 自修复管线的三个阶段

从 PostHog Self-driving mode 获得灵感,在 baby-harness 中实现了一个三阶段闭环:trace 错误模式检测 → 自动生成修复 → 提交 GitHub Draft PR。记一次从产品思路到代码落地的完整过程。

上周研究 PostHog 时,被他们的 "Self-driving mode" 打动了——AI scouts 自动扫描产品信号、诊断问题、生成修复 PR。这不就是我们自进化系统缺的那一环吗?


一、起点

我们的自进化管线已经跑了几个月:

Hermes 会话轨迹 → 平行矿工 → 层级蒸馏 → skill 更新

这个管线能把聊天记录里的经验提炼成 skill。但有一个明显的缺口:它只能进化 "知识"(skill),不能进化 "代码"。

如果一个 agent 反复 timeout、反复 import 出错、反复连不上 API——skill 可以告诉你 "应该加 retry",但不会自动给你发一个修复 PR。你需要手动读日志、分析、改代码、提 PR。

PostHog 的 self-driving mode 给了我们一个清晰的靶子:把 trace 错误检测到 PR 提交的链路自动化。


二、三阶段架构

Phase A: Trace Healer — 错误模式检测

最底层的模块。扫描 bh-traces.db(span-based SQLite 轨迹库),匹配 7 种内置错误模式:

模式严重度检测条件
timeout🔴 HIGHmetadata 含 "timed out"
import_error🔴 HIGHModuleNotFoundError
connection_error🔴 HIGHConnection refused / reset
assertion_error🔴 HIGHAssertionError
exit_failure🟡 MEDIUM进程非零退出
rate_limit🟡 MEDIUM429 / rate limit
long_duration🔵 LOW耗时 > 30s
generic_error🟡 MEDIUM其他未分类错误

每个模式有一个 match_fn(谓词函数)和一个 fix_prompt_template(传给 LLM 的修复提示模板)。扩展新模式只需加一行 dataclass。

设计要点:

  • 零外部依赖,只用 Python stdlib + sqlite3
  • first-match-wins,防止一个 span 被多个模式重复计数
  • CLI + API 双用python -m baby_harness.trace_healer scanfrom trace_healer import scan
# 使用示例
from baby_harness.trace_healer import scan, query_spans
 
spans = query_spans(only_errors=True)
report = scan(spans)
for match in report.matches:
    print(f"{match.pattern.severity} {match.pattern.name}: {match.span.name}")

Phase B: AutoFixHook — 失败时自动修复

上一层的 Phase A 是被动扫描——你需要主动去跑 scan 命令。Phase B 把它变成了 事件驱动

Coordinator 执行任务 → 失败 → on_task_failed
  ├── 1. 从 task + ExecutionOutput 构建合成 SpanRecord(无需 trace DB)
  ├── 2. trace_healer.scan() 匹配错误模式
  ├── 3. 生成 fix_prompt(零成本)
  ├── 4. [可选] PiExecutor 执行修复(消耗 token)
  └── 5. 持久化 FixRecord → ~/.hermes/data/auto-fixes/

关键设计:

合成 SpanRecord:Hook 不依赖 trace DB,直接从内存中的 Task + ExecutionOutput 构造。这意味着即使 DB 没开,自动修复也能工作。

auto_run 保护:默认只生成 prompt(零成本),只有显式传入 executor 才能执行修复。AutoFixHook(auto_run=True) 如果没有 executor 会直接抛 ValueError。

max_fixes 限频:默认每 session 最多 5 次,防止 runaway token 消耗。

from baby_harness.auto_fix_hook import AutoFixHook
from baby_harness.hook import CompositeHook, LoggingHook
 
# 仅检测(零 token 成本)
detect = AutoFixHook()
 
# 检测 + 自动修复(走 PiExecutor)
repair = AutoFixHook(executor=pi_executor, auto_run=True, max_fixes=5)
 
# 组合使用
hooks = CompositeHook([LoggingHook(), repair, SharedMemoryHook(sm)])

持久化的 FixRecord 包含完整上下文:原始错误、匹配的模式、生成的 fix prompt、执行结果(如果有)、时间戳。可以用 AutoFixHook.report() 生成可读报告。

Phase C: AutoPRBot — 自动提交 GitHub PR

闭环的最后一公里。读取 FixRecord,自动走完 GitHub PR 的全部流程:

FixRecord → clone repo → checkout -b auto-fix/<pattern>-<task>
  → 写入 .auto-fixes/ 目录 → commit → push → gh pr create --draft
  → 更新 FixRecord 的 pr_url + submitted_at

安全设计:

  • Draft PR,不自动合并
  • 写到 .auto-fixes/ 目录,不修改原代码
  • dry_run 模式预览操作,不执行 git/gh
  • submitted_at + pr_url 防重复提交
from baby_harness.auto_pr_bot import AutoPRBot
 
# 预览
bot = AutoPRBot(repo="lennney/baby-harness", dry_run=True)
bot.process_all()
 
# 实际提交
bot = AutoPRBot(repo="lennney/baby-harness", executor=pi_executor)
results = bot.process_all(limit=5)

每个 PR 包含:错误模式、严重度、原始错误信息、Fix Prompt、Fix Output。人到 review 时能看到完整的上下文链条。


三、整体架构图

                              ┌──────────────────┐
                              │  Coordinator      │
                              │  执行任务          │
                              └────────┬─────────┘
                                       │ 失败
                                       ▼
                              ┌──────────────────┐
                              │  AutoFixHook      │
                              │  Phase B          │
                              └────────┬─────────┘
                                       │
                    ┌──────────────────┼──────────────────┐
                    ▼                  ▼                  ▼
           ┌────────────────┐ ┌────────────────┐ ┌────────────────┐
           │ Trace Healer   │ │ Fix Prompt     │ │ PiExecutor     │
           │ Phase A        │ │ (零成本)        │ │ (opt-in)       │
           └────────────────┘ └────────────────┘ └────────────────┘
                                       │
                                       ▼
                              ┌──────────────────┐
                              │  FixRecord        │
                              │  ~/..../auto-fixes │
                              └────────┬─────────┘
                                       │ (cron / CLI)
                                       ▼
                              ┌──────────────────┐
                              │  AutoPRBot        │
                              │  Phase C          │
                              └────────┬─────────┘
                                       ▼
                              ┌──────────────────┐
                              │  GitHub Draft PR  │
                              │  (等人 review)    │
                              └──────────────────┘

四、和 PostHog 的对比

维度PostHog Self-driving我们的实现
信号源产品数据(error/rage click)Agent trace spans
诊断方式AI Scouts(LLM)规则匹配 + LLM
修复输出直接生成 PR生成 prompt → 可选执行 → PR
安全-Draft PR + dry_run + 不写原代码
副作用可能生成无效 PRmax_fixes 限频

PostHog 更"大胆"——他们的 AI scouts 直接改代码。我们更保守:生成 fix prompt → 人 review → 决定是否合并。这是刻意的设计选择:在自治 agent 领域,信任需要逐步建立。


五、教训与取舍

1. "合成 SpanRecord" 比查 DB 更实用

一开始我想让 AutoFixHook 去查 trace DB。后来发现没必要——所有信息都在 on_task_failed(task, output) 的参数里。合成比查询更简单、更快、零依赖。

2. FixPrompt 用模板比用 LLM 生成更可靠

Phase A 的 fix_prompt_template 是手写的 f-string,Phase B 的 auto_run 才走 LLM。分层设计让模式匹配(低成本、高精度)和修复执行(高成本、有创造力)解耦。

3. 文件名冲突是个坑

FixRecord 最初用 task_id 做文件名。两个修复同个 task 的 record 会互相覆盖。换成 UUID 后解决。

4. gh CLI 比 GitHub API 好用

Phase C 一开始想用 PyGithub。但 gh pr create --draft 一行命令搞定的事,用 API 需要 20 行代码 + token 管理。gh 的 auth 状态还跨会话持久。


六、下一步

  • Phase D:解析 fix output 中的 diff 块,自动应用到目标 repo(减少人肉 copy-paste)
  • Eval:给自动修复加评估指标——生成的 PR 有多少被合并?减少多少 debug 时间?
  • 反馈循环:被关闭/合并的 PR 反馈回模式库,让 Detector 越用越准