支持私有化部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


用子模优化法为DeepResearch生成多样性查询

发布日期:2025-07-07 12:28:21 浏览次数: 1545
作者:Jina AI

微信搜一搜,关注“Jina AI”

推荐语

探索如何用子模优化法为DeepResearch生成高多样性查询,突破传统提示词方法的局限。

核心内容:
1. DeepResearch系统中查询多样性的关键挑战
2. 传统提示词方法的不足与实验对比
3. 子模优化结合句向量的创新解决方案

杨芳贤
53AI创始人/腾讯云(TVP)最具价值专家

在开发DeepResearch时,生成多样化的查询 (query) 是一个关键细节。我们在开发时会在至少两处遇到这个问题。第一处是在解析用户的原始输入,并生成网页搜索查询,因为直接把原始输入抛给搜索引擎,效果往往不尽人意。第二处,是许多 DeepResearch 系统会前置一个“研究方案规划器”,负责将原始问题分解为多个正交的子问题,然后调用 Agent 来分而治之的解决这些子问题,最后再整合它们的处理结果。

无论是生成搜索查询,还是拆解子任务,我们的生成目标是相同的:生成的查询必须既能紧扣原始问题,又要足够多样化,从不同视角全面探索问题。 同时,为了控制成本,我们还必须控制查询的数量,避免向搜索引擎或大语言模型(LLM)发起不必要的请求。

虽然业界都认识到查询多样性的重要价值,但多数开源的 DeepResearch 项目在处理这一优化问题时,方法却略显粗放。它们往往选择了一条捷径:直接将多样性、相关性等要求写死在提示词(Prompt)里。有些方案会更进一步,追加一轮 LLM 调用,让模型自行评估和调整,以期提升多样性。这些方法构成了当前主流的实现思路,但其效果和稳定性仍有待商榷。

上图展示了两种主流的提示词策略,来引导 LLM 生成多样化查询。上方的提示词言简意赅,下方的则设计得更为精密、结构化。实验中,我们设定原始查询为“向量与重排器”(embeddings and rerankers),并采用 gemini-2.5-flash 模型,目标是测试在给定查询数量下,这两种提示词能否有效地产出足够多样化的结果。

为此,本文将探讨一种更为严谨的解决之道,它结合了句向量(Sentence Embeddings)与子模优化(Submodular Optimization)。子模优化与 L-BFGS,都是我在读博时非常喜欢的算法。接下来,我将具体演示如何运用该方法,在严格的查询数量限制下,精炼出一个最优的多样化查询组合,从而显著提升 DeepResearch 系统的综合表现。

通过提示词生成查询

那么,单靠提示词(Prompting)能否有效生成多样化的查询?复杂的提示词,效果是否真的优于简单的提示词?为了回答这些问题,我们设计了一场对比实验,直接检验以下两种提示的性能。

  1. 简单提示词:

你是一位生成多样化搜索查询的专家。对于任何输入主题,生成 {num_queries} 个不同的搜索查询,以探索该主题的各个角度和方面。

 2. 结构化提示词:
你是一位专业的研究策略师。请生成一个最优的多样化搜索查询集,以最大化信息覆盖范围,同时最小化冗余。

任务:根据任意给定的输入,创建恰好 {num_queries} 个满足以下条件的搜索查询:
- 相关性:每个查询必须与原始输入在语义上相关。
- 多样性:每个查询应探索一个独特的方面,且重叠最小。
- 覆盖率:所有查询合在一起应能全面地涵盖该主题。

流程:
1. 分解:将输入分解为核心概念和维度。
2. 视角映射:识别不同的角度(理论、实践、历史、比较等)。
3. 查询构建:为每个视角精心设计具体的、可搜索的查询。
4. 多样性检查:确保查询之间的语义重叠最小。

实验流程如下:我们选用 gemini-2.5-flash 模型,以 embeddings and rerankers 为原始查询,分别运用上述两种提示,迭代生成 1 至 20 个查询。生成查询后,我们随即调用 jina-embeddings-v3 的 text-matching,从两个维度进行评估:一是衡量生成查询与原始查询的相关性,二是衡量生成查询集内部彼此的多样性。评估指标均为句向量的余弦相似度。

从结果图(右侧两图)来看,两种提示词在“查询集内部相似度”这一指标上,表现出惊人的一致性。 无论生成多少查询,其内部相似度的中位数始终徘徊在 0.4 到 0.6 的高位。有意思的是,当查询数量增多时,简单提示词产出的结果反而更多样化一些;而结构化提示的优势则在于,它能更稳定地维系结果与原始查询的相关度,使其保持在 0.6 左右。

然而,两张右侧图表也暴露了更深层的问题。首先,两种方法生成的查询相似度得分方差巨大,甚至大量落在 0.7 至 0.8 的区间,这说明模型产出了许多语义几乎完全重复的查询。此外,随着查询数量的增加,两种方法都显现出维持多样性的乏力。我们期待看到的“查询越多、内部相似度越低”的理想趋势并未出现,取而代之的是一条居高不下的稳定的水平线。这意味着,模型新增的查询,很可能只是对已有视角的不断复述。

为什么会这样?Wang 等人(2025)的研究或许能给我们一些启示。他们发现,即使有提示词加以引导,大语言模型依然会不成比例地放大主流群体的观点,表现出对普遍视角的固有偏好。

https://arxiv.org/abs/2505.15229

这背后的根源,在于其训练数据对某些观点的过度呈现,使得模型在生成内容时,自然而然地向这些主流视角靠拢。无独有偶,Abe 等人(2025)的研究也印证了这一点,指出基于 LLM 的查询扩展会优先采纳流行的解释,而忽视那些小众但同样有价值的角度。

https://arxiv.org/abs/2505.12349

一个典型的例子是,当你询问“人工智能的好处”,模型很可能滔滔不绝地列举自动化、高效率、合乎伦理等众所周知的优点,却极易遗漏像“加速新药研发”这类不那么大众化,却意义重大的应用场景。

问题建模

看到这里,或许有人会说,之前的实验结论为时过早,你们应该继续优化提示词,再做尝试。诚然,改进提示词无疑会影响结果,但这次实验带给我们一个洞察:通过生成越来越多的查询,总会有一两个与众不同的查询出现。 尽管副产物是大量重复的查询,但这至少给了我们一个可行的起点。

既然广撒网的成本如此低廉,又能确保捞到几条好鱼,那我们何不换个思路,将问题转化为一个经典的子集选择问题(subset selection problem)呢?

在数学语言中,这个问题可以精确地描述为:我们有一个由大语言模型生成的候选查询全集 ,它源于用户的原始输入 。我们的任务,就是从全集  中,挑选出一个大小为  的子集 ,并确保这个子集能在最大化信息覆盖面的同时,最小化内部的语义冗余

然而,这条路从一开始就困难重重。要想从  个候选项中找到包含  个查询的最优组合,我们需要暴力搜索全部  种可能性,其计算复杂度呈指数级增长。举个简单的例子,即便我们只从 20 个候选查询中挑选 5 个,也需要进行多达 15504 次组合检查,这在实际应用中几乎是不可行的。

子模函数

既然暴力破解此路不通,我们就必须另辟蹊径。在介绍更优的解法之前,有必要先引入两个核心术语:子模性(Submodularity)与子模函数(Submodular Function)。

这两个词初听或许有些陌生,但它们所描述的现象,其实你早就熟悉,那就是经济学中著名的“边际效益递减”(Diminishing Returns)定律。可以说,子模性正是这一定律的数学化身。

我们可以用一个安装 Wi-Fi 路由器的例子来直观地理解它。想象一下,你要为一整栋大楼提供网络覆盖。你安装第一台路由器时,它创造了巨大的价值,瞬间点亮了大片原本没有信号的区域。接着,你装上第二台,它同样贡献了可观的价值,但由于其部分信号与第一台重叠,它带来的新增覆盖(即边际效益)已然不如前者。当你继续添加第三台、第四台……你会发现,每台新路由器能覆盖的“全新”区域越来越小,因为楼内大部分空间早已信号满格。等到你安装第十台路由器时,它可能仅仅是填补了某个角落的微弱信号,其边际价值已趋近于零。

这个例子抓住了子模性的精髓。在数学上,我们可以将它严格定义如下:对于一个定义在集合上的函数  是子模的,如果它对任意满足  的集合 A 和 B,以及任意一个不属于  的元素 ,都恒久满足下列不等式,那么我们称这个函数是子模的::

用通俗的语言解释就是:将一个新元素添加到一个小集合里,所获得的增益,总是不小于将这个完全相同的元素添加到一个包含这个小集合的大集合里所获得的增益。

理解了这一点,我们再回过头看查询生成问题,便会豁然开朗。显然,挑选查询的过程,天然地符合这种“边际效益递减”的模式:

  • 我们选择的第一个查询,覆盖了全新的语义空间,,价值最大。
  • 第二个查询覆盖了不同的方面,但不可避免地与第一个查询产生部分重叠,边际价值有所下降。
  • 我们不断加入新查询,每增加一个,它能覆盖的“新”信息就越少,因为大部分核心概念已被触及。

上图取自我 2013 年在 AAAI 会议报告中的一张幻灯片,我当时用一个“球袋”来形容子模性。往袋中不断加球,可以提升整个袋子的综合“效用”,但我们能从右侧 y 轴递减的增量(delta)中清晰地看到,其边际效用正在不断缩小。

基于向量的子模函数设计

有了子模性这个理论武器,我们便可以着手设计目标函数了。首先,我们利用一个先进的向量模型(如 jina-embeddings-v3,将每个候选查询  转化为一个高维向量 。基于这些嵌入向量,我们主要可以从以下两种思路来构建我们的子模目标函数:

方法一:借鉴“设施选址”思想,追求最大化覆盖

这个函数的核心目标,是衡量我们选定的查询集  对所有候选查询的覆盖程度有多好。我们来逐一拆解这个函数,它通过累加对每一个候选查询  的“覆盖分”来计算总效用。

对于任何一个候选查询  而言,它的覆盖分取决于两个值的 较大者

  • 与原始查询的相关度,即 。这里的  是一个我们可调的权重,用来确保我们选出的查询始终紧扣主题。

  • 与已选集合  的最大相似度,即 。这个值衡量我们已选的查询组合对候选查询  的最佳覆盖能力。

要注意的是,这种方法不是直接命令模型去追求多样性,而是通过一种巧妙的机制间接地促成多样化。它没有设置任何条款来惩罚已选集合  内部的相似度。多样性之所以能涌现,完全源于子模性自身的“边际效益递减”:如果你试图选择两个非常相似的查询,那么第二个查询能提供的新增“覆盖收益”将微乎其微,因此在贪心选择中自然会被忽略。

方法二:显式地平衡覆盖度与多样性

如果我们希望更精准地拿捏多样性的“尺度”,则可以采用第二种方法,即在目标函数中显式地引入多样性作为优化项:

这里的多样性项  设计得也颇为巧妙,其定义如下:

这个式子计算的是“已选查询集 ” 与 “未选查询集 ” 之间的总相似度。这个值在何时会最大化呢?答案是:当我们挑选出的查询,与那些我们未选择的查询差异最大时。从图论的视角看,这其实是在最大化两个集合之间的“割”(cut),因此也常被看作一种图割函数。

两种方法的区别

无论是方法一还是方法二,我们设计的函数都精妙地保持了子模性这一关键特征。

先看 设施选址函数。它作为一类经典的子模函数,其子模性的根源在于 max 运算的内在逻辑。具体来说,当我们向已选集合中增添一个新查询  时,系统会重新评估它对所有候选查询的覆盖情况。对于任意一个候选查询 ,它只会被我们已选集合中与它相似度最高的那个查询所覆盖。因此,若将新查询  加入一个本身就很小的集合 ,它很有可能成为许多候选查询的“新晋最优覆盖者”,从而带来显著的增益。反之,若将其加入一个已经很庞大、覆盖面很广的集合 (且 ),那么大部分候选查询早已被  中的其他成员牢牢覆盖,新成员  的用武之地自然就大大减少,其边际增益也就更低。

再看 图割多样性函数。其核心项   之所以也是子模的,是因为它本质上在衡量已选集和未选集之间的“割”的大小。我们可以这样理解:每当我们将一个新查询从未选集移动到已选集时,新加入的成员能一下子带来大量指向未选集合的“新连接”(即增加了割的权重)。而当已选集合已经很大,未选集合所剩无几时,新成员能带来的“新连接”数量自然就少了。这同样完美符合边际效益递减的规律。

综上所述,两种方法的核心区别在于实现多样性的路径不同:设施选址依靠覆盖度的内部竞争来间接催生多样性,而显式方法则直接将多样性作为一个指标进行量化和优化。 因此,两者在理论上都行之有效,但显式方法赋予了开发者更直接、更灵活的控制权,让我们可以自由地在“相关性”与“多样性”之间找到最佳平衡点。

代码实现

我们将完整的实现代码开源在了 GitHub,欢迎开发者查阅、使用和贡献。

https://github.com/jina-ai/submodular-optimization

正因为我们构建的函数是子模的,我们可以用贪心算法 (greedy algorithm)来解这个优化问题。针对下面这个旨在寻找最优子集的问题,贪心算法不仅速度飞快,更能提供一个坚实的理论保证:它找到的解,其质量至少能达到最优解的 

下面,我们就以“设施选址”(即隐式多样性)方法为例,展示其核心代码实现:

def greedy_query_selection(candidates, embeddings, original_embedding, k, alpha=0.3):
    """
    用于子模查询选择的贪心算法

    Args:
        candidates: 候选查询字符串列表
        embeddings: 查询嵌入矩阵 (n x d)
        original_embedding: 原始查询的嵌入 (d,)
        k: 要选择的查询数量
        alpha: 相关性权重参数
    """

    n = len(candidates)
    selected = []
    remaining = set(range(n))

    # 预计算相关性得分
    relevance_scores = cosine_similarity(original_embedding, embeddings)

    for _ in range(k):
        best_gain = -float('inf')
        best_query = None

        for i in remaining:
            # 计算添加查询 i 的边际增益
            gain = compute_marginal_gain(i, selected, embeddings,
                                       relevance_scores, alpha)
            if gain > best_gain:
                best_gain = gain
                best_query = i

        if best_query isnotNone:
            selected.append(best_query)
            remaining.remove(best_query)

    return [candidates[i] for i in selected]

def compute_marginal_gain(new_idx, selected, embeddings, relevance_scores, alpha):
    """计算将 new_idx 添加到已选集合的边际增益"""
    ifnot selected:
        # 第一个查询:增益是所有相关性得分的总和
        return sum(max(alpha * relevance_scores[j],
                      cosine_similarity(embeddings[new_idx], embeddings[j]))
                  for j in range(len(embeddings)))

    # 计算当前的覆盖范围
    current_coverage = [
        max([alpha * relevance_scores[j]] +
            [cosine_similarity(embeddings[s], embeddings[j]) for s in selected])
        for j in range(len(embeddings))
    ]

    # 计算加入新查询后的覆盖范围
    new_coverage = [
        max(current_coverage[j],
            cosine_similarity(embeddings[new_idx], embeddings[j]))
        for j in range(len(embeddings))
    ]

    return sum(new_coverage) - sum(current_coverage)

代码中的平衡参数 alpha,是我们手中一个关键的调节旋钮,它精准地控制着相关性与多样性二者之间的权衡:

  • 高  (例如, 0.8): 优先考虑与原始查询高度相关的条目,可能会牺牲多样性。
  • 低  (例如, 0.2): 优先保证所选查询之间的多样性,但结果可能会偏离用户的初始意图。
  • 中等  (例如, 0.4-0.6):这是一种平衡策略,在大多数实际应用中都能取得稳健而出色的效果。

懒贪心算法

细心的读者可能已经发现,上述标准贪心算法存在一个效率瓶颈。在每一轮挑选最佳查询的迭代中,我们都一丝不苟地为所有尚未被选中的候选项重新计算了一遍边际增益。这种地毯式的计算,其实存在巨大的优化空间。

for i in remaining:
    # 计算添加查询 i 的边际增益
    gain = compute_marginal_gain(i, selected, embeddings,relevance_scores, alpha)

为此,我们可以引入一种更精巧的优化策略——懒贪心算法(Lazy Greedy Algorithm)。它充分利用了子模函数的“边际效益递减”特性,来避免大量冗余计算。其核心洞察在于:如果一个元素 A 在第  轮的边际增益本就高于另一个元素 B,那么在下一轮(第  轮)中,由于所有已有元素的增益都在递减,A 的增益大概率仍然会领先于 B。

import heapq

def lazy_greedy_query_selection(candidates, embeddings, original_embedding, k, alpha=0.3):
    """
    用于子模查询选择的惰性贪心算法
    通过避免不必要的边际增益计算,比标准贪心算法更高效
    """

    n = len(candidates)
    selected = []

    # 预计算相关性得分
    relevance_scores = cosine_similarity(original_embedding, embeddings)

    # 初始化优先队列:(-边际增益, 最后更新轮次, 查询索引)
    # 使用负增益因为heapq是最小堆
    pq = []
    for i in range(n):
        gain = compute_marginal_gain(i, [], embeddings, relevance_scores, alpha)
        heapq.heappush(pq, (-gain, 0, i))

    for iteration in range(k):
        whileTrue:
            neg_gain, last_updated, best_idx = heapq.heappop(pq)

            # 如果这个增益是在当前轮次计算的,那它肯定是最好的
            if last_updated == iteration:
                selected.append(best_idx)
                break

            # 否则,重新计算边际增益
            current_gain = compute_marginal_gain(best_idx, selected, embeddings,relevance_scores, alpha)
            heapq.heappush(pq, (-current_gain, iteration, best_idx))

    return [candidates[i] for i in selected]

懒贪心算法的工作方式如下:

  1. 初始化一个按候选项的边际增益排序的优先队列。
  2. 在每一轮选择中,只从队首取出当前边际增益最高的元素,并只为这一个元素重新计算其真实的边际增益。
  3. 如果重新计算后它仍然是队列里最高的,就直接选择它,完成本轮选择。
  4. 否则,就根据其最新的增益值,将其插回队列的正确位置,然后重复第 2 步,考察新的队首元素。

通过这种“非必要,不计算”的惰性策略,算法避免了为那些排名靠后、显然不会被选中的元素进行耗时的增益重算,实现显著的速度提升。

结果

现在,我们用实验结果来最终检验这套新方法的威力。我们重复之前的实验流程:首先,沿用简单的提示词,生成一个包含 20 个候选查询的初始集合。然后,我们分别运用提示词工程(直接从 1 到 20 个生成)和子模优化(从 20 个候选中挑选 k 个)两种策略,并采用相同的余弦相似度指标进行评估。

原始查询 = "embeddings and rerankers"
原始查询:"generative ai"
原始查询:"geopolitics USA and China"
原始查询:"google 2025 revenue breakdown"

结果不言而喻:经由子模优化精炼出的查询组合,其多样性显著更高,集合内部相似度也远低于前者。

最后一个问题:为何子模化建模至关重要

行文至此,你心中或许会浮现一个终极问题:我们为何要大费周章地将问题上升到子模优化的高度?直接设计一套巧妙的启发式规则(heuristics),或者尝试其他优化算法,难道不可行吗?

答案很简单,但意义深远。因为子模化建模,将一个依赖直觉、规则临时的“启发式”任务,彻底转变为一个拥有坚实理论后盾的严谨优化问题。 它赋予了我们三样宝贵的东西:可证明的性能保证可预期的算法效率,以及可量化的优化目标

压倒性的效率与性能保证

一旦我们将目标函数证明为子模函数,就等于解锁了强大的理论保证与高效的算法。贪心算法的运行时间仅为 ,与暴力搜索  种组合相比,效率有天壤之别。更重要的是,它能确保找到的解,其质量至少达到最优解的 。这意味着,我们的方案拥有一个可靠的性能底线。这是任何拍脑袋想出的启发式规则都无法给予的承诺。

更进一步,子模函数的数学结构还为“懒贪心”这类更快的算法铺平了道路。加速的秘诀,正是源于“边际效益递减”的特性:那些在早期迭代中就表现平平的元素,在后续迭代中逆袭的可能性微乎其微。因此,懒性贪心算法无需遍历,它通常只需聪明地聚焦于少数几个排名靠前的种子选手,重新评估它们的增益即可。

告别手工作坊式的启发式规则

如果没有一个成体系的框架,我们很容易陷入“手搓”式的开发模式,依赖各种临时的启发式规则,例如“确保查询间的余弦相似度低于 0.7”,或是“强制平衡不同类别的关键词”。这类规则不仅难以调试和优化,更缺乏跨场景的泛化能力。

子模优化则提供了一套有章可循、基于数学的科学方法。你可以利用验证集,系统性地调优超参数;你可以在生产环境中,持续监控方案的实际质量;当系统表现不佳时,你手握清晰的量化指标,可以精准地定位并调试问题所在。

最后,子模优化是隶属于离散优化领域的成熟研究。这意味着,我们不仅能从懒贪心算法起步,还可以借力于学术界更前沿的成果,例如加速贪心、局部搜索等更高级的算法,借鉴关于不同模型适用场景的扎实理论,甚至还能将框架扩展,从容应对预算限制、公平性约束等更复杂的现实需求。

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询