从零开始构建一个应用可以让我们快速理解应用的各个部分。
这个方法实则超级适用于 RAG。
我在以前的文章中有介绍过 RAG 的概念、原理以及应用等,但实则,亲自动手来构建一个 RAG 系统或许能够让我们更快速的理解 RAG 到底是什么。
有关于 RAG 的介绍的相关文章可翻阅:
一文读懂 RAG:它是如何重新定义 AI 的未来?
更高级的 RAG 架构:提升 AI 大模型回答准确性的前沿技术
本文将为读者提供一个从零开始搭建一个 RAG 应用的入门教程。
本文涉及一部分 Python 代码,需要必定的编程基础。你可以在我的 GitHub 上获取到本文所有代码:https://github.com/Erichain/ai-application-demos/blob/main/create-rag-from-scratch.py
在开始之前,我还是打算再次简要的介绍一下 RAG。
在 Meta 的官方 Blog 上有这样一段话:
Building a model that researches and contextualizes is more challenging, but it's essential for future advancements. We recently made substantial progress in this realm with our Retrieval Augmented Generation (RAG) architecture, an end-to-end differentiable model that combines an information retrieval component (Facebook AI’s dense-passage retrieval system) with a seq2seq generator (our Bidirectional and Auto-Regressive Transformers BART model). RAG can be fine-tuned on knowledge-intensive downstream tasks to achieve state-of-the-art results compared with even the largest pretrained seq2seq language models. And unlike these pretrained models, RAG’s internal knowledge can be easily altered or even supplemented on the fly, enabling researchers and engineers to control what RAG knows and doesn’t know without wasting time or compute power retraining the entire model.
这段话主要讲述了一个新的模型架构,也就是 RAG (检索增强生成) 的重大性和优势。可以概括为以下几点:
这段话信息量很大,但是作为初学者,简而言之:
RAG 的本质是在传递给 LLM 的提示语中,通过一个检索工具来添加自己的数据。
接下来,我们就要开始准备我们的 RAG 应用了。
这是简化版的 RAG 组件系统,我们不需要思考向量存储,甚至目前还不需要 LLM。
它假设了许多背景信息,比我们预设的简化版要复杂的多。
对于想要深入研究的人来说,这篇论文很有价值,但是对于我们想要入门的人来说,通过一步一步构建自己的 RAG 系统来学习才更适合。
这里的后处理即将检索到的文档和用户输入发送给 LLM 进行处理,最终生成回答。
类似性度量是指用来评估两个对象之间类似程度的方法。在文本处理和信息检索中,类似性度量可以协助我们确定两个文本之间的类似度。在 RAG 系统中,我们可以使用这些类似性度量方法之一来比较用户输入和文档集合中的每个文档,从而找到最相关的文档。
目前,我们将以一个具体的案例从零开始来构建一个 RAG 系统。
以下是简化版的流程图。

以下是具体步骤。
我们第必定义一个简单的文档语料库。
corpus_of_documents = [
"Take a leisurely walk in the park and enjoy the fresh air.",
"Visit a local museum and discover something new.",
"Attend a live music concert and feel the rhythm.",
"Go for a hike and admire the natural scenery.",
"Have a picnic with friends and share some laughs.",
"Explore a new cuisine by dining at an ethnic restaurant.",
"Take a yoga class and stretch your body and mind.",
"Join a local sports league and enjoy some friendly competition.",
"Attend a workshop or lecture on a topic you're interested in.",
"Visit an amusement park and ride the roller coasters."
]目前我们需要一种方法来衡量我们将要接收的用户输入与我们组织的文档集合之间的类似性。
可以说,最简单的类似性度量是杰卡德类似性。
杰卡德类似性(Jaccard Similarity)是一种衡量两个集合类似程度的简单方法。它计算两个集合的交集和并集的比例,用于比较两个文本之间的类似性。简而言之就是,杰卡德类似性看两个集合中共同元素的数量占所有元素的总数量的比例。
由于我们需要进行类似性度量,所以需要将字符串处理成集合。
我们可以使用最简单的方式来进行预处理,也就是将字符串转换为小写并按照空格分割。
# 将语料库字符串按照空格分割,并返回杰卡德类似性的结果
def jaccard_similarity(query, document):
query = query.lower().split(" ")
document = document.lower().split(" ")
intersection = set(query).intersection(set(document))
union = set(query).union(set(document))
return len(intersection)/len(union)然后,我们需要定义一个函数,该函数接受用户的准确查询和我们的语料库,并根据类似性的结果将最匹配的文档返回给用户。
def return_response(query, corpus):
similarities = []
for doc in corpus:
similarity = jaccard_similarity(user_input, doc)
similarities.append(similarity)
return corpus_of_documents[similarities.index(max(similarities))]目前,我们可以试着运行一下。
定义用户查询输入。
user_input = "I like to hike"将输出的结果打印出来。
print(return_response(user_input, corpus_of_documents))

如果不想在自己电脑上配置 Python 环境,可以直接使用线上的 Python 编辑器,列如:https://www.programiz.com/python-programming/online-compiler/
至此,我们已经构建出了一个最基本的 RAG 系统。
由于我们选择了一个超级简单的类似性度量方法来学习,所以会带来一些问题。
它没有语义概念,只是简单地看两个文档中有哪些词,然后进行对比。
也就是说,只要我们提供的用户输入里包含这些词,那么我们就会得到一样的结果,由于那就是最接近的文档。
列如,我将用户输入换成了 user_input = "I don't like to hike"。

输出结果和上文的输出结果一样。
这是一个在 RAG 中会常常遇到的话题,我们会在后面解决这个问题。
目前,我们还没有对我们检索到的文档进行任何后处理。只是实现了 RAG 的「检索」功能。
下一步是通过结合 LLM 来增强生成。
要方便快捷的添加 LLM,我们可以直接在本地机器上运行一个开源的 LLM。
这里,我将使用 Ollama 的 Llama 3.1 模型。当然,你也可以使用 OpenAI 的 GPT-4 或 Anthropic 的 Claude 或者其他 LLM。
可以到 ollama 官网下载安装自己想要的 LLM:https://ollama.com/
目前,我们需要对代码做些修改了。
如果是在本地运行 LLM,那么,你需要在自己电脑上配置好 Python 相关的环境,这样在后续步骤中,才能将代码运行起来。
目前,需要引入一些库。
import requests
import json我们的步骤会有所变化:
user_input = "I like to hike"
relevant_document = return_response(user_input, corpus_of_documents)
full_response = []
prompt = """
You are a bot that makes recommendations for activities. You answer in very short sentences and do not include extra information.
This is the recommended activity: {relevant_document}
The user input is: {user_input}
Compile a recommendation to the user based on the recommended activity and the user input.
"""定义好以上步骤之后,我们目前来调用 Ollama 的 API。
在编辑此代码之前,你需要先运行 LLM 在后台,直接在命令行里输入 ollama serve 即可。
url = 'http://localhost:11434/api/generate'
data = {
"model": "llama3.1",
"prompt": prompt.format(user_input=user_input, relevant_document=relevant_document)
}
headers = {'Content-Type': 'application/json'}
response = requests.post(url, data=json.dumps(data), headers=headers, stream=True)
try:
count = 0
for line in response.iter_lines():
if line:
decoded_line = json.loads(line.decode('utf-8'))
full_response.append(decoded_line['response'])
finally:
response.close()
print(''.join(full_response))运行以上代码,即可看到结果。

目前我们已经从零开始构建了一个完整的 RAG 系统。
目前 LLM 就可以直接帮我们处理上文提到的类似性问题。如果把用户输入改成 I don't like to hike.,那么我们会得到以下这样的回答。

尽管我们已经搭建出了一个完整的 RAG 系统,但是,真实场景下的 RAG 系统也许会更加复杂,涉及向量数据库(Vector Database)、嵌入(Embedding)和提示语工程(Prompt Engineering)等。
如果想要更加深入的学习 RAG,你也许需要在此基础之上思考以下的一些改善点。
你可以在我的 GitHub 上获取到本文所有代码:https://github.com/Erichain/ai-application-demos/blob/main/create-rag-from-scratch.py