下面给出一个代码示例: 展示如何使用 LangChain + 通义千问(Qwen),通过 ReAct Agent 安全地调用自定义工具完成数学计算任务。
示例代码取自《AI Agent智能体开发实践》第8章。
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 25 09:55:25 2025
@author: liguo
"""
from langchain.tools import tool
from langchain.agents import AgentExecutor, create_react_agent
from langchain import hub
from langchain_community.chat_models import ChatTongyi
import os
# 👇 核心计算函数,不被 @tool 装饰,避免递归调用工具对象
def _factorial(n: int) -> int:
"""内部阶乘计算函数,使用循环避免递归栈和工具调用问题"""
if n < 0:
raise ValueError("阶乘不支持负数")
result = 1
for i in range(1, n + 1):
result *= i
return result
# 👇 工具层函数,负责接收输入、类型转换、调用核心函数
@tool
def calculate_factorial(n: int) -> int:
"""计算给定数字的阶乘"""
# 确保输入是整数(兼容字符串输入)
if isinstance(n, str):
n = int(n.strip())
return _factorial(n)
# 工具列表
tools = [calculate_factorial]
# 初始化 Qwen 模型(请替换为你自己的 DashScope API Key)
llm = ChatTongyi(
model="qwen-max", # 也可用 qwen-turbo, qwen-plus
temperature=0,
#dashscope_api_key=os.getenv("DASHSCOPE_API_KEY")
# 推荐:在终端或系统环境变量中设置 DASHSCOPE_API_KEY
)
# 拉取 ReAct 提示词模板
prompt = hub.pull("hwchase17/react")
# 创建 Agent
agent = create_react_agent(llm, tools, prompt)
# 创建执行器
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 执行任务
if __name__ == "__main__":
response = executor.invoke({"input": "计算5的阶乘"})
print("\n" + "="*50)
print("最终结果:", response["output"])
下面我按模块 + 执行流程,对你给出的代码做一次逐行、偏实战向的解析,帮助你真正理解每一步在干什么、为什么要这样写。
一、整体结构概览
你的代码可以分为 6 个层次:
-
导入依赖
-
核心计算函数(不暴露给 Agent)
-
LangChain Tool 包装函数
-
工具注册 & LLM 初始化
-
ReAct Agent 构建
-
执行与结果输出
二、逐段解析
1️⃣ 头部 & 编码声明
# -*- coding: utf-8 -*-
✅ 确保 Python 能正确处理中文注释
✅ 在 Python 3 中不是必须,但写工具/脚本时仍是好习惯
2️⃣ 导入模块
from langchain.tools import tool
from langchain.agents import AgentExecutor, create_react_agent
from langchain import hub
from langchain_community.chat_models import ChatTongyi
import os
各模块作用
| 模块 | 作用 |
|---|---|
|
| 把普通函数注册为 Agent 可调用的工具 |
|
| 实际运行 Agent 的引擎 |
|
| 创建 ReAct 模式 Agent |
|
| 从 LangChain Hub 拉取 Prompt |
|
| 通义千问(Qwen)LLM |
|
| 读取环境变量 |
3️⃣ 核心计算函数(⚠️ 关键点)
def _factorial(n: int) -> int:
"""内部阶乘计算函数,使用循环避免递归栈和工具调用问题"""
if n < 0:
raise ValueError("阶乘不支持负数")
result = 1
for i in range(1, n + 1):
result *= i
return result
✅ 为什么不用递归?
❌ 递归写法:
def factorial(n):
return n * factorial(n-1)
问题:
-
Python 递归深度有限
-
LangChain Agent 容易误以为这是工具
-
极易触发 无限递归调用工具
✅ 你用了 循环 + 私有函数:
-
_factorial:只做计算 -
不会被
@tool装饰 -
Agent 永远看不到它
✅ 这是 LangChain 工具设计的最佳实践
4️⃣ Tool 层(Agent 可见的唯一入口)
@tool
def calculate_factorial(n: int) -> int:
"""计算给定数字的阶乘"""
if isinstance(n, str):
n = int(n.strip())
return _factorial(n)
这一层做了 3 件事:
✅ 1. 注册为工具
@tool会把它变成:
Tool(
name="calculate_factorial",
func=calculate_factorial,
description="计算给定数字的阶乘"
)
Agent 只能看到这个工具
✅ 2. 输入类型容错
if isinstance(n, str):
n = int(n.strip())
原因:
-
LLM 经常返回
"5"而不是5 -
JSON / Agent 解析不稳定
✅ 这一步非常关键,否则容易报错
✅ 3. 调用核心逻辑
return _factorial(n)
✅ 工具 ≠ 业务逻辑
✅ 解耦,方便测试、复用、维护
5️⃣ 工具注册
tools = [calculate_factorial]
✅ Agent 可用的工具列表
✅ 以后加工具只需:
tools = [calculate_factorial, add, multiply]
6️⃣ 初始化大模型(Qwen)
llm = ChatTongyi(
model="qwen-max",
temperature=0,
)
参数解释
| 参数 | 含义 |
|---|---|
|
| 通义千问模型 |
|
| 输出确定性强(数学问题必选) |
✅ API Key 通过环境变量注入(安全)
7️⃣ 拉取 ReAct Prompt
prompt = hub.pull("hwchase17/react")
ReAct 是什么?
ReAct = Reasoning + Acting
Prompt 强制 LLM 按如下格式思考:
Thought: 我要算 5 的阶乘
Action: calculate_factorial
Action Input: 5
Observation: 120
Final Answer: 120
✅ 这是工具调用的标准范式
⚠️ 缺点:依赖 LangChain Hub(有时慢)
8️⃣ 创建 Agent
agent = create_react_agent(llm, tools, prompt)
等价于告诉 Agent:
你有一个大脑(LLM)
你有这些工具(tools)
你必须按这个 Prompt 的格式思考
9️⃣ 创建执行器(AgentExecutor)
executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True
)
AgentExecutor 的职责
| 功能 | 说明 |
|---|---|
| 解析 LLM 输出 | Thought / Action |
| 调用工具 |
|
| 把结果塞回 LLM | Observation |
| 控制循环 | 防止死循环 |
✅ verbose=True:打印完整推理过程
🔟 执行任务
response = executor.invoke({"input": "计算5的阶乘"})
实际发生了什么?
-
Agent 收到问题
-
决定使用
calculate_factorial -
调用
calculate_factorial(5) -
得到
120 -
生成最终自然语言回答
最终输出
print(response["output"])
示例:
5的阶乘是120。
三、整体执行流程图(文字版)
用户输入
↓
AgentExecutor
↓
ReAct Prompt + LLM
↓
Action: calculate_factorial
↓
Tool 调用
↓
_factorial()
↓
Observation
↓
Final Answer
四、一句话总结
✅ 这是一个 结构正确、工程化、可扩展的 LangChain Tool + ReAct Agent 示例
✅ 你已经避开了 90% 新手会踩的坑(递归 / 工具循环 / 输入类型)
1194



被折叠的 条评论
为什么被折叠?



