Hello,欢迎来到我们的讲座“你的冰箱是有效的支付设备:生产中的RAG”。让我先解释一下这个名字。这是来自我们客户规则的一页,这是我们构建RAG系统的众多文档之一。你期待看到枯燥的金融文件,而在大多数情况下确实如此。
但其中也有有趣的时刻。例如这一页列出了可用于支付的设备,有预期的东西如手机或卡片,但也有更有创意的:袖扣、手提包、衣服甚至冰箱。我觉得在应该非常无聊的文件中发现这样有趣的事实真的很酷。好吧,让我们自我介绍一下。
这是我们唯一的一张合照。我们通常不戴VR头显,但那天在办公室里我们正在尝试各种有趣的 gadgets(小工具)。我是Nina,一名AI工程师。我是Eleanor,一名产品工程师。
我们在In The Pocket工作,这是一家总部位于比利时的数字产品工作室。我们帮助客户进行AI转型和软件开发。今天我们将谈论我们为一家大型比利时金融科技公司创建的RAG项目,为了本报告我们保留她的匿名性。本质上这是一个经典的RAG问题。
在本报告中,我们要展示必须做出的技术决策并深入探讨我们如何构建它。这是我们在项目开始时对我们有用的报告。那么,我们的问题是什么?我们的客户运营着比利时最大的支付系统,所有规则都写在规则书中。大约有270个PDF文件。
这大约是6,000页,大部分信息包含在表格中,大约3000个表格。这就是他们组织知识的方式,很多重要信息包含在表格里本身。规则书经常互相引用一个集合指向另一个,那个又指向另一个等等。
为金融机构使用RAG有其自身的挑战首先是引用当然答案必须始终正确但在金融背景下尤其重要因此每个答案都应包含指向规则特定章节和页面的链接以便用户可以看到这个答案来自哪里。
第二点是正当性,运行在系统核心的LLM是一个通用模型所以它是在一般知识上训练的。所以她知道很多包括其他支付系统他们如何运作以及他们的费用是多少我们必须决定并确保可以使用哪些知识。例如知道什么是银行是正常的合乎逻辑的并不是规则中定义的东西但是另一个支付系统收取多少费用或如何运作不应该出现在答案中如果它不在规则中的话。
这就是我们创建的解决方案的样子这是一个聊天机器人你问一个问题它就根据这些规则回答如果答案在表格中它也会显示该表。在答案下方是使用形成它的源列表包括规则和部分的章节以便用户可以打开此文档在这个例子中我问:“我可以用我的冰箱支付吗?”
得到了答案然后我可以问后续问题这就是整个系统的样子它由两部分组成左边是索引这是我们离线做的随着每个新规则版本的发布我们将这些PDF文件转换为片段并存储在向量数据库中。右边是服务即针对每个用户问题的在线部分代理搜索此数据库然后响应流式传输给用户。
我将谈论索引Eleanor将谈论维护部分所以关于索引设置相当简单我们获取PDF文件将它们分解为片段并为每个片段添加元数据我们对表格进行特殊处理然后生成嵌入我们生成两种类型的嵌入密集的和BM25的所以我将详细介绍前三步Eleanor稍后谈论嵌入。
第一步是将PDF拆分为片段我们从每页提取文本将其划分为段落并将段落分组为一个块直到达到大约2000个字符并确保不在两个片段之间打断表格。我们还使用正则表达式定义章节标题例如“第三节”或“4.2.1”,这为每个片段提供了到分区的路径。
例如这个片段在第四节第4.2段,此路径用于添加链接以及当代理需要更多上下文时检索整个部分这是有效的因为我们不重叠片段所以一章只是片段的集合如果你按正确的顺序排列它们就会恢复到原始形式。
第二步是为每个片段添加元数据例如这里是从冰箱用户手册中提取的文本片段下面是某些元数据字段我们需要指南标题和页码以便在链接或用户的源卡上显示它们这允许用户直接转到PDF文件。
我们还使用章节路径进行引用以便代理可以一次性获取整个部分我们还有一个片段索引允许代理组合一个甚至整个指南,代理使用指南ID来获取所有可用指南的列表。在索引每个手册时我们使用LLM生成摘要目录表添加到每个片段作为指南的描述因此代理知道摘录来自哪个文档。
其余的元数据是业务逻辑字段即规则书是什么它适用于什么规则是否包含表格这是代理使用或随搜索结果接收的内容。第三阶段是表处理这很重要因为正如我所说的表中有很多知识起初我们没有意识到这一点初始处理只是从PDF文件中简单提取文本并且格式化的表格很差。
但在本例中你可以看到“国内”字段跨越多行,“跨境”再跨两行而“退款”则两者都包含所以如果没有清楚地转换为文本格式代理可能会得到错误的信息特别是对于像佣金这样重要的事情正确获取行和列非常重要所以我们把表格转换为CSV格式。
基本上复制那些字段然后这就是我们让数据到达代理的方式当它得到时它会读取正确的行正是他需要的那一行这确实显着提高了答案的准确性。所以这就是关于索引现在我们大约有4,000个片段现在Eleanor将谈论服务部分是的所以上一部分是关于建立索引。
这部分将关于从准备好的4000个片段中找出相同的10个。一个明显的步骤是在提问时使用密集嵌入,并将每个片段转换为向量以进行距离比较。这对于理解内容非常有效,但对于规则代码、消息名称和精确标识符来说,嵌入模型会将其视为噪声,尽管对于提问的用户而言,这些往往是问题的核心。而BM25则呈现镜像效果:精确的标记词有效,但同义改写无效。
因此,我们将这两种方法结合在一个查询中,并使用互逆等级融合(RRF)方法进行组合。密集向量取前50名,BM25取前50名,我们将它们合并为前30名,然后根据章节内容重新排序,并将前10个传递给智能体。RRR的一个优点是无需校准这两种方法之间的评分。在单独评估63个问题的搜索结果时,与密集嵌入相比,混合方法的成功率从92%提高到97%,且没有遗漏任何密集嵌入可能找到的问题。
因此,我们得到了10个不错的片段,但并非每个问题都适合放入这10个片段中。有些问题是相互关联的,一个引用指向另一个,只有跟随该链接才能得出完整答案。有些问题需要更广泛的背景信息。例如,此类交易的所有费用永远不会出现在任何事物的前10名中。因此,我们的智能体没有采用固定的“搜索然后生成”管道,而是拥有四个工具。
从窄到宽。目录搜索始终排在首位。这些就是我们在上一张幻灯片中讨论过的10个片段。如果这还不够,目录分区提取工具会通过其路径提取整个分区。这正是Nina之前提到的方法:存储分区的目录路径。而这里,当你删除它时——只是获取具有该路径的每个片段,并按片段索引排序。
对于小目录而言,仅获取完整目录是可行的。而对于智能体需要知道存在哪些内容时,则提供可用目录列表。一些安全措施帮助了这一方案在实践中发挥作用。首先,如果目录太大——超过10万令牌——完整目录工具拒绝将其内容加载到上下文中,而是返回可用章节列表并发出警告。
其次,如果章节名称不匹配,它会返回可用部分供智能体重试,而不是放弃或试图捏造一个名称。最后一个是查询中的“纪律指令”工具。由于大多数问题需要两到四次调用,在第六次调用之前,智能体会被指示询问自己是否获得了真正的新信息,还是仅仅重新表述了已经失败的搜索。
这个想法源于我们观察真实案例时发现的:智能体会一遍又一遍地运行相同的搜索,希望最终会出现一个数字。因此,这一切的代价是延迟,每次响应的中位数为38秒。这不仅仅是一个聊天响应,而是一个小型研究任务。我们依赖流式传输让38秒感觉像工作流程,而不是冻结的聊天。这引出了下一张幻灯片。
在第一轮迭代中,我们确实希望摆脱基本的聊天机器人界面。我们准备了接口组件并将其描述为智能体可用的Pydantic模型,说明:“这些是你拥有的组件。在回答中使用它们以及文本。”例如,来源卡片是一个组件,事件可以成为一个组件,可折叠列表也可以成为一个组件等。想法是拥有一个由AI生成的界面,使其比简单的文本表更易读。
问题在于无法流式传输部分Pydantic对象。只有在最后一个令牌到达后,它才成为有效的JSON。正如你所见,对于一个需要38秒才能响应的系统来说,这意味着38秒的空白屏幕,然后所有内容一次性出现。因此,拯救用户体验的解决方案是将响应拆分为部分。智能体现在逐个令牌地流式传输Markdown文本,界面在到达时实时对其进行样式处理。
然后来源卡片成为一个独立的元素,它是界面的真实组件。它不包含你在阅读完整个答案之前想要点击的信息。因此,在添加此组件前等待完整答案没有任何损失。这为我们提供了交互式卡片而不是普通的文本内链接,这对用户更友好。但一旦答案出现在屏幕上,下一个问题是用户将如何处理它:点击来源、复制响应、继续对话还是离开。
我们想要追踪的就是这种用户体验,这引出了日志记录。目前,我们为日志记录选择了最简单的解决方案——Azure Table Storage。我们记录每个问题、工具调用、响应、反馈(正面或负面),以及点击来源或复制响应等事件,并保留故意保持简短的对话历史。这些仅仅是作为上下文的最后20条帖子。
对于目前这个产品的阶段,这就足够了。随着产品的发展,我们将了解届时需要什么。我们如何知道这一切真的有效?我们的评估问题来自真实来源,我们与真实用户进行了两轮测试,此外还有针对AI系统行为准则的规则,规定其能做什么和不能做什么。例如:不提供建议、对竞争对手保持中立,并明确指出某些内容不在指南中。
我们大约有86个端到端测试通过Promptfoo运行,由LLM智能体根据标准进行全面评估,以及检查是否引用了正确的来源,还有对之前阶段搜索的单独评估。我们从经验中学到了两个教训:首先,LLM评分存在背景噪声,在信任结果之前需要重新运行测试。例如,在我们自己的评估中,12次结果变化中有5次没有重复。
其次,有时问题在于评估系统本身而不是模型。我们曾遇到过关于日期“幻觉”的情况,裁判一直给予罚款,结果发现该日期实际上是规则中的真实文本。是的,现在谈谈使用情况。我们在六月推出了这个规则助手。最初仅向客户的内部用户开放,目标是在年底前向其外部参与者和客户提供访问权限。因此,在这一点上,我们已经有一些使用指标。
到目前为止,我们收到了大约900个问题。这些问题分布在100多个对话中,实际上有约34人在使用它们。我们还通过“点赞”和“点踩”收集了反馈,但大多数人在第一个月后就停止使用此功能。我们还拥有关于来源被点击次数或答案被复制次数的数据,但这就是所有的数字信息了。
我们还利用AI进行分析。在右侧我们可以看到LLM是如何解读这些数据的。我们注意到,在第一个月引入用户时,发生了一些“红队测试”行为。人们试图黑客攻击助手、使其偏离主题或提出奇怪的问题。但到了第三个月,超过90%的问题变得真实且切中要害。
其次,人们将其用于现实案例。我们看到用户插入客户的电子邮件并请求帮助回复;或者他们插入协议中的真实消息、表格等,然后编写想要检查的答案。第三点,由于通过“点赞”和“点踩”获得的反馈不多,我们尝试通过分析后续问题来确定对话的语气。
如果人们说“谢谢”或转到新话题,我们将其计为积极互动;如果他们再次询问相同的内容,则很可能是消极互动。我们发现大多数对话都是成功的。那么,总结一下到目前为止该项目的主要结论是什么?在文档方面,我们发现保持其结构很重要。
因此,“章节”对于获取答案以及让代理以某种方式捕获知识都很重要。我们还看到,表格需要单独的方法和特殊处理。在代理方面,如前所述,提供不同粒度的级别并定义所需的上下文量很重要;总体而言,当我们从一次性LLM查询转向代理搜索时受益匪浅。我们观察到评估分数显著增加。
在用户体验方面,数据流式传输使该工具变得方便和有用。我们仅在用户界面需要时才添加结构化组件。最后,我们认为尽早与真实用户进行测试非常重要,并且利用这些访谈来创建评估提示词,帮助我们塑造产品并决定我们认为的改进是否真正有效。感谢聆听我们的报告,谢谢。