本文将探讨如何使用最新技术构建生产级检索增强生成(RAG)系统,包括健壮的架构、向量数据库(Faiss、Pinecone、Weaviate)、框架(LangChain、LlamaIndex)、混合搜索、重排序器、流式数据接入、评估策略以及实际部署技巧。
大型语言模型功能强大,但常常会产生幻觉——由于缺乏最新或事实性数据,它们可能会生成不正确的信息。
检索增强生成(RAG)通过为语言模型配备检索机制来解决这一问题:模型可以从外部知识源获取相关文档,并利用它们作为回答的依据。这种方法结合了两个世界的优点——搜索系统或数据库的知识,以及生成模型的流畅性。精心设计的RAG系统能够提供准确、上下文感知的回答,即使是关于语言模型训练数据中没有的小众或最新信息。
它已成为企业聊天机器人(回答公司文档问题)、带自然语言回答的搜索引擎(如必应聊天)以及任何需要事实准确性的领域的首选架构。
简单来说,RAG的工作方式如下:当问题进入系统时,首先对文档索引或向量数据库执行搜索(使用问题或其处理形式)以检索相关段落。然后将这些最相关的文档与用户问题一起作为上下文输入到语言模型的提示中。语言模型生成一个引用或整合了检索信息的答案。结果是一个基于真实数据的答案,大大减少了幻觉并提高了正确性。这种架构在2020年左右开始流行,并在2024年发展成为成熟的生产系统。
下面将探讨如何使用最新工具和方法构建生产就绪的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系统使用语言模型生成更好的搜索查询(查询重构)或进行多跳检索(先找到相关部分,然后使用它找到更详细的信息)。此架构是模块化的,可以根据需要进行扩展。
从头开始构建RAG系统很复杂,但好消息是有一个工具生态系统可以帮助:
这些是专门用于存储嵌入向量并支持快速相似性搜索的数据库:
对于小规模或简单部署,即使是内存中的Faiss索引或SQLite全文搜索也足够——但生产系统通常需要专用服务的健壮性。
开发者使用如LangChain和LlamaIndex(前身为GPT Index)等框架,而不是手动编写所有检索和提示逻辑:
这些框架还提供上下文分割工具——它们可以将文档分成适合语言模型上下文窗口的大小。它们通常使用智能分块逻辑来保持段落语义连贯(例如,按段落或部分分块),甚至可以按标题或元数据存储文本分割,以便提供给语言模型的上下文是有意义的。
一些搜索引擎结合了密集和稀疏搜索:
检索步骤通常会获取10-20个候选段落:
在实践中,可能会将这些结合起来:使用LangChain定义一个检索链,首先进行混合搜索(例如,使用Weaviate的混合查询,同时使用向量相似度和关键词匹配),然后通过交叉编码器重排序结果,然后将顶部段落格式化为GPT-3.5或Llama-2等语言模型的提示,最后使用语言模型的答案。向量数据库执行繁重工作(向量数学等);LangChain只是协调各部分。这种模块化方法意味着可以交换组件(例如,使用不同的语言模型或切换到更高效的向量索引),而无需重写整个管道。
检索上下文的质量对RAG成功至关重要。以下是截至2025年出现的一些最佳实践和高级技术:
不要只是将文档分成随机的512个标记块。保留章节和标题:
仅依靠向量相似性有时会在精确关键词匹配上失败:
输入数据库的检索查询不必与用户的问题完全相同。有时查询扩展或分解会有所帮助:
确保向量索引以正确的速度与准确性权衡构建:
如果检索到的文档太大,无法放入语言模型上下文窗口(可能是4k到16k标记):
构建生产RAG系统最困难的部分之一是评估。传统的准确性指标并不直接适用,因为输出是自由形式的文本,可能引用源材料。然而,健壮的评估至关重要:正如一位RAG专家指出,许多团队"在这方面失败",因为他们没有严格的评估,然后他们的系统在生产中出乎意料地失败。
关键评估策略包括:
如果可能,为领域组装问答对数据集(带有真实答案):
由于RAG应该基于检索文本的答案:
在早期阶段,团队成员可能会简单地提出一堆问题并判断答案是否"看起来正确":
根据用例,可能会跟踪不同的指标:
重要的是,不能优化没有测量的东西。RAG系统有许多可调节的旋钮(嵌入模型选择、块大小、文档数量、提示格式等)。全面的评估套件允许对这些组件进行A/B测试。例如,可能会实验:添加重排序器是否如预期提高了答案精度?将检索段落数量从3增加到5是否真的会产生更好的答案,还是只会使语言模型混淆?只有通过适当的评估才能系统地迭代。许多团队扩展开源框架或构建内部仪表板。一些团队采用两层评估:每次构建的自动检查(以捕捉明显的退步)和定期更深入的人工评估。
成功的RAG部署大量投资于评估——涵盖查询理解、源引用、响应完整性和幻觉率。他们通常需要开发特定领域的测试集,因为通用基准没有捕捉到他们用户的需求。例如,法律文档助手可能会创建一套法律问答,并确保系统为每个答案引用法律中的正确条款,专门评估这一点。
2024年的一个前沿发展是流式RAG——使RAG系统能够实时处理持续更新的数据流。在经典RAG中,索引定期更新(可能每天一次或在新文档摄入时)。但如果信息每分钟都在变化(想想股票价格、实时体育评论、突发新闻)呢?流式RAG扩展了架构以支持连续摄入和检索。
在流式RAG管道中,可能有类似:文档(或数据库更新)的Kafka流不断输入到向量数据库索引中。系统执行增量索引——当新文本到达时,它立即被嵌入并索引到向量存储中(过时时可能也被移除)。检索器设计为始终查询索引的最新状态。这涉及为快速写入和读取设计向量数据库。现代向量数据库如Weaviate和Pinecone已经改进了他们的更新性能,以处理高QPS的插入,使这成为可能。
此外,语言模型推理可能需要意识到时间。流式RAG可以在提示中纳入时间意识(例如,添加一个语句如"当前日期时间:2025-05-01 10:00 EST",让模型知道优先考虑最新信息)。此外,如果数据在生成过程中快速变化,可以采用自适应生成等策略——对于长时间运行的输出,可能在生成中间检查是否有新信息(虽然在实践中,生成通常非常快,除非是非常慢的过程,否则这通常不是必要的)。
流式RAG的参考架构可能包括:
一个真实世界的场景:回答有关股市事件的财务助手。使用流式RAG,一旦关于公司X的新闻文章发布并进入新闻源,它就会被索引。如果用户几秒钟后问"今天公司X的股票怎么样?“,检索器会找到该新鲜新闻文章,语言模型可以将其整合,说"公司X的股票今天上涨5%,此前报告本早上创纪录的收益”——静态语言模型不会知道的事情。这使语言模型应用更接近实时商业智能。
当然,使其生产就绪意味着谨慎处理吞吐量和一致性。如果摄入量非常高,可能需要扩展索引工作者并分区向量索引。此外,考虑增量重排序或窗口搜索:对于流,有时更关心最近N小时的数据——可以为效率和相关性限制搜索到最近项目。
设计架构和选择工具后,部署RAG系统有一些实际考虑:
生产RAG管道引入额外步骤(检索等),因此保持低延迟很重要:
如果检索器未扩展,可能成为瓶颈;确保向量数据库的大小能够处理查询负载:
在生产中,不仅要监控典型的基础设施指标(延迟、错误率),还要监控RAG特定的指标:
如果知识库包含敏感数据,在检索中实现访问控制:
系统上线后,收集用户反馈:
许多公司已经部署了RAG:
为了具体说明RAG的应用,我们以使用LlamaIndex和向量数据库部署文档问答助手为例:
我们为GPT-4(或开源LLM如Llama2-Chat 70B)构建如下提示:
你是一个回答技术问题的助手。请使用以下文档摘录来回答问题。如果在摘录中找不到答案,请直接说不知道。
摘录1(来自文档:入门指南):"...[内容]..."
摘录2(来自文档:安装指南):"...[内容]..."
问题:如何在Ubuntu上安装该工具?
答案:
这个提示明确告诉模型使用文档摘录并避免胡编乱造信息。
LLM会生成类似这样的回答:“要在Ubuntu上安装该工具,首先更新apt,然后运行sudo apt-get install toolname
。(来自安装指南)”。由于我们的提示指示,它会包含参考文档,并且不会引入摘录中不存在的信息。如果不知道答案,会直接表明"抱歉,我没有这方面的信息"。
# 假设文档已在向量存储中索引,且我们有查询函数
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系统将信息检索的稳健性与LLM的生成能力相结合。通过遵循最佳实践—精选高质量数据源、保持索引更新、优化提示以获得有根据的答案、严格评估性能—团队可以构建可靠且可扩展的RAG系统。最新进展如流式更新和更好的混合搜索确保RAG不断发展,能够处理更大、更动态的知识库。
到2025年,我们看到RAG技术将应用于从客户支持机器人到医疗助手(结合严格的隐私控制)等各种场景。它是知识感知型AI的基础架构。通过掌握RAG设计并了解新工具动态,开发人员可以大大拓展LLM应用的能力—提供纯LLM无法实现的真实、最新且上下文相关的响应。