2. 基础概念
好,我们进入第二章。这一章聊的是工具调用的「身份证」——工具定义与 Schema。
说白了,AI Agent 要调用一个工具,它得先知道这个工具是干什么的、需要什么参数、返回什么结果。这些信息不能靠猜,得有一套标准化的描述语言。这就是 OpenAPI/Swagger 规范和 JSON Schema 要做的事。
我刚开始做 Agent 编排时,觉得工具定义不就是写个接口文档吗?后来发现,差之毫厘,谬以千里。一个参数类型写错,AI 可能就给你传个字符串当数组用,整个流程就崩了。
2.1 为什么需要标准化的工具定义?
想象一下,你让一个实习生去拿快递。你只说「去拿个东西」,他肯定懵。你得说清楚:去哪拿、拿什么、拿几个、什么时候拿回来。
AI Agent 也一样。它面对的不是人类,是 API。API 不会猜你的心思。所以我们需要一份精确的「说明书」。
核心价值:标准化工具定义 = 机器可读的契约。它让 AI 能自动理解工具的能力边界,避免「鸡同鸭讲」。
目前业界最通用的标准,就是 OpenAPI 规范(以前叫 Swagger)。它最初用于 RESTful API 的文档化,现在成了 AI Agent 工具定义的基石。
2.2 OpenAPI/Swagger 规范:工具的门面
OpenAPI 规范定义了一个工具(API)的三大要素:
- 端点(Endpoint): 工具在哪?比如
GET /api/weather - 操作(Operation): 工具能做什么?比如查询天气
- 参数(Parameter): 需要什么输入?比如城市名、日期
来看一个最简单的例子。这是一个查询天气的工具定义:
openapi: 3.0.0
info:
title: 天气查询工具
description: 根据城市名称获取当前天气信息
version: 1.0.0
paths:
/weather:
get:
summary: 获取指定城市的天气
operationId: getWeather
parameters:
- name: city
in: query
required: true
description: 城市名称,例如 "北京"
schema:
type: string
responses:
'200':
description: 成功返回天气数据
content:
application/json:
schema:
type: object
properties:
temperature:
type: number
description: 当前温度(摄氏度)
humidity:
type: number
description: 湿度百分比
这段 YAML 告诉 AI:
- 工具名叫「天气查询工具」
- 调用方式是 GET 请求,路径是
/weather - 必须传一个参数
city,类型是字符串 - 返回的数据包含温度和湿度
嗯,这里要注意。很多初学者会忽略 operationId。我个人习惯给它起一个语义明确的名字,比如 getWeather 而不是 queryData。因为 AI 模型在意图匹配时,会优先看这个 ID 和 summary 字段。
2.3 JSON Schema:参数的精确约束
OpenAPI 里定义参数时,底层依赖的是 JSON Schema。它才是真正定义「参数长什么样」的硬规则。
JSON Schema 可以约束:
- 类型: 字符串、数字、布尔值、数组、对象
- 取值范围: 最小值、最大值、枚举值
- 格式: 邮箱、日期、URI
- 嵌套结构: 对象里的属性、数组里的元素类型
举个例子。假设我们要定义一个「创建订单」的工具,参数比较复杂:
{
"type": "object",
"properties": {
"userId": {
"type": "string",
"description": "用户ID,格式为UUID",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
},
"items": {
"type": "array",
"description": "订单商品列表",
"minItems": 1,
"maxItems": 50,
"items": {
"type": "object",
"properties": {
"productId": { "type": "string" },
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 100
},
"price": { "type": "number", "minimum": 0 }
},
"required": ["productId", "quantity", "price"]
}
},
"shippingAddress": {
"type": "object",
"properties": {
"province": { "type": "string" },
"city": { "type": "string" },
"detail": { "type": "string" }
},
"required": ["province", "city", "detail"]
}
},
"required": ["userId", "items", "shippingAddress"]
}
我的经验: 参数描述越详细,AI 调用越准确。尤其是 description 字段,别写「用户ID」就完了。我会写成「用户唯一标识,从登录接口获取的 UUID 字符串」。AI 看到这个,就知道去哪找这个值。
2.4 工具描述与意图匹配:让 AI 听懂人话
定义好参数只是第一步。更难的是让 AI 知道「什么时候该用这个工具」。这就靠工具描述了。
工具描述是写给 AI 模型看的,不是给人看的。它决定了意图匹配的准确率。
来看一个对比:
| 描述方式 | 示例 | AI 理解效果 |
|---|---|---|
| 太简单 | 查询天气 | 用户说「今天冷吗?」可能匹配不上 |
| 太啰嗦 | 这是一个用于查询天气的工具,它可以通过城市名称获取天气数据,包括温度、湿度、风速等 | 关键词分散,可能误匹配 |
| 精准描述 | 根据城市名称查询实时天气,返回温度、湿度、风力。适用于询问「今天天气怎么样」「冷不冷」「要不要带伞」等场景 | 高准确率匹配 |
我曾经踩过一个坑。有个工具描述写的是「发送邮件」,结果用户说「帮我发个通知」,AI 死活不调用这个工具。后来我把描述改成「发送邮件通知,支持文本和 HTML 内容,适用于发送系统通知、验证码、营销邮件等场景」,准确率直接翻倍。
避坑指南: 工具描述里不要写「如果……那么……」这种条件逻辑。AI 模型不是规则引擎,它靠语义理解。你写「当用户要求发送验证码时使用此工具」,不如直接写「发送验证码邮件」。把场景写进描述,而不是写逻辑。
2.5 知识结构:工具定义的核心要素
我把工具定义的核心要素整理成了一张表,方便你对照检查:
| 要素 | 说明 | 常见错误 |
|---|---|---|
| 工具名称 | 简短、唯一、语义化 | 用拼音或缩写,如 getWth |
| 工具描述 | 包含触发场景、输入输出说明 | 只写功能,不写场景 |
| 参数定义 | 类型、格式、取值范围、是否必填 | 缺少 description,AI 不知道参数含义 |
| 返回结构 | 定义返回数据的 Schema | 不定义返回结构,AI 无法解析结果 |
| 错误处理 | 定义可能的错误码和错误信息 | 忽略错误场景,AI 调用失败后不知所措 |
2.6 工具定义的整体流程
下面这张图展示了从需求到工具定义的完整链路:
2.7 实战建议:如何写出高质量的工具定义
最后,分享几个我实际项目里总结出来的经验:
- 描述要「场景化」:别只写功能,写清楚用户在什么场景下会触发这个工具。比如「查询天气」不如「用户想知道今天要不要带伞、穿多少衣服时使用」。
- 参数名要「自解释」:用
cityName而不是c,用startDate而不是sd。AI 模型对语义敏感,好的参数名能提升填充准确率。 - 枚举值要写全:如果参数只有固定几个选项,用
enum列出来。比如天气工具的城市列表,写死支持的几个城市,AI 就不会乱传。 - 返回结构别偷懒:很多同学只定义请求参数,不定义返回结构。结果 AI 拿到返回数据后不知道怎么用。你想想看,它连返回字段叫什么都搞不清楚,怎么提取信息?
一个小技巧: 写完工具定义后,用自然语言描述一遍这个工具是干什么的,然后让 ChatGPT 或 Claude 根据你的描述去匹配。如果它匹配错了,说明你的描述需要优化。我每次上线新工具都会做这个测试,屡试不爽。