RAG 检索层实战:Redis 缓存 + ES 混合检索 + BGE-Rerank 重排全链路落地与踩坑

前言

近期基于 FastAPI + LangGraph 自研智能客服 RAG 平台,初期仅使用 Chroma 向量检索存在两大痛点:

  1. 单纯向量检索关键词召回能力弱,大量业务相关文档漏召,回答准确率偏低;
  2. 高频重复问答、会话历史频繁查询数据库与向量引擎,接口响应慢、频繁触发大模型 API 限流。

为此搭建一套分层优化架构:ES和Chroma 做粗粒度多路召回、BGE-Rerank 做语义精排、Redis 多层缓存减负,完整覆盖检索、排序、性能优化全流程。本文结合项目源码,拆解整套方案设计、实现细节与线上踩坑经验,所有代码均已开源。

一、整体架构链路

用户问答请求完整执行流程:

markdown 复制代码
用户提问 → Redis缓存判断
    ├─ 缓存命中:直接返回历史回答,终止流程
    └─ 缓存未命中 → ES 关键词+向量混合粗召回 → 多路文档合并去重
        → BGE-Rerank 相关性打分、过滤低相关分片
        → 截取高分片段拼接上下文送入LLM
        → 生成回答存入Redis缓存,返回前端

各组件分工:

表格

组件 核心职责 数据结构
Redis 会话缓存、问答结果缓存、热门分片缓存 String、Set,差异化过期时间
Elasticsearch 全文关键词 + 向量混合粗召回,存储全部知识库分片 双路召回+IK分词
BGE-Rerank 对召回候选文档做语义精细打分、过滤无关内容 本地离线推理,限制候选数量控制耗时

二、Elasticsearch 混合检索实现

2.1 设计思路

单一向量检索缺少关键词匹配能力,纯文本检索缺少语义理解,采用双路召回逻辑进行检索

  1. 同时检索文本相似度与向量相似度,两路结果融合,扩大候选文档覆盖范围;
  2. 限制单次召回 20 条分片,平衡召回完整性与后续重排推理耗时。

2.2 核心简化代码

ini 复制代码
# 异步ES混合检索示例
async def adual_retrieve(self, query: str, user_id: int, top_k_es=20) -> list[Document]:
    skip_filter = (user_id == 0)
    retriever = self.get_retriever(user_id)
    vector_docs = await retriever.ainvoke(query)
    # 异步ES
    es_hits = await es_client.abm25_search(
        query=query,
        user_id=user_id,
        top_k=top_k_es,
    )
    es_docs = []
    for hit in es_hits:
        doc = Document(
            page_content=hit["content"],
            metadata={
                "source": hit["source"],
                "file_md5": hit["file_md5"],
                "user_id": hit["user_id"],
                "chunk_idx": hit["chunk_idx"],
                "_score": hit["_score"]
            }
        )
        es_docs.append(doc)
    seen_keys = set()
    merged = []
    for d in vector_docs + es_docs:
        md5 = d.metadata.get("file_md5")
        idx = d.metadata.get("chunk_idx")
        if md5 and idx is not None:
            key = f"{md5}_{idx}"
            if key in seen_keys:
                continue
            seen_keys.add(key)
        merged.append(d)
    return merged

2.3 踩坑总结

  1. 无分词器:原生 ES 对中文切割混乱,必须安装 IK 分词;
  2. 频繁重复检索:高频相同 query 反复请求 ES,造成接口延迟升高,上层依赖 Redis 缓存拦截。

三、BGE-Rerank 重排精排优化

3.1 为什么需要重排

ES 粗召回会返回 20 条候选文档,其中包含大量语义无关、弱相关分片,直接送入大模型会产生两个问题:

  1. 无关文本占用 Token,增加调用成本;
  2. 干扰 LLM 判断,容易生成幻觉、答非所问。

重排模型输入「用户问题 + 单条文档」,计算精准相关性分数,过滤低分内容,保留高价值上下文。

3.2 实现逻辑

  1. ES 召回 20 条文档,提取纯文本列表;
  2. 批量送入 BGE-rerank-base-v2 模型打分;
  3. 文本与分数绑定排序,按自定义阈值剔除低相关内容;
  4. 截取前 5 条高分文档还原完整 Document 对象,拼接上下文。

3.3 核心代码

python 复制代码
from FlagEmbedding import FlagReranker

class RerankClient:
    def __init__(self):
        self.ENABLE = True
        self.reranker = FlagReranker("BAAI/bge-reranker-base-v2")

    def rerank(self, query: str, text_list: list[str], top_k=5, threshold=-5.0):
        # 降级兜底
        if not self.ENABLE or self.reranker is None or len(text_list) == 0:
            return text_list[:top_k]
        
        # 批量打分
        pairs = [[query, text] for text in text_list]
        scores = self.reranker.compute_score(pairs)
        text_score = list(zip(text_list, scores))
        # 分数降序
        text_score.sort(key=lambda x: x[1], reverse=True)
        # 阈值过滤
        filter_text = [t for t, s in text_score if s >= threshold]
        return filter_text[:top_k]

3.4 关键工程处理

  1. 异步线程隔离:重排是 CPU 密集计算,使用 asyncio.to_thread 执行,不阻塞 FastAPI 异步事件循环;
  2. 自动降级:模型加载失败、内存不足时直接跳过重排,使用原始召回结果,保证服务不中断;
  3. 候选数量控制:召回上限 20 条,数量过多会大幅增加推理耗时。

四、Redis 多层缓存架构设计

4.1 三级缓存策略

  1. 对话会话缓存 以会话 ID 为 key,缓存历史对话记录,减少 SQLite 频繁读写,消除数据库锁报错;过期 2 小时。
  2. 问答结果缓存 对完全一致的用户问题缓存完整回答,规避大模型 API 重复调用、缓解 429 限流;过期 12 小时。
  3. 热门知识库分片缓存 高频检索文档存入 Redis,避免重复请求 ES,降低检索耗时。

4.2 核心特性

  1. 连接池复用:全局单例 Redis 客户端,复用连接减少握手开销;
  2. 差异化过期时间:会话短期失效、知识库分片长期缓存;
  3. 容错重试:Redis 连接失败自动降级,走原始 ES 检索链路;
  4. Set 集合去重:缓存文档 ID,避免重复加载相同分片。

五、通用落地经验总结

  1. RAG 检索分层标准范式:粗召回 (ES) → 精排 (Rerank) → 缓存削峰 (Redis) ,兼顾召回精度与服务性能;
  2. 分层容错设计是生产必备:向量引擎、重排模型、缓存任一组件异常均有兜底逻辑,不会导致服务不可用;
  3. 重排不要无限增加候选数量,20 条以内是速度与效果平衡点;
  4. 缓存核心解决两大问题:数据库压力、大模型 API 限流,是低成本性能优化方案。

六、后续优化方向

  1. 数据库升级:SQLite 替换为 PostgreSQL,支持高并发多用户访问;
  2. Redis 集群部署:单机 Redis 扩容为集群,支持分布式多实例项目;
  3. Rerank 模型轻量化 / 微调:针对业务领域语料微调,进一步提升行业文档匹配精度;
  4. 接入 LangSmith 全链路监控,可视化缓存命中率、重排耗时、检索召回指标。

结尾

整套检索优化方案完整开源在 Gitee, 在企业级知识库场景中,ES 混合检索 + BGE 重排是提升回答质量的标准方案,搭配 Redis 缓存可低成本解决并发性能瓶颈。 欢迎评论区交流你们在 RAG 落地过程中遇到的检索、缓存相关问题!

相关推荐
️学习的小王9 小时前
Git项目提交忽略文件怎么做?以Python项目为例,详解.gitignore
git·python·elasticsearch
Elasticsearch10 小时前
安装 Elasticsearch
elasticsearch
南梦浅10 小时前
GitLab 企业级实操手册:从基础命令到生产环境安全回退(revert 完整流程)
大数据·elasticsearch·搜索引擎
Elasticsearch11 小时前
Elastic:防范由 AI 驱动的社会工程攻击
elasticsearch
Elasticsearch16 小时前
避免和纠正热点:Elasticsearch Serverless 如何平衡分片
elasticsearch
Elastic 中国社区官方博客17 小时前
机构如何统一智慧城市数据以改善公共服务?
大数据·人工智能·物联网·elasticsearch·搜索引擎·全文检索·智慧城市
阿里云大数据AI技术1 天前
Agentic Search 2.0:从单轮对话迈向企业级 AI 搜索自动驾驶Agent
人工智能·elasticsearch·agent
Elasticsearch1 天前
你的 AI agent 需要一个不在场证明:Elastic 中 Agent Builder 的可观测性和审计追踪
elasticsearch
Elasticsearch2 天前
仪表板活动日志:了解哪些 Kibana 仪表板会被使用
elasticsearch