NVIDIA AI-AGENT夏季训练营
项目名称:AI-AGENT夏季训练营 — RAG智能对话机器人
报告日期:2024年8月18日
项目负责人:soulme
项目概述
在当今信息爆炸的时代,如何快速、精准地获取美食信息成为了众多美食爱好者面临的挑战。为了满足这一需求,我们基于最新的Retrieval-Augmented Generation (RAG) 技术,开发了一款智能美食小助手。该助手不仅能够回答各种美食相关的问题,还能基于庞大的数据集进行个性化推荐,帮助用户发现最适合自己口味的美食。
应用场景
这款美食小助手的应用场景广泛,包括但不限于:
- 美食推荐:用户可以根据自己的口味偏好、所在地区或特定需求(如素食、无麸质食品等)获取个性化的美食推荐。
- 餐厅点评:结合用户的查询和评价数据,提供针对不同菜品或餐厅的深度点评。
- 旅游美食指南:为出行在外的用户提供当地最具特色的美食推荐,助力吃货们在旅途中也能品尝到地道美味。
通过引入RAG技术,这款智能助手能够结合检索和生成能力,从庞大的美食数据中提取最相关的信息,并生成自然、流畅的回答。这不仅提升了用户的查询体验,也使得信息获取更加高效和精准。
技术方案与实施步骤
在本项目中,我们采用了基于NVIDIA提供的AI模型和RAG(Retrieval-Augmented Generation)技术,构建了一个智能美食助手。这一部分将详细介绍模型选择、数据构建、代码实现、环境搭建、以及测试与调优的具体步骤。
模型选择
为了确保美食助手能够提供高质量的推荐和回答,我们选择了NVIDIA的microsoft/phi-3-small-128k-instruct
模型作为核心的语言生成模型。这款模型以其强大的自然语言处理能力著称,能够在大规模数据上进行训练,并生成符合上下文的精确答案。
使用RAG模型的主要优势包括:
- 高效的检索能力:RAG模型能够从预先构建的文本数据库中检索到与用户查询最相关的内容,确保回答的精准性。
- 自然的生成能力:在检索到相关内容后,RAG模型能够生成流畅、自然的语言回复,这对于提升用户体验至关重要。
- 扩展性:RAG模型可以方便地扩展到其他领域,如旅游指南、健康建议等,为进一步开发提供了良好的基础。
数据的构建
数据构建是整个项目的基础。我们从本地文本数据库中收集了大量与美食相关的文本数据,这些数据包含了全国各地的美食介绍、餐厅点评、食谱推荐等内容。为了使这些数据能够被模型有效利用,我们采用了NVIDIA的**nv-embedqa-e5-v5
**嵌入模型对数据进行向量化处理。
具体步骤如下:
- 数据收集:从本地文件系统中读取所有与美食相关的文本文件,并将其汇总到一个数据列表中。
- 数据清理:对收集到的文本数据进行基本清理,去除空行和无用的字符,确保数据的质量。
- 文本向量化:使用**
nv-embedqa-e5-v5
**模型将清理后的文本数据转化为向量表示,这些向量将用于后续的检索和生成任务。 - 向量存储:将处理后的向量数据存储在Faiss向量存储中,方便后续的高效检索。
环境搭建
创建Python环境
首先需要安装Miniconda:
清华大学镜像地址: https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/
安装完之后,打开Anaconda Powershell
:
进行依次安装
pip install langchain-nvidia-ai-endpoints
pip install jupyterlab
pip install langchain_core
pip install langchain
pip install numpy
pip install faiss-cpu==1.7.2
pip install openai
代码实现
在代码实现部分,我们将上述过程进行了自动化处理,以下是关键代码的实现步骤:
-
检查并设置NVIDIA API Key:
import getpass import os # 检查 NVIDIA API Key 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()
这段代码的作用是确保在程序运行前,已经设置并验证了NVIDIA API Key。这是访问NVIDIA AI模型服务的前提条件,只有在API Key正确配置后,后续的模型初始化和推理任务才能顺利进行。
通过这种方式,我们可以在不将API Key硬编码到脚本中的情况下安全地进行环境配置,同时也为用户提供了便捷的输入和检查流程。这不仅提高了代码的安全性,也为后续的功能实现奠定了基础。
-
初始化SLM并执行推理任务:
# 初始化SLM llm = ChatNVIDIA( model="microsoft/phi-3-small-128k-instruct", nvidia_api_key=nvapi_key, max_tokens=512 ) # 执行推理任务 result = llm.invoke("南京都有哪些好吃的?") print(result.content)
模型初始化:通过指定模型名称和API Key,成功实例化一个能够处理自然语言的模型对象。这一步确保我们能够访问和利用NVIDIA提供的强大语言模型进行自然语言处理任务。
推理执行:调用invoke
方法,模型能够即时处理用户输入的问题,并返回智能化的回答。这使得我们的美食小助手能够实际运行并提供美食推荐。
通过这一步,我们实现了一个简单的自然语言问答系统,能够回答用户关于美食的问题。这个步骤是整个智能美食助手功能的核心,为后续的高级功能实现(如RAG检索和多轮对话)奠定了基础
输出结果:南京是中国著名的菜饮文化中心之一,以其丰富多样的菜肴而闻名。以下是南京一些推荐的美食:
- 红烧肉:南京的传统烧肉,以其鲜红的色泽和鲜嫩的口感而闻名。
- 宫保鸡丁:南京的宫保鸡丁以其辛辣的口感和丰富的香料而著称。
- 麻婆豆腐:南京的麻婆豆腐以其独特的麻辣口感和滋味而受到许多人的喜爱。
- 豆腐花:南jing的豆腐花是一种经典的南京菜,以其细腻的口感和清爽的味道而闻名。
- 鲜花炒鸡蛋:南京的鲜花炒鸡蛋以其清新的口感和丰富的营养价值而受到许多人的喜爱。
- 鸡丁面:南京的鸡丁面以其鲜嫩的面条和美味的鸡丁汤而闻名。
- 鸡血煎:南京的鸡血煎是一道传统的南京菜,以其独特的口感和美味而受到许多人的喜爱。
- 豆腐泥:南京的豆腐泥以其细腻的口感和丰富的香料而著称。
- 豆腐干:南京的豆腐干是一道经典的
显而易见的,这些美食明显不是南京的特色,真让人失望(╯︵╰,)。别着急,让我们先构建好RAG模型
-
初始化向量模型并处理文本数据集向量化模型的初始化:
# 初始化nv-embedqa-e5-v5向量模型 from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings embedder = NVIDIAEmbeddings(model="nvidia/nv-embedqa-e5-v5") # 获取文本数据集 ps = os.listdir("D:/Text Datebase/") data = [] sources = [] for p in ps: if p.endswith('.txt'): path2file="D:/Text Datebase/"+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) # 进行一些基本的清理并删除空行 documents = [d for d in data if d != '\n'] len(data), len(documents), data[0]
向量化模型的初始化:在RAG模型中,文本的向量表示是关键。通过初始化
nv-embedqa-e5-v5
模型,我们可以将大规模的文本数据转化为可以用于高效检索的向量,这为后续的智能问答和推荐功能提供了技术支持。
数据集的加载和清理:加载美食相关的文本数据并进行基本清理是实现美食助手功能的前提。只有在确保数据质量的情况下,模型的推理结果才能够准确可信。
这一步的完成,标志着我们已经准备好了大量的美食数据,并通过向量化处理,为接下来的检索任务奠定了基础。
输出结果:(1,
1,
'南京作为中国的历史文化名城,以其丰富多样的美食文化而闻名。最具代表性的盐水鸭以鲜嫩味美著称,而鸭血粉丝汤则凭借其鲜美滑嫩的口感赢得了无数食客的喜爱。南京的小笼包皮薄馅多,汁水丰富,是当地人早餐的常见选择。桂花糖芋苗这道传统甜点香甜软糯,深受人们喜爱。什锦菜作为南京人过年必备的家常菜,口感丰富,营养全面。金陵菜饭以青菜和腊肉的搭配而独具风味,状元豆和活珠子则是南京特色小吃,外皮酥脆、风味独特。每一道美食都蕴含着南京深厚的历史文化底蕴,令人回味无穷。') -
将文档处理到Faiss向量存储并保存到磁盘:
# 将文档处理到 faiss vectorstore 并将其保存到磁盘 from langchain.vectorstores import FAISS from langchain.text_splitter import CharacterTextSplitter # 只需要执行一次,后面可以重读已经保存的向量存储 text_splitter = CharacterTextSplitter(chunk_size=400, separator=" ") docs = [] metadatas = [] for i, d in enumerate(documents): splits = text_splitter.split_text(d) docs.extend(splits) metadatas.extend([{"source": sources[i]}] * len(splits)) store = FAISS.from_texts(docs, embedder, metadatas=metadatas) store.save_local('D:/Text Datebase/nv_embedding') # 重读之前处理并保存的 Faiss Vectore 存储 store = FAISS.load_local("D:/Text Datebase/nv_embedding", embedder, allow_dangerous_deserialization=True)
文本切分与向量化处理:通过将长文本切分为小片段并进行向量化,我们能够提高检索精度,并且使得模型可以处理更大规模的数据集。
高效的向量存储与检索:利用FAISS库,我们能够高效地存储和检索大规模向量化数据,这对于实现快速和精确的智能问答和推荐系统至关重要。
持久化与复用:将向量存储保存到本地磁盘并在需要时加载,减少了重复计算的开销,提升了系统的运行效率。 -
提出问题并基于
phi-3-small-128k-instruct
模型进行
# 提出问题并基于phi-3-medium-128k-instruct模型进行RAG检索 retriever = store.as_retriever() from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough 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() ) # 执行 RAG 检索 result2 = chain.invoke("南京都有哪些好吃的?") print(result2)
上下文检索与生成:通过RAG(检索增强生成)技术,系统可以从大量文档中检索出与用户问题最相关的内容,并生成一个基于上下文的自然语言回答。这使得模型能够提供更精准、更有深度的答案。
动态构建与执行:通过链式操作,整个检索与生成过程变得更加模块化和灵活。每个步骤的输出都可以作为下一个步骤的输入,这样不仅提高了代码的可读性和维护性,还方便了未来的扩展和调试。
输出:以南京作为中国历史文化名城而闻名的美食文化为特色,南京有多道美食值得一试。其中最具代表性的是盐水鸭,以其鲜嫩味著称;鸭血粉丝汤,凭借鲜美滑嫩的口感赢得了无数食客的喜爱。南京的小笼包皮薄馅多,汁水丰富,是当地人早餐的常见选择。桂花糖芋苗这道传统甜点香甜软糯,深受人们喜爱。什锦菜作为南jing人过年必备的家常菜,口感丰富,营养全面。金陵菜饭以青菜和腊肉的搭配而独具风味,状元豆和活珠子则是南京特色小吃,外皮酥脆、风味独特。
我们可以看到,最后生成的答案跟实际结果很接近,这是经过RAG修正的结果,这才是南京正宗的特色美食!٩(^ᴗ^)۶
-
使用 Edge-TTS 进行语音合成
# 使用 edge-tts 进行语音合成 import subprocess edge_command = f'edge-tts --text "{result2}" -v=zh-TW-HsiaoChenNeural --write-media demo.mp3' subprocess.run(edge_command, shell=True) print("语音文件已生成:demo.mp3")
语音合成:通过 Edge-TTS 工具,将文本内容转换为语音。这使得智能美食助手不仅可以通过文字回答用户的问题,还能够通过语音形式提供回答,为用户提供更为生动和人性化的交互体验。
文件生成与保存:生成的语音文件被保存为demo.mp3
,用户可以方便地播放或分享这个音频文件,实现文本内容的多媒体输出。
输出:
听听台湾甜妹的声音,让我们更有寻找美食的动力!(*≧∀≦*)
音频文件放在百度网盘里,需要自取
链接:https://pan.baidu.com/s/1agN7YJRFUPItkKaGJl1NOQ?pwd=q154
提取码:q154 -
使用Gradio对智能体进行封装
# Gradio 接口函数 def ask_question(question): # 执行 RAG 检索 result2 = chain.invoke(question) # 生成语音文件 edge_command = f'edge-tts --text "{result2}" -v=zh-TW-HsiaoChenNeural --write-media demo.mp3' subprocess.run(edge_command, shell=True) return result2, "demo.mp3" # 设置 Gradio 界面 iface = gr.Interface( fn=ask_question, inputs=gr.Textbox(lines=2, placeholder="请输入你的问题..."), outputs=["text", "audio"], title="AI美食助手", description="输入一个问题,获取美食相关的回答,并生成语音文件。", ) # 启动 Gradio 应用 if __name__ == "__main__": iface.launch()
封装效果:
好啦,一个完美的美食AI小助手就完成啦!
y(^ヮ^)y
问题与解决方案
在项目实施过程中,我们遇到了几个问题,以下是这些问题的详细描述以及我们采取的解决措施。这些问题的解决不仅提高了项目的执行效率,也优化了系统的整体性能。
1. 环境搭建中的软件包下载慢或下载失败
问题描述:在项目的环境搭建过程中,由于网络连接不稳定或其他原因,软件包的下载速度非常慢,有时甚至出现下载失败的情况。这种情况在下载大型依赖包或访问国外服务器时尤为明显。
解决措施:为了解决这一问题,我们使用了国内的镜像源,具体而言是清华大学开源软件镜像站。通过将Python的包管理工具pip的源切换到清华源,大大提升了软件包的下载速度,避免了下载失败的问题。
具体操作:
- 在命令行中使用以下命令切换pip源
- 这种方式不仅适用于Python包的下载,还可以应用于其他需要访问外部资源的情况。
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
2. Faiss 查找相关性一般
问题描述:在使用Faiss进行相似性检索时,我们发现结果的相关性并不总是理想的,特别是当提供的文本数据与用户问题之间关联性不强时,检索结果的质量会显著下降。这会导致最终生成的回答缺乏针对性,无法满足用户需求。
解决措施:为了解决这一问题,我们首先确保提供给Faiss的数据与用户可能提出的问题紧密相关。具体来说,我们在数据收集和预处理阶段,严格筛选了与美食主题高度相关的文档。同时,在向量化处理时,进一步优化了文本的切分和嵌入策略,使得生成的向量更加精确地表示文本的语义内容。
优化策略:
- 数据筛选:仅选择那些与用户可能提出的问题直接相关的文本数据,从源头上提升检索结果的质量。
- 文本预处理:在向量化之前,对文本内容进行预处理,去除无关信息和冗余内容,确保向量表示的纯度。
3. Chunk Size 的设置问题
问题描述:在文本切分过程中,chunk size 的设置直接影响到模型的性能。如果 chunk size 过小,可能导致信息过于分散,影响上下文语义的完整性;如果 chunk size 过大,可能会超过嵌入模型的最大tokens限制,导致模型无法处理这些片段。
解决措施:为了解决这一问题,我们根据嵌入模型的最大tokens数来动态调整 chunk size。具体而言,使用以下经验值来设置 chunk size:
- 对于最大tokens为512的嵌入模型,建议将 chunk size 设置在200-400之间,以确保每个片段既包含足够的上下文信息,又不会超过模型的处理能力。
- 对于最大tokens为1024的嵌入模型,建议将 chunk size 设置在500-1000之间,允许模型处理更大的文本片段,从而在确保语义完整性的同时,最大化利用模型的处理能力。
调整方法:
- 根据不同的嵌入模型,通过实验调整 chunk size,观察模型性能的变化,最终确定最佳的chunk size范围。
- 在开发过程中,持续监控模型的处理性能和检索结果的质量,必要时进一步微调 chunk size。
项目总结与展望
项目总结
在本项目中,我们成功开发了一个基于RAG(Retrieval-Augmented Generation)技术的智能美食助手。该助手能够根据用户输入的问题,通过NVIDIA提供的AI模型进行智能检索和生成,并输出符合用户需求的美食推荐。同时,项目还集成了语音合成功能,使得用户不仅可以通过文本形式获取回答,还能通过语音形式获得更为生动的交互体验。
主要成果包括:
- 成功集成RAG技术:通过结合检索和生成的能力,系统能够从大量文本数据中找到最相关的信息,并生成自然流畅的回答。
- 高效的文本向量化与检索:利用FAISS向量存储实现了高效的文本检索,确保系统能够在大规模数据中快速找到匹配的内容。
- 语音合成功能的实现:通过Edge-TTS工具,项目实现了将文本内容转换为语音,增强了用户的交互体验。
- 用户友好的Web界面:利用Gradio构建了简洁易用的用户界面,使得智能美食助手可以方便地部署并供用户使用。
在项目实施过程中,我们也遇到了一些挑战,如环境搭建时的软件包下载问题、Faiss检索相关性不足的问题、以及chunk size设置的优化问题。但通过有效的解决方案,我们克服了这些挑战,成功交付了预期的功能。
项目展望
尽管本项目已经实现了预定的目标,并取得了一定的成果,但仍有一些方面可以进一步改进和扩展:
-
多模态集成:
- 视觉信息集成:未来可以将图像识别功能集成到系统中,使得美食助手不仅能处理文本数据,还能识别和推荐与用户上传的美食照片相关的菜品或餐厅。
- 视频推荐:集成视频内容检索,推荐与用户问题相关的美食视频,提高信息的丰富性和用户的沉浸体验。
-
模型性能优化:
- 更先进的嵌入模型:随着NVIDIA不断推出更为先进的嵌入模型,可以考虑在未来的项目中使用这些新模型,以提高检索和生成的准确性和效率。
- 动态调整算法:进一步优化检索和生成过程中的参数设置,如动态调整chunk size,以适应不同的应用场景和数据规模。
-
用户个性化推荐:
- 用户数据分析:通过分析用户的历史查询和反馈数据,系统可以学习用户的口味偏好,并提供更为个性化的美食推荐。
- 推荐系统集成:未来可以集成基于协同过滤或深度学习的推荐系统,进一步提升推荐的精准度和用户满意度。
-
语音交互的增强:
- 多语言支持:扩展语音合成的语言支持,覆盖更多的用户群体,使得美食助手能够为来自不同语言背景的用户服务。
- 对话式交互:在现有的问答基础上,开发多轮对话功能,使得用户可以与助手进行更自然、更连贯的交流。
通过这些改进,智能美食助手可以扩展到更广泛的应用场景,为用户提供更加全面、个性化的服务。同时,随着技术的进步和用户需求的变化,本项目有潜力发展成为一个多功能的智能助手平台,不仅限于美食推荐,还可以应用于其他生活服务领域。
附件与参考资料
2024 NVIDIA开发者社区夏令营环境配置指南(Win & Mac)使用 NVIDIA NIM 上的 Phi-3 构建一个语音机器人原型
NVIDIA 加速推理平台