RAG流程
困境
大模型的知识在训练后就固定了,所以大模型是无法直接拿到最新知识和私有数据的。此时,通常会采用RAG和微调这两种主流方案来使其拿到这些知识。
微调更新知识成本高耗时长,而且每次更新都要重新训练一遍,性价比低。
RAG是在生成回答之前,去外部知识库检索相关内容,然后将检索结果和用户问题一起交给大模型去回答。
RAG流程
RAG整体分为离线解析和在线检索阶段。
离线阶段:
- 首先是文档加载,比如用LangChain的DocumentLoader将原始数据加载进来;
- 然后是文档切割,通常将文档切成500~1000token的chunk片段,注意chunk过大会导致引入噪音、占用更多prompt,过小会导致上下文被切断而语义不完整,所以还会做比如100token的重叠,避免切断语义;
- 接下来是embedding向量化,使用embedding模型将token序列转为高维浮点数向量。
- 最后入库与构建索引,将chunk的向量和原始文本一起存入向量数据库比如开启pgvector的postgreSQL,构建HNSW索引,也可以同时建倒排索引做关键词检索,合在一起是混合检索。
在线阶段:
- 首先是Query处理,用户提问可能是口语化的,可能有指代语义,需要转化为适合检索的形式。
- 然后是向量检索(粗排),用户问题转为向量后,通过向量相似度算法比如余弦相似度或欧氏距离召回语义最相似的top-k个向量。这个阶段召回速度快百万级向量数据库也能在几十ms内返回,但是只比较向量距离而非查询和文档的语义关系的话,容易引入相似但不相关的内容。
- 接下来是Rerank(精排),使用Rerank模型将用户问题和chunk拼在一起,深度理解相关性,可以将粗排返回的top20筛出top3~5。这个阶段相关性判断更准,但是依赖模型输出,成本高耗时高。
- 最后是生成,将用户问题和精排结果拼接入prompt给大模型去生成最终结果。
RAG的价值主要体现在,一是知识可以随时热更新,只需要往知识库添加新文档,比微调成本低;二是可追踪可观测性,每次生成可以追溯到出错chunk。
HNSW
当前主流向量数据库均采用HNSW(Hierarchical Navigable Small World,分层可导航小世界)来做向量检索,实现亿级数据毫秒级召回。
HNSW我理解是原理上采用类似跳表的分层结构,用冗余的多层空间换取快速的搜索时间。它每层是NSW,由向量节点通过边连接相似向量,贪心找近邻。上层是稀疏图,通过跳跃实现快速达到目标区域,下层是稠密图,上层节点作为下一层的入口点,直至最底层去执行一次小范围的KNN算法,选出top-k个结果。
HNSW索引构建有两个关键参数影响性能:一是M最大连接数,M越大占用内存越多、构建速度慢,但是M越小,可能丢失重要连接,召回率下降;二是efConstruction插入新节点时在候选集中的搜索宽度,越大,需要计算距离的候选节点就越多,建索延迟越高,但是越能找到近似邻,召回率越高。
还有一个查询时参数,efSearch最底层KNN时候选集大小,越大耗时越久,但是召回率也越高。