如何解决长文档切片后 Embedding 检索丢失上下文的问题
我之前在用 LangChain 做一个技术文档库,发现直接用 RecursiveCharacterTextSplitter 效果极差,很多细节在检索时因为缺乏上下文而被 LLM 误读。为了解决这个问题,我尝试了三种实操方案,效率提升最明显的是父子索引(Parent Document Retrieval)。
核心逻辑: 存储时用小切片(Child)做 Embedding 保证检索精度,但喂给 LLM 时,通过 ID 映射直接拉取该切片所属的大段落或整页(Parent)。
具体配置步骤如下:
1. 建立双层索引结构
不要把切片直接存入向量库,而是先存入一个 Key-Value 存储(如 Redis 或本地 JSON),用 ID 关联。
# 伪代码逻辑:将大文档存入 docstore,小切片存入 vectorstore
parent_id = store.add(full_document)
for chunk in split_into_small_chunks(full_document):
vectorstore.add(chunk, metadata={"parent_id": parent_id})2. 检索时的映射回溯
检索时先搜小块,拿到 parent_id 后,立即从 store 中取出原文档。
# 检索流程
retrieved_docs = vectorstore.similarity_search(query, k=3)
# 关键步骤:用 child 的 metadata 换回 parent 的完整内容
final_context = [store.get(doc.metadata["parent_id"]) for doc in retrieved_docs]除了父子索引,如果不想搞太复杂的架构,滑动窗口重叠(Overlap)是最低成本的补救方案。但千万别随便设 10% 或 20%,建议根据文档的平均段落长度,将 chunk_overlap 设置在 200-300 token 之间。
踩坑提醒:
很多开发者习惯在 Prompt 里写“请根据上下文回答”,但如果你的切片本身就丢失了主语(例如:第一段说“这款产品”,第二段说“它支持...”,你刚好搜到了第二段),LLM 无论怎么努力也猜不到“它”是谁。
进阶技巧:上下文注入(Contextual Retrieval)
最近在试一种更激进的方法:在切片之前,先让 Claude 把整个文档总结成 200 字的摘要,然后将这个摘要强行拼在每个切片的前面。
[文档全局摘要:本文是关于 X 框架的部署指南,重点讨论了 K8s 集群的配置] + [具体的切片内容]这样即使只检索到一个片段,LLM 也能通过前缀知道当前内容在全局中的位置。这种方法虽然增加了 Token 消耗,但检索准确率在我的测试集里提升了约 15%。
全部回复 (0)
还没有回复,来发第一条吧!
