很多 Agent 平台的成功率统计都有一个乐观偏差:工具返回了 PR URL,任务就算成功。
但 PR 页面可能同时挂着三件事:审阅者要求修改、required check 失败、分支落后产生冲突。URL 是真的,代码也是真的,唯一不真的,是"工作已经完成"。
9 月 4 日,GitHub 的 Copilot 周报提到 VS Code Agent Merge 进入 public preview。官方文档描述,它会处理 review feedback、failed required checks、落后分支和 merge conflicts,重新运行工作流,并持续对账到 PR ready-to-merge。它还可以单独配置是否最终合并。
这篇不讨论要不要开启这个预览功能,而是实现一个更通用的 DeliveryReconciler。

success 应该回答"什么状态",而不是"有没有 URL"
先把接口从布尔值改成阶段状态:
ts
type DeliveryState =
| "OUTPUT_READY"
| "VALIDATING"
| "BLOCKED_AUTOMATABLE"
| "BLOCKED_HUMAN"
| "READY_FOR_HANDOFF"
| "DELIVERED"
| "STOPPED_BUDGET";
type DeliveryResult = {
state: DeliveryState;
targetId: string;
revision: string;
blockers: Blocker[];
nextAction?: NextAction;
};
OUTPUT_READY 对应 PR 已创建、文件已生成或文章已写完。它不是失败,但也不能冒充 DELIVERED。
Blocker 是状态输入,不是日志字符串
如果 Agent 只能读到"检查失败",它无法判断该重跑、修代码还是等待。把阻塞项收敛成窄类型:
ts
type Blocker =
| {
id: string;
type: "REVIEW";
revision: string;
required: boolean;
threadIds: string[];
}
| {
id: string;
type: "CHECK";
revision: string;
checkName: string;
status: "PENDING" | "FAILED";
rerunnable: boolean;
}
| {
id: string;
type: "BASELINE";
expectedBase: string;
currentBase: string;
conflicts: string[];
}
| {
id: string;
type: "HUMAN";
reason: "AMBIGUOUS" | "HIGH_RISK" | "FINAL_APPROVAL";
};
revision 是这里最重要的字段。旧 commit 的绿灯晚到,不能把新 commit 误判为通过。
Reconciler 只决定下一步,不替业务判真
收尾服务可以由事件触发,也可以低频轮询。每轮都重新读取当前快照:
ts
async function reconcile(job: DeliveryJob): Promise<DeliveryResult> {
const snapshot = await loadSnapshot(job.targetId);
if (snapshot.targetIdentity !== job.lockedIdentity) {
return stop(job, "BLOCKED_HUMAN", "TRACKED_TARGET_CHANGED");
}
const fresh = snapshot.evidence.filter(
e => e.revision === snapshot.revision
);
const blockers = deriveBlockers(snapshot, fresh);
if (blockers.length === 0) {
return ready(job, snapshot.revision, fresh);
}
const action = chooseAction(blockers, job.policy);
if (!action || action.permission === "human") {
return blocked(job, snapshot.revision, blockers, action);
}
await runOnce(action, {
idempotencyKey: [job.id, snapshot.revision, action.blockerId].join(":"),
});
return validating(job, snapshot.revision);
}
VS Code 文档提到,如果会话开始跟踪另一条分支或另一个 PR,Agent Merge 会关闭并要求重新启用。这个细节不花哨,却很关键:授权是给某个确定对象的,不是给一个会随导航变化的窗口。
把 CI 看成版本化证据
CI 事件经常乱序到达。下面的 check_success(r3) 即使最后到,也不能证明 r4:
text
10:02 commit(r3)
10:04 check_started(r3)
10:05 commit(r4)
10:07 review_required(r4)
10:09 check_success(r3)
Reducer 不该"最后写入者获胜",而应先检查事件是否属于当前 revision:
ts
function reduceEvidence(
currentRevision: string,
evidence: Evidence[],
): Evidence[] {
return evidence
.filter(e => e.revision === currentRevision)
.sort((a, b) => a.observedAt - b.observedAt);
}
审阅批准、内容事实核验、图片上传完成也有同样问题。产物一改,旧证据是否仍有效必须由明确策略决定,不能默认继承。
"一直在修"也可能是失败
最典型的振荡是:根据评论改代码 → CI 失败 → 修 CI → 又触发新的评论。
给循环一个进展分数:
ts
const progress =
previous.hardBlockers - current.hardBlockers
+ current.freshPassedChecks - previous.freshPassedChecks
- current.newBlockers;
同时设置:
maxRounds:最大收尾轮数;maxModelRequests:模型调用预算;maxChangedFiles:修复可扩大到的范围;maxNoProgressRounds:允许连续无进展次数;deadlineAt:总等待截止时间。
达到预算后返回 STOPPED_BUDGET,并附最近几轮的 blocker 变化。不要把无限重试伪装成自治。
自动修复和自动合并必须分权

官方文档专门提醒:Agent Merge 会进入 Autopilot with Assisted permissions,消耗模型请求;自动合并是可选配置,而且合并前会再次检查 readiness。
应用到自己的系统,可以把策略写成:
yaml
allowed:
- read_review_threads
- edit_work_branch
- rerun_required_checks
humanGate:
- permission_expansion
- destructive_migration
- ambiguous_merge_conflict
- final_merge_or_publish
能修改分支,不代表能替团队合并;能把文章预填到后台,也不代表能绕过页面状态直接公开。
观测指标不要只看"最终成功率"
建议记录:
- 从
OUTPUT_READY到READY_FOR_HANDOFF的时长; - 每类 blocker 的出现率和平均清除轮数;
- blocker 复发率;
- 每次交付的模型请求与人工接管次数;
- readiness 误判率,例如宣称可交付后又被 required check 拦下。
如果生成速度提升一倍,却让人工收尾时间增加,整体交付并没有变快。
最后交付一张 receipt
json
{
"state": "READY_FOR_HANDOFF",
"target": "pull-request/218",
"revision": "r7",
"freshEvidence": ["review:approved", "ci:passed", "base:current"],
"openBlockers": [],
"finalAction": "requires_human",
"observedAt": "2026-09-07T10:18:42+08:00"
}
这张 receipt 才是下游可靠的输入。它说明当前是哪一版、通过了什么、还有什么没做,而不是只留一个"已完成"的聊天气泡。
从代码交付迁移到内容岗位
这也是我们做 Tipkay 时的一个取舍。Tipkay 面向小微企业、一人公司和小团队,提供按需使用的垂类 AI 员工。内容工作不能停在生成正文,还要继续做资料核验、平台改写、配图、排版、后台预填和页面回读;最终公开动作则服从授权与平台真实状态。
低风险脑暴不需要完整 Reconciler。但只要工作存在异步检查、外部审阅、状态变化或不可逆动作,就应该把"有产物"和"可交付"分开。
Agent 提交 PR 后能不能下班,不由 PR URL 决定。答案在三样东西里:当前 revision、尚未清除的 blocker,以及最新的 readiness evidence。