RAG 混合检索深度解析:BM25 + 向量检索 + RRF 算法,让 AI 真正"找到"你要的内容

人工智能RAG混合检索LangChain4jSpring BootBM25
person smallyoungcalendar_today 2026年4月25日

你是否遇到过这样的困境:明明知识库里有"iPhone 14 参数",用户一搜,AI 却给出了"智能手机发展历史"的答案?这不是大模型的问题,是检索出了问题。本文将带你彻底理解混合检索(Hybrid Retrieval)的设计哲学,以及 RRF 算法如何公平地融合两路结果,并通过 Spring Boot + LangChain4j 的完整代码,让你当天就能落地。

📌 适合人群:了解 RAG 基础概念的 Java 后端开发者、AI 工程落地工程师

text
# 混合检索 Hybrid RAG
## 为什么需要混合
- 向量检索的语义盲点
- 专业术语/编号精确匹配失败
- 单一策略的天花板
## BM25 的原理
- 词频 TF 与饱和函数
- 逆文档频率 IDF
- 文档长度归一化
## 向量检索的原理
- Embedding 语义编码
- 余弦相似度计算
- 擅长语义泛化
## RRF 融合算法
- 不依赖原始分数
- 排名倒数加权
- 平滑常数 k=60
## 代码落地
- HybridRetriever 构建
- Spring Boot 集成配置
- 参数调优策略

关于本文档

本文围绕 RAG 系统中最关键的检索层展开,从单路检索的缺陷出发,逐步构建混合检索的完整理解,最终通过可运行的 Java 代码完成落地。

  • ✅ 理解纯向量检索在专业术语场景下的核心缺陷
  • ✅ 掌握 BM25 算法的工作原理(TF、IDF、文档长度归一化)
  • ✅ 理解 RRF 算法如何做到不依赖分数而只依赖排名的公平融合
  • ✅ 使用 Spring Boot + LangChain4j 构建完整混合检索器
  • ✅ 掌握生产环境中 topK 与 rrfK 的调参策略

1. 纯向量检索为什么会"答非所问"

1.1 向量检索的工作方式

向量检索(Dense Retrieval)是目前 RAG 系统的主流做法。它的核心思想是:将文档和用户问题都转换成高维数值向量(Embedding),然后通过计算两个向量之间的余弦相似度,找出语义上最接近的文档片段。

这种方式在语义泛化场景下效果出色。比如你问"如何让身体更健康",即使知识库里写的是"养生食谱"和"运动方案",向量检索也能把它们找出来——因为它们在语义空间里距离很近。

1.2 向量检索的"词汇鸿沟"问题

向量检索的问题在于,它擅长理解意思相近的内容,却不擅长精确匹配特定词汇。当用户搜索:

  • iPhone 14 Pro Max → 可能返回所有手机相关文档,而非这款机型的具体参数
  • 订单号 ORD-20241201-9527 → 向量模型根本无法理解这串编号的独特性
  • Apache Kafka 2.8.0 版本变更 → 版本号"2.8.0"在语义空间里和"2.7.1"几乎一样近

IMPORTANT

向量模型学到的是语义相似度,而非字符精确性。对于专有名词、产品编号、版本号、人名,纯向量检索的召回率会显著下降,这是结构性缺陷,不是调参能解决的。

查询类型向量检索效果BM25 效果
语义问答("如何提升免疫力")✅ 优秀⚠️ 一般,依赖关键词是否出现
同义词泛化("汽车" vs "小轿车")✅ 优秀❌ 较差,字面不匹配则召回失败
精确专有名词("GPT-4o")⚠️ 一般✅ 优秀,精确字符匹配
产品编号/型号("ORD-9527")❌ 较差✅ 优秀
代码方法名("getUserById")❌ 较差✅ 优秀

1.3 解决思路:两路召回,取长补短

既然每种检索各有所长,最直觉的解法就是同时用两种方式检索,然后把结果合并。这就是**混合检索(Hybrid Retrieval)**的核心思想。

2. BM25:经典关键词检索的核心原理

2.1 BM25 是什么

BM25(Best Match 25)是信息检索领域最经典的排序算法之一,也是 Elasticsearch、Lucene 等搜索引擎的默认排序算法。它的名字里的"25"来自算法的第 25 次迭代版本。

一句话类比:BM25 就像图书馆管理员。你说要找"苹果手机",他会扫描所有书的索引,数一数每本书提到"苹果"和"手机"多少次,优先推荐那些反复提到这两个词、且整本书不太厚(避免被长书稀释)的书籍。

BM25 的评分综合三个核心因子:

因子全称作用举例
TFTerm Frequency(词频)词在文档中出现越多,相关性越高"苹果"出现 5 次 > 出现 1 次
IDFInverse Document Frequency(逆文档频率)词在所有文档中越罕见,越有辨别力"苹果"比"的"更有价值
文档长度归一化Document Length Normalization长文档中词出现多次不一定比短文档更相关防止长文档"霸榜"

2.2 BM25 公式详解

BM25 的核心得分公式如下:

BM25(q,d)=tqIDF(t)f(t,d)(k1+1)f(t,d)+k1(1b+b|d|avgdl)

参数说明:

参数含义典型默认值
f(t, d)词 t 在文档 d 中的出现次数
|d|文档长度(词数)
avgdl语料库中文档的平均长度
k1词频饱和参数(防止高频词无限加分)1.2 ~ 2.0
b文档长度归一化系数0.75

NOTE

k1 参数引入了"词频饱和"效应:一个词出现 1 次和出现 100 次,给文档带来的加分不是线性增长的,而是越来越慢趋向饱和。这避免了某个词反复堆砌就能刷高分的问题。

2.3 BM25 vs 向量检索:互补而非替代

3. RRF 算法:公平地融合两路结果

3.1 分数融合的难题

当 BM25 和向量检索各返回了 20 条结果后,我们面临一个棘手的问题:两路的分数不在同一个量纲上

  • BM25 返回的得分可能是 12.78.35.1……(无上限)
  • 向量检索的余弦相似度是 0.930.870.82……(在 0~1 之间)

直接把这两个分数加权相加(如 0.5 × BM25分 + 0.5 × 向量分)会有严重问题:BM25 分数的绝对量级远大于余弦相似度,导致向量检索的贡献被完全淹没,且需要大量调参和归一化工作。

3.2 RRF 算法:只看排名,不看分数

RRF(Reciprocal Rank Fusion,倒数排名融合)是由滑铁卢大学和 Google 联合研究提出的算法,其核心思想极其优雅:

不管你的原始分数是多少,我只关心你在各自榜单里排第几名。

RRF 对每个文档 d 的最终得分计算公式为:

RRF_score(d)=rR1k+rankr(d)
符号含义
R所有检索结果列表的集合(这里是 BM25 列表和向量检索列表)
rank_r(d)文档 d 在列表 r 中的排名(从 1 开始)
k平滑常数,业界标准值为 60,防止低排名文档得分趋近于 0

3.3 用一个例子彻底理解 RRF

假设检索"苹果14 售价",两路各召回 5 条结果:

排名BM25 结果向量检索结果
第 1 名文档A(iPhone 14 参数)文档C(苹果产品历史)
第 2 名文档B(手机价格比较)文档A(iPhone 14 参数)
第 3 名文档C(苹果产品历史)文档D(苹果公司财报)
第 4 名文档D(苹果公司财报)文档B(手机价格比较)
第 5 名文档E(Android 手机推荐)文档E(Android 手机推荐)

使用 k=60 计算各文档的 RRF 分:

文档BM25 排名向量排名RRF 得分最终排名
文档A121/(60+1) + 1/(60+2) = 0.0321🥇 第 1
文档C311/(60+3) + 1/(60+1) = 0.0321🥈 第 1(同分)
文档B241/(60+2) + 1/(60+4) = 0.0317🥉 第 3
文档D431/(60+4) + 1/(60+3) = 0.0317第 4
文档E551/(60+5) + 1/(60+5) = 0.0308第 5

TIP

文档 A(iPhone 14 参数)在两路结果中都排名靠前,RRF 算法将其综合排名拉到最高,完美体现了"两路都认可的文档更可信"的直觉。k=60 的作用是让第 1 名和第 2 名的得分差距适中,防止排名极度集中。

4. Spring Boot + LangChain4j 实现混合检索

4.1 项目依赖配置

首先在 pom.xml 中引入必要依赖:

xml
<properties>
    <langchain4j.version>1.13.1</langchain4j.version>
</properties>

<dependencies>
    <!-- Spring Boot 基础 Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- LangChain4j 核心(1.x 正式版) -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j</artifactId>
        <version>${langchain4j.version}</version>
    </dependency>

    <!-- OpenAI 模型实现 -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai</artifactId>
        <version>${langchain4j.version}</version>
    </dependency>

    <!-- 内置 Embedding 模型(本地语义检索) -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId>
        <version>${langchain4j.version}</version>
    </dependency>

    <!-- (可选) LangChain4j Spring Boot Starter -->
    <!-- 如果不使用 Starter,需手动配置 Bean,见下文 -->
    <!--
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-spring-boot-starter</artifactId>
        <version>${langchain4j.version}</version>
    </dependency>
    -->
</dependencies>

4.2 构建混合检索器(核心代码)

第一步:实现 BM25 关键词检索器

java
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.rag.content.Content;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.query.Query;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 基于 BM25 算法的内存关键词检索器
 * <p>
 * langchain4j 1.x 未内置 BM25 ContentRetriever,此类手动实现。
 * 中文场景建议将 tokenize() 替换为 jieba 或 HanLP 分词器。
 * 若已集成 Elasticsearch,可直接调用其 BM25 接口替换本类。
 */
public class BM25ContentRetriever implements ContentRetriever {

    private static final double K1 = 1.5;   // 词频饱和参数
    private static final double B  = 0.75;  // 文档长度归一化系数

    private final List<TextSegment> corpus = new ArrayList<>();
    private final int topK;

    public BM25ContentRetriever(int topK) {
        this.topK = topK;
    }

    /**
     * 向 BM25 索引注册文档(文档入库时同步调用)
     */
    public void index(TextSegment segment) {
        corpus.add(segment);
    }

    @Override
    public List<Content> retrieve(Query query) {
        if (corpus.isEmpty()) return Collections.emptyList();

        String[] queryTerms = tokenize(query.text());
        double avgdl = corpus.stream()
                .mapToInt(s -> tokenize(s.text()).length)
                .average()
                .orElse(1.0);

        return corpus.stream()
                .map(seg -> Map.entry(seg, bm25Score(queryTerms, seg.text(), avgdl)))
                .filter(e -> e.getValue() > 0)
                .sorted(Map.Entry.<TextSegment, Double>comparingByValue().reversed())
                .limit(topK)
                .map(e -> Content.from(e.getKey()))
                .collect(Collectors.toList());
    }

    private double bm25Score(String[] queryTerms, String docText, double avgdl) {
        String[] docTerms = tokenize(docText);
        Map<String, Long> tf = Arrays.stream(docTerms)
                .collect(Collectors.groupingBy(t -> t, Collectors.counting()));

        double score = 0.0;
        int N = corpus.size();

        for (String term : queryTerms) {
            long df = corpus.stream()
                    .filter(s -> s.text().toLowerCase().contains(term))
                    .count();
            if (df == 0) continue;

            // IDF:文档频率越低,辨别力越强
            double idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);

            // TF 饱和:词频越高加分越慢,防止词语堆砌
            double termFreq = tf.getOrDefault(term, 0L);
            double numerator   = termFreq * (K1 + 1);
            double denominator = termFreq + K1 * (1 - B + B * (docTerms.length / avgdl));

            score += idf * (numerator / denominator);
        }
        return score;
    }

    /** 简单空格/标点分词;中文场景请替换为 jieba / HanLP */
    private String[] tokenize(String text) {
        return text.toLowerCase().split("[\\s\\p{Punct}]+");
    }
}

第二步:实现 RRF 融合检索器

java
import dev.langchain4j.rag.content.Content;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.query.Query;

import java.util.*;
import java.util.stream.Collectors;

/**
 * RRF(倒数排名融合)多路检索融合器
 * <p>
 * 对任意数量的 ContentRetriever 结果按排名加权融合,不依赖原始分数。
 * RRF 得分公式:score(d) = Σ 1 / (rrfK + rank_r(d))
 */
public class RrfFusionContentRetriever implements ContentRetriever {

    private final List<ContentRetriever> retrievers;
    private final int rrfK; // 平滑常数,业界标准值 60

    /**
     * @param retrievers 多路检索器列表(如 BM25 + 向量)
     * @param rrfK       RRF 平滑常数(值越大,低排名文档贡献越平稳)
     */
    public RrfFusionContentRetriever(List<ContentRetriever> retrievers, int rrfK) {
        this.retrievers = retrievers;
        this.rrfK = rrfK;
    }

    @Override
    public List<Content> retrieve(Query query) {
        // 1. 依次调用各路检索器
        List<List<Content>> allResults = retrievers.stream()
                .map(r -> r.retrieve(query))
                .collect(Collectors.toList());

        // 2. 以文本内容为 Key,计算并累加 RRF 得分
        Map<String, Double>  rrfScores  = new LinkedHashMap<>();
        Map<String, Content> contentMap = new LinkedHashMap<>();

        for (List<Content> results : allResults) {
            for (int rank = 0; rank < results.size(); rank++) {
                Content content = results.get(rank);
                String  key     = content.textSegment().text(); // 文本去重
                double  score   = 1.0 / (rrfK + rank + 1);     // rank 从 0 开始

                rrfScores.merge(key, score, Double::sum);
                contentMap.putIfAbsent(key, content);
            }
        }

        // 3. 按 RRF 得分降序返回融合结果
        return rrfScores.entrySet().stream()
                .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
                .map(e -> contentMap.get(e.getKey()))
                .collect(Collectors.toList());
    }
}

第三步:Spring Configuration — 组装混合检索 Bean

java
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
public class HybridRetrieverConfig {

    private static final int BM25_TOP_K   = 20;
    private static final int VECTOR_TOP_K = 20;
    private static final int RRF_K        = 60;

    @Value("${langchain4j.open-ai.api-key:demo}")
    private String apiKey;

    /**
     * 手动定义 ChatModel Bean(非 Starter 模式)
     */
    @Bean
    public ChatModel chatModel() {
        return OpenAiChatModel.builder()
                .apiKey(apiKey)
                .build();
    }

    /**
     * 手动定义 EmbeddingModel Bean(本地模型)
     */
    @Bean
    public EmbeddingModel embeddingModel() {
        return new AllMiniLmL6V2EmbeddingModel();
    }

    /**
     * 手动定义 EmbeddingStore Bean(内存存储)
     */
    @Bean
    public EmbeddingStore<TextSegment> embeddingStore() {
        return new InMemoryEmbeddingStore<>();
    }

    /**
     * 构建混合检索器(BM25 + 向量语义 + RRF 融合)
     * <p>
     * langchain4j 1.x 已移除 HybridRetriever,
     * 改为手动组合两路 ContentRetriever 并通过 RrfFusionContentRetriever 融合。
     *
     * @param embeddingModel 向量化模型(将文本转为语义向量)
     * @param embeddingStore 向量数据库(存储 & 检索向量)
     */
    @Bean
    public ContentRetriever hybridContentRetriever(
            EmbeddingModel embeddingModel,
            EmbeddingStore<TextSegment> embeddingStore) {

        // ── 向量语义检索器 ─────────────────────────────
        // EmbeddingStoreContentRetriever 是 langchain4j 1.x 的标准向量检索实现
        ContentRetriever vectorRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .maxResults(VECTOR_TOP_K)    // 向量侧召回 Top-20 候选
                .build();

        // ── BM25 关键词检索器 ──────────────────────────
        // langchain4j 1.x 不内置 BM25,使用自定义实现
        // 文档入库时需同步调用 bm25Retriever.index(segment) 建立索引
        BM25ContentRetriever bm25Retriever = new BM25ContentRetriever(BM25_TOP_K);

        // ── RRF 融合:统一暴露为 ContentRetriever Bean ─
        // RrfFusionContentRetriever 内部完成:
        //   1. 并发调用两路检索器
        //   2. 按排名倒数加权(公式:1 / (rrfK + rank))
        //   3. 去重 + 降序排列,返回最终候选列表
        return new RrfFusionContentRetriever(
                List.of(bm25Retriever, vectorRetriever),
                RRF_K
        );
    }
}

4.3 在 RAG Service 中使用混合检索器

java
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.service.AiServices;
import org.springframework.stereotype.Service;

@Service
public class KnowledgeBaseService {

    private final KnowledgeAssistant assistant;

    /**
     * langchain4j 1.13.1+:使用 ChatModel 替代 ChatLanguageModel
     */
    public KnowledgeBaseService(
            ChatModel chatModel,
            ContentRetriever hybridContentRetriever) {

        this.assistant = AiServices.builder(KnowledgeAssistant.class)
                .chatModel(chatModel)
                .contentRetriever(hybridContentRetriever)
                .build();
    }

    /**
     * 知识库问答入口
     * @param userQuestion 用户问题
     * @return AI 生成的答案(基于混合检索的上下文)
     */
    public String ask(String userQuestion) {
        return assistant.answer(userQuestion);
    }
}

NOTE

与旧版的关键差异:0.x 中需要将 HybridRetriever 手动包装为 ContentRetriever 的 lambda;1.x 中 hybridContentRetriever Bean 本身就实现了 ContentRetriever 接口,Spring 可直接按类型注入。

4.4 定义 AI Service 接口

java
import dev.langchain4j.service.SystemMessage;

/**
 * 知识库问答 AI Service
 * LangChain4j 会自动将混合检索结果注入为上下文
 */
public interface KnowledgeAssistant {

    @SystemMessage("""
            你是一个专业的知识库问答助手。
            请严格根据提供的上下文内容来回答用户问题。
            如果上下文中没有相关信息,请直接告知用户,不要编造答案。
            """)
    String answer(String userQuestion);
}

4.5 REST API 层(完整接入)

java
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/knowledge")
public class KnowledgeController {

    private final KnowledgeBaseService knowledgeBaseService;

    public KnowledgeController(KnowledgeBaseService knowledgeBaseService) {
        this.knowledgeBaseService = knowledgeBaseService;
    }

    /**
     * 问答接口
     * POST /api/knowledge/ask
     * Body: {"question": "苹果14的屏幕分辨率是多少?"}
     */
    @PostMapping("/ask")
    public AnswerResponse ask(@RequestBody QuestionRequest request) {
        String answer = knowledgeBaseService.ask(request.question());
        return new AnswerResponse(answer);
    }

    // 请求/响应 DTO(Java 16+ record)
    record QuestionRequest(String question) {}
    record AnswerResponse(String answer) {}
}

5. 混合检索架构全局视图

6. 最佳实践与调参指南

6.1 topK 与 rrfK 参数如何设置

这两个参数是混合检索中最常调整的,理解其含义能避免踩坑:

参数作用过小的问题过大的问题推荐起点
bm25TopKBM25 侧召回候选数量漏掉关键词相关文档噪声增加,后续重排负担重20
vectorTopK向量侧召回候选数量漏掉语义相关文档同上20
rrfKRRF 平滑常数结果过度集中于两路都排名 #1 的文档低排名噪声文档得分变高60(业界标准)

TIP

bm25TopKvectorTopK 的值建议设为最终返回文档数的 4~5 倍。例如你想最终给大模型 5 条上下文,那两路各召回 20 条,经 RRF 融合后取前 5,这样有足够的候选池来保证质量。

6.2 常见错误:不做去重直接合并

错误做法:直接将两路结果简单拼接,不经过 RRF 或去重

java
// ❌ 错误:两路各 20 条,合并后 40 条全塞给大模型
List<TextSegment> bm25Results = bm25.retrieve(query);
List<TextSegment> vectorResults = vector.retrieve(query);
List<TextSegment> combined = new ArrayList<>();
combined.addAll(bm25Results);
combined.addAll(vectorResults); // 可能包含大量重复文档,且未按质量排序

正确做法:通过 hybridContentRetrieverContentRetriever Bean)统一调用,RRF 自动处理去重与排序

java
// ✅ 正确:hybridContentRetriever 内部完成两路召回 + RRF 融合 + 去重
List<Content> results = hybridContentRetriever.retrieve(Query.from(userQuery));
// results 已按 RRF 分数降序排列,无重复文档

6.3 常见问题排查表

问题现象可能原因解决方案
专业术语始终召回不到BM25 分词器不支持中文术语接入 IK 分词器或使用 jieba 预处理
召回结果全是语义相关但不精确bm25TopK 设置过低或 BM25 索引未初始化检查 .init() 是否调用,适当增大 bm25TopK
两路结果高度重叠,混合意义不大知识库内容同质化,或向量模型欠训练评估语料多样性,换用更强的 Embedding 模型
RRF 后结果与预期偏差大rrfK 设置不当在 20~80 之间调参,较小的 k 让前排文档优势更大

WARNING

混合检索虽然提升了召回率,但并不等于精排。生产环境建议在混合检索之后再接一层重排序(Reranker),使用 Cross-Encoder 模型对召回的 Top-20 候选进行精细打分,最终取 Top-5 送入大模型,可以进一步提升答案质量。

7. 三种检索策略横向对比

对比维度纯向量检索纯 BM25 检索混合检索(推荐)
语义泛化能力⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
专有名词精确匹配⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
系统复杂度⭐⭐⭐⭐⭐⭐⭐(适中)
索引维护成本向量库倒排索引两者均需
2026 年生产推荐❌ 不建议单独使用❌ 不建议单独使用✅ 强烈推荐

IMPORTANT

何时必须用混合检索?

  • ✅ 知识库中有产品型号、订单编号、API 名称等精确标识符
  • ✅ 用户会搜索人名、地名、特定版本号
  • ✅ 知识库文档来源多样,语义分布广
  • ❌ 如果知识库非常小(< 100 条)且查询均为语义问题,纯向量检索已足够

8. 总结

核心概念一句话解释
BM25基于词频(TF)和逆文档频率(IDF)的关键词精确匹配算法,擅长专有名词召回
向量检索将文本转为 Embedding 向量后计算语义相似度,擅长语义泛化
混合检索同时运行 BM25 和向量检索两路召回,各取所长
RRF 算法不依赖原始分数,仅通过各榜单排名的倒数加权来公平融合多路结果
rrfK=60RRF 平滑常数的业界标准值,平衡顶部排名优势与尾部文档贡献
HybridRetrieverlangchain4j 0.x 提供的混合检索器(1.x 已移除);1.x 中改用 ContentRetriever + RrfFusionContentRetriever 手动组合
ChatModellangchain4j 1.13.1+ 推荐的聊天模型接口(替代旧版的 ChatLanguageModel

TIP

推荐学习路径

  1. 先用纯向量检索搭起 RAG 基础框架,体验其优缺点
  2. 引入 BM25,对比两者在专业术语场景下的召回差异
  3. 接入 HybridRetriever + RRF,观察综合召回率的提升
  4. 在混合检索之后再接 Reranker 重排模型,追求极致的答案质量

9. 参考资料

核心论文

论文作者/机构年份主要贡献
Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning MethodsGordon V. Cormack 等,滑铁卢大学 / Google2009提出 RRF 算法,证明其优于单一排序方法
Okapi BM25: a Non-Binary Model of Document Indexing and RetrievalRobertson 等1994BM25 算法原始论文

推荐资源


关注我们

更多 AI 工具实战内容

关注微信公众号 小杨技术笔记,第一时间获取大模型、Agent、RAG 等前沿技术解析与实战分享。

# AI 智能体# RAG 应用# 技术复盘
小杨技术笔记
扫码即刻关注