系列目标 :30 天从 LangChain 入门到企业级部署
今日任务:理解 ReAct Agent 原理 → 实现自定义 Tool → 构建"查订单 + 发通知"自动化工作流!
🤖 一、为什么需要 Function Calling?
普通聊天机器人只能"说",但企业需要 AI 能"做":
- "帮我查订单 1001 的状态"
- "如果已发货,发邮件通知客户"
- "顺便查下今天北京天气"
传统 Chain 的局限:
- ❌ 固定流程,无法动态决策
- ❌ 不能组合多个外部服务
解决方案:
✅ Agent + Tools ------ 让 LLM 自主规划 、调用工具 、整合结果!
💡 今天,我们就用 LangChain 的 ReAct Agent + 自定义 Tool,打造一个会"思考+行动"的 AI 助手!
🧠 二、Agent 核心原理:ReAct 框架
ReAct = Reasoning(推理) + Acting(行动)
工作流程:
- 用户提问 → Agent 分析是否需要工具
- 若需要 → 选择合适 Tool + 生成参数
- 调用 Tool → 获取结果
- 基于结果生成最终回答(可多轮)
🔑 关键:LLM 必须支持 Function Calling(结构化输出)
✅ Qwen 通过 Ollama 支持 Function Calling (需ollama>=0.1.34)
🛠️ 三、动手实践 1:定义自定义 Tool
假设我们有两个内部服务:
Tool 1:查询订单状态(模拟)
python
# day23_agent_tools.py
from langchain_core.tools import tool
@tool
def get_order_status(order_id: str) -> str:
"""根据订单ID查询物流状态。输入必须是纯数字字符串,如 '1001'"""
# 模拟数据库查询
mock_db = {
"1001": "已发货,快递单号 SF123456789CN",
"1002": "处理中",
"1003": "已签收"
}
return mock_db.get(order_id, "订单不存在")
Tool 2:发送邮件通知(模拟)
python
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""发送邮件给指定收件人"""
# 模拟发送
return f"✅ 邮件已发送至 {to},主题:{subject}"
✅ 使用
@tool装饰器 → 自动提取函数名、描述、参数 schema!
🧩 四、动手实践 2:创建 ReAct Agent
步骤 1:初始化支持 Function Calling 的 LLM
ini
# day23_react_agent.py
from langchain_ollama import ChatOllama
# 关键:启用 function calling(Ollama 会自动处理)
llm = ChatOllama(
model="qwen:7b",
temperature=0,
format="json" # 强制 JSON 输出(部分版本需要)
)
⚠️ 确保 Ollama 版本 ≥ 0.1.34,并已拉取最新 Qwen 模型:
ollama pull qwen:7b
步骤 2:绑定 Tools 到 Agent
ini
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub
# 加载 ReAct Prompt(官方模板)
prompt = hub.pull("hwchase17/react")
# 创建 Agent
agent = create_react_agent(
llm=llm,
tools=[get_order_status, send_email], # 自动注册!
prompt=prompt
)
# 创建执行器
agent_executor = AgentExecutor(
agent=agent,
tools=[get_order_status, send_email],
verbose=True,
handle_parsing_errors=True # 容错
)
✅
hub.pull("hwchase17/react")是 LangChain 官方维护的 ReAct Prompt!
步骤 3:测试多步任务
ini
# 场景:用户要求"查订单并通知"
query = "订单 1001 如果已发货,请发邮件通知 customer@example.com"
result = agent_executor.invoke({"input": query})
print("\n🤖 最终回答:", result["output"])
▶️ 执行过程(verbose=True 输出):
makefile
Thought: 我需要先查询订单状态
Action: get_order_status
Action Input: {"order_id": "1001"}
Observation: 已发货,快递单号 SF123456789CN
Thought: 订单已发货,需要发送邮件
Action: send_email
Action Input: {"to": "customer@example.com", "subject": "您的订单已发货", "body": "订单 1001 已发货..."}
Observation: ✅ 邮件已发送至 customer@example.com...
Thought: 任务完成
Final Answer: 已为您查询订单 1001,状态为"已发货",并已发送邮件通知客户。
✅ 完美实现:
- 自主决策调用顺序
- 正确解析参数
- 整合多工具结果
🌐 五、进阶:自动注册目录下所有 Tool
避免手动导入每个函数:
python
import inspect
from pathlib import Path
def auto_register_tools(tool_module_path: str):
"""自动注册指定模块下的所有 @tool 函数"""
spec = importlib.util.spec_from_file_location("tools", tool_module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
tools = []
for name, obj in inspect.getmembers(module):
if hasattr(obj, "_tool_name"): # langchain tool 标记
tools.append(obj)
return tools
# 使用
tools = auto_register_tools("./my_company_tools.py")
agent_executor = AgentExecutor(agent=agent, tools=tools, ...)
💡 适用于微服务架构:每个业务域提供自己的
tools.py
⚠️ 六、注意事项 & 最佳实践
表格
| 问题 | 建议 |
|---|---|
| LLM 不调用 Tool | 在 Prompt 中强调"必须使用工具";检查函数描述是否清晰 |
| 参数解析错误 | 使用明确的类型注解(str, int);避免复杂嵌套 |
| 工具副作用(如真发邮件) | 开发阶段用 Mock;生产加审批开关 |
| 循环调用 | 设置 max_iterations=5 防死循环 |
| 中文工具描述失效 | 确保 Ollama/Qwen 支持中文 Function Calling(Qwen 表现良好) |
💡 安全建议:
- 所有 Tool 输入做校验(如
order_id.isdigit())- 敏感操作(如删数据)需人工确认
- 记录完整 Action 日志用于审计
📦 七、配套代码结构
bash
langchain-30-days/
└── day23/
├── my_company_tools.py # 自定义 Tool 定义
└── react_agent_main.py # Agent 创建与执行
📝 八、今日小结
- ✅ 理解了 ReAct Agent 的"推理+行动"机制
- ✅ 学会了用
@tool装饰器定义自定义工具 - ✅ 实现了多步任务自动化(查订单 → 发邮件)
- ✅ 掌握了自动注册 Tool 的工程技巧
- ✅ 知道了安全与容错的最佳实践
🎯 明日预告:Day 24 ------ Plan-and-Execute Agent!让 AI 先制定计划再执行,解决复杂任务!