资讯动态

多 Loop 协调实战:在 loop-engineering 中用状态文件、优先级与 advisory lock 防止 Agent 循环互相打架

发布时间:2026/9/23 22:08:55 来源:尧图企业网站定制
人工智能AI AgentAgent 工作流CLI研发协作AI 技能MCP 服务【免费下载链接】loop-engineeringPractical patterns, starters CLI tools for loop engineering with AI coding agents. Design systems that prompt and orchestrate agents (inspired by Addy Osmani and Boris Cherny). Includes loop-audit, loop-init, loop-cost.项目地址https://gitcode.com/gh_mirrors/lo/loop-engineering点击查看免费下载在同一个仓库中同时运行两个或更多 AI 编码 Agent 循环例如 Daily Triage 与 PR Babysitter是常态但让它们彼此之间没有边界就会演变成循环互相打架重复的修复提交、上下文串扰、预算被烧穿。本文以 loop-engineering 仓库的 docs/multi-loop.md 为核心结合 tools/loop-worktree 与 tools/loop-sandbox 的源码实现系统讲解多循环协调的五条原则、状态文件布局、冲突优先级栈、调度排布、基于acting_on与 advisory lock 的碰撞检测以及聚合 token 预算的落地方式。读完你可以为任何同时运行多个 Agent 循环的仓库设计出一套可执行的防冲突方案。为什么需要多循环协调真实的碰撞事故先看两个来自本仓库stories/的真实教训它们直接催生了 docs/multi-loop.md 中的协调规则。CI Sweeper vs PR BabysitterPR #318见 stories/multi-loop-collision.md。CI Sweeper 以/loop 15m运行、PR Babysitter 以/loop 10m运行两者同时盯上fix/auth-token-refresh分支。CI Sweeper 在 14:02 派生了 worktree 修复PR Babysitter 在 14:07 又在同一个 PR 上派生了另一个不同的 minimal fix——两个提交、两套互相冲突的方案审阅者被搞糊涂该 PR 的 token 消耗约 40 万正常约 8 万人工梳理耗时 45 分钟。根因是没有任何acting_on碰撞检查。Dependency Sweeper vs CI Sweepermain 分支见 stories/dependency-vs-ci-sweeper-collision.md。CI Sweeper 正在修复 main 上的测试回归时Dependency Sweeper 在预定时间把一笔安全的 minor 依赖更新直接合并到 red main引入传递性回归CI Sweeper 在已经损坏的新环境中继续修旧 bug一小时烧掉约 150 万 token两个循环还同时写loop-run-log.md导致 git push 失败和锁重试。根因是Dependency Sweeper 忽略了 CI 状态。这两起事故的共同教训多循环必须有显式的所有权边界与优先级纪律不能靠碰巧不同时运行。五条协调原则docs/multi-loop.md 将多循环协调凝练为五条原则这是所有后续机制的设计基础每条分支只有一个所有者One owner per branch——任何时刻至多一个循环可以在一个小时窗口内改动一条分支。状态文件彼此分离Separate state files——triage 用STATE.md动作类循环用各自 pattern 专属的状态文件。Triage 只报告动作循环才执行Triage reports, action loops execute——L1 的 Daily Triage 永远不与 CI Sweeper 的修复竞争。共享 denylistShared denylist——把同一份路径 denylist 复制进每一个LOOP.md保证所有循环遵守相同的禁区边界。聚合 token 预算Aggregate token budget——参见 templates/loop-budget.md.template。这三条原则分支所有权、状态隔离、职责分层正是事故故事中缺失的部分。本仓库自身在 LOOP.md 的 Multi-loop coordination 一节中直接引用了这份文档并把优先级固化为一句话CI Sweeper → PR Babysitter → Dependency Sweeper → Post-Merge / Changelog Drafteroff-peak→ Daily Triage只报告。推荐的状态文件布局协调的前提是每个循环有独立、可读、可被其他循环检查的状态。文档给出的推荐布局STATE.md # Daily Triage (priorities, human inbox) pr-babysitter-state.md # PR watcher ci-sweeper-state.md # Active CI failures attempt counts dependency-sweeper-state.md # In-flight package updates post-merge-state.md # Cleanup backlog loop-run-log.md # Append-only observability关键点STATE.md是唯一的人类收件箱与共享优先级源由 Daily Triage 写入本仓库由 scripts/github-triage.mjs 基于开放 PR 与 issue 生成见 LOOP.md动作循环各自持有 pattern 专属状态文件例如ci-sweeper-state.md记录当前活跃的 CI 失败 尝试次数loop-run-log.md是只追加的可观测日志。事故故事明确指出多个循环同时写它会引发 git push 冲突与锁重试因此写入必须错峰见下文调度。对应地每个循环 starter 都有配套的状态文件模板例如 starters/pr-babysitter、starters/ci-sweeper、starters/dependency-sweeper、starters/post-merge-cleanup 目录下的*-state.md.example可以直接复制使用。冲突时的优先级栈当循环冲突时文档给出了一张明确的优先级表低优先级循环必须让路优先级循环原因1CI SweeperRed main 阻塞一切2PR Babysitter活跃 PR 对时间敏感3Dependency SweeperCI red 时暂停4Post-Merge Cleanup非高峰时段、最低紧迫度5Daily Triage仅 L1 报告负责调度其他循环这个优先级栈直接来自事故复盘Dependency Sweeper 事故后文档明确了严格优先级栈——永远不要在高层级修复循环活跃时运行低优先级变更循环。Dependency Sweeper 现在必须在执行前做 pre-flight 检查读取ci-sweeper-state.md确认 main 的 CI 为绿色否则跳过执行并在 2 小时后重试。调度协调在 LOOP.md 中固化时刻表多循环的调度必须集中写在根目录 LOOP.md 中让每个循环的启动脚本都能读到并遵守。文档给出的示例## Multi-loop schedule - CI Sweeper: /loop 15m (active hours) - PR Babysitter: /loop 10m (active hours, skip if CI Sweeper acting on same PR) - Daily Triage: /loop 1d 08:00 - Dependency Sweeper: /loop 6h (skip if main CI red) - Post-Merge: /loop 1d 22:00调度纪律包含三点错峰Post-Merge 放 22:00 非高峰、Daily Triage 放 08:00、条件跳过PR Babysitter 在 CI Sweeper 正处理同一 PR 时跳过Dependency Sweeper 在 main CI red 时跳过、避免同一时刻并发写共享文件事故中loop-run-log.md写冲突就是由错峰 cron 解决。本仓库自身的实际运行节奏参见 LOOP.md 的 Automation status 表Daily Triage 走daily-triage.yml工作日执行PR Babysitter 与 Dependency Sweeper 目前处于 L2 手动/部分自动化状态——这也印证了先证明再自动化的渐进路线。碰撞检测从acting_on约定到 advisory lock第一层状态文件里的acting_on字段文档规定的基线做法是每个动作循环在写 fix 之前先把acting_on: branch-or-pr-id写进自己的状态文件。派发修复前按以下步骤执行读取所有其他 pattern 的状态文件若发现其他循环的acting_on与自己的目标匹配 → 跳过本次修复并记入loop-run-log.md。这正是 PR #318 事故后补上的机制CI Sweeper 现在拥有 red CI 的所有权PR Babysitter 在ci-sweeper-state.md显示对该 PR 有acting_on时跳过。第二层loop-worktree 将约定代码化为 advisory lock手写检查约定很容易被遗忘tools/loop-worktree 把它升级为可执行的 advisory lock建议锁非强制锁。在 tools/loop-worktree/src/cli.ts 中可以看到完整命令面loop-worktree lock --paths glob1,glob2,... --owner name [--ttl 6h] [--wait 15m] loop-worktree unlock --owner name loop-worktree locks [--sweep] [--force] [--json]--paths该 owner 即将触碰的路径 glob逗号分隔--owner锁身份通常是 pattern 名如ci-sweeper--ttl锁的自动过期时间如30m、6h、1d省略则永不过期、必须显式 unlock--wait路径被占用时的最长等待时间如15m超时失败locks --sweep报告过期锁仅报告加--force才删除。文档特别强调了一个容易踩坑的约定loop-worktree create本身不检查锁。锁的获取与 worktree 的创建必须由控制脚本成对调用——先lock --paths ... --owner ...再create完成后unlock --owner ...——就像loop-context --check与loop-worktree mark --status escalated成对使用一样这是由约定保证、而非由工具强制的设计。第三层锁的实现原理源码级在 tools/loop-worktree/src/lock.ts 中可以看到这套 advisory lock 的底层实现锁文件每个 owner 一个 JSON 文件存放在.loop-worktrees/locks/owner.jsonlock.ts中lockFile与LockEntry定义字段含owner、paths、lockedAt、可选的expiresAt路径重叠判定pathsOverlaplock.ts的pathsOverlap函数逐段比较两个 glob通配段*/**与任何段兼容较短路径只有在末段为通配符时才覆盖较长路径如src/**覆盖src/nested/foo.ts。源码注释明确这是deliberately simple——advisory lock不是完整的 glob 引擎跨进程原子性withLocksMutex用open(path, wx)独占创建.loop-worktrees/locks/.mutex文件来串行化检查-写入临界区POSIX 与 Windows 均原子否则两个lock调用可能在彼此写入前同时通过重叠检查——这正是该机制要防止的碰撞等待超过 5 秒会报错并提示手工删除残留 mutex死锁检测detectDeadlock--wait模式下等待方会把waitingOn写进owner.wait.json并用 DFS 在有向等待图中检测环发现环立即抛错如Deadlock detected: A - B - A解锁与清扫unlockOwner同时删除锁文件与残留 wait 文件幂等sweepExpiredLocks报告并配合--force删除超过自身 TTL 的过期锁与过期等待记录且绝不动仍在有效期内的活跃锁。对同一 owner 重复lock会替换该 owner 之前的锁路径与 TTL 一起更新因此一个循环在生命周期内可以持续续期或调整覆盖面。第四层loop-sandbox 通过--lock-paths复用同一套锁一次性 sandbox 运行的 Agent 本质上也是一种会与定时循环文件发生碰撞的控制脚本因此 tools/loop-sandbox 通过自己的--lock-paths选项复用同一套 advisory lock。文档明确指出这是 opt-in 的——裸的loop-sandbox run不受锁保护。在 tools/loop-sandbox/src/sandbox.ts 中可以看到集成细节runInSandbox在options.lockPaths非空时调用lockPaths({ root, owner: lockOwner, paths, ttl, wait })lockOwner默认为本次运行生成的sandbox-hexid清理逻辑特意在拆除 worktree 之前先unlockOwner释放锁因为锁是其他循环被阻塞的资源不应被慢速的 worktree 移除拖累即便lockPaths()因被其他 owner 阻塞且未设--wait而失败也通过lockRequested标志在清理时把可能残留的owner.wait.json一并扫掉。CLI 参数面见 tools/loop-sandbox/src/cli.ts--lock-paths Comma-separated globs to hold a loop-worktree advisory lock --lock-owner Lock owner name (default: the runs generated id) --lock-ttl e.g. 30m -- passed through to loop-worktrees --ttl --lock-wait e.g. 5m -- passed through to loop-worktrees --wait典型用法npx cobusgreyling/loop-sandbox run --lock-paths src/**,docs/** -- npx my-agent人类收件箱跨循环的歧义必须有人裁决当两个循环对同一件事都给出信号时机器不应自行裁决而是上交人类。文档建议在STATE.md中维护共享的 Human Inbox 区块## Human Inbox (ambiguous / cross-loop) - [ ] PR #42: CI Sweeper and PR Babysitter both flagged — human pick owner这与 LOOP.md 中的安全设计一致State 文件是live loop statedenylistshowcase HTML/CSS、核心 primitives 文档、audit 评分逻辑等不经人工审阅不得改动并由 tools/loop-gate 的loop-gate check机械地强制执行。聚合 token 预算让每个循环有独立且受限的资源文档第五条原则指向 templates/loop-budget.md.template。多循环场景下预算必须按循环拆分 聚合上限防止一个循环烧掉属于另一个循环的资源。模板给出了可直接复制的预算表结构LoopMax runs/dayMax tokens/dayMax sub-agent spawns/runDaily Triage2100k0 (L1) / 2 (L2)PR Babysitter2882M3CI Sweeper961M3Dependency Sweeper4500k3Post-Merge Cleanup1200k2模板还定义了超支后的标准处置流程On budget exceed暂停所有调度器scheduler_delete或禁用自动化在loop-run-log.md追加事件通知人类Slack / issue / STATE.md High Priority。以及全局 Kill switch命令或 issue 标签loop-pause-all只有人类在STATE.md中清除标志后才能恢复。Dependency Sweeper 事故中一小时烧掉约 150 万 token的教训正是这类预算与熔断机制要防住的场景。本仓库的观测与估算配套可参考 loop-budget.md、loop-run-log.md 以及 tools/loop-metricstoken 消耗指标与npx cobusgreyling/loop-cost --pattern name单循环估算。实战示例安全的三循环起步配置文档给出的推荐起步组合强调克制——不要一上来就上满所有循环LoopLevelCadenceDaily TriageL11dPR BabysitterL210mPost-Merge CleanupL1 → L21d off-peak并且有一个明确的准入门槛只有当 PR Babysitter 的尝试次数限制与 verifier 机制被证明稳定运行两周之后才把 CI Sweeper 加进来。这与 LOOP.md 的演进思路一致——仓库自身的 PR Babysitter、Dependency Sweeper、CI Sweeper 目前都停留在 L2 手动或部分自动化就是在用真实运行数据验证可靠性后再逐步自动化。把上述机制组装起来一个受保护的循环控制脚本的骨架大致如下# 动作循环如 CI Sweeper开始前 loop-worktree lock --paths src/** --owner ci-sweeper --ttl 6h --wait 15m loop-worktree create --run-id $RUN_ID --pattern ci-sweeper # ...在 worktree 内执行修复、验证器校验... loop-worktree mark --run-id $RUN_ID --status accepted|rejected|escalated loop-worktree unlock --owner ci-sweeper # 一次性沙箱运行则改用等价、自带清理 npx cobusgreyling/loop-sandbox run --lock-paths src/** -- npx my-agent结语边界即协同多循环协调的本质不是减少循环而是为每个循环划定显式的所有权边界分支归谁、状态文件在哪、何时运行、冲突时谁让路、超预算时怎么熔断。loop-engineering 给出的这套方案是分层的——先靠acting_on状态约定再用 tools/loop-worktree 的 advisory lock 把约定变成可执行命令配 TTL、等待与死锁检测最后让 tools/loop-sandbox 的一次性运行也并入同一套锁面。配合 docs/multi-loop.md 的优先级栈与 templates/loop-budget.md.template 的预算表你可以在自己的仓库里复现这套多个独立 worker 只通过定义良好的共享信息通信、绝不共享内部状态的协同模型避免重蹈 PR #318 与 main 分支那两次事故的覆辙。赞分享人工智能AI AgentAgent 工作流CLI研发协作AI 技能MCP 服务【免费下载链接】loop-engineeringPractical patterns, starters CLI tools for loop engineering with AI coding agents. Design systems that prompt and orchestrate agents (inspired by Addy Osmani and Boris Cherny). Includes loop-audit, loop-init, loop-cost.项目地址https://gitcode.com/gh_mirrors/lo/loop-engineering点击查看免费下载相关推荐Loop Engineering 实战模板用 goal、Maker/Checker 与状态文件构建自治 Agent 循环Loop Engineering 实战模板用 goal、Maker/Checker 与状态文件构建自治 Agent 循环 本文基于 Learn Harnessloop-worktree 公开 lock 子路径用 advisory 路径锁为多循环协作兜底loop worktree 公开 lock 子路径用 advisory 路径锁为多循环协作兜底 loop worktree 是 loop engineerin人工智能AI AgentAgent 工作流CLI研发协作AI 技能MCP 服务Loop Engineering 多循环失败模式从碰撞事故到 branch lock 与优先级栈的实战复盘Loop Engineering 多循环失败模式从碰撞事故到 branch lock 与优先级栈的实战复盘 导读 当同一个仓库里同时跑着多个 AI 编码 A人工智能AI AgentAgent 工作流CLI研发协作AI 技能MCP 服务创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价