🐎 第3章 授人以渔:Skills 工程实践
📄 一句话概括

技能被选中靠两条通道:人明确点名,或者模型自己读描述做判断。 后者是常态,所以描述写得好不好,直接决定技能存不存在。 描述要写三段:做什么、什么时候用、什么时候不要用。第三段最容易被漏,也最能防过触发。

📝 本章和前面几章的关系

上一节说清了三级的付费时机,这一节讲第一级那个判断是怎么做出的。 这一节是全章的核心:三级披露的前提是「一行描述够判断」,而能不能判断,取决于描述怎么写。

一、两条通道:点名与自选

第一条通道是人点名:打斜杠命令,或者直接说「用 deploy-check 跑一遍」。 这条通道不依赖描述,技能叫什么就用什么。 第二条通道是模型自选:用户说了句普通的话(「发版前帮我查一下」),模型在常驻的那一堆描述里找, 觉得哪个合适就调哪个。 第二条是常态。真实使用里很少人会记得技能名,多数时候就是正常说话。

所以「技能能不能用上」这件事,实际上由描述决定,而不是由内容决定。 内容决定的是「用上之后干得好不好」。两件事分清楚,才知道该往哪使劲。

二、description 是灵魂

模型看到的只有一行描述:它得从这一行里判断出「这次的任务是不是归它管」。 这是个分类问题,而分类的准确率取决于两件事:描述里有没有这次场景的词, 以及不同技能之间的描述能不能分开。 演示里造了十二个技能、八个请求。三组描述:全都写得很泛、都写准、都只写几个关键词。

写得泛那组,八个请求命中了三个,其实等于全错——十二个技能的描述一模一样,分值也一样, 排序只能退到按名字排。 所以「适用面很广」这几个字,在分类问题里等于「什么都能干」,也就等于「什么都别找我」。

三、过触发与欠触发

过触发是「不该用它的时候用了」。 典型症状:描述里全是行业大词,「代码」「数据」「分析」都沾边,于是任何请求都能被它接住。 欠触发是「该用它的时候没用」。典型症状:描述里写的是内部术语,用户说的是人话,两边词对不上。 两种错的方向相反,修法也相反:过触发要加排除条件,欠触发要加场景词。

演示里写得泛那组就是过触发:八个请求里七个被送给了同一个技能。不是它更合适,是它的名字字典序靠前。 写得窄那组反过来:只写「代码审查」四个字,于是 code-review、sql-explain、perf-profile 三个都沾边, 分不开。 注意窄不等于准。窄是「词少」,准是「词对得上场景」。词少到分不开,准确率一样会掉。

四、参考型与任务型:两种哲学

参考型技能的目标是「被正确的人在正确的时候查到」。 它不怕没人用,怕的是被不该用的人用上,所以描述宁可窄一点。 任务型技能的目标是「该触发的时候一定触发」。它怕漏,所以描述要把用户可能说的各种说法都写进去。 两种哲学的差别落在描述上:参考型多写「什么时候不要用」,任务型多写「用户说……时使用」。

还有一个差别在正文:参考型正文薄、重量在 refs;任务型正文就是全部,不需要 refs。 这条在 3.5 节和 3.9 节会各自展开。 判断自己的技能属于哪一类,问一句:它被误用一次,代价大,还是漏用一次,代价大?

五、描述的三段写法

第一段写做什么:一句话说清动作和产出。比如「审查代码改动,输出问题清单」。 第二段写什么时候用:把用户可能说的词列进去,用「用户说……时使用」这种句式。 这一段是给分类器喂关键词的。 第三段写什么时候不用:把最容易混淆的邻居场景点出来。「只是解释代码怎么跑时,不要用这个技能」。

三段里第三段最值钱。 正向描述告诉模型什么时候用它,反向描述告诉它什么时候别用。只写前者,等于只给了一半信息。 演示第 ④ 段打了一份完整的描述:三行,六十字出头。 这个长度既够判定,又不会在二十个技能的时候把常驻预算吃穿。

六、为什么这件事只能靠试

描述的写法没有标准答案,因为它面对的是一个语义判断,不是规则匹配。 同一句话,在只有三个技能的时候够用,装到二十个就可能分不开——因为邻居变多了。 所以调描述的办法只能是:备一批带标准答案的请求,改一版跑一遍,看错的是哪一类。

这套流程在 3.11 节会完整跑一遍。 这一节只需要记住一件事:描述是可以被测的,而没测过的描述只是「看起来还行」。

七、动手 Demo:同一批技能,描述写宽、写准、写窄,命中率差多少

十二个技能、八个请求,每个请求都有唯一正确答案。 三组描述:全都写得很泛、都写准、都只写一两个关键词。 这里用字面重合度代替模型的判断——真机上是模型读描述做语义匹配,本沙箱没有模型, 所以只能比【排序对不对】,不能比【分值准不准】。

for q, want in REQS:
    got = pick(q, lib, top=1)[0][1]     # 按字面重合度取第一名
    ok = (got == want)
    print(q[:16], u"→", got, u"对" if ok else u"错(该是 %s)" % want)

实跑输出:

--------------------------------------------------------------------
同一批技能,描述写宽、写准、写窄,命中率差多少
--------------------------------------------------------------------

  十二个技能、八个请求,每个请求都有唯一正确答案。
  三组描述:全都写得很泛、都写准、都只写一两个关键词。
  这里用字面重合度代替模型的判断——真机上是模型读描述做语义匹配,本沙箱没有模型,
  所以只能比【排序对不对】,不能比【分值准不准】。
| 描述写法 | 首名命中 |
|---|---:|
| 写得泛 | 1/8 |
| 写得准 | 7/8 |
| 写得窄 | 5/8 |


  ① 写得泛:全都一样,等于没写
  --------------------------------------------------------------------
  帮我把这次发版前该查的东西过 → api-design(该是 deploy-check)
  这份结算单金额对不上,帮我核 → api-design(该是 invoice-format)
  昨天那次故障写个复盘 → api-design(该是 incident-report)
  十二个描述一模一样,算出来的分也一模一样,排序只能退到按名字排。
  所以八个请求里有七个都被送给了同一个技能——不是它更合适,是它的名字字典序靠前。
  ! 过触发不是模型贪心,是描述没给它排除的依据。
  「适用面很广」这几个字,意思是「什么都能干」,也就等于「什么都别找我」。

  ② 写得准:场景词对齐了才命中
  --------------------------------------------------------------------
  帮我把这次发版前该查的东西过 → deploy-check  对
  这份结算单金额对不上,帮我核 → invoice-format  对
  昨天那次故障写个复盘 → incident-report  对
  看看这个 PR 有没有问题 → code-review  对
  八个请求里第一次就对了 7 个。
  错的那一个:这张表重复行太多,洗一下 → api-design(该是 data-clean)
  对的那几个有个共同点:请求里的词跟描述里的词对得上——「发版」对「发版」,「结算单」对「结算单」。

  ③ 写得窄:命中了,但误判也多
  --------------------------------------------------------------------
  只写关键词,命中率 5/8。看着还行,但那是因为这八个请求都用到了核心词。
  错:帮我把这次发版前该查的东西过 → api-design(该是 deploy-check)
  错:看看这个 PR 有没有问题 → api-design(该是 code-review)
  错:这张表重复行太多,洗一下 → api-design(该是 data-clean)
  ! 窄描述的问题不在漏,在【分不开】。
  只写「代码审查」四个字,code-review、sql-explain、perf-profile 三个都沾边,排序全靠字面撞运气。

  ④ 描述该怎么写
  --------------------------------------------------------------------
  三段:做什么、什么时候用、什么时候不用。第三段最容易被漏,也最能防过触发。

  [description 原样 · 本沙箱只比字面排序]
  | description: 审查代码改动:看边界条件、错误处理、测试覆盖,输出问题清单。
  |   用户说审查、看看这个 PR、有没有问题时使用。
  |   只是解释代码怎么跑、或者问某个函数什么意思时,不要用这个技能。
  第三行是排除条件。有了它,模型在「解释代码」的场景就不会误调审查技能。

①写得泛那组:一个都没对。 十二个描述一模一样,算出来的分也一样, 排序只能退到按名字排——所以八个请求里有七个被送给了同一个技能。 不是它更合适,是它的名字字典序靠前。这就是过触发的典型样子:描述没给模型任何排除的依据。 ②写得准那组:八个里对了七个。 对的那几个有个共同点——请求里的词跟描述里的词对得上,「发版」对「发版」,「结算单」对「结算单」。 错的那一个是「这张表重复行太多,洗一下」,被判给了 api-design,该是 data-clean。 原因是「表」这个字在两个描述的邻域里都出现过。 ③写得窄那组:五个对。看着还行,但那是因为这八个请求都用了核心词; 错的三个全是「分不开」——只写「代码审查」四个字,三个技能都沾边。 窄描述的问题不在漏,在分不开。词少不等于准,词少到无法区分,准确率一样掉。 ④最后打了一份三段式描述:做什么、什么时候用、什么时候不用。第三行是排除条件。 有了它,模型在「解释代码」的场景就不会误调审查技能——这一句是整段描述里最值钱的。

30 秒实验

  1. 把「写得泛」那组的描述改成各写一句不同的,重跑第 ① 步。命中率能到多少? 这说明过触发的根子是「描述相同」还是「描述太长」?
  2. 把请求里加一个生僻词(比如「帮我洗一下这张宽表」),重跑第二组。「写得准」那组的命中率会掉吗? 为什么?
  3. 把第三组的关键词从两个词扩到四个词,看能否把「分不开」修掉。扩词之后常驻预算涨了多少?

常见误区

  1. 以为技能能不能用上取决于内容质量。取决于描述——内容是「用上之后」才起作用的东西。
  2. 把描述写成产品介绍:「功能强大、适用多种场景、提升效率」。这类词在分类时提供不了任何区分度。
  3. 只写正向描述,不写排除条件。过触发的根子就在这里——模型没有「不该用」的依据。
  4. 以为描述越窄越准。窄是词少,准是词对得上;词少到分不开,准确率一样掉。
  5. 凭感觉判断描述写好了。没有带标准答案的请求集,任何描述都只是「看起来还行」。
自测三问(点开对答案) 1. 技能被触发有哪两条通道?哪条是常态? —— 人明确点名(斜杠命令或直接说技能名),以及模型自己读描述做判断。 后者是常态,因为真实使用里多数人只是正常说话。 2. 过触发和欠触发的修法有什么不同? —— 过触发要加排除条件(什么时候不要用),欠触发要加场景词(用户会怎么说)。方向相反。 3. 为什么描述要写第三段「什么时候不用」? —— 因为正向描述只说明「能用」,不说明「别用」。相邻技能最容易混,一段排除条件就能把边界钉住。

描述决定它被不被选中,下一节讲选中之后那一段正文该写成什么形状。

相关笔记