第 1 章 · 为什么需要多 Agent

一句话结论:多 agent 不是为了炫技,也不是「人多力量大」,而是因为单个 agent 的上下文窗口是一个有硬上限的物理资源。把一个大任务塞进一个 agent 的上下文里,等于让一个人同时记住一本百科全书的每一页再去答题——不是能力问题,是「工作记忆」装不下。


1. 先看一个失败的「单 agent」方案

假设你要 agent 做一份深度研报:《2026 年 AI 视频生成赛道竞争格局》。

最 naive 的写法,是给一个大 agent 一个循环,让它自己一步步干:

def single_agent_run(question):
    ctx = [system_prompt, user(question)]          # 上下文从一句话开始
    for step in range(MAX_STEPS):
        action = llm(ctx)                           # 让模型自己决定下一步
        if action.type == "search":
            result = web_search(action.query)       # 搜索
            ctx.append(result)                      # 把结果塞回上下文
        elif action.type == "browse":
            result = crawl(action.url)
            ctx.append(result)
        elif action.type == "write":
            return compose_report(ctx)              # 最终成稿

这个循环在 demo 里跑得不错。但任务一复杂,你会立刻撞上三堵墙:

  1. 上下文溢出:研报要搜 20 个公司、读 50 篇网页、跑 3 段数据分析。每个 ctx.append(result) 都在往同一个窗口里塞东西。塞到 100k token,模型开始「忘掉」开头——第 1 步搜到的东西,写到第 30 步已经模糊了。这是物理上限,不是 prompt 调优能解决的。
  2. 注意力稀释:一个窗口里既有「我下一步该搜什么」的规划,又有「搜回来的 30 段原文」,还有「报告的格式要求」。模型的分辨率被摊薄到所有内容上,反而容易在关键处犯低级错误。
  3. 耦合到死:搜索逻辑、代码执行、最终成稿全挤在一个 agent 里。想换一个搜索工具,得动整个 prompt;想让代码执行更稳,也没法单独加固。

2. 思考的分叉:面对这个问题,有三条路

方向 A:换更大的上下文窗口的模型,硬扛。

  • 代价:窗口再大也有上限,且 token 越长越贵、越慢、注意力越稀释。这是在「买更大的桶」,不是在解决「为什么要一次性倒进去」。

方向 B:不做 agent 了,写死一个 pipeline(搜索→总结→成稿)。

  • 代价:省心,但丢掉了 agent 最核心的价值——面对未知任务的自适应能力。pipeline 每一步写死了,遇到「需要先算个数才能决定搜什么」这种分支就抓瞎。

方向 C:把任务拆成多个 agent,每个 agent 只扛一个小上下文,靠协调拼出整体。

  • 代价:要解决一个新问题——这些 agent 之间怎么协调。这正是本教程要讲的全部内容。

为什么 DeerFlow(以及几乎所有成熟框架)走 C:

  • 拆开后,每个 agent 的上下文短、专注、可替换。研究员的窗口里只有「搜什么、搜回来的内容」,报告员的窗口里只有「一堆半成品结论、怎么写」。
  • 「协调」虽然难,但它是一次性投入、长期复用的工程问题;而「上下文溢出」是每次运行都会踩、且无法靠单点优化根除的物理问题。用可控的工程复杂度,换掉不可控的物理瓶颈,这笔账划算。

一个生活化类比:你一个人写 5 万字论文,写到一半会忘了开头写过什么;但你把「查资料」「跑数据」「动笔」分给三个人,每人只盯自己的那份材料,最后靠一个「提纲」对齐,三个人反而都能记得住自己那摊子事。拆分的本质,是让每个人都只装「他那一份」上下文。


3. 伪代码:把「单 agent」重构成「多 agent」的骨架

第一步,先把任务从「一个大循环」变成「若干角色 + 一份共享计划」:

class AgentRole:
    """一个角色:有自己的一套工具,只干一件事"""
    def __init__(self, name, tools, system_prompt):
        self.name = name
        self.tools = tools
        self.system_prompt = system_prompt
 
    def run(self, task, ctx):
        # 每个角色在自己的上下文里跑,ctx 是「别人交给我的那一小份」
        return llm(self.system_prompt + task, tools=self.tools)

关键变化不在代码,在心智模型:

  • 单 agent:一个大循环,全装一个 ctx
  • 多 agent:多个角色,各装各的 ctx,靠显式传递对齐

但这里立刻冒出一个新问题——角色之间靠什么「对表」?如果只是让 agent A 把原始输出丢给 agent B,那和单 agent 的 ctx.append 没有本质区别,上下文还是会在传递中越滚越大。

这个问题,就是第 3 章「协调的契约」要解决的。而在这之前,第 2 章得先回答一个更基础的问题:到底该拆成哪几个角色?


思考题

Q:既然上下文窗口是硬上限,那为什么不干脆「拆到极致」——一个 agent 只搜一次网页、另一个 agent 只写一句话,让每个 agent 的上下文小到可以忽略?

参考答案:

拆到极致是另一个极端,会掉进「协调成本 > 单 agent 上下文压力」的坑。核心矛盾是:协调本身也要花钱、也占上下文、也会出错。

  • 每多一个 agent,就多一次「任务分派 + 结果汇总」的往返。这个往返本身要消耗 token、引入延迟、增加出错面(A 没听懂 B 的任务怎么办)。
  • 当一个 agent 干的活小到「搜一次网页」时,它那点上下文压力,远不如「为了让它干这点活所付出的协调成本」大。

所以合理的拆法不是「越细越好」,而是按「职责的自然边界」拆——每个角色是一类内聚的工作(规划 / 检索 / 计算 / 成稿),而不是一个机械的步骤。这正是第 2 章「角色的诞生」要讲的分寸感。