[LangChain Agent] 03 ReAct 框架:思考→行动→观察

一、本篇要解决什么问题

第 02 篇已经能看见 tool_calls。模型面对「股价多少,并介绍一下」,完全可能在同一条 AIMessage 里同时申请 get_priceget_info。这对「两个查询互相独立」的题目很省事。

有一类题不行:下一步依赖上一步的观察。更常见的教学约束是------先想清楚再伸手,一次只做一件事,方便人把轨迹读成故事,而不是并行火花。

本篇用 BMI 把这件事做死:身高、体重来自两把工具,公式在模型脑子里。系统提示强制 ReAct,并且每轮只准调用一个工具

1.1 学习目标

  1. 能默写 ReAct 的四个节拍:思考 → 行动 → 观察 → 再思考

  2. 理解「每轮只调一个工具」是提示词纪律,不是 create_agent 的默认开关

  3. 能根据 stream 日志,把一次 BMI 计算还原成完整循环

  4. 分清本篇和上一篇在轨迹形态上的差别

1.2 一句对照

第 02 篇问:模型选了哪些工具? 本篇问:模型有没有按「想完再做、做完再看」的节奏走?

代码骨架几乎照抄上一篇的 stream,变化集中在工具含义系统提示

二、ReAct 是什么

ReAct 是 Reason + Act 的合成:让语言模型在同一条轨迹里交替进行推理行动,而不是先写完整篇分析再一次性调用,也不是闷头调工具不解释。

四个节拍可以画成:

复制代码
        ┌──────────┐
        │  思考     │  现在缺什么?下一步做什么?
        └────┬─────┘
             │
             ▼
        ┌──────────┐
        │  行动     │  调用恰好一把工具
        └────┬─────┘
             │
             ▼
        ┌──────────┐
        │  观察     │  读 ToolMessage
        └────┬─────┘
             │
             ▼
        够不够算最终答案?
          │否            │是
          ▼              ▼
        回到思考        输出结论

对应到 LangChain 的消息:

ReAct 节拍 落在哪条消息上
思考 AIMessage.content(本课要求按「思考、行动、观察」结构写出来)
行动 同一条 AIMessagetool_calls,且长度应为 1
观察 随后的 ToolMessage.content
再思考 下一轮 AIMessage

框架帮你把「行动 / 观察」在协议层跑通;「思考必须说出来、一次只准一把工具」要靠 system_prompt

三、案例:计算我的 BMI

对照文件:P5_Agent智能体/03ReAct案例.py

3.1 两把互不包含的工具

复制代码
@tool(description="获取体重,返回值是整数,单位千克")
def get_weight() -> int:
    return 90


@tool(description="获取身高,返回值是整数,单位厘米")
def get_height() -> int:
    return 172

设计是刻意的:

  1. 没有一把「直接算 BMI」的工具。 模型必须自己记公式,而不能把计算外包掉。

  2. 身高体重拆开。 一次只调一把时,循环至少两轮,ReAct 才看得出来。

  3. 描述里写清单位。 体重千克、身高厘米。BMI 要用米:172 / 100 = 1.72。单位写在 description 里,模型才不会把 172 当成米。

桩数据固定:体重 90 kg,身高 172 cm。

属于肥胖区间。最终答复里出现这个数(或四舍五入后的 30.4),说明观察被正确带进了计算,而不是模型随口编了一个 BMI。

3.2 系统提示把 ReAct 写成硬纪律

复制代码
system_prompt="""你是严格遵循ReAct框架的智能体,必须按「思考→行动→观察→再思考」的流程解决问题,
    且**每轮仅能思考并调用1个工具**,禁止单次调用多个工具。
    并告知我你的思考过程,工具的调用原因,按思考、行动、观察三个结构告知我""",

三句话,三层约束:

句子 约束的是
必须按「思考→行动→观察→再思考」 节奏,不许跳步
每轮仅能调用 1 个工具 轨迹形态,禁止第 02 篇那种一次两个 tool_calls
按思考、行动、观察三个结构告知我 content 的版式,方便人读日志

注意:create_agent 不会 因为这句话就在运行时拒绝第二个 tool call。它仍然把模型发出的调用都执行掉。纪律能否被遵守,取决于模型是否听话。教学上把要求写进系统提示,再在 stream 日志里验收 tool_calls 的长度是不是 1。

3.3 执行方式沿用 stream

复制代码
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "计算我的BMI"}]},
    stream_mode="values"
):
    latest_message = chunk['messages'][-1]

    if latest_message.content:
        print(type(latest_message).__name__, latest_message.content)

    try:
        if latest_message.tool_calls:
            print(f"工具调用: { [tc['name'] for tc in latest_message.tool_calls]  }")
    except AttributeError as e:
        pass

和上一篇相比,变的是用户问题和工具集合,读帧方式一字未改。专栏故意这么写:学会一种观察手段,后面只换任务。

用户问题极短------「计算我的BMI」。身高体重都没给。模型若直接输出一个数字,就是幻觉;按 ReAct,它必须先承认「我还没有这两个数」。

四、一次合格循环长什么样

模型听话时,日志应接近下面这条时间线(先体重还是先身高都可以,不能并行):

复制代码
HumanMessage     计算我的BMI

AIMessage        【思考】要算 BMI,缺体重和身高。先取体重。
                 【行动】调用 get_weight
工具调用: ['get_weight']

ToolMessage      90

AIMessage        【观察】体重 90 kg
                 【思考】还缺身高。
                 【行动】调用 get_height
工具调用: ['get_height']

ToolMessage      172

AIMessage        【观察】身高 172 cm
                 【思考】BMI = 90 / (1.72^2) ≈ 30.4
                 (本轮不再带 tool_calls,循环结束)

用表格验收:

检查项 合格 不合格
最终数字 约 30.4 编造 22、24 这种「标准身材」
每一帧 tool_calls 长度 0 或 1 出现 ['get_weight', 'get_height']
两把工具是否都出现过 都出现 只调一把,另一半靠猜
content 结构 能看到思考 / 行动 / 观察 只有一句「你的 BMI 是 30」

4.1 为什么禁止一次调两把

技术上,身高体重互不依赖,并行完全正确,也更快。课程禁止它,是为了把循环拉成可讲解的带子

  1. 学生能指着某一帧说「这是思考,这是行动」

  2. 和第 02 篇形成对照:同一套 stream,提示词能改变轨迹形状

  3. 为后面更难的依赖题打样------比如「先查库存再下单」,第二步必须看见第一步的观察

生产里要不要禁止并行,按任务定,不要把教学纪律当成框架默认。

4.2 模型若一次点了两把怎么办

日志会打出:

复制代码
工具调用:  ['get_weight', 'get_height']

说明系统提示没完全管住。处理办法通常是收紧提示、换更听话的模型,或像第 04 篇那样在中间件里拦截。本课停留在「先把合格轨迹跑出来」。

4.3 公式必须由模型算

两把工具只返回原始整数。90 / (1.72 ** 2) 出现在最后一轮 AIMessage 里,这是 LLM 在当计算器。教学上够用;对精度敏感的业务,应再提供一把 calc_bmi(weight, height) 工具,让观察里直接出现可靠数字。本课不提供,就是为了让「再思考」这一拍有实质工作。

五、和上一篇并排看

第 02 篇 股票 本篇 BMI
用户问题 两个并列意图 一个计算目标
工具关系 互相独立 结果要在最后一步汇合
提示词 告知思考过程 ReAct + 每轮 1 个工具 + 固定版式
合格的 tool_calls 可以一次两个名字 每一帧最多一个名字
循环最少轮次 可能 1 轮工具 + 1 轮总结 至少 2 轮工具 + 1 轮总结

同一份 create_agent + stream,换工具和提示词,智能体从「能并行查数的助手」变成「按节拍解题的助手」。这是 Agent 开发里比堆工具更要紧的一课:人设即控制流


六、本篇小结

  1. ReAct = 思考 → 行动 → 观察 → 再思考,直到能给结论

  2. 行动落在 tool_calls,观察落在 ToolMessage,思考落在 content

  3. 「每轮一个工具」写在 system_prompt 里,用日志里的列表长度验收

  4. BMI 案例故意不提供计算工具,逼模型在最后一轮完成公式

  5. stream 读帧代码可以原样复用,换的是任务而不是观察手段

相关推荐
minji...1 天前
LangChain AI应用开发框架核心组件的使用 - 消息组件的使用 : 消息,历史消息的缓存,历史消息的管理(裁剪过滤合并)
langchain
旖旎夜光1 天前
【LangChain实战】LangChain 学习笔记(一):从定义大模型到工具调用
人工智能·笔记·python·学习·langchain
BreezeJiang1 天前
聊天历史不是越多越好:用 LangChain 把“记住”和“带上”分开
langchain
花间相见2 天前
【LangChain中间件01】—— LangChain 中间件入门:六个钩子让 Agent 可插拔
中间件·langchain
Boop_wu2 天前
[LangGraph] 案例 2 : 支持搜索的智能代理系统
服务器·windows·python·langchain
测试开发Kevin2 天前
DeepEval + Eval‑Harness 完整讲解(结合 Playwright UI 自动化例子)
人工智能·ai·langchain
码农小麦3 天前
LangChain 1.3.18 + DeepSeek-v4-flash 工具调用踩坑实录
网络·数据库·langchain
10年前端老司机3 天前
别卷CRUD了!前端用Next.js+LangChain.js,低成本冲进AI高薪赛道
前端·langchain·next.js
cui_ruicheng3 天前
LangChain 应用开发(十四):Agent 上下文与记忆机制
服务器·人工智能·python·langchain