高级 📋 6 个步骤 第 557 / 557 篇

照着 Meta 的 Thin Orchestrator 造一个「薄编排器」:状态机路由、专家子智能体与不可变 JSON 交接

多智能体最常见的死法是编排器太聪明。Meta 把「数据列接入」的编排器削到约 1300 行:不做任何领域判断,只定序、做可靠性检查、管状态交接,把领域推理下放给十四个上下文隔离的专家子智能体,用带签名的 JSON 工件交接。本教程带你复刻这个模式:状态机路由、专家隔离、不可变交接与工作流模板复用。

2026.10.22· 13 分钟上手· 约 4046 字· 🧭 Thin Orchestrator / 🤖 多智能体编排

多智能体系统最常见的死法,不是某个子智能体不够聪明,而是编排器太聪明。Meta 的基础设施团队讲了一个很典型的翻车:他们要让智能体自动化「数据列接入」——一个横跨三个代码仓库、三种编程语言、最多六个流水线阶段的流程。最初那版做法和大多数团队一样,造一个什么都知道的智能体,把全部领域知识、API 契约、schema 校验规则和代码生成套路都塞进它的提示词。演示时一切正常,上了生产就开始生成无效配置。复盘结论是:上下文过载之后,继续增加知识反而让表现变差——它会把一个子系统的字段命名规则套到另一个子系统上,也会在填充 A 数据源时错用 B 数据源的代码生成模板。他们给出的解法叫「薄编排器」:一个约 1300 行的路由引擎,自己不做任何领域判断,只负责定序、可靠性检查与状态交接,把领域推理全部下放给十四个彼此隔离的专家子智能体。本教程带你把它造出来。

🧭 本教程适合:已经在生产里跑多智能体、并且开始遇到「改一条规则就全线崩」的团队。你需要能写 Python(或任意语言,模式与语言无关)与一个大模型的调用能力。需要说明的是:Meta 公开的是这套架构的做法与蓝图,并不存在一个官方可 pip 安装的编排器包;本教程是按公开材料复刻这个模式,代码为可运行骨架,你需要按自己的业务补齐子智能体与校验规则。文中的效果数字(99.2% 完成率、token 下降约六成、人工介入从 28% 降到 1.4%)均来自 Meta 公开的生产口径,不代表你在自己场景下的结果。

Step 1:先想清楚「胖编排器」为什么会崩

1 上下文过载,不是提示词写得不好

把胖编排器的失败归因为「提示词没写好」,会导向错误的优化方向。Meta 的复盘给出了更准确的描述:当编排器同时承担「理解领域语义」和「管理工作流状态」两件事时,它会交叉污染概念。他们试过另一条路——为每个任务手工精调系统提示词,成功率能到七到八成,但那要求工程师为每个具体任务单独调提示词;去掉这种人工陪跑,成功率掉到一到两成。这说明问题不在提示词工程,而在架构:你要求一个上下文同时承载三套领域知识和六阶段状态机,它在边界情况上必然失控。另一处佐证是,多智能体编排在批量任务上的准确率显著高于单体智能体,原因正是任务隔离避免了上下文互相干扰。

# 胖编排器:一个提示词里塞全部(示意,勿抄)
FAT_PROMPT = """
你是数据平台专家。以下是三个仓库的完整 schema 定义……
以下是 Python / C++ / SQL 三套语法与代码生成规则……
以下是六个流水线阶段的依赖顺序与校验要求……
以下是十类数据源的填充模式与错误恢复流程……
请完成用户的列接入请求。
"""

别把「换更大的上下文窗口」当解法:Meta 明确记录过,即使使用上下文很长的前沿模型,单体方案依然出现了 token 注意力分散、错调工具抽象、以及在数小时执行中丢失中间状态的问题。窗口变大只是推迟了阈值,没有改变「一个上下文同时装多套领域知识」这个结构性错误。

Step 2:划清薄编排器只做三件事

2 定序、可靠性检查、状态管理

薄编排器的职责边界要写得像合同一样硬。它只拥有三件事:一是工作流定序,即根据当前状态决定下一个跑谁;二是可靠性检查,包括超时、重试与错误边界;三是状态管理,把上一个子智能体的产出传给下一个。它不做的事同样要写死:不解释 schema、不生成配置、不校验领域逻辑——它甚至不需要理解「数据列」是什么、六个阶段之间是什么关系。这条边界一旦被突破(比如让编排器顺手「看一眼校验结果对不对」),它就开始重新积累领域知识,几轮迭代后又会变回胖子。

class ThinOrchestrator:
    """只做三件事:定序、可靠性检查、状态管理。不做任何领域判断。"""

    def __init__(self, agents, templates, timeouts=30, max_retries=3):
        self.agents = agents          # 名字 -> 专家子智能体
        self.templates = templates    # 工作流模板:阶段顺序的状态机
        self.timeouts = timeouts
        self.max_retries = max_retries

    def run(self, template_name, initial_state):
        template = self.templates[template_name]
        state = dict(initial_state)
        for stage in template.stages:
            result = self.call_with_reliability(stage, state)
            state = template.apply(stage, state, result)   # 状态快照写在这里
            if state.get("error"):
                return self.agents["error_handler"].run(state)
        return state

值得注意的是,这个编排器对每一个请求都走固定的七步协议:读取目标子智能体的说明文件、检查目标是否已存在以避免重复、向用户收集必需字段、自动推断能推断的信息(仓库类型、已有配置等)、做一次可靠性闸门检查(有无告警、是否需要评审人)、把完整上下文交给子智能体、最后处理结果并确认。这七步里只有第六步涉及大模型推理,而且发生在上下文隔离的子智能体内部;其余六步都是确定性代码。

💡 用「它是否需要理解业务名词」来检验边界。如果你写的某行编排器代码需要知道某个业务字段的含义,那这行代码多半放错了地方,应该下移到对应的专家子智能体里。

Step 3:状态机路由——只查标志位,不看内容

3 决策树里没有领域语义

路由是薄编排器的心脏,也是最容易写胖的地方。正确写法是:路由只看工作流状态里的标志位,不检查任何内容。比如下面这段,它判断「schema 是否已被提出」「校验是否通过」,然后决定下一个是谁;它完全不去看 schema 长什么样、校验结果写了什么,而是信任专家子智能体把标志位设对。这样做的好处是路由逻辑与领域语义彻底解耦——你改了某个子系统的字段规则,路由代码一行都不用动。

def route_next_agent(self, workflow_state):
    if workflow_state.stage == "schema_inference":
        if workflow_state.schema_proposed:
            return self.agents["validation"]
        return self.agents["schema_inference"]

    if workflow_state.stage == "config_generation":
        lang = workflow_state.target_language          # 只是取值,不理解含义
        return self.agents["config_gen_" + lang]

    if workflow_state.stage == "deployment":
        if workflow_state.validation_passed:
            return self.agents["deployment"]
        return self.agents["error_handler"]

    raise UnknownStageError(workflow_state.stage)

这里的取舍要提前接受:薄编排器会用延迟换可靠性。胖编排器可能一步同时生成并校验配置,薄编排器要分别调用生成器、校验器,再回来确认结果,多出几次模型往返。Meta 的实践里这个代价是可接受的,因为数据列接入是批处理而非面向用户的接口;如果你的场景是强实时的用户请求,就要重新权衡,可能只在链路最长的少数环节上做拆分。

信任标志位,就要校验标志位:既然编排器不查内容,子智能体设错的标志位就会被原样传递下去,故障表现为「流程跑完了但结果是错的」。所以每个标志位都要有对应的 schema 约束(类型、枚举、必填),交接时校验不通过就进错误分支,而不是静默继续。

Step 4:专家子智能体的上下文隔离

4 没人看到全貌,这是特性不是缺陷

十四个专家子智能体各管一块:schema 管理知道配置注册表,填充知道十类数据源各自的代码生成方式,校验知道构建系统与质量检查。关键在于每个专家只加载自己那部分知识源(通常是各自几千 token 的领域文件),并且拿到彼此隔离的工具集。于是 schema 推断只看到源数据与已有列定义,Python 配置生成只看到目标 schema 与 Python 语法规则——没有任何一个智能体需要全貌。正是这种「看得少」换来了单任务 token 消耗的大幅下降,也避免了跨子系统的概念污染。

# 子智能体声明:知识源 + 工具集,两者都按域收窄
SPECIALISTS = {
  "schema_inference": {
      "knowledge": ["docs/config_registry.md"],       # 只加载这几份
      "tools":     ["read_source_table", "list_columns"],
      "emits":     {"schema_proposed": "boolean"},
  },
  "config_gen_python": {
      "knowledge": ["docs/python_syntax.md", "docs/target_schema.md"],
      "tools":     ["render_python_config", "lint_python"],
      "emits":     {"config_path": "string"},
  },
  "validation": {
      "knowledge": ["docs/build_system.md", "docs/quality_gates.md"],
      "tools":     ["build_check", "type_check"],
      "emits":     {"validation_passed": "boolean"},
  },
}

每个子智能体遵循统一结构,这点是规模化的前提:声明运行时要加载哪些知识源、可用的工具集、以及它会产出哪些结构化字段。有了这份声明,编排器就能自动做两件事——在调用前按声明装配上下文,在返回后按声明校验产出。新增一个专家不需要改编排器,只需要在注册表里加一项;这也是为什么十四个专家能被二十多条工作流复用来复去。

💡 知识源要做版本与体积双控。给每个子智能体的领域文件设一个体积上限(比如几千 token),内容膨胀时强制拆分或拆出更细的专家;否则「隔离」会在半年后悄悄失效——每个专家的上下文都长到了原来的三倍。

Step 5:不可变 JSON 交接与可靠性闸门

5 不带对话历史,只带签名工件

子智能体之间不传递无界的对话历史,只传递结构化的 JSON 工件:包含输出状态与执行元数据,并且带签名。编排器在唤醒下一个子智能体之前,先拿这份工件去校验 schema 不变量;校验不过就走错误分支。这套「不可变交接」有两个直接收益:一是故障定位变得便宜——出了问题,你看是哪个专家交出了不合 schema 的工件,而不是去猜编排器当时为什么做了那个路由决定;二是重试能做到局部化——某个子智能体编译异常或超时,只重试它自己,不必重新评估整条工作流历史。

import hashlib, json, jsonschema

def handoff(agent_name, payload):
    artifact = {
        "agent": agent_name,
        "payload": payload,
        "prev_hash": state.last_hash,                     # 串成哈希链
    }
    artifact["hash"] = hashlib.sha256(
        json.dumps(artifact, sort_keys=True).encode()).hexdigest()
    jsonschema.validate(payload, SCHEMAS[agent_name])     # 不变量校验
    return artifact                                        # 校验不过会抛异常

可靠性检查也要做成「不理解语义也能执行」的通用件:每次调用带三十秒超时,失败最多重试三次且间隔指数退避,单个智能体出错不会拖垮编排器,每完成一步就持久化状态快照。这四条对所有专家一视同仁,编排器不需要知道「schema 推断」是什么意思,也能给它加超时。这里有个细节值得照抄:错误隔离意味着故障被限制在子智能体边界内,而不是演变成编排器整体崩溃——这正是胖编排器的典型死法。

调试方式会变,团队要提前适应:薄模式下出问题时,你追的是「哪个专家设错了标志位」;胖模式下你追的是「编排器为什么做了这个路由决定」,往往要翻它关于领域语义的内部推理。前者可查证、后者靠猜。如果团队习惯了看思维链排查,切换初期会有不适应,建议先把交接工件与状态快照的日志做扎实。

Step 6:用工作流模板复用,而不是再造智能体

6 零新代码支持新场景

这套架构里最省钱的洞察是:把「要发生什么」和「每步怎么做」分开。编排器用工作流模板定义阶段顺序——也就是一个约束大模型决策空间的状态机。列接入是「schema → 校验 → 填充 → 测试」,事件接入是「schema → 填充 → 校验 → 测试 → 事件处理」,流水线迁移是「评估 → 规划 → 填充 → 校验 → 监控」。三条模板复用同一批十四个专家,只是顺序不同。所以当业务提出「我们还要支持流水线迁移」时,团队没有写任何新的智能体代码,只写了一条新模板,就拿到了完整的生产能力。

# 模板 = 阶段顺序的状态机;专家池是共享的
TEMPLATES = {
  "column_onboarding": ["schema", "validation", "population", "testing"],
  "event_onboarding":  ["schema", "population", "validation", "testing", "event_processing"],
  "pipeline_migration":["assessment", "planning", "population", "validation", "monitoring"],
}

落地顺序上给一条务实建议:不要一上来就拆十四个专家。先挑一条你已经在跑的、领域知识明显混在一起的工作流,把它拆成三到四个专家加一个薄路由,跑两周看故障定位是不是变快了、token 是不是降了;确认有效再横向扩。判断该不该用这个模式的标准也很清楚:它适合多领域、且可靠性与可维护性比延迟更重要的工作流;反过来,如果任务是单一领域、短链路,或者延迟是硬指标,胖一点的单体反而更合适。

💡 加新场景时的纪律:先问「能不能用现有专家重排一条模板解决」。每次这个问句成功一次,你就省下了一个需要长期维护的智能体;每次绕过它直接造新专家,你的维护面就扩大一圈。

公开数字不是你的验收结论:99.2% 完成率、token 降六成、人工介入降到 1.4% 这些,是 Meta 在自家推荐系统列接入这一特定场景、跑四千五百多次流程得出的生产口径,他们的前提(三仓库、三语言、六阶段、批处理)与你的场景未必一致。请把它们当作「这个方向值得试」的证据,而不是「照做就能拿到」的承诺;自己先在小流量上量一遍再决定要不要全面铺开。

常见问题速查

现象原因与处理
编排器又开始「看一眼内容」了边界被突破;把那行需要理解业务名词的代码下移到对应专家
流程跑完但结果是错的标志位设错;给每个标志位加 schema 约束,交接时校验不通过就进错误分支
延迟明显变长模式固有代价(更多次模型往返);实时场景只在最长链路做拆分,或改回偏胖设计
排查变难了还没适应新调试方式;先把交接工件与状态快照日志补齐,再据此定位
半年后 token 又涨回来专家的知识源膨胀导致隔离失效;给知识源设体积上限并强制拆分
一个新场景就要造一个新专家先试着重排模板;模板能覆盖的就不要新增专家
单个专家出错拖垮整条流程缺错误隔离与局部重试;补上超时、指数退避重试与状态快照
找不到官方编排器包Meta 公开的是架构做法与蓝图,需按公开材料自行实现
← 返回教程中心