本地 RAG:切块、embedding 模型、向量库、引用与评测

LLM 知识 · 使用 · 第 6 篇 更新于 2026-10-15

硬件前提

  • 纯 API 方案:不需要 GPU
  • 全本地方案:单卡 8GB 或 16GB 内存即可(embedding 模型比生成模型小一个数量级)

先确认该不该用 RAG

适合:文档多且常更新、需要出处、知识是长尾的。
不适合:需要稳定风格/格式(去微调)、需要多步操作(去 Agent)、文档只有几十页且塞得进上下文(直接塞更准)。

五个环节,按顺序搭

1. 切块(chunking)

做法参数起点说明
按结构切优先按标题/段落/条款有结构的文档(手册、法条、API 文档)必须按结构切
定长切300~800 字,重叠 10%~20%无结构文本的兜底
  • 块太小 → 语义断裂,检索到半句话;块太大 → 一个块里混多个主题,答案含糊。
  • 每个块带上元数据:来源文件、标题路径、更新时间。没有元数据就没法做引用与过滤。

2. embedding 模型

选「中文效果好 + 维度可接受」的开源 embedding 模型,本地跑很轻。要点:

  • 索引和查询必须用同一个模型,换模型 = 全量重建索引;
  • 维度越高越占内存,多数场景中等维度足够;
  • 别用生成模型顺手做 embedding,质量差很多。

3. 向量库

场景选择
几千~几万块,单机验证进程内向量索引(文件持久化即可)
需要过滤 + 生产可用带元数据过滤的向量库或数据库扩展

元数据过滤比相似度更值钱:先按时间/来源/权限过滤,再算相似度,能挡掉大量错误答案。

4. 检索与重排

# 伪代码:召回 → 过滤 → 重排 → 组装
hits = vector_search(query, top_k=20)
hits = [h for h in hits if h.meta["updated"] >= cutoff]
reranked = rerank(query, hits, top_n=5)     # 有重排模型时质量提升明显
context = "\n\n".join(f"[{i+1}] {h.text}\n来源: {h.meta['source']}"
                      for i, h in enumerate(reranked))
  • 召回 20、给模型 5 是常见配比;给到 20 通常只会稀释注意力。
  • 有重排(rerank)模型时,质量提升往往大于换更大的生成模型。

5. 引用与可核查

提示里要求「每个结论标注来源编号」,然后程序校验:编号必须存在于本次给的片段里,否则判定为不可信并重试或降级。这是本地 RAG 唯一可靠的幻觉防线。

评测:没有这一步就是玄学

准备 30~50 条真实问题,每条标注「正确来源」。跑三个指标:

指标怎么测不合格的表现
召回命中正确来源是否进 top-5低于 80% 先修切块与检索,别调提示
答案忠实度答案能否被检索片段支撑抽查 10 条,看有没有片段外的内容
拒答正确率库里没有的问题是否回答「未提及」硬凑答案 → 提示缺拒答条件

顺序纪律:召回不行 → 修切块/检索;召回行但答案差 → 修提示与片段数量;都对但风格差 → 才考虑微调。

常见失败与排查

现象原因处理
检索到的都是无关段落切块过大/过小、查询太长按结构切;查询用用户原句而非整段历史
答案混了旧版本没做时间过滤元数据加更新时间,检索时过滤
表格/代码检索效果差表格被切成碎片表格整块保留,或转成「行描述」文本
明明有资料却答不出片段太多被忽略减到 5 条以内,最相关的放最后
换 embedding 模型后全乱索引没重建全量重建,别混用

完成标志

  • 一条命令能重建索引;
  • 一份 30+ 条的评测集,三个指标有数字;
  • 每条线上答案都能给出可点开的来源,且编号经过校验。