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 | 🔴 HIGH | metadata 含 "timed out" |
import_error | 🔴 HIGH | ModuleNotFoundError |
connection_error | 🔴 HIGH | Connection refused / reset |
assertion_error | 🔴 HIGH | AssertionError |
exit_failure | 🟡 MEDIUM | 进程非零退出 |
rate_limit | 🟡 MEDIUM | 429 / 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 scan或from 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/ghsubmitted_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 + 不写原代码 |
| 副作用 | 可能生成无效 PR | max_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 越用越准