ReAct 模式:先思考,再行动,然后再思考

周一早上的周会刚结束,小王把笔记本屏幕转向老李,上面密密麻麻刷着 Agent 的运行日志。

“老李,你看看这个——”

[Thought] 用户想知道公司去年年会的举办城市。我应该查一下内部知识库。
[Action] search_knowledge_base("2025年年会举办城市")
[Observation] 找到3条结果,最相关的是《2025年会通知》,地点是杭州。
[Thought] 知识库里明确写了是杭州,但我还需要确认日期,因为用户可能接下来会问。
[Action] search_knowledge_base("2025年年会日期")
[Observation] 结果是11月8日。
[Thought] 信息齐全了,可以回答用户了。
[Final Answer] 2025年公司年会于11月8日在杭州举行。

老李盯着日志看了半分钟,保温杯往桌上一顿:“我就问它‘年会在哪开的’,它查了两次资料?还自己琢磨了那么多?这不浪费时间吗?我以前写个SQL直接返回结果,一步到位。”

“老李你想啊——你早上出门前,是先看天气预报再决定穿什么,还是直接冲出去,冻得半死再回来加衣服?”

老李张了张嘴,没说出话。

小王趁热打铁:“你再看看,同样的问题,如果直接让大模型回答,不加任何思考过程,它会给出什么——”

他打开另一个窗口,里面是一个纯 LLM 的回答记录。老李凑近一看,脸立刻黑了。

“2025年公司年会于12月中旬在北京总部举行,年会主题是‘共创未来’,设有文艺表演和抽奖环节。”

“这全是编的!它根本不知道咱们年会在哪!”老李怒了。

“对。没有 ReAct 的模型就像一个有问必答但从不核实的人。你问他啥他都能接上话,但真假不管。ReAct 就是教它一个习惯:回答之前,先想想自己知不知道;不知道的,先去查;查完再确认一遍,然后再开口。”

老李皱着眉重新看了一遍日志,这次看得慢了。


ReAct 三步舞:思考、行动、观察

小王在白板上画了一个三角形,三个顶点分别写着“Thought”“Action”“Observation”。

“这就是 ReAct 的核心循环,我管它叫‘三步舞’。每一步都不能省。”

第一步,Thought——思考。 AI 拿到问题后,不急着回答,而是先分析:‘这个问题我需要查什么?我已知的信息够不够?’就像你接到一个任务,先在心里盘算一下,而不是抓起工具就干。”

第二步,Action——行动。 根据思考的结果,去调用工具。可能是搜知识库、查数据库、调 API、发邮件——任何能给 AI 提供外部信息的手段都算 Action。”

第三步,Observation——观察。 看看行动的结果是什么。查到有用的信息了吗?还是查错了?查到的信息完整吗?如果不够,就再回到第一步——继续思考下一步怎么办。”

图:ReAct 循环——信息不够就继续跳,够了就停

“所以它不是‘一步到位’,而是‘步步为营’。”老李若有所思,“这跟我做技术方案挺像——先调研,再设计,发现不对再回头调研。”

“对!你写方案就是这样,只不过你把这个过程叫‘经验’,ReAct 把它叫‘推理’。”


拆解一个完整的 ReAct:帮老板订机票

“我给你演示一个更复杂的例子,”小王打开终端,“假设你要我帮你订一张下周二去成都的机票,但要最便宜的那班。一个纯 LLM 会怎么做?”

老李想了想:“它会编一个航班号,编一个价格,然后告诉你订好了——全是假的。”

“对。因为它没有查询真实航班的能力。现在给它装上 ReAct,再看——”

python
# ReAct 的核心循环示意:订机票 Agent
def react_loop(user_request: str, max_steps=5):
    messages = [{"role": "system", "content": REACT_SYSTEM_PROMPT}]
    messages.append({"role": "user", "content": user_request})
    
    for step in range(max_steps):
        response = llm.generate(messages)  # LLM 输出 Thought/Action/Observation
        parse_result = parse_react_output(response)
        
        if parse_result["type"] == "final_answer":
            return parse_result["content"]
        
        if parse_result["type"] == "action":
            # 执行工具调用
            tool_result = execute_tool(parse_result["action"], parse_result["input"])
            # 把观察结果追加到对话中
            messages.append({"role": "assistant", "content": response})
            messages.append({"role": "user", "content": f"Observation: {tool_result}"})
    
    return "抱歉,我没能在限定步数内完成任务。"

“你看,每一步 LLM 都会输出它当前的 Thought 和下一步要做的 Action。执行 Action 得到的结果,作为 Observation 喂回去。这个循环自动持续,直到 LLM 认为信息足够,给出 Final Answer。”

老李追问:“那它怎么知道什么时候该停?不会一直循环下去吗?”

“靠两个约束。第一,最大步数限制——比如最多循环 10 次,超了就强行终止。第二,Prompt 里明确规定了‘什么时候该停止’——当信息足够回答原始问题时,必须输出 Final Answer。这俩一结合,基本就能管住。”


Prompt 是灵魂:如何写好 ReAct 的系统指令

“最关键的是这个 System Prompt,”小王打开一个配置文件,“它定义了 Agent 的‘思维格式’。如果 Prompt 写得不好,Agent 要么乱想,要么想都不想直接编。”

他展示了一段精简版的 ReAct Prompt:

python
REACT_SYSTEM_PROMPT = """
你是一个能使用工具的智能助手。回答问题时,请严格遵循以下格式:

Thought: 分析当前情况,思考下一步需要做什么。
Action: 要执行的动作,格式为 `tool_name(input)`。
Observation: 工具返回的结果(由系统自动提供,你不要生成这个)。

如果你已经有了足够的信息来回答用户的问题,输出:
Thought: 我现在有足够的信息来回答这个问题。
Final Answer: [你的回答]

规则:
1. 每一步要么输出 Action,要么输出 Final Answer,不能同时输出。
2. 不要编造任何信息,一切以 Observation 为准。
3. 如果连续两次同样的 Action 都得不到有用信息,换一个思路。

可用工具:
- search_knowledge_base(query: str): 搜索公司内部知识库。
- get_date(): 获取当前日期。
"""

“看到没有?格式、规则、可用工具——三条线框死了 Agent 的行为。它不是随意发挥,而是按照这个模板进行结构化推理。”

老李盯着 Prompt 看了许久:“这跟我给新人写操作手册差不多——告诉他们流程是什么,什么时候该做什么,遇到问题怎么处理。”

“完全一样。你现在明白为什么我说 Agent 不是一个套壳的 LLM 了吧?LLM 只是大脑,真正让 Agent 跑起来的是这套结构化推理流程精心设计的约束规则。没有这些,大脑再聪明也是散漫的。”


ReAct 的局限:循环地狱和幻觉行动

“那它有什么问题?”老李习惯性地开始挑刺。

“三个。”小王伸出手指。

“第一,循环地狱。Agent 有时候会陷入死胡同——比如查到一个模糊的信息,然后反复用不同的关键词搜同一个库,永远觉得‘还不够’。如果不设最大步数,它能把 API 额度烧光。”

“第二,幻觉行动。LLM 偶尔会‘发明’一个不存在的工具。比如 Prompt 里只给了 search_knowledge_base,但它一上头输出了 Action: query_database(...)——这个工具根本不存在,系统执行失败,Observation 返回错误信息,然后 Agent 就更懵了。”

“第三,效率问题。简单问题也被它搞复杂了。你问‘今天几号’,它可能去思考‘我需要调用 get_date 工具’,然后行动,然后观察,然后回答。三步搞完一秒钟,但对用户来说,这跟直接回答没有任何区别——因为 LLM 本来就知道日期,根本不需要查。”

老李点点头:“所以我刚才那个年会问题,如果知识库里没有,它反复查了两次,也是效率问题。”

“对。但相比之下,准确性更重要。尤其是涉及内部知识、实时数据的问题,慢一点换来的是靠谱的答案。就像你让实习生去查资料再汇报,肯定比让他凭记忆瞎说要慢,但你敢用后者的结果吗?”


当老李突然明白自己也是 ReAct

老李沉默了一会儿,拧开保温杯,慢慢地喝了一口。

“我有个想法,”他说,“我每天早上的状态,其实就是个 ReAct。”

小王眼睛亮了:“你说说看。”

“我打开电脑,先看邮件——这是 Observation。看完之后想,哪个紧急、哪个可以缓——这是 Thought。然后决定‘先回老赵的电话’——这是 Action。打完电话拿到新信息,再想‘那周三的上线可以推迟’——这又是一个 Thought。我这一整天,就是 Thought → Action → Observation 的循环。”

“老李,你这总结比我的比喻都精准。”小王由衷地说。

老李摆摆手,嘴角却藏不住一丝得意:“下不为例啊。下次你直接把 ReAct 说成‘做事之前先过脑子’,我就不跟你抬杠了。”

他站起来,走到门口又停住:“那什么,你刚才说的那个幻觉行动——如果 Agent 发明了一个不存在的工具,系统怎么优雅地处理?能不能在 Observation 里给它一个明确的引导,让它回到正轨?你给我看看你那个错误处理代码。”

小王笑着比了个 OK。老李端着保温杯走了,枸杞晃晃悠悠,像 ReAct 循环里那个不断转动的思维齿轮,终于卡进了正确的槽位。

给 Agent 装上超能力:Tool Calling 完全指南
AI Agent 是什么?它不是套了壳的 LLM