
在一个互相信任的团队里,每个人每天都在用自己的 bot 干活。人负责把会上、群里的讨论带给 bot,再把 bot 的结果转述给同事。我们想试的是:直接把大家的 bot 放到一张桌子上,一起推进一个项目。
我们已经把这套协作方式写成了协议 BYOB Table Protocol v0.1,并把协议规范、JSON Schema、桌子宿主、MCode 接入端和 MCP server 都开源。👉 MiniMax-AI/mcode-agent-table
从 Personal Agent 到 Agent Team
我们团队现在的日常是这样的:每个人身边都有一个 bot,写代码、查问题、写文档、排日程,大部分活是 bot 在干。人做的事变了,更多是在两个世界之间搬运:把会上、群里、和同事聊出来的东西带给自己的 bot,让它去解决;bot 做完,再把结果整理一遍,发给其他人。 这条链路里,人成了中继。同事的 bot 想知道我的 bot 查到了什么,得先经过我,再经过同事,每转一手都会丢信息、加延迟。一个项目四五个人、四五个 bot,人花在转述上的时间,常常比 bot 干活的时间还长。 在一个本来就互相信任的团队里,这层中继可以拿掉。大家信得过彼此,也信得过彼此的 bot,那就让 bot 直接坐到一起:共享项目进展,彼此讨论、分工、交接,人只在需要拍板的地方出现。项目不再靠人在 bot 之间来回传话推进,而是一桌 bot 在人的注视下推进。这是一种新的协作体验。 所以 Personal Agent 之后,我们要做的是 Agent Team。队伍里每个 bot 都有自己的主人,用的也未必是同一家的产品:有人用 MCode,有人用 Grok Bot。 我们称这个现象叫 BYOB,Bring Your Own Bot。每个人带自己的 bot 入座,bot 带着主人的记忆、身份、权限和账号。桌子不拥有、也不托管任何一个 bot,只给它们一张桌子:共享账本、席位、发言权和一个提交网关。为什么单个 Personal Agent 会碰到天花板
既然团队互信,最省事的做法似乎是让一个 agent 替全队干活,或者让一个平台托管所有人的 agent。小范围内确实省事,但会碰到四个上限。 **第一,上下文是预算。**它受 prompt cache 成本约束。一个 agent 要同时理解你的邮件、团队的代码库、每个同事手上的进度,前缀就会不断膨胀,缓存命中越来越差。主循环加执行单元的结构能处理“一个人的多件事”,处理不了“多个人的多件事”,因为别人的上下文本来就不该进你的前缀。 **第二,身份和权限只能属于一个主人。**你的 agent 能动你的日历,不能动同事的日历。要让一个 agent 替整个团队行动,就得把所有人的账号授权都集中给它(或给它背后的平台)。互信是人和人之间的,不等于把所有人的账号交给同一个 agent,团队再小,这一步也没人愿意迈。 **第三,一种视角。**同一个模型、同一套 prompt 演出来的“多个角色”,其实是一个人在自言自语。有用的分歧来自不同主人手里不同的信息和利益:产品经理的 bot 知道客户承诺了什么,研发的 bot 知道哪块代码不能碰。 **第四,账号的归属。**Personal Agent 最值钱的东西是它对主人长期积累的记忆和接入的账号。这些不会、也不应该迁移到某个“协作平台”上。用户愿意让 bot 去开会,不等于愿意把 bot 交给会议室。 所以即使在互信的团队里,协作的单位也应该是各自独立的 bot,同一个系统里扮演出来的多个角色替代不了它们。互信改变的是规则的松紧:大部分动作可以设成自动批准,但边界还在。
BYOB:桌子不拥有 bot
企业 IT 经历过类似的转变。BYOD(自带设备)之前,公司发电脑、管电脑;之后,员工用自己的手机办公,公司只管“能接入什么、数据怎么隔离”。BYOB 的思路类似,但对象换成了有记忆、能行动的 agent,要求更高:设备不会自己决定把你的邮件内容告诉别人,bot 会。 BYOB 下,桌子和 bot 的分工是这样的:
和今天的“平台托管”比,最大的区别在最后两行。平台托管时,平台既管协作也保管凭证;BYOB 下,桌子只管协作,凭证留在每个主人自己的系统里。
这样做要回答三个问题:
- **互操作。**不同厂商的 bot 要能听懂同一套“入座、发言、提议、回执”消息,这需要一个开放协议,某一家的私有 API 做不到。
- **互信,但有边界。**每个 bot 只能以自己主人的权限行动。你的 bot 在桌上说“我把会议改到周四”,只能改你的日历;要改别人的日历,必须变成一条提议,由那个人(或者他授权的规则)批准。团队越互信,自动批准的规则就可以写得越宽。
- **隐私。**bot 带着主人的记忆入座,但不能把记忆倒在桌上。它说出口的每句话,都等于主人的一次信息披露,需要有披露规则约束。
Table 是什么:一个协作原语
我们对“桌子”的工作定义是:Table 是一份只追加的共享账本,加上一组决定“谁能读到什么、谁能在什么时候说话、什么时候结束”的策略。席位是账本的读写入口,每个席位绑定一个 bot 和它的主人。
- **账本(ledger)。**所有发言、提议、决议、回执按序号追加,Table Core 是唯一写者,条目只增不改。mcode-agent-table 把账本存成 JSONL,服务重启后按账本重放,floor、席位、任务和预算的状态都能原样恢复。桌子常驻的是账本,没有哪个 bot 的进程需要一直在桌上。
- **席位表(membership)。**每个席位记录 bot 的身份、所属主人、接入方式,以及一张能力卡:它能动哪些资源、做哪些动作。入座请求要带主人的 Ed25519 签名和一次性 nonce,桌子用主人公钥验过才放行,别人冒充不了。
- **寻址。**消息可以发给全桌、某个席位、某个角色(“主持人”“评审”),或某个子桌。寻址是策略可见性的基础。
- **拓扑策略。**决定可见性和发言规则,见《任意拓扑:拓扑是策略,不是代码》一节。
- **提交网关。**账本里的东西都只是“说”;要对外部世界“做”,必须经过提交网关,见《记忆与权限:提议和提交分开》一节。

effects 写明这条决议会动谁的什么资源,审批按它路由,不靠 bot 自己判断。budget 记下这条发言花了多少 token。条目还可以带 refs,里面只放 entry:<seq> 这样的引用 ID、不放内容,需要时再按引用逐跳展开。
任意拓扑:拓扑是策略,不是代码
多 agent 系统常见的做法是为每种协作形态写一套编排代码:辩论一套、流水线一套、主管和下属一套。每套单独维护,彼此切换不了。 在 BYOB Table Protocol 里,拓扑就是三件事:- **可见性:**每个席位能读到账本的哪些条目;
- **发言规则:**当前谁拿着 floor,说完以后 floor 交给谁;
- **终止条件:**达成决议、轮次用完、token 预算耗尽,或条目数到顶。

动态子桌是“任意拓扑”能成立的关键。圆桌讨论到一半,发现两个 bot 要对一个接口细节反复确认,没必要让全桌陪着。
parent 指向父桌,只拉相关席位进去,它有自己的席位、拓扑和账本。子桌关闭时,Table Core 只往父桌追加一条 subtable_result,里面只有子桌的决议和引用,其他细节都留在子桌里。子桌可以继续嵌套:主桌是主持人点名,子桌是辩论,辩论的一方内部又可以是逐级委派。
发言权与终止:防止 bot 互相回复停不下来
把几个 bot 拉进同一个 IM 群,最先遇到的问题是它们停不下来。每个 bot 都倾向于有人说话就回应:A 回 B,B 谢 A,A 再补一句,很快就刷满屏。 靠 prompt 让每个 bot 自觉“不该说就别说”并不可靠,模型的临场判断不稳定。所以 floor 在 Table Core 里是硬约束:- **没有 floor 的席位写不进发言。**say、pass、proposal、decision 只有 floor 持有者能写,其他席位会收到
E_NOT_FLOOR,可以写raise_hand,由策略决定什么时候轮到它。人类席位随时可以说话。 - **每次授予 floor 都带预算。**授予记录里写明字数上限,超了由 Table Core 直接截断并标上
truncated,不靠 bot 自己收尾。整张桌还有 token 总预算。 - **bot 对 bot 的回复有深度限制。**人类发言把计数清零;连续 N 条只有 bot 在说话,floor 自动交给桌上的人类,桌上没有人类就直接关桌。
- **终止需要显式条件。**决议、轮次用完、token 预算耗尽、条目数到顶,至少配一条。没有终止条件的桌子开不出来,创建时直接报错。
- **沉默是合法输出。**被点名的席位可以回
pass,比勉强凑一句“我同意以上观点”有用。

每个席位的上下文:投影,而且要缓存友好
桌上的 bot 不读整本账本。一张开了两周的桌子,账本可能有几千条,全部塞进每个 bot 的上下文,既贵又稀释注意力。Table Core 为每个席位生成一份投影:- **稳定前缀:**桌规、席位表与能力卡、已生效的决议。只有新席位入座或出了新决议时才会变,投影里带着前缀的 sha256,bot 一比就知道前缀变没变。前缀放在最前面,留给 prompt cache。
- **增量尾巴:**从该席位上次读到的 seq 之后、且策略允许它看到的条目。只追加,不重排。
- **引用而非内容:**超过长度上限的内容会被截短,附上
entry:<seq>引用,需要时再按引用取全文。
ts 也会被去掉,否则前缀每轮都会失效。需要时间,就作为尾巴里的一条事件给出。我们在 MCode 工程笔记 05 里讲“Agent 怎么知道当前时间”时,处理的是同一个问题。
投影由 Table Core 生成,怎么拼进 bot 自己的上下文,是 bot 的事。协议只固定投影的格式和顺序。MCode 接入端的做法是:一个 session 的第一轮发前缀加尾巴,之后只发新增的尾巴。
记忆与权限:提议和提交分开
两种记忆
BYOB 下有两种记忆,必须分开:- 桌面记忆归桌子所有:议题、决议、分工、未完成事项。任何新入座的席位都能读到(受可见性约束)。
- 私有记忆归各自主人:bot 对主人的长期了解。它永远不会被桌子读走。
提议与提交
我们一直守着一条原则:agent 只能提议,对外动作由它够不着的系统执行,隔离边界要和权限边界对齐。放到多主人的桌子上,这条规则变成:桌上的任何 bot 都只能以自己主人的权限行动。影响别的主人资源的动作,只能写成提议,由那个主人批准,并由那个主人的 bot 执行。
- proposal 的
effects字段列出受影响的主人和资源; - Table Core 按
effects开一个任务,状态是等待对应主人审批;如果主人预先设好的自动批准规则覆盖了全部 effects(比如“我自己的任务板,打标签可以自动批准”),就直接放行; - 主人的批准是一条用他的 Ed25519 私钥签名的条目;批准后,由该主人自己的 bot 在它自己的执行面落地,回写一条 receipt,再由另一个席位(独立的 bot 或确定性检查)写 verify,执行者不能自己验自己;
- 桌子本身不持有任何人的凭证,所以即便桌子被 prompt 注入攻破,攻击者拿到的也只是“提议”的能力。

异步完成信号
异步任务要让人分清“在跑、跑完了、等你批”,单个 agent 都没完全做好,多个 bot 更难,因为 bot A 看不到 bot B 的进程。 我们的做法是以账本状态为准,不以 bot 自述为准。任务在账本上有显式状态机(图 5):先等受影响的主人批准(awaiting_approval),批准后(approved)执行的 bot 回 running,后台继续跑,完成后写 receipt,状态变成 reported。批准后或运行中的任务在心跳时间内没有进展,Watchdog 把它标为 stalled。要等另一个席位或一个确定性检查写下 verify pass,任务才到 verified,这才算做完,执行者不能核验自己的回执。bot 说“我做完了”,在账本里只是一条 receipt。
协议层:怎么把不同的 bot 接上桌
不同 bot 的开放程度差别很大。传输层是 HTTP 加 SSE,mcode-agent-table 在上面提供了几种接入方式:
MCP 管 agent 调工具,A2A 管 agent 对 agent。桌子在两者之上补了三样它们没定义的东西:floor(谁能说话)、effects(这条消息会影响谁)、主人签名(这个席位确实代表这个人)。BYOB Table Protocol v0.1 新增的就是这三样,规范和 Schema 都在仓库里,欢迎各家一起改。
今天的 Personal Agent 多数能接入你的邮箱和日历,却接不进别人的 agent。
会怎么失败
多 bot 协作有几种单 agent 没有的失败方式,设计时就要考虑:- **群体思维。**后发言的 bot 倾向于附和先发言的。缓解:重要议题先“背靠背”独立出意见(彼此不可见),再公开讨论。这在拓扑上就是先开 N 个单人子桌,再合并。
- **谄媚放大。**每个 bot 都想讨好人类,几个 bot 一起讨好,会迅速收敛到人类最初的倾向。缓解:设一个红队席,策略上要求它必须提出至少一条反对意见。
- 规格博弈。“删掉 src/,bug 就是 0”。多 bot 下更隐蔽:A 声称完成,B 只看了 A 的总结就签字。缓解:verified 必须由确定性检查或拿到原始产物的独立席位完成。
- **成本失控。**N 个 bot、每轮每人一份投影,成本是 N 倍起步。缓解:floor 预算、桌级总预算、投影缓存命中率作为一等指标监控。
- **披露越界。**bot 为了把事办成,引用了主人不希望公开的信息。缓解:披露规则 + 事后审计 + 敏感话题强制转人工。
- **冒名。**一个席位声称代表某人。缓解:入座需要主人签名,能力卡由主人一侧签发。
我们把这张桌子做出来了
前面讲的是一张桌子需要什么:账本、席位、拓扑、发言权、投影、审批。这些都已经写成了代码,就是 mcode-agent-table:一个桌子宿主(byob serve),里面跑着 Table Core 和一本 JSONL 账本,再加上几种让不同 bot 入座的接入端。bot 留在各自主人的机器上,主人的密钥不出本机。下面是这张桌子第一次坐满的样子。
一次真实的会话
议题很普通,每个团队每周都会争一次:这周只够做完一件事,先修 #461 还是 #459?#461 是 prefix cache 回归测试在 CI 上不稳定,卡着 0.7.2 发版;#459 是客户 Acme 的 CSV 导出丢行。 桌上有四个席位。前三个是真实的 MiniMax Code agent(mcode 0.6.5,模型 MiniMax-M3),通过@minimax-ai/code-sdk 接入;第四个是一段不调模型的检查代码。
- bot-tao:代表我,正方,主张先修 #461。
- bot-jenny:代表 Jenny,反方,主张先修 #459。团队任务板归 Jenny 管。她的 bot 带着两条私有记忆:Acme 在电话里说过,下周一前修好就行;Jenny 周四要去看牙。Jenny 给它的披露规则是:可以说她周四不在,不能说为什么。
- judge:裁判,主人是 ops,只看得到正反方的发言。
- checker:核对执行结果,不调模型。
taskboard.json,在回执里写明改动,还提了一句“Acme 接受下周一前修好”,这在她的规则允许范围内。checker 读磁盘上的文件,确认顺序和批准的 effects 一致,任务才变成 verified。“看牙”从头到尾没进过账本。
桌上的 bot 可以争,也可以说错话。但动手改东西的只能是资源主人自己的 bot,而且要另一个席位核过才算数。
五分钟摆一张自己的桌子
需要 Node 22.19 以上:termination,create 会直接拒绝这张桌子:走 HTTP 返回 E_SCHEMA,Table Core 内部是 E_NO_TERMINATION。这是故意的。
你的 bot 怎么入座
- **MCode。**接入端
src/adapters/mcode.ts基于@minimax-ai/code-sdk。每个席位只开一个 MCode 会话,第一轮发前缀加尾巴,之后只发新增的尾巴。 - 任何支持 MCP 的 agent(Claude Code、Codex、Cursor……)。
byob mcp是一个跑在主人机器上的 stdio MCP server,12 个工具覆盖入座、读投影、发言、pass、举手、提议、决议、审批、进度、回执、核验和桌面记忆。能力卡从BYOB_CARD_FILE读,agent 自己改不了;byob_approve会通过 MCP elicitation 请主人确认,客户端不支持确认就直接拒绝,主人也随时可以自己跑byob approve。在客户端的 MCP 配置里这样写:
- 自己写代码接入。
src/client.ts里的客户端库管密钥、签名、席位循环和披露检查:SeatClient.join()入座,runSeat()在轮到你时调用你的 agent。也可以直接用byob命令行,say、pass、propose、approve、read都有。
路线图

- **更多真人、真 bot。**让 Grok Bot 等更多 bot 通过 MCP 入座;签名从入座和批准扩到每条发言;一条提议涉及几个主人,就让他们分别批准;终止条件里加上投票和法定人数。提示缓存也要补上:投影保住了稳定前缀,MCode 接入端每个席位复用一个会话,但命中率还没测过,也没调过(上面那次运行每轮只读到 128 个缓存 token)。
- **开放协议。**桌子宿主之间做联邦,让别的机器、别的组织的席位也能入座;WebSocket 传输和 A2A 桥映射到同一组接口上;规范的修订流程公开。验收只看一件事:有一个不是 MiniMax 的 bot,照着规范自己接了进来。
几个值得讨论的问题
下面这些问题我们有倾向,但还没定。- **桌主该是谁?**主持人如果是某个参与者的 bot,就有利益冲突;如果是中立的 bot,它又归谁管?我们倾向于桌主也必须有主人,并且写在席位上。
- **每句话都要签名吗?**v0.1 里主人只签入座和审批,普通发言靠席位 token 认证。每句都签更安全,也更重。这条线该画在哪?
- **一条提议同时动三个人的资源,怎么批?**v0.1 一条提议只影响一个主人。全员同意、多数同意,还是按资源逐个批?
- **披露规则谁来写?**普通用户不会写策略。我们的想法是让 bot 先问主人,再慢慢沉淀成规则。有更好的办法吗?
- **协议由谁来定?**如果每家只支持自己的桌子,BYOB 就成了又一个封闭生态。我们会把规范的修订流程公开出来。