超越 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)成为关键瓶颈:

  1. 冗余浪费:注册表里很多 Skill 功能高度重叠(比如 5 个 Skill 都能查天气),独立打分取 Top-k 会把多个等价 Skill 全部塞进上下文,挤占真正有用的 Skill。
  2. 复杂任务需要互补集合:单 Skill 解决不了的问题,需要功能互补的多个 Skill 组合,独立打分不保证互补性。
  3. 伪冗余 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 给出具体数字)。

亮点与局限

亮点

  1. 重新定义问题:把 Skill routing 从"独立排序"升级到"互补集合选择",是一个问题形式化层面的贡献,比单纯提一个模型更根本。
  2. query-residual diversity kernel 设计精巧:精准解决了"被查询拉到一起的相关性 ≠ 冗余"这一常见误判。
  3. 方法学可迁移:DPP + query-residual 的思路可推广到 RAG 文档重排、推荐系统去冗余、多模态检索等场景。
  4. 多 Skill 查询增益大:直接对应工业痛点(复杂任务需要多个互补 Skill)。
  5. 概念简洁:DPP 是经典模型,重新解读后立刻能用,没有引入新的训练范式。

局限

  1. 没有公开绝对数字(⚠️ 原文 abstract 未给出):recall 与 full coverage 的具体百分点提升需要查 PDF 主表。
  2. DPP 的计算成本:行列式 / 特征分解在 N 较大时是 O(N^3),Skill 注册表到几万条时需要近似算法(论文未明确是否讨论近似,⚠️ 原文未明确)。
  3. sim(s_i, s_j) 的定义:Skill 之间的相似度如何计算(embedding?描述文本?API schema?)直接影响效果,abstract 未明确(⚠️ 原文未明确)。
  4. query embedding 与 Skill embedding 的对齐:query-residual kernel 需要两者在同一向量空间,论文是否训练专用 embedding 还是直接复用通用 embedding(⚠️ 原文未明确)。
  5. 未与现有 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-largebge-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