用决策模型做技能推荐:官方 cookbook 拆解
先说结论
- 官方 skill suggestion cookbook 解决的不是“从全量技能库召回”,而是把已经给定的候选技能做“最后一跳”选择:一次请求先对全量候选排名,再一次请求复核前 3 名。
- 官方数据:在 Hermes 182 个技能、488 次请求、claude-haiku-4-5-20251001 的设置下,带 TypeSafe suggestion 后,加载错技能从 16.8% 降到 7.3%,加载一个不合适技能的情况从 9.8% 降到 4.0%。
- 最小请求骨架可以很薄:state 放任务描述+候选技能摘要,questions 放一个 Choice 选技能、一个 Noul 判断是否需要技能/澄清;但候选集质量和技能描述边界决定效果上限。
- 低置信度不要硬选一个:官方 Confidence 文档明确低置信度应“Do not act”,工程上可映射为多推荐几个、请求澄清或转人工。
- 这个思路可以类推到内容推荐、模板推荐,但需要重新设计候选集与评价维度,不能直接照搬。
官方 cookbook 在解决什么
官方文档开头就点明问题:如果 agent 的技能很多,把全部技能截断后塞进 system message,会增加成本、降低技能选择性能,还会让后续上下文“腐烂”。官方原文说:
Agents choose skills by truncating and loading them all into the system message, which increases costs, degrades skill selection performance, and induces context rot for the rest of the session. —— 官方 cookbook:Skill suggestion
这段话说的是:全量加载不是一个可扩展方案。所以官方用两次 TypeSafe 请求来解决,官方描述是:
Picks at most one skill for an agent turn out of the 182 in Nous Research’s Hermes catalog, using two TypeSafe requests to rank and re-check the top candidates.
翻译成工程语言:第一跳从 182 个技能中粗筛,第二跳从粗筛结果里精筛,最多选出一个技能;如果都不合适,可以一个都不选。这个“至多一个”和“可以空选”是理解这篇 cookbook 的关键。
两次请求具体怎么走
官方 cookbook 给出的路线是渐进披露(progressive disclosure)。第一跳便宜地读全部 182 个技能,第二跳只详细读前 3 个。官方原文:
The first ranks every skill in the roster against the user’s turn and answers whether the turn needs a skill at all. The second re-reads only the top three, now with each skill’s full description and the opening of its instructions, and is free to reject all of them.
这里有两个动作很重要:第一次不仅排名,还要回答“这轮到底需不需要技能”;第二次带完整描述和指令开头,允许拒绝所有候选。这意味着系统不会硬选一个。
选中后,胜出技能名只作为一行额外 system prompt 加进去:
<skill_relevance>
Relevant to the current request: pptx-author. Ignore this if it does not fit what the user actually asked for.
</skill_relevance>
注意官方特别强调:rosters 本身不变,所以前缀缓存仍然有效。这可以降低额外成本。
官方 cookbook 里出现的一些常量值得记录(官方代码片段):SHORTLIST = 3、EXCERPT_CHARS = 700、GATE_THRESHOLD = 0.30、FITS_THRESHOLD = 0.30。其中 SHORTLIST = 3 表示第一跳只保留 3 个候选进入第二跳;GATE_THRESHOLD = 0.30 是三组 noul 的均值门槛,低于这个值就不给任何建议;FITS_THRESHOLD = 0.30 表示短名单里最佳“是否匹配”noul 低于这个值就丢掉。这些是官方 cookbook 的设定,不是普适标准。
官方给出的效果数据如下(官方数据,来源 1):在 488 次请求、claude-haiku-4-5-20251001 模型下,加载错技能的比率:agent 仅用 roster 为 16.8%,agent 加 TypeSafe suggestion 为 7.3%,agent 直接拿到正确答案为 2.5%;加载了一个但实际不需要的技能的比率:9.8% / 4.0% / 1.2%。第三行 2.5% 和 1.2% 是“给正确答案”的下限,说明即使给对技能,agent 也不一定加载,因此任何选择方法都有地板上限。
一个最小请求骨架(本文做法,不是官方示例)
下面给出一个最薄的请求骨架。它基于官方 Primitives 文档中 Choice、Noul、state、questions 的字段定义,但组合方式是本文的做法,不是官方 cookbook 原样代码。
from typesafe_sdk import Choice, Noul
state = """任务:帮我做一个 pitch deck,需要编辑 .pptx 文件。
候选技能:
- pptx-author:从零创建 PowerPoint 演示文稿
- pptx-edit:编辑现有 PowerPoint 文件
"""
questions = {
"pick_skill": Choice(
instructions="哪个技能最适合处理当前任务?如果都不合适请选 none",
criteria={
"pptx-author": "从零创建 PowerPoint 演示文稿",
"pptx-edit": "编辑现有 PowerPoint 文件",
"none": "没有合适的技能",
},
),
"needs_clarification": Noul(
instructions="是否需要进一步澄清用户意图才能可靠选择技能?",
),
}
这个骨架做了两件事:pick_skill 用 Choice 在候选技能里选一个,并带上 none 选项以防都不合适;needs_clarification 用 Noul 判断是否需要澄清。注意:官方 cookbook 里的 Noul 问的是“这轮是否需要技能”,而不是“是否需要澄清”。后者是我在最小骨架里做的类推,也算工程上常用的门控方式,但不是官方原文的固定字段。
候选集从哪来:召回与判断的分工
这一节必须写清楚,因为很多读者会误以为官方 cookbook 在做一个全库检索系统。我的判断是:官方 cookbook 假设候选集已经存在,即那 182 个 Hermes 技能已经在一个 roster 文件里;第一跳虽然读了全部 182 个,但那是排序和初筛,不是从更大的技能库中召回。官方原文说:“reading all 182 skills cheaply and then reading three of them in detail.” 这句话里的 182 是给定候选集的大小,不是官方教你从零检索。
所以召回仍然要靠你自己的检索/规则。决策模型只做“最后一跳”的判断——给定一批候选,哪个相对最合适、是否一个都不合适。如果召回漏了正确技能,决策模型再准也选不出来。这是自己推算,不是官方文档中的原话,但符合官方 cookbook 的结构安排。
边界与坑
第一个坑是技能描述含糊。官方 cookbook 举例:默认截断到 60 字符时,编辑 .pptx 文件的技能和创建 .pptx 文件的技能看起来几乎一样,用户要 pitch deck 时可能加载错。官方原文:
For example, at that width the skill that edits .pptx files reads nearly the same as the one that authors them.
因此候选技能的能力边界必须写清。Choice 的 criteria 里每个选项最好是一句能区分“做什么、不做什么”的描述,而不是一个含糊名字。官方 Primitives 文档也说 Choice 适合“one of a known set of options with no order between them”,并建议当列表可能无法覆盖所有输入时加 other 或 none of the above。
第二个坑是低置信度硬选一个。官方 Confidence 文档明确,低置信度对应“Do not act”,可以路由到人工、请求澄清或回退到其他系统。官方原文是:Low confidence: Do not act. Route to a human, request clarification, or fall back to a different system. 虽然 Noul 答案本身不带 confidence 属性(官方 Confidence 文档写明:Noul answers don’t carry one),但你可以用多个 noul 的均值做门槛,正如官方 cookbook 里 GATE_THRESHOLD = 0.30 所示。那只是一个示例常量,不是官方推荐的所有场景统一阈值;官方 Confidence 文档也提醒:“The correct threshold values depend on your domain and the performance of the model for your use case. Start with conservative thresholds, test with your own data, and adjust as you observe results.” 所以我建议把低置信度策略设为“多推荐几个”或“请求澄清”,而不是硬选一个。
迁移到内容/模板推荐时要注意什么(类推)
这一节明确是类推,不是官方示例。技能推荐中的“候选技能”可以替换成“候选内容条目”或“候选模板”。但有几个点需要重新设计:
- 候选集:内容和模板的候选量可能远大于 182,第一步全量粗筛的成本需要重新评估。如果你有自己的检索系统,应该先召回 top-k,再让决策模型做最后一跳。
- 评价维度:技能推荐通常是一个单选;内容推荐可能需要同时考虑相关性、时效性、质量等多个维度。你可以用 Primitives 里的
Score对每个维度打分,再在代码里加权,而不是让模型一次给最终排序。 - 技能描述对应模板的用途、输入输出、适用场景;描述含糊会造成同样的问题。
- 澄清问题需要重新定义:对技能推荐是“是否需要更多信息来判断”,对内容推荐可能是“用户意图是否明确到足以给出一个结果”。
这些是本文的类推判断,不是 TypeSafe 官方的迁移指南。
自己的判断:一个可落地清单
综合官方材料,我会这样落地:
- 先建候选集,再接决策模型。候选集质量决定上限,决策模型只能做最后一跳。
- 候选描述必须写清能力边界。不要用截断的短名,
criteria里写“做什么、不做什么”。 - Choice 加
none选项。让模型有“都不合适”的出口。 - 用 Noul 做门控。至少回答“这轮是否需要技能”,必要时加“是否需要澄清”。
- 低置信度不要硬选。低于阈值时返回多个备选、请求澄清或转人工。
- 阈值不能照抄官方示例。官方 0.30 是基于其模型、技能库和任务分布的设定,你需要用自己的数据重调。
- 离线评估要看错误加载率和空转加载率。官方 cookbook 已经给出这两个指标的基线,可以照此做对照实验。
这次没核实的
- 官方 cookbook 没有展开如何从技能商店、知识库或文件系统生成那 182 个 Hermes skills 的 roster 文件,所以官方推荐的召回方式我无法核实。
- 官方 Confidence 文档提到会写一个单独 cookbook 讨论不同置信度计算的优劣,并说“will add the link here when we do!”,这个链接在本文写作时尚未提供,因此未能核实更细节的置信度计算方案。
- 官方 cookbook 没有提供“澄清问题”的具体骨架;本文代码块里的
needs_clarification是类推,不是官方原文。
参考来源
评论区
登录后可评论。