Beads bd merge-slot 完全指南:用互斥合并槽串行化多 Agent 冲突解决
【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads
bd merge-slot是 Beads 提供给编码 Agent 的排他访问原语(exclusive access primitive):每个 rig 只能有一个 Agent 同时持有合并槽,从而把合并队列中的冲突解决过程串行化,避免多个 Agent 同时抢着解决冲突而制造"级联冲突"。本文以 docs/cli-reference/merge-slot.md 为骨架,结合cmd/bd/merge_slot.go的命令实现与internal/storage/merge_slot.go的存储层实现,完整讲解create / check / acquire / release四个子命令、--holder与--wait两个关键参数,以及槽位状态机、等待队列与事务级原子性保证,读完即可在自己的多 Agent 协作工作流中直接落地使用。
一、为什么需要 merge-slot:串行化冲突解决
在多 Agent 并行协作的场景下,多个 Agent(文档中戏称为 "polecats")可能同时检测到合并冲突并争相解决。如果它们各自基于同一份冲突现场做修改,结果往往是互相覆盖、冲突越解决越多——这就是文档中所说的 "monkey knife fights"(猴群抢刀混战)以及"级联冲突"(cascading conflicts)。
bd merge-slot正是为此设计的排他访问机制:一个合并槽同一时刻只允许一个 Agent 持有。谁持有槽,谁才有资格进行冲突解决;其他人要么等待,要么排队。这样冲突解决被强制串行化,从根源上消除了并发写冲突。
在 Beads 的存储接口定义中,这一原语被明确注释为"serialized conflict resolution primitive"(见 internal/storage/storage.go),属于Storage接口能力之一,与DoltStore、EmbeddedDoltStore两种存储实现均兼容。
二、merge slot 的数据模型
合并槽不是一个独立实体,而是以issue(珠子 bead)的形式存储在仓库中的,每个 rig 恰好有一个合并槽珠子:
- 槽 ID:
<prefix>-merge-slot,其中 prefix 取自配置键issue_prefix。例如issue_prefix=gt时槽 ID 为gt-merge-slot;未配置时回退为bd-merge-slot。该规则由MergeSlotID实现(见 internal/storage/merge_slot.go)。 - 标签:所有合并槽统一打上
gt:slot标签(常量mergeSlotLabel),便于工具无需知道精确 ID 就能通过标签检索到槽位(见 internal/storage/merge_slot.go)。 - 标题与描述:创建时标题为
Merge Slot,描述为 "Exclusive access slot for serialized conflict resolution in the merge queue.",类型为TypeTask(见 internal/storage/merge_slot.go)。
槽位状态通过两个字段联合表达:
| 字段 | 取值 | 含义 |
|---|---|---|
status | open | 槽位可用,等待被获取 |
status | in_progress | 槽位已被某个 Agent 持有 |
metadata.holder | 字符串 | 当前持有者的身份标识 |
metadata.waiters | 字符串数组 | 按优先级排序的等待者队列 |
其中metadata内部序列化为 JSON,结构为:
type slotMeta struct { Holder string `json:"holder,omitempty"` Waiters []string `json:"waiters,omitempty"` }(见 internal/storage/merge_slot.go)。parseSlotMeta负责从 issue 的Metadata字段反序列化出持有者与等待队列(internal/storage/merge_slot.go)。
三、命令总览
bd merge-slot是挂在根命令下的子命令组,属于issues命令分组(GroupID: "issues"),完整用法:
bd merge-slot [flags]常用示例:
bd merge-slot create # 为当前 rig 创建合并槽 bd merge-slot check # 检查槽位是否可用 bd merge-slot acquire # 尝试获取槽位 bd merge-slot release # 释放槽位命令组下共四个子命令:create、check、acquire、release,全部通过 cobra 注册(见 cmd/bd/merge_slot.go)。四个子命令均声明Args: cobra.NoArgs,即不接受位置参数;同时设置了SilenceUsage与SilenceErrors,错误信息会走统一的错误处理通道。
四、bd merge-slot create:创建槽位
为当前 rig 创建用于串行化冲突解决的合并槽珠子:
bd merge-slot create [flags]要点:
- 槽 ID 根据 beads 前缀自动生成(如
gt-merge-slot),规则见上文MergeSlotID。 - 创建后槽位状态为
status=open(可用)。 - 幂等:如果槽位已存在,直接返回现有槽位而不报错(见 internal/storage/merge_slot.go),可以放心重复执行。
成功输出示例:
✓ Created merge slot: gt-merge-slot使用 JSON 输出(--json)时返回{"id": "...", "status": "open"}。注意create属于写操作:代码中先调用CheckReadonly("merge-slot create"),在只读模式下会被拒绝(cmd/bd/merge_slot.go)。
五、bd merge-slot check:检查槽位状态
检查合并槽当前是可用还是被持有:
bd merge-slot check [flags]返回三种结果:
available:槽位可被获取(status=open);held by <holder>:槽位正被某 Agent 持有;not found:当前 rig 尚不存在合并槽。
not found时命令会打印槽 ID 并提示先执行bd merge-slot create,不会以错误退出(cmd/bd/merge_slot.go)。
槽位被持有时,非 JSON 输出会展示持有者与等待队列(cmd/bd/merge_slot.go):
○ Merge slot held: gt-merge-slot Holder: agent-a Waiters: 3 1. agent-b 2. agent-c 3. agent-dJSON 输出结构为{"id", "available", "holder", "waiters"},其中空holder会被规范化为null(nilIfEmpty,见 cmd/bd/merge_slot.go)。
六、bd merge-slot acquire:获取槽位(核心)
尝试获取合并槽以获得排他访问权:
bd merge-slot acquire [flags]Flags:
--holder string Who is acquiring the slot (default: BEADS_ACTOR) --wait Add to waiters list if slot is held行为规则:
- 槽位可用(
status=open):直接获取成功——status置为in_progress,holder设为请求者身份。 - 槽位被持有(
status=in_progress):命令失败(退出码非 0),并提示:slot held by: <holder> Use --wait to add yourself to the waiters queue.除非传入
--wait,此时请求者会被加入等待队列。
--holder用于指定"谁在获取",默认取BEADS_ACTOR环境变量;若两者都为空,命令直接报错no holder specified; use --holder or set BEADS_ACTOR env var(cmd/bd/merge_slot.go)。
成功获取的输出:
✓ Acquired merge slot: gt-merge-slot Holder: agent-a--wait入队后的输出(注意此时命令以静默退出码结束,SilentExit()):
○ Slot held by agent-a, added to waiters queue (position 2)JSON 模式下,成功返回{"id", "acquired": true, "holder"};入队返回{"id", "acquired": false, "waiting": true, "holder", "position"};获取失败返回{"id", "acquired": false, "holder"}。
原子性:为什么不会两个 Agent 同时拿到槽
acquire的关键在于原子 check-and-set。存储层实现MergeSlotAcquireImpl将整个"读取槽位 → 判断状态 → 更新状态/元数据"过程包在RunInTransaction事务中(见 internal/storage/merge_slot.go),确保两个 Agent 并发调用时只有一个能观察到open状态并成功置为in_progress,另一个必然看到in_progress而走失败/排队分支。这正是"排他"语义的底层保障,事务描述字符串形如bd: acquire merge slot gt-merge-slot for agent-a。
此外,入队逻辑自带去重:若--wait的请求者已在waiters中,不会重复追加(internal/storage/merge_slot.go)。
七、bd merge-slot release:释放槽位
冲突解决完成后释放合并槽:
bd merge-slot release [flags]Flags:
--holder string Who is releasing the slot (for verification)行为规则:
- 将
status置回open,并清空holder字段; --holder用于身份校验:若传入的持有者与当前metadata.holder不一致,释放失败并报错slot held by <X>, not <Y>(见 internal/storage/merge_slot.go);- 若槽位本身已是
open,释放为幂等操作,直接成功返回; - 释放时保留
waiters队列(newMeta := slotMeta{Waiters: meta.Waiters}),因为等待者仍需要排队;文档明确建议:若有等待者,应由优先级最高的等待者随后获取(acquire)槽位。
成功输出:
✓ Released merge slot: gt-merge-slotJSON 输出为{"id", "released": true}。与create、acquire一样,release也是写操作,受只读模式保护(CheckReadonly("merge-slot release"),cmd/bd/merge_slot.go)。
八、存储层实现:两种后端共享同一套逻辑
Beads 的存储抽象由DoltStore与EmbeddedDoltStore两种后端实现,二者都满足Storage接口,因此合并槽的全部业务逻辑被抽到internal/storage包中的共享实现函数,两个后端只是薄薄地转发:
- internal/storage/dolt/merge_slot.go:
DoltStore的四个方法全部委托给MergeSlot*Impl; - internal/storage/embeddeddolt/merge_slot.go:
EmbeddedDoltStore同样委托给同一组共享实现。
而MergeSlotStatus(SlotID / Available / Holder / Waiters)与MergeSlotResult(SlotID / Acquired / Waiting / Holder / Position)两个返回结构定义在 internal/storage/storage.go,Position为 1 起始的等待队列位置。这种"接口 + 共享实现 + 后端适配"的结构意味着:无论底层是独立 Dolt 服务还是嵌入式 Dolt,bd merge-slot的命令行语义与事务保证完全一致。
九、典型多 Agent 工作流
将上述命令串起来,就是一个标准的"串行化冲突解决"协作流程:
# 0. 初始化(仅需一次,幂等可重复执行) bd merge-slot create # 1. 进入合并队列的 Agent 先检查槽位 bd merge-slot check # 2. 尝试获取排他访问权;被占用时选择排队等待 bd merge-slot acquire --holder agent-a --wait # 3. 拿到槽位后,执行冲突解决、合并等写操作…… # 4. 完成后释放槽位(可校验持有者身份) bd merge-slot release --holder agent-a # 5. 释放后,等待队列中优先级最高的 Agent 应随即 acquire 接管需要特别说明的两个环境前提:
--holder默认取BEADS_ACTOR环境变量,多 Agent 场景下务必保证每个 Agent 设置了唯一且稳定的身份值,否则 acquire/release 的持有者校验无法生效;- 当前版本的
bd merge-slot四个子命令在proxied-server 模式(客户端代理服务器模式)下均不支持,会直接返回 "merge-slot ... is not supported in proxied-server mode"(见 cmd/bd/merge_slot.go 等处的usesProxiedServer()守卫),请确认运行环境为直接/嵌入式模式。
十、小结
bd merge-slot是 Beads 多 Agent 协作体系中解决"冲突解决本身发生冲突"的关键原语:以<prefix>-merge-slot珠子承载互斥状态,用status+metadata.holder+metadata.waiters表达"可用/被持有/排队"三态,靠RunInTransaction保证 acquire 的原子 check-and-set,并通过--holder身份校验与--wait排队机制让多个 Agent 有序接力。理解并善用这四个子命令,即可在自己的多 Agent 合并队列中彻底规避级联冲突。
如需查阅命令行原始文档,可直接阅读 docs/cli-reference/merge-slot.md(由bd help --doc merge-slot自动生成,请勿手动编辑);命令实现见 cmd/bd/merge_slot.go,存储层共享实现见 internal/storage/merge_slot.go。
【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考