← 博客书房

Agent 实战:从 Dify 到 LangGraph·RAG 深水区:从能用到好用

实战30 分钟

RAG 深水区:从能用到好用

chunking 策略、混合检索、rerank、评测体系——JD 里最重的 RAG 落地经验,面试深水区。

能跑通 RAG 只需 30 分钟,让 RAG 达到生产水平需要处理五个深水问题。

五个深水问题

RAG 深水区:五个从能用到好用的分水岭 ① 分段策略 多大一段合适? 按什么切? ② 混合检索 纯向量不够 + 关键词匹配 ③ 重排序 向量排序 ≠ 语义相关性 ④ 评测体系 怎么知道变好了? 召回率 · 准确率 ⑤ 幻觉 检索不到时 模型会编答案 能跑通 = 建好了管道。好用 = 管道里每一步都优化过。 面试官问的不是「你会不会搭 RAG」而是「你的 RAG 召回率多少、怎么测的」

① 分段策略

没有万能的 chunkSize。按内容类型选:

内容类型推荐分段原因
FAQ / 商品说明按条目分(一个 FAQ = 一段)天然独立,一段一个答案
技术文档按 H2/H3 标题分保持语义完整性
长文 / 论文500-1000 字 + 15% overlap平衡召回率和精度
对话记录按轮次分一轮对话一个上下文
代码按函数/类分保持逻辑完整
import { MarkdownTextSplitter } from "@langchain/textsplitters";

// Markdown 文档按标题分段
const mdSplitter = new MarkdownTextSplitter({
  chunkSize: 800,
  chunkOverlap: 100,
});

② 混合检索

纯向量检索的盲区:关键词匹配。用户搜「ERR_CONN_RESET」这种错误码,向量检索可能找不到(因为 embedding 把它当成普通文字),但 BM25 关键词检索能精准命中。

// 混合:向量 + BM25 各取 Top-K,合并去重
const vectorResults = await vectorStore.similaritySearch(query, 5);
const keywordResults = await bm25Retriever.getRelevantDocuments(query, 5);
const merged = dedup([...vectorResults, ...keywordResults]).slice(0, 8);

③ 重排序

向量检索的排序依据是「余弦相似度」,这不等于「语义相关性」。Reranker 模型(如 Cohere Rerank、bge-reranker)能更准确地判断「这段文字是否真的回答了这个问题」。

检索(粗筛): 从 10000 段中取 Top-20 → 快但粗糙
重排(精排): 从 Top-20 中精选 Top-3 → 慢但精准

④ 评测:不测不知道好不好

三个核心指标:

指标测什么怎么算
召回率(Recall@K)正确答案是否被检索到命中数 / 应命中总数
准确率(Precision@K)检索结果中有多少是相关的相关数 / 返回总数
忠实度(Faithfulness)回答是否基于检索结果人工或 LLM 判断

搭一个简单评测集:准备 20-50 个「问题 + 正确答案所在文档段」的配对,每次改动后跑一遍,看召回率变化。

⑤ 幻觉防护

检索不到时,模型不会说「不知道」,它会编一个格式完美的答案。

// 在 Prompt 里明确说
const prompt = `基于以下参考资料回答问题。
如果参考资料中没有足够信息,直接说「根据现有资料无法回答」。

参考资料:
{context}

问题:{question}`;

这是第一道防线。更严格的做法是在代码层检查:如果检索结果的相似度都低于阈值,直接返回固定话术,不调 LLM。

本章要点

  1. 分段没有万能值——按内容类型选策略
  2. 混合检索 = 向量(语义)+ BM25(关键词),互补盲区
  3. Reranker 是「粗筛后精排」,显著提升最终质量
  4. 评测不是可选项——没有指标的优化是盲目的
  5. 幻觉防护:Prompt 约束 + 代码层阈值兜底

→ 下一章:部署、可观测与面试