![[Bug]: Sync recursive retrieval misses `ref_doc_id` in dedup key](https://www.chat-gpts.plus/wp-content/uploads/2026/06/21033-2bfb3103.jpg)
[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 行)。
解决步骤
- 定位源代码文件:找到你环境中的
base_retriever.py,通常位于llama_index/core/base/base_retriever.py。 - 修改同步去重逻辑:在
_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))) - 重启应用:保存修改后,重启你的 Python 应用或服务使更改生效。
- 替代方案(可优先尝试):如果你不希望直接修改框架源码,可以考虑暂时改用
aretrieve()方法进行递归检索,异步版本不受此 Bug 影响。
验证方法
使用 Issue 中提供的复现代码片段,创建一个包含两个文本相同但来源文档不同的节点的检索器,分别调用同步 retrieve() 和异步 aretrieve(),确认同步路径也能返回 2 个节点(各带正确的 ref_doc_id),输出应与异步路径一致。



