首页
/ RAG 与向量数据库实战:在 generative-ai-for-beginners 中用自有数据为 LLM 应用注入知识基础

RAG 与向量数据库实战:在 generative-ai-for-beginners 中用自有数据为 LLM 应用注入知识基础

2026-09-04 10:50:16作者:江焘钦

本文基于 generative-ai-for-beginners 课程第 15 课《检索增强生成(RAG)与向量数据库》的阿拉伯语版本文档展开,系统讲解 RAG 的工作机制与向量数据库的构建方法,并结合仓库中配套的可运行 Notebook(notebook-rag-vector-databases.ipynb)与示例数据(data/perceptron.mddata/frameworks.md),带你完整走一遍"文档切块 → 向量化 → 相似度检索 → LLM 接地生成"的 RAG 全流程。

课程定位与学习目标

在"搜索应用"一课中,课程简要介绍了如何把自有数据接入大型语言模型(LLM)。本课则深入剖析把数据"接地"(grounding)到 LLM 应用的完整过程:流程背后的机制、数据的存储方式(包括嵌入向量与文本两种形态)。

完成本课后,你将能够:

  • 解释 RAG 在数据检索与处理中的意义;
  • 搭建 RAG 应用,并把你的数据接地到 LLM;
  • 在 LLM 应用中有效集成 RAG 与向量数据库。

本课场景:用自有数据增强教育初创公司的 LLM

课程设定了一个教育初创公司场景:把公司内部的学习笔记接入聊天机器人,让学习者能更好地研习不同主题、更高效地复习备考。具体技术选型如下:

  • Azure OpenAI:用于构建聊天机器人的 LLM;
  • 面向初学者的 AI 课程中"神经网络"这一课:作为接地 LLM 的知识数据;
  • Azure AI Search 与 Azure Cosmos DB:作为向量数据库,用于存储数据并建立搜索索引。

基于这些数据,用户可以完成三件事:从笔记中生成练习测验(practice quizzes)、生成复习卡片(flash cards)、把笔记总结为简洁的概览。在阿拉伯语版课程目录中,仓库为 Notebook 准备了阿拉伯语语料(translations/ar/15-rag-and-vector-databases/data/perceptron.md 等),与英文版使用的神经网络主题文档一一对应。

RAG(检索增强生成)的工作原理

由 LLM 驱动的聊天机器人处理用户提示并生成回复,它被设计得可以就广泛话题与用户交互。但其回复受限于所给上下文与基础训练数据:例如 GPT-4 的知识截止时间为 2021 年 9 月,不了解此后的事件;同时 LLM 的训练数据天然不包含机密信息(个人笔记、公司产品手册等)。

假设你要部署一个"从笔记生成测验"的聊天机器人,就需要连接知识库——这正是 RAG 的用武之地。RAG 按以下四个环节运转:

RAG 工作流程示意图

  • 知识库(Knowledge base):检索之前,文档需要先被摄取和预处理——通常是把大文档切分为更小的块(chunks)、转换为文本嵌入(embeddings)、再存入数据库;
  • 用户查询(User Query):用户提出问题;
  • 检索(Retrieval):嵌入模型从知识库中检索相关信息,为提示词提供更多上下文;
  • 增强生成(Augmented Generation):LLM 基于检索到的数据增强其回复,使输出不仅依赖预训练数据,还融合注入的上下文信息,最终把答案返回给用户。

RAG 编码器-解码器架构示意图

从架构上看,RAG 由**编码器(encoder)解码器(decoder)**两部分构成。当用户提问时,输入文本先被"编码"为捕捉词义关系的向量,再"解码"映射到文档索引上,并基于用户查询生成新文本;LLM 使用编码器-解码器模型产生最终输出。

原始论文《Retrieval-Augmented Generation for Knowledge intensive NLP Tasks》提出两种实现方式:

  • RAG-Sequence:利用检索到的文档直接预测对用户查询的最佳答案;
  • RAG-Token:利用文档逐个生成下一个 token,边生成边检索以回答用户查询。

为什么要用 RAG

  • 信息丰富度:确保文本回复保持最新、与时俱进,通过访问内部知识库增强领域特定任务的表现;
  • 减少虚构(hallucination):利用知识库中可验证的数据为用户查询提供上下文;
  • 成本效益:相比微调(fine-tuning)一个 LLM,RAG 在经济上更划算。

构建知识库:向量数据库

本应用基于个人数据,即本课程"神经网络"一课的内容。

什么是向量数据库

向量数据库不同于传统数据库,它是专门设计用于存储、管理和检索嵌入向量的数据库,保存的是文档的数值化表示。把数据拆解成数值嵌入,让 AI 系统更容易理解和处理。

把嵌入存入向量数据库的原因在于:LLM 能接受的输入 token 数量有上限,无法把全部嵌入一次性传入。因此需要把数据切块(chunking)——当用户提问时,把与问题最相似的嵌入随提示词一起返回。切块同时降低了经 LLM 传输的 token 数量带来的成本。

常见的向量数据库包括:Azure Cosmos DB、Clarifyai、Pinecone、ChromaDB、ScaNN、Qdrant、DeepLake。使用 Azure CLI 创建 Azure Cosmos DB 资源的命令如下:

az login
az group create -n <resource-group-name> -l <location>
az cosmosdb create -n <cosmos-db-name> -r <resource-group-name>
az cosmosdb list-keys -n <cosmos-db-name> -g <resource-group-group-name>

仓库配套 Notebook 中演示了如何用 azure-cosmos SDK 连接该数据库(数据库名 rag-cosmos-db、容器名 data),连接凭据从环境变量 COSMOS_DB_ENDPOINTCOSMOS_DB_KEY 读取:

from azure.cosmos import CosmosClient

url = os.getenv('COSMOS_DB_ENDPOINT')
key = os.getenv('COSMOS_DB_KEY')
client = CosmosClient(url, credential=key)

database = client.get_database_client('rag-cosmos-db')
container = database.get_container_client('data')

从文本到嵌入

在存入数据库之前,需要先把数据转换为向量嵌入。如果处理的是大文档或长文本,可以按预期的查询方式来切块——切块可以发生在句子级或段落级。由于块的含义来自其周围的词,你还可以为块补充额外上下文,例如加上文档标题,或包含块前后的一些文本。课程给出的切块函数实现如下:

def split_text(text, max_length, min_length):
    words = text.split()
    chunks = []
    current_chunk = []

    for word in words:
        current_chunk.append(word)
        if len(' '.join(current_chunk)) < max_length and len(' '.join(current_chunk)) > min_length:
            chunks.append(' '.join(current_chunk))
            current_chunk = []

    # 如果最后一个块未达到最小长度,仍然加入
    if current_chunk:
        chunks.append(' '.join(current_chunk))

    return chunks

在配套 Notebook 中,该函数以 split_text(x, 400, 300) 调用——即每个块保持在约 300~400 字符的区间内;随后用 DataFrame.explode('chunks') 把"每文件一行、块存为列表"的宽表展开为"每块一行"的扁平平铺表(flattened_df),方便逐块嵌入。

切块完成后,可以使用不同嵌入模型把文本向量化,例如:word2vec、OpenAI 的 ada-002、Azure Computer Vision 等。模型选择取决于:所用的语言、编码的内容类型(文本/图像/音频)、可编码的输入大小、以及嵌入输出的维度长度。使用 OpenAI text-embedding-ada-002 模型对单词 "cat" 嵌入的效果示意如下:

单词 cat 的嵌入向量示意图

检索与向量搜索

当用户提问时,检索器(retriever)先用查询编码器(query encoder)把问题转换为向量,然后在文档搜索索引中查找与输入相关的向量;完成后,把输入向量与文档向量都还原为文本,连同上下文一起交给 LLM。

检索(Retrieval)

检索就是系统从索引中快速找出满足搜索条件的文档的过程。检索器的目标是拿到能"接地" LLM、为你的数据提供上下文的文档。数据库中常用的搜索方式有三种:

  • 关键词搜索(Keyword search):用于纯文本匹配;
  • 向量搜索(Vector search):用嵌入模型把文档从文本转为向量表示,从而支持基于词义匹配的语义搜索(semantic search)——检索时查询与用户问题向量表示最接近的文档;
  • 混合搜索(Hybrid):关键词搜索与向量搜索的组合。

检索的一个难题是:当数据库中没有与查询相似的内容时,系统只能返回它能拿到的"最像"的信息。应对手段包括设定相关性的最大距离阈值,或使用混合搜索。本课采用的就是混合搜索策略,数据会存进一个 DataFrame,其中各列分别保存文本块(chunks)与对应的嵌入向量(embeddings)。

向量相似度

检索器会在知识库中寻找彼此靠近、互为最近邻(closest neighbour)的嵌入——因为它们对应相似的文本。场景流程是:用户查询先被嵌入,再与相似的嵌入进行匹配。衡量向量之间相似度的常用度量是余弦相似度(cosine similarity),它基于两个向量之间的夹角。其他可选度量还有:欧氏距离(两个向量端点之间的直线距离)与点积(两个向量对应元素乘积之和)。

搜索索引(Search index)

执行检索前,需要为知识库建立搜索索引。索引保存全部嵌入,即使数据库很大也能快速取出最相似的块。课程使用 scikit-learn 在本地创建索引:

from sklearn.neighbors import NearestNeighbors

embeddings = flattened_df['embeddings'].to_list()

# 创建搜索索引
nbrs = NearestNeighbors(n_neighbors=5, algorithm='ball_tree').fit(embeddings)

# 查询索引可使用 kneighbors 方法
distances, indices = nbrs.kneighbors(embeddings)

其中 n_neighbors=5 表示每次查询返回 5 个最近邻,algorithm='ball_tree' 选用球树算法加速邻域搜索。配套 Notebook 进一步把 indicesdistances 存回 DataFrame 的对应列,供后续展示与调试。

重排序(Re-ranking)

查询数据库后,可能需要按相关度从高到低对结果排序。重排序模型利用机器学习技术提升搜索结果的相关性。在 Azure AI Search 中,重排序由语义重排序器(semantic reranker)自动完成。用最近邻实现重排序的示例如下:

# 查找最相似的文档
distances, indices = nbrs.kneighbors([query_vector])

index = []
# 打印最相似的文档
for i in range(3):
    index = indices[0][i]
    for index in indices[0]:
        print(flattened_df['chunks'].iloc[index])
        print(flattened_df['path'].iloc[index])
        print(flattened_df['distances'].iloc[index])
    else:
        print(f"Index {index} not found in DataFrame")

集成 LLM:把一切组合起来

最后一步是把 LLM 加入流程,使回答真正基于你的数据。完整实现如下:

user_input = "what is a perceptron?"

def chatbot(user_input):
    # 将问题转换为查询向量
    query_vector = create_embeddings(user_input)

    # 查找最相似的文档
    distances, indices = nbrs.kneighbors([query_vector])

    # 将文档加入查询以提供上下文
    history = []
    for index in indices[0]:
        history.append(flattened_df['chunks'].iloc[index])

    # 组合上下文与用户输入
    history.append(user_input)

    # 构造消息对象
    messages=[
        {"role": "system", "content": "You are an AI assistant that helps with AI questions."},
        {"role": "user", "content": "\n\n".join(history) }
    ]

    # 调用生成接口获得回复
    response = client.responses.create(
        model="gpt-4o-mini",
        temperature=0.7,
        max_output_tokens=800,
        input=messages,
        store=False,
    )

    return response.output_text

chatbot(user_input)

这里可以看到完整的 RAG 调用链:create_embeddings 把用户问题向量化 → nbrs.kneighbors 从索引取出最相似的块 → 块文本拼接成带上下文的 user 消息 → 由 LLM 生成接地后的回答。仓库英文版 Notebook 中的客户端配置展示了 Azure OpenAI 的连接方式:以 {endpoint}/openai/v1/ 作为 base_url 初始化 OpenAI 客户端,嵌入与聊天模型的部署名分别从环境变量 AZURE_OPENAI_EMBEDDINGS_DEPLOYMENTAZURE_OPENAI_DEPLOYMENT 读取。阿拉伯语版 Notebook(translations/ar/15-rag-and-vector-databases/notebook-rag-vector-databases.ipynb)则以 text-embedding-ada-002-2 作为嵌入模型、gpt-35-turbo-1106 作为聊天模型完成同样的流程,阅读两个版本可以直观对比 OpenAI SDK 接口从旧版 openai.embeddings.create 到新版 client.embeddings.create 的演进。

评估应用:RAG 应用怎么"算好"

课程给出了四项评估指标:

  • 回复质量:确保回答听起来自然、流畅、拟人;
  • 数据接地性(Groundedness):评估回答是否确实来源于所提供的文档;
  • 相关性(Relevance):评估回答是否与所提问题匹配、相关;
  • 流畅度(Fluency):回答在语法上是否通顺。

配套 Notebook 还演示了用 Mean Average Precision(MAP) 做检索相关性的量化评估:为"什么是感知机"等 4 个测试问题分别准备相关/不相关的回答集合,用 sklearn.metrics.average_precision_score 对每个查询计算平均精度,再取平均得到 MAP 分数。

RAG 与向量数据库的典型用例

  • 问答系统(Q&A):把公司数据接地到聊天机器人,供员工提问;
  • 推荐系统:构建匹配最相似值的系统,如电影、餐厅推荐等;
  • 聊天机器人服务:存储对话历史,基于用户数据个性化对话;
  • 基于向量嵌入的图像搜索:在图像识别与异常检测场景中十分有用。

小结与练习

本部分覆盖了 RAG 的基本领域:从把数据加入应用、用户查询到最终输出。要简化 RAG 的搭建,可以使用 Semantic Kernel、LangChain、AutoGen 等框架。

练习(来自原文档 Assignment 一节):

  • 使用你偏好的框架为应用构建前端界面;
  • 使用 LangChain 或 Semantic Kernel 框架重建你的 RAG 应用。

可继续深入的资料:课程英文版 15-rag-and-vector-databases/README.md、可交互 Notebook 15-rag-and-vector-databases/notebook-rag-vector-databases.ipynb,以及三份神经网络主题示例文档 data/perceptron.mddata/frameworks.mddata/own_framework.md——它们既是本课程的 RAG 语料,也是理解"切块粒度如何影响检索质量"的一手素材。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384