数十年前,图灵抛出的时代之问"Can machines think?"将人工智能从科幻拉至现实,奠定了后续人工智能发展的基础。之后,无数计算机科学的先驱开始解构人类智能的形成,希望找到赋予机器智能的蛛丝马迹。时至今日,我们站在了一个新的起点上,机器不仅能够"思考",更能够通过学习和模仿人类的交流方式,与我们进行自然而流畅的对话。
在这篇文章中,我们将探讨如何利用LangChain + LLM + Amazon Bedrock等技术构建一个检索增强型问答GPT应用程序。这个应用程序不仅能够理解用户的问题,还能够从海量的数据中检索出相关信息,生成准确而有用的回答。
一、环境配置与安装
首先进入Amazon Bedrock 控制台:dev.amazoncloud.cn/experience/... Bedrock** 提供了构建生成式人工智能应用程序所需的一切,它是专门为创新者量身打造的平台,其通过一个简化的 API 接口,提供来自AI21 Labs、Anthropic、Cohere、Meta、Stability AI 等行业领先公司的高性能基础模型,为开发者提供了一个广泛的功能集,以便安全、私密且负责任地构建生成式 AI 应用程序。
扫码登录后,输入相关的信息进入操作界面,在上方搜索Cloud 9并进入Cloud 9 主界面
然后新建一个AWS Cloud 9环境,设置环境详细信息:
- 设置名称为 bedrock
- 设置实例类型 t3.small
- 平台 Ubuntu Server 22.04 LTS
- 超时 30 分钟
- 网络选择AWS Systems Manager (SSM)
创建好后打开Cloud 9 IDE:
在Amazon Cloud9 IDE 中,选择 终端 ,复制以下内容到终端,执行命令,以下载和解压缩代码
bash
cd ~/environment/
curl 'https://dev-media.amazoncloud.cn/doc/workshop.zip' --output workshop.zip
unzip workshop.zip
解压完成:
继续使用 终端 ,安装实验所需的环境依赖项
javascript
pip3 install -r ~/environment/workshop/setup/requirements.txt -U
二、构建检索增强型问答RAG-GPT应用程序
本节实验,我们将使用Retrieval Augmented Generation(简称RAG),首先将用户输入的提示词传递给数据存储,模拟以Amazon Kendra类似的查询方式出现。同时使用Amazon Titan Embeddings创建提示的数字表示,以传递到矢量数据库。然后,我们从数据存储中检索最相关的内容,以支持大型语言模型的响应。
在这个实验中,使用内存中的FAISS数据库来演示RAG模式。在实际的生产环境中 ,我们可能需要使用类似Amazon Kendra 这样的持久数据存储或Amazon OpenSearch Serverless的矢量引擎。
打开 workshop/labs/rag 文件夹,应用程序由三个文件组成:
- rag_lib.py用于支持库以调用 Bedrock。其通过导入
langchain
生态系统中的多个库和组件来构建一个基于语言模型的检索增强问答逻辑。 - rag_app.py用于Streamlit 前端,其利用Streamlit框架编写一个应用程序,创建用户输入界面,利用前面提到的检索增强生成问答系统,处理用户输入并生成回答的逻辑。
- 2022-Shareholder-Letter.pdf是私有的PDF文档,作为检索增强的个人私有知识库,这里可以替换为自己的知识库。
将本地准备的私有知识库pdf文件上传到 workshop/labs/rag 文件夹
然后打开rag_lib.py 文件,将以下代码复制进去 :
ini
from langchain_community.embeddings import BedrockEmbeddings
from langchain.indexes import VectorstoreIndexCreator
from langchain_community.vectorstores import FAISS
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.llms import Bedrock
def get_llm():
model_kwargs = {
"max_gen_len": 2048,
"temperature":0.5,
"top_p":0.9
}
llm = Bedrock(
model_id="meta.llama2-70b-chat-v1",model_kwargs=model_kwargs) #设置调用模型
return llm
def get_index(): #creates and returns an in-memory vector store to be used in the application
embeddings = BedrockEmbeddings() #创建一个 Titan Embeddings 客户端
pdf_path = "2022-Shareholder-Letter.pdf" # 本地 PDF 文件
loader = PyPDFLoader(file_path=pdf_path) #加载 PDF 文件
text_splitter = RecursiveCharacterTextSplitter( #创建一个文本拆分器
separators=["\n\n", "\n", ".", " "], #以(1)段落、(2)行、(3)句子或(4)单词的顺序,在这些分隔符处拆分块
chunk_size=1000, #使用上述分隔符将其分成 1000 个字符的块
chunk_overlap=100 #与前一个块重叠的字符数
)
index_creator = VectorstoreIndexCreator( #创建一个向量存储创建器 vectorstore_cls=FAISS, #为了演示目的,使用内存中的向量存储
embedding=embeddings, #使用 Titan 嵌入
text_splitter=text_splitter, #使用递归文本拆分器
)
index_from_loader = index_creator.from_loaders([loader]) #从加载器创建向量存储索引
return index_from_loader #返回索引以由客户端应用程序进行缓存
def get_rag_response(index, question): #rag 客户端函数
llm = get_llm()
response_text = index.query(question=question, llm=llm) #针对内存中的索引进行搜索,将结果填充到提示中并发送给语言模型
return response_text
其通过加载PDF文件,创建文本的嵌入表示,并利用这些嵌入来构建一个向量索引,最后使用该索引来生成对特定问题的回答,其中:
-
get_llm
函数:- 主要用于模型获取与设置 ,用于初始化和返回一个
Bedrock
语言模型。 Bedrock
模型通过指定model_id
来调用特定的预训练模型,这里是meta.llama2-70b-chat-v1
。
- 主要用于模型获取与设置 ,用于初始化和返回一个
-
get_index
函数:- 主要用于向量存储创建 , 负责创建一个内存中的向量存储,用于后续的文本检索。
BedrockEmbeddings
用于生成文本的嵌入表示。PyPDFLoader
加载指定路径的PDF文件,以便从中提取文本。RecursiveCharacterTextSplitter
是一个文本拆分器,它根据指定的分隔符将文本分割成小块,并设置重叠以保持上下文连贯性。VectorstoreIndexCreator
创建一个向量存储索引,使用FAISS
作为后端,它是一个高效的相似性搜索和密集向量聚类库。
-
get_rag_response
函数:-
主要用于响应生成,它使用之前创建的向量索引和语言模型来生成对用户问题的回答。
-
调用
get_llm
获取语言模型实例。并使用index.query
方法,结合问题文本和语言模型,查询向量索引以生成回答。
-
打开rag_app.py文件,将以下代码复制进去:
python
import streamlit as st #所有 streamlit 命令都可以通过"st"别名使用
import rag_lib as glib # 对本地库脚本的引用
st.set_page_config(page_title="Retrieval-Augmented Generation") #HTML title
st.title("Retrieval-Augmented Generation") #page title
if 'vector_index' not in st.session_state: #查看向量索引是否尚未创建
with st.spinner("Indexing document..."): #在这个 with 块运行的代码时显示一个旋转器
st.session_state.vector_index = glib.get_index() #通过支持库检索索引并存储在应用程序的会话缓存中
input_text = st.text_area("Input text", label_visibility="collapsed") #创建一个多行文本框
go_button = st.button("Go", type="primary") #按钮
if go_button:
with st.spinner("Working..."):
response_content = glib.get_rag_response(index=st.session_state.vector_index, question=input_text) #通过支持库调用模型
st.write(response_content)
这段代码通过使用Streamlit框架构建的简单Web应用程序,实现检索增强型文本生成(Retrieval-Augmented Generation)。程序通过加载文档建立一个向量索引,并允许用户输入文本,然后基于用户输入生成响应。整个程序的逻辑是:初始化页面 -> 创建并存储向量索引 -> 接收用户输入 -> 生成并展示响应:
-
初始化和页面设置:
- 导入
streamlit
库并设置别名st
,用于后续的所有Streamlit命令。
- 导入
-
向量索引的创建与存储:
- 检查
st.session_state
中是否已经存在vector_index
,如果不存在,则执行索引创建过程。 - 使用
st.spinner
显示一个旋转指示器,告知用户正在进行索引操作。调用glib.get_index
方法创建向量索引,并将结果存储在Streamlit会话状态中,以便后续使用。
- 检查
-
用户输入和操作:
- 使用
st.text_area
创建一个多行文本框,允许用户输入文本。
- 使用
-
响应生成与展示:
-
检测到"Go"按钮被点击后,使用
st.spinner
显示另一个旋转指示器,表示程序正在处理用户的请求。 -
调用
glib.get_rag_response
方法,传入用户输入的文本和之前创建的向量索引,生成响应。使用st.write
将生成的响应内容写入到页面中,展示给用户。
-
然后在终端输入以下代码,安装相应依赖并运行
bash
pip install chromadb
cd ~/environment/workshop/labs/rag
streamlit run rag_app.py --server.port 8080
当终端中显示如下内容时,说明成功运行:
You can now view your Streamlit app in your browser.
Network URL: http://*************:8080
External URL: http://*************:8080
然后打开Cloud9菜单栏- >Preview ->Preview Running Application进行预览:
可以看到应用可以从数据存储中检索最相关的内容,以支持大型语言模型的响应。
当然,在这个实验中,仅仅使用内存中的FAISS数据库来演示RAG模式。在实际的生产环境中 ,可能需要使用类似Amazon Kendra 这样的持久数据存储或Amazon OpenSearch Serverless的矢量引擎。