深度解析ReAct模式:推理与行动循环如何让Agent真正具备执行力


引言

痛点引入

相信每一位在2023-2024年关注过LLM(大语言模型)应用落地的开发者、产品经理,甚至是普通技术爱好者,都曾遇到过这样的场景:

当你让GPT-4帮你「计算一下2024年3月15日到2024年10月27日之间北京飞往上海虹桥的所有直达航班的平均折扣率」时,它可能会一本正经地编造一堆不存在的航班时刻表、机票价格区间,甚至虚构「中国国航2024年春夏季虹桥航线折扣白皮书」——哪怕你给它开了联网插件权限,它也可能会胡乱调用搜索引擎找一篇几年前的旧文,或者把春秋航空的会员日折扣当成平均折扣;

当你让Claude 3.5 Sonnet帮你「用Python爬虫爬取豆瓣读书Top 250的书名、评分、作者、出版社信息,保存到CSV文件里,并且自动用pandas统计一下评分在9.0以上的科幻类作品数量」时,它可能会写出一段代码,但要么是豆瓣读书的反爬虫User-Agent没设置(直接403),要么是解析器用的是早已失效的正则表达式,要么是把「科幻奇幻」标签下的所有作品都算进去(没有过滤非虚构或奇幻),甚至当你指出CSV文件乱码时,它只会说「请重新运行代码并检查输出路径」——完全不会去思考自己是不是忘了加encoding='utf-8-sig'或者没有正确处理豆瓣的HTML转义字符;

当你让Gemini Advanced帮你「作为一个客服机器人,帮我处理用户的退款请求:先查一下用户的订单是否在7天无理由退换期内、商品是否未拆封、物流信息是否显示已签收,然后根据规则回复,如果没问题就引导用户填写退款申请单,如果有问题就给出具体的驳回理由」时,它要么会跳过所有查询步骤直接给用户回复模板,要么会编造订单状态、物流单号、签收时间——哪怕你给它配置了公司内部的订单管理系统API、物流查询API的访问密钥,它也可能会因为参数格式错误、API调用顺序混乱、返回结果解析失败而导致整个流程中断。

为什么会出现这些问题?明明这些大模型在「自然语言理解」「文本生成」「逻辑推理(尤其是封闭域逻辑推理,比如做数学题、解逻辑谜题、写学术论文大纲)」方面已经表现得非常出色,甚至超过了大多数人类,但一旦涉及到「开放域信息获取」「多步骤工具调用」「真实世界任务执行」「根据实时反馈调整策略」时,它们就会变得像一个「只会背书的书呆子」或者「只会猜答案的赌徒」?

答案其实藏在大模型的核心缺陷里:

  1. 知识滞后性:几乎所有主流的预训练大模型都有一个「知识截止日期」(比如GPT-4的知识截止日期是2023年10月,Claude 3.5 Sonnet是2024年7月),它们无法获取截止日期之后的任何实时信息(比如天气、股票价格、航班时刻表、新闻事件);
  2. 上下文窗口限制:哪怕是目前上下文窗口最大的大模型之一(比如GPT-4 Turbo有128K上下文窗口,Claude 3 Opus有200K上下文窗口,Gemini 1.5 Pro有10M上下文窗口),也无法一次性加载整个互联网的信息、整个公司的数据库、整个软件项目的源代码——它们只能处理有限的、被精心筛选过的信息;
  3. 幻觉(Hallucination)问题:这是大模型最致命的缺陷之一——当大模型遇到自己不确定的问题或者信息不足的问题时,它们不会说「我不知道」或者「我需要更多信息」,而是会编造一些听起来非常合理但实际上完全不存在的事实、数据、代码、文档——因为它们的核心目标是「生成一段通顺的、符合上下文逻辑的文本」,而不是「生成一段真实的、准确的、有用的文本」;
  4. 缺乏行动感知与反馈迭代能力:目前大多数大模型都是「被动响应」的——它们只会根据用户输入的文本生成一段输出,然后就结束了任务;它们不会主动去「观察」周围的环境,不会主动去「采取」行动,不会主动去「接收」行动的反馈,更不会主动去「根据反馈调整」自己的策略。

解决方案概述

为了解决这些问题,让大模型真正从「只会生成文本的工具」变成「能够执行真实世界任务的智能体(Agent)」,人工智能领域的研究者们提出了一系列的方法和框架——其中最经典、最有效、最被广泛应用的方法之一,就是ReAct模式

ReAct模式是由Google Research的姚顺雨(Shunyu Yao)、赵萌(Meng Zhao)、戴安娜·杨(Diana Yang)、杰夫·迪恩(Jeff Dean)、诺亚·古德曼(Noah Goodman)等人在2022年10月发表的论文《ReAct: Synergizing Reasoning and Acting in Language Models》中提出的——这里的「ReAct」不是「React(反应)」,而是「Reasoning(推理)」和「Acting(行动)」的组合缩写。

ReAct模式的核心思想非常简单,但非常强大:让大模型像人类一样,在执行任务的过程中,不断地进行「推理→行动→观察→推理→行动→观察→…」的循环——具体来说:

  1. Reasoning(推理):大模型先根据当前的任务目标、已有的上下文信息、之前的行动和观察结果,进行「结构化的自然语言推理」(比如思考「我现在需要做什么?」「为什么要这么做?」「这么做可能会有什么结果?」「如果失败了应该怎么办?」);
  2. Acting(行动):大模型根据推理的结果,生成一个具体的、可执行的行动指令——这个行动指令可以是调用一个外部工具(比如搜索引擎、计算器、Python解释器、数据库查询API、网页浏览器),也可以是生成一段输出文本(比如回复用户的问题、填写退款申请单);
  3. Observing(观察):外部环境(比如工具的返回结果、用户的回复、网页的内容)接收行动指令并执行,然后生成一个具体的、可解析的观察结果
  4. 循环迭代:大模型把观察结果作为新的上下文信息,重新进入推理阶段,直到任务目标完成。

ReAct模式的优势非常明显:

  1. 解决了知识滞后性问题:通过调用搜索引擎、新闻API、天气API等外部工具,大模型可以获取实时的、最新的信息;
  2. 缓解了上下文窗口限制问题:通过调用网页浏览器、PDF解析器、数据库查询API等外部工具,大模型可以按需加载、处理、筛选信息,而不需要一次性加载所有信息;
  3. 大大减少了幻觉问题:因为大模型的所有行动和输出都必须基于「真实的观察结果」,而不是「自己的想象」——如果大模型编造了一个事实,观察结果就会否定它,迫使它重新推理和行动;
  4. 具备了行动感知与反馈迭代能力:大模型可以主动地观察环境、采取行动、接收反馈、调整策略,从而能够执行复杂的、多步骤的、开放域的真实世界任务。

最终效果展示 (可选)

为了让大家更直观地感受到ReAct模式的威力,我们先来看两个对比实验——一个是用「纯大模型(没有ReAct)」处理任务,另一个是用「ReAct模式的大模型」处理同一个任务。

对比实验1:实时信息查询

任务目标:计算一下2024年10月27日当天,比特币(BTC)对美元(USD)的收盘价,以及2024年1月1日到2024年10月27日之间的涨幅。

纯大模型(GPT-4 Turbo,知识截止日期2023年10月)的输出
很抱歉,我的知识截止日期是2023年10月,无法获取2024年的实时比特币价格信息。不过,我可以告诉你一些2023年10月的比特币价格数据:2023年10月1日,比特币的收盘价约为27,000美元;2023年10月31日,比特币的收盘价约为34,500美元——2023年10月的涨幅约为27.8%。

如果你需要2024年的实时比特币价格信息,建议你使用CoinMarketCap、Binance、CoinGecko等加密货币交易所或行情网站查询。
ReAct模式的大模型(GPT-4 Turbo + 联网插件 + ReAct提示词)的输出
我现在需要帮用户计算2024年10月27日比特币对美元的收盘价,以及2024年1月1日到10月27日的涨幅。首先,我需要获取这两个时间点的比特币收盘价数据——我可以调用CoinGecko的API或者直接用搜索引擎查询。

---
Thought: 我应该先搜索2024年10月27日比特币对美元的收盘价
Action: Search[2024年10月27日 BTC/USD 收盘价 CoinGecko]
Observation: 根据CoinGecko的数据,2024年10月27日UTC时间23:59:59,比特币(BTC)对美元(USD)的收盘价为**67,892.34美元**。
---
Thought: 好的,我已经获取到了2024年10月27日的收盘价。接下来,我需要获取2024年1月1日的收盘价——同样用CoinGecko的数据更准确。
Action: Search[2024年1月1日 BTC/USD 收盘价 CoinGecko]
Observation: 根据CoinGecko的数据,2024年1月1日UTC时间00:00:00,比特币(BTC)对美元(USD)的开盘价为**42,287.12美元**,收盘价为**42,451.67美元**(因为UTC时间1月1日的23:59:59是1月1日的最后一刻)。
---
Thought: 现在我有了两个时间点的数据:2024年1月1日收盘价是42,451.67美元,2024年10月27日收盘价是67,892.34美元。接下来,我需要计算涨幅——涨幅的公式是(最新价格 - 初始价格)/ 初始价格 * 100%。让我调用计算器工具来计算,避免出错。
Action: Calculate[(67892.34 - 42451.67)/42451.67 * 100]
Observation: 计算结果为**59.93%**(保留两位小数)。
---
Thought: 所有需要的信息都已经获取到了,任务目标已经完成。现在我可以给用户生成最终的回复了。
Final Answer: 根据CoinGecko的实时数据:
1. 2024年10月27日UTC时间23:59:59,比特币(BTC)对美元(USD)的收盘价为**67,892.34美元**;
2. 2024年1月1日UTC时间23:59:59,比特币(BTC)对美元(USD)的收盘价为**42,451.67美元**;
3. 2024年1月1日到2024年10月27日之间的涨幅为**59.93%**(保留两位小数)。
对比实验2:多步骤工具调用与代码生成

任务目标:用Python爬虫爬取豆瓣读书Top 250的前10页(共250本书)的书名、评分、作者、出版社信息,保存到UTF-8 BOM格式的CSV文件里,并且用pandas统计一下评分在9.0以上的作品数量、评分最高的5部作品及其作者。

纯大模型(Claude 3.5 Sonnet,知识截止日期2024年7月)的输出
好的,我来帮你写这段Python代码。以下是完整的代码:

```python
import requests
from bs4 import BeautifulSoup
import pandas as pd

# 豆瓣读书Top 250的基础URL
base_url = "https://book.douban.com/top250?start="

# 存储书籍信息的列表
books = []

# 爬取前10页
for i in range(10):
    url = base_url + str(i * 25)
    response = requests.get(url)
    soup = BeautifulSoup(response.text, "html.parser")
    
    # 解析每一本书的信息
    items = soup.find_all("div", class_="item")
    for item in items:
        # 书名
        title = item.find("div", class_="pl2").find("a")["title"]
        # 评分
        rating = item.find("span", class_="rating_nums").text
        # 作者、出版社等信息
        info = item.find("div", class_="pl").text.split("/")
        author = info[0].strip()
        publisher = info[-2].strip()
        # 添加到列表
        books.append({"书名": title, "评分": float(rating), "作者": author, "出版社": publisher})

# 保存到CSV文件
df = pd.DataFrame(books)
df.to_csv("douban_book_top250.csv", index=False)

# 统计评分在9.0以上的作品数量
high_rating_count = df[df["评分"] >= 9.0].shape[0]
print(f"评分在9.0以上的作品数量:{high_rating_count}")

# 统计评分最高的5部作品及其作者
top5_books = df.sort_values(by="评分", ascending=False).head(5)
print("\n评分最高的5部作品及其作者:")
print(top5_books[["书名", "作者", "评分"]])

这段代码应该可以完成你的任务。你只需要安装requests、bs4、pandas这三个库就可以运行了。


##### ReAct模式的大模型(Claude 3.5 Sonnet + Python解释器插件 + ReAct提示词)的输出

我现在需要帮用户完成这个豆瓣读书Top 250的爬虫任务。首先,我需要理清楚任务的步骤:

  1. 爬取前10页的豆瓣读书Top 250(每页25本,共250本);
  2. 解析每本书的书名、评分、作者、出版社信息;
  3. 保存到UTF-8 BOM格式的CSV文件;
  4. 用pandas做两个统计:评分≥9.0的数量、评分最高的5部作品。

不过,我需要注意几个问题:

  1. 豆瓣有反爬虫机制,不能直接用默认的User-Agent请求;
  2. 豆瓣读书的HTML结构可能会有变化,我需要先验证一下;
  3. 作者、出版社等信息的分隔符可能不止一个斜杠(比如有些作者有多个名字用斜杠分隔,有些出版社有分社);
  4. CSV文件需要用UTF-8 BOM格式,否则Excel打开会乱码;
  5. 我需要一步一步来,先验证爬虫的有效性,再继续后面的步骤。

Thought: 首先,我应该先写一段简单的代码,验证一下豆瓣读书Top 250的HTML结构,并且设置正确的User-Agent避免403错误。
Action: Python_Run[
import requests
from bs4 import BeautifulSoup

设置正确的User-Agent

headers = {
“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36”
}

测试第一页的请求

url = “https://book.douban.com/top250?start=0”
response = requests.get(url, headers=headers)
print(f"请求状态码:{response.status_code}")

如果状态码是200,就解析一下第一页的第一个item

if response.status_code == 200:
soup = BeautifulSoup(response.text, “html.parser”)
first_item = soup.find(“div”, class_=“item”)
print(“\n第一页第一个item的HTML结构:”)
print(first_item.prettify()[:2000]) # 只打印前2000个字符,避免太长
]
Observation: 请求状态码:200

第一页第一个item的HTML结构:

红楼梦 可试读 [清] 曹雪芹 著 / 高鹗 续
9.6 ( 543210人评价 )

都云作者痴,谁解其中味?

[清] 曹雪芹 著 / 高鹗 续 / 人民文学出版社 / 1996-12 / 59.70元
--- Thought: 太好了,请求状态码是200,说明反爬虫机制暂时绕过了。我也看到了第一页第一个item的HTML结构——和我之前想的差不多,但有几个细节需要注意: 1. 书名在div.pl2下的a标签的title属性里(或者text里也可以,但title更准确,因为有些书名有换行); 2. 评分在span.rating_nums的text里; 3. 作者、出版社、出版时间、价格都在div.pl的text里,分隔符确实是斜杠,但有些作者有多个部分(比如「[清] 曹雪芹 著 / 高鹗 续」),所以不能简单地用info[0]当作者、info[-2]当出版社——应该更灵活一点:比如,把最后两个斜杠后面的内容分别当成价格和出版时间,剩下的前面的内容(从第一个到倒数第三个斜杠)合并成作者/译者信息?或者等下,我再看看其他的item,确认一下div.pl的结构是不是统一的。 不过,先不管这个,我先写一个完整的爬虫,爬取前1页的25本书,看看解析的结果对不对,然后再调整作者和出版社的解析逻辑。 另外,CSV文件需要用UTF-8 BOM格式,所以df.to_csv的时候要加encoding='utf-8-sig'。 --- Thought: 接下来,我写一个完整的爬虫,先爬取前1页的25本书,验证解析结果,特别是作者和出版社的解析逻辑。 Action: Python_Run[ import requests from bs4 import BeautifulSoup import pandas as pd import time

设置正确的User-Agent和请求间隔(避免被封)

headers = {
“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36”
}
request_interval = 2 # 每次请求间隔2秒

豆瓣读书Top 250的基础URL

base_url = “https://book.douban.com/top250?start=”

存储书籍信息的列表

books = []

先爬取前1页测试

for i in range(1):
url = base_url + str(i * 25)
print(f"正在爬取:{url}")
response = requests.get(url, headers=headers)
time.sleep(request_interval) # 等待2秒

if response.status_code != 200:
    print(f"请求失败,状态码:{response.status_code}")
    continue

soup = BeautifulSoup(response.text, "html.parser")
items = soup.find_all("div", class_="item")

for item in items:
    # 书名
    title = item.find("div", class_="pl2").find("a")["title"]
    # 评分
    rating = float(item.find("span", class_="rating_nums").text.strip())
    # 解析div.pl的信息
    info_text = item.find("div", class_="pl").text.strip()
    info_parts = [part.strip() for part in info_text.split("/") if part.strip()]
    
    # 灵活解析:最后一个是价格,倒数第二个是出版时间,倒数第三个是出版社,前面的都是作者/译者/整理者
    if len(info_parts) >= 4:
        price = info_parts[-1]
        publish_date = info_parts[-2]
        publisher = info_parts[-3]
        author = " / ".join(info_parts[:-3])
    else:
        # 有些特殊情况,比如info_parts只有3个(比如没有价格?或者没有出版时间?)
        price = info_parts[-1] if len(info_parts) >=1 else "未知"
        publish_date = info_parts[-2] if len(info_parts) >=2 else "未知"
        publisher = info_parts[-3] if len(info_parts) >=3 else "未知"
        author = " / ".join(info_parts[:-3]) if len(info_parts) >=4 else "未知"
    
    # 添加到列表
    books.append({
        "书名": title,
        "评分": rating,
        "作者/译者": author,
        "出版社": publisher,
        "出版时间": publish_date,
        "价格": price
    })

打印前5本书的信息,验证解析结果

print(“\n前5本书的解析结果:”)
for i, book in enumerate(books[:5], 1):
print(f"\n第{i}本:“)
for key, value in book.items():
print(f” {key}:{value}")
]
Observation: 正在爬取:https://book.douban.com/top250?start=0

前5本书的解析结果:

第1本:
书名:红楼梦
评分:9.6
作者/译者:[清] 曹雪芹 著 / 高鹗 续
出版社:人民文学出版社
出版时间:1996-12
价格:59.70元

第2本:
书名:活着
评分:9.4
作者/译者:余华
出版社:作家出版社
出版时间:2012-8-1
价格:20.00元

第3本:
书名:三体
评分:9.4
作者/译者:刘慈欣
出版社:重庆出版社
出版时间:2008-1
价格:23.00元

第4本:
书名:百年孤独
评分:9.3
作者/译者:[哥伦比亚] 加西亚·马尔克斯 著 / 范晔 译
出版社:南海出版公司
出版时间:2017-8
价格:55.00元

第5本:
书名:小王子
评分:9.0
作者/译者:[法] 安托万·德·圣-埃克苏佩里 著 / 李继宏 译
出版社:天津人民出版社
出版时间:2018-8
价格:45.00元

Thought: 太棒了!解析结果非常准确——前5本书的信息都是对的,作者/译者、出版社、出版时间、价格都解析正确了。现在,我可以把爬虫扩展到前10页(共250本),然后保存到UTF-8 BOM格式的CSV文件,最后用pandas做统计。
另外,我需要注意:每次请求之间要加2秒的间隔,避免被豆瓣封IP;还要处理可能的请求失败的情况(比如重试几次)。

Thought: 现在,我把爬虫扩展到前10页,保存到CSV文件,然后做统计。
Action: Python_Run[
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time

设置正确的User-Agent、请求间隔和重试次数

headers = {
“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36”
}
request_interval = 2
max_retries = 3

豆瓣读书Top 250的基础URL

base_url = “https://book.douban.com/top250?start=”

存储书籍信息的列表

books = []

爬取前10页

for i in range(10):
url = base_url + str(i * 25)
print(f"正在爬取第{i+1}页:{url}")

# 重试机制
for retry in range(max_retries):
    try:
        response = requests.get(url, headers=headers, timeout=10)
        if response.status_code == 200:
            break
        else:
            print(f"请求失败,状态码:{response.status_code},正在重试(第{retry+1}次)...")
            time.sleep(request_interval)
    except Exception as e:
        print(f"请求异常:{e},正在重试(第{retry+1}次)...")
        time.sleep(request_interval)
else:
    print(f"第{i+1}页爬取失败,跳过...")
    continue

# 等待2秒
time.sleep(request_interval)

# 解析HTML
soup = BeautifulSoup(response.text, "html.parser")
items = soup.find_all("div", class_="item")

for item in items:
    # 书名
    title = item.find("div", class_="pl2").find("a")["title"]
    # 评分
    rating = float(item.find("span", class_="rating_nums").text.strip())
    # 解析div.pl的信息
    info_text = item.find("div", class_="pl").text.strip()
    info_parts = [part.strip() for part in info_text.split("/") if part.strip()]
    
    # 灵活解析
    if len(info_parts) >= 4:
        price = info_parts[-1]
        publish_date = info_parts[-2]
        publisher = info_parts[-3]
        author = " / ".join(info_parts[:-3])
    else:
        price = info_parts[-1] if len(info_parts) >=1 else "未知"
        publish_date = info_parts[-2] if len(info_parts) >=2 else "未知"
        publisher = info_parts[-3] if len(info_parts) >=3 else "未知"
        author = " / ".join(info_parts[:-3]) if len(info_parts) >=4 else "未知"
    
    # 添加到列表
    books.append({
        "书名": title,
        "评分": rating,
        "作者/译者": author,
        "出版社": publisher,
        "出版时间": publish_date,
        "价格": price
    })

print(f"\n爬取完成!共爬取了{len(books)}本书。")

保存到UTF-8 BOM格式的CSV文件

df = pd.DataFrame(books)
csv_filename = “douban_book_top250_20241027.csv”
df.to_csv(csv_filename, index=False, encoding=‘utf-8-sig’)
print(f"\n书籍信息已保存到:{csv_filename}")

统计评分在9.0以上的作品数量

high_rating_count = df[df[“评分”] >= 9.0].shape[0]
print(f"\n评分在9.0以上的作品数量:{high_rating_count}")

统计评分最高的5部作品及其作者/译者、评分

top5_books = df.sort_values(by=“评分”, ascending=False).head(5)
print(“\n评分最高的5部作品:”)
print(top5_books[[“书名”, “作者/译者”, “评分”]].to_string(index=False))
]
Observation: 正在爬取第1页:https://book.douban.com/top250?start=0
正在爬取第2页:https://book.douban.com/top250?start=25
正在爬取第3页:https://book.douban.com/top250?start=50
正在爬取第4页:https://book.douban.com/top250?start=75
正在爬取第5页:https://book.douban.com/top250?start=100
正在爬取第6页:https://book.douban.com/top250?start=125
正在爬取第7页:https://book.douban.com/top250?start=150
正在爬取第8页:https://book.douban.com/top250?start=175
正在爬取第9页:https://book.douban.com/top250?start=200
正在爬取第10页:https://book.douban.com/top250?start=225

爬取完成!共爬取了250本书。

书籍信息已保存到:douban_book_top250_20241027.csv

评分在9.0以上的作品数量:68

评分最高的5部作品:
书名 作者/译者 评分
红楼梦 [清] 曹雪芹 著 / 高鹗 续 9.6
三体Ⅱ·黑暗森林 刘慈欣 9.3
三体Ⅲ·死神永生 刘慈欣 9.2
百年孤独 [哥伦比亚] 加西亚·马尔克斯 著 / 范晔 译 9.3
活着 余华 9.4
(哦,等下,这里的top5排序好像有点问题?可能是因为有相同评分的书,pandas默认是按索引排序的?没关系,我可以调整一下排序的key,加上按书名的字母顺序或者按评分人数?不过用户的要求只是统计评分最高的5部作品及其作者,所以目前的结果也可以——不过如果有相同评分的,我可以按评分人数排序,会更准确。不过先不管这个,用户的主要任务已经完成了。)


---

## 准备工作
### 环境/工具
在深入学习ReAct模式之前,你需要准备以下的环境和工具:
1.  **Python开发环境**:Python 3.8或更高版本(推荐Python 3.10或3.11,因为大多数LLM应用框架都支持这两个版本);
2.  **LLM API访问密钥**:你需要一个或多个主流LLM的API访问密钥,比如OpenAI的GPT-4/GPT-4o/GPT-4 Turbo API、Anthropic的Claude 3/Claude 3.5 API、Google的Gemini API——这些API都需要付费,但通常会有免费的试用额度(比如OpenAI新用户有5-10美元的免费额度,Anthropic新用户有5-15美元的免费额度,Google新用户有300美元的免费额度);
3.  **LLM应用框架**:为了更方便地实现ReAct模式,你可以使用一些成熟的LLM应用框架,比如LangChain、LlamaIndex(原GPT Index)、AutoGen、CrewAI——其中LangChain是目前最流行、功能最强大的LLM应用框架,它已经内置了完整的ReAct模式实现;
4.  **外部工具(可选)**:如果你想实现更强大的ReAct Agent,你可以准备一些外部工具的API访问密钥,比如Serper(搜索引擎API)、OpenWeatherMap(天气API)、CoinGecko(加密货币行情API)、Zapier(自动化工具API)——当然,你也可以自己编写一些自定义的工具;
5.  **代码编辑器**:VS Code、PyCharm或其他你喜欢的代码编辑器;
6.  **Jupyter Notebook(可选)**:Jupyter Notebook可以帮助你更方便地调试和测试ReAct Agent的代码。

### 环境安装步骤
下面是详细的环境安装步骤:
1.  **安装Python开发环境**:
    *   如果你使用的是Windows系统,你可以从Python官网(https://www.python.org/downloads/)下载Python 3.10或3.11的安装包,然后双击运行安装——注意,安装的时候要勾选「Add Python to PATH」选项;
    *   如果你使用的是macOS系统,你可以使用Homebrew(https://brew.sh/)安装Python:`brew install python@3.11`;
    *   如果你使用的是Linux系统(比如Ubuntu),你可以使用apt-get安装Python:`sudo apt-get update && sudo apt-get install python3.11 python3.11-pip`;
2.  **验证Python和pip的安装**:
    *   打开终端(Windows是Command Prompt或PowerShell,macOS和Linux是Terminal),输入以下命令:
        ```bash
        python --version
        pip --version
        ```
    *   如果输出了Python 3.8或更高版本的版本号,以及pip的版本号,说明安装成功;
3.  **安装LLM应用框架和其他依赖库**:
    *   我们主要使用LangChain,所以需要安装LangChain、LangChain OpenAI、LangChain Community、python-dotenv(用于管理环境变量)、requests(用于调用外部API)、bs4(用于解析HTML)、pandas(用于处理数据):
        ```bash
        pip install langchain langchain-openai langchain-community python-dotenv requests beautifulsoup4 pandas
        ```
    *   如果你想使用Anthropic的Claude API,你还需要安装`langchain-anthropic`:
        ```bash
        pip install langchain-anthropic
        ```
    *   如果你想使用Google的Gemini API,你还需要安装`langchain-google-genai`:
        ```bash
        pip install langchain-google-genai
        ```
4.  **获取LLM API访问密钥**:
    *   **OpenAI API**:访问OpenAI官网(https://platform.openai.com/),注册账号,然后进入「API Keys」页面(https://platform.openai.com/api-keys),点击「Create new secret key」,创建一个新的API密钥——注意,API密钥只会显示一次,你需要把它保存好;
    *   **Anthropic API**:访问Anthropic官网(https://console.anthropic.com/),注册账号,然后进入「API Keys」页面,创建一个新的API密钥;
    *   **Google Gemini API**:访问Google AI Studio官网(https://aistudio.google.com/),注册账号,然后进入「API Keys」页面,创建一个新的API密钥;
5.  **配置环境变量**:
    *   在你的项目根目录下创建一个名为`.env`的文件,然后把你获取到的API密钥添加到这个文件里:
        ```env
        # OpenAI API密钥
        OPENAI_API_KEY=你的OpenAI API密钥
        # Anthropic API密钥(可选)
        ANTHROPIC_API_KEY=你的Anthropic API密钥
        # Google Gemini API密钥(可选)
        GOOGLE_API_KEY=你的Google Gemini API密钥
        # Serper API密钥(可选,用于搜索引擎)
        SERPER_API_KEY=你的Serper API密钥
        ```
    *   注意:`.env`文件里的内容是敏感信息,不要把它提交到Git仓库里——你可以在项目根目录下创建一个名为`.gitignore`的文件,然后把`.env`添加到这个文件里。

### 基础知识
为了更好地理解ReAct模式,你需要具备以下的前置知识:
1.  **Python编程基础**:你需要掌握Python的基本语法、数据结构(列表、字典、元组、集合)、函数、类、异常处理、模块导入等知识;
2.  **大语言模型(LLM)基础**:你需要了解什么是大语言模型、大语言模型的工作原理(比如Transformer架构、自注意力机制)、大语言模型的能力(自然语言理解、文本生成、逻辑推理、代码生成)、大语言模型的缺陷(知识滞后性、上下文窗口限制、幻觉问题、缺乏行动感知能力);
3.  **提示工程(Prompt Engineering)基础**:你需要了解什么是提示工程、如何写好提示词(比如结构化提示词、角色提示词、示例提示词、思维链提示词);
4.  **API调用基础**:你需要了解什么是API、如何使用Python的requests库调用第三方API、如何处理API的返回结果(比如JSON格式);
5.  **HTML解析基础**(可选):你需要了解什么是HTML、HTML的基本结构、如何使用Python的BeautifulSoup库解析HTML。

如果你对以上的前置知识不太熟悉,你可以参考以下的学习资源:
1.  **Python编程基础**:
    *   《Python编程:从入门到实践》(Eric Matthes著);
    *   廖雪峰的Python教程(https://www.liaoxuefeng.com/wiki/1016959663602400);
    *   Codecademy的Python课程(https://www.codecademy.com/learn/learn-python-3);
2.  **大语言模型基础**:
    *   《Attention Is All You Need》论文(https://arxiv.org/abs/1706.03762)——这是Transformer架构的开山之作;
    *   李沐的《动手学深度学习》课程(https://zh.d2l.ai/)——里面有Transformer架构的详细讲解;
    *   OpenAI的GPT-4技术报告(https://arxiv.org/abs/2303.08774);
    *   Andrej Karpathy的《State of GPT》演讲(https://www.youtube.com/watch?v=bZQun8Y4L2A);
3.  **提示工程基础**:
    *   OpenAI的提示工程指南(https://platform.openai.com/docs/guides/prompt-engineering);
    *   Anthropic的提示工程指南(https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview);
    *   《Prompt Engineering for Generative AI》书籍(Ben Auffarth著);
    *   Learn Prompting网站(https://learnprompting.org/zh-Hans);
4.  **API调用基础**:
    *   Python requests库的官方文档(https://requests.readthedocs.io/zh_CN/latest/);
5.  **HTML解析基础**:
    *   BeautifulSoup库的官方文档(https://www.crummy.com/software/BeautifulSoup/bs4/doc/zh_CN/)。

---

## 核心概念
### 什么是ReAct模式
ReAct模式的全称是「Reasoning + Acting」模式,它是由Google Research的姚顺雨等人在2022年10月发表的论文《ReAct: Synergizing Reasoning and Acting in Language Models》中提出的——正如其名,ReAct模式的核心是**将大语言模型的「推理能力」和「行动能力」有机地结合起来**,让大模型像人类一样,在执行任务的过程中不断地进行「推理→行动→观察→推理→…」的循环,从而能够执行复杂的、多步骤的、开放域的真实世界任务。

在ReAct模式提出之前,研究者们已经提出了两种主要的方法来增强大语言模型的能力:
1.  **纯推理方法(比如思维链提示词Chain-of-Thought Prompting,简称CoT)**:纯推理方法的核心是让大模型在生成最终答案之前,先生成一段「结构化的自然语言推理过程」——比如思考「这个问题的已知条件是什么?」「未知条件是什么?」「应该用什么方法来解决?」「第一步应该做什么?」「第二步应该做什么?」——纯推理方法可以大大提高大模型在封闭域逻辑推理任务(比如数学题、逻辑谜题、常识问答)上的表现,但它无法解决知识滞后性、上下文窗口限制、幻觉问题,也无法让大模型执行真实世界的任务;
2.  **纯行动方法(比如WebGPT、Toolformer)**:纯行动方法的核心是让大模型根据用户的输入,直接生成一个或多个「可执行的行动指令」(比如调用搜索引擎、调用计算器),然后根据行动的结果生成最终答案——纯行动方法可以解决知识滞后性、上下文窗口限制问题,也可以让大模型执行真实世界的任务,但它缺乏「推理能力」,无法解释自己为什么要采取某个行动,也无法根据反馈调整自己的策略,容易出现「行动顺序混乱」「参数格式错误」「返回结果解析失败」等问题。

ReAct模式则**结合了纯推理方法和纯行动方法的优势,同时避免了它们的劣势**:
*   它保留了纯推理方法的「结构化自然语言推理过程」,让大模型能够解释自己的行为,也能够根据反馈调整自己的策略;
*   它保留了纯行动方法的「外部工具调用能力」,让大模型能够获取实时的、最新的信息,也能够执行真实世界的任务;
*   它通过「推理→行动→观察→推理→…」的循环,将推理和行动有机地结合起来——推理指导行动,行动的观察结果又反过来修正推理,从而大大提高了大模型的任务执行能力和准确性,同时大大减少了幻觉问题。

### ReAct模式的核心要素组成
ReAct模式的核心

更多推荐