- ❓ RAG 是什么
- ❓ Reranker 是什么
- ❓ embedding 是什么
-
❓ chunk分块 是什么
h7.png
业务场景
这里要说明的是,大模型本身不会联网、知识有限的,例如:"娱乐圈今天xxx出轨了"大模型是不知道的,你数据库里有一些资料文件大模型也是不知道的!
A 股现在上市有五六千家,每个季度都会公布一份pdf文件财报,一份财报文件少说有十几页,多的有上百页,现在我们要做一个AI搜索功能
按照2年的数据量,5000 X 4 X 2 = 40000 份文件要存入数据库中
这个量已经非常的大了,现在要做一个大模型搜索功能,比如你询问 茅台2025年第一季度净利润多少?
在以前大模型没有诞生的年代,通常的做法是
- 将这4W份pdf解析成文字,存入数据库或者elasticsearch
- 将问题
茅台2025年第一季度净利润多少?进行分词拆分
2.1 例如es的ik分词器拆分为茅台2025第一季度净利润多少?
2.2 进行es的打分搜索,取出来分值靠前的几个pdf文件 - 如果使用mysql的话,也可以将整句话进行全文/like搜索(没几个人这么干)
这种方式会将与关键词有关联的原始文章搜索出来
现在有了大模型,要对大模型进行数据交互,发送大模型的提示词可以写
请读取是我的资料库,回答我的问题 "茅台2025年第一季度净利润多少?"
4万份pdf文件解析的财报文本……
……
……
然后你就会发现,4W份pdf文件解析的文本一并发送给了大模型,token直接干了上千万。。。。
但即使你使用上面的步骤搜索出来的有关联原始文章发给大模型,也会有两个问题
- 即使是几篇pdf,token也是非常之大
- 由于全靠词的相似度进行搜索,里面没有自然语言的理解成分在内,例如中石化的财报里提到了
2025年茅台第一季度与我公司合作这几个字,也会被搜出来,其实与我们要问的问题八杆子打不着
下面,我们以一种高效的、自然语言方式的存储搜索方法来实现
检索增强生成(RAG)
步骤一:原始数据解析
例如,我们的4万份A股财报,我们对其预处理,其中可能会有html、pdf、word、text格式文件,我们在程序上进行适配解析,确保能够解析成为文本,这里面有几个技术点需要自己慢慢磨、调试,这是一个很折磨的过程!
- 文件中有图片的,如果也要解析,就要引入ocr识别
- 有一些文件内容有大范围的空格,例如pdf换页的时候,段落会有数行的空格,看是否需要保留
- 表格、目录、标题、图表等,这类内容解析成文本时格式异常的错乱无序
- ……
LangChain 提供了很多种文件解析方案,但如果要做到精益求精,也得需要对解析结果格式不断的测试
步骤二:解析分块chunk
❓ 4万份 pdf 文件解析成文本后,存入数据库中的数据是4万表记录么?
❓ 茅台的财报pdf文件85页,讲 净利润 的只有短短两句话,你要把整个pdf文件发给大模型么?
我们有提到过,有些单个文件里面的内容非常之大,例如 中石化1个季度的财报pdf文件有80多页,这种内容是很多的,我们应当对单个 pdf 文件或其他类型的文件,再进行拆分分割,例如有这么几种方式
- 直接按照pdf页码拆分,一页到了强制分割(这会出现一段话跨页时被分成了2块)
- 按照内容的token数量,例如 800 个token切割一次(token是干嘛的自行gg)
- 按照自然语言进行拆分,例如 一段 或者 两段 完整的段落切块一次
这里和步骤一一样,是一个技术打磨的过程,这里不在细讲
LangChain 是个伟大解决方案框架,也为文件内容分块做了很多基础方法封装,上面列的几种情况都有现成的方法可供调用
这样,一个原始文件就被拆分成了>=1个字文件内容
步骤三:向量化
将自然语言文本(如单词、句子或段落)转换为数值向量(即一串数字)的过程。
例如,有一段很长的内容
2月3日,媒体报道称从权威渠道获悉,浙江天台夫妻食用网购娃娃菜后被诊断出老鼠药中毒一案,警方进一步侦查发现,下毒者系杨女士的丈夫,目前涉事男子已被警方采取刑事强制措施,案件还在进一步调查之中。
3日,红星新闻记者联系当地官方人士,对方表示上述报道“部分不属实”“警方还在进一步调查”。红星新闻了解到,杨女士丈夫系陈某波,二人已结婚十几年。二人家乡的村支书表示,夫妻二人平时关系很好,村民均反映没有什么矛盾。
3日下午,红星新闻从浙江台州天台县相关官方人士处进一步了解到,警方在进一步侦查过程中发现,丈夫的下毒行为系夫妻双方共谋,两人试图通过网购娃娃菜食用后制造中毒迹象,以此要挟,向平台或商家骗取巨额赔偿。但最终被警方侦查识破。
该人士指出,“他其实不是给他老婆下毒”,夫妻二人为了骗取钱财“自己给自己下毒”。目前,杨女士、陈某波均已被刑拘。
对此,红星新闻记者也询问了夫妻二人家乡的村支书。村支书告诉红星新闻,1月31日,他和另一名村干部出于对村民的关爱,去县医院探望杨女士、陈某波,夫妻二人均尚在病房治疗,“杨女士血液里的老鼠药毒素还没清除干净”。2月3日看到新闻后,村支书多次拨打二人电话,希望了解真实情况,但已无人接听
将其转化为完整的转换成向量,例如阿里云提供了 1024 纬度的文本向量转换API,这个转化过程就称为 embedding
大致长这样 [0.23232323, 0.2389273923,0.237943934,0.984973924..............] 类似一个1024个元素的数组
截至2024年3月份是这么用的,现在是2026年,据说一些云数据库pgsql内部自带embedding方法了
步骤四:存储
| id | aid(源文件) | chunk_id(分块id) | content(内容) | embedding(向量值) |
|---|---|---|---|---|
| 1 | 1 | 1 | 2月3日,媒体报道称从权威渠道获悉,浙江天台夫妻食用网购娃娃菜后被诊断出老鼠药中毒一案,警方进一步侦查发现,下毒者系杨女士的丈夫,目前涉事男子已被警方采取刑事强制措施,案件还在进一步调查之中。 | [0.23232323, 0.2389273923,0.237943934,0.984973924..............] |
| 2 | 1 | 2 | 3日,红星新闻记者联系当地官方人士,对方表示上述报道“部分不属实”“警方还在进一步调查”。红星新闻了解到,杨女士丈夫系陈某波,二人已结婚十几年。二人家乡的村支书表示,夫妻二人平时关系很好,村民均反映没有什么矛盾。 | [0.23232323, 0.2389273923,0.237943934,0.984973924..............] |
| 3 | 1 | 3 | 3日下午,红星新闻从浙江台州天台县相关官方人士处进一步了解到,警方在进一步侦查过程中发现,丈夫的下毒行为系夫妻双方共谋,两人试图通过网购娃娃菜食用后制造中毒迹象,以此要挟,向平台或商家骗取巨额赔偿。但最终被警方侦查识破。 | [0.23232323, 0.2389273923,0.237943934,0.984973924..............] |
| 4 | 1 | 4 | 对此,红星新闻记者也询问了夫妻二人家乡的村支书。村支书告诉红星新闻,1月31日,他和另一名村干部出于对村民的关爱,去县医院探望杨女士、陈某波,夫妻二人均尚在病房治疗,“杨女士血液里的老鼠药毒素还没清除干净”。2月3日看到新闻后,村支书多次拨打二人电话,希望了解真实情况,但已无人接听 | [0.23232323, 0.2389273923,0.237943934,0.984973924..............] |
这是一个表结构,我们将一个文件内容,分成了4块,并分别进行了向量化存储
目前,postgresql数据库的Vector,是用的多的向量存储数据类型(这里要测试一下,Vector字段是否能够有效命中索引)
步骤五:搜索
queryDb.Raw("select id, aid, page_index, chunk_index, doc, 1-(embedding <=> ?) as score from reportify_chunk_embedding_test where article_category=? and 1-(embedding <=> ?) > ? order by score desc limit ?", sliceStr, base.Category, sliceStr, threshold, limit).Scan(&result)
上面是一个真实的向量字段搜索sql
postgresql 提供了向量字段,也提供了对应的 sql 搜索方式,例如余弦搜索
例如你询问,茅台有多少位股东,我们将这句话转化为向量值,然后通过余弦搜索sql去表里面搜全表,取出来前10条记录拿去使用
注意,文本=>embedding 的过程,结果值是非幂等的,且不能混用的
- 有1000份文本需要向量化入库,
- 前500条使用阿里云的 embedding api 入库
- 后500条使用百度的 embedding api 入库
这时去通过 sql 去查库,查出来的肯定是不精准的
检索增强生成(Reranker)
向量搜索有个非常鸡肋的缺点,幻听严重
例如,你询问 茅台董事长叫什么? 向量搜索的10条记录,排名第一的可能是包含 茅台的股东是谁、茅台的老板是谁之类的原文所在段落,包含茅台的董事长的段落可能在地第5条
这时候需要重新拍一下序,来更一步精确打分与问题解决的记录
这个就叫做 Reranker(重排序器),也是很多的商业API可供调用
这个过程属于精确优化内容,例如你通过向量搜索从海量数据表中取出来10条记录,再通过 Reranker 排个序,获取前 3 条记录,扔给大模型,从而提高内容仅准率,也节省了token
