[{"content":" 一个多月、四十万行代码、五个甚至更多Claude Code会话同时开工之后，我发现真正稀缺的东西已经不是模型的产能，而是我自己的注意力。这篇文章讲我为什么、以及怎么做了goal_control——一个管很多Coding Agent同时干活的薄控制面。它不是产品说明书，是一份带着教训的工程复盘。\n一、瓶颈换了地方 五个终端同时闪 先说一个场景：某天晚上九点多，我面前开着五个Claude Code终端，各跑一个任务。左上角那个在问我\u0026quot;接口返回404还是409\u0026quot;，右边那个刚跑完想让我确认能不能提交，下面一个撞上了权限确认在等我按回车，另外两个我已经不太记得它们跑到哪儿了。\n我回答完左上角那个，切到右边，看了两分钟diff，回过头发现左上角那个又停了，这次问的是另一个问题。等我把五个都扫一遍，最早那个已经空等了二十分钟。\n这就是过去一个多月的常态：该项目项目40+万行代码、前后端好几个仓（多编程语言），周期短，强度高，我几乎全程用多会话并行开发。刚开始两三个会话并行的时候很爽，产能肉眼可见地翻倍。到了五个以上，情况开始变化：Agent还是那么能干，但我不行了。\n具体说，我遇到了四个问题：\n注意力被持续打断： 每个会话在任意时刻都可能停下来找我，澄清、审批、权限确认混在一起，随机到达； 多任务状态说不清： 现在哪些在跑？跑到什么程度？谁做完了？做完了下一个该起谁？这些问题我要靠翻终端和记忆回答。 任务之间靠人衔接： 一个任务跑完，是我去判断\u0026quot;可以起下一个了\u0026quot;，然后开新会话执行下一个任务。Agent能自主完成\u0026quot;一个任务\u0026quot;，但\u0026quot;很多任务\u0026quot;的调度全在我手里。 决策不可回溯：一个任务跑几个小时，中间我做了七八次决定。事后想知道\u0026quot;它为什么最后长成这样\u0026quot;，得在几十万token的对话里翻。 说实话，头两周我以为这是我自己不够专注。后来我意识到不对：人脑本来就不适合当抢占式调度器。\n说白了是两类问题，不是四个 我把这四个问题放在一起看了很久，最后的结论是：它们不是四件事，是两件事，而且有明确的因果链。\n问题1、2、3的根子是同一个：状态没有外置。 Coding Agent会话的\u0026quot;状态\u0026quot;是隐式的，散落在对话历史里。会话自己到后期都要花力气回忆\u0026quot;我是谁、我在做哪一片\u0026quot;，你当然无法观测一个从未被写下来的东西。状态不外置，就没法知道谁跑到哪儿；不知道谁跑到哪儿，就没法自动触发下一个；于是一切都得人来衔接。\n有意思的是，\u0026ldquo;进度\u0026quot;这件事我其实早就定义好了。我的开发方法是先把需求拆成任务切片，每片有验收标准，再把切片聚成Goal编排成依赖图。进度就是\u0026quot;第几片完成了\u0026rdquo;，粒度天然存在。缺的只是让会话把这个状态持续写到上下文之外的某个地方。\n问题4才是真正的硬问题：交互模型是中断驱动的。 每个Agent都可以在任意时刻以最高优先级抢占我，而人的上下文切换成本是分钟级的。N个Agent 的打断随机到达，我被迫在脑子里做抢占式调度。这件事跟\u0026quot;状态透不透明\u0026quot;无关，就算每个会话都把状态写得清清楚楚，它们还是会随时打断我。\n所以，解决问题的顺序也就清楚了：先做状态外置，再改交互模型。前者是后者的前置条件。\n为什么原生Coding Agent不管这件事 有人会问，Claude Code、Codex这么强，为什么不直接管这件事？\n我的看法是：它们的一等公民是\u0026quot;会话\u0026quot;，而我需要的一等公民是\u0026quot;目标\u0026quot;。一个会话就是一个Agent加一段上下文，它天生擅长把一个任务做完。但\u0026quot;很多任务之间的依赖、调度、状态汇总、决策排队\u0026quot;这些东西不属于任何一个会话，它们属于会话之上的那一层。原生工具没有这一层，不是能力不够，而是这不在它的抽象范围内。\n好在原生工具把这一层需要的原语都给了。Claude Code有headless模式（claude -p），可以像跑一个脚本一样跑一个Agent；有--resume可以续回一个会话；有stream-json输出格式可以逐行拿到会话的每一步。采集面和控制面的原语都在，缺的只是我这一侧薄薄的一层约定和聚合。\n我先否掉的方案：K8s式编排层 动手之前我评估过一个很自然的想法：既然有很多\u0026quot;工作负载\u0026quot;要调度，能不能做一个K8s式的会话编排层？声明式期望状态、控制循环、健康探针、自愈重启。\n结论是：思想能借，形态不能抄。\n能借的是三样：期望状态与实际状态分离（Goal编排文档是期望状态，外置的运行状态是实际状态）；控制循环（持续比对\u0026quot;该跑而没跑\u0026quot;并触发）；统一的状态上报协议。\n不能抄的原因在于负载性质完全相反。K8s管的是同质、无状态、可廉价重启的进程，核心价值是自愈、扩缩容、资源装箱。Agent任务恰恰相反：有状态、重启昂贵、不可抢占、完成判定模糊。你不需要调度器决定任务跑在哪，不需要副本数，更不需要健康探针——Agent的\u0026quot;健康\u0026quot;是语义层面的，一个进程活着但在原地打转，探针探不出来。\n更贴切的心智模型其实是CI/CD流水线 + 带人工审批门禁的收件箱：Goal约等于一个CI job，依赖约等于needs:，人工决策点约等于when: manual。任务是一次性的，有明确产出物（一个提交），失败了重跑而不是迁移。\n于是goal_control的形态就定下来了：不是一个平台，是\u0026quot;文件 + 薄脚本 + 一个人机协作控制台\u0026quot;。\n二、我的解决思路 这一部分讲设计思想，不讲功能。功能在后面。\n先说清两个词：任务切片与Goal 我简单介绍一下我的部分开发模式，先建立一个基本认知就够了。\n任务切片是可以独立实现、独立测试、独立合并的最小单元，自带验收标准。它是开发方法层面的粒度：拆解需求的时候产出的就是它。一片切片通常对应几个文件的改动和一组测试。\nGoal是拓扑序上连续的2到4个切片。它是执行层面的粒度：一个Goal对应一段完整的 Prompt，交给一个headless会话一口气跑完，组尾做一次验证。为什么不能只用一种粒度？因为两种粒度服务的对象不同。切片太细，如果每片起一个会话，会话之间要反复重建上下文，启动开销和上下文丢失都吃不消；Goal太粗，如果只以Goal为单位汇报，一个跑三小时的Goal中间就是黑盒，你不知道它卡在第一片还是第三片。所以调度按Goal，汇报按切片。\n任务组则是一份清单，也就是一张Goal之间的依赖图，goal_control调度的对象是Goal和Goal之间的边，切片只是它眼里的进度刻度。\n1. 会话是易耗品，Goal才是一等公民 这是整个设计最底层的一条：Session不是一等公民，它只是Goal某一次运行的执行载体，可以死、可以被--resume、可以换一个新会话接着跑。Goal的状态必须活在会话之外，才谈得上一切：谁在跑、卡在哪、下一个起谁、事后复盘。\n对应到实现上，claude -p起一个headless进程，输出逐行落盘，退出即被收割。Goal状态文件里只记一个session_id，作为\u0026quot;续回去\u0026quot;的句柄。人的每一次答复、每一次介入，本质上都是\u0026quot;结束当前进程，带着新信息把同一个会话续起来\u0026quot;。\n2. 状态必须活在会话之外 我给执行会话定了一条协议：编排层不读你的对话记录，goalctl命令是你向外汇报的唯一通道。 每完成一片跑slice-done，阶段性进展跑note，要人拍板跑ask，做完了跑 done。这段协议头会被拼在每个Goal的Prompt前面，逐字送进claude -p。\n有人会说，靠Agent自觉汇报靠得住吗？长上下文后期它会不会忘？会。所以除了它自报的语义状态，还有一层机械状态：runner持有子进程句柄，进程退出了它一定知道；日志是逐行落盘的，Agent每一步做了什么都在盘上。语义状态告诉你\u0026quot;它在做第几片\u0026quot;，机械状态告诉你\u0026quot;它还活着吗、在等人吗\u0026quot;。两层互补。\n我曾经想过让一个\u0026quot;监督Agent\u0026quot;去读其他Agent的完整transcript来汇总状态。很快放弃了：让LLM转述LLM的状态是一条不可靠的信道，token成本极高，而且摘要会失真——我踩过\u0026quot;收尾摘要跟事实相反\u0026quot;的坑，结构化状态文件才可靠。\n3. 注意力调度：让Agent少打断人，而不是让人更快响应 这是goal_control最重要的一个设计方向。我把它压缩成一句话：\n不是让人更快地响应Agent，而是让Agent更少地打断人。 怎么做到？四步：\n第一步，给\u0026quot;打断\u0026quot;建立分类学。 过去所有打断在我眼里是同一种东西，这就是疲劳的来源，实际上至少有四类：\n类型 例子 应有的处理方式 阻断性决策 需求歧义、方案二选一、范围裁决 进队列，Agent停下等答复 危险操作审批 删数据、推送、改共享配置、跨阶段门禁 高风险实时推送；低风险应预授权放行 阶段性验收 一个Goal收尾、验收标准全绿待确认 天然的批处理点，攒着 FYI / 进度通知 \u0026ldquo;第3片完成了\u0026rdquo; 只写状态文件，永不打断 只有前两行的高风险子集配得上实时打断你。经验上，这部分不到全部打断的十分之一。\n第二步，把决策收件箱做成一等公民。 Agent遇到需要人的事项，不是在终端里干等，而是写一个结构化的决策请求：背景摘要（不需要读对话记录就能懂）、选项、它自己的推荐和理由、风险等级。写完它就结束回合，把进程让出来。人在自己选的时间集中清空收件箱，答案写回文件，runner用--resume 把答案送回原会话。\n这里的收益不只是打断次数变少，同类决策放在一起做，决策质量更高，上下文相近的裁决放在一起，比随机到达的裁决做得好，这是我实际体会到的。\n第三步，从源头减少决策请求的产生。 这一步比前两步都便宜。权限确认这种机械打断，靠允许清单和Goal Prompt里的预授权条款就能消掉大半。对低风险歧义，协议里明确要求 Agent按自己的推荐默认值继续执行、把假设记下来，人在验收时批量复核，有异议再翻案。把\u0026quot;事前阻断\u0026quot;变成\u0026quot;事后可否决\u0026quot;，是消灭队列长度的最强手段。还有决策点前移：很多中途打断其实是切片定义不清的下游症状，在拆解阶段多花十分钟封口语义，比运行期处理五个澄清请求便宜得多。\n第四步，只保留一条高优先级打断通道。 真正的高风险事项（不可逆操作、整组阻塞）走桌面通知实时触达，而且这条通道必须保持低频。一旦它变成常规通道，你就退回原点了。\n4. 管一个Goal的一生，而不只是\u0026quot;跑没跑\u0026quot; 我要管的不是\u0026quot;进程在不在\u0026quot;，是一个Goal从进入系统到沉淀下来的完整过程：什么时候能开工、开工了跑到哪、跟别的Goal什么关系、中间谁介入过、说了什么、什么时候算完、产出了哪些提交、事后怎么复盘。\n这决定了状态模型必须显式：一个Goal有八个真实状态（待启动、等门禁、执行中、被按停、等额度、等人裁决、失败、完成），每一次跃迁都记进只追加的事件流水，每一次会话输出都完整落盘。复盘不是事后\u0026quot;总结\u0026quot;出来的，是这些事实原样拼出来的。\n5. 尽量并行，但\u0026quot;安全\u0026quot;的边界是仓 Goal之间的依赖是显式的DAG。没有前置依赖的Goal可以并发，前置全DONE的Goal自动解锁，一批完成自动进入下一批。这部分是纯机械动作，难点在于\u0026quot;完成\u0026quot;这个事件得能被机器检测到，而这正是状态外置解决的。\n但有一条边界我坚持得很死：同一个代码仓同一时刻只跑一个Goal（worktree除外）。 原因很实际。多个会话共用一个工作区时，收口那一步的git add会把别人的半成品一起卷进来，这是真实发生过的事故。依赖图上能并行，不代表在同一个工作区里能安全并行。goal_control的并行度上限是\u0026quot;仓的数量\u0026quot;，不是\u0026quot;依赖图的宽度\u0026quot;。\n6. 放弃虚假的ETA 复杂Coding Agent任务的执行时间不确定性很大。同一个Goal，代码侦察可能十分钟也可能四十分钟，一次全量验证可能一个半小时。给一个看起来精确的\u0026quot;预计14:32完成\u0026quot;，价值很低，而且会让人产生错误的预期。\n所以goal_control明确不做ETA。它只呈现现在是什么状态、进度到第几片、当前在做什么、是不是阻塞、要不要人。切片计数就是最好的进度条，它粗糙，但它是真的。\n7. 不信任Agent的自述 Agent说\u0026quot;我做完了\u0026quot;，只是它觉得它做完了。是否真的完成由机械复核裁决：跑一遍该仓的验证命令，检查其他仓有没有被越界改动，两条都过才允许提交。这不是对模型的不信任，是对任何\u0026quot;自述\u0026quot;的不信任。人也一样。这条原则后来抓到过一次真实的\u0026quot;只说不做\u0026quot;，后面会讲。\n8. 控制面要薄 在\u0026quot;零基础设施\u0026quot;和\u0026quot;完整Supervisor Agent\u0026quot;之间，我选了中间偏轻的位置：文件承载状态，脚本承载调度，人只通过收件箱和一个控制台介入。没有数据库，没有消息队列，没有常驻的监督Agent。\n为什么不上Supervisor Agent？因为它引入了一个新的不可靠层。监督者自己也会错，而你没法再雇一个监督者去监督它，薄的那一层已经覆盖了九成收益。\n三、goal_control是怎么设计的 这一部分讲机制，目标是让你理解：一个Goal是怎么进来、怎么被调度、怎么跑、什么时候找人、最后怎么完成和沉淀的。\n一张图看全貌 图3里有四个角色：\n清单是期望状态：一份JSON，列出每个Goal的依赖、所属仓、Prompt文件、开工前门禁、超时、模型与推理强度，以及每个仓的验证命令，一份清单就是一个任务组。 runner是控制循环：它每20秒reconcile 一轮，读清单和状态文件，按固定顺序做九件事：落实人点下的暂停、收割已退出的进程、处理超时、续跑人工介入过的会话、续跑人答复过的会话、恢复额度限流的会话、处理门禁、启动就绪的Goal、全部完成后跑一次任务组级全量验证。 执行会话是易耗执行体：claude -p起的headless进程，Prompt是协议头加Goal正文，输出以stream-json 逐行落盘。它通过goalctl向外汇报，通过两个锚点行告诉runner本轮的结局。 状态文件是实际状态，也是整个系统唯一的事实来源：每个Goal一个JSON，每条决策一个JSON，一份只追加的事件流水，人工介入档案，以及每次运行的完整日志。控制台读这些文件，人的答复写回这些文件，runner下一轮读到，闭环。 有一个细节值得单独说：控制台没有任何内存状态。 它是标准库起的HTTP服务，每次请求都重读文件出JSON。这意味着服务重启不丢任何决策，页面刷新后跟文件完全一致，命令行和页面可以同时操作同一条消息（靠文件锁串行化）。这不是懒，是刻意的：状态只在一个地方，就不会有两份真相。\n一个Goal是怎么进来、怎么被调度的 图4是Goal的状态机，三种颜色对应三个写者：深色是Agent自报，青色是runner推进，琥珀色是人裁决。\nGoal从PENDING开始。配了开工前门禁的，要等前置依赖全DONE才开审批单，转GATED等人放行——依赖没跑完就开单，等于让人核对一个还不存在的成果。 依赖全DONE、门禁放行、该仓没被占、工作区干净，四条齐了runner就起claude -p，转RUNNING。控制台把\u0026quot;下一轮就起\u0026quot;派生为READY显示，判据跟runner一字不差，否则就会\u0026quot;显示READY但什么都没发生\u0026quot;。 RUNNING期间有一条硬规矩：状态文件只有执行会话一个写者，runner不写。 超时收割和人工暂停是例外，也得先终止进程、等它退出才落笔——原子写只防半截文件，不防互相覆盖，这条不变量只能靠流程。 从RUNNING出去有五条路：Agent问人（BLOCKED_ON_HUMAN）、人按停（PAUSED）、额度限流（QUOTA_WAIT，到点自动续跑）、自述完成（进机械复核，派生VERIFYING）、超时或异常退出（FAILED）。前三条都回RUNNING，靠同一个动作：--resume带着新信息续回原会话。\n图6是一次真实任务组的依赖图：六个Goal、十五个切片、三道门禁。它说明了两件事。一是波次是自动推进的，一个Goal DONE之后下游在下一轮自动解锁，我不需要\u0026quot;点下一个\u0026quot;。二是G-02和G-03在依赖图上可以并行。\n什么时候找人：两条通道，故意不合并 goal_control里有两条人机通道，方向相反，我刻意没有合并它们。\n第一条是收件箱：Agent找人。 图5是一次完整的决策闭环。\nAgent要拍板就执行goalctl ask，带上背景、选项和风险等级：写一条OPEN消息、把Goal置为BLOCKED_ON_HUMAN，然后进程退出——它不会在那儿空等你。人答完，消息变 ANSWERED；runner下一轮用--resume 把答复注入原会话，消息变APPLIED，Goal回到RUNNING，上下文没丢。门禁审批走同一条通道。\n写者分工是硬的：OPEN到ANSWERED只有人能做，ANSWERED到APPLIED只有runner 会做。\n第二条是介入：人找Agent，看实时流发现方向偏了，就按停、补一段指导、接着跑。为什么不塞进收件箱？收件箱的语义是\u0026quot;Agent在等你\u0026quot;，它驱动热泳道告警。人自己发起的动作混进去，这句提醒就不可信了。\nclaude -p是一次性headless进程，stdin空着，没有控制通道，\u0026ldquo;不杀进程的暂停\u0026quot;不存在——暂停就是\u0026quot;终止进程，再--resume 续跑\u0026rdquo;。我拿真实会话验证过，续回去上下文不丢、session_id不变。而持有子进程句柄的只有runner，所以页面上点\u0026quot;暂停\u0026quot;只是写一张请求单，下一轮才落实。\n怎么完成、怎么沉淀 Agent跑完全部切片，执行goalctl done留下完成自述，然后退出。runner收割到自述后，进入图7的收口流水线。\n第一步是机械复核：逐仓跑验证命令，查有没有越界改别的仓。越界按\u0026quot;比启动时多出来的\u0026quot;算，不按\u0026quot;其他仓必须干净\u0026quot;算——工作区常年压着无关改动，后一种口径每个Goal都假失败。复核不过就判FAILED，不提交任何改动；人修好后goalctl finish 只重跑复核。 复核过了，逐文件git add（绝不用-A），提交信息带story前缀和自述，推送后回填评审地址。推送有三种拿不准的情形：本仓已领先远端、待补推的sha对不上HEAD、push失败。一律停下交给人，本地提交不丢——宁可让人多点一次，也不替人推别人的提交。 沉淀是自然发生的：状态跃迁、决策正文、逐行日志各自落盘，复盘页原样拼出来。 四、现在能做什么 1. 接管一个项目 配置工作区 goal_control是独立仓，被它编排的工作区在另一个地方。在清单里配一个workroot 指向工作区根，再列出各子仓的验证命令，这个项目就被接管了。 每个Goal声明自己的仓、依赖、Prompt文件；跨前后端的Goal用repos声明允许改动的全部仓，启动前置、并发占用、越界判据、逐仓复核和逐仓提交均按整批仓算。 按Goal绑定模型 每个Goal可单独绑定执行模型与推理强度。简单的片用快模型，难的片上更强的模型。未配置则一字不传，由Claude Code默认决定——升级不会悄悄改动任何存量Goal的执行。 任务组切换 一份清单对应一个任务组，换任务组就换一份清单，状态和日志按Goal ID共存，互不干扰。 2. Goal总览 顶栏四个数字回答四个问题：等你决策几条、执行中几个、阻塞几个、已完成几个。这里的\u0026quot;阻塞\u0026quot;口径是刻意放宽的：等人裁决、等门禁、等额度、失败，四种都算，因为它们都是\u0026quot;不动它就永远不会动\u0026quot;，漏掉任何一种顶栏数字就骗人。 左侧导轨按story分组列出全部Goal，每行带状态、切片进度、所属仓、模型短标。被卡住的Goal打呼吸的\u0026quot;等你裁决\u0026quot;角标。 3. 决策队列（重要） 这是最重要的一页，消息分三组：\nREALTIME 实时通道：高风险且未推迟的OPEN，唯一允许打断你的。 BLOCKING阻断性决策：真的停住了某个Goal的。 DEFERRABLE可后置：其余的，Agent已按默认值继续，你复核或翻案。 只要有决策真卡着 Goal，控制台就不许它安静：自动弹窗、顶栏横幅、导轨角标、标签页标题变成\u0026quot;(2) 等你裁决\u0026quot;。决策能数字键加回车快速过，审批不行——不可逆的动作只能明确点下去。\n\u0026ldquo;稍后处理\u0026quot;只压住弹窗，横幅、角标、标题一个不少。藏起来的只有打扰，阻塞藏不掉。\nAgent写的决策正文常常上千字。控制台把它按角色分层：决定、候选、事实、时机、后果。只还原不改写，规则没命中就退回原样显示。跳过说明文字的审批等于没有审批，这一层就是为了让人不跳过。 答复能捎一句自由指令，随答案注入会话。也可以\u0026quot;下一轮再看\u0026rdquo;——只影响排序，Goal该卡还是卡着。开了提醒开关，这两组会用桌面通知加提示音叫你。 没做的：低风险预授权直接放行。开关摆在页面上但禁用着——谁有权替人放行、怎么留审计，还没想清楚。\n4. 调度DAG 依赖图按拓扑层序展开，每层一行。头部四个指标：\n关键路径长度 理论并行度 当前前沿 依赖漂移（前置未DONE却已开跑的数量）。 READY节点有一个\u0026quot;启动\u0026quot;按钮，runner常驻时禁用并提示\u0026quot;会自动调度\u0026quot;，runner不在时可以手工推一轮。\n5. 执行态 每个在飞的Goal一张卡片：\n状态 已运行时长 切片进度条 最近三条事件 进程号 本轮实际用的模型和推理强度 累计成本 进度条的分母来自切片声明，切片标题从Goal正文里解析出来。\n6. 执行过程与人工介入 这一页是会话的实时执行流展示（这个板块目的是为了提高”心理安全感“😂）：说明文字、思考块、工具调用与返回、心跳、答复接缝、收尾汇总。日志是stream-json 逐行落盘的，所以边跑边看，进程被杀也保住已发生的；轨迹按字节游标增量读。 \u0026ldquo;介入这次执行\u0026quot;就是那条通道：停下会话，写下判断，它带着完整上下文接着做；留空提交就是\u0026quot;看了一眼，没问题\u0026rdquo;。每次介入都归档，此前介入过几次、说了什么都在。 它必要，但不该成为常规操作。频繁介入多半是切片或Goal定义出了问题，该回拆解阶段修。 7. 复盘 Goal完成以后，复盘页回答\u0026quot;这个任务为什么变成了现在这样\u0026quot;：会话次数、墙上时钟、累计成本、对话轮次、token； 状态机走过的路和每态停留时长；每段会话的起因、退出码和成本；每条决策的提问、答复、时间；逐仓提交和评审地址；以及完整事件流水。 8. 其它顺手的能力 查看完整Prompt：这Goal真正被送进claude -p的那一整段，协议头加正文，逐字一致，一个\u0026lt;pre\u0026gt;原样渲染，不切段不高亮，所见即所执行。 命令行双入口：goalctl status、goalctl inbox、goalctl answer，跟页面走同一条写路径。控制台挂了不影响编排。 离线只读看板：runner每轮自动重生成一个静态HTML，控制台起不来时的兜底。 额度限流自动重试：撞上用量限制自动等到恢复时间续跑，不需要人。 五、真实跑了一个任务组之后 数据来自一次真实的任务组执行：六个Goal、十五个切片、三道门禁，落在一个后端仓和一个前端仓上。\n一昼夜的账 Goal 切片 Agent提问 门禁 会话段 成本（美元） 备注 G-01 3 2 0 3 12.1 首轮3小时14分超时被收割，人工续回 G-02 3 1 0 2 50.3 提问四选一，人两小时后答复 G-03 1 2 1 3 18.3 G-04 3 1 1 2 21.2 机械复核拦下一次 G-05 2 1 0 2 25.3 切片里的\u0026quot;证据\u0026quot;与代码不符，Agent停下来问 G-06 3 0 1 1 22.6 零提问，两处保留意见记在note里 十二次人工答复，约一百五十美元，从G-01续跑到G-06收口墙上时钟约一昼夜。其中十三个多小时是G-06在等一个被我弄脏的前端工作区——这个后面单说。\n打断次数：变少了，更重要的是变\u0026quot;可选\u0026quot;了 先说诚实的部分：这次实跑里，\u0026ldquo;批量清空收件箱\u0026quot;只真正发生了一次。那天下午两点零六分，我一口气答了两条，一条门禁、一条四选一的方案裁决，它们从上午十一点五十几分起就在队列里等着，而我那两个小时在做别的事。其余的答复大多在一到四分钟内完成，因为新工具上线第一周我其实是盯着的。\n所以\u0026quot;打断次数明显减少\u0026quot;目前是一个观察，不是一个测量。但有几类打断确实消失了，这是确定的：权限确认没有了（claude -p用自动模式加协议约束）；\u0026ldquo;跑完了吗\u0026quot;这类进度询问没有了（看导轨就行）；\u0026ldquo;下一个起谁\u0026quot;没有了（runner自己起）。剩下的打断只有两种：Agent真的拿不准的裁决，和门禁。\n更重要的变化是打断变成了\u0026quot;可选的\u0026rdquo;。Agent停下来等我的时候，它是安静地停在文件里，不是在终端里闪。什么时候去处理，是我决定的。\n\u0026ldquo;可控\u0026quot;具体指什么 过去同时跑五个会话，我最怕的不是某个会话出问题，是我不知道哪个出了问题。现在\u0026quot;可控\u0026quot;的意思很具体：任何时刻，下面四个问题我都能在三秒内回答，而且答案来自文件，不来自记忆。\n哪些Goal在跑，跑到第几片？ 哪些Goal停了，停在什么状态，等的是什么？ 有几条事在等我，哪一条真的卡着执行？ 下一个会起谁，为什么现在还没起？ 第四个问题过去是最难回答的，现在DAG页的\u0026quot;当前前沿\u0026quot;和dry-run直接给出来。\n意外收获 决策被文档化了：G-02那条四选一的裁决，Agent写了两千多字的背景：切片定义的文件域漏掉了装配点，切片要求\u0026quot;不产生迁移\u0026quot;但数据库的 CHECK 约束让这条不可能成立，四个出口各自的代价是什么，它推荐哪个、为什么。我读完就能拍板，不用翻它的对话记录。事后想知道\u0026quot;为什么这个Goal动了迁移文件\u0026rdquo;，那条消息就是答案。\n切片的\u0026quot;漂移守卫\u0026quot;真的救了场：G-05的切片里有一条标为\u0026quot;证据等级A\u0026quot;的代码现状描述，跟库内原文不符。按原文件域实施会写一个全仓没有读者的计数，验收静默失败。Agent按守卫条款停下来问，而不是硬做。这说明\u0026quot;决策点前移\u0026quot;和\u0026quot;运行期收件箱\u0026quot;是配合工作的：拆解阶段封口越严，运行期的提问质量越高。\ndefault-with-veto 在工作：G-06全程零提问，但留了两处\u0026quot;须知\u0026quot;在note里：一处接线不在文件域内所以没接，一处接缝暴露了但没有调用方。这正是协议要求的行为：低风险歧义按推荐默认值继续，把假设记下来。\n成本可见了：过去我不知道一个Goal花多少钱（订阅制）。现在每段会话的成本、轮次、token都在复盘页上，累计一个任务组约一百五十美元，这个数字第一次有了。\n六、下一步 支持更多Coding Agent goal_control对Claude Code的依赖其实很薄：一条启动命令、一条续跑命令、两个锚点行、一段协议头、一种日志格式。这些都集中在启动器和轨迹解析两个模块里。Codex也有headless和goal特性，接进来主要是适配层的工作。真正需要抽象的是\u0026quot;一个执行体应该提供什么能力\u0026rdquo;：可启动、可续回、可逐行观测、可被终止。\n从Goal模型再抽象一层 目前整个系统建立在我自己的\u0026quot;切片 → Goal → DAG\u0026quot;方法之上。清单的字段、切片轨道的解析规则、组尾验证的约定，都带着这套方法的形状。\n我想把它抽象成四个更通用的概念：工作单元（一段Prompt加一个工作区加一组完成判据）、依赖、完成判据（机械可验证的那部分）、决策协议（工作单元怎么向外汇报、怎么提问）。切片和 Goal只是这四个概念的一种实例化。做到这一步，用别的任务组织方式的人也能用它。\n其它值得做的方向 低风险预授权。 现在所有决策都要人点一下，default-with-veto的假设记录目前只落在note里，不是一等对象，所以\u0026quot;可后置\u0026quot;那一组在真实数据里基本是空的。把假设做成可复核、可翻案的对象，是让队列长度真正下降的下一步。 hooks兜底采集。 现在的机械状态靠runner持有的进程句柄，Claude Code的hooks可以在会话不知情的情况下把\u0026quot;停下来了、在等输入\u0026quot;这类事件写进中央日志，是更优雅的采集点。 会话恢复的一等入口。 超时被收割的会话，其transcript还在，session_id就是文件名。现在恢复靠一套手工步骤，应该做成一个命令。 进度感知的超时。 硬超时会误杀一个还在正常推进的长Goal。既然有逐行轨迹，\u0026ldquo;多久没有新进展\u0026quot;比\u0026quot;跑了多久\u0026quot;更适合当收割判据。 多人协作与鉴权。 控制台只绑本机、不做鉴权，因为它现在只服务一个人。团队用需要这一层。 最后 中断这个机制是为CPU发明的，它成立的前提只有一条：CPU的上下文切换便宜到可以忽略。可就算是CPU，负载一高也扛不住。Linux的网卡驱动里有个叫NAPI的机制，包来得太密的时候，它做的第一件事是把中断关掉，改成内核自己按节奏去轮询，队列空了再把中断打开。内核工程师早就想明白了：事件多到一定程度，被动响应不如主动去取。\nCPU / 网卡 人 / Coding Agent 上下文切换接近零成本 上下文切换是分钟级的，贵得离谱 包密到一定程度，NAPI关中断转轮询 Agent多到五个以上，你就是那块高负载的网卡 NMI等高优先级中断谁也拦不住 只有高风险事项配得上实时打断你 人的上下文切换不是便宜，是贵得离谱——开头那个晚上，我切走两分钟，回来最早那个已经空等了二十分钟。所以\u0026quot;从中断到收件箱\u0026quot;不是我的偏好，是负载决定的：五个以上Agent同时跑，你就是那块高负载的网卡，该关中断了。\n关中断不是全关。CPU有屏蔽位、有优先级，NMI谁也拦不住，对应到人，就是这篇文章我最想留下的一句：\n只有高风险配得上打断你：其余的进队列，你按自己的节奏取。\ngoal_control是这套想法的一个粗糙实现。它迟早可能会被哪个产品内置掉，这不重要。重要的是我花了两周才想明白、而你读到这里只花了二十分钟的那件事：今天的Coding Agent默认你是一块CPU，随时可中断，切换不要钱，而你不是。\n","permalink":"https://leonyoung.tech/posts/parallel-coding-agent-attention-economics/","summary":"并行跑五个以上 Coding Agent 之后，真正稀缺的不再是模型产能，而是人的注意力。本文复盘我为什么、以及怎样做了 goal_control：把会话状态外置，把中断驱动的交互改成收件箱，只让高风险决策打断人。","title":"人不能扩容，只能被保护 —— 并行Coding Agent的注意力经济学"}]