Hierarchical RAG分层检索增强生成以父子块结构平衡检索精度与生成上下文的完整实践指南【免费下载链接】ottomator-agentsAll the open source AI Agents hosted on the oTTomator Live Agent Studio platform!项目地址: https://gitcode.com/GitHub_Trending/ot/ottomator-agents分层检索Hierarchical RAG是 oTTomator Live Agent Studio 平台开源的 Advanced RAG 策略合集all-rag-strategies中编号为 09 的检索策略其核心思路是用小粒度子块child chunk做向量检索以保证匹配精度命中后用大粒度父块parent chunk回填上下文以供 LLM 生成。本文以 docs/09-hierarchical-rag.md 为骨架结合 examples/09_hierarchical_rag.py、PG Vector 表结构与仓库源码完整讲解分层索引的构建、检索链路、元数据增强与适用边界读完即可在 PostgreSQL pgvector Pydantic AI 环境中复现一套可运行的父子块 RAG 方案。什么是 Hierarchical RAG传统扁平 RAGflat RAG在分块大小上存在天然矛盾块越小语义越聚焦向量检索命中越精准但块内信息量不足以支撑 LLM 生成完整回答例如只看到净利润率提升到 12%却不知道这是哪个季度、哪份报告里的结论块越大上下文越充分但单个块的语义混杂检索噪声升高召回精度下降。Hierarchical RAG 用父子块parent-child结构同时解决这两个问题对子块做嵌入并参与检索命中后返回其父块作为生成上下文。子块负责找得准父块负责讲得全元数据metadata负责维护子块与父块之间的引用关系从而支撑对文档层级的快速导航。在 all-rag-strategies 的策略矩阵中它被标注为 Pseudocode OnlyREADME.md 策略总览表因为仓库的完整实现implementation/采用了 Agentic RAG语义检索 全文档拉取来达成类似目标而本策略以小于 50 行的可运行伪代码示例呈现聚焦展示父子块机制本身属于教育性、可落地的参考实现。核心机制只嵌入子块检索后回填父块原文档给出了一个非常直观的索引与检索示例完整继承如下# Index structure document { parent: Q2 Financial Report - Full Section, children: [ Revenue increased 3% to $314M, Operating costs decreased 5%, Net profit margin improved to 12% ] } # Embed only child chunks for child in document[children]: index.add(embed(child), metadata{parent_id: document[parent]}) # Retrieval query What was Q2 revenue? child_match vector_search(query) # Finds Revenue increased 3%... # Return parent context instead of just the child full_context get_parent(child_match.metadata[parent_id]) # LLM sees entire Q2 section for better reasoning这段示例揭示了分层 RAG 的三条设计要点嵌入只发生在子块上父块通常不生成向量或不参与相似度排序因为父块是上下文仓库而非检索入口元数据充当外键子块通过metadata{parent_id: ...}关联父块检索命中子块后凭该 ID 反查父块内容返回给 LLM 的是父块最终送入生成环节的是包含更广阔上下文章节、小节乃至整篇文档片段的父块而不是孤立的子块。实战基于 Pydantic AI 与 PG Vector 的完整实现仓库中的 examples/09_hierarchical_rag.py 给出了可运行的完整版本它使用 Pydantic AI 的agent.tool装饰器定义检索工具使用 PG VectorPostgreSQL 的余弦距离运算符做相似度搜索。全文不超过 60 行是理解该策略的最佳入口。分层索引Ingestion两级分块写入两张表def ingest_document(text: str, doc_title: str): # Create parent chunks (large sections) parent_chunks [text[i:i2000] for i in range(0, len(text), 2000)] with conn.cursor() as cur: for parent_id, parent in enumerate(parent_chunks): # Store parent with simple metadata metadata json.dumps({heading: f{doc_title} - Section {parent_id}, type: detail}) cur.execute(INSERT INTO parent_chunks (id, content, metadata) VALUES (%s, %s, %s), (parent_id, parent, metadata)) # Create child chunks from parent child_chunks [parent[j:j500] for j in range(0, len(parent), 500)] for child in child_chunks: embedding get_embedding(child) cur.execute( INSERT INTO child_chunks (content, embedding, parent_id) VALUES (%s, %s, %s), (child, embedding, parent_id) ) conn.commit()这里展示了两级分块的经典参数组合父块 2000 字符承载足够支撑推理的上下文子块 500 字符语义聚焦便于精准匹配每个父块内部再切分成若干子块子块通过parent_id外键指向父块父块写入时附带heading标题路径与type块类型元数据供检索后组装带上下文的返回结果。检索Retrieval搜子块、连父块、带标题返回agent.tool def search_knowledge_base(query: str) - str: Search children, return parents with heading context with conn.cursor() as cur: query_embedding get_embedding(query) # Find matching children and join with parent metadata cur.execute( SELECT p.content, p.metadata FROM child_chunks c JOIN parent_chunks p ON c.parent_id p.id ORDER BY c.embedding %s LIMIT 3, (query_embedding,) ) # Return parents with heading context results [] for content, metadata_json in cur.fetchall(): metadata json.loads(metadata_json) results.append(f[{metadata[heading]}]\n{content}) return \n\n.join(results) result agent.run_sync(What is backpropagation?) print(result.data)检索链路的关键是一句 SQLJOIN parent_chunks p ON c.parent_id p.id。向量排序只发生在child_chunks.embedding上ORDER BY c.embedding %s而返回的却是父块内容并在前面拼接[{heading}]标题路径。这样 LLM 拿到的每一条检索结果都自带它出自文档的哪个位置的结构信息回答时能明确引述来源章节。数据库表结构examples/README.md 中给出了分层结构对应的最小表结构CREATE TABLE parent_chunks (id INT PRIMARY KEY, content TEXT); CREATE TABLE child_chunks (id SERIAL PRIMARY KEY, content TEXT, embedding vector(768), parent_id INT);对比同一文件中给出的基础扁平块表CREATE TABLE chunks ( id SERIAL PRIMARY KEY, content TEXT, embedding vector(768) ); CREATE INDEX ON chunks USING ivfflat (embedding vector_cosine_ops);可以直观看到分层方案多出一张父表并在子表上以parent_id建立层级引用生产环境通常会为child_chunks(parent_id)建立外键约束与索引以加速 JOIN同时为 embedding 列建立ivfflat (embedding vector_cosine_ops)索引。仓库完整实现中的 implementation/sql/schema.sql 使用了vector(1536)维度对应 OpenAI text-embedding-3-small并提供了match_chunks()存储过程封装1 - (embedding query_embedding)的相似度计算——从源码结构看分层策略在真实系统中同样可以复用这套 pgvector 基础设施只需把单块表拆成父子双表即可。元数据增强让返回子块还是父块可决策原文档在策略进阶部分指出分层 RAG 可以通过更丰富的元数据进一步智能化。仓库 README 的策略 9 章节给出了具体设计见 README.mdMetadata EnhancementCan store metadata likesection_type(summary, table, detail) andheading_pathto intelligently decide when to return just the child vs. the parent, or to include heading context.也就是说父块元数据可以记录两类信息元数据字段取值示例用途section_typesummary/table/detail判断该块性质决定只返回子块还是回填父块heading_pathQ2 Report Financials Revenue携带标题层级路径供 LLM 理解上下文出处例如命中一个section_typesummary的子块时其自身可能已足够回答问题可以只返回子块命中detail型子块时则回填整个父块保证信息完整。这种按需决定返回粒度的能力是分层 RAG 相比固定块 RAG 的核心灵活性来源。优点与缺点原文档对利弊的总结非常精炼完整继承如下优点Pros在检索精度小块匹配与生成上下文大块回填之间取得平衡显著降低搜索噪声同时为推理提供足够的上下文对具有天然层级结构的文档章节、小节效果尤其突出。缺点Cons需要精心设计父子关系与切分粒度父块 2000 / 子块 500 这类参数需要按文档类型调优索引逻辑与检索逻辑都比扁平分块更复杂需要父子双表结构与元数据维护增加了数据库设计与写入成本。仓库 README 的性能对照表将 Hierarchical 策略标注为速度 ⚡⚡、成本 $、质量 ⭐⭐⭐⭐与简单分块质量 ⭐⭐相比质量收益明显且不依赖额外的 LLM 调用成本仅为一档属于性价比型增强策略。何时使用 / 何时不要使用推荐使用When to Use It小块匹配效果好、但单块上下文不足以支撑回答的场景如财务报告、技术手册、法律条文文档具有清晰层级结构章节 / 小节 / 段落切分边界天然存在需要让 LLM 明确答案出自哪个章节即对可溯源性有要求的问答系统。不推荐使用When NOT to Use It文档本身缺乏清晰层级结构如松散的短文本集合、聊天记录简单扁平分块已经能为你的用例提供足够上下文——此时引入父子结构只会徒增复杂度对索引吞吐要求极高、难以承担双表写入开销的轻量场景。仓库内的替代实现与对照参考从源码结构看all-rag-strategies 的完整实现implementation/刻意未单独实现分层 RAGimplementation/STRATEGIES.md 对此的说明是Hierarchical RAG (Parent-Child)Agentic RAG with full document retrieval provides similar benefits more flexibly.其替代方案是在 implementation/rag_agent_advanced.py 中由 Agent 先调用search_knowledge_base()小块语义搜索发现上下文不足时再调用retrieve_full_document()拉取完整文档。该函数通过SELECT title, content FROM documents WHERE title ILIKE $1按标题模糊匹配整篇文档并整体返回——这本质上是一种两级粒度的决策式检索只是把固定父子块换成了Agent 自主选择。此外仓库的分块层 implementation/ingestion/chunker.py 使用 Docling 的 HybridChunker 并调用contextualize(chunk)将标题层级heading hierarchy写入每个块的上下文中生成{heading_path}风格的上下文前缀。从源码结构看这一能力天然可以被分层 RAG 复用先用 Docling 解析出文档的章节层级再按层级边界切分为父块章节与子块段落元数据中的heading_path即可直接取自 Docling 的标题结构——这正是Context-Aware Chunking策略 7 Hierarchical RAG策略 9的组合路线也是将该策略从伪代码升级为生产实现的可行路径。总结Hierarchical RAG 的价值在于用一次索引期的结构化投入换取检索期的精准与生成期的完整双赢子块负责语义命中父块负责上下文供给元数据负责连接与决策。它在结构化的长文档财报、手册、规范场景下是介于扁平分块与Agentic 全文档拉取之间的理想折中方案。若想进一步深入可对照阅读同一仓库中的 examples/09_hierarchical_rag.py完整伪代码、examples/README.md表结构与通用模式、implementation/STRATEGIES.md未实现原因与替代方案以及 implementation/sql/schema.sql生产级 pgvector 模式并结合 docs/08-late-chunking.md 等相邻策略文档进行横向对比选型。【免费下载链接】ottomator-agentsAll the open source AI Agents hosted on the oTTomator Live Agent Studio platform!项目地址: https://gitcode.com/GitHub_Trending/ot/ottomator-agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考