把每个 Bot 的输入输出先写死——Grok Bot 多 Bot 协作才拆得开

文章导读
多 Bot 协作卡住,多数时候不是模型不够聪明,而是交接处没有说清楚「上一步交出来的东西长什么样」。把每个 Bot 的输入字段和输出字段先写死,等于用字段契约替代口头分工:接手的 Bot 不需要再回头确认格式,格式不对也会在交接那一刻停下来,而不是传到第三、第四个 Bot 才炸出来。下面给的是可落地的切分标准、契约模板和校验骨架,均可通过配置、日志或页面行为核对。
📋 目录
  1. A 先把一个任务拆成几个可独立交付的片段
  2. B 给每个片段写明输入字段与输出字段
  3. C 在交接处加一次格式校验
  4. D 用一次完整跑通验证契约能闭环
  5. E 契约变更时怎么改不炸掉下游
A A

多 Bot 协作卡住,多数时候不是模型不够聪明,而是交接处没有说清楚「上一步交出来的东西长什么样」。把每个 Bot 的输入字段和输出字段先写死,等于用字段契约替代口头分工:接手的 Bot 不需要再回头确认格式,格式不对也会在交接那一刻停下来,而不是传到第三、第四个 Bot 才炸出来。下面给的是可落地的切分标准、契约模板和校验骨架,均可通过配置、日志或页面行为核对。

多 Bot 协作的瓶颈通常在交接格式,而不在单 Bot 能力。建议先把任务切成可单独验证的片段,再为每个片段写死输入/输出的字段名、类型、必填与示例值;每次交接加一次必填校验,不通过就停在那里。契约变更时按「先加可选字段、再切换写入方、最后删旧字段」推进,并留回退条件。具体字段与校验强度需要结合你的编排环境和日志能力确认。

先把一个任务拆成几个可独立交付的片段

切分不是按「谁做哪块」分,而是按「这块产出能不能被单独验证」分。一个片段如果必须等另一个片段跑完、拿到它的中间状态才知道自己要干什么,那它就不该被当成独立片段,通常应该和上游合并,或者把上游产出提升为显式输入字段。

可以用两条判断条件过一遍:

  • 是否可单独验证:给这个片段一组固定的输入,它的输出能不能被一条规则或一次人工检查判断对错。不能,说明输出定义还太模糊。
  • 是否依赖别人的中间状态:它需要的是上游的最终产出,还是上游跑到一半的草稿。依赖草稿,往往意味着两个片段其实是一个片段。

切完之后,每个片段清单建议包含这些描述项,缺一项后面就容易返工:片段名、职责一句话、上游片段是谁、下游片段是谁、输入契约文件位置、输出契约文件位置、失败时由谁停、由谁重跑。把这份清单先写成配置文件或表格,Bot 编排里引用同一份,不要在两处各写一遍。

给每个片段写明输入字段与输出字段

契约的目的是让接手的 Bot 不用再问一遍。字段写死到「名、类型、必填、示例」四件套,再加上字段缺失时应该报什么错误文本。错误文本提前定好,后面查日志时就能一眼定位是哪个字段没给。

下面是一个可替换的片段契约骨架,字段按你的实际场景改:

把每个 Bot 的输入输出先写死——Grok Bot 多 Bot 协作才拆得开
{
  "segment": "extract_keywords",
  "inputs": {
    "raw_text":   { "type": "string",  "required": true,  "example": "一段用户需求描述" },
    "max_count":  { "type": "integer", "required": false, "example": 10 },
    "locale":     { "type": "string",  "required": false, "example": "zh-CN" }
  },
  "outputs": {
    "keywords":   { "type": "array<string>", "required": true, "example": ["多bot协作", "契约"] },
    "source_len": { "type": "integer",       "required": true, "example": 128 }
  },
  "errors": {
    "missing_raw_text": "input.raw_text is required but empty",
    "missing_keywords": "output.keywords is required but empty"
  }
}

几个取舍点:必填字段宁少勿多,只把下游真正会读的字段标成 required;可选字段尽量给默认值,避免每个调用方都要填;数组类字段要约定空数组算不算合法,这直接影响下一步校验怎么写。类型字段最好和你在编排层实际解析的类型一致,否则校验通过了、运行时仍然会解析失败。

在交接处加一次格式校验

校验放在每次交接之前,也就是上游产出之后、下游启动之前。它的职责只有三件事:读取上一步产出、检查必填字段、不通过就停止并记录。不在这里做业务判断,业务判断留给下游 Bot。

一个通用校验骨架可以长这样,伪代码形式,替换成你环境里的实际读法即可:

def check_handoff(contract, payload):
    missing = []
    for name, spec in contract["inputs"].items():
        if spec["required"] and (name not in payload or isEmpty(payload[name])):
            missing.append(name)

    if missing:
        log("handoff_check=fail", segment=contract["segment"], missing=missing)
        raise HandoffError("missing required fields: " + ",".join(missing))

    log("handoff_check=pass", segment=contract["segment"], fields=sorted(payload.keys()))
    return payload

校验日志里建议固定出现:片段名、检查结果(pass/fail)、缺失字段列表或实际收到的字段列表、以及本次交接的 trace 或批次标识。有了这几项,出问题时你能直接按片段名过滤日志,看是哪一步没给字段,而不是从头翻整条链路。

把每个 Bot 的输入输出先写死——Grok Bot 多 Bot 协作才拆得开

用一次完整跑通验证契约能闭环

契约写得对不对,靠一次从头到尾的跑通来确认,重点是看有没有字段在中间丢失或被悄悄改写。观察点建议按顺序布:

  1. 第一个 Bot 的输入:确认它收到的是你手工准备好的原始输入。
  2. 每个片段输出后的交接日志:确认 handoff_check=pass,且字段名与契约一致。
  3. 最后一个 Bot 的输入:把它的实际输入和中间某一步的输出做字段级对照,看名字、类型、值有没有被改过。

最容易出问题的是「字段被改名」和「空值被转成默认值」这两类。前者在交接日志里表现为字段列表对不上,后者表现为 pass 了但下游读到的是空壳。查看产出的位置通常是最后一个 Bot 的输入快照,或编排层保存的中间产物;如果编排层不存中间产物,至少在交接校验里把 payload 打一条精简日志。

契约变更时怎么改不炸掉下游

改字段最忌讳一步到位。建议按三段顺序推进,每段之间留一次跑通的时间窗:

  1. 先加可选字段:新字段标成非必填,上游可以先写、也可以不写,下游此时还不依赖它。
  2. 再切换写入方:确认新字段确实被稳定产出后,把下游改成读新字段,旧字段仍保留。
  3. 最后删旧字段:等所有下游都不再读旧字段,再把它从契约里移除。

回退的判断条件建议提前写死:如果切换写入方后交接校验连续失败、或下游读到的字段为空、或关键片段报错率明显上升,就回退到上一段状态,也就是让写入方同时写新旧两个字段,下游切回读旧字段。回退动作本身也要走同一次跑通验证,确认链路恢复。

字段类型的收紧同样要当变更处理,比如把 string 改成 integer,先按新增可选字段的方式过渡,不要直接改类型。契约文件建议纳入版本管理,并让编排层在启动时打印当前生效的契约版本,这样出问题时能先确认大家用的是不是同一版。