💡前言
在日常工作中,企业会拥有企业的知识库,个人会拥有个人的知识库。但这些知识库沉淀多了,且如果不进行分类的话,久而久之,我们在查找问题答案的时候,就会发现「查找」这个事情,变得困难了许多。
基于此背景下,且在AI时代的爆发下,纷纷涌现出一些「智能问答系统」的落地方案。在下面的文章中,就将来聊聊,关于大模型在智能文档问答领域相关的一些落地方案。
以下文章整理自 稀土开发者大会2023·大模型与AIGC-掘金 第三部分,讲解 JINA AI
公司基于Langchain
和 Langchain-server
开发出来的一套智能文档问答系统。
一、📡背景介绍
首先来谈下这个项目的背景。JINA AI
作为开源软件的公司,维护了很多开源项目。那在此过程中,每天都会收到来自开源社区的大量问题,这间接导致工程师们每天要花很多时间去回答用户的问题。因此,基于此背景下,也就有了现在这个智能文档问答系统。
二、🔦文档问答系统基础
2.1 问题定义:文档问答系统
首先先简单看下整个问答系统的定义。
整个输入包含:用户的问题,还有完整的文档集合。而输出就是,问题的答案。
2.2 文档问答系统的算法范式
对于问答系统来说,本身在60年代的时候就开始有人在研究了。只不是以前可能用的是一些比较基础的方法,而在2017年以后,就引入了关于机器学习、深度学习和神经网络中一些比较典型的算法范式,来解决问答系统的问题。
那下面就来介绍整个问答系统用到的几种算法范式。
1、两阶段方法
在机器学习中,所谓的"两阶段方法",指的是一种处理复杂任务的方法论,分为两个阶段:召回阶段(Retrieval Stage)和阅读理解阶段(Reading Comprehension Stage)。
召回阶段(Retrieval Stage):
这个阶段的主要任务是从大量数据中找出与当前任务相关的数据。例如,如果你在搜索引擎中输入一个查询,搜索引擎首先会在其索引中查找与这个查询相关的网页,这就是一个召回阶段。在这个阶段,通常会使用一些高效的算法(例如 k-最近邻算法,TF-IDF加权等)来找出与当前任务(例如查询)最相关的数据。
阅读理解阶段(Reading Comprehension Stage):
这个阶段的主要任务是对召回阶段找到的数据进行理解和分析。例如,如果你在阅读一篇关于机器学习的文章,你可能会先阅读标题、摘要和段落的主题句,以大致了解文章的内容。然后,你会仔细阅读全文,以深入理解文章的主题和细节。这个过程可以看作是阅读理解阶段。
在机器学习中,阅读理解阶段通常涉及到对文本数据的深度理解和分析,例如命名实体识别(NER)、关系提取、情感分析等。这些任务通常需要更复杂的模型和算法,例如基于深度学习的模型和算法。
两阶段方法的好处在于它将复杂的任务分解为两个相对简单的子任务,这使得我们可以更有效地处理大量的数据和复杂的任务。同时,我们还可以使用各种不同的算法和技术来优化每个阶段的性能。
2、端到端方法
端到端方法(End-to-End Method)是一种在机器学习中常用的方法,尤其在处理自然语言处理(NLP) 和语音识别等序列到序列(sequence-to-sequence)的问题时。这种方法从原始输入到最终输出,将整个过程看作一个整体的、连续的流程,而不是分解为多个独立的步骤。
端到端方法的主要特点有以下几点:
- 整体性: 端到端方法将整个处理过程视作一个整体,从原始输入开始,经过一系列的处理和转换,到最后得到输出结果。这个过程被看作一个连续的、整体的流程,而不是一系列独立的步骤。
- 连续性: 在端到端方法中,输入和输出之间的映射关系是连续的。这意味着输入的微小变化可能会引起输出的微小变化。这种连续性使得端到端方法对输入的变化更加敏感。
- 自适应性: 端到端方法具有自适应性,能够自动学习和调整模型参数以适应不同的数据分布和任务需求。这使得端到端方法能够适应各种复杂的任务和环境。
- 学习能力: 端到端方法通过在大量数据上进行训练,能够自动学习和提取有用的特征。这使得端到端方法能够适应各种不同的任务,并且能够自动优化模型的性能。
- 端到端方法的缺点包括需要大量的数据和计算资源进行训练,以及可能出现的过拟合问题。然而,随着数据集的增大和计算能力的提升,端到端方法在许多领域取得了显著的成果。
总之,端到端方法是一种将整个处理过程看作一个整体的方法,它具有自适应性、连续性和学习能力等优点,被广泛应用于自然语言处理、语音识别、图像处理等领域。
3、生成式方法
第三种要介绍的方法是生成式方法(Generative Methods),生成式方法基于生成式模型,假设所有的数据都是由同一个潜在的模型"生成"的。
在生成式方法中,给定样本x,其真实类别标记为y,其中y属于所有可能的类别。生成式方法通过潜在模型的参数将未标记数据与学习目标联系起来,未标记数据的标记则可看作模型的缺失参数,通常可基于EM(Expectation-Maximization)算法进行极大似然估计求解。
生成式方法和判别式方法都用于无监督学习,两者的主要区别在于:生成式方法试图学习数据的潜在模型,而判别式方法则试图学习一个映射函数,将输入数据映射到输出标签。
生成式模型的应用范围很广,包括文本生成、图像生成、语音识别等。例如,在文本生成 中,生成式模型可以学习文本的统计分布,然后生成新的、与训练数据类似的新文本。在图像生成中,生成式模型可以学习图像的统计分布,然后生成新的、与训练数据类似的图像。
常见的生成式模型包括以GPT
为代表的生成式模型。
2.3 文档网站问答系统的难点
回到整个项目背景,我们希望的是自动化地为很多个文档网站,快速地去建立一个问答系统。但在开发过程中,会遇到以下几个难题:
三、⚙️大模型解决方案
3.1 算法解决方案
1、算法设计融合方案
前面讲到了三种解决方案和遇到的难点,那下面,就来讲解整个系统最终的算法设计。
基于上面谈到的3种算法范式,我们对其做了一个融合。
最终整体的设计链路 是:当用户的问题拿到之后,我们先做一个预估识别 ,判断用户是不是真正在提问问题。如果是的话,那么分两个链路去寻找答案,第一种 是从我们现在已有的问答库去找答案,第二种是通过问答系统的方式,去文档里面去找答案。
2、两种算法设计方式
(1)算法设计:从问答库召回答案
先来谈谈第一种算法设计:从问答库召回答案 。这种方法就有点像是兜底的 方法,就好比方说你有一个问答库,然后用户的问题和问答库里的问题已经用向量 表示,然后放进了向量索引里面,所以就直接做了向量的匹配。
这种方法特别适合去对一些高频的问题进行解答,且它的准确率也会非常高。
不过有个局限性 是,问答库里面的问题数量有限,所以会导致最终结果的召回率可能没有那么高。
(2)算法设计:从文档召回答案
第二种算法设计是:从文档召回答案。这种方法就是从文档里面去找到用户想要的答案。
在实现过程中,我们发现,向量匹配 和传统的关键词匹配 ,更多的是一个互补的关系。因此,我们是分了两条支路去处理。如下图所示:
3、如果对Prompt进行优化
最后一个要提到的是,如何去优化Prompt
。这里要提到我们做过的一个工具是,使用PromptPerfect
来优化提示词。一般是输入我们原始的Prompt
,然后再一轮一轮地,让AI模型 去帮助我们优化Prompt
。
3.2 工程解决方案
下面将讲解在大规模语言落地的时候,会遇到的一些问题。
1、MLOps和LLMOps
(1)从MLOps到LLMOps
MLOps
指的是用于管理ML驱动的 应用程序的生命周期,LLMOps
专门用于管理大型语言模型(LLM)支持的 应用程序的生命周期。下面我们要谈到的是LLMOps
这种解决方案。
一般来说,在调用大模型本身的过程中,它的开发工程量是很小的,需要的代码没多少。但是从模型开发 到模型上线 ,会有一个巨大的鸿沟。真正的所有AI应用 里面,所有跟模型相关的代码只占据5%,剩下95%都是工程在落地时的胶水代码。因为要为了去让整个模型能符合我们的业务逻辑和整个项目系统的链路,这过程就会产生很多胶水代码。
那在工程设计的时候,我们主要分为四个层面来设计:API层、控制层、运行时、存储模块。如下图右边区域所示:
(2)LLMOps
下面我们来谈谈这四个层面,具体的实现过程。
首先在控制器层,我们直接用了LangChain
来完成整个应用的逻辑开发。但是LangChain
只解决了逻辑层开发的问题,那其他模型部署,服务上线等等问题均未得到解决。
这时,JINA AI公司 开源了LangChain-Serve
,这套服务背后调用的是它们从2022年开始开发的一套框架Jina
。
Jina框架做的事情是,API层 使用了FastAPI
,FastAPI
能够快速地帮你提供API层 的实现。运行时选用的是Docker
。Jina
其实是把LangChain
里面的各种Chain
,翻译成Jina
中各种对应Executor
的模块,然后每个Executor
就可以放在一个Docker容器里面,单独地去运行和单独地去进行缩放。
在最下面的存储模块 中,也是用我们自己开源的项目,叫DocArray
。这个地方之所以我们不选用其他类似于向量数据库,主要是我们观察到,在我们当前的这种使用场景里面,我们有大量的项目要去构建服务,但其实每个项目里面,需要构建索引的数量特别小。比如把一个文档网站给爬下来,切完块,也就几千条几万条数据,基本用不上特别大的特别复杂的向量数据库。
然后对于DocArray
来说,它是针对多模态数据 的工具集合,然后呢,我们在这里面提供了关于向量索引方面几种简单的方案,然后主要是基于内存去进行的一个存储。
同时,我们为了保证整个服务是一个stateless无状态的 服务,所以我们还专使用了一个Docker Ready
给DocArray
提供了一个服务,这个服务是Docker Ready
特有的,就是可以把我的向量,直接Push
到S3
的存储里面。然后当服务出现问题,挂掉了,我还能从S3
里面再Pull
下来。
2、系统从开发到部署
了解了整个工程设计方案后,下面我们来看看,整个智能问答系统,从开发到部署的过程。
(1)使用LangChain开发问答系统
LangChain
本身就是针对大规模语言模型设计的开发语言框架,可以说它是专门针对问答系统量身定做的。它本身预置了很多问答系统预置的模块。
包括前面说到的DocArray
,也是在LangChain
的基础上来开发的DocArray
。所以我们基于LangChain
来搭建整个系统的开发周期都非常的短。
另外想说的比较好的一点是,LangChain
里面提供了比较好的Prompt管理 ,可以从LangChain
的源代码里面,去学习到很多Prompt
的使用经验。
(2)使用LangChain-serve部署服务
上面说到了用LangChain
来开发,那开发完了之后,我们就使用了LangChain-serve
来进行部署。如下图所示:
(3)私有化部署
最后一点是讲一下Jina AI Cloud
的实现。我们的用户每次想要去部署一个服务的时候,我们会在Amazon API Gateway
上面,去接受用户的请求,然后去调用对应的 lamda function
,以这种无服务化的形式,来调用用户的请求。
在起整个服务的过程中,时间是比较长的。在这里我们就用了Amazon Batch
,来完成这个工作。它背后会在我们自己或者客户的k8S集群里面, 去调用对应的operator
,然后把整个服务,给创建起来,最后对外暴露一个API
。
目前这整个Jina AI Cloud
的解决方案,是我们在做私有化部署时使用的一套方案。所以在做私有化部署的时候,就不会有这么多AWS
的依赖。然后用户完全就可以直接在自己的K8S
集群里面,完全地通过operator
的形式,就可以进行部署。
3.3 实现效果
最后来说下整套系统的一些优点:
- 低成本,能够快速地部署问答系统,不会单独对某个网站做特别的优化。
- 通过使用
LangChain
和Langchain-Serve
,整体的代码量也非常小,让整个系统的维护性提高了很多。 - 另外,系统都是在
k8s
上去进行部署的,整体稳定性和伸缩性都很高,部署的成本也相对较低。
四、🔍结束语
到这里,关于本文的讲解就结束啦!
在上面的文章中,一开始介绍了文档问答系统的三种算法范式,以及一些会遇到的开发难点。之后我们谈到了该系统的两种算法设计方式 ,从问答库召回答案 和从文档召回答案 。最后,我们从工程层面出发,去讲解了整个系统如何从0~1去进行开发和部署。
这篇文章基于小编对开发者大会相关模块的主题做了一些整理和输出,有可能存在部分内容还理解不到位,欢迎小伙伴们交流&勘误。salute~🍻