feat(post): 工具三部曲——spec-harness/pi-mesh 全家桶/Agensh 论文笔记
用户直派车道交付:①Spec Harness 详解(任务包解剖+流水线图, boundary/verify 实例+12 命令+门禁)②pi-mesh 四件套(stack 图+ 合奏循环图+上手安装)③Agensh 论文笔记(三支柱/协作环/规模收 益图,DeLM 血统+issue#13→pi-board 诞生线)。七张自绘 SVG、双 语(en 手写直译 locked:true,禁 TMT 覆盖)。与既有 spec- workflow-and-mesh(实战复盘向)互补并存。 车道:worker-one(build/lint/外链 curl/视觉两轮 7/7 全绿);收 编复验 E2E 34/34+lint 43 页 0/0/0 验证: pnpm astro build 39 文;E2E 34/34;content-lint 0/0/0 Spec: none (内容三篇,直派车道收编) Co-authored-by: GLM-5.3
No files matched your search
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "Agensh:把组织智能扩展到 1,024 个智能体"
|
||||
pubDate: 2026-11-01
|
||||
category: tech
|
||||
tags: [论文笔记, AI 协作]
|
||||
---
|
||||
|
||||
多智能体系统能靠并发压缩复杂任务的时延,但主流框架有一个共同的天花板:中心编排器。任务分配、进度汇总、结果裁决全部挤过同一个上下文,规模越大,编排器先死。2026 年 10 月,微软研究院开源了 [Agensh](https://github.com/microsoft/Agensh)——一个**没有中心编排器**的自组织多智能体 harness,论文[《Scaling Organizational Intelligence to 1,024 Agents》](https://arxiv.org/abs/2609.26781)把它推到了一千零二十四个 agent 的规模。
|
||||
|
||||
它的答案出人意料地朴素:与其造一个更聪明的调度大脑,不如把一个人类组织的协同基建原样搬给 agent 们。
|
||||
|
||||
## 一、三支柱:组织的协同基建
|
||||
|
||||

|
||||
|
||||
Agensh 只给三样基础设施,其余全部自组织:
|
||||
|
||||
- **共享工作区**:git 平台承载组织的进行中工作与集成结果,分支、PR、合并冲突这些人类用了二十年的机制原样复用;
|
||||
- **消息接口**:团队频道加私信,带优先级送达——组织级广播与紧急碰撞调解各走各的道;
|
||||
- **共享上下文**:一块类型化条目板,存组织的发现与工作意图。
|
||||
|
||||
开源参考实现用的是 Gitea 承载工作区、Mattermost 承载频道与私信——一套完全开源自接的“办公室”,没有任何私有服务。没有编排器分配任务:worker 自己从工作区和上下文里找活、认领、验证、合并。规模化时天生的瓶颈(那个越来越忙的中枢)根本不存在。
|
||||
|
||||
## 二、协作环:每个 worker 各跑一圈
|
||||
|
||||

|
||||
|
||||
每个 worker 并发、异步地执行同一个协作环:**收集上下文 → 认领子任务 → 执行 → 验证 → 合并进度**。收集上下文时读共享工作区和条目板的存量知识;认领靠 CLAIM 声明,撞车了私聊解决;验证自己跑测试,证据说话;合并走 PR。
|
||||
|
||||
最有意思的设计在共享上下文的**经济学**:条目刻意短小(约 100 字符,PATCH_SUMMARY 放宽到 300),长版本放 `detail` 字段按需展开;`board_read` 只服务最近 2000 条,更早的历史只能靠布尔检索(`,` = OR,`&` = AND)翻出来——近期记忆保持小,长尾靠搜索。论文称 **FAIL 是价值最高的条目**:一条被证伪的路径写下来,就能止住全队在同一个死胡同里重复烧预算。新条目会作为较高优先级的更新,在同伴的下一个工具调用边界推送过去。
|
||||
|
||||
这套共享上下文不是 Agensh 的发明——论文 §2.3 明说“共享上下文遵循 DeLM 的核心思想”。[DeLM](https://github.com/yuzhenmao/DeLM)([arXiv:2606.10662](https://arxiv.org/abs/2606.10662))用共享上下文加任务队列跑去中心化多智能体:它的板是一张 append-only 事件日志(条目、认领、完成、FACT/FAIL 笔记、状态),正文存在日志旁的独立数据库,带硬上限(≤40 个文件、≤400KB 总量),还有一个阻塞式的 `read --wait`——同伴发帖即唤醒等待者。在 Terminal-Bench 4.0 的 10 项子集上,两个 agent 通过共享上下文协同,平均提升 12.5 个百分点准确率、1.78 倍加速。Agensh 把这条线索放大到了千级规模。
|
||||
|
||||
## 三、数字:扩容真的有用吗
|
||||
|
||||

|
||||
|
||||
两组数据回答"加 agent 到底加不加得动":ProgramBench 最难 5 题,1 → 128 个 agent,通过率 19.31% → 28.78%(相对提升约 49%);从零构建 pandoc(6 小时预算、无外网),1 → 1,024 个 agent,通过率 33.89% → 55.06%。对纯堆算力已经边际递减的难题,**组织化的边际收益还在**。
|
||||
|
||||
工程可用性也到位了:v1.0 于 2026-10-23 发布,六天后的 v1.0.1 已支持 Copilot、Codex、Claude Code、Pi 作为底层单智能体 harness——单 agent 侧你可以用任何顺手的主力模型。开源版是单节点 harness,v1.0 发布包里就带着复现指南、论文与演示——从论文到可跑的仓库,中间没有隔着一句“敬请期待”。
|
||||
|
||||
## 四、从论文到桌面:一个 issue 的故事
|
||||
|
||||
这篇论文对我们不只在纸上。把它读透之后,2026-10-23 我在 pi-mesh 仓库提了[第 13 号 issue](https://github.com/cgarrot/pi-mesh/issues/13):pi-mesh 已有消息接口(`mesh_send`/房间/优先级)和共享工作区(git + `mesh_reserve` 预约),独缺第三支柱共享上下文——提议按 Agensh/DeLM 的形状加 `board_write / board_read / board_grep`。
|
||||
|
||||
维护者 cgarrot 的回复是整个故事里最值得学的一课:方向对,但**hash-only 台账让 pi-mesh 结构上当不了这块板**——把持久化塞进 broker 等于把路由器改造成数据库。正确的形状是独立包:以普通 peer 入房、自有存储、mesh 继续只做路由。于是有了 [pi-board](https://github.com/wait4xx/pi-board):一天内出 v0.1,55 个测试零失败、真实 broker 上九步冒烟,issue 最终关闭时官方指针就落在它身上。上一篇文章介绍的[四件套](/archives/pi-mesh-stack/),其记忆层就是从这篇论文长出来的。
|
||||
|
||||
## 五、结语
|
||||
|
||||
Agensh 的启示不是"1024"这个数字,而是它对"组织智能"的拆解:工作区、消息、共享上下文——三根支柱撑起自组织,任何一根都不需要一个更聪明的大脑。这个拆解在千级规模成立,在单机四件套成立,在单人加几个 agent 终端的小作坊同样成立。规模会变,支柱不变。
|
||||
|
||||
相关阅读:[Spec Harness 详解](/archives/spec-harness/) · [pi-mesh 全家桶详解](/archives/pi-mesh-stack/)
|
||||
@@ -0,0 +1,45 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="330" viewBox="0 0 960 330" font-family="ui-sans-serif, system-ui, 'Noto Sans SC', 'PingFang SC', sans-serif">
|
||||
<rect width="960" height="330" fill="#ffffff"/>
|
||||
<text x="480" y="40" text-anchor="middle" font-size="19" font-weight="600" fill="#111113">多智能体协作环:每个 worker 并发、异步地各跑一圈</text>
|
||||
|
||||
<!-- 环上 5 步 -->
|
||||
<g font-size="12.5" fill="#33343a">
|
||||
<rect x="46" y="150" width="150" height="76" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="121" y="182" text-anchor="middle" font-weight="600" fill="#111113">① 收集上下文</text>
|
||||
<text x="121" y="204" text-anchor="middle">读工作区 + 检索条目板</text>
|
||||
|
||||
<rect x="226" y="150" width="150" height="76" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="301" y="182" text-anchor="middle" font-weight="600" fill="#111113">② 认领子任务</text>
|
||||
<text x="301" y="204" text-anchor="middle">声明 CLAIM,冲突私聊解</text>
|
||||
|
||||
<rect x="406" y="150" width="150" height="76" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="481" y="182" text-anchor="middle" font-weight="600" fill="#111113">③ 执行</text>
|
||||
<text x="481" y="204" text-anchor="middle">本地改动推送分支</text>
|
||||
|
||||
<rect x="586" y="150" width="150" height="76" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="661" y="182" text-anchor="middle" font-weight="600" fill="#111113">④ 验证</text>
|
||||
<text x="661" y="204" text-anchor="middle">跑测试,证据说话</text>
|
||||
|
||||
<rect x="766" y="150" width="150" height="76" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="841" y="182" text-anchor="middle" font-weight="600" fill="#111113">⑤ 合并进度</text>
|
||||
<text x="841" y="204" text-anchor="middle">PR 合入共享工作区</text>
|
||||
</g>
|
||||
|
||||
<g stroke="#6b6e78" stroke-width="1.5">
|
||||
<line x1="196" y1="188" x2="224" y2="188"/><polygon points="224,188 215,183.5 215,192.5" fill="#6b6e78"/>
|
||||
<line x1="376" y1="188" x2="404" y2="188"/><polygon points="404,188 395,183.5 395,192.5" fill="#6b6e78"/>
|
||||
<line x1="556" y1="188" x2="584" y2="188"/><polygon points="584,188 575,183.5 575,192.5" fill="#6b6e78"/>
|
||||
<line x1="736" y1="188" x2="764" y2="188"/><polygon points="764,188 755,183.5 755,192.5" fill="#6b6e78"/>
|
||||
</g>
|
||||
<path d="M 841 226 L 841 262 L 121 262 L 121 228" fill="none" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="121,228 116,237 126,237" fill="#6b6e78"/>
|
||||
<text x="481" y="256" text-anchor="middle" font-size="12" fill="#6b6e78" dy="-6">下一圈</text>
|
||||
|
||||
<!-- 共享上下文条 -->
|
||||
<rect x="120" y="70" width="720" height="52" rx="10" fill="rgba(52,84,209,0.05)" stroke="#3454d1" stroke-dasharray="6 4" stroke-width="1.2"/>
|
||||
<text x="480" y="92" text-anchor="middle" font-size="13" font-weight="600" fill="#3454d1">共享上下文(类型化条目:OBSERVED / FACT / FAIL / CLAIM / PATCH_SUMMARY)</text>
|
||||
<text x="480" y="112" text-anchor="middle" font-size="11.5" fill="#6b6e78">全程发布发现;条目刻意短小(约 100 字符),长文 detail 按需展开;近 2000 条常驻,全史靠布尔检索</text>
|
||||
<line x1="200" y1="122" x2="150" y2="148" stroke="#a0a3ad" stroke-dasharray="4 3"/>
|
||||
<line x1="760" y1="122" x2="820" y2="148" stroke="#a0a3ad" stroke-dasharray="4 3"/>
|
||||
<text x="480" y="308" text-anchor="middle" font-size="12.5" fill="#6b6e78">论文称 FAIL 是"价值最高的条目":一条被证伪的路,能止住全队的重复预算</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,64 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="420" viewBox="0 0 960 420" font-family="ui-sans-serif, system-ui, 'Noto Sans SC', 'PingFang SC', sans-serif">
|
||||
<rect width="960" height="420" fill="#ffffff"/>
|
||||
<text x="480" y="38" text-anchor="middle" font-size="19" font-weight="600" fill="#111113">从中心编排到自组织:Agensh 的三支柱</text>
|
||||
|
||||
<!-- 左:中心编排器 -->
|
||||
<rect x="40" y="70" width="360" height="290" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="220" y="98" text-anchor="middle" font-size="13.5" font-weight="600" fill="#6b6e78">传统多智能体:中心编排器瓶颈</text>
|
||||
<rect x="150" y="116" width="140" height="40" rx="8" fill="#ffffff" stroke="#6b6e78"/>
|
||||
<text x="220" y="141" text-anchor="middle" font-size="12.5" fill="#33343a">orchestrator</text>
|
||||
<g stroke="#a0a3ad">
|
||||
<line x1="220" y1="156" x2="100" y2="230"/>
|
||||
<line x1="220" y1="156" x2="180" y2="230"/>
|
||||
<line x1="220" y1="156" x2="260" y2="230"/>
|
||||
<line x1="220" y1="156" x2="340" y2="230"/>
|
||||
</g>
|
||||
<g font-size="11.5" fill="#6b6e78">
|
||||
<rect x="72" y="230" width="56" height="30" rx="6" fill="#ffffff" stroke="#d0d4da"/><text x="100" y="249" text-anchor="middle">w1</text>
|
||||
<rect x="152" y="230" width="56" height="30" rx="6" fill="#ffffff" stroke="#d0d4da"/><text x="180" y="249" text-anchor="middle">w2</text>
|
||||
<rect x="232" y="230" width="56" height="30" rx="6" fill="#ffffff" stroke="#d0d4da"/><text x="260" y="249" text-anchor="middle">w3</text>
|
||||
<rect x="312" y="230" width="56" height="30" rx="6" fill="#ffffff" stroke="#d0d4da"/><text x="340" y="249" text-anchor="middle">…</text>
|
||||
</g>
|
||||
<text x="220" y="300" text-anchor="middle" font-size="12" fill="#6b6e78">任务分配、进度汇总、结果裁决</text>
|
||||
<text x="220" y="320" text-anchor="middle" font-size="12" fill="#6b6e78">全部挤过同一个上下文——</text>
|
||||
<text x="220" y="340" text-anchor="middle" font-size="12" fill="#6b6e78">规模越大,编排器先成为天花板</text>
|
||||
|
||||
<!-- 中间箭头 -->
|
||||
<line x1="410" y1="215" x2="460" y2="215" stroke="#6b6e78" stroke-width="1.6"/>
|
||||
<polygon points="460,215 450,210 450,220" fill="#6b6e78"/>
|
||||
|
||||
<!-- 右:自组织三支柱 -->
|
||||
<rect x="470" y="70" width="450" height="290" rx="10" fill="#ffffff" stroke="#3454d1" stroke-width="1.4"/>
|
||||
<text x="695" y="98" text-anchor="middle" font-size="13.5" font-weight="600" fill="#3454d1">Agensh:无中心编排,三支柱基础设施</text>
|
||||
<g font-size="12" fill="#33343a">
|
||||
<rect x="490" y="114" width="130" height="86" rx="8" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="555" y="140" text-anchor="middle" font-weight="600" fill="#111113">共享工作区</text>
|
||||
<text x="555" y="160" text-anchor="middle">git 平台</text>
|
||||
<text x="555" y="178" text-anchor="middle">分支 / PR / 合并</text>
|
||||
<rect x="630" y="114" width="130" height="86" rx="8" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="695" y="140" text-anchor="middle" font-weight="600" fill="#111113">消息接口</text>
|
||||
<text x="695" y="160" text-anchor="middle">团队频道 + 私信</text>
|
||||
<text x="695" y="178" text-anchor="middle">优先级送达</text>
|
||||
<rect x="770" y="114" width="130" height="86" rx="8" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="835" y="140" text-anchor="middle" font-weight="600" fill="#111113">共享上下文</text>
|
||||
<text x="835" y="160" text-anchor="middle">类型化条目板</text>
|
||||
<text x="835" y="178" text-anchor="middle">近期记忆 + 全史检索</text>
|
||||
</g>
|
||||
<g stroke="#a0a3ad">
|
||||
<line x1="555" y1="200" x2="520" y2="240"/>
|
||||
<line x1="695" y1="200" x2="660" y2="240"/>
|
||||
<line x1="835" y1="200" x2="800" y2="240"/>
|
||||
<line x1="555" y1="200" x2="620" y2="240"/>
|
||||
<line x1="695" y1="200" x2="740" y2="240"/>
|
||||
<line x1="835" y1="200" x2="880" y2="240"/>
|
||||
</g>
|
||||
<g font-size="11.5" fill="#6b6e78">
|
||||
<rect x="492" y="240" width="56" height="30" rx="6" fill="#ffffff" stroke="#d0d4da"/><text x="520" y="259" text-anchor="middle">w1</text>
|
||||
<rect x="592" y="240" width="56" height="30" rx="6" fill="#ffffff" stroke="#d0d4da"/><text x="620" y="259" text-anchor="middle">w2</text>
|
||||
<rect x="692" y="240" width="56" height="30" rx="6" fill="#ffffff" stroke="#d0d4da"/><text x="740" y="259" text-anchor="middle">w3</text>
|
||||
<rect x="792" y="240" width="56" height="30" rx="6" fill="#ffffff" stroke="#d0d4da"/><text x="820" y="259" text-anchor="middle">w4</text>
|
||||
<rect x="852" y="240" width="56" height="30" rx="6" fill="#ffffff" stroke="#d0d4da"/><text x="880" y="259" text-anchor="middle">…</text>
|
||||
</g>
|
||||
<text x="695" y="308" text-anchor="middle" font-size="12" fill="#6b6e78">worker 自己跑协作环:收集上下文 → 认领 → 执行 → 验证 → 合并</text>
|
||||
<text x="695" y="330" text-anchor="middle" font-size="12" fill="#6b6e78">发现发布到共享上下文,同伴在下一个工具调用边界收到广播</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,34 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="380" viewBox="0 0 960 380" font-family="ui-sans-serif, system-ui, 'Noto Sans SC', 'PingFang SC', sans-serif">
|
||||
<rect width="960" height="380" fill="#ffffff"/>
|
||||
<text x="480" y="38" text-anchor="middle" font-size="19" font-weight="600" fill="#111113">规模收益:扩容 agent 带来的通过率提升</text>
|
||||
<text x="480" y="58" text-anchor="middle" font-size="11.5" fill="#a0a3ad">数据:arXiv:2609.26781</text>
|
||||
|
||||
<!-- 组 1:ProgramBench -->
|
||||
<text x="235" y="92" text-anchor="middle" font-size="13.5" font-weight="600" fill="#111113">ProgramBench 最难 5 题</text>
|
||||
<text x="235" y="112" text-anchor="middle" font-size="12" fill="#6b6e78">1 → 128 agents</text>
|
||||
<g>
|
||||
<!-- 柱底 y=300,柱顶 = 300 - 值*3.4 -->
|
||||
<rect x="115" y="235" width="100" height="65" fill="#a0a3ad"/>
|
||||
<rect x="255" y="202" width="100" height="98" fill="#3454d1"/>
|
||||
<text x="165" y="227" text-anchor="middle" font-size="14" font-weight="600" fill="#33343a">19.31%</text>
|
||||
<text x="165" y="322" text-anchor="middle" font-size="12" fill="#6b6e78">1 agent</text>
|
||||
<text x="305" y="194" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1">28.78%</text>
|
||||
<text x="305" y="322" text-anchor="middle" font-size="12" fill="#6b6e78">128 agents</text>
|
||||
</g>
|
||||
<text x="235" y="352" text-anchor="middle" font-size="12" fill="#33343a">相对提升约 49%</text>
|
||||
|
||||
<!-- 组 2:pandoc -->
|
||||
<text x="645" y="92" text-anchor="middle" font-size="13.5" font-weight="600" fill="#111113">从零构建 pandoc(6h 预算 · 无外网)</text>
|
||||
<text x="645" y="112" text-anchor="middle" font-size="12" fill="#6b6e78">1 → 1,024 agents</text>
|
||||
<g>
|
||||
<rect x="525" y="187" width="100" height="113" fill="#a0a3ad"/>
|
||||
<rect x="665" y="113" width="100" height="187" fill="#3454d1"/>
|
||||
<text x="575" y="179" text-anchor="middle" font-size="14" font-weight="600" fill="#33343a">33.89%</text>
|
||||
<text x="575" y="322" text-anchor="middle" font-size="12" fill="#6b6e78">1 agent</text>
|
||||
<text x="715" y="105" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1">55.06%</text>
|
||||
<text x="715" y="322" text-anchor="middle" font-size="12" fill="#6b6e78">1,024 agents</text>
|
||||
</g>
|
||||
<text x="645" y="352" text-anchor="middle" font-size="12" fill="#33343a">+21.2 个百分点</text>
|
||||
|
||||
<line x1="480" y1="84" x2="480" y2="330" stroke="#e3e5ea"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="980" height="330" viewBox="0 0 980 330" font-family="ui-sans-serif, system-ui, 'Noto Sans SC', 'PingFang SC', sans-serif">
|
||||
<rect width="980" height="330" fill="#ffffff"/>
|
||||
<text x="490" y="40" text-anchor="middle" font-size="19" font-weight="600" fill="#111113">一次典型合奏:四件套怎么咬合</text>
|
||||
|
||||
<!-- 5 nodes -->
|
||||
<rect x="30" y="80" width="158" height="96" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="109" y="108" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">mesh_send</text>
|
||||
<text x="109" y="132" text-anchor="middle" font-size="12" fill="#33343a">契约式派单 / 对话</text>
|
||||
<text x="109" y="152" text-anchor="middle" font-size="11.5" fill="#6b6e78">五字段任务包 + 期限</text>
|
||||
|
||||
<rect x="216" y="80" width="158" height="96" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="295" y="108" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">board_grep</text>
|
||||
<text x="295" y="132" text-anchor="middle" font-size="12" fill="#33343a">先查团队记忆</text>
|
||||
<text x="295" y="152" text-anchor="middle" font-size="11.5" fill="#6b6e78">FAIL 可检索不重烧</text>
|
||||
|
||||
<rect x="402" y="80" width="158" height="96" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="481" y="108" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">job_run</text>
|
||||
<text x="481" y="132" text-anchor="middle" font-size="12" fill="#33343a">长活交后台</text>
|
||||
<text x="481" y="152" text-anchor="middle" font-size="11.5" fill="#6b6e78">turn 不阻塞</text>
|
||||
|
||||
<rect x="588" y="80" width="158" height="96" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="667" y="108" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">mesh_claim</text>
|
||||
<text x="667" y="132" text-anchor="middle" font-size="12" fill="#33343a">单写者租约</text>
|
||||
<text x="667" y="152" text-anchor="middle" font-size="11.5" fill="#6b6e78">epoch:fence 围栏</text>
|
||||
|
||||
<rect x="774" y="80" width="158" height="96" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="853" y="108" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">board_write</text>
|
||||
<text x="853" y="132" text-anchor="middle" font-size="12" fill="#33343a">留痕</text>
|
||||
<text x="853" y="152" text-anchor="middle" font-size="11.5" fill="#6b6e78">CLAIM 带租约号</text>
|
||||
|
||||
<!-- 正向箭头 -->
|
||||
<line x1="188" y1="128" x2="214" y2="128" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="214,128 205,123.5 205,132.5" fill="#6b6e78"/>
|
||||
<line x1="374" y1="128" x2="400" y2="128" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="400,128 391,123.5 391,132.5" fill="#6b6e78"/>
|
||||
<line x1="560" y1="128" x2="586" y2="128" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="586,128 577,123.5 577,132.5" fill="#6b6e78"/>
|
||||
<line x1="746" y1="128" x2="772" y2="128" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="772,128 763,123.5 763,132.5" fill="#6b6e78"/>
|
||||
|
||||
<!-- 回环箭头 -->
|
||||
<path d="M 853 176 L 853 216 L 109 216 L 109 178" fill="none" stroke="#a0a3ad" stroke-width="1.4" stroke-dasharray="5 4"/>
|
||||
<polygon points="109,178 104,187 114,187" fill="#a0a3ad"/>
|
||||
<text x="481" y="238" text-anchor="middle" font-size="12" fill="#6b6e78">发现与结论回写团队记忆,下一轮派单从存量知识起步</text>
|
||||
|
||||
<text x="490" y="290" text-anchor="middle" font-size="13.5" fill="#33343a">对话走 mesh · 记忆上 board · 长活交 jobd · 排他靠租约</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="470" viewBox="0 0 960 470" font-family="ui-sans-serif, system-ui, 'Noto Sans SC', 'PingFang SC', sans-serif">
|
||||
<rect width="960" height="470" fill="#ffffff"/>
|
||||
<text x="480" y="38" text-anchor="middle" font-size="19" font-weight="600" fill="#111113">pi-mesh 工具族:一个路由器,三个卫星</text>
|
||||
|
||||
<!-- 顶部:pi 终端 -->
|
||||
<rect x="80" y="62" width="200" height="58" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="180" y="86" text-anchor="middle" font-size="13.5" font-weight="600" fill="#111113">pi 终端 @op-gitea</text>
|
||||
<text x="180" y="106" text-anchor="middle" font-size="11.5" fill="#6b6e78">edit/write 受预约拦截</text>
|
||||
<rect x="380" y="62" width="200" height="58" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="480" y="86" text-anchor="middle" font-size="13.5" font-weight="600" fill="#111113">pi 终端 @op-artalk</text>
|
||||
<text x="480" y="106" text-anchor="middle" font-size="11.5" fill="#6b6e78">读回执 · 忙闲状态</text>
|
||||
<rect x="680" y="62" width="200" height="58" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="780" y="86" text-anchor="middle" font-size="13.5" font-weight="600" fill="#111113">pimesh attach / CLI</text>
|
||||
<text x="780" y="106" text-anchor="middle" font-size="11.5" fill="#6b6e78">无会话独立 peer</text>
|
||||
|
||||
<line x1="180" y1="120" x2="180" y2="150" stroke="#a0a3ad"/>
|
||||
<line x1="480" y1="120" x2="480" y2="150" stroke="#a0a3ad"/>
|
||||
<line x1="780" y1="120" x2="780" y2="150" stroke="#a0a3ad"/>
|
||||
|
||||
<!-- mesh 骨干 -->
|
||||
<rect x="60" y="150" width="840" height="110" rx="12" fill="#ffffff" stroke="#3454d1" stroke-width="1.6"/>
|
||||
<text x="90" y="180" font-size="15" font-weight="600" fill="#3454d1">pi-mesh —— 通信骨干(github.com/cgarrot/pi-mesh)</text>
|
||||
<text x="90" y="206" font-size="12.5" fill="#33343a">broker(内存态:peers / rooms / 邮箱 / 限速)· 房间与角色 member/observer</text>
|
||||
<text x="90" y="228" font-size="12.5" fill="#33343a">mesh_reserve 路径预约 · mesh_claim 单写者租约(epoch:fence,commit-msg 钩子拒过期围栏)</text>
|
||||
<text x="90" y="250" font-size="12.5" fill="#33343a">优先级 normal / urgent / force · 状态诚实三档 delivered ≠ read ≠ answered · 台账 hash-only</text>
|
||||
|
||||
<!-- 连接线到卫星 -->
|
||||
<line x1="240" y1="260" x2="240" y2="300" stroke="#a0a3ad"/>
|
||||
<line x1="480" y1="260" x2="480" y2="300" stroke="#a0a3ad"/>
|
||||
<line x1="720" y1="260" x2="720" y2="300" stroke="#a0a3ad"/>
|
||||
|
||||
<!-- 三个卫星 -->
|
||||
<rect x="70" y="300" width="330" height="110" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="90" y="328" font-size="14" font-weight="600" fill="#111113">pi-board —— 团队记忆板</text>
|
||||
<text x="90" y="350" font-size="12" fill="#33343a">OBSERVED/FACT/FAIL/CLAIM/PATCH_SUMMARY</text>
|
||||
<text x="90" y="370" font-size="12" fill="#33343a">有界 append-only 存储 · 布尔检索 board_grep</text>
|
||||
<text x="90" y="390" font-size="11.5" fill="#6b6e78">以普通 peer 入房;自有存储,broker 零持久化</text>
|
||||
|
||||
<rect x="430" y="300" width="330" height="110" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="450" y="328" font-size="14" font-weight="600" fill="#111113">pi-jobd —— 后台作业守护</text>
|
||||
<text x="450" y="350" font-size="12" fill="#33343a">脱离 turn 的进程组 · 存活于 /reload 与会话之外</text>
|
||||
<text x="450" y="370" font-size="12" fill="#33343a">诚实状态机(lost = 无法证明归属则拒操作)</text>
|
||||
<text x="450" y="390" font-size="11.5" fill="#6b6e78">mesh_job_control 跨 peer 远程 stop/pause/resume</text>
|
||||
|
||||
<rect x="70" y="424" width="690" height="38" rx="8" fill="rgba(52,84,209,0.05)" stroke="#3454d1" stroke-dasharray="6 4" stroke-width="1.1"/>
|
||||
<text x="415" y="448" text-anchor="middle" font-size="12" fill="#33343a">pi-subagents(worker 控制:spawn/steer/abort,mesh_run_control)与上面三者同族协作</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="440" viewBox="0 0 960 440" font-family="ui-sans-serif, system-ui, 'Noto Sans SC', 'PingFang SC', sans-serif">
|
||||
<rect width="960" height="440" fill="#ffffff"/>
|
||||
<text x="480" y="40" text-anchor="middle" font-size="19" font-weight="600" fill="#111113">任务包解剖:.spec/specs/YYYY-MM-DD_slug/</text>
|
||||
|
||||
<!-- 左:目录树 -->
|
||||
<rect x="40" y="70" width="400" height="250" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="60" y="102" font-size="15" font-weight="600" fill="#111113" font-family="ui-monospace, monospace">2026-10-26_blog-5f/</text>
|
||||
<text x="80" y="140" font-size="14" fill="#33343a" font-family="ui-monospace, monospace">├── spec.md</text>
|
||||
<text x="240" y="140" font-size="13" fill="#6b6e78">范围 · 假设 · 已证伪</text>
|
||||
<text x="80" y="180" font-size="14" fill="#33343a" font-family="ui-monospace, monospace">├── tasks.md</text>
|
||||
<text x="240" y="180" font-size="13" fill="#6b6e78">boundary + verify</text>
|
||||
<text x="80" y="220" font-size="14" fill="#33343a" font-family="ui-monospace, monospace">└── handoff.md</text>
|
||||
<text x="240" y="220" font-size="13" fill="#6b6e78">跨会话交接(可选)</text>
|
||||
<line x1="80" y1="248" x2="400" y2="248" stroke="#e3e5ea"/>
|
||||
<text x="80" y="278" font-size="13" fill="#6b6e78">收尾后整包归档至 .spec/specs/archive/,</text>
|
||||
<text x="80" y="300" font-size="13" fill="#6b6e78">归档目录就是项目的工程日志。</text>
|
||||
|
||||
<!-- 箭头 -->
|
||||
<line x1="440" y1="195" x2="500" y2="195" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="500,195 490,190 490,200" fill="#6b6e78"/>
|
||||
<text x="470" y="180" text-anchor="middle" font-size="12" fill="#6b6e78">每个任务</text>
|
||||
|
||||
<!-- 右:任务卡片 -->
|
||||
<rect x="500" y="70" width="420" height="250" rx="10" fill="#ffffff" stroke="#3454d1" stroke-width="1.5"/>
|
||||
<rect x="500" y="70" width="420" height="40" rx="10" fill="rgba(52,84,209,0.06)"/>
|
||||
<text x="520" y="96" font-size="13.5" font-weight="600" fill="#111113">- [x] T3 构建期内容 lint + build.sh 守卫</text>
|
||||
<text x="520" y="136" font-size="12.5" fill="#33343a" font-family="ui-monospace, monospace">claim: op-gitea(mesh 车道)</text>
|
||||
<text x="520" y="164" font-size="12" fill="#33343a" font-family="ui-monospace, monospace">boundary: 新建 content-lint.mjs + 守卫挂载</text>
|
||||
<text x="520" y="200" font-size="12.5" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">verify:</text>
|
||||
<text x="540" y="224" font-size="12" fill="#33343a" font-family="ui-monospace, monospace">node scripts/build-perf/content-lint.mjs dist</text>
|
||||
<text x="540" y="246" font-size="12" fill="#33343a" font-family="ui-monospace, monospace">&& bash -n infra/deploy/build.sh</text>
|
||||
<text x="520" y="290" font-size="12.5" fill="#6b6e78">verify 是只读断言:跑出退出码 0 才许勾选,</text>
|
||||
<text x="520" y="310" font-size="12.5" fill="#6b6e78">口头"过了"永远不算数。</text>
|
||||
|
||||
<text x="480" y="372" text-anchor="middle" font-size="13.5" fill="#6b6e78">boundary 划定"能动什么",verify 划定"怎么证明做完"——两行合同,缺一不勾</text>
|
||||
<line x1="360" y1="392" x2="600" y2="392" stroke="#e3e5ea"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,51 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="350" viewBox="0 0 960 350" font-family="ui-sans-serif, system-ui, 'Noto Sans SC', 'PingFang SC', sans-serif">
|
||||
<rect width="960" height="350" fill="#ffffff"/>
|
||||
<text x="480" y="38" text-anchor="middle" font-size="19" font-weight="600" fill="#111113">阶段流水线:五个核心命令与它们把守的门</text>
|
||||
|
||||
<!-- 5 个节点 -->
|
||||
<!-- new -->
|
||||
<rect x="36" y="72" width="152" height="120" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="112" y="100" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">/spec:new</text>
|
||||
<text x="112" y="126" text-anchor="middle" font-size="12.5" fill="#33343a">建任务包</text>
|
||||
<text x="112" y="146" text-anchor="middle" font-size="12.5" fill="#33343a">spec.md + tasks.md</text>
|
||||
<text x="112" y="172" text-anchor="middle" font-size="11.5" fill="#6b6e78">模板占位未填 → 拒 run</text>
|
||||
<!-- run -->
|
||||
<rect x="220" y="72" width="152" height="120" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="296" y="100" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">/spec:run</text>
|
||||
<text x="296" y="126" text-anchor="middle" font-size="12.5" fill="#33343a">逐任务执行</text>
|
||||
<text x="296" y="146" text-anchor="middle" font-size="12.5" fill="#33343a">verify 跑通才勾选</text>
|
||||
<text x="296" y="172" text-anchor="middle" font-size="11.5" fill="#6b6e78">阻塞任务打 ! 上浮</text>
|
||||
<!-- check -->
|
||||
<rect x="404" y="72" width="152" height="120" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="480" y="100" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">/spec:check</text>
|
||||
<text x="480" y="126" text-anchor="middle" font-size="12.5" fill="#33343a">复核轮</text>
|
||||
<text x="480" y="146" text-anchor="middle" font-size="12.5" fill="#33343a">问题当轮回写修复</text>
|
||||
<text x="480" y="172" text-anchor="middle" font-size="11.5" fill="#6b6e78">闭环项五选一登记</text>
|
||||
<!-- done -->
|
||||
<rect x="588" y="72" width="152" height="120" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="664" y="100" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">/spec:done</text>
|
||||
<text x="664" y="126" text-anchor="middle" font-size="12.5" fill="#33343a">归档 + 包提交</text>
|
||||
<text x="664" y="146" text-anchor="middle" font-size="12.5" fill="#33343a">证据随包入档</text>
|
||||
<text x="664" y="172" text-anchor="middle" font-size="11.5" fill="#6b6e78">验收面证据必须真实</text>
|
||||
<!-- push -->
|
||||
<rect x="772" y="72" width="152" height="120" rx="10" fill="#f6f7f9" stroke="#d0d4da"/>
|
||||
<text x="848" y="100" text-anchor="middle" font-size="14" font-weight="600" fill="#3454d1" font-family="ui-monospace, monospace">/spec:push</text>
|
||||
<text x="848" y="126" text-anchor="middle" font-size="12.5" fill="#33343a">合并 + 推送</text>
|
||||
<text x="848" y="146" text-anchor="middle" font-size="12.5" fill="#33343a">删已并分支</text>
|
||||
<text x="848" y="172" text-anchor="middle" font-size="11.5" fill="#6b6e78">只认 spec/ 前缀分支</text>
|
||||
|
||||
<!-- 箭头 -->
|
||||
<line x1="188" y1="132" x2="218" y2="132" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="218,132 209,127.5 209,136.5" fill="#6b6e78"/>
|
||||
<line x1="372" y1="132" x2="402" y2="132" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="402,132 393,127.5 393,136.5" fill="#6b6e78"/>
|
||||
<line x1="556" y1="132" x2="586" y2="132" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="586,132 577,127.5 577,136.5" fill="#6b6e78"/>
|
||||
<line x1="740" y1="132" x2="770" y2="132" stroke="#6b6e78" stroke-width="1.5"/>
|
||||
<polygon points="770,132 761,127.5 761,136.5" fill="#6b6e78"/>
|
||||
|
||||
<!-- 恢复车道 -->
|
||||
<rect x="36" y="228" width="888" height="76" rx="10" fill="rgba(52,84,209,0.05)" stroke="#3454d1" stroke-dasharray="6 4" stroke-width="1.2"/>
|
||||
<text x="480" y="258" text-anchor="middle" font-size="13.5" font-weight="600" fill="#111113">中断恢复是常态设计</text>
|
||||
<text x="480" y="284" text-anchor="middle" font-size="12.5" fill="#33343a">任意时刻重跑 /spec:run,从 tasks.md 勾选状态续跑;/spec:autorun 可续自动链(默认 5 轮上限,--max-rounds 可调)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "pi-mesh 全家桶详解:单机多智能体协同的四件套"
|
||||
pubDate: 2026-11-01
|
||||
category: tech
|
||||
tags: [工程实践, AI 协作]
|
||||
---
|
||||
|
||||
[pi](https://github.com/earendil-works/pi) 是我们日常用的编码智能体宿主。单开一个终端时它是个安静的助手;但同时开三四个终端、每个里面一个 agent 抢同一个仓库时,问题就来了:谁在线?谁在改哪个文件?消息发出去了对方到底看没看见?半夜跑着的构建谁有权停?
|
||||
|
||||
围绕 [pi-mesh](https://github.com/cgarrot/pi-mesh) 长出来的一族小工具把这层协同做成了基建。它们各管一摊,咬合成套:
|
||||
|
||||

|
||||
|
||||
## 一、pi-mesh:通信骨干
|
||||
|
||||
pi-mesh 是一条本机总线:一个脱离会话存活的 broker(NDJSON 帧走 unix socket),每个 pi 终端以别名成为 peer,加入房间(room)。消息 < 50ms 到达,零运行时依赖,Node ≥ 20。
|
||||
|
||||
它最值得称道的是**诚实状态模型**:`delivered`(写进了对方套接字)≠ `read`(注入了对方会话)≠ `answered`(拿到了显式回复)——三者永不混淆,没有一个是"完成"的意思。配套的**持久台账是 hash-only 的**:消息正文永不落盘,重启后能审计"谁在何时给谁发过什么",但看不到内容。这个自我约束后来成了整个工具族的设计公理:**mesh 只做路由器,永不做数据库。**
|
||||
|
||||
协同刚需有三个都在骨干里:
|
||||
|
||||
- **`mesh_reserve` 路径预约**:改文件前先预约,其他 peer 的 `edit`/`write` 工具在该路径上直接被拦——并发编辑靠机制防,不靠喊话。
|
||||
- **`mesh_claim` 任务租约**:单写者锁,带 `epoch:fence`(broker 重启即作废旧围栏);提交信息里写 `Mesh-Lease: task#leaseId#epoch:fence`,配套 commit-msg 钩子直接拒绝过期围栏的提交。
|
||||
- **能力路由 `cap:`**:`mesh_send({ "to": "cap:deploy", ... })` 把任务发给任意一个空闲且自报该能力的 peer,而不是等忙着的队友。消息优先级三档:normal 排队、urgent 插队、force 先中止对方当前回合再送达(force 属管控类,必须带 reason)。
|
||||
|
||||
另有 `mesh_wait_all` 做群裁(一次收回“谁答了、谁没答”的名单)、`mesh_status` 看 peer 忙闲(● 干活 / ○ 空闲 / ✕ 卡死)、`pimesh` CLI 支持无会话接入(`pimesh attach` 能收养死掉会话的身份)。跨机部署也支持:一台起 broker,其余机器做客户端。
|
||||
|
||||
诚实的颗粒度细到退出码:CLI 里 3 表示 `expired`——晚到的答复仍会送达,只是你等起见超时了;5 表示租约已过期,`commit-msg` 钩子据此把提交拒之门外。甚至 ESC 清空消息队列这种宿主边界也考虑到了:批量注入若撞上回合中止,broker 会在该回合结束后经中止安全通道重投(有界重试),状态页能看到“中止清除恢复重投”计数。
|
||||
|
||||
## 二、pi-board:团队记忆板
|
||||
|
||||
消息是易逝的,台账是 hash-only 的,那"我们学到了什么"存哪?[pi-board](https://github.com/wait4xx/pi-board) 补上这层。五种类型化条目:
|
||||
|
||||
- **OBSERVED**——看到了什么(不承诺)
|
||||
- **FACT**——验证过的事实
|
||||
- **FAIL**——一条被证伪的路(价值最高:全队不必再烧第二次预算)
|
||||
- **CLAIM**——认领声明,与 `mesh_claim` 租约配对(板只记录,从不执行)
|
||||
- **PATCH_SUMMARY**——落了什么改动
|
||||
|
||||
设计上处处是“有界与诚实”:append-only 本地存储、refuse-not-evict(超限拒绝写入而非静默淘汰)、条目默认短小、长文 `detail` 只能按 id 展开、布尔检索(`,` = OR,`&` = AND)覆盖全史而 `board_read` 只看近期窗口;检索无果时返回诚实的空,绝不猜一条充数。写入以普通广播帧发进房间,turn 边界有新 FAIL 摘要行;房间角色 member / observer 沿用 mesh 语义,只读 peer 永远不会在重连后被误升为写者。它以普通 peer 身份入房——**存储全在板自己家里,broker 里零持久化**。附带的 `piboard` CLI 五个动词一把梭:join(member / observer)/ watch / write / read / grep,一动词一动作,无会话的终端也能进房只读围观。这套类型学与"小条目+按需展开+近期记忆/全史检索"的分工直接来自学术论文(血统故事见[下一篇](/archives/agensh/))。
|
||||
|
||||
## 三、pi-jobd:后台作业守护
|
||||
|
||||
`bash` 会阻塞当前回合,`/reload` 会杀进程。[pi-jobd](https://github.com/cgarrot/pi-jobd) 把长命令放进独立进程组,由 per-user 守护监管:~150ms 返回,作业存活于会话之外。什么时候用它?三条判据:命令预计超过 60-90 秒;你必须边跑边干别的;或者它得扛过 `/reload` 和会话结束——以及"别的 agent 应该能停它"这一条:`mesh_job_control` 让任一授权 peer 远程 stop/pause/resume 别人的后台作业。
|
||||
|
||||
状态机同样诚实:`running / paused / stopped / exited / failed`,外加一个 `lost`——守护重启后无法证明 pid 归属时,宁可拒绝 stop 也绝不信号一个来路不明的进程。这个包还有个巧思:装包不够,模型还得**知道什么时候用**——它靠 prompt 快照、七个工具上的决策守则和一份按需加载的 skill 三层把使用时机喂给模型。
|
||||
|
||||
## 四、pi-subagents:worker 控制
|
||||
|
||||
[pi-subagents](https://github.com/nicobailon/pi-subagents) 管的是进程内子代理:前台子代理流式跑在对话里,后台子代理活在独立的 runner 进程中随时可查。装完即用,不需要学斜杠命令——直接说"用 reviewer 审这个 diff"就行。它预置了 scout(侦察)、reviewer(审查)这类常用角色,也支持保存的工作流和并行审查。与 mesh 的接口是 `mesh_run_control`:远程 stop 或 steer 一个 peer 的 RPC worker。
|
||||
|
||||
## 五、四件套的一次合奏
|
||||
|
||||

|
||||
|
||||
## 六、上手五分钟
|
||||
|
||||
安装各一行:mesh 与 subagents 走 pi 包管理——`pi install npm:pi-mesh-extension`、`pi install npm:pi-subagents`;pi-board 还没发 npm,源码三步(clone、`npm ci`、`npm run build`),且可 CLI 独立用(不需要 mesh 也能读写本地板);pi-jobd 同样源码构建,`pijob run / list / log / stop` 开箱即用。装完在同一目录开两个 pi 会话,就是两个 peer,`mesh_status` 互相可见——协同基建的“零配置”不是口号。
|
||||
|
||||
对话走 mesh,记忆上 board,长活交 jobd,排他靠租约——四个包各司其职,没有任何一个越界。这个博客的重设计期就是这套东西的实战场:两个 mesh 终端(op-gitea、op-artalk)与主线并行,一天 19 连发;发布链中断的修复窗口里,主线预约了 build.sh 紧急处置,另一边赶去补刀的编辑被预约机制正确拦下——撞车靠规则防住,不靠谁手快。
|
||||
|
||||
四件套加起来没有一个数据库、没有一个中心大脑,却把"谁在线、谁在干啥、学到过啥、长活谁看着"四件事全部钉死。小工具、硬边界、诚实状态——这套哲学比任何一个具体包都值得抄。
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: "Spec Harness 详解:把长程开发装进可验收的任务包"
|
||||
pubDate: 2026-11-01
|
||||
category: tech
|
||||
tags: [工程实践, AI 协作]
|
||||
---
|
||||
|
||||
AI 编程智能体最不缺的是写代码的带宽,最缺的是记忆。一个跨了好几轮会话的任务,改到一半的方案、已经否决的路线、验收口径,散落在聊天记录里就等于丢了——换一个会话、重启一次终端,上下文归零。[Spec Harness](https://github.com/MicTx/spec-harness) 解决的就是这个问题:它是一个面向 AI 编程智能体的任务包工作流工具,把长程改动保存为**可恢复、可验证的 Development Record**,用范围、证据和 Git 门禁管住全程。它不提供模型,也不替你做产品决策。
|
||||
|
||||
一句话概括它的世界观:**当一个编码任务的寿命超过一段对话,缺的不是更聪明的模型,而是一份大家(包括未来的你)都能读的合同。**
|
||||
|
||||
## 一、核心抽象:任务包
|
||||
|
||||
所有工作装进 `.spec/specs/YYYY-MM-DD_slug/` 目录,核心是两个文件:`spec.md` 写范围、假设与取舍(还有一节很特别的"已证伪路径",后面细说),`tasks.md` 写任务清单。收尾后整包归档到 `.spec/specs/archive/`,归档目录本身就是项目的工程日志。
|
||||
|
||||
每个任务必须带两行合同——**boundary**(这个任务能动哪些文件)和 **verify**(怎么证明做完了)。verify 是只读断言,跑出退出码 0 才许勾选。拿这个博客真实归档的 `2026-10-26_blog-5f` 包里 T3 任务举例:
|
||||
|
||||

|
||||
|
||||
boundary 划定"能动什么",verify 划定"怎么证明做完"——两行齐备,缺一不勾。这不是形式主义:多 agent 并行时,boundary 就是车道之间的护栏(我们的实践里,全局样式文件只许主线动,车道要新变量走申请);verify 则把"看起来好了"和"证明做完了"区分开,口头"过了"永远不算数。
|
||||
|
||||
`spec.md` 里最有味道的一节是**已证伪路径**:被否决的方案连否定理由一起入库。我们的 blog-5f 包里记着三条——首屏 banner 化(视觉裁定否决)、热力图换绿(破坏灰阶骨架)、标题 27px 收到 22px(层级锚点,要轻减字重不减号)。下次谁想再试一遍,先读档案,少烧一轮预算。
|
||||
|
||||
## 二、阶段流水线:五个核心命令与它们把守的门
|
||||
|
||||

|
||||
|
||||
五个核心命令串起一条可验证链:**目标 → 范围 → 任务 → 验证 → 归档 → 提交 → 推送**。每个阶段之间都有门把守:`spec.md` 有模板占位符就拒绝进入 run;`done` 要求每个任务 boundary/verify 齐备、验收面全绿且至少有一条非占位证据(脚本输出、测试结果);`push` 只合并 `spec/` 前缀的集成分支,文件改动与提交永远分开做,禁用 `--no-verify`。
|
||||
|
||||
被阻塞的任务会打 `!` 标记上浮给用户,绝不静默跳过;复核轮(check)发现的可执行问题必须当轮写回任务包并修复,不允许把"待办"当交付物甩给用户。
|
||||
|
||||
**中断恢复是常态设计**——这是它对“任务寿命超过对话”最直接的回答:任意时刻重跑 `/spec:run`,从 `tasks.md` 的勾选状态续跑,不依赖任何对话记忆。更进一步,`/spec:autoplan` 做交互式规划(澄清目标后过框架、细节、复核三轮,规划就绪即停,不自动开工),`/spec:autorun` 按规划逐包执行,默认最多 5 轮自动链(`--max-rounds` 可调),暂停后从同一步恢复;一键式入口 `/spec:goal` 则把规划、执行、复核、归档、提交一次跑完。
|
||||
|
||||
命令面共 12 个阶段命令:core 档装五个核心命令,full 档补齐 goal / autoplan / autorun / update / status / doctor / organize 七个扩展件——`doctor` 做环境自检与修复,`organize` 做结构审计。还有一个容易被忽略的细节:安装只是放文件,不会主动逐任务评估;想让它在你动手前主动问一句“这事要不要开包”,需要显式接线一次 `install_activation_hooks.py --mode on`。判据看交付形状——独立验收面、实际实施者、可恢复的在途任务;小修、只读调查、一次性实验都直接做。它防的是“忘了评估”,不防刻意瞒报——文档里对此毫不讳言。
|
||||
|
||||
## 三、多 agent 编排:一个编排者,有边界的帮工
|
||||
|
||||
Spec Harness 对多 agent 的态度很克制:**主会话是唯一的编排者**,路由、集成、验收和 `done`/`push` 门禁永远不出主线。旁支工作(探索、构建、审查这类上下文无关的活)可以委托,但要先过机器路由决策(`scripts/route_decision.py`)并写下任务合同:目标、范围、排除区、产出、验证方式五项齐备才许派。它甚至为"跨会话交接"准备了可选的 `handoff.md`——生成式快照头加 append-only 条目日志,单人、多人、agent、集群四种协作模式各有一套必填小节。
|
||||
|
||||
扩展位(slots)机制则把"别的团队流程"插进来:`team-loop` 管团队循环、`workflow-runner` 管工作流扇出,各自带 README 协议和测试。源码库里还附了 orchestrator/planner/reviewer/confirmer 四个编排代理定义。
|
||||
|
||||
## 四、安装:一个安装器,十余个宿主
|
||||
|
||||
Spec Harness 是 Python 3.9+ 的标准库实现(PolyForm Noncommercial 许可),安装就三步:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/MicTx/spec-harness.git /tmp/spec-harness
|
||||
cd /tmp/spec-harness
|
||||
git checkout v0.22.2
|
||||
bash install.sh
|
||||
```
|
||||
|
||||
安装器支持十余个宿主:Claude Code、Codex、Gemini CLI、Grok、OpenCode、OpenClaw、Hermes、Pi、ZCode、DeepSeek Harness、Devin、Cursor……不指定时自动探测已装的宿主。安装档位分 core / full:core 只装五个核心阶段命令加 Git 钩子;full 补齐全部 12 个阶段命令和 slots、agents 扩展组件。我们在 Pi 上用 `INSTALL_HOSTS=pi bash install.sh`,装完 `/spec` 系列命令直接可用。
|
||||
|
||||
## 五、跑在一个真实博客上
|
||||
|
||||
这个博客就是它的用户。归档目录里躺着从裸 clone 构建改造、移动端布局修复到 SEO 解封的一串任务包:blog-5f 一个包八个任务、三条车道(两条 mesh 终端加主线)并行,每个任务的 verify 都是能在命令行跑出退出码的断言,页面类任务统一挂 E2E——`E2E_BASE=http://127.0.0.1:18924 node tests/e2e.mjs`,34 项全过才算数。包与包接力,一天 19 个提交全绿落地。
|
||||
|
||||
门禁也真的拦过烂账:一次一个全 manual、从未执行的任务包混进了 `.spec/specs/`,check 门禁直接判 blocked,连带拦下所有携带归档路径的提交——直到把它移出。门禁防的不只是烂尾,还有“看起来有规划”的空包。
|
||||
|
||||
工具会迭代,但这套思路不会过期:**把"要干什么"写成合同,把"做完了"写成证据,把过程归档成日志。** 无论协作方是人还是 agent,这三件事都是长程工程的地基。
|
||||