【LangChain】P23 LangChain 工具篇(一)为 AI 插上翅膀:深入理解大模型工具(Tools)的核心要素与构建之道
目录

前言
在构建强大 AI 应用的征程中,仅仅依赖大语言模型(LLM)的文本生成能力,如同纸上谈兵。真正的突破,在于让模型能够与真实世界互动,从“认识世界”迈向“改变世界”。工具(Tools) 正是为模型插上这双翅膀的关键,它将模型的智慧延伸至无限可能。
从本篇博文开始的两篇博文,我们将浅层的讲解 Tools 的定义、构建方法以及使用策略,提供一些基本的示例,详细内容将在下一个篇章 —— Agent,即真正应用工具实现场景互动的应用范式中详细提供,请期待。
什么是 Tools?为什么它如此重要?
简单来说,Tools 是让 LLM 能够调用外部世界能力的接口。它将 LLM 从一个封闭的“文本大脑”变成了一个可以实际操作的“行动派”。无论是执行一次网络搜索、查看当前时间戳、进行一次数学计算、查询数据库,还是运行一段代码解释器,都离不开 Tools 的支持。
核心特点
- 功能增强: 让 LLM 突破纯文本的局限,执行搜索、计算、API 调用等实际操作。
- 智能决策: 在 Agent 工作流中,LLM 能根据用户意图,动态地选择最合适的工具来完成任务。
- 模块化设计: 每个 Tool 专注一个功能,易于复用和组合,例如:搜索工具 + 计算工具 + 天气查询工具,可以协同完成复杂任务。
剖析 Tool 的五大核心要素
要让 LLM 精准地理解并使用我们创建的工具,就必须按照约定来定义它。一个标准的 Tool 通常包含以下五个核心要素:
| 要素 | 类型 | 功能说明 |
|---|---|---|
name | string | 工具的唯一标识符。 一个简洁、明确的名称是 LLM 识别工具的第一步 |
description | string | 工具的灵魂所在。 这是你写给 LLM 的“使用说明书”,必须清晰、准确地描述工具的功能、使用场景以及它能解决什么样的问题。LLM 完全依赖此描述来判断何时调用该工具 |
orgs_schema | Pydantic BaseModel | 工具的输入参数规范。 它定义了工具需要哪些输入,以及每个输入的类型和格式。这能确保 LLM 以正确的格式提供参数 |
func | callable | 工具要执行的函数。 这是工具背后真正的逻辑,是实际执行任务的代码 |
return_direct | boolean | 是否将结果直接返回给最终用户。 如果为 True,则工具的执行结果将作为最终答案,工作流结束。如果为 False(默认),结果将返回给 LLM,由 LLM 决定下一步动作 |
工作流程简述
- 提供上下文: 我们将定义好的工具列表(包含
name,description,args_schema)提供给 LLM。 - LLM 推理决策: LLM 根据用户的指令和它所理解的工具描述,判断是否需要调用工具。如果需要,它会生成一个包含工具名称 (
name) 和具体调用参数 (args) 的指令。 - 应用执行回调: 我们的应用程序接收到 LLM 的指令后,调用相应的
func并传入参数,执行工具,然后将结果返回给 LLM 或用户。
自定义工具的两种实战方式
LangChain 为我们提供了两种便捷的方式来创建自定义工具:
- @tool 装饰器
- StructuredTool.from_function 类方法
方式一:@tool 装饰器(快速、便捷)
这是最简单、最 Pythonic 的方式。它能将任何一个 Python 函数快速封装成一个 Tool。
- 高价值说明:
默认情况下,@tool会自动使用函数名作为工具的name,并抽取 函数的文档功能注释 作为工具的description。因此,编写清晰的函数文档描述至关重要!
1. 基础定义
我们定义一个简单的加法工具,并查看其自动生成的属性。
from langchain_core.tools import tool
@tool
def add_number(a: int, b: int) -> int:
"""计算并返回两个整数的和。"""
return a + b
# 查看工具属性
print(f"名称 (name) = {add_number.name}")
print(f"描述 (description) = {add_number.description}")
print(f"参数 (args) = {add_number.args}")
输出结果:
名称 (name) = add_number
描述 (description) = 计算并返回两个整数的和。
参数 (args) = {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}
2. 自定义参数描述
默认的参数描述(如 {'title': 'A'})过于简单,LLM 可能无法理解其含义。我们可以通过 Pydantic 的 BaseModel 和 Field 来提供更丰富的参数描述,帮助 LLM 更好地理解如何传递参数。
- 重点功能:使用
args_schema精准控制输入
通过pydantic.Field的description字段,我们可以为每个参数添加详细的说明,这是提升 LLM 调用准确率的关键技巧。
from langchain_core.tools import tool
from pydantic import BaseModel, Field
# 1. 定义一个符合 args_schema 规范的类
class CalculatorInput(BaseModel):
a: int = Field(description="进行加法运算的第一个整数")
b: int = Field(description="进行加法运算的第二个整数")
# 2. 将其传递给 @tool 装饰器
@tool(args_schema=CalculatorInput)
def add_number(a: int, b: int) -> int:
"""计算并返回两个整数的和。"""
return a + b
# 查看现在更丰富的参数定义
print(f"名称 (name) = {add_number.name}")
print(f"描述 (description) = {add_number.description}")
print(f"参数 (args) = {add_number.args}")
输出结果,可以看到 description 已经被成功注入:
名称 (name) = add_number
描述 (description) = 计算并返回两个整数的和。
参数 (args) = {'a': {'title': 'A', 'description': '进行加法运算的第一个整数', 'type': 'integer'}, 'b': {'title': 'B', 'description': '进行加法运算的第二个整数', 'type': 'integer'}}
方式二:StructuredTool.from_function() (灵活、可配置)
当你的函数已经定义好,或者你需要更灵活地配置工具属性时,StructuredTool.from_function() 是一个绝佳的选择。
- 高价值说明:
这种方式将函数定义与工具封装彻底分离,代码结构更清晰,特别适合于将项目中已有的函数库封装为 AI 工具的场景。它提供了比装饰器更多的配置选项。
1. 基础封装
将一个已有的 search_google 函数封装成 Tool。
from langchain_core.tools import StructuredTool
# 假设这是一个已存在的函数
def search_google(query: str) -> str:
"""一个模拟的谷歌搜索函数。"""
print(f"正在搜索: {query}")
return f"关于“{query}”的搜索结果..."
# 使用 from_function 方法进行封装
google_search_tool = StructuredTool.from_function(
func=search_google,
name="GoogleSearch",
description="当需要回答关于实时信息、事件或需要网络查询才能知道的问题时,使用此工具。"
)
# 查看工具属性
print(f"名称 (name) = {google_search_tool.name}")
print(f"描述 (description) = {google_search_tool.description}")
# 调用工具
google_search_tool.invoke({"query": "AI领域的最新进展"})
重点: 在这里,name 和 description 是在封装时明确指定的,而不是从函数本身推断。这给了我们极大的灵活性。
2. 配置参数与返回方式
与 @tool 类似,我们也可以使用 args_schema 来美化参数,并可以设置 return_direct=True。
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
query: str = Field(description="需要谷歌搜索的关键词或问题")
google_search_tool_pro = StructuredTool.from_function(
func=search_google,
name="DirectGoogleSearch",
description="用于网络搜索,并将结果直接返回给用户。",
args_schema=SearchInput,
return_direct=True # 结果直接输出,不再返回给LLM
)
print(f"名称 (name) = {google_search_tool_pro.name}")
print(f"参数 (args) = {google_search_tool_pro.args}")
print(f"直接返回 (return_direct) = {google_search_tool_pro.return_direct}")
总结 & 如何选择?
- 使用
@tool装饰器: 当你正在编写新函数,并希望快速将其转换为工具时。它代码紧凑,非常适合快速原型开发和简单场景。 - 使用
StructuredTool.from_function(): 当你需要将一个已存在的函数封装成工具,或者需要更精细、更明确地控制工具的各项属性(如name,description)时。它提供了更好的代码分离和配置灵活性。
无论选择哪种方式,请始终记住:一个高质量的 description 是让 LLM 能否在正确时机、以正确方式调用你工具的决定性因素。 在描述上投入的时间,将会在 AI 应用的性能上得到百倍的回报。
2025.10.13 金融街
更多推荐



所有评论(0)