工具定义与注册
工具定义是 Agent 工具系统的基石。一个好的工具定义能让 LLM 准确理解工具的用途、正确构建参数、避免误用。本章详细介绍工具定义的标准、注册机制和设计原则。
一、核心原理
1.1 工具定义的组成要素
一个完整的工具定义包含以下核心要素:
┌─────────────────────────────────────────────────────────────┐
│ 工具定义组成要素 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Tool Definition │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ 1. 元数据 (Metadata) │ │
│ │ • name: 工具唯一标识符 │ │
│ │ • description: 自然语言描述 │ │
│ │ • version: 版本号 │ │
│ │ • category: 分类标签 │ │
│ │ │ │
│ │ 2. 参数模式 (Parameter Schema) │ │
│ │ • type: 参数类型 (object) │ │
│ │ • properties: 各参数定义 │ │
│ │ • required: 必填参数列表 │ │
│ │ • definitions: 共享类型定义 │ │
│ │ │ │
│ │ 3. 行为配置 (Behavior Config) │ │
│ │ • timeout: 超时时间 │ │
│ │ • retry_policy: 重试策略 │ │
│ │ • permission: 权限等级 │ │
│ │ • side_effects: 副作用标记 │ │
│ │ │ │
│ │ 4. 返回模式 (Return Schema) │ │
│ │ • type: 返回值类型 │ │
│ │ • properties: 返回字段定义 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘1.2 JSON Schema 基础
工具参数使用 JSON Schema 定义,这是工具定义的核心:
{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"limit": {
"type": "integer",
"description": "返回结果数量限制",
"default": 10,
"minimum": 1,
"maximum": 100
},
"filters": {
"type": "object",
"description": "过滤条件",
"properties": {
"date_range": {
"type": "object",
"properties": {
"start": {"type": "string", "format": "date"},
"end": {"type": "string", "format": "date"}
}
},
"source": {
"type": "string",
"enum": ["web", "news", "academic"]
}
}
}
},
"required": ["query"]
}1.3 JSON Schema 类型系统
| 类型 | 说明 | 示例 |
|---|---|---|
string | 字符串 | "hello" |
integer | 整数 | 42 |
number | 数字(含浮点) | 3.14 |
boolean | 布尔值 | true |
array | 数组 | [1, 2, 3] |
object | 对象 | {"key": "value"} |
null | 空值 | null |
二、工具定义标准
2.1 OpenAI Function Calling 格式
"""
OpenAI Function Calling 工具定义格式
"""
# 标准格式
openai_tool_format = {
"type": "function",
"function": {
"name": "search_web", # 工具名称
"description": "搜索互联网获取信息,适用于需要实时数据或最新信息的场景", # 工具描述
"parameters": { # 参数 Schema
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,建议使用简洁精确的词语"
},
"num_results": {
"type": "integer",
"description": "返回结果数量",
"default": 5,
"minimum": 1,
"maximum": 20
},
"search_type": {
"type": "string",
"enum": ["web", "news", "images", "videos"],
"description": "搜索类型"
}
},
"required": ["query"]
}
}
}2.2 Anthropic Tool Use 格式
"""
Anthropic Claude Tool Use 工具定义格式
"""
anthropic_tool_format = {
"name": "search_web",
"description": "搜索互联网获取信息,适用于需要实时数据或最新信息的场景",
"input_schema": { # Anthropic 使用 input_schema 而非 parameters
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"num_results": {
"type": "integer",
"description": "返回结果数量",
"default": 5
}
},
"required": ["query"]
}
}2.3 LangChain Tool 格式
"""
LangChain 工具定义方式
"""
from langchain.tools import BaseTool, tool
from pydantic import BaseModel, Field
from typing import Optional, Literal
# 方式 1: 使用 @tool 装饰器(推荐)
@tool
def search_web(
query: str,
num_results: int = 5,
search_type: Literal["web", "news", "images"] = "web"
) -> str:
"""
搜索互联网获取信息
Args:
query: 搜索关键词,建议使用简洁精确的词语
num_results: 返回结果数量,默认5条
search_type: 搜索类型,可选 web/news/images
Returns:
搜索结果摘要
"""
# 实现逻辑...
return f"搜索 '{query}' 的结果..."
# 方式 2: 使用 Pydantic 定义输入模型
class SearchWebInput(BaseModel):
"""搜索工具输入参数"""
query: str = Field(description="搜索关键词")
num_results: int = Field(default=5, ge=1, le=20, description="返回结果数量")
search_type: Literal["web", "news", "images"] = Field(default="web", description="搜索类型")
# 方式 3: 继承 BaseTool 类
class SearchWebTool(BaseTool):
"""搜索工具类"""
name = "search_web"
description = "搜索互联网获取信息"
args_schema = SearchWebInput
def _run(
self,
query: str,
num_results: int = 5,
search_type: str = "web"
) -> str:
"""执行搜索"""
# 实现逻辑...
return f"搜索 '{query}' 的结果..."
async def _arun(
self,
query: str,
num_results: int = 5,
search_type: str = "web"
) -> str:
"""异步执行搜索"""
# 异步实现...
return f"搜索 '{query}' 的结果..."2.4 MCP 工具定义格式
"""
MCP (Model Context Protocol) 工具定义格式
"""
mcp_tool_format = {
"name": "search_web",
"description": "搜索互联网获取信息",
"inputSchema": { # MCP 使用 inputSchema
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
}
},
"required": ["query"]
},
# MCP 特有的元数据
"annotations": {
"title": "Web Search",
"readOnlyHint": True, # 只读操作
"destructiveHint": False, # 非破坏性操作
"idempotentHint": True # 幂等操作
}
}三、工具注册中心
3.1 注册中心架构
┌─────────────────────────────────────────────────────────────┐
│ 工具注册中心架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Tool Registry │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ 工具索引 │ │ 分类目录 │ │ │
│ │ │ │ │ │ │ │
│ │ │ • 名称索引 │ │ • 信息获取 │ │ │
│ │ │ • 功能索引 │ │ • 数据处理 │ │ │
│ │ │ • 语义索引 │ │ • 系统操作 │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ 元数据存储 │ │ 权限管理 │ │ │
│ │ │ │ │ │ │ │
│ │ │ • 版本信息 │ │ • 权限等级 │ │ │
│ │ │ • 依赖关系 │ │ • 访问控制 │ │ │
│ │ │ • 统计数据 │ │ • 审计日志 │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 工具实例池 │ │
│ │ │ │
│ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │
│ │ │搜索 │ │计算 │ │文件 │ │API │ │代码 │ │ │
│ │ │工具 │ │工具 │ │工具 │ │工具 │ │执行 │ │ │
│ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘3.2 注册中心实现
"""
工具注册中心实现
支持工具注册、检索、权限管理
"""
from typing import Dict, List, Optional, Callable, Any
from dataclasses import dataclass, field
from enum import Enum
import json
class PermissionLevel(Enum):
"""权限等级"""
READ_ONLY = 1 # 只读操作
LIMITED_WRITE = 2 # 有限写入
FULL_ACCESS = 3 # 完全访问
SYSTEM = 4 # 系统级操作
@dataclass
class ToolMetadata:
"""工具元数据"""
name: str
description: str
parameters: Dict[str, Any]
version: str = "1.0.0"
category: str = "general"
permission_level: PermissionLevel = PermissionLevel.READ_ONLY
timeout: int = 30 # 秒
has_side_effects: bool = False
tags: List[str] = field(default_factory=list)
examples: List[Dict] = field(default_factory=list)
class ToolRegistry:
"""
工具注册中心
功能:
- 工具注册与注销
- 工具检索(名称、功能、语义)
- 权限检查
- 版本管理
"""
def __init__(self):
self._tools: Dict[str, ToolMetadata] = {}
self._functions: Dict[str, Callable] = {}
self._categories: Dict[str, List[str]] = {}
self._tags: Dict[str, List[str]] = {}
def register(
self,
name: str,
function: Callable,
description: str,
parameters: Dict[str, Any],
**kwargs
) -> None:
"""
注册工具
Args:
name: 工具名称(唯一标识)
function: 执行函数
description: 工具描述
parameters: 参数 Schema
**kwargs: 其他元数据
"""
if name in self._tools:
raise ValueError(f"工具 '{name}' 已存在")
# 创建元数据
metadata = ToolMetadata(
name=name,
description=description,
parameters=parameters,
**kwargs
)
# 存储
self._tools[name] = metadata
self._functions[name] = function
# 更新分类索引
category = metadata.category
if category not in self._categories:
self._categories[category] = []
self._categories[category].append(name)
# 更新标签索引
for tag in metadata.tags:
if tag not in self._tags:
self._tags[tag] = []
self._tags[tag].append(name)
print(f"[注册] 工具 '{name}' 注册成功")
def unregister(self, name: str) -> bool:
"""注销工具"""
if name not in self._tools:
return False
metadata = self._tools[name]
# 从分类索引中移除
if metadata.category in self._categories:
self._categories[metadata.category].remove(name)
# 从标签索引中移除
for tag in metadata.tags:
if tag in self._tags:
self._tags[tag].remove(name)
# 删除工具
del self._tools[name]
del self._functions[name]
return True
def get_tool(self, name: str) -> Optional[ToolMetadata]:
"""获取工具元数据"""
return self._tools.get(name)
def get_function(self, name: str) -> Optional[Callable]:
"""获取工具执行函数"""
return self._functions.get(name)
def list_tools(
self,
category: Optional[str] = None,
tags: Optional[List[str]] = None
) -> List[ToolMetadata]:
"""
列出工具
Args:
category: 按分类过滤
tags: 按标签过滤
Returns:
工具元数据列表
"""
if category:
names = self._categories.get(category, [])
return [self._tools[n] for n in names if n in self._tools]
if tags:
names = set()
for tag in tags:
names.update(self._tags.get(tag, []))
return [self._tools[n] for n in names if n in self._tools]
return list(self._tools.values())
def get_openai_tools(self) -> List[Dict]:
"""获取 OpenAI 格式的工具定义列表"""
return [
{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.parameters
}
}
for tool in self._tools.values()
]
def check_permission(
self,
name: str,
required_level: PermissionLevel
) -> bool:
"""检查权限"""
tool = self._tools.get(name)
if not tool:
return False
return tool.permission_level.value >= required_level.value
# 使用示例
if __name__ == "__main__":
registry = ToolRegistry()
# 注册工具
registry.register(
name="search_web",
function=lambda query: f"搜索结果: {query}",
description="搜索互联网获取信息",
parameters={
"type": "object",
"properties": {
"query": {"type": "string", "description": "搜索关键词"}
},
"required": ["query"]
},
category="information",
tags=["search", "web"],
permission_level=PermissionLevel.READ_ONLY
)
registry.register(
name="delete_file",
function=lambda path: f"删除文件: {path}",
description="删除指定文件",
parameters={
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"}
},
"required": ["path"]
},
category="system",
tags=["file", "delete"],
permission_level=PermissionLevel.SYSTEM,
has_side_effects=True
)
# 列出工具
print("\n所有工具:")
for tool in registry.list_tools():
print(f" - {tool.name}: {tool.description}")
# 按分类查询
print("\n信息类工具:")
for tool in registry.list_tools(category="information"):
print(f" - {tool.name}")
# 获取 OpenAI 格式
print("\nOpenAI 工具定义:")
print(json.dumps(registry.get_openai_tools(), indent=2, ensure_ascii=False))3.3 大规模工具注册策略
当工具数量从 10 个增加到 1000+ 个时,需要采用分层注册策略:
┌─────────────────────────────────────────────────────────────┐
│ 大规模工具注册策略 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 问题:工具数量过多导致 │
│ • 上下文爆炸(Token 消耗) │
│ • 工具选择准确率下降 │
│ • 注册和维护困难 │
│ │
│ 解决方案:分层注册 + 按需加载 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 全局注册中心 │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 信息层 │ │ 处理层 │ │ 系统层 │ │ │
│ │ │ Registry│ │ Registry│ │ Registry│ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ │ │ │ │ │ │
│ │ ↓ ↓ ↓ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │搜索工具 │ │计算工具 │ │文件工具 │ │ │
│ │ │查询工具 │ │分析工具 │ │进程工具 │ │ │
│ │ │API工具 │ │转换工具 │ │网络工具 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 流程: │
│ 1. 意图分类 → 确定哪个层级 │
│ 2. 加载对应层级的工具定义 │
│ 3. 在该层级内选择具体工具 │
│ │
│ 示例: │
│ 用户: "搜索 Python 教程" │
│ → 意图分类: 信息获取 │
│ → 加载: 信息层 Registry (搜索、查询工具) │
│ → 工具选择: search_web │
│ │
└─────────────────────────────────────────────────────────────┘"""
分层工具注册实现
"""
class HierarchicalToolRegistry:
"""分层工具注册中心"""
def __init__(self):
self.registries: Dict[str, ToolRegistry] = {
"information": ToolRegistry(),
"processing": ToolRegistry(),
"system": ToolRegistry()
}
self.classifier = None # 意图分类器
def register_tool(
self,
layer: str,
name: str,
function: Callable,
**kwargs
):
"""在指定层级注册工具"""
if layer not in self.registries:
raise ValueError(f"未知层级: {layer}")
self.registries[layer].register(name, function, **kwargs)
def get_tools_for_intent(self, intent: str) -> List[Dict]:
"""根据意图获取相关工具"""
# 意图到层级的映射
intent_to_layer = {
"search": "information",
"query": "information",
"calculate": "processing",
"analyze": "processing",
"file": "system",
"process": "system"
}
# 确定层级
layer = intent_to_layer.get(intent, "information")
# 返回该层级的工具
return self.registries[layer].get_openai_tools()四、工具设计最佳实践
4.1 描述设计原则
┌─────────────────────────────────────────────────────────────┐
│ 工具描述设计原则 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 功能明确 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ❌ 模糊描述: │ │
│ │ "处理数据" │ │
│ │ │ │
│ │ ✅ 明确描述: │ │
│ │ "将 JSON 数据转换为 CSV 格式,支持嵌套结构展开" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 2. 适用场景 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ✅ 包含使用场景说明: │ │
│ │ "搜索互联网获取实时信息,适用于: │ │
│ │ • 需要最新数据(如股价、天气) │ │
│ │ • 训练数据截止后的信息 │ │
│ │ • 实时变化的动态内容" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 3. 限制说明 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ✅ 说明工具的限制: │ │
│ │ "注意:此工具仅支持公开可访问的网页, │ │
│ │ 无法访问需要登录的页面或内网资源" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 4. 使用示例 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ✅ 在参数描述中包含示例: │ │
│ │ "city: 城市名称,如 '北京'、'Shanghai'" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘4.2 参数设计原则
# 好的参数设计示例
good_parameters = {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,如 'Python教程' 或 'machine learning'"
},
"date_range": {
"type": "object",
"description": "时间范围过滤,可选",
"properties": {
"start": {
"type": "string",
"format": "date",
"description": "起始日期,格式 YYYY-MM-DD"
},
"end": {
"type": "string",
"format": "date",
"description": "结束日期,格式 YYYY-MM-DD"
}
}
},
"language": {
"type": "string",
"enum": ["zh", "en", "ja"],
"description": "语言过滤,默认不限",
"default": None
},
"num_results": {
"type": "integer",
"description": "返回结果数量,1-20之间",
"default": 10,
"minimum": 1,
"maximum": 20
}
},
"required": ["query"]
}
# 差的参数设计示例(避免)
bad_parameters = {
"type": "object",
"properties": {
"q": { # 参数名不清晰
"type": "string"
# 缺少 description
},
"n": { # 参数名缩写过度
"type": "integer"
# 缺少范围限制
}
}
# 缺少 required
}4.3 常见工具类型模板
"""
常用工具类型模板
"""
# 1. 搜索类工具
SEARCH_TOOL_TEMPLATE = {
"name": "search",
"description": "搜索{source}获取信息",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"filters": {
"type": "object",
"description": "过滤条件"
}
},
"required": ["query"]
}
}
# 2. 计算/处理类工具
CALCULATOR_TOOL_TEMPLATE = {
"name": "calculate",
"description": "执行数学计算或数据处理",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "计算表达式或处理指令"
},
"precision": {
"type": "integer",
"description": "结果精度(小数位数)",
"default": 2
}
},
"required": ["expression"]
}
}
# 3. 文件操作类工具
FILE_TOOL_TEMPLATE = {
"name": "file_operation",
"description": "{operation}文件",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "文件路径"
},
"content": {
"type": "string",
"description": "文件内容(写入时)"
},
"encoding": {
"type": "string",
"enum": ["utf-8", "gbk", "ascii"],
"default": "utf-8"
}
},
"required": ["path"]
}
}
# 4. API 调用类工具
API_TOOL_TEMPLATE = {
"name": "api_call",
"description": "调用{service} API",
"parameters": {
"type": "object",
"properties": {
"endpoint": {
"type": "string",
"description": "API 端点路径"
},
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"],
"default": "GET"
},
"params": {
"type": "object",
"description": "请求参数"
},
"body": {
"type": "object",
"description": "请求体(POST/PUT)"
}
},
"required": ["endpoint"]
}
}五、面试高频问题
Q1: 如何设计一个好的工具描述?
答案要点:
- 功能明确:清晰说明工具能做什么
- 适用场景:说明何时应该使用此工具
- 限制说明:说明工具不能做什么
- 使用示例:在参数描述中包含典型值
Q2: 工具数量很多时,如何解决上下文爆炸问题?
答案要点:
| 策略 | 说明 |
|---|---|
| 分层注册 | 按功能分类,按需加载 |
| Tool RAG | 语义检索相关工具 |
| 工具摘要 | 压缩工具描述 |
| 动态加载 | 根据意图动态注入工具定义 |
Q3: JSON Schema 中如何表示复杂参数类型?
答案要点:
{
"type": "object",
"properties": {
"nested_object": {
"type": "object",
"properties": {
"field": {"type": "string"}
}
},
"array_field": {
"type": "array",
"items": {"type": "string"}
},
"enum_field": {
"type": "string",
"enum": ["option1", "option2"]
},
"union_type": {
"oneOf": [
{"type": "string"},
{"type": "integer"}
]
}
}
}Q4: 如何防止 Agent 调用破坏性工具?
答案要点:
- 权限分级:工具设置权限等级,高风险工具需要更高权限
- 人类确认:破坏性操作需要人工确认
- 沙箱执行:在隔离环境中执行
- 审计日志:记录所有操作,便于追溯
Q5: 工具注册中心需要哪些核心功能?
答案要点:
| 功能 | 说明 |
|---|---|
| 工具注册 | 支持动态添加/删除工具 |
| 工具检索 | 按名称、分类、标签检索 |
| 格式转换 | 输出不同平台的工具定义格式 |
| 权限管理 | 检查工具调用权限 |
| 版本管理 | 支持工具版本控制 |
六、小结
| 概念 | 一句话总结 |
|---|---|
| 工具定义 | 包含元数据、参数 Schema、行为配置的完整描述 |
| JSON Schema | 标准化的参数类型定义语言 |
| 注册中心 | 管理工具生命周期、提供检索和权限控制 |
| 设计原则 | 功能明确、适用场景、限制说明、使用示例 |
| 大规模策略 | 分层注册 + 按需加载 + Tool RAG |
一句话总结:好的工具定义是 Agent 正确使用工具的前提,需要关注描述清晰、参数完整、权限分级和大规模场景下的分层管理。
最后更新:2026年3月18日