


























自ChatGPT诞生以来,各个企业都开始尝试引入LLM落地实施“智能”应用,而目前并没有太多文章系统地介绍应该怎么落地实施一个基于LLM的应用,到底应该做哪些步骤。本人从2023年12月份开始,陆陆续续开发了3个LLM应用的项目了。这几个项目都是会话型的应用,都借助了LLM的能力,所以想趁着记忆还算新鲜,来总结一下这类项目的一些落地实施经验。最后面我会以最近的一个项目做的事情来作为案例,供大家学习和探讨。
在了解怎么去落地实施LLM应用前,我们最好先看看它一般长什么样。LLM的能力本质上可以算是传统NLP任务的升级版,在问了LLM传统的NLP(Natural Language Processing,自然语言处理)任务按照应用场景划分有哪些之后,它给了我一个比较完善的答案:
我稍微进行了整理:
可以看到前面几类场景我都给加上了“任务”二字,这里可以引出我对于LLM应用的分类,分别是会话型应用和任务型应用。会这么划分也是因为在这些项目上见识到了会话型应用的“复杂性”,在落地实施项目前,对项目复杂性的理解也能帮助我们去规划和设计我们的项目。
我对于任务型LLM应用的定义是:该应用没有一个用户可以对话的入口,应用的输入来自系统数据。
这类应用的基本特征包含:
我做过的比较典型的该类应用大多是和信息抽取相关的,比如:
我对于会话型LLM应用的定义是:该应用的唯一入口是聊天框,应用的输入来自用户的任意文本。
这类应用的基本特征包含:
而会话型LLM应用也可以继续划分成3类:
下面这张图就是我对应用分类的总结:
前面对于一个应用的分类是从应用场景的角度出发的,虽然应用场景能部分决定复杂度,但是我们还是需要一个更清晰的视角来评估我们应用的复杂度。
OpenAI在2024年7月的时候给AGI划分了5个阶段:
并且认为自己目前处于1阶段且靠近2阶段的地方。
显然,这个是对于AGI的定义,而我们要开发一个LLM应用的话,基本上会聚焦到某个具体的场景上。所以划分的方式也得做相应的调整。首先是4、5阶段不属于LLM应用的关注范围,所以我们会将视角限制在前三个阶段。而对于Agent,它能变得很复杂,所以我在复杂性划分上,会将Agent分成简单Agent和复杂Agent。最终我梳理后的复杂度划分成了4级:
下面我将以知识问答的场景,分别来说一下L1-L4的应用会长什么样:
不提供任何额外的知识,纯依靠模型自身的能力进行问答,模型有可能进行过垂直领域的微调:
引入了RAG,通过知识库补足模型的知识欠缺,避免幻觉。
引入了根据目标进行反思的能力,在RAG中,需要Agent判断用户的问题是否清楚,否则需要进行澄清;也需要判断RAG的检索是否找到能回答的知识,否则再次进行检索。
引入长期的记忆,可以根据用户的习惯,判断其想表达的意思来主动消除用户问题中的歧义;引入规划,能在多个知识库之间规划先去哪个知识库找数据,再根据找到的数据从哪些知识库找详情数据,再...
continue在这篇博客中提到了用户体验风险的概念,这个概念对于我们如何去考虑一个LLM应用是否会失败(成功如攀登高峰,每一步都至关重要;失败似堤坝溃决,一处漏洞足以致命)很重要。
下面是用户体验风险的公式:
先从错误影响说起,在一个LLM应用中,错误影响往往是固定的,取决于使用场景。
代码补全就是一个错误影响比较小的好例子,因为代码补全的用户是开发,开发本身对于代码的掌控程度是很高的,对于生成出的代码有那么一点小错误,开发也可以很好地定位错误并解决。
而面向业务人员的Text-to-SQL就是错误影响很大的例子,业务人员压根不知道SQL是否正确,如果不是返回的结果有数量级上的差错,那么业务人员将很难看出错误,以错误数据作为判断的依据就会产生很糟糕的结果。
所以,在错误影响比较高的情况下,要重点降低任务失败的概率,只要任务不失败(结果错误),就不存在错误影响了。这个时候牺牲一点执行时间也是值得的。
如果错误影响比较小,那么就可以权衡一下任务失败概率和任务执行时间,看什么情况下这个公式的风险值最小,还是拿代码补全来说,这个场景下,任务执行时间就很关键,我们需要让用户快速得到补全代码,如果不对,快速换下一个结果,一部分的错误是可以接受的。
在确定并分析完场景之后,接下来讲一下L3级LLM应用的构建方式(L3级应用能涵盖L2、L1的构建要点,而L4级目前感觉模型能力还不够,也是我没有落地过这个级别的应用,就不瞎说了)
如上图所示,第一步就是确定架构,这里的架构主要指的是LLM应用的流程架构,确定流程架构后就知道什么地方需要进行知识检索,知识检索的形式也确定下来了,就可以进行知识工程的开发;同时也知道了Agent开发的关键点有什么,可以进行Agent开发了;还能知道我们需要对哪些步骤进行评估,以及评估的指标是什么,确定评估体系了。
和传统机器学习、深度学习项目不同的是,测试集的构建并不一定要在一开始就构建好(当然如果有那肯定更好),我们可以通过LLM先生成一大堆的数据,对这些数据进行小小的修改就可以作为测试集了。能这么操作的原因是LLM本身的能力已经很强了,尤其是像意图识别这种任务,除非涉及到的意图太多了,不然它的准确率都能在95%以上。既然它准确率能这么高,我们为啥不让它来帮我们构建测试集呢。我们只需要抛出问题就行了,答案能自动收集起来,最后标上对错即可。这个思想和标注工具中常见的借助模型进行预标注的概念很像。
有了测试集后,我们就可以依靠测试集的测试结果,来改进我们的系统了,知识不足就补知识,prompt不足就调prompt,模型不足就微调优化,架构需要调整就进行调整。这就是一个迭代优化的过程,和传统机器学习或深度学习的模型开发就很像了。
之后就和普通的应用开发一样,先经过UAT小批量用户的验证后最终上线,上线后需要定期回顾生产数据,补充进测试集。
在我做过的这么多LLM项目中,常见的错误主要来自以下几种:
据此,我总结了一下LLM应用最重要的3个能力,分别是架构、知识、模型:
LLM应用的架构和传统应用的架构不太一样;前面已经说了,主要是做任务拆解和检索增强。
上图是我对好几个项目进行总结后,提炼出来的一个比较通用的架构。
在任务拆解部分,一般会分成多个Agent:
对于检索增强部分,网上已经有大批大批的资料专门介绍了,这边就不展开讲了。主要就是关键字提取、多路检索、重排序、RAG Agent等,根据项目的复杂度,选择具体要用的技术即可。
对于知识工程方面,可以从数据原始的结构形式出发
上面的这些方法可以不用都使用,根据具体的场景、数据,选择合适的使用即可。
针对Prompt优化部分,也都是一些老生常谈的东西了:
针对模型微调主要分两部分:
一切优化的前提都是有评估指标。没有评估指标,你都不知道你的应用什么时候才算做完:写了个简单的RAG,找了几个问题测试了一下,发现效果还不错,这样的应用可用么?显然是不行的,我们需要有量化的指标来判断咱们得RAG系统到底是否可用.
我针对上面列出来的任务,都给定了评估指标:
和传统的机器学习、深度学习一样,LLM应用也是需要将每次运行的实验都记录在案的,MLflow就是一个很不错的工具,可以帮助我们把Prompt、Temperature、Top P等参数以及实验运行的结果都记录下来。下图就是MLflow某个实验的多次运行结果的截图,每次运行的参数、要监控的指标都可以在总览中很直观的看到,每次运行时的测试数据和Prompt在详情页中也会记录:
接下来我会从一个真实的案例出发,过一遍整个LLM应用构建的流程。
第一步是做用户访谈和调研,收集用户的需求和痛点。上面就是我们针对这次的业务用户进行访谈后收集到的需求。将需求和场景做一下映射就得到了上图。
接下来就是分析应用类型。由于是直接面向客户经理的,所以这俩都属于会话型应用,政策文档问答明显属于问答型,Text-to-SQL则属于任务型。
经过前期的需求确认,政策问答不需要做得特别复杂,文档数量比较少,内容也都比较有结构,所以定在了L2级别。而该场景的Text-to-SQL涉及到了大量的业务、概念,数据也很复杂,定制的规则也很多,所以定在了L3级别,而且其业务价值更高,所以属于本次的重点。
首先还是从错误影响开始分析,政策文档问答的错误影响属于中等,通过返回引用内容,可以将错误影响下降到低。所以只需要平衡好响应时间和错误率即可。
而Text-to-SQL的错误影响很高,由于这是面向客户经理的,他们看不懂SQL,如果SQL错了,返回了一个差不多数量级的值,他们就会产生错误的判断,对业务影响比较大。第一步就是想办法降低错误影响,能想到的一个解决方案就是让LLM来解释生成的SQL,现有的LLM对于解释SQL还是比较在行的。这样就把整体的错误影响降低到了中,甚至是低。只有在解释SQL的LLM出问题的时候,才会产生比较高的错误影响。
然后就是本次场景下的Text-to-SQL错误率会很高,原因就是前面提到的那些复杂性。所以本次项目的重点还是落在降低错误率。除了通过上一节提到的手段来降低错误率,还可以使用一些工程手段来降低。比如将数据合成一张大宽表来比较模型做过多的join、将枚举值都换成中文,来避免模型进行枚举值转换的时候出现幻觉、将一些很复杂的指标计算提前计算好等等。
接下来就是梳理架构,由于我们是一个会话型应用,肯定需要一个意图识别的Agent,同时它需要去澄清一些用户带有歧义的问题,我最喜欢拿来举例的就是:帮我查询一下购买了A产品和B产品的客户,这句话本身就有两种解释,一种是交集,就是同时购买了A产品和B产品的客户;另外一种是并集,就是购买了A产品或购买了B产品的客户。这就需要Agent帮我们去澄清。
然后就是有时候用户的问题既可以算是文档问答,也可以算是Text-to-SQL,所以还需要通过检索增强的方式,将具体的数据检索回来辅助LLM判断属于哪个意图。当检索回来的内容显示两种意图都可能出现时,就需要抛问题给用户,到底是想要做什么了。
当确定是Text-to-SQL的意图后,就可以让Text-to-SQL的Agent进行澄清了。这次澄清的内容是任务相关的,一个简单的例子是:用户想查询肯德基XXX店的销量,但是叫作XXX店的可能有XXX一店、XXX二店。那就需要让用户澄清一下,具体查的是哪个店。这里的澄清,是只有在我们知道是Text-to-SQL任务,然后通过数据库的值进行召回,发现存在歧义后才可能澄清的。所以两个地方的澄清都是必要的。
澄清完就可以进入到任务的主流程中了,在Text-to-SQL的场景下,就是做好Schema Linking,也就是选表、选列、补充描述、补充数据库的候选值供where/having/case when等语句使用。
接着就是SQL生成,这里可以用Self Consistency来进一步降低错误率。然后可以用一个Revise Agent对语法错误进行修正。
最终让LLM进行SQL的翻译。
该应用的数据主要分成:
根据他们的存储形式,再结合上一节提到的的原则处理就行。
这里的指标就不详细展开说了,看图就很明确了。
一切准备就绪,接下来就是开发加优化了。我们这套架构也是在一次次调整中财获得的,最开始的时候,是没有这些澄清步骤,没有数据召回辅助意图识别判断的。真就是上了UAT才发现...,上了生产才发现...
该项目通过该方法论最终将Text-to-SQL的准确率从一开始的10%(由于LLM缺乏大量的业务知识和默认规则,所以基本都是错的),提升到了90%+。
LLM应用的开放范式正在快速迭代,欢迎大家一起来探讨探讨。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。