父子分块与由小到大的检索
存储较小的子分块以实现精准检索,但将其较大的父分块返回给 LLM 以提供更丰富的上下文,在检索精度与生成质量之间取得平衡。
父子分块与由小到大的检索 是 CoddyKit 上的免费 AI Engineering Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Engineering Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Engineering Academy 课程共包含 4 节课。
精确率与上下文的两难
RAG 系统面临一个矛盾:小分块聚焦于单一想法,因此能够以较高精确率被检索到,但缺少 LLM 生成完整答案所需的周围上下文。大分块提供丰富的上下文,却会降低检索精确率,因为它们只是与许多查询弱匹配,而不是与某一个查询强匹配。父子分块解决了这一难题。
父子架构
在父子分块中,您会从同一份文档创建两层分块。子分块较小(例如 1–3 个句子),会被生成嵌入并建立索引以供检索。父分块是更大的部分(例如完整段落或页面),会被单独存储。检索到子分块后,返回给 LLM 的不是子分块,而是它的父分块。
构建分块层级
第一步是将文档拆分成较大的父分块,然后将每个父分块再拆分成较小的子分块。每个子分块都会保留一个引用,通常是 parent_id 元数据字段,用于指向其父分块。借助这种映射,您可以根据任意检索到的子分块查找完整的父级段落。
from langchain.text_splitter import RecursiveCharacterTextSplitter
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=0)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)
parent_chunks = parent_splitter.split_documents(docs)
child_chunks = []
for i, parent in enumerate(parent_chunks):
children = child_splitter.split_documents([parent])
for child in children:
child.metadata['parent_id'] = i
child_chunks.extend(children)仅为子分块建立索引
只有子分块会被生成嵌入并存储在向量数据库中。父分块会存储在单独的键值存储中(内存字典、Redis 或文档数据库)。这样既能让向量索引保持紧凑和精确,又能将丰富的上下文存放在索引之外。
# Store parents in a docstore
parent_store = {i: chunk.page_content for i, chunk in enumerate(parent_chunks)}
# Embed and index only children
vectorstore = Chroma.from_documents(
child_chunks,
embedding=OpenAIEmbeddings()
)检索:输入子分块,输出父分块
检索过程中,系统会为用户查询生成嵌入,并将其与子分块进行匹配。找到排名前 k 的子分块后,通过查找父级存储来解析它们的 parent_id 引用。随后注入 LLM 提示的是父级段落,而不是子分块。LLM 获得了广泛的上下文,而检索过程保持了精确性。
def retrieve_with_parents(query, vectorstore, parent_store, k=5):
child_results = vectorstore.similarity_search(query, k=k)
seen_parent_ids = set()
parent_contexts = []
for child in child_results:
pid = child.metadata['parent_id']
if pid not in seen_parent_ids:
parent_contexts.append(parent_store[pid])
seen_parent_ids.add(pid)
return parent_contextsLangChain ParentDocumentRetriever
LangChain 提供了开箱即用的 ParentDocumentRetriever 类来实现这一模式。您需要提供父级分割器、子级分割器、用于存储子级嵌入的向量存储,以及用于存储父级文档的文档存储。它会建立层次结构,并透明地处理检索。
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter
)
retriever.add_documents(docs)
results = retriever.invoke('What is the refund policy?')小到大检索详解
小到大检索是同一概念的另一种称呼:先检索较小且精确的文本块,然后在发送给 LLM 之前,将其扩展为周围的上下文。有些实现不会扩展到固定的父级文本块,而是扩展到相邻句子的窗口,为模型提供匹配文本块之前和之后的句子,从而保持上下文的连贯性。
def retrieve_with_window(query, vectorstore, sentences, window=2, k=5):
results = vectorstore.similarity_search(query, k=k)
expanded = []
for r in results:
idx = r.metadata['sentence_index']
start = max(0, idx - window)
end = min(len(sentences), idx + window + 1)
expanded.append(' '.join(sentences[start:end]))
return expanded对父级文本块去重
针对一次查询,可能会检索到来自同一父级的多个子级文本块。如果不去重,同一段父级内容就会在提示中出现多次,浪费令牌。在组装上下文之前,务必按父级 ID 去重。上方检索函数中的代码示例通过 seen_parent_ids 集合实现了这一点。
何时使用父子级分块
当文档具有清晰的层次结构时,父子级检索的效果最佳,例如包含章节和节的文档、包含段落的文章,或包含子节的维基。对于较长的技术文档,它尤其有效:精确的问题需要定位明确的答案,但这些答案只有放在更广泛的上下文中才有意义。
选择子级和父级的大小
一种典型配置是:包含 200-400 个令牌的子级文本块(聚焦于单一思想)和包含 1000-2000 个令牌的父级文本块(完整的节)。如果子级文本块过小,就会变成脱离上下文便缺乏意义的单个句子。如果父级文本块过大,就会重新引入您原本想要避免的上下文稀释问题。
方法比较:总结
总结目前介绍的分块策略:固定大小分块速度快,但会破坏上下文;语义分块能够保持主题连贯性;父子级分块则同时优化检索精确度和 LLM 上下文的丰富程度。对于大多数处理长文档的生产环境 RAG 系统,父子级分块能够在复杂度可控的情况下提供最佳的检索质量。
快速检查
测试您对本课父子级分块内容的理解。
课程回顾
本课中您学到了:子级文本块提供精确的检索,而父级文本块提供丰富的上下文;LangChain 的 ParentDocumentRetriever 会自动实现这一过程;以及按父级 ID 去重可以避免上下文重复。接下来,我们将探索针对代码文件和 HTML 文档的特定文档分块策略。
用 AI 导师学习 Python — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 30
- 课程
- 120
常见问题解答
「父子分块与由小到大的检索」课时是免费的吗?
是的 — 「父子分块与由小到大的检索」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Engineering Academy 课程的其余内容,请升级到 CoddyKit PRO。 AI Engineering Academy 课程共包含 4 节课。
「父子分块与由小到大的检索」这节课中我会学到什么?
存储较小的子分块以实现精准检索,但将其较大的父分块返回给 LLM 以提供更丰富的上下文,在检索精度与生成质量之间取得平衡。 你通过在浏览器中直接运行的动手代码来练习 AI Engineering Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 AI Engineering Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 AI Engineering Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「父子分块与由小到大的检索」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 AI Engineering Academy 课中编写并运行代码吗?
能。每节 AI Engineering Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 朴素分块为何会损害检索效果
- 使用嵌入相似度进行语义分块
- 父子分块与由小到大的检索
- 针对代码和 HTML 的文档专用策略