主题
什么是工具?
📚 本节目标
AI 智能体 的关键能力在于执行行动。正如前文所述,这通过 工具 的使用实现。
本章节主要是学习工具的定义、有效设计方法,以及如何通过系统消息将其集成到智能体中。
通过为智能体配备合适的工具——并清晰描述这些工具的工作原理——可显著提升 AI 的能力边界。让我们开始探讨!
AI 工具的定义
工具 是赋予 LLM 的函数,该函数应实现明确的目标。
以下是 AI 智能体中常用的工具示例:
| 工具类型 | 描述 | 应用场景 |
|---|---|---|
| 网络搜索 | 允许智能体从互联网获取最新信息 | 实时新闻查询、股价查询、天气信息 |
| 图像生成 | 根据文本描述生成图像 | 创意设计、内容创作、可视化展示 |
| 信息检索 | 从外部数据源检索信息 | 文档查询、知识库检索、数据库查找 |
| API 接口 | 与外部 API 交互 | GitHub 操作、数据搜索、音乐播放 |
| 计算工具 | 执行数学计算和数据处理 | 复杂计算、统计分析、公式求解 |
| 文件操作 | 处理文件的读写和管理 | 文档编辑、数据导入导出、文件转换 |
💡 重要提醒
以上仅为示例,实际可为 任何用例 创建工具!
工具的价值
优秀工具应能补充 LLM 的核心能力。
加载图表中...
具体示例
例如,若需执行数学运算,为 LLM 提供 计算器工具 将比依赖模型原生能力获得更好结果。
此外,LLM 基于训练数据预测提示的补全,意味着其内部知识仅包含 训练截止前的信息。因此,若智能体需要最新数据,必须通过工具获取。
⚠️ 幻觉问题
例如,若直接询问 LLM(无搜索工具)今日天气,LLM 可能会产生随机幻觉:
用户: "今天北京的天气如何?"
无工具的LLM: "今天北京晴朗,气温25°C,微风。" ❌(完全编造)
有搜索工具的智能体: "让我为您查询最新天气信息..." ✅(实时准确)
工具的构成要素
合格工具应包含:
- 📝 函数功能的文本描述
- 🔧 可调用对象(执行操作的实体)
- 📋 带类型声明的参数
- 📤 (可选)带类型声明的输出
加载图表中...
工具如何运作?
正如前文所述,LLM 只能接收文本输入并生成文本输出。它们无法自行调用工具。
当我们谈论为智能体提供工具时,本质是:
- 教导
LLM认识工具的存在 - 要求模型在需要时生成调用工具的文本
工具调用流程
加载图表中...
详细步骤解释
例如,若我们提供从互联网获取某地天气的工具:
- 用户询问:"上海天气如何?"
- LLM 识别:该问题适合使用
"天气"工具 - 生成调用:
LLM生成代码形式的文本来调用该工具 - 智能体执行:解析
LLM的输出,识别工具调用需求,执行工具调用 - 返回结果:工具的输出返回给
LLM - 生成回复:
LLM生成最终用户响应
💡 关键理解
工具调用的输出是对话中的另一种消息类型。工具调用步骤通常对用户不可见:智能体检索对话、调用工具、获取输出、将其作为新消息添加,并将更新后的对话再次发送给 LLM。
从用户视角看,仿佛 LLM 直接使用了工具,但实际执行的是我们的应用代码(智能体)。
如何为 LLM 提供工具?
完整答案可能看似复杂,但核心是通过 系统提示(system prompt) 向模型通过文本描述可用工具:
python
system_prompt = """
你是一个智能体, 名字是小马。
拥有以下工具:
1. compare_numbers(a: int, b: int) -> str
描述: 比较两个数字的大小
2. get_weather(location: str) -> dict
描述: 获取特定位置的当前天气信息
当需要使用工具时,请使用以下格式响应:
TOOL_CALL: 工具名称(参数=值)
"""为确保有效性,必须精准描述:
- 🎯 工具功能
- 📝 预期输入格式
因此工具描述通常采用 结构化表达方式(如编程语言或 JSON)。虽然不是强制,但任何其他格式均可。
具体示例:计算器工具
若觉抽象,我们通过具体示例理解。
我们将实现简化的计算器工具,仅执行两整数相乘。Python 实现如下:
python
def compare_numbers(a: int, b: int) -> str:
"""比较两个数字的大小"""
if a > b:
return f"{a} 大于 {b}"
elif a < b:
return f"{a} 小于 {b}"
else:
return f"{a} 等于 {b}"因此我们的工具:
- 名称:
compare_numbers - 功能:比较两个数字的大小
- 输入:
a(int):整数,b(int):整数 - 输出:
str:a 与 b 的大小关系
让我们将这些信息整合成 LLM 可理解的工具描述文本:
工具名称:compare_numbers,描述:比较两个数字的大小。参数:a: int, b: int,输出:a 与 b 的大小关系⚠️ 重要提示
此文本描述是我们希望 LLM 了解的工具体系。
当我们将上述字符串作为输入的一部分传递给 LLM 时,模型将识别其为工具,并知晓需要传递的输入参数及预期输出。
若需提供更多工具,必须保持格式一致性。此过程可能较为脆弱,容易遗漏某些细节。
那么,是否有更好的方法?
自动化工具描述生成
我们的工具采用 Python 实现,其代码已包含所需全部信息:
- ✅ 功能描述性名称:
compare_numbers - ✅ 详细说明:通过函数文档字符串实现
比较两个数字的大小 - ✅ 输入参数及类型:函数明确要求两个
int类型参数 - ✅ 输出类型:
-> str
这正是人们使用编程语言的原因:表达力强、简洁且精确。
虽然可以将 Python 源代码作为工具规范提供给 LLM,但具体实现方式并不重要。关键在于工具名称、功能描述、输入参数和输出类型。
Python 自省特性
我们将利用 Python 的自省特性,通过源代码自动构建工具描述。只需确保工具实现满足:
- 使用类型注解(Type Hints)
- 编写文档字符串(Doc Strings)
- 采用合理的函数命名
完成这些之后,我们只需使用一个 Python 装饰器 来指示 compare_numbers 函数是一个工具:
python
@tool
def compare_numbers(a: int, b: int) -> str:
"""比较两个数字的大小"""
if a > b:
return f"{a} 大于 {b}"
elif a < b:
return f"{a} 小于 {b}"
else:
return f"{a} 等于 {b}"
print(compare_numbers.to_string())注意函数定义前的 @tool 装饰器。
通过我们即将看到的实现,可以利用装饰器提供的 to_string() 方法从源代码自动提取以下文本:
工具名称:compare_numbers,描述:比较两个数字的大小。参数:a: int, b: int,输出:a 与 b 的大小关系正如你所见,这与我们之前手动编写的内容 完全一致 ! 🎉 🎉
通用工具类实现
我们创建通用Tool类,可在需要时重复使用:
📝 说明
此示例实现为虚构代码,但模拟了主流工具库的实际实现方式。
python
class Tool:
"""
一个工具的类,用于包装函数并提供工具的文本描述
属性:
name (str): 工具的名称
description (str): 工具功能的文本描述
func (callable): 该工具包装的函数
arguments (list): 参数列表
outputs (str or list): 被包装函数的返回类型
"""
def __init__(self,
name: str, # 工具名称
description: str, # 工具描述
func: callable, # 可调用的函数对象
arguments: list, # 参数列表
outputs: str): # 输出类型
"""
初始化工具对象
"""
self.name = name # 设置工具名称
self.description = description # 设置工具描述
self.func = func # 设置被包装的函数
self.arguments = arguments # 设置参数列表
self.outputs = outputs # 设置输出类型
def to_string(self) -> str:
"""
返回工具的字符串表示形式,
包括工具名称、描述、参数和输出类型
"""
# 将参数列表转换为字符串格式:参数名: 参数类型
args_str = ", ".join([
f"{arg_name}: {arg_type}"
for arg_name, arg_type
in self.arguments
])
# 返回格式化的工具信息字符串
return (
f"工具名称: {self.name}," # 工具名称
f" 描述: {self.description}," # 工具描述
f" 参数: {args_str}," # 参数信息
f" 输出: {self.outputs}" # 输出类型
)
def __call__(self, *args, **kwargs):
"""
使工具对象可以像函数一样被调用
将提供的参数传递给底层函数并执行
"""
# 调用被包装的函数并返回结果
return self.func(*args, **kwargs)类结构解析
虽然看似复杂,但逐步解析即可理解其工作机制。我们定义的Tool类包含以下核心要素:
| 属性 | 类型 | 说明 |
|---|---|---|
name | str | 工具名称 |
description | str | 工具功能简述 |
function | callable | 工具执行的函数 |
arguments | list | 预期输入参数列表 |
outputs | str/list | 工具预期输出 |
| 方法 | 说明 |
|---|---|
__call__() | 调用工具实例时执行函数 |
to_string() | 将工具属性转换为文本描述 |
手动创建工具实例
可通过如下代码创建工具实例:
python
compare_numbers_tool = Tool(
"compare_numbers", # 工具名称
"比较两个数字的大小", # 工具描述
compare_numbers, # 可调用的函数对象
[("a", "int"), ("b", "int")], # 参数列表
"str", # 输出类型
)但我们可以利用 Python 的inspect模块自动提取这些信息!这正是@tool装饰器的实现原理。
🔍 装饰器实现代码
python
import inspect
from typing import get_type_hints
def tool(func):
"""
装饰器:将函数自动转换为 Tool 实例
"""
# 1. 获取函数签名信息(参数名、默认值等)
sig = inspect.signature(func)
# 2. 获取函数的类型提示信息
type_hints = get_type_hints(func)
# 3. 提取参数信息
arguments = []
for param_name, param in sig.parameters.items():
# 从类型提示中获取参数类型,如果没有则默认为 'any'
param_type = type_hints.get(param_name, 'any')
# 处理类型名称的显示
if hasattr(param_type, '__name__'):
type_str = param_type.__name__ # 如:int, str, float
else:
type_str = str(param_type) # 如:List[int], Optional[str]
# 添加到参数列表:(参数名, 参数类型)
arguments.append((param_name, type_str))
# 4. 提取返回类型信息
return_type = type_hints.get('return', 'any')
if hasattr(return_type, '__name__'):
output_str = return_type.__name__
else:
output_str = str(return_type)
# 5. 提取函数的文档字符串作为描述
description = func.__doc__.strip() if func.__doc__ else "没有提供描述"
# 6. 创建 Tool 实例
tool_instance = Tool(
name=func.__name__, # 函数名作为工具名
description=description, # 文档字符串作为描述
func=func, # 原始函数
arguments=arguments, # 自动提取的参数信息
outputs=output_str # 自动提取的返回类型
)
# 7. 将 Tool 的方法添加到原函数对象上
func.to_string = tool_instance.to_string # 添加信息展示方法
func.__call__ = tool_instance.__call__ # 保持可调用性
# 8. 返回增强后的函数
return func使用装饰器
在应用此装饰器后,我们可以按如下方式实现工具:
python
@tool
def compare_numbers(a: int, b: int) -> str:
"""比较两个数字的大小"""
if a > b:
return f"{a} 大于 {b}"
elif a < b:
return f"{a} 小于 {b}"
else:
return f"{a} 等于 {b}"
print(compare_numbers.to_string())输出:
工具名称:compare_numbers,描述:比较两个数字的大小。参数:a: int, b: int,输出:a 与 b 的大小关系我们可以使用 Tool 类的 to_string 方法自动生成适合 LLM 使用的工具描述文本。
该描述将被注入系统提示。以本节初始示例为例,替换 tools_description 后的系统提示如下:
python
system_prompt = f"""
你是一个智能体, 名字是小马。
拥有以下工具:
{compare_numbers.to_string()}
当需要使用工具时,请使用以下格式响应:
TOOL_CALL: 工具名称(参数=值)
"""在 Actions 章节,我们将探讨智能体如何调用刚创建的这个工具。
模型上下文协议(MCP):统一的工具接口
模型上下文协议(MCP) 是一种开放式协议,它规范了应用程序向 LLM 提供工具的方式。
加载图表中...
MCP 生态系统
| MCP 工具类型 | 示例 | 用途 |
|---|---|---|
| 文件系统 | 文件读写、目录操作 | 文档处理、数据管理 |
| 数据库连接 | MySQL、PostgreSQL、SQLite | 数据查询、统计分析 |
| API 集成 | GitHub、Notion | 外部服务交互 |
| 开发工具 | Git 操作、代码执行 | 软件开发、版本控制 |
🔗 了解更多
访问 Model Context Protocol 了解更多关于 MCP 的信息和可用工具。
📚 总结与下一步
工具在增强 AI 智能体能力方面至关重要。
✅ 本节要点总结:
- 🔧 工具定义: 通过提供清晰的文本描述、输入参数、输出结果及可调用函数
- ⚡ 工具本质: 赋予LLM额外能力的函数(如执行计算或访问外部数据)
- 🎯 工具必要性: 帮助智能体突破静态模型训练的局限,处理实时任务并执行专业操作
- 🤖 自动化生成: 利用Python装饰器和自省特性自动生成工具描述
- 🌐 统一标准: MCP协议提供统一的工具接口规范
关键理解
加载图表中...