loops-l1.md
2.95 KB
L1 · Unit Loop
最内层。在单一 Unit 范围内跑「写代码 → 跑单测 → 失败重写」直到全绿或耗尽预算。L1 是 L2 的子例程,几乎所有「自动化软件开发」体感来自这里。
状态机
状态字典
| 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 |
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 接管)。