当 Skill 池膨胀到万级,模型上下文窗口装不下所有工具描述,"让 Agent 选对工具"就成了一个系统工程问题。本文梳理一套可落地的生产级方案——从 4 层漏斗架构、SkillRouter 论文实测数据、到置信度分流机制,完整走一遍。
一、核心矛盾:1 万个 Skill,模型看不过来
一个 MCP 工具的 schema 描述(名称 + 功能说明 + 参数定义)约 150 token。当 Skill 池达到 10,000 个,仅工具描述就需要约 150 万 token——远超当前主流模型的上下文窗口上限(128K–256K tokens)。
这不是"工具太多"的问题,而是"模型能看到的工具必须是经过筛选的子集"的问题。我们需要一个分层漏斗,把 10,000 个候选逐步收窄到模型能处理的 5 个以内。
二、系统全景:4 层漏斗 + 执行 + 反馈
用户输入
↓
┌─────────────────────────────────────────────┐
│ Layer 1: 意图分类(规则引擎 + LLM 兜底) │
│ 10,000 → ~2,000 │
│ 延迟 <100ms │
└──────────────┬──────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Layer 2: 向量检索(SkillRouter Embedding) │
│ 2,000 → 20 │
│ 延迟 <50ms │
└──────────────┬──────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Layer 3: 精排(SkillRouter Reranker) │
│ 20 → 5 │
│ 延迟 <200ms │
└──────────────┬──────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Layer 4: LLM 最终选择 │
│ 5 → 1 │
│ 延迟 <500ms │
└──────────────┬──────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Layer 5: Skill 加载 + 执行 │
│ OpenViking 三层加载 → CubeSandbox/本地执行 │
│ 延迟 <1s │
└──────────────┬──────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Layer 6: 反馈闭环 │
│ 用户确认/纠正 → 训练集 → 定期重训模型 │
└─────────────────────────────────────────────┘
设计思路借鉴了业界头部玩家的共识——阿里千问的 Nacos MCP Router 语义检索和字节豆包的 Coze 三层渐进加载(详见附录)。核心原则一致:模型永远不直接面对全量工具,路由层做筛选,执行层做隔离。
三、各层详解:从论文数据到工程选型
Layer 1:意图分类——先缩小战场
职责:把 10,000 个 Skill 按领域缩小到约 2,000 个候选。
分类体系:
| 领域 | Skill 数量 | 典型关键词 |
|---|---|---|
| data-reporting | 2,000 | 报表、数据、分析、导出 |
| code-generation | 2,500 | 代码、函数、测试、重构 |
| document-writing | 1,500 | 写作、翻译、排版 |
| devops | 1,500 | 部署、监控、运维 |
| communication | 1,000 | 邮件、消息、通知 |
| file-management | 800 | 文件、格式、转换 |
| other | 700 | 兜底 |
实现:规则引擎优先(关键词 + 正则),置信度低于阈值时降级为 LLM 分类。延迟控制在 100ms 以内。
容错设计:分类错误不影响最终结果——Layer 2 的向量检索会跨领域召回,分类只是缩小范围的加速手段。
Layer 2:向量检索——用 SkillRouter Embedding 粗筛
职责:从约 2,000 个候选中检索 Top 20。
组件:SkillRouter Embedding 0.6B + FAISS 向量库。
关键设计决策:
1. 用 SKILL.md 全文做 embedding,不是只有 description。
这是最重要的工程决策。SkillRouter 论文(arXiv: 2608.11312)实测证明:去掉 SKILL.md 正文后,检索准确率暴跌 37–44 个百分点。正文中的工作步骤、约束条件、Few-shot 示例是决定性信号——Reranker 91.7% 的注意力集中在正文,而非 description。
2. 分类内优先检索,候选不足时全局补充。
先在 Layer 1 划定的领域内检索,如果 Top 20 不满再扩展到全量池,避免漏召回。
3. 索引完全放内存。
10K Skills × 768 维 × 4 字节 ≈ 35MB,FAISS 索引常驻内存,检索延迟 <50ms。
索引更新策略:Skill 注册/更新时增量更新索引,每周全量重建一次保证一致性。
Layer 3:Reranker 精排——Cross-encoder 二次排序
职责:从 Top 20 精排到 Top 5。
组件:SkillRouter Reranker 0.6B(Cross-encoder 架构)。
工作方式:Reranker 的输入是 query + 每个候选的完整 SKILL.md 正文。它不是简单的向量相似度,而是逐对交叉编码,关注正文中的实现细节、脚本说明、边缘情况处理。
实测效果:
| 指标 | 数值 | 说明 |
|---|---|---|
| 修正率 | 12.7% | 把检索阶段的错误修正为正确 |
| 退化率 | 4.0% | 反而改错了 |
| 净提升 | +8.7pp | 修正远大于退化 |
Reranker 的价值在于它能"读懂"正文。当两个 Skill 的 description 高度相似时(比如"生成销售报表"vs"生成销售周报"),只有读了正文中的具体步骤和约束才能区分。
Layer 4:LLM 最终选择——从 Top 5 中决策
职责:从 Top 5 中选 1 个,返回选择理由和置信度。
输入:Top 5 的 name + description + L2/L3 分数。
输出:选中的 Skill ID + 置信度(0–1)+ 选择理由。
置信度是后续分流机制的依据(见第五章)。
Layer 5:Skill 加载与执行——三层渐进 + 沙箱隔离
组件:OpenViking(存储+加载) + CubeSandbox / 本地进程(执行)。
三层渐进式加载(借鉴 Coze 与 Anthropic Agent Skills 标准):
| 层级 | 内容 | Token 消耗 | 使用时机 |
|---|---|---|---|
| L0 摘要 | name + description + tags | ~100 | 路由决策阶段 |
| L1 概览 | L0 + 工作步骤 + 约束 | ~2,000 | Agent 理解阶段 |
| L2 全文 | 完整 SKILL.md | 完整 | 执行阶段 |
OpenViking 实测效果:Token 节省 34–91%,延迟降低 58–66%。
执行环境按信任等级隔离:
| 信任等级 | 执行环境 | 说明 |
|---|---|---|
| public | 本地进程 | 公开 Skill,直接跑 |
| internal | CubeSandbox | 内部 Skill,沙箱隔离 |
| confidential | CubeSandbox + 网络隔离 | 机密 Skill |
| untrusted | CubeSandbox 严格模式 | 不可信 Skill |
Layer 6:反馈闭环——让路由越用越准
反馈信号:
| 反馈类型 | 来源 | 权重 |
|---|---|---|
| 用户确认"对" | 显式确认 | 强正反馈 |
| 用户纠正选择 | 显式纠正 | 强负反馈(权重 2.0) |
| 用户说"不对" | 隐式否定 | 弱负反馈 |
| 执行成功 | 系统 | 弱正反馈 |
| 执行失败 | 系统 | 弱负反馈 |
更新节奏:每周重训 Reranker(近 30 天数据),每月重训 Embedding(全量数据),每次更新后用测试集验证——Hit@1 下降超过 2% 自动回滚。
四、Skill 数据模型
每个 Skill 注册时生成的元数据:
| 字段 | 说明 | 示例 |
|---|---|---|
| skill_id | 唯一标识 | sales-report-generator |
| name | 显示名 | 销售报表生成器 |
| description | 触发描述(必须含"什么时候用") | 生成销售月报/周报... |
| category | 领域分类 | data-reporting |
| tags | 关键词标签 | 报表, 销售, Excel |
| trust_level | 信任等级 | internal |
| execution_env | 执行环境 | sandbox |
| body_tokens | 正文 token 数 | 2,400 |
| avg_latency_ms | 平均执行时长 | 3,200 |
| success_rate | 历史成功率 | 0.96 |
| trigger_count | 被调用次数 | 1,523 |
| confirm_count | 被确认次数 | 1,480 |
每次路由的完整日志(query、各层候选、最终选择、用户反馈、执行结果)是后续模型重训的数据基础。
五、置信度分流:不是所有请求都需要同样的精度
三级分流策略
LLM 选择返回置信度
↓
┌───────────────────────────────────┐
│ 置信度 ≥ 0.8 │
│ → 直接执行(无需确认) │
│ 预期占比:~60% │
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ 置信度 0.6 - 0.8 │
│ → 返回 Top 3 让 LLM 自我确认 │
│ 预期占比:~25% │
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ 置信度 < 0.6 │
│ → 让用户从 Top 3 中选择 │
│ 预期占比:~15% │
└───────────────────────────────────┘
用户感知准确率推算
| 场景 | 准确率 | 用户感知 |
|---|---|---|
| 高置信度直接执行 | 85%(这部分本身就很准) | 85% 无感 |
| 中置信度 LLM 确认 | LLM 确认后 >95% | 95%+ |
| 低置信度用户选择 | 100%(用户自己选的) | 100% |
| 加权总计 | ~92% | >95%(错了能纠正) |
关键洞察:用户感知准确率 ≠ 系统准确率。低置信度时让用户选择,看似"没选对",但用户体验反而是最好的——因为给了他明确的候选,一秒就能纠正。
六、监控与告警
核心指标
| 指标 | 计算方式 | 告警阈值 | 级别 |
|---|---|---|---|
| 路由准确率 | 确认正确 / 总路由 | <80% | P0 |
| 用户纠正率 | 纠正次数 / 总路由 | >15% | P1 |
| 平均路由延迟 | P50 路由耗时 | >2s | P1 |
| P99 路由延迟 | P99 路由耗时 | >5s | P1 |
| 执行成功率 | 成功数 / 执行总数 | <90% | P0 |
| 空结果率 | 无匹配 / 总路由 | >5% | P2 |
| 高频纠正 Skill | 单个 Skill 纠正次数 | >10次/周 | P2 |
| ### 异常检测规则 |
- 准确率突降:最近 1 小时准确率比 30 天均值低 15% 以上 → 告警
- 某 Skill 被频繁纠正:7 天内纠正 >10 次 → 检查 description 质量
- 延迟突增:P99 >5s → 检查 Reranker 和 LLM API 状态
七、实测数据:SkillRouter 论文与推算
SkillRouter 论文基准(80K Skill 池)
| 指标 | 数值 | 说明 |
|---|---|---|
| Hit@1 | 74.0% | 一次选对的比例 |
| MRR@10 | 77.1% | 前 10 候选的平均排名 |
| 参数量 | 1.2B | 0.6B Embedding + 0.6B Reranker |
| 延迟中位数 | 495.8ms | 两阶段总耗时 |
| GPU 内存 | 比 16B 基线少 15.8% | 轻量级优势明显 |
| ### 各模型组合对比 |
| 组合 | Hit@1 | MRR@10 | 参数量 |
|---|---|---|---|
| SR-Emb-0.6B + SR-Reranker-0.6B | 74.0% | 77.1% | 1.2B |
| Qwen3-Emb-8B + SR-Reranker-0.6B | 71.3% | 74.9% | 8.6B |
| Qwen3-Emb-0.6B + SR-Reranker-0.6B | 70.0% | 73.9% | 1.2B |
| Qwen3-Emb-8B + Qwen3-Rank-0.6B | 68.7% | 73.4% | 8.6B |
| Qwen3-Emb-8B + Qwen3-Rank-8B | 68.0% | 72.3% | 16B |
| SR-Emb-0.6B(无 Reranker) | 65.4% | 72.3% | 0.6B |
原生 SkillRouter 组合最优——比 16B 大模型基线高 6 个百分点,参数量只有 1/13。
池规模对准确率的影响
| 池规模 | 预期 Hit@1(推算) | 说明 |
|---|---|---|
| 80,000 | 74.0% | 论文实测 |
| 50,000 | 75–76% | |
| 20,000 | 76–77% | |
| 10,000 | 77–79% | 目标场景 |
| 5,000 | 79–81% | |
| 1,000 | 83–85% |
池缩小 → 候选间区分度增大 → 检索和精排的准确率同步上升。
Reranker 的关键发现
去掉 SKILL.md 正文(只用 description),准确率暴跌 37–44 个百分点。 Reranker 91.7% 的注意力集中在正文,而非 description。这意味着:Skill 的 description 是给检索用的"名片",正文才是给 Reranker 用的"灵魂"。写好正文比写好 description 更重要。
OpenViking 评测数据
| 评测 | 数据 | 说明 |
|---|---|---|
| 长对话记忆(LoCoMo) | 准确率 80–83%(原生 24–57%) | 记忆管理能力 |
| Agent 任务(tau2-bench) | 成功率 +6.87pp(零售)和 +11.87pp(航空) | 任务执行能力 |
| Token 节省 | 34–91% | 三层渐进式加载效果 |
| 延迟降低 | 58–66% | 减少无效加载 |
八、延迟预算
| 步骤 | 目标延迟 | 组件 |
|---|---|---|
| Layer 1 意图分类 | <100ms | 规则引擎 |
| Layer 2 向量检索 | <50ms | FAISS CPU |
| Layer 3 Reranker | <200ms | SR-Reranker 0.6B |
| Layer 4 LLM 选择 | <500ms | API 调用 |
| Skill 加载 | <100ms | Redis 缓存 |
| 路由总计 | <950ms | |
| Skill 执行 | 1–300s | 取决于 Skill 复杂度 |
| 端到端 | <310s |
路由层控制在 1 秒以内——用户感知是"几乎瞬间选对了工具"。
| 组件 | 项目 | 协议 | 用途 |
|---|---|---|---|
| Skill 路由 | SkillRouter | MIT | Embedding 检索 + Reranker 精排 |
| Skill 存储 | OpenViking | Apache-2.0 | 三层渐进式加载 + 检索轨迹 |
| 沙箱执行 | CubeSandbox | Apache-2.0 | 不可信 Skill 隔离执行 |
| 向量索引 | FAISS | MIT | 向量相似度检索 |
| 监控 | Grafana + Prometheus | AGPL / Apache-2.0 | 指标采集 + 仪表盘 |
| 数据库 | PostgreSQL | PostgreSQL License | 路由日志存储 |
| 缓存 | Redis | BSD | L0/L1 加速 |
全部开源,无闭源依赖。
附录:参考项目与业界实践
| 组件 | 地址 | 说明 |
|---|---|---|
| SkillRouter | github.com/AIFoundry-ai/SkillRouter | Embedding + Reranker 路由模型 |
| OpenViking | github.com/AIFoundry-ai/OpenViking | 三层渐进式加载 + 记忆管理 |
| CubeSandbox | github.com/nicepkg/aide | 不可信 Skill 沙箱执行 |
业界共识:阿里千问(Nacos MCP Router + Higress 语义检索)和字节豆包(Coze 三层渐进加载 + 技能商店)均已实现万级工具管理。核心思路一致——模型永远不看全量工具,路由层筛选,执行层隔离。详见 Nacos MCP Router 文档 和 Coze 技能概述。
文章评论