研究者用 60 个问题对比了多 Agent 团队与单 Agent 的效果:多 Agent 找到的事实数量是单 Agent 的 2.11 倍,但单位成本下单 Agent 反而胜出,因为多个 Agent 大量重复发现相同事实而非互补。
每个多智能体(multi-agent)方案都以同样的方式结尾:这是研究员,这是评论员,这是编辑,这是他们产出的精炼简报。
缺失的是那个决定这一切是否值得的比较:同一个问题交给一个智能体来处理,然后把两份账单摆到台面上。
所以我构建了这个 Crew(智能体团队),让比较本身成为产品。
$ python -m research_crew --compare --seeds 60
found bogus prec cost fails found/cost
-----------------------------------------------------
crew 9.50 4.55 0.69 18.0 0.00 0.528
solo 4.50 1.65 0.74 4.0 0.00 1.125
the crew found 2.11x as much and cost 4.50x as much.
per unit of spend, solo wins.
Crew 发现了超过两倍的真实事实——但相比它所替代的单一智能体,精确度反而更低,价格却是 4.5 倍。两个事实同时成立,而演示中只呈现了其中一个。
Repo: https://github.com/dev48v/research-crew — PUBLIC, MIT, 仅使用标准库,23 个测试,无需 API key 和网络。在浏览器中运行完整测试:https://dev48.infy.uk/agentlab/vol1-04-research-crew.html
四个研究员回答同一个问题,并不会找到四个互不重叠的事实集。他们会重复发现相同的东西,所以真实发现的增长会趋于饱和。而他们的错误不会——每个研究员都会自己发明一套,所以噪音随着 Crew 规模线性增长。
两条形状不同的曲线:
从 8 个研究员扩展到 16 个:额外花费 24 成本,却只增加了 0.08 个事实,精确度还急剧下降。效率峰值出现在两个研究员的时候。一旦超过召回率(recall)饱和点,更宽的 Crew 就是在直接把金钱转化为噪音。
正因为如此,Crew 一开始就在精确度上落后于单一智能体,只有在评论员足够严格时——大约 0.56 的严格程度——才能超过它:
在这条线以下,你花 4.5 倍的钱,得到的是一个可信度更低的答案。
而评论员自身的成本从未被单独列出
过滤器有两个误报率。所有人都在讨论第一个:
catch_rate = 0.9 * strictness # 它移除的虚构材料
collateral_rate = 0.25 * strictness # 沿途移除的正确材料
把评论员调到最高可以获得 29 个百分点的精确度,但代价是牺牲掉 2.7 个真实事实。没有一个设置能两者兼得。
这两条线最初是一条巧妙的表达式,但一个测试把它杀死了:
def test_the_critic_removes_correct_claims_too():
"""一个没有附带损害的过滤器根本不是在过滤。"""
...
assert real_lost, "the critic never discarded a correct claim"
测试失败了,而原因比修复方案更有价值:原来的代码把两个不同的误报率合并到了一个公式里,所以附带损害是不可见的——包括对我自己而言,而那行代码是我写的。
while attempts < max_attempts:
attempts += 1
spent_here += task.cost # 无论成功与否都计费
report.spent += task.cost
一个定价为 3 的任务失败一次花费 6,报告里写的是 6。只计算成功的尝试会让你少报账单,缺少的恰好是你的失败率——而这恰恰是你试图去度量的数字。
依赖任务失败的任务会被跳过,永远不会执行。
broken = [d for d in task.depends_on if d in failed]
if broken:
report.results.append(TaskResult(task.id, task.role, "skipped",
reason=f"depends on {', '.join(broken)}"))
failed.add(task.id)
continue
如果每个研究员都失败了,而编辑仍然运行,它会得到一个空的上下文,然后写出一份自信的、结构良好的空总结——在有人检查之前,与真正的总结毫无区别。测试断言 by["edit"].output == ""。
而且失败点本来就更多。在每个任务 20% 失败率的情况下:Crew 每次运行有 0.53 个非正常任务,单一智能体只有 0.05 个。十倍的暴露面,这正是七个任务而非一个任务所预测的结果。
图就是 Crew;角色(persona)是最不承重的部分。
def waves(self) -> list[list[Task]]:
remaining = {t.id: set(t.depends_on) for t in self.tasks}
done, out = set(), []
while remaining:
ready = sorted(tid for tid, deps in remaining.items() if deps <= done)
if not ready:
raise CycleError(f"dependency cycle among: {', '.join(sorted(remaining))}")
out.append([known[tid] for tid in ready])
done.update(ready)
for tid in ready:
del remaining[tid]
return out
Kahn 算法,一次收集一整层。扁平的拓扑排序是一种正确的顺序,但它抛弃了图存在所要表达的唯一信息。这里在构建时就拒绝循环——而不是在运行时让两个智能体互相等待,什么都不打印。
critical_path() 从中自然流出:plan(2) + research(3) + critique(2) + edit(2) = 9,无论扇出宽度多大。十六个研究员不会比两个研究员更快完成。更多的worker 买到的是覆盖范围,而不是更低的延迟。
Ground truth 是一个固定的十二个事实的列表,所以评分是集合运算——在测量循环中没有 LLM-as-judge,因为一个与被评判对象共享失败模式的评判者不能作为度量工具。这使得 600 个 Crew 可以在 0.16 秒内运行完毕,并给你和我得到的相同的数字。
换一个真正的模型进去只需要一个可调用对象:
def nim_agent(task: Task, context: dict[str, str]) -> str:
... # 在可恢复错误时抛出 AgentFailure,否则返回文本
run(research_crew("your question", n_researchers=4), nim_agent, budget=50_000)
在每个测试设置下,单一智能体在每单位成本的发现量上都获胜。如果预算受限,答案就是不要用 Crew。
Crew 在覆盖范围上获胜——发现了 2.11 倍的内容——而且只有在评论员超过盈亏平衡严格度时才成立。
保持小规模。两到四个。效率峰值在两个,召回天花板在八个到来。
在扇出之前先数一数失败。
这些数字刻画的是这个智能体行为模型,不是 GPT-5。可迁移的是形状:召回率饱和与噪音线性增长,一个必须证明自己价值的评论员,以及随 Crew 增长的失败暴露面。用你自己测量的召回率和幻觉率替换进去,同样的测试会为你的配置给出答案。
Agent Lab Vol 1 第 4 个项目——可克隆运行的完整工具:https://dev48.infy.uk/agentlab.php