选技能不是选 Top-k:这篇论文把 Agent 技能路由改造成了"组队问题"
你有没有想过一个场景:你的 Agent 背后挂着一个八万多个技能的技能库,用户来了一句"帮我把这个 Excel 里的数据清洗一下,然后画个图"。路由模块吭哧吭哧算了一圈相关度,返回的前 10 个技能里,有 6 个都是"数据清洗"的近亲——描述长得像、实现也像,个个相关度分数都很高。而真正需要的"画图表"技能,被挤到了第 15 名开外。
上下文窗口就那么大,塞进去一堆重复技能,真正的短板反而没补上。
arXiv 2609.05824 这篇论文盯的就是这个问题。说实话我第一反应是:这不就是检索里的多样性重排嘛,MMR 二十多年前就干了。但往下看才发现,直接把多样性惩罚搬过来在技能路由场景里会翻车,而且翻得挺惨——论文消融实验里, naive 的余弦相似度 DPP 把多技能 Full Coverage@10 从 0.424 干到了 0.254,越加多样性越差。这篇论文值钱的地方,就是讲清楚了为什么会翻车,以及怎么修。
核心摘要
大规模技能库里,很多技能功能上是冗余的,而复杂任务往往需要的是一组互补技能。现有技能路由器把每个候选技能独立打分取 Top-k,容易浪费宝贵的上下文预算在重复技能上。作者提出 DSR(Diverse Skill Routing),用行列式点过程(DPP)做多样性感知的子集选择,核心创新是一个"查询残差多样性核"——先把每个技能 embedding 里和查询对齐的分量扣掉,再算技能之间的冗余。在 SkillRouter 基准(约 8 万技能、75 条专家验证查询)上,DSR 在多技能查询上把 Full Coverage@50 从 0.458 提到 0.551,涨了 9.3 个点。我的判断:这不是底层突破,是一个想法干净、实现便宜、定位精准的工程改进,但它指出的"技能路由本质是互补集合选择"这个视角,值得每个在做 Agent 技能系统的人认真想一想。
论文信息
- 标题:Beyond Top-k Skill Retrieval: Diversity-Aware Skill Routing for LLM Agents
- 作者:Wang Wei、Tiankai Yang、Samyadeep Basu、Hongjie Chen、Yue Zhao、Zhengzhong Tu、Xiyang Hu、Franck Dernoncourt、Ryan A. Rossi、Hoda Eldardiry(通讯作者)
- 机构:Virginia Tech、University of Southern California、Adobe Research、Dolby Labs、Texas A&M University、Arizona State University
- 发表:2026 年 9 月 5 日提交,arXiv:2609.05824(cs.AI)
- 链接:https://arxiv.org/abs/2609.05824
🎯 问题动机:技能多了,路由反而成了瓶颈
先交代一下背景。这两年 Agent 系统的一个明显趋势是"技能化"——不再靠重训模型加能力,而是把程序性知识打包成可复用的技能模块(指令、脚本、示例、参考文档),推理时按需加载进上下文。Voyager 在具身场景里让 Agent 自己攒技能库,SkillRouter 则直接研究了在约 8 万个技能的大规模池子上做路由有多难,还发现完整的技能实现文本(不只是名字和描述)里藏着重要的路由信号。
问题来了。当技能池膨胀到几万、几十万的量级,你不可能把所有技能都塞给 Agent——上下文窗口装不下,无关技能还会干扰执行。所以需要一个路由器,从大海里捞出相关的几个。
现有路由器(包括 SkillRouter)的做法是典型的两段式:先用双塔 encoder 检索候选,再用逐点(pointwise)重排模型给每个候选独立打一个相关度分数,取 Top-k 完事。
对单技能任务,这没毛病——找到那一个对的就行。但真实 Agent 任务往往是组合式的:文档解析、信息抽取、数据变换、可视化,一条流水线要好几环。这时候独立打分的毛病就暴露了:好几个功能雷同的技能因为描述相似,相关度分数都高,抱团挤进 Top-k;而任务需要的另一环技能,分数稍低就被挤掉了。
结果就是:上下文预算被冗余技能吃掉,多步工作流的覆盖面反而变差。
作者的论点一句话就能说清:大规模技能路由不该只是相关度排序,它本质上是互补集合选择——你要选的不是"k 个最相关的技能",而是"k 个合起来能覆盖任务的技能"。
🧠 方法核心:DPP 组队,但先把"查询成分"扣掉
直觉先行
如果让我用一句话讲 DSR 的核心 idea:选技能就像组队打篮球,不是选五个得分最强的,而是选五个位置互补的——但注意,"都和这场比赛相关"不算重复,"干的活一样"才算重复。
这后半句就是全篇的技术灵魂。
整体流程
DSR 站在标准 retrieve-and-rerank 流水线的肩膀上,只动最后一步:
- 候选检索:encoder 把查询 \(x\) 和每个技能 \(s_i\) 各自编码成 L2 归一化的 embedding \(\mathbf{e}_x\)、\(\mathbf{e}_i\),用余弦相似度 \(a_i(x)=\mathbf{e}_x^\top\mathbf{e}_i\) 从全库里捞出 Top-\(M\) 候选集 \(\mathcal{C}_x\)(\(M \ll N\),实验里 \(M=50\))。
- 质量打分:用逐点重排模型(reranker)给每个候选算相关度 logit \(h_i(x)\),过 sigmoid 得到非负质量分 \(q_i(x)=\sigma(h_i(x))\)。
- DPP 子集选择:在候选集上构造 DPP 核矩阵,贪心地选出一个既高质量又不冗余的 \(k\) 元子集,最后按质量分排序输出。
前两步和 SkillRouter 完全一样——这是刻意的,作者要隔离出"多样性选择"这一个变量的效果。
DPP 是什么,为什么用它
行列式点过程(Determinantal Point Process)是子集选择问题的一个经典概率框架,天生偏好"质量高且彼此不同"的集合。DSR 在候选集上构造核矩阵:
其中 \(\phi_x(s_i,s_j)\) 是查询条件下的技能间相似度。对任意子集 \(A\),DPP 给的分数是行列式 \(F(A;x)=\det(\mathbf{L}^{(x)}_A)\)。行列式有个很妙的几何直觉:它正比于这些向量张成的平行多面体体积——质量分高让边更长,彼此不相似让边之间更"撑得开",体积自然大。冗余技能向量方向接近,体积塌缩,行列式就小。
精确求最优子集是难的,DSR 用标准的贪心 MAP:从空集出发,每步加入使对数行列式边际增益最大的候选,直到凑够 \(k\) 个,用增量 Cholesky 分解避免反复重算行列式。
有个细节挺优雅:贪心第一步时 \(A=\emptyset\),边际增益是 \(\log L^{(x)}_{ii}=\log q_i(x)^2\),也就是第一个被选中的永远是质量分最高的技能——reranker 的头名预测被完整保留,多样性只在后续位置发力。这个设计很稳。
查询残差核:全篇真正的关键
如果直接用技能 embedding 的余弦相似度当 \(\phi_x\),就是标准 DPP。但作者指出,这在技能路由里有个隐蔽的坑:
两个技能 embedding 接近,可能有两个完全不同的原因——
- 它们真的冗余(功能重叠,该罚);
- 它们都和同一个查询相关(比如"清洗数据"和"画图表"都因为和"分析这个表格"相关而共享了查询方向的 embedding 分量,但它们干活的部分并不一样,不该罚)。
不分青红皂白地惩罚所有相似度,就会把"同任务但互补"的技能当成冗余踢掉。这就是消融实验里 naive 核翻车的原因。
DSR 的解法干净得像教科书:先把每个技能 embedding 里沿查询方向的分量投影掉,得到残差
再把残差和原 embedding 按比例混合并归一化:
\(\lambda\in[0,1]\) 控制残差投影的强度(实验取 0.85),保留一小部分原始表示是为了稳定性。最后相似度算在残差空间里:
映射到 \([0,1]\),且保证 \(\phi_x(s_i,s_i)=1\)。
这样,"因为都相关所以相似"的部分被扣掉了,多样性惩罚只落在"扣掉查询成分之后还剩下的重叠"上——那才是真正的冗余。这个思路让我想到残差网络和投影矩阵的老把戏,不新鲜,但用在这里恰到好处,而且只需要一行投影运算,几乎零成本。
🧪 实验:多技能查询上的差距藏不住了
实验设置
基准是 SkillRouter:从 Claude Skill Registry 衍生的约 8 万候选技能池,75 条专家验证查询,覆盖 55 个领域、8 个大类。其中单技能查询 24 条,多技能查询 51 条(每条需要 2 到 5 个目标技能)。还分两个难度档:Easy 档 78,361 个技能,Hard 档 79,141 个(混入了 780 个 LLM 生成的干扰技能),论文报告两档平均。
对比基线是完整 SkillRouter 流水线:SR-Emb-0.6B 检索 + SR-Rank-0.6B 逐点重排。DSR 用一模一样的检索器和重排器,只把最后的"取 Top-k"换成"查询残差 DPP 选择"。指标是 Recall@k(目标技能召回了多少)和 Full Coverage@k(该查询的所有目标技能是否全部进了前 k——对多技能任务这个指标更严苛也更实际,缺一环整条流水线就跑不起来)。\(k\in\{10,20,50\}\),实验在 A100 80GB 上跑。
一个读表时要知道的坑:SkillRouter 官方发布的输出只有 20 条排序结果,所以它的 Recall@50 和 Full Coverage@50 就等于 @20 的值。@50 的对比其实是看"DSR 能不能构造出更长、更不冗余的短名单"。
主结果
| 方法 | R@10 | R@20 | R@50 | FC@10 | FC@20 | FC@50 |
|---|---|---|---|---|---|---|
| 全部查询(75 条) | ||||||
| SkillRouter | .705 | .754 | .754 | .520 | .560 | .560 |
| DSR | .712 | .768 | .808 | .527 | .573 | .633 |
| 多技能查询(51 条) | ||||||
| SkillRouter | .659 | .704 | .704 | .424 | .458 | .458 |
| DSR | .668 | .739 | .773 | .432 | .492 | .551 |
坦率地讲,共享截断点 @20 上的提升不算炸裂:全量查询 Recall@20 从 0.754 到 0.768,Full Coverage@20 从 0.560 到 0.573。考虑到没换检索器也没换重排器,只动了选择策略,这个幅度合理但谈不上惊艳。
真正的差距在更长名单和多技能查询上。多技能子集,Full Coverage@50 从 0.458 到 0.551——9.3 个点。这符合理论预期:逐点重排容易让相似技能抱团占据前列,而 DPP 会主动把名额让给覆盖任务不同侧面的技能。任务越组合化,名单越长,多样性选择的红利越大。
附录还给了 MRR 作为早精度诊断:DSR 的 MRR@10 是 0.784,SkillRouter 是 0.788,几乎持平。也就是说覆盖率的提升不是靠牺牲首个正确技能的排名换来的——这和"贪心第一步必选质量分最高者"的设计对上了。
消融:残差核是灵魂,naive 多样性反而有毒
这张表我觉得是全篇最有信息量的:
| 核函数 | 质量分来源 | R@10 | 多技能 FC@10 |
|---|---|---|---|
| 无(逐点基线) | Reranker | .705 | .424 |
| 标准余弦核 | Embedding | .540 | .178 |
| 标准余弦核 | Reranker | .534 | .254 |
| 查询残差核 | Embedding | .618 | .237 |
| 查询残差核 | Reranker | .711 | .441 |
两个发现。
其一,naive 多样性是有害的。同样用 reranker 质量分,标准余弦核 DPP 的多技能 FC@10 只有 0.254,连不做任何多样性处理的逐点基线(0.424)都远远不如。换成残差核,同样条件下飙到 0.441。这就是前面说的那个坑的实锤:余弦核把"共享任务上下文"误判成冗余,把该一起选的互补技能拆散了。0.254 对 0.441,这个差距比主实验的任何一组对比都大,说明整个方法的成败几乎全押在残差投影这一步上。
其二,质量信号仍然重要。固定残差核,把 reranker 质量换成 embedding 相似度质量,R@10 从 0.711 掉到 0.618,多技能 FC@10 从 0.441 掉到 0.237。DPP 只是选择器,原料不行,选得再巧也没用。最好的组合是"学出来的相关度模型 + 查询残差多样性",两者缺一不可。
和其他排序策略的横评
| 方法 | 单技能 R@10 | 单技能 FC@10 | 多技能 R@10 | 多技能 FC@10 |
|---|---|---|---|---|
| Top-k 检索(纯 embedding) | .875 | .875 | .630 | .381 |
| LLM ranker(Qwen3-8B 零样本) | .719 | .719 | .616 | .331 |
| SkillRouter(SR-Rank-0.6B) | .875 | .875 | .659 | .424 |
| DSR | .875 | .875 | .668 | .432 |
单技能查询上四家打平——只要目标技能只有一个,强逐点信号就够用了,多样性没处发力。多技能查询上才是分水岭。还有个值得一瞟的数字:零样本 LLM 重排(Qwen3-8B)全面垫底,多技能 FC@10 只有 0.331,连纯 embedding 检索都不如。在上万个相似技能里做细粒度区分,prompt 一把梭看来是真不行,这倒也侧面支持了"专门训练的小重排器仍有存在价值"。
🤔 我的判断
亮点在哪。 这篇论文最大的贡献其实不是方法本身——DPP 是老工具,残差投影是老技巧——而是把问题重新定义清楚了:技能路由 = 互补集合选择,而不是相关度排序。这个视角的切换带来了一个可直接落地的改进,而且工程成本极低:冻结检索器和重排器,只在最后加一步 50×50 矩阵上的贪心 DPP,增量 Cholesky 实现,开销可以忽略。消融实验做得诚实,把"naive 多样性会翻车"这个负结果明明白白摆出来,这比主实验涨的那几个点更有说服力。
问题在哪。 也有几个让我皱眉的地方。
第一,评估规模偏小。75 条专家查询,多技能子集只有 51 条——Full Coverage@50 上 9.3 个点的差距,换算下来大约对应 5 条查询的翻转。方向是可信的(消融和横评的模式都一致),但统计噪声不小。作者自己在 Limitations 里也承认了这一点。
第二,@50 的对比对基线有点不公平。SkillRouter 官方输出只到 20 条,它的 @50 指标天然被截断,DSR 在 @50 上的"大胜"有一部分是名单长度带来的。真正干净的对比是 @20,而 @20 上的提升是温和的。作者对此倒是坦率,明确说了 @20 才是共享截断对比。
第三,指标停在检索层。Recall 和 Full Coverage 度量的是"技能捞没捞全",不是"Agent 最终任务做没做成"。技能全捞到了,Agent 照样可能因为规划失误或技能指令冲突而翻车。检索覆盖到端到端成功率之间还隔着一层,这篇论文没碰。
第四,DSR 完全受制于候选生成。如果检索器压根没把必需技能捞进 Top-50,后面 DPP 再精巧也无力回天。多样性选择治不了召回的漏。
对工程的启发。 如果你在做技能库、工具库或者 MCP server 池子比较大的 Agent 系统,这个思路几乎可以直接抄:保留现有检索+重排,在输出前加一层查询残差 DPP 选择,几十行代码的事。但抄之前先想清楚一件事——你的场景里多技能查询占比多大?如果绝大多数请求单技能就能解决,这层多样性基本白加(单技能上四个方法打平已经说明了)。另外,千万别图省事直接用 embedding 余弦相似度做多样性惩罚,这篇论文用 0.254 vs 0.441 的数字告诉你,那是负优化。
顺着这个方向再追问一步:残差核扣掉的是"和查询线性对齐"的分量,但技能之间的互补性未必活在 embedding 的线性结构里——"清洗数据"和"画图表"在工作流里是上下游关系,这种功能性互补靠 embedding 投影能不能完全捕捉,我是存疑的。也许下一步是把工作流结构(技能之间的调用依赖)显式建模进选择目标里。那才是"组队问题"的完整形态。
觉得有启发的话,欢迎点赞、在看、转发。跟进最新AI前沿,关注我