概述
混合搜索在 Fess 中结合了传统的关键词搜索(BM25)与语义(向量)搜索,并通过 Rank Fusion 将两组结果合并,以生成更准确、更相关的排名。Rank Fusion 会将多个搜索器的结果整合为单一的优化排名。
在 Fess 15.8 中,语义搜索(内容分块 + 向量搜索)作为核心功能提供。启用后,语义搜索器会自动 注册到 Rank Fusion 中。有关如何配置,请参阅 语义搜索(内容分块 + 向量搜索)。
Fess 的 Rank Fusion 功能可以整合多个搜索结果, 提供更精确的搜索结果。
什么是 Rank Fusion
Rank Fusion 是一种将多个搜索算法或评分方法(例如关键词/BM25 与语义/向量搜索)的结果 组合起来,生成单一优化排名的技术。
主要优点:
结合不同算法的优势
提高搜索精度
提供多样化的搜索结果
支持的算法
Fess 支持 RRF(Reciprocal Rank Fusion)算法进行 Rank Fusion。
RRF (Reciprocal Rank Fusion)
RRF 通过对每个搜索结果中文档排名的倒数求和来计算分数。 当一个文档被多个搜索器检索到时,其各项分数会被累加。
计算公式:
k: 调整排名影响程度的常数参数(默认值:20)rank(d): 文档 d 在各搜索结果中的排名(从 0 开始)Σ: 对文档 d 出现的所有搜索器求和
Note
融合算法固定为 RRF,没有可切换到其他算法的设置。 此外,也不支持按搜索器设置权重。各搜索器的贡献会以相同的权重进行合计。 唯一能够调整排名倾向的设置是 rank.fusion.rank_constant。
配置
fess_config.properties
基本配置:
| 属性 | 默认值 | 说明 |
|---|---|---|
rank.fusion.window_size | 200 | 从各搜索器中获取用于融合的最大结果数量。必须 >= paging.search.page.max.size × 2(默认为 200);若设定值低于此最小值,将自动提升至该最小值(启动时会输出 WARN 日志)。 |
rank.fusion.rank_constant | 20 | RRF 计算公式中的常数 k。值越大,高排名与低排名结果之间的分数差越小。 |
rank.fusion.threads | -1 | 并行运行多个搜索器的固定线程池的线程数。指定 0 或以下时,将自动使用 可用 CPU 核心数 × 3 ÷ 2 + 1(由于是整数运算,小数部分会被舍去。例如:4 核 → 7,5 核 → 8)。 |
rank.fusion.score_field | rf_score | 用于存储融合后分数的结果文档字段名。 |
Note
配置的生效时机
以上 4 项配置的更改均需要重启 Fess 才能生效。从 fess_config.properties 读取的值会被缓存在 JVM 内,因此在运行过程中改写该文件不会生效。
另外,rank.fusion.window_size 仅在启动时读取一次,rank.fusion.threads 则在创建线程池时读取。线程池是在注册了 default 以外的搜索器 (例如语义搜索器)时创建的,因此在语义搜索未启用的情况下,线程池本身不会被创建。
JVM 系统属性
使用的搜索器通过 JVM 系统属性指定。 在 fess.in.sh 中添加如下内容:
如果是 fess.in.bat,则按如下方式添加:
此属性的行为如下:
以 JVM 选项形式设置,而非在
fess_config.properties中配置。键名请直接指定rank.fusion.searchers本身。其他设置中常用的-Dfess.config.或-Dfess.system.前缀形式(例如-Dfess.config.rank.fusion.searchers)不会被识别。除了 JVM 选项之外,也可以在管理界面“系统 > 常规”的“系统属性”栏中,以
rank.fusion.searchers=default,semantic_chunk这样的形式写成一行。 但该栏中的值仅在同名系统属性尚未设置时才会被应用。 因此,若已通过-D指定,则 JVM 选项优先;而要更改已经应用的值, 需要重启 Fess。default是执行标准关键词搜索的搜索器,始终可用。搜索器的名称由其实现类名去掉末尾的
Searcher,再转换为小写蛇形命名(snake_case)得来 (SemanticChunkSearcher→semantic_chunk)。核心集成的语义搜索器 (语义搜索(内容分块 + 向量搜索))注册的名称为semantic_chunk。若未指定此属性,将使用所有已注册的搜索器。若指定的名称与任何已注册搜索器均不匹配,则仅使用
default搜索器。如果您使用核心集成的语义搜索器(语义搜索(内容分块 + 向量搜索)),通常完全不需要设置此属性。结果融合仅在可用搜索器为 2 个或以上时执行。若只有 1 个搜索器可用,则不进行融合,直接返回普通搜索结果。
Warning
如果您此前在 Fess 15.7 或更早版本中使用过 fess-webapp-semantic-search 插件,可能曾被 告知要将此属性设置为 -Drank.fusion.searchers=default,semantic。该插件将其搜索器注册为 名称 semantic,这与 15.8 中引入的核心集成搜索器名称 semantic_chunk 是不同的 搜索器。如果您原样将这个 15.7 时代的设置带入 15.8,允许列表中将永远不包含 semantic_chunk,导致核心集成的语义搜索(内容分块 + 向量搜索)完全无法工作——Fess 会静默地继续返回普通关键词搜索结果(启动时会记录一条警告日志,但每次请求的排除行为本身仅以 DEBUG 级别记录)。如果您的配置中指定了 default,semantic,请移除该设置,或为其添加 semantic_chunk。详情请参阅 语义搜索(内容分块 + 向量搜索) 中的“从 15.7 及更早版本迁移”一节。
与混合搜索的集成
Rank Fusion 在结合关键词搜索与语义搜索的 混合搜索中尤为有效。要使用语义搜索,请在配置内容分块功能之后设置 content_chunker.search.enabled=true。
Warning
content_chunker.enabled 和 content_chunker.search.enabled 等 content_chunker.* 配置属于系统属性,而不是 fess_config.properties。 请将其写入 conf/system.properties,或以 -Dfess.system.content_chunker.search.enabled=true 这样的形式指定为 JVM 选项。 即使写入 fess_config.properties 也不会生效。 另外,content_chunker.search.enabled 仅在启动时评估,因此启用后需要重启 Fess。
详情请参阅 语义搜索(内容分块 + 向量搜索)。
融合结果的确认
要确认 Rank Fusion 是否确实在工作,可以查看搜索结果中附加的以下两个字段。
| 字段 | 内容 |
|---|---|
searcher | 检索到该文档的搜索器名称数组(例如 ["default", "semantic_chunk"])。若两者都包含在内,则表示该文档在关键词搜索和语义搜索中均被命中。 |
rf_score | 通过 RRF 计算出的融合后分数。字段名可通过 rank.fusion.score_field 更改。 |
这两个字段都是在搜索时动态附加的值,不会保存到索引中。 此外,默认情况下它们不会包含在 /api/v2/search 的响应中,若要确认,请在 fess_config.properties 中进行以下设置并重启 Fess:
Note
query.additional.api.response.fields 用于向“允许包含在 v2 搜索 API 响应中的字段” 许可列表中追加条目。如果追加 role 或 virtual_host 等访问控制用字段, 访问控制信息将暴露在搜索 API 的响应中,因此请勿追加这类字段。
对命中数量的影响
执行 Rank Fusion 时,返回的总命中数量并非直接使用主搜索器(注册在首位的 default 搜索器)的数量,而是按如下方式进行修正:
修正值是指:在融合后的前 window_size ÷ 2 条结果中,未包含在主搜索器前 window_size ÷ 2 条结果内的文档数量。也就是说,命中数量会增加仅由语义搜索找到的 文档数量。 因此,即使是相同的查询,启用与不启用混合搜索时的命中数量也可能不同。
另外,当主搜索器的总命中数量以概算值(下限值)返回时,不会进行此修正。
使用示例
基本混合搜索
通过关键词搜索计算 BM25 分数
通过语义搜索计算向量相似度
使用 RRF 融合两种结果
生成最终排名
搜索流程:
性能注意事项
内存使用
由于需要保留多个搜索结果,内存使用量会增加。
可使用
rank.fusion.window_size限制融合目标的最大数量。主搜索器(排在首位的default搜索器)最多获取window_size条结果,其他搜索器各获取window_size ÷ 搜索器数量条结果(搜索器数量是包含主搜索器在内的总数,除法会向下取整)。例如,当有 2 个搜索器(
default和semantic_chunk)且window_size=200时,主搜索器获取 200 条、语义搜索器获取 100 条,因此最多会保留 300 条文档。
Warning
rank.fusion.window_size 不能低于 paging.search.page.max.size × 2。 当 paging.search.page.max.size 为默认值 100 时,下限即为 200,这与 rank.fusion.window_size 的默认值相同。也就是说,在默认配置下无法将 window_size 设置为小于默认值。即使设置了更小的值,启动时也会输出 WARN 日志并将其提升至 200。 若要实际减小该值,需要先降低 paging.search.page.max.size,但这同时也会降低搜索界面 和 API 中每页可请求的最大结果数量。
处理时间
由于需要执行多次搜索,响应时间会增加。
使用
rank.fusion.threads设置并行执行的线程数。
Note
搜索器的执行没有设置超时。如果存在不返回响应的搜索器,搜索请求将一直等待其完成。
搜索器故障时的行为
当任一搜索器因异常而失败时,该搜索器的结果会被视为空,在输出 WARN 日志之后, 仅使用其余搜索器的结果继续进行融合。搜索请求本身不会变成错误。
但查询语法错误(InvalidQueryException)和分页上限超出 (ResultOffsetExceededException)除外,它们会直接作为错误返回。 此外,在不进行融合的深层页面(起始位置 × 2 大于等于 rank.fusion.window_size 的位置), 主搜索器中发生的异常会直接作为搜索请求的错误返回。
语义搜索器在无法连接到嵌入提供商或嵌入处理失败时,会返回空结果。 这种情况下同样不会产生错误,而是仅返回关键词搜索的结果。
故障排除
搜索结果与预期不符
症状:Rank Fusion 后的结果与预期不符
检查事项:
确认
searcher字段(请参阅“融合结果的确认”)。如果所有文档都仅为["default"],则说明语义搜索器没有返回结果。确认语义搜索是否被跳过。除了包含搜索语法(如
":AND等)的查询之外, 在通过标签、排序、分面进行筛选,以及位置信息搜索、相似文档搜索时,语义搜索器也不会返回 结果,仅返回关键词搜索的结果。 跳过条件的详细信息请参阅 语义搜索(内容分块 + 向量搜索)。分别确认各搜索类型的结果
调整
rank.fusion.rank_constant的值在翻页较深的页面(
起始位置 × 2大于等于rank.fusion.window_size的位置,默认情况下 为第 101 条之后)不会执行融合,仅使用主搜索器进行搜索。若希望在更多页面上获得融合结果, 请增大rank.fusion.window_size。
搜索缓慢
症状:启用 Rank Fusion 时搜索变慢
解决方法:
调整
rank.fusion.threads:减小
rank.fusion.window_size。但由于不能低于下限 (paging.search.page.max.size × 2),在默认配置下需要将以下两项成对设置:请注意,每页可请求的最大结果数量也会随之下降。设置后需要重启。
内存不足
症状:发生 OutOfMemoryError
解决方法:
按照“搜索缓慢”中的相同步骤减小
rank.fusion.window_size增加 JVM 堆大小
参考信息
语义搜索(内容分块 + 向量搜索) - 语义搜索(内容分块)的配置
脚本概述 - 脚本概述
搜索相关配置 - 高级搜索设置
AI 搜索(RAG)与 LLM 集成概述 - LLM 集成指南(语义搜索)