生产级RAG系统一些经验总结
本文将探讨如何使用最新技术构建生产级检索增强生成(RAG)系统,包括健壮的架构、向量数据库(Faiss、Pinecone、Weaviate)、框架(LangChain、LlamaIndex)、混合搜索、重排序器、流式数据接入、评估策略以及实际部署技巧。
引言:检索增强生成的力量
大型语言模型功能强大,但常常会产生幻觉——由于缺乏最新或事实性数据,它们可能会生成不正确的信息。
检索增强生成(RAG)通过为语言模型配备检索机制来解决这一问题:模型可以从外部知识源获取相关文档,并利用它们作为回答的依据。这种方法结合了两个世界的优点——搜索系统或数据库的知识,以及生成模型的流畅性。精心设计的RAG系统能够提供准确、上下文感知的回答,即使是关于语言模型训练数据中没有的小众或最新信息。
它已成为企业聊天机器人(回答公司文档问题)、带自然语言回答的搜索引擎(如必应聊天)以及任何需要事实准确性的领域的首选架构。
简单来说,RAG的工作方式如下:当问题进入系统时,首先对文档索引或向量数据库执行搜索(使用问题或其处理形式)以检索相关段落。然后将这些最相关的文档与用户问题一起作为上下文输入到语言模型的提示中。语言模型生成一个引用或整合了检索信息的答案。结果是一个基于真实数据的答案,大大减少了幻觉并提高了正确性。这种架构在2020年左右开始流行,并在2024年发展成为成熟的生产系统。
下面将探讨如何使用最新工具和方法构建生产就绪的RAG流水线。
核心RAG架构:组件和工作流程
总的来说,RAG系统有几个关键组件:
-
文档存储/索引:知识库文档,可以是向量数据库(用于语义搜索)或传统搜索索引(用于关键词搜索),或两者结合。流行的选择包括Faiss(开源向量相似度搜索库)、托管向量数据库服务如Pinecone或Weaviate,甚至是具有密集向量功能的Elasticsearch/OpenSearch。
-
检索器:给定查询,从存储中检索前k个相关文档的逻辑。这通常涉及将查询嵌入为向量并找到最近邻(语义相似性)。它还可能使用关键词或元数据过滤。
-
语言模型提示模块:获取检索到的文档并为语言模型构建提示或上下文。通常,它可能会格式化提示,如:“以下是一些相关段落:\n[文档1]\n[文档2]\n问题:[用户问题]\n回答:”。这个提示的设计(文档如何插入,任何分隔符或指令)可能会影响性能。
-
生成(语言模型):大型语言模型(如GPT-3、Llama 2等)基于问题和检索上下文生成答案。
-
后处理:(可选)步骤如引用来源(识别哪个检索文档支持答案的各个部分)、过滤低置信度答案(例如,如果模型没有找到足够信息,它可能会响应一个回退答案),或格式化答案。
在生产部署中,这些组件通常被解耦为微服务。例如,向量数据库服务(如单独运行的Pinecone、Milvus、Weaviate等)处理存储和相似性搜索;检索器服务(可能只是调用数据库或WebSearch)处理查询逻辑;语言模型推理服务(可能使用API或本地模型服务器)处理生成。
典型的请求流程是:用户查询 → [检索器] → 前3个文档 → [语言模型] → 最终答案。
这可能会通过中间步骤进行扩展:例如,一些RAG系统使用语言模型生成更好的搜索查询(查询重构)或进行多跳检索(先找到相关部分,然后使用它找到更详细的信息)。此架构是模块化的,可以根据需要进行扩展。
主要工具和框架(2024版)
从头开始构建RAG系统很复杂,但好消息是有一个工具生态系统可以帮助:
向量数据库
这些是专门用于存储嵌入向量并支持快速相似性搜索的数据库:
- Faiss是一个库(C++带Python绑定),可以在内存中处理数百万个向量,并提供各种索引策略(flat、IVF、HNSW)用于高效的近似最近邻搜索。
- Pinecone和Weaviate是云原生向量数据库服务,处理扩展、持久性和过滤。它们允许不仅存储嵌入向量,还存储元数据(如文档ID、标签),并执行混合查询(结合向量相似性和元数据过滤)。
- Chroma(开源)和Milvus是2024年其他流行的向量存储。这些允许自托管持久向量数据库。
对于小规模或简单部署,即使是内存中的Faiss索引或SQLite全文搜索也足够——但生产系统通常需要专用服务的健壮性。
检索/编排框架
开发者使用如LangChain和LlamaIndex(前身为GPT Index)等框架,而不是手动编写所有检索和提示逻辑:
- LangChain提供涉及语言模型的行动链抽象,例如一个RetrievalQA链,它接收问题,使用检索器获取文档,然后用这些文档调用语言模型。它支持与许多向量数据库和语言模型API的集成,所以只需几行代码,就可以配置一个使用Pinecone进行检索和GPT-4进行生成的LangChain QAChain。
- LlamaIndex也专注于索引方面。它帮助构建数据索引(具有各种结构,如列表索引、树索引用于分层检索等),并提供简单的查询接口。例如,可以构建文档的向量索引,然后执行index.query(“我的问题”),它将处理查询嵌入、检索最佳节点,并为语言模型组装上下文。
这些框架还提供上下文分割工具——它们可以将文档分成适合语言模型上下文窗口的大小。它们通常使用智能分块逻辑来保持段落语义连贯(例如,按段落或部分分块),甚至可以按标题或元数据存储文本分割,以便提供给语言模型的上下文是有意义的。
混合搜索解决方案
一些搜索引擎结合了密集和稀疏搜索:
- 例如,Azure认知搜索和ElasticSearch可以混合使用关键词BM25搜索和向量搜索,这可以改善某些查询的结果,特别是那些具有特定关键词(如错误代码或专有名词)的查询,纯语义搜索可能会遗漏这些。
- LlamaIndex等工具允许插入混合检索器(一步使用关键词搜索,另一步使用向量搜索,并合并结果)。
重排序器
检索步骤通常会获取10-20个候选段落:
- 使用重排序模型(通常是较小的基于BERT的交叉编码器,它接收查询和每个候选段落并评分相关性)可以大大提高精度。
- 重排序器重新阅读每个候选段落并考虑完整的查询上下文,给出相关性分数,允许系统选择最佳的3个提供给语言模型。
- 这种两阶段先检索后重排序的方法,类似于网络搜索引擎,越来越多地用于RAG以提高答案质量。
- 例如,可以使用SentenceTransformers的交叉编码器模型(如cross-encoder/ms-marco-MiniLM-L-6-v2)对向量搜索的初始结果进行重排序。
在实践中,可能会将这些结合起来:使用LangChain定义一个检索链,首先进行混合搜索(例如,使用Weaviate的混合查询,同时使用向量相似度和关键词匹配),然后通过交叉编码器重排序结果,然后将顶部段落格式化为GPT-3.5或Llama-2等语言模型的提示,最后使用语言模型的答案。向量数据库执行繁重工作(向量数学等);LangChain只是协调各部分。这种模块化方法意味着可以交换组件(例如,使用不同的语言模型或切换到更高效的向量索引),而无需重写整个管道。
提升检索质量:分块、混合搜索和上下文
检索上下文的质量对RAG成功至关重要。以下是截至2025年出现的一些最佳实践和高级技术:
带上下文标题的语义分块
不要只是将文档分成随机的512个标记块。保留章节和标题:
- 例如,如果有一个标题为"安装步骤"的文档,在该部分的每个块中包含该标题文本作为"标题"。
- 通过添加上下文标题,检索器的嵌入向量能更好地编码该块的上下文(“安装步骤:…内容…”),从而带来更准确的匹配。
- 保持块适中大小(可能200-300字),以确保每个块覆盖特定主题。太小会失去上下文,太大会使嵌入向量稀释相关信息。
- 许多团队找到了一个最佳点,并经常允许块之间有一些重叠,以确保边界处的重要信息不会被遗漏。
混合检索(词法+语义)
仅依靠向量相似性有时会在精确关键词匹配上失败:
- 例如,询问"错误0x803d"或特定名称的查询,在嵌入空间中可能与包含它的文档不相似。
- 健壮的RAG系统通常结合BM25关键词搜索和向量搜索。这可以通过并行运行两个搜索来完成:一个使用传统搜索(如ElasticSearch),一个使用嵌入,然后合并结果(确保没有重复并从两者中取最佳)。
- 一些向量数据库(如Weaviate、Milvus)和搜索服务直接支持混合查询,可以权衡语义和词法分数。
- 结果是更高的召回率——捕获一种方法或另一种方法可能遗漏的情况。
- 许多生产系统默认使用混合搜索以增加安全性,特别是在客户支持领域,准确的术语或错误代码很重要。
智能查询处理
输入数据库的检索查询不必与用户的问题完全相同。有时查询扩展或分解会有所帮助:
- 例如,Perplexity.ai引入了查询分解:如果检测到复杂查询,将其分解为子查询,为每个子查询检索,然后组合。
- 示例:问题"2023年能源危机的原因和影响是什么?“可以分为"2023年能源危机的原因"和"2023年能源危机的影响”,为两个方面检索文档,并将两组结果输入到答案中。
- 另一个技巧是伪相关反馈:使用初始检索结果用重要术语扩展查询(或使用语言模型以更明确的方式重新表述查询)。
- 这些可以产生更相关的文档,但会增加复杂性和成本。
检索器效率
确保向量索引以正确的速度与准确性权衡构建:
- 例如,HNSW索引可以在给定延迟下调整召回率。
- 尽可能使用缓存——如果经常问同样的问题,缓存检索文档甚至最终答案(如果数据更新,则适当失效)。
- 如果需要吞吐量,也可以考虑批处理检索请求(如果使用API,将多个用户查询分组到数据库)。
- 这些优化使实时使用的延迟保持较低。
上下文长度和摘要
如果检索到的文档太大,无法放入语言模型上下文窗口(可能是4k到16k标记):
- 可能需要修剪或总结它们。一种常见方法是先检索,然后运行较小的语言模型或启发式算法,将每个检索到的文档总结为一段话,然后将这些摘要输入最终的语言模型。
- 这有时被称为上下文压缩或自适应上下文。LlamaIndex等工具可以自动执行第二阶段,使用语言模型在最终答案之前将初始检索集压缩成更短的形式。
- 仅在绝对需要时使用此方法,因为任何摘要都是额外的生成步骤,可能会引入自己的错误。
- 随着2025年16k+标记模型的出现,如果适合,许多RAG系统尝试只提供原始文本,因为这保留了最大的细节。
评估:如何知道系统是否有效?
构建生产RAG系统最困难的部分之一是评估。传统的准确性指标并不直接适用,因为输出是自由形式的文本,可能引用源材料。然而,健壮的评估至关重要:正如一位RAG专家指出,许多团队"在这方面失败",因为他们没有严格的评估,然后他们的系统在生产中出乎意料地失败。
关键评估策略包括:
真实问答对
如果可能,为领域组装问答对数据集(带有真实答案):
- 对于每个问题,测试RAG系统的答案——使用与参考答案的ROUGE或F1等指标。
- 这在开放域问答研究中很常见(例如,NaturalQuestions数据集)。
- 然而,在许多情况下,用户可能提出的所有问题并没有现成的真实答案。
检查源准确性
由于RAG应该基于检索文本的答案:
- 可以评估答案与源的一致性。RAGAS库(检索增强生成评估套件)已发布,提供了答案-上下文相关性和幻觉检测等指标。
- 它可以检查答案是否包含在提供的文档中找不到的陈述,标记潜在的幻觉。
- RAGAS或类似开源工具检查:模型是否真正使用了检索文本?它是否与源冲突?
- 这些工具给出数字分数,但通常仍需人工验证。
人工评估(“感官检查”)
在早期阶段,团队成员可能会简单地提出一堆问题并判断答案是否"看起来正确":
- 这种临时测试是一个不错的开始,但要进入生产阶段,需要将其正式化。
- 这可能意味着聘请领域专家或众包工作者根据正确性、完整性、连贯性、引用源等标准评估答案。
- 通常,即使在部署后,系统中的随机问答样本也会定期由人类审查,以捕捉性能下降或新的失败模式。
自定义评估指标
根据用例,可能会跟踪不同的指标:
- 例如,对于支持聊天机器人,可能会测量偏转率(机器人无需人工代理解决问题的频率)。
- 对于内部知识助手,可能会测量用户反馈或用户是否跟随提供的链接。
- 关键是将系统性能与用户满意度相关的可衡量指标联系起来。
重要的是,不能优化没有测量的东西。RAG系统有许多可调节的旋钮(嵌入模型选择、块大小、文档数量、提示格式等)。全面的评估套件允许对这些组件进行A/B测试。例如,可能会实验:添加重排序器是否如预期提高了答案精度?将检索段落数量从3增加到5是否真的会产生更好的答案,还是只会使语言模型混淆?只有通过适当的评估才能系统地迭代。许多团队扩展开源框架或构建内部仪表板。一些团队采用两层评估:每次构建的自动检查(以捕捉明显的退步)和定期更深入的人工评估。
成功的RAG部署大量投资于评估——涵盖查询理解、源引用、响应完整性和幻觉率。他们通常需要开发特定领域的测试集,因为通用基准没有捕捉到他们用户的需求。例如,法律文档助手可能会创建一套法律问答,并确保系统为每个答案引用法律中的正确条款,专门评估这一点。
流式RAG:实时数据集成
2024年的一个前沿发展是流式RAG——使RAG系统能够实时处理持续更新的数据流。在经典RAG中,索引定期更新(可能每天一次或在新文档摄入时)。但如果信息每分钟都在变化(想想股票价格、实时体育评论、突发新闻)呢?流式RAG扩展了架构以支持连续摄入和检索。
在流式RAG管道中,可能有类似:文档(或数据库更新)的Kafka流不断输入到向量数据库索引中。系统执行增量索引——当新文本到达时,它立即被嵌入并索引到向量存储中(过时时可能也被移除)。检索器设计为始终查询索引的最新状态。这涉及为快速写入和读取设计向量数据库。现代向量数据库如Weaviate和Pinecone已经改进了他们的更新性能,以处理高QPS的插入,使这成为可能。
此外,语言模型推理可能需要意识到时间。流式RAG可以在提示中纳入时间意识(例如,添加一个语句如"当前日期时间:2025-05-01 10:00 EST",让模型知道优先考虑最新信息)。此外,如果数据在生成过程中快速变化,可以采用自适应生成等策略——对于长时间运行的输出,可能在生成中间检查是否有新信息(虽然在实践中,生成通常非常快,除非是非常慢的过程,否则这通常不是必要的)。
流式RAG的参考架构可能包括:
- 使用Kafka或Kinesis等消息队列摄入变更(新文档、更新)。
- 消费者获取每个新项目并更新向量索引(例如,Pinecone更新或支持添加的本地Faiss索引)。
- 检索器组件监控这些变化或简单地查询实时索引,以便始终获得最新结果。
- 可能有机制使旧数据过期或存档,如果只有最近数据相关(以便索引不会无限增长陈旧信息)。
- 管道的其余部分保持相似(语言模型使用检索到的文档)。
一个真实世界的场景:回答有关股市事件的财务助手。使用流式RAG,一旦关于公司X的新闻文章发布并进入新闻源,它就会被索引。如果用户几秒钟后问"今天公司X的股票怎么样?“,检索器会找到该新鲜新闻文章,语言模型可以将其整合,说"公司X的股票今天上涨5%,此前报告本早上创纪录的收益”——静态语言模型不会知道的事情。这使语言模型应用更接近实时商业智能。
当然,使其生产就绪意味着谨慎处理吞吐量和一致性。如果摄入量非常高,可能需要扩展索引工作者并分区向量索引。此外,考虑增量重排序或窗口搜索:对于流,有时更关心最近N小时的数据——可以为效率和相关性限制搜索到最近项目。
实用技巧和部署考虑因素
设计架构和选择工具后,部署RAG系统有一些实际考虑:
延迟优化
生产RAG管道引入额外步骤(检索等),因此保持低延迟很重要:
- 可能时使用异步调用——例如,如果进行多个检索或模型调用,可以并行从向量数据库和语言模型获取。
- 如果支持,还可以启用语言模型的流式标记输出(以便用户可以在生成答案时开始看到,改善感知延迟)。
- 一些语言模型API和服务器允许标记流式传输,这对长答案的用户体验非常好。
扩展和缓存
如果检索器未扩展,可能成为瓶颈;确保向量数据库的大小能够处理查询负载:
- 多层缓存可以帮助——缓存频繁查询的嵌入,缓存流行问题的检索结果,甚至缓存非常重复的查询的最终答案(仔细验证数据未更改)。
- 负载均衡应考虑语言模型和数据库组件。
监控
在生产中,不仅要监控典型的基础设施指标(延迟、错误率),还要监控RAG特定的指标:
- 例如,跟踪答案的平均标记数、没有源的答案比例(如果应该有源,这可能表示检索失败/幻觉),或者触发回退"我不知道"响应的频率。
- 如果自己托管用于查询编码的嵌入模型,监控其延迟也很重要。
安全和访问控制
如果知识库包含敏感数据,在检索中实现访问控制:
- 常见的模式是用权限标签标记嵌入,并在查询时按用户权限过滤。
- 例如,向量数据库如Pinecone或Weaviate可以存储元数据(如文档所有者或分类级别),可以在查询中添加元数据过滤器(例如,如果财务用户在提问,只检索document_department = 'finance’的文档)。
- 语言模型本身应该被指示不要透露检索上下文之外的信息,理想情况下,如果没有找到相关公共信息,应该说它无法回答(而不是猜测)。
持续改进
系统上线后,收集用户反馈:
- 如果用户可以评价答案,或者可以观察他们的后续行为(比如他们是否总是重新表述问题,可能系统最初失败),反馈这些。
- 可能会根据这些反馈定期重新训练或微调嵌入模型或调整提示。
- RAG系统通常有许多"旋钮"可以调整,所以将其视为一个持续过程。
实际案例
许多公司已经部署了RAG:
- OpenAI自己的插件系统(ChatGPT搜索网络或数据库)实际上是RAG的底层实现。
- 微软的必应聊天使用网络搜索(必应索引)获取相关网页,然后必应聊天阅读以形成答案,引用来源。
- 在企业方面,Docker和Reddit等公司已经建立了使用RAG处理文档和论坛数据的支持机器人。
- 这些部署表明,通过正确的数据策划和系统设计组合,RAG可以大大增强语言模型在实际任务中的用处。
完整流程示例
为了具体说明RAG的应用,我们以使用LlamaIndex和向量数据库部署文档问答助手为例:
数据准备
- 将所有文档页面按章节进行分块(每个分块保留章节标题)
- 使用LlamaIndex创建向量索引(可基于Faiss或Pinecone实现)
- 通过OpenAI的text-embedding-ada-002或类似模型为每个分块生成嵌入向量
- 存储元数据(源页面URL、章节标题)
用户查询处理
- 查询进入时,使用相同的嵌入模型处理问题,并从Pinecone获取相似度最高的5个分块
- 同时在标题的倒排索引上进行关键词搜索,找出潜在的精确匹配(作为备选)
重排序(可选)
- 将问题与每个检索到的分块连接起来,输入小型BERT重排模型评分
- 按相关性排序并选取前3个结果
LLM提示构建
我们为GPT-4(或开源LLM如Llama2-Chat 70B)构建如下提示:
你是一个回答技术问题的助手。请使用以下文档摘录来回答问题。如果在摘录中找不到答案,请直接说不知道。摘录1(来自文档:入门指南):"...[内容]..."
摘录2(来自文档:安装指南):"...[内容]..."问题:如何在Ubuntu上安装该工具?
答案:
这个提示明确告诉模型使用文档摘录并避免胡编乱造信息。
生成回答
LLM会生成类似这样的回答:“要在Ubuntu上安装该工具,首先更新apt,然后运行sudo apt-get install toolname
。(来自安装指南)”。由于我们的提示指示,它会包含参考文档,并且不会引入摘录中不存在的信息。如果不知道答案,会直接表明"抱歉,我没有这方面的信息"。
后处理
- 检查答案中是否有虚构信息的迹象(可能要求必须引用至少一个源文档标题)
- 确认无误后,将答案返回给用户
- 记录问题、检索文档和答案以便分析
Python伪代码
# 假设文档已在向量存储中索引,且我们有查询函数
query = "如何在Ubuntu上安装该工具?"# 1. 检索相关文档
embedding = embed_model.encode(query)
docs = vector_store.search(embedding, top_k=5, filter=None)# 2. (可选) 使用交叉编码器重新排序文档
for doc in docs:doc.score = reranker.score(query, doc.text)
docs.sort(key=lambda d: d.score, reverse=True)
top_docs = docs[:3]# 3. 用检索到的文档构建提示
context = ""
for i, doc in enumerate(top_docs, start=1):context += f"摘录 {i}: {doc.text}\n"
prompt = f"你是一个助手...请使用以下摘录来回答。\n{context}\n问题: {query}\n答案:"# 4. 用LLM生成答案
answer = llm.generate(prompt)
这段伪代码概述了文档检索和提示使用的流程。实际应用中,你会使用LangChain或LlamaIndex方法而非手动编写,但这展示了数据流。注意我们指示模型使用提供的摘录。通过提示设计和选择遵循指令的LLM,确保模型不偏离主题对RAG至关重要。
结论:生产环境中的RAG系统
生产级RAG系统将信息检索的稳健性与LLM的生成能力相结合。通过遵循最佳实践—精选高质量数据源、保持索引更新、优化提示以获得有根据的答案、严格评估性能—团队可以构建可靠且可扩展的RAG系统。最新进展如流式更新和更好的混合搜索确保RAG不断发展,能够处理更大、更动态的知识库。
到2025年,我们看到RAG技术将应用于从客户支持机器人到医疗助手(结合严格的隐私控制)等各种场景。它是知识感知型AI的基础架构。通过掌握RAG设计并了解新工具动态,开发人员可以大大拓展LLM应用的能力—提供纯LLM无法实现的真实、最新且上下文相关的响应。