LangChain使用之Tools

Tools概述

介绍

要构建更强大的AI工程应用给,只有生成文本这样的"纸上谈兵"能力自然是不够的。工具Tools不仅仅是"肢体"的延申,更是为"大脑"插上了想象力的翅膀。借助工具,才能让AI应用的能力真正具备无限的可能,才能从"认识世界"走向"改变世界"

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

特点

  • 增强LLM功能:让LLM突破纯文本生成的限制,执行实际操作(如调用搜索引擎、查询数据库、运行代码等)
  • 支持智能决策:在Agent工作流中,LLM根据用户输入动态选择最合适的Tool完成任务
  • 模块化设计:每个Tool专注一个功能,便于复用和组合(例如:搜索工具+计算工具+天气查询工具)

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

Tools | 🦜️🔗 LangChain

Tool的要素

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

Tool通常包含如下几个要素

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

实操步骤

  • 步骤1:将name、description和JSON模式作为上下文提供给LLM
  • 步骤2:LLM会根据提示词推断出需要调用哪些工具,并提供具体的调用参数信息
  • 步骤3:用户需要根据返回的工具调用信息,自行出发相关工具的回调

注意:如果工具具有精心选择的名称、描述和JSON模式,则模型的性能更好。下一章我们可以看到工具的调用动作可以通过Agent自主接管

自定义工具

两种自定义方式

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

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

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

第二种:使用StructredTool.from_function类方法

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

几个常用属性

Tool由几个常用属性组成:

属性 类型 描述
name str 必选的,在提供给LLM或Agent的工具集中必须是唯一的
description str 可选但建议,描述工具的功能。LLM或Agent将使用此描述作为上下文,使用它确定工具的使用
args_schema PydanticBaseModel 可选但建议,可用于提供更多信息或验证预期参数
return_direct boolean 仅对Agent相关,当为True时,在调用给定工具后,Agent将停止并将结果直接返回给用户

具体实现

方式1:@tool装饰器

例1:

python 复制代码
from langchain.tools import tool

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

print(f"name = {add_number.name}")
print(f"args = {add_number.args}")
print(f"description = {add_number.description}")
print(f"return_direct = {add_number.return_direct}")

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

name = add_number

args = {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}

description = 将两个整数相加

return_direct = False

30


说明:return_direct参数的默认值是False。当return_direct=False时,工具结果会返回给Agent,让Agent决定下一步操作;而return_direct=True则会中断这个循环,直接结束流程,返回结果给用户。

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

python 复制代码
from langchain.tools import tool
from pydantic import BaseModel,Field

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

@tool(name_or_callable="add_two_numbers",description="two numbers",
      args_schema=FieldInfo,return_direct=True)
def add_number(a:int, b:int)->int :
    """将两个整数相加"""
    return a+b

print(f"name = {add_number.name}")
print(f"args = {add_number.args}")
print(f"description = {add_number.description}")
print(f"return_direct = {add_number.return_direct}")

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

name = add_two_numbers

args = {'a': {'description': '第一个参数', 'title': 'A', 'type': 'integer'}, 'b': {'description': '第二个参数', 'title': 'B', 'type': 'integer'}}

description = two numbers

return_direct = True

30


方式2:StructedTool的From_function()

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

例1:

python 复制代码
from langchain_core.tools import StructuredTool

def search_function(query:str):
    return "查询的结果"

search1 = StructuredTool.from_function(
    func = search_function,
    name = "google_search",
    description = "当你想要搜索东西的时候,使用这个工具"
)

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

search1.invoke("微软什么时候成立的")

name = google_search

description = 当你想要搜索东西的时候,使用这个工具

args = {'query': {'title': 'Query', 'type': 'string'}}

'查询的结果'


例2:

python 复制代码
from langchain_core.tools import StructuredTool
from pydantic import BaseModel,Field

class FieldInfo(BaseModel):
    query:str = Field(description="要检索的关键词")
    
def search_function(query:str):
    return "检索的结果"

search1 = StructuredTool.from_function(
    func = search_function,
    name = "Search",
    description = "当你想要搜索东西的时候,使用这个工具",
    args_schema=FieldInfo,
    return_direct = True
)

print(f"name = {search1.name}")
print(f"description = {search1.description}")
print(f"args = {search1.args}")
print(f"return_direct = {search1.return_direct}")
search1.invoke("Search")

name = Search

description = 当你想要搜索东西的时候,使用这个工具

args = {'query': {'description': '要检索的关键词', 'title': 'Query', 'type': 'string'}}

return_direct = True

'检索的结果'


工具调用举例

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

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

python 复制代码
from langchain_core.messages import HumanMessage
from langchain_community.tools import MoveFileTool
from langchain_openai import ChatOpenAI
from langchain_core.utils.function_calling import convert_to_openai_function
import os
import dotenv

dotenv.load_dotenv()

os.environ["OPENAI_API_KEY"]=os.getenv("OPENAI_API_KEY")
os.environ["OPENAI_BASE_URL"]=os.getenv("OPENAI_BASE_URL")

chat_model = ChatOpenAI(model = "gpt-4o-mini")

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

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

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

#模型使用函数
response = chat_model.invoke(
    input = messages,
    functions = functions,
)
print(response)

content='' additional_kwargs={'function_call': {'arguments': '{"source_path":"a","destination_path":"C:\\Users\\你的用户名\\Desktop\\a"}', 'name': 'move_file'}, 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 30, 'prompt_tokens': 77, 'total_tokens': 107, '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', 'id': 'chatcmpl-CQumwKR6NS0IcycejHuYLjsdbETv8', 'service_tier': None, 'finish_reason': 'function_call', 'logprobs': None} id='run--4d85fd07-1864-482f-b3f0-d4759c9dcbba-0' usage_metadata={'input_tokens': 77, 'output_tokens': 30, 'total_tokens': 107, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}


模型绑定工具,调用模型,传入Message对象

作为对照,修改代码:

python 复制代码
# 提供大模型调用消息列表
messages = [HumanMessage(content="今天天气怎么样")]

#模型使用函数
response = chat_model.invoke(
    input = messages,
    functions = functions,
)
print(response)

content='今天天气怎么样通常取决于您所在的地点。请告诉我您的位置,我可以为您提供更具体的天气信息。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 31, 'prompt_tokens': 73, 'total_tokens': 104, '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', 'id': 'chatcmpl-CQupMjPt44hkkACgmQ8RIouDSVERP', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None} id='run--e7491b83-ea28-4951-ab16-59e7e9ecfc3c-0' usage_metadata={'input_tokens': 73, 'output_tokens': 31, 'total_tokens': 104, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}


调用工具说明

两种情况

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

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

  • content:通常为空(因为模型选择调用工具,而非生成自然语言回复)
  • additional_kwargs:包含工具调用的详细信息

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

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

举例2:确定工具并调用

python 复制代码
# 定义LLM模型
chat_model =ChatOpenAI(model="gpt-4o-mini",temperature=0)
# 定义工具
tools = [MoveFileTool()]
# 将工具转换为openai函数
functions = [convert_to_openai_function(t) for t in tools]
# 提供消息列表
messages = [HumanMessage(content="将本目录下的abc.txt文件移动到F:\")]
# 模型调用
response = chat_model.invoke(
input=messages,
functions=functions
)
print(response)

content='' additional_kwargs={'function_call': {'arguments': '{"source_path":"abc.txt","destination_path":"F:\\abc.txt"}', 'name': 'move_file'}, 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 81, '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', 'id': 'chatcmpl-CQv1pZw4nzgDzVcaPQPmbNGSNLogA', 'service_tier': None, 'finish_reason': 'function_call', 'logprobs': None} id='run--e5e2b0f0-910e-4b6e-a5a6-f1977dd39466-0' usage_metadata={'input_tokens': 81, 'output_tokens': 25, 'total_tokens': 106, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}


(1)检查是否需要调用工具

python 复制代码
import json

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}")
else:
    print("模型回复:", response.content)

调用工具: move_file, 参数: {'source_path': 'abc.txt', 'destination_path': 'F:\abc.txt'}


(实际执行工具调用)

python 复制代码
from langchain.tools import MoveFileTool
if "move_file" in response.additional_kwargs["function_call"]["name"]:
    tool = MoveFileTool()
    result = tool.run(tool_args) # 执行工具
    print("工具执行结果:", result)

工具执行结果: File moved successfully from abc.txt to F:\abc.txt.


最后可以发现abc.txt被成功移动到F:\下

相关推荐
FreeCode4 小时前
智能体化系统(Agentic System)开发面临的挑战及应对
人工智能·agent
大模型真好玩20 小时前
低代码Agent开发框架使用指南(四)—Coze大模型和插件参数配置最佳实践
人工智能·agent·coze
大模型教程1 天前
一文搞懂RAG:凭啥阿里70K算法岗都在用它?
程序员·llm·agent
大模型教程1 天前
告别传统 RAG,用智能 Agent 方法构建 AI 知识库
程序员·llm·agent
AI大模型1 天前
从原理到落地:RAG 技术全解析,手把手教你搭建专属知识库
程序员·llm·agent
AI大模型1 天前
RAG:企业数智化的“知识引擎”,让AI真正读懂你的业务
程序员·llm·agent
ezl1fe1 天前
第零篇:把 Agent 跑起来的最小闭环
人工智能·后端·agent
嚴寒1 天前
被10个终端窗口逼疯后,我用Rust写了个零依赖跨平台终端Agent启动神器
rust·agent
深度学习机器1 天前
如何选择合适的 AI Agent框架?OpenAI vs Claude vs LangGraph功能特点汇总
llm·openai·agent