一起学-基于 NIM 建构多模态 AI-Agent(词典助手)

常看脚下 2024-09-15 08:31:01 阅读 87

文章目录

概要项目概述技术栈应用场景及亮点

技术方案与实施步骤模型选择:数据的构建:功能整合:

实施步骤环境搭建:代码实现:调试与优化

项目成果与展示应用场景展示:

问题与解决方案项目总结与展望项目评估:未来方向:

附件与参考资料

概要

NVIDIA AI-AGENT夏季训练营

项目名称:AI-AGENT夏季训练营 — RAG智能词典助手(多模态)

报告日期:2024年8月18日

项目负责人:赵冠齐

项目概述

技术栈

本项目基于NIM的模型接口,结合Langchain的链式编程,Gradio作为UI界面以及Funasr,Edge-tts等开源模型,实现了一套完整的RAG智能对话助手(多模态)

应用场景及亮点

本项目以构建一个智能词典助手为目标,通过多模态输入方式来实现查询词典的功能。

用户通过前端输入要查询的词语,可以是文字输入,或通过语音输入,甚至图片输入。

技术方案与实施步骤

模型选择:

本项目基于RAG构建,因此Embedding模型选择如下:

<code>NVIDIAEmbeddings(model="nvidia/nv-embedqa-mistral-7b-v2")code>

在vectorstores,向量存储时,使用工具为FAISS

from langchain_community.vectorstores import FAISS

在调用llm模型时选择如下,由于llama-3.1-405b是当前llama最新最大模型,多方面生成效果都很优秀,因此优先选择:

ChatNVIDIA(model="meta/llama-3.1-405b-instruct", nvidia_api_key=nvapi_key, max_tokens=512)code>

考虑到多模态输入,在读取图片信息的时候,ai-phi-3-vision-128k-instruct对于图像分析有专门的优化,整体效果很好,因此优先选择:

ChatNVIDIA(model="ai-phi-3-vision-128k-instruct")code>

考虑到多模态输入,在音频输入时,选择了开源模型Funasr,由阿里达摩院出品,因为本次查询词典场景更加偏向中文环境,因此选择了对于中文能力更加优秀的Funasr,而不是Whisper。同时Funasr作为开源模型,可快速本地部署,对于低成本的测试也很友好:

from funasr import AutoModel

考虑到多模态输入,在音频输出时,选择了开源模型Edge-TTS,由微软出品,

同样可快速本地部署,节约成本。(由Day2培训时,老师推荐,因此就拿来测试了)

import edge_tts

数据的构建:

因为应用场景为查询词典助手,因此我选择了《现代汉语词典》(第7版)的txt文本作为数据源。以下为数据源来源:

https://github.com/CNMan/XDHYCD7th?tab=readme-ov-file

文本效果如下:

词典文本

优化: 拥有数据源后,需要进行数据的embeding以及后续的向量化存储,再此之前,我们先要对数据进行一定的预处理,优化他的存储结构,才能在后续的向量化检索中得到更加优秀的表现:

数据切分

在上图中我将文档按照规则切分,使词典中每一个词语作为个单独的数据块,并提取词语本身作为title,提取数据所在文件路径作为sources。

数据向量化

在上图中我们在切分文本后,考虑到模型的最大输入512,因此需要再次对文本进行大小上的限制二次切分,并将切分结果,结合之前的元数据,一起封装为可以直接embeding的document格式。

使用FAISS工具调用embeding模型将数据向量化并存储在本地作为向量库。

注:由于整个词典切分后块数过多,导致embeding时间过长。所以可以适当的减少文档中的数据条数

功能整合:

语音输入:

语音功能的实现策略参考了一下项目作为框架:

https://github.com/Ikaros-521/voice_talk_chatgpt

替换了其中的模型以及模型调用的方式。

方案流程为

前端输入录音文件,或直接语音输入。后端通过读取音频文件,调用Funasr模型来转化为文字。将文本结合Prompt,调用LLM模型来进行回答。后端获取回复后调用Edge-TTS模型将文本转化为语音输出。最终在前端呈现。

图片输入:

图片功能的实现,参考了培训day3的multi_agent_demo.ipynb作为框架:

替换了其中的模型以及模型调用的逻辑。

方案流程为

前端输入图片。后端读取图片,调用模型来解释图片中的物品信息(返回为英文)调用LLM模型来翻译物品信息(转为中文),获取文本。调用本地向量库进行搜索匹配数据,将匹配数据结合Prompt调用LLM模型进行总结,整合,润色。

实施步骤

环境搭建:

python环境推荐3.8+(本人使用的3.10)

其他库如下

<code>gradio

openai

colorlog

scipy

langchain-nvidia-ai-endpoints

langchain_core

langchain

langchain-community

matplotlib

faiss-cpu

openai

# Funasr包含后续调用中使用的推理模型,切分模型等安装

Funasr

edge_tts

部署内容:

注册获取NIM平台,并获取模型API KEYRag的数据向量化及本地向量库存储部署Funasr及Edge-TTS模型部署Gradio及相关配置

代码实现:

整个数据向量化及存储过程已在前面的章节展示了。

音频处理的核心代码如下图:

def send_msg(audio, text):

try:

# 创建一个临时文件来存储录制的音频数据

output_file = "out/" + common.get_bj_time(4) + ".wav"

if text:

stt_ret = text

if audio:

# 创建一个示例的 int16 音频数据

int16_audio_data = np.array(audio[1], dtype=np.int16)

# 使用 scipy.io.wavfile 将数据保存为 WAV 文件

wavfile.write(output_file, audio[0], int16_audio_data)

res = model.generate(output_file)

stt_ret = res[0]['text']

logging.info(f"语音识别内容:{ stt_ret}")

# 数据回显

text_input.value = stt_ret

'''

llm调用stt_ret

返回值chat_ret

'''

chat_ret = chain1.invoke(stt_ret)

logging.info(f"对话返回:{ chat_ret}")

audio_path = './'+change_wav_to_mp3(output_file)

communicate = edge_tts.Communicate(chat_ret, VOICE)

communicate.save_sync(audio_path)

logging.info(f"合成音频输出在:{ audio_path}")

return audio_path, chat_ret

except Exception as e:

logging.error(f"Error processing audio: { str(e)}")

return None

Funasr的模型配置组合如下:

model = AutoModel(model="paraformer-zh", vad_model="fsmn-vad", punc_model="ct-punc")code>

Prompt及chain的构建如下:

prompt1 = ChatPromptTemplate.from_messages(

[

(

"system",

"You are a dictionary assistant, capable of helping answer questions"

" related to the explanations of characters and words. Answer solely "

"based on the following context:{context}",

),

("user", "{question}"),

]

)

chain1 = (

{ "context": retriever, "question": RunnablePassthrough()}

| prompt1

| llm

| StrOutputParser()

)

图片处理的核心代码如下:

def chart_agent_gr(image_b64, user_input):

embedder = NVIDIAEmbeddings(model="nvidia/nv-embedqa-mistral-7b-v2")code>

folderpath="./data/"code>

store=FAISS.load_local(folderpath+"/nv_embedding", embedder,allow_dangerous_deserialization=True)

retriever = store.as_retriever(search_type="mmr",search_kwargs={ 'k':10,'fetch_k':100})code>

image_b64 = image2b64(image_b64)

# Chart reading Runnable

chart_reading = ChatNVIDIA(model="microsoft/phi-3-vision-128k-instruct")code>

chart_reading_prompt = ChatPromptTemplate.from_template(

'Tell me the objects recognized based on the following image data, only respond with nouns, do not describe, : <img src="data:image/png;base64,{image_b64}" />'code>

)

chart_chain = chart_reading_prompt | chart_reading

translate_llm = ChatNVIDIA(model="meta/llama-3.1-405b-instruct")code>

translate_prompt = ChatPromptTemplate.from_template(

'Only translate the following English words into Chinese, do not reply with any other description:<content {content}>'

)

translate_chain = translate_prompt | translate_llm

instruct_chat = ChatNVIDIA(model="meta/llama-3.1-405b-instruct")code>

prompt1 = ChatPromptTemplate.from_messages(

[

(

"system",

"You are a dictionary assistant, capable of helping answer questions related to the explanations of characters and words. Answer solely based on the following context:\n<Documents>\n{context}\n</Documents>",

),

("user", "{question}"),

]

)

chain1 = (

chart_chain

| RunnableLambda(print_and_return)

| translate_chain

)

chain2 = (

{ "context": retriever, "question": RunnablePassthrough()}

| RunnableLambda(print_and_return)

| prompt1

| instruct_chat

| StrOutputParser()

)

return chain2.invoke(chain1.invoke({ "image_b64": image_b64}).content)

调用chain1来解读图片内容,并翻译图片内容从英文–>中文。

调用chain2来搜索本地向量库并将匹配内容交给LLM进行最终总结。

调试与优化

整个项目的多模态由于时间问题,拆分为了音频搜索,和图片搜索两个页面,独立功能都可以正常运行。

但没有来的及整合在同一页面下,完成最终的集成测试。

项目成果与展示

应用场景展示:

首先对比在没有使用RAG的方案下,大模型本身对于汉语生僻字查询的能力:

模型

请添加图片描述

是否正确?我们看一下正确答案

请添加图片描述

显然大模型没有正确回答问题,而且给了我们错误的导向

再来看一下加入了RAG方案的问答场景(首先是文字输入):

请添加图片描述

可以看到,在调用了同一个模型的情况下,RAG明显回答正确,并且还有语音播报。来看一下后台的日志:

在这里插入图片描述

程序将模型调用后的返回文字保存为out目录中的3.MP3

好的,我们来继续展示音频输入的效果如何:

请添加图片描述

首先我来对着麦克风说一段话,说了什么呢。在前端还在等待最终输出的时候,我们可以看一下后端服务打印的日志来理解,录音上传后的中间过程

请添加图片描述

程序成功的识别了我的录音内容,并将模型调用后的返回文字保存为out目录中的4.MP3

看来过程很顺利,这时,前端应该也开始播放我的搜索结果了:

请添加图片描述

好的,我们打开图片输入的页面(时间问题,两个输入模式没有统一在一个页面中):

请添加图片描述

那么识别什么呢,好像刚刚结束的奥运会,我们表现得还不错

请添加图片描述

看了下,好像跑通了,但是结果却没找到奥运五环。我们来看下后台的日志

请添加图片描述

可以看到,图片识别出了奥运五环,但是在向量库匹配时却只能找到奥运会的解释。所以最终输出没有对于奥运五环的解释。

那这样的搜索结果对么?去看看材料原文是什么样的:

请添加图片描述

看来RAG没有骗我,原文中确实没有奥运五环的解释。

问题与解决方案

问题:首先,基于我们效果展示的最后一个案例。在没有得到确切答案时,模型最终回复了没有找到答案。

解决方案:这样的回复显然不是很好,我们可以在PE工程中,添加对于没有找到答案的相应处理方式,再告知没有准确答案的同时可以推荐一些意思相近的词语的解释,或是跳过向量库提供的材料,自己回答。

这需要我们提供一套完备的prompt模板以及多次测试(尚未完成)

问题:对于文本过大时,切分策略值得我们思考,如何能让切分的效果最佳,不截断原来文本连贯的意思。同时保证搜索时,能返回切分后的多个文本块。

解决方案:这需要我们深入理解业务场景下文本的结构,在切分同时,我们可以提取所切文本的抽象信息,如标题,其中的关键字啊,所属段落啊等等来构成元数据metadata,再从搜索时加入一些元数据的匹配策略,从而更提高召回质量

项目总结与展望

项目评估:

项目整体完成了对于本次培训内容的实际应用以及场景构想。

成功的让我接触了很多以往没有测试过的模型。

熟悉了langchain的基于chain的快速搭建流程(熟悉了以后确实很方便)

实现了文字,图片,语音等多模态输入方式。

未来方向:

由于培训时间有限,对于RAG的很多内容,止步于搭建和测试。在实际中如召回的优化,数据的清洗,向量化等多个环节的优化讨论是缺少的。

因此,未来希望更多的从RAG的能力深入到RAG的质量。不断地优化,提高正确率。

附件与参考资料

https://github.com/CNMan/XDHYCD7th?tab=readme-ov-file

https://github.com/Ikaros-521/voice_talk_chatgpthttps://github.com/rany2/edge-ttshttps://github.com/modelscope/FunASR/tree/mainhttps://build.nvidia.com/explore/discoverhttps://python.langchain.com/



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。