RAG 文档切分、索引优化与 Reranker 学习笔记

RAG 文档切分、索引优化与 Reranker 学习笔记

记录一次询问GPT的完整过程 :从业务场景到工程落地的一次完整梳理


前言:这次讨论在解决什么问题

这次对话围绕 RAG 系统中的三个关键问题展开:

  1. 文档到底应该如何切分?
  2. 索引应该如何优化?
  3. 为什么还需要 Reranker,以及 Reranker 通常有哪些做法?

一开始讨论的是文档切分和索引优化,后来进一步引出了 RAG 检索链路中的排序问题,也就是:即使向量检索能找出一批看起来相关的 chunk,系统仍然需要判断哪些 chunk 最能回答用户问题。于是自然进入了 Reranker 的讨论。

这份笔记不是简单罗列概念,而是按照这次对话中的思考路径来整理:

text 复制代码
文档切分与索引优化
  ↓
为什么需要 Reranker
  ↓
Reranker 是什么
  ↓
Reranker 通常有哪些做法
  ↓
如何把整个 RAG 检索链路串起来

一、文档切分到底应该怎么做

最开始关注的问题是:

在文档切分和索引优化部分,如何具体切分文档?如何根据不同业务场景切分?以及如何做索引优化?

这个问题的核心并不是简单问 chunk_size 应该设成多少,而是在问:

RAG 系统中的文档切分,如何从业务问题出发,设计出真正适合检索和问答的知识单元?


1. 为什么不能直接按固定长度切分

很多 RAG 初学者会直接采用固定长度切分,例如:

text 复制代码
每 500 tokens 切一块,overlap 设置为 50 tokens

这种方式实现简单,但是问题也很明显:它不理解文档结构,也不理解业务语义。

例如一段退款规则:

text 复制代码
退款条件在 chunk A
退款例外情况在 chunk B
退款到账时间在 chunk C

如果用户问:

text 复制代码
退款多久能到账?

系统可能只召回 chunk C,却丢失了前面的退款条件和审核规则,最终导致回答不完整。

所以固定长度切分只能作为兜底方案,不应该作为所有业务文档的默认最优方案。


2. 文档切分的本质:让 chunk 粒度匹配用户问题粒度

文档切分的核心不是"把文档切碎",而是:

让每一个 chunk 尽可能成为一个独立、完整、可被检索的问题答案单元。

也就是说,chunk 的大小应该由用户问题决定,而不是由文档长度决定。

例如:

用户问题类型 合适的 chunk 粒度
怎么申请退款? 一个 FAQ 问答对
合同里甲方有哪些义务? 一个或多个合同条款
这个接口怎么调用? 一个完整接口说明
这个 Java 方法为什么报错? 一个方法体 + 类名 + 上下文
推荐几部高分国产剧情片 一部电影作为一个实体 chunk
这篇论文方法是什么? 一个章节或语义段

所以切分之前,应该先问:

text 复制代码
用户通常会怎么问?
回答这个问题最小需要多少上下文?
这个上下文在原文中的自然边界是什么?

3. 切分文档时应该先看结构,再看语义,最后才看长度

比较合理的文档处理流程是:

text 复制代码
原始文档
  ↓
文本清洗 / OCR / 表格解析 / Markdown 解析 / HTML 解析
  ↓
识别结构:标题、章节、段落、表格、代码块、列表、页码
  ↓
按业务语义切分
  ↓
控制 chunk_size 和 overlap
  ↓
补充 metadata
  ↓
embedding + index

换句话说,切分顺序应该是:

text 复制代码
先看结构
再看语义
最后才看长度

不推荐这样做:

text 复制代码
拿到文档 → 直接每 500 字切一次 → embedding → 入库

更推荐这样做:

text 复制代码
拿到文档
  ↓
识别标题、章节、表格、代码块、条款、业务实体
  ↓
按自然语义边界切分
  ↓
过长的部分再用长度或语义切分兜底

4. 常见切分方式对比

4.1 固定长度切分

固定长度切分就是按字符数或 token 数硬切。

例如:

text 复制代码
chunk_size = 500
chunk_overlap = 50

优点:

  • 实现简单
  • 成本低
  • 对无明显结构的长文本比较方便

缺点:

  • 容易切断语义
  • 不理解标题、条款、表格、代码块
  • 可能导致召回内容不完整

适合:

  • 日志文本
  • 流水记录
  • 无结构长文本
  • 粗粒度全文搜索

不适合:

  • 合同
  • 政策
  • FAQ
  • 技术文档
  • 代码
  • Excel / 表格数据

4.2 RecursiveCharacterTextSplitter 递归切分

递归切分的思路是:优先按照更自然的分隔符切分,如果切出来的块仍然太长,再继续使用更细粒度的分隔符。

例如:

text 复制代码
先按章节切
如果太长,再按段落切
如果还太长,再按句子切
如果还太长,最后按字符切

中文场景下可以设置类似分隔符:

python 复制代码
from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=600,
    chunk_overlap=80,
    separators=[
        "\n## ",
        "\n### ",
        "\n\n",
        "。", "!", "?", ";",
        "\n",
        " ",
        ""
    ]
)

它适合:

  • 普通知识库
  • 博客文章
  • 产品说明
  • 课程讲义
  • Markdown 文档

这个方法比固定长度切分更自然,因为它会尽量保留段落和句子的完整性。


4.3 按标题和章节切分

对于结构明显的文档,标题和章节通常就是最自然的切分边界。

例如:

text 复制代码
1. 产品介绍
2. 登录流程
3. 权限说明
4. 常见问题

可以先按一级、二级、三级标题切成 parent chunk,再对过长的章节继续切成 child chunk。

结构可以是:

text 复制代码
Parent Chunk:
  第 3 章:权限说明

Child Chunk 1:
  3.1 普通用户权限

Child Chunk 2:
  3.2 管理员权限

Child Chunk 3:
  3.3 内部成员权限

检索时可以采用 small-to-big retrieval:

text 复制代码
用小 chunk 做精确检索
返回时带上 parent chunk 或相邻 chunk

这样做的好处是:

text 复制代码
小 chunk 检索更精准
大 chunk 回答更完整

适合:

  • 技术文档
  • 项目文档
  • 产品手册
  • 安装说明
  • 课程讲义
  • Markdown / HTML 文档

4.4 语义切分 Semantic Chunking

语义切分不是按固定长度切,而是根据句子之间的语义相似度来判断哪里应该断开。

例如:

text 复制代码
A 段讲登录
B 段讲注册
C 段讲密码重置
D 段讲支付

语义切分可能会把登录、注册、密码重置放在一组,把支付单独切出去。

适合:

  • 长文章
  • 学术论文
  • 访谈稿
  • 会议纪要
  • 法律解释文档
  • 业务分析报告

缺点:

  • 成本更高
  • 切分速度更慢
  • 有时会切出过大的 chunk
  • 需要设置最大 token 限制

比较稳妥的做法是:

text 复制代码
先按标题 / 段落粗切
再对长段落做语义切分
最后加最大 token 限制

4.5 Sentence Window 切分

Sentence Window 的思路是:

text 复制代码
embedding 时只嵌入一句话
生成答案时带上这句话前后的上下文

例如原文:

text 复制代码
句子 1:用户可以上传资源。
句子 2:只有管理员可以删除资源。
句子 3:普通用户只能下载公开资源。
句子 4:内部成员可以访问本州资源。

索引时:

text 复制代码
chunk = 句子 2
metadata.window = 句子 1 + 句子 2 + 句子 3

检索时用句子 2 精准命中,回答时把窗口一起交给 LLM。

适合:

  • FAQ
  • 客服知识库
  • 制度问答
  • 细粒度事实查询

它解决的是:

text 复制代码
小粒度检索精准,但上下文不足

4.6 表格 / Excel 数据切分

表格和 Excel 不能简单当成普通长文本切。

例如电影数据:

text 复制代码
电影名 | 类型 | 地区 | 上映时间 | 评分 | 简介 | 演员 | 评论

对于这种数据,更合理的方式是:

text 复制代码
一行 = 一个业务实体 = 一个 chunk

例如:

text 复制代码
chunk_id: movie_001
movie_name: 霸王别姬
genre: 剧情
score: 9.6
actors: 张国荣, 巩俐
summary: ...

如果简介、剧情、评论很长,可以继续拆成:

text 复制代码
movie_001_summary
movie_001_plot_1
movie_001_plot_2
movie_001_review_summary

但是它们要共享同一组 metadata:

text 复制代码
movie_id
movie_name
genre
year
country
score

也就是说,Excel / 数据库类 RAG 的重点不是 chunk_size,而是:

text 复制代码
实体建模 + 字段 metadata + 过滤检索

二、不同业务场景下应该如何切分


1. 客服 FAQ / 售后知识库

典型问题:

text 复制代码
怎么退款?
怎么修改手机号?
为什么订单被取消?
会员怎么续费?

推荐切分:

text 复制代码
一个问题 + 一个答案 = 一个 chunk

示例:

text 复制代码
Q: 如何申请退款?
A: 用户可以在订单详情页点击申请退款...

推荐配置:

项目 建议
chunk 粒度 一个问答对
chunk_size 100 - 300 tokens
overlap 基本不需要,或者很小
检索方式 BM25 + Vector + Reranker

推荐 metadata:

text 复制代码
product
business_line
question_type
user_type
version
effective_date

客服场景里 BM25 很重要,因为用户的问题中经常有精确业务词,例如:

text 复制代码
退款
发票
手机号
订单号
会员

2. 企业制度 / 政策 / 合同文档

典型问题:

text 复制代码
试用期离职工资怎么算?
这个合同里违约责任是什么?
甲方有哪些义务?
年假规则是什么?

推荐切分:

text 复制代码
按章、节、条、款切分

例如:

text 复制代码
第二章 请假制度
  第 5 条 年假规则
    5.1 员工连续工作满一年...
    5.2 年假不可折现...

推荐配置:

项目 建议
chunk 粒度 一个条款 / 相邻条款
chunk_size 300 - 800 tokens
overlap 50 - 120 tokens
检索方式 条款级向量索引 + BM25 + parent-child retrieval

推荐 metadata:

text 复制代码
doc_type
chapter
article_no
clause_no
effective_date
company
department
version
page_no

合同和制度类文档特别需要上下文扩展,因为一个问题可能涉及多个条款:

text 复制代码
违约责任条款
赔偿条款
解除合同条款
争议解决条款

所以推荐:

text 复制代码
先召回条款级 chunk
再扩展同一章节下的相邻条款
最后让 LLM 基于证据回答

3. 技术文档 / API 文档 / 开发手册

典型问题:

text 复制代码
这个接口怎么调用?
这个参数是什么意思?
如何配置 Redis?
这个错误码怎么解决?

推荐切分:

text 复制代码
一个接口说明 = 一个完整 chunk
一个配置项说明 = 一个 chunk
一个错误码说明 = 一个 chunk

接口文档不应该被硬切开。比如下面内容应该尽量作为一个整体:

text 复制代码
POST /api/user/login

描述:
用户登录接口

请求参数:
username
password

响应:
token
expireTime

错误码:
401
403

推荐 metadata:

text 复制代码
module
api_path
method
version
language
framework
error_code
heading_path

技术文档一定要重视关键词检索,因为里面有大量精确符号:

text 复制代码
RedisTemplate
MessageWindowChatMemory
@PostMapping
401
NullPointerException
/api/user/login

推荐策略:

text 复制代码
精确符号 / API path / 类名 → BM25 权重大
自然语言问题 → 向量权重大
最后 reranker 重排

4. 源码 RAG / 代码问答

典型问题:

text 复制代码
这个方法是干什么的?
这个类在哪里被调用?
为什么这里会空指针?
如何修改这段逻辑?

代码不能用普通文本切法。

推荐切分:

text 复制代码
类级别
方法级别
函数级别
接口级别
配置文件级别

Java 项目可以这样切:

text 复制代码
UserService.java
  - class metadata
  - method getLoginUser()
  - method updateUser()
  - method deleteUser()

每个方法 chunk 应该包含:

text 复制代码
方法签名
注解
方法体
关键 import
所在类名
包名
调用关系 metadata

源码 RAG 最好不是单纯向量搜索,而是:

text 复制代码
symbol index
+
BM25
+
vector search
+
AST / call graph

例如:

text 复制代码
问题:getLoginUser 在哪里被调用?

这个问题应该优先走符号索引和引用关系,而不是向量搜索。

如果问题是:

text 复制代码
用户登录后权限是怎么校验的?

这类问题才更适合:

text 复制代码
向量检索 + 调用链扩展

5. 学术论文 / 长报告 / 项目文档

典型问题:

text 复制代码
这篇论文的方法是什么?
实验结果说明了什么?
这个项目的风险有哪些?
报告里对市场趋势怎么看?

推荐切分:

text 复制代码
标题层级切分
摘要单独索引
结论单独索引
图表说明单独索引
长段落语义切分

可以建立多个索引:

text 复制代码
summary_index:
  摘要、结论、章节摘要

detail_index:
  正文段落

table_index:
  表格、实验结果

figure_index:
  图注、图表说明

推荐流程:

text 复制代码
先查 summary_index 判断相关章节
再查 detail_index 找证据
最后查 table_index 补充数据

6. 商品、电商、招聘、简历匹配

典型问题:

text 复制代码
推荐适合 Java 后端的岗位
找 3 年经验 Spring Boot 岗位
这款商品适合学生吗?
有哪些 500 元以内的耳机?

这类业务最重要的是:

text 复制代码
结构化字段过滤 + 向量语义召回

不要把所有字段拼成一段文本后只做向量搜索。

例如岗位数据应该有结构化字段:

text 复制代码
job_id
title
city
salary_min
salary_max
experience
skills
description
company

用户问:

text 复制代码
重庆 Java 后端 15k 以上,要求 Spring Boot 和 Redis

应该先过滤:

text 复制代码
city = 重庆
salary_max >= 15000
skills contains Java / Spring Boot / Redis

再做语义匹配。


7. 新闻 / 公告 / 时效性知识库

典型问题:

text 复制代码
最近有什么政策变化?
最新公告是什么?
这个项目最近进展如何?

推荐切分:

text 复制代码
一条新闻 / 一条公告 = 一个主 chunk
长公告按小标题切

metadata 必须包含:

text 复制代码
publish_time
effective_time
source
region
topic
status

索引优化重点:

text 复制代码
时间衰减排序
增量索引
旧版本归档
同主题聚合

否则用户问"最新政策",系统可能召回旧政策。


8. 扫描 PDF / 表格 / 图片型文档

这类文档最重要的问题不是 chunk,而是解析质量。

需要处理:

text 复制代码
OCR
页眉页脚
脚注
双栏排版
表格
图片
图注

推荐流程:

text 复制代码
1. OCR
2. 去除页眉页脚
3. 保留页码
4. 表格转 Markdown table 或 JSON
5. 图片生成 caption
6. 图表和正文建立引用关系

表格可以按以下方式切:

text 复制代码
一个表格 = 一个 chunk
一行 = 一个 chunk
一组业务行 = 一个 chunk

选择哪种取决于用户怎么问。

例如财务报表:

text 复制代码
用户问某一年营收 → 行级 chunk
用户问整体趋势 → 表级 chunk

三、chunk_size 和 overlap 应该怎么设置

经验表如下:

场景 推荐 chunk 粒度 chunk_size overlap
FAQ / 客服 一个问答对 100-300 tokens 0-30
普通知识库 一个段落 / 小节 300-700 tokens 50-100
技术文档 一个接口 / 一个配置项 / 一个小节 400-1000 tokens 80-150
合同 / 制度 一个条款 / 相邻条款 300-800 tokens 50-120
学术论文 一个小节 / 语义段 500-1200 tokens 80-150
代码 一个函数 / 一个类片段 40-100 行 10-20 行
Excel / 商品 / 岗位 一个实体 / 一行记录 不按固定 token 通常不需要
新闻公告 一条公告 / 一个小标题 300-800 tokens 50-100

需要注意:overlap 不是越大越好。

它的作用是防止语义断裂,但过大的 overlap 会带来:

text 复制代码
索引膨胀
重复召回
成本增加
上下文里重复内容变多

推荐:

text 复制代码
普通文档:10% - 20%
FAQ:0% - 10%
代码:按行 overlap
合同:按条款上下文 overlap

四、metadata 为什么比 chunk_size 更重要

一个高质量 chunk 不应该只有 text,还应该包含丰富的 metadata。

例如:

json 复制代码
{
  "chunk_id": "policy_2026_leave_005",
  "doc_id": "policy_2026_leave",
  "parent_id": "policy_2026_leave_chapter_2",
  "title": "员工请假制度",
  "heading_path": "第二章/第五条/年假规则",
  "text": "员工连续工作满一年后,可享受带薪年假...",
  "doc_type": "policy",
  "department": "HR",
  "effective_date": "2026-01-01",
  "version": "v3",
  "page": 12,
  "tenant_id": "company_a",
  "permission": ["employee", "hr_admin"]
}

metadata 可以解决很多问题:

text 复制代码
权限过滤
租户隔离
版本过滤
时间过滤
业务线过滤
文档类型过滤
精确定位引用

在真实系统里,metadata 往往比 chunk_size 更关键。


五、索引优化应该怎么做

进一步讨论索引优化时,核心结论是:

索引优化不是只调向量数据库参数,而是优化整个检索链路。

可以分成四层:

text 复制代码
数据层优化
检索层优化
排序层优化
上下文扩展

1. 数据层优化:让 chunk 更容易被召回

数据层优化包括:

text 复制代码
清洗噪声
去重
补标题
补 metadata
补关键词
补摘要
补同义词

例如原始 chunk:

text 复制代码
支持该操作。

这个 chunk 几乎没有检索价值。

可以改造成:

text 复制代码
【资源管理 / 权限控制 / 删除资源】
管理员可以删除资源,普通用户只能查看和下载公开资源。

这样 embedding 后的语义会更明确,也更容易被召回。


2. 检索层优化:BM25 + Vector 混合检索

不要只做向量搜索。

更推荐:

text 复制代码
BM25 / keyword
+
dense vector
+
metadata filter

原因是很多业务场景里有大量精确词:

text 复制代码
类名
接口名
错误码
合同编号
产品型号
岗位技能
疾病名称
药品名称

纯向量搜索可能会召回语义相似但关键词不准确的内容。

例如技术文档里用户搜:

text 复制代码
NullPointerException
/api/user/login
RedisTemplate

这些内容应该优先依赖 BM25 或符号索引。


3. 排序层优化:引入 Reranker

第一阶段召回通常是粗召回。

流程可以是:

text 复制代码
BM25 top 50
Vector top 50
  ↓
合并去重
  ↓
RRF 融合
  ↓
Reranker 重排
  ↓
取 top 3 / top 5

Reranker 的作用是判断:

text 复制代码
这个 chunk 是否真的能回答用户的问题?

它不是负责从全量知识库找内容,而是负责从候选内容中挑出最相关的部分。


4. 上下文扩展:小 chunk 检索,大 chunk 回答

推荐做法:

text 复制代码
检索时:用小 chunk
回答时:扩展 parent / neighbor chunk

例如命中:

text 复制代码
chunk_12

可以同时取:

text 复制代码
chunk_11
chunk_12
chunk_13
parent_section

这样可以兼顾:

text 复制代码
小 chunk 的精准召回
大上下文的完整回答

5. 向量索引本身如何优化

如果使用 Qdrant、Milvus、Weaviate、FAISS、Elasticsearch、OpenSearch,常见底层索引是 ANN 近似最近邻。

常见优化点:

text 复制代码
HNSW 参数
向量维度
索引分片
payload filter
tenant 隔离
量化压缩
冷热数据分层

以 HNSW 为例,常见参数包括:

text 复制代码
m
ef_construct
ef_search

大致理解:

text 复制代码
m 越大:
  图连接越多,召回更好,但内存更高

ef_construct 越大:
  建索引更慢,但索引质量更好

ef_search 越大:
  查询更准,但延迟更高

工程上的取舍:

text 复制代码
低延迟场景:
  topK 小
  ef_search 低一些
  reranker 处理较少候选

高准确率场景:
  topK 大
  ef_search 高一些
  reranker 重排更多候选

多租户场景:
  tenant_id 建 payload index
  或者使用多 collection

强过滤场景:
  常用 filter 字段必须建索引

六、为什么会进一步讨论 Reranker

在讨论完文档切分和索引优化后,自然出现了一个问题:

即使系统通过 BM25 和向量检索召回了一批 chunk,怎么保证真正有用的内容排在最前面?

这就是 Reranker 要解决的问题。

向量检索可以找到"语义相关"的内容,但它不一定能判断"哪个内容最能回答问题"。

例如用户问:

text 复制代码
为什么 Redis 限流要用 Lua 脚本?

向量检索可能召回:

text 复制代码
Redis 基础介绍
令牌桶算法
RedisTemplate 使用方式
Redis + Lua 原子性
分布式锁 setnx

这些都和 Redis 或限流有关系,但真正直接回答问题的是:

text 复制代码
Redis + Lua 可以保证判断令牌和扣减令牌这两个操作的原子性。

Reranker 的价值就在于:把真正能回答问题的 chunk 排到前面。


七、Reranker 是什么

接着进一步讨论的问题是:

Reranker 是什么?

Reranker,中文通常叫"重排模型"或"重排序器"。

它的作用是:

第一阶段先粗略召回一批可能相关的文档,第二阶段再让 Reranker 重新判断这些文档和用户问题的真实相关性,把最有用的内容排到最前面。

Reranker 不是负责"从全库找资料"的,而是负责:

text 复制代码
从已经召回的候选 chunk 里,挑出最适合回答问题的 chunk

1. Reranker 在 RAG 流程中的位置

完整流程:

text 复制代码
用户问题
  ↓
第一阶段:粗召回
  - 向量检索 top 50
  - BM25 检索 top 50
  ↓
候选文档合并去重
  ↓
第二阶段:Reranker 重排
  - 判断每个 chunk 和问题的相关性
  - 重新打分
  - 重新排序
  ↓
取 top 3 / top 5 给 LLM
  ↓
生成答案

可以理解成:

text 复制代码
Retriever:海选
Reranker:复试
LLM:写最终答案

2. 向量检索和 Reranker 的区别

向量检索

向量检索通常是:

text 复制代码
query → embedding 向量
chunk → embedding 向量

比较两个向量的相似度

特点:

text 复制代码
速度快
适合大规模召回
能处理语义相似
但是判断比较粗
Reranker

Reranker 通常会把:

text 复制代码
用户问题 + 候选 chunk

一起输入模型,让模型判断:

text 复制代码
这个 chunk 能不能回答这个问题?
相关性有多高?

示例:

text 复制代码
Query:
为什么 Redis 限流要用 Lua 脚本?

Document A:
Redis 是一种基于内存的 key-value 数据库。

Score: 0.31

Document B:
Redis + Lua 脚本可以保证令牌判断和扣减操作的原子性。

Score: 0.94

Reranker 会把 Document B 排到前面。


3. 为什么 Reranker 通常更准

普通向量检索多采用 bi-encoder 思路:

text 复制代码
query 单独编码
document 单独编码
然后比较两个向量距离

它速度快,因为 document embedding 可以提前算好。

但它没有真正逐字逐句比较 query 和 document。

Reranker 常见方式是 cross-encoder:

text 复制代码
输入 = [用户问题, 候选文档]
输出 = 相关性分数

它把问题和文档放在一起理解,所以判断更细。

类比:

text 复制代码
向量检索:
  看两个人的整体画像像不像

Reranker:
  让两个人坐下来详细对话,看他们到底匹不匹配

4. Reranker 能解决哪些问题

4.1 解决"看起来相关,但不能回答"的问题

比如用户问:

text 复制代码
Java 中 CompletableFuture 和 Future 有什么区别?

向量检索可能召回:

text 复制代码
Future 基础介绍
线程池基础介绍
CompletableFuture 异步编排
Runnable 和 Callable 区别

Reranker 会更倾向于把真正比较 Future 和 CompletableFuture 区别的文档排到前面。


4.2 解决关键词相似但语义不匹配的问题

例如:

text 复制代码
苹果公司的创始人是谁?

知识库里可能有:

text 复制代码
苹果是一种水果...
苹果公司由 Steve Jobs 等人创立...

Reranker 会根据完整问题判断用户问的是 Apple Inc.,而不是水果。


4.3 解决长文档 chunk 排序不准的问题

长文档切成很多 chunk 后,向量搜索常常会召回一堆半相关片段。

Reranker 可以进一步判断:

text 复制代码
哪个 chunk 最直接支持答案
哪个 chunk 只是背景信息
哪个 chunk 完全跑偏

5. Reranker 的局限性

Reranker 有用,但不能解决所有问题。

它的主要局限有:

text 复制代码
更慢
更贵
不能替代第一阶段召回

最重要的一点是:

Reranker 只能重排已经召回的候选内容。

如果第一阶段根本没有召回正确 chunk,Reranker 也救不了。

所以要记住:

text 复制代码
Retriever 负责"有没有"
Reranker 负责"排不排前"

八、Reranker 通常有哪些做法

接着讨论的问题是:

Reranker 通常有哪些做法?

常见做法可以分成六类:

text 复制代码
1. 规则打分型 Reranker
2. RRF / 分数融合型 Reranker
3. Cross-Encoder Reranker
4. LLM-as-Reranker
5. Learning-to-Rank
6. Late Interaction,例如 ColBERT

1. 规则打分型 Reranker

规则打分型 Reranker 不一定使用模型,而是根据业务规则重新加权。

例如用户问:

text 复制代码
重庆 Java 后端 15k 以上岗位

可以设计:

text 复制代码
最终分数 =
  语义相似度 * 0.4
  + 城市匹配 * 0.2
  + 技能匹配 * 0.2
  + 薪资匹配 * 0.1
  + 更新时间 * 0.1

候选岗位:

text 复制代码
岗位 A:重庆 + Java + Spring Boot + 18k
岗位 B:上海 + Java + Spring Boot + 30k
岗位 C:重庆 + Python + 16k

如果只看向量相似度,岗位 B 可能也很高。

但业务规则会把岗位 A 排到最前面。

适合:

text 复制代码
招聘系统
商品推荐
电影推荐
本地生活
企业知识库权限过滤
新闻公告时效排序

优点:

text 复制代码
快
可控
容易解释
不需要额外模型

缺点:

text 复制代码
需要人工设计规则
语义判断能力弱
规则多了维护成本高

2. RRF / 分数融合型 Reranker

RRF 全称 Reciprocal Rank Fusion,更准确地说,它是 rank fusion,但在工程里常被放进 rerank 流程。

假设有两路检索:

text 复制代码
BM25 检索 top 10
向量检索 top 10

BM25 排名:

text 复制代码
A, B, C, D

向量检索排名:

text 复制代码
C, A, E, F

RRF 会综合两个排名,让同时在多个检索器中排名靠前的文档得分更高。

简单公式:

text 复制代码
score(d) = Σ 1 / (k + rank_i(d))

其中:

text 复制代码
rank_i(d) 表示文档 d 在第 i 个检索器里的排名
k 通常取 60 左右,用来平滑分数

适合:

text 复制代码
技术文档
API 文档
代码检索
合同条款
FAQ
企业知识库

优点:

text 复制代码
实现简单
速度快
不需要训练模型
比单独向量检索更稳定

缺点:

text 复制代码
只是融合排名
不能真正理解 query 和 chunk 的细节关系

3. Cross-Encoder Reranker

这是 RAG 中最常见、效果也很好的做法。

输入:

text 复制代码
用户问题 + 候选 chunk

输出:

text 复制代码
相关性分数

示例:

text 复制代码
Query:
为什么 Redis 限流要用 Lua 脚本?

Document A:
Redis 是一个高性能 key-value 数据库。

Document B:
Redis + Lua 可以保证令牌判断和扣减操作的原子性。

Cross-Encoder 输出:

text 复制代码
Query + Document A → score = 0.31
Query + Document B → score = 0.94

适合:

text 复制代码
通用 RAG 问答
企业知识库
技术文档
法律合同
论文问答
客服问答

优点:

text 复制代码
相关性判断准确
效果通常明显好于纯向量检索
接入简单

缺点:

text 复制代码
比向量检索慢
候选 chunk 太多时成本高
通常只适合重排 top 20 - top 100

典型流程:

text 复制代码
向量 / BM25 先召回 top 50
  ↓
Cross-Encoder reranker 重排
  ↓
取 top 5 给 LLM

4. LLM-as-Reranker

也可以直接让大模型来做重排。

例如 prompt:

text 复制代码
用户问题:
为什么 Redis 限流要用 Lua 脚本?

下面有 10 个候选文档,请判断哪些最能回答问题,
按照相关性从高到低排序,并说明原因。

LLM 可能输出:

text 复制代码
1. chunk C:直接解释 Lua 保证原子性
2. chunk B:介绍令牌桶算法背景
3. chunk D:介绍 RedisTemplate,相关性较弱

适合:

text 复制代码
复杂分析类问题
多文档综合判断
项目文档分析
法律条款综合判断
论文综述
商业报告分析

优点:

text 复制代码
理解能力强
能处理复杂推理
可以解释为什么这样排序

缺点:

text 复制代码
慢
贵
输出可能不稳定
候选数量不能太多

所以一般不建议直接让 LLM rerank 100 个 chunk。

更推荐:

text 复制代码
BM25 + Vector 召回 top 50
普通 Reranker 排到 top 10
LLM 从 top 10 中选择最有用证据

5. Learning-to-Rank 排序模型

Learning-to-Rank 是搜索推荐系统里更传统、也更工程化的一类方法。

它会把多个特征放进排序模型:

text 复制代码
向量相似度
BM25 分数
标题是否匹配
关键词命中数量
文档更新时间
用户权限
点击率
历史采纳率
业务类型匹配
chunk 长度

然后训练排序模型:

text 复制代码
输入:query + document + features
输出:排序分数

常见模型:

text 复制代码
LambdaMART
XGBoost Ranker
LightGBM Ranker
神经网络排序模型

适合:

text 复制代码
搜索系统
电商推荐
招聘匹配
广告排序
内容推荐
大规模企业知识搜索

优点:

text 复制代码
可以融合大量业务特征
适合大规模生产系统
可以通过用户反馈持续优化

缺点:

text 复制代码
需要训练数据
工程复杂度高
冷启动比较难

6. Late Interaction Reranker,例如 ColBERT

Late Interaction 介于普通向量检索和 Cross-Encoder 之间。

普通向量检索是:

text 复制代码
一个 query → 一个向量
一个 document → 一个向量

Late Interaction 的思路是:

text 复制代码
query 里的每个 token 有自己的向量
document 里的每个 token 也有自己的向量
最后做细粒度匹配

可以理解成:

text 复制代码
普通向量检索:
  看整体像不像

Cross-Encoder:
  把 query 和 document 放一起深度理解

Late Interaction:
  保留 token 级别匹配能力,但尽量保持检索效率

适合:

text 复制代码
大规模搜索
技术文档检索
长文档检索
需要兼顾速度和准确率的 RAG

优点:

text 复制代码
比普通向量检索精细
比 Cross-Encoder 更容易扩展到大规模

缺点:

text 复制代码
实现复杂
索引成本更高
工程接入门槛更高

7. 多阶段 Reranker 组合方案

真实业务里往往不是只用一种 Reranker,而是多阶段组合。

一个成熟链路可以是:

text 复制代码
用户问题
  ↓
Query Rewrite / Query Expansion
  ↓
第一阶段粗召回
  - BM25 top 50
  - Vector top 50
  - Metadata filter
  ↓
RRF 融合
  ↓
Cross-Encoder Reranker
  ↓
业务规则加权
  ↓
上下文扩展
  - parent chunk
  - neighbor chunk
  ↓
LLM 生成答案

最终分数可以设计成:

text 复制代码
final_score =
  reranker_score * 0.6
  + bm25_score * 0.1
  + vector_score * 0.1
  + business_score * 0.15
  + freshness_score * 0.05

不同业务场景权重不同。


九、不同业务场景应该选择哪种 Reranker

场景 推荐做法
普通知识库问答 Vector / BM25 + Cross-Encoder
FAQ 客服 BM25 + Cross-Encoder + 类目规则
技术文档 BM25 + Vector + RRF + Cross-Encoder
代码问答 Symbol Search + BM25 + Vector + 规则重排
法律合同 条款召回 + Cross-Encoder + parent chunk 扩展
招聘匹配 结构化过滤 + 业务规则 + Learning-to-Rank
商品推荐 结构化过滤 + 业务分数 + 向量语义
新闻公告 语义相关性 + 时间衰减
学术论文 Summary index + detail index + Cross-Encoder
复杂分析 普通 reranker 后再用 LLM reranker

十、把整个 RAG 检索链路串起来

最终完整流程可以这样理解:

text 复制代码
用户问题
  ↓
Query Rewrite / Query Expansion
  ↓
Metadata Filter
  ↓
Hybrid Retrieval
  - BM25
  - Vector Search
  - Symbol Search
  ↓
Merge + Deduplicate
  ↓
RRF / Score Fusion
  ↓
Cross-Encoder Reranker
  ↓
Business Rule Reranker
  ↓
Context Expansion
  - parent chunk
  - neighbor chunk
  - sentence window
  ↓
LLM Generate Answer
  ↓
Answer with citations / evidence

这条链路里,每一层职责不同:

模块 作用
文档切分 把知识变成适合检索的单元
metadata 支持过滤、权限、版本、溯源
BM25 解决关键词和精确匹配
Vector Search 解决语义召回
RRF 融合多路召回结果
Reranker 判断哪些候选最能回答问题
Context Expansion 补充上下文,避免答案不完整
LLM 基于证据生成最终回答

十一、推荐的工程落地路线

如果从 0 到 1 做 RAG,可以按这个顺序升级。

text 复制代码
vector search top 5

优点:简单。

问题:容易召回半相关内容,排序不稳定。


第二版:BM25 + Vector + RRF

text 复制代码
BM25 top 20
Vector top 20
RRF 融合 top 10

效果通常比纯向量检索稳定很多。


第三版:加入 Cross-Encoder Reranker

text 复制代码
BM25 top 50
Vector top 50
RRF top 30
Cross-Encoder rerank top 5

这是大多数 RAG 项目中性价比很高的方案。


第四版:加入业务规则和权限过滤

text 复制代码
reranker_score
+
权限
+
时间
+
版本
+
业务字段

适合真实业务上线。


第五版:基于反馈做 Learning-to-Rank

当系统有用户点击、点赞、采纳、人工标注数据后,可以训练排序模型,让排序效果持续优化。


十二、结合电影 Excel RAG 场景的落地设计

前面对话中也结合了电影 Excel 数据进行说明。

这类数据通常长这样:

text 复制代码
电影名 | 类型 | 地区 | 上映时间 | 评分 | 简介 | 演员 | 评论

不能简单把整个 Excel 拼成文本后固定切分。


1. summary_docs 应该如何设计

一部电影一个 summary chunk:

text 复制代码
电影名:xxx
类型:剧情 / 喜剧
地区:中国
年份:2020
评分:8.7
主演:xxx
简介:xxx

适合回答:

text 复制代码
推荐电影
找评分高的电影
某类型电影有哪些
某演员出演过哪些电影

metadata:

python 复制代码
{
    "movie_name": "xxx",
    "genre": "剧情",
    "country": "中国",
    "year": 2020,
    "score": 8.7,
    "sheet_name": "xxx"
}

2. content_docs 应该如何设计

如果电影有长简介、剧情、影评,可以继续切:

text 复制代码
movie_id = xxx
chunk_type = plot / review / analysis

例如:

text 复制代码
霸王别姬_plot_1
霸王别姬_review_summary
霸王别姬_actor_info

适合回答:

text 复制代码
这部电影讲了什么?
为什么评分高?
适合什么人看?
主题是什么?

如果用户问:

text 复制代码
推荐几部 9 分以上的国产剧情片

不要只走向量搜索。

应该先做 metadata filter:

text 复制代码
score >= 9
country = 中国
genre = 剧情

然后再结合向量搜索:

text 复制代码
query = 推荐 高质量 电影

如果用户问:

text 复制代码
有没有类似《霸王别姬》的电影?

可以:

text 复制代码
先找到《霸王别姬》的 summary embedding
再向量搜索相似电影
再用 genre / country / theme 过滤

如果用户问:

text 复制代码
这部电影为什么评分高?

应该:

text 复制代码
先用 movie_name 精确过滤
再查 content_docs 里的 plot / review / analysis

如果用户问:

text 复制代码
推荐几部适合学习英语口语的电影

Reranker 可以根据以下维度重新排序:

text 复制代码
对话是否生活化
台词是否多
语速是否适中
发音是否清晰
是否适合模仿

而不是只看评分高低。


十三、最终总结

1. 文档切分的核心结论

文档切分不是把文本切碎,而是设计检索单元。

可以这样记:

text 复制代码
FAQ:一个问答一块
合同:一个条款一块
技术文档:一个接口 / 一个配置项一块
代码:一个函数 / 一个类一块
Excel:一个业务实体 / 一行一块
论文:一个小节 / 一个语义段一块
新闻:一条新闻 / 一个公告一块

2. 索引优化的核心结论

不要只做 vector search。

更合理的是:

text 复制代码
metadata filter
+
BM25 keyword search
+
dense vector search
+
RRF fusion
+
Reranker
+
parent / neighbor chunk expansion

3. Reranker 的核心结论

Reranker 负责把第一阶段召回出来的候选内容重新排序。

一句话:

text 复制代码
Retriever 负责"有没有"
Reranker 负责"排不排前"
LLM 负责"怎么回答"

4. Reranker 常见做法

text 复制代码
1. 规则重排
2. RRF / 分数融合
3. Cross-Encoder 重排
4. LLM 重排
5. Learning-to-Rank
6. Late Interaction,例如 ColBERT

工程上最常用、性价比最高的是:

text 复制代码
BM25 + Vector 召回
  ↓
RRF 融合
  ↓
Cross-Encoder Reranker
  ↓
业务规则加权
  ↓
取 topK 给 LLM

5. RAG 工程化的整体认知

真正好的 RAG 不是:

text 复制代码
文档 → 固定切分 → embedding → vectorDB

而是:

text 复制代码
业务问题分析
  ↓
知识单元建模
  ↓
结构化切分
  ↓
metadata 设计
  ↓
多路索引
  ↓
混合检索
  ↓
重排
  ↓
上下文扩展
  ↓
可评估优化

最终可以用一句话概括:

切分决定知识有没有被正确表达,索引决定正确知识能不能被准确召回,重排决定正确知识能不能排到前面。

相关推荐
Elastic 中国社区官方博客2 小时前
Elastic 9.4:Workflows 正式发布、Agent Builder 更新,以及 Prometheus / PromQL 支持
运维·数据库·人工智能·elasticsearch·搜索引擎·信息可视化·prometheus
ㄟ留恋さ寂寞2 小时前
html如何修改备注
jvm·数据库·python
2401_884454152 小时前
c++如何读取YAML格式配置文件_yaml-cpp库快速入门【详解】
jvm·数据库·python
2301_775639892 小时前
mysql升级时如何使用Ansible进行自动化部署_mysql自动化管理
jvm·数据库·python
WUYOUGYLU2 小时前
第一次买云服务器,最该先看什么?
数据库
CLX05052 小时前
怎样设置外键的更新级联操作_ON UPDATE CASCADE配置
jvm·数据库·python
李少兄2 小时前
高性能MySQL实战:应用层关联查询的深度优化
数据库·mysql
zjy277772 小时前
SQL Server如何实现编写表与字段注释_Navicat兼容操作步骤
jvm·数据库·python
m0_702036532 小时前
CSS移动端实现响应式导航菜单_利用媒体查询切换显示隐藏状态
jvm·数据库·python