聊聊检索增强,LangChain一把梭能行吗?

news2024/11/18 9:40:38

背景

ChatGPT诞生之初,大家仿佛从中看到了未来:可以拿着大语言模型(LLM)这把锤子,锤遍业务上的钉子。其中最被看好的场景,莫过于搜索,不仅是微软、谷歌、百度这样的大公司将LLM用到自己的搜索业务中,Github上也有很多知识库、文档问答相关的优秀开源项目,究其本质,是通过检索相关知识来增强LLM的回答效果

搭建一个检索增强pipeline其实很简单,“embedding模型+向量检索+LLM”这一套组合拳就足够,而且LangChain中都已经封装好了,简单写几行代码调用就能收工下班!但如果认真去分析最终回复的效果,就会发现这个pipeline只完成了工作的冰山一角,而本文,会展开聊聊冰山之下的秘密。

分片+检索+回答

首先需要对整体的流程进行概述:

  1. 文本分片。不同格式的原始数据,需要转成文本。因为文本在后续的操作中会拼接到prompt中,所以对于过长的文本,需要进行分片,控制在一定的长度之内。

  2. 文本检索。离线阶段,可以将文档片段都进行embedding,将embedding向量存储到向量数据库中;在线阶段,将用户query的embedding向量在向量数据库中进行检索,返回TopK的相关文档。

  3. LLM回答。将TopK的相关文档组织成prompt,请求LLM得到最终的回答。

这三个步骤环环相扣,每一个环节的错误都会层层传递到后续的环节。下面会分别介绍这三个环节中可能会碰到的挑战。

图来源于[3],”分片“对应图中第1、2步,”检索“对应第3、4步,”回答“对应第5步

图来源于[3],”分片“对应图中第1、2步,”检索“对应第3、4步,”回答“对应第5步

文本分片

文本数据抽取

且不论多模态数据,单单是文本数据的存储格式,常见的就有:TXT、Word、PDF、Latex、CSV、Excel、HTML、Markdown……不同格式的数据要转成纯文本,需要话费不少功夫,即使是Langchain-Loaders[1]已经提供了多种格式的文本提取接口,但对于复杂格式的文件,提取效果依然不太理想,如PDF。这时候就需要投入更多精力来进行数据清洗,否则会直接影响到最终的效果。

语义完整性

Langchain-Doc Transformers[2]中介绍了按照长度、指定符号(句号、换行符)这些简单的规则进行文本分片的方法,但用这些方法得到的分片很多情况下会有语义不完整的问题,比如:

  • 文档格式转换(如PDF无法识别段落)出问题

  • 整体语义蕴含在长文本中

  • 当文档编写者不遵守语法,胡乱断句换行

  • ……

为了保证语义的完整性,可以尝试以下方案:

  • 使用模型(比如Bert)进行分片,而不是简单的依靠规则切分。

  • 对文档进行摘要,这样可以大幅缩短文档的篇幅,保证语义的完整性。

  • 使用LLM根据文档构造多个QA pair,由于LLM可接受的prompt长度会大于文本片段的长度,通过这种方法得到QA pair能够将距离较远的信息有结构地组织到一起,控制在可接受的长度内。

文本检索

过滤无关文本片段

如果只是取检索结果的TopK拼接到后续LLM的Prompt中,那TopK中难免会有和用户问题不相关的结果,所以需要进行过滤,尽量保证给的LLM的都是相关的文本片段。

常用的过滤方式就是根据相关性分数过滤了,可以选择一个阈值,丢弃相关性低于该阈值的文档。这么做的前提,需要Embedding模型最好满足以下要求:

  • Embedding模型返回的向量最好是归一化后的,这样使用内积或欧氏距离表示相关性时,取值都在固定范围内,容易选择阈值。

  • Embedding模型最好做过**校准(Calibration)**。校准这一操作在CTR预估中很常见,主要是因为训练时一般采用pairwise,同时也主要会使用AUC、NDCG这类排序类型的评价指标,比如A>B>C,模型预测ABC的分数为[0.1, 0.2, 0.3]或[0.7, 0.8, 0.9]在评价指标上是一样的,但这样不利于选取合适的阈值来过滤无关文本,所以需要加上校准这一步骤。

多轮问答下的检索Query

假设用户前后的两个问题是:”颐和园在哪里?“、”门票多少钱?“。但如果单看第二个问题,无法知道用户想问的是哪里的门票。所以,如果只用最新的query来检索,会造成上下文依赖丢失的问题。

针对这个问题,可以尝试以下解法:

  • 增加一个模型(比如:Bert),用户判断当前的query和历史的query是否有上下文依赖关系,维护一个依赖关系链,如果有,则将依赖链上的query拼接起来用于文本检索,否则只需将最新的query用于检索即可。

  • 让LLM根据近几轮的问答生成检索query,发挥LLM的通用性。

LLM回答

Prompt

用什么样的Prompt才能让LLM输出满意的结果呢?首先来看一个常用来做检索增强的Prompt:

Use the following context as your learned knowledge, inside <context></context> XML tags.
<context>
{{context}}
</context>
When answer to user:
- If you don't know, just say that you don't know.
- If you don't know when you are not sure, ask for clarification. 
Avoid mentioning that you obtained the information from the context.
And answer according to the language of the user's question.

Here is the chat histories between human and assistant, inside <histories></histories> XML tags.
<histories>
{{histories}}
</histories>

human:{{question}}
ai:

以上这个Prompt,将相关文档、历史对话、用户问题都放进去了,看着是个很强的baseline。但如果你用的LLM不是强如ChatGPT,而只是6B、13B规模的开源模型,你会发现这个Prompt的效果不够稳定,经常出现Badcase。

我认为它最致命的缺点是没有采用对话的形式。之前在介绍ChatGPT的三步走方案时提到了,在第二、三步时训练数据的格式是对话的形式,所以,如果将Prompt能够保持对话的形式,效果应该能更好,下面是一个示例。关于Chatgpt是如何组织对话的,可以参考以往的这篇文章。

[
    {
        "role":"system",
        "content":"""
Use the following context as your learned knowledge, inside <context></context> XML tags.
<context>
{{context}}
</context>
When answer to user:
- If you don't know, just say that you don't know.
- If you don't know when you are not sure, ask for clarification. 
Avoid mentioning that you obtained the information from the context.
And answer according to the language of the user's question."""
    },
    {
        "role":"user",
        "content":"histories_user_1"
    },
    {
        "role":"assistant",
        "content":"histories_assistant_1"
    },
    ...
    {
        "role":"user",
        "content":"question"
    },
]

礼貌拒答

即使有了检索增强,也不见得LLM能答上所有问题,相反,我们更希望LLM在没有把握的时候,选择有礼貌地拒答,所谓有礼貌,在很多场景中是和”人设“相关的,比如落地的场景是企业助手、电商客服等。想要实现人设和拒答的效果,主要的工作是在调优Prompt上。Prompt工程这活儿,感觉只能起到锦上添花的作用,效果想要更上一层楼还是得使用更强的LLM。

写在最后

创新到普及之间,技术到产品之间,可用到好用之间,存在着一道隐秘的GAP。篇幅有限,以上的一些经验,无法覆盖到方方面面,欢迎大家一起来讨论!

Reference

[1] Langchain-Loaders

[2] Langchain-Doc Transformers

[3] Langchain-Question Answering

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/957252.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【ES6】Promise的入门介绍

Promise 是 JavaScript 中的一个对象&#xff0c;用于处理异步操作。Promise 对象代表一个最终可能完成&#xff08;并得到结果&#xff09;或失败&#xff08;并被拒绝&#xff09;的操作&#xff0c;以及其结果的值。 一个 Promise 有三种状态&#xff1a; Pending&#xf…

记录--vue 拉伸指令

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前言 在我们项目开发中,经常会有布局拉伸的需求,接下来 让我们一步步用 vue指令 实现这个需求 动手开发 在线体验 codesandbox.io/s/dawn-cdn-… 常规使用 解决拉伸触发时机 既然我们使用了指令的方式…

10.引入导航栏样式

1.导航栏为单独一个组件 在element-ui中引入导航栏的代码 &#xff01;注意 内容一定要在template中&#xff0c;否则bug遇到很久 <template><div><!-- 页面布局 --><el-container><!-- 侧边栏 --><el-aside width"200px"><…

模拟电子技术基础学习笔记二 杂质半导体

通过扩散工艺&#xff0c;在本征半导体中掺入少量合适的杂质元素&#xff0c;可得到杂质半导体。 按掺入的杂质元素不同&#xff0c;可形成N型半导体和P型半导体 控制掺入杂质元素的浓度&#xff0c;可以控制杂质半导体的导电性能。 一、N型半导体&#xff08;negative Semic…

独家首发!openEuler 主线集成 LuaJIT RISC-V JIT 技术

RISC-V SIG 预期随主线发布的 openEuler 23.09 创新版本会集成 LuaJIT RISC-V 支持。本次发版将提供带有完整 LuaJIT 支持的 RISC-V 环境并带有相关软件如 openResty 等软件的支持。 随着 RISC-V SIG 主线推动工作的进展&#xff0c;LuaJIT 和相关软件在 RISC-V 架构下的支持也…

活用 命令行通配符

本文是对 阮一峰老师命令行通配符教程[1]的学习与记录 通配符早于正则表达式出现,可以看作是原始的正则表达式. 其功能没有正则那么强大灵活,而胜在简单和方便. - 字符 切回上一个路径/分支 如图: !! 代表上一个命令, 如图: [Linux中“!"的神奇用法](https://www.cnblogs.…

【ES6】Promise.all用法

Promise.all()方法用于将多个 Promise 实例&#xff0c;包装成一个新的 Promise 实例。 const p Promise.all([p1, p2, p3]);上面代码中&#xff0c;Promise.all()方法接受一个数组作为参数&#xff0c;p1、p2、p3都是 Promise 实例&#xff0c;如果不是&#xff0c;就会先调…

华为数通方向HCIP-DataCom H12-821题库(单选题:181-200)

第181题 某管理员需要创建AS Path过滤器(ip as-path-iter)&#xff0c;允许AS_Path中包含65001的路由通过&#xff0c;那么以下哪一项配置是正确的? A、​​ip as-path-filter 1 permit 65001​​ B、​​ip as-path-filter 1 permit "65001​​ C、​​ip as-path-f…

开源电子合同签署平台小程序源码 在线签署电子合同小程序源码 合同在线签署源码

聚合市场上各类电子合同解决方案商&#xff0c;你无需一个一个的对接电子合同厂商&#xff0c;费时&#xff0c;费力&#xff0c;因为这个工作我们已经做了适配&#xff0c;你只需要一个接口就能使用我们的所有服务商&#xff0c;同时你还可以享受我们的接口渠道价格。 Mini-C…

Redis图文指南

1、什么是 Redis&#xff1f; Redis&#xff08;REmote DIctionary Service&#xff09;是一个开源的键值对数据库服务器。 Redis 更准确的描述是一个数据结构服务器。Redis 的这种特殊性质让它在开发人员中很受欢迎。 Redis不是通过迭代或者排序方式处理数据&#xff0c;而是…

weblogic/CVE-2018-2894文件上传漏洞复现

启动docker环境 查看帮助文档 环境启动后&#xff0c;访问http://your-ip:7001/console&#xff0c;即可看到后台登录页面。 执行docker-compose logs | grep password可查看管理员密码&#xff0c;管理员用户名为weblogic&#xff0c;密码为lFVAJ89F 登录后台页面&#xff0c;…

说说大表关联小表

分析&回答 Hive 大表和小表的关联 优先选择将小表放在内存中。小表不足以放到内存中&#xff0c;可以通过bucket-map-join(不清楚的话看底部文章)来实现&#xff0c;效果很明显。 两个表join的时候&#xff0c;其方法是两个join表在join key上都做hash bucket&#xff0c…

OceanBase 4.1解读:读写兼备的DBLink让数据共享“零距离”

梁长青&#xff0c;OceanBase 高级研发工程师&#xff0c;从事 SQL 执行引擎相关工作&#xff0c;目前主要负责 DBLink、单机引擎优化等方面工作。 沈大川&#xff0c;OceanBase 高级研发工程师&#xff0c;从事 SQL 执行引擎相关工作&#xff0c;曾参与 TPC-H 项目攻坚&#x…

为什么聊天头像ChatGPT是橙色的?

目录 ChatGPT的不同版本及其颜色 了解绿色和橙色的ChatGPT徽标 颜色变化的重要性 橙色标志的原因 故障排除和常见问题解答 常见问题3&#xff1a;如何查看ChatGPT的服务器状态&#xff1f; 常见问题4&#xff1a;如果使用ChatGPT时遇到错误&#xff0c;我该怎么办&#…

第 3 章 栈和队列(用递归调用求 Ackerman(m, n) 的值)

1. 示例代码&#xff1a; /* 用递归调用求 Ackerman(m, n) 的值 */#include <stdio.h>int Ackerman(int m, int n);int main(void) {int m 0, n 0;printf("Please input m and n: ");scanf_s("%d%d", &m, &n);printf("Ackerman(%d, …

信息安全-应用安全-蚂蚁集团软件供应链安全实践

8月10日&#xff0c;由悬镜安全主办、以“开源的力量”为主题的DSS 2023数字供应链安全大会在北京国家会议中心隆重召开。蚂蚁集团网络安全副总经理程岩出席并发表了《蚂蚁集团软件供应链安全实践》主题演讲。 图1 蚂蚁集团网络安全副总经理程岩发表主题演讲 以下为演讲实录&am…

css实现文字翻转效果

csss实现文字翻转效果 主要实现核心属性 direction: rtl; unicode-bidi: bidi-override; direction: rtl; 这个属性用于指定文本的方向为从右到左&#xff08;Right-to-Left&#xff09;。它常用于处理阿拉伯语、希伯来语等从右向左书写的文字样式。当设置了 direction: rtl; …

Java队列有哪些?8大常用Java队列总结

什么是队列? 队列是一种操作受限的线性表&#xff0c;只允许在表的前端&#xff08;front&#xff09;进行删除操作又称作出队&#xff0c;在表的后端进行插入操作&#xff0c;称为入队&#xff0c;符合先进先出&#xff08;First in First out&#xff09;的特性。在队尾插入…

Rstudio开不开了怎么办?R is taking longer to start than usual

Rstudio Server 启动时卡死 在使用 linux 服务器版 RstudioServer 的过程中&#xff0c;发现出现了一个问题&#xff0c;导致没有办法正常载入工作页面&#xff0c;网页提示信息是“R is taking longer to start than usual”&#xff0c;直接翻译过来就是“这次启动 R 会比平常…

Unity RenderStreaming 云渲染-黑屏

&#x1f96a;云渲染-黑屏 网页加载出来了&#xff0c;点击播放黑屏 &#xff0c;关闭防火墙即可&#xff01;&#xff01;&#xff01;&#xff01;