第 10 章
第 2 周:什么时候不该用 Agent?
读 Anthropic 的 Building effective agents:workflow 和 agent 的区别、五种 workflow 模式、每种模式怎么测、哪里容易坏,以及步骤越多为什么越难可靠。一段讲解视频,一个可以调参数的模式模拟器。
这一周的其他章节会从零写 agent loop、做工具调用、写 MCP server、给 loop 加 trace。动手之前,先退一步看全局:这个需求真的需要 agent 吗?Anthropic 在 2024 年 12 月发表的 Building effective agents(Erik Schluntz、Barry Zhang)给出的第一条建议是:先用最简单的方案,确实需要时再加复杂度。这一章把这篇文章读一遍,并且从测开的角度看每种模式该怎么测。
讲解视频
互动演示
选一种模式,调每步成功率、步数、票数、评估者的漏判率,实时看端到端成功率、调用次数和延迟,下面还有所有模式的对比。之后是一个"给需求选模式"的场景判断练习,页面底部有自动判分的练习。
两个定义:谁决定下一步
原文把这类系统统称为 agentic systems,再按"谁决定流程"分成两种:
- Workflow:LLM 和工具按预先写好的代码路径编排。先调哪个、再调哪个,是你的代码说了算。
- Agent:LLM 动态地决定自己的流程和工具使用,自己掌控怎么完成任务。
| Workflow | Agent | |
|---|---|---|
| 谁决定下一步 | 代码 | 模型 |
| 步数 | 固定,或有明确上限 | 不固定,靠停止条件兜底 |
| 成本和延迟 | 可以提前算 | 是一个分布,要看 P50 / P95 |
| 原文的适用场景 | 定义清楚的任务,要可预测、结果一致 | 需要灵活性、需要模型在规模化场景下自己做决策 |
| 测开怎么测 | 每个节点单独测,再算端到端 | 测端到端结果 + 看 trace + 测停止条件 |
原文的判断是:agentic 系统通常是拿延迟和成本换更好的任务表现。很多应用只要把单次 LLM 调用优化好(加检索、加 in-context 示例)就够了。
所有模式都用同一块积木:增强型 LLM,也就是加上了检索、工具、记忆的 LLM。原文要求这些能力按场景裁剪,接口要简单、文档要写清楚;接第三方工具的一种方式是 MCP,本周有单独一章。从测试角度看,这一块就是一次调用,最好测、最便宜:固定输入、跑 n 次、数成功次数,第 1 周的 rule of three、Wilson、pass^k 全都直接能用。
五种 workflow,各自怎么测
1. 提示链(prompt chaining)
把任务拆成固定的几步,每次调用处理上一步的输出,中间可以加程序化检查(原文叫 gate)。适合能干净拆成固定子任务的场景,用延迟换准确率。原文的例子:先写营销文案再翻译;先写大纲、检查大纲,再写正文。
- 端到端成功率是乘出来的:5 步各 95%,全对只有 \(0.95^5 \approx 77.4\%\)。
- 每一步单独测:把上游输出存成快照固定喂给下一步,出问题能定位到哪一步。
- gate 本身也要测:它放过了多少坏的中间结果。
2. 路由(routing)
先对输入分类,再交给专用的下游处理。适合输入有几类明显不同、分开处理更好,并且分类能做准的场景。原文的例子:客服问题分成一般咨询、退款、技术支持;简单问题给小模型,难题给大模型。
- 分类准确率是天花板:分类 92%、下游 95%,误路由按失败算,端到端最多 87.4%。
- 看混淆矩阵,不只看准确率。退款被分到一般咨询和反过来,代价不一样。每一类单独算准确率并带上 Wilson 区间,小类的区间会很宽。
3. 并行(parallelization)
几个 LLM 同时干活,输出由代码聚合。分段是把任务拆成独立的几块并行做(例如一个实例回答用户、另一个同时筛查不当内容);投票是同一个任务跑多次(例如用几个不同的 prompt 审查代码漏洞)。
注意:原文的投票不限于过半。漏洞审查的例子是几个 prompt 各自审查、发现问题就标记,相当于任一票即报警的或规则;内容审核的例子是用不同的票数阈值平衡误报和漏报。阈值越低越偏召回,越高越偏精确。下面的计算以多数投票为例。
- 分段的延迟约等于一次调用,但成功率仍然是乘积:3 块各 95% 就是 85.7%。
- 投票和 pass@k 不是一回事。pass@k 是"k 次里有一次对就行",默认你有一个可靠的检查器挑出那一次;多数投票没有检查器,要求过半的票是对的;换成“任一票报警就算”的阈值,召回更高,误报也更多。单票 80%、5 票:pass@5 = 99.97%,多数投票只有 94.2%。
- 单票低于 50% 时越投越差:单票 40%,3 票 35.2%,9 票 26.7%。
- 票与票之间要独立。同一个模型、同一个 prompt 跑 5 次,错误往往扎堆,这就是第 1 周"独立性假设"里的簇内相关。
4. 编排者-工人(orchestrator-workers)
一个中心 LLM 动态拆解任务,分给工人 LLM,再汇总结果。和并行的区别是:子任务不是预先定义的,而是编排者根据输入决定的。原文的例子:每次要改多个文件的编程产品;从多个来源收集信息的搜索任务。
- 工人个数不固定,成本就不固定,报告里要给调用次数的分布。
- 拆解本身要测:有没有漏需求、有没有重复。
- 汇总要测忠实度:结果里的每句话能不能在某个工人的输出里找到出处。排查这些问题离不开 trace。
5. 评估者-优化者(evaluator-optimizer)
一个 LLM 生成,另一个 LLM 评估并给反馈,循环修改。适合有明确评估标准、迭代确实能带来可衡量提升的场景。原文的例子:文学翻译;由评估者决定还要不要继续搜的多轮搜索。
评估者就是一个 Judge,会判错:漏判(坏稿子被放行)和误杀(好稿子被打回)。设生成器单次做对的概率为 p,漏判率 m,误杀率 f,轮数无限多时成功率收敛到:
$$ \frac{p(1-f)}{p(1-f) + (1-p)\,m} $$p = 60%、f = 10%、m = 20% 时,上限是 87.1%;最多 3 轮时是 83.2%。只要漏判率 m > 0,上限就到不了 100%:坏稿子一旦被放行,流程就结束了,轮数再多也改不回来。这个模型偏保守:它假设每轮生成器独立重来、成功率不变,相当于拒绝采样。原文用这个模式的前提是反馈有用,反馈真能让下一稿变好时,成功率和上限都会更高。所以评估者要先拿人工标注校准,算一致率和 Cohen’s κ,这是第 5 周的内容。另外必须设最大轮数。
自主 Agent
从用户的指令开始,agent 自己规划、自己行动,每一步从环境拿"真实情况"(工具结果、代码执行结果)判断进展,必要时回来找人确认,常用最大迭代次数作为停止条件。原文说,实现上它们通常就是一个 LLM 在循环里根据环境反馈调用工具,所以工具集和工具文档的设计至关重要。
适用于开放式问题:很难或无法预测要多少步,路径写不死,并且你对模型的决策有一定信任。原文的例子是解决 SWE-bench 任务的编程 agent 和 computer use 参考实现。原文点明的代价是成本更高、错误会累积,建议在沙箱里充分测试并加上护栏。
测开视角:
- 只能测端到端。路径每次不一样,没有固定的"第 3 步"可以断言。按任务算 pass^k,τ-bench 就是这么评客服 agent 的。
- 步数、成本、延迟都是分布,报 P50 和 P95,门禁设在 P95 上。
- 测停止条件:卡住时会不会在最大步数处停下。
- 测恢复能力:工具返回错误或超时后,agent 能不能从环境反馈里发现并纠正。这是 agent 相比固定链路真正的优势,第 4 周做故障注入时会专门测。
可靠性账:步骤越多,越要小心
把链路上每一步的成功率乘起来,就是端到端成功率:
$$ P_{\text{端到端}} = p_1 \times p_2 \times \cdots \times p_n $$| 每步成功率 | 5 步 | 10 步 | 20 步 |
|---|---|---|---|
| 90% | 59.0% | 34.9% | 12.2% |
| 95% | 77.4% | 59.9% | 35.8% |
| 99% | 95.1% | 90.4% | 81.8% |
- 这就是 pass^k 的公式,k 从"同一请求跑 k 次"换成了"链路上的 k 步",前提也一样:每步独立,且每步的成功率要在真实上游输出上测。如果各步错误正相关(难的输入每步都难),真实成功率反而会比乘积高,这和第 1 周 Agent B 的例子是同一个道理。真正会让成功率低于乘积的,是每步只用干净快照测出的 p:上游“做对了但质量偏差”的输出,会让下一步的实际成功率低于快照测出的值。
- 反推:10 步想要端到端 90%,每步要 \(0.9^{1/10} \approx 98.95\%\)。
- 证明每步 99%:按 rule of three,零失败要跑约 300 次(精确法 299 次)。步数越多,要证明的东西越多。
- 乘积可以被打破:gate 检出错误后重试、agent 从环境反馈里纠错,都能把成功率拉回来,代价是多出来的调用和延迟。
workflow 的步数固定,成本和延迟能提前算。agent 的步数由模型决定,同一个任务这次 8 步、下次 14 步。视频里模拟了 400 次 agent 运行(所需步数 4 到 12 步、每步 95%、出错后 70% 能纠正):成功率 90%,P50 是 8 步,P95 是 12 步。评测 agent 时,成本和延迟要当成随机变量来报。
原文的核心建议
原文 Summary 部分给出了三条核心原则:保持简单;让 agent 的规划步骤透明可见;认真设计 agent-computer interface(ACI)。另外,它对框架单独给了建议。
**原则 1:保持简单,先用最简单的方案。**从单次调用开始,只有当更复杂的方案能证明效果更好时才加复杂度。“能证明"就是评测的活:新旧方案在同一批用例上比,数字说话。
**原则 2:让规划步骤透明。**agent 打算做什么、为什么这么做,要显式地展示出来。对测开来说,这就是可观测性:规划和每一步决策都进 trace,出错时才能定位(本周 trace 一章)。
**另外:谨慎使用框架。**框架省掉了调 API、解析工具、串联调用这些底层工作,但多出来的抽象层会遮住底层的 prompt 和响应,让调试更难,也容易加上不需要的复杂度。原文建议先直接用 LLM API;如果用框架,要搞懂它底层在做什么,对底层的错误假设是客户出错的常见原因。
**原则 3:认真设计 ACI。**ACI 要像人机界面一样投入:工具要写好文档、充分测试。具体做法:
- 站在模型的角度读工具描述,只看描述和参数能不能明白怎么用。
- 参数名和说明写得像给新同事看的 docstring,写上例子、边界情况、和其他工具的分工。
- 用很多不同的输入试调用,看模型犯什么错,然后改工具。
- 防呆(poka-yoke):改参数设计,让错误更难犯。做 SWE-bench agent 时,他们花在工具上的时间比花在整体 prompt 上还多;模型切换目录后用相对路径老出错,改成必须传绝对路径后就再也没用错过。
对测开来说,ACI 就是被测系统和模型之间的接口契约。工具契约测试(参数校验、错误信息能不能让模型看懂并自我纠正)是这一章最直接能落地的测试工作。
from math import comb
def chain(p: float, n: int) -> float:
"""提示链:n 步各自成功率 p,各步独立时的端到端成功率。"""
return p ** n
def majority_vote(p: float, m: int) -> float:
"""m 票(奇数)多数投票,每票独立、正确率 p。"""
if m % 2 == 0:
raise ValueError(f"票数要是奇数,收到 m={m}")
return sum(comb(m, k) * p**k * (1 - p)**(m - k) for k in range(m // 2 + 1, m + 1))
def evaluator_optimizer(p: float, rounds: int, miss: float, false_reject: float) -> float:
"""每轮生成器成功率 p;评估者漏判率 miss、误杀率 false_reject;
最多 rounds 轮,最后一轮被打回也照样输出。"""
accept_good = p * (1 - false_reject)
accept_bad = (1 - p) * miss
reject = 1 - accept_good - accept_bad
ok = sum(reject**i * accept_good for i in range(rounds))
return ok + reject**(rounds - 1) * p * false_reject
print(chain(0.95, 10)) # 0.5987
print(majority_vote(0.8, 5)) # 0.9421
print(evaluator_optimizer(0.6, 3, 0.2, 0.1)) # 0.8318
原文顶部后来加了编者按:文中提到的工具生态自 2024 年 12 月以来变化很大。编者按只说工具生态变了,并指向 Claude Managed Agents 的最新做法。我们的判断是:上面的模式和原则仍然适用。
常见错误说法
- “用 agent 肯定比 workflow 强”:agent 拿成本、延迟和可预测性换灵活性,任务路径固定时是亏的。
- “每一步都 95%,整体应该也差不多”:10 步乘下来只有 59.9%。
- “投票次数越多越准”:单票低于 50% 时越投越差;同源的票相关,提升也比独立时小。
- “加一个评估者自动把关就行了”:评估者自己会漏判,只要漏判率 m > 0,成功率的上限就到不了 100%。
- “agent 平均一次花 8 步”:只报平均值看不到长尾,门禁要看 P95。
- “先上一个框架再说”:原文建议先直接用 LLM API,搞懂底层再决定要不要框架。