本文目录导读:

LangChain 的工具调用是其核心功能之一,它允许 LLM 与外部系统(API、数据库、自定义函数等)进行交互,其底层原理核心是 Function Calling(函数调用)能力。
以下是实现 LangChain 工具调用的完整指南,从最简单的自定义函数到复杂的多工具链。
核心概念
- Tool(工具): 一个被 LLM 调用的函数,它需要有一个清晰的名称、描述和参数定义。
- 模型绑定: 将工具的定义(通过 JSON Schema)传给 LLM。
- 执行循环: LLM 决定调用哪个工具 -> 提取参数 -> 执行工具函数 -> 将结果返回给 LLM -> LLM 生成最终答案。
使用 @tool 装饰器(推荐,最简单)
这是最常用的方式,只需将一个普通 Python 函数转化为工具。
安装依赖:
pip install langchain langchain-openai
代码实现:
from langchain_openai import ChatOpenAI
from langchain.agents import tool
# 1. 定义工具 - 使用 @tool 装饰器
@tool
def calculate_sum(a: int, b: int) -> int:
"""计算两个整数的和。"""
return a + b
@tool
def get_weather(location: str) -> str:
"""获取指定地点的天气情况(模拟数据)。"""
# 这里应该调用真实的天气 API
return f"{location} 今天天气晴朗,温度25°C。"
# 2. 初始化模型
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 3. 将工具绑定到模型
tools = [calculate_sum, get_weather]
llm_with_tools = llm.bind_tools(tools)
# 4. 调用模型
response = llm_with_tools.invoke("北京天气怎么样?")
print(response)
# 5. 检查模型是否想调用了工具
if response.tool_calls:
for tool_call in response.tool_calls:
# tool_call 包含 'name' 和 'args'
print(f"调用工具: {tool_call['name']}, 参数: {tool_call['args']}")
# 手动执行工具(更常见的做法是用 Agent)
if tool_call['name'] == 'get_weather':
result = get_weather.invoke(tool_call['args'])
print(f"工具返回: {result}")
输出示例:
content='' additional_kwargs={'tool_calls': [{'id': 'call_...', 'function': {'name': 'get_weather', 'arguments': '{"location": "北京"}'}}]}
调用工具: get_weather, 参数: {'location': '北京'}
工具返回: 北京 今天天气晴朗,温度25°C。
使用 Tool 类(手动定义,更灵活)
当需要更精细控制时(例如指定返回格式、处理异常),可以使用 Tool 类。
from langchain.tools import Tool
from langchain_openai import ChatOpenAI
def multiply(a: int, b: int) -> int:
"""乘法函数"""
return a * b
# 使用 Tool 类包装
multiply_tool = Tool(
name="multiply",
func=multiply,
description="非常有用,当你需要将两个整数相乘时。",
# 可选:定义参数架构(如果不定义,LLM 可能不准确)
args_schema=None # 通常可以留空,依赖 LLM 的理解
)
# 另一个例子:输入参数更复杂的
from pydantic import BaseModel, Field
class CalculatorInput(BaseModel):
a: int = Field(description="第一个数字")
b: int = Field(description="第二个数字")
calculator_tool = Tool(
name="calculator",
func=multiply, # 假设乘法
description="当需要计算两个数字的乘积时使用。",
args_schema=CalculatorInput # 提供 Pydantic schema 帮助 LLM 理解参数
)
llm = ChatOpenAI(model="gpt-4o-mini")
llm_with_tools = llm.bind_tools([calculator_tool])
response = llm_with_tools.invoke("3 * 5 等于多少?")
print(response.tool_calls)
使用 Agent(自动执行循环)
手动解析 tool_calls 很麻烦,LangChain Agent 可以自动处理这个循环:调用 LLM -> 解析工具调用 -> 执行工具 -> 结果反馈给 LLM -> 生成最终答案。
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain.agents import tool
from langchain_core.prompts import ChatPromptTemplate
@tool
def search_web(query: str) -> str:
"""搜索网络获取信息(模拟)"""
return f"'{query}' 的搜索结果:这是模拟数据。"
@tool
def get_current_time() -> str:
"""获取当前时间"""
from datetime import datetime
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 1. 创建 Agent
tools = [search_web, get_current_time]
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个聪明的助手,请通过调用必要的工具来回答用户的问题。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"), # 这是关键,Agent 的内部工作空间
])
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 创建 agent(使用 tool-calling 能力)
agent = create_tool_calling_agent(llm, tools, prompt)
# 2. 创建 AgentExecutor 来自动执行循环
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 打印运行过程,便于调试
handle_parsing_errors=True
)
# 3. 运行
result = agent_executor.invoke({"input": "现在几点了?顺便帮我搜一下今天的新闻。"})
print("\n最终答案:", result['output'])
输出示例(verbose=True):
> Entering new AgentExecutor chain...
Invoking: `get_current_time` with `{}`
2024-12-20 10:30:00
Invoking: `search_web` with `{'query': '今天的热点新闻'}`
'今天的热点新闻' 的搜索结果:这是模拟数据。
> Finished chain.
最终答案: 现在是 2024-12-20 10:30:00,关于今天的新闻,这是模拟搜索结果...
进阶技巧与最佳实践
工具描述的“玄学”
LLM 选择工具主要靠描述,描述要清晰、具体。
- ❌ 不好:
get_user_info,描述:“获取信息”。 - ✅ 好:
get_user_info,描述:“当用户想查看自己的账户信息、余额或用户名时使用,接受一个 user_id 参数。”
错误处理
工具可能会失败(如 API 超时),让工具返回明确的错误字符串,LLM 会理解并尝试重试或告知用户。
@tool
def get_stock_price(symbol: str) -> str:
"""获取股票实时价格"""
try:
# 模拟 API 调用
if symbol == "ERROR":
raise ValueError("API 调用失败")
price = 100.0
return f"{symbol} 当前价格为 ${price}"
except Exception as e:
return f"查询失败:{str(e)}"
结构化输出(Tools 返回复杂对象)
通过 Pydantic 定义返回结构。
from pydantic import BaseModel
from typing import Optional
class MovieInfo(BaseModel): str
year: Optional[int] = None
rating: float
@tool(args_schema=..., return_direct=True)
def search_movie(query: str) -> MovieInfo:
"""搜索电影信息"""
return MovieInfo(title="Inception", year=2010, rating=8.8)
多模态工具(非文本输入)
工具可以接受图片、文件等输入。
@tool
def analyze_image(image_path: str) -> str:
"""分析图片内容(路径由 LLM 参数提供)"""
# 这里可以调用图像识别 API
return "图片中包含一只猫"
总结选型
| 场景 | 推荐方法 | 理由 |
|---|---|---|
| 简单查询 / 单次计算 | @tool + bind_tools + 手动解析 |
轻量级,控制力强,适合无状态调用 |
| 多步骤推理 / 复杂任务 | create_tool_calling_agent + AgentExecutor |
自动处理循环、错误重试、中间步骤 |
| 不希望 LLM 有权决定调用 | Tool 类 + invoke 直接调用 |
手动调用工具,绕过 LLM 决策 |
| 需要异步 / 流式输出 | astream_events 或 RunnableLambda |
高级用法,适合实时交互 |
建议从 方法一(@tool) 和 方法三(Agent) 开始,它们覆盖了 90% 的需求。