【Agentic RL 专题】五、深入浅出Reasoning and Acting (ReAct)

🧔 这里是九年义务漏网鲨鱼,研究生在读,主要研究方向是人脸伪造检测,长期致力于研究多模态大模型技术;国家奖学金获得者,国家级大创项目一项,发明专利一篇,多篇论文在投,蓝桥杯国家级奖项、妈妈杯一等奖。

✍ 博客主要内容为大模型技术的学习以及相关面经,本人已得到B站、百度、唯品会等多段多模态大模型的实习offer,为了能够紧跟前沿知识,决定写一个"从零学习 RL"主题的专栏。这个专栏将记录我个人的主观学习过程,因此会存在错误,若有出错,欢迎大家在评论区帮助我指出。除此之外,博客内容也会分享一些我在本科期间的一些知识以及项目经验。

🌎 Github仓库地址:Baby Awesome Reinforcement Learning for LLMs and Agentic AI

📩 有兴趣合作的研究者可以联系我:yirongzzz@163.com

文章目录

    • [🧠 前言:从 CoT 到 ReAct](#🧠 前言:从 CoT 到 ReAct)
    • [一、ReAct 范式长什么样?](#一、ReAct 范式长什么样?)
    • [二、从 CoT 到 ReAct](#二、从 CoT 到 ReAct)
      • [三、手撕"迷你 ReAct 循环"](#三、手撕“迷你 ReAct 循环”)
    • [四、 LangChain 框架实现ReAct Agent](#四、 LangChain 框架实现ReAct Agent)
      • [3.1 定义 LLM 与工具](#3.1 定义 LLM 与工具)
      • [3.2 ReAct Prompt](#3.2 ReAct Prompt)
      • [3.3 使用 `create_react_agent` 构建 Agent + Executor](#3.3 使用 create_react_agent 构建 Agent + Executor)
      • [3.4 测试](#3.4 测试)
    • 四、ReAct的发展
      • [🧠 4.1 在 ReAct 之上叠"反思(Reflexion)"](#🧠 4.1 在 ReAct 之上叠“反思(Reflexion)”)
      • [🧭 4.2 与「规划(Planning)」结合](#🧭 4.2 与「规划(Planning)」结合)
    • 五、总结

🧠 前言:从 CoT 到 ReAct

在只有基础对话能力的阶段,大模型更多像一个"一次性回答机":

User: 问题 LLM : 一次性生成答案

即便我们加上了 Memory、RAG,智能体也只是多了"能记"和"会查":

  • Memory:记住过去发生了什么(多轮对话、历史任务状态)

  • RAG:在回答前去查一查知识库或互联网

但这仍然是"问一答一"的模式,缺少真正的多步决策与行动能力。

ReAct(Reasoning + Acting) 正是为了解决这个问题提出的:

在推理过程中,显式地交替输出"思考内容(Thought)"和"行动指令(Action)",再利用环境反馈(Observation)更新后续推理。

一句话概括:
ReAct 让LLM一边自言自语地推理,一边调用工具,是一种更加高级prompting技术

一、ReAct 范式长什么样?

原论文《ReAct: Synergizing Reasoning and Acting in Language Models》采用的典型轨迹格式如下:

✍ 论文地址:ReAct: Synergizing Reasoning and Acting in Language Models

python 复制代码
Question: (用户问题)

Thought 1: 我需要先查一下 X 的相关背景。
Action 1: search["X 的维基百科页面"]

Observation 1: (搜索工具返回的摘要文本)

Thought 2: 现在我知道了 X 的定义,还需要确认 Y。
Action 2: lookup["Y 相关条目"]

Observation 2: (查具体条目返回的信息)

Thought 3: 根据 Observation 1 和 2,可以得出结论......
Final Answer: (给用户的最终回答)

可以发现,在思考的过程中的关键元素如表所示:

元素 是否改变环境 主要作用 示例
Thought ❌ 不改变环境 作为"内心独白",用于规划下一步、整理当前已知信息、解释为什么要调用某个工具 "我需要先查一下 2024 年奥运会的举办城市。"
Action ✅ 通过调用工具/环境间接改变环境 发出结构化"命令",触发具体操作(检索、查表、移动等) search["query"]lookup["entity"]move["north"](如 ALFWorld 中的移动)
Observation ✅ 环境产生的结果 记录环境或工具对 Action 的反馈,为下一步 Thought 提供依据 搜索结果文本、API 返回的 JSON、环境状态描述
Final Answer ❌ 自身不再行动(但对用户是最终输出) 标志推理/行动序列结束,给出最终对用户的回答 Final Answer: 2024 年奥运会举办城市是巴黎...

LLM 的工作就变成了:

在 Thought 里"想清楚下一步要做什么",在 Action 里"实际动手",通过 Observation 不断修正推理轨迹。


二、从 CoT 到 ReAct

先来看一下CoT(Chain-of-Thought) 的流程形式:

python 复制代码
Q: 2 + 3 * 4 = ?
A:
Step 1: 先算乘法 3*4 = 12
Step 2: 再算加法 2 + 12 = 14
所以答案是 14

链式思考的出现大大提升了大模型的数学推理能力,他并不是重新训练大模型来得到这种能力的,而是通过prompting来挖掘大模型本就有的推理能力。

CoT的基础上,ReAct 的核心变化就是:在 "思考链(Thought)" 中间插入 "动作(Action)" 和 "环境反馈(Observation)", 然后多轮循环。具体流程如下:

step 1. 在 Prompt 里定义协议

你是一个可以一边思考一边操作工具的助手。

你必须严格按下面格式输出(非常重要):

Question: {用户问题}

Thought: ...

Action: 工具名["参数"]

Observation: ...

...

当你已经可以回答用户问题时,输出:

Final Answer: ...

step 2. 循环控制伪代码

python 复制代码
while True:
    llm_output = call_llm(prompt)

    if "Final Answer:" in llm_output:
        提取最终答案,结束

    action = parse_action(llm_output)         # 找 Action: xxx["..."]

    obs = run_tool(action)                    # 真正调用工具
    prompt += llm_output + f"\nObservation: {obs}\n"

学到这里,我们可以说,ReAct其实本质就是一种更加"高级 prompting + 外层控制逻辑"

三、手撕"迷你 ReAct 循环"

为了帮助读者更好的理解ReAct·,我们先手撕简化版 ReAct 循环(伪代码,逻辑关键):

step 0. 工具自定义

python 复制代码
import re
from typing import Dict, Callable

# 假设这是一个Chat LLM 接口
def call_llm(prompt: str) -> str:
    pass

TOOLS: Dict[str, Callable[[str], str]] = {}

def register_tool(name: str):
    def decorator(fn):
        TOOLS[name] = fn
        return fn
    return decorator

@register_tool("calculator")
def calculator(expr: str) -> str:
    """计算简单数学表达式"""
    try:
        return str(eval(expr))
    except Exception as e:
        return f"计算错误: {e}"

step 1. 在 Prompt 里定义协议

python 复制代码
REACT_SYSTEM_PROMPT = """
你是一个可以一边思考一边使用工具的助手。
交互格式如下:

Thought: 先解释你在想什么
Action: 工具名["参数"]
Observation: 我会用工具返回的结果填在这里
...
最后当你可以回答用户问题时,请输出:
Final Answer: 给出最终回答

当前可用工具:
- calculator: 计算数学表达式,格式如 calculator["1+2*3"]
"""

step 2. 循环控制

python 复制代码
def react_loop(question: str, max_steps: int = 5):
    scratchpad = ""
    for step in range(1, max_steps + 1):
        prompt = (
            REACT_SYSTEM_PROMPT
            + f"\nQuestion: {question}\n"
            + scratchpad
            + "\n请给出下一步 Thought / Action 或 Final Answer:"
        )
        llm_output = call_llm(prompt)

        # 1. 先看是否已经给出 Final Answer
        if "Final Answer:" in llm_output:
            answer = llm_output.split("Final Answer:")[1].strip()
            print("✅ Final Answer:", answer)
            return answer

        # 2. 解析 Action
        action_match = re.search(r"Action:\s*(\w+)\[\"(.*)\"\]", llm_output)
        if action_match:
            tool_name, tool_input = action_match.groups()
            if tool_name not in TOOLS:
                observation = f"工具 {tool_name} 不存在。"
            else:
                observation = TOOLS[tool_name](tool_input)

            # 把这一步的 Thought / Action / Observation 追加到 scratchpad
            scratchpad += (
                f"\n{llm_output.strip()}\n"
                f"Observation: {observation}\n"
            )
        else:
            # 如果没解析出 Action,只把 Thought 拼进去,继续下一轮
            scratchpad += f"\n{llm_output.strip()}\n"

    print("⚠️ 达到最大步数仍未得到 Final Answer")
    return None
  1. 拼 prompt:系统提示 + 问题 + 之前的 Thought/Action/Observation 轨迹;

  2. 让 LLM 输出下一步

    • 要么继续 Thought + Action;
    • 要么直接给出 Final Answer;
  3. 解析 Action,调用对应工具,补上 Observation;

  4. 循环直到终止

LangChain 做的事情,本质上就是把这个"循环 + 解析 + 工具调用"封装成一个可复用的 LCEL 运行图,并提供不少内置工具和 Prompt 模板。


四、 LangChain 框架实现ReAct Agent

⚙️ 依赖安装(示例环境)

cmd 复制代码
pip install -U langchain langchain-openai langchain-community duckduckgo-search`

3.1 定义 LLM 与工具

python 复制代码
# 带搜索 + 计算器的 ReAct Agent 示例
import os
from langchain_openai import ChatOpenAI
from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.tools import tool

# 远程调用,需要KEY
os.environ["OPENAI_API_KEY"] = "你的 key"

# 1️⃣ LLM:底层使用 Chat 接口(如 GPT-4 / DeepSeek 等)
llm = ChatOpenAI(
    model="gpt-4o-mini",   # 或者 deepseek/chat 等
    temperature=0
)

# 本地定义llm
#from langchain_community.chat_models import ChatOllama

#llm = ChatOllama(
#    model="qwen2.5:7b-instruct",  # 本地 ollama 已经拉好的模型
#    temperature=0
#)


# 2️⃣ 定义一个自定义工具:计算器
@tool
def calculator(expression: str) -> str:
    """计算一个数学表达式,例如 '1+2*3'。"""  #docstring
    try:
        return str(eval(expression))
    except Exception as e:
        return f"计算出错: {e}"

#内置的 DuckDuckGo 搜索工具,简单的网页搜索工具,无需key
search = DuckDuckGoSearchRun()

tools = [calculator, search]

@tool 装饰器会自动把 Python 函数包装成 LangChain 的 BaseTool 对象;

3.2 ReAct Prompt

在第三章我们提过,需要在 Prompt 里定义协议;

而在LangChain Hub 中这些事已经不需要我们做了, 因为已经有现成的 ReAct Prompt(hwchase17/react)以直接拿来用:[

python 复制代码
from langchain import hub

# 从 LangChain Hub 拉取 ReAct Prompt
prompt = hub.pull("hwchase17/react")

print(prompt.template[:400], "...")

你打印一下会看到类似结构(英文),明确告诉 LLM 要循环输出 Thought / Action / Observation,直到给出 Final Answer。

复制代码
Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {input}
Thought:

3.3 使用 create_react_agent 构建 Agent + Executor

LangChain 提供了一个封装好的工厂函数:create_react_agent,专门用来构造 ReAct 风格的 Agent。

python 复制代码
from langchain.agents import create_react_agent, AgentExecutor

# 5️⃣ 构建 ReAct Agent(本质是一个 LCEL runnable)
agent = create_react_agent(
    llm=llm,
    tools=tools,
    prompt=prompt,
)

# 6️⃣ 用 AgentExecutor 包一层循环执行逻辑
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,        # 打印中间 Thought / Action / Observation 轨迹
    max_iterations=5,    # 最多迭代 5 轮
    handle_parsing_errors=True,
)

3.4 测试

python 复制代码
query = "请查一下 2024 年奥运会举办城市,然后算一下这个城市名称的长度平方是多少?"

result = agent_executor.invoke({"input": query})
print("\n🟢 最终回答:", result["output"])

verbose=True 的情况下,你会在控制台看到类似这样的轨迹(示意):

复制代码
Thought: 我需要先查一下 2024 年奥运会的举办城市。
Action: duckduckgo_search
Action Input: "2024 Summer Olympics host city"

Observation: The 2024 Summer Olympics were held in Paris, France. ...

Thought: 我知道了举办城市是 Paris。现在需要计算城市名称长度的平方。
Action: calculator
Action Input: "len('Paris')**2"

Observation: 25

Thought: 我已经知道了最终答案。
Final Answer: 2024 年奥运会举办城市是巴黎(Paris),
城市名 "Paris" 的长度为 5,5 的平方为 25,因此答案是 25。

分析一下:

复制代码
1. 第一步 Thought:说明"先查办奥运会城市";
2. 第一步 Action:调用 `duckduckgo_search`;    
3. Observation 包含了搜索结果(告诉你是 Paris);    
4. 第二步 Thought:决定用 calculator 计算长度平方;    
5. 第二步 Action:调用 `calculator`;    
6. Observation 给出 25;    
7. 最后 Thought + Final Answer:给出解释 + 最终结论。

可以发现,这个过程和我们上面手写的 ReAct loop 是一一对应的,只不过 LangChain 帮我们封装了细节。

四、ReAct的发展

🧠 4.1 在 ReAct 之上叠"反思(Reflexion)"

在 ReAct 的基础循环上,再叠一层"学习自己的经验"的外环。比如 Reflexion 让 Agent 在失败后,用自然语言总结"刚才哪一步错了,下次应该怎么改",再写入 Memory,下一轮参考。Reflexion: Language Agents with Verbal Reinforcement Learning

🧭 4.2 与「规划(Planning)」结合

原生 ReAct 偏"短视"------每一步都是 "想一点 → 做一步",对需要长远规划、多阶段子任务的场景不够强。比如 2025 的 Pre-Act 工作就直接点名:ReAct 通常是"即时思考 + 即时动作",对复杂任务效果有限,于是提出"先做多步规划,再结合 ReAct 执行"的框架

✍ 论文地址:Pre-Act: Multi-Step Planning and Reasoning Improves Acting in LLM Agents

五、总结

到这一章,我们已经让智能体具备了三种"能力模块":

Memory 让它记住发生过什么,RAG 让它查询外部世界的知识,而 Planner 则让它从"一问一答"升级为"多步决策的任务执行者"。

但目前这一切,仍然是"静态策略":

规划规则、检索策略、记忆使用方式,都是我们人为写在 prompt 里或硬编码在逻辑中的。 在未来的学习中,我们希望让这些策略自己学会变好,自然就得进一步引出 Agentic RL:

用奖励来告诉 Agent:哪种规划、哪种检索、哪种记忆使用方式,才是真正"聪明"的。

相关推荐
爱泡脚的鸡腿2 小时前
uni-app D3实战(小兔仙)
前端
嬉皮客2 小时前
Gird布局详解
前端·css
烛阴2 小时前
C#常量(const)与枚举(enum)使用指南
前端·c#
Wect2 小时前
学习React-DnD:实现多任务项拖拽-useDrag处理
前端
mucheni3 小时前
迅为RK3568开发板OpeHarmony学习开发手册-修改应用程序名称
linux·前端·学习
WebGirl3 小时前
SSE服务
前端
Mintopia3 小时前
🛰️ 低带宽环境下的 AIGC 内容传输优化技术
前端·人工智能·trae
h***34633 小时前
Nginx 缓存清理
android·前端·后端
Mintopia3 小时前
⚡Trae Solo Coding 的效率法则
前端·人工智能·trae