AI Day 20
AI Day 20: 生产级RAG(2):检索优化与Reranking — 找到最相关的信息
检索优化与Reranking是生产级RAG系统的"精准定位引擎"——昨天我们解决了"数据怎么准备"(解析+Chunking),今天解决"数据怎么找到"。核心问题是:面对百万级Chunks,如何在毫秒内从中精确找到回答用户问题最需要的那5-10个片段。这不是简单的向量相似度搜索,而是一个多阶段、多策略的信息精炼流程——从Query理解、粗筛召回、精排重排到上下文组装,每一步都有巨大优化空间。
2026-04-21
RetrievalRerankingCross-EncoderHyDEQueryMulti-QuerySelf-RAGCorrectiveColBERTRAPTORNDCGRecall@KContextLost-in-the-Middle
日期:2026-04-21
阶段:第二阶段 — 工程实践 (Day 16-30)
标签:Retrieval Optimization Reranking Cross-Encoder HyDE Query Rewriting Multi-Query Self-RAG Corrective RAG ColBERT RAPTOR NDCG Recall@K Context Assembly Lost-in-the-Middle
学习路径树
AI/LLM 深度技术学习 50天计划
├── 第一阶段:模型基础 (Day 1-15) ✅
│ ├── Day 1: Transformer架构与LLM基础 ✅
│ ├── Day 2: 模型量化与本地部署 ✅
│ ├── Day 3: 训练过程深度:Pre-training / SFT / RLHF / DPO ✅
│ ├── Day 4: Prompt Engineering与上下文学习(ICL)原理 ✅
│ ├── Day 5: RAG架构:检索增强生成全链路 ✅
│ ├── Day 6: 向量数据库与Embedding模型 ✅
│ ├── Day 7: Fine-tuning实战:LoRA / QLoRA / Adapter ✅
│ ├── Day 8: 推理优化:vLLM / TensorRT-LLM / SGLang ✅
│ ├── Day 9: 长上下文技术:RoPE扩展 / Ring Attention ✅
│ ├── Day 10: 多模态模型:Vision-Language架构 ✅
│ ├── Day 11: Reasoning模型:CoT / o1 / R1 / Extended Thinking ✅
│ ├── Day 12: Agent框架:ReAct / Tool Use / Planning ✅
│ ├── Day 13: MCP协议与Tool生态 ✅
│ ├── Day 14: 模型评估:Benchmark / Arena / 安全评估 ✅
│ └── Day 15: 阶段复习与架构总结 ✅
│
├── 第二阶段:工程实践 (Day 16-30)
│ ├── Day 16: LLM应用架构设计 — API Gateway/路由/缓存 ✅
│ ├── Day 17: LLM安全与Guardrails — 生产环境防护体系 ✅
│ ├── Day 18: LLM可观测性 — 日志/Tracing/指标/告警 ✅
│ ├── Day 19: 生产级RAG(1) — 文档解析与Chunking工程 ✅
│ ├── Day 20: 生产级RAG(2) — Retrieval优化与Reranking ← 你在这里
│ ├── Day 21: 生产级RAG(3) — 评估框架与持续迭代
│ ├── Day 22-25: Agent系统工程化(状态管理/错误恢复/成本控制)
│ └── Day 26-30: 多模型编排/部署策略/端到端项目
│
├── 第三阶段:金融零售AI应用 (Day 31-42)
│ ├── Day 31-35: 金融AI(风控模型/智能投顾/合规/反欺诈)
│ ├── Day 36-40: 零售AI(推荐系统/智能客服/供应链预测/营销)
│ └── Day 41-42: CeFi x DeFi x AI融合架构
│
└── 第四阶段:面试冲刺 (Day 43-50)
├── Day 43-46: 系统设计面试(LLM平台/RAG/Agent/推荐)
├── Day 47-49: 产品/架构面试模拟
└── Day 50: 总结与作品集
核心概念
一句话定义
检索优化与Reranking是生产级RAG系统的"精准定位引擎"——昨天我们解决了"数据怎么准备"(解析+Chunking),今天解决"数据怎么找到"。核心问题是:面对百万级Chunks,如何在毫秒内从中精确找到回答用户问题最需要的那5-10个片段。这不是简单的向量相似度搜索,而是一个多阶段、多策略的信息精炼流程——从Query理解、粗筛召回、精排重排到上下文组装,每一步都有巨大优化空间。
金融类比
检索优化 = 投研分析师的信息筛选能力 —— 不是读更多报告,而是更快找到关键段落
投研分析师工作流: RAG检索Pipeline:
├── 理解问题 ├── Query理解与转换
│ ├── 基金经理问"看好新能源吗?" │ ├── 用户问"ETH质押收益怎样?"
│ ├── 分析师解读: 要看政策+产能+估值 │ ├── Query Rewriting: 拆解成多个子问题
│ ├── 扩展关键词: 光伏/锂电/风电/储能 │ ├── Multi-Query: ETH staking APY + 验证者收益 + LST对比
│ └── 明确范围: 2025年Q3、A股+港股 │ └── 明确检索范围: 最新数据 + 技术文档 + 市场分析
│ │
├── 粗筛(Retrieval) ├── 初始检索(Recall Stage)
│ ├── 先筛行业报告库: 100份相关报告 │ ├── 向量搜索 + BM25混合: 召回100个Chunks
│ ├── 按标题/关键词快速过滤 │ ├── 按相似度分数初步排序
│ └── 形成"待精读"清单 │ └── 形成"候选集"
│ │
├── 精排(Reranking) ├── Reranking(精排)
│ ├── 逐篇快速浏览: 看摘要、看结论 │ ├── Cross-Encoder逐对打分
│ ├── 按相关性重新排序 │ ├── 按Query-Chunk对的相关性精确排序
│ ├── 丢掉明显离题的(新能源车→电动自行车) │ ├── 过滤掉低于阈值的Chunks
│ └── 精选10篇核心报告 │ └── 精选Top-10最相关Chunks
│ │
├── 组装(Context Assembly) ├── 上下文组装
│ ├── 提取关键数据点: 估值、产能、政策 │ ├── 按逻辑排序、去重
│ ├── 按逻辑组织: 政策→供需→估值→结论 │ ├── 压缩冗余信息
│ ├── 控制篇幅: 基金经理没耐心看50页 │ ├── 控制Token预算: LLM上下文窗口有限
│ └── 标注出处: 每个数据的来源 │ └── 保留出处: 每个Chunk的来源信息
│ │
└── 核心: 好分析师不是读最多报告的 └── 核心: 好检索不是召回最多的
而是最快找到关键信息的 而是最精准找到相关信息的
→ 信息过载反而降低决策质量 → 塞太多无关Context反而干扰生成
→ 100份报告中找到5个关键段落 >> 读完100份 → 100个Chunks中精选5个 >> 把100个都塞给LLM
连接Day 19
| Day 19学的 | Day 20补充的 |
|---|---|
| 文档解析:把PDF/Word变成干净文本 | Query理解:把用户问题变成好的检索请求 |
| Chunking:把文本切成语义片段 | Retrieval:从百万Chunks中召回候选集 |
| 质量门控:确保Chunk可用 | Reranking:对候选集精确排序 |
| 索引入库:存进向量数据库 | Context Assembly:组装最终上下文 |
Day 19决定了RAG的上限(数据质量),Day 20决定了能接近这个上限的程度(检索精度)。两者缺一不可。
知识点1: 检索Pipeline完整设计
端到端检索流程
用户原始Query
│
▼
┌─────────────────┐
│ Query理解与转换 │ ← Step 1: 把"模糊的人话"变成"精确的检索意图"
│ (Query Understanding)
│ ├── Intent分类
│ ├── Query Rewriting
│ ├── Query Expansion
│ └── Query Decomposition
└────────┬────────┘
│
▼
┌─────────────────┐
│ 多路召回 │ ← Step 2: "广撒网"——宁可多召回,不可漏掉
│ (Multi-path Retrieval)
│ ├── Dense Retrieval (向量搜索)
│ ├── Sparse Retrieval (BM25/TF-IDF)
│ ├── Structured Search (Metadata过滤)
│ └── Knowledge Graph (实体关联)
└────────┬────────┘
│ 候选集: 50-200 Chunks
▼
┌─────────────────┐
│ Reranking精排 │ ← Step 3: "精准狙击"——从候选中找最相关的
│ ├── Cross-Encoder打分
│ ├── 分数阈值过滤
│ └── 多样性控制 (MMR)
└────────┬────────┘
│ 精排结果: 5-15 Chunks
▼
┌─────────────────┐
│ Context组装 │ ← Step 4: "装弹"——把精选Chunks组织成LLM友好的上下文
│ ├── 去重合并
│ ├── 逻辑排序
│ ├── Token预算分配
│ └── 压缩(可选)
└────────┬────────┘
│ 结构化上下文
▼
┌─────────────────┐
│ LLM生成 │ ← Step 5: "开枪"——基于精准上下文生成回答
│ ├── System Prompt + Context + Query
│ ├── Citation生成
│ └── 置信度评估
└─────────────────┘
每一步的优化杠杆
| 步骤 | 常见问题 | 优化方向 | 效果量级 |
|---|---|---|---|
| Query理解 | 用户问题模糊/歧义 | HyDE/Multi-Query/Decomposition | +15-30% Recall |
| 多路召回 | 向量搜索遗漏关键词匹配 | Hybrid Search (Dense+Sparse) | +10-20% Recall |
| Reranking | 召回的Top-K不是真正最相关的 | Cross-Encoder重排序 | +10-25% Precision |
| Context组装 | 无关信息干扰/重复/超长 | 压缩+去重+Token预算管理 | +5-15% Answer Quality |
| LLM生成 | 幻觉/不引用来源 | 更好的Prompt/Citation约束 | +5-10% Faithfulness |
经验法则:优化顺序
如果你的RAG效果不好,按这个顺序排查(投入产出比从高到低):
1. 检查Chunking质量 (Day 19) → 数据本身有问题?修数据
2. 改进Query理解 → 用户问题没被正确理解?做Query转换
3. 加Reranking → 召回的不够相关?加Cross-Encoder
4. 上Hybrid Search → 关键词匹配漏了?加BM25
5. 优化Context Assembly → 上下文太冗余?做压缩/去重
6. 调整LLM Prompt → 生成格式不对?改Prompt模板
80%的RAG问题出在前3步。
别一上来就换大模型——那是最后的手段。
知识点2: Query理解与转换
为什么原始Query往往不适合直接检索
用户实际提问: 检索系统的困境:
"ETH最近怎么样?" → "怎么样"是什么意思?价格?技术?生态?
"DeFi借贷安全吗?" → "安全"指合约安全?资金安全?监管风险?
"帮我对比一下L2" → 对比什么维度?哪些L2?哪个时间段?
"上次那个攻击事件后果如何?" → "上次"是什么时候?"那个"是哪个?
核心矛盾:
用户用自然语言提问(模糊、简略、有上下文依赖)
检索系统需要精确的语义匹配(明确、具体、自包含)
五大Query转换策略
策略1: Query Rewriting (查询改写)
原理: 用LLM把用户的口语化/模糊查询改写成更适合检索的形式
原始Query: "ETH质押划算吗"
改写后: "以太坊ETH Staking质押收益率APY与风险分析"
原始Query: "那个被黑的桥"
改写后: "跨链桥安全事件黑客攻击案例"
实现:
System: "你是一个搜索查询优化助手。将用户的口语化问题改写为
更适合在知识库中检索的查询。保持原意,增加相关术语,
去除口语化表达。只输出改写后的查询,不要解释。"
User: "{original_query}"
金融场景示例:
原始: "理财产品靠谱吗"
改写: "银行理财产品风险等级评估收益率对比安全性分析"
成本: 1次LLM调用 (约$0.001-0.01)
延迟: +200-500ms
效果: Recall提升10-20%,尤其对口语化查询效果显著
策略2: HyDE (Hypothetical Document Embeddings)
原理: 让LLM先生成一个"假设性的理想答案",然后用这个答案去做向量检索
(不是用Query去搜,而是用"理想答案"去搜,因为答案和文档更相似)
为什么有效?
Query: "什么是无常损失" ← 短、抽象
文档: "无常损失(Impermanent ← 长、详细、包含解释
Loss)是指当LP提供流动
性时,由于价格波动导致
的价值损失..."
Query和文档的语义"距离"很远
但如果我们先生成一个假设答案,它和文档的语义就很近
流程:
Step 1: LLM生成假设答案
Query: "什么是无常损失"
HyDE: "无常损失是去中心化交易所(DEX)中流动性提供者(LP)
面临的一种风险。当LP存入代币对到AMM池中后,如果
代币价格发生变化,LP取出时的资产价值可能低于
直接持有。这种损失称为无常损失,因为如果价格
恢复,损失会消失..."
Step 2: 用HyDE假设答案做Embedding检索
→ 比用原始Query检索,Recall提升15-30%
注意:
- HyDE增加了一次LLM调用(延迟+成本)
- 如果LLM对该领域完全不了解,假设答案可能误导检索
- 最适合: 知识密集型、LLM有基础认知的领域(金融/法律/医疗)
- 不适合: 实时数据查询、LLM完全不了解的私有知识
策略3: Multi-Query (多查询扩展)
原理: 从不同角度重述用户问题,每个角度各做一次检索,合并结果
Query: "DeFi借贷风险有哪些?"
扩展为:
Q1: "DeFi借贷协议智能合约安全漏洞风险" → 技术风险角度
Q2: "DeFi超额抵押借贷清算机制与系统性风险" → 金融风险角度
Q3: "Aave Compound借贷协议预言机操纵攻击" → 具体案例角度
每个Q分别检索Top-20 → 合并去重 → 得到更全面的候选集
实现(LangChain MultiQueryRetriever):
# LLM生成3-5个变体查询
# 每个变体做独立检索
# 合并+去重候选Chunks
# 送入Reranker精排
优势: 显著提高Recall(从不同角度覆盖)
劣势: 3-5倍的检索延迟和成本
适用: 复杂问题、研究性查询、允许更高延迟的场景
策略4: Step-Back Prompting (后退一步提问)
原理: 先问一个更抽象/更高层的问题,用其结果补充上下文
Query: "Uniswap V3的集中流动性LP无常损失比V2大还是小?"
→ 这是一个非常具体的问题,直接检索可能找不到精确答案
Step-Back: "Uniswap V3集中流动性机制原理是什么?"
→ 更通用的问题,更容易检索到相关文档
流程:
1. LLM生成Step-Back问题
2. 检索Step-Back问题的结果(提供背景知识)
3. 检索原始问题的结果(提供具体信息)
4. 合并两个检索结果作为Context
5. LLM基于完整上下文回答原始问题
金融场景:
原始: "2025年Q3巴塞尔III最终规则对中小银行资本充足率影响多大?"
Step-Back: "巴塞尔III最终规则(Basel III Endgame)主要变化有哪些?"
→ 先理解框架,再回答具体问题
策略5: Query Decomposition (子问题分解)
原理: 把复杂问题拆解成多个简单子问题,分别检索后综合回答
Query: "对比Aave和Compound的利率模型、治理机制和安全记录"
分解为:
Sub-Q1: "Aave的利率模型如何运作?"
Sub-Q2: "Compound的利率模型如何运作?"
Sub-Q3: "Aave的DAO治理机制设计"
Sub-Q4: "Compound的治理机制设计"
Sub-Q5: "Aave历史安全事件与审计记录"
Sub-Q6: "Compound历史安全事件与审计记录"
每个子问题独立检索 → 分别获取最相关Chunks → 合并组装
优势:
- 复杂问题分而治之,每个子问题检索更精准
- 最终回答更结构化、更全面
- 适合对比类、多维度分析类问题
劣势:
- 延迟显著增加(N个子问题 = N次检索)
- 需要LLM分解问题(可能分解不当)
最佳实践:
- 只对复杂问题使用(简单问题别过度分解)
- 限制子问题数量(3-6个)
- 分解后做合并去重,避免重复Chunks
Query转换策略选择决策树
用户Query进入
│
├── 问题是否模糊/口语化?
│ └── 是 → Query Rewriting (低成本高回报)
│
├── 问题是否需要深度知识检索?
│ └── 是 → HyDE (用假设答案增强语义匹配)
│
├── 问题是否有多个角度/维度?
│ └── 是 → Multi-Query (多角度覆盖)
│
├── 问题是否过于具体/专业?
│ └── 是 → Step-Back (先获取背景知识)
│
└── 问题是否包含多个子问题?
└── 是 → Query Decomposition (分而治之)
注意: 策略可以组合使用
例: Query Rewriting → Multi-Query → Hybrid Search → Reranking
但每增加一步都增加延迟和成本,需要在质量和性能间权衡
知识点3: Reranking深度
为什么需要Reranking
向量检索(Bi-Encoder)的局限:
原理: Query和Document分别编码为向量,用余弦相似度匹配
Query → Encoder → q_vec ─┐
├── cosine_sim(q_vec, d_vec) = score
Doc → Encoder → d_vec ─┘
问题:
1. 信息压缩损失: 一段500字文本压缩到768维向量,必然丢失细节
2. 独立编码: Query和Doc分别编码,没有交叉注意力(Cross-Attention)
3. 语义近≠问答相关: "以太坊Gas费为什么高"和"以太坊Gas费降低方案"
语义相似但一个是问原因一个是讲解决方案
Reranker(Cross-Encoder)的优势:
原理: Query和Document拼接后一起送入模型,有完整的交叉注意力
[CLS] Query [SEP] Document [SEP] → Cross-Encoder → relevance_score
Query和Document的每个token可以互相attention
→ 能理解"Query问的是X,Document回答的恰好是X"
→ 比向量点积精确得多
代价:
- 不能预计算(每次都要Query+Doc一起过模型)
- 速度慢: 1M文档全部Rerank需要数小时
- 所以只能在粗筛后对Top-50~200做Rerank
Bi-Encoder vs Cross-Encoder 详细对比
| 维度 | Bi-Encoder (向量检索) | Cross-Encoder (Reranker) |
|---|---|---|
| 编码方式 | Query和Doc分别编码 | Query+Doc拼接后联合编码 |
| 交互方式 | 无交互,仅向量点积/余弦 | 完整Cross-Attention交互 |
| 精度 | 中等(压缩损失) | 高(保留完整语义交互) |
| 速度 | 极快(毫秒级,ANN索引) | 慢(每对需要单独推理) |
| 可扩展性 | 百万/亿级文档 | 仅适合Top-50~200 |
| 预计算 | 可以(Document向量提前算好) | 不可以(依赖Query) |
| 适合阶段 | 粗筛(Recall) | 精排(Precision) |
| 模型大小 | 通常110M-330M参数 | 通常110M-560M参数 |
主流Reranker对比 (2025-2026)
| Reranker | 类型 | 大小 | 特点 | 适用场景 |
|---|---|---|---|---|
| Cohere Rerank 3.5 | API服务 | 闭源 | 最高精度、多语言支持、简单易用 | 预算充足的生产环境 |
| BGE-Reranker-v2-m3 | 开源Cross-Encoder | 568M | BAAI开源、多语言、中文优秀 | 自部署、中文场景、隐私敏感 |
| Jina Reranker v2 | API+开源 | 137M/278M | 轻量高效、多语言、长文档支持8K | 需要长文档Rerank |
| FlashRank | 开源轻量 | 20-60M | 极小极快、CPU可运行、适合边缘 | 低延迟要求、资源受限 |
| ColBERT v2 | Late Interaction | 110M | Token级交互、可预计算、精度高 | 需要速度和精度平衡 |
| RankLLM/RankGPT | LLM-as-Reranker | 7B-70B | 用LLM做Rerank、零样本泛化强 | 冷启动、无训练数据 |
| MixedBread mxbai-rerank | 开源Cross-Encoder | 330M | 2025新锐、英文性能强 | 英文为主场景 |
金融场景Reranker选型建议
场景1: 银行内部知识库(合规要求,不能出云)
→ BGE-Reranker-v2-m3 自部署
→ 理由: 中文效果好、开源可审计、数据不出内网
场景2: DeFi分析助手(面向用户,需要低延迟)
→ Cohere Rerank API + 本地缓存
→ 理由: 精度最高、延迟可接受(50-100ms)、多语言
→ 备选: Jina Reranker(更便宜)
场景3: 嵌入式钱包安全检测(端侧运行)
→ FlashRank
→ 理由: 20MB模型、CPU运行、延迟<10ms
场景4: 冷启动/无训练数据的新领域
→ RankGPT (GPT-4o做Rerank)
→ 理由: 零样本泛化能力最强,无需训练数据
→ 注意: 成本高、延迟大,适合验证阶段而非生产
两阶段检索:粗筛 → 精排
经典的两阶段检索架构(工业界标准):
百万级文档库
│
┌───────────┼───────────┐
│ Stage 1: 粗筛召回 │
│ (Recall Stage) │
│ │
│ 方法: Hybrid Search │
│ ├── Dense: ANN向量搜索 │ ← 毫秒级,从1M中找Top-100
│ ├── Sparse: BM25 │ ← 毫秒级,关键词匹配Top-100
│ └── 合并去重 │
│ │
│ 输出: 50-200候选Chunks │
└───────────┬───────────┘
│
┌───────────┼───────────┐
│ Stage 2: 精排重排 │
│ (Ranking Stage) │
│ │
│ 方法: Cross-Encoder │
│ ├── 逐对打分 │ ← 每对~5ms,200对≈1秒
│ ├── 按分数排序 │
│ ├── 分数阈值过滤 │
│ └── 多样性控制(MMR) │
│ │
│ 输出: 5-15精选Chunks │
└───────────┬───────────┘
│
▼
Context Assembly → LLM
粗筛数量选择经验:
文档库 < 10K → 粗筛 Top-30 → 精排 Top-5~8
文档库 10K-100K → 粗筛 Top-50 → 精排 Top-8~12
文档库 > 100K → 粗筛 Top-100 → 精排 Top-10~15
为什么不直接对所有文档做Cross-Encoder?
1M文档 × 5ms/对 = 5000秒 ≈ 83分钟 → 完全不可接受
所以必须先用快速方法(ANN/BM25)缩小候选集
Hybrid Search: Dense + Sparse融合
为什么混合搜索比纯向量搜索好:
向量搜索擅长: BM25擅长:
✅ 语义相似("汽车" ≈ "车辆") ✅ 精确关键词匹配("ERC-4337")
✅ 同义词/释义("借贷" ≈ "放贷") ✅ 专有名词("Uniswap V3")
✅ 多语言语义("staking" ≈ "质押") ✅ 数字/代码("0x1234...")
❌ 精确术语匹配弱 ❌ 语义理解弱
❌ 稀有词/新词embedding差 ❌ 同义词无法匹配
混合策略(Reciprocal Rank Fusion - RRF):
dense_results = vector_search(query, top_k=100)
sparse_results = bm25_search(query, top_k=100)
# RRF融合公式
for doc in all_results:
score = 0
if doc in dense_results:
score += 1 / (k + dense_rank(doc)) # k通常=60
if doc in sparse_results:
score += 1 / (k + sparse_rank(doc))
final_score[doc] = score
# 按final_score排序
→ 两个列表中都靠前的文档分数最高
→ 只在一个列表中靠前的也有机会
典型效果提升:
纯Dense Recall@10 ≈ 0.72
纯Sparse Recall@10 ≈ 0.65
Hybrid (RRF) Recall@10 ≈ 0.82 ← 显著提升
知识点4: 上下文组装策略
Context Window预算分配
LLM的上下文窗口是有限且昂贵的资源,必须精打细算:
假设模型窗口: 128K tokens (GPT-4o / Claude Sonnet)
实际可用分配:
┌──────────────────────────────────────────┐
│ System Prompt ~500 tokens │ ← 角色、规则、格式要求
│ Retrieved Context ~4000 tokens │ ← 检索到的Chunks(核心)
│ Chat History ~2000 tokens │ ← 多轮对话历史
│ User Query ~200 tokens │ ← 当前问题
│ Output Budget ~2000 tokens │ ← 预留给模型回答
│──────────────────────────────────────────│
│ Total ~8700 tokens │
└──────────────────────────────────────────┘
为什么不把128K都塞满Context?
1. 成本: GPT-4o $2.50/1M input tokens → 128K = $0.32/次 → 日均1万次 = $3200/天
2. 延迟: 输入越长,TTFT(首Token时间)越慢
3. Lost-in-the-Middle: 研究表明模型对中间位置的信息关注度低
4. 噪声干扰: 无关信息越多,生成质量越差
金融类比:
Context Window = 信贷审批的一页纸报告
→ 不是把客户所有交易记录都打印出来
→ 而是提炼关键信息: 收入、负债、还款记录、风险评分
→ 一页纸要能让审批人快速做出准确判断
Lost-in-the-Middle问题与应对
2023年斯坦福研究发现:
当Context很长时,LLM对"开头"和"结尾"的信息关注度高,
对"中间"的信息关注度显著下降。
┌─────────────────────────────────────┐
│ 关注度 │
│ ▓▓▓▓▓▓ │ ← 开头: 高关注
│ ▓▓▓▓ │
│ ▓▓ │
│ ▓ ← 中间: 低关注 │
│ ▓▓ │
│ ▓▓▓▓ │
│ ▓▓▓▓▓▓▓ │ ← 结尾: 高关注
└─────────────────────────────────────┘
位置: 开头 中间 结尾
应对策略:
1. 关键信息放首尾
→ 最相关的Chunk放在Context开头
→ 次相关的放结尾
→ 背景信息/补充信息放中间
2. 控制Context长度
→ 宁可少而精,不要多而杂
→ 5个高相关Chunk > 20个中相关Chunk
3. 结构化标记
→ 给每个Chunk加编号和标题
→ "[来源1] xxx" "[来源2] yyy"
→ 帮助模型定位和引用
4. 2025年进展:
→ Claude 3.5/4.x 和 GPT-4o 已显著改善此问题
→ 但在超长Context(>50K tokens)下仍需注意
→ 最佳实践: 即使模型改善了,也保持精简Context的习惯
去重、排序与压缩
去重策略:
├── 精确去重: 相同Chunk ID去重(Multi-Query返回的重复结果)
├── 语义去重: 两个Chunk语义相似度>0.95,只保留分数更高的
├── 来源去重: 同一文档的多个Chunks,优先保留分数最高的2-3个
└── 段落去重: 因为Chunk重叠(overlap)导致的内容重复
排序策略:
├── 按相关性排序: Reranker分数从高到低(最常用)
├── 按时间排序: 最新的信息优先(适合时效性问题)
├── 按逻辑排序: 背景→原因→分析→结论(适合分析类问题)
└── 按来源排序: 同一来源的Chunks放一起(保持上下文连贯)
压缩策略:
├── LLM Summarization: 让LLM压缩每个Chunk保留关键信息
│ → 500 tokens → 150 tokens,保留核心事实
│ → 额外延迟+成本,但能显著减少Context长度
├── Extractive: 从Chunk中提取与Query最相关的句子
│ → 不需要LLM,速度快
│ → 可能丢失上下文关系
└── LongLLMLingua: 微软的Prompt压缩框架
→ 自动识别并移除不重要的tokens
→ 压缩率2-5x,信息保留率>90%
动态Top-K选择
固定Top-K的问题:
- Top-5: 简单问题可能够了,复杂问题信息不足
- Top-20: 复杂问题够了,简单问题引入噪声
动态Top-K策略:
方法1: 分数阈值截断
retrieved = rerank(query, candidates)
context = [c for c in retrieved if c.score > 0.5] # 只取分数>阈值的
# 自然地:高相关性多则多取,低相关性多则少取
方法2: 分数Gap检测
scores = [0.92, 0.88, 0.85, 0.72, 0.45, 0.42, 0.38, ...]
# 检测分数下降最大的Gap: 0.85→0.72(Gap=0.13)或0.72→0.45(Gap=0.27)
# 在最大Gap处截断 → 取前4个
方法3: Token预算约束
budget = 4000 # tokens
context_chunks = []
used_tokens = 0
for chunk in ranked_chunks:
if used_tokens + len(chunk.tokens) > budget:
break
context_chunks.append(chunk)
used_tokens += len(chunk.tokens)
方法4: LLM自适应判断
让LLM判断"是否还需要更多信息来回答这个问题"
→ 如果够了就停止检索
→ Self-RAG的核心思想(见知识点5)
知识点5: 高级检索模式
Self-RAG: 自适应检索
论文: "Self-RAG: Learning to Retrieve, Generate, and Critique" (2023)
核心思想: 让模型自己决定"要不要检索"、"检索到的有没有用"、"生成的对不对"
传统RAG: 无论什么问题,一律先检索再生成(有时候不需要检索)
Self-RAG: 模型在生成过程中插入"反思Token"来自我监督
流程:
Query进入
│
├── 模型判断: 需要检索吗?
│ ├── [Retrieve=Yes] → 执行检索
│ └── [Retrieve=No] → 直接生成(简单事实/模型已知的知识)
│
├── 检索后判断: 检索到的信息有用吗?
│ ├── [IsRelevant=Yes] → 使用该Chunk
│ └── [IsRelevant=No] → 丢弃该Chunk,可能再检索
│
├── 生成后判断: 生成的内容有事实依据吗?
│ ├── [IsSupported=Fully] → 保留
│ ├── [IsSupported=Partially] → 标注不确定部分
│ └── [IsSupported=No] → 重新生成
│
└── 最终判断: 回答整体质量如何?
├── [Utility=5] → 输出
└── [Utility<3] → 尝试不同的检索策略
优势: 减少不必要的检索(节省成本)、降低幻觉(自我验证)
劣势: 需要特殊训练、推理更复杂
Corrective RAG (CRAG): 带纠错的检索
论文: "Corrective Retrieval Augmented Generation" (2024)
核心思想: 对检索结果做质量评估,如果质量不够就"纠正"
流程:
Query → 检索Top-K
│
├── 评估每个Chunk的相关性(轻量级分类器)
│ ├── Correct: 高度相关 → 直接使用
│ ├── Ambiguous: 可能相关 → 提取关键信息后使用
│ └── Incorrect: 不相关 → 丢弃
│
├── 如果大部分Chunks都是Incorrect:
│ └── 触发Web Search作为补充(外部知识补救)
│
└── 用精炼后的Chunks生成回答
金融场景:
Query: "2025年Q4以太坊质押APY是多少?"
检索结果: [2023年的旧数据Chunk, 2024年的数据, 不相关的NFT文章]
CRAG评估: 全部Incorrect(过时+不相关)
纠正动作: 触发实时API调用获取最新数据
实现思路(LangGraph):
1. 检索节点 → 2. 评估节点(Grader) → 3a. 质量OK→生成
→ 3b. 质量差→Web Search→生成
Adaptive RAG: 智能路由
核心思想: 根据问题复杂度,选择不同的检索策略
问题分类:
├── 简单事实查询 → 直接RAG(单次检索+生成)
│ "USDC的发行方是谁?"
│
├── 多步推理 → 迭代RAG(检索→推理→再检索→最终回答)
│ "对比Aave和Compound的清算机制,哪个对借款人更友好?"
│
├── 实时数据 → 跳过RAG,直接调API
│ "ETH现在的价格是多少?"
│
└── 模型已知 → 跳过检索,直接生成
"什么是区块链?"
路由逻辑(可用LLM或分类器实现):
Input Query → Router
├── "no_retrieval" → LLM直接回答
├── "single_retrieval" → 标准RAG流程
├── "iterative_retrieval" → 多轮迭代RAG
└── "api_call" → 调用外部API
效果: 简单问题更快(跳过不必要的检索),复杂问题更准(多轮迭代)
RAPTOR: 树状递归检索
论文: "RAPTOR: Recursive Abstractive Processing for Tree-Organized Retrieval" (2024)
核心思想: 不只检索原始Chunks,而是构建一棵"摘要树"
构建过程:
Level 0 (叶子): 原始Chunks
[C1] [C2] [C3] [C4] [C5] [C6] [C7] [C8] ...
\ / \ / \ / \ /
Level 1 (摘要): 相邻Chunks聚类后LLM生成摘要
[S1=summary(C1,C2)] [S2=summary(C3,C4)] [S3=...]
\ / /
Level 2 (高层摘要): 进一步聚合
[SS1=summary(S1,S2,S3)]
...
Level N (根): 全文档摘要
检索时:
├── 宏观问题("这篇论文的核心贡献是什么?")
│ → 在高层(Level 2-N)检索 → 返回摘要
│
└── 细节问题("Table 3中的F1值是多少?")
→ 在底层(Level 0)检索 → 返回原始Chunks
优势: 同时支持宏观和微观问题,不像传统RAG只能检索局部Chunks
劣势: 索引构建成本高(需要大量LLM调用生成摘要)、存储量翻倍
适用: 长文档(100+页)、需要同时回答概述和细节问题的场景
ColBERT: Late Interaction (晚期交互)
ColBERT = Contextualized Late Interaction over BERT
定位: 在Bi-Encoder(快但不精)和Cross-Encoder(精但慢)之间的折中
原理:
Bi-Encoder: Query → [q_vec] Doc → [d_vec] → 1个score
(整体压缩成1个向量)
ColBERT: Query → [q1,q2,...qN] Doc → [d1,d2,...dM] → MaxSim
(每个token保留独立向量)
Cross-Encoder: [CLS] Query [SEP] Doc [SEP] → Transformer → score
(完整交叉注意力)
ColBERT的MaxSim计算:
对Query中的每个token qi:
找Document中与qi最相似的token dj → max_sim_i = max(sim(qi, dj))
最终分数 = sum(max_sim_i for all i)
→ Token级别的精确匹配,但Document向量可以预计算
→ 速度比Cross-Encoder快100x,精度比Bi-Encoder高10-15%
ColBERT v2 (2025更新):
- 残差压缩: 每个token向量从128维压缩到2字节,存储减少10x
- PLAID引擎: 优化的检索算法,速度提升3x
- 可以直接替代Bi-Encoder做第一阶段召回
知识点6: 检索质量评估
核心指标体系
检索评估 = 组件评估 + 端到端评估
组件评估(检索模块本身的质量):
├── Recall@K: Top-K结果中包含了多少真正相关文档
├── Precision@K: Top-K结果中有多少是真正相关的
├── MRR: 第一个相关结果出现在什么位置
├── NDCG: 考虑相关性等级的排序质量
└── MAP: 多个Query的平均精度
端到端评估(RAG系统最终回答的质量):
├── Answer Correctness: 答案是否正确
├── Faithfulness: 答案是否基于检索到的Context
├── Answer Relevancy: 答案是否回答了问题
└── Context Relevancy: 检索到的Context是否与问题相关
详解每个指标
1. Recall@K (召回率)
定义: 在Top-K个检索结果中,真正相关文档被找到的比例
公式: Recall@K = |Top-K ∩ 真正相关| / |真正相关|
例子:
真正相关文档: {A, B, C, D, E} (共5个)
Top-10检索结果: {A, X, B, Y, Z, C, W, V, U, T}
Recall@10 = |{A,B,C}| / |{A,B,C,D,E}| = 3/5 = 0.60
解读: 越高越好。Recall@10=0.80是生产级RAG的基本要求。
金融类比: 信贷审批时,是否把所有该查的征信记录都调出来了
2. MRR (Mean Reciprocal Rank, 平均倒数排名)
定义: 第一个正确结果排在第几位的倒数,多个Query取平均
公式: MRR = (1/N) × Σ(1/rank_i)
例子:
Query1: 第一个相关在第1位 → 1/1 = 1.00
Query2: 第一个相关在第3位 → 1/3 = 0.33
Query3: 第一个相关在第2位 → 1/2 = 0.50
MRR = (1.00 + 0.33 + 0.50) / 3 = 0.61
解读: 越高越好。MRR关注"第一个正确结果出现多快"。
金融类比: 客服系统中,用户第一眼看到的答案是否正确
3. NDCG@K (Normalized Discounted Cumulative Gain)
定义: 考虑文档相关性等级和位置的综合排序质量指标
关键: 不是简单的"相关/不相关",而是"多相关"(3=高度,2=一般,1=略相关,0=无关)
例子:
理想排序: [3, 3, 2, 2, 1, 0, 0, ...]
实际排序: [3, 1, 2, 0, 3, 2, 0, ...]
DCG = Σ(relevance_i / log2(i+1)) → 位置越靠后,折扣越大
NDCG = DCG / IDCG (除以理想DCG归一化到0-1)
解读: NDCG@10 ≥ 0.75 是好的检索系统。
金融类比: 投研报告推荐排序——最有投资价值的报告是否排在最前面
4. MAP (Mean Average Precision)
定义: 在每个相关文档出现的位置计算Precision,取平均
适合: 需要找到多个相关文档的场景
例子:
排序: [R, X, R, X, X, R] (R=相关, X=无关)
P@1 = 1/1, P@3 = 2/3, P@6 = 3/6
AP = (1 + 0.67 + 0.50) / 3 = 0.72
组件评估 vs 端到端评估
为什么两种评估都要做:
只做组件评估的问题:
检索Recall@10 = 0.95(很好!)
但最终回答质量很差
→ 可能是Context Assembly或LLM Prompt有问题
只做端到端评估的问题:
最终回答质量好
但不知道哪个环节贡献最大
→ 改进时无从下手
最佳实践: 分层评估
Layer 1: 检索质量 → Recall@K, MRR, NDCG
Layer 2: Rerank质量 → NDCG@K (Rerank前 vs 后)
Layer 3: Context质量 → Context Relevancy, Context Recall
Layer 4: 生成质量 → Faithfulness, Correctness, Relevancy
金融类比:
风控系统评估不能只看"坏账率":
├── 模型层: AUC/KS (模型区分力)
├── 策略层: 通过率/拒绝率 (策略合理性)
├── 业务层: 坏账率/利润率 (业务结果)
└── 每层独立评估 + 端到端贯通
构建评估数据集(Golden Set)
Golden Set = 标注好的 {Query, 正确Answer, 相关Chunks} 三元组
构建方法:
方法1: 人工标注(金标准)
- 业务专家对100-300个Query标注正确答案和相关来源
- 成本高但最准确
- 金融场景建议: 至少让2位分析师交叉验证
方法2: LLM辅助生成(经济方案)
- 给LLM一段文档,让它生成"可以用这段文档回答的问题"
- 人工审核筛选
- 成本低但可能有偏差(LLM倾向生成简单问题)
方法3: 用户日志挖掘(实战方案)
- 从生产环境收集真实Query
- 人工标注哪些回答是正确的
- 最贴近真实分布
推荐: 方法2生成初始集 + 方法3持续补充 + 方法1审核
数量建议:
开发阶段: 50-100个Golden Queries
上线前: 200-300个Golden Queries
持续运营: 每月新增20-50个(覆盖新场景)
A/B测试框架
RAG系统的A/B测试比Web A/B测试更复杂:
测试维度:
├── 检索策略A/B: Hybrid Search vs 纯Dense Search
├── Reranker A/B: Cohere vs BGE-Reranker
├── Chunking策略A/B: 512 vs 1024 tokens
├── Prompt模板A/B: 不同的System Prompt
└── 端到端A/B: 完整Pipeline A vs Pipeline B
指标采集:
├── 自动化指标: Recall@K, NDCG, Faithfulness(RAGAS自动评估)
├── 人工评估: 正确性、有用性(1-5分人工打分)
└── 用户行为: 点赞/点踩、追问率、会话长度
实施注意:
1. 同一个Query在A/B两组都要跑(Paired test)
2. 至少200个不同Query才有统计显著性
3. 区分"检索变好了"和"碰巧测试集简单了"
4. 金融场景: 错误答案的代价不对称(投资建议错了比不回答更糟)
→ 除了平均分,还要关注"严重错误率"
今日思考
思考1: 检索优化的ROI问题
问题: 团队资源有限,应该在检索Pipeline的哪个环节投入最多?
我的思考:
从投入产出比(ROI)角度排序:
1. 加Reranker(低投入,高回报): 一行代码加Cohere Rerank API,Precision提升15-25%
2. Hybrid Search(中投入,稳定回报): 需要维护BM25索引,但Recall提升10-20%
3. Query Rewriting(低投入,场景化回报): 对口语化Query效果显著,对已经很清晰的Query提升小
4. 高级模式(高投入,长期回报): Self-RAG/CRAG需要更多工程投入,但能解决长尾问题
金融类比: 就像银行做风控优化——
→ 先上规则引擎(快速见效) → 再加模型评分(中期提升) → 最后做实时特征工程(长期竞争力)
→ 不要一上来就追求最复杂的方案
思考2: 检索精度 vs 检索召回的取舍
问题: Precision和Recall不可兼得时,RAG应该偏向哪个?
我的思考:
取决于场景的"错误成本":
高Recall优先(宁可多不可少):
→ 金融合规查询: 漏掉一条监管规定可能导致处罚
→ 医疗问答: 漏掉一个禁忌症可能危及生命
→ 策略: 粗筛阶段设宽松阈值,Reranking阶段再精筛
高Precision优先(宁可少不可错):
→ 客服自动回复: 推送错误信息损害品牌(不如说"我不确定")
→ 投资建议: 给出错误的数据可能导致亏损
→ 策略: 设高阈值,低于阈值就不检索/不回答
RAG场景的特殊性:
传统搜索: 用户可以自己从结果列表中筛选(Precision要求低)
RAG: LLM直接基于检索结果生成答案(Precision要求高——垃圾进垃圾出)
→ 所以RAG通常更强调Precision,这也是Reranking如此重要的原因
思考3: 未来趋势判断
问题: 2025-2026年,RAG检索技术的演进方向?
我的判断:
1. 长上下文会取代简单RAG吗?
→ 不会完全取代。Gemini 2.0 10M tokens很强,但:
a) 成本: 把100万Chunks全塞进去太贵
b) 精度: 长上下文中找信息不如专门的检索系统精准
c) 实时性: 向量索引可以秒级更新,重跑10M context不行
→ 趋势: 长上下文 + RAG混合(小知识库直接塞,大知识库走RAG)
2. Agentic RAG将成为主流
→ 从"一次检索就生成"进化到"多轮检索-推理-反思"
→ Self-RAG/CRAG是早期形态,Agent+RAG是终极形态
→ LLM自主决定何时检索、检索什么、是否足够
3. 端到端学习的检索(Learned Retrieval)
→ 不是分别优化Embedding、Reranker、LLM
→ 而是端到端训练:让LLM的反馈信号传导到Retriever
→ Google的RETRO/Atlas已经探索这个方向
4. 多模态检索
→ 不只检索文本Chunks,还检索图表、代码、结构化数据
→ Vision-Language模型让图表检索成为可能
面试表达模板
问题: "你们的RAG检索效果怎么优化的?"
框架回答 (2分钟版):
"我们的RAG检索优化分四个阶段推进:
第一,Query理解层。用户的原始问题往往模糊或口语化,我们做了Query Rewriting,
用LLM把口语化查询改写成检索友好的形式。对于复杂问题,还做了Query Decomposition,
拆解成子问题分别检索。这一步把Recall提升了约15-20%。
第二,多路召回。不是只用向量搜索,而是Dense + BM25的Hybrid Search。
向量搜索擅长语义匹配,BM25擅长精确关键词匹配(比如专有名词、合约地址)。
用RRF融合两路结果,Recall又提升了10%左右。
第三,两阶段Reranking。粗筛阶段召回Top-100,然后用Cross-Encoder Reranker
对这100个候选精确打分,取Top-10。Reranker能做Query和Document的深度语义交互,
比纯向量相似度精确得多。这一步Precision提升了约20%。
第四,Context Assembly。对精排后的Chunks做去重、逻辑排序、Token预算控制。
最相关的放开头(应对Lost-in-the-Middle),控制总长度在4000 tokens以内。
整个Pipeline用RAGAS框架做评估,Recall@10从基线的0.65提升到0.85,
Faithfulness从0.72提升到0.91。"
问题: "为什么需要Reranker?直接用更好的Embedding模型不行吗?"
回答:
"这是个好问题。更好的Embedding确实能提升检索质量,但Reranker解决的是
Embedding从根本上无法解决的问题。
Embedding模型(Bi-Encoder)的工作方式是把Query和Document分别压缩成一个固定长度的
向量,然后算相似度。这个过程有信息损失——一段500字的文档压缩成768维向量,
细节必然丢失。而且Query和Document是独立编码的,没有交叉注意力。
Reranker(Cross-Encoder)是把Query和Document拼在一起送入模型,每个token之间
都有完整的Attention交互。它能理解'Query问的是A,Document恰好回答了A'这种
精确的问答对应关系。
但Cross-Encoder不能预计算,对100万文档全部做Rerank需要几十分钟,不可接受。
所以工业界的标准做法是两阶段:先用Embedding快速召回Top-100(毫秒级),
再用Reranker精排到Top-10(约1秒)。这样兼顾了速度和精度。
用数字说话:我们实测,只换更好的Embedding(从BGE-small到BGE-large),
NDCG@10提升了约5%。加上Reranker后,NDCG@10额外提升了15%。
两者不是替代关系,而是互补。"
问题: "Self-RAG和CRAG是什么?你在生产环境用过吗?"
回答:
"Self-RAG和CRAG都是2023-2024年提出的高级RAG范式,核心思想都是
给RAG加上'自我反思'能力。
Self-RAG让模型自己判断三件事:是否需要检索、检索到的是否有用、生成的是否可靠。
通过在训练时加入特殊的反思Token来实现。好处是减少了不必要的检索(节省成本),
也降低了幻觉(自我纠错)。
CRAG更实用一些——它对检索结果做质量评估,如果判断检索到的信息不够好,
会触发补充检索(比如Web Search)来纠正。
在生产环境,我们没有直接用原论文的Self-RAG(需要特殊训练),但借鉴了它的思想:
用LangGraph实现了一个Corrective流程——检索后加一个轻量级的相关性评估节点,
如果评估分低于阈值,就触发二次检索(换个Query策略或扩大检索范围)。
这个方案实现简单但效果显著,把长尾Query的回答质量提升了约25%。"
实操练习建议
1. 实现两阶段检索Pipeline:
→ 用LlamaIndex或LangChain搭建: BM25 + Dense Hybrid Search
→ 加上Cohere Rerank或BGE-Reranker
→ 对比加Reranker前后的Recall@10和NDCG@10
2. 测试Query转换策略:
→ 对同一批测试Query,分别用原始Query/Rewritten/HyDE/Multi-Query检索
→ 对比Recall差异
→ 找到你的数据集上效果最好的策略组合
3. 用LangGraph实现Corrective RAG:
→ 检索 → 评估(LLM给相关性打分) → 分支(合格→生成 / 不合格→Web搜索→生成)
→ 测试几个故意用过时信息的Query
4. 搭建评估Pipeline:
→ 构建50个Golden Query (Q+A+相关Chunks)
→ 用RAGAS评估Recall/Faithfulness/Relevancy
→ 建立Baseline,为后续优化提供对比基准
资源推荐
| 资源 | 说明 | 链接 |
|---|---|---|
| Self-RAG论文 | 自适应检索+反思生成 | arxiv.org/abs/2310.11511 |
| CRAG论文 | 纠错式检索增强生成 | arxiv.org/abs/2401.15884 |
| RAPTOR论文 | 树状递归检索 | arxiv.org/abs/2401.18059 |
| ColBERT v2 | Late Interaction检索 | github.com/stanford-futuredata/ColBERT |
| Lost-in-the-Middle论文 | LLM对长上下文中间部分注意力下降 | arxiv.org/abs/2307.03172 |
| LangChain Retrieval文档 | Query转换/Hybrid Search/Reranking实现 | python.langchain.com/docs/how_to/#retrievers |
| LlamaIndex Reranking指南 | Reranker集成与对比 | docs.llamaindex.ai |
| RAGAS文档 | RAG评估框架 | docs.ragas.io |
| Cohere Rerank API | 商业Reranker(效果最佳之一) | docs.cohere.com/docs/reranking |
| BGE-Reranker | BAAI开源Reranker(中文最佳) | huggingface.co/BAAI/bge-reranker-v2-m3 |
明日预告
Day 21: 生产级RAG(3) — 评估框架与持续迭代
预习问题:
1. RAGAS框架的四个核心指标(Faithfulness/Relevancy/Correctness/Context)如何计算?
2. "Vibe评估" vs 系统化评估有什么区别?为什么直觉不可靠?
3. RAG系统上线后,如何建立持续迭代的飞轮?
连接点:
Day 19(解析+Chunking) → Day 20(检索+Reranking) → Day 21(评估+迭代)
→ Day 19确定数据质量上限
→ Day 20确定检索精度
→ Day 21回答"怎么知道做得好不好"和"怎么持续变好"
→ 三天合起来 = 一套完整的生产级RAG工程方法论