跳到主要内容

"模型胡说八道"这件事,靠调 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,性价比最高的一步
  • 评估:先查检索命中率,再谈其他

最后更新于:

本站内容采用 CC BY-NC 4.0 许可