课程0基础Agent开发课 / Prompt工程 / ReAct模式详解-Agent的行动逻辑
— 12 min read

ReAct模式详解-Agent的行动逻辑

> **本文适合谁**

ReAct 模式详解:Agent 的行动逻辑

本文适合谁

准备构建 Agent 系统的开发者。ReAct 是 Agent 决策循环的 Prompt 层实现——"先思考,再行动,观察结果,再思考"的循环。理解这个模式,才能理解 Agent 为什么能完成多步骤任务。


LLM 本身没有访问外部数据的能力。当需求是让 LLM 自动查询某个服务的 P99 延迟(P99 即第 99 百分位响应时间,意思是 99% 的请求都在这个时间内完成,是衡量服务性能的常用指标),然后判断是否需要告警时,会面临一个根本性的问题:LLM 的知识截止到训练数据的日期,Prometheus(一款开源监控系统,用于收集和存储应用运行指标)、Grafana(一款开源数据可视化工具,常配合 Prometheus 展示监控图表)里的实时指标它根本看不到。

问它"我们的订单服务今天 P99 多少毫秒",它要么说不知道,要么编一个数字。这都不是想要的结果。

解决这个问题的方式,就是给 LLM 工具——让它不只是"想",还能"做"。ReAct 就是这套思路的具体实现。

ReAct 之前的世界:纯推理和纯行动的缺陷

未完成

已完成

用户问题

Thought
思考下一步

Action
调用工具

Observation
观察结果

Answer
输出答案

ReAct 模式的核心循环——思考(Thought)、行动(Action)、观察(Observation)三步迭代,直到任务完成

在 ReAct 出现之前,让 LLM 完成需要外部信息的任务,主要有两种思路,它们各自有致命的缺陷。

纯推理(Pure Reasoning):让模型仅靠内部知识推断答案,不提供任何工具。这种方式在知识截止日期内的问题上勉强可用,但面对实时数据(监控指标、天气、股价)或私有数据(公司内部文档、数据库)就彻底失效,模型只能"幻觉"出一个听起来合理但实际错误的答案。

纯行动(Pure Acting):外部程序预先规划好所有步骤,让 LLM 只负责执行特定动作,比如"你现在调用 API A,然后调用 API B,把结果总结"。这种方式的问题是刚性——规划是硬编码的,无法根据实际情况调整。如果 API A 返回了异常数据,程序不知道怎么处理;如果任务需要动态决策("如果延迟高于阈值才查 Runbook"),硬编码的流程做不到。

这两种方式的核心矛盾在于:推理和行动是分离的。要么模型只能想不能做,要么程序只能做但不会根据结果重新想。

ReAct = Reasoning + Acting

ReAct 这个名字来自 2022 年的一篇论文,全称"Synergizing Reasoning and Acting in Language Models"(让语言模型的推理与行动协同工作)。

ReAct 的核心创新是:把推理和行动交织在一起,形成"想一步、做一步、再想、再做"的循环。这个设计解决了纯推理和纯行动各自的缺陷:

  • 每次行动之前都有推理,所以行动是动态决策的,不是硬编码的
  • 每次行动之后都有观察,观察结果立即成为下一轮推理的输入,所以模型能根据实际情况调整策略
  • 如果某个工具调用失败,模型能感知到(Observation 会包含错误信息),并推理出备选方案

这种"先想再做,做完再想"的结构比"一次性规划所有步骤"更鲁棒,原因在于复杂任务的完整信息在执行前是不可知的。只有在查到 Prometheus 数据之后,模型才能知道下一步是直接告警还是先查 Runbook——这个决策依赖实际的查询结果,无法在执行前预先规划。

循环的每一轮包含三步:

  • Thought(思考):模型分析当前情况,决定下一步做什么
  • Action(行动):调用一个工具,比如搜索、查数据库、执行代码
  • Observation(观察):拿到工具的返回结果

然后进入下一轮:根据 Observation 继续 Thought,决定是再调用工具,还是已经有足够信息可以回答用户了。

这个循环一直持续到模型认为任务完成,输出最终答案。

一个完整的执行过程

以下用一个具体例子展示这个循环是怎么运转的。

用户问题:Anthropic 最新发布的模型是什么,它的上下文窗口有多大?


Thought 1:用户在问 Anthropic 最新的模型。这是实时信息,训练数据可能不包含最新发布的消息,需要搜索。

Action 1:调用搜索工具,查询"Anthropic latest model release 2026"

Observation 1:搜索返回了几条新闻,提到 Claude Opus 4 系列,支持 200K token 上下文。


Thought 2:已经拿到了足够的信息。用户问了两个问题:最新模型名称,和上下文窗口大小。搜索结果已经覆盖了这两点,不需要再查了,可以直接回答。

Final Answer:Anthropic 目前最新的旗舰模型是 Claude Opus 4 系列(claude-opus-4-6),支持 200K token 的上下文窗口。具体最新版本请以官方文档为准,因为模型持续迭代。


注意 Thought 2 里的决策过程:模型评估了已有信息是否足够,决定不再调用更多工具。这个"够了,可以回答了"的判断完全由模型自己做出,不是外部程序决定的。这就是 ReAct 和普通流程编排的本质区别。

ReAct 在 LangChain 中的实现

LangChain(一个流行的 Python 框架,专门用来构建 AI Agent 应用)对 ReAct 有内置支持,入口是 create_react_agent

先来看工具怎么定义:

python
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub

# 定义工具
@tool
def query_prometheus(metric_query: str) -> str:
    """查询 Prometheus 监控指标。

    使用 PromQL(Prometheus Query Language,Prometheus 的查询语言)语法查询实时监控数据,包括请求延迟、错误率、QPS(每秒请求数,衡量服务处理能力的指标)等。
    适用场景:查看某个服务当前或历史的性能指标。

    示例查询:
    - histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
    - sum(rate(http_requests_total{status="500"}[5m]))

    返回:指标的当前值或时间序列数据。
    """
    # 实际实现里这里调用 Prometheus HTTP API
    # 这里用伪代码示意
    import requests
    response = requests.get(
        "http://prometheus:9090/api/v1/query",
        params={"query": metric_query}
    )
    return str(response.json()["data"]["result"])


@tool
def search_runbook(service_name: str, error_type: str) -> str:
    """在团队 Runbook 中搜索故障处理手册。

    当服务出现告警或异常时,查找对应的处理步骤。

    参数:
    - service_name:服务名称,如 order-service、payment-service
    - error_type:问题类型,如 high_latency、high_error_rate、oom

    返回:匹配的 Runbook 内容,包含排查步骤和处理建议。
    """
    # 实际实现里这里查 Confluence(一款常见的企业内部知识库和文档协作平台)或内部文档系统
    return f"找到 {service_name}{error_type} 处理手册:1. 检查 GC 日志 2. ..."

注意 @tool 装饰器下面的 docstring。这不是给程序员看的注释,是给模型看的说明书。模型完全靠 docstring 决定要不要用这个工具、怎么用这个工具。 docstring 写得不清楚,模型就不知道什么情况下该调用它,或者用错参数。

然后组装 Agent(验证目的:确认 ReAct 循环能正确触发,Thought/Action/Observation 链路完整):

python
# 加载 ReAct Prompt 模板(langchain hub 上有现成的)
prompt = hub.pull("hwchase17/react")

# 初始化模型
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# 创建 Agent
tools = [query_prometheus, search_runbook]
agent = create_react_agent(llm, tools, prompt)

# 创建 AgentExecutor(负责循环执行)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,        # 打开后可以看到每一步的 Thought/Action/Observation
    max_iterations=5,    # 最多循环 5 次,防止死循环
    handle_parsing_errors=True  # 工具调用格式出错时不崩溃
)

# 运行
result = agent_executor.invoke({
    "input": "检查 order-service 最近 5 分钟的 P99 延迟,如果超过 500ms,帮我查一下对应的处理手册"
})

print(result["output"])

verbose=True 打开,能看到完整的 Thought/Action/Observation 链,对理解模型在干什么非常有帮助,调试时基本靠它。

工具设计的关键细节

工具的 docstring 是整个 Agent 能不能正常工作的核心。常见的问题包括以下几类。

工具名字太相似。 如果有两个工具,一个叫 get_order_status,一个叫 query_order_info,模型经常选错。命名要有明确区分,或者在 docstring 里写清楚"这个工具只用于查订单状态,不包含订单详情"。

docstring 太简洁。 "查询数据库"这种描述完全不够用。要写清楚:这个工具干什么、接受什么参数、返回什么格式、适用什么场景。

工具报错没处理。 工具调用失败时,如果直接抛出 Exception,整个 Agent 就挂了。应该在工具函数内部 try-catch,把错误信息作为字符串返回给模型,让模型决定怎么处理,比如换一个工具或者告诉用户查询失败。

python
@tool
def query_prometheus(metric_query: str) -> str:
    """查询 Prometheus 监控指标。..."""
    try:
        response = requests.get(
            "http://prometheus:9090/api/v1/query",
            params={"query": metric_query},
            timeout=5
        )
        response.raise_for_status()
        return str(response.json()["data"]["result"])
    except requests.Timeout:
        return "错误:查询超时,Prometheus 可能不可用"
    except Exception as e:
        return f"错误:查询失败,原因:{str(e)}"

让工具返回有意义的错误信息,而不是让异常冒泡出去,这是写 Agent 工具的基本原则。这样做的原因是:错误信息会作为 Observation 进入下一轮 Thought,模型才能推理出如何应对失败情况。如果直接抛出异常,循环就断了。

ReAct 的局限

单线程,顺序执行。 ReAct 的每一步必须等上一步完成才能开始。如果一个任务需要同时查三个数据源,ReAct 会一个一个查,总时间是三个查询时间之和。没有并行能力。

任务越长越容易失控。 循环次数多了,模型上下文越来越长,注意力逐渐分散,可能忘记最初的目标,或者陷入重复调用同一个工具的循环。max_iterations 是必须设置的保险丝。

复杂任务需要 LangGraph。 LangGraph 是 LangChain 的扩展框架,允许用图(节点和连线)来描述复杂的 Agent 流程。如果任务有分支逻辑(比如"如果 A 失败就走 B 路径")、并行子任务、或者需要人工介入,ReAct 的单线性结构就撑不住了,需要 LangGraph 的状态图来编排。

但在这些局限之外,ReAct 仍然是最简单、最常用的 Agent 模式。大多数实际场景——查数据、调 API、搜索文档、执行操作——用 ReAct 都够用。

没有 ReAct,LLM 是一个封闭系统,一旦问题涉及实时信息或外部操作,它就无能为力。

ReAct 打开了一扇门。通过 Thought-Action-Observation 这个循环,LLM 第一次有了感知和操作外部世界的能力——查实时数据、调用 API、执行代码、操作文件,从"知识库"变成了"能干活的助手"。

后面所有更复杂的 Agent 架构——Multi-Agent(多智能体,多个 AI Agent 协作的系统)、LangGraph、甚至 AutoGPT(早期著名的自主 AI Agent 项目,能自动分解任务并循环执行)——都是在 ReAct 这个基础思路上演化来的。把 ReAct 搞透,后面的东西看起来就不神秘了。

本页目录