技术长文
Weaver Harness:系统持久运转,人类异步评判
§1 — 核心定义与思路
Weaver 是一套封装在 coding agent(claude -p / codex exec)之上的编排层。主链只有三个对象:Spec → Task Graph → Node Loop。Spec 定义什么才算完成。Task Graph 把工作与依赖持久化。Node Loop 让每个节点经历执行、反思、对抗审计,再由图上的独立任务做对抗验收。session 可以结束,项目不随之失忆。
它针对的问题是:单个 session 无法走完一个大型项目。问题不只是上下文长度。大型项目的经验面和流程面都太宽。一个商业 APP 往往横跨需求挖掘、市场调研、产品设计、实现、上线与运营。它们有不同的输入、判断标准和失败模式,也会在数天甚至数周内反复改写彼此。把整条链压进一次会话,等于把项目状态押在一个短命进程里。
系统分为两层。代码控制层保持克制,只包含 coding agent 的薄封装、DAG 状态机、可靠性硬约束和持续轮询的 daemon。它负责让工作不断、状态不丢、明显故障不被静默吞掉。Prompt 资产层承载编排实践、角色姿态、经验参考和基础设施知识。前者处理可机械判定的事实,后者处理需要模型判断的工作。
预期输入是一个目标加可用资源,预期输出是可上线的产品。人类不进入执行循环,只异步做两件事:输入目标,评判产出。反馈由 Planner 改写为新的节点、依赖或验收要求,然后进入同一张图。机器持续运行,人类按自己的节奏判断方向。
§2 — 核心组件与架构
图 1 给出系统边界。代码层不试图成为第二个 agent。它保存状态、选择可运行节点、组装上下文、启动底层 agent,并把结果送回验收面。真正的工作仍由模型完成。这样的分工接近 orchestrator-workers 模式[1],但调度对象不是一次请求中的 worker,而是跨越长时间的持久任务图。
Task Graph:把项目状态移出 session
Weaver 选择 Session = Task 作为执行粒度。每个 task 对应一个 agent session,task.agent_session_id 保存其身份。首次执行开启新 session,后续反思与修改继续 resume 同一 session。上下文归节点,依赖归图,两者不混在一个无限增长的对话里。
DAG 只承认显式状态。depends_on 的上游必须到达 DONE,下游才会解锁。可运行节点按能解锁的后继数量排序,Planner meta-task 优先。于是一次 session 失败只影响一个节点。进程退出后,图、依赖和验收指针仍在磁盘上。
Daemon:持续运行不是一个超长调用
daemon 的关键性质是异步。它不等待一个 agent 完整结束再恢复系统生命,而是持续轮询任务图,把可运行节点派发给子进程。子进程通过 stream-json 增量写回,验证与归档沿另一条路径推进。人类反馈也不需要抢占当前 session,只需改写图,下一轮 poll 就会看到新的前沿。
长期运行依赖一组不聪明但必要的恢复规则。continueExpiredInProgressTasks 发现 IN_PROGRESS 节点既无活子进程又已超时,会将其转回 TODO,并增加 failure_count。task 总超时默认 1440 分钟。输出流沉默 10 分钟进入 deferTaskForRetry 或 deferOrEscalate。transient 错误最多重试 10 次并退避,累计失败 5 次升级为 NEEDS_HUMAN。
调度看门狗采用合取条件,避免把慢误判成死:锁持有超过 30 分钟,日志沉默超过 5 分钟,同时不存在活子进程,才发送 NEEDS_HUMAN inbox 通知。它检测异常,不替代判断。
「真死循环」与「沉默但在工作」的区分则交给死循环仲裁,原则是 arbitrate before kill。流沉默超时触发时系统先不杀进程:一个独立 session 读取 transcript 尾部做判断。判 WORKING 就原地延长窗口,进程继续跑,不消耗重试计数;判 DEAD_LOOP 或 HUNG 才终止并重启;仲裁自身的机械失败记为 ARBITER_ERROR,走传统重启路径,不污染统计。每个任务最多仲裁两次,之后稳定升级 NEEDS_HUMAN。判定是 JUDGMENT,所以交给模型读上下文,而不是写成正则。该机制经三轮 cross-model 评审后合入主干,仍在生产验证期。
异步协作:人类是图上的一种依赖,不是循环里的审批者
人类进入系统只有两个入口,都不打断执行。输入目标:新需求和反馈由 Planner 改写成节点、依赖或验收要求,下一轮 poll 生效。评判产出:对已交付结果的判断同样落回图上,变成 repair task、被重写的依赖或修改后的 SPEC 节点。系统里没有同步审批环节,人类的意见和其它输入一样,以图的变更形式被消费。
反方向的接口是 NEEDS_HUMAN。当一个节点确认自己被真实外部条件挡住(缺凭据、缺账号、需要人类的产品决策),它升级为 NEEDS_HUMAN,写入 inbox 并外发通知,gate 自带回复协议。关键性质是阻塞局部化:只有依赖它的下游停下,图的其余前沿继续推进。人类按自己的节奏回复,任务翻回 TODO,unblocks 链自动解锁后续。等待不冻结系统,只冻结一条依赖边。
这个状态被刻意收窄,防止它变成甩锅出口。宪法把它约束为最后手段:资源不够时 agent 应当主动调研替代方案,而不是「默默交付次优产出、或沉默地停在 NEEDS_HUMAN」。blocker 分类型处理:进程类失败(STALE_PROCESS_BLOCKER)由 daemon 自动恢复,不惊动人;只有真外部阻塞(REAL_EXTERNAL_BLOCKER)才等待人类。升级路径全部是机械的:累计失败五次、看门狗合取告警、仲裁预算耗尽。宪法给这套设计的标题只有八个字:AI 自驱,人类异步。
Node Loop:局部自纠错,独立上下文问责
一个节点先由 runDevScheduler 开启执行 session。随后 resumeDevScheduler 读取 continuation.md,在同一 session 中做多轮反思。这里复用会话不是为了省 token,而是让修改者保留刚刚作出决策时的局部上下文。它对应 reflection 的经典位置[2]。
反思之后,Devil 通过 runImprovementAgent 发起对抗审计。ACCEPT 才允许节点完成。REJECT 产生 REVISE,并回到原执行 session,最多运行 devil_review 轮。最后的对抗验收不塞进这个 task。Planner 在图上安排一个下游 audit task,用新的上下文检查上游产物。前三阶段修正“自己认为做完”,第四阶段检验“独立观察者是否同意”。
Prompt:姿态、知识与当前验收面的即时组合
角色定义保持很薄。Planner、Coder、Tester、Devil 等十余个 role 的 role.md 只回答三件事:我是谁,目标是什么,何时升级求助。常驻能力来自 bound-skills.yaml,任务相关能力由 searched skill 按需检索。流程知识不复制进每个 role。
buildVars 的顺序固定为:role_context → bound_skills → searched_skills → prd_index → acceptance → task 字段。最后一段包含 raw_request、title 与 notes。acceptance 每次 live 渲染当前 spec 与 spec-state,而不是把旧文本复制进 task。prompt 因而不是一份巨型手册,只是角色姿态、所需知识、输入数据与当前验收面的临时组合。
这套取舍由一条更一般的纪律约束:“根因是姿态不足,解药是强化姿态,不是加 checklist。”另一条更接近实现边界:“LLM 即可靠的人:设计天然互检的 role 关系(Coder→Tester),别往 runner.ts 堆运行时 guard。”代码层只守 FACT、状态转换和不可接受的静默失败。JUDGMENT 留在有上下文的角色关系里。
资产层:编排实践、技能与参考的长期沉淀
代码层可以很薄,资产层不能。它决定同一个 coding agent 在具体领域能不能做出不业余的东西,是这套系统里更耗人力的一半。除十余个 role 与五十余个 skill 外,还有一份声明式 infrastructure.yaml:它把可用 provider、默认与备选、以及二十余个 provision 命令收在一处,让系统自动开通 Supabase、Stripe、OpenRouter、FAL 等外部服务,而不是把密钥和开通流程散落进 prompt。
再往下是成体系的参考库:设计范本与品牌参照、命名用的活语料、上百篇脚本与内容参考、各服务的 provisioning recipe,以及一整套编排实践。这些是「怎么做得好」的原材料。skill 与参考不是一次写完的静态文档,而是随真实项目暴露的失败被持续增删——一个 skill 若把产出拉到低于「什么都不加载」的基准,宪法要求删掉它,而不是继续打补丁。资产层因此是一条一直在收敛的长线,不是一次性的配置。
SPEC:不是需求文本,而是可失效的验收状态
Planner 在拆 task 的同时写入 specRefs,指向 SPEC 节点 id。验收面被拆成三层:spec.yaml 保存无状态声明,spec-state 保存验证结果与证据快照,task 只保存指针。往 spec.yaml 写 verdict 会 fail-loud,因为声明与结论一旦混写,旧结论就会伪装成当前事实。
# 1) spec.yaml:声明“什么必须为真”,本文件不保存判决
nodes:
- id: core-loop-playable # 稳定节点 ID;task 和 ledger 都靠它寻址
text: agent completes a game # 当前验收命题;修改它必须使旧 verdict 失效
hardness: HARD # HARD 参与 done-gate;SOFT 只提供质量信号
verify: e2e-full-game # 建议的验证方式;说明证据应如何产生
refs: # 验收依赖的外部真值;原路径内容变化也要重验
- docs/rules/game.md
# 2) spec-state/<node-id>.yaml:保存“上次如何判、依据什么判”
verdict: PASS # 独立 ledger 中的判决;绝不回写 spec.yaml
verdict_by: audit-task-42 # 判决来源;让责任与复查路径可追踪
verified_against: # 判决时的节点快照;CAS 防止边验边改
text: agent completes a game
hardness: HARD
verify: e2e-full-game
refs_fingerprint: sha256:… # 外部参考物内容指纹;捕获同路径静默替换
# 3) tasks.yaml:任务只持有指针,不复制验收文本或 verdict
- id: implement-core-loop
specRefs: # 本 task 对哪些 SPEC 节点负责
- core-loop-playable
有效性来自三个机制。第一,write-path invalidation:节点一旦被修改,旧 verdict 立即清空。系统不在交付门临时比较两份文本,因为那种门可以靠重新复制旧文本绕过。第二,compare-and-swap:写 verdict 时携带 --expected 节点快照。如果验证期间节点已变化,本次写入失败。第三,refs 指纹:设计稿即使在原路径被重画,也会触发重新验证。这三者共同保证 PASS 描述的是当前命题与当前参考物。
Polis R14→R15 暴露了静态验收面的危险。owner 的 mandate 是“本地 agent 必须赢完整盘 Civ 对局”。它只存在于任务散文和一个 QUEUED spec-write 中,没有成为活的 non-PASS 节点。reconcile 从 spec-state 推进前沿时,看见旧 mock 上的 50 个 HARD 节点全部 PASS,于是清掉过程任务并宣告完成。验收面全绿,核心论证一次没跑。问题不是 verifier 不够强,而是系统验证了错误的世界。要求必须先成为可失效节点,机器才能看见它。
visual-pairs 则给出相反教训:并非所有判断都该机器化。这道门用截图像素匹配设计稿。Coder 的最优应对是交付静态 WebView:视觉最接近,也完全不交互。门越硬,Goodhart 越彻底。该机制随后被 revert,设计品味转交独立 E2E 整体评估。FACT 适合硬门,JUDGMENT 需要完整上下文与对抗判断。宪法对此有一句更朴素的表达:“一个在破损工作上沉默地打 DONE,和一个假 UI,一样违反准则。”
Spec Kit 与 OpenSpec 提供了相邻参照[3]。它们主要把 spec 用作开发前的人类对齐文档。Weaver 更关心无人监督执行期间的状态问题:哪个命题仍有效,谁给出判决,判决针对哪个版本,参考物是否变化。格式相似,运行语义不同。
治理:成功标准不能由优化者重写
lint 扫描全仓一致性。constitution.md 则由人类维护,定义系统认为什么更好,evolve 无权修改它。评价函数与优化过程必须分开,否则自我改进最容易做的事,就是把成功改写成自己已经做到的样子。宪法里的编辑原则也刻意保持可执行:“删 → 替 → 合 → 拆 → 扩,扩是最后一招。”
消融:哪些注入增强,哪些注入压制
编排层不是天然增益,也不是天然拖累。同样是往 coding agent 上加东西,有的注入把产出抬高,有的把产出压低。几组隔离实验(同 brief、盲选、n>1)的价值不在回答“harness 好不好”,而在把这两类分开。
| 问题 | 对照 | 观察 | 约束出的结论 |
|---|---|---|---|
| Logo | grounding-only vs full apparatus | grounding-only 2/4 胜;full apparatus 0/4,从不胜 | 真实参考提供增益,方法装置可能压低上限 |
| Naming | live-culture harvest 开 / 关 | harvest 是质量分水岭 | 鲜活文化材料比抽象命名套路更关键 |
| 设计管线 | 同一 PRD:裸 Codex vs Weaver | 裸 Codex 更优,Weaver 产出贫血 | 降质位于编排层,不在 brief 或 PRD |
| 内容生成 | story-first vs 晚期补逻辑 | story-first 有效,晚期补逻辑无效 | 决定质量的约束要在生成前进入,事后硬化救不回核心 |
规律是一致的。增强来自内容:真实的顶级参考、活的文化语料、先定义后收敛——它们把模型缺的那点领域具体性喂进去。压制来自装置:多阶段套路、否定优先的 framing、覆盖率棘轮、事后硬化——它们用流程的“完整”换掉了模型的判断,把产出拉回安全的平庸。所以 harness 工程不是少加或多加,是分清每一层加的是内容还是装置:内容沉淀下来并加厚,装置一旦模型单独做得更好就撤掉。一个能自我改进的系统,必须既能积累经验,也能撤回自己的复杂性。
§3 — 参考
- [1] Anthropic. Building Effective Agents. 2024. 本文采用其中 workflows / agents 的区分与 orchestrator-workers 作为相邻架构参照。
- [2] Weng, Lilian. LLM Powered Autonomous Agents. Lil’Log, 2023. planning、reflection 与 memory 对应 Planner、Node Loop 反思和文件化状态。
- [3] GitHub. Spec Kit. Fission-AI. OpenSpec. 两者作为 spec-driven development 的对照。
§4 — 未来展望与发展
第一条边界是评估,也是几乎所有“哪个更好”最终都会撞上的元瓶颈。没有可信的比较式评估器,自我改进只能优化代理指标,多数架构主张也无法干净消融。我构想的审美层不是训一个打分模型,而是一只对比式的评估眼:绝对打分不可信(模型自评会系统性高估),成对盲选却容易且稳,价值不只在排序,更在它被迫给出的、可迁移的理由。它天然骑着基模型的升级,而不是把今天的判断冻进一个自训的小引擎。
要让这只眼成为进化的一环,实验设计得够严。同一 brief 只变一处,盲化加随机排序治 position bias,再加一条 PLACEHOLDER 臂——把 skill 换成最小 stub:若它与真 skill 无异,起作用的就是那个槽的结构,而不是内容。测量系统本身也要先自证可用:三套物理隔离的数据集(校准 / 版本 benchmark / 永不调参的 holdout),而人评自身的漂移就是误差的地板,不能要求自动打分器比它更稳。审美层的终局我猜是两种形态:标准化的部分内置进通用 harness,个性化的品味作为用户数据留在各自的数据层。
Craft → Harden 仍需在完整管线中消融。已有实验提示,前置全部约束会把生成压向安全解。先 Craft 定义产品,再 Harden 补全状态、边界和 SPEC,可能更合理。风险同样明确:Harden 可能磨掉 Craft 的独特性。部分独立实验成立,不等于 Weaver 在环已经成立。
role 是否必要,也不是常量。当前系统通过十余个 role 切分责任。Lope 作为 Weaver 的极简实验分支,把循环核心收缩到约 1,300 行(loop / decider / facts / adapter / store / assembler / gates,不含基础设施接入),测试无角色、大阶段的单写者循环。它已经跑出一个结果:在 GDPval 的单件文档任务上,Lope 相对裸 codex 没有提升,成本还更高。这条负结果有用——它说明 harness 的价值不在把单次生成打磨得更漂亮,而在多步项目循环里的状态累积与回炉;也说明把结构一次性删到极简,会连同装置一起丢掉那些真在扛事的经验沉淀。收缩要有纪律,不是推倒重来。两端谁更优,最终取决于模型能否在更长跨度内自己保持目标、判断与自纠错——今天有用的角色边界,模型变强后可能成为明天的上下文税。
调度并行仍是工程待办。当前默认单槽串行曾造成关键路径饥饿,并出现过 627 美元零产出的案例。并行槽与关键路径优先值得实现,但它们不会自动提高质量。并行只缩短正确图的时间,也会更快放大错误图的成本。
更长远的命题,是创新路径能否被抽象,进而被编排。绝大多数商业 APP 并不依赖一次不可解释的灵光。创新通常来自排列、组合与遍历:一个新的场景需求遇到一种新的技术方案,便产生新的产品空间。把“场景需求 × 新技术方案”作为组合轴,系统已经可以遍历大量候选,再用资源、约束与评估逐步收敛。人类历史上当然存在极少数难以还原为组合的跃迁,但它们不是商业创新系统必须依赖的常态。
这正是长期愿景的依据:给系统种子策略与资源,让它持续搜索组合空间;人类异步提供目标与品味,把有价值的方向写回图。可编排的不是灵感本身,而是产生、检验和淘汰候选的路径。
最终的限制仍在模型。评估、品味与跨长程连贯性并不完全属于 harness。站在模型外部,编排层只能不断适配别人训练好的能力分布,也会随着模型升级删掉旧机制。真正的杠杆不是把 harness 越做越厚,而是让 harness 与模型共同演化:模型暴露新的能力边界,系统用真实项目与消融把边界变成训练信号,再由下一代模型反过来简化系统。