RAG(Retrieval-Augmented Generation)已经是私有知识问答的默认方案。但「跑通 demo」和「上线可用」之间,差距往往比想象中大:检索到的内容看似相关,回答却答非所问;模型引用了来源,但来源里根本没有那句话。

这篇文章按数据流顺序,把 RAG 拆成五步,并给出每一步的判断标准和最小实现。

一、先明确:RAG 解决什么问题

RAG 适合三类需求:模型训练数据里没有的私有文档、需要时效性的知识、以及必须给出引用来源的回答。它的本质是把「记忆」外置成可检索的索引,让模型在生成前先查资料。

反过来,如果需求涉及实时事务、复杂计算或跨系统写操作,工具调用通常比 RAG 更合适。把两者混为一谈,是架构设计阶段最常见的错误。

二、数据切分:按语义而不是按字数

切分决定了检索的上限。一个实用的默认策略是:

  • 先按标题层级切,让每个 chunk 自带上下文,而不是从段落中间硬切。
  • 控制长度:中文 400 到 800 字为宜,重叠 10% 到 15%。
  • 保留标题路径:把「第三章 > 3.2 退款规则」拼在正文前面,召回质量通常会有肉眼可见的提升。
  • 特殊内容单独处理:表格按行切并保留表头,代码块不要切断。
def build_chunks(markdown: str, max_chars: int = 600, overlap: int = 80):
    """按标题切分,再按长度兜底,并保留标题路径。"""
    sections = split_by_headings(markdown)  # [(标题路径, 正文), ...]
    chunks = []

    for path, text in sections:
        step = max_chars - overlap
        for i in range(0, len(text), step):
            piece = text[i:i + max_chars]
            chunks.append({
                "text": f"标题路径:{path}\n\n{piece}",
                "metadata": {"path": path, "offset": i},
            })

    return chunks

切完不要急着嵌入。先抽 20 个 chunk 人工读一遍:如果单独看这一段,你能回答它想表达什么吗?如果人都看不懂,模型更看不懂。

三、检索:向量只是起点

向量检索擅长语义相近,但对订单号、产品型号、法条编号这类精确匹配并不敏感。实际系统里,最常见的组合是「向量 + 关键词」的混合检索:

方式优势短板
向量检索语义泛化,能处理同义表达对精确编号、罕见词不敏感
关键词检索精确命中,可解释性强换个说法就召回不到
混合检索互补,通常优于单一策略需要融合与调参

两路结果可以用 RRF(Reciprocal Rank Fusion)合并,不需要关心两边的分数尺度是否可比:

def retrieve(question: str, k: int = 8):
    vector_hits = vector_store.search(embed(question), k=k)
    keyword_hits = bm25.search(question, k=k)

    merged = reciprocal_rank_fusion(vector_hits, keyword_hits)
    return rerank(question, merged)[:4]

另一个容易被忽略的点是 metadata 过滤。多租户系统必须在检索前按租户过滤,而不是检索完再筛。否则既浪费算力,也可能把别人的文档带进上下文。

四、重排与上下文组装

初筛可以宽一些,比如召回 20 到 50 条,再用交叉编码器重排,取前 4 到 6 条。重排在中文场景的收益通常比换更大的嵌入模型更直接。

组装上下文时,注意三件事:

  1. 去重:相邻 chunk 可能内容重叠,重复信息会稀释注意力。
  2. 标注来源:给每段资料编号,要求模型在回答中引用编号。
  3. 预留指令:明确告诉模型「只根据资料回答;资料不足时直接说不知道」。
小技巧

把检索到的资料放在用户问题之前,并在资料末尾重复一次问题。长上下文模型对「末尾指令」的遵循度通常更好。

五、评测:没有评测就没有优化

RAG 的优化必须能分层定位问题。建议准备 50 到 100 条真实问答,至少记录四个指标:

  • Recall@k:正确资料有没有被召回,衡量检索质量。
  • MRR:正确资料排在第几位,衡量排序质量。
  • Faithfulness:回答是否忠于资料,衡量幻觉程度。
  • 引用准确率:引用的编号是否真的支持对应结论。

当回答出错时,先看检索结果里有没有正确资料:没有,就是检索问题;有但没答对,才是生成或提示词问题。这个分流能省下大量盲目调参的时间。

六、常见误区

  • 只调提示词:检索没召回,提示词再漂亮也变不出答案。
  • chunk 越大越好:上下文被无关内容占满,模型反而更容易漏掉关键句。
  • 一次塞二十条资料:噪音增加,成本上升,效果下降。
  • 索引不更新:文档改了、删了,索引还是旧的,回答自然过时。
  • 忽略权限:检索层不做租户隔离,是最危险也最容易被忽视的问题。

RAG 不是一个模型问题,而是一个数据工程问题。把切分、检索、重排、评测四件事分别做好,效果通常比换一个更大的模型更明显。

RAG 检索 向量数据库
← 返回全部文章