最近有个业务接触到检索增强生成,大概了解了一下原理,把检索出来的文章段落组合一下喂给大模型,让它帮你理顺成易于理解的信息,这使用方式倒是挺靠谱。
拥有正确的数据来支持您的用例对于在任何业务中成功实现llm都是至关重要的。虽然大多数开箱即用的llm很擅长一般任务,但他们可能会努力解决特定的业务问题。他们没有针对您的业务问题进行数据训练,因此他们没有足够的上下文来解决它。
企业通常有大量的内部数据和文档,可以满足特定环境的需求。但是,这里有一个问题:我们如何将所有这些有用的数据(上下文)集成到LLM中,而不进行资源密集和耗时的重新训练或微调LLM?
答案是检索增强生成(RAG),一种通过密切上下文信息的即时检索来增强llm的技术。
在这篇文章中,我们将介绍如何使用LlamaIndex和LangChain来实现LLM上下文数据的存储和检索。我们将使用LlamaIndex解决RAG的一个上下文相关问题,然后将该解决方案轻松部署到Heroku。
在我们开始编码之前,让我们快速介绍一下核心概念。
RAG和LlamaIndex简介
当你问你的LLM一个需要上下文来回答的问题时,RAG会检索上下文数据以帮助LLM给出更准确和具体的回答。这就像让一名副厨师迅速跑到农贸市场去买食品柜里没有的最新鲜的食材,这样行政总厨就可以用所有必要的食材做出完美的菜。
RAG工作流提供上下文的一个关键是使用向量数据库和向量搜索索引。让我们来分解一些核心概念以及这一切需要什么。
-
向量是一组编码的数字,表示一段文本(例如一个单词、短语、句子,甚至是整个文档)的含义和上下文。
-
嵌入是向量中的实际数值,但大多数人倾向于将“向量”和“嵌入”互换使用。
-
在文档上训练了一个嵌入模型,以便它可以将新输入的文本转换为向量。不是所有的文本都讨论相同的事情或以相同的方式-考虑一组学术研究论文与一组营销材料。因此,我们有不同的嵌入模型——每个模型都在特定的数据集上训练,心中有特定的目标。
-
使用嵌入模型,我们可以从文档中创建嵌入,将这些文档中的文本分解为它们的编码数字。创建嵌入可能涉及像文档分块这样的策略,它将大型文档分割为较小的、可管理的片段。从那里,每个块都被转换为嵌入。
-
当你查询向量数据库时,你的问题会转化为嵌入,并与存储在向量数据库中的所有其他嵌入进行比较。
-
当你建立向量搜索索引时,你可以执行非常快速和准确的向量搜索(也称为相似性搜索)。使用向量数据库可以让你执行快速而准确的搜索——不仅仅是像传统数据库中那样匹配特定字符串的存在,还可以匹配与你使用的单词含义相似的文档。
在RAG的上下文中,我们使用原始的提示符对向量数据库中的所有文档执行向量搜索。然后,这些匹配的文档作为上下文发送到LLM应用程序。LLM现在有一套详细的说明,以参考时,起草其原始提示的答案。
LlamaIndex是一个关键的框架,它简化了集成、组织和检索私有或专用数据的过程。它将帮助我们创建文档嵌入和向量搜索索引。然后,我们将依靠LangChain将它们拼凑在一起,执行相似性搜索并将结果发送给我们的LLM以获取响应。LlamaIndex和LangChain共同为处理RAG工作流提供了一个安全可靠的解决方案。
你准备好构建一些东西了吗?我们开始吧!
介绍我们的演示项目
要学习使用LlamaIndex和Heroku学习RAG,最好的方法是编写一个小型示例应用程序。为了我们的目的,让我们假设我们与古登堡计划合作,这是一个拥有7万多本免费电子书的图书馆。我们想构建一个基于llm的聊天机器人,可以回答关于项目中免费书籍的特定问题。
这是使用RAG的完美用例,我们可以使用LlamaIndex来利用我们可用的大量书籍文本语料库。为简单起见,我们将使用公元401年的畅销书《圣奥古斯丁的忏悔录》。
我们完成的项目的代码库可以在这个GitHub仓库中找到。如果愿意,你可以克隆仓库并将应用部署到Heroku。或者,您可以一步一步地了解我们是如何得到我们所做的代码的。
我们将遵循以下一般步骤。
-
设置我们的项目。
-
加载数据。
-
建立索引。
-
存储索引。
-
LangChain集成。
-
部署到Heroku。
步骤1:设置我们的项目
为Python项目创建一个新文件夹。然后,激活venv并安装我们需要的初始依赖项。
(venv) ~/project$ pip install llama-index langchain langchain-openai
接下来,我们将加载要索引的数据。
步骤2:加载数据
当为RAG创建内部数据索引时,你必须将所有数据(文本)收集到一个单独的地方。在我们的例子中,这是我们上面提到的书的文本。我们很快将使用LlamaIndex将其转换为嵌入的向量索引。
在典型的用例中,您的上下文数据将是一个适合您试图解决的业务问题的大型文本语料库。
在这个小演示程序中,我们将创建一个名为 data
的子文件夹,然后在该文件夹中以单个文件的形式下载本书。
(venv) ~/project$ mkdir data
(venv) ~/project$ curl \
https://www.gutenberg.org/cache/epub/3296/pg3296.txt \
-o data/confessions.txt
(venv) ~/project$ ls data
confessions.txt
第3步:建立索引
在单个目录中收集完所有数据后,就该构建索引了。我们将编写一个简单的Python应用程序,使用LlamaIndex索引数据,然后查询索引。
为此,你需要一个OpenAI账户和API密钥。这是因为LlamaIndex使用OpenAI的text-embedding-3-small作为默认的嵌入模型。(更改这些默认值超出了本文的范围。)
在我们的项目根目录中,我们创建一个名为 index.py
的文件。初始内容如下所示:
# index.py
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
import os
if os.environ.get('OPENAI_API_KEY') is None:
exit('You must provide an OPENAI_API_KEY env var.')
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("In which city is Saint Augustine the Bishop?")
print(response)
我们运行文件并收到预期的响应:
(venv) ~/project$ OPENAI_API_KEY=sk-******** python index.py
Hippo
当然,我们可以再次检查我们的数据。看看这本书的前几行,我们看到:
THE CONFESSIONS OF SAINT AUGUSTINE
By Saint Augustine
Bishop of Hippo
如你所见,LlamaIndex完成了它的任务。我们的Python应用程序完成了我们的问题,完全符合我们对向量索引数据的预期。
步骤4:存储索引
需要注意的是,在上面的例子中,我们只将索引数据存储在内存中,而不是磁盘上。我们的索引(现在是内存中的一系列向量嵌入)将在我们调用OpenAI模型并完成工作流后完全丢失。
为我们的文本创建向量索引(嵌入)不是免费的,所以我们不想每次调用模型时都重新计算这些结果。最好有一个单独的工作流,将索引持久化到磁盘。然后,我们可以在以后的任何时候引用它。
一种常见的方法是将嵌入存储在PostgreSQL数据库中,并使用pgvector执行相似性搜索。为了使我们的演示简单,我们只将索引数据存储为平面文件。
因此,我们将这个简单的步骤添加到 index.py
文件中:
PERSIST_DIR='./my_vector_indexes/gutenberg/'
index.storage_context.persist(persist_dir=PERSIST_DIR)
现在,运行文件后,我们可以检查存储的索引。
(venv) ~/project$ OPENAI_API_KEY=sk-******** python index.py
Hippo
(venv) ~/project$ tree
.
├── data
│ └── confessions.txt
├── index.py
└── my_vector_indexes
└── gutenberg
├── default__vector_store.json
├── docstore.json
├── graph_store.json
├── image__vector_store.json
└── index_store.json
3 directories, 7 files
第五步:整合LangChain
我们已经了解了什么是向量索引存储,以及构建一个向量索引存储有多么容易。但是,要真正构建一个将所有内容链接在一起的端到端应用程序,我们使用LangChain。这样,我们可以将解决方案部署为API。重写index.py代码,让它更适合生产环境。
我们将展示下面的代码,然后解释我们接下来要做什么。它可能看起来有很多代码,但我们只是添加了一些新的步骤。
# index.py
import os
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferWindowMemory
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from langchain_community.retrievers import LlamaIndexRetriever
from fastapi import FastAPI
from pydantic import BaseModel
if os.environ.get('OPENAI_API_KEY') is None:
exit('You must provide an OPENAI_API_KEY env var.')
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
# For this demo, we will not persist the index.
retriever = LlamaIndexRetriever(index=index.as_query_engine())
llm = ChatOpenAI(model_name="gpt-3.5-turbo", max_tokens=2048)
memory = ConversationBufferWindowMemory(
memory_key='chat_history',
return_messages=True,
k=3
)
conversation = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=retriever,
memory=memory,
max_tokens_limit=1536
)
class Prompt(BaseModel):
question: str
app = FastAPI()
@app.post("/prompt")
async def query_chatbot(prompt:Prompt):
response = conversation.invoke({'question':prompt.question})
return response['answer']
if __name__=='__main__':
import uvicorn
uvicorn.run(app, host="localhost", port=8000)
首先,注意我们现在直接使用LangChain和OpenAI。我们设置了 llm
和一些 memory
,这样我们的 conversation
就可以在后续的查询中被“记住”。现在,我们有了一个可以与之交互的聊天机器人。
从那里,我们使用FastAPI创建一个API服务器,在 /prompt
端点上侦听 POST
请求。对该端点的请求预计具有一个带有问题的请求体,然后将其(连同从我们的向量索引的上下文)传递给LLM。
我们使用 uvicorn
在端口8000上启动服务器。
在我们启动服务器之前,让我们添加这些新的Python依赖:
(venv) ~/project$ pip install fastapi pydantic uvicorn
现在,是测试的时候了。首先启动服务器。
(venv) ~/project$ OPENAI_API_KEY=sk-******** python index.py
INFO: Started server process [1101807]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://localhost:8000 (Press CTRL+C to quit)
在另一个终端中,我们向我们的终端发送curl请求。
$ curl -X POST \
--header "Content-type:application/json" \
--data '{"question":"Who is Ambrose?"}' \
http://localhost:8000/prompt
"Ambrose is a person mentioned in the text provided. He is described as a respected
and celibate man who was esteemed by the author. Ambrose is depicted as a figure of
great honor and excellence, particularly known for his dedication to reading and
studying."
成功!我们的向量索引似乎已经启动并运行起来,我们的聊天机器人也功能齐全。是时候部署了。
步骤6:部署到Heroku
在完成主要工作后,我们只需要采取几个简单的步骤,将应用程序部署到Heroku。
创建requirements.txt文件,包含Python依赖项
Heroku在构建项目时需要知道要安装哪些Python依赖。它在名为 requirements.txt
的文件中查找该列表。我们可以使用以下命令轻松生成一个:
(venv) ~/project$ pip freeze > requirements.txt
创建Procfile文件
我们还需要告诉Heroku如何启动Python应用程序。我们在名为 Procfile
的文件中执行此操作。
(venv) ~/project$ echo \
'web:uvicorn index:app --host=0.0.0.0 --port=${PORT}' > Procfile
创建runtime.txt文件
最后, runtime.txt
将告诉Heroku我们希望使用哪个Python运行时版本。
(venv) ~/project$ echo 'python-3.11.8' > runtime.txt
这些就是我们需要的所有文件。这是我们项目文件夹结构的样子(我们已经删除了持久化的向量索引):
~/project$ tree
.
├── data
│ └── confessions.txt
├── index.py
├── Procfile
├── requirements.txt
└── runtime.txt
1 directory, 5 files
如果你从头开始工作,并且没有为这个演示项目克隆GitHub仓库,那么请将这些文件提交到自己的Git仓库。
创建一个Heroku应用
下载并安装Heroku CLI后,执行如下命令。你可以为你的应用程序选择任何名称,并且需要提供唯一的OpenAI API密钥。
~/project$ heroku login
~/project$ heroku apps:create my-llamaindex-app
~/project$ heroku git:remote -a my-llamaindex-app
~/project$ heroku config:add OPENAI_API_KEY=replaceme -a my-llamaindex-app
~/project$ git push heroku main
…
remote: -----> Building on the Heroku-22 stack
remote: -----> Determining which buildpack to use for this app
remote: -----> Python app detected
remote: -----> Using Python version specified in runtime.txt
…
remote: -----> Launching...
remote: Released v4
remote: https://my-llamaindex-app-6b48faa3ee6a.herokuapp.com/ deployed to Heroku
部署应用程序后,通过向API服务器发送curl请求进行测试:
$ curl -X POST \
--header "Content-type:application/json" \
--data '{"question":"Who is Ambrose?"}' \
https://my-llamaindex-app-6b48faa3ee6a.herokuapp.com/prompt
"Ambrose is a significant figure in the text provided. He is being described as a
respected and happy man, known for his celibacy and his dedication to reading and
studying. He is referred to as a holy oracle and a person of great influence and
wisdom."
请记住,上述 curl
调用在我们的部署中使用了唯一的Heroku应用程序URL。你的会和这里显示的不一样。
我们在Heroku上运行起来了!
结论
我们已经清楚地看到了LlamaIndex的力量以及它在构建RAG应用程序以与llm交互时所扮演的重要角色。当我们可以轻松地添加特定的数据源作为LLM的上下文,而不需要昂贵的模型重新训练时,这是一个巨大的胜利。对于希望进一步推进LLM工作流程的公司和开发人员来说,这是一个胜利。
将LlamaIndex与其他LangChain工具集结合起来也是无缝且直接的。构建聊天机器人只需要几行额外的代码。最后,能够快速轻松地将我们的解决方案部署到Heroku,使我们的应用程序可以立即访问,而无需任何麻烦。像这样的简单部署使开发人员可以专注于构建基于llm的解决方案的更复杂和更重要的任务。
读者福利:如果大家对大模型感兴趣,这套大模型学习资料一定对你有用
对于0基础小白入门:
如果你是零基础小白,想快速入门大模型是可以考虑的。
一方面是学习时间相对较短,学习内容更全面更集中。
二方面是可以根据这些资料规划好学习计划和方向。
包括:大模型学习线路汇总、学习阶段,大模型实战案例,大模型学习视频,人工智能、机器学习、大模型书籍PDF。带你从零基础系统性的学好大模型!
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费
】🆓
👉AI大模型学习路线汇总👈
大模型学习路线图,整体分为7个大的阶段:(全套教程文末领取哈)
第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;
第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;
第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;
第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;
第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;
第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;
第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。
👉大模型实战案例👈
光学理论是没用的,要学会跟着一起做,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
👉大模型视频和PDF合集👈
观看零基础学习书籍和视频,看书籍和视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;
• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;
• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;
• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。
👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费
】🆓