能用 workflow 解决的事,别用 agent;能用 single agent 的事,别用 multi-agent。
过去一年「agent」被滥用到几乎失去意义——任何带 LLM 调用的脚本都自称 agent,任何 demo 都堆 3 个角色互相 debate。但当你真的把这些跑进生产,会发现一条残酷的曲线:agent 化程度越高,可靠性越低、延迟越大、token 成本越爆。Anthropic 在 Building Effective Agents 里给出了那条被反复引用的结论——「Find the simplest solution possible, and only increase complexity when needed」。这一期不讲哪个框架更好(LangGraph / CrewAI / AutoGen 谁更香这种事毫无意义),讲真正决定可靠性的四件事:什么时候 不 该用 agent、ReAct/Plan/Reflexion 三种主流 loop 的真实差异、为什么 multi-agent 几乎都是反模式、以及让 agent 项目挂掉的五种典型失败模式与诊断方法。
Anthropic Building Effective Agents 给出了一个被严重低估的二分法:
区别不在「用没用 tool」,而在控制流由谁决定。一个跑 5 步 prompt chain 的 RAG 是 workflow,不是 agent;一个能自己决定要不要再查一次资料、要不要换工具的才是 agent。
为什么这件事重要?因为 agent 的可靠性大致等于「每一步成功率」的 N 次方。每步 95% 成功的 agent,跑 10 步只剩 60%;跑 20 步只剩 36%。workflow 把不必要的「自决」固化成代码,等于把指数衰减压成线性。同样的任务,能写成 workflow 就不要写成 agent——这不是性能洁癖,是 reliability 的物理事实。
什么时候 该 用 agent?Anthropic 的判断标准只有一条:任务的步骤数和路径不可预先枚举。比如「修这个 bug」——你不知道要读几个文件、改几处;「研究这个开源项目」——你不知道要 fetch 多少链接。其余 90% 的场景(提取信息 + 改写 + 校验 + 存库;分类 + 路由;翻译 + 润色 + 翻回去对照)都是 workflow。
「给一份会议录音生成结构化纪要 + 待办 + 关键决策」——是 workflow,不是 agent:
# —— 这是 workflow(5 行业务代码 + 3 次 LLM 调用) ——
transcript = whisper.transcribe(audio_path)
summary = llm(SUMMARY_PROMPT, transcript)
todos = llm(TODO_PROMPT, transcript)
decisions = llm(DECISION_PROMPT, transcript)
return {"summary":summary, "todos":todos, "decisions":decisions}
# 步数固定 = 3, 不需要 agent loop / tool selection / state machine
# p99 延迟 = 3 × LLM 调用上界, 可缓存可批量可并行
「修这个 issue:用户报告 dashboard 在 Safari 上偶发 white screen」——是 agent:
# —— 这是 agent(路径不可枚举) ——
# 可能的步骤:
# 读 issue → grep dashboard 入口 → 读 entry → 看 git log →
# 找 Safari 相关 polyfill → 跑测试 → 改 → 再跑 → 写 PR
# 但「可能」二字意味着具体走哪条路要看每步看到什么
agent(task=issue_body, tools=[read,grep,bash,edit,write,fetch])
一个简单决策口诀:能在白板上把所有可能路径画出来,就是 workflow;画不出来,才是 agent。
三种最常见的 agent loop,被 LangChain / LlamaIndex 文档讲得像三种「框架选择」,其实是三种不同复杂度场景的不同收益曲线。理解它们什么时候 work、什么时候不 work,比记住它们的名字重要 100 倍。
ReAct(Yao et al. 2022, arXiv:2210.03629)——Thought / Action / Observation 三段交替,每一步看完结果再决定下一步。优点:简单、低延迟、容错好(错了下一步可纠)。Claude Code、Cursor 的 agent loop 本质都是 ReAct 的变体。这是默认应该选的 pattern。
Plan-and-Execute(Wang et al. 2023, arXiv:2305.04091)——先用一次 LLM 调用生成完整计划(List[step]),再逐步执行。优点:步骤之间可并行、计划阶段可被人 review、token 利用率高。缺点:计划基于不完整信息,执行中遇到意外只能强行走完或全盘 replan。适用:步骤多(≥ 8)、每步代价高(涉及钱 / 时间)、且初始信息足够支持靠谱 plan 的场景。
Reflexion(Shinn et al. 2023, arXiv:2303.11366)——做完后让 LLM 反思「这次哪里没做好」,把反思写进 memory,下一轮带着反思重新做。它在论文里在 HumanEval / AlfWorld 上有明显提升,但前提是任务结果可验证(代码能跑测试、游戏有 score)。在「写文章」「客服对话」这类没 ground truth 的任务上,反思容易变成自我感觉良好的 noise——模型反思出来的「下次该这样」很可能是错的。
# —— ReAct 骨架(你自己 harness 的 hot path) ——
while True:
r = llm(system=SYS, tools=TOOLS, messages=msgs)
msgs.append({"role":"assistant","content":r.content})
if r.stop_reason == "end_turn": break
results = [run_tool(b) for b in r.content if b.type=="tool_use"]
msgs.append({"role":"user","content":results})
# —— Plan-and-Execute 骨架 ——
plan = llm(PLAN_PROMPT, task) # List[Step], step 间标 deps
for wave in topo_sort(plan): # 按依赖分波
await asyncio.gather(*[execute(s) for s in wave])
if not satisfied(plan.goal):
plan = llm(REPLAN_PROMPT, task, history) # 整体 replan, 不是局部修
# —— Reflexion 骨架(只在 verifier 存在时用) ——
for attempt in range(MAX_ATTEMPTS):
traj = react_agent(task, memory=reflections)
result = verifier(traj) # 例:跑测试 / 校验 schema
if result.passed: return traj
reflections.append(llm(REFLECT_PROMPT, traj, result.errors))
选型决策树:
2023-2024 年 AutoGen / CrewAI / MetaGPT 把 multi-agent 炒成「未来」,2025 年的工程现实是:大多数 multi-agent 系统的产出,不如同一个模型 + 一个写得好的 system prompt + 一组好工具。原因相当物理:
那 multi-agent 什么时候真的值?Anthropic 给出的具体场景特征:
这也是为什么 Claude Code 的 Task tool 就是 orchestrator-worker:主 agent 调 Task 启动子 agent 做独立调研,子 agent 完成后返回一段总结。不是让子 agent 互相对话。
「调研 5 家竞品 + 写一份对比报告」——这是 multi-agent 真正赢的场景:
# —— Orchestrator-Worker:lead 拆任务, workers 并行调研, lead 汇总 ——
async def research_orchestrator(query):
plan = lead_agent(f"Break this research into 3-6 independent subtopics: {query}")
# plan = ["company A pricing", "company B product", ...]
subreports = await asyncio.gather(*[
worker_agent(topic, tools=[web_search, fetch], max_iters=15)
for topic in plan
]) # 每个 worker 自己一个 context window, 不互相通信
return lead_agent(f"Synthesize into a report: {subreports}")
# 收益: 每个 worker 自己处理 50k token 的 web 内容
# 单 agent 串行做要 ~250k context, 不仅慢, 还 lost-in-the-middle
# 分给 5 个 worker 后, lead 只看 5 段总结 ~5k token, 高质量汇总
「写一个函数」——这是 multi-agent 不该出现的场景:
# BAD: "coder + reviewer + tester" 三 agent debate
# - 3× token, 3× latency, 错误叠加
# - reviewer 看不到 coder 没说出的上下文
# - tester 跑测试这步本来就不是 LLM, 是 tool
# GOOD: 1 个 agent + 测试 tool + 好 system prompt
agent(task, tools=[read, edit, run_tests],
system="Write code, run tests, fix until green.")
# 1× token, 用 verifier (tests) 替代 reviewer/tester agent
判断 multi-agent 该不该上的口诀:「能不能让 worker 之间不说话」?能 → orchestrator-worker 可上;不能(worker 必须 debate / 协作 / 互相依赖)→ 改回 single agent 或拆 workflow。
当你跑过几个真实 agent 项目,会发现失败长得很像——它们反复以以下 5 种「死法」出现。把它们做成内部 incident 分类,能让你的迭代速度翻倍。
这 5 种失败几乎都不会在 dev 时被发现——开发者跑 3 个 happy path 就部署,生产里第一周就全部撞见。诊断不靠看 log,靠 trace:每个 turn 记 (tool_name, args_hash, result_hash, token_delta, elapsed),用脚本扫异常 pattern。
给你的 agent harness 加一个 30 行的 failure detector:
from hashlib import md5
def hash_call(name, args):
return md5(f"{name}|{sorted(args.items())}".encode()).hexdigest()[:8]
class AgentMonitor:
def __init__(self):
self.history = [] # list of (turn, name, args_hash, result_hash)
self.token_path = []
def record(self, turn, name, args, result, tokens):
ah, rh = hash_call(name, args), md5(str(result).encode()).hexdigest()[:8]
self.history.append((turn, name, ah, rh))
self.token_path.append(tokens)
def check(self):
h = self.history
# 1. Loop lock: 同 (name, args_hash) 连续 ≥ 3 次, 且 result_hash 几乎不变
if len(h) >= 3 and len(set((x[1],x[2]) for x in h[-3:])) == 1:
return "LOOP_LOCK"
# 2. Tool thrashing: 最近 5 轮调了 4+ 个不同 tool
if len(h) >= 5 and len(set(x[1] for x in h[-5:])) >= 4:
return "TOOL_THRASHING"
# 3. Context bloat: token 占用单调上升且 > 80% 窗口
if self.token_path and self.token_path[-1] > 0.8 * MAX_CTX:
return "CONTEXT_BLOAT"
return None
# 在 harness 主循环里调用
if (state := monitor.check()):
if state == "LOOP_LOCK": inject_msg("You're repeating. Try a different approach or ask the user.")
elif state == "TOOL_THRASHING": inject_msg("Stop trying tools. State your hypothesis first.")
elif state == "CONTEXT_BLOAT": compact_history(msgs)
对应防御策略:
把这 4 节浓缩成一份「开 agent 项目前先过」的 checklist。下次有人喊「我想做个 AI agent」时拿这张表问回去:
能在这 7 步里诚实回答「不需要 agent / single agent 够 / 不需要 multi-agent」是顶级工程素养。Anthropic / Cognition / Cursor 工程团队的共识,归结起来就是一句话——complexity is a tax, pay only when it's worth it。