"模型胡说八道"这件事,靠调 prompt 只能缓解,不能根治。根因是它脑子里没有你的数据,于是只能用通用知识补全。
RAG(检索增强生成)的思路很直接:先把相关资料找出来,塞进上下文,再让它基于资料回答。听起来简单,但效果差距全在细节里。
为什么要用 RAG
先明确 RAG 解决什么、不解决什么:
| 问题 | RAG 能解决吗 |
|---|---|
| 模型不知道你公司的报销规则 | ✅ 能 |
| 模型不知道上周刚改的配置 | ✅ 能 |
| 模型算术不好 | ❌ 不能,交给工具 |
| 模型推理能力差 | ❌ 不能,换模型 |
一句话:RAG 解决的是"知识没有",不是"脑子不好"。
切片策略
切片是效果的地基。切错了,后面检索再强也救不回来。
三个参数
python
CHUNK_SIZE = 500 # 每片字符数
CHUNK_OVERLAP = 50 # 相邻片重叠字符数- 太小:语义不完整,"的一半"这种句子答非所问
- 太大:噪声多,嵌入向量被稀释,检索不精准
- 重叠必须有,否则答案正好被切断在边界上就丢了
经验值:中文文档 300~600 字,重叠 10%~15%。
按结构切,别按字数硬切
python
from langchain_text_splitters import MarkdownHeaderTextSplitter
splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[('#', 'h1'), ('##', 'h2'), ('###', 'h3')]
)
chunks = splitter.split_text(md_text)沿标题切的好处是每一片自带上下文:检索到片段后,可以顺带把父标题拼回去,模型能知道这段话出自哪一节。
一个低成本技巧
给每片前面拼上"文档标题 + 章节路径",例如 《报销制度》> 差旅 > 住宿标准。几乎零成本,命中率提升明显。
检索与重排
只用向量检索是不够的
向量检索擅长"语义相近",不擅长"关键词精确匹配"。用户搜 SO20260922001 这种订单号,纯向量大概率找不到。
正确做法是混合检索:
python
def hybrid_search(query, top_k=20):
vec_hits = vector_store.search(embed(query), top_k) # 语义召回
kw_hits = bm25_store.search(query, top_k) # 关键词召回
return reciprocal_rank_fusion(vec_hits, kw_hits) # RRF 融合RRF(倒数排序融合)不需要调两路权重,简单且稳:
python
def reciprocal_rank_fusion(*hit_lists, k=60):
scores = {}
for hits in hit_lists:
for rank, hit in enumerate(hits, start=1):
scores[hit.id] = scores.get(hit.id, 0) + 1 / (k + rank)
return sorted(scores, key=scores.get, reverse=True)重排是性价比最高的一步
粗排召回 50 条,再用 rerank 模型精排取 5 条:
python
candidates = hybrid_search(query, top_k=50)
final = rerank_model.rank(query, candidates)[:5]代价是多一次模型调用(几十毫秒级),收益是上下文里的噪声大幅减少。这一步我实测对答案正确率的提升,比换更大的向量模型还明显。
别把 50 条全塞进去
上下文里塞一堆不相关内容,模型反而更容易跑偏——这就是所谓"中间迷失"现象。宁缺毋滥。
让模型学会说"不知道"
text
只根据以下资料回答。如果资料中没有答案,直接回答"资料中没有相关信息",不要自行推测。
资料:
{context}
问题:{question}这句话看着简单,但它决定了系统是"偶尔答错"还是"稳定地承认不知道"。对客服、法务类场景,后者才是可交付的。
效果评估
没有评估就没有优化。最小可用的评估集长这样:
json
[
{
"question": "差旅住宿标准是多少?",
"expected_answer": "一线城市每晚 500 元,其他城市 350 元",
"source_chunk_id": "reimburse.md#h2-3"
}
]关注三个指标,分开看才有意义:
- 检索命中率:正确答案所在的切片有没有被召回(这一步错了,后面全白搭)
- 答案正确率:最终回答对不对
- 拒答率:该说"不知道"的时候说了没有
先盯检索命中率。它上不去,换模型、改 prompt 都是在做无用功。
小结
- 切片:按结构切 + 拼章节路径,比调参数更有效
- 检索:向量 + BM25 混合,用 RRF 融合
- 重排:粗排 50、精排 5,性价比最高的一步
- 评估:先查检索命中率,再谈其他