检索增强生成:Retrieval-Augmented Generation,RAG,结合检索和生成的能力,为文本序列生成任务引入外部知识
RAG 不仅仅依赖于训练数据中的信息,还可以从大型外部知识库中检索信息,特别适合处理在训练数据中未出现的问题,通常可以概括为三个步骤:
- 检索:根据输入的问题,使用检索系统从文档集合中查找相关片段。检索系统常基于密集向量搜索,如 ChromaDB、Faiss 等向量数据库
- 上下文编码:将检索的文档段落与原始问题输入一起编码
- 生成:使用编码的上下文信息,模型生成输出,通常由 LLM 完成
文档加载
LangChain 提供各种文档加载器,并与 Airbyte、Unstructured.IO 等供应商集成
| 名称 | 说明 | 加载类 |
|---|---|---|
| TextLoader | 加载文本文档 | TextLoader |
| CSVLoader | 加载CSV 文档 | CSVLoader |
| HTMLLoader | 加载HTML文档 | UnstructuredHTMLLoader |
| JSONLoader | 加载JSON 文档 | JSONLoader |
| MarkdownLoader | 加载Markdown 文档 | UnstructuredMarkdownLoader |
| PDFLoader | 加载 PDF 文档 | PyPDFLoader |
文本转换
将文本拆分成更小的块(片,节点等)
文本文档
文本分割器工作原理为:
- 将文本分成小的、具有语义意义的块(通常是句子)
- 将小块组合成一个更大的块,直到达到一定的大小
- 生成新文本块时要和上一个块有一定的重叠,保持块之间的上下文
| 分割器 | 说明 |
|---|---|
| 字符分割器 CharacterTextSplitter | 基于单个字符进行分割,默认的分隔符为 \n\n,分块大小为字符数。 |
| 递归字符分割器 RecursiveCharacterTextSplitter | 用一个分隔符列表进行分割,按照顺序逐个尝试列表中的分割符直到块足够小为止。默认的分隔符列表是 ["\n\n","\n","",“"] |
| Markdown 标题分割器 MarkdownHeaderTextSplitter | 根据指定的标题标记来分割 Markdown 文件,如 # 或 ##。 |
| 令牌分割器 TokenTextSplitter SpacyTextSplitterSentence TransformersTokenTextSplitter NLTKTextSplitter | 在分割文本时考虑令牌数量,如 TokenTextSplitter 使用 TikToken 估计分割后的令牌数量 |
| 分割器和分片策略主要考虑以下因素: |
- LLM Token 限制,还要预留一定数量的 Token 作为输入提示
- 考虑任务类型
- 需要细致查看文本的任务,最好使用较小的分块:需要识别文本中的单个单词或字符,分析类任务,关键字提取任务等
- 需要全面了解文本的任务,则使用较大的分块:需要理解文本的整体含义,识别文本中不同部分之间的关系,创意写作等
- 所分割的文本的性质,文本结构很强(HTML,代码等)的需要较大的块
其他文件
- 过滤冗余的文档:EmbeddingsRedundantFilter 工具可以识别相似的文档并过滤掉冗余信息,节省存储空间并提高检索效率。
- 翻译文档:通过与工具 doctran 进行集成,可以将文档从一种语言翻译成另一种语言。
- 提取元数据:通过与工具 doctran 进行集成,可以从文档内容中提取关键信息(日期、作者、关键字等)存储为元数据,更有效地管理、分类和检索文档。
- 转换对话格式:通过与工具 doctran 进行集成,可以将对话式的文档内容转化为问答(QA)格式,更容易地提取和查询特定的信息或回答,在处理访谈、对话或其他交互式内容时非常有用。
文本嵌入
嵌入 Embeddings 将一段文本转换为向量表示,在向量空间中查找最相似的文本片段,由 LLM 完成。
LangChain 提供 Embeddings 类表示 LLM 文本嵌入功能的接口
embed_documents:接收多个文本作为输入,一次性将多个文档转换为向量表示embed_query:查询创建嵌入,只接收一个文本作为输入
存储嵌入
存储计算后的嵌入结果
缓存存储
LangChain 提供 CacheBackedEmbeddings 将嵌入缓存在键值存储中,通常使用 from_bytes_store(<embedder>, <cache>, <namespace>) 创建
embedder:Embeddings 类的实现类,实际计算嵌入的嵌入器cache:存储文档嵌入的缓存,可用内置缓存工具InMemoryStore:内存缓存LocalFileStore:本地文件系统存储- 其他数据库,如
RedisStore或其他向量数据库
namespace:可选,文档缓存的命名空间,避免冲突
向量数据库
根据具体需求选择
- 数据规模和速度需求:考虑你的数据量大小以及查询速度的要求
- 持久性和可靠性:根据你的应用场景,确定你是否需要数据的高可用性、备份和故障转移功能
- 易用性和社区支持:考虑向量数据库的学习曲线、文档的完整性以及社区的活跃度
- 成本:考虑总体拥有成本,包括许可、硬件、运营和维护成本
- 特性:考虑你是否需要特定的功能,例如多模态搜索等
- 安全性:确保向量数据库符合你的安全和合规要求
数据检索
检索器 Retriever 是数据检索模块的核心入口,通过非结构化查询返回相关文档
最常用的检索器是向量存储检索器,使用 VectorstoreIndexCreator 创建,通过 vectorstore 类的 as_retriever 方法直接作为检索器
LangChain中还提供很多种其他的检索工具
索引
索引:Index,一种高效地管理和定位文档信息的方法,确保每个文档具有唯一标识并便于检索。
- 避免重复内容:确保你的向量存储中不会有冗余数据。
- 只更新更改的内容:能检测哪些内容已更新,避免不必要的重写。
- 省时省钱:不对未更改的内容重新计算嵌入,从而减少了计算资源的消耗。
- 优化搜索结果:减少重复和不相关的数据,从而提高搜索的准确性。
LangChain 使用记录管理器 RecordManager 跟踪写入向量存储的文档,通过哈希处理确保每个文档都有一个唯一的标识
- 文档哈希:基于文档内容和元数据计算出的唯一标识。
- 写入时间:记录文档何时被添加到向量存储中
- 源 ID:这是一个元数据字段,表示文档的原始来源
记录管理器确保了即使文档经历了多次转换或处理,也能够精确地跟踪它的状态和来源,确保文档数据被正确管理和索引。