使用 LangChain 构建 Agent
前面几章我们都是"手工"构建 Agent——自己写工具 Schema、自己管理消息循环、自己处理工具调用。这虽然有助于理解底层原理,但在实际项目中太繁琐了。LangChain 提供了标准化的工具接口和 AgentExecutor,大大简化了 Agent 开发。
LangChain 做的事情,本质上是把我们之前手写的那些"样板代码"封装成了可复用的组件:
- 工具定义:不用再手写 JSON Schema,用
@tool装饰器或继承BaseTool即可 - Agent 创建:不用再自己写执行循环,
create_openai_tools_agent一行搞定 - 执行管理:
AgentExecutor自动处理工具调用循环、超时控制、错误恢复
LangChain 工具定义
LangChain 提供了两种定义工具的方式:
方式一:@tool 装饰器——最简单直接。你只需要写一个普通的 Python 函数,加上 @tool 装饰器,LangChain 会自动从函数签名和 docstring 中提取参数信息、生成 Schema。这个 docstring 会成为工具的描述——所以一定要写清楚。
方式二:继承 BaseTool 类——当你需要更复杂的逻辑时使用,比如工具需要维护内部状态、需要异步执行、或者需要自定义参数验证。
from langchain_core.tools import tool, BaseTool
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
# ⚠️ 注意:AgentExecutor 是 LangChain 的 legacy Agent 方案。
# LangChain 官方推荐新项目使用 LangGraph 构建 Agent(见第12章)。
# 此处使用 AgentExecutor 是为了快速理解 Agent 概念。
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import math
# ============================
# 方式1:@tool 装饰器(最简单)
# ============================
@tool
def calculate(expression: str) -> str:
"""
计算数学表达式。
支持基本运算和 math 模块函数(sqrt, sin, cos, log 等)。
示例:calculate("sqrt(144) + 2 * 3")
"""
try:
safe_env = {k: getattr(math, k) for k in dir(math) if not k.startswith('_')}
result = eval(expression, {"__builtins__": {}}, safe_env)
return f"{expression} = {result}"
except Exception as e:
return f"计算错误:{e}"
@tool
def search_knowledge(query: str) -> str:
"""
在知识库中搜索相关信息。
适合查询产品信息、政策文档、FAQ等内部资料。
"""
# 模拟知识库查询
knowledge_base = {
"退款": "退款政策:购买7天内可以申请退款,需要保留原始包装",
"发货": "发货时间:工作日1-3天内发货,节假日顺延",
"保修": "保修政策:硬件产品享有1年保修期",
}
for keyword, info in knowledge_base.items():
if keyword in query:
return info
return "未在知识库中找到相关信息"
@tool
def get_order_status(order_id: str) -> str:
"""
查询订单状态。
输入:订单编号(格式:ORD-XXXXXXXX)
返回:订单的当前状态和物流信息
"""
# 模拟订单查询
mock_orders = {
"ORD-12345678": "已发货,预计明天到达,快递单号:SF1234567890",
"ORD-87654321": "处理中,预计明天发货",
}
return mock_orders.get(order_id, f"订单 {order_id} 不存在,请检查订单号是否正确")
# ============================
# 方式2:继承 BaseTool(更灵活)
# ============================
from typing import Type
from pydantic import BaseModel, Field
class WeatherInput(BaseModel):
city: str = Field(description="城市名称")
unit: str = Field(default="celsius", description="温度单位:celsius 或 fahrenheit")
class WeatherTool(BaseTool):
name: str = "get_weather"
description: str = "获取指定城市的当前天气信息"
args_schema: Type[BaseModel] = WeatherInput
def _run(self, city: str, unit: str = "celsius") -> str:
"""同步执行"""
# 模拟天气数据
weather = {"北京": 15, "上海": 20, "广州": 28}
temp = weather.get(city, 18)
if unit == "fahrenheit":
temp = temp * 9/5 + 32
unit_str = "°F"
else:
unit_str = "°C"
return f"{city}:{temp}{unit_str},晴"
async def _arun(self, city: str, unit: str = "celsius") -> str:
"""异步执行(可以调用异步API)"""
return self._run(city, unit) # 这里简单地调用同步版本
# ============================
# 创建 Agent
# ============================
tools = [calculate, search_knowledge, get_order_status, WeatherTool()]
# 系统提示
prompt = ChatPromptTemplate.from_messages([
("system", """你是一个智能客服助手。
你可以使用以下工具帮助用户:
- calculate:数学计算
- search_knowledge:查询产品政策和FAQ
- get_order_status:查询订单状态
- get_weather:获取天气
遇到需要工具的问题先使用工具,再给出回答。"""),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"), # Agent 推理空间
])
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 创建 OpenAI Tools Agent
agent = create_openai_tools_agent(llm, tools, prompt)
# AgentExecutor:负责运行循环
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 打印推理过程
max_iterations=5,
return_intermediate_steps=True # 返回中间步骤
)
# ============================
# 使用 Agent
# ============================
# 单次对话
result = agent_executor.invoke({
"input": "帮我查询订单 ORD-12345678 的状态,另外北京今天天气怎么样?",
"chat_history": []
})
print(result["output"])
# 带历史的多轮对话
from langchain_core.messages import HumanMessage, AIMessage
chat_history = []
def chat_with_agent(user_message: str) -> str:
result = agent_executor.invoke({
"input": user_message,
"chat_history": chat_history
})
# 更新历史
chat_history.extend([
HumanMessage(content=user_message),
AIMessage(content=result["output"])
])
return result["output"]
# 多轮对话测试
print(chat_with_agent("我想了解退款政策"))
print(chat_with_agent("如果我昨天买的,还能退吗?")) # 应该记住上下文
上面代码中有几个关键概念值得理解:
MessagesPlaceholder("agent_scratchpad"):这是 Agent 的"推理空间"——LangChain 会把工具调用的中间步骤(调用了什么工具、得到什么结果)填充到这里,让模型能看到之前的推理过程AgentExecutor:它是整个 Agent 循环的"驱动器"。设置verbose=True可以在终端看到完整的推理过程,方便调试return_intermediate_steps=True:让我们能在最终结果中看到 Agent 经历了哪些中间步骤——调用了哪些工具、传了什么参数、得到什么结果
自定义 Agent 执行策略
在生产环境中,你需要对 Agent 的执行行为做更精细的控制。以下参数是最常用的:
max_iterations:防止 Agent 陷入无限循环(比如模型反复调用同一个工具)max_execution_time:设置总超时时间,保护用户体验handle_parsing_errors:当模型输出格式异常时自动恢复,而不是直接报错early_stopping_method:超限时的停止策略——"generate"会让模型根据当前信息生成一个尽可能好的回答
# 控制 Agent 的执行行为
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=10, # 最大循环次数
max_execution_time=30, # 最大执行时间(秒)
handle_parsing_errors=True, # 自动处理解析错误
early_stopping_method="generate", # 超限时如何停止
verbose=True
)
小结
LangChain Agent 的关键组件:
@tool装饰器:最快的工具定义方式BaseTool:需要复杂逻辑时使用create_openai_tools_agent:创建使用 OpenAI Function Calling 的 AgentAgentExecutor:负责运行 Agent 循环,处理工具调用