ErnieRAG:用ErnieBot打造企业级RAG应用
项目背景
随着人工智能技术的深入发展,大语言模型(Large Language Model,LLM)已如狂潮般席卷了各行各业,其在自然语言处理、计算机视觉、推荐系统等诸多领域展现出了惊人的实力。然而,当面对特定行业或专业领域的深入应用时,通用大模型往往会遇到专业知识不足的挑战。针对这一问题,相对于成本高昂且效果不稳定的SFT或Lora微调方法,基于检索增强生成(Retrieval-Augmented Generation,RAG)的技术方案正成为越来越多企业的优选。
RAG架构是一种创新性的解决方案,它通过将大模型的生成能力与外部知识库的检索能力相结合,实现了在回答问题或执行任务时能够引入最新、最准确的专业知识。具体而言,当用户提出问题或需求时,RAG架构首先会在外部知识库中进行向量搜索,通过语义相似度匹配找到相关的专业知识,然后再将这些知识融入到大模型的生成过程中,从而得到更加专业、准确的回答或结果。
本文将基于文心大模型Erniebot和Milvus向量数据库,从零开始搭建一个通用的RAG企业级应用。
项目演示
RAG原理
如上图所示,我把一个RAG拆成了知识数据流
和用户数据流
。首先,先讲解知识数据流。
- 知识数据流:首先需要准备行业知识文档,可以是word、pdf、excel等容易被编程语言读取的格式文件,因为文档都是连续的长文档,为了匹配和计算方便,需要对长文本进行分块,分成若干个短文档,文本分块有自动分块(参考langchain的文本处理和截断),也有人工分块(工作量大,但因为人工对语义分块有优势,后期输出效果会更好),本项目采用人工分块的方式,提前将回答整理成了逐行的文档。分块后,将若干分块分别输入embedding模型中进行编码,将分块原文本存入关系型数据库,embedding编码向量存入向量数据库(本文为了方便演示,直接将原文本存入向量数据库内,但产业落地时建议将原文本存入关系型数据库,用id进行映射对应,获得更快的读取速度和方便知识管理)。
- 用户数据流:用户输入一个问题,对用户的问题进行embedding编码得到语义向量,将向量与向量数据库里存的数据向量进行计算比对,取相似度最大的值,通过id对应找到知识原文本,再将用户问题与知识原文本交给大语言模型进行理解、提取、修饰等二次加工,输出专业准确的回答。
环境安装
RAG应用环境主要包括两部分,一部分是大模型LLM,即文心Erniebot;另一部分是向量数据库,在这个案例中我们使用Milvus的轻量级版本Milvus Lite
,方便在Jupyter notebook中给大家运行展示。
In [ ]
!pip install "milvus[client]" --user
!pip install --upgrade erniebot --user
Erniebot配置
请注意,这一步需要把erniebot.access_token
替换成自己的Token。
Token获取链接 -> 注册用户免费领取百万Tokens
In [3]
# 如遇到No module named 'erniebot'报错,请先重启内核。
import erniebot
erniebot.api_type = "aistudio"
erniebot.access_token = "bb4a0232d99ebf32a24c76d2b3bb60xxxxxx"
Embedding模型
Embedding是将自然语言转换为计算机能理解和处理的编码过程,这里采用在线的文心百中语义模型 - ernie-text-embedding
作为Embedding模型对文本进行编码。这时可能就有很多人会问,为什么不用word2vec
之类的小模型来处理?这里我给大家从以下几点解答:
word2vec
大部分的模型都是基于英文语料训练的,对中文支持不够。word2vec
的训练语料和模型参数规模都很小,语言理解能力不够。word2vec
极少能支持同义理解、近似表达等,如世界最高的山脉
和珠穆朗玛峰
,word2vec
的计算结果就认为这是两个名词。word2vec
不支持问答匹配,如世界第二高的山脉是哪一个?
和乔戈里峰
,word2vec
计算出来的相似度极低。
与之相对,这里选择文心百中
语义模型具有以下特点:
- 文心百中源自文心千亿参数模型的语义理解能力,单模型刷新60+中文NLP任务基准,并曾登顶SuperGLUE全球榜首。
- 大模型具备强大的小样本能力、优化门槛低,行业迁移所需的标注数据量少,部分场景无需优化即可上线。
In [ ]
def embedding(text):
response = erniebot.Embedding.create(
model="ernie-text-embedding",
input=[text])
return response.get_result()
# 测试效果
embedding("你好,世界")
文本生成模型
文本生成这里我们选择了ernie-4.0
作为演示,企业可根据自己的需求、效果要求以及预算,采用其他例如ernie-3.5
,ernie-turbo
或者ernie-longtext
模型。
In [3]
def chat(text, temperature=0.9, top_p=0.9):
response = erniebot.ChatCompletion.create(
model="ernie-4.0",
messages=[{"role": "user", "content": text}],
temperature=temperature,
top_p=top_p)
return response.get_result()
知识库数据准备
在本案例中使用了某保险公司的一个问答数据集,为了测试方便,我们这里仅取了10条进行批量插入和问答演示。
In [1]
import pandas as pd
data_df = pd.read_csv("baoxianzhidao_filtertest.csv", encoding="gbk")
reply = data_df.iloc[:, 0]
for rly in reply:
print(rly)
您好,这个是重疾险中给予投保者的一项权利,安*长青树保障责任规定,投保者可以享受多次赔付,豁免等权益。也就是说不同轻症累计5次赔付,理赔1次轻症豁免后期所交保费,人性化的设计,无需加保费。 您好,HUTS保险中的乐游全球(探亲版)-慧择旅游保险澳新计划是澳*新西兰探亲专属保障,承保年龄可达90周岁,含有50万高额医疗保障,完全满足境外医疗保障需求,需要注意的是这款产品仅承保出行目的为境外探亲的人群,理赔时需提供相关签证或亲属关系证明等 您好,水上运动比赛,尤其是带有奖金的比赛一般承保的公司比较少。不过,HUTS保险中的众行天下-水上运动保险赛事版B就是适合帆船等水上比赛的产品,含户外溺水保障,是水上运动专属定制的保障,意外住院有津贴,保障期限灵活可选,还可以投保有奖金的赛事,您可以根据情况看看。 您好,端午出行的人比较多,而且自驾存在一定风险,所以有保险意识还是很好的。考虑到价格以及保障内容等相关因素,您可以看看HUTS保险中的畅玩神州-慧择旅游保险计划三,适合驾驶私家车走南闯北国内旅游,自驾意外累计赔付,承保的范围也较为广泛,适合带家人出游,保障全面,三天仅需75元,性价比还是蛮高的。 不到10块钱……………… 您好,赛龙舟是一项比较传统的活动,很有意义。不过由于是在水上活动,建议安全保障还要做足,HUTS保险中有针对水上运动风险特别定制的众行天下-水上运动保险,可以针对这种赛事进行保障,含有意外住院有津贴以及一系列保障,性价比较高,关键是费用也比较实惠。 您好,这点是不用担心的。投保后保险公司会在约定的保险费交纳日从消费者购买时填写的银行账号中划扣当期应交的安*长青树重疾险的保险费,所以您老婆是不用亲自去保险公司缴费的。 您好,是可以的。该医疗垫付目的便是解决投保者的切实困难,可以说是很人性化的一项保障了。在以下几个情况下可以申请医疗垫付:①发生重大意外或突发急性病;②所投保保险涵盖该医疗费用补偿责任;③医疗费用高客户无法承担 您好,去北极探险本身就存在一定的风险,建议选择专业的装备以及在专业人士的陪同下进行。至于保险,市面上关于此类的保险并不多,不过HUTS保险中却有一款专门针对南北极旅游的定制产品,保障内容充足,户外伤害、医疗保障甚至的紧急救援都具备,详情可以多了解下。
启动向量数据库
请注意只能启动一次,重复启动会报错,如需重启数据库,请先重启环境。
In [5]
from milvus import default_server
from pymilvus import connections
try:
default_server.start()
except:
default_server.cleanup()
default_server.start()
connections.connect(host='127.0.0.1', port=default_server.listen_port)
创建数据表
创建一个数据集合(可以理解为Mysql里面的数据表),里面包含三个字段,分别是answer_id
(自增数据ID)、answer
(回答文本)以及最重要的answer_vector
(回答文本的对应向量),使用文心百中语义模型,其输出的数据维度为384,如果是其他模型,请遵循其他模型对应的输出维度。然后把这个集合记为"qadb"。
In [6]
from pymilvus import utility, Collection
from pymilvus import CollectionSchema, FieldSchema, DataType
answer_id = FieldSchema(
name="answer_id",
dtype=DataType.INT64,
is_primary=True,
auto_id=True
)
answer = FieldSchema(
name="answer",
dtype=DataType.VARCHAR,
max_length=1024,
)
answer_vector = FieldSchema(
name="answer_vector",
dtype=DataType.FLOAT_VECTOR,
dim=384
)
schema = CollectionSchema(
fields=[answer_id, answer, answer_vector],
description="vector data"
)
collection_name = "qadb"
Collection(
name=collection_name,
schema=schema,
using='default',
shards_num=2
)
collection = Collection("qadb")
创建索引和加载数据库
创建集合后,还需要对该集合的answer_vector向量列添加索引,以进行后续的向量运算。这里使用欧氏距离和IVF_FLATIVF_FLAT
算法对向量建立索引。
In [7]
index_params = {
"metric_type":"L2",
"index_type":"IVF_FLAT",
"params":{"nlist":1024}
}
collection.create_index(
field_name="answer_vector",
index_params=index_params
)
utility.index_building_progress("qadb")
collection.load()
插入数据
数据库、数据集合和索引都建立之后,我们就可以往里面插入数据。
In [8]
import time
for rly in reply:
rlyEmbedding = embedding(rly)
data = [
[rly],
rlyEmbedding
]
collection.insert(data)
time.sleep(0.5) # 加延时,防止embedding接口超QPS
检索数据
数据插入完成后,我们尝试从数据库里面检索数据,观察问题和检索结果是否有一定的关联度。
In [9]
question = "假期出行买哪种保险好?"
qEmbedding = embedding(question)
search_params = {
"metric_type": "L2",
"offset": 0,
"ignore_growing": False,
"params": {"nprobe": 10}
}
results = collection.search(
data=qEmbedding,
anns_field="answer_vector",
param=search_params,
limit=1,
expr=None,
output_fields=['answer'],
consistency_level="Strong"
)
answer = results[0][0].entity.get('answer')
answer
'您好,端午出行的人比较多,而且自驾存在一定风险,所以有保险意识还是很好的。考虑到价格以及保障内容等相关因素,您可以看看HUTS保险中的畅玩神州-慧择旅游保险计划三,适合驾驶私家车走南闯北国内旅游,自驾意外累计赔付,承保的范围也较为广泛,适合带家人出游,保障全面,三天仅需75元,性价比还是蛮高的。'
从上面的回答中,根据用户“假期出行”的信息,向量计算后从数据库匹配到了一条跟假期出行保险相关的回答,但回答是端午出行的,而用户的提问是假期出行,虽匹配但明显不合语义,那我们应该怎么将数据库的答案变成更贴合用户问题的回复呢,这时候我们就用文心4.0对用户问题进行分析和对答案进行二次加工处理输出。
结合Erniebot问答
将检索结果输入Erniebot处理,让文心大模型对文本进行提取、修饰和补充,得到更加专业、准确的回答或结果。
In [10]
erniebotInput = "使用以下文段来回答最后的问题。仅根据给定的文段生成答案。如果你在给定的文段中没有找到任何与问题相关的信息,就说你不知道,不要试图编造答案。保持你的答案富有表现力。用户最后的问题是:" + question + "。给定的文段是:" + answer
chatResult = chat(erniebotInput)
chatResult
'假期出行买HUTS保险中的畅玩神州-慧择旅游保险计划三比较好。这款保险产品适合驾驶私家车国内旅游,提供自驾意外累计赔付,承保范围广泛,非常适合带家人出游,保障全面,而且价格合理,三天仅需75元,性价比较高。但请注意,每个人的具体需求和情况可能会有所不同,所以在购买保险时,最好还是根据自己的实际情况来选择。'
从上面的回复可以看出,经过文心4.0处理后,能够把原数据库答案里的产品信息进行提取,并结合用户的问题进行了二次加工生成了合适贴切的回答。
Gradio应用部署
本文开头所示的Gradio应用已经打包在本项目根目录下的app.gradio.py
文件内,大家可按照aistudio应用部署的方法进行在线部署,也可下载文件到本地进行本地运行。
具体步骤如下:
- 编辑器右上角找到
部署
按钮
- 选择
Gradio部署
- 填写应用信息,执行文件选择
app.gradio.py
,部署环境选择CPU
即可,最后点击部署,接下来耐心等待部署完成。
总结
本文介绍了如何使用ErnieBot和Milvus向量数据库打造企业级RAG(检索增强生成)应用。RAG架构通过将大模型的生成能力与外部知识库的检索能力相结合,实现了在回答问题或执行任务时能够引入最新、最准确的专业知识。文章详细阐述了RAG的原理和环境安装,包括ErnieBot的配置和Embedding模型的选择。同时,还介绍了文本生成模型的选择、知识库数据的准备、向量数据库的启动、数据表的创建、索引和加载数据库、插入数据以及检索数据等步骤。最终,通过检索数据,验证RAG应用的有效性。该方案为企业提供了一种大语言模型落地企业应用的解决方案,能够帮助大语言模型更好地应对特定行业或专业领域的深入应用挑战。