构建 RAG 系统(使用 Meilisearch):一份综合指南
探索构建 RAG 系统的最佳实践,包括优化文档、集成 AI 以及为何高效检索是成功的关键。

检索增强生成 (RAG) 已成为现代 AI 应用的重要组成部分,使大型语言模型 (LLM) 能够提供更准确、更可控的响应。尽管向量数据库是 RAG 的标准,但 Meilisearch 作为一种快速、开源的替代方案脱颖而出,它具有 AI 驱动的搜索、出色的相关性和卓越的速度。
本指南将引导您使用 Meilisearch 构建和优化 RAG 系统。
理解 RAG
RAG 是一种通过将 LLM 输出基于外部可检索数据来增强其性能的过程。RAG 系统不依赖于模型训练的知识,而是首先从精心策划的知识库中检索相关信息,然后使用此上下文生成响应。
典型的 RAG 工作流包含三个主要步骤
- 检索:查询知识库以查找相关文档或段落
- 增强:将检索到的信息与用户的查询相结合
- 生成:使用 LLM 根据查询和检索到的上下文生成响应
RAG 的关键组件
RAG 系统由三个基本组件组成
-
外部数据源 外部数据源是 RAG 系统的基础。这些来源,例如知识库或技术文档,提供了 LLM 用于生成响应的信息。这些数据的质量直接影响性能;它们必须组织良好并定期更新,以确保准确性和相关性。
-
向量存储 向量存储作为原始数据和 LLM 之间的桥梁。它将文本转换为向量嵌入——含义的数值表示。这些向量允许高效的相似性搜索,从而快速检索相关信息。像 Meilisearch 这样的现代工具将关键词搜索与语义相似性相结合,以提供快速且可扩展的结果。
-
大型语言模型 LLM 是系统的智能核心,负责理解用户查询并生成连贯、相关的响应。它将用户查询与从向量存储中检索到的上下文相结合,以生成准确的答复。像 GPT-4、Claude 或 Llama 2 这样的模型擅长在给定上下文的约束下创建类人响应。
为什么 LLM 需要 RAG:克服关键限制
大型语言模型擅长处理通用知识,但面临两个显著的限制
- 它们难以处理专业领域的特定信息
- 它们受限于上次训练会话,依赖过时知识,并且常常落后于当前进展数月甚至数年。
RAG 让您能够同时解决这两个挑战。例如,一家律师事务所可以通过整合其历史案件档案以及最新的法院判决和法规变更来增强其 LLM 的能力。医疗保健提供商可以整合已有的医学文献以及最新的临床试验或更新的治疗方案。
持续更新知识库的能力确保您的 LLM 驱动应用程序能够提供准确、最新的响应,将深厚的领域专业知识与您领域的最新信息相结合。
如何在 RAG 系统中优化文档检索
高效的信息检索对于 RAG 至关重要。如果没有精确和相关的文档检索,即使是最先进的 LLM 也可能产生不准确或不完整的响应。目标是确保对查询只检索最相关、上下文最丰富的文档。
选择合适的文档检索系统是此过程中的关键一步。Meilisearch 提供了一个快速、开源的搜索引擎,支持关键词搜索以及更高级的 AI 驱动搜索方法,这些方法将精确单词匹配与语义搜索相结合。这种双重功能使其成为 RAG 系统的理想工具,RAG 系统的目标不仅是检索匹配关键词的文档,还要检索语义相关的文档。
Meilisearch 提供了一系列特别适合 RAG 系统的功能
- 轻松集成嵌入器:Meilisearch 自动生成向量嵌入,以最少的设置实现高质量的语义检索,并灵活选择最新的嵌入器模型。
- 混合搜索功能:结合关键词和语义(基于向量)搜索,以提供更广泛、更准确的文档检索。
- 速度和性能:Meilisearch 提供超快的响应时间,确保检索永远不会成为您的 LLM 工作流中的瓶颈。
- 可定制的相关性:根据新鲜度或重要性等属性调整排名规则和文档排序,以优先显示最有价值的结果。设置相关性阈值以从搜索中排除不那么相关的结果。
一旦您建立了检索系统,下一步就是优化数据的存储、索引和检索方式。以下策略——文档分块、元数据丰富和相关性调优——将确保每个搜索查询都返回最有用和上下文最相关的信息。
如何对文档进行分块以最大限度地提高相关性
将文档分解成最佳大小的块对于有效检索至关重要。块应足够大以保持上下文,但又足够小以保持特异性和相关性。考虑段落或章节等语义边界,而不是任意的字符计数。
丰富元数据以提高搜索精度
用丰富的元数据增强您的文档,以提高检索准确性。包括类别、标签、时间戳、作者和其他相关属性。例如,用特定产品版本标记技术文档可以显著提高检索质量。
调整相关性以获得准确结果
根据您的特定用例微调搜索参数。调整混合搜索语义比例,根据您领域的需求平衡概念理解和精确匹配。使用排名分数阈值过滤掉低质量匹配,但请注意不要设置过高而遗漏有价值的上下文信息。
为 RAG 设置 Meilisearch
检索系统的质量直接影响生成响应的准确性和可靠性。Meilisearch 因其 AI 驱动的搜索功能、可定制的文档处理和高级排名控制而成为 RAG 实现的杰出搜索引擎。
设置 Meilisearch
与仅依赖语义搜索的传统向量存储不同,Meilisearch 将向量相似性与全文搜索相结合,为您提供了两全其美的优势。
首先,您需要创建一个 Meilisearch 项目并激活AI 驱动的搜索功能。
然后,您需要配置您选择的嵌入器。我们将使用 OpenAI 嵌入器,但 Meilisearch 还支持来自 HuggingFace、Ollama 以及通过 RESTful API 访问的任何嵌入器。
import os import meilisearch client = meilisearch.Client(os.getenv('MEILI_HOST'), os.getenv('MEILI_API_KEY')) # An index is where the documents are stored. index = client.index('domain-data') index.update_embedders({ "openai": { "source": "openAi", "apiKey": "OPEN_AI_API_KEY", "model": "text-embedding-3-small", "documentTemplate": "A document titled '{{doc.hierarchy_lvl1}}'. Under the section '{{doc.hierarchy_lvl2}}'. This is further divided into '{{doc.hierarchy_lvl3}}'. It discusses {{doc.content}}." } })
注意:您需要将 OPEN_AI_API_KEY 替换为您的 OpenAI API 密钥。
使用 Meilisearch 的文档模板进行智能文档处理
Meilisearch 的文档模板允许您为每个文档自定义嵌入,确保只包含最相关的字段。
自定义文档处理有助于您
- 通过精确嵌入提高检索相关性
- 通过减少不必要的 token 降低成本
- 确保不同文档类型之间的一致性
- 支持针对独特数据格式的领域特定需求
- 随着系统演进,迭代并完善嵌入策略
这是 Meilisearch 文档中的一个示例文档
{ "hierarchy_lvl1":"Filter expression reference" "hierarchy_lvl2":"Filter expressions" "hierarchy_lvl3":"Creating filter expressions with arrays" "content":"Inner array elements are connected by an OR operator. The following expression returns either horror or comedy films" "hierarchy_lvl0":"Filtering and sorting" "anchor":"creating-filter-expressions-with-arrays" "url":"https://meilisearch.org.cn/docs/learn/filtering_and_sorting/filter_expression_reference#creating-filter-expressions-with-arrays" "objectID":"bbcce6ab00badb2a377b455ba16180d" "publication_date":"1733986800" }
为了优化此文档的嵌入,我们决定专注于最有意义的字段
- 标题:hierarchy_lvl0 到 hierarchy_lvl3 的值将包含在嵌入中,以保留文档结构和上下文
- 内容:content 的值将被嵌入,因为它提供了语义搜索所需的基本文本
其他字段,如 publication_date
,将从嵌入中排除,但仍可用于排序。这使得 Meilisearch 可以在按日期排序的同时,保持嵌入精简并专注于相关性。
Meilisearch 可自定义的排名规则
Meilisearch 提供对结果排名的精细控制,使您能够自定义搜索结果的排序和优先级。这种控制确保用户首先看到最相关的内容,并根据您特定的业务或领域需求进行定制。
与固定排名系统不同,Meilisearch 允许您定义自己的排名规则。这种灵活性有助于您优先处理某些类型的内容,推广更新或更相关的结果,并创建符合用户期望的搜索体验。
例如,我们在默认排名规则中添加了一个自定义规则,以提升较新文档的优先级。
# Configure settings import os import meilisearch # Initialize the Meilisearch client client = meilisearch.Client(os.getenv('MEILI_HOST'), os.getenv('MEILI_API_KEY')) # An index is where the documents are stored. index = client.index('domain-data') index.update_settings({ 'rankingRules': [ "words", "typo", "proximity", "attribute", "sort", "exactness", "publication_date:desc", ], 'searchableAttributes': [ 'hierarchy_lvl1', 'hierarchy_lvl2', 'hierarchy_lvl3', 'content' ] })
索引您的文档
在设置好 Meilisearch 并使用文档分块和元数据丰富等最佳实践准备好数据后,您现在可以将数据推送到 Meilisearch。
Meilisearch 接受 .json
、.ndjson
和 .csv
格式的数据。有几种方法可以上传您的文档
- 将文件拖放到 Cloud UI 中。
- 通过
/indexes/{index_uid}/documents
路由使用 API。 - 从您偏好的 SDK 调用方法。
💡 注意:您的文档必须具有唯一标识符 (id)。这对于 Meilisearch 正确识别和更新记录至关重要。
以下是如何使用 Python SDK 上传文档
import os import meilisearch import json # Initialize Meilisearch client client = meilisearch.Client(os.getenv('MEILI_HOST'), os.getenv('MEILI_API_KEY'))) # Select or create the index index = client.index('domain-data') # Load the JSON file with open('path/to/your/file.json', 'r') as file: documents = json.load(file) # Load the array of JSON objects as a Python list # Add documents to Meilisearch index.add_documents(documents)
执行 AI 驱动的搜索
使用 q
和 hybrid
执行 AI 驱动的搜索,以使用您之前配置的嵌入器检索搜索结果。
Meilisearch 将返回语义匹配和全文匹配的混合结果,优先显示与查询含义和上下文匹配的结果。您可以使用 semanticRatio
参数微调这种平衡。
index.search( userQuery, { "hybrid": { "embedder": "openai", "semanticRatio": 0.7 # 70% semantic, 30% full-text } } )
这种灵活的控制让您能够
- 优化平衡以适应您的特定用例。
- 根据查询模式实时调整。
- 结合两种方法的优势,确保您不会遗漏关键结果。
这种双重方法确保您不会遗漏纯语义搜索可能遗漏的相关结果,同时保持语义理解的优势。
通过排名分数阈值进行质量控制
rankingScoreThreshold
参数确保搜索响应中只包含高质量的结果。它与排名分数协同工作,排名分数是一个介于 0.0(匹配不佳)到 1.0(完美匹配)之间的数值。任何排名分数低于指定 rankingScoreThreshold
的结果都将被排除。
通过设置排名分数阈值,您可以
- 过滤掉低相关性结果,以提高整体结果质量
- 为 RAG 系统提供更好的上下文,确保 LLM 使用更高质量的数据
- 减少搜索结果中的噪音,最大限度地减少不相关信息
- 自定义相关性,以符合您的特定用例需求
以下查询只返回排名分数大于 0.3 的结果
index.search( userQuery, { "hybrid": { "embedder": "openai", "semanticRatio": 0.7 # 70% semantic, 30% full-text }, "rankingScoreThreshold": 0.4 } )
准备好构建您的 RAG 系统了吗?现在我们已经设置好 Meilisearch。我们将引导您完成使用 Meilisearch 创建 RAG 系统的步骤。
使用 Meilisearch 实现 RAG
我们将使用 Meilisearch 文档作为示例知识库来构建 RAG 系统,演示如何检索、处理和生成准确、上下文感知的响应。
使用的关键技术
我们的实现利用了以下几种关键技术
- FastAPI:驱动处理用户查询的 API
- Meilisearch:检索相关内容
- OpenAI 的 GPT-4:生成类人、上下文感知的响应
- LangChain:通过连接搜索和 LLM 响应生成来编排 AI 工作流。
系统如何运作
当用户提交问题时,系统遵循以下步骤
- 用户输入:用户向 API 提交查询
- 内容检索:Meilisearch 使用关键词和语义搜索的组合来查找最相关的内容
- 上下文构建:系统从搜索结果中构建分层上下文
- LLM 生成:上下文和用户查询被发送到 GPT-4 以生成准确、实用的响应
- 响应交付:系统返回 LLM 生成的答案以及用于生成答案的来源
环境设置
API 密钥和凭据存储在 .env 文件中的环境变量中。我们使用 dotenv
加载它们。
以下是关键服务如何初始化的
- Meilisearch 客户端:使用主机和 API 密钥连接到 Meilisearch 实例。
- OpenAI 客户端:通过 API 密钥认证 GPT-4 LLM。
- FastAPI 应用程序:设置 Web API,供用户与系统交互。
import os
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from meilisearch import Client
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Initialize FastAPI application
app = FastAPI()
# Initialize Meilisearch client
client = meilisearch.Client(os.getenv('MEILI_HOST'), os.getenv('MEILI_API_KEY')))
# Initialize OpenAI
llm = ChatOpenAI(temperature=0, model="gpt-4o", api_key=os.getenv('OPENAI_API_KEY'))
配置 CORS 中间件
为了确保系统可以处理来自不同来源(例如前端客户端)的请求,我们为 FastAPI 应用程序配置了跨域资源共享 (CORS)。这允许来自任何域的跨域请求。
# Configure CORS middleware to allow cross-origin requests
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allows all origins
allow_credentials=True, # Allows credentials (cookies, authorization headers, etc.)
allow_methods=["*"], # Allows all HTTP methods
allow_headers=["*"], # Allows all headers
)
定义查询数据模型
Query
类定义了传入 POST
请求的数据结构。这确保只接受包含有效问题的查询。
class Query(BaseModel):
question: str
工作原理
- 输入验证:FastAPI 将自动验证传入的
POST
请求是否包含一个有效的字符串类型问题字段。 - 数据解析:传入的查询被解析成一个
Query
对象,该对象可以在端点内部使用。
定义 API 端点
该 API 暴露了一个单独的 POST
端点(/query
),用户可以在此处发送查询。该端点检索相关内容,构建上下文,并从 GPT-4 返回答案。
@app.post("/query")
async def query_documents(query: Query):
"""Query documents and generate response using RAG."""
向 Meilisearch 查询相关文档
该系统使用混合搜索方法查询 Meilisearch,该方法结合了语义搜索(70%)和关键词搜索(30%)。它还强制执行 rankingScoreThreshold
为 0.4
,确保只包含高质量的结果。
try:
# Prepare search parameters
search_params = {
"hybrid": {
"embedder": "openai",
"semanticRatio": 0.7 # 70% semantic, 30% full-text
},
"limit": 5, # restricts results to 5 documents
"rankingScoreThreshold": 0.4
}
# Search Meilisearch
search_results = meili.index('domain-data').search(
query.question,
search_params
)
为 GPT-4 构建上下文
一旦 Meilisearch 返回搜索结果,系统会对其进行处理以创建结构化上下文。该上下文保留了文档的分层结构,确保标题和副标题得到保留。
上下文构建过程
- 提取分层数据:系统从搜索结果中提取分层级别(hierarchy_lvl0、hierarchy_lvl1 等)。
- 拼接上下文:将标题和主要内容合并,创建清晰、可读的上下文。
- 分隔部分:每个文档的上下文使用“---”分隔,以提高 GPT-4 的清晰度。
# Prepare context from search results
contexts = []
for hit in search_results['hits']:
context_parts = []
# Add hierarchical path
for i in range(4): # levels 0-3
hierarchy_key = f'hierarchy_lvl{i}'
if hit.get(hierarchy_key):
context_parts.append(f"{' ' * i}> {hit[hierarchy_key]}")
# Add content
if hit.get('content'):
context_parts.append(f"\nContent: {hit['content']}")
contexts.append("\n".join(context_parts))
context = "\n\n---\n\n".join(contexts)
使用 GPT-4 生成响应
组装好的上下文连同用户问题一起传递给 GPT-4。精确的提示确保响应是
- 实用且注重实施
- 基于实际文档
- 当信息不可用时,清晰说明限制
# Create prompt template
prompt_template = """You are a helpful Meilisearch documentation assistant. Use the following Meilisearch documentation to answer the question.
If you cannot find the answer in the context, say so politely and suggest checking Meilisearch's documentation directly.
Provide practical, implementation-focused answers when possible.
Context:
{context}
Question: {question}
Answer (be concise and focus on practical information):"""
使用 LangChain 运行 LLMChain
- 创建 LLMChain:这将 GPT-4 链接到格式化的提示。
- 发送输入:用户查询和上下文被发送到 LLM 进行处理。
- 返回响应:LLM 的响应返回给用户。
prompt = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
# Create and run chain
chain = LLMChain(llm=llm, prompt=prompt)
response = chain.run(context=context, question=query.question)
组装最终的 API 响应
最终的 API 响应包括
- LLM 生成的答案
- 来源(使用的文档的 URL 和层次结构)
return {
"answer": response,
"sources": [{
'url': doc.get('url', ''),
'hierarchy': [
doc.get(f'hierarchy_lvl{i}', '')
for i in range(4)
if doc.get(f'hierarchy_lvl{i}')
]
} for doc in search_results['hits']]
}
处理错误和异常
为避免系统崩溃,所有异常都会被捕获并作为错误响应返回。
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
运行应用程序
最后,您可以使用 Uvicorn 在本地运行 API。此命令将在 localhost:8000 上启动 FastAPI 应用程序。
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
至此,您的 RAG 系统已上线,能够使用 Meilisearch 和 GPT-4 检索相关上下文并生成精确的答案。
如何评估 RAG 系统的性能
确保 RAG 系统中的高质量内容
为您的文档库保持高标准。定期审计和更新您的内容,以确保准确性和相关性。删除可能稀释搜索结果的重复或过时信息。建立信息验证和更新流程,以维护知识库的完整性。
监控性能以识别瓶颈
实施监控以跟踪检索效率。观察失败查询或持续低排名结果的模式。使用这些数据来完善您的文档处理和搜索参数。同时监控技术指标(如响应时间)和质量指标(如相关性分数),以确保最佳性能。这可以通过Meilisearch Cloud 的监控指标和分析仪表板轻松完成。
收集用户反馈
用户反馈是改进 RAG 系统性能最有价值的来源之一。虽然查询延迟或相关性分数等指标提供了技术洞察,但用户反馈揭示了真实世界的问题。
通过收集和分析反馈,您可以识别仅靠系统指标难以发现的问题,例如
- 误报:当对查询返回不相关结果时
- 遗漏上下文:当系统未能检索到用户期望看到的文档时
- 响应缓慢:当用户遇到加载时间慢或响应不完整的情况时
用户反馈可以指导您微调 Meilisearch 配置。它可能会突出需要调整排序以优先显示最新文档,提高 rankingScoreThreshold 以过滤掉低相关性结果,优化 documentTemplate 以嵌入更多相关上下文,或将大型文档分块为更小、更有针对性的部分以提高检索准确性。
要点:使用 Meilisearch 最大化 RAG 性能
使用 Meilisearch 实现 RAG 提供了几个关键优势
- 灵活性:可轻松与各种数据源和 LLM 集成。
- 性能:提供快速检索时间和高效的资源利用。
- 准确性:结合关键词和语义搜索以获得更精确的结果。
- 可扩展性:轻松处理大型、不断增长的知识库。
Meilisearch 强大的功能和高性能使其成为生产就绪 RAG 实现的坚实基础。为了最大限度地发挥系统作用,请关注以下方面:
- 数据准备和索引:确保您的知识库干净、组织良好且结构清晰
- 领域特定微调:根据您的独特上下文调整排名规则、相关性阈值和嵌入策略
- 持续评估:使用用户反馈、系统指标和 LLM 响应来优化系统性能
- 知识库更新:定期审查和更新内容,以保持响应的准确性和相关性
随着 Meilisearch 和 LLM 技术的不断发展,未来的进步将为 RAG 系统带来更高的效率、准确性和灵活性——使其成为 AI 驱动应用程序越来越有价值的方法。