· 约 8 分钟 · 2,803 字

三层路由架构:从关键词匹配到语义回退的渐进式设计

问题

在 AI Agent 系统中,用户的一句话需要被路由到正确的技能(skill)去处理。如果路由失败,用户要么得到”我不理解”的回复,要么需要自己手动指定技能——体验极差。

一个直观的方案是关键词匹配:用户说”帮我发邮件” → 匹配 email 技能。但关键词总有覆盖不到的 edge case,比如用户说”给张三发个消息说今晚不去了”——里面没有「邮件」二字。

另一个极端是全部走语义搜索:每次查询都做一次向量检索,成本高(~5s),而且对于简单查询(“打开空调”)完全没必要。

核心矛盾:常用查询(80%)用简单规则就能搞定,但剩下的 20% 需要高级推理。用单一策略无法同时满足成本和覆盖率。

方案:三层递进路由

用户查询
├─ ① 关键词匹配 (O(1) 查表)
│ ├─ 命中 → 预加载 SKILL.md ✅
│ └─ 未命中 → ②
├─ ② 语义回退 (向量检索)
│ ├─ 命中 → 匹配画像库 → 预加载 ✅
│ └─ 未命中 → ③
└─ ③ 通用 fallback(不加载任何 skill)

第一层:关键词匹配

最轻量,O(1) 查表。维护一个 70+ 条目的关键词映射表:

"邮件/邮箱/发邮件" → email
"天气" → browser_visible
"摄像头" → desktop-control
"记得/复习" → brain-v1.1.9

评分规则:名称匹配 (0.35) + 描述匹配 (0.20) + 关键词匹配 (0.20) + 类别匹配 (0.15) + 文件名暗示 (0.10),阈值 0.1(宁低勿高)。

关键设计:软归一化 + 边际递减——2 个匹配是 1 个匹配的 1.5× 而非 2×。一个匹配 > 零个匹配,这是最重要的非线性。

第二层:语义回退

关键词失配时,自动调用本地 sentence-transformers 做语义搜索(all-MiniLM-L6-v2,384 维),搜索结果与 93 个技能画像交叉匹配。

匹配维度

  • 技能名全文匹配 (0.35)
  • 名称词素相似度 (0.15)
  • 关键词命中 (0.20)
  • 描述词重叠 (0.15)
  • 文件名暗示 (0.30)

阈值策略:只要有任何语义匹配就 preload,不设严格阈值。即使匹配弱,加载的 SKILL.md 也能在下文对话中提供上下文。

第三层:通用 fallback

前两层都失败时,不加载任何 skill,直接用 core tools 兜底。这是最差情况,但生产运行中 25 个真实查询 0% 走到这一层。

关键设计决策

开销分离

最常用的路径(关键词命中)成本最低(查表微秒级),只有在关键词失配时才触发昂贵的语义搜索(~5s)。

不跳过层次

从不直接跳到③。语义回退即使匹配弱也比全 fallback 有用——至少 preload 一些背景知识给 LLM。

容错设计

语义搜索失败(超时、崩溃)不阻断主流程,退化为通用 fallback。不因高级功能 crash 导致整个请求失败。

阈值宁低勿高

关键词阈值设为 0.1,语义无阈值。宁可误匹配多加载几个无关 skill,也不错过应该命中的。

生产数据

指标数据
关键词直接命中率62.5%(25 查询中 5/8→80% 关键词命中)
语义回退命中率37.5%(剩余全部被语义层捕获)
全回退率0%
总画像数93 个(筛掉 7 个无映射的)
路由延迟关键词查表 <1ms / 语义回退 ~5s

工程权衡

优势

  1. 精准 vs 成本的自适应 — 简单查询低成本,复杂查询高成本,系统自动选择
  2. 渐进可扩展 — 新技能只需注册到关键词表 + 画像库,立即可路由
  3. 容错设计 — 语义搜索崩溃不影响主流程
  4. 可观测 — 每层路由结果都可日志化,用于持续优化

限制

  1. 框架 autolist 不可控 — 系统 prompt 中 ~15K chars 技能列表无法从用户侧移除(框架层锁定)
  2. 语义精度天花板 — 当记忆库内容偏斜时(100% 技术内容),非技术查询的语义匹配退化为随机词重叠
  3. 映射漂移 — 画像库与文件系统有 7% 的自然漂移率,需要持续治理

治理:映射漂移

profiles.jsonskills/<name>/SKILL.md 的映射有 7% 的自然漂移率。来源:

类型举例
版本号不同步brain-v1.1.9 → 实际 brain-v1.1.8
索引格式不兼容chrome9222 在 INDEX.md 有,目录无
占位目录diagramming/gifs/ 只有 DESCRIPTION.md
幽灵条目memory-lancedb-pro/ 不存在

治理方案

  1. build-profiles.js 自动验证 hasRealSKILL() 三级检查(exact → alias → clean name)
  2. 别名映射表 SKILL_DIR_ALIAS 处理版本漂移
  3. 过滤器移除无 SKILL.md 的 profile(93 个最终状态)
  4. 每次 build 后自动跑完整性测试(110 断言全绿)

适用场景

任何需要从候选集中选择最优资源的系统:

  • 技能路由(本案例)
  • 知识库检索 → 先关键词,再语义,最后问用户
  • 插件/工具调度 → 常用工具快速分发,冷门工具语义搜索
  • 推荐系统 → 用户偏好匹配 + 内容语义匹配
  • 客服分配 → 关键词分流 + 语义匹配 + 人工兜底

进化方向

路由器当前是「读端」(路由→加载),未来可变为「写端」的闭环:

路由器积累日志 → RL 训练 → 模型内化工具调用模式
三层路由 → 记录路由决策日志 → 训练路由策略模型

当路由日志积累足够多后,可以用 RL 训练模型「直接知道」什么时候该走哪一层,不再需要人工维护关键词表。

评论