状态机
状态字典
| state | 谁写入 | 含义 | 退出条件 |
|---|---|---|---|
pending | Architect 创建 unit 时 | spec 已就绪,等待 LoopController 调度 | 有空闲 Coder Agent → coding |
coding | LoopController | Coder 正在写 code/ 下文件 | Coder 返回 success → testing |
testing | LoopController | Tester 正在 vitest run | 全绿 → passed · 红 → failed |
failed | Tester | 本轮单测失败,retries++ | retries < budget → coding · 否则 → blocked |
passed | Tester | 单测全绿,等所有 sibling Unit 跟进 | 所有 Unit passed → L2 gate |
blocked | LoopController | 耗尽 retry,无法自愈,需 L3 介入 | 人类反馈 → 重置回 pending |
调度与并行
LoopController 复用 server/src/core/scheduler.js 的 getExecutionGroups():根据 depends_on 把所有 pending Unit 分成一波一波。同一波之间并行,跨波串行。
// units.json 节选 { "units": [ { "id": "auth-be", "depends_on": [] }, { "id": "profile-be", "depends_on": [] }, { "id": "auth-fe", "depends_on": ["auth-be"] }, { "id": "profile-fe", "depends_on": ["profile-be"] } ] } // scheduler.getExecutionGroups() → // wave 1: [auth-be, profile-be] (parallel) // wave 2: [auth-fe, profile-fe] (parallel)
Coder → Tester 握手协议
两个 Agent 通过文件系统握手,不直接通信。这让任意一个 Agent 死掉都不会丢上下文。
Coder 写入
units/{id}/code/**— 源码units/{id}/contract.json— 暴露给下游的契约units/{id}/coder.log— 决策日志(供 retry 时回顾)
Tester 读取 + 写入
- 读
units/{id}/code/** - 读
units/{id}/spec.json推导测试用例 - 写
units/{id}/tests/** - 写
units/{id}/runs/{runId}/result.json
重试策略 — retryWithReflection
从 evolution-loop.js 抽出共用函数。每次 Tester 失败,把失败原因、上次 Coder 的决策日志、原 spec 一起喂回 Coder,让模型「反思」上次错在哪。
async function retryWithReflection(unit, budget) { let failures = []; for (let i = 0; i < budget; i++) { const coderInput = { spec: unit.spec, history: failures }; await agentManager.callAgent('coder', coderInput); const r = await agentManager.callAgent('tester', { unit }); if (r.ok) return { ok: true, retries: i }; failures.push(r.failures); } return { ok: false, reason: "budget_exceeded" }; }
预算与降级
R1 风险联动:每个 unit 全局 retry 预算 = 5(可在
units.json 用 retry_budget 覆盖)。耗尽后置为 blocked,LoopController 不会自动进 L2,而是发 SSE 事件等待人类介入(L3 接管)。