这里写自定义目录标题
项目名称:AI-AGENT夏季训练营 — RAG智能对话机器人
报告日期:2024年8月18日
项目负责人:VanPCA
项目概述
随着AI领域的发展和信息量的爆炸性增长,人们通过自己处理庞大信息所需要付出的成本越来越高,而通过人工智能检索和处理信息便显得尤为重要。
然而目前的很多大语言模型会产生“幻觉”,导致针对自己数据库出发对问题的回答会出现错误。RAG的出现很好地解决了这个问题,支持通过自己输入相应地数据指导,改进对应的答案。
此外通过添加一些功能模块,可以实现不局限于对话和简单回答问题,在多模块领域如处理图像、制作表格等领域都可以做到一定程度的快速解决。
技术方案与实施步骤
模型选择
- 首先为了搭建对话机器人,我们需要前往NVIDIA-NIM(https://build.nvidia.com)上挑选适合自己的模型。
- 选择一个模型,例如 llama-3.1-405b-instruct。
- 进入之后会显示在不同语言和环境下的使用说明,我们使用的是python,因此选择python的指导。
- 我们使用模型时会需要用到NVIDIA账号的API Key,所以在这里我们点击Get API
Key来获取。当然你也可以在构建时再来获取,但是注意每次获取API Key都会变化,所以需要注意在代码中更改。
之后我们就可以在自己的项目中使用这个模型了。
数据构建
- 读入所需的数据。
- 进行数据的清洗工作,保证数据的有效性。
- 将数据处理到 faiss vectorstore 并将其保存到磁盘形成向量库。
功能整合
在对话和检索的基础上,通过构建多模态的AI-agent实现对图片和表格的自动操作和按要求修改。通过使用Microsoft Phi 3 vision来实现解析图片数据。具体来说,我们会需要一些辅助函数来对对数据进行一些预处理,调整成模型可以接受的形式之后再进行一系列指导操作。
实施步骤
环境搭建
由于使用的是python,所以自然需要安装python的环境。
conda create --name ai_agent python=3.8
conda activate ai_agent
pip install langchain-nvidia-ai-endpoints jupyterlab langchain_core langchain matplotlib faiss-cpu==1.7.2 openai
对于多模态内容,我们还需要安装额外的几个工具包:
- langchain_nvidia_ai_endpoint: 用来调用nvidia nim的计算资源
- langchain: 用来构建对话链,将智能体的各个组件串联起来
- base64: 因为本实验是构建多模态的智能体, 需要base64来对图像进行编解码
对于一些其他的库,则是看缺什么就补什么。
代码实现
获取文本数据集并进行处理
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)
documents=[d for d in data if d != '\n']
len(data), len(documents), data[0]
将数据保存到本地形成向量化存储
text_splitter = CharacterTextSplitter(chunk_size=400, separator=" ")
docs = []
metadatas = []
for i, d in enumerate(documents):
splits = text_splitter.split_text(d)
#print(len(splits))
docs.extend(splits)
metadatas.extend([{"source": sources[i]}] * len(splits))
store = FAISS.from_texts(docs, embedder , metadatas=metadatas)
store.save_local('./zh_data/nv_embedding')
重读存储的向量化数据并进行RAG检索
store = FAISS.load_local("./zh_data/nv_embedding", embedder,allow_dangerous_deserialization=True)
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()
)
多模态:辅助函数
对于多模态问题,我们需要预先准备一些辅助函数来对数据进行转化。具体来说需要编码为base64格式保证模型能够正确读入。
# 将 langchain 运行状态下的表保存到全局变量中
def save_table_to_global(x):
global table
if 'TABLE' in x.content:
table = x.content.split('TABLE', 1)[1].split('END_TABLE')[0]
return x
# helper function 用于Debug
def print_and_return(x):
print(x)
return x
# 对打模型生成的代码进行处理, 将注释或解释性文字去除掉, 留下pyhon代码
def extract_python_code(text):
pattern = r'```python\s*(.*?)\s*```'
matches = re.findall(pattern, text, re.DOTALL)
return [match.strip() for match in matches]
# 执行由大模型生成的代码
def execute_and_return(x):
code = extract_python_code(x.content)[0]
try:
result = exec(str(code))
#print("exec result: "+result)
except ExceptionType:
print("The code is not executable, don't give up, try again!")
return x
# 将图片编码成base64格式, 以方便输入给大模型
def image2b64(image_file):
with open(image_file, "rb") as f:
image_b64 = base64.b64encode(f.read()).decode()
return image_b64
多模态:定义多模态数据分析agent
定义了提示词模板,我们输入的图片会变为base64格式的string传输给它
将处理好的提示词输入给char_reading, 也就是大模型来进行数据分析, 得到我们需要的表格。
将模型处理好的table和提示词输入给另一个大模型llama3.1, 修改数据并生成代码
将生成的代码通过上面的执行函数来执行python代码, 并得到结果。
def chart_agent(image_b64, user_input, table):
# Chart reading Runnable
chart_reading = ChatNVIDIA(model="microsoft/phi-3-vision-128k-instruct")
chart_reading_prompt = ChatPromptTemplate.from_template(
'Generate underlying data table of the figure below, : <img src="data:image/png;base64,{image_b64}" />'
)
chart_chain = chart_reading_prompt | chart_reading
# Instruct LLM Runnable
# instruct_chat = ChatNVIDIA(model="nv-mistralai/mistral-nemo-12b-instruct")
# instruct_chat = ChatNVIDIA(model="meta/llama-3.1-8b-instruct")
#instruct_chat = ChatNVIDIA(model="ai-llama3-70b")
instruct_chat = ChatNVIDIA(model="meta/llama-3.1-405b-instruct")
instruct_prompt = ChatPromptTemplate.from_template(
"Do NOT repeat my requirements already stated. Based on this table {table}, {input}" \
"If has table string, start with 'TABLE', end with 'END_TABLE'." \
"If has code, start with '```python' and end with '```'." \
"Do NOT include table inside code, and vice versa."
)
instruct_chain = instruct_prompt | instruct_chat
# 根据“表格”决定是否读取图表
chart_reading_branch = RunnableBranch(
(lambda x: x.get('table') is None, RunnableAssign({'table': chart_chain })),
(lambda x: x.get('table') is not None, lambda x: x),
lambda x: x
)
# 根据需求更新
update_table = RunnableBranch(
(lambda x: 'TABLE' in x.content, save_table_to_global),
lambda x: x
)
# 执行绘制图表的代码
execute_code = RunnableBranch(
(lambda x: '```python' in x.content, execute_and_return),
lambda x: x
)
chain = (
chart_reading_branch
| instruct_chain
| update_table
| execute_code
)
return chain.invoke({"image_b64": image_b64, "input": user_input, "table": table}).content
项目成果与展示
在不使用RAG进行检索增强的情况下,对于提出的问题,可能会出现回答错误或是不能令人满意的情况。
但在添加了自己的数据进行指导之后,可以看到就能得到合理的回答。
由此我们可以看出通过RAG指导,AI对话机器人的效果能得到很好的改善。对此对于一些商品的信息查询甚至是一些新型产品的内容都可以得到很好的答案。
对于多模态,我们可以通过指导机器人在理解图片信息的基础上进行想要的改动,甚至可以在原有信息的基础上生成新的图片。
可以看到机器人可以输出图片中的信息。
之后我们尝试改动图片内容并生成新图片。
问题与解决方案
对于大语言模型,由于现在很多公司的开源,模型已经不再是阻碍项目开发的最大阻力。正如这次课程中所说,世界上的数据马上将不在足够,因此有效数据的获取便成了一大难题。
数据的准备不限于文本,还可以通过HTML网页、语音、图片等形式准备数据,对于不同的数据就需要不同的处理方式,本次实验只针对图片进行了探索,对于其他的数据类型处理还有很大的学习空间。
项目总结与展望
项目评估:项目在一定程度上实现了多模态的数据处理和分析,但除了图片外,语音等其他数据类型也应该纳入考虑范围内。
未来方向: 结合常见的数据类型,收集对应数据进行项目搭建,并研究如何能提升对话效果和效率。