加载中

本演示通过一个实际示例展示了完整的 RAG(检索增强生成,Retrieval-Augmented Generation)实现过程。我们将从零开始构建一个向量数据库,并使用一份示例报告文档执行 RAG 工作流的全部五个步骤。

搭建向量数据库

该实现使用了一个自定义的 VectorIndex 类,用于处理嵌入(embeddings)的存储以及执行相似度搜索。这个类为我们的向量数据库操作提供了所需的核心功能。

五步 RAG 实现

第一步:按章节对文本进行分块

首先,我们使用之前介绍过的基于章节的分块方法,加载并对源文档进行分块:

python
with open("./report.md", "r") as f:
    text = f.read()

chunks = chunk_by_section(text)

这会将我们的报告拆分为可以独立处理的逻辑章节。

第二步:为每个分块生成嵌入

接下来,我们使用列表推导式为每个分块创建嵌入:

python
embeddings = [generate_embedding(chunk) for chunk in chunks]

这一步涉及多次 API 调用,因此需要一些时间才能完成。每个分块都会被转换为一个数值向量表示。

第三步:将嵌入存储到向量数据库中

现在我们创建向量存储,并用嵌入及其关联的文本来填充它:

python
store = VectorIndex()

for embedding, chunk in zip(embeddings, chunks):
    store.add_vector(embedding, {"content": chunk})

这里的关键点在于,我们同时存储了嵌入和原始文本。仅仅得到一串数字是没有用的——我们需要与这些嵌入相对应的实际文本内容。这些元数据使我们之后能够检索到有意义的结果。

第四步:生成用户查询的嵌入

当用户提出问题时,我们将其转换为相同的嵌入格式:

python
user_embedding = generate_embedding("What did the software engineering dept do last year?")

这会创建用户问题的向量表示,以便与我们存储的嵌入进行比较。

第五步:搜索并检索相关分块

最后,我们在向量存储中进行搜索,以找到最相似的内容:

python
results = store.search(user_embedding, 2)

for doc, distance in results:
    print(distance, "\n", doc["content"][0:200], "\n")

这会返回两个最相关的分块及其余弦距离分数。距离越小,表示相似度越高。

理解结果

搜索结果按相关性排序。在我们的示例中,软件工程部分的距离最小(0.71),因此是最相关的匹配项。方法论部分位居第二,距离为 0.72。

距离指标有助于您了解系统对每个结果相关性的置信程度。距离越近,意味着与用户查询的匹配度越好。

为什么要将文本与嵌入一起存储

一个关键的设计决策是将原始文本与每个嵌入一起存储。如果不这样做,您只能得到数字数组,这对生成响应没有用处。通过包含源文本,您可以立即使用检索到的分块为您的语言模型提供上下文。

这就完成了核心的 RAG 工作流,不过在实际应用场景中,还有一些额外的优化和改进可以提升性能。