工具设计原则
一个好的 Agent 工具应遵循以下原则:
- 单一职责:每个工具只做一件事,做好一件事
- 清晰的接口:输入输出明确,易于理解和使用
- 健壮的错误处理:优雅地处理各种异常情况
- 可观测性:提供日志和指标,便于调试和监控
- 安全性:对敏感操作有权限控制和审计
工具接口设计
from pydantic import BaseModel, Field
from typing import Optional, List, Any
from enum import Enum
class ToolCategory(str, Enum):
SEARCH = "search"
DATA = "data"
ACTION = "action"
UTILITY = "utility"
class ToolParameter(BaseModel):
name: str
type: str
description: str
required: bool = True
enum: Optional[List[str]] = None
class ToolDefinition(BaseModel):
name: str = Field(description="工具名称")
description: str = Field(description="详细描述,帮助Agent理解何时使用")
category: ToolCategory
parameters: List[ToolParameter]
risk_level: str = Field(default="low", pattern="^(low|medium|high|critical)$")
def to_openai_function(self) -> dict:
"""转换为 OpenAI Function Calling 格式"""
properties = {}
required = []
for param in self.parameters:
prop = {"type": param.type, "description": param.description}
if param.enum:
prop["enum"] = param.enum
properties[param.name] = prop
if param.required:
required.append(param.name)
return {
"name": self.name,
"description": self.description,
"parameters": {
"type": "object",
"properties": properties,
"required": required
}
}实现一个完整的工具
import logging
import time
from functools import wraps
logger = logging.getLogger(__name__)
def tool_logger(func):
"""工具装饰器:自动记录调用日志和耗时"""
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
try:
result = func(*args, **kwargs)
elapsed = time.time() - start
logger.info(f"Tool {func.__name__} succeeded in {elapsed:.2f}s")
return result
except Exception as e:
elapsed = time.time() - start
logger.error(f"Tool {func.__name__} failed in {elapsed:.2f}s: {e}")
raise
return wrapper
class DatabaseQueryTool:
"""数据库查询工具"""
definition = ToolDefinition(
name="query_database",
description="查询数据库,支持SQL查询。用于获取用户数据、订单信息、统计报表等",
category=ToolCategory.DATA,
parameters=[
ToolParameter(
name="query_type",
type="string",
description="查询类型",
enum=["user", "order", "stats"]
),
ToolParameter(
name="filters",
type="object",
description="查询过滤条件",
required=False
)
],
risk_level="medium"
)
def __init__(self, db_connection):
self.db = db_connection
self.allowed_tables = ["users", "orders", "products"]
@tool_logger
def execute(self, query_type: str, filters: dict = None):
# 安全校验
if query_type not in ["user", "order", "stats"]:
return {"error": f"不支持的查询类型: {query_type}"}
# 构建安全的查询
query = self._build_safe_query(query_type, filters or {})
# 执行查询
result = self.db.execute(query)
return {
"success": True,
"data": result,
"count": len(result)
}
def _build_safe_query(self, query_type: str, filters: dict):
# 使用参数化查询,防止 SQL 注入
# 只允许查询预定义的表
pass工具注册与发现
class ToolRegistry:
"""工具注册中心"""
def __init__(self):
self._tools: dict[str, Any] = {}
def register(self, tool):
"""注册工具"""
name = tool.definition.name
if name in self._tools:
raise ValueError(f"工具 {name} 已注册")
self._tools[name] = tool
logger.info(f"注册工具: {name}")
def get_all_definitions(self):
"""获取所有工具定义(用于 Function Calling)"""
return [
tool.definition.to_openai_function()
for tool in self._tools.values()
]
def execute(self, name: str, params: dict):
"""执行工具"""
if name not in self._tools:
return {"error": f"工具不存在: {name}"}
tool = self._tools[name]
# 高风险操作需要确认
if tool.definition.risk_level in ["high", "critical"]:
if not self._confirm_execution(name, params):
return {"error": "操作被用户取消"}
return tool.execute(**params)工具测试
import pytest
class TestDatabaseQueryTool:
def setup_method(self):
self.tool = DatabaseQueryTool(MockDB())
def test_valid_query(self):
result = self.tool.execute("user", {"name": "张三"})
assert result["success"] is True
assert "data" in result
def test_invalid_query_type(self):
result = self.tool.execute("delete_all")
assert "error" in result
def test_sql_injection_prevention(self):
result = self.tool.execute("user", {"name": "'; DROP TABLE users; --"})
assert result["success"] is True # 不应该执行恶意SQL工具性能优化
- 结果缓存:对相同参数的查询结果进行缓存
- 超时控制:为每个工具设置执行超时时间
- 并行执行:独立的工具调用可以并行执行
- 结果裁剪:返回给 LLM 的结果应精简,只包含必要信息
总结
工具是 Agent 能力的延伸。好的工具设计让 Agent 如虎添翼,差的工具设计让 Agent 寸步难行。投入时间做好工具设计和测试,是构建高质量 Agent 系统的关键。