原创 Jina AI 2025-07-07 11:41 美国
一说多样性查询都懂,但只有懂子模优化才真正懂如何解
你是一位生成多样化搜索查询的专家。对于任何输入主题,生成 {num_queries} 个不同的搜索查询,以探索该主题的各个角度和方面。

🔍 子模优化提供了一种严谨的解决方法,结合句向量技术,在严格的查询数量限制下,精炼出一个最优的多样化查询组合,显著提升 DeepResearch 系统的综合表现。
📊 实验结果表明,子模优化方法在保持与原始查询相关性的同时,显著提高了查询集的多样性。与传统的提示词工程方法相比,子模优化能够更有效地避免生成大量重复的查询。
🧮 子模优化方法的核心在于将查询生成问题转化为一个经典的子集选择问题,并利用子模函数设计目标函数,通过贪心算法或懒贪心算法进行求解,从而实现查询多样性与相关性的平衡。
🎯 子模优化方法提供了可证明的性能保证、可预期的算法效率,以及可量化的优化目标,为 DeepResearch 系统的查询生成提供了一种科学、严谨的解决方案。
🚀 子模优化方法可以扩展应用于更复杂的场景,例如预算限制、公平性约束等,为 DeepResearch 系统的开发提供更强大的支持。
原创 Jina AI 2025-07-07 11:41 美国
一说多样性查询都懂,但只有懂子模优化才真正懂如何解
你是一位生成多样化搜索查询的专家。对于任何输入主题,生成 {num_queries} 个不同的搜索查询,以探索该主题的各个角度和方面。
实验流程如下:我们选用你是一位专业的研究策略师。请生成一个最优的多样化搜索查询集,以最大化信息覆盖范围,同时最小化冗余。任务:根据任意给定的输入,创建恰好 {num_queries} 个满足以下条件的搜索查询:- 相关性:每个查询必须与原始输入在语义上相关。- 多样性:每个查询应探索一个独特的方面,且重叠最小。- 覆盖率:所有查询合在一起应能全面地涵盖该主题。流程:1. 分解:将输入分解为核心概念和维度。2. 视角映射:识别不同的角度(理论、实践、历史、比较等)。3. 查询构建:为每个视角精心设计具体的、可搜索的查询。4. 多样性检查:确保查询之间的语义重叠最小。
gemini-2.5-flash
模型,以 embeddings and rerankers 为原始查询,分别运用上述两种提示,迭代生成 1 至 20 个查询。生成查询后,我们随即调用jina-embeddings-v3
的text-matching
,从两个维度进行评估:一是衡量生成查询与原始查询的相关性,二是衡量生成查询集内部彼此的多样性。评估指标均为句向量的余弦相似度。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,以及任意一个不属于 的元素 ,都恒久满足下列不等式,那么我们称这个函数是子模的::用通俗的语言解释就是:将一个新元素添加到一个小集合里,所获得的增益,总是不小于将这个完全相同的元素添加到一个包含这个小集合的大集合里所获得的增益。理解了这一点,我们再回过头看查询生成问题,便会豁然开朗。显然,挑选查询的过程,天然地符合这种“边际效益递减”的模式:我们选择的第一个查询,覆盖了全新的语义空间,,价值最大。第二个查询覆盖了不同的方面,但不可避免地与第一个查询产生部分重叠,边际价值有所下降。我们不断加入新查询,每增加一个,它能覆盖的“新”信息就越少,因为大部分核心概念已被触及。
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):这是一种平衡策略,在大多数实际应用中都能取得稳健而出色的效果。懒贪心算法细心的读者可能已经发现,上述标准贪心算法存在一个效率瓶颈。在每一轮挑选最佳查询的迭代中,我们都一丝不苟地为所有尚未被选中的候选项重新计算了一遍边际增益。这种地毯式的计算,其实存在巨大的优化空间。为此,我们可以引入一种更精巧的优化策略——懒贪心算法(Lazy Greedy Algorithm)。它充分利用了子模函数的“边际效益递减”特性,来避免大量冗余计算。其核心洞察在于:如果一个元素 A 在第 轮的边际增益本就高于另一个元素 B,那么在下一轮(第 轮)中,由于所有已有元素的增益都在递减,A 的增益大概率仍然会领先于 B。for i in remaining: # 计算添加查询 i 的边际增益 gain = compute_marginal_gain(i, selected, embeddings,relevance_scores, alpha)
懒贪心算法的工作方式如下:初始化一个按候选项的边际增益排序的优先队列。在每一轮选择中,只从队首取出当前边际增益最高的元素,并只为这一个元素重新计算其真实的边际增益。如果重新计算后它仍然是队列里最高的,就直接选择它,完成本轮选择。否则,就根据其最新的增益值,将其插回队列的正确位置,然后重复第 2 步,考察新的队首元素。通过这种“非必要,不计算”的惰性策略,算法避免了为那些排名靠后、显然不会被选中的元素进行耗时的增益重算,实现显著的速度提升。结果现在,我们用实验结果来最终检验这套新方法的威力。我们重复之前的实验流程:首先,沿用简单的提示词,生成一个包含 20 个候选查询的初始集合。然后,我们分别运用提示词工程(直接从 1 到 20 个生成)和子模优化(从 20 个候选中挑选 k 个)两种策略,并采用相同的余弦相似度指标进行评估。import heapqdef 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]
https://las.inf.ethz.ch/submodularity/ 对于有兴趣深入探索子模优化的读者,我们强烈推荐这个网站。
AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。
鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑