核心摘要

多智能体编排不是在三张流行架构图之间做选择,而是一套负责转移、状态、预算、副作用、终止和恢复的控制系统。先用单 Agent 或确定性流程建立基线;当一个组件需要持续掌控全局时使用 Supervisor,当专家需要接管下一轮交互时使用 Handoff,当独立治理的子团队需要嵌套协调时才引入层级,并且只对真正独立的任务做并行。模型可以提出决策,但边界必须由代码执行。

目录

  1. 核心要点
  2. 从最小控制面开始
  3. 分开设计拓扑与执行流
  4. 主要编排模式
  5. 先定义控制契约再写提示词
  6. 可运行 Go 编排器
  7. 故障恢复与副作用
  8. 安全边界
  9. 评估系统而不是演示
  10. 映射到当前主流框架
  11. 生产演进路径
  12. 常见问题
  13. 总结
  14. 一手资料

核心要点

  • 多 Agent 是优化项,不是默认需求。 必须与单 Agent 和确定性流程做同口径比较。
  • 拓扑和执行流是两项独立选择。 Supervisor 内部同样可以运行流水线、路由、循环或并行分支。
  • Handoff 转移的是逻辑控制权。 它不会自动消除 Runtime、状态存储或模型服务的单点风险。
  • 控制契约比提示词更重要。 状态所有权、允许转移、终止、预算和副作用语义必须可被机器校验。
  • 没有幂等性就不能安全重试。 外部操作发出后超时,表示结果未知,而不是“肯定没执行”。
  • 评估必须覆盖协作故障。 除了任务成功率,还要测错路由、漏做、重复、循环、状态丢失、越权、尾延迟和人工升级。

本文只解决编排拓扑与转移契约。若你还没有证明角色拆分的必要性,请先阅读多智能体系统:何时值得构建以及如何落地。

从最小控制面开始

默认架构应该是能够完成任务的最小动态系统。每增加一个 Agent,就增加一份提示词、一个上下文边界、一组失败入口、一段延迟和一次授权判断。

建议按以下顺序演进:

基线方案 适用条件 何时增加控制能力
确定性代码 步骤与依赖已知 某一步需要规则难以可靠表达的语义判断
单 Agent 加工具 一个上下文可以容纳任务与工具集 工具、上下文或权限范围过宽
单 Agent 加确定性工作流 大部分步骤固定,少数节点需要模型判断 下一位责任人无法预先确定
多 Agent 角色确实需要独立上下文、工具、策略或并行责任 实测结果优于更简单的基线

这套顺序不是排斥 Agent,而是隔离概率推理真正产生价值的位置。固定解析、鉴权、支付写入、结果合并和终止计数都应该留在代码中,即使外围计划由 AI Agent 提出。

Agent 数量不是容量指标。两个可以循环 Handoff、并拥有生产写权限的 Agent,可能比二十个由代码扇出的只读 Worker 更难运维。选型应基于依赖关系、控制权、风险和评测证据,而不是“少于 5 个用 Supervisor、超过 15 个用层级”之类固定阈值。

分开设计拓扑与执行流

拓扑回答“谁有权决定”,执行流回答“任务如何推进”。把两者混成一个选型问题,会让架构标签掩盖真实控制语义。

flowchart LR A["用户目标"] --> B["控制权归属"] B --> C["执行流"] C --> D["状态与副作用运行时"] D --> E["验证结果或人工升级"] B --> B1["确定性代码"] B --> B2["Supervisor"] B --> B3["当前专家"] B --> B4["多级 Manager"] C --> C1["流水线"] C --> C2["路由"] C --> C3["并行与合并"] C --> C4["有界循环"]

三组决策彼此独立:

  • 控制权归属: 确定性代码、中央 Supervisor、当前活跃专家,或嵌套 Manager。
  • 执行流: 顺序流水线、条件路由、并行扇出,或有界评审循环。
  • 运行时语义: 状态持久化、取消、重试、幂等、鉴权和可观测性。

Supervisor 架构可以并行分派相互独立的 Worker;Handoff 网络可以完整运行在单个中心化进程里;层级团队内部也可以使用确定性流水线。尤其要警惕 “Swarm” 这个含义不稳定的词:它可能表示对等 Handoff、群聊、去中心化规划,也可能只是“很多 Worker”。技术方案中应写清真正的控制权和转移语义。

主要编排模式

有工程价值的模式目录包含四种基础原语和它们的混合,而不是从“简单”到“企业级”的统一等级。

模式 谁选择下一步 Worker 获得什么状态 主要优势 主要风险 适合场景
代码编排的流水线或路由 应用代码 类型化任务切片 可预测、易测试 规则逐渐僵化 稳定流程、高风险副作用
Supervisor 调用专家 Supervisor 保留控制权 每次调用所需上下文 集中综合与策略控制 中心瓶颈、路由偏差 调研、分析、子任务委派
Handoff 网络 当前 Agent 转交控制权 交接信封与筛选后历史 专家直接接管对话 循环、上下文漂移、责任不清 客服分流、领域升级
层级或嵌套团队 Manager 协调子团队 团队局部状态与产物 策略和上下文隔离 协调开销、信息压缩损失 独立治理的业务域
混合模式 代码约束模型可选边 显式产物 在硬边界内保留灵活性 需要测试更多语义 大多数生产系统

代码编排的流水线路由与并行

依赖关系已知时,代码编排是最稳妥的默认方案。模型可以识别意图或生成产物,但代码负责选择允许分支、启动并行任务、设置超时并合并结果。

前一步有明确产物、后一步消费该产物时使用流水线;只应选择一个或有限几个 Worker 时使用路由;子任务相互独立且合并规则明确时使用并行扇出与合并。不要并行执行会写同一资源或依赖共享可变对话的任务。

链式编排解释固定阶段契约,工作流编排解释持久化调度与恢复。两者都不要求每个节点必须是 Agent。

Supervisor 调用专家

Supervisor Agent 始终拥有任务控制权,并把专家 Agent 当作工具调用。专家返回产物,但不会成为当前对话的责任人。

以下条件适合 Supervisor:

  • 一个组件必须负责最终综合;
  • Worker 不应看到完整对话;
  • 中央策略需要控制可用专家和工具范围;
  • 任务需要多轮委派或评审。

Supervisor 不能成为不受约束的超级用户。代码仍应验证它提出的路由、限制调用次数、筛选 Worker 上下文并要求类型化输出。若每个请求始终走相同路径,应把模型路由替换成代码。

Handoff 交接网络

Handoff 会把当前控制权交给专家。目标 Agent 接收结构化原因、当前目标、必要状态和有界历史,然后直接回复用户,或者执行下一次被允许的交接。

它适合专家必须直接接管后续对话的场景,例如账单客服排查后发现产品故障,需要把当前会话交给技术支持。它不代表系统没有中心 Runtime 或单点故障。Runner 仍要持久化状态、执行转移图、应用 Guardrail 并记录 Trace。

安全 Handoff 更接近 API 调用,而不是转发整段聊天记录。目标 Agent 只应获得完成职责所需的最小上下文,来源和目标双方也都必须有权执行这次转移。

层级与嵌套团队

只有当子团队拥有独立策略、上下文、工具或生命周期时,层级才真正有价值,而不是因为系统里“Agent 很多”。顶层 Manager 应与子团队 Manager 交换压缩且可验证的产物,不应把每个 Worker 的完整对话向上传递。

合理边界包括不同安全域、数据地域、业务线或长期运行的专业流程。照搬组织架构、却没有降低上下文和策略复杂度的层级,只会制造额外 LLM 调用。每层 Manager 都可能压缩掉关键证据,因此必须实测该层是否改善了分解和验证。

混合编排

多数生产系统最终是混合模式:确定性代码拥有图结构,模型只能从少量目标中提议路由,Supervisor 委派分析任务,部分 Worker 并行执行,敏感副作用由人工审批。

flowchart TD A["已校验请求"] --> B{"代码路由"} B -->|"已知路径"| C["确定性工作流"] B -->|"开放式分析"| D["有界 Supervisor"] D --> E["只读 Worker A"] D --> F["只读 Worker B"] E --> G["确定性合并"] F --> G G --> H{"是否有敏感副作用"} H -->|"否"| I["返回结果"] H -->|"是"| J["人工审批"] J --> K["幂等执行器"]

关键边界只有一句话:模型可以提出方案,可信代码负责授权、持久化、执行和终止。

先定义控制契约再写提示词

生产级编排契约必须定义每次转移允许改变什么。提示词可以解释角色,却无法可靠执行系统不变量。

任务与产物契约

稳定的交接信封至少包含:

字段 作用 必须满足的不变量
run_id 与 task_id 关联、恢复与重放 整个运行期间不可变
from 与 to 控制权转移提议 对应边必须存在于白名单
goal 当前有界目标 不能静默扩大范围
artifact_refs 或类型化产物 角色间传递证据 Schema 与来源通过验证
hop 与 depth 控制循环与递归 由代码执行硬上限
remaining_budget Token、调用、成本或工作预算 只能单调递减
deadline 端到端时间边界 传播给所有子任务
idempotency_key 外部副作用的稳定身份 在副作用边界唯一
principal 与策略上下文 鉴权 不能从 Agent 文本推断
status 与原因 完成或升级 必须匹配允许的终态

优先传递产物引用或任务级摘要,而不是共享完整 Transcript。Researcher 拥有证据产物,Reviewer 拥有评审结论,Orchestrator 拥有路由和预算。避免两个 Agent 并发写同一字段;必须汇聚时,使用状态版本或 Compare-and-Swap。

如果交接信封使用 JSON,可以用 JSON 格式化工具检查抓取的载荷,并用 JSON Schema 生成器从代表性样本生成初始 Schema。生产校验仍必须位于服务边界,并拒绝未知字段和错误结构。

转移与终止契约

不能只在提示词里要求 LLM “完成后停止”。代码至少应执行:

  1. 来源到目标的有向转移白名单;
  2. 最大跳数、嵌套深度、模型调用、Token、成本和总截止时间;
  3. 要求指定产物类型的终止谓词;
  4. 无进展检测,例如状态摘要重复或必填字段持续不变;
  5. 显式的 failed、partial 或 needs_human 终态。

并行派发前必须先预留预算。否则多个 Worker 会同时看到相同剩余预算,最终总消耗突破上限。

上下文契约

上下文工程既是 Token 管理,也是访问控制。要为每个 Agent 明确定义:

  • 可以读取哪些指令;
  • 可以读取哪些对话轮次和产物;
  • 可以调用哪些工具与资源范围;
  • 可以写入哪些字段;
  • 哪些秘密必须脱敏;
  • 数据保留与删除策略。

所有同伴 Agent 产物都应视为不可信输入。不要把 Worker 输出直接拼接到更高优先级的指令通道。保留来源信息,让最终综合者能够区分用户输入、检索证据、模型判断、工具结果和策略决策。

可运行 Go 编排器

下面的纯标准库 Go 程序实现了一个有界 Supervisor 流程。各 Worker 函数是模型或工具 Adapter 的确定性替身,因此控制行为可重复验证。Engine 而不是提示词负责执行转移白名单、类型化产物、跳数与工作预算、Context 截止时间、状态拷贝,以及当前进程内已成功步骤的去重。

go
package main

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"
)

type Artifact struct {
	Kind   string
	Body   string
	Source string
}

type Handoff struct {
	RunID           string
	TaskID          string
	From            string
	To              string
	Goal            string
	Hop             int
	RemainingBudget int
	Artifacts       []Artifact
}

type Outcome struct {
	Artifact Artifact
	Next     string
	Cost     int
	Done     bool
}

type Agent func(context.Context, Handoff) (Outcome, error)

type journalEntry struct {
	done    chan struct{}
	outcome Outcome
	err     error
}

type Journal struct {
	mu      sync.Mutex
	entries map[string]*journalEntry
}

func NewJournal() *Journal {
	return &Journal{entries: make(map[string]*journalEntry)}
}

func (j *Journal) Do(
	ctx context.Context,
	key string,
	fn func() (Outcome, error),
) (Outcome, error) {
	j.mu.Lock()
	if entry, ok := j.entries[key]; ok {
		j.mu.Unlock()
		select {
		case <-entry.done:
			return entry.outcome, entry.err
		case <-ctx.Done():
			return Outcome{}, ctx.Err()
		}
	}

	entry := &journalEntry{done: make(chan struct{})}
	j.entries[key] = entry
	j.mu.Unlock()

	entry.outcome, entry.err = fn()

	j.mu.Lock()
	if entry.err != nil {
		delete(j.entries, key)
	}
	close(entry.done)
	j.mu.Unlock()
	return entry.outcome, entry.err
}

type Engine struct {
	Agents  map[string]Agent
	Allowed map[string]map[string]bool
	MaxHops int
	Journal *Journal
}

func (e Engine) Run(ctx context.Context, h Handoff) (Artifact, error) {
	if h.RunID == "" || h.TaskID == "" || h.Goal == "" {
		return Artifact{}, errors.New("run_id, task_id, and goal are required")
	}

	for {
		if err := ctx.Err(); err != nil {
			return Artifact{}, fmt.Errorf("run deadline: %w", err)
		}
		if h.Hop >= e.MaxHops {
			return Artifact{}, fmt.Errorf("maximum hops reached: %d", e.MaxHops)
		}
		if h.RemainingBudget <= 0 {
			return Artifact{}, errors.New("work budget exhausted")
		}
		if !e.Allowed[h.From][h.To] {
			return Artifact{}, fmt.Errorf("transition denied: %s -> %s", h.From, h.To)
		}

		agent, ok := e.Agents[h.To]
		if !ok {
			return Artifact{}, fmt.Errorf("unknown agent: %s", h.To)
		}

		input := h
		input.Artifacts = append([]Artifact(nil), h.Artifacts...)
		stepKey := fmt.Sprintf("%s/%s/%d/%s", h.RunID, h.TaskID, h.Hop, h.To)

		outcome, err := e.Journal.Do(ctx, stepKey, func() (Outcome, error) {
			return agent(ctx, input)
		})
		if err != nil {
			return Artifact{}, fmt.Errorf("agent %s: %w", h.To, err)
		}
		if outcome.Cost <= 0 || outcome.Cost > h.RemainingBudget {
			return Artifact{}, fmt.Errorf("invalid cost from %s: %d", h.To, outcome.Cost)
		}
		if outcome.Artifact.Kind == "" || outcome.Artifact.Body == "" {
			return Artifact{}, fmt.Errorf("invalid artifact from %s", h.To)
		}

		remaining := h.RemainingBudget - outcome.Cost
		fmt.Printf(
			"hop=%d agent=%s artifact=%s remaining=%d\n",
			h.Hop,
			h.To,
			outcome.Artifact.Kind,
			remaining,
		)

		if outcome.Done {
			if outcome.Next != "" || outcome.Artifact.Kind != "final" {
				return Artifact{}, errors.New("invalid terminal outcome")
			}
			return outcome.Artifact, nil
		}
		if outcome.Next == "" {
			return Artifact{}, errors.New("non-terminal outcome has no next agent")
		}

		h.Artifacts = append(h.Artifacts, outcome.Artifact)
		h.From, h.To = h.To, outcome.Next
		h.Hop++
		h.RemainingBudget = remaining
	}
}

func hasArtifact(artifacts []Artifact, kind string) bool {
	for _, artifact := range artifacts {
		if artifact.Kind == kind {
			return true
		}
	}
	return false
}

func main() {
	researcher := func(ctx context.Context, h Handoff) (Outcome, error) {
		if err := ctx.Err(); err != nil {
			return Outcome{}, err
		}
		return Outcome{
			Artifact: Artifact{
				Kind:   "evidence",
				Body:   "primary sources collected",
				Source: "researcher",
			},
			Next: "reviewer",
			Cost: 2,
		}, nil
	}

	reviewer := func(ctx context.Context, h Handoff) (Outcome, error) {
		if !hasArtifact(h.Artifacts, "evidence") {
			return Outcome{}, errors.New("evidence artifact is required")
		}
		return Outcome{
			Artifact: Artifact{
				Kind:   "review",
				Body:   "evidence and scope approved",
				Source: "reviewer",
			},
			Next: "supervisor",
			Cost: 1,
		}, nil
	}

	supervisor := func(ctx context.Context, h Handoff) (Outcome, error) {
		if !hasArtifact(h.Artifacts, "evidence") ||
			!hasArtifact(h.Artifacts, "review") {
			return Outcome{}, errors.New("verified evidence is required")
		}
		return Outcome{
			Artifact: Artifact{
				Kind:   "final",
				Body:   "architecture brief ready",
				Source: "supervisor",
			},
			Cost: 1,
			Done: true,
		}, nil
	}

	engine := Engine{
		Agents: map[string]Agent{
			"researcher": researcher,
			"reviewer":   reviewer,
			"supervisor": supervisor,
		},
		Allowed: map[string]map[string]bool{
			"supervisor": {"researcher": true},
			"researcher": {"reviewer": true},
			"reviewer":   {"supervisor": true},
		},
		MaxHops: 4,
		Journal: NewJournal(),
	}

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	final, err := engine.Run(ctx, Handoff{
		RunID:           "run-42",
		TaskID:          "architecture-brief",
		From:            "supervisor",
		To:              "researcher",
		Goal:            "produce a source-backed architecture brief",
		RemainingBudget: 4,
	})
	if err != nil {
		fmt.Println("run failed:", err)
		return
	}
	fmt.Println("final:", final.Body)
}

预期输出:

text
hop=0 agent=researcher artifact=evidence remaining=2
hop=1 agent=reviewer artifact=review remaining=1
hop=2 agent=supervisor artifact=final remaining=0
final: architecture brief ready

示例刻意把模型调用放在控制核心之外。真实 Adapter 应把模型输出解析成 Outcome,拒绝未知字段和目标,再由 Engine 执行转移。该 Journal 只能在进程存活期间去重已成功步骤,不能让外部副作用自动获得“恰好一次”语义。涉及支付、发消息、建工单等操作前,必须使用持久化 Effect Record、唯一幂等键和下游对账。

故障恢复与副作用

恢复策略必须取决于系统究竟知道什么。统一“重试三次”会重复付款、发信或写数据。

故障类型 已知事实 正确处理
执行前被拒绝 尚未启动副作用 修正输入、改路由或失败
临时读取失败 操作只读 在截止时间内退避重试
语义输出不合法 模型内容不可用 修复一次、重新规划或升级人工
已确认副作用失败 操作没有提交 使用同一幂等键重试
副作用结果未知 发出请求后超时 按幂等键查询对账,不得创建新操作
策略拒绝 行为不允许 停止或请求有权限的人审批
预算或时间耗尽 任务可能只完成一部分 返回类型化部分结果或升级

显式隔离副作用

把推理与外部操作拆成五步:

  1. Agent 用类型化参数提出动作;
  2. 策略代码校验 Principal、资源和额度;
  3. Effect Executor 写入幂等键;
  4. Executor 执行操作或对账;
  5. 结果成为不可变产物。

多步副作用应使用显式补偿的 Saga,而不是假装整个流程是一个事务。补偿是新的业务动作,不是数据库自动回滚:退款、关闭重复工单或发送更正通知本身都可能失败,也需要独立审计记录。

并行前先定义完成条件

并行分支必须预先选择完成策略:

  • 全部成功: 所有必需分支都要完成;
  • 法定数量: 达到预先定义的 Quorum 即可;
  • 尽力而为: 返回成功产物和类型化失败;
  • 首个有效结果: 接受第一个通过校验的结果,并取消其余任务。

合并过程应尽量确定化。使用稳定任务 ID 和产物 ID 去重,保留来源,并把父任务取消传播给所有子任务。模型可以总结合并后的证据,但不能静默决定某个必需分支缺失是否无关紧要。

Checkpoint 应保存在有业务语义的边界,而不是每个 Token 之后。有效检查点包含运行版本、当前节点、已接受产物、已预留预算、待确认副作用和下一组允许动作。Trace 与成本事件设计可参考 AI Agent 可观测性。

安全边界

每次 Agent 转移都是一次信任边界跨越。接收方不能因为另一个 Agent 发起了 Handoff,就自动继承额外权限。

至少执行以下控制:

  • Principal 与策略上下文独立于自然语言消息传递;
  • 每个 Agent 只获得最小工具、资源、租户、地域和消费额度;
  • Handoff 与工具调用在副作用前由可信代码鉴权;
  • 同伴输出、检索内容和工具结果都按不可信数据处理;
  • Secret 不进入共享 Transcript 和摘要;
  • 高影响或不可逆操作必须人工审批;
  • 记录策略判断、Agent 提议、获批参数、副作用结果和操作者。

Supervisor 不应持有所有 Worker 的凭证。它只需要对任务类型的委派权,真正执行操作的服务仍要独立验证用户与资源权限。AI Agent 工具安全详细说明了最小权限和工具结果投毒。

OpenAI Handoff 文档还提示了一个容易遗漏的生命周期边界:Handoff 需要的鉴权或数据加载应在目标 Agent 产生副作用前完成;只挂在入口 Agent 上的 Input Guardrail 不会自动覆盖之后所有 Agent。Guardrail 的生效范围是需要验证的实现属性,不是框架名称自带的保证。

评估系统而不是演示

多智能体评测必须与更简单的端到端基线比较,并单独定位协作故障。一段成功 Transcript 只是样例,不是发布门禁。

评测集应包含正常任务、模糊路由、证据缺失、产物冲突、重复 Handoff、工具故障、慢 Worker、恶意同伴输出和副作用结果未知。每条样本要记录预期路由、必需产物、禁止动作、可接受终态和人工升级条件。

至少测量:

维度 示例指标
任务结果 相对单 Agent 与确定性基线的成功率和 Rubric 得分
路由 目标正确率、不必要委派和相对最佳合法路由的质量损失
覆盖 缺失的必需子任务或产物
重复 重复工作和重复外部副作用
终止 循环率、最大跳数耗尽率和无进展退出
状态完整性 丢失、过期、冲突或过度暴露的上下文
安全 越权提议、被拦截副作用和审批绕过
可靠性 重试恢复、部分结果质量和升级准确性
效率 模型调用、Token、工具调用、成本与 p50/p95/p99 延迟

MAST 研究分析了七种框架中的 1,600 多条多智能体运行 Trace,并报告了十四类故障,覆盖系统设计、Agent 间不一致和任务验证。它适合作为测试分类表,但不能证明某个固定架构或框架具有普遍失败率。

要主动做故障注入:在副作用发出后让 Worker 超时,返回错误 Schema,重放旧 Handoff,拒绝工具权限,在子任务运行时取消父任务,并让两个分支产生冲突。只有 Runtime 能在不重复副作用的前提下进入预期终态,才具备上线条件。

映射到当前主流框架

框架提供编排原语,但不会替代应用策略。下表依据文末官方文档;API 会演进,实现前仍需核验当前版本。

框架 相关原语 设计含义
OpenAI Agents SDK Agents as tools、Handoff、代码编排 明确 Manager 是保留控制还是转交控制;筛选 Handoff 输入并核验 Guardrail 范围
LangChain 多智能体 Subagent、Handoff、Skill、Router、自定义工作流 根据上下文和控制需求选模式;单 Agent 仍可能已经足够
AutoGen AgentChat Round-robin、Selector、Swarm、Magentic-One 团队与终止条件 团队预设仍需要显式停止条件和任务级评测
CrewAI Sequential 与 Hierarchical Process 层级模式需要 Manager Model 或 Manager Agent;Process 选择不定义副作用语义

OpenAI 原 Swarm 仓库明确将自己定位为实验和教学项目,并引导生产用户迁移到 Agents SDK。它仍有助于理解 Handoff,但新的生产示例不应继续把 Swarm 当成当前生产 API。

Magentic-One 展示了 Orchestrator 如何在多个专家 Agent 之间规划、追踪进度并重规划。论文基准只能证明该系统在特定任务和实验环境下的结果,不能外推为“层级一定优于简单流程”。

生产演进路径

可靠上线顺序是在控制契约可观测之后逐步增加自主性。

  1. 建立基线。 分别用确定性代码和单 Agent 完成任务,记录质量、成本、延迟与错误类型。
  2. 抽取类型化边界。 在增加角色前定义任务、产物、转移、终态和副作用 Schema。
  3. 只增加一个专业角色。 先委派最明确的有界任务,并只授予只读工具,然后比较结果。
  4. 约束动态路由。 模型只能从白名单目标中提出选择,预算和鉴权继续由代码负责。
  5. 持久化运行状态。 增加 Checkpoint、Lease、幂等记录、取消和可重放恢复。
  6. 测试恶劣故障。 注入循环、旧上下文、畸形输出、并行部分失败、超时和策略拒绝。
  7. 最后开放敏感副作用。 加入人工审批、窄权限凭证、对账与 Kill Switch。
  8. 只按证据扩展。 评测确认具体收益后,再增加 Handoff、并行或层级。

除非运行记录了带版本的图结构和迁移语义,否则不要在一个进行中的 Run 内热切换拓扑。恢复时应使用创建 Checkpoint 的图版本,或者执行显式状态迁移。

常见问题

什么是多智能体编排

多智能体编排是协调专业 Agent 的控制层。它决定下一位执行者、限制每个角色可见的状态、校验输出、授权转移和工具、合并并行结果、持久化进度,并判断完成或人工升级。只有一张拓扑图,还不能构成生产编排设计。

什么时候应该用多 Agent 而不是单 Agent

当专业化产生可测量收益时才使用多 Agent,例如隔离上下文、拆分工具、区分权限、并行独立任务或建立责任边界。先与单 Agent 和确定性流程比较。如果只是把相同模型、上下文、工具和策略拆成多个角色提示词,协调成本往往上升,却没有增加能力。

Supervisor 和 Handoff 有什么区别

Supervisor 保留控制权,只调用专家获取有界产物;Handoff 改变当前责任人,由目标专家接管下一轮交互,并可能继续转交。两种模式都可以使用同一个中心 Runner 和持久化状态库。逻辑上的对等控制不等于物理上的去中心化基础设施。

如何防止 Agent 无限 Handoff

使用转移白名单、最大跳数与深度、端到端截止时间、单调递减预算、显式完成条件、重复状态检测和人工升级终态。记录每次提议和获准的转移。不能只依赖“不要循环”这样的提示词。

多智能体框架应该如何选择

先定义控制契约,再选择最容易映射该契约的框架。OpenAI Agents SDK 直接区分 Manager 调用专家和 Handoff;LangChain 提供多种模式与自定义工作流;AutoGen 提供团队预设和终止条件;CrewAI 提供顺序与层级流程。用同一套评测集测试最小可行方案,而不是只比较功能列表。

总结

生产级多智能体编排,本质是在不确定条件下执行受控状态转移。先从确定性代码或单 Agent 开始,分开设计拓扑与执行流,只在评测证明收益的位置增加自主性。转移白名单、预算、终止、鉴权、持久化和副作用幂等都必须位于提示词之外。最佳架构不是 Agent 最多的架构,而是能够达到目标、并在失败时保持有界和可解释的最小系统。

QubitTool 相关阅读

一手资料