当前位置: 首页 > news >正文

Hybrid Search + RRF + Reranker:打造电商 RAG 的精准检索三件套

文章目录

  • 前言
    • 为什么需要混合检索?
    • 整体架构:三段式流水线
    • Stage 1:并行检索
      • 1.1 向量搜索 — `vectorSearch()`
      • 1.2 关键词搜索 — `keywordSearchWithScore()`
    • Stage 2:RRF(Reciprocal Rank Fusion)融合
    • Stage 3:LLM Reranker
      • 设计思路
      • Prompt 设计
      • 防抖设计
    • 完整流程:`searchSimilarDocuments()`
      • 几个关键设计决策
    • Embedding 缓存层
    • 总结
    • 下阶段优化建议
      • 2. Documents API 迁移到 Hybrid Search
      • 3. Query Rewriting(查询改写)
      • 4. 多轮对话上下文增强
      • 5. 检索结果缓存
      • 6. Chunk 上下文窗口(Contextual Retrieval)
      • 7. 元数据过滤增强
      • 8. 评估体系搭建
    • 优先级总结

前言

喜欢公众号阅读的玩家🚀https://mp.weixin.qq.com/s/TM0hTLJMcXFY0yWdvYEYYw

之前的AI客服系统,电商 RAG 这一块
1.支持上传相关知识库,实现了向量搜索,可以语义咨询
2.优化了用户query,这里使用的nodejieba库,去掉无关的噪音询问与提取关键分词,如:你好啊,我需要退货。这里的关键词就是 “退货“。
3.添加了知识库文档召回逻辑,让回答更加匹配。
但是这里还是有一些问题,例如有时候用户问:苹果15,这样比较精准的词,走精准匹配搜索比较合适,而不是向量搜索。
解决这类问题的方案:Hybrid Search + RRF + Reranker。接下来我们具体讨论下。


为什么需要混合检索?

在构建 RAG(Retrieval-Augmented Generation)系统时,检索质量决定了最终回答的上限。单一检索方式各有短板:

检索方式优势劣势
向量搜索语义理解强,能匹配同义词、近义表达对精确关键词(如产品型号 “SKU-8843”)可能漏检
关键词搜索精确匹配专有名词、数字、ID无法理解同义词,命中不到语义相近但用词不同的文档

电商场景尤其典型—— 用户可能问"iPhone 15 Pro 多少钱",也可能问"那款苹果最新手机的价位"。前者需要关键词精确匹配型号,后者需要语义理解"苹果"对应"iPhone"。

Hybrid Search(混合搜索)正是解法:同时执行向量和关键词两路检索,再通过算法融合排序,取长补短。


整体架构:三段式流水线

RRF 融合,大白话理解,就是它把每个检索器的分数,根据公式重新算分。
k值一般固定。

用户 Query │ ▼ ┌──────────────────────────────────────────────┐ │ Stage1:并行检索 │ │ ┌──────────┐ ┌──────────────────┐ │ │ │ 向量搜索 │ │关键词搜索(BM25)│ │ │ │ pgvector │ │ILIKE+jieba │ │ │ │ recall=3x│ │ recall=3x │ │ │ └────┬─────┘ └────────┬─────────┘ │ └───────┼──────────────────────────┼───────────┘ │ │ ▼ ▼ ┌──────────────────────────────────────────────┐ │ Stage2:RRF融合 │ │1/(k+rank_vector)+1/(k+rank_keyword)│ │ merge&sort │ │ top-10候选 → │ └──────────────────────┬───────────────────────┘ │ ▼ ┌──────────────────────────────────────────────┐ │ Stage3:LLMReranker │ │ qwen3-8b对候选文档打分(0-10)│ │ 按 relevance 精排 │ │ → top-5结果 │ └──────────────────────────────────────────────┘

Stage 1:并行检索

1.1 向量搜索 —vectorSearch()

核心思路:

  1. 将 query 转为 1536 维向量(使用qwen/qwen3-embedding-8b模型)
  2. 通过 pgvector 的余弦距离操作符<=>DocumentChunk表中检索
  3. 召回topK × 3数量的候选(多召回方便后续融合)
  4. 过滤similarity < minSimilarity (0.35)的低质量结果

关键 SQL:

SELECTdc.id,dc.title,dc.content,...,1-(dc.embedding<=>$queryEmbedding::vector)ASsimilarityFROM"DocumentChunk"dcJOIN"Document"dONd.id=dc."documentId"WHEREdc.embeddingISNOTNULLORDERBYdc.embedding<=>$queryEmbedding::vectorLIMIT$recallCount

使用<=>余弦距离并用1 - distance转换为相似度 [0, 1]。

1.2 关键词搜索 —keywordSearchWithScore()

两步走:关键词提取 → BM25 风格打分

Step 1:jieba TF-IDF 提取关键词

constkeywords=jieba.extract(text,5).map(k=>k.word).filter(word=>!STOP_WORDS.has(word)&&!/^\d+$/.test(word))

使用 nodejieba 的 TF-IDF 算法提取 Top-5 关键词,并过滤掉"的、了、是"等停用词和纯数字。

Step 2:ILIKE 数据库召回

SELECTdc.id,dc.title,dc.content,...FROM"DocumentChunk"dcJOIN"Document"dONd.id=dc."documentId"WHEREdc.titleILIKEANY($likePatterns)ORdc.contentILIKEANY($likePatterns)ORDERBYdc."createdAt"DESCLIMIT$recallCount

每个关键词转为%keyword%通配模式,使用 PostgreSQLILIKE ANY()批量匹配,不区分大小写。

Step 3:BM25 风格评分

// 归一化到分块长度,避免长文档天然高分constnormalizedScore=matchCount/Math.sqrt(doc.length)

核心思想:

  • 关键词在title中每命中一次权重×3
  • 关键词在content中统计出现次数
  • Math.sqrt(docLength)做长度归一化
  • 最终分数做[0, 1]Min-Max 归一化

Stage 2:RRF(Reciprocal Rank Fusion)融合

两路检索各自返回一个排序列表,需要合并为一个统一排序。RRF 是业界验证的简洁有效方案:

RRF_score(d)=Σ1/(k+rank_i(d))

其中rank_i(d)是文档d在第i个检索结果中的排名,k是常数(本文取 60)。

为什么k=60比较小?

较小的k让 keyword 结果在融合时权重更高。这对于中文电商场景很有意义——精确的产品型号、规格参数必须优先保证不会在向量搜索中"丢失"。

functioncomputeRRFScores(vectorRanks:RankedItem[],keywordRanks:RankedItem[],):Map<string,{rrfScore:number;vectorRank:number|null;keywordRank:number|null}>{constscoreMap=newMap()// 登记两路结果的排名for(constitemofvectorRanks){scoreMap.set(item.id,{vectorRank:item.rank,keywordRank:null,rrfScore:0})}for(constitemofkeywordRanks){constexisting=scoreMap.get(item.id)if(existing){existing.keywordRank=item.rank// 两路都有 = overlap}else{scoreMap.set(item.id,{vectorRank:null,keywordRank:item.rank,rrfScore:0})}}// 计算 RRF 得分for(const[,scores]ofscoreMap){letscore=0if(scores.vectorRank!==null)score+=1/(RRF_K+scores.vectorRank)if(scores.keywordRank!==null)score+=1/(RRF_K+scores.keywordRank)scores.rrfScore=score}returnscoreMap// 按 rrfScore 降序排列}

RRF 的优势:

  • 不需要对两路分数做归一化(向量余弦相似度和关键词 BM25 分数尺度不同)
  • 仅依赖排名,天然抗异常值
  • 计算量极小,无外部依赖

Stage 3:LLM Reranker

RRF 融合后得到 top-10 候选,但它们仍是"机械组合"。当候选数多于最终需要的数量时(比如需要 5 个结果),让 LLM 再做一次精排。

设计思路

exportasyncfunctionrerankResults(query,candidates,options={}){// 候选数 ≤ 目标数 → 跳过,直接返回if(candidates.length<=topK)returncandidates.slice(0,topK)// 构建 prompt,让 LLM 打分constprompt=buildPrompt(query,candidates)constresponse=awaitopenai.chat.completions.create({model:'qwen/qwen3-8b',messages:[{role:'user',content:prompt}],temperature:0.1,// 低温度,稳定输出max_tokens:500,// 控制成本response_format:{type:'json_object'},// 要求返回 JSON})// 解析打分结果,按 relevance 降序重排constparsed=JSON.parse(content)constreordered=candidates.map((doc,i)=>({...doc,similarity:relevanceMap.get(i)/10// 归一化})).sort((a,b)=>b.similarity-a.similarity)returnreordered.slice(0,topK)}

Prompt 设计

LLM 对每个候选文档从 0 到 10 打分,并给出简短理由:

你是一个文档相关性评估专家。请判断以下文档与用户问题的相关程度。 用户问题: "{query}" 打分标准: - 0 = 完全不相关 - 5 = 部分相关 - 10 = 高度相关,直接回答用户问题 只返回一个 JSON 对象: {"scores": [{"index": 0, "relevance": 8, "reason": "简短理由"}, ...]}

防抖设计

整个 reranker 处处有兜底 — 任何环节失败都 fallback 到原始排序:

  • JSON 解析失败 → 返回原顺序
  • LLM 返回空 → 返回原顺序
  • API 调用异常 → 返回原顺序

Reranker 默认关闭,通过RERANKER_ENABLED=true环境变量或调用参数显式打开。开启后约增加 200ms 延迟和少量 token 消耗。


完整流程:searchSimilarDocuments()

exportasyncfunctionsearchSimilarDocuments(query,options={}){const{mode='hybrid',reranker,topK=5}=options// 1. 并行检索const[vectorResults,keywordResults]=awaitPromise.allSettled([vectorSearch(query,{topK,...}),keywordSearchWithScore(query,{topK,...}),])// 2. RRF 融合constrrfScores=computeRRFScores(vectorRanks,keywordRanks)letfinal=merged.sort(byScore).slice(0,HYBRID_TOP_K)// top-10// 3. LLM Reranker(可选)if(reranker&&final.length>topK){final=awaitrerankResults(query,final,{topK})}else{final=final.slice(0,topK)}returnfinal}

几个关键设计决策

1.Promise.allSettled而非Promise.all

两端检索独立运行,任意一端失败不阻塞另一端。比如关键词提取失败(jieba 抽取不到有效关键词),向量搜索结果仍然可用。

2. 3 倍召回乘数

constVECTOR_RECALL_MULTIPLIER=3constKEYWORD_RECALL_MULTIPLIER=3

两路各自召回topK × 3条结果,给 RRF 融合和 reranker 留足筛选空间。

3. 文本降级兜底

当两路检索都返回空时(例如 jieba 抽取不到有效关键词 + 向量相似度过低),系统会触发原始 ILIKE 降级查询,确保不出现"零结果"。


Embedding 缓存层

每次调用generateEmbedding()前先查EmbeddingCache表,命中则直接返回,节省 API 调用和延迟:

exportasyncfunctiongenerateEmbedding(text:string):Promise<number[]>{consttextHash=hashText(text)// MD5 哈希// 查缓存constrows=awaitprisma.$queryRaw`SELECT "embedding"::text FROM "EmbeddingCache" WHERE "textHash" =${textHash}AND "model" =${modelToUse}LIMIT 1`if(rows.length>0)returnparseEmbedding(rows[0].embedding)// 调 API 并写入缓存constembedding=awaitopenai.embeddings.create({...})awaitprisma.$executeRaw`INSERT INTO "EmbeddingCache" (...) VALUES (...) ON CONFLICT ("textHash", "model") DO NOTHING`returnembedding}

总结

这套Hybrid Search + RRF + Reranker三段式架构,在电商 RAG 场景下解决了单一检索的痛点:

阶段职责技术选型
向量搜索语义检索pgvector + qwen3-embedding-8b
关键词搜索精确匹配jieba TF-IDF + ILIKE + BM25 评分
RRF 融合两路结果合并Reciprocal Rank Fusion (k=60)
LLM Reranker精排qwen3-8b 打分 (0-10),兜底保障

关键工程实践:

  • 并行检索用Promise.allSettled做容错
  • 3 倍召回留足候选空间
  • 零结果时触发文本降级兜底
  • Reranker 层层 fallback 不死链
  • Embedding 缓存避免重复 API 调用

下阶段优化建议

2. Documents API 迁移到 Hybrid Search

现状:[documents/route.ts 的 GET](file:///Users/linruitao/Documents/100-study/200-reactjs/next-mobile/src/app/api/documents/route.ts#L26-L75) 仍使用老旧的单路向量搜索,未复用searchSimilarDocuments()

建议:将 documents 搜索也走统一的 hybrid search,保持检索口径一致。

3. Query Rewriting(查询改写)

问题:用户输入"那个苹果手机多少钱",其中"那个"是口语化指代,jieba 可能提取出"苹果手机"但丢失了指代消解的需求。

方案:在检索前增加一个轻量级 LLM query rewriting 步骤:

原始: "那个苹果手机多少钱" 改写: "iPhone 最新款 价格"

可以用极低成本模型(如qwen/qwen3-8b,单次 < $0.001)做一次小请求,显著提升关键词提取和向量检索质量。与 reranker 形成互补:rewriter 改善召回,reranker 改善排序

4. 多轮对话上下文增强

现状:[chat/route.ts](file:///Users/linruitao/Documents/100-study/200-reactjs/next-mobile/src/app/api/chat/route.ts#L130-L132) 只用最后一条 user 消息做检索:

constlastUserMessage=[...enhancedMessages].reverse().find((msg)=>msg.role==='user')

问题:多轮对话中,用户可能说"那个怎么退款?",缺少上文"我刚买了一件衣服"的上下文。

方案

  • 将最近 2-3 轮对话拼接为检索 query
  • 或对完整对话历史做 LLM 摘要,用摘要代替单条消息检索

5. 检索结果缓存

问题:同一 query 反复请求会重复执行完整的三段式流水线(embedding 已有缓存,但检索本身无缓存)。

方案:对<query, topK, category>做短期缓存(TTL 5-10 分钟),可以用 Redis 或内存 LRU。电商场景下用户常问相同问题(“退货政策”“运费多少”),命中率预计 20-30%。

6. Chunk 上下文窗口(Contextual Retrieval)

问题:当前分块互相独立,检索只返回匹配的那一块,丢失了前后文。

方案:返回匹配块的同时,附带前一个和后一个 chunk 作为上下文窗口。成本极低(仅多查两条 SQL),但显著提升 LLM 对文档的整体理解。

7. 元数据过滤增强

现状:仅支持category过滤。

方案:扩展为支持多维度过滤(tagsdateRangecontentType、自定义 metadata key),让检索在缩小范围的同时保持精度。

8. 评估体系搭建

问题:无法量化"这套检索效果好不好"。

方案:构建一个小型 benchmark:

  • 准备 30-50 个 QA pair(问题 + 期望文档 ID)
  • 计算 Recall@5、MRR、NDCG@5
  • 每次优化后跑一遍,用数据而非感觉做决策

优先级总结

优先级任务预期收益实施成本
P0接入 Reranker 到 Chat检索精度立刻提升低(改 1 行)
P0Documents API 迁移 Hybrid统一检索口径
P1Query Rewriting提升口语化查询召回中(增加一次 LLM 调用)
P1多轮上下文增强改善对话连续性
P1Chunk 上下文窗口改善 LLM 理解
P2检索结果缓存降低延迟和成本
P2元数据过滤增强精准过滤
P3评估体系量化优化效果高(准备数据)

http://www.gsyq.cn/news/1465990.html

相关文章:

  • 2026 张家界防水补漏三家品牌横向测评:厨卫屋面地下室修缮哪家靠谱?吉修匠 99.8 分五星稳居榜首 - 吉修匠
  • DenseNet实战:用TensorFlow 2.x在小型数据集上做图像分类,参数少效果也不错
  • 不只是驱动问题:深度解析TI XDS100仿真器EEPROM数据损坏的根源与预防
  • 跳出传统 Agent 桎梏,浅析代码即智能体的底层运行逻辑与落地实践
  • MuleSoft企业级AI编排:让大模型真正融入ERP/CRM核心业务流
  • 2026年高县亲子水上乐园选型指南:龙源溪山泉水乐园深度评测 - 企业名录优选推荐
  • 别再傻傻分不清了!SCI、EI、IEEE到底该投哪个?给研究生和工程师的选刊避坑指南
  • 2026 黄石防水补漏三家品牌横向测评:厨卫屋面地下室修缮哪家靠谱?吉修匠 99.8 分五星稳居榜首 - 吉修匠
  • CMOS图像传感器硬件设计参考图集:含像素结构、读出电路与接口连接详解
  • 宿舍党福音:用40块的斐讯K2+Padavan搞定校园网锐捷6.41认证(静态IP版)
  • C++嵌入式智能车自动驾驶工程包,含双分支开发目录与可编译源码
  • 从‘老师点名’到芯片调度:用生活例子彻底搞懂Round Robin仲裁器的工作原理与设计陷阱
  • PX4飞控调试避坑指南:Offboard模式前必须检查的7个参数(安全第一)
  • 重新定义汽车保养!别只换机油,90%车主忽略的养车真相!
  • 2026年天津滨江道必吃海鲜攻略:本地人私藏的海肠捞饭大王与平价海鲜正餐指南 - 优质企业观察收录
  • SSM架构的Java网上书城实战项目(含前后台+数据库+演示视频)
  • 2026新疆靠谱持证导游TOP8 本地人纯玩高评分推荐 - 盛世西域旅行
  • 2026 三门峡防水补漏三家品牌横向测评:厨卫屋面地下室修缮哪家靠谱?吉修匠 99.8 分五星稳居榜首 - 吉修匠
  • 正在拖慢你 AI 智能体落地的 5 个数据基础与技术栈缺口
  • 河南隔音房厂家直销_性价比高降噪效果好
  • 如何用AnythingLLM打造你的专属AI知识库:零配置快速上手指南
  • 树莓派TF卡坏了别慌!手把手教你用Win32 Disk Imager无损克隆系统盘(Raspberry Pi 4实测)
  • TrafficMonitor插件:5分钟打造你的Windows桌面全能助手
  • 粽香投票评选怎么创建?云众评选策划方案 - 微信投票小程序
  • 2026年贵阳卤菜加盟完全指南:5大品牌深度对比与创业决策 - 优质企业观察收录
  • 智能家居B端生态位架构:从单体应用到微服务化分拆的八大关键角色
  • 上海执行异议律师事务所哪家专业:2026年执行异议领域律所实力对比 - 品牌2026
  • 记一次渗透测试前端js审计+未授权访问漏洞
  • 深度解析:BepInEx 6.0架构演进中的IL2CPP签名优化与资源加载稳定性解决方案
  • 2026 晋江防水补漏哪家好?住建实地测评权威榜单 TOP5|滨海渔村 / 老城小区 / 闽南古厝 / 鞋服染整厂房渗漏修缮白皮书(6 月专项调研) - 苏易修缮