能力路由:从目录检索到可执行方案
能力路由要把用户意图变成满足输入、身份和执行条件的方案。沿“能力描述—召回—正文精排—执行规划—结果反馈”分析整条链路,分别处理误召回、不可执行和执行失败。
最近修订:2026.09.07
阅读导航 · 第 4 / 4 篇
能力路由要把用户意图变成满足输入、身份和执行条件的方案。沿“能力描述—召回—正文精排—执行规划—结果反馈”分析整条链路,分别处理误召回、不可执行和执行失败。
把上周的客户会议整理成飞书文档,先不要发群。
先辨认能力,再讨论路由
| 对象 | 本质 | 选中后的动作 |
|---|---|---|
| Skill | 任务方法、说明和配套资源 | 当前 Agent 读取说明,按步骤做 |
| MCP Server | 通过 MCP 提供工具等能力的服务 | 连接并发现具体能力 |
| MCP Tool | 有参数定义的具体操作 | 校验参数并调用 |
| Agent | 可以接受委派的执行主体 | 交付任务,跟踪并验收结果 |
找到飞书 MCP Server,不等于已经选对“创建文档”工具;加载会议总结 Skill,也不等于把任务交给另一个 Agent。
因此,系统可以统一回答“有什么能力”,却必须分别处理“怎么使用”。发现、执行和评测都需要保留这条边界。
为什么需要一层能力路由?
小目录可以直接放进模型上下文。随着能力增长,目录成本、相似描述和执行差异会一起增加。
Agent Skills 规范已有渐进式披露:先展示名称和描述,匹配后再读取 SKILL.md,最后按需访问资源。Agent Skills 规范
但假设每个摘要平均 100 tokens,一万个摘要仍约需 100 万 tokens。这个容量估算说明:到了大目录场景,连目录也需要先检索。
于是,模型保留“搜索能力”“读取详情”等少量稳定入口;完整目录留在外部系统中。注意,万级条目不等于万级连接,更不等于万级并发任务。这三种规模需要分别验证。
目录、路由、执行与验收怎样连接
能力目录提供版本化描述,发现层产生候选,选择层检查依赖与限制,执行层调用能力,验收层读取实际产物。策略贯穿可见性与授权,执行证据回流为下一次检索和选择的依据。 读图时先沿任务主路径从上往下走:
任务理解保留目标、输入和限制。“不要发群”与“创建文档”同样重要;客户范围未知时,应保留未知状态。
发现候选在允许发现的目录中检索。它负责找出可能合适的能力,不负责承诺任务一定能完成。
选择方案比较输入条件、依赖和预算。它可能选一个 Agent,也可能选 Skill 与多个工具的组合。
执行与验收读取说明、调用接口或委派任务,再检查实际产物。返回“成功”不等于文档内容和访问权限都正确。
目录更新改变可选能力,策略更新改变可见与可执行范围;两者都需要版本记录。观测应覆盖检索、选择和执行,最终成功标记无法解释中途失败。
系统有两个不同的工作时机
理解这一区别,可以避免“每次请求都扫描一万个服务”的误区。
| 时机 | 主要工作 | 产物 |
|---|---|---|
| 能力注册或更新时 | 验证来源、整理描述、保存版本、构建索引 | 可查询的能力目录与定义 |
| 用户请求到来时 | 过滤、检索、比较、加载、授权、执行 | 一次任务的方案与结果 |
前者是能力供给的准备过程,后者是运行时决策。已经批准的 MCP 服务可以缓存工具目录,不必每次重新连接全部服务;执行时仍要确认定义和权限有效。
统一目录也不意味着必须使用一个数据库。多个团队可以维护各自目录,通过受控同步或联邦发现提供统一入口。对外搜索本身也可能传出客户信息,因此不能默认向所有公共目录广播原始请求。
把开源项目放回架构里
看项目时,先问它补哪一层,而不是看它是否宣称“支持海量工具”。
| 职责 | 参考项目 | 不应混淆的边界 |
|---|---|---|
| 能力格式与执行框架 | Agent Skills、Deep Agents | 定义与加载 Skill,不自动证明万级路由质量 |
| 注册与发现 | MCP Registry、ARD | 服务器目录或跨类型发现,不等于任务决策器 |
| 动态工具集合 | BigTool、FastMCP | 减少模型可见工具,不替代最终授权 |
| 运行与治理 | ToolHive | 托管、注册与网关能力,不是相关性算法 |
这些是不同层的实现线索,不是必须一起部署的套餐。本文按官方资料梳理逻辑职责,不声称它们已被联合部署或通过万级生产压测。
同一个任务,可能有两条合理路径
会议整理任务可以由当前 Agent 加载 Skill,配合会议读取和文档创建工具完成;也可以委派给具备必要能力的会议助理 Agent。
如果已有逐字稿、现有工具也齐备,第一条路径可能更直接。如果另一 Agent 有专长,位于允许的数据域,并能按要求交付,第二条也可能成立。一个无法关闭群发的工作流,则不满足任务限制。
配套提供离线实验与阅读路线。发现、执行与评测文章使用它验证局部机制;实验不连接飞书,不把模拟调用包装成真实业务效果。
能力描述和检索策略的实现见发现与检索。
设用户要把客户会议整理为文档,禁止上传外部服务或发群。用户说“提取客户讨论的结论和后续动作”,目录却写“会议纪要”;两个 Skill 摘要相同,正文却分别要求本地整理和外部上传。这两种错误的来源不同:前者搜不到,后者分不清。
我们把“找出可能相关的候选”称为召回,把“仔细比较候选并重新排序”称为精排。
发现质量,先取决于能力怎样被表达
搜索系统需要的不只是名字。下面是一条自定义路由记录,不是某个协议的标准 Schema:
{
"id": "acme/meeting-summary",
"kind": "skill",
"version": "1.0",
"description": "把会议逐字稿整理为决策和待办",
"inputs": ["transcript"],
"outputs": ["summary"],
"do_not_use_when": ["只有录音,尚未转写"],
"requires": [],
"side_effects": [],
"detail_ref": "skills/meeting-summary/SKILL.md"
}
它回答四件事:什么时候用、需要什么、产出什么、什么时候不适用。创建文档是后续工具步骤,不应偷偷塞进“纯总结”的承诺里。
作者可以描述用途,但准入、身份、运行健康和来源验证应由平台独立维护。不要把作者自报的“安全”或“高成功率”当作权限与质量证明。
同一能力的镜像、别名和多个版本还需要去重,否则五个候选可能只是同一个能力的五份副本。可结合发布方、稳定 ID、版本和内容摘要识别同源记录,保留来源用于追溯。
索引在离线准备,候选在线产生
实线是在线处理步骤,虚线是索引或详情供给;圆柱是存储,菱形是准入判断。精排只处理已召回候选,不能补回被遗漏的能力。 注册或更新时,生成关键词索引、向量及必要的正文片段。用户请求到来后,查询已建立的索引,而不是重新读取一万个正文、重新计算全部向量。
权限过滤不能只放在最后:没有权限发现的名称和描述,本身也可能是敏感信息。允许发现但尚未登录的能力则可以保留,标记为“需要授权”,不要与不存在混淆。
同样需要区分“没有匹配”与“搜索尚未完成”。一个目录超时,只能说明当前结果不完整。
检索方法各自解决一类问题
精确匹配适合能力 ID、工具名和用户点名;关键词适合产品名、文件格式和业务术语;向量适合自然语言与目录描述之间的表达差异。
一种可实验的组合是:关键词和向量各召回一组候选,融合去重后再精排。不要直接相加不同系统的原始分数,可以先尝试按排名融合的 RRF:
def rrf(rankings, k=60):
scores = {}
for ranking in rankings:
# 同一路结果中的重复 ID 只计算一次
unique = dict.fromkeys(ranking)
for rank, item_id in enumerate(unique, start=1):
scores[item_id] = scores.get(item_id, 0) + 1 / (k + rank)
return sorted(scores, key=lambda item_id: (-scores[item_id], item_id))
这是融合机制,不负责权限、风险和任务覆盖;k=60 也是实验参数。用户明确点名的能力应作为约束处理,不能仅仅加一点分后又被悄悄替换。
如果增加领域分类,应保留跨领域召回。错误领域同样可能凑满 20 个结果,不能以“候选够数”作为不再全局搜索的理由。
正文参与路由,不等于全文进入 Agent
假设两个 Skill 都叫“会议整理”,正文却分别写着:
- 只读取逐字稿,输出摘要文本。
- 上传录音到外部端点,生成文档后自动分享。
真正决定是否合适的,是输入、步骤和副作用,不只是摘要里的“会议”二字。
因此,路由器可以看正文,执行 Agent 仍然按需加载。正文可以用于离线索引,也可以供精排模型读取;主 Agent 接收少量卡片与匹配依据,确认后再读取执行说明。
SkillRouter 论文 v1 在约 8 万个 Skill、75 条专家验证查询的基准上报告:其 0.6B 编码器与 0.6B 精排模型组合达到 74.0% Hit@1,并展示了正文的作用。这是特定基准的路由结果,不是生产完成率;多 Skill 查询中首位命中一个必要能力,也不代表所有依赖都被覆盖。原论文
长正文还要比较全文、分块与任务相关片段,记录截断策略。对 MCP 工具,应关注接口和参数语义;对 Agent,则是交付范围及接口能力,不能统一当成 SKILL.md。
动手验证:先看清基线会怎样失败
在配套仓库根目录运行:
python3 docs/capability-routing/lab.py retrieve --query "会议 纪要"
python3 docs/capability-routing/lab.py retrieve --query "讨论 结论" --body
python3 docs/capability-routing/lab.py fusion
这是 Python 标准库离线实验:用词项匹配演示摘要与正文的信息差异,用两组固定排名演示 RRF。**它没有实现向量模型或 Cross-encoder,不是 SkillRouter 的复现。**查询按空格分词,中文要显式分词,不把这个教学假设带进生产。
先比较不带和带 --body 的结果;再加入一个只有摘要相似、正文行为不同的能力。观察正文是否帮助召回,也观察它是否引入更多干扰。否定约束单独保存,不能指望增加正文就自动理解所有“不要”。
接入真实检索器后,仍使用同一组人工核验任务做对照:关键词、混合检索、增加精排。分别记录必要能力覆盖、误选和耗时。每增加一层,都要知道它改善了哪类失败。
发现层的输出,还不是执行命令
候选结果应携带稳定 ID、版本、匹配依据、限制与详情地址;模型自报的置信度不是已经校准的成功概率。
精排只能重排已召回的候选;若必要能力被召回阶段遗漏,应调整检索或补充查询。候选相关也不证明它能独立完成任务。
候选可能是 Skill、工具或 Agent。下一步应比较完成同一任务的执行路径,见方案与执行。
相关的工具,不一定能完成任务
设用户要求“把上周客户会议整理成飞书文档,先不要发群”。结果要求包括:取得会议内容,整理决策和待办,创建飞书文档,检查内容和访问权限,不发群。
“创建文档”工具只覆盖其中一步。一个总结 Skill 可以提供整理方法,却未必具备读取会议记录的工具。会议助理 Agent 可能覆盖全程,也可能需要把数据发送到另一环境。
因此,每个候选都需要经过可行性检查:输入是否已有,依赖是否可用,数据是否允许流向目标环境,结果是否可验收,成本是否在预算内。一个方案可以包含多个能力,相关度最高的单个能力不一定属于最合适的方案。
第一版无需实现任意复杂规划。先把已知任务组织为少量可检查的模板:本地 Skill 加工具,或委派给允许使用的 Agent;遇到模板无法覆盖的任务,就明确报告缺口。再根据真实失败决定是否增加动态规划。
三种执行语义,不能被一个函数名抹平
以会议整理为例:当前 Agent 加载 Skill,读取范围内的记录,提炼结论,获准后创建文档,再回读内容与权限。禁止发群的约束贯穿全程。
实线箭头表示请求,虚线箭头表示返回;纵向顺序表示一次任务推进。图展示成功读取后的主路径与授权分流;真实实现仍要处理接口错误,不能假定每次调用成功。 **Skill 路径:加载方法。**读取正文后核对输入与依赖,再由当前 Agent 执行。正文要求发邮件,不代表用户授权发邮件。来源可信也不意味着可以覆盖当前任务限制。
**MCP 路径:调用操作。**工具执行适配器负责参数、身份、超时和返回值。服务器发现与具体工具选择要分开;对已批准服务可以缓存目录,执行时仍需检查定义与授权。发现未知服务不等于允许自动安装。
**Agent 路径:委派任务。**向另一个执行主体交付目标、必要输入、禁止事项、预算与验收条件。Agent Card 提供发现信息,但不是质量证明;也不能把其中的能力描述条目直接当成可下载的 SKILL.md。状态查询、取消和结果获取能力需要按实际接口验证。A2A Agent Discovery
三条路径可以共享追踪 ID 和结果封装,但执行内部不能一律替换成“运行文本”。
动态加载的,到底是哪一层?
LangGraph BigTool 展示了先注册工具对象,再通过检索 ID 改变模型可用工具集合的模式。动态变化的是模型当前可见集合,不一定是下载代码。
FastMCP Tool Search 通过搜索与调用代理减少全量工具暴露;skills-mcp 则可参考搜索、正文和资源读取的分层接口。
因此,接入时先确认三个位置:能力定义存在哪里,模型何时看到它,真正由哪个执行端调用它。否则很容易把搜索代理误认为安全网关,把加载成功误认为权限就绪。
专业 Agent 的委派还有额外成本:上下文传输、异步等待、结果核对。当前 Agent 已经能做好的简单任务,不必再转交一次;需要独立专长或运行环境时,委派才可能值得。
失败恢复,也属于方案设计
| 状态 | 下一步 | 不该做什么 |
|---|---|---|
| 缺逐字稿,只有录音 | 在允许范围内追加检索转写能力 | 假装已经拿到文字 |
| 缺关键客户范围 | 向用户澄清 | 自动猜测 |
| 接口参数不匹配 | 校验定义并修正参数 | 原样重试到超时 |
| 权限拒绝 | 停止或进入授权流程 | 换身份或连接器绕过 |
| 写入超时、结果未知 | 查询状态或使用支持的幂等机制 | 立即换工具重复创建 |
权限拒绝不是普通网络错误。写操作超时也不能简单判断失败:文档可能已经创建,只是响应丢失。恢复前要核对已有状态,并遵守原来的授权和总预算。
已执行的副作用不会因为“重新规划”而消失。任务状态至少记录已经读取什么、已经创建什么、结果是否确定,否则模型每轮都可能重复工作。
动手验证:把不同执行路径显式暴露出来
在仓库根目录运行离线实验:
python3 docs/capability-routing/lab.py execute --path skill
python3 docs/capability-routing/lab.py execute --path mcp
python3 docs/capability-routing/lab.py execute --path agent
python3 docs/capability-routing/lab.py execute --path skill --no-auth
python3 docs/capability-routing/lab.py execute --path skill --stale
实验显式选择路径,模拟不同执行语义;不会访问飞书、发送消息或启动远程 Agent。skill 路径展示说明加载与工具依赖,mcp 路径接收已有摘要,agent 路径记录任务交付。不能将这些模拟步骤当作真实 MCP 或 A2A 协议测试。
预期验收:正常路径返回标记为模拟的文档结果;缺授权返回 NEEDS_AUTH;版本过期返回 BLOCKED,而不是继续使用旧定义。完整结果包含路径与效果记录,便于确认未发生消息发送。
真正接入服务时,用实际 SDK 替换适配器,并在测试空间验证身份、工具定义、写入与结果查询。这一步的验收对象是可查证的产物,不是模型的一句“完成了”。
执行过程会反过来改变路由
拿到录音才知道需要转写,读取正文才发现依赖不可用,调用后才知道资源已存在。每一步都会产生新的证据,影响下一次选择。
所以,路由不是任务开始前的静态开关。它是一个有限循环:发现、选择、执行、更新状态;可以重选,也必须能够结束。
先按失败阶段定位问题
菱形是诊断问题,矩形是排查方向;“是”表示继续定位,不代表上一层已经没有优化空间。多能力任务要检查一条可接受方案的完整覆盖,而非只命中一个工具。 检索指标回答“可不可以被选到”,选择指标回答“选得是否合理”,执行指标回答“操作是否有效”,结果指标回答“用户目标是否达到”。先区分,再汇总。
测试集需要表示多解、无解和限制
不要给每条任务只标一个“唯一正确工具 ID”。同一个任务可能有多个合理方案,也可能缺信息,或者目录里没有答案。
一个简化的标注记录可以是:
{
"query": "整理会议纪要,不要发群",
"acceptable_plans": [
["meeting.read", "summary.skill", "document.create"],
["meeting-assistant.agent"]
],
"forbidden_effects": ["message.send"],
"expected_status": "SELECT"
}
这是教学格式,真实验收仍需核对这些能力是否满足输入、权限和结果要求。
从几十条人工核验任务起步,覆盖同义表达、中文请求配英文目录、相似能力、明确禁止事项、缺参数、缺授权、目录无解和版本变化。让同一能力的大量近重复问法同时进入训练与测试,会得到过于乐观的结果;可以按任务族、时间或能力版本留出测试集。
必须提前约定标注口径。某个 Agent 虽然能完成任务,但会把数据发到不允许的环境,就不属于可接受方案。
对照实验应该回答“多一层值不值”
保持测试任务和运行环境一致,对照关键词、混合检索、增加正文精排三种方案。至少同时报告:
| 指标 | 解释 |
|---|---|
| 候选覆盖 | 是否包含某条可接受方案所需的全部能力 |
| 方案正确率 | 是否满足目标、依赖和限制 |
| 完成率 | 实际产物是否通过验收 |
| 错误自动执行率 | 不该执行时却执行的比例 |
| 路由 P50 / P95 | 典型和较慢请求的耗时 |
| 每次成功任务成本 | 总投入除以成功任务数,失败成本也计入 |
均值不是 P50;评测集上的分类分数也不是线上概率。数字需要附带样本量、数据范围、版本和测量环境。
精排可能提高选择质量,同时增加延迟。若收益集中在高度相似的候选,可只对这类请求启用;若简单请求原本就很明确,强制多一轮模型确认可能只是增加成本。
弃选不是异常,而是一个合法结果
状态由事件和条件驱动迁移。“等待授权”需要超时、取消和总预算;不确定写入必须先对账,不能直接重复执行。 模型自报“置信度 0.9”不意味着十次有九次正确。若用分数决定是否自动选择,需要在独立数据上检验不同分数区间的准确性和覆盖率;高风险动作仍需独立授权。
确认自己选对了“发送邮件”工具,与用户允许发送这封邮件,是两个不同判断。
治理必须进入任务链路
至少记录任务 ID、候选列表、索引版本、模型实际看到的内容、选择依据、执行定义版本、授权结果和产物。没有这条证据链,就难以区分没搜到、搜到但没展示、展示了却选错。
缓存也有权限语义:缓存键与失效条件需要考虑租户、权限、目录版本和策略变化。执行端重新授权,避免撤权后继续使用旧候选。日志需脱敏、限权并设保留期限,不无差别保存客户内容和凭证。
公共 Skill 只是公开可获取,不代表可信;容器隔离也不等于已经具备完整授权。ToolHive 等平台可以承担部分托管与访问治理,应用仍需配置身份策略并验收业务结果。
反馈还会产生偏差:常被展示的能力更容易累积“成功”;执行失败可能来自权限而非能力不合适。不要把所有失败都当作检索负样本,也不要直接按全局历史成功率排序。
动手验证与上线边界
运行配套实验:
python3 docs/capability-routing/lab.py evaluate
python3 -m unittest discover -s docs/capability-routing -p 'test_*.py'
python3 docs/capability-routing/lab.py scale --count 10000
前两项验证教学样本和路由契约;最后一项在合成目录上测一次本地查询耗时。它不是并发压测,也不是万级真实任务准确率证明。详细测试范围见实验说明。
接入真实系统后,先做离线回放,再做影子路由:新策略只计算选择,不额外触发写操作。对错误分布有把握后,才在明确授权的低风险范围灰度。回滚条件根据业务代价定义,不能直接抄一个通用阈值。
进入灰度前,可以用四项检查收尾:
- 能区分召回、选择、执行与结果失败,而不只记录一个成功率。
- 有无匹配、待授权和版本变化的测试,且不会越权回退。
- 写入结果不确定时会先核对状态,不盲目重复执行。
- 新策略有对照结果、成本记录和可执行的回滚条件。
成熟架构也需要知道何时简化
固定审批流程、明确点名的工具、少量稳定能力,未必需要全套动态检索与规划。确定性路径已经能满足目标时,增加自由度往往也增加故障面。
因此,落地顺序应由失败证据决定:
- 同义表达总漏掉能力,补充语义召回。
- 描述相似但行为不同,补正文精排。
- 经常缺依赖,补方案可行性检查。
- 结果不确定时重复写入,补状态核对与幂等。
- 无法解释误选,补候选和执行追踪。
这不是要求系统永远简单,而是要求复杂度有明确用途。模块可以逻辑分离,不必第一天就拆成多个微服务;已有项目能覆盖的部分,也不需要重造。