我觉得[[RAG]]这道题,其实核心在问一个工程问题:怎么让大模型在不知道答案的时候,能自己去找答案,而不是瞎编。说白了就是给LLM配一个外挂知识库,让它只回答知识库里有的东西。
先说离线索引阶段。第一步是文档切分,这里有个常见的坑:切太细,比如一句话一段,上下文就断了,模型看不懂;切太粗,比如整篇文章一段,检索回来一堆噪声,答案也不准。我一般会按语义段落来切,控制256到512个token,前后保留一点重叠,保证上下文连贯。
切完之后用 [[Embedding]] 模型转成向量,比如BGE或者M3E。然后存到 [[Vector Database]] 里,像Milvus或者[[Faiss]]。不过纯向量检索有个问题,它只认语义相似,不认关键词精确匹配。比如说用户问“订单号12345”,向量检索可能找不到,因为订单号是精确字符串。所以实际落地我往往会做 [[Hybrid Search]],把向量和 [[BM25]] 关键词检索结合起来,这样语义和精确匹配都能覆盖。
在线查询阶段,用户来了一个query。这里我会先做一步查询改写,因为用户问得可能不准确。比如客服场景,用户说“我上次买的那个杯子退不了款”,系统可以改写成“退款政策 订单状态 杯子”,这样检索更准。然后去向量库取Top-K,比如取20条。取回来之后,我会加一道 [[重排]],用一个 [[Cross-Encoder]] 模型重新算一遍相关性,把最相关的3到5条挑出来拼到prompt里,最后丢给LLM生成答案。
说到优化,我比较常用的是上下文压缩。检索回来的片段里可能有噪声,直接拼进去会干扰模型。我会先用一个小模型过滤一遍,只保留确实相关的句子。还有一个方向是 [[Self-RAG]],让模型自己判断要不要检索,如果知识库里没有就直接说不知道,避免 [[Hallucination]]。
举个例子,企业SOP合规场景。员工问“报销餐费需要什么附件”,知识库里可能有几十条政策。如果切分粒度不对,可能只找回“餐费标准”而漏了“附件要求”。所以我会按政策条款的语义边界来切,每条政策单独一段,再配上元数据过滤,比如只选最新的版本。这样召回率能高很多。
这里有个延伸点:RAG的召回质量其实高度依赖Embedding模型和切分策略的配合。如果Embedding模型没针对领域微调,比如用通用模型去匹配法律术语,那召回效果会打折扣。我倾向于先做领域适配,或者用 [[GraphRAG]] 引入实体关系来补足语义。
最后说落地的风险和取舍。前提是知识库质量要够好,如果文档本身就有错,RAG只会放大错误。常见失败场景是切分破坏了上下文,导致模型答非所问。所以上线前我会特别关注召回率的评估,用一套标准query跑一遍,看Recall@K有没有达标。如果召回率低,我会先调切分策略或者换Embedding模型。
所以我的判断是,RAG不是万能的,它最适合知识库相对静态、答案有明确来源的场景,比如客服、合规、产品文档。如果知识库频繁变动,或者问题需要多步推理,那可能要上 [[Agent]] 或者 [[ReAct]] 方案。我更倾向把RAG当成一个基础模块,和微调、Agent组合使用,而不是孤立地看。