
检索增强生成(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 的文档模板允许您为每个文档自定义嵌入,确保只包含最相关的字段。
自定义文档处理有助于您:
- 通过精确嵌入提高检索相关性
- 通过减少不必要的令牌来降低成本
- 确保不同文档类型之间的一致性
- 支持针对独特数据格式的特定领域需求
- 随着系统发展迭代和完善嵌入策略
这是一个来自 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.com.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 以获取相关文档
系统使用混合搜索方法(语义搜索占 70%,关键字搜索占 30%)查询 Meilisearch。它还强制执行 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 驱动应用程序越来越有价值的方法。