逆风飞翔的小叔头像
关注
【AI智能体】Langchain 工具调用实战操作详解封面图

【AI智能体】Langchain 工具调用实战操作详解

目录

一、前言

二、Tools 工具介绍

2.1 Tools 是什么

2.1.1 Tool 组成结构

2.1.2 Tool 的重要性

2.2 Tools 有什么作用

2.3 Tool 的特点

2.4 Tool 使用场景

三、工具的调用方式

3.1 工具的两种调用

3.1.1 直接调用

3.1.2 绑定到模型(主流)调用

3.1.3 工具调用完整流程

3.2 从Message流转看工具的调用

3.3 工具描述的各部分参数详解

3.3.1 convert_to_openai_to

3.3.2 description 说明

3.3.3 参数说明

3.3.4 参数类型说明

3.3.5 参数默认值

3.4 使用@tool装饰器定义工具

3.4.1 自定义工具描述:description

3.4.2 添加工具描述:description

3.4.3 更改工具名称

3.5 自定义args_schema

3.5.1 pydantic类型的定义

3.5.2 限定参数取值范围

3.6 使用Json Schema定义

四、写在最后


一、前言

在使用Langchain 进行智能体开发中,会涉及到一个非常重要的组件调用,就是工具(Tool),工具作为AI应用与大模型的中间层,承载着非常重要的角色,可以说搞清楚了Tool在其中的作用,对AI智能体调用的完整过程就有了一个深刻全面的理解,本篇将详细介绍下Langchain 中的工具使用。

二、Tools 工具介绍

2.1 Tools 是什么

LangChain 工具(Tool)是赋予大模型调用外部能力的标准化接口。

大模型本身只能生成文本,无法主动联网、查数据库、执行代码。工具就是把这些外部能力封装成 LLM 能理解、能调用的函数,让模型从“只会说”变成“能动手做”。

2.1.1 Tool 组成结构

一个工具通常包含三部分:

  • 名称(name):唯一标识

  • 描述(description):LLM 据此判断何时调用,最关键

  • 功能(function):实际执行的代码逻辑

工具包(Toolkit)则是一组相关工具的集合,比如 SQLDatabaseToolkit 把列表、查结构、执行查询等工具打包在一起。

2.1.2 Tool 的重要性

要构建更强大的AI工程应用,只有生成文本这样的“ 纸上谈兵 ”能力自然是不够的。

工具是赋予大语言模型 与外部世界交互能力 的关键组件,从而能让智能体执行搜索、计算、数据库查询、邮件发送或调用第三方API等,进而构建功能强大的AI应用。借助工具,大模型才能从“ 认识世界 ”走向“ 改变世界 ”。

从图中可以看到,工具是构建智能体的核心要素之一,具有非常重要的地位

2.2 Tools 有什么作用

Tool的核心作用是扩展大模型的能力边界,具体包括:

  1. 突破知识时效性:通过搜索工具获取实时信息,弥补训练数据截止的局限。

  2. 突破能力限制:通过计算器、代码执行工具完成精确计算和复杂逻辑。

  3. 连接私有数据:通过数据库、文件系统工具访问企业或个人私有数据。

  4. 对接外部系统:通过 API 工具调用第三方服务,完成真实业务操作。

  5. 让 Agent 自主决策:工具是 Agent 的“手脚”,Agent 根据任务自主选择并调用合适的工具。

2.3 Tool 的特点

Tool 主要有如下特点:

  1. 描述驱动:LLM 完全依赖工具的 description 来决定是否调用、如何调用,描述写得越清楚,调用越准确。

  2. 标准化接口:所有工具都遵循统一调用方式(如 .invoke()),便于组合和替换。

  3. 可自定义:用 @tool 装饰器就能把普通 Python 函数变成工具,扩展成本低。

  4. 可组合:多个工具可同时提供给 Agent,由 Agent 按需调度。

  5. 安全可控:工具的执行逻辑由开发者定义,可以对输入输出做校验和限制。

2.4 Tool 使用场景

场景

典型工具

解决的问题

实时信息查询

DuckDuckGo、SerpAPI

获取最新新闻、股价、天气

知识问答

WikipediaQueryRun

补充百科类知识

数据分析/计算

PythonREPLTool

精确计算、数据处理

数据库交互

SQLDatabaseToolkit

自然语言查数据库

文件操作

FileManagementToolkit

读写、管理本地文件

业务系统对接

OpenAPIToolkit

按规范调用 REST API

多工具协作

Agent + 多工具

复杂任务自动拆解执行

一句话总结:

LangChain 工具是让大模型“能做事”的桥梁,通过标准化封装和描述驱动机制,使 LLM 能自主调用外部能力,适用于一切需要模型与真实世界交互的场景。

三、工具的调用方式

在LangChain中,工具(Tools)实际上是指明确定义了输入和输出的 可调用函数 。因此, 工具调用(Tool Calling) 也被称为 函数调用(Function Calling) 。具体来说主要有2种调用方式,下面分别介绍。

3.1 工具的两种调用

3.1.1 直接调用

直接调用这种方式,适合测试时使用,下面是一个基础的工具调用案例代码

from langchain_core.tools import Tool, tool

@tool
def get_weather(city: str) -> str:
    """
    获取指定城市的天气信息

    参数:
        city: 城市名称,如"北京"、"上海"

    返回:
        天气信息字符串
    """
    # 你的实现
    return city + "晴天,温度 15°C"

# 使用 .invoke() 方法
result = get_weather.invoke({"city": "北京"})
print(result)

运行一下效果如下

3.1.2 绑定到模型(主流)调用

这种方式,让AI来调用,开发中使用最多的方式

from dotenv import load_dotenv
import os

from langchain_openrouter import ChatOpenRouter

# 1、加载配置文件
load_dotenv(override=True)

OPENROUTER_API_KEY = os.getenv ("OPENROUTER_API_KEY")
OPENROUTER_API_BASE = os.getenv ("OPENROUTER_API_BASE")

# 2、模型初始化
model = ChatOpenRouter(
    model="deepseek/deepseek-v3.2",
    api_key=OPENROUTER_API_KEY,
    base_url=OPENROUTER_API_BASE,
)

from langchain_core.tools import tool

# 定义工具
@tool
def get_weather(city: str) -> str:
    """获取指定城市的天气"""
    # 你的实现
    return "晴天,温度 15°C"

# 绑定工具
model_with_tools = model.bind_tools([get_weather])

# AI 可以决定是否调用工具
response = model_with_tools.invoke("北京天气如何?")

# response = model_with_tools.invoke("2 + 3 = ?")
# 检查 AI 是否要调用工具
if response.tool_calls:
    print("AI 想调用工具:", response.tool_calls)
else:
    print("AI 直接回答:", response.content)

调用一下,看到下面的效果,在真实的业务场景下,可能会在tool的方法内部对接外部厂商的API ,以获取天气为例,就可以拿到真实的天气信息数据

3.1.3 工具调用完整流程

大模型能根据对话上下文决定何时调用工具以及传递哪些参数。

上面编写的LangChain应用正好对应图中的AI助手或应用

经典流程如下:

3.2 从Message流转看工具的调用

接下来深入了解一下工具内部在代码中是怎么调用的,前提:代码中配置模型初始化

from dotenv import load_dotenv
from langchain.messages import HumanMessage, ToolMessage
import os

from langchain_openrouter import ChatOpenRouter

# 1、加载配置文件
load_dotenv(override=True)

OPENROUTER_API_KEY = os.getenv ("OPENROUTER_API_KEY")
OPENROUTER_API_BASE = os.getenv ("OPENROUTER_API_BASE")

# 2、模型初始化
model = ChatOpenRouter(
    model="deepseek/deepseek-v3.2",
    api_key=OPENROUTER_API_KEY,
    base_url=OPENROUTER_API_BASE,
)

1、不使用@tool修饰

通过定义普通的方法形式使用,完整代码如下

from dotenv import load_dotenv
from langchain.messages import HumanMessage, ToolMessage
import os

from langchain_openrouter import ChatOpenRouter

# 1、加载配置文件
load_dotenv(override=True)

OPENROUTER_API_KEY = os.getenv ("OPENROUTER_API_KEY")
OPENROUTER_API_BASE = os.getenv ("OPENROUTER_API_BASE")

# 2、模型初始化
model = ChatOpenRouter(
    model="deepseek/deepseek-v3.2",
    api_key=OPENROUTER_API_KEY,
    base_url=OPENROUTER_API_BASE,
)

from langchain_core.tools import tool

def get_weather(city: str):
    """获取天气的工具"""
    return f"{city}天气晴朗"

# 模型与工具绑定
model_with_tools = model.bind_tools([get_weather])

messages = [
    HumanMessage("今天北京天气如何")
]

# 模型生成调用工具请求
response = model_with_tools.invoke(messages)

# 添加AIMessage
messages.append(response)
tool_calls = response.tool_calls

for tool_call in tool_calls:
    if tool_call["name"] == "get_weather":
        # 拼接出ToolMessage实例
        tool_response = ToolMessage(
            content=get_weather(**tool_call["args"]),
            tool_call_id=tool_call["id"],
            name=tool_call["name"]
        )
        messages.append(tool_response)

print("=====================> messages <=====================")
for msg in messages:
    msg.pretty_print()
print("=====================> messages <=====================")

final_response = model_with_tools.invoke(messages)
print(f"final_response: \n{final_response}")

上述代码对应的完整调用流程如下图

2、使用@tool修饰

使用@tool修饰之后,完整的调用代码如下

from langchain_core.tools import tool

@tool
def get_weather(city: str):
    """获取天气的工具"""
    return f"{city}天气晴朗"

# 模型与工具绑定
model_with_tools = model.bind_tools([get_weather])

messages = [
    HumanMessage("今天北京天气如何")
]

# 模型生成调用工具请求
response = model_with_tools.invoke(messages)

# 添加AIMessage
messages.append(response)
tool_calls = response.tool_calls

for tool_call in tool_calls:
    if tool_call["name"] == "get_weather":
        tool_response = get_weather.invoke(tool_call)
        print(type(tool_response))
        messages.append(tool_response)

print("=====================> messages <=====================")
for msg in messages:
    msg.pretty_print()
print("=====================> messages <=====================")

final_response = model_with_tools.invoke(messages)
print(f"final_response: \n{final_response}")

说明:

被 @tool 修饰的函数可以调用 invoke 接收模型返回的入参信息执行函数,并返回ToolMessage 实例,我们不再需要手动拼接 ToolMessage 。

工具流程调用总结

如果真正要大模型根据工具调用结果进行回复,完整的调用流程包括如下四个步骤:

  • 步骤1:模型绑定工具 :通过model.bind_tools([...])绑定一个或者多个工具

  • 步骤2:模型生成工具调用请求 :用户输入问题,调用模型(比如invoke())。如果需要调用工具,模型返回包含工具调用信息(如工具名称和参数)的AIMessage

  • 步骤3:开发者手动执行工具 :用户从响应中提取工具调用信息并手动调用对应的工具(比如工具.invoke())

  • 步骤4:将工具执行结果ToolMessage传递给模型生成最终结果 :将之前用户提问内容和手动执行工具结果ToolMessage返回模型,模型最终生成回复

特别注意:大模型调用工具是单次推理,直接响应,需要开发者手动执行工具并管理循环,适合简

单、确定的任务。

3.3 工具描述的各部分参数详解

3.3.1 convert_to_openai_to

在上面的代码中,执行 model.bind_tools([get_weather]) ,其底层最终会调用 convert_to_openai_tool 生成工具描述。所以可以直接调用后者查看解析后的工具描述。

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint

def get_weather(city: str):
    return f"{city}天气晴朗"

rprint(convert_to_openai_tool(get_weather))

输入结果如下

上面的结果字段说明:

  • type:定义当前数据节点必须是什么数据类型。

    • 常见类型有 string, number, integer, boolean, object, array, null。object即是json对象

  • properties:

    • 用于定义JSON 对象(Object)中可以包含哪些属性(键),以及每个属性对应的值类型和说明

  • required:

    • 当 type为 "object"时使用,是一个数组,列出了对象中必须存在的属性名

到这里有同学有疑问了,为什么不使用@tool装饰器修饰的函数,也可以理解为工具呢?

查看 convert_to_openai_tool 底层源码:

elif isinstance(function, langchain_core.tools.base.BaseTool): 
    oai_function = cast("dict", _format_tool_to_openai_function(function)) 
elif callable(function): 
    oai_function = cast( 
        "dict", _convert_python_function_to_openai_function(function) 
    )

相当于加了@tool修饰的函数走上面的分支,没有加@tool修饰的函数走下面的分支,后者会基于函数定义和docstring生成pydantic模式的描述,然后转换为规范的tool_schema。

3.3.2 description 说明

convert_to_openai_tool 会从 docstring(文档字符串) 加载工具的描述信息,上面的案例中,docstring 为空,所以抽取的 description 为空。

  • docstring,文档字符串,使用三个双引号表示开始和结束

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint

def get_weather(city: str):
    """
    天气查询工具
    """
    return f"{city}天气晴朗"

rprint(convert_to_openai_tool(get_weather))

再次运行,此时在输出结果的描述字段中就可以看到有信息了

3.3.3 参数说明

convert_to_openai_tool 会从 docstring 加载参数说明,这里的 docstring 必须遵循 Google 风格 。

Google 风格 docstring 说明:styleguide | Style guides for Google-originated open-source projectsGoogle 风格 docstring 示例:新建标签页

Python docstring 通用约定:PEP 257 – Docstring Conventions | peps.python.org

基础用法不必完整阅读规范,只需要按照下面的示例仿写即可。

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint

def get_weather(city: str):
    """
    天气查询工具

    Args:
        city: 城市名称
    """
    return f"{city}天气晴朗"


rprint(convert_to_openai_tool(get_weather))

在输出结果中可以看到,参数的详细信息都输出了

使用 Args: 、 Returns: 、 Raises: 等关键字,这种方式可读性强。Agent通过工具的这些注释来理解工具的用途和调用时机,因此清晰、准确的文档字符串是工具能被正确调用的前提。AI 依赖 docstring 来理解工具,下面就是一个好的示例:

@tool
def search_products(query: str) -> str:
    """
        在产品数据库中搜索产品

    Args:
        query: 搜索关键词,如"笔记本电脑"、"手机"

    Returns:
        产品列表的 JSON 字符串
    """
...

3.3.4 参数类型说明

参数类型来源于函数的类型注解,比如下面的代码

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint

def get_weather(city):
    """
    天气查询工具
    """
    return f"{city}天气晴朗"


rprint(convert_to_openai_tool(get_weather))

删除了参数类型注解,则工具描述中也不包含参数类型说明

注意:如果docstring中包含参数说明,则对应的参数必须有类型注解,否则报错,比如下面的代码

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint

def get_weather(city):
    """
    天气查询工具

    Args:
        city: 城市名称
    """
    return "天气晴朗"

rprint(convert_to_openai_tool(get_weather))

运行直接报下面的错误

3.3.5 参数默认值

如果参数没有默认值,则会包含 在required对应的列表 中。

反之,则参数的描述信息会包含 default 字段,并且 不会出现在required列表 中。

下面的代码中,给入参增加一个默认值

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint

def get_weather(city : str = "北京"):
    """
    天气查询工具

    Args:
        city: 城市名称
    """
    return f"{city}天气晴朗"

rprint(convert_to_openai_tool(get_weather))

在输出的结果中在default 这里就能看到这个参数默认值了

3.4 使用@tool装饰器定义工具

使用 @tool 装饰器修饰,可以自动将普通 Python 函数转化为智能体可调用的工具。

此方式 最直接 ,代码量极少,非常适合快速验证想法或创建参数简单的工具。

3.4.1 自定义工具描述:description

在下面的代码中,方法使用@tool 修饰,但是没有描述(docstring)信息,运行的时候将会报错

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool

@tool
def get_weather(city):
    return f"{city}天气晴朗"

rprint(convert_to_openai_tool(get_weather))

添加上去之后就正常了

小结:

  • 在bind_tools()调用时,先将函数封装为 BaseTool 类型的对象,再传递给 convert_to_openai_tool 函数,生成工具的描述。

  • @tool 会从 docstring 生成描述信息,同样要求遵循 Google docstring 规范 。如果没有 docstring 则报错,如下。

Traceback...

ValueError: Function must have a docstring if description not provided.

3.4.2 添加工具描述:description

@tool 的参数 description 可以更改工具描述,优先级高于 docstring 的函数说明

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool

@tool(description="获取具体城市的天气信息")
def get_weather(city):
    """
    获取城市天气信息

    Args:
        city: 城市名称
    """
    return f"{city}天气晴朗"

rprint(convert_to_openai_tool(get_weather))

运行一下可以看到@tool后面的描述信息被输出了

解析docstring:parse_docstring

当我们没有向 @tool 传递 description 参数时,默认情况下, tool 会将 docstring 整体视为 description ,

  • 通过将 parse_docstring 设置为True,docstring会被解析,填充到相应的字段描述中

  • 不使用 @tool 装饰器时,docstring不合法会被视为普通文本,作为 description ,但如果使用了 @tool 时 docstring 不合法,将会抛出异常

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool

@tool(description="获取具体城市的天气信息",parse_docstring=True)
def get_weather(city):
    """
    获取城市天气信息
    Args:
        city: 城市名称
    """
    return f"{city}天气晴朗"

rprint(convert_to_openai_tool(get_weather))

下面这样被认定为不合法的话会抛出异常

3.4.3 更改工具名称

默认情况,使用函数名作为工具名称,但可以向@tool 传参 name_or_callable ,以更改工具名称,如下代码,通过name_or_callable 这个参数重新赋值给工具名称

  • 注意:虽然有这种方式能对工具重命名,但在开发中,习惯使用函数名作为工具名称,不推荐自定义工具名称

from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool

@tool(description="获取具体城市的天气信息",name_or_callable="weather_tool")
def get_weather(city):
    """
    获取城市天气信息

    Args:
        city: 城市名称
    """
    return f"{city}天气晴朗"

rprint(convert_to_openai_tool(get_weather))

此时工具的名称已经变了

3.5 自定义args_schema

当工具的参数变得复杂,需要 枚举值 、 范围限制 或 更复杂的业务逻辑验证 时,Pydantic 模型是理想的选择,提供强大的类型检查和数据验证。

使用Pydantic 的主要优势在于能够精确控制工具参数的格式和验证规则,让大模型更准确地理解如何调用工具。

3.5.1 pydantic类型的定义

通过继承核心基类 BaseModel 定义数据模型,从而声明字段结构、类型约束、默认值以0及校验规则。

from pydantic import BaseModel
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool

class WeatherInput(BaseModel):
    city: str

@tool(args_schema=WeatherInput)
def get_weather(city):
    """
    获取城市天气信息
    """
    return f"{city}天气晴朗"

rprint(convert_to_openai_tool(get_weather))

WeatherInput 对象中还可以补充更多的信息,如下,增加描述和默认值

class WeatherInput(BaseModel):
    city: str = Field(
        description="具体的城市",
        default="北京"
    )

3.5.2 限定参数取值范围

某些情况下为了安全起见,对参数的取值范围做限定,可以使用 Literal类型限定参数为固定选项

  • Literal :表示字段不能是任意某种类型的值,而只能是几个固定字面量之一

class WeatherInput(BaseModel):
    city: str = Field(
        description="具体的城市",
        default="北京"
    ),
    unit: Literal["celsius", "fahrenheit"]

3.6 使用Json Schema定义

在 LangChain 中,还可以直接使用 JSON Schema 字典 来定义工具的参数模式。这种方式提供了极大的灵活性。因为工具参数模式可以基于数据库配置或用户输入在 运行时动态生成 ,所以这种方式特别适合参数结构需要动态生成的场景。

如下完整的代码

from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint

json_schema = {
    'parameters': {
        'properties': {
            'city': {'type': 'string'},
            'unit': {'enum': ['celsius', 'fahrenheit'], 'type': 'string'}
        },
        'required': ['unit'],
        'type': 'object'
    }
}

@tool(args_schema=json_schema)
def get_weather(city):
    """
    获取城市天气信息
    """
    return f"{city}天气晴朗"


rprint(convert_to_openai_tool(get_weather))

运行一下,效果与上面的输出效果一致

四、写在最后

本文详细介绍了Langchain 中工具的调用,并通过 案例的方式详细演示了内部的参数使用,希望对看到的同学有用,本篇到此结束,感谢观看。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/congge_study/article/details/165243374

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--