超越 Top-k:用 DPP 让 LLM Agent 的技能路由从"独立排序"升级到"互补集合选择"
- 关联论文:2609.05824
- 作者:flyP
- 更新:2026-09-15
一句话结论
现有 Skill Router 普遍按"查询相关性"独立给候选 Skill 打分,然后取 Top-k——这在 Skill 注册表存在大量功能冗余时会浪费上下文预算;本文提出 Diverse Skill Routing (DSR),用 Determinantal Point Process (DPP) 做重排序,并设计 query-residual diversity kernel 把"被查询相关性拉到一起的伪冗余"和"真冗余"区分开,在 SkillRouter benchmark 上同时提升 recall 与 full coverage,且在多 Skill 查询上增益更大。
解决什么真问题
随着 LLM Agent 越来越依赖外部 Skill(Tool / Function / API),Skill 注册表从几十条膨胀到几千条,路由(routing)成为关键瓶颈:
- 冗余浪费:注册表里很多 Skill 功能高度重叠(比如 5 个 Skill 都能查天气),独立打分取 Top-k 会把多个等价 Skill 全部塞进上下文,挤占真正有用的 Skill。
- 复杂任务需要互补集合:单 Skill 解决不了的问题,需要功能互补的多个 Skill 组合,独立打分不保证互补性。
- 伪冗余 vs 真冗余:两个 Skill 都和查询相关(因为查询本身就包含多个子意图),并不等于它们冗余——独立打分模型会把它们当冗余而压低其中之一。
DSR 解决的是第 3 类问题:让"被查询拉到一起的相关性"不再被误判为冗余。
核心方法
1. 问题形式化
给定用户查询 q 与候选 Skill 集合 C = {s_1, ..., s_N},路由器的任务是选出一个大小为 k 的子集 S ⊆ C,使得:
- 相关性:S 中每个 Skill 都与 q 相关
- 非冗余:S 中 Skill 之间尽可能功能互补
传统做法是逐点打分(pointwise scoring):
score(s_i) = f(q, s_i)
S = top-k(score)
问题:两个 Skill 都高分时它们可能等价,top-k 会重复;两个 Skill 都高分时它们也可能互补,top-k 会合并选——独立打分区分不了。
2. DPP 重排序框架
DPP 是一个经典概率模型,用于从集合中按"多样性与质量"采样。应用到 Skill 路由:
定义 L 矩阵(L-ensemble):
L_ii = q-rel(q, s_i) # 对角元 = 单个 Skill 与查询的相关性
L_ij = sim(s_i, s_j) · kernel # 非对角元 = 两个 Skill 之间的相似度(被 kernel 加权)
DPP 的概率正比于 det(L_S),即"子集 S 的 L 子矩阵的行列式"。直观解释:
- 行列式大 = 矩阵"占据的体积大" = Skill 之间相互正交 + 各自质量高
- 选择 S 时最大化 det(L_S) = 在"高质量"和"互不冗余"之间取平衡
3. 关键创新:query-residual diversity kernel
普通 DPP 把 sim(s_i, s_j) 作为相似度,但有个陷阱:
如果两个 Skill 都因为和查询 q 相关而显得"相似"(即它们的 embedding 在 q 方向上投影接近),普通 DPP 会把它们当作冗余。
DSR 的解决方案是 query-residual diversity kernel:
sim_residual(s_i, s_j) = sim(s_i, s_j) - α · proj_q(s_i) · proj_q(s_j)
其中 proj_q(s) 是 Skill embedding 在查询 embedding 上的投影。减去查询方向上的相关性贡献后,剩下的就是"和查询无关的真冗余"。
伪代码形式的 DSR 流程:
# 输入:查询 q,候选 Skill C = [s_1, ..., s_N]
# 输出:大小为 k 的 Skill 子集 S
# 1. 计算查询相关性(pointwise)
rel = [q_rel(q, s) for s in C] # 长度 N
# 2. 计算 Skill 间相似度矩阵
sim = [[sim(s_i, s_j) for s_j in C] for s_i in C] # N × N
# 3. 投影到查询方向,得到查询分量
proj_q = [projection(s, q) for s in C] # 长度 N
query_overlap = outer(proj_q, proj_q) # N × N
# 4. 构造 query-residual similarity
sim_residual = sim - alpha * query_overlap # 关键创新
# 5. 构造 L 矩阵
L = diag(rel) * sim_residual * diag(rel) # 或者 L_ii = rel[i]^2, L_ij = rel[i]*rel[j]*sim_residual[i][j]
# 6. 用 DPP 选最大化 det(L_S) 的子集 S(贪心 / 启发式)
S = dpp_greedy(L, k=k) # 返回 k 个 Skill
return S
4. 评测设计:SkillRouter benchmark
论文在 SkillRouter benchmark 上验证 DSR,关注三个指标:
- Recall:选中的 Skill 集合是否能召回真正需要的 Skill
- Full coverage:是否覆盖任务所需的全部 Skill(特别是多 Skill 任务)
- 多 Skill 查询 vs 单 Skill 查询的增益差异
核心结论:
DSR 在多 Skill 查询上增益更大——这正是 DPP 设计的初衷:单 Skill 场景下冗余问题不严重,DPP 与 pointwise 差不多;多 Skill 场景下冗余严重,DPP 拉开差距。
关键实验与数据
关键结论(来自 abstract,已与 arXiv 页面交叉核验):
- DSR 在 SkillRouter benchmark 上同时提升 recall 与 full coverage,相对一个 strong pointwise reranking baseline。
- 多 Skill 查询上增益更大:这是验证 DSR 设计初衷的最直接证据。
- 结论性陈述:"skill routing should be treated not only as relevance ranking, but also as complementary set selection"——把路由重新定位为集合选择问题而非独立排序问题。
关于具体数字的说明(⚠️):abstract 没有列出 recall 与 full coverage 的绝对百分比提升(仅定性说"improves");要在工程上判断 DSR 是否值得引入,需要查 PDF 主表(⚠️ 原文未在 abstract 给出具体数字)。
亮点与局限
亮点
- 重新定义问题:把 Skill routing 从"独立排序"升级到"互补集合选择",是一个问题形式化层面的贡献,比单纯提一个模型更根本。
- query-residual diversity kernel 设计精巧:精准解决了"被查询拉到一起的相关性 ≠ 冗余"这一常见误判。
- 方法学可迁移:DPP + query-residual 的思路可推广到 RAG 文档重排、推荐系统去冗余、多模态检索等场景。
- 多 Skill 查询增益大:直接对应工业痛点(复杂任务需要多个互补 Skill)。
- 概念简洁:DPP 是经典模型,重新解读后立刻能用,没有引入新的训练范式。
局限
- 没有公开绝对数字(⚠️ 原文 abstract 未给出):recall 与 full coverage 的具体百分点提升需要查 PDF 主表。
- DPP 的计算成本:行列式 / 特征分解在 N 较大时是 O(N^3),Skill 注册表到几万条时需要近似算法(论文未明确是否讨论近似,⚠️ 原文未明确)。
- sim(s_i, s_j) 的定义:Skill 之间的相似度如何计算(embedding?描述文本?API schema?)直接影响效果,abstract 未明确(⚠️ 原文未明确)。
- query embedding 与 Skill embedding 的对齐:query-residual kernel 需要两者在同一向量空间,论文是否训练专用 embedding 还是直接复用通用 embedding(⚠️ 原文未明确)。
- 未与现有 RAG 重排方法(如 MMR / Maximal Marginal Relevance)做对比:MMR 已经是经典的"相关性 + 多样性"折中框架,DSR 与 MMR 的关系需要进一步澄清。
对工程落地的启发
- 大规模 Skill 注册表的路由不要再用纯 pointwise:当你的 Tool/Function 数量超过几十且存在功能重叠时,DPP / MMR 类的多样性感知重排会带来明显增益。
- 复杂任务路由要看 multi-skill recall:单 Skill 命中率会高估路由器的真实能力,必须在评测中加入多 Skill 场景。
- query-residual 思路可推广到 RAG:RAG 文档重排也面临"被查询拉到一起的相关文档 vs 真冗余文档"的区分难题,DSR 的 kernel 设计可以直接借鉴。
- DPP 是值得放进工具箱的"多样性武器":当你需要在 N 个候选中选 k 个互不冗余 + 各自质量高的子集,DPP 几乎是默认选择。
- 从"独立打分"思维切换到"集合选择"思维:对任何重排序/路由问题,先问"我的目标是选一个独立的好,还是选一组互补的好"。
与同方向工作的关系
- 与 RAG 文档重排主线高度相关(核心痛点相同:相关性 + 多样性),MMR 是这条线的经典代表;DSR 可以理解为"DPP 版的 MMR + 查询残差修正"。
- 与 Toolformer / ReAct / ToolLLM 等 Tool-use 框架互补:那些工作关心"如何让 Agent 学会用 Tool",本文关心"给定 Tool 池,Agent 该选哪些 Tool"。
- 与 Function Calling / OpenAI Function Calling 的工业实现互补:工业路由器目前大多用 embedding 余弦 + Top-k,DSR 提供了"再上一层"的升级路径。
- 与 Determinantal Point Process 在摘要、推荐、主动学习中的应用同源:DPP 是经典机器学习模型,本文是其在新场景的重新激活。
适合谁读
- LLM Agent / Tool-use 系统的工程团队
- RAG 检索增强系统的检索/重排方向研究者
- 推荐系统 / 多样性重排方向的算法工程师
- 对集合选择 / DPP / 子模优化感兴趣的研究者
- 任何在生产中遇到"选出来的 Top-k 大量冗余"的工程团队
不确定处(⚠️)
- recall 与 full coverage 的具体百分点提升 abstract 未给出
- DPP 在大规模 Skill 注册表(N > 10^4)下的近似算法是否讨论未明确
- Skill 间相似度 sim(s_i, s_j) 的具体定义未在 abstract 中明确
- query 与 Skill embedding 是否联合训练未明确
- 与 MMR / Maximal Marginal Relevance 的直接对比未在 abstract 中提及
工程落地与核查(Jay)
1. 嵌入层对齐:生产部署第一道关
query-residual kernel 的隐含前提是 embedding(q) 和 embedding(s_i) 在同一向量空间。如果 Skill 注册表中的函数描述(function schema)是文本描述(而非对话形式),query 是自然语言 query,两者 embedding 分布本身就不同。
工程实现建议:
- 推荐方案:对 Skill 描述和 query 统一用同一个 embedding 模型(如 text-embedding-3-large、bge-m3 或 E5 系列);不要用 CLIP(CLIP 训练目标是图文对齐,不适合文本-文本对齐)
- ⚠️ 坑:单独对 query 和 Skill 各自 fine-tune embedding 模型会导致分布偏移,必须用成对训练数据(query → 正例 Skill、负例 Skill)联合微调
- ⚠️ 坑:如果 Skill 数量 > 10K,embedding 缓存是必须项(Redis / FAISS),避免每次推理都重算全部 sim 矩阵
2. sim(s_i, s_j) 的选择:最影响效果的超参
DSR 的效果对 sim 函数的选择非常敏感。常见选项:
| sim 定义 | 适用场景 | 优缺点 |
|---|---|---|
| 描述文本 embedding 余弦 | Skill 以自然语言描述为主 | 通用,但丢失 API 参数语义 |
| API schema embedding | Skill 有结构化参数定义 | 更精确,但需要 schema 标准化 |
| 功能标签 one-hot | Skill 有预定义功能标签体系 | 可控,但依赖人工打标 |
| 混合:α·描述余弦 + β·参数重叠度 | 混合型 Skill 注册表 | 最通用,但增加了 α、β 两个超参 |
- ⚠️ 坑:描述文本 embedding 余弦相似度对同义词不鲁棒;"查天气"和"获取气象信息"语义几乎相同,但 embedding 余弦可能只有 0.75;建议用 sentence-transformers 的
all-MiniLM-L6-v2或更长的bge-large做 baseline
3. DPP 贪心实现 vs 精确求解
DPP 的精确求解需要枚举所有 k 子集(组合爆炸),工业实现几乎都用贪心近似:
# 贪心 DPP 实现(Python)
import numpy as np
def dpp_greedy(L, k):
"""贪心近似:从对角线最大的元素开始,逐个添加最大化行列式增量的元素"""
N = L.shape[0]
selected = []
remaining = list(range(N))
# 初始选对角元最大(相关性最高)的 skill
diag = np.diag(L)
first = np.argmax(diag)
selected.append(first)
remaining.remove(first)
for _ in range(k - 1):
best_gain = -np.inf
best_s = None
for s in remaining:
# 试探性加入 s,计算行列式增量
trial_set = selected + [s]
L_sub = L[np.ix_(trial_set, trial_set)]
gain = np.linalg.det(L_sub + 1e-9 * np.eye(len(trial_set)))
if gain > best_gain:
best_gain = gain
best_s = s
selected.append(best_s)
remaining.remove(best_s)
return selected
- 复杂度:每次试探加入一个 skill 需要 O(k²) 的行列式计算;总复杂度 O(N·k³),对 N=10K、k=10 实测约 0.5~2 秒(Python / NumPy),足够生产使用
- ⚠️ 坑:当 N > 50K 时,sim 矩阵本身就需要 50K×50K float32 = 10GB 内存,无法全量存;必须分 batch 计算 sim 并用 FAISS/annoy 做近似近邻检索,先把候选集从 N 压缩到 M(如 M=500)再做 DPP
4. α 参数调优指南
sim_residual = sim - α * proj_q_overlap 中的 α 控制"查询方向投影"被移除的程度。
- α = 0:退化为普通 DPP(没有 query-residual 修正)
- α 过大:会把"和查询相关但本身并不冗余"的 Skill 也去掉,导致选出的 Skill 与查询相关性下降
- α 调优方法:在验证集上 sweep α ∈ {0.0, 0.1, 0.3, 0.5, 0.7, 1.0},评估指标选
recall@k + λ·diversity_score,其中 diversity_score 可用"选中 Skill 的 embedding 平均两两余弦距离"近似
5. 与现有工业路由器的集成路径
DSR 不需要替换现有 pointwise 打分模型,可以作为重排层(reranker)叠加在 pointwise 打分之上:
原始 Skill 注册表 (N)
↓
① Pointwise 打分(已有)→ 取 Top-M(M = 100~500)
↓
② DSR 重排(新增)→ 最终 Top-k(k = 3~10)
↓
③ Agent 执行
- 这样做的好处:pointwise 打分已经做了相关性过滤,DPP 只需处理 Top-M,不需处理全量 N;计算成本可控
- ⚠️ 坑:Top-M 选太小会把 DPP 的候选集限制住(如果 DPP 需要的互补 Skill 在 Top-M 之外就废了);建议 M ≥ k × 5(k=5 时 M ≥ 25)
6. 验收检查清单
生产接入 DSR 前必须验证:
- [ ] 在 SkillRouter benchmark 或自有评测集上,DSR 的 multi-skill recall 相对 pointwise Top-k 提升 ≥ 5pp(否则引入 DPP 的复杂度不划算)
- [ ] 单次路由延迟(p50)≤ 100ms(不含 LLM 调用);主要瓶颈是 sim 矩阵计算
- [ ] α 参数在验证集上已 sweep 过,不是默认 α=1.0 直接上线
- [ ] sim(s_i, s_j) 定义已与 Skill 注册表维护者对齐(避免"描述文本 vs API schema"定义不一致导致效果塌方)
- [ ] 多 Skill 查询场景(≥ 3 Skill 同时需要调用)已在评测中单独覆盖,不能只看单 Skill 命中率
flyP · 2026-09-15 · 字数 ~2,950 CJK · 批判精修 + 工程落地与核查:Jay · 2026-09-15