从零构建简易RAG系统:核心原理与Python实现指南
引言
近年来,检索增强生成(Retrieval-Augmented Generation, RAG)作为人工智能与大语言模型(LLM)领域的一项关键技术,正迅速改变着AI应用的构建方式。RAG通过结合信息检索与文本生成,使模型能够利用外部知识源,从而显著提升回答的准确性和时效性。无论是问答系统、对话机器人还是内容生成工具,RAG都展现出了巨大的应用潜力。
本文旨在从零开始,带领读者构建一个简易的RAG系统。我们将使用Python和Ollama工具,通过清晰的代码示例,深入剖析RAG的核心组件与工作原理。无论你是AI初学者还是经验丰富的开发者,本文都将为你提供实用的指导,帮助你理解并实现自己的RAG应用。
RAG的基本概念
传统聊天机器人的局限
设想一个简单的聊天机器人,它基于预训练的语言模型进行回答。当用户询问“我妈妈的名字是什么?”时,模型由于缺乏外部知识,往往无法给出准确答案。这是因为模型的知识仅限于训练数据,无法获取用户个性化的实时信息。
RAG的引入
RAG通过引入外部知识检索机制,有效解决了上述问题。其核心思想是:在生成回答之前,先从外部知识库中检索与用户查询相关的信息,然后将这些信息作为上下文提供给语言模型,从而生成更准确、更相关的回答。
一个典型的RAG系统包含两个关键组件:
- 检索模型:负责从外部知识源(如数据库、搜索引擎)中获取相关信息。
- 语言模型:基于检索到的知识生成最终回答。
RAG的实现方式多种多样,包括Graph RAG、Hybrid RAG、Hierarchical RAG等,我们将在后文简要介绍。
简易RAG系统设计
为了便于理解,我们将构建一个简易的RAG系统,其核心组件包括:
- 嵌入模型:将文本转换为向量表示,用于语义相似度计算。
- 向量数据库:存储知识块及其对应的嵌入向量,支持高效检索。
- 聊天机器人:基于检索到的知识生成回答。
索引阶段
索引阶段是RAG系统的第一步。我们将数据集(或文档)切分为较小的块(chunk),并为每个块计算嵌入向量。这些向量将用于后续的相似度检索。
块的大小可根据应用场景灵活调整。例如,在文档检索中,块可以是段落或句子;在对话系统中,块可以是对话轮次。
索引完成后,每个块及其嵌入向量被存储在向量数据库中。以下是一个示例:
| 块内容 | 嵌入向量 |
|---|---|
| Italy and France produce over 40% of all wine in the world. | [0.1, 0.04, -0.34, 0.21, ...] |
| The Taj Mahal in India is made entirely out of marble. | [-0.12, 0.03, 0.9, -0.1, ...] |
| 90% of the world's fresh water is in Antarctica. | [-0.02, 0.6, -0.54, 0.03, ...] |
| ... | ... |
这些向量可用于根据查询向量检索最相关的块。类似于SQL的WHERE子句,但基于向量相似度而非精确匹配。
检索阶段
当用户输入查询时,系统首先计算查询的嵌入向量,然后与数据库中的向量进行相似度比较,返回最相关的Top N个块。这些块将作为上下文提供给语言模型。
相似度计算常用余弦相似度,公式如下:
余弦相似度 = (A·B) / (||A|| * ||B||)
其中A和B分别为两个向量。余弦相似度值越接近1,表示两个向量越相似。
代码实现
环境准备
我们将使用Ollama来运行模型,它支持在本地运行Hugging Face上的模型,无需云服务。首先,安装Ollama并下载所需模型:
ollama pull hf.co/CompendiumLabs/bge-base-en-v1.5-gguf
ollama pull hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF同时,安装Python的Ollama库:
pip install ollama加载数据集
我们使用一个简单的猫事实数据集,每行一个事实,作为知识块。
dataset = []
with open('cat-facts.txt', 'r') as file:
dataset = file.readlines()
print(f'Loaded {len(dataset)} entries')实现向量数据库
我们使用Ollama的嵌入模型将每个块转换为向量,并存储在列表中。
import ollama
EMBEDDING_MODEL = 'hf.co/CompendiumLabs/bge-base-en-v1.5-gguf'
LANGUAGE_MODEL = 'hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF'
VECTOR_DB = []
def add_chunk_to_database(chunk):
embedding = ollama.embed(model=EMBEDDING_MODEL, input=chunk)['embeddings'][0]
VECTOR_DB.append((chunk, embedding))将数据集中的每个块添加到数据库:
for i, chunk in enumerate(dataset):
add_chunk_to_database(chunk)
print(f'Added chunk {i+1}/{len(dataset)} to the database')实现检索函数
首先,定义余弦相似度计算函数:
def cosine_similarity(a, b):
dot_product = sum([x * y for x, y in zip(a, b)])
norm_a = sum([x ** 2 for x in a]) ** 0.5
norm_b = sum([x ** 2 for x in b]) ** 0.5
return dot_product / (norm_a * norm_b)
然后,实现检索函数,返回Top N个最相关的块:
def retrieve(query, top_n=3):
query_embedding = ollama.embed(model=EMBEDDING_MODEL, input=query)['embeddings'][0]
similarities = []
for chunk, embedding in VECTOR_DB:
similarity = cosine_similarity(query_embedding, embedding)
similarities.append((chunk, similarity))
similarities.sort(key=lambda x: x[1], reverse=True)
return similarities[:top_n]生成阶段
在生成阶段,我们将检索到的知识块作为上下文,构造提示词,并调用语言模型生成回答。
input_query = input('Ask me a question: ')
retrieved_knowledge = retrieve(input_query)
print('Retrieved knowledge:')
for chunk, similarity in retrieved_knowledge:
print(f' - (similarity: {similarity:.2f}) {chunk}')
instruction_prompt = f'''You are a helpful chatbot.
Use only the following pieces of context to answer the question. Don't make up any new information:
{'\n'.join([f' - {chunk}' for chunk, similarity in retrieved_knowledge])}
'''
stream = ollama.chat(
model=LANGUAGE_MODEL,
messages=[
{'role': 'system', 'content': instruction_prompt},
{'role': 'user', 'content': input_query},
],
stream=True,
)

print('Chatbot response:')
for chunk in stream:
print(chunk['message']['content'], end='', flush=True)完整代码与运行
将上述代码整合到一个demo.py文件中,运行python demo.py即可体验。例如,询问“tell me about cat speed”,系统将检索相关事实并生成回答。
改进方向
尽管我们的简易RAG系统已能工作,但仍存在诸多改进空间:
- 多主题查询:当查询涉及多个主题时,单一检索可能不够准确。可让模型生成多个查询,或使用多查询检索。
- 重排序:基于余弦相似度的Top N结果可能不够精准,可引入重排序模型(如Cross-Encoder)对结果进行二次排序。
- 向量数据库扩展:内存存储不适合大规模数据,可改用Qdrant、Pinecone等专业向量数据库。
- 智能分块:简单的按行分块可能不理想,可根据语义或结构进行更精细的分块。
- 更大模型:1B参数的语言模型能力有限,可尝试更大的模型以提升生成质量。
其他类型的RAG
RAG的实现方式多种多样,以下是一些常见类型:
- Graph RAG:将知识表示为图结构,节点为实体,边为关系,模型可遍历图进行检索。
- Hybrid RAG:结合知识图谱和向量数据库,提升问答系统的性能。
- Modular RAG:采用模块化设计,支持路由、调度和融合机制,实现更灵活的流程。
结语
通过本文的实践,我们成功构建了一个简易的RAG系统,掌握了嵌入、检索和生成的核心流程。RAG技术为语言模型注入了外部知识,使其回答更加准确和可靠。未来,随着向量数据库、重排序模型等技术的不断进步,RAG将在更多场景中发挥重要作用。希望本文能为你进一步探索RAG提供坚实的基础。