1. 从“向量检索”到“推理式检索”PageIndex 到底想解决什么RAG 这个词在过去两年被聊烂了。但凡做过 LLM 应用的人手里都有一套“文档切片 → 向量化 → 存向量数据库 → 相似度召回 → 塞进 prompt”的流水线。这套东西能跑但跑久了你会发现一个很尴尬的事实召回率上不去答案质量就上不去而向量检索的天花板比想象中低得多。我自己踩过的坑很典型。一份 80 页的技术白皮书用户问“第三章提到的那个限流策略和第五章的降级方案是怎么配合的”。向量检索会把“限流”“降级”相关的片段各召回几段但这两段之间的逻辑关系——谁先触发、谁兜底、阈值怎么联动——全丢了。因为向量检索本质上是“按语义相似度找孤立片段”它不理解文档的结构也不理解片段之间的因果链。PageIndex 这个项目核心思路就是冲着这个痛点去的。它不把文档当成一堆待切片的文本而是先把文档解析成一棵带层级的树章节、小节、段落、表格然后让 LLM 在这棵树上做推理式检索——像人翻书一样先看目录定位到相关章节再逐层下钻到具体段落最后把命中的节点连同它的上下文路径一起交给生成模型。关键词里出现的 RAG、LLM、SDK、向量数据库基本勾勒出了它的技术坐标它是一个 RAG 框架底层依赖 LLM 做推理对外提供 SDK 接入并且和向量数据库是互补而非替代的关系。热搜词里“rag瓶颈”“rag hit rate”“ontology rag”这些说明大家真正焦虑的是召回质量和知识组织方式而不是再搭一个 demo。这篇文章适合两类人看一类是已经搭过基础 RAG、但被召回率折磨过的工程师另一类是想理解“推理式检索”和“向量检索”到底差在哪、值不值得迁移的技术决策者。我会把 PageIndex 的设计逻辑、核心机制、实操接入、参数取舍、踩坑经验全部拆开讲尽量让你看完能直接上手复现。2. 核心设计拆解为什么是“树 推理”而不是“切片 向量”2.1 向量检索的三个结构性缺陷先把问题说透不然没法理解 PageIndex 的设计动机。向量检索在工程上很成熟但它在知识组织这件事上有三个绕不过去的缺陷。第一个是上下文断裂。切片是物理切割一个完整的论证被切成三段每段单独向量化之后语义都变得不完整。你召回的是“碎片”不是“知识单元”。第二个是结构信息丢失。文档的标题层级、章节归属、表格与正文的对应关系在切片那一刻就没了。模型拿到一段文字不知道它属于哪一章、上一节讲了什么、这个结论的前提是什么。第三个是检索粒度僵化。切片大小是预设的512 token 也好1024 token 也好都是拍脑袋定的。但用户的问题粒度是变化的有时候要一个精确数字有时候要一整节的论证逻辑。固定粒度必然在某些问题上召回过多、在另一些问题上召回不足。提示如果你现在的 RAG 系统在“跨章节推理”“多跳问答”“表格数值查询”这三类问题上表现很差那基本可以确认是检索层的结构缺陷而不是生成模型的问题。换更大的模型解决不了。2.2 PageIndex 的树形索引是怎么建的PageIndex 的第一步不是切片是解析文档结构。它会把 PDF、Markdown、Word 这类文档解析成一棵层级树每个节点带几个关键属性节点类型章节、小节、段落、表格、图注、列表项层级路径比如3.2.1以及它的父节点链原始文本该节点的完整内容不切断摘要由 LLM 为该节点生成的一句话概括用于快速筛选子节点引用指向下一层节点这棵树建好之后检索就变成了在树上做路径搜索。用户提问时LLM 先读根节点的子节点摘要相当于看目录判断哪些分支可能相关然后进入相关分支继续读下一层摘要逐层收敛直到定位到具体的叶子节点把完整内容取出来。这个过程和向量检索最大的区别是它保留了节点之间的父子关系和兄弟关系。当模型定位到“3.2.1 限流阈值配置”这个节点时它同时知道这个节点属于“3.2 限流策略”而“3.2”又属于“第三章 稳定性设计”。这些路径信息会一起进入生成阶段的上下文模型就能理解“这个阈值是在稳定性设计的大框架下、作为限流策略的一部分”被提出的。2.3 推理式检索 vs 向量检索一张表说清差异维度向量检索PageIndex 推理式检索索引单元固定长度切片结构化节点章节/段落/表格检索方式语义相似度 Top-KLLM 逐层推理下钻上下文保留仅切片内文本完整层级路径 兄弟节点多跳问答弱依赖重排强天然支持跨节点推理表格处理易被切碎整表作为节点保留延迟低毫秒级较高多次 LLM 调用成本低较高推理 token 消耗适合场景海量文档粗召回高价值文档精检索这张表不是要否定向量检索。实际工程里两者是互补的用向量检索做第一层粗筛从十万文档里选出 50 篇相关的再用 PageIndex 在这 50 篇里做精检索。热搜词里“向量数据库 选型”和“rag框架”同时出现说明很多人已经在做这种分层架构了。2.4 为什么这个设计能提升 hit rate“rag hit rate”是热搜里的高频词说明召回率是大家最关心的指标。PageIndex 提升 hit rate 的逻辑有三层。第一层是摘要筛选比向量相似度更准。节点摘要是 LLM 生成的它压缩了该节点的核心语义比原始文本的向量更能代表“这一节在讲什么”。用摘要做初筛误召和漏召都会减少。第二层是逐层收敛降低了噪声。向量检索是一次性 Top-KK 大了噪声多K 小了漏召回。推理式检索是逐层缩小范围每一层只保留相关分支最终命中的节点精度更高。第三层是路径上下文补全了语义。即使某个叶子节点本身文本不够完整它的父节点摘要和兄弟节点信息也能补全语义让生成模型有足够信息作答。3. 实操接入从零把 PageIndex 跑起来3.1 环境准备与依赖安装PageIndex 对外提供 SDK接入方式和主流 RAG 框架类似。下面是我实测下来比较稳的一套环境配置。# 建议用独立虚拟环境避免和现有 RAG 项目的依赖打架 python -m venv pageindex-env source pageindex-env/bin/activate # Windows 用 pageindex-env\Scripts\activate # 核心依赖 pip install pageindex-sdk pip install pymupdf # PDF 解析比 pdfplumber 快很多 pip install tiktoken # token 计数控制上下文长度如果你打算和现有向量数据库配合使用还需要装对应的客户端。以常见的几种为例pip install chromadb # 轻量本地向量库适合验证 pip install pymilvus # 生产级适合大规模 pip install qdrant-client # 性能和易用性平衡得不错注意PageIndex 本身不强制依赖向量数据库。它的树形索引可以存在本地文件系统也可以存进数据库。向量库是在你需要“粗筛 精检索”分层架构时才引入的。3.2 文档解析与树构建这是整个流程里最容易被低估的一步。文档解析质量直接决定树的质量树的质量决定检索质量。我见过太多人随便找个 PDF 解析库就上结果表格全乱、标题层级全丢后面怎么调都救不回来。from pageindex import PageIndex, DocumentParser # 初始化解析器指定文档类型 parser DocumentParser( doc_typepdf, extract_tablesTrue, # 表格单独作为节点不要拍平 detect_headingsTrue, # 自动识别标题层级 min_heading_font14, # 字号阈值低于这个不算标题 ) # 解析文档得到结构化树 doc_tree parser.parse(technical_whitepaper.pdf) # 初始化 PageIndex 索引 index PageIndex( llm_provideropenai, # 或 anthropic / local modelgpt-4o-mini, # 建树阶段用便宜模型即可 summary_max_tokens80, # 每个节点摘要的长度上限 ) # 构建索引这一步会调用 LLM 生成节点摘要 index.build(doc_tree) index.save(./pageindex_store/whitepaper)这里有几个参数值得展开说。extract_tablesTrue非常关键表格如果被拍平成文本行列关系就没了数值查询基本废掉。summary_max_tokens80是我反复试出来的经验值太短摘要信息不足太长则初筛阶段 token 消耗大、且摘要本身变得像原文失去压缩意义。建树阶段的 LLM 选型也有讲究。这一步是批量调用一份 100 页文档可能有几百个节点每个节点都要生成摘要。用 GPT-4 级别模型成本会很高用 mini 级别完全够用因为摘要任务本身不难。真正需要强模型的是检索阶段的推理那时候调用次数少但每次都要准。3.3 检索流程与参数调优索引建好之后检索就是核心环节了。PageIndex 的检索接口设计得比较直观query 第三章的限流策略和第五章的降级方案是怎么配合的 result index.retrieve( queryquery, max_depth4, # 最大下钻深度 branch_factor3, # 每层最多保留几个分支 return_pathTrue, # 返回完整层级路径 include_siblingsTrue, # 包含兄弟节点摘要 ) for node in result.nodes: print(f路径: {node.path}) print(f内容: {node.text[:200]}) print(---)max_depth和branch_factor是两个最需要调的参数。max_depth4意味着最多下钻四层对应“章 → 节 → 小节 → 段落”。如果你的文档层级很深可以调到 5如果文档比较扁平3 就够。branch_factor3是每层保留的分支数调大召回更全但噪声更多调小精度更高但可能漏。我的一般建议是先用max_depth4, branch_factor3跑一批测试问题看召回结果再针对性调整。如果发现经常漏掉某些章节把 branch_factor 调到 4 或 5如果发现召回内容太杂降到 2。3.4 与生成模型对接检索出来的节点最终要拼成上下文交给生成模型。这里有个细节不要把节点文本简单拼接要保留路径信息。def build_context(nodes): context_parts [] for node in nodes: # 路径作为标题帮助模型理解上下文归属 part f[{node.path}] {node.title}\n{node.text} context_parts.append(part) return \n\n.join(context_parts) context build_context(result.nodes) prompt f基于以下文档内容回答问题。注意内容之间的章节关系。 {context} 问题{query} 路径信息进入上下文之后模型在生成时就能引用“根据第三章 3.2 节”这样的表述答案的可追溯性也更强。这一点在需要引用来源的场景里特别有价值。4. 常见问题与排查技巧实录4.1 树构建阶段的典型问题问题一标题层级识别错误。很多 PDF 的标题字号不统一或者用加粗代替字号变化。这时候min_heading_font就不管用了。我的做法是加一个正则兜底匹配第[一二三四五六七八九十]章、\d\.\d这类模式作为标题识别的补充规则。问题二表格被解析成乱码。扫描版 PDF 或者复杂合并单元格的表格解析出来经常是乱的。这种情况建议单独处理表格用专门的表格识别工具比如 Camelot 或 Tabula先抽出表格转成 Markdown 表格再塞回树里。问题三摘要生成质量差。如果节点文本本身很短比如只有一个标题LLM 生成的摘要会很空。这时候可以让摘要生成时带上父节点标题作为上下文帮助模型理解这个节点在讲什么。4.2 检索阶段的排查思路检索不准的时候不要急着换模型先按这个顺序排查看树建得对不对。把树打印出来人工检查层级和节点划分是否合理。树错了后面全错。看摘要质量。随机抽几个节点看摘要是否准确概括了内容。摘要不准初筛就会跑偏。看下钻路径。把检索过程中每一层保留的分支打印出来看是在哪一层跑偏的。最后才调参数。max_depth和branch_factor是最后的手段不是第一手段。4.3 常见问题速查表现象可能原因解决方向召回内容完全不相关树结构错误 / 摘要质量差检查解析结果重新生成摘要跨章节问题答不好max_depth 太小增大下钻深度召回内容太杂branch_factor 太大减小分支数提高精度表格数值查不到表格被拍平开启表格独立节点延迟太高LLM 调用次数过多建树用 mini 模型减少下钻层数成本超预算摘要生成 token 太多降低 summary_max_tokens4.4 几个我踩过的坑坑一以为树越深越好。一开始我把max_depth设成 6结果检索时 LLM 要在很多层做判断延迟飙升而且深层节点文本往往很短摘要质量差反而拉低了精度。后来降到 4效果和速度都更好。坑二忽略兄弟节点。include_siblingsTrue这个选项我一开始没开后来发现开了之后模型能利用“同一节下其他段落”的信息做交叉验证答案准确率有明显提升。代价是上下文变长需要配合 token 预算控制。坑三建树和检索用同一个模型。建树是批量任务检索是精度任务两者对模型的要求完全不同。建树用便宜模型省钱检索用强模型保质量这个组合最划算。坑四不做缓存。同一份文档的树结构是稳定的但每次检索都要重新走一遍 LLM 推理。我在检索层加了一层查询缓存相同或高度相似的 query 直接返回缓存结果命中率大概能到 30%延迟直接砍掉三分之一。5. 和现有 RAG 栈怎么配合分层架构的落地方式5.1 粗筛 精检索的两段式架构如果你已经有了一套基于向量数据库的 RAG 系统不需要推倒重来。最务实的做法是在向量检索之后加一层 PageIndex 精检索。流程是这样的用户提问 → 向量数据库粗筛出 Top-50 相关文档 → 对这 50 篇文档分别用 PageIndex 做推理式检索 → 汇总精检索结果 → 交给生成模型。这个架构的好处是向量库负责“从海量文档里快速缩小范围”PageIndex 负责“在少量高价值文档里精准定位”。两者各司其职成本也可控——PageIndex 的 LLM 调用只发生在 50 篇文档上而不是全量。5.2 什么场景值得上 PageIndex不是所有 RAG 场景都需要推理式检索。我的判断标准是看文档价值和问题复杂度。值得上的场景技术文档问答、法律合同检索、医疗指南查询、金融研报分析——这些场景文档价值高、问题往往需要跨章节推理、答案精度要求高。不太需要的场景海量网页问答、简单事实查询、FAQ 机器人——这些场景向量检索够用上 PageIndex 是杀鸡用牛刀成本不划算。5.3 和 ontology rag 的关系热搜词里“ontology rag”和“llm ontology”出现频率不低说明大家在思考“知识组织”这件事。PageIndex 的树形索引其实是一种轻量级的本体结构它用层级关系表达了“部分-整体”“前提-结论”这类语义关系但没有走到完整的本体建模比如实体关系图、属性约束。如果你需要更强的知识组织能力可以在 PageIndex 的树之上再叠一层实体链接把树节点里的关键实体抽出来链接到外部知识库检索时同时利用树结构和实体关系。这是进阶玩法基础场景用纯树结构就够了。6. 性能与成本实测数据和优化方向6.1 延迟构成PageIndex 的延迟主要来自 LLM 调用。一次完整检索的调用次数大致是max_depth × branch_factor的量级。以max_depth4, branch_factor3为例最坏情况是十几次 LLM 调用。实测下来用 GPT-4o-mini 做检索推理单次调用平均 800ms 左右一次完整检索在 3-8 秒。这个延迟对交互式问答偏高对后台批处理可以接受。优化方向有三个一是并行化同一层的多个分支可以并发判断二是缓存相同 query 直接返回三是剪枝在高层就用摘要快速排除明显不相关的分支减少下钻次数。6.2 成本估算成本主要是 token 消耗。建树阶段一份 100 页文档大约 5 万 token 的摘要生成量用 mini 模型成本在几毛钱级别。检索阶段每次查询消耗几千 token用强模型的话单次查询成本在几分钱到一毛钱。对比一下如果不用 PageIndex而是把整份文档塞进长上下文模型单次查询成本可能是一块多。所以 PageIndex 在长文档场景下反而是省钱的因为它只把相关节点送进生成阶段而不是整份文档。6.3 什么时候该考虑换方案如果出现这几种情况说明 PageIndex 可能不适合你的场景文档量极大十万级以上且问题简单这时候向量检索性价比更高延迟要求极高亚秒级LLM 推理的延迟扛不住预算极紧连 mini 模型的调用成本都覆盖不了。技术选型没有银弹PageIndex 解决的是“高价值文档的精准检索”问题不是所有 RAG 问题的通用答案。7. 我个人的一些实操体会PageIndex 这个方向我是看好的因为它抓住了 RAG 的真正瓶颈——不是生成模型不够强而是检索层没有把知识组织好。向量检索是“扁平化”的知识表示而人类理解文档是“结构化”的这个鸿沟就是推理式检索要填的。实际用下来我觉得最值得投入精力的地方是树构建质量。很多人把注意力放在检索参数调优上但树建得不好后面怎么调都是白搭。我的建议是花时间把文档解析做扎实表格单独处理标题层级用规则兜底摘要生成带上父节点上下文。这些基础工作做到位检索效果自然就上来了。另外一个体会是不要追求一步到位。先在少量高价值文档上跑通 PageIndex验证效果再逐步扩大范围。我见过有人一上来就想把整个知识库都迁过去结果成本失控、效果还没验证最后项目黄了。小步快跑用数据说话这才是工程上稳妥的做法。最后分享一个小技巧建树的时候把文档的目录页单独抽出来作为根节点的一个特殊子节点。这样检索时 LLM 可以先读目录快速定位到大致章节再下钻能省掉不少无效的分支判断。这个技巧在长文档上效果特别明显实测能减少 20% 左右的 LLM 调用次数。