[Bug]: Sync recursive retrieval misses `ref_doc_id` in dedup key

用户在使用 BaseRetriever 的同步 retrieve() 方法进行递归检索时触发此问题。当检索结果中包含两个 文本内容完全相同 但 来源文档不同 的节点时,同步路径会错误地将第二个节点丢弃(仅保留一个),而异步路径 aretrieve() 则能正确返回两个节点。

[Bug]: Sync recursive retrieval misses `ref_doc_id` in dedup key

[Bug]: Sync recursive retrieval misses ref_doc_id in dedup key

快速结论:该 Bug 发生在 LlamaIndex 同步递归检索时,只使用 node.hash 进行去重,导致来自不同源文档但文本内容相同的节点被错误丢弃。而异步版本已正确使用 (node.hash, node.ref_doc_id) 作为复合去重键。优先排查你当前使用的 base_retriever.py_handle_recursive_retrieval 方法的去重逻辑。

问题场景

用户在使用 BaseRetriever 的同步 retrieve() 方法进行递归检索时触发此问题。当检索结果中包含两个文本内容完全相同来源文档不同的节点时,同步路径会错误地将第二个节点丢弃(仅保留一个),而异步路径 aretrieve() 则能正确返回两个节点。

报错原文

# 同步路径输出(错误)
Sync: 1 node(s)
  ref_doc_id=doc-1 score=0.9

# 异步路径输出(正确)
Async: 2 node(s)
  ref_doc_id=doc-1 score=0.9
  ref_doc_id=doc-2 score=0.8

注:这不属于运行时异常报错,而是业务逻辑 Bug 导致的结果不正确。

原因分析

LlamaIndex 的两个递归检索方法在去重键的定义上不一致:

  • 同步路径 _handle_recursive_retrieval 仅使用 node.hash 作为去重依据:
    if not (n.node.hash in seen or seen.add(n.node.hash))
  • 异步路径 _ahandle_recursive_retrieval 使用 (node.hash, node.ref_doc_id) 作为复合去重键:
    if not ((n.node.hash, n.node.ref_doc_id) in seen or seen.add((n.node.hash, n.node.ref_doc_id)))
  • 异步版本中有一行注释明确说明 # remove any duplicates based on hash and ref_doc_id,表明这是有意的设计决策,但同步路径在 PR #14383 更新后未被同步修改。

当两个节点的 node.hash 相同(即文本和元数据完全一致)但 ref_doc_id 不同时,同步路径会将其视为重复并丢弃一个。

环境排查

  • LlamaIndex 版本:0.14.17(包含此 Bug 的版本范围),建议检查当前使用的版本号。
  • 涉及文件:llama-index-core/llama_index/core/base/base_retriever.py 中的 _handle_recursive_retrieval 方法(约第 177-182 行)和 _ahandle_recursive_retrieval 方法(约第 210-217 行)。

解决步骤

  1. 定位源代码文件:找到你环境中的 base_retriever.py,通常位于 llama_index/core/base/base_retriever.py
  2. 修改同步去重逻辑:_handle_recursive_retrieval 方法中,找到去重条件语句,将 seen 集合的存储键从 n.node.hash 改为 (n.node.hash, n.node.ref_doc_id) 复合键。
    修改前:
    if not (n.node.hash in seen or seen.add(n.node.hash))
    修改后(与异步版本保持一致):
    if not ((n.node.hash, n.node.ref_doc_id) in seen or seen.add((n.node.hash, n.node.ref_doc_id)))
  3. 重启应用:保存修改后,重启你的 Python 应用或服务使更改生效。
  4. 替代方案(可优先尝试):如果你不希望直接修改框架源码,可以考虑暂时改用 aretrieve() 方法进行递归检索,异步版本不受此 Bug 影响。

验证方法

使用 Issue 中提供的复现代码片段,创建一个包含两个文本相同但来源文档不同的节点的检索器,分别调用同步 retrieve() 和异步 aretrieve(),确认同步路径也能返回 2 个节点(各带正确的 ref_doc_id),输出应与异步路径一致。

参考来源

run-llama/llama_index #21033

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

celebrityanime
celebrityanime
文章: 7857

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注