(17-6-07)检索增强生成(RAG):时间加权向量存储检索器

5.6.10  时间加权向量存储检索器

时间加权向量存储检索器(TimeWeightedVectorStoreRetriever)是LangChain中的一个高级检索工具,在检索过程中结合了语义相似性和时间衰减(time decay)两个因素。这种检索器的评分算法如下:

语义相似性 + (1.0 - 衰减率) ^ 过去小时数

这里的“过去小时数”指的是自检索器中的对象最后一次被访问以来经过的小时数,而不是自对象创建以来的时间。这意味着频繁访问的对象会保持“新鲜”。

在实际应用中,可以通过设置不同的衰减率来控制对象的“记忆”持续时间,具体说明如下:

  1. 当衰减率较低时(例如,接近0),对象的“记忆”会持续更长时间。如果衰减率为0,则意味着对象永远不会被遗忘,这使得检索器等同于普通的向量查找。
  2. 如果设置较高的衰减率(例如,多个9),对象的新鲜度得分会迅速降至0。如果将衰减率设置为1,则所有对象的新鲜度在一段时间后都会变为0,这同样使得检索器等同于向量查找。

此外,LangChain提供了一些实用工具来模拟时间组件,这对于测试和调试非常有用。通过使用这些工具,可以模拟不同的时间点来观察检索器的行为,而不需要等待实际的时间流逝。

总的来说,时间加权向量存储检索器为LangChain提供了一种灵活的方法来处理和检索文档,它可以根据文档的访问频率和时间来调整检索结果,从而为用户提供更相关和“新鲜”的信息。

请看下面的例子,展示了在LangChain中使用TimeWeightedVectorStoreRetriever的过程。这是一个考虑了时间因素的向量存储检索器,通过结合语义相似性和文档的最后访问时间来对检索结果进行评分和排序。另外,本实例还演示了使用mock_now工具来模拟不同的时间点,以测试检索器在不同时间条件下的行为的用法。

实例5-14对检索结果进行评分和排序(源码路径:codes\5\jian14.py

实例文件jian14.py的具体实现流程如下所示。

(1)首先初始化了一个OpenAIEmbeddings模型作为文档的嵌入生成器,并创建了一个FAISS索引作为向量存储的基础。接着,创建了一个TimeWeightedVectorStoreRetriever实例,设置了一个非常低的衰减率,这样文档的最后访问时间对检索结果的影响就会非常小。

# 定义嵌入模型
embeddings_model = OpenAIEmbeddings()

# 初始化向量存储的索引
embedding_size = 1536
index = faiss.IndexFlatL2(embedding_size)

# 初始化向量存储,初始为空
vectorstore = FAISS(embeddings_model, index, InMemoryDocstore({}), {})

(2)然后,通过retriever.get_relevant_documents方法对查询"hello world"进行第1次检索,这次使用较低的衰减率。

# 初始化时间加权向量存储检索器
retriever = TimeWeightedVectorStoreRetriever(
    vectorstore=vectorstore, decay_rate=0.0000000000000000000000001, k=1
)
retriever.add_documents([Document(page_content="hello foo")])
# 查询"hello world",第一个返回"Hello World"是最相关的,因为衰减率接近0,表示它仍然是最新的
retriever.get_relevant_documents("hello world")

# 查询"hello world",第一个返回"Hello World"是最相关的,因为衰减率接近0,表示它仍然是最新的

retriever.get_relevant_documents("hello world")

执行后会输出:

[Document(page_content='hello world', metadata={'last_accessed_at': datetime.datetime(2023, 12, 27, 15, 30, 18, 457125), 'created_at': datetime.datetime(2023, 12, 27, 15, 30, 8, 442662), 'buffer_idx': 0})]

上面的输出结果表明检索器返回了一个与查询最相关的文档,它的最后访问时间是2023年12月27日,并且由于检索器的配置,这个文档在当前的检索环境中被认为是“新鲜”的。这可能是由于检索器的衰减率设置得很低,或者是因为last_accessed_at时间相对较近。

(3)通过retriever.get_relevant_documents方法对查询"hello world"进行第2次检索,将第2次将衰减率设置为0.999,这是一个非常高的衰减率,意味着文档的最后访问时间对结果的影响会非常大。

# 重新初始化检索器,设置衰减率为0.999
retriever = TimeWeightedVectorStoreRetriever(
    vectorstore=vectorstore, decay_rate=0.999, k=1
)
retriever.add_documents([Document(page_content="hello foo")])
# 使用相同的查询"hello world","Hello Foo"是最相关的,因为"hello world"文档已经被遗忘了
retriever.get_relevant_documents("hello world")

在上述代码中,由于设置了高衰减率(0.999),先前添加的"hello world"文档的相关性会迅速降低,因此不会被检索器返回。相反,新添加的"hello foo"文档因为没有过去的访问记录,所以在当前的查询中被认为是最相关的。执行后会输出:

[Document(page_content='hello foo', metadata={'last_accessed_at': datetime.datetime(2023, 12, 27, 15, 30, 50, 57185), 'created_at': datetime.datetime(2023, 12, 27, 15, 30, 44, 720490), 'buffer_idx': 1})]

(4)最后,使用mock_now工具来模拟一个特定的时间点(2024年2月3日10点11分),并在这个时间点下执行一次检索。由于mock_now工具的作用,检索器会认为"Hello World"文档的最后访问时间相对较新,因此它会被返回为最相关的结果。

# 模拟最后访问时间为2024年2月3日10点11分
with mock_now(datetime.datetime(2024, 2, 3, 10, 11)):
    # 再次进行查询,观察到"Hello World"文档被返回为最相关的,因为此时的最后访问时间仍然较新
    print(retriever.get_relevant_documents("hello world"))

执行后将显示一个Document对象,该对象的last_accessed_at字段被更新为模拟的时间,即MockDateTime(2024, 2, 3, 10, 11)。这意味着在这个模拟时间点,"hello world"文档被认为刚刚被访问过,因此它在检索结果中被返回为最相关的文档。执行后会输出:

[Document(page_content='hello world', metadata={'last_accessed_at': MockDateTime(2024, 2, 3, 10, 11), 'created_at': datetime.datetime(2023, 12, 27, 15, 30, 44, 532941), 'buffer_idx': 0})]

本实例展示了TimeWeightedVectorStoreRetriever灵活地处理时间因素,以及如何通过模拟时间来测试检索器行为的用法。这种方法可以帮助开发者理解和优化检索器的性能,确保它在实际应用中能够根据文档的新鲜度和相关性提供最佳的检索结果。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值