编写按需加载的技能
将模型仅在相关时才拉取的能力打包——创建它并将其附加到智能体上。
list_skillscreate_skillupdate_agenttest_skill_trigger原则——保持技能数量精简。 仅附加该智能体工作所需的技能(约 5 个或更少)。模型根据其单行
description选择技能;你附加的越多,它加载错误技能或完全不加载的概率就越高。如果两个技能在何时使用上重叠,请将它们合并。更多详情请参阅 设计原则。
技能是一个按需加载的能力包:系统提示词仅携带其 description(“何时使用”);模型仅在判断技能相关时才提取完整的 instructions。因此,
description 必须简短并说明何时使用,否则模型将不知道去调用它。
list_skills() # what already exists
create_skill(
name="refund-policy",
description="Use when the user asks about refunds, cancellations or chargebacks.",
instructions="The full procedure: eligibility windows, how to word the outcome, when to escalate …",
)
update_agent(name="Support", add_skills=["refund-policy"]) # attach it (incremental — keeps existing skills)将流程保留在 instructions(加载时获取)中,而不是放在智能体的 soul 中(后者在每一轮提示词中)。一条说明何时使用的单行 description 才能使技能可被发现——模糊的描述意味着模型永远不会调用它。
工具可以依附于技能
技能可以携带其自己的工具绑定——create_skill(tools=[…]) 或
update_skill(name, add_tools=[…]),因此技能作为一个自包含的能力包交付:流程加上它所需的工具,一次性附加。
技能自带的工具仅在技能加载后才可用。 它们在轮次开始时不在工具集中;加载技能会将它们加入其中。因此顺序是固定的:先读取流程,然后获取工具——模型不能跳过流程直接去使用工具并即兴发挥。
这种顺序在代码中强制执行,而不是在提示词中请求,因为请求并没有起作用。2026-08-22 的测量数据显示,在“研究公司 X 的竞争对手”这一任务中,将流程和工具一起提供:模型调用了三次网络搜索,但从未加载技能。它从记忆中编造了竞争对手名称,搜索这些编造的名称,未找到任何结果(它们不存在),并将编造的名称作为答案写出——而它从未阅读的流程第一步说*“首先确定该公司实际销售什么”*。可选的流程不会被阅读。
在依赖它之前,有两点需要了解:
- 技能工具不出现在智能体自己的
tools列表中。get_agent仅显示智能体直接附加的工具;控制台的“工具”选项卡将技能绑定的工具显示为单独的只读“via skills”行。在验证“工具 X 是否启用”时,请检查这两个地方——或者只是在测试聊天中调用该工具。 - 智能体已有的工具不受影响。 如果智能体本身携带
web_search,它在整个轮次中始终可用;只有因为技能而到达的工具才等待该技能加载。 - 当工具仅在特定流程内有意义时(退款技能内的退款查询),优先将其绑定到技能;当它在整个轮次中普遍有用时(
save_contact、web_search),将其绑定到智能体。
绑定工具仅代表可用性,而非策略——指令必须说明何时调用它
在技能(或智能体)上检查工具仅使其可调用。模型在每一轮都能看到工具的 schema 及其单行描述,因此它可能会机会主义地使用它——访客主动提供电子邮件,save_contact 就会触发。但任何必须可靠发生的行为都需要写下来,且其每一部分都有一个正确的归属:
| 你指定的内容 | 放置位置 |
|---|---|
| 何时加载此技能 | 技能的 description(每一轮都在提示词中) |
| 流程:在哪个步骤调用哪个工具,以及使用什么参数 | 技能的 instructions —— 明确命名工具(“在呼叫者确认兴趣后,调用 save_contact 并传入电子邮件和电话;然后调用 schedule_followup 安排 1 个工作日后的跟进”) |
| 必须驱动整个对话的行为(始终捕获线索,从不报价) | 智能体的 soul / task |
指令未命名工具的失败模式:一切看起来都配置好了——工具已勾选,技能已加载——但智能体仍然从不调用它,或以猜测的参数调用它。不会报错。将流程写得像是在向新员工简报:步骤、工具名称、参数,以及“完成”的样子。
强制工具调用:触发器必须位于 description 中,而不仅限于 instructions
instructions 仅在模型调用 load_skill 之后对模型可见——而模型经常在不加载技能的情况下直接回答。因此,仅在 instructions 中编写的规则如“当用户提供电话号码时,你必须调用 save_contact”在关键时刻是不可见的:工具静默地未被调用,模型甚至可能声称它已保存了联系人。这在生产中发生过——一个智能体连续回答了多条包含联系信息的消息,“确认”细节已记录,但实际上没有任何记录。
修复方法是在 description 中加一句话(这句话确实在每一轮的系统提示词中):
update_skill(
name="consultative-sales",
description="Consultative sales: understand the case, recommend products, arrange expert callbacks. "
"When the user provides a phone number or email, call save_contact BEFORE answering anything else.",
)将详细流程保留在 instructions 中;将任何强制调用的触发器放在
description 中。
两条相关规则:
- 永远不要脚本化工具不产生的返回值——逐字转发真实值。 内置的
save_contact和schedule_followup在成功时返回一个真实参考代码(格式AB2C-D3EF,存储在服务器端,并在最终用户详情页对租户可见)。指示模型逐字转发工具结果中的代码——永远不要编造一个或将其重新格式化为“#12345”。对于任何其他工具,在脚本化之前验证它是否实际返回 ID:承诺但缺失的 ID 将被编造。 create_skill/update_skill/get_skill现在返回一个warnings列表,用于标记 exactly 这两种模式(trigger_hidden_in_instructions、promised_tool_return_id)。警告是建议性的——保存成功——但将其视为 linter:修复,不要忽略。
instructions 中的示例可显著提高工具调用合规率
我们在生产后端上对此进行了基准测试(6 条难度递增的线索捕获消息——数字隐藏在长问题中、带空格的数字组、更正——按条件采样)。技能加载后,单纯的“你必须调用 X”指令仅达到 3–4/6;添加简短的示例块使两个测试模型都达到了 6/6。移动指令部分的位置没有任何效果——位置无关紧要,示例才关键。
一个有效的示例块包含四种类型的条目,每种一行:
### Worked examples (follow exactly)
1. User: "I'd like to know about X, my phone is 13800138000"
→ call save_contact(phone="13800138000") first, then answer about X.
2. User: "email me the offer: li@example.com"
→ call save_contact(email="li@example.com").
3. COUNTER-EXAMPLE (forbidden): user gives a phone number and you reply
"I've noted it down" WITHOUT calling the tool — claiming success without
the call is the worst failure.
4. User: "sorry, wrong number — it's 13633334444"
→ call save_contact again with the corrected value.
5. Numbers may contain spaces ("138 0013 9000") — still a phone number;
strip the spaces and call save_contact(phone="13800139000").反例 (3) 和格式边缘情况 (5) 关闭了大部分剩余的遗漏——模型在识别(“这是电话号码吗?”)和压力下的诚实度(先回答丰富的领域问题并声称保存已发生)上失败,多于在意愿上。保持约 5 个条目;使用确切的调用语法和真实参数。
在发布前测试触发器——不要在生产环境中统计尸体
技能是否真正触发是可测量的,因此请测量它。test_skill_trigger 对你的消息进行干运行,使用生产提示词组装、工具 schema 和模型路由,并报告模型的决定——工具永远不会执行,不会存储任何内容,token 计入你的配额(上限:5 条消息 × 5 个样本)。
test_skill_trigger(
agent="advisor",
messages=[
"My phone is 555 0123, call me back", # easy
"long question about the product … oh and my number is 555 0123", # buried
"555 0123 — that's me", # implicit
"sorry, wrong number, it's 555 9999", # correction
],
expect_tool="save_contact",
samples=3,
loaded=true, # simulate post-load_skill → tests instructions quality
) # loaded=false (default) → first turn, tests the description trigger像这样解读结果:
hit_rate在真实消息上低于 ~90% → 加强触发器(description)或添加示例(instructions),然后重新测试。claimed_without_call> 0 是最严重的失败——模型告诉用户“已记录!”但未调用工具。添加上述块中的反例。- 测试两种模式:
loaded=false证明描述本身在首轮触发;loaded=true证明加载的指令没有稀释它(长指令确实会稀释——这就是示例块补偿的原因)。
结果还包含一个 advice 列表:当样本遗漏或撒谎时,它确切地告诉你应用哪种修复(将触发器放入描述、添加示例块、添加反例或格式边缘情况),并附带每个推荐背后的基准数字——应用它并重新测试。
完整循环:create_skill → 修复任何 warnings(静态 lint)→ test_skill_trigger(动态现实检查)→ 应用其 advice → 重新测试直到命中率稳定。