乐于分享
好东西不私藏

[开源工具]你给AI装了N个技能,但是它偏偏挑错的用

[开源工具]你给AI装了N个技能,但是它偏偏挑错的用

不是用LLM测LLM,是用确定性测试覆盖不确定性。

一、问题:你的AI助手,能听懂人话吗?

做AI工具的人,99%的时间都在跟一个问题较劲:

用户说的到底是啥意思?

“帮我看看这个项目能不能投”——这是想做可行性分析。

“查查竞品资料,然后帮我写篇文章总结”——这是两步:先查资料,再写文章。

“帮我写点东西”——这个最坑,“东西”可能是公众号推文、方案文档、代码注释、或者朋友圈文案。

如果路由错了,后面全崩。

我们之前用LLM做意图识别,准确率大概85%。听起来不错?错。剩下的15%全是翻车现场——把写方案路由成写情书,把查资料路由成直接生成,用户当场血压飙升。

试过各种prompt优化、few-shot、chain-of-thought,发现一件事:

LLM做路由,本质上是在猜。猜就有错。

二、灵感来源:TDD不是只给代码用的

我在看superpowers团队的TDD实践时突然想到:

如果他们对AI Agent做测试驱动开发,那我们为什么不能对路由引擎做同样的事?

不是用另一个LLM去测LLM(那还是在猜),而是:

用确定性规则,覆盖真实用户的非确定性表达。

于是有了这套东西。

三、15道“高考题”:真实用户话术

我们收集了15条真实用户输入,覆盖了路由引擎最难搞的场景:

#用户原话考验点
1“帮我看看这个项目能不能投”单一意图精确匹配
2“查查竞品资料,然后帮我写篇文章总结”双意图+依赖排序
3“帮我写个方案”泛化触发词vs具体触发词竞争
4“别问了,直接写”用户强制覆盖前置关卡
5“看看这个项目靠不靠谱,靠谱就推进,不靠谱就算了”条件意图+中止行为
6“帮我把这篇文章去掉AI味,再加点AI味进去”矛盾意图检测
7“今天天气怎么样”零匹配兜底
8“帮我看看”上下文缺失兜底
9“教教我Python怎么用”教学类触发词
10“分析一下这份文档,然后帮我写个总结”analyze→output顺序
11“然后帮我写篇文章”连接词多意图识别
12“如果效果好就继续做”条件句式识别
13“我要做竞品分析”领域锚定验证
14“帮我写一个关于AI的方案”具体触发词优先
15“帮我搞一下”极度泛化兜底

每一道题都有唯一正确答案。不是“大概对”,是“必须对”。

四、三件套:模拟器 + 跑分器 + 防篡改检测

1. router_sim.py — 确定性路由模拟器

不用LLM,纯规则驱动。核心机制8步:

1. 矛盾检测 — “去掉AI味再加AI味” → 直接反问用户

2. 窗口匹配 — 触发词字符必须在输入的 len+2 窗口内聚齐,防止散射误匹配

3. 领域锚定 — 特定领域skill必须输入含对应特征词才生效

4. 泛化消歧 — “帮我写”这类太宽泛的词,遇到具体触发词自动让位

5. 分类排序 — gate→research→analyze→output,保证执行顺序

6. 前置关卡 — 自动插入审核关卡,除非用户说“别问了直接干”

7. 条件识别 — “如果/算了/不能” → 标记on_abort=skip

8. 兜底反问 — 零匹配时给3个选项,不给开放题

2. run_eval.py — 跑分器

对15条用例逐字段比对:

路由类型对不对?(route/fallback/ask)

skill顺序对不对?

gate插没插?

覆盖生效了吗?

条件中止标记了吗?

全部通过=退出码0,有失败=退出码1。CI/CD直接挂钩子,没过不让合并。

3. check_triggers.py — 防篡改检测器

在路由之前先检查配置层:

重复触发词(两个skill写了一样的触发词)

包含冲突(A是B的子串,匹配时谁赢?)

字符集包含(A的多重字符集⊆B,窗口匹配时B可能误吞A)

空触发词/空列表

缺category字段(eval排序依赖它)

chain.gate引用了不存在的skill

规则文件的bug,在代码跑之前就被抓出来。

五、效果:从“大概对”到“必须对”

关键不是“我们测了15条全过”,而是——

我故意改坏了一处配置,测试立刻变红。

我把某个skill的前置审核关卡删掉,重新跑测试。结果:

[FAIL] dual-research-then-bid: '查查竞品资料,然后帮我写篇文章总结'  ✗ gate_inserted: 期望True 实际False[FAIL] containment-skill: '帮我写个方案'  ✗ skills: 期望['grill-me', 'article-writing'] 实际['article-writing'][FAIL] conditional-abort: '看看这个项目靠不靠谱,靠谱就推进,不靠谱就算了'  ✗ gate_inserted: 期望True 实际False

3条用例精确报错,精确到“gate_inserted这个字段期望True实际False”。

改回去,15/15绿。

这才叫测试。不是自己出题自己考,是故意改坏它,看它能不能抓住你。

六、核心认知

LLM适合做“理解”,不适合做“决策”。

路由是决策层的东西——用户说了A,你应该走B这条路径。这个判断不需要“大概对”,需要“必须对”。

能用确定性规则的,就不要用概率。

这也是为什么我们没有用语义匹配/embedding来做路由。规则优先、零依赖、改YAML就生效。一套规则,不同Agent各自读,不锁定任何框架。

七、怎么用

这套方法论是通用的。你可以套到任何领域:

1. 把你的skill列表写进rules.yaml

2. 给每个skill配3-5个触发词

3. 写15条真实用户话术作为测试用例

4. 跑一遍run_eval.py,看有没有意外的路由结果

5. 改一条规则,再跑一遍——如果变红,说明你的测试有覆盖力

八、开源

代码已开源,欢迎参考:

GitHub: github.com/charlotty2026/skill-router

Gitee: gitee.com/fenglinhuoshanmen/skill-router

包含:SKILL.md(Agent指令文件)、rules.yaml(路由规则)、eval/(自测集全套)。

最后一句话:测试不是写完代码补的,是写代码之前就想好的。