LangChain Agents 和 Tools 完全指南:打造你的智能助手
想象一下:你请了一位博学多才的私人助理,TA 不仅能回答各种问题,还能主动查天气、算数学、搜资料、订机票......这就是 LangChain Agents 和 Tools 能为你创造的神奇体验。本文将带你全面了解如何用 LangChain 1.2.0 构建能够自主决策、调用工具的智能 Agent。
1. 初识 Tools 和 Agents
1.1 什么是 Tools?
Tools(工具)就像是给 AI 配备的"瑞士军刀"------它们扩展了 AI 的能力边界,让 AI 从"只会说话"变成"能办事"。
想象一下:
- 没有工具的 AI:就像一位被关在房间里的智者,只能靠记忆回答问题
- 有了工具的 AI:就像一位配备了手机、计算器、地图的现代人,可以实时获取信息、执行操作
Tools 能做什么?
- 🌤️ 获取实时数据(天气、股票、新闻)
- 🧮 执行计算和代码
- 🔍 查询数据库和知识库
- 🌐 调用外部 API 和系统
1.2 什么是 Agents?
Agents(智能体)是"大脑+手脚"的完美结合------它能理解任务、制定计划、调用工具,直到完成目标。
用户请求 → Agent 分析 → 选择工具 → 执行工具 → 检查结果 → 完成或继续
Agent 就像一个聪明的项目经理:
- 理解需求:分析用户想要什么
- 规划步骤:决定需要做什么
- 调度工具:调用合适的工具收集信息
- 整合结果:把工具返回的信息组织成完整回答
2. Tools 基础:从简单到复杂
2.1 使用 @tool 装饰器创建工具
创建工具最简单的方式是使用 @tool 装饰器。函数的 docstring 会成为工具描述,type hints 用于定义参数。
python
from langchain_core.tools import tool
@tool
def search_database(query: str, limit: int = 10) -> str:
"""搜索客户数据库中匹配的记录。
Args:
query: 搜索关键词
limit: 返回结果的最大数量
Returns:
搜索结果字符串
"""
# 模拟数据库搜索
mock_data = [
{"id": 1, "name": "张三", "email": "zhangsan@example.com"},
{"id": 2, "name": "李四", "email": "lisi@example.com"},
{"id": 3, "name": "王五", "email": "wangwu@example.com"},
]
results = [item for item in mock_data if query.lower() in item["name"].lower()]
return f"找到 {min(len(results), limit)} 条结果: {results[:limit]}"
@tool
def get_current_time(timezone: str = "Asia/Shanghai") -> str:
"""获取指定时区的当前时间。
Args:
timezone: 时区名称,如 'Asia/Shanghai', 'America/New_York'
Returns:
当前时间字符串
"""
from datetime import datetime
import pytz
try:
tz = pytz.timezone(timezone)
current_time = datetime.now(tz)
return f"当前时间 ({timezone}): {current_time.strftime('%Y-%m-%d %H:%M:%S')}"
except Exception as e:
return f"错误: 未知时区 '{timezone}'"
💡 小贴士:
- 使用
snake_case命名工具- docstring 很重要!它告诉模型什么时候该用这个工具
- 必须添加 type hints,否则 LangChain 无法生成参数 schema
2.2 自定义工具名称和描述
有时候你需要更精确地控制工具的名称和描述,让模型更好地理解何时使用它。
python
# 自定义工具名称
@tool("web_search") # 使用自定义名称
def search_web(query: str) -> str:
"""搜索网络上的信息。"""
# 模拟网络搜索
return f"搜索结果: 关于 '{query}' 的相关信息..."
# 自定义工具描述
@tool(
"calculator",
description="执行数学计算。当需要进行任何数学运算时使用此工具。支持加减乘除和复杂表达式。"
)
def calculate(expression: str) -> str:
"""计算数学表达式。"""
try:
# 注意: 生产环境应使用更安全的计算方式
result = eval(expression)
return f"计算结果: {expression} = {result}"
except Exception as e:
return f"计算错误: {str(e)}"
| 参数 | 说明 | 推荐做法 |
|---|---|---|
name |
工具名称 | 使用 snake_case,如 get_weather |
description |
工具描述 | 详细说明使用场景,"何时使用此工具" |
args_schema |
参数结构 | 复杂参数用 Pydantic 定义 |
return_direct |
直接返回 | 设为 True 时跳过模型直接返回结果 |
2.3 使用 Pydantic 定义复杂参数
当工具需要复杂输入时,用 Pydantic 模型定义参数 schema 是最佳选择。
python
from pydantic import BaseModel, Field
from typing import Literal
class WeatherInput(BaseModel):
"""天气查询的输入参数。"""
location: str = Field(
description="城市名称"
)
units: Literal["celsius", "fahrenheit"] = Field(
default="celsius",
description="温度单位: celsius(摄氏度) 或 fahrenheit(华氏度)"
)
include_forecast: bool = Field(
default=False,
description="是否包含5天天气预报"
)
@tool(args_schema=WeatherInput)
def get_weather(location: str, units: str = "celsius", include_forecast: bool = False) -> str:
"""获取指定城市的当前天气和可选的天气预报。
使用此工具查询任何城市的天气信息。
"""
# 模拟天气数据
weather_data = {
"北京": {"temp": 25, "condition": "晴天", "humidity": 45},
"上海": {"temp": 28, "condition": "多云", "humidity": 60},
"成都": {"temp": 18, "condition": "小雨", "humidity": 70},
}
# 查找城市
city_data = weather_data.get(location)
if city_data is None:
return f"未找到城市 '{location}' 的天气信息"
# 格式化温度
temp = city_data["temp"]
if units == "fahrenheit":
temp = temp * 9/5 + 32
temp_str = f"{temp}°F"
else:
temp_str = f"{temp}°C"
result = f"{location}天气: {city_data['condition']}, 温度 {temp_str}, 湿度 {city_data['humidity']}%"
if include_forecast:
result += "\n5天预报: 明天多云, 后天晴, 之后几天温度稳定"
return result
2.4 将工具绑定到模型
定义好工具后,需要用 bind_tools() 方法将工具绑定到模型,让模型知道有哪些工具可用。
python
from langchain.chat_models import init_chat_model
import os
# 初始化模型
baidu_api_key = os.getenv("BAIDU_API_KEY")
baidu_base_url = os.getenv("BAIDU_BASEURL")
model = init_chat_model(
model="kimi-k2.5",
model_provider="openai",
base_url=baidu_base_url,
api_key=baidu_api_key,
temperature=0.7,
)
# 绑定工具到模型
tools = [get_weather, calculate, search_database]
model_with_tools = model.bind_tools(tools)
print(f"✅ 工具已绑定到模型,共 {len(tools)} 个工具")
3. 完整工具调用流程
3.1 观察模型如何决定是否调用工具
python
# 测试工具调用
user_message = "北京和上海的天气怎么样?"
print(f"👤 用户: {user_message}\n")
# 模型决定是否调用工具
response = model_with_tools.invoke(user_message)
print(f"🤖 模型响应类型: {type(response).__name__}")
print(f"🔧 是否有工具调用: {len(response.tool_calls) > 0}")
if response.tool_calls:
print(f"\n📋 工具调用详情:")
for tc in response.tool_calls:
print(f" - 工具名: {tc['name']}")
print(f" 参数: {tc['args']}")
print(f" 调用ID: {tc['id']}")
3.2 完整的工具调用循环
真正的工具调用是一个交互过程:模型决定调用 → 执行工具 → 返回结果 → 模型生成回答。
python
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
# Step 1: 用户输入
messages = [HumanMessage(content="北京今天天气怎么样?请用摄氏度显示。")]
print(f"1️⃣ 用户输入: {messages[0].content}\n")
# Step 2: 模型决定调用工具
ai_response = model_with_tools.invoke(messages)
messages.append(ai_response)
print(f"2️⃣ 模型决定调用工具")
if ai_response.tool_calls:
# Step 3: 执行工具
for tool_call in ai_response.tool_calls:
print(f"\n3️⃣ 执行工具: {tool_call['name']}")
print(f" 参数: {tool_call['args']}")
# 执行对应的工具
if tool_call['name'] == 'get_weather':
result = get_weather.invoke(tool_call)
elif tool_call['name'] == 'calculate':
result = calculate.invoke(tool_call)
else:
result = ToolMessage(content="未知工具", tool_call_id=tool_call['id'])
print(f" 结果: {result.content}")
messages.append(result)
# Step 4: 模型生成最终回答
final_response = model_with_tools.invoke(messages)
print(f"\n4️⃣ 最终回答:\n{final_response.content}")
4. Agents 基础:创建你的智能助手
4.1 使用 create_agent 创建 Agent
LangChain 提供了 create_agent 函数来创建 Agent。这是最原生的方式,与 LangGraph 的 create_react_agent 不同。
python
from langchain.agents import create_agent
# 定义更多工具
@tool
def search_knowledge(query: str) -> str:
"""搜索知识库。
Args:
query: 搜索关键词
"""
knowledge_base = {
"python": "Python 是一种高级编程语言,以简洁易读著称。",
"langchain": "LangChain 是一个用于开发大语言模型应用的框架。",
"agent": "Agent 是能够自主决策和调用工具的 AI 系统。",
"tool": "Tool 是 Agent 可以调用的外部功能或服务。",
}
for key, value in knowledge_base.items():
if key in query.lower():
return value
return f"未找到关于 '{query}' 的信息。"
@tool
def create_task(task_name: str, priority: str = "medium") -> str:
"""创建一个新任务。
Args:
task_name: 任务名称
priority: 优先级 (high/medium/low)
"""
return f"✅ 已创建任务: {task_name} (优先级: {priority})"
# 创建 Agent 工具列表
agent_tools = [get_weather, calculate, search_knowledge, create_task]
# 创建 Agent
my_agent = create_agent(
model=model,
tools=agent_tools,
system_prompt="""你是一个智能助手,可以帮助用户:
1. 查询天气
2. 进行数学计算
3. 搜索知识库
4. 创建任务
请根据用户的需求选择合适的工具,并给出友好的回复。"""
)
print("✅ Agent 创建成功")
4.2 Agent 参数详解
| 参数 | 类型 | 说明 | 推荐值 |
|---|---|---|---|
model |
str 或 Model | 推理引擎 | 使用 init_chat_model 初始化 |
tools |
list | 可用工具列表 | 根据需求定义 |
system_prompt |
str | 系统提示,塑造 Agent 行为 | 详细描述 Agent 角色和能力 |
name |
str | Agent 名称 | 使用 snake_case,如 travel_assistant |
response_format |
Schema | 结构化输出配置 | 需要格式化输出时使用 |
state_schema |
AgentState | 自定义状态字段 | 需要额外状态时使用 |
middleware |
list | 中间件钩子 | 需要自定义处理逻辑时使用 |
4.3 调用 Agent:invoke 和 stream
python
# Agent invoke 调用 - 同步方式
result = my_agent.invoke({
"messages": [{"role": "user", "content": "帮我查询北京的天气,然后计算 100 + 200 等于多少"}]
})
# 获取最后一条 AI 消息
for msg in reversed(result["messages"]):
if isinstance(msg, AIMessage) and not getattr(msg, 'tool_calls', None):
print(f"最终回复:\n{msg.content}")
break
python
# Agent stream 调用 - 流式输出
for chunk in my_agent.stream(
{"messages": [{"role": "user", "content": "LangChain 是什么?"}]},
stream_mode="values"
):
# 打印每个事件中的最后一条消息
if "messages" in chunk:
last_msg = chunk["messages"][-1]
if isinstance(last_msg, AIMessage):
tool_calls = getattr(last_msg, 'tool_calls', None)
if tool_calls:
print(f"[调用工具: {tool_calls[0]['name'] if tool_calls else 'N/A'}]")
elif last_msg.content:
print(f"AI: {last_msg.content[:200]}...")
5. Agents 进阶特性
5.1 命名 Agent
为多 Agent 系统设置名称,便于识别和调试。
python
named_agent = create_agent(
model=model,
tools=agent_tools,
name="research_assistant", # 使用 snake_case 命名
system_prompt="你是一个研究助手,帮助用户搜索和整理信息。"
)
print(f"✅ Agent 创建成功,名称: {named_agent.name}")
5.2 结构化输出
使用 response_format 让 Agent 输出结构化的数据。
python
from pydantic import BaseModel, Field
# 定义结构化输出 Schema
class TravelPlan(BaseModel):
"""旅行计划"""
destination: str = Field(description="目的地城市")
duration_days: int = Field(description="旅行天数")
budget: float = Field(description="预算(元)")
activities: list[str] = Field(description="推荐活动列表")
notes: str = Field(description="旅行注意事项")
# 创建带结构化输出的模型
model_structured = model.with_structured_output(TravelPlan)
# 调用
result = model_structured.invoke("帮我规划一个3天的北京旅行,预算5000元")
print(f"目的地: {result.destination}")
print(f"天数: {result.duration_days}")
print(f"预算: {result.budget}元")
print(f"活动: {result.activities}")
print(f"注意事项: {result.notes}")
5.3 多轮对话
多轮对话是 Agent 的核心能力,通过维护对话历史,Agent 可以理解上下文。
python
# 手动管理对话历史
conversation_messages = []
# ===== 第一轮对话 =====
user_input_1 = "北京今天天气怎么样?"
print(f"👤 用户: {user_input_1}")
conversation_messages.append({"role": "user", "content": user_input_1})
result_1 = my_agent.invoke({"messages": conversation_messages})
# 获取 AI 回复并添加到历史
for msg in reversed(result_1["messages"]):
if isinstance(msg, AIMessage) and not getattr(msg, 'tool_calls', None):
print(f"🤖 AI: {msg.content[:200]}...")
conversation_messages.append({"role": "assistant", "content": msg.content})
break
# ===== 第二轮对话 - 引用上下文 =====
user_input_2 = "那上海呢?" # Agent 能理解这是在问上海的天气
print(f"👤 用户: {user_input_2}")
conversation_messages.append({"role": "user", "content": user_input_2})
result_2 = my_agent.invoke({"messages": conversation_messages})
for msg in reversed(result_2["messages"]):
if isinstance(msg, AIMessage) and not getattr(msg, 'tool_calls', None):
print(f"🤖 AI: {msg.content[:200]}...")
conversation_messages.append({"role": "assistant", "content": msg.content})
break
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 手动管理 | 手动维护 messages 列表 | 简单场景、学习测试 |
| checkpointer | 自动保存和恢复状态 | 生产环境、需要持久化 |
| session 管理 | 按 session_id 管理对话 | 多用户场景 |
6. 实战案例:旅行规划助手
现在让我们整合所学知识,创建一个完整的旅行规划助手。
6.1 定义旅行相关工具
python
from pydantic import BaseModel, Field
from typing import Optional, Literal
class AttractionInput(BaseModel):
"""景点搜索输入参数"""
city: str = Field(description="城市名称")
category: Optional[str] = Field(
default=None,
description="景点类型: 历史/自然/文化/娱乐"
)
@tool(args_schema=AttractionInput)
def search_attractions(city: str, category: Optional[str] = None) -> str:
"""搜索指定城市的旅游景点。
Args:
city: 城市名称
category: 景点类型(可选)
"""
# 模拟景点数据库
attractions_db = {
"北京": {
"历史": ["故宫", "长城", "天坛", "颐和园"],
"文化": ["国家博物馆", "798艺术区", "南锣鼓巷"],
"自然": ["香山", "北海公园", "奥林匹克森林公园"],
"娱乐": ["欢乐谷", "环球影城", "三里屯"],
},
"上海": {
"历史": ["外滩", "豫园", "静安寺"],
"文化": ["上海博物馆", "中华艺术宫", "田子坊"],
"自然": ["世纪公园", "辰山植物园", "崇明岛"],
"娱乐": ["迪士尼乐园", "南京路", "新天地"],
},
"成都": {
"历史": ["武侯祠", "杜甫草堂", "宽窄巷子"],
"文化": ["四川博物院", "锦里", "文殊院"],
"自然": ["青城山", "都江堰", "大熊猫基地"],
"娱乐": ["春熙路", "太古里", "九眼桥"],
},
}
if city not in attractions_db:
return f"未找到城市 '{city}' 的景点信息"
city_attractions = attractions_db[city]
if category and category in city_attractions:
result = f"{city}的{category}景点: {', '.join(city_attractions[category])}"
else:
result = f"{city}的热门景点:\n"
for cat, places in city_attractions.items():
result += f" - {cat}: {', '.join(places)}\n"
return result
@tool
def calculate_budget(
days: int,
daily_hotel_cost: float = 300,
daily_food_cost: float = 150,
daily_transport_cost: float = 50,
ticket_cost: float = 200,
shopping_budget: float = 500,
) -> str:
"""计算旅行预算。
Args:
days: 旅行天数
daily_hotel_cost: 每日酒店费用(元)
daily_food_cost: 每日餐饮费用(元)
daily_transport_cost: 每日交通费用(元)
ticket_cost: 门票总费用(元)
shopping_budget: 购物预算(元)
"""
hotel_total = days * daily_hotel_cost
food_total = days * daily_food_cost
transport_total = days * daily_transport_cost
total = hotel_total + food_total + transport_total + ticket_cost + shopping_budget
breakdown = f"""预算明细({days}天旅行):
=============================
住宿费用: {hotel_total}元 ({days}天 × {daily_hotel_cost}元/天)
餐饮费用: {food_total}元 ({days}天 × {daily_food_cost}元/天)
交通费用: {transport_total}元 ({days}天 × {daily_transport_cost}元/天)
门票费用: {ticket_cost}元
购物预算: {shopping_budget}元
=============================
总计: {total}元"""
return breakdown
@tool
def get_flight_info(origin: str, destination: str) -> str:
"""查询航班信息。
Args:
origin: 出发城市
destination: 目的地城市
"""
# 模拟航班数据
flights = {
("北京", "上海"): {"price": 800, "duration": "2小时", "airlines": ["国航", "东航", "南航"]},
("北京", "成都"): {"price": 1200, "duration": "3小时", "airlines": ["国航", "川航"]},
("上海", "成都"): {"price": 1000, "duration": "2.5小时", "airlines": ["东航", "川航"]},
}
key = (origin, destination)
reverse_key = (destination, origin)
if key in flights:
info = flights[key]
return f"{origin} → {destination}: 票价约{info['price']}元,飞行时间{info['duration']}"
elif reverse_key in flights:
info = flights[reverse_key]
return f"{origin} → {destination}: 票价约{info['price']}元,飞行时间{info['duration']}"
else:
return f"未找到 {origin} 到 {destination} 的直飞航班信息"
6.2 创建旅行规划 Agent
python
# 旅行助手工具列表
travel_tools = [get_weather, search_attractions, calculate_budget, get_flight_info]
# 创建旅行规划助手 Agent
travel_agent = create_agent(
model=model,
tools=travel_tools,
name="travel_assistant",
system_prompt="""你是一个专业的旅行规划助手,可以帮助用户:
1. 🌤️ 查询目的地天气 - 使用 get_weather 工具
2. 🏛️ 搜索旅游景点 - 使用 search_attractions 工具
3. 💰 计算旅行预算 - 使用 calculate_budget 工具
4. ✈️ 查询航班信息 - 使用 get_flight_info 工具
当用户询问旅行相关问题时,请:
- 主动询问缺失的信息(如出发地、目的地、天数、预算等)
- 综合使用多个工具提供完整建议
- 给出详细且有条理的旅行建议
回复时请使用中文,保持友好和专业。"""
)
print(f"✅ 旅行规划助手 Agent 创建成功,名称: {travel_agent.name}")
6.3 使用旅行规划助手
python
# 示例1:简单查询
user_query = "我想去南京玩3天,请帮我看一下天气"
print(f"👤 用户: {user_query}\n")
result = travel_agent.invoke({
"messages": [{"role": "user", "content": user_query}]
})
# 打印最终回复
for msg in reversed(result["messages"]):
if isinstance(msg, AIMessage) and not getattr(msg, 'tool_calls', None):
print(f"🤖 旅行助手:\n{msg.content}")
break
python
# 示例2:复杂规划请求
user_query = """我想从上海去成都旅游,计划玩4天。请帮我:
1. 查一下成都的天气
2. 推荐一些好玩的历史景点
3. 查一下航班信息
4. 帮我算一下预算(酒店按400元/天)"""
print(f"👤 用户:\n{user_query}\n")
print("-"*50)
# 流式输出观察 Agent 的思考过程
for chunk in travel_agent.stream(
{"messages": [{"role": "user", "content": user_query}]},
stream_mode="values"
):
if "messages" in chunk:
last_msg = chunk["messages"][-1]
if isinstance(last_msg, AIMessage):
tool_calls = getattr(last_msg, 'tool_calls', None)
if tool_calls:
print(f"\n🔧 正在调用工具: {[tc['name'] for tc in tool_calls]}")
elif last_msg.content:
print(f"\n🤖 旅行助手回复:\n{last_msg.content}")
7. 最佳实践总结
7.1 工具设计最佳实践
┌─────────────────────────────────────────────────────────────┐
│ 工具命名规范 │
├─────────────────────────────────────────────────────────────┤
│ ✅ 使用 snake_case: get_weather, search_database │
│ ❌ 避免驼峰命名: getWeather, SearchDatabase │
│ ✅ 使用动词开头: get_, search_, calculate_ │
│ ❌ 避免模糊命名: tool1, func_a, my_tool │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 工具描述编写指南 │
├─────────────────────────────────────────────────────────────┤
│ ✅ 说明何时使用: │
│ "当需要查询天气信息时使用此工具" │
│ │
│ ✅ 说明参数含义: │
│ "location: 城市名称,如'北京'、'上海'" │
│ │
│ ❌ 避免过于简单: │
│ "获取天气"、"搜索工具" │
└─────────────────────────────────────────────────────────────┘
7.2 Agent 设计检查清单
| 检查项 | 说明 |
|---|---|
| 工具精简 | 每个 Agent 只配备必要的工具,避免工具过多导致选择困难 |
| 描述清晰 | system_prompt 中明确说明 Agent 的能力和工具用途 |
| 错误处理 | 工具执行失败时返回友好的错误信息 |
| 多轮对话 | 维护好 messages 历史,让 Agent 理解上下文 |
| 命名规范 | Agent 和工具都使用 snake_case 命名 |
7.3 完整 Agent 配置模板
python
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
# 1. 初始化模型
model = init_chat_model(
model="gpt-4o",
model_provider="openai",
api_key=api_key,
base_url=base_url,
temperature=0.3, # 低温度让 Agent 更确定性地选择工具
)
# 2. 定义工具(根据业务需求)
tools = [tool_1, tool_2, tool_3]
# 3. 创建 Agent
agent = create_agent(
model=model,
tools=tools,
name="my_assistant",
system_prompt="""你是一个专业的助手,可以帮助用户完成以下任务:
- 任务1:使用 tool_1
- 任务2:使用 tool_2
- 任务3:使用 tool_3
请根据用户需求选择合适的工具,并给出清晰、友好的回复。"""
)
# 4. 调用 Agent
result = agent.invoke({
"messages": [{"role": "user", "content": "用户输入"}]
})
8. 总结
本文全面介绍了 LangChain 1.2.0 中 Agents 和 Tools 的核心概念和用法:
核心概念回顾
| 概念 | 说明 |
|---|---|
| Tool | 扩展 Agent 能力的函数,有明确的输入输出 |
| @tool 装饰器 | 创建工具的最简单方式,docstring 成为描述 |
| Agent | 结合模型和工具,能自主决策和执行任务 |
| create_agent | LangChain 原生的 Agent 创建函数 |
| bind_tools | 将工具绑定到模型,让模型知道可用的工具 |
| tool_calls | 模型返回的工具调用请求 |
| 多轮对话 | 维护 messages 历史,实现上下文理解 |
关键步骤
- 定义工具 :使用
@tool装饰器,写好 docstring 和 type hints - 绑定工具 :使用
model.bind_tools(tools)让模型知道可用工具 - 创建 Agent :使用
create_agent()组合模型和工具 - 调用 Agent :使用
invoke()或stream()与 Agent 交互 - 维护历史:手动管理或使用 checkpointer 保存对话状态
📝 个人笔记:本文档基于 LangChain 1.2.0 版本,不同版本可能存在 API 差异,请以官方文档为准。Agents 和 Tools 是构建智能应用的核心能力,掌握它们能让你的 AI 应用从"聊天机器人"升级为"智能助手",Agent中多轮对话和中间件是个复杂的用法,后面需要单独学习。