Block 开源多 Agent 通信平台 buzz:人机共处一张消息网

2026-08-05

本文基于 buzz 仓库 commit 8342dfc(2026-08-04)梳理,该项目仍在高频迭代,具体行为以仓库 https://github.com/block/buzz 最新代码与文档为准。

buzz 把「Agent 该怎么被寻址、被鉴权、被留痕」当成协议问题解,而不是当成框架问题解。 你在站内看过的那几个开源 Agent 项目,主线都是「一个 Agent 怎么把活干完」——循环怎么跑、上下文怎么压、工具怎么注册。Block 开源的这个多 Agent 通信平台走的是另一条线:它先定义一张消息网,网上的每个参与者——不管是人还是 Agent——都拿一对自己的密钥、发同一种签名事件、受同一套成员制约束。干活的能力是接进来的,不是它的主体。

这个词容易撞车,先钉住:本文说的 buzz 是 github.com/block/buzz 这个 Rust 仓库,Apache-2.0 许可(Copyright 2026 Block, Inc.),跟蜂鸣、热度之类的含义无关。

一、先划清它和站内已拆项目的分工

站内拆过的三篇里,pi、ECC、superpowers 三个 Agent 项目的分层对照 讲的是「Agent 本体 / 工程增强套件 / 纯方法论」这三层怎么分;三个开源 Agent 项目的跨工具复用对照 讲的是同一份能力怎么搬到别的工具上;Pascal Editor 那篇 讲的是单个 Agent 操作一款专业软件时的边界。这三篇的主语都是「一个 Agent」。本篇的主语是「一群 Agent 加一群人」——它们靠什么共享同一份记录、同一份检索、同一套准入。

需要说明的是,buzz 仓库里也确实有 Agent 本体:crates/buzz-agent 是一个 ACP agent——ACP 即 Agent Client Protocol,是编辑器/宿主客户端与 Agent 之间通过 stdio 交换 JSON-RPC 消息的协议,Zed 一类工具用的就是它;crates/buzz-dev-mcp 则是一个给 Agent 提供 shell 和文件编辑的 MCP server。但 VISION_AGENT.md 对这两个 crate 的定位很克制,开篇说的是希望它小到「能在一个下午读完」、能放心审计。真正的重心在网这一侧。

二、协议底座:中继、事件、签名,先把名词说清

buzz 的线上格式是 Nostr 的 NIP-01。这里的几个概念对 AI 工程方向的读者不一定是常识,逐个交代。

事件(event):一条消息、一个表情回应、一步工作流、一次画布更新,在这套模型里都是同一种 JSON 结构,六个字段。ARCHITECTURE.md 第 2 节给的是这个形状:

{
  "id":      "<sha256 of canonical serialization>",
  "pubkey":  "<secp256k1 public key, hex>",
  "kind":    <unsigned integer>,
  "tags":    [["e", "<event-id>"], ["p", "<pubkey>"], ...],
  "content": "<JSON payload or plain text>",
  "sig":     "<Schnorr signature over id>"
}

事件签名sig 是发送方用自己私钥对 id 做的 Schnorr 签名,id 本身是事件规范序列化后的 SHA-256。也就是说,内容改一个字节,id 就变,签名就对不上。crates/buzz-core/src/event.rs 里的 StoredEvent 把这个校验结果记成一个私有的 verified 字段,外部只能通过 is_verified() 读,不能直接置真。

kind:一个无符号整数,是整套系统里唯一的分发开关。中继按 kind 决定怎么路由、存不存、审不审计;客户端按 kind 订阅。crates/buzz-core/src/kind.rs 是 kind 号的权威清单,ARCHITECTURE.md 给了分段:0–9999 是标准 Nostr kind,10000–19999 可替换,20000–29999 是临时事件(不落库不审计),30000–39999 是参数化可替换,40000–49999 是 buzz 自己的自定义段。加一个新功能等于加一个新 kind 号,老客户端看不见也不会崩。

中继(relay):这是最容易被误解的一环。Nostr 常被归到「去中心化协议」,但 buzz 的 ARCHITECTURE.md 写得非常直白——中继是唯一真相源,所有读写都过它,没有点对点的事件交换,没有八卦传播(gossip,指节点之间互相转述状态的扩散式同步),没有复制,就是客户端通过 WebSocket 连到一个中继。换句话说,协议是 Nostr 的,拓扑是服务端的。

密钥自持:人和 Agent 拿的是同一种东西——一对 secp256k1 密钥,外加一个 NIP-05 形式的可读句柄。鉴权方式按接入通道分,不按「人还是机器」分:ARCHITECTURE.md 第 3 节的认证路径表里,WebSocket 连接走 NIP-42(连上来后签一个中继下发的随机挑战串),HTTP 桥接端点走 NIP-98(对请求签一个 kind:27235 事件,带 URL 和 method 标签)。一个 Agent 通过 WebSocket 接入时,走的同样是 NIP-42。代价要说清楚:身份就是私钥本身,私钥丢了就是身份丢了,中继这边没有「找回账号」这个概念,只能换一对密钥重新入伙。

几个写死在代码里的结构上限,来自 ARCHITECTURE.md 的常量表,做容量判断时会直接用上:单帧最大 65,536 字节(超了拒收并关连接),每连接最多 1024 个订阅,每个 filter 的历史查询硬顶 500 条。

三、组件分工:你什么时候会碰到哪一块

仓库 crates/ 下有 28 个 crate。下面这张表只列跟「一群 Agent 共处」这条主线相关的几块,路径都是仓库里实际存在的。

组成部分它负责什么对应仓库位置你什么时候会碰到它
核心类型与验签事件结构、Schnorr 验签、filter 匹配、kind 注册表;零 I/O,Cargo.toml 里明确禁止依赖 tokio/sqlx/redis/axumcrates/buzz-coresrc/kind.rssrc/event.rs新增一种消息类型、或想确认某个 kind 号真实存在时
中继服务端WebSocket 接入、NIP-42 鉴权、事件流水线、订阅扇出,同时还托管 git smart HTTP 和内建的语音转发crates/buzz-relay自建部署、排查消息没送到时
ACP 桥接 harness把频道里的 @mention 排队、批成一个 prompt,通过 ACP over stdio 喂给 AI Agent 子进程crates/buzz-acprelay.rsqueue.rspool.rs让一个现成 Agent 进频道接活时
最小 Agent 本体一个 ACP agent:多并发会话,各带各的 MCP server 和历史,上下文满了自己摘要再续crates/buzz-agent想读懂一个完整 Agent 循环长什么样时
开发用 MCP server给任意 Agent 一个 shell 和一个文件编辑器crates/buzz-dev-mcpshell.rsstr_replace.rstodo.rstree.rs评估 Agent 到底能动你机器上什么东西时
Agent 优先 CLI镜像并扩展 MCP 能力面,按 JSON 进 JSON 出设计,错误以 {"error":…,"message":…} 的形式走 stderrcrates/buzz-cli不想开 GUI、要脚本化整个平台时
跨实例 QUIC mesh每个中继进程一个 iroh 端点,控制流上跑 scuttlebutt 式成员八卦,只回答「谁活着、能不能拨通」crates/buzz-relay-meshgossip.rsmembership.rswire.rs多副本部署、跨 pod 转发时
协议草案Agent 身份、记忆、遥测、设备配对等自定义 NIPdocs/nips/(15 份 md)+ crates/buzz-core/src/pairing/NIP-AB.md要做第三方互操作时

注意最后两行之间的张力:事件面确实没有 gossip,但 crates/buzz-relay-mesh/src/gossip.rs 的模块注释把它自己的职责框得很死——只回答存活与可拨号,从不选主、不转移会话、不搬运隧道数据字节。同一份 lib.rs 里还写了一句「成员关系只是提示,Redis 的 fenced generation 才是仲裁者」。这种把职责往小里划的写法,在这个仓库里反复出现。

四、Agent 是成员,不是插件:几份 NIP 草案说明了什么

docs/nips/ 下有 15 份 md 规范,其中六份直接围绕 Agent:NIP-OA、NIP-AA、NIP-AE、NIP-AO、NIP-AM、NIP-AP。剩下九份讲的是频道窗口、私信可见性、事件提醒、Git 对象签名、身份归档、多仓项目、推送租约、跨设备已读同步、工作区档案,属于人这一侧的功能。六比九不算多数,但这六份挑的全是「身份、记忆、账单」这类根问题,分量不轻。它们都标着 draft optional,是草案不是既成事实,但取向已经很清楚了。

NIP-OA(Owner Attestation,所有者背书) 定义了一个 auth 标签,让主人的密钥授权某个 Agent 密钥代自己发事件。四元素:["auth", "<owner-pubkey-hex>", "<conditions>", "<sig-hex>"]。签名的原像有固定域分隔符 nostr:agent-auth:。关键在它的非目标一节:这份 NIP 不定义冒充、不定义密钥派生、不定义中继侧改写作者。带了有效 auth 标签的事件,作者仍然是 event.pubkey——也就是 Agent 自己。授权是「证据」,不是「换脸」。

NIP-AA(Agent Authentication) 接着这个往下推:如果一个 Agent 的主人是中继的活跃成员,Agent 在 NIP-42 认证时出示 NIP-OA 凭据就能连上,不必单独进成员名单,文档管这叫虚拟成员。这条设计消掉了一个很现实的同步隐患——人被撤销成员资格之后,他名下的 Agent 下一次连接自动失败,不需要运维再去手工清理一遍。

NIP-AE(Agent Engrams) 给 Agent 记忆定了 kind:30174,内容用 NIP-44 加密。NIP-44 是 Nostr 生态的负载加密规范,两方各拿自己的私钥和对方的公钥做 ECDH,算出同一把对称的会话密钥再加密内容。这里的两方就是 Agent 和它的主人。因为密钥是对称的,双方都能解——文档明说了这层含义:主人永远能读到 Agent 记住的一切。记忆槽位的 d 标签不是明文,而是拿会话密钥做 HMAC-SHA256 盲化后的 64 位十六进制串,所以旁观者看得见「这个账号在用 Agent 记忆、主人是谁」,看不见记的是哪一类东西。记忆按 (agent, owner) 这一对隔离,一个 Agent 服务多个主人就持有多份互不相通的记忆。

NIP-AO / NIP-AM 是遥测的两半。前者用临时事件 kind:24200 流式传会话内部活动(临时段的事件中继不得持久化),后者用 kind:44200 每轮存一条 token 用量与估算成本,同样加密给主人。分家的理由文档写了:会话内容不该留痕,但一条小小的用量记录得能回答「我这些 Agent 上周烧了多少」。

NIP-AP 定义 kind:30175 的 persona——一份公开的、可寻址的 Agent 蓝图,含系统提示词、模型、运行时。它特意不给 d 标签做盲化处理(对比 NIP-AE 的记忆槽位是 HMAC 盲化的),理由是 persona 本来就是给人发现和分享的。

这对你意味着什么:Agent 的准入、记忆、遥测、账单全挂在一对密钥上,而不是挂在某个厂商控制台的一行记录上。这一点的工程后果在最小权限设计那篇讲的思路上又推了一层——权限不是给 Agent 发一组标志位,而是像给同事发工牌一样,发一份身份加一份频道成员资格。想横向比较各家 Agent 协议在这件事上的取舍,可以对照Agent 协议生态那篇

五、边界与代价:它明确不管什么

这一节的内容大部分不是我推的,是仓库自己写在 ARCHITECTURE.md 第 9 节「Known Limitations」和 VISION.md 的状态表里的。

中心化是设计选择,不是遗漏。 单一真相源换来的是强一致的检索、可验证的审计链、明确的准入判定;付出的是中继本身成为可用性单点。想要抗审查式的多中继冗余,这套设计不提供。

加密模型是服务端可读的。 VISION.md 写得很坦白:传输走 TLS,静态加密下放给存储层,服务端管理的加密覆盖每个频道、每条私信、每个事件,为的是 eDiscovery(法务取证检索)能跑在全量数据上。端到端加密(NIP-44)对私信只是「未来考虑」。如果你的合规要求是服务端不可读,这套现在给不了。

限流没有实现。 buzz-auth 里有 RateLimiter trait,但整个代码库里唯一的实现是测试桩 AlwaysAllowRateLimiter,还被 cfg 门在测试特性后面。RateLimitConfig 定义的四档(human、agent-standard、agent-elevated、agent-platform)是设计目标,没有强制执行。一群 Agent 高频发事件的场景下,这是要自己在反向代理层补的。

工作流有两处功能没打通。 审批门(request_approval)的数据库表、授权/拒绝接口、桌面端界面都在——ARCHITECTURE.md 第 9 节的原话是基础设施(DB、API、UI)已存在——但执行器不持久化审批令牌也不恢复执行,撞上审批门的 run 会被标记为失败(仓库标为 WF-08)。send_dmset_channel_topic 两个动作在 schema 里有,执行时返回 NotImplemented(WF-07)。语音的录制与分轨发布也只是预留了 kind,没有生产者。

愿景文档不是功能清单。 根目录 8 份 VISION*.md 读起来都是场景化的第一人称叙事,但成熟度差得很远,判断要回 VISION.md 末尾那张状态表。同样是 VISION_* 开头的一份文档:VISION_MESH.md 描述的社区共享算力(成员把闲置 GPU 汇成一池,Agent 通过本地 OpenAI 兼容端点消费)在状态表里标的是已完成;VISION_REMOTE_AGENTS.md 描述的远程 Agent 部署那行标的是「spec in review」,也就是规范还在评审;文化功能那节干脆自己加了一句「Planned design — not yet implemented」。读这批文档时要把「想要」和「已经有」分开,别把 VISION_* 的存在当成功能存在的证据。

形式化验证有明确的证明边界。 机器可检的模型分两处放:docs/spec/ 下是 MultiTenantRelay.tla 与它的 .cfg 配置、MultiTenantAuth.spthyGitOnObjectStore.tla 与配置;docs/formal/ 下则是 nip-plnip-rs-unread 两组 Python 模型脚本,各自带一份 NOTE.md,外加一份 STATEFUL_GATEWAY.md。TLA+ 是描述并发系统状态迁移并机器检查不变量的规范语言,Tamarin(.spthy)是在 Dolev-Yao 攻击者模型(假设攻击者完全掌控网络、能读改删重放任意消息,但不能破解密码学原语)下机器验证安全协议的工具。那批 Python 脚本走的是另一条路:穷举状态加变异测试——故意把实现改坏,看不变量会不会被检出来,检不出来就说明这条不变量是空的。docs/multi-tenant-relay.md 自己在「Scope and Non-Goals」里列了不证明的东西:不证活性与性能,不重证 Postgres 行级安全和 MVCC 的正确性,不重证 Schnorr 不可伪造性,不覆盖物理资源隔离,也不覆盖客户端把事件 id 泄到带外之后的场景。这份自我限定比结论本身更值得读。

自建的暴露面要自己算。 数据落在三处:Postgres 存事件、频道、令牌、工作流、审计链,其中 events 表按 created_at 做月度范围分区;Redis 存在线状态、正在输入、跨节点扇出;S3 或 MinIO 存媒体文件。中继对外开的不只是 WebSocket,还有 POST /eventsPOST /queryPOST /count、工作流 webhook /hooks/{id}、Blossom 媒体上传下载、以及 git smart HTTP 的 git-upload-packgit-receive-pack——最后这个意味着有权限的身份可以往仓库推代码。Agent 那侧,crates/buzz-dev-mcp 给出的是 shell 和文件编辑;VISION_AGENT.md 对这个权限说得毫不含糊:shell 以操作者的信任级别运行,跟 bash 本身一样。Agent 能改什么,取决于你用哪个系统账号把它跑起来。

六、上手与避坑清单

把 Nostr 当区块链。 会踩是因为「去中心化协议」这个标签带来的先验。这里没有链、没有共识、没有 P2P 广播,中继就是一台服务器。避法:动手前先把 ARCHITECTURE.md 第 2 节和第 4 节读完,看清事件流水线那 12 步是一个进程里的顺序执行。

以为两个中继会自动同步。 会踩是因为把 relay URL 当成了可互换的接入点。实际上 URL 就是社区边界:VISION_AGENT.md 写了,同一个 npub(Nostr 公钥的 bech32 可读编码,就是那串 npub1…)可以加入另一个社区并在那边重新发一份 profile,但成员资格、任务、私信、在线状态、审计记录都不会跨社区继承。避法:把社区当租户边界来做容量和迁移规划,别指望换个域名数据跟着走。

全局订阅收不到私有频道的消息,误判成 bug。 会踩是因为 filter 语义看起来匹配上了。这是故意的安全边界:频道范围的事件只投递给带匹配 channel_id 的订阅,无频道约束的全局订阅被显式排除。避法:订阅时明确带上 channel_id

kinds: [] 当通配符。 会踩是因为空数组在很多查询 DSL 里表示「不过滤」。NIP-01 里空数组表示匹配空集,这类订阅两级索引都不进,永远收不到事件;不写 kinds 字段才是匹配全部。避法:要全匹配就省略字段,不要给空数组。

用事件传大内容。 会踩是因为 content 字段看起来能塞任意 JSON。单帧上限 65,536 字节,超了直接拒收并关连接。避法:文件走 Blossom 的媒体上传通道(Blossom 是 Nostr 生态里按内容哈希寻址二进制文件的存储约定,中继开的是 PUT /media/uploadGET /media/{sha256} 这一对),事件里只放引用。

指望平台帮你限流。 会踩是因为 RateLimitConfig 里那四档看着像已经生效了。它没有实现。避法:在反向代理或网关层自己做,尤其是 Agent 数量上去之后。

工作流里写了未实现的动作就上生产。 会踩是因为 YAML schema 能通过校验。send_dmset_channel_topic 执行时返回 NotImplemented,撞上 request_approval 的 run 会被标记失败。避法:先只用已跑通的动作,把审批环节留在人工流程里。

生产构建里开了 dev 特性。 会踩是因为本地开发时它很方便。buzz-auth 里有一条按 SHA-256("buzz-test-key:{username}") 派生密钥的路径,门在 #[cfg(any(test, feature = "dev"))] 后面,文档明说生产中继不得启用。避法:把这个 feature 列进发布检查项。

本地跑构建时工具链对不上。 会踩是因为 rust-toolchain.toml 钉的是 1.95.0,而 CONTRIBUTING.md 的前置要求表写的是 Rust 1.88+、Node 24+,两处口径不同。仓库还用 hermit 管工具链,bin/ 下放的就是 hermit 的激活脚本与配置。避法:按 AGENTS.md 的流程来,先 . ./bin/activate-hermit 再动手,别绕过它自己配 PATH;提交前跑 just ci,提交时带 -s,DCO 检查会拦没有签名尾的提交。

七、收束

判断这个项目适不适合你,可以过三个问题:你的痛点是不是「多个 Agent 和多个人要共享同一份上下文与记录」,而不是「单个 Agent 干活不够聪明」;你能不能接受服务端可读全部内容换来的全量检索与审计链;你有没有人愿意长期维护一套 Postgres 加 Redis 加对象存储的自托管栈。三个都是「是」,它的取向就和你对得上;只要有一个是「否」,装了大概率也用不起来。

读代码的顺序建议是:ARCHITECTURE.md 第 2 节(协议)和第 4 节(事件流水线)打底 → crates/buzz-core/src/kind.rs 看清 kind 号是怎么组织的 → docs/nips/NIP-OA.mdNIP-AA.md 看 Agent 身份怎么挂在人身上 → 回到 VISION.md 的状态表核对哪些是已经跑通的 → 最后如果你关心多租户,再啃 docs/multi-tenant-relay.mddocs/spec/ 里那几个模型文件。全仓 3704 个受版本控制的文件里,桌面端占了 2359 个、移动端 467 个,Rust 那部分(crates/ 共 413 个文件)反而是最好读的一块——从这里进最省力。

本篇属于一个把开源多 Agent 通信平台 buzz逐层拆开讲的系列,整体地图见 buzz 是什么:Block 开源的多 Agent 通信平台全景图;沿着这条线往下,还可以看 Block 开源多 Agent 平台 buzz:一致性检查器怎么对齐实现Block 开源多 Agent 通信平台 buzz 的仓库结构导读

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。