Skip to content

第2章 Agent 架构模式全景

在上一章中我们讨论了为什么需要 Harness 工程——LLM 本身只是引擎,真正让它在复杂任务中跑起来的,是围绕它搭建的架构骨架。本章将系统梳理当前业界主流的六种 Agent 架构模式。对于每一种模式,我们会回答三个问题:它是什么怎么实现什么时候该用

这六种模式并非互斥。在实际系统中,你经常会看到它们的组合——例如一个 Multi-Agent 系统中的每个子 Agent 可能各自采用 ReAct 模式,而整个协作流程则由一个状态机来编排。理解每种模式的核心思想和取舍,是做出正确架构决策的前提。

2.1 Tool-Augmented LLM:最简模式

核心思想

这是最朴素的 Agent 形态:给 LLM 提供一组工具(函数),让它根据用户输入决定是否调用工具、调用哪个工具、传什么参数。整个流程只有一轮决策。

这就是 OpenAI Function Calling 和 Claude Tool Use 最基础的使用方式。很多开发者在第一次接触 Agent 概念时,其实已经在不知不觉中使用了这种模式——你定义了一组函数签名,LLM 选择调用哪个函数,你执行函数并把结果返回给 LLM,然后 LLM 组织最终回答。整个过程非常直观,没有复杂的状态管理,也不需要额外的框架支持。

值得注意的是,即便是这个最简模式,工具定义的质量也至关重要。一个好的工具 schema 应该包含清晰的描述、准确的参数类型以及有意义的示例值。这决定了 LLM 能否正确选择工具并传入合理的参数。我们会在第5章详细讨论工具设计的最佳实践。

伪代码实现

python
def tool_augmented_llm(user_message: str, tools: list[Tool]) -> str:
    """最简单的 Tool-Augmented LLM 模式"""
    # 第一步:把用户消息和工具定义一起发给 LLM
    response = llm.chat(
        messages=[{"role": "user", "content": user_message}],
        tools=[t.schema for t in tools],
    )

    # 第二步:如果 LLM 决定调用工具
    if response.tool_calls:
        tool_results = []
        for call in response.tool_calls:
            tool = find_tool(call.name, tools)
            result = tool.execute(**call.arguments)
            tool_results.append(result)

        # 第三步:把工具结果返回给 LLM,生成最终回答
        final = llm.chat(
            messages=[
                {"role": "user", "content": user_message},
                {"role": "assistant", "content": response},
                {"role": "tool", "content": tool_results},
            ],
        )
        return final.content

    return response.content

适用场景

  • 任务只需要一次工具调用即可完成(查天气、汇率换算、数据库单次查询)
  • 延迟敏感,需要快速响应
  • 工具数量有限(10 个以内效果最好)

局限性

这个模式最大的问题是没有循环。如果第一次工具调用的结果不够好,或者任务需要多步操作,它就束手无策了。举一个具体的例子:用户问"帮我查一下北京明天的天气,如果会下雨就提醒我带伞"。单轮模式可以查到天气,但"判断是否下雨 → 决定是否提醒"这个条件逻辑就需要在一轮调用中全部完成,稍微复杂一点的条件分支就会超出它的能力范围。这正是后续模式要解决的问题。

尽管如此,永远从最简模式开始是一个重要的工程原则。如果你的需求用 Tool-Augmented LLM 就能满足,那就不要引入更复杂的架构。过度设计是 Agent 工程中最常见的错误之一。

2.2 ReAct:推理与行动交织

核心思想

ReAct(Reasoning + Acting)是当前最主流的 Agent 模式,由 Yao 等人于 2022 年提出。其核心是让 LLM 在一个循环中交替进行推理(Thought)行动(Action),每次行动后观察结果(Observation),再决定下一步。

LangChain 的标准 Agent、Claude Code 的主循环,都是这个模式的变体。

相比于 Tool-Augmented LLM,ReAct 的关键进步在于引入了循环。LLM 不再是一次性决策,而是在一个 while 循环中持续运行,直到它判断任务已完成或外部条件触发终止。这个循环就是我们常说的 "Agent Loop"(Agent 主循环),它是几乎所有复杂 Agent 系统的基础构件。

ReAct 模式之所以有效,还有一个重要原因:推理过程(Thought)本身就是一种"自我提示"。当 LLM 在输出中写下"我需要先查询用户的订单信息"时,这段文字会出现在下一轮的输入上下文中,相当于给自己设定了一个明确的短期目标。这种思维链(Chain-of-Thought)与行动的交织,显著提升了 LLM 在多步任务中的表现。

伪代码实现

python
def react_agent(user_message: str, tools: list[Tool], max_steps: int = 10) -> str:
    """ReAct 模式:推理-行动循环"""
    messages = [{"role": "user", "content": user_message}]

    for step in range(max_steps):
        # LLM 同时输出推理过程和工具调用决策
        response = llm.chat(messages=messages, tools=[t.schema for t in tools])
        messages.append({"role": "assistant", "content": response})

        # 如果 LLM 没有调用工具,说明它认为任务完成了
        if not response.tool_calls:
            return response.content

        # 执行所有工具调用,把结果追加到对话历史
        for call in response.tool_calls:
            tool = find_tool(call.name, tools)
            result = tool.execute(**call.arguments)
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": str(result),
            })

    return "达到最大步数限制,任务未完成"

关键设计要素

循环终止条件是 ReAct 模式最重要的工程问题之一。上面的代码用了两个条件:LLM 不再调用工具(自然终止),或达到最大步数(强制终止)。实际系统中还需要考虑:

  • Token 预算耗尽:上下文窗口快满时需要决策——是截断历史、做摘要、还是直接输出当前最好的结果?
  • 错误累积:连续多次工具调用失败时,应该提前退出并告知用户,而不是反复重试。
  • 用户中断:交互式系统(如 Claude Code)允许用户随时中断 Agent 循环。

适用场景

  • 需要多步信息收集和推理的任务
  • 任务路径不确定,需要根据中间结果动态调整
  • 大多数通用型 Agent 的默认选择

局限性

ReAct 的推理是逐步的,每一步只看当前状态决定下一步。对于需要全局规划的复杂任务(比如"把这个 Python 项目重构为微服务架构"),逐步推理容易迷失在细节中,忘记全局目标。这就好比一个人在迷宫中只看脚下一步,虽然每一步都是合理的,但可能走了很多弯路甚至走进死胡同。

另一个实际问题是上下文窗口的压力。ReAct 循环中,每一轮的推理文本和工具返回结果都会累积在对话历史中。当任务需要几十步操作时,对话历史可能会膨胀到数万甚至数十万 Token,逼近模型的上下文窗口限制。如何在循环中管理上下文——何时截断、何时做摘要、何时丢弃旧的工具结果——是 ReAct 模式落地时必须解决的工程问题。我们会在第11章(短期记忆)中深入讨论这个话题。

2.3 Plan-and-Execute:先规划,后执行

核心思想

Plan-and-Execute 模式将任务分为两个阶段:先由一个 Planner 生成完整计划,再由一个 Executor 逐步执行。BabyAGI 和 AutoGPT 是这个模式的早期代表。

伪代码实现

python
def plan_and_execute(user_message: str, tools: list[Tool]) -> str:
    """Plan-and-Execute 模式"""
    # 阶段一:生成计划
    plan = planner_llm.chat(
        messages=[{
            "role": "user",
            "content": f"""请为以下任务制定执行计划,输出为步骤列表。
任务:{user_message}
可用工具:{[t.name for t in tools]}"""
        }]
    )
    steps = parse_plan(plan.content)  # 解析为结构化步骤列表

    # 阶段二:逐步执行
    results = []
    for i, step in enumerate(steps):
        # 每一步都可以是一个小型 ReAct 循环
        step_result = react_agent(
            user_message=f"执行以下步骤:{step}\n\n背景:{user_message}\n已完成步骤:{results}",
            tools=tools,
            max_steps=5,
        )
        results.append({"step": step, "result": step_result})

        # 可选:检查是否需要重新规划
        if should_replan(results, steps[i+1:]):
            remaining_steps = replan(user_message, results, steps[i+1:])
            steps = steps[:i+1] + remaining_steps

    # 阶段三:汇总
    summary = llm.chat(
        messages=[{
            "role": "user",
            "content": f"任务:{user_message}\n执行结果:{results}\n请汇总最终答案。"
        }]
    )
    return summary.content

关键设计要素

**重规划(Replanning)**是这个模式的核心难点。计划一旦生成,执行过程中必然会遇到预期之外的情况——工具返回了意外结果、某个步骤失败了、甚至发现原始计划遗漏了关键步骤。好的 Plan-and-Execute 系统需要在以下时机触发重规划:

  1. 某一步执行失败且重试无效
  2. 某一步的结果与预期严重偏离
  3. 发现了新信息,使得后续步骤不再适用

重规划本身也有成本(额外的 LLM 调用),因此需要设定合理的重规划预算。

适用场景

  • 复杂任务,需要多步骤协调
  • 任务有明确的阶段性(调研 → 设计 → 实现 → 测试)
  • 需要给用户展示执行计划、让用户确认后再执行

局限性

  • 初始规划的质量高度依赖 LLM 对任务的理解,如果理解有偏差,整个计划都会跑偏
  • 两阶段架构引入了额外的延迟和 Token 消耗
  • 对于简单任务来说过于重量级

2.4 Reflexion / Self-Critique:自我评估与迭代

核心思想

Reflexion 模式的核心洞察是:LLM 可以评估自己(或另一个 LLM)的输出质量,并基于评估结果进行改进。这形成了一个"生成 → 评估 → 改进"的迭代循环。

生成初始输出


┌──────────────────────────────────────┐
│  ┌──────────┐    评估意见    ┌─────┐ │
│  │ Critic   │ ────────────→ │生成器│ │
│  │ (评估LLM)│ ←──────────── │(LLM)│ │
│  └──────────┘   改进后输出   └─────┘ │
│              迭代循环                 │
└──────────────────────────────────────┘


质量达标 → 最终输出

伪代码实现

python
def reflexion_agent(
    user_message: str,
    max_iterations: int = 3,
    quality_threshold: float = 0.8,
) -> str:
    """Reflexion 模式:自我评估与迭代改进"""

    # 第一轮:生成初始输出
    output = generator_llm.chat(
        messages=[{"role": "user", "content": user_message}]
    ).content

    for iteration in range(max_iterations):
        # 评估当前输出
        critique = critic_llm.chat(
            messages=[{
                "role": "user",
                "content": f"""请评估以下输出的质量。
原始任务:{user_message}
当前输出:{output}

请给出:
1. 质量评分(0-1)
2. 具体问题列表
3. 改进建议"""
            }]
        ).content

        score = parse_score(critique)
        if score >= quality_threshold:
            break  # 质量达标,退出循环

        # 基于评估意见改进输出
        output = generator_llm.chat(
            messages=[{
                "role": "user",
                "content": f"""请根据以下反馈改进你的输出。
原始任务:{user_message}
当前输出:{output}
评估反馈:{critique}

请输出改进后的版本。"""
            }]
        ).content

    return output

关键设计要素

**Critic 的质量决定了整个系统的上限。**一个糟糕的 Critic 会让系统在错误方向上迭代,越改越差。设计 Critic 时的关键考量:

  • 评估维度要具体:不是笼统地问"好不好",而是从正确性、完整性、格式规范等具体维度评估
  • Critic 和 Generator 可以用不同模型:例如用 Claude Opus 做 Critic,用 Haiku 做 Generator,平衡质量和成本
  • 引入外部验证:对于代码生成任务,可以用单元测试作为客观 Critic,比 LLM 评估更可靠

适用场景

  • 代码生成(写代码 → 跑测试 → 修 bug → 再跑测试)
  • 文案撰写、翻译等需要反复打磨的创作任务
  • 任何有明确质量评估标准的任务

局限性

  • 每轮迭代都消耗额外的 Token,成本是线性增长的
  • LLM 评估 LLM 存在"自我认同偏差"——倾向于认为自己的输出已经足够好
  • 对于没有明确评估标准的开放性任务,Critic 的效果有限

2.5 Multi-Agent:多智能体协作

核心思想

Multi-Agent 模式将一个复杂任务拆分给多个专门化的 Agent,每个 Agent 有自己的角色定义、工具集和系统提示词。Agent 之间通过某种协议进行通信和协作。

CrewAI、AutoGen、以及 Claude Code 的 subagent 系统都属于这一类。

伪代码实现

python
@dataclass
class AgentConfig:
    name: str
    system_prompt: str
    tools: list[Tool]
    model: str  # 不同 Agent 可以用不同模型

def multi_agent_system(user_message: str, agents: list[AgentConfig]) -> str:
    """Multi-Agent 模式:多智能体协作"""

    # 协调者决定任务分配
    coordinator_response = coordinator_llm.chat(
        messages=[{
            "role": "system",
            "content": "你是任务协调者。根据用户任务和可用 Agent 列表,制定协作方案。",
        }, {
            "role": "user",
            "content": f"任务:{user_message}\n可用Agent:{[a.name for a in agents]}",
        }]
    )
    task_assignments = parse_assignments(coordinator_response.content)

    # 按依赖顺序执行各 Agent 的子任务
    context = {}  # 共享上下文
    for assignment in topological_sort(task_assignments):
        agent_config = find_agent(assignment.agent_name, agents)
        # 每个子 Agent 内部可以用 ReAct 模式
        result = react_agent(
            user_message=f"{assignment.task}\n\n共享上下文:{context}",
            tools=agent_config.tools,
            system_prompt=agent_config.system_prompt,
            model=agent_config.model,
        )
        context[assignment.agent_name] = result

    # 协调者汇总各 Agent 的输出
    final = coordinator_llm.chat(
        messages=[{
            "role": "user",
            "content": f"原始任务:{user_message}\n各Agent执行结果:{context}\n请汇总最终答案。",
        }]
    )
    return final.content

协作拓扑

Multi-Agent 系统的通信拓扑有几种典型形式:

星型拓扑(Hub-and-Spoke):一个协调者 Agent 管理所有子 Agent,子 Agent 之间不直接通信。Claude Code 的 subagent 模式就是这种——主 Agent 可以启动子 Agent 处理独立任务,子 Agent 完成后把结果返回给主 Agent。这是最容易理解和调试的拓扑。

链式拓扑(Pipeline):Agent A 的输出是 Agent B 的输入,形成流水线。适合有明确阶段的任务,例如"调研 → 撰写 → 审校"。

网状拓扑(Mesh):Agent 之间可以自由通信。AutoGen 的 GroupChat 模式就是这种——多个 Agent 在一个"聊天室"里讨论,轮流发言。这种拓扑表达力最强,但也最难控制。

适用场景

  • 任务涉及多个差异很大的专业领域
  • 需要"分而治之"的大规模任务
  • 希望通过角色分离来提高输出质量(写代码的和审代码的分开)

局限性

  • 通信开销大:每次 Agent 间传递信息都需要 LLM 调用
  • 协调复杂:任务分配、冲突解决、结果合并都是工程难题
  • 调试困难:问题可能出在任何一个 Agent 或者 Agent 之间的接口上

2.6 State Machine / Graph-based:显式状态转移

核心思想

前面的模式或多或少都有一个隐含假设:流程的走向由 LLM 动态决定。State Machine 模式则反其道而行——用显式的状态图来定义流程,LLM 在每个状态节点内执行具体任务,但状态之间的转移逻辑是预定义的。

LangGraph 是这个模式的代表性框架。

伪代码实现

python
from enum import Enum
from typing import Callable

class State(Enum):
    UNDERSTAND = "understand"
    SEARCH = "search"
    GENERATE_CODE = "generate_code"
    REVIEW = "review"
    RESPOND = "respond"
    END = "end"

@dataclass
class Node:
    state: State
    action: Callable  # 该状态要执行的动作(通常包含 LLM 调用)
    transitions: dict[str, State]  # condition_name → next_state

def state_machine_agent(user_message: str, graph: dict[State, Node]) -> str:
    """State Machine 模式:显式状态转移"""
    current_state = State.UNDERSTAND
    context = {"user_message": user_message, "history": []}

    while current_state != State.END:
        node = graph[current_state]

        # 在当前状态执行动作
        result, condition = node.action(context)
        context["history"].append({
            "state": current_state.value,
            "result": result,
        })

        # 根据条件转移到下一个状态
        next_state = node.transitions.get(condition)
        if next_state is None:
            raise ValueError(f"状态 {current_state} 没有条件 {condition} 的转移")
        current_state = next_state

    return context["history"][-1]["result"]

# 定义状态图
def understand_action(ctx):
    response = llm.chat(messages=[{
        "role": "user",
        "content": f"分析用户意图:{ctx['user_message']}\n输出:need_search / can_answer_directly",
    }])
    intent = parse_intent(response.content)
    return response.content, intent  # (结果, 转移条件)

graph = {
    State.UNDERSTAND: Node(
        state=State.UNDERSTAND,
        action=understand_action,
        transitions={
            "need_search": State.SEARCH,
            "can_answer_directly": State.RESPOND,
        },
    ),
    State.SEARCH: Node(
        state=State.SEARCH,
        action=search_action,
        transitions={"done": State.RESPOND},
    ),
    State.RESPOND: Node(
        state=State.RESPOND,
        action=respond_action,
        transitions={"done": State.END},
    ),
}

关键设计要素

确定性与灵活性的平衡是这个模式的核心张力。状态图越详细,系统行为越可预测,但灵活性越低。反之,状态节点越少、每个节点内的 LLM 自由度越高,系统越灵活但越难控制。

实践中的建议是:把流程层面的确定性交给状态机,把内容层面的灵活性交给 LLM。例如"先搜索再回答"这个流程是确定的,但"搜索什么关键词""如何组织回答"则由 LLM 自由发挥。

适用场景

  • 业务流程有严格的合规要求(金融、医疗)
  • 需要精确控制 Agent 行为,确保不会"跑偏"
  • 多团队协作开发 Agent,需要清晰的接口定义
  • 需要可视化监控 Agent 的执行过程

局限性

  • 设计状态图本身需要对任务有深入理解,前期投入大
  • 难以处理完全开放性的任务("帮我做一个有创意的东西")
  • 状态爆炸问题:复杂任务的状态图可能变得非常庞大

2.7 架构模式横向对比

理解了每种模式的原理之后,关键问题是:在实际项目中,该如何选择?我们从四个维度来对比。

对比表

模式延迟Token 成本可靠性实现复杂度
Tool-Augmented LLM (1-2 轮 LLM 调用)中 (无纠错机制)极低
ReAct中 (3-10 轮)中高 (可以自我纠错)
Plan-and-Execute (规划 + 执行多轮)中 (依赖计划质量)
Reflexion高 (每次迭代翻倍) (显式质量把关)
Multi-Agent极高 (多 Agent 协作)极高中 (协调是薄弱环节)
State Machine中 (取决于图复杂度)极高 (行为可预测)中高

选择决策树

面对一个具体任务,可以按以下顺序思考:

**第一问:任务能在一轮工具调用中完成吗?**如果是,用 Tool-Augmented LLM。不要过度设计。一个查天气的功能不需要 Multi-Agent 架构。

**第二问:任务需要多步操作,但路径大致可预测吗?**如果路径可预测(比如"先查询数据库,再格式化结果,最后发邮件"),用 State Machine。明确的流程用明确的控制。

**第三问:任务需要多步操作,但路径不确定?**这是 ReAct 的主场。大多数通用型 Agent(聊天助手、代码助手)都该从 ReAct 起步。

**第四问:任务很复杂,需要全局规划?**在 ReAct 的基础上加一层 Planner,变成 Plan-and-Execute。但要注意重规划机制,否则一旦计划出错就全盘皆输。

**第五问:输出质量至关重要,可以牺牲速度?**叠加 Reflexion。让 Agent 生成初稿后自我审查并迭代。特别适合代码生成——写完代码跑测试,测试不过就改。

**第六问:任务涉及多个差异很大的领域?**考虑 Multi-Agent。但务必从最简单的星型拓扑开始,不要一上来就搞网状通信。

模式组合的实际案例

以 Claude Code 的架构为例,它实际上组合了多种模式:

  1. 主循环是 ReAct:用户输入 → 推理 → 调用工具(读文件、写文件、执行命令)→ 观察结果 → 继续推理
  2. 子任务用 Multi-Agent:遇到独立的子任务时,可以启动 subagent 并行处理
  3. 隐式的 Reflexion:当工具执行失败(比如代码编译报错),Agent 会分析错误信息并修正,这本质上就是一个 Reflexion 循环
  4. 权限控制是 State Machine:某些操作(如写文件、执行命令)需要用户确认,这部分是确定性的状态转移逻辑

这说明真正的生产系统很少只用一种模式。理解每种模式的核心思想,才能在合适的层次选择合适的模式。

2.8 从架构到工程

本章梳理的六种模式提供了一张"菜单"。但选好了菜式,还需要厨师的手艺。后续章节将深入每一个工程细节:

  • 第3章将深入 Agent 主循环的实现——循环控制、Token 管理、错误恢复
  • 第5-7章将讨论工具设计与编排——工具是 Agent 的"手脚",设计不好再好的架构也白搭
  • 第8-10章将讨论 Prompt 架构——系统提示词如何组织、指令优先级如何处理
  • 第16章将专门深入 Multi-Agent 模式的工程实践

架构模式给出了骨架,而接下来的章节将为这个骨架填充血肉。

本章小结

  1. Tool-Augmented LLM 是最简模式,适合单轮工具调用场景,务必从这里开始,不要过度设计
  2. ReAct 是当前最主流的 Agent 模式,通过推理-行动循环实现多步任务,是大多数通用 Agent 的默认选择
  3. Plan-and-Execute 在 ReAct 之上增加了全局规划,适合复杂的多阶段任务,但要注意重规划机制
  4. Reflexion 通过自我评估和迭代来提升输出质量,特别适合有明确质量标准的任务(如代码生成)
  5. Multi-Agent 通过角色分工来应对跨领域复杂任务,但通信和协调的工程开销不可低估
  6. State Machine 通过显式状态图来确保行为可预测,适合合规性要求高或流程明确的场景
  7. 真实系统通常是多种模式的组合——在不同层次、不同模块选择最合适的模式

基于 VitePress 构建