Research Notes · Issue-to-PR Agent · 第二季

生产级 Coding Agent
是一条可验证交付链

从一条 Issue 到一份被合并的 PR,中间不能跳步。每一段路径都要有自己的输入契约、失败信号、可回滚产物与可独立审查的 Artifact。本笔记把整套端到端架构按"不可省略"的原则重新组织,沉淀成可直接对照执行的 16 节。

来源 B 站调研 · 第二季
主题 Production-grade Coding Agent
形态 长读模式 · 非 deck
§ 01

第一性原理:端到端的 5 个不变命题

从 Issue 到 PR 这条链上,无论 Agent 如何演进,这 5 条不能变。

  1. 不可跳步 —— Issue 分析、检索、理解、规划、修改、测试、Review、评测,一步都不能省。省了任何一步,PR 都不能称为"交付"。
  2. 可定位 —— 任何失败都必须能落到具体模块。没有归属的失败,不允许简单重跑。
  3. 可回滚 —— Agent 引入的修改必须可逆,且必须能区分"用户改的"与"Agent 改的"。
  4. 可审查 —— 交付物不是一段回答,而是一份 Patch、一组测试日志、一份 PR 说明与一份评测报告,每一份都要能被打回重做。
  5. 可复现 —— 全链路 Trace 必须从 Issue 一直贯穿到 PR。没有 Trace,PR 是怎么生成的永远无法解释。

真正的交付物不是"PR 文本",而是一份可审查系统——每一段产出都有证据链。— 这份笔记的全部主张都建立在这 5 条之上

§ 02

端到端总架构

Agent Orchestrator 是协调器,不是合并器;12 个模块各自有专属职责。

UNDERSTAND ACT VERIFY TRUST & SAFETY Agent Orchestrator CENTRAL · COORDINATOR IssueAnalyzer 解析 Issue Search Layer 检索候选文件 Code Understanding 构建调用链 Planner / TaskGraph 拆解为 TaskNode DelegationManager 分配 Sub-Agent Coder Sub-Agent 局部 Patch Patch Pipeline 最小化 Diff Sandbox TestRunner 受控环境执行 Reviewer Agent Diff & 风险 PRBuilder 产出可合并 PR Rollback Manager 只撤 Agent 改的 EvaluationRunner 指标 & 报告 Failure Classifier 分类后再处理 → P1 · 分析 → P2 · 检索 → P3 · 理解 → P4 · 规划 → P5 · 委派 P0 · 入口
图 1 · 端到端架构:12 个模块按 4 个职责簇组织,Orchestrator 只做协调

设计原则

  • Orchestrator 负责模块间协调与状态流转
  • 不替代各模块的专属职责
  • 不把所有逻辑塞进一个 Agent Prompt

核心约束

  • 每个模块有独立输入输出契约
  • 失败可定位到具体模块
  • 全链路 Trace 从 Issue 贯穿到 PR

Anti-Pattern · 巨型 Prompt

把所有职责塞进同一个 Prompt 看似省事,实际上让失败无法定位——一旦它出错,你不知道是"分析错了"还是"理解错了"。把模块拆开是给失败一个家。

§ 03

IssueAnalyzer:先把问题读清楚

自然语言 → 结构化任务输入。这是后续 Search 与 Planner 共用的"事实基础"。

提取维度

现象 & 触发条件

用户看到的现象、复现路径

成功标准

什么样的修复算成功?

影响模块

大致涉及哪些模块,风险多大

缺口信息

偏环境、复现步骤、版本差异

输出契约

// IssueAnalyzer 输出(结构化)
{
  "phenomenon": "刷新后偶发退出",
  "trigger":    "登录后刷新页面",
  "scope":      ["Auth", "Session", "Router"],
  "success_criteria": [
    "刷新后不再退出",
    "补充 Session 刷新测试"
  ],
  "constraints": ["不破坏现有登录主流程"],
  "risk_level": "medium"
}

为什么契约必须明确

同一个 Issue 在不同模块看来意义不同:Search 关心 scope,Planner 关心 success_criteria,TestRunner 关心 constraints。没有这个共同"事实层",后端各模块会各自脑补出不同版本的 Issue。

§ 05

诊断:根因假设必须可验证

Agent 不应直接宣布根因。每个假设必须携带证据、验证文件、验证方法。

假设 A · 异步时序

refreshSession 返回 Promise,但路由守卫没有 await,它就基于一个尚未完成的会话状态去做判断。

验证方法:检查 routerGuard 是否 await restoreSession

假设 B · 401 中间件

401 中间件在 Session 刷新期间过早早清了 Session 状态,导致后续刷新被误判为未登录。

验证方法:检查 401 拦截器与 refreshSession 的执行顺序。

假设 C · 状态恢复顺序

Cookie 与 localStorage 的状态恢复顺序在不同浏览器存在差异,导致 Session 还没准备好路由就走了。

验证方法:检查 restoreSession 的两个存储读取顺序。

Anti-Pattern · "我看着像"式结论

模型靠经验拍脑袋给的根因,常常恰好蒙对——但错的时候没人能复盘验证。根因必须挂在"可重现的命令 / 可断言的代码位置 / 可跑的脚本"上,否则就是猜测。

§ 06

规划:TaskGraph 而非一锅汤

修复任务 = 一张有依赖关系的图。每个节点都有契约,不是"一句口语任务"。

T1 · 验证 Session 恢复调用链
用 Code Understanding 的输出作为锚
T2 · 确认异步时序缺口
对应根因 假设 A
T3 · 修改 AuthProvider 或路由守卫
最小修改、局部可逆
T4 · 补充刷新保持登录测试
复现 + 防回归
T5 · 运行 Auth 相关测试套件
由 Sandbox 隔离执行
T6 · Review Diff
Reviewer Agent 自动审查
T7 · 生成 PR 说明
忠实反映修改与验证

每个 TaskNode 必须包含

  • Input:前置任务输出的 Artifact
  • Output:本任务产出的结构化 Artifact
  • Dependencies:依赖的节点 ID
  • Success Criteria:可验证的通过条件(最好绑定测试)
  • Risk Level:low / medium / high

硬约束

修复任务必须拆成可独立验证的节点,而不是一句"把登录改对"。节点太粗等于让 Sub-Agent 重新陷入"一锅汤"困境。

§ 07

执行取舍:什么时候用 Sub-Agent

Sub-Agent 不是越多越好,它是"为可隔离的 TaskNode 开的副作用空间"。

适合派给 Sub-Agent

  • Search Agent · 并发检索全部相关调用方
  • Coder Agent · 实现局部 Patch,范围明确
  • Tester Agent · 运行测试、分析日志
  • Reviewer Agent · 检查 Diff 和风险边界

不适合的场景

  • 高度耦合的同一文件修改
  • 极限风险的操作(如生产凭证擦写)
  • 无法结构化验证的主观判断
  • 过于细小、委派成本高于收益的任务

Sub-Agent 是给"可隔离、可验证、可并行"的 TaskNode 开的。

§ 08

安全修改:Patch Pipeline

模型不能直接覆盖文件。修改的原子单位是 Patch(Diff),而不是整个文件。

01 · 读取 当前文件 02 · 最小编辑 构建 Edit Unit 03 · 生成 Diff 可审查 Patch 04 · 检查版本 匹配已验证快照 05 · 应用 落盘 + Artifact AGENT EDITS ONLY AS DIFF · NEVER OVERWRITE THE WHOLE FILE
图 2 · Patch 五步:每一步都是单独可审查的产物

Patch 必须满足

  • 可审查 · 最小修改范围
  • 可验证 · 基于已验证的正确文件版本
  • 幂等 · 不覆盖用户修改
  • 可回滚 · 留 Artifact 记录

交付单位

代码修改的文件单位必须是 Patch(Diff),不是整文件覆盖。这也是 Rollback 与 Review 能成立的前提。

§ 09

验证:Sandbox TestRunner + Reviewer

验证发生在受控环境里;Review 发生在 Patch 落盘之后、PR 之前。

Sandbox TestRunner · 受控执行

  1. 选择相关测试集:基于 Patch 影响范围自动选取
  2. 创建 Sandbox:限制资源、隔离网络与文件系统
  3. 运行并截断日志pnpm test session-refresh
  4. 结构化失败分类:输出 Test Artifact,供 Replan 使用

铁律

测试结果必须来自真实工具执行,不是模型口头声称——一旦 Agent 能"编"测试结果,整个验证闭环失效。

Reviewer Agent · 三维度门控

Diff 范围

  • Diff 是否过大
  • 是否越权修改
  • 是否遗漏调用方

API & 安全

  • 破坏公共 API?
  • 引入安全风险?
  • 违反安全策略?

测试 & 说明

  • 是否补强测试
  • PR 说明是否忠实
  • 是否有未解决问题

Review 是 PRBuilder 的前置门控

测试通过 ≠ 可合并。Review 负责发现"测试覆盖不到的" 风险。

§ 10

失败处理:分类决定路径

失败不是终点,是进入 Replan 或 Rollback 的信号。先分类,再决定。

失败原因 具体表现 处理路径
实现错误 Coder Sub-Agent 产出的 Patch 与意图不符 Rollback · 回滚 Patch,更新 TaskGraph 再 Replan
根因错误 修好了症状,没修到根因 补充读取代码,回到 IssueAnalyzer 重新分类
测试选择 现有测试集没暴露这个 Bug 扩大检索范围 / 补充新测试
环境缺失 依赖缺失、版本问题、Sandbox 拒绝 请求用户确认环境来源,或转人工介入
权限被拒 Sandbox 拒绝命令(敏感路径 / 网络) 请求提升白名单,或拆分命令为更安全的形式
反复失败 同一 TaskNode 连续 N 次失败 转人工介入,存档为 Memory 中的失败模式

核心原则

  • 系统不能在测试失败后简单重跑
  • 必须先由 FailureClassifier 对失败原因分类
  • 分类后再决定分支,分类不明先扩大检索
  • 超出 Agent 处理边界 → 升级人工介入

设计隐喻

失败是 Replan / Rollback 的入口,不是"再来一次"的信号。每一次失败都必须 留下结构化原因,否则下次模型会用同一套假设碰同一面墙。

§ 11

回滚:只撤销 Agent 引入的修改

Rollback 是 Patch Pipeline 的镜像:保护用户工作区,而不是清盘。

  1. 01定位 applied_patch:从 Agent Edit Ledger 查找对应记录
  2. 02检查当前文件 hash:确认是否混入用户自己改过的内容
  3. 03反向应用 Agent Patch:只撤销 Agent 这一份变更
  4. 04保存 rollback Artifact:更新 TaskGraph 状态,触发 Replan

硬性禁止

⛔ 禁止默认执行 git reset --hard。这会把用户当天的工作区一起清掉,且无法审计。

保护原则

回滚必须保护用户工作区,只撤销 Agent 通过 PatchApplier 引入的变更,且必须留下可审计的 Artifact。

§ 12

记忆边界:Memory 参与而不替代

Memory 给你"线索",但不能替代你"重新读代码"。

Memory 可以提供

  • 项目测试命令与模块路径
  • 历史失败经验与修复模式
  • 用户输出偏好与安全约束
  • 常见修复流程(加速路径生成)

Memory 不能替代

  • 读取当前代码(文件可能已变更)
  • 运行当前测试(环境可能已变更)
  • 当前 Patch 的 Review 验证

正确用法

历史记录:曾因缺少 await 导致 Session 测试失败。

→ 用作假设 A 的支持线索,再去代码里检查 routerGuard 是否 await restoreSession

错误用法

直接跳过代码读取环节,给出结论。

→ 模型把历史当事实,结论无法证伪,下次问题相同。

Memory 加速定位,但不能取代当前证据链。

§ 13

缓存边界:稳定前缀 vs 动态内容

缓存能省钱,但 Stale Cache 会污染权限判断和工具结果。

可缓存(稳定前缀)

  • System Prompt + Tool Schema:跨任务不变,缓存收益最高
  • 安全规则 + 项目稳定摘要:每次任务重复加载,适合缓存
  • Task 协议 + Reviewer 输出 Schema:结构固定,缓存可降低多轮成本

动态内容(不可缓存)

  • 最新工具执行结果
  • 当前 Patch Diff
  • 测试日志与错误输出
  • 当前 TaskNode 状态

工程准则

必须严格区分稳定前缀与动态内容。一旦把"工具结果"放进缓存前缀,下一轮 Agent 会基于上一轮的旧结果做判断——这是最容易出问题的隐性 bug。

§ 14

可观测性:每一步独立 Span

没有 Trace,就无法解释"PR 是怎么生成的",也无法定位失败原因。

1 Issue 分析 IssueAnalyzer Span + Artifact 2 检索 & 理解 Search / CallGraph Span 规划 & 执行 Model / Tool / TaskNode + Sub-Agent 事件 3 Patch & 验证 测试结果 / Review / Rollback 4 5 PR & 评测 PR 生成 + Eval 指标
图 3 · Observability 五阶段 · 每步独立 Span + Artifact 引用

一个 Span = 一个可问责单元

每一步都要有独立的 Span(耗时、token、关键决策)和 Artifact 引用(上游输出物)。Trace 缺失等同于:"为什么这个 PR 被合并了"——没人能答。

§ 15

评测 Gate 与关键指标

单次成功可被 Gate 验证;多次成功被指标刻画。

成功标准 · Gate 检查

  • ROOT 根因定位正确,有证据支持
  • PATCH Patch 能干净应用,无冲突
  • TEST 相关测试通过,无回归
  • DIFF Diff 可审查、无越权修改
  • DESC PR 说明忠实反映修改
  • ACCEPT Reviewer 或用户接受

Task Success Rate

端到端完成率

Patch Correctness

Patch 正确性

Regression Rate

回归引入率

Human Intervention Rate

人工介入率

关键提醒

Issue-to-PR 成功不是"PR 文本"生成,而是可验证的修复

§ 16

项目级评估:双维度 8 指标

单轮 Gate 验证是底线,长期看的是交付质量能否稳定。

过程质量

Root Cause Accuracy

根因定位准确率

Patch Correctness

Patch 正确应用率

Regression Rate

回归引入率(越低越好)

Rollback Success Rate

回滚成功且保护用户改

交付质量

Issue Resolution Rate

Issue 最终被解决率

PR Acceptance Rate

PR 被 Reviewer 接受率

Cost per Merged PR

每个可合并 PR 的推理成本

Human Intervention Rate

需要人工介入的任务比例

最终评估的核心问题只有一个:
能否稳定交付可合并 PR,且人工介入率持续下降?

§ 17

收束:3 条 takeaway + 9 步交付流

把这一季的所有主张压缩到一页。

1 · 不能跳步

从 Issue 到 PR 必须经历检索、理解、规划、修改、测试、Review 与评估。每一步都有结构化的输入输出和 Artifact。

2 · 失败是闭环的一部分

失败后能 Rollback 或 Replan,Trace 可追溯。不让模型以"成功"替代硬验证

3 · 真正的交付物

不是一段回答,而是一份可审查系统——Patch、测试结果、PR 说明与评测报告,每一项都有证据链。

① 找到代码 ② 理解代码 ③ 规划任务 ④ 分工执行 ⑤ 安全修改 ⑥ 长期记忆 ⑦ 性能优化 ⑧ 评测上线 ⑨ 最终交付 PR
图 4 · 从代码到 PR 的 9 步交付流