Multi-Agent 编排:五种模式,以及何时单 agent 胜出
同一张发票用四种方式处理并列出成本:orchestrator 成本是单 agent 的 1.66 倍,却得到同一结论。
本页内容
第 24 章以一个它自己推导出来的问题收尾:当一个 sub-agent 错了,父级到底能看到什么?
本章用一张账单回答它。一个任务——客户质疑一张发票并要求回复——用四种方式解决;它们都用 第 23 章的 harness 跑同一个脚本化 provider,都用同一个 encoder 计算同样的 tokens,都按第 16 章在 2026 年 9 月 6 日读取到的费率计价。
| arrangement | model calls | input tokens | output | cost | wall clock | verdict |
|---|---|---|---|---|---|---|
| prompt chaining | 4 | 900 | 165 | $0.003780 | 1,648 ms | wrong |
| one agent, four tools | 5 | 2,697 | 179 | $0.007542 | 2,224 ms | right |
| parallel sections | 9 | 2,910 | 324 | $0.009708 | 2,165 ms | right |
| orchestrator-workers | 12 | 3,628 | 438 | $0.012512 | 5,090 ms | right, and it cannot prove it |
把第一行和最后一行放在一起读:它们之间就是这个行业眼下所有争论。最便宜的安排也是最快的,并生成了一个自信、错误、可直接发送的答案。最贵的安排答对了,花了 3.3 倍的钱和 3.1 倍的时间,最后引用了一个 worker 的结论,而它没有办法核验这个结论。
这些表里没人放进去的是第二行:一个带四个工具的 agent 得到了与 orchestrator 相同的结论,只花了 60% 的钱和 44% 的 wall clock。 这不是对简单性的偏好。这是一次测量;本章余下部分讲的是它在什么时候不再成立。
查看详情
本章需要前文提供的东西。
- **第 18 章**提供工具契约:模型能看到一个 schema,看不到一个 endpoint。整个 agent 都能藏在那个接口后面,而这就是 multi-agent 的全部。
- **第 22 章**提供两个公开的、彼此不一致的“agent”定义,也提供了一个算术事实:一串 prompts 是 N 次调用。
- 第 23 章提供 loop、五种退出方式、运行状态和 trace。下面每一种安排都是那个文件,只是调用方式不同。
- 第 24 章提供一个 window 的成本,以及从 window 里掉出去的东西。sub-agent 是它四种策略中的第四种,也是唯一一种不是策略而是第二个 agent 的策略。
没有张量。这里的一切都是 TypeScript,除了两项针对真实本地模型的测量。
任务,以及里面的陷阱
链接到此部分:任务,以及里面的陷阱一家葡萄牙公司来信询问发票 FT-2026-0918。邮件说 VAT 看起来不对,并附上了发票:净额 EUR 248.00,按 21% 收取 VAT,EUR 52.08,总额 EUR 300.08。
作答所需事实分布在三个地方,而其中只有一个在邮件里:
| where | what it says |
|---|---|
| the attached invoice | 卖方在西班牙,VAT 按 21% 计收,EUR 52.08 |
| the order record | 买方注册地为葡萄牙,拥有有效 VAT 识别号,business-to-business |
| the tax table | 西班牙国内税率 21%;欧盟内部 business-to-business 且有有效识别号,reverse charge,0% |
把三者放在一起,发票就是错的:应适用 reverse charge,VAT 应为零,应开具 EUR 52.08 的贷项通知单。只看发票,它在算术上完全正确——248.00 加 52.08 等于 300.08——而你会这么说。
邮件确实写着“我们是一家葡萄牙公司”。那是一个声称,不是记录;没有计费系统会仅凭一个声称就开贷项通知单。这个陷阱不是把戏:它就是业务工作的普通形状,决策需要一个没人想到要去取的事实。
上面所有内容都跑在一个脚本化 provider 上,风格沿用第 23 章,并且只有一条规则:
答案只能使用 prompt 中存在的事实。
这个“模型”会按目录顺序向它拥有的每个工具各请求一次,然后对它能看到的文本应用一条固定规则。没有任何 arrangement 级别的脚本,所以开头表格里的差异不是关于模型智能的主张:它们是被测量出来的信息路由。真实模型会在这些问题之上增加自己的失败;它不会移除这些失败。
五种模式,约四十行
链接到此部分:五种模式,约四十行下面五个名称来自 Anthropic 的 Building effective agents,这套词汇就是在那里稳定下来的。1 五个想法都不新,而说清楚哪家把什么命名了、哪个想法更早,本身就是理解它们的一半价值。
/* 1. Prompt chaining: a fixed pipeline. The control flow is yours. */
export async function chain(steps: Step[], first: string) {
let carry = first, all = first;
for (const s of steps) {
const r = await step(s.role, s.system, s.accumulate ? all : carry);
carry = r.text;
all = `${all}\n${r.text}`;
}
return carry;
}
/* 2. Routing: one cheap call picks the branch. The fallback is not a model. */
export async function route<T>(input: string, classify: Classifier,
routes: Record<string, Branch<T>>, fallback: Branch<T>) {
let label: string | undefined;
try { label = await classify(input); } catch { label = undefined; }
return ((label && routes[label]) || fallback)(input);
}
/* 3. Parallelisation. The pattern IS this line. */
export const parallel = <T>(workers: Branch<T>[], input: string) =>
Promise.all(workers.map((w) => w(input)));
/* 4. Orchestrator-workers: an agent behind a tool. Chapter 18's interface, unchanged. */
export function agentTool(o: WorkerSpec): Tool {
return {
name: o.name, description: o.description, readOnly: true,
parameters: { type: "object", properties: { question: { type: "string" } } },
async run(args: { question: string }) {
const child = newRun(o.system, args.question); // its own window
await runTracked(child, o.tools, o.usage); // its own limits
const conclusion = child.output ?? "no result";
if (!o.carryFindings) return conclusion;
return `${conclusion}\nFINDINGS ${evidence(child)}`;
},
};
}
/* 5. Evaluator-optimiser: make, judge, remake. Rounds are calls. */
export async function refine(make: Make, judge: Judge, maxRounds: number) {
let draft = "", feedback: string | undefined;
for (let r = 1; r <= maxRounds; r++) {
draft = (await make(feedback)).text;
const j = await judge(draft);
if (j.ok) return { draft, rounds: r };
feedback = j.note;
}
return { draft, rounds: maxRounds };
}这就是整个工具箱:五个函数,没有框架,而 parallel 那个只有一行——这正是把它写出来而不是画出来的意义。下面逐个看它们的来历、价格,以及它会出错的场景。
Chaining,以及它替你做出的决定
链接到此部分:Chaining,以及它替你做出的决定Prompt chaining“把任务分解为一系列步骤,其中每次 LLM 调用都处理上一次的输出”。1 这个想法早于语言模型:它就是 pipeline,而 pipeline 的交易是——用提前固定好的控制流换取清晰性。
我们的任务有四步:提取发票字段,检查算术,判断应欠什么,写回复。这里它以两种不同方式失败,这比只失败一次更有启发。
--- relay: each step sees only the previous step's output
extract: FIELDS invoice_id=FT-2026-0918 net=248.00 vat_rate_applied=21 vat_amount=52.08 ...
check: ARITHMETIC ok 248.00+52.08=300.08
decide: VERDICT=unknown reason=no_invoice_in_context
draft: "we are looking into invoice FT-2026-0918 and will come back to you."
--- accumulating: each step sees the email and everything produced so far
extract: FIELDS invoice_id=FT-2026-0918 net=248.00 vat_rate_applied=21 ...
check: ARITHMETIC ok 248.00+52.08=300.08
decide: VERDICT=invoice_correct reason=net_248.00_plus_21pct_vat_52.08_equals_300.08
draft: "we have checked FT-2026-0918 and it is correct... Nothing is owed back."relay chain 花费 $0.001940,并在第二步和第三步之间丢掉了发票字段,因为第三步拿到的只是一个关于算术的句子,别的什么都没有。它生成了一条搁置消息:没用,而且显然没用。
accumulating chain——也就是开头表格那一行——花费 $0.003780,对四次相同调用来说贵了 95%,因为现在每一步都携带此前的全部内容。它生成了危险的输出。流畅,引用了自己的算术,提到的每个数字都正确,却告诉客户没有欠款,尽管实际欠 EUR 52.08。
两者之间的差异是一个三元表达式。携带较少内容的 chain 会生成明显不完整的答案;携带全部内容的 chain 会生成自信但错误的答案——而只有第二种会被发送出去。
真正的失败不是这两者。真正的失败是 pipeline 在读取任何内容之前就决定了:这个任务是对一封邮件内容做四步处理。这个结构里没有任何位置能说“注册国家不在这封邮件里;去取它”。当分解方式事先已知且稳定时,chaining 是对的。这里它只是猜测,而这个猜测被发出去了。
Routing,最古老的那个,以及没人写下来的 plan B
链接到此部分:Routing,最古老的那个,以及没人写下来的 plan BRouting“对输入进行分类,并把它导向一个专门的后续任务”。1 名称是新的;机制是 dispatcher,比本书里几乎所有东西都更古老。新的地方在于分类器可以是模型——也正是这一点让它以 switch 从未有过的方式失败。
const answer = await route(email,
(q) => classifyWithSmallModel(q), // cheap model, one call
{ billing: billingAgent, tax: taxAgent, dunning: dunningAgent },
taxAgent, // deterministic, chosen in advance
);关于最后一个参数有两点。它不是错误处理;它就是这个模式。基于模型的 router 有 dispatcher 没有的 failure mode:它可能返回一个不存在的标签、超时,或者——代价最高的那种——返回一个看似合理但错误的标签,而且没有任何信号表明它错了。这三种都必须落到某个地方,而那个地方不能是另一次模型调用,因为你已经处在“模型调用失败”的分支里。
第二点是 router 自己的 prompt 不是免费的。为了选择模型,router 需要一个可选模型目录,而其中每一个条目都是 router 在读到用户问题之前就要付费的输入。按本课程定价所用的输入费率,一个约 3,800 tokens 的目录,成本就已经相当于开篇表格里整个五次调用的 agent 运行。实践中,路由调用运行在一个便宜的模型上,这正是路由能收回成本的全部原因;但这笔算术值得朝这个方向真正算一遍,而不是想当然。当被路由的任务比路由决策本身更便宜时,路由恰恰是错的。
Parallelisation:sections,以及 voting,也就是 self-consistency
链接到此部分:Parallelisation:sections,以及 voting,也就是 self-consistencyAnthropic 把这一类拆成两种:sectioning——“把任务拆成独立 subtask 并行运行”——以及 voting——“多次运行同一个任务以获得多样输出”。1 它们共用一张图,却几乎没有别的共同点。
Sectioning 是廉价的胜利,也是 patterns.ts 里的那一行:三个 specialist——billing、tax、policy——各自有自己的 window 和工具,处理同一封邮件,最后做一次 synthesis 调用。同样的工作,两种排序:
| model calls | input | output | cost | wall clock | |
|---|---|---|---|---|---|
| the three workers, one after another | 9 | 2,910 | 324 | $0.009708 | 3,894 ms |
the same three, Promise.all | 9 | 2,910 | 324 | $0.009708 | 2,165 ms |
逐 token 完全相同,速度快了 1.8 倍。这就是这个模式值得拥有自己名称的原因:它是五种模式里唯一一个不增加成本就改善某项指标的模式。问题在于 sections 必须真正独立——给 section B 一个由 section A 产生的事实,Promise.all 就会让二者都在一个尚不存在的状态上运行。for loop 会把这个 bug 藏起来;这一行代码会暴露它。
Voting 是披着同一张图的另一种动物。把同一个问题运行 k 次并取多数,就是 self-consistency,由 Wang 等人在 2022 年 3 月作为一种 decoding 策略发表,几乎比任何人称它为编排模式早了三年。它的摘要精确说明了机制——“先采样一组多样的推理路径,而不是只取贪婪路径,然后通过 marginalizing out 被采样的推理路径来选择最一致的答案”——也说明了收益:GSM8K 上 +17.9 个点。2
图里隐藏了两件事。第一,voting 需要第 17 章的 sampling:temperature 为零时,全部 k 个样本都是同一个样本,多数票只是把一个答案付费 k 次。第二,它只在多数有意义时有效——对上面的发票回复来说没有什么可计数,因为五份草稿就是五个不同句子。Voting 适用于答案简短、可比较的任务,这正好是 Wang 的 benchmarks,也几乎不是任何面向客户的 agent 会做的事。
这里用第 23 章的本地模型逐步推理,在 20 道三步文字题上测量;这些题的答案通过计算得到,而不是由人判断:
| model calls | input | output | cost for the 20 | correct | 95 % interval | |
|---|---|---|---|---|---|---|
| one greedy chain | 20 | 1,330 | 2,649 | $0.034448 | 9/20 | 26–66 % |
| majority of 5, temperature 0.8 | 100 | 6,650 | 13,245 | $0.172240 | 9/20 | 26–66 % |
五倍调用、五倍 tokens、正好五倍账单,却没有多一个正确答案。 Voting 是一场赌注,不是改进,而这次它输了。
在有人把这句话当作对 Wang 的反驳之前,先给两个限定。二十次试验分不清 45% 和 60%——区间宽度本身就等于主张的宽度,这是第 4 章的纪律转而作用在我自己的结果上。并且,论文中的收益来自大了几个数量级的模型,在那里 voting marginalise 的那些多样推理路径确实是多样的。能迁移的不是那个数字,而是:乘数是精确的、事先已知的,而收益不是。
Orchestrator-workers,以及 summary 不是什么
链接到此部分:Orchestrator-workers,以及 summary 不是什么在 orchestrator-workers 工作流中,“一个中央 LLM 动态拆解任务,把它们委派给 worker LLM,并综合它们的结果”;它与 sectioning 的区别是“subtasks 不是预先定义的,而是由 orchestrator 决定的”。1 这里的祖先根本不来自语言模型:这是 master-worker;而 worker 把发现写入共享空间、controller 读取它们的版本,就是 1970 年代语音理解研究中的 blackboard architecture。2026 年的新东西是 controller 是一个模型,因此分解可以按输入动态决定——这一句话里同时包含了灵活性和成本。
相对于单 agent 的 5 次调用,它花了 12 次模型调用,并得到了相同结论。然后它做了一件值得仔细看的事:
orchestrator final: VERDICT=credit_note_due amount=52.08 source=worker_unverified
| PO_MISMATCH=yes source=worker_unverified
single agent: VERDICT=credit_note_due amount=52.08 reason=reverse_charge_should_have_applied
| PO_MISMATCH=yes invoice_says=PO-4417 order_says=PO-4471两者都对。只有一个知道为什么。tax worker 在自己的 window 里同时拥有发票、订单和税表,得出结论,并且还注意到——没人要求它——发票上的采购订单号与订单不匹配。然后它返回了一个 summary。orchestrator 可以复述这两条,但无法核验任何一条,因为证据留在了它从未见过的 window 里。这就是第 24 章结尾问题的答案:父级能看到的是子级选择写下来的任何东西。
修复方法是一个 flag,而它有价格:
| what the worker returns | orchestrator input tokens | cost | what the parent can do |
|---|---|---|---|
| its conclusion | 3,628 | $0.012512 | repeat it |
| its conclusion and its evidence | 4,065 | $0.013554 | derive it again, and disagree |
多 12% 的 input tokens,多 8.3% 的钱,然后短语 source=worker_unverified 从答案里消失。这就是每个 multi-agent system 里的交易,却几乎从不被明说:child 的干净 window 值得拥有,parent 审计它的能力也值得付费,而你不能免费同时拥有两者。
那么 orchestrator-workers 什么时候是错的?这里,在这个任务上。它买到了一个正确答案,而一个带同样四个工具的 agent 也能得到;它的成本是 1.66 倍,wall clock 是 2.3 倍,并且让那个答案更难辩护。Anthropic 自己的指导在模式开始前就这么说:寻找“尽可能简单的解决方案,只在需要时增加复杂性”,因为“agentic 系统常常用 latency 和成本换取更好的任务表现”。1 上面的表格就是给这句话配上数字。
Evaluator-optimiser,以及出题的 judge
链接到此部分:Evaluator-optimiser,以及出题的 judge一次调用生成,另一次调用评估,然后 loop 重复,直到评估通过。1 公开的祖先是 Self-Refine——同一个模型担任“generator、refiner 和 feedback provider”,报告七个任务平均约 20 个百分点的绝对提升3——以及 Reflexion,它把 critique 存入跨尝试的 episodic buffer,并报告 HumanEval 上 91% pass@1,而 baseline 为 80%。4
它的成本模型是五者中最简单的:每轮两次调用,而轮数不由你决定。 一个本来只需一次调用的任务做三轮 refinement,就是六次调用,所以这个模式的底线是 6×,上限是你设置的任何 cap——这让第 23 章的预算退出不再只是整洁,而是必需。
上限更微妙,而且可以测量。同样 20 道题,本地模型答对了 9 道。然后把每个答案给它看,问它是否正确——不告诉它答案是它自己的,这移除了讨好自己的混杂因素,只留下能力因素:
| the model's own answer | it said “yes” | it said “no” |
|---|---|---|
| the 9 that were right | 9 | 0 |
| the 11 that were wrong | 3 | 8 |
这是一个比小节标题暗示的更好的 judge,而说出这一点正是测量而非断言的意义:它没有拦下任何正确答案,并抓到了 11 个错误中的 8 个。作为 filter,它值得这些调用。
但作为 stopping rule——而这正是 evaluator-optimiser loop 实际用它做的事——那三个批准就是全部故事:它们让 loop 带着错误答案结束,而且再多轮数也永远到不了它们。refinement loop 不可能比它的 judge 更正确。 买更多轮数,买到的是对 judge 能看见的错误进行尝试,而且按全价付费;对它看不见的错误则什么也买不到。
所以规则是:只有当 evaluator 拥有 generator 没有的东西时,它的调用才值钱。 编译器、测试套件、schema validator、不同模型、人类。Self-Refine 自己的结果是用人类偏好和任务指标衡量的,从不是用模型对自己的判断。如果你的 evaluator 唯一优势只是不同 prompt,那你是在付双倍的钱买一致意见。第 29 章会构建真正有优势的版本:一个答案预先写好的 golden set。
loops 不是 patterns
链接到此部分:loops 不是 patterns上面五个是给你的代码用的形状。它们下面还有第二类,常被列在一起,但不该这样列:ReAct、Reflexion、plan-and-execute 和 tree of thoughts 是推理 loops,它们的成本体现在请求数上。
第 12 章讲的是模型内部的推理,你在一次调用的 output tokens 中为它付费。这里是另一种。账单到来时,这个差异很重要:更长的 chain of thought 会让一次调用更贵,而推理 loop 会把一个任务变成多次调用,每次都重新发送此前的一切——也就是第 23 章在失控表格里测到的二次方增长。
| loop | calls, per task | what the extra calls buy |
|---|---|---|
| ReAct | 每一步一次,直到停止 | 模型对工具返回的内容作出反应5 |
| plan-and-execute | 一次用于计划,然后每一步一次 | 计划在第一步运行前就固定6 |
| Reflexion | attempts × (act + reflect) | critique 会保留到下一次尝试4 |
| tree of thoughts | branching factor × depth,外加每个 node 一次 evaluation | search,带 backtracking7 |
tree-of-thoughts 论文公布了自己的成本表,这比它应有的频率更罕见。在 Game of 24 上使用 GPT-4:input/output prompting best-of-100 以每例 $0.13 解出 33%,chain of thought best-of-100 以 $0.47 解出 49%,tree of thoughts 以 $0.74 解出 74%,作者还指出它“可能需要比 CoT 多 5-100 倍的 generated tokens”。7
价格接近便宜方法的六倍,成功率略高于两倍。这是否划算取决于一个失败 case 对你意味着什么——这是采用这四种方法中任何一种前都该问的问题。
本课程不重新实现它们。四者都有作者自己的 Python reference implementations,而它们的价值在于作为源头,而不是翻译:ysymyth/ReAct、noahshinn/reflexion、princeton-nlp/tree-of-thought-llm 和 AGI-Edgerunners/Plan-and-Solve-Prompting。去读那些 repository 里的 prompts;那些 prompts 就是论文。
两种拓扑,其中一种不会回来
链接到此部分:两种拓扑,其中一种不会回来现在进入真正的 multi-agent,也就是大部分混乱所在。一个 agent 让另一个 agent 参与有两种方式;它们不是变体,差异在于之后谁掌控局面。
Agent as a tool。 父级调用它,拿到答案,然后继续。它就是第 18 章的工具接口,只是接口后面放了整个 agent;父级从不失去控制权。这就是上面的 orchestrator 做的事。
Handoff。 父级转移对话,并且不再拿回来。OpenAI 的指南给出了最清晰的公开表述:handoffs 是“一次单向转移,允许一个 agent 委派给另一个 agent……如果某个 agent 调用 handoff function,我们会立即开始执行那个被 handoff 到的新 agent,同时也转移最新的 conversation state。”8
一个词汇警告,因为这常常绊倒人:“handoff”是某个 SDK 的词,不是标准。 它是 OpenAI Agents SDK 及其指南中的术语;该指南还把两种安排命名为“manager”和“decentralized”,并指出在 manager pattern 中“边表示 tool calls,而在 decentralized pattern 中,边表示 handoffs”。8 这个领域确实有一个开放标准——A2A,版本 1.0.0,版权归 Linux Foundation,带版本化 release history 和记录 breaking changes 的文档列表,其声明原则是 opaque execution:agents“基于声明的 capabilities 和交换的信息协作,而无需共享它们的 internal thoughts、plans 或 tool implementations”。9 那不是 handoff,二者的比较属于第 26 章。这里重要的是,这两个词中一个是库的 API,另一个是有治理的规范。
这个区别是数据结构,不是图:
export type EdgeKind = "tool" | "handoff";
export interface AgentEdge { from: string; to: string; kind: EdgeKind }
export interface AgentGraph { root: string; agents: Record<string, AgentSpec>; edges: AgentEdge[] }
/** One agent may not be both a tool of X and a handoff target of X. */
export function conflicts(g: AgentGraph): AgentEdge[] {
const seen = new Map<string, EdgeKind>();
const bad: AgentEdge[] = [];
for (const e of g.edges) {
const key = `${e.from}->${e.to}`;
const other = seen.get(key);
if (other && other !== e.kind) bad.push(e);
else seen.set(key, e.kind);
}
return bad;
}
/** Every agent reachable from the root, and at what depth. */
export function reachable(g: AgentGraph): Map<string, number> {
const depth = new Map([[g.root, 0]]);
const queue = [g.root];
while (queue.length) {
const id = queue.shift()!;
for (const e of g.edges.filter((x) => x.from === id)) {
if (depth.has(e.to)) continue;
depth.set(e.to, depth.get(id)! + 1);
queue.push(e.to);
}
}
return depth;
}二十行,两个否则你会在生产中才发现的 bug。reachable 找出没人能到达的 agent——已配置、已付费、从未被调用。conflicts 拒绝同时属于两种类型的 edge;这听起来吹毛求疵,直到你把它大声读出来:父级既保留控制权,又把控制权交出去。在一个包含一个 orphan 和一条 double edge 的五 agent system 上运行它:
reachable: lead@0 billing@1 tax@1 dunning@1
orphans: ghost
conflicts: lead->tax边界上真正穿过的是什么
链接到此部分:边界上真正穿过的是什么现在是本节存在的那项测量,也是本章唯一一项针对真实模型而非脚本化模型的测量。
客户在第一条消息里陈述一个约束——我们的账户注册在葡萄牙,不是西班牙;所有涉税内容都必须使用葡萄牙——随后聊了别的事,接着提出一个必须由 billing 回答的问题。case 被转移。二十四次试验,每次不同国家和公司,四种 transfer payload,然后问接收 agent 一个问题:这个客户的账户注册在哪个国家?
| what was transferred | mean payload | the constraint was in it | the specialist recalled it | 95 % interval |
|---|---|---|---|---|
| the whole conversation | 173 tokens | 24/24 | 20/24 — 83 % | 64–93 % |
| a summary the sending agent wrote | 62 tokens | 1/24 | 0/24 — 0 % | 0–14 % |
| only the last user message | 61 tokens | 0/24 | 0/24 — 0 % | 0–14 % |
| a typed record | 69 tokens | 24/24 | 24/24 — 100 % | 86–100 % |
第三行是 control,并且表现得像 control:事实不在那里,所以无法被回忆。其他三行才是发现。
完整 transcript 是 173 tokens,83% 的时候有效;它的四次失败是第 24 章的主题,不是本章的主题。typed record 是 69 tokens——只比 summary 多 7 个——并且每次都有效,因为约束位于一个命名字段里,而不是一个句子里。
而 summary 是值得盯着看的那一行。它 24 次里失败了 24 次,原因不是 reader 漏看了。这个约束总共只出现在 24 份 summary 中的 1 份里。 接收 agent 并不粗心;它拿到的是一个不含答案的文本。summary 是一种你没有写的压缩,由一个你看不见其 window 的模型生成,优化目标是读起来像 summary——而“客户说我们的记录里国家错了”正是 summariser 会当作流程噪声丢掉的那类从句。
对这个数字要诚实地设限:summariser 是一个五亿参数模型,更大的模型会保留更多。不会随规模改善的是风险的形状——发送 agent 会在每次 handoff、每种措辞下,以不可观察的方式决定哪些事实存活。typed record 完全不依赖那种判断,所以它是由构造取胜,而不是由智能取胜。凡是必须在 transfer 中存活的东西,都应该是字段,而不是句子。
同样的推理反向适用于 agent-as-tool 拓扑,前面的表已经给出了价格:worker 返回的东西同样是 summary,而多付 8.3% 让证据随它一起返回,是从 parent 侧看到的同一个修复。
什么时候单 agent 胜出
链接到此部分:什么时候单 agent 胜出三个收尾事实,都来自上面的表。
multi-agent system 会放大调用次数,而调用在 context 中是二次方成本。 orchestrator 做了 12 次模型调用,而一个 agent 做了 5 次;并且每次调用都携带自己的增长 transcript——3,628 个 input tokens 对 2,697,随着任务长度增长,这个差距会扩大。
每个边界都是有损 channel。 两个 agents 意味着一个 summary。四个 agents 串成 chain,意味着三个 summary,彼此复合,每一个都由一个优化目标并非你的决策的模型写成。
单 agent 发现了没人要求它找的东西。 采购订单不匹配之所以浮现,是因为一个 window 同时持有发票和订单。把工作拆给 specialists,也会拆掉注意到两个事实彼此矛盾的能力。
这并不是反对已发表的 multi-agent frameworks;它们值得作为 primary sources 阅读,而不是通过 tutorials 了解。10 它是在要求第二个 agent 证明自己值得存在。
所以,这是一项测试,不是偏好。满足至少一个条件时再添加第二个 agent:sub-task 需要一个 parent 不能继承的干净 window(第 24 章);sub-tasks 真正独立且 wall clock 重要,也就是上面的 1.8×;sub-task 需要不同权限或不同模型,这会在第 30 章变成一个安全论证;或者 sub-task 归别人所有,这时真正的协议才开始重要。如果答案是“这样每个 agent 的 prompt 更清楚”,那就给这一个 agent 写一个更清楚的 prompt。它是免费的。
接下来去哪里
链接到此部分:接下来去哪里你现在可以说出五种模式的名称,在同一个任务上比较它们的价格,区分 orchestrator 与 sectioner、tool call 与 handoff,并用表格而不是偏好为单 agent 辩护。
这里每种安排都共享了一个一旦接触现实就不会继续存在的便利条件:所有工具都属于我们。发票、订单、税表、orchestrator 背后的 workers——同一个 repository、同一个 deploy、同一套 types、同一批人。
现在把其中一个放到公司边界的另一边。税表属于会计 vendor,订单记录属于仓储系统,而二者都没有读过你的 Tool 接口。你需要一种方式,让一个不是你写的模型去发现、描述并调用别人运营的能力——带 authentication(这是第 27 章的一半)、versioning,并保证 server 不能读取你对话的其余部分。这是一个协议问题,它有一份带 normative schema 的规范,而几乎所有被索引到的相关内容都在描述一个已经不存在的 revision。
第 26 章会阅读那份规范,而不是总结它,并从在终端里手动输入 JSON-RPC 开始。
来源与方法
链接到此部分:来源与方法以上所有成本和 token 计数都来自第二节描述的脚本化 provider,在 Node 22 上通过 loopback interface 运行,使用 o200k_base encoding 计数,并按第 16 章在 2026 年 9 月 6 日读取到的费率计价——每百万 input tokens $2.00,每百万 output tokens $12.00。Wall-clock 数字来自同一批 run,provider latency 设为每次调用 400 ms,工具设为 50 ms,因此测量的是 arrangement,而不是任何 provider。两项真实模型测量——handoff 表和 voting-and-judging 表——使用 Qwen/Qwen2.5-0.5B-Instruct,在 CPU 上以 float32 运行,endpoint 形状相同;除非声明 temperature,否则为 greedy;区间用第 4 章的 Wilson 方法计算。本章没有任何请求发送到付费 endpoint,也没有任何数字是估算值。
参考资料
链接到此部分:参考资料-
Anthropic,Building effective agents,2024 年 12 月 19 日,
anthropic.com/engineering/building-effective-agents,2026 年 9 月 7 日读取。以上使用的五个 workflow 名称及其中每个被引用短语的来源——prompt chaining、routing、parallelisation 及其 sectioning 和 voting 变体、orchestrator-workers、evaluator-optimiser——也包括寻找“尽可能简单的解决方案,只在需要时增加复杂性”的建议,以及“agentic 系统常常用 latency 和成本换取更好的任务表现”的观察。第 22 章和第 23 章引用了它对 agent 的定义。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 -
Wang, X., Wei, J., Schuurmans, D., Le, Q., Chi, E., Narang, S., Chowdhery, A. and Zhou, D. Self-Consistency Improves Chain of Thought Reasoning in Language Models. arXiv:2203.11171(2022 年 3 月)。voting pattern 的源头,在那里它被描述为一种 decoding 策略而非架构:采样多样推理路径,然后“通过 marginalizing out 被采样的推理路径来选择最一致的答案”,并报告 GSM8K +17.9、SVAMP +11.0、AQuA +12.2、StrategyQA +6.4、ARC-challenge +3.9 的提升。 ↩
-
Madaan, A. et al. Self-Refine: Iterative Refinement with Self-Feedback. arXiv:2303.17651(2023)。evaluator-optimiser loop 中一个模型担任全部三个角色——“generator、refiner 和 feedback provider”——在七个任务上把“task performance 平均绝对提升约 20%”,衡量依据是人类偏好和自动指标,而不是模型自己的判断。 ↩
-
Shinn, N., Cassano, F., Berman, E., Gopinath, A., Narasimhan, K. and Yao, S. Reflexion: Language Agents with Verbal Reinforcement Learning. arXiv:2303.11366(2023)。在多次尝试之间加入 self-critiques 的 episodic memory——“reinforce language agents not by updating weights, but through linguistic feedback”——报告 HumanEval 上 91% pass@1,而 GPT-4 baseline 为 80%。注意它的结果所依赖的要求:来自环境的真实信号,例如失败的测试,而不是模型对自己的意见。 ↩ ↩2
-
Yao, S., Zhao, J., Yu, D., Du, N., Shafran, I., Narasimhan, K. and Cao, Y. ReAct: Synergizing Reasoning and Acting in Language Models. arXiv:2210.03629(2022)。交错的 reasoning traces 和 actions;第 23 章构建了这个 loop。这里引用它是为了成本形状,而不是结果:每一步一次模型调用,每次都重新发送完整 transcript。 ↩
-
Wang, L., Xu, W., Lan, Y., Hu, Z., Lan, Y., Lee, R. K.-W. and Lim, E.-P. Plan-and-Solve Prompting: Improving Zero-Shot Chain-of-Thought Reasoning by Large Language Models. arXiv:2305.04091(2023)。“First, devising a plan to divide the entire task into smaller subtasks, and then carrying out the subtasks according to the plan”——先计划再执行的形状,也是本章关心的 trade 的来源:计划在第一次观察到来前就固定,这就是 prompt chaining,只是分解由模型写,而不是由你写。 ↩
-
Yao, S., Yu, D., Zhao, J., Shafran, I., Griffiths, T. L., Cao, Y. and Narasimhan, K. Tree of Thoughts: Deliberate Problem Solving with Large Language Models. arXiv:2305.10601(2023)。对中间“thoughts”做 search,带 self-evaluation 和 backtracking;Game of 24 上 74%,而 chain-of-thought prompting 为 4%。上文引用的成本数字来自论文自己的 Appendix B.3, Table 7:每个 case,input/output prompting best-of-100 以 $0.13 达到 33%,chain of thought best-of-100 以 $0.47 达到 49%,tree of thoughts 以 $0.74 达到 74%,并附作者说明:ToT“could require 5-100 times more generated tokens than CoT”。 ↩ ↩2
-
OpenAI,A practical guide to building agents(PDF),2026 年 9 月 7 日读取。manager 与 decentralized 的划分、上文引用的 graph framing(“在 manager pattern 中,edges represent tool calls,而在 decentralized pattern 中,edges represent handoffs”),以及 handoff 的定义:“一次单向转移……我们会立即开始执行那个被 handoff 到的新 agent,同时也转移最新的 conversation state”。注意最后一个从句解决了什么:在这个 SDK 中,conversation state 确实会随之传递;这是该库的设计决定,不是 handoffs 的一般属性。 ↩ ↩2
-
Agent2Agent (A2A) Protocol Specification,最新发布版本 1.0.0,
a2a-protocol.org/latest/specification/,2026 年 9 月 7 日读取;版权归 Linux Foundation,Apache-2.0。上文引用:“open standard designed to facilitate communication and interoperability between independent, potentially opaque AI agent systems”,以及 opaque execution 原则——agents“collaborate based on declared capabilities and exchanged information, without needing to share their internal thoughts, plans, or tool implementations”。该页面包含 release history(0.1.0、0.2.6、0.3.0、1.0.0)、breaking changes 附录,以及关于它与 MCP 关系的附录。第 26 章会做这个比较。 ↩ -
本章不教学的 multi-agent frameworks,给想读 primary sources 而非教程的读者:Wu, Q. et al., AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation, arXiv:2308.08155(2023),其中 agents 是“customizable, conversable”,conversation 本身就是 programming model;Hong, S. et al., MetaGPT: Meta Programming for a Multi-Agent Collaborative Framework, arXiv:2308.00352(2023),它把 standard operating procedures 编码进 role prompts,并明确指出“solutions to more complex tasks are complicated through logic inconsistencies due to cascading hallucinations caused by naively chaining LLMs”——也就是本章开头测量到的自信但错误的 chain,以摘要中的名字出现;以及 Park, J. S. et al., Generative Agents: Interactive Simulacra of Human Behavior, arXiv:2304.03442(2023),二十五个带 memory、reflection 和 planning 的 agents,这是对“如果不断增加 agents 会怎样”这个问题的最大规模公开回答。 ↩