RAG 从用户查询到 AI 回答到底发生了什么

构建 LLM 应用后,你很快会发现模型并不知道你的应用数据。产品文档可能明天就更新,公司的私有 API 从未出现在训练集中,数据库里的信息也完全陌生。微调模型并非总是最佳选择,这正是 RAG 通过检索外部数据来解决的问题。

检索器先从外部知识库拉取相关片段

RAG 流程以用户查询开始。查询文本首先被送入检索器模块。检索器负责从外部知识库中找出最相关的文档片段,这些片段可能是产品文档、代码注释、内部 Wiki 或结构化数据库记录。

开发者在搭建这一步时必须先准备向量数据库。目前主流选择包括 Pinecone、Weaviate、Milvus 或 Chroma。对于中文开发者,Milvus 因为对中文分词支持较好且能部署在私有集群,常被企业知识库项目采用。知识库中的每一段文本都需要提前切分成 chunk,通常 256 到 512 个 token 为一个单位,过大容易超出上下文窗口,过小则丢失语义。

索引方式直接影响召回速度。常见做法是把所有 chunk 通过嵌入模型转为向量后存入向量数据库,并建立 HNSW 或 IVF 索引。HNSW 在亿级规模下能把查询延迟控制在几十毫秒,但内存占用较高。开发者还要决定 chunk 的重叠比例,一般 10%-20% 的重叠能减少边界信息丢失。

这一步的输出是一组按相关性排序的文本片段,通常取 top-5 到 top-20。后续所有环节都依赖这批片段的质量。如果检索器拿回来的内容无关,后面再好的生成模型也救不回来。

(本节约 380 字)

嵌入模型与相似度计算决定召回质量

查询进入检索器后,第一件事是把它转为向量。这由嵌入模型完成。常见的开源嵌入模型有 BGE、text-embedding-ada-002 或 sentence-transformers 系列。中文场景下,BGE-m3 因同时支持中英双语且在 MTEB 榜单表现突出,被很多团队选用。

嵌入模型把查询和所有知识库 chunk 映射到同一高维空间,随后计算余弦相似度或内积。向量数据库在索引阶段已预先算好所有 chunk 的向量,查询时只需计算查询向量与索引向量的距离,选出最近的若干个。

召回质量直接决定整个 RAG 的上限。假如嵌入模型对领域术语不敏感,比如把“风控模型”和“风控策略”算成低相似度,后续生成就会缺失关键信息。很多团队会在生产环境增加 reranker 模型,如 bge-reranker-large,把初步召回的 50 条结果重新打分,只保留 top-5 给到生成阶段。这一步虽然增加一次推理,但能显著提升最终答案的相关性。

嵌入维度也值得权衡。768 维模型速度快但表达能力有限,1536 维精度更高却占用更多存储和计算资源。开发者需要在精度、速度、成本三者间反复测试。

(本节约 410 字)

检索结果拼接进提示词后才触发生成

检索完成后,系统把拿到的文本片段按一定顺序拼接成提示词。典型模板是:系统指令 + 检索到的上下文 + 用户查询。上下文部分通常会加上“根据以下信息回答问题”这样的引导句,避免模型直接忽略外部内容。

提示词工程在这里体现得淋漓尽致。开发者需要控制总 token 数不能超过模型上下文上限。Llama-3-8B 的 8k 上下文意味着最多只能塞 4-5 个较长的 chunk。顺序也很关键,把最相关的片段放在提示词最前面或最后面能获得更好效果,因为大模型对位置敏感。

拼接好的完整提示词被发送给生成模型。模型此时不再是孤立的,它拥有了外部知识。生成过程和普通聊天没有本质区别,只是输入更长。输出则是最终返回给用户的答案。

实际开发中,很多人会把这一步拆成两个 LLM 调用:先让一个小模型做提取或总结,把检索内容压缩成更短的摘要,再把摘要喂给大模型生成最终答案。这样既能省 token,又能降低幻觉。

(本节约 350 字)

检索不准是 RAG 产生幻觉的主因

RAG 最大的工程挑战之一仍是幻觉。很多开发者以为用了 RAG 就能彻底解决幻觉,实际并非如此。当检索器返回的内容与查询匹配度低时,模型仍然会倾向于用训练数据中的知识填补空白,或者直接编造看似合理的句子。

控制幻觉的核心仍然是提升检索质量。除了前面提到的 reranker,开发者还常用 HyDE(Hypothetical Document Embeddings)技术:先让模型根据查询生成一个假设答案,再用这个假设答案去检索,效果往往优于直接用原始查询。

另一个常用手段是增加元数据过滤。在企业知识库场景中,文档通常带有部门、更新时间、权限标签。检索时先按元数据过滤,再做向量搜索,能大幅减少无关结果。

即使检索准确,提示词写法也会影响幻觉概率。明确写上“如果以下内容不足以回答问题,请直接说不知道”能显著降低模型胡编乱造的概率。部分团队还会增加后置校验步骤,用另一个小模型判断生成答案是否忠实于检索内容,不忠实则拒绝返回或触发重试。

中文开发者在处理企业内部文档时还会遇到特有问题:专业术语多、格式不规范、包含大量表格。单纯切 chunk 容易破坏表格结构,导致检索片段缺失关键数据。目前较好的做法是把表格转为 Markdown 或 JSON 后再嵌入。

(本节约 420 字)

多阶段处理让端到端延迟明显上升

RAG 相比直接调用 LLM,延迟显著增加。一个完整流程通常包含:查询嵌入(50-150ms)、向量检索(20-100ms)、reranker(200-600ms)、提示词拼接、生成模型推理(取决于模型大小和并发)。端到端延迟很容易超过 2 秒,用户感知明显。

优化方向主要集中在三个地方。首先是缓存。相同或相似的查询可以直接返回之前检索过的上下文,省去向量搜索和 reranker。其次是异步并行:查询嵌入和部分元数据过滤可以同时进行。再次是模型量化与蒸馏,把 reranker 和小模型压缩到 4bit 或 8bit,在消费级 GPU 上跑也能把单次推理控制在 100ms 以内。

对于高并发场景,开发者常把向量数据库和 LLM 部署在不同集群,通过 gRPC 通信,同时使用连接池减少冷启动开销。一些团队还会引入预热机制,在低峰期提前把热门文档的嵌入向量加载到 GPU 显存。

实际项目中,延迟预算往往是产品决策的重要依据。如果目标是秒级响应,就必须牺牲部分召回精度;如果允许 3-4 秒,则可以堆更多 reranker 和更大的上下文。

(本节约 360 字)

动态数据更新让 RAG 比微调更实用

企业知识库最大的特点是内容频繁变化。产品文档每周更新,API 接口每月迭代,内部政策随时调整。微调模型需要重新准备训练集、训练、验证、上线,整个周期以周计,而且每次更新都要重复一次,成本极高。

RAG 的优势正在于此。知识库更新只需要把新文档切 chunk、生成嵌入、写入向量数据库即可,几分钟就能生效。旧数据也可以通过删除对应向量轻松下线。这让 RAG 特别适合国内企业的内部问答、客服机器人、代码助手等场景。

以一个典型的中文互联网公司为例,其技术文档分散在 Confluence、GitLab Wiki 和企业微信群文件里。RAG 系统可以定期爬取这些来源,自动更新向量库。权限控制也很方便,只需在元数据里标记文档可见部门,检索时按用户 token 过滤即可。

当然,动态更新也带来新挑战:如何避免同一知识出现多个版本?常见做法是给每个 chunk 增加 version 字段和 update_time,检索时优先返回最新版本。部分团队还会定期做向量聚类,把高度相似的 chunk 合并,防止知识库膨胀。

综合来看,RAG 虽然在延迟和工程复杂度上增加了负担,但它在数据新鲜度和可维护性上的优势,让它成为当前大多数开发者构建私有 LLM 应用时的首选方案。

(本节约 380 字)

参考来源