如何解决 RAG 在处理超长文档时丢失中间上下文的问题?
直接把 50 页的 PDF 怼进 RAG 流程,最典型的症状就是「中间丢失」:模型能记得开头和结尾,但中间那几页的关键逻辑直接被过滤掉了。这本质上是 Embedding 检索的局限性,单纯靠向量相似度,很容易在海量分片(Chunks)中丢失语义连续性。
当 AI 检索到子块时,实际喂给 LLM 的是其对应的父块。
我最近在处理一份技术白皮书时,通过「层级索引 + 窗口重叠」方案解决了这个问题。
核心操作步骤:
1. 调整分片策略,引入 Overlap
不要用简单的固定长度切割。我把 chunk_size 设为 512,但 chunk_overlap 必须给到 100-150 个 token。这样能保证每个片段都携带一部分前文,避免语义在切割点被腰斩。
2. 构建父子索引(Parent Document Retrieval)
这是解决上下文丢失的杀手锏。不要直接检索最小的片段,而是建立一套映射:
- 子块(Child Chunk):小粒度(如 128 token),用于高精度向量检索。
- 父块(Parent Chunk):大粒度(如 1024 token),包含子块及其周围的上下文。
当 AI 检索到子块时,实际喂给 LLM 的是其对应的父块。
3. 配置 LangChain 的 ParentDocumentRetriever
如果你用 LangChain,可以参考这个配置逻辑:
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
# 初始化存储和分割器
store = InMemoryStore()
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1000)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=200)
# 绑定向量库和存储
vectorstore = Chroma(embedding_function=OpenAIEmbeddings())
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter,
)踩过的坑:
之前尝试过直接增加 k 值(检索更多片段),结果导致 Prompt 长度激增,反而触发了 LLM 自身的「Lost in the Middle」现象(模型对长文本中间部分的关注度下降)。后来发现,精准地提供一段长上下文,比提供十段碎片化短上下文效果好得多。
效率提升点:
在使用 Cursor 调试这段代码时,我发现直接让它写 ParentDocumentRetriever 容易写出旧版本 API。建议在 .cursorrules 里明确指定 LangChain 的版本号,或者直接把最新的 API 文档贴给它,这样生成的代码一次性运行通过率更高。
现在的效果是,即使问题涉及文档中间页的细节,AI 也能通过父块索引还原出完整的论证逻辑,不再出现「文档中未提及」的幻觉。
免费 AI 工具箱 · 全部完全免费
全部回复 (0)
还没有回复,来发第一条吧!
