1. 内容讲解
1.1 RAG (Retrieval-Augmented Generation) 简介
大型语言模型(LLM)虽然知识渊博,但它们存在两个主要问题:
RAG (Retrieval-Augmented Generation,检索增强生成) 是一种强大的技术,旨在解决这些问题。其核心思想非常直观:
通过 RAG,LLM 就好像有了一个可以随时查阅的“开卷资料”,从而能够回答特定领域的问题,并大大减少“幻觉”(胡说八道)的产生。
1.2 LlamaIndex 的核心流程:Load, Index, Query
LlamaIndex (前身为 GPT Index) 是一个专门为构建和优化 RAG 应用而设计的框架。它将复杂的 RAG 流程简化为三个核心步骤:
-
Load (加载数据):
- 这是 RAG 的第一步:将你的数据源(PDF、Markdown、Notion、数据库等)加载到内存中。
- LlamaIndex 提供了大量的 Reader (也称为 Loader),可以轻松地从各种数据源读取数据并将其转换为统一的 Document 对象。
-
Index (构建索引):
- 加载数据后,我们需要将其处理成一种易于检索的结构,这个过程就是“构建索引”。
- 最常见的索引类型是 VectorStoreIndex (向量存储索引)。它的工作原理是:
- 将加载的 Document 分割成更小的文本块(Node)。
- 使用一个“嵌入模型”(Embedding Model,如 OpenAI 的 text-embedding-ada-002)将每个文本块转换成一个数学向量(一长串数字)。这个向量可以被认为是该文本块在语义空间中的“坐标”。
- 将这些文本块及其对应的向量存储起来(可以存在内存中,也可以存在专门的向量数据库里)。
Query (查询):
- 当用户提出问题时,查询阶段开始:
- LlamaIndex 首先使用相同的嵌入模型将用户的问题也转换成一个向量。
- 然后,它在索引中计算用户问题向量与所有文本块向量之间的“相似度”(通常是余弦相似度)。
- 它找出与问题最相似的 Top-K 个文本块。
- 最后,它将这些最相关的文本块和原始问题一起提交给 LLM,生成最终答案。
1.3 使用 SimpleDirectoryReader 加载本地文档
SimpleDirectoryReader 是 LlamaIndex 中最常用的加载器之一。它非常方便,可以递归地加载一个文件夹内所有支持的文件类型(如 .txt, .md, .pdf 等)。你只需要告诉它文件夹的路径,它就会自动处理剩下的事情。
1.4 创建向量索引(VectorStoreIndex)
VectorStoreIndex 是构建 RAG 应用的起点。通过调用 VectorStoreIndex.from_documents(),LlamaIndex 会在后台自动完成文本分割、向量嵌入和索引构建的全过程,极大地简化了开发流程。
1.5 从索引中查询信息
一旦索引构建完成,我们就可以通过其 as_query_engine() 方法创建一个查询引擎。这个查询引擎封装了从提问到生成答案的整个 RAG 流程。我们只需要调用其 query() 方法并传入问题字符串,就可以得到最终的答案。
2. 代码示例
我们将创建一个新的 FastAPI 端点,允许用户上传一个文本文件,然后基于该文件的内容进行问答。
2.1 准备工作:创建数据目录
在项目根目录下创建一个用于存放上传文件的目录:
mkdir data
2.2 更新 Pydantic 模型
app/api/v1/models/ai.py (新增以下内容)
# … existing code …
from fastapi import UploadFile, File
class RAGQueryRequest(BaseModel):
query: str = Field(..., description=\”The query to ask the RAG system\”)
index_id: str = Field(..., description=\”The ID of the index to query (e.g., filename)\”)
class RAGQueryResponse(BaseModel):
response: str
source_nodes: List[Dict] # 包含来源节点的信息
2.3 更新 AI 服务层
app/services/ai.py (新增方法和状态管理到 AIService)
# … existing code …
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
import os
DATA_DIR = \”data\”
class AIService:
def __init__(self):
# … existing __init__ code …
# LlamaIndex 全局设置
Settings.llm = OpenAI(model=\”gpt-3.5-turbo\”, api_key=settings.OPENAI_API_KEY, base_url=settings.OPENAI_API_BASE)
Settings.embed_model = OpenAIEmbedding(api_key=settings.OPENAI_API_KEY, base_url=settings.OPENAI_API_BASE)
# 用于缓存索引的字典
self.indices = {
}
os.makedirs(DATA_DIR, exist_ok=True)
# … existing methods …
async def create_index_from_file(self, file: UploadFile) –> str:
# 将上传的文件保存到 data 目录
file_path = os.path.join(DATA_DIR, file.filename)
with open(file_path, \”wb\”) as buffer:
buffer.write(
评论前必须登录!
注册