Tools概述
介绍
要构建更强大的AI工程应用给,只有生成文本这样的"纸上谈兵"能力自然是不够的。工具Tools不仅仅是"肢体"的延申,更是为"大脑"插上了想象力的翅膀。借助工具,才能让AI应用的能力真正具备无限的可能,才能从"认识世界"走向"改变世界"
Tools用于扩展大预言模型(LLM)的能力,使其能够与外部系统、API或自定义函数交互,从而完成仅靠文本生成无法实现的任务(如搜索、计算、数据库查询等)

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