image

Tools概述


介绍

image

Tools 用于扩展大语言模型(LLM)的能力,使其能够与外部系统、API 或自定义函数交互,从而完成仅靠文本生成无法实现的任务(如搜索、计算、数据库查询等)。

LangChain 拥有大量第三方工具。请访问工具集成查看可用工具列表。

python.langchain.com/v0.2/docs/integrations/tools/

Tool 的要素

Tools 本质上是封装了特定功能的可调用模块,是Agent、Chain或LLM可以用来与世界互动的接口。

Tool 通常包含如下几个要素:

  • name:工具的名称
  • description:工具的功能描述
  • 该工具输入的 JSON 模式
  • 要调用的函数
  • return_direct:是否应将工具结果直接返回给用户(仅对 Agent 相关)

实操步骤:

  1. 将name、description 和 JSON模式作为上下文提供给LLM

  2. LLM会根据提示词推断出需要调用哪些工具,并提供具体的调用参数信息

  3. 用户需要根据返回的工具调用信息,自行触发相关工具的回

自定义工具


两种自定义方式

第1种:使用@tool装饰器(自定义工具的最简单方式)

装饰器默认使用函数名称作为工具名称,但可以通过参数name_or_callable来覆盖此设置。

同时,装饰器将使用函数的文档字符串作为工具的描述,因此函数必须提供文档字符串。

第2种:使用StructuredTool.from_function类方法

这类似于@tool装饰器,但允许更多配置和同步/异步实现的规范。

几个常用属性

Tool由几个常用属性组成:

属性类型描述
namestr必选,在提供给 LLM 或 Agent 的工具集中必须是唯一的。
descriptionstr可选但建议,描述工具的功能。LLM 或 Agent 将使用此描述作为上下文,使用它确定工具的使用
args_schemaPydantic BaseModel可选但建议,可用于提供更多信息(例如,few-shot 示例)或验证预期参数。
return_directboolean仅对 Agent 相关。当为 True 时,在调用给定工具后,Agent 将停止并将结果直接返回给用户。

具体实现

方式1:@tool 装饰器

举例1:

@tool
def add_number(a: int, b: int) -> int:
    """两个整数相加"""
    return a + b


def main():
    """主函数入口(你的核心代码写在这里)"""
    print(f"name={add_number.name}")
    print(f"name={add_number.args}")
    print(f"name={add_number.description}")
    print(f"name={add_number.return_direct}")

    res = add_number.invoke({"a": 10, "b": 20})
    print(res)

main()

输出如下:

name=add_number
name={'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}
name=两个整数相加
name=False
30

举例2:通过@tool的参数设置进行重置

@tool(name_or_callable="add_two_number", description="two number add", return_direct=True)
def add_number(a: int, b: int) -> int:
    return a + b


def main():
    """主函数入口(你的核心代码写在这里)"""
    print(f"name={add_number.name}")
    print(f"name={add_number.args}")
    print(f"name={add_number.description}")
    print(f"name={add_number.return_direct}")

    res = add_number.invoke({"a": 10, "b": 20})
    print(res)

输出如下:

name=add_two_number
name={'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}
name=two number add
name=True
30

补充:还可以修改参数的说明

class FieldInfo(BaseModel):
    a: int = Field(description="第1个参数")
    b: int = Field(description="第2个参数")


@tool(name_or_callable="add_two_number", description="two number add", return_direct=True, args_schema=FieldInfo)
def add_number(a: int, b: int) -> int:
    return a + b


def main():
    """主函数入口(你的核心代码写在这里)"""
    print(f"name={add_number.name}")
    print(f"name={add_number.args}")
    print(f"name={add_number.description}")
    print(f"name={add_number.return_direct}")

    res = add_number.invoke({"a": 10, "b": 20})
    print(res)
main()

输出如下:

name=add_two_number
name={'a': {'description': '第1个参数', 'title': 'A', 'type': 'integer'}, 'b': {'description': '第2个参数', 'title': 'B', 'type': 'integer'}}
name=two number add
name=True
30

方式2:StructuredTool的from_function()

StructuredTool.from_function 类方法提供了比@tool装饰器更多的可配置性,而无需太多额外的代码。

举例1:

def search_function(query: str):
    return "LangChain"

search1 = StructuredTool.from_function(
    func=search_function,
    name="Search",
    description="useful for when you need to answer questions about current events"
)

def main():
    print(f"name={search1.name}")
    print(f"name={search1.args}")
    print(f"name={search1.description}")

    search1.invoke("hello")
main()

输出如下:

name=Search
name={'query': {'title': 'Query', 'type': 'string'}}
name=useful for when you need to answer questions about current events

举例2:

class FieldInfo(BaseModel):
    query: str = Field(description="要检索的关键词")


def search_function(query: str):
    return "LangChain"


search1 = StructuredTool.from_function(
    func=search_function,
    name="Search",
    description="useful for when you need to answer questions about current events. You should ask targeted questions",
    args_schema=FieldInfo,
    return_direct=True
)


def main():
    print(f"name={search1.name}")
    print(f"name={search1.args}")
    print(f"name={search1.description}")
    print(f"return_direct={search1.return_direct}")

    search1.invoke("hello")
main()

输出如下:

name=Search
name={'query': {'description': '要检索的关键词', 'title': 'Query', 'type': 'string'}}
name=useful for when you need to answer questions about current events. You should ask targeted questions
return_direct=True

工具调用举例


我们通过大模型分析用户需求,判断是否需要调用指定工具。

举例1:大模型分析调用工具

load_dotenv()

# 定义LLM
chat_model = ChatOpenAI(model=os.getenv("LLM_MODEL"), temperature=0)

# 定义工具
tools = [MoveFileTool()]

# 4.需要将工具转换为openai函数,后续再将函数传入模型调用
functions = [convert_to_openai_function(t) for t in tools]

# 5.提供大模型调用的消息列表
messages = [HumanMessage(content="将文件a.txt移到桌面")]

# 6.调用大模型
response = chat_model.invoke(
    input=messages,
    functions=functions
)

print(response)

输出如下:

content='' additional_kwargs={'function_call': {'arguments': '{"source_path":"a.txt","destination_path":"/Users/YourUsername/Desktop/a.txt"}', 'name': 'move_file'}, 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 29, 'prompt_tokens': 77, 'total_tokens': 106, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_efad92c60b', 'finish_reason': 'function_call', 'logprobs': None} id='run--c6bb46e7-6739-4927-83f3-04cbb29dbd8d-0' usage_metadata={'input_tokens': 77, 'output_tokens': 29, 'total_tokens': 106, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}

可以看到其中有additional_kwargs的详细信息,如果是换成没有的工具,如下:

response = chat_model.invoke(
	[HumanMessage(content="今天的天气怎么样?")],
	functions=functions
)
print(response)

输出如下:

content='\n\n我目前无法直接获取实时天气信息。不过,您可以通过以下方式查询:\n1. 打开手机天气预报应用\n2. 在电脑浏览器搜索"天气+您所在城市"\n3. 使用智能音箱或语音助手询问\n\n如果您告诉我您所在的城市,我可以为您提供一些天气相关的建议或常识性信息。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 243, 'prompt_tokens': 12, 'total_tokens': 255, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 175, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_name': 'Qwen/Qwen3-14B', 'system_fingerprint': '', 'finish_reason': 'stop', 'logprobs': None} id='run--564e4a18-ce41-4d9e-afaf-7f54764bc4bd-0' usage_metadata={'input_tokens': 12, 'output_tokens': 243, 'total_tokens': 255, 'input_token_details': {}, 'output_token_details': {'reasoning': 175}}

可以看到其中有additional_kwargs​的详细信息只有refusal: None

调用工具说明

有两种情况,如下

情况1:大模型决定调用工具

如果模型认为需要调用工具(如MoveFileTool​),返回的message会包含:

content: 通常为空(因为模型选择调用工具,而非生成自然语言回复)。

additional_kwargs: 包含工具调用的详细信息

AIMessage(
    content="",  # 无自然语言回复
    additional_kwargs={
        'function_call': {
            'name': 'move_file',  # 工具名称
            'arguments':
'{"source_path":"a","destination_path":"/Users/YourUsername/Desktop/a"}'  # 工具参数
        }
    }
)

情况2:大模型不调用工具

如果模型认为无需调用工具(例如用户输入与工具无关),返回的 message 会是普通文本回复:

AIMessage(
	content='我没有找到需要移动的文件。', # 自然语言回复
	additional_kwargs={'refusal': None} # 无工具调用
)

举例2:确定工具并调用

tip:

该操作目前可能只用OpenAI原生支持,其他模型估计得使用MCP1

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@Project :LangChain-tutorial 
@File    :demo01.py
@Author  :zxb
@Date    :2025/12/9 17:06
"""
import json
import os

from dotenv import load_dotenv
from langchain_community.tools import MoveFileTool
from langchain_core.messages import HumanMessage
from langchain_core.utils.function_calling import convert_to_openai_function
from langchain_openai import ChatOpenAI

load_dotenv()

# 定义LLM
chat_model = ChatOpenAI(model=os.getenv("LLM_MODEL"), temperature=0)

# 定义工具
tools = [MoveFileTool()]

# 4.需要将工具转换为openai函数,后续再将函数传入模型调用
functions = [convert_to_openai_function(t) for t in tools]

# 5.提供大模型调用的消息列表
messages = [HumanMessage(content="将本目录下的a.txt移动到F盘")]

# 6.调用大模型
response = chat_model.invoke(
    input=messages,
    functions=functions
)

# print(response)

if "function_call" in response.additional_kwargs:
    tool_name = response.additional_kwargs["function_call"]["name"]
    tool_args = json.loads(response.additional_kwargs["function_call"]["arguments"])
    print(f"调用工具: {tool_name}, 参数: {tool_args}")
    if "move_file" in response.additional_kwargs["function_call"]["name"]:
        tool = MoveFileTool()
        result = tool.run(tool_args) # 执行工具
        print("工具执行结果:", result)
else:
    print("模型回复:", response.content)

输出如下:

调用工具: move_file, 参数: {'source_path': './a.txt', 'destination_path': 'F:/a.txt'}
工具执行结果: File moved successfully from ./a.txt to F:/a.txt.

欢迎关注我的公众号【zxb的博客】!

Footnotes

  1. MCP

    • 🐈‍⬛ 1.MCP介绍2
    • 📄 2.MCP实战3

  2. 1.MCP介绍

    如果说大模型(ChatGPT、Deepseek)被称为人工智能的大脑,那么MCP就可以被称为人工智能的手臂

    MCP提供统一且可靠的方式来访问所需数据,克服了以往集成方法的局限性。

    特征MCPTCP/IP、HTTPS
    作用协议(Protocol)协议(Protocol)
    作用标准化AI模型与上下文来源/工具标准化设备之间的网络通信方式
    之间的数据交互方式
    目标让不同模型应用可以用统一方式访问资源/工具让不同设备、系统可以互通数据
    好处消除碎片化集成、形成生态闭环解决设备互联、实现互联网基础

    MCP资源推荐


    MCP应用场景


    应用领域经典场景MCP价值代表实现
    智能编程助手代码生成、Bug修复、API集成安全访问本地代码库、CI/CD系统Cursor、VSCode插件
    数据分析工具自然语言查询数据库、可视化生成安全查询内部数据库、连接BI工具XiYanSQL-MCP、数据库MCP服务器
    企业知识管理知识库查询、文档生成、邮件撰写安全访问内部文档、保护隐私数据文件系统MCP、Email-MCP
    创意设计工具3D建模、图形生成、UI设计与专业软件无缝集成BlenderMCP、浏览器自动化
    工作流自动化多系统协调、事件驱动流程跨系统安全协作CloudflareMCP、AWS自动化套件
  3. 2.MCP实战

    MCP通信机制


    MCP的通信机制(传输方式)如下:

    • stdio(标准输入输出) :主要作用在 本地服务 上,操作你本地的软件或本地的文件。比如 Blender 这类软件只能使用 Stdio。
    • SSE(Server-Sent Events) :主要用在 远程通信服务 上,这个服务本身就有在线的API,比如访问你的邮箱,查看天气情况等。

    stdio方式优缺点

    优点:

    • 这种方式适用于客户端和服务器在同一台机器上运行的场景,简单
    • stdio模式无需外部网络依赖,通信速度快,适合快速响应的本地应用。
    • 可靠性高,且易于调试
      缺点:
    • Stdio 的配置比较复杂,我们需要做些准备工作,你需要提前安装需要的命令行工具。
    • stdio模式为单进程通信,无法并行处理多个客户端请求,同时由于进程资源开销较大,不适合在本地运行大量服务。(限制了其在更复杂分布式场景中的使用)

    SSE方式优缺点

    场景

    • SSE方式适用于客户端和服务器位于不同物理位置的场景。
    • 适用于实时数据更新、消息推送、轻量级监控和实时日志流等场景
    • 对于分布式或远程部署的场景,基于 HTTP 和 SSE 的传输方式则更为合适。

    优点

    • 配置方式非常简单,基本上就一个链接就行,直接复制他的链接填上就行

    关于在IDE中使用mcp:

    MCP的工作原理


    MCP的C/S架构

    MCP遵循客户端-服务端架构(client-server),其中包含以下核心概念:

    • MCP主机(MCP Hosts)
    • MCP客户端(MCP Clients)
    • MCP服务端(MCP Servers)
    • 本地资源(Local Resources)
    • 远程资源(Remote Resources)

    MCP Host

    作为运行MCP的主应用程序,例如 Claude Desktop、Cursor、Cline或AI小工具。
    为用户提供LLM交互的接口,同时继承 MCP client 以连接MCP Server。

    MCP Client

    MCP Client充当LLM和MCP Server之间的桥梁,嵌入在主机程序中,主要负责:

    • 接受来自LLM的请求
    • 将请求转发到相应的MCP Server
    • 将 MCP Server 的结构返回给LLM

    官网提出的常用的MCP Client: https://modelcontextprotocol.io/clients

    分为两类:

    • AI编程IDE:Cursor、Cline、Continue......
    • 聊天客户端:Cherry Studio、Claude、Librechat......

    MCP Server

    每个MCP服务器都提供了一组特定的工具,负责从本地数据或远程服务中检索信息,是MCP架构中的关键组件。