做 Agent 开发,我越来越觉得一件事:选范式比选模型重要。

模型能力不够可以换,但范式选错了,整个架构就是别扭的。你见过那种项目吗?明明用 ReAct 就能搞定的事,非得上 Plan-and-Execute,结果每次对话都要等三秒才开始回复,用户体验一塌糊涂。也见过反过来的——复杂任务用纯 ReAct 硬扛,结果 Agent 在那死循环转圈,Token 烧了一堆,啥也没干成。

今天聊聊三代 Agent 范式:ReAct、Plan-and-Execute、Reflexion。不是纯理论,最后会结合我自己的 NVC 项目讲讲选型思路。

第一代:ReAct —— 推理与行动的交替

ReAct 是 2022 年出来的,论文标题就很直白:ReAct: Synergizing Reasoning and Acting in Language Models。核心想法很简单——让 LLM 一边想一边做。

传统做法是两种极端:要么纯推理(Chain-of-Thought,光想不做),要么纯行动(直接调工具,不想为什么调)。ReAct 把两者揉在一起,搞了个循环:

Thought: 我需要查一下今天的天气
Action: search("今天北京天气")
Observation: 北京今天晴,25°C
Thought: 用户问的是要不要带伞,晴天不用带
Action: respond("今天北京是晴天,25度,不用带伞")

画成流程图大概是这样:

用户输入

Thought

Action

Observation

任务完成?

输出结果

三个角色轮流上场:Thought 负责推理下一步该干嘛,Action 负责实际执行(调工具、查数据库、发请求),Observation 把执行结果喂回去。循环往复,直到任务完成。

ReAct 的优势很明显。简单。真的就是简单。你不需要额外的规划模块,不需要复杂的状态管理,一个 while 循环加个 LLM 调用就能跑起来。灵活性也强——Agent 可以根据中间结果随时调整策略,不用被预设计划绑死。可解释性也不错,每一步的 Thought 都在那摆着,出了问题你能回溯。

但用久了你会发现问题。

缺乏全局视野。ReAct 是走一步看一步,Agent 不知道后面还有多少步。就像你去超市买东西,走进去才想起来要买什么,每次都是现想现找。效率低不说,还容易漏东西。

容易死循环。这是我踩过最深的坑。Agent 遇到一个它解决不了的问题,会反复尝试同一个方案。Thought 说"我再试试",Action 执行一遍,Observation 说"还是不行",然后 Thought 又说"我再试试"……Token 哗哗地烧,最后要么触发最大步数限制,要么你手动杀掉。

代码层面,一个典型的 ReAct 循环长这样:

import openai

def react_loop(user_input: str, tools: dict, max_steps: int = 10):
    messages = [{"role": "user", "content": user_input}]

    for step in range(max_steps):
        response = openai.chat.completions.create(
            model="gpt-4",
            messages=messages,
            tools=tools,
        )

        msg = response.choices[0].message

        # 没有工具调用,说明任务完成
        if not msg.tool_calls:
            return msg.content

        # 执行工具调用
        for call in msg.tool_calls:
            tool_name = call.function.name
            tool_args = call.function.arguments
            result = tools[tool_name](**tool_args)

            messages.append(msg)
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": str(result),
            })

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

简单粗暴,能用。但生产环境你得加很多保护:超时、重试、死循环检测、Token 预算控制。

第二代:Plan-and-Execute —— 先想清楚再动手

2023 年出来的 Plan-and-Execute(论文:Plan-and-Solve Prompting),思路是"磨刀不误砍柴工"。先让 LLM 做一个完整计划,然后按计划逐步执行。

Plan:
1. 查询用户的订单列表
2. 筛选出最近30天的订单
3. 计算总金额
4. 生成消费报告

Execute: 按计划逐步执行...

架构上比 ReAct 多了一层:

用户输入

Planner

执行计划

Executor

执行结果

需要调整计划?

所有步骤完成?

输出结果

Planner 和 Executor 是两个角色。Planner 只负责规划,Executor 只负责执行。有些实现里这两个用不同的模型——Planner 用大模型(推理能力强),Executor 用小模型或专用模型(成本低、速度快)。

全局视野是 Plan-and-Execute 最大的优势。Agent 在开始执行之前就知道整个任务要分几步,哪些步骤可以并行,哪些有依赖关系。不会像 ReAct 那样走一步看一步,也不会因为中间某个意外就迷失方向。

减少无效调用也是个实实在在的好处。规划阶段就能判断哪些步骤不需要,避免了 ReAct 里那种"试了才发现不需要"的浪费。

但代价也不小。

灵活性不足。计划一旦定了,执行过程中遇到意外情况,调整成本很高。你去买菜,计划是先买鸡蛋再买牛奶,结果鸡蛋卖完了——ReAct 的反应是"那我先去买牛奶,回头再看看有没有鸭蛋";Plan-and-Execute 的反应是"等等,我得重新规划一下"。

延迟更高。用户说一句话,你得先花两秒钟规划,再开始执行。对于实时对话场景,这两秒钟的等待是致命的。用户会觉得你的 Agent 很"迟钝"。

def plan_and_execute(user_input: str, tools: dict):
    # 第一步:规划
    plan = create_plan(user_input)
    # 这一步就要花 1-2 秒

    # 第二步:逐步执行
    results = []
    for step in plan.steps:
        result = execute_step(step, tools)
        results.append(result)

        # 检查是否需要调整计划
        if needs_replanning(step, result):
            plan = create_plan(user_input, context=results)
            # 重新规划又要花时间

    return synthesize_results(results)

代码结构上看,Plan-and-Execute 比 ReAct 复杂不少。你需要维护一个计划对象,处理计划调整的逻辑,还要考虑计划执行失败的回退策略。

第三代:Reflexion —— 执行后反思

Reflexion 是 2023 年的另一篇论文,核心思想用一句话概括:做完之后想一想,下次怎么能做得更好

执行任务

评估结果

满意?

输出

生成反思

存入记忆

Reflexion 的循环多了一个关键步骤:反思。执行完之后,不是直接结束或者重试,而是先停下来想想——刚才哪里做得不好?为什么会失败?下次应该怎么做?

这个反思会存进一个"记忆库"里。下次再遇到类似问题,Agent 会先查记忆,看看以前踩过什么坑。有点像人类的经验积累——第一次做某件事可能笨手笨脚,但复盘几次之后就越来越熟练。

自我纠错是 Reflexion 最大的亮点。ReAct 和 Plan-and-Execute 都没有这个能力——它们失败了就是失败了,下次遇到同样问题还是会犯同样错误。Reflexion 不一样,它能从失败中学习。

但额外开销是绕不开的问题。每次执行完都要反思,反思本身就要调一次 LLM,还要维护记忆库。对于简单任务,这个开销可能比任务本身还大。

反思质量也不稳定。LLM 的反思不一定靠谱——它可能反思了个寂寞,也可能反思出错误的经验。你得设计好反思的模板和评估标准,不然这个机制就是摆设。

def reflexion_loop(task: str, tools: dict, memory: list, max_trials: int = 3):
    for trial in range(max_trials):
        # 查询历史反思
        relevant_reflections = search_memory(memory, task)

        # 执行任务(带上历史反思)
        result = execute_with_reflection(task, tools, relevant_reflections)

        # 评估结果
        score = evaluate_result(task, result)

        if score >= 0.8:  # 满意
            return result

        # 不满意,生成反思
        reflection = generate_reflection(task, result, score)
        memory.append(reflection)  # 存入记忆

    return result  # 达到最大重试次数

实战:NVC 项目的选型

理论讲完了,聊聊实战。

我在做的 NVC(非暴力沟通)练习平台是一个实时对话助手,帮用户在冲突场景下更好地表达自己。技术栈是 Spring Boot 4.0 + Java 21(虚拟线程)+ Spring AI 2.0 + PostgreSQL + pgvector + Redis。这个场景有几个特点:

  1. 延迟敏感——用户在对话中,等三秒才回复是不可接受的
  2. 任务相对简单——每次交互就是理解和改写一句话,不需要复杂规划
  3. 需要持续对话——不是一次性任务,是多轮对话

这三个特点直接排除了 Plan-and-Execute。两秒的规划延迟在实时对话里就是灾难。

Reflexion 呢?理论上很诱人——Agent 可以从每次对话中学习,越用越好。但问题在于,反思是需要时间的。你不能让用户等你反思完了再回复。

最终我选了 ReAct 作为主体范式,但做了两个关键改造:

改造一:异步反思

反思不能不做,但也不能阻塞主流程。NVC 的方案是通过 EvaluationTriggerHookevaluate_nvc 工具成功后异步触发 Wiki 自动生成:

// EvaluationTriggerHook - 评估触发 Hook
// 在 evaluate_nvc 工具执行成功后,异步触发 Wiki 自动生成
public class EvaluationTriggerHook implements ToolHook {
    @Override
    public void afterToolCall(ToolCall call, ToolResult result, ToolContext context) {
        if ("evaluate_nvc".equals(call.getName()) && result.isSuccess()) {
            // 异步触发 Wiki 生成,不阻塞主流程
            CompletableFuture.runAsync(() -> {
                wikiWriteTool.generateFromEvaluation(result, context);
            }, executorService);
        }
    }
}

EvaluateNvcTool 负责判断用户的表达是否符合非暴力沟通的四步法(观察不评判、表达感受而非指责、说出需要、提出请求)。评估成功后,EvaluationTriggerHook 异步触发 Wiki 生成,存进知识库,供后续 rag_search 检索参考。

用户感受到的是毫秒级的响应,Agent 实际上在后台悄悄积累知识。

改造二:IntentRouter 意图预路由

ReAct 的一个痛点是"走一步看一步",那我能不能在开始之前就多看几步?

NVC 的方案是在 AgentLoop 入口加一个 IntentRouter——在 ReAct 循环开始之前,通过正则匹配快速识别高置信度意图,跳过 LLM 直接执行工具:

// IntentRouter.java
public AgentResult execute(PracticeContext context) {
    // 1. 意图预路由(跳过 ReAct 循环)
    IntentMatch match = intentRouter.match(context.getUserMessage());
    if (match != null && match.getConfidence() >= 0.8) {
        // 高置信度意图 → 直接执行工具,不走 LLM
        return toolExecutor.executeDirectly(match.getToolName(), match.getArgs());
    }

    // 2. 低置信度 → 进入 ReAct 循环
    return reactLoop(context);
}

IntentRouter 的匹配规则:

意图 匹配模式 对应 Tool
profile_update “我是/我的职业是/帮我记录到档案” ProfileUpdateTool
profile_query “看看档案/查看档案” ProfileQueryTool
dashboard_query “练习数据/练习统计” DashboardQueryTool

正则匹配 50ms 内就能完成意图判断。虽然不能完全避免 ReAct 的"走一步看一步"问题,但高频操作(查档案、看数据)直接跳过 LLM,延迟从 2-3 秒降到 500 毫秒。

这三块组合在一起,形成了 NVC 项目的 Agent 架构:

正则命中

未命中

evaluate_nvc 成功后

下次 rag_search

用户输入

IntentRouter

直接执行 Tool

AgentLoop ReAct

实时响应

EvaluationTriggerHook

异步 Wiki 生成

pgvector 知识库

实线是主流程,虚线是异步流程。用户只感受到实线的速度,但虚线在持续积累 NVC 实践知识。

选型建议

最后给个简单的选型参考:

场景 推荐范式 理由
实时对话 ReAct 延迟低,响应快
复杂多步任务 Plan-and-Execute 全局视野,减少浪费
需要持续优化 Reflexion 能从错误中学习
实时 + 持续优化 ReAct + 异步反思 两全其美

没有银弹。选范式就像选工具——锤子很好,但你不能拿它拧螺丝。理解每个范式的优缺点,结合自己的场景做取舍,才是正道。

我的 NVC 项目选 ReAct 不是因为 ReAct 最好,而是因为它最适合。如果你的场景是批量处理文档、生成报告这类非实时任务,Plan-and-Execute 可能是更好的选择。如果你在做代码生成、数学推理这类需要反复试错的任务,Reflexion 的自我纠错能力会让你事半功倍。

关键是想清楚你的场景需要什么,不需要什么。延迟敏感?那就别上 Plan-and-Execute。任务复杂?那就别指望纯 ReAct。需要持续优化?那就考虑 Reflexion 或者异步反思。

选对了范式,后面的路会顺很多。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐