LangChain核心模块 Retrieval——Indexing

Indexing

  • 索引

LangChain Indexing API将数据从任何来源同步到向量存储中并保持同步,可以做到:

  • 避免将重复内容写入矢量库
  • 避免重写未更改的内容
  • 避免在未更改的内容上重新计算嵌入

最重要的是,Indexing API 甚至可以处理相对于原始源文档经历了多个转换步骤(例如,通过文本分块)的文档。

How it works

LangChain Indexing利用记录管理器(RecordManager) 来跟踪文档写入向量存储的情况。

索引内容时,会计算每个文档的哈希值,并将以下信息存储在记录管理器中:

  • the document hash (hash of both page content and metadata)
  • write time
  • the source id——每个文档应在其元数据中包含信息,以便我们确定该文档的最终来源

Deletion modes

将文档索引到向量存储时,向量存储中的一些现有文档可能会被删除。

在某些情况下,可能希望删除与正在编制索引的新文档源自相同来源的任何现有文档。

在其他情况下,您可能希望批量删除所有现有文档。索引 API 删除模式可让您选择所需的行为:

Cleanup ModeDe-Duplicates ContentParallelizableCleans Up Deleted Source DocsCleans Up Mutations of Source Docs and/or Derived DocsClean Up Timing
None××
Incremental×不断地
Full×索引结束时
  • De-Duplicates Content:删除重复内容
  • Parallelizable:可并行性
  • Cleans Up Deleted Source Docs:清理已删除的源文档
  • Cleans Up Mutations of Source Docs and/or Derived Docs:清理源文档和/或派生文档的突变
  • Clean Up Timing:清理时间

None 不执行任何自动清理,允许用户手动清理旧内容。

incrementalfull 提供以下自动清理功能:

  • 如果源文档或派生文档的内容发生更改,incrementalfull 都会清理(删除)内容的先前版本。
  • 如果源文档已被删除(意味着它不包含在当前正在索引的文档中),则 full 会将其从向量存储中正确删除,但incremental不会。

当内容发生变化(例如,源 PDF 文件被修改)时,在索引期间将有一段时间新旧版本都可能返回给用户。这种情况发生在新内容写入之后、旧版本被删除之前。

  • incremental索引可以最大限度地减少这段时间,因为它能够在写入时连续进行清理。
  • full 模式在所有批次写入后进行清理。

Requirements

  1. 不要与已预先填充了独立于索引 API 的内容的存储一起使用,因为记录管理器不会知道之前已插入记录。
  2. 仅适用于支持以下功能的 LangChain 矢量存储:
    • 按 id 添加文档(带有ids参数的add_documents方法)
    • 按 id 删除(带有 ids 参数的删除方法)

兼容的矢量存储:AnalyticDB, AstraDB, AwaDB, Bagel, Cassandra, Chroma, CouchbaseVectorStore, DashVector, DatabricksVectorSearch, DeepLake, Dingo, ElasticVectorSearch, ElasticsearchStore, FAISS, HanaDB, Milvus, MyScale, OpenSearchVectorSearch, PGVector, Pinecone, Qdrant, Redis, Rockset, ScaNN, SupabaseVectorStore, SurrealDBStore, TimescaleVector, Vald, Vearch, VespaStore, Weaviate, ZepVectorStore

Caution

记录管理器依靠基于时间的机制来确定可以清理哪些内容(当使用incrementalfull 清理模式时)。

如果两个任务连续运行,并且第一个任务在时钟时间更改之前完成,则第二个任务可能无法清理内容。

这在实际设置中不太可能成为问题,原因如下:

  1. RecordManager 使用更高分辨率的时间戳。
  2. 数据需要在第一个和第二个任务运行之间发生变化,如果任务之间的时间间隔很小时,这种情况就不太可能发生。
  3. 索引任务通常需要几毫秒以上的时间。

Quickstart

from langchain.indexes import SQLRecordManager, index
from langchain_core.documents import Document
from langchain_elasticsearch import ElasticsearchStore
from langchain_openai import OpenAIEmbeddings

初始化向量存储并设置嵌入:

collection_name = "test_index"embedding = OpenAIEmbeddings()vectorstore = ElasticsearchStore(es_url="http://localhost:9200", index_name="test_index", embedding=embedding
)

使用适当的命名空间初始化记录管理器。

建议:使用同时考虑向量存储和向量存储中的集合名称的命名空间;例如,

“redis/my_docs”、“chromadb/my_docs”或“postgres/my_docs”。

namespace = f"elasticsearch/{collection_name}"
record_manager = SQLRecordManager(namespace, db_url="sqlite:///record_manager_cache.sql"
)

在使用记录管理器之前创建架构

record_manager.create_schema()

索引一些测试文档:

doc1 = Document(page_content="kitty", metadata={"source": "kitty.txt"})
doc2 = Document(page_content="doggy", metadata={"source": "doggy.txt"})

索引到空向量存储中:

def _clear():"""Hacky helper method to clear content. See the `full` mode section to to understand why it works."""index([], record_manager, vectorstore, cleanup="full", source_id_key="source")
None deletion mode

该模式不会自动清理旧版本的内容;但是,它仍然负责内容重复删除。

_clear()
index([doc1, doc1, doc1, doc1, doc1],record_manager,vectorstore,cleanup=None,source_id_key="source",
)
{'num_added': 1, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 0}
_clear()
index([doc1, doc2], record_manager, vectorstore, cleanup=None, source_id_key="source")
{'num_added': 2, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 0}

第二次将跳过所有内容:

index([doc1, doc2], record_manager, vectorstore, cleanup=None, source_id_key="source")
{'num_added': 0, 'num_updated': 0, 'num_skipped': 2, 'num_deleted': 0}
incremental deletion mode
_clear()
index([doc1, doc2],record_manager,vectorstore,cleanup="incremental",source_id_key="source",
)
{'num_added': 2, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 0}

再次索引应该会导致两个文档都被跳过——同时也会跳过嵌入操作!

index([doc1, doc2],record_manager,vectorstore,cleanup="incremental",source_id_key="source",
)
{'num_added': 0, 'num_updated': 0, 'num_skipped': 2, 'num_deleted': 0}

如果我们不提供增量索引模式的文档,则什么都不会改变。

index([], record_manager, vectorstore, cleanup="incremental", source_id_key="source")
{'num_added': 0, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 0}

如果我们改变一个文档,新版本将被写入,所有共享相同源的旧版本将被删除。

changed_doc_2 = Document(page_content="puppy", metadata={"source": "doggy.txt"})
index([changed_doc_2],record_manager,vectorstore,cleanup="incremental",source_id_key="source",
)
{'num_added': 1, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 1}
full deletion mode

full模式下,用户应该传递将其索引到索引函数中的完整内容。

任何未传递到索引功能且存在于向量存储中的文档都将被删除!

此行为对于处理源文档的删除很有用。

_clear()
all_docs = [doc1, doc2]
index(all_docs, record_manager, vectorstore, cleanup="full", source_id_key="source")
{'num_added': 2, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 0}

假设有人删除了第一个文档:

del all_docs[0]
all_docs
[Document(page_content='doggy', metadata={'source': 'doggy.txt'})]

使用full模式也会清除已删除的内容。

index(all_docs, record_manager, vectorstore, cleanup="full", source_id_key="source")
{'num_added': 0, 'num_updated': 0, 'num_skipped': 1, 'num_deleted': 1}

Source

元数据属性包含一个称为source的字段。该来源应指出与给定文档相关的最终出处。

例如,如果这些文档代表某个父文档的块,则两个文档的source应该相同并引用父文档。

一般来说,应始终指定source。如果您从不打算使用incremental模式,并且由于某种原因无法正确指定source字段,则仅使用 None。

from langchain_text_splitters import CharacterTextSplitter
doc1 = Document(page_content="kitty kitty kitty kitty kitty", metadata={"source": "kitty.txt"}
)
doc2 = Document(page_content="doggy doggy the doggy", metadata={"source": "doggy.txt"})
new_docs = CharacterTextSplitter(separator="t", keep_separator=True, chunk_size=12, chunk_overlap=2
).split_documents([doc1, doc2])
new_docs
[Document(page_content='kitty kit', metadata={'source': 'kitty.txt'}),Document(page_content='tty kitty ki', metadata={'source': 'kitty.txt'}),Document(page_content='tty kitty', metadata={'source': 'kitty.txt'}),Document(page_content='doggy doggy', metadata={'source': 'doggy.txt'}),Document(page_content='the doggy', metadata={'source': 'doggy.txt'})]
_clear()
index(new_docs,record_manager,vectorstore,cleanup="incremental",source_id_key="source",
)
{'num_added': 5, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 0}
changed_doggy_docs = [Document(page_content="woof woof", metadata={"source": "doggy.txt"}),Document(page_content="woof woof woof", metadata={"source": "doggy.txt"}),
]

这应该删除与doggy.txt源关联的文档的旧版本,并将其替换为新版本。

index(changed_doggy_docs,record_manager,vectorstore,cleanup="incremental",source_id_key="source",
)
{'num_added': 2, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 2}
vectorstore.similarity_search("dog", k=30)
[Document(page_content='woof woof', metadata={'source': 'doggy.txt'}),Document(page_content='woof woof woof', metadata={'source': 'doggy.txt'}),Document(page_content='tty kitty', metadata={'source': 'kitty.txt'}),Document(page_content='tty kitty ki', metadata={'source': 'kitty.txt'}),Document(page_content='kitty kit', metadata={'source': 'kitty.txt'})]

Using with loaders

索引可以接受文档的可迭代或任何加载器。

from langchain_community.document_loaders.base import BaseLoaderclass MyCustomLoader(BaseLoader):def lazy_load(self):text_splitter = CharacterTextSplitter(separator="t", keep_separator=True, chunk_size=12, chunk_overlap=2)docs = [Document(page_content="woof woof", metadata={"source": "doggy.txt"}),Document(page_content="woof woof woof", metadata={"source": "doggy.txt"}),]yield from text_splitter.split_documents(docs)def load(self):return list(self.lazy_load())
_clear()
loader = MyCustomLoader()
loader.load()
[Document(page_content='woof woof', metadata={'source': 'doggy.txt'}),Document(page_content='woof woof woof', metadata={'source': 'doggy.txt'})]
index(loader, record_manager, vectorstore, cleanup="full", source_id_key="source")
{'num_added': 2, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 0}
vectorstore.similarity_search("dog", k=30)
[Document(page_content='woof woof', metadata={'source': 'doggy.txt'}),Document(page_content='woof woof woof', metadata={'source': 'doggy.txt'})]

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

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

相关文章

WM8978 —— 带扬声器驱动程序的立体声编解码器(2)

接前一篇文章:WM8978 —— 带扬声器驱动程序的立体声编解码器(1) 六、引脚详细说明 引脚(PIN)名称(NAME)类型(TYPE)描述(DESCRIPTION)1LIP模拟输入…

无极低码SQL模板引擎使用教程示例,自己手撸一个sql模板引擎进行动态sql生成。

无极低码 :https://wheart.cn 无极低码SQL模板使用教程 一、模板结构与规则 无极低码SQL模板通过简洁的Markdown格式,使SQL语句具有更强的灵活性和适应性,简化了根据业务需求定制SQL的过程。 无极低码SQL模板是一种基于Markdown格式的特殊…

006、Dynamo Python 之Revit元素类别

今天我们来聊聊 Revit 元素这点事,不仅仅是在 Dynamo Python 之中涉及,我们在日常使用 Revit 的时候,也涉及这个问题,只是对我们日常画图没什么影响,所以很多人并没太在意这块。 Revit Elements 分为六个组&#xff1a…

Redis实战篇-4

实战篇Redis 1.3 、实现发送短信验证码功能 页面流程 具体代码如下 贴心小提示: 具体逻辑上文已经分析,我们仅仅只需要按照提示的逻辑写出代码即可。 发送验证码 Overridepublic Result sendCode(String phone, HttpSession session) {// 1.校验手机…

第6章 准时与专注

< 回到目录 第6章 准时与专注 自己预定的会议一定要准时参加。如果无法准时出席呢&#xff1f;当你意识到自己即将迟到时&#xff0c;必须第一时间通知所有与会人员&#xff0c;让大家有充分的准备。这是基本的社交礼仪&#xff0c;更重要的&#xff0c;它还能最大限度地减…

算法打卡day15

今日任务&#xff1a; 1&#xff09;110.平衡二叉树 2&#xff09;257. 二叉树的所有路径 3&#xff09;404.左叶子之和 110.平衡二叉树 题目链接&#xff1a;110. 平衡二叉树 - 力扣&#xff08;LeetCode&#xff09; 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树…

基于大数据的空气质量预测和可视化分析

城市空气质量数据采集系统设计与实现 &#x1f3d9;️ 研究背景 &#x1f32c;️ 城市化与环境挑战&#xff1a;随着城市化进程的加快&#xff0c;环境污染问题&#xff0c;尤其是空气质量问题&#xff0c;已成为公众关注的焦点。数据监测的重要性&#xff1a;城市空气质量数…

控价其实是对品牌市场的保护

品牌发展过程中&#xff0c;如果有越来越多的经销商加入&#xff0c;必然要做好控价&#xff0c;否则渠道的混乱&#xff0c;会使得品牌价值受损&#xff0c;比如低价的出现&#xff0c;会影响正规经销商的出货&#xff0c;使其竞争力增加&#xff0c;同时价格的不稳定会连带产…

小游戏-扫雷

扫雷大多人都不陌生&#xff0c;是一个益智类的小游戏&#xff0c;那么我们能否用c语言来编写呢&#xff0c; 我们先来分析一下扫雷的运行逻辑&#xff0c; 首先&#xff0c;用户在进来时需要我们给与一个菜单&#xff0c;以供用户选择&#xff0c; 然后我们来完善一下&#…

Vue 实现带拖动功能的时间轴

1.效果图 2. 当使用timeline-slider-vue组件时&#xff0c;你可以设置以下属性&#xff1a; date&#xff1a;用于设置时间轴滑块的初始日期&#xff0c;格式通常为 YYYY-MM-DD。 mask&#xff1a;一个布尔值&#xff0c;用于控制是否显示背景遮罩。 markDate&#xff1a;一…

Java 面试宝典:什么是大 key 问题?如何解决?

大家好&#xff0c;我是大明哥&#xff0c;一个专注「死磕 Java」系列创作的硬核程序员。 本文已收录到我的技术网站&#xff1a;https://skjava.com。有全网最优质的系列文章、Java 全栈技术文档以及大厂完整面经 回答 Redis 大 key 问题是指某个 key 对应的 value 值很大&am…

链表去重介绍

链表去重是数据结构中一个常见的问题&#xff0c;它涉及到遍历链表、比较节点值以及修改链表结构等操作。链表去重的目的是删除链表中重复的元素&#xff0c;使得每个元素在链表中只出现一次。这个问题在实际应用中具有广泛的用途&#xff0c;比如在处理用户数据、优化搜索引擎…

C语言——sizeof与strlen的对比

一.sizeof 我们在学习操作符的时候&#xff0c;就了解到了sizeof操作符&#xff0c;它的作用是求参数所占内存空间的大小&#xff0c;单位是字节。如果参数是一个类型&#xff0c;那就返回参数所占的字节数。 #include <stdio.h>int main() {int a 10;size_t b sizeo…

Mamba 基础讲解【SSM,LSSL,S4,S5,Mamba】

文章目录 Mamba的提出动机TransformerRNN Mama的提出背景状态空间模型 (The State Space Model, SSM)线性状态空间层 (Linear State-Space Layer, LSSL)结构化序列空间模型 &#xff08;Structured State Spaces for Sequences, S4&#xff09; Mamba的介绍Mamba的特性一&#…

美团2024届秋招笔试第二场编程真题

要么是以0开头 要么以1开头 选择最小的答案累加 import java.util.Scanner; import java.util.*; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和…

C# 右键快捷菜单(上下文菜单)的两种实现方式

在C#中&#xff0c;ContextMenuStrip是一种用于创建右键菜单的控件。它提供了一种方便的方式来为特定的控件或窗体添加自定义的上下文菜单选项。有两种实现方式&#xff0c;如下&#xff1a; 一.通过ContextMenuStrip控件实现 1.从工具箱中拖一个ContextMenuStrip控件到窗体上…

ROS2 高效学习系列

ROS2 高效学习系列 1 说明2 正文 1 说明 2023 年&#xff0c;我们总结输出了 ROS高效入门系列 和 ROS高效进阶系列&#xff0c;系统学习了 ros1 基础知识和 ros 的机器人算法。由于 ros2 的普及&#xff0c;我们将系统学习 ros2 &#xff0c;包括 ros2 基础知识和相关高级组件…

LLM - 大语言模型的分布式训练 概述

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/136924304 大语言模型的分布式训练是一个复杂的过程&#xff0c;涉及到将大规模的计算任务分散到多个计算节点上。这样做的目的是为了处…

Python学习笔记01

第一章、你好Python 初识Python Python的起源 1989年&#xff0c;为了打发圣诞节假期&#xff0c;Gudiovan Rossum吉多范罗苏姆(龟叔)决心开发一个新的解释程序(Python雏形) 1991年&#xff0c;第一个Python解释器诞生 Python这个名字&#xff0c;来自龟叔所挚爱的电视剧M…

领域、系统和组织-《实现领域驱动设计》中译本评点-第2章(4)

相关链接 DDD领域驱动设计批评文集>> 汪峰哭晕在厕所-《实现领域驱动设计》中译本评点-第2章&#xff08;1&#xff09; 可不是乱打的-《实现领域驱动设计》中译本评点-第2章&#xff08;2&#xff09; “领域”的错误定义-《实现领域驱动设计》中译本评点-第2章&…