NVIDIA AI-AGENT夏季训练营
项目名称:AI-AGENT夏季训练营 — RAG智能对话机器人
报告日期:2024年8月18日
项目负责人:shu
项目概述:
基于NVIDIA NIM平台构建可使用的ai,本质上是按需挑选不同模型实现RAG对话机器人和构建可接收修改绘制图片的多模态智能体。大模型以高准确率及强泛化率著称,但因其所需计算资源大、时间长,而设计精简的轻量级模型以其牺牲计算效率交换速度和计算资源需求实现了更广的适用性,适合部署在资源受限的设备,如手机等移动端或嵌入式系统。该技术在智能客服,教育科研及娱乐等各种场景都具有极大应用价值。该项目以其高效的资源利用、快速响应能力、广泛的适用性和经济实惠的特点,在特定应用场景中展现出独特的优势。例如在智能家居控制、车载导航对话中使用该智能对话机器人可使用户不局限于设定好的口令,提高用户体验。
技术方案与实施步骤
- 模型选择:
首先选择轻量级的phi-3-small-128k-instruct模型,由于其“small”和“128k”参数的特性,该模型相较于大型AI模型,在训练和推理时需要的计算资源和内存显著减少,使得它能够在低成本硬件或资源受限的环境中高效运行。同时处理速度更快,能够在时间敏感的应用中快速响应,如实时对话系统或需要即时反馈的场景。特定于指令(instruct)的调整意味着该模型在遵循指令、理解和生成符合特定目标或场景的文本方面表现出色,适合用于教学指导、客服对话等需要明确指令响应的任务。维护和部署成本较低,对于初创企业或预算有限的项目来说,是一个性价比高的选择。Rag技术通过结合大语言模型(LLM)和动态检索机制,能够实时连接外部数据源,从而显著提升模型的输出质量及准确性,使ai处理更复杂的查询,提供更加个性化和智能化的用户体验。
2.数据的构建:
数据构建过程:收集原始信息,进行数据清洗,异常值处理及去除错误值、重复值、缺失值。
向量化处理方法:利用ai-embed-qa-4向量模型处理文本数据,进行RAG检索。
优势: RAG将检索模块与生成模型结合,能从大量文本中检索相关上下文信息,提高了回复的准确性和多样性。相较于完全依赖于端到端训练的生成模型,RAG系统的知识库可以独立于模型进行更新和扩展,能更快地吸收新信息,无需重新训练整个模型,降低了维护成本。利用高效的向量化处理,尤其是密集向量表示(如DPR),RAG能够迅速在大规模文档集中定位到与查询最相关的片段。在提升了响应速度同时保证了回复的相关性。向量化处理不仅关注关键词匹配,还能捕获文本的语义相似度,使得RAG能够处理含有隐喻、俚语或复杂表述的查询,提高了在处理模糊或多义性问题时的表现。由于RAG能够指出其回复所依据的具体文档或段落,用户可以追溯模型决策的依据,增加了交互的透明度和信任度。
实施步骤:
一、环境搭建*
创建Python环境
首先安装Miniconda,(官网地址:https://docs.conda.io/en/latest/miniconda.html
清华大学镜像地址: https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/),再打开Anaconda Powershell,在打开的终端中按照下面的步骤依次执行以配置环境:
conda create --name ai_endpoint python=3.8 #创建python 3.8虚拟环境
conda activate ai_endpoint #进入虚拟环境
pip install langchain-nvidia-ai-endpoints #安装nvidia_ai_endpoint工具
pip install jupyterlab #安装Jupyter Lab
pip install langchain_core #安装langchain_core
pip install langchain #安装langchain
pip install matplotlib #安装matplotlib
pip install numpy #安装Numpy
pip install faiss-cpu==1.7.2 #安装faiss, 这里如果没有GPU可以安装CPU版本
pip install openai #安装OPENAI库
jupyter-lab #利用Jupyter Lab打开课件执行
二、代码实现
Step 1 - 使用NVIDIA_API_KEY
import getpass
import os
if os.environ.get("NVIDIA_API_KEY", "").startswith("nvapi-"):
print("Valid NVIDIA_API_KEY already in environment. Delete to reset")
else:
nvapi_key = getpass.getpass("NVAPI Key (starts with nvapi-): ")
assert nvapi_key.startswith("nvapi-"), f"{nvapi_key[:5]}... is not a valid key"
os.environ["NVIDIA_API_KEY"] = nvapi_key
from langchain_nvidia_ai_endpoints import ChatNVIDIA
ChatNVIDIA.get_available_models()
Step 2 - 初始化SLM
llm = ChatNVIDIA(model="microsoft/phi-3-small-128k-instruct", nvidia_api_key=nvapi_key, max_tokens=512)
result = llm.invoke("泰坦尼克号的导演是谁?")
print(result.content)
Step 3 - 初始化ai-embed-qa-4向量模型
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
embedder = NVIDIAEmbeddings(model="NV-Embed-QA")
Step 4 - 获取文本数据集
import os
from tqdm import tqdm
from pathlib import Path
# Here we read in the text data and prepare them into vectorstore
ps = os.listdir("./zh_data/")
data = []
sources = []
for p in ps:
if p.endswith('.txt'):
path2file="./zh_data/"+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 并将其保存到磁盘
# Here we create a vector store from the documents and save it to disk.
from operator import itemgetter
from langchain.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain.text_splitter import CharacterTextSplitter
from langchain_nvidia_ai_endpoints import ChatNVIDIA
import faiss
Step 6b - 重读之前处理并保存的 Faiss Vectore 存储
# Load the vectorestore back.
store = FAISS.load_local("./zh_data/nv_embedding", embedder,allow_dangerous_deserialization=True)
Step 7- 提出问题并基于phi-3-small-128k-instruct模型进行RAG检索
retriever = store.as_retriever()
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Answer solely based on the following context:\n<Documents>\n{context}\n</Documents>",
),
("user", "{question}"),
]
)
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
chain.invoke("泰坦尼克号的导演是谁?")
- 功能整合: 介绍多模态功能的整合策略与实现方法。
Step 1 -导入构建对话系统所需的库
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
Step 2 -获取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
Step 3 -定义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 ""
Step 4 -定义数据向量化工具
def create_embeddings(embedding_path: str = "./embed"):
embedding_path = "./embed"
print(f"Storing embeddings to {embedding_path}")
# 包含 NVIDIA NeMo toolkit技术文档的网页列表
urls = [
"https://docs.nvidia.com/nemo-framework/user-guide/latest/nemotoolkit/"
]
# 使用html_document_loader对NeMo toolkit技术文档数据进行加载
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")
Step 5 -定义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")
for document in documents:
texts = splitter.split_text(document.page_content)
# 根据url清洗好的文档内容构建元数据
metadatas = [document.metadata]
# 创建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(texts, metadatas=metadatas)
update.save_local(folder_path=dest_embed_dir)
else:
docsearch = FAISS.from_texts(texts, embedding=embeddings, metadatas=metadatas)
docsearch.save_local(folder_path=dest_embed_dir)
Step 6 -使用定义好的相关函数和工具执行文档嵌入Embeddings的生成
create_embeddings()
embedding_model = NVIDIAEmbeddings(model="NV-Embed-QA")
# Embed documents
embedding_path = "embed/"
docsearch = FAISS.load_local(folder_path=embedding_path, embeddings=embedding_model, allow_dangerous_deserialization=True)
Step 7 -langchain结合NIM实现LLM-RAG检索:并对比未使用RAG的llm输出与使用LLM-RAG的输出效果
llm = ChatNVIDIA(model="mistralai/mixtral-8x7b-instruct-v0.1")
result = llm.invoke("Tell me something about nemo")
print(result.content)
llm = ChatNVIDIA(model="mistralai/mixtral-8x7b-instruct-v0.1")
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
question_generator = LLMChain(llm=llm, prompt=CONDENSE_QUESTION_PROMPT)
chat = ChatNVIDIA(model="mistralai/mixtral-8x7b-instruct-v0.1", 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 = "Tell me something about nemo"
result = qa({"question": query})
rag_result = result.get("answer")
rag_result
三、集成与部署
1、使用NVIDIA_API_KEY初始化SLM (Smoothed Language Model)
集成方法: 首先确保你的环境中安装了支持NVIDIA GPU的库,如TensorFlow或PyTorch,并正确配置了NVIDIA的CUDA和cuDNN环境。然后,在代码的开始部分,使用环境变量os.environ["NVIDIA_API_KEY"]来访问和验证NVIDIA API密钥。
部署步骤: 在部署环境中,确保NVIDIA驱动、CUDA、cuDNN以及相应的深度学习库版本兼容,并通过环境变量设置好NVIDIA_API_KEY。
2、初始化ai-embed-qa-4向量模型
集成方法: 这通常涉及到加载一个预训练的嵌入模型,如Hugging Face Transformers库中的模型。你可能需要使用类似from transformers import AutoModel, AutoTokenizer这样的代码来加载模型和分词器。
部署步骤: 将模型和相关依赖打包成适合生产环境的格式(如Docker镜像),确保在目标服务器上安装所有必要的库。部署时,考虑模型的大小和资源需求,选择合适的服务器配置。
3、获取文本数据集
集成方法: 数据集可以从本地文件系统、云存储(如S3)、数据库或API接口获取。使用Python标准库或第三方库(如pandas)来加载数据。
部署步骤: 确保数据集路径或访问凭证在部署环境中有效且可访问。自动化数据获取流程,确保数据更新时能自动同步到生产环境。
4、数据清理与处理
集成方法: 使用Python的Pandas或正则表达式等工具进行数据清洗,去除空行、格式化文本等。
部署步骤: 将数据清洗逻辑封装成函数或脚本,确保其稳健并能处理各种异常情况。在部署时作为数据预处理的一部分执行。
5、构建faiss vectorstore•
集成方法: 利用faiss库和相关工具(如sentence_transformers)将文本转换为向量,然后使用Faiss进行索引。这一步可能包括将向量和对应文档ID存储到磁盘。•
部署步骤: 在离线环境下完成向量构建,然后将Faiss索引文件和其他必要资源一起部署到生产服务器。确保索引文件的加载和查询效率。
6、重读Faiss Vectorstore并执行RAG检索•
集成方法: 从磁盘加载之前保存的Faiss Vectorstore,使用提问模型(例如phi-3-small-128k-instruct)基于检索的向量来生成回答。•
部署步骤: 将检索和生成答案的逻辑集成到后端服务中,通过API接口接受提问,处理请求,执行检索和生成回答,最后返回结果给前端或客户端。最终部署到实际运行环境。
包含上述步骤的整个应用程序应当被打包成可部署的单元,比如Docker容器。使用Jupyter Lab或类似平台进行容器编排,以确保高可用性、自动扩展和故障恢复。
在生产环境中实施监控和日志记录,以便跟踪性能和解决出现的问题。
项目成果与展示:
应用场景展示:
教育科研:在教育科研领域,对话机器人可以担任虚拟助教的角色,提供个性化的学习辅导和答疑服务。例如,学生可以通过与机器人对话来获得课程内容的解释、作业帮助、推荐学习资源或进行互动式测验。此外,科研人员能利用对话机器人来辅助文献搜索、整理实验数据和讨论研究思路,促进学术交流和合作。
智能家居:智能家居场景中,对话机器人成为家庭的智能控制中心,通过语音指令或文字交互,用户可以控制家中的各种智能设备,如调整温度、开关灯光、播放音乐或查询天气预报。机器人还能根据用户的习惯和偏好,提供个性化的居家建议和服务,增强居住体验和便利性。
客户服务:在企业客户服务方面,对话机器人能够24/7无间断地处理客户咨询、投诉和订单管理等事务,大幅提高服务效率和响应速度。客户可以通过聊天界面快速获取产品信息、解决技术问题、追踪物流状态或完成简单交易,减少了人工客服的压力。同时,机器人能收集用户反馈,帮助企业优化产品和服务。
功能演示: 列出并展示实现的主要功能,附上UI页面截图,直观展示项目成果。
图
根据相关文本提取信息回答问题的小型对话机器人
图2创建出的具有图片识别与根据提示词修改图片功能的网页
问题与解决方案:
- 问题分析: 详细描述在项目实施过程中遇到的主要问题。
- 解决措施: 阐述针对每个问题采取的具体解决措施及心路历程,体现问题解决能力。
1、部署过程中会出现warninng,根据提示替换模型即可。
2、需要把代码文件和jupyter脚本放在同一个文件夹目录下
3、step6a-出现报错,可能需要退出lab重新升级pip install -U langchain-community。请注意报错是否返回缺了langchain-community
4、要注意重新生成的key是不是没有覆盖代码块里的旧key,覆盖以后重新运行那个代码块
5、若一直报kernel崩溃,关闭代理,重启一下
6、想要读取docx文件,并进行向量化,可以选择对应的读取工具包,比如langchain和llamaIndex等是提供了比较丰富的文档读取API的,可以借助于这些工具读取文本。之后进行splitter+chunk
7、如果对一篇长文档已经分chunk建立了向量知识库,但是要检索非常细的问题,比如说一个合同文件检索合同金额,这种情况下RAG适合解决这种问题吗?RAG也可以处理的,不过对精准度有苛刻要求的话建议结合检索方法综合来做
8、如果按课件上处理pdf,pdf应该包含了图片、文字和表格,需要对文件中的文本,图表等进行提取,之后可以选择多模态嵌入。
9、RAG对自身的行为结合环境进行优化和进化离不开数据,所以整体的应用程序搭建好之后,还需要对数据库进行维护,比如说定时更新,这样可以保证RAG所获取的内容是实时更新的。
10、如果对一篇非常长的文档已经分chunk建立了向量知识库,但是要检索非常细的问题,比如一个合同文件检索合同金额,可以采用高级RAG的方法,比如说进行混合检索,也就是相似性检索+关键字检索。
11、如果报错@root-validator相关将pydantic降级到2以下,如果报langchain缺少属性,可加个格子运行import langchain
langchain.verbose = False
langchain.debug = False
langchain.llm_cache = ()
12、一个文档如果已经embedding并存入向量库中了,再继续embedding,会重复加入还是会自动去重,取决于所用的数据库是否支持根据upsert,也就是插入更新。如果是支持的,那可能设置primary key, 比如说用文件名字作为这个key,那支持upsert操作的数据库 当获得第二次embedding进行插入时 如果是同样的文件名字,那就会覆盖。
13、若chunk size太大,问题过短,能匹配的内容不够长,而embedding 长度是固定的,容易丢失细节。 且chunk size太大对于llm的负担也较大,小的模型对于长文本的效果远没有短的好。因此当 Embeding 最大token512的话 chunk size 适合范围为200-400,Embeding模型最大1024的话,chunk size 适合范围为500-1000。
项目总结与展望:
- 项目评估: 对项目的整体表现进行客观评估,总结成功点和存在的不足。
- 未来方向: 基于项目经验,提出未来可能的改进方向和发展规划。
成功点:
1. 技术栈选择合理:采用NVIDIA GPU加速和NVIDIA_API_KEY进行SLM初始化,表明项目充分利用了高性能计算资源,提升了模型训练和推理的效率。
2. 模型集成高效:成功集成并初始化了先进的ai-embed-qa-4向量模型和phi-3-small-128k-instruct模型,为高质量的语义理解和生成回答打下了基础。
3. 数据处理流程完善:从数据获取、清理、向量化到存储,形成了一套完整的流程。特别是利用Faiss构建vectorstore并保存至磁盘,既便于管理和迭代,也提高了查询效率。
4. RAG机制应用:基于检索增强的生成方式(RAG),结合了信息检索和生成模型的优势,能更准确、灵活地生成回答,增强了对话机器人的实用性和鲁棒性。
存在的不足:
1. 数据质量和多样性:虽然进行了基本的数据清理,但未明确提及数据的质量评估和多样性增强策略,可能导致模型学习受限,影响回答的广泛性和准确性。
2. 模型定制化程度:虽然选用了高性能模型,但项目未深入探讨模型的微调或定制化过程,这可能影响模型在特定领域的适应性和精准度。
3. 系统的可扩展性和维护性:报告中未详细说明关于系统架构设计如何支持未来数据量增长、模型更新或新增功能的考量,这可能对长期运维构成挑战。
4. 用户反馈循环:缺乏对用户反馈机制的描述,如何收集并利用用户与机器人的交互数据来持续优化模型是一个关键成功因素,项目中对此提及较少。
数据增强与质量提升:
扩充数据集来源,确保覆盖更广泛的领域和话题,提高回答的多样性和深度。•引入更精细的数据预处理步骤,如实体识别、情感分析等,提升数据清洗的质量,使模型学习到更精确的上下文信息。
实施主动学习策略,根据机器人与用户的交互反馈,动态筛选并加入新样本,持续优化模型性能。
模型定制与优化:
对phi-3-small-128k-instruct模型进行领域特定的微调,利用领域内数据加强模型的专业知识,提升回答的针对性和准确性。
探索更大规模或更专业的预训练模型,如基于行业知识图谱的融合模型,以增强模型的泛化能力和知识覆盖范围。
优化检索与生成机制,研究如何更高效地平衡检索精度与生成的流畅度,提升对话连贯性和自然度。
系统架构升级:
设计模块化、可插拔的系统架构,便于未来集成最新技术或模型,提高系统的灵活性和扩展性。
引入容器化和微服务技术,提升部署和运维的效率,支持大规模并发访问和数据处理需求。
加强系统的监控与自动化测试,确保服务稳定性和故障快速恢复能力。
用户体验与交互创新:
引入多模态交互,结合语音识别、图像理解等技术,打造更加自然、沉浸式的对话体验。
优化用户反馈机制,建立闭环优化系统,及时捕获并响应用户需求和评价,持续迭代改进。
开发个性化服务功能,利用用户画像和历史交互数据,提供更加贴合个人偏好的内容和建议。
伦理与安全强化:
建立严格的内容过滤机制,防止生成不当或有害信息,确保对话内容的健康与合规。
加强隐私保护措施,确保用户数据的安全,遵循相关法律法规要求。
开展伦理审查,确保技术发展与应用符合社会伦理标准,促进人工智能的可持续发展。
附件与参考资料
[1] 2024 NVIDIA开发者社区夏令营环境配置指南(Win & Mac)_csdn 2024nvidia开发者-CSDN博客