RAG 与向量数据库实战:在 generative-ai-for-beginners 中用自有数据为 LLM 应用注入知识基础
本文基于 generative-ai-for-beginners 课程第 15 课《检索增强生成(RAG)与向量数据库》的阿拉伯语版本文档展开,系统讲解 RAG 的工作机制与向量数据库的构建方法,并结合仓库中配套的可运行 Notebook(notebook-rag-vector-databases.ipynb)与示例数据(data/perceptron.md、data/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 按以下四个环节运转:
- 知识库(Knowledge base):检索之前,文档需要先被摄取和预处理——通常是把大文档切分为更小的块(chunks)、转换为文本嵌入(embeddings)、再存入数据库;
- 用户查询(User Query):用户提出问题;
- 检索(Retrieval):嵌入模型从知识库中检索相关信息,为提示词提供更多上下文;
- 增强生成(Augmented Generation):LLM 基于检索到的数据增强其回复,使输出不仅依赖预训练数据,还融合注入的上下文信息,最终把答案返回给用户。
从架构上看,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_ENDPOINT 与 COSMOS_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" 嵌入的效果示意如下:
检索与向量搜索
当用户提问时,检索器(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 进一步把 indices 与 distances 存回 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_DEPLOYMENT、AZURE_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.md、data/frameworks.md、data/own_framework.md——它们既是本课程的 RAG 语料,也是理解"切块粒度如何影响检索质量"的一手素材。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00


