多个 Grok Bot 同时改一份产物——是先排队还是先合并?

文章导读
先看这份产物能不能拆成互不重叠的部分:能拆,就让每个 Bot 各改一片、改完按固定顺序合并;拆不开,就让它们排队串行、同一时刻只有一个 Bot 持有写权限。判断入口不是“同时有几个 Bot”,而是“产物有没有天然的不重叠边界”。
📋 目录
  1. 先判断这份产物能不能拆成互不重叠的部分
  2. 走排队时的串行写法与等待上限
  3. 走合并时的分片与合并顺序
  4. 验证合并后没有残留冲突痕迹
  5. 把选择依据写成一句判断规则放进流程
A A

先看这份产物能不能拆成互不重叠的部分:能拆,就让每个 Bot 各改一片、改完按固定顺序合并;拆不开,就让它们排队串行、同一时刻只有一个 Bot 持有写权限。判断入口不是“同时有几个 Bot”,而是“产物有没有天然的不重叠边界”。

判断顺序建议固定为两步:先看产物能否按段落或字段切出互不重叠的分片,能切就走分片合并,不能切就走排队串行。分片合并适合结构清晰、边界稳定的产物,代价是合并顺序必须写死并做幂等检查;排队串行适合整段改写、前后相互引用的产物,代价是后到的 Bot 要等,必须配等待上限和超时分支。两种方式在写回或合并之后,都要检查重复段落、截断句和缺失小节。

先判断这份产物能不能拆成互不重叠的部分

可拆的判定条件通常有三条,需要同时满足:段落独立(每个 Bot 只拥有自己那几个小节,彼此不交叉)、字段独立(配置文件这类产物里,各自只改自己的键,不改共用键)、无相互引用(A 分片里不需要出现 B 分片刚定的术语、变量名或编号)。三条都满足,分片合并的成功率才稳定;只满足一两条时,容易出现“改的时候看不出问题,合并以后才对不上”。

不可拆时硬走合并,后果一般是三类:同一段落被两个 Bot 各写一版,合并后出现重复段落;一个 Bot 删掉了另一个 Bot 依赖的句子,合并处出现截断句;双方各自新增的小节编号撞车,产物里出现两个同名小节或缺失小节。反过来,可拆的产物如果硬走排队,代价只是慢,不会坏——所以在拿不准时,先排队通常比先合并更保守。可以先做一次试拆:把产物按标题切出来,看每个分片里是否出现了别的分片的标志词。有,就说明还需要排队。

走排队时的串行写法与等待上限

排队的核心是“写权限只有一个”,并且等待必须有上限,否则后到的 Bot 会一直挂着。下面是一个通用串行骨架,锁工具、锁路径和超时秒数都需要结合运行环境替换;放进每个 Bot 写产物前后的包装脚本里执行:

多个 Grok Bot 同时改一份产物——是先排队还是先合并?
LOCK=/tmp/artifact.lock
TIMEOUT=300   # 等待上限(秒),按单次改写耗时调整

exec 9>"$LOCK"
if ! flock -w "$TIMEOUT" 9; then
  echo "lock timeout, artifact busy"
  exit 75            # 约定 75 = 等不到锁
fi

# 拿到锁后按顺序做三件事:
# 1. 重新读取产物最新版本(不要复用排队前读到的副本)
# 2. 执行 Bot 的改写
# 3. 写回;脚本退出时锁自动释放

exit 0

第 1 步容易被忽略:如果 Bot 在排队前就把产物读进内存,等拿到锁后直接写回,前面的结果会被整体覆盖,这和“没排队”是一样的。超时后的处理分支建议按改写性质决定:改写是可重放的(同一输入重复执行结果一致),就按退避间隔重试有限次数;改写依赖当次上下文(例如带随机采样、带抓取时间戳),就放弃并把该任务标成待人工处理,不要静默丢弃。风险边界在于等待上限设置得比单次改写耗时还短,会持续误判超时,需要先用日志里的实际持锁时长校准一遍。

走合并时的分片与合并顺序

分片命名要包含序号、Bot 标识和区域,序号用固定宽度补零,否则字典序会把第 10 片排到第 2 片前面。合并顺序按区域约定,不按各 Bot 的完成时间,这样同一批分片无论谁先跑完,合并结果都一样:

多个 Grok Bot 同时改一份产物——是先排队还是先合并?
shards/
  010-botA-intro.md
  020-botB-config.md
  030-botC-faq.md

# 按文件名排序拼接,顺序由文件名决定
cat $(ls shards/*.md | sort) > artifact.merged.md

# 幂等性检查:同一份输入合并两次,结果应完全一致
cat $(ls shards/*.md | sort) > /tmp/a.merged
cat $(ls shards/*.md | sort) > /tmp/b.merged
diff -q /tmp/a.merged /tmp/b.merged && echo "idempotent OK"

合并骨架本身要保持“纯函数”风格:不写当前时间、不使用未排序的目录遍历、不依赖执行环境变量。如果上面的 diff 报出差异,通常就是合并脚本里混进了时间戳、随机顺序或对同一目录的重复遍历。分片内部还建议约定首尾标记(例如每个分片以固定标题开头、以空行结尾),这样拼接点是否干净一眼能看出来。

验证合并后没有残留冲突痕迹

合并完成后按下面三项逐条过,每一项都有固定的观察位置,不要只看开头就下判断:

  • 重复段落:同一句关键句或同一个标题行在产物里出现两次。观察位置是分片拼接点附近,也就是各分片首尾相接的那几行。做法:对每个分片的标志句执行 grep -c,计数大于 1 就说明有重叠。
  • 截断句:句尾没有标点、括号未闭合、代码块围栏只有开头没有结尾。观察位置同样在拼接点,尤其是分片末尾与下一分片开头之间。可用 grep -n '[^。!?;:]$' 粗筛,会存在误报,需要配合人工确认。
  • 缺失小节:把分片清单里的标题与实际产物里的标题列表做比对,数量和顺序都要对得上。观察位置是产物的标题层,而不只是正文。

这三项检查的边界是把“结构性问题”和“内容质量问题”分开:它们只能证明合并动作没有留下拼接痕迹,不能证明合并后的正文写得对。内容层面的判断仍需人工或另一次审阅。

多个 Grok Bot 同时改一份产物——是先排队还是先合并?

把选择依据写成一句判断规则放进流程

把上面的取舍压缩成一句可判定的话,下次遇到同类产物不必再讨论。表述形式建议是“条件 → 动作”两段式,例如:产物能按段落或字段切出互不重叠的分片 → 走分片合并,合并顺序按分片序号;任一分片需要引用另一分片的内容 → 走排队串行,并设置等待上限。规则里要带上失败时的动作(超时重试还是放弃、合并后查哪三项),否则执行者仍然要临场判断。

放置位置建议三处之一,按团队习惯选:产物模板的头部注释(写产物的人第一眼能看到)、Bot 编排脚本的启动检查(机器可读,能在跑之前拦一道)、以及变更流程模板的检查项里(评审时有人确认)。放好后,规则改动也要走同一份模板的修改,避免规则散落在多个地方各说一套。