如何设计一个 Skill 的描述 元信息结构,使 Agent 能够自动理解和选用合适的 Skill?
面试官:“如果要让 Agent 自动理解和选用合适的 Skill,你会怎么设计 Skill 的描述或元信息结构?”
候选人:
“这是个很实际的问题。Skill 元信息就像给 Agent 看的‘岗位说明书’,写得太简陋 Agent 看不懂,写得太复杂又难以维护。我觉得设计这套结构时,核心要回答三个问题:这个 Skill 能干什么(能力声明)、它需要什么输入(参数语义)、在什么情况下该用它(触发与约束)。
下面是我在实践中总结的一套分层元信息结构。
第一层:基础身份 —— 唯一标识与分类
{
"name": "book_flight",
"version": "1.2.0",
"category": "travel/transportation",
"tags": ["flight", "booking", "payment", "travel"],
"owner": "travel-team"
}
-
name:唯一标识,Agent 内部通过它来调度。
-
version:版本号,支持多版本共存、灰度上线。
-
category / tags:分类和标签,方便 Agent 做粗粒度的意图筛选。比如用户问旅行相关的问题时,优先在
travel分类下找 Skill。 -
owner:维护团队,方便出问题时快速找到责任人。
第二层:意图与描述 —— 让 Agent 知道“什么时候该用我”
这是最关键的一层,直接决定了 Agent 能否正确匹配 Skill。
{
"description": {
"short": "预订国内国际机票",
"long": "根据出发地、目的地、日期、乘客人数等条件,查询可用航班并完成机票预订,支持经济舱/商务舱/头等舱筛选。",
"trigger_keywords": ["订机票", "买机票", "航班", "出行", "飞往"],
"trigger_examples": [
"帮我订一张明天去上海的机票",
"查询北京到深圳的航班",
"我要买下周三去成都的头等舱"
],
"semantic_vector": "0x4a7b..."
}
}
这里有几个设计要点:
-
分层描述:
short用于快速筛选,long用于精确匹配,Agent 在粗筛时看 short,锁定候选 Skill 后再比 long。 -
触发关键词与示例:这不是简单的字符串匹配,而是给 Agent 做语义相似度计算用的种子样本。Agent 拿到用户输入后,会用嵌入模型分别计算用户输入与
trigger_examples的相似度,超过阈值就激活这个 Skill。 -
语义向量:把长描述预计算成向量存起来,运行时直接做向量相似度搜索,比每次实时编码快得多。这在 Skill 数量超过几十个时尤其重要。
第三层:输入输出 Schema —— 让 Agent 知道“该给我什么”
{
"parameters": [
{
"name": "departure",
"type": "string",
"required": true,
"description": "出发城市名称",
"default": null
},
{
"name": "destination",
"type": "string",
"required": true,
"description": "到达城市名称",
"default": null
},
{
"name": "date",
"type": "string",
"required": true,
"description": "出发日期,格式 YYYY-MM-DD",
"default": null
},
{
"name": "cabin_class",
"type": "enum",
"enum_values": ["economy", "business", "first"],
"required": false,
"description": "舱位等级,默认经济舱",
"default": "economy"
}
],
"output": {
"type": "object",
"description": "返回符合条件的航班列表及预订状态",
"schema": { "flights": "array", "booking_id": "string", "status": "string" }
}
}
设计要点:
-
必填 vs 可选:Agent 看到
required: true的参数如果缺失,就会触发澄清流程,主动反问用户。 -
枚举约束:像
cabin_class这种有限值的参数,直接用枚举定义,防止 Agent 自己发明不存在的值。 -
默认值:降低用户输入负担,也减少 Agent 追问的次数。
-
输出描述:不仅让人看,也让 Agent 知道调用完能拿到什么,方便它规划后续步骤(比如预订成功后是否需要调用“发送行程邮件”Skill)。
第四层:前置条件与约束 —— 让 Agent 知道“什么时候不能乱用”
{
"preconditions": {
"required_capabilities": ["payment_authorized", "user_logged_in"],
"rate_limit": { "max_calls_per_minute": 10, "max_calls_per_user_hour": 3 },
"timeout_ms": 30000,
"retry_policy": { "max_retries": 2, "backoff": "exponential" }
},
"side_effects": ["creates_order", "charges_payment"],
"rollback_skill": "cancel_flight"
}
设计要点:
-
前置条件:Agent 在触发 Skill 前会先检查这些条件是否满足。比如用户没登录,就先把“登录”Skill 激活,再回来继续。
-
频率限制:防止 Agent 或用户在短时间内疯狂触发同一个 Skill,保护后端服务。
-
超时与重试:声明这个 Skill 的预期响应时间,Agent 可以根据它来设定等待策略。
-
副作用声明:明确告诉 Agent “这个 Skill 会产生订单、会扣款”,这样 Agent 在规划时会把它放在关键路径上,也方便事后审计。
-
回滚 Skill:如果后续出错需要补偿,Agent 知道该调用哪个 Skill 来做撤销。
第五层:依赖与协作 —— 让 Agent 知道“我和谁有关系”
{
"dependencies": {
"requires": ["user_profile", "payment"],
"optional": ["travel_insurance", "hotel_booking"],
"conflicts_with": ["refund_processing"]
},
"composition": {
"can_be_subtask_of": ["travel_planning"],
"can_use_as_subtask": ["payment", "send_invoice"]
}
}
设计要点:
-
必须依赖:比如“支付”Skill 挂了,那“订机票”就不能执行。Agent 在规划时会检查依赖的可用性。
-
可选协作:如果用户也需要订酒店,可以在订机票后顺带推荐,这是提升体验的机会点。
-
冲突声明:有些 Skill 不能同时跑(比如“退款”和“下单”),Agent 会做互斥判断。
-
组合声明:声明这个 Skill 可以作为哪些大任务的一部分,以及它可以调用哪些小 Skill。这让 Agent 在规划复杂任务时能自动拼装多个 Skill 形成一条链。