三层路由架构:从关键词匹配到语义回退的渐进式设计
问题
在 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 |
工程权衡
优势
- 精准 vs 成本的自适应 — 简单查询低成本,复杂查询高成本,系统自动选择
- 渐进可扩展 — 新技能只需注册到关键词表 + 画像库,立即可路由
- 容错设计 — 语义搜索崩溃不影响主流程
- 可观测 — 每层路由结果都可日志化,用于持续优化
限制
- 框架 autolist 不可控 — 系统 prompt 中 ~15K chars 技能列表无法从用户侧移除(框架层锁定)
- 语义精度天花板 — 当记忆库内容偏斜时(100% 技术内容),非技术查询的语义匹配退化为随机词重叠
- 映射漂移 — 画像库与文件系统有 7% 的自然漂移率,需要持续治理
治理:映射漂移
profiles.json 到 skills/<name>/SKILL.md 的映射有 7% 的自然漂移率。来源:
| 类型 | 举例 |
|---|---|
| 版本号不同步 | brain-v1.1.9 → 实际 brain-v1.1.8 |
| 索引格式不兼容 | chrome9222 在 INDEX.md 有,目录无 |
| 占位目录 | diagramming/、gifs/ 只有 DESCRIPTION.md |
| 幽灵条目 | memory-lancedb-pro/ 不存在 |
治理方案:
build-profiles.js自动验证hasRealSKILL()三级检查(exact → alias → clean name)- 别名映射表
SKILL_DIR_ALIAS处理版本漂移 - 过滤器移除无 SKILL.md 的 profile(93 个最终状态)
- 每次 build 后自动跑完整性测试(110 断言全绿)
适用场景
任何需要从候选集中选择最优资源的系统:
- 技能路由(本案例)
- 知识库检索 → 先关键词,再语义,最后问用户
- 插件/工具调度 → 常用工具快速分发,冷门工具语义搜索
- 推荐系统 → 用户偏好匹配 + 内容语义匹配
- 客服分配 → 关键词分流 + 语义匹配 + 人工兜底
进化方向
路由器当前是「读端」(路由→加载),未来可变为「写端」的闭环:
路由器积累日志 → RL 训练 → 模型内化工具调用模式 ↓ 三层路由 → 记录路由决策日志 → 训练路由策略模型当路由日志积累足够多后,可以用 RL 训练模型「直接知道」什么时候该走哪一层,不再需要人工维护关键词表。