LangChain工具调用怎么实现

wen python案例 1

本文目录导读:

LangChain工具调用怎么实现

  1. 核心概念
  2. 方法一:使用 @tool 装饰器(推荐,最简单)
  3. 方法二:使用 Tool 类(手动定义,更灵活)
  4. 方法三:使用 Agent(自动执行循环)
  5. 进阶技巧与最佳实践
  6. 总结选型

LangChain 的工具调用是其核心功能之一,它允许 LLM 与外部系统(API、数据库、自定义函数等)进行交互,其底层原理核心是 Function Calling(函数调用)能力。

以下是实现 LangChain 工具调用的完整指南,从最简单的自定义函数到复杂的多工具链。

核心概念

  1. Tool(工具): 一个被 LLM 调用的函数,它需要有一个清晰的名称、描述和参数定义。
  2. 模型绑定: 将工具的定义(通过 JSON Schema)传给 LLM。
  3. 执行循环: 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_eventsRunnableLambda 高级用法,适合实时交互

建议从 方法一(@tool)方法三(Agent) 开始,它们覆盖了 90% 的需求。

抱歉,评论功能暂时关闭!