本地 RAG:切块、embedding 模型、向量库、引用与评测
硬件前提
- 纯 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+ 条的评测集,三个指标有数字;
- 每条线上答案都能给出可点开的来源,且编号经过校验。