项目名称:1小时搭建“汪汪宠物狗”公司的N种模态文档的RAG智能对话机器人
报告日期:2024年8月18日
项目负责人:“汪汪宠物狗”公司创始人“汪汪”
项目概述:
大型语言模型(LLM)在实际应用中,尽管功能强大且多领域适用,但常出现幻觉现象,即生成的内容包含编造成分或错误。为解决这一问题,引入了LLM-RAG框架。
LLM-RAG通过结合检索和生成技术,提高了信息检索的准确性和效率,并能生成更贴近用户需求的文本。其检索库可定期更新,确保生成的文本内容基于最新信息,同时增强了回复的可解释性和定制能力1。
研究表明,与无RAG的LLM相比,RAG系统在回答问题方面的可靠性显著提高。在没有上下文的情况下,LLM平均只能正确回答34.7%的问题,而有了RAG,准确率提高到了94%2。这表明LLM-RAG框架在提升LLM性能方面具有显著优势。
基于NVIDIA AI 的智能库NIM,能够在1小时内快速地完成搭建基于文本,网页等等N种模态文档资料的RAG增强问答系统。当前版本代码,只支持了网页,txt格式2种模态文档数据的检索增强生成。后续版本会支持语音,图像,图表,PDF等等更多N种模态文档数据的检索增强生成。希望粉丝们,宝子们多多支持哦。
技术方案与实施步骤
模型选择:
RAG检索增强生成
RAG检索增强生成(Retrieval Augmented Generation, RAG)是一种结合信息检索和语言生成的技术,旨在提高对用户查询的响应准确性和丰富性。在这一流程中,用户提出问题后,系统通过检索器从多种信息源(如维基百科和私有文档)中获取相关数据。检索到的信息被整合成增强提示,随后通过大型语言模型生成最终答案。这种方法不仅能够提供准确的答案,还能确保信息的时效性和多样性,从而提升用户体验。RAG技术在处理复杂查询和提供背景信息方面展现出了显著的优势,成为现代人工智能系统中重要的组成部分。
RAG框架如下图所示。这张图展示了检索增强生成(RAG)的工作流程,具体步骤如下:
用户提问:用户提出问题,例如“英伟达(NVIDIA)是什么时候成立的?”。
检索器:系统通过检索器访问公开可用的信息(如维基百科)和内部/私有文档(如PDF、DOC等),以获取相关数据。
向量数据库:检索到的信息被存储在向量数据库中,便于快速检索。
增强提示:系统将用户的问题与从数据库中检索到的信息结合,生成增强提示。这一提示包含了相关的上下文信息,例如“英伟达成立于1993年4月5日,由2024年首席执行官黄仁勋(Jensen Huang)创办”。
生成器(LLM):增强提示被发送到大型语言模型(LLM),模型根据提供的上下文生成回答。
AI生成的答案:最终,系统返回生成的答案,例如“英伟达成立于1993年”。
这一流程有效结合了信息检索与生成技术,能够快速、准确地回答用户的问题,提升了用户体验。
LLM-RAG
LLM-RAG(大型语言模型与检索增强生成)是一种结合了大型语言模型(LLM)和信息检索技术的先进框架,旨在提高信息获取和生成的效率与准确性。LLM-RAG通过将检索技术与生成模型结合,能够在面对复杂问题时,提供更准确和上下文相关的回答。其核心思想是利用外部知识库来增强模型的生成能力。
LLM-RAG框架如下图所示。这张图展示了检索增强生成(RAG)的工作流程,具体步骤如下:
文档检索与处理:
数据源:系统从企业知识库中检索各种文档,通常包括PDF文件和其他格式的文档。这些文档包含了组织内的知识和信息。
预处理:检索到的文档经过预处理,以提取有用的信息和结构化数据。预处理的目的是清理数据,使其适合后续的嵌入生成。
文档嵌入:经过预处理的文档被输入到嵌入模型中,生成文档嵌入(document embeddings)。这些嵌入是高维向量,能够捕捉文档的语义特征,使得相似文档能够在向量空间中靠近。
用户查询与响应生成:
用户交互:用户通过聊天机器人网页应用提交查询。用户的问题可以是具体的请求或信息查询。
查询处理:系统接收用户查询,并将其转换为嵌入形式,形成嵌入查询(embedded query)。此步骤确保查询能够与文档嵌入进行有效匹配。
向量数据库检索:嵌入查询被发送到向量数据库,系统根据相似度检索相关的文档嵌入。向量数据库能够快速找到与用户查询相匹配的文档。
生成响应:检索到的相关文档嵌入与用户查询一起被送入大型语言模型(LLM)。LLM利用这些信息生成响应,可能会进行提示调优(prompt tuning),以提高生成文本的质量和相关性。
实时响应:最终,系统生成的文本响应被返回给用户,提供实时、准确的信息。
这一流程的优势在于,它结合了信息检索的高效性和语言生成的灵活性,能够处理复杂的查询并提供上下文丰富的答案。通过这种方式,LLM-RAG不仅提升了用户体验,还能在各种应用场景中发挥重要作用,如客户支持、知识管理和信息检索等领域。
AI-Agent
AI-Agent是一种智能系统,能够自主感知环境、做出决策并执行任务。它通过模块化结构实现功能,包括记忆模块(存储信息)、Agent核心(处理用户请求)、规划模块(制定行动计划)和工具执行(调用外部资源),从而在无需人工干预的情况下高效地完成复杂任务。
AI-Agent是一种先进的智能系统,具备感知环境、进行决策和执行任务的能力。其核心功能包括:
自主执行任务:AI-Agent能够独立完成指定任务,无需人工干预,提升了工作效率。
环境感知和决策能力:通过分析环境信息和用户请求,AI-Agent可以做出合理的决策,以适应动态变化的环境。
模块化结构:
记忆模块:负责信息的存储和检索,帮助Agent在执行任务时维持上下文和历史记录。
Agent核心:作为系统的控制中心,处理用户请求并协调各个模块之间的交互。
规划模块:根据目标制定详细的行动计划,确保任务的有效执行和资源的合理利用。
工具执行:能够调用外部工具和资源,以完成具体的操作和任务。
通过以上模块的协同工作,AI-Agent不仅能够灵活应对复杂任务,还能在不断变化的环境中优化决策过程,从而提升整体工作效率和智能化水平。
AI-Agent的框架如下图所示。
数据的构建:
网页格式数据处理:
使用 BeautifulSoup 工具从指定网页(比如,宠物狗的百度百科这个网址 https://baike.baidu.com/item/%E5%AE%A0%E7%89%A9%E7%8B%97/6677317?fr=ge_ala)提取信息。通过解析HTML文档,提取所需的文本内容、标题和其他结构化数据,以便后续处理和分析。在提取信息后,进行数据清洗,包括:去除HTML标签:清除多余的标签,保留纯文本内容。去除冗余信息:删除无关的文本,如广告、导航链接等。统一格式:标准化文本格式,确保一致性(如大小写、空格处理)。
文本格式数据处理:
对于TXT文本数据,直接读取文件内容,确保数据的完整性和原始性。在此过程中,进行以下预处理步骤:去除空行和多余空格:清理文本中的空行和多余的空格,确保数据整洁。字符编码处理:确保文本的字符编码一致,避免乱码问题。分词与标记化:将文本分割为单词或句子或段落,以便后续分析和处理。通过上述数据清洗和预处理步骤,确保数据的质量,为后续的机器学习和深度学习任务提供高质量的基础数据。
向量化处理方法:
在数据处理的过程中,向量化是将文本数据转换为高维向量的重要步骤,这一过程能够使得文本数据适用于机器学习和深度学习任务。具体实现中,我们使用 NVIDIAEmbeddings 模型进行向量化,示例代码如下:
embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4")
此模型将文本数据转换为高维向量,使其能够在机器学习和深度学习任务中使用。
向量化处理将文本转换为数值形式,显著提高了计算效率,便于进行高效的数学运算和模型训练。它能够捕捉文本中的语义关系,从而提升自然语言处理任务的准确性。此外,向量化支持相似度计算,适用于信息检索和推荐系统,且有助于降维和特征提取,减少计算复杂性。最终,向量化增强了模型的泛化能力,使其更好地适应不同的输入数据,从而为后续的机器学习和深度学习任务奠定了高质量的数据基础。
功能整合:
主要思路:
本方案主要在AI-Agent中进行了RAG检索增强生成,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。比如,
有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司,“汪汪宠物狗”公司创始人是“汪汪”,这个公司成立于宇宙时间10000年1000月100日。公司的简单介绍如下:1.公司愿景:致力于打造一个宠物狗与主人共享幸福生活的乐园,让每一只宠物狗都能得到最好的关爱和照顾。2.公司使命:通过提供专业的宠物狗服务,传播宠物文化,增进人与宠物之间的情感联系,提升宠物狗的生活品质。3.核心价值观:爱:以爱为出发点,对待每一只宠物狗如同家人一般。专业:不断学习和创新,提供最高水平的宠物狗服务和产品。责任:承担起对宠物狗、客户和社会的责任,积极参与公益活动。4.公司历史与创始人故事:公司由一位热爱宠物狗的资深宠物爱好者创立,他/她曾经救助过许多流浪狗,并深感宠物狗给人类带来的快乐和陪伴是无价的。因此,决定创立这家公司,将这份爱传递给更多的宠物狗和它们的主人。5.公司特色项目:“宠物狗才艺秀”:定期举办宠物狗才艺比赛,展示宠物狗的智慧和才能。“宠物狗公益行”:组织志愿者团队,参与宠物狗的救助和领养活动,推广宠物保护意识。“宠物狗主题度假村”:打造一个宠物狗和主人可以共同享受的度假村,提供豪华住宿和宠物设施。6.公司成就与荣誉:荣获“宇宙时间10000年度的最佳宠物服务品牌”称号,由宠物行业协会颁发。成为多家宠物慈善机构的合作伙伴,共同推动宠物福利事业。累计帮助数千只流浪狗找到温暖的家,受到社会各界的广泛赞誉。7.公司未来规划:扩大业务范围,开设更多的宠物狗专卖店和服务中心。推出自主研发的宠物狗食品和保健品系列,注重天然和健康。建立宠物狗训练学校,提供专业的宠物狗训练课程和教育服务。8.公司文化与氛围:公司内部充满活力和创意,鼓励员工提出新的想法和建议。定期举办员工培训和团队建设活动,提升员工的专业技能和服务意识。营造一个温馨、和谐的工作环境,让员工和宠物狗都能感受到家的温暖。
假如我想检索公司名称是“汪汪宠物狗”公司,使用LLM肯定是没有结果,但是如果加入了这个txt文档内容的限制条件,就可以返回我想要的结果了。
同样,假如我想基于某个网页资料的内容,进行相关问题的检索,也可以得到我想要的检索结果信息。
本文中的案例,可以尝试输入一下文本,就可以得到自己想要的检索结果了。
宠物狗的介绍?
宠物狗的介绍?请将结果翻译成中文。
宠物狗的百度百科的介绍?
宠物狗的百度百科的介绍?请将结果翻译成中文。
汪汪宠物狗的公司的介绍?
汪汪宠物狗的公司的介绍,请将结果翻译成中文。
多模态文档数据:
代码中已经实现了txt格式文档和网页2种模态的文档数据的RAG检索增强生成,具体修改的位置如下所示。
def create_embeddings(embedding_path: str = "./embed"):
embedding_path = "./embed"
print(f"Storing embeddings to {embedding_path}")
# 包含 宠物狗 技术文档的网页列表
urls = [
"https://baike.baidu.com/item/%E5%AE%A0%E7%89%A9%E7%8B%97/6677317?fr=ge_ala"
]
def create_embeddings_text(text_path: str = "./zh_data/", embedding_path: str = "./embed"):
embedder = NVIDIAEmbeddings(model="ai-embed-qa-4")
# Here we read in the text data and prepare them into vectorstore
ps = os.listdir(text_path)
绿色的可以进行网址的修改,以及txt格式文件路径的修改。
这样就可以在AI-Agent中进行RAG检索增强生成了,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。
比如,有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司。而且有相关的txt格式文档或者网页资料数据。
多模态文档数据合并:
AI-Agent中进行RAG检索增强生成了,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。
比如,有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司。而且有相关的txt文档或者网页资料数据。
代码中实现了2个函数,因为使用了同一个Embedding Model 的嵌入向量模型,比如
embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4")
所以,可以直接对数据库进行更新写入即可。
create_embeddings() ## 网页数据
create_embeddings_text() ## txt格式文档数据
### 使用定义好的相关函数和工具执行文档嵌入Embeddings的生成
import os
import shutil
def ensure_empty_directory(dir_path):
if os.path.exists(dir_path):
# 如果文件夹存在,删除它
shutil.rmtree(dir_path)
ensure_empty_directory("./embed")
##
create_embeddings() ## 网页数据
create_embeddings_text() ## txt格式文档数据
embedding_model = NVIDIAEmbeddings(model="ai-embed-qa-4")
# Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
# 只需要执行一次,后面可以重读已经保存的向量存储
text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
docs = []
metadatas = []
dest_embed_dir = embedding_path
for i, d in enumerate(documents):
splits = text_splitter.split_text(d)
# print('splits: ', len(splits), len(splits[0]), splits)
#print(len(splits))
docs.extend(splits)
metadatas.extend([{"source": sources[i]}] * len(splits))
# print('docs: ', docs)
# print('metadatas: ', metadatas)
docs = remove_control_chars(docs)
# print('docs: ', docs)
# print('metadatas: ', metadatas)
# 创建embeddings嵌入并通过FAISS进行向量存储
if os.path.exists(dest_embed_dir):
update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embedder, allow_dangerous_deserialization=True)
update.add_texts(docs, metadatas=metadatas)
update.save_local(folder_path=dest_embed_dir)
else:
os.makedirs(dest_embed_dir)
docsearch = FAISS.from_texts(docs, embedder , metadatas=metadatas)
docsearch.save_local(folder_path=dest_embed_dir)
多模态AI-Agent智能聊天机器人的封装和部署:
使用将多模态智能体封装进Gradio,需要要对函数再进行一次封装,需要满足gr.Interface的函数调用格式。
对于服务api的端口只能使用一次,需要再次更改端口号码。
### 将多模态智能体封装进Gradio
def chat_gr(query):
qa = ConversationalRetrievalChain(
retriever=docsearch.as_retriever(),
combine_docs_chain=doc_chain,
memory=memory,
question_generator=question_generator,
)
result = qa({"question": query})
rag_result = result.get("answer")
return rag_result
import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
inputs=['text'],
outputs=['text'],
title="Multi Modal chat agent",
description="Multi Modal chat agent",
allow_flagging="never")
multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")
本地服务器可以使用一下链接,在浏览器中直接进行访问了。
实施步骤:
环境搭建:
## 配置环境:
## Win11系统,Anaconda安装完之后,打开Anaconda Powershell Prompt:
######
conda create --name ai_endpoint python=3.8
conda activate ai_endpoint
pip install -i https://mirrors.aliyun.com/pypi/simple langchain-nvidia-ai-endpoints
pip install -i https://mirrors.aliyun.com/pypi/simple jupyterlab
pip install -i https://mirrors.aliyun.com/pypi/simple langchain_core
pip install -i https://mirrors.aliyun.com/pypi/simple langchain
pip install -i https://mirrors.aliyun.com/pypi/simple matplotlib
pip install -i https://mirrors.aliyun.com/pypi/simple numpy
pip install -i https://mirrors.aliyun.com/pypi/simple faiss-cpu==1.7.2
pip install -i https://mirrors.aliyun.com/pypi/simple openai
pip install -i https://mirrors.aliyun.com/pypi/simple gradio
pip install -i https://mirrors.aliyun.com/pypi/simple -U langchain-community
######
## 启动 jupyter-lab
jupyter-lab
代码实现:
### 导入相关包
from langchain_nvidia_ai_endpoints import ChatNVIDIA
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
from langchain.chains import ConversationalRetrievalChain, LLMChain
from langchain.chains.conversational_retrieval.prompts import CONDENSE_QUESTION_PROMPT, QA_PROMPT
from langchain.chains.question_answering import load_qa_chain
from langchain.memory import ConversationBufferMemory
from langchain.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
### 获取 NVIDIA API key
import getpass
import os
if not os.environ.get("NVIDIA_API_KEY", "").startswith("nvapi-"):
nvapi_key = getpass.getpass("Enter your NVIDIA API key: ")
assert nvapi_key.startswith("nvapi-"), f"{nvapi_key[:5]}... is not a valid key"
os.environ["NVIDIA_API_KEY"] = nvapi_key
### 定义html_document_loader网络文档数据加载器:进行文档解析返回文档内容
import re
from typing import List, Union
import requests
from bs4 import BeautifulSoup
def html_document_loader(url: Union[str, bytes]) -> str:
"""
Loads the HTML content of a document from a given URL and return it's content.
Args:
url: The URL of the document.
Returns:
The content of the document.
Raises:
Exception: If there is an error while making the HTTP request.
"""
try:
response = requests.get(url)
html_content = response.text
except Exception as e:
print(f"Failed to load {url} due to exception {e}")
return ""
try:
# 创建Beautiful Soup对象用来解析html
soup = BeautifulSoup(html_content, "html.parser")
# 删除脚本和样式标签
for script in soup(["script", "style"]):
script.extract()
# 从 HTML 文档中获取纯文本
text = soup.get_text()
# 去除空格换行符
text = re.sub("\s+", " ", text).strip()
return text
except Exception as e:
print(f"Exception {e} while loading document")
return ""
### 定义数据向量化工具
def create_embeddings(embedding_path: str = "./embed"):
embedding_path = "./embed"
print(f"Storing embeddings to {embedding_path}")
# 包含 宠物狗 技术文档的网页列表
urls = [
"https://baike.baidu.com/item/%E5%AE%A0%E7%89%A9%E7%8B%97/6677317?fr=ge_ala"
]
# 使用html_document_loader对 宠物狗 文档数据进行加载
documents = []
for url in urls:
document = html_document_loader(url)
documents.append(document)
#进行chunk分词分块处理
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=0,
length_function=len,
)
texts = text_splitter.create_documents(documents)
index_docs(url, text_splitter, texts, embedding_path)
print("Generated embedding successfully")
import re
def remove_control_chars(text_list):
for index, text in enumerate(text_list):
if not isinstance(text, str):
raise ValueError("Input must be a string")
# text_list[index] = re.sub(r'[\u0000-\u001F\u007F-\u009F\u200C\u200D\n\r\t]', '', text)
text_list[index] = text
return text_list
### 定义index_docs函数作为构建向量储存和文档检索工具
def index_docs(url: Union[str, bytes], splitter, documents: List[str], dest_embed_dir) -> None:
"""
Split the document into chunks and create embeddings for the document
Args:
url: Source url for the document.
splitter: Splitter used to split the document
documents: list of documents whose embeddings needs to be created
dest_embed_dir: destination directory for embeddings
Returns:
None
"""
# 通过NVIDIAEmbeddings工具类调用NIM中的"ai-embed-qa-4"向量化模型
embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4")
# Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
docs = []
metadatas = []
# 只需要执行一次,后面可以重读已经保存的向量存储
for document in documents:
texts_raw = splitter.split_text(document.page_content)
# print('texts_raw****: ', len(texts_raw), len(texts_raw[0]) ,texts_raw)
splits = text_splitter.split_text(texts_raw[0])
# print('splits****: ', len(splits), len(splits[0]) ,splits)
#print(len(splits))
docs.extend(splits)
metadatas.extend([document.metadata]*len(splits))
# print('docs: ', docs)
# print('metadatas: ', metadatas)
docs = remove_control_chars(docs)
# print('docs: ', docs)
# print('metadatas: ', metadatas)
# 创建embeddings嵌入并通过FAISS进行向量存储
if os.path.exists(dest_embed_dir):
update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embeddings, allow_dangerous_deserialization=True)
update.add_texts(docs, metadatas=metadatas)
update.save_local(folder_path=dest_embed_dir)
else:
os.makedirs(dest_embed_dir)
docsearch = FAISS.from_texts(docs, embeddings , metadatas=metadatas)
docsearch.save_local(folder_path=dest_embed_dir)
### 获取文本数据集并,追加保存到向量数据库
import os
from tqdm import tqdm
from pathlib import Path
from langchain.text_splitter import CharacterTextSplitter
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
def create_embeddings_text(text_path: str = "./zh_data/", embedding_path: str = "./embed"):
embedder = NVIDIAEmbeddings(model="ai-embed-qa-4")
# Here we read in the text data and prepare them into vectorstore
ps = os.listdir(text_path)
data = []
sources = []
for p in ps:
if p.endswith('.txt'):
path2file=text_path+p
with open(path2file,encoding="utf-8") as f:
lines=f.readlines()
for line in lines:
if len(line)>=1:
data.append(line)
sources.append(path2file)
# Step 5 - 进行一些基本的清理并删除空行
documents=[d for d in data if d != '\n']
len(data), len(documents), data[0]
# Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
# 只需要执行一次,后面可以重读已经保存的向量存储
text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
docs = []
metadatas = []
dest_embed_dir = embedding_path
for i, d in enumerate(documents):
splits = text_splitter.split_text(d)
# print('splits: ', len(splits), len(splits[0]), splits)
#print(len(splits))
docs.extend(splits)
metadatas.extend([{"source": sources[i]}] * len(splits))
# print('docs: ', docs)
# print('metadatas: ', metadatas)
docs = remove_control_chars(docs)
# print('docs: ', docs)
# print('metadatas: ', metadatas)
# 创建embeddings嵌入并通过FAISS进行向量存储
if os.path.exists(dest_embed_dir):
update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embedder, allow_dangerous_deserialization=True)
update.add_texts(docs, metadatas=metadatas)
update.save_local(folder_path=dest_embed_dir)
else:
os.makedirs(dest_embed_dir)
docsearch = FAISS.from_texts(docs, embedder , metadatas=metadatas)
docsearch.save_local(folder_path=dest_embed_dir)
### 使用定义好的相关函数和工具执行文档嵌入Embeddings的生成
import os
import shutil
def ensure_empty_directory(dir_path):
if os.path.exists(dir_path):
# 如果文件夹存在,删除它
shutil.rmtree(dir_path)
ensure_empty_directory("./embed")
##
create_embeddings()
create_embeddings_text()
embedding_model = NVIDIAEmbeddings(model="ai-embed-qa-4")
# Embed documents
embedding_path = "embed/"
docsearch = FAISS.load_local(folder_path=embedding_path, embeddings=embedding_model, allow_dangerous_deserialization=True)
### langchain结合NIM实现LLM-RAG检索:并对比未使用RAG的llm输出与使用LLM-RAG的输出效果
llm = ChatNVIDIA(model="ai-mixtral-8x7b-instruct")
result = llm.invoke("Tell me something about nemo")
print(result.content)
llm = ChatNVIDIA(model="ai-mixtral-8x7b-instruct")
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
question_generator = LLMChain(llm=llm, prompt=CONDENSE_QUESTION_PROMPT)
chat = ChatNVIDIA(model="ai-mixtral-8x7b-instruct", temperature=0.1, max_tokens=1000, top_p=1.0)
doc_chain = load_qa_chain(chat , chain_type="stuff", prompt=QA_PROMPT)
qa = ConversationalRetrievalChain(
retriever=docsearch.as_retriever(),
combine_docs_chain=doc_chain,
memory=memory,
question_generator=question_generator,
)
query = "宠物狗的介绍?"
print(query)
query = "宠物狗的介绍?请将结果翻译成中文。"
print(query)
query = "宠物狗的百度百科的介绍?"
print(query)
query = "宠物狗的百度百科的介绍?请将结果翻译成中文。"
print(query)
query = "汪汪宠物狗的公司的介绍?"
print(query)
query = "汪汪宠物狗的公司的介绍,请将结果翻译成中文。"
print(query)
### 将多模态智能体封装进Gradio
def chat_gr(query):
qa = ConversationalRetrievalChain(
retriever=docsearch.as_retriever(),
combine_docs_chain=doc_chain,
memory=memory,
question_generator=question_generator,
)
result = qa({"question": query})
rag_result = result.get("answer")
return rag_result
import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
inputs=['text'],
outputs=['text'],
title="Multi Modal chat agent",
description="Multi Modal chat agent",
allow_flagging="never")
multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")
测试与调优:
代码中已经实现了txt格式文档和网页2种模态的文档数据的RAG检索增强生成,具体修改的位置如下所示。
def create_embeddings(embedding_path: str = "./embed"):
embedding_path = "./embed"
print(f"Storing embeddings to {embedding_path}")
# 包含 宠物狗 技术文档的网页列表
urls = [
"https://baike.baidu.com/item/%E5%AE%A0%E7%89%A9%E7%8B%97/6677317?fr=ge_ala"
]
def create_embeddings_text(text_path: str = "./zh_data/", embedding_path: str = "./embed"):
embedder = NVIDIAEmbeddings(model="ai-embed-qa-4")
# Here we read in the text data and prepare them into vectorstore
ps = os.listdir(text_path)
绿色的可以进行网址的修改,以及txt格式文件路径的修改。
这样就可以在AI-Agent中进行RAG检索增强生成了,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。
比如,有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司。而且有相关的txt格式文档或者网页资料数据。
假如我想检索公司名称是“汪汪宠物狗”公司,使用LLM肯定是没有结果,但是如果加入这个txt格式文档内容的限制条件,就可以返回我想要的结果了。
本文中的案例,可以尝试输入一下文本,就可以得到自己想要的检索结果了。
宠物狗的介绍?
宠物狗的介绍?请将结果翻译成中文。
宠物狗的百度百科的介绍?
宠物狗的百度百科的介绍?请将结果翻译成中文。
汪汪宠物狗的公司的介绍?
汪汪宠物狗的公司的介绍,请将结果翻译成中文。
集成与部署:
AI-Agent中进行RAG检索增强生成了,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。
比如,有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司。而且有相关的txt或者网页资料数据。
代码中实现了2个函数,因为使用了同一个Embedding Model 的嵌入向量模型,比如
embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4")
所以,可以直接对数据库进行更新写入即可。
create_embeddings() ## 网页数据
create_embeddings_text() ## txt格式文档数据
### 使用定义好的相关函数和工具执行文档嵌入Embeddings的生成
import os
import shutil
def ensure_empty_directory(dir_path):
if os.path.exists(dir_path):
# 如果文件夹存在,删除它
shutil.rmtree(dir_path)
ensure_empty_directory("./embed")
##
create_embeddings() ## 网页数据
create_embeddings_text() ## txt格式文档数据
embedding_model = NVIDIAEmbeddings(model="ai-embed-qa-4")
使用将多模态智能体封装进Gradio,需要要对函数再进行一次封装,需要满足gr.Interface的函数调用格式。
对于服务api的端口只能使用一次,需要再次更改端口号码。
### 将多模态智能体封装进Gradio
def chat_gr(query):
qa = ConversationalRetrievalChain(
retriever=docsearch.as_retriever(),
combine_docs_chain=doc_chain,
memory=memory,
question_generator=question_generator,
)
result = qa({"question": query})
rag_result = result.get("answer")
return rag_result
import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
inputs=['text'],
outputs=['text'],
title="Multi Modal chat agent",
description="Multi Modal chat agent",
allow_flagging="never")
multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")
本地服务器可以使用一下链接,在浏览器中直接进行访问了。
说明各模块集成方法及最终部署到实际运行环境的步骤。
使用将多模态智能体封装进Gradio,需要要对函数再进行一次封装,需要满足gr.Interface的函数调用格式。
对于服务api的端口只能使用一次,需要再次更改端口号码。
### 将多模态智能体封装进Gradio
def chat_gr(query):
qa = ConversationalRetrievalChain(
retriever=docsearch.as_retriever(),
combine_docs_chain=doc_chain,
memory=memory,
question_generator=question_generator,
)
result = qa({"question": query})
rag_result = result.get("answer")
return rag_result
import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
inputs=['text'],
outputs=['text'],
title="Multi Modal chat agent",
description="Multi Modal chat agent",
allow_flagging="never")
multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")
本地服务器可以使用一下链接,在浏览器中直接进行访问了。
项目成果与展示:
应用场景展示:
(1)在AI-Agent中进行了RAG检索增强生成,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。比如,
有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司,“汪汪宠物狗”公司创始人是“汪汪”,这个公司成立于宇宙时间10000年1000月100日。公司的简单介绍如下:1.公司愿景:致力于打造一个宠物狗与主人共享幸福生活的乐园,让每一只宠物狗都能得到最好的关爱和照顾。2.公司使命:通过提供专业的宠物狗服务,传播宠物文化,增进人与宠物之间的情感联系,提升宠物狗的生活品质。3.核心价值观:爱:以爱为出发点,对待每一只宠物狗如同家人一般。专业:不断学习和创新,提供最高水平的宠物狗服务和产品。责任:承担起对宠物狗、客户和社会的责任,积极参与公益活动。4.公司历史与创始人故事:公司由一位热爱宠物狗的资深宠物爱好者创立,他/她曾经救助过许多流浪狗,并深感宠物狗给人类带来的快乐和陪伴是无价的。因此,决定创立这家公司,将这份爱传递给更多的宠物狗和它们的主人。5.公司特色项目:“宠物狗才艺秀”:定期举办宠物狗才艺比赛,展示宠物狗的智慧和才能。“宠物狗公益行”:组织志愿者团队,参与宠物狗的救助和领养活动,推广宠物保护意识。“宠物狗主题度假村”:打造一个宠物狗和主人可以共同享受的度假村,提供豪华住宿和宠物设施。6.公司成就与荣誉:荣获“宇宙时间10000年度的最佳宠物服务品牌”称号,由宠物行业协会颁发。成为多家宠物慈善机构的合作伙伴,共同推动宠物福利事业。累计帮助数千只流浪狗找到温暖的家,受到社会各界的广泛赞誉。7.公司未来规划:扩大业务范围,开设更多的宠物狗专卖店和服务中心。推出自主研发的宠物狗食品和保健品系列,注重天然和健康。建立宠物狗训练学校,提供专业的宠物狗训练课程和教育服务。8.公司文化与氛围:公司内部充满活力和创意,鼓励员工提出新的想法和建议。定期举办员工培训和团队建设活动,提升员工的专业技能和服务意识。营造一个温馨、和谐的工作环境,让员工和宠物狗都能感受到家的温暖。 假如我想检索公司名称是“汪汪宠物狗”公司,使用LLM肯定是没有结果,但是如果假如了这个txt的限制条件,就可以返回我想要的结果了。
同样,假如我想基于某个网页资料的内容,进行相关问题的检索,也可以得到我想要的检索结果信息。
功能演示:
本地服务器可以使用一下链接,在浏览器中直接进行访问了。
本文中的案例,可以尝试输入一下文本,就可以得到自己想要的检索结果了。
宠物狗的介绍?
宠物狗的介绍?请将结果翻译成中文。
宠物狗的百度百科的介绍?
宠物狗的百度百科的介绍?请将结果翻译成中文。
汪汪宠物狗的公司的介绍?
汪汪宠物狗的公司的介绍,请将结果翻译成中文。
问题与解决方案:
- 问题分析: 详细描述在项目实施过程中遇到的主要问题。
- 解决措施: 阐述针对每个问题采取的具体解决措施及心路历程,体现问题解决能力。
问题1:创新方案的构思和构建问题。
(1)从RAG本身的多模态信息来源的思路,支持文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的增强文档资料。
(2)从输入查询信息的角度,可以输入支持文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的增强文档资料+query文本查询信息。
选择了(1),而且选择了便于实现的就是txt格式和网页格式的2种模态的增强文档资料。
问题2:就是txt格式和网页2种模态的增强文档资料的向量数据库的合并问题。
解决措施:因为使用了同一个Embedding Model 的嵌入向量模型,比如
embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4")
所以,可以直接对数据库进行更新写入即可。
关键代码如下:
# Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
# 只需要执行一次,后面可以重读已经保存的向量存储
text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
docs = []
metadatas = []
dest_embed_dir = embedding_path
for i, d in enumerate(documents):
splits = text_splitter.split_text(d)
# print('splits: ', len(splits), len(splits[0]), splits)
#print(len(splits))
docs.extend(splits)
metadatas.extend([{"source": sources[i]}] * len(splits))
# print('docs: ', docs)
# print('metadatas: ', metadatas)
docs = remove_control_chars(docs)
# print('docs: ', docs)
# print('metadatas: ', metadatas)
# 创建embeddings嵌入并通过FAISS进行向量存储
if os.path.exists(dest_embed_dir):
update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embedder, allow_dangerous_deserialization=True)
update.add_texts(docs, metadatas=metadatas)
update.save_local(folder_path=dest_embed_dir)
else:
os.makedirs(dest_embed_dir)
docsearch = FAISS.from_texts(docs, embedder , metadatas=metadatas)
docsearch.save_local(folder_path=dest_embed_dir)
问题3:遇到了使用其他网页,其他txt资料进行向量化的错误问题。
解决措施:刚开始以为是txt文档中的控制字符的问题,去掉控制字符依旧没有解决这个问题。只能一行一行的删减txt文档中的文字,发现了只要字符窜的长度太长了就会有问题。只要修改代码中字符串的长度了就可以解决问题了。也就是使用“。”进行分割就正常了。如果是英文资料需要用“.”。也可以自己编写一个分割函数,兼容2种模式。
text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
从此推断,网页也是这个问题。修改代码如下:
# 通过NVIDIAEmbeddings工具类调用NIM中的"ai-embed-qa-4"向量化模型
embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4")
# Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
docs = []
metadatas = []
# 只需要执行一次,后面可以重读已经保存的向量存储
for document in documents:
texts_raw = splitter.split_text(document.page_content)
# print('texts_raw****: ', len(texts_raw), len(texts_raw[0]) ,texts_raw)
splits = text_splitter.split_text(texts_raw[0])
# print('splits****: ', len(splits), len(splits[0]) ,splits)
#print(len(splits))
docs.extend(splits)
metadatas.extend([document.metadata]*len(splits))
# print('docs: ', docs)
# print('metadatas: ', metadatas)
docs = remove_control_chars(docs)
# print('docs: ', docs)
# print('metadatas: ', metadatas)
# 创建embeddings嵌入并通过FAISS进行向量存储
if os.path.exists(dest_embed_dir):
update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embeddings, allow_dangerous_deserialization=True)
update.add_texts(docs, metadatas=metadatas)
update.save_local(folder_path=dest_embed_dir)
else:
os.makedirs(dest_embed_dir)
docsearch = FAISS.from_texts(docs, embeddings , metadatas=metadatas)
docsearch.save_local(folder_path=dest_embed_dir)
问题4:将多模态智能体封装进Gradio错误。
解决措施:需要对函数再进行一次封装,需要满足gr.Interface的函数调用格式。对于服务api的端口只能使用一次,需要再次更改端口号码。
### 将多模态智能体封装进Gradio
def chat_gr(query):
qa = ConversationalRetrievalChain(
retriever=docsearch.as_retriever(),
combine_docs_chain=doc_chain,
memory=memory,
question_generator=question_generator,
)
result = qa({"question": query})
rag_result = result.get("answer")
return rag_result
import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
inputs=['text'],
outputs=['text'],
title="Multi Modal chat agent",
description="Multi Modal chat agent",
allow_flagging="never")
multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")
项目总结与展望:
项目评估: 对项目的整体表现进行客观评估,总结成功点和存在的不足。
目前已经实现了txt格式和网页2种模态的增强文档资料进行RAG了。但是,还有很多可以做,比如,语音,图像,PDF,WORD,PPT等等多种模态的增强文档资料都很容易集成进去的。
未来方向: 基于项目经验,提出未来可能的改进方向和发展规划。
未来会在考虑
(1)在RAG阶段加入更多的模态:文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的增强文档资料。
(2)在输入查询信息的直接就支持:文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的增强文档资料输入,以及query查询信息也支持文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的查询信息输入了。
附件与参考资料
- NVIDIA AI Endpoint介绍页面: NVIDIA AI Foundation Endpoints | 🦜️🔗 LangChain
- NVIDIA NIM页面: Try NVIDIA NIM APIs
- NVIDIA DLI课程学习资料页面:NVIDIA 深度学习培训中心(DLI)| 在线实战培训