工具设计原则
工具(Tool)是 Agent 与外部世界交互的唯一接口。工具设计的质量直接决定 Agent 能否正确调用——描述不清会让模型猜错参数,职责模糊会导致调用失败或副作用难以追踪。
核心原则
| 原则 | 说明 |
|---|---|
| 单一职责 | 每个工具只做一件事,避免”万能工具” |
| 描述即契约 | description 要说清楚:做什么、何时用、边界是什么 |
| 参数最小化 | 只暴露必要参数,可选参数给合理默认值 |
| 幂等优先 | 读操作天然幂等;写操作设计成可重试(同一请求多次调用结果一致) |
| 错误语义明确 | 失败时返回结构化错误,而不是抛出未捕获异常或返回空值 |
| 权限最小化 | 工具只拥有完成其职责所需的最低权限 |
工具 Schema 规范
{
"name": "search_web",
"description": "搜索互联网获取最新信息。适用于需要实时数据、近期事件或模型训练截止日期之后的内容。不适用于已知的、静态的知识。",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,使用自然语言,不超过 100 字"
},
"max_results": {
"type": "integer",
"description": "返回结果数量,默认 5,最大 20",
"default": 5
}
},
"required": ["query"]
}
}描述的黄金公式:[动词] + [对象] + [适用场景] + [不适用场景(可选)]
常见设计错误
❌ 描述太短
{ "name": "run_code", "description": "运行代码" }模型不知道:什么语言?有副作用吗?能访问文件系统吗?
✅ 描述充分
{
"name": "run_python",
"description": "在沙箱环境中执行 Python 代码并返回 stdout/stderr。可访问预装库(pandas、numpy、matplotlib)。无网络访问,无文件持久化,执行超时 30 秒。适合数据处理、计算、绘图。"
}❌ 参数过于宽泛
{ "action": "string 任意操作" }✅ 使用枚举约束
{
"action": {
"type": "string",
"enum": ["read", "write", "delete"],
"description": "操作类型:read 只读,write 创建或覆盖,delete 永久删除"
}
}返回值设计
# 统一返回结构
@dataclass
class ToolResult:
success: bool
data: Any # 成功时的结果
error: str | None # 失败时的原因(模型可读)
metadata: dict # 可选:执行时长、来源等
# 示例:搜索工具返回
{
"success": True,
"data": [
{"title": "...", "url": "...", "snippet": "..."}
],
"error": None,
"metadata": {"query_time_ms": 312}
}危险工具分级
| 级别 | 示例 | 要求 |
|---|---|---|
| 🟢 安全 | 读文件、搜索、查天气 | 直接执行 |
| 🟡 敏感 | 发邮件、写数据库、调第三方 API | 日志记录 + 限速 |
| 🔴 危险 | 删数据、执行系统命令、转账 | 人工确认(Human-in-the-loop) |
# 危险操作强制人工确认
def delete_file(path: str) -> ToolResult:
if not require_human_approval(f"确认删除 {path}?"):
return ToolResult(success=False, error="用户取消操作", data=None)
...工具测试清单
- 参数全部合法时能正确执行
- 参数缺失/类型错误时返回清晰的错误信息
- 权限不足时拒绝并说明原因
- 超时或外部服务故障时能优雅降级
- 多次调用(幂等性)结果一致
- 返回的错误信息模型能理解并据此重试