给财务报告、法务合同这类长文档做问答,向量检索有个绕不过去的问题:它按语义相似度召回,可相似不等于相关。你问「2024 年经营现金流是多少」,它可能把「现金流」相关的描述段落都给你,却漏掉真正写数字的那一页;反过来又会把「相似但答非所问」的段落塞进来。PageIndex 的出发点就是这个:检索真正需要的是相关性,而相关性要靠推理,不是靠向量距离。
它用一句话概括自己的路线——不做向量库、不做切块(No Vector DB, No Chunking),换成生成树状索引,再让 LLM 像人翻报告一样,一路推理到该读的那一节。
树索引与两段式检索
判断写进第一句:PageIndex 的核心是把「一次性语义召回」拆成「先建树、再走树」两步,用 LLM 的推理替代向量相似度。
所谓树索引,是把一篇文档的层级结构(章节、小节、段落)整理成一棵树。它和 AlphaGo 的思路相通:不是全局扫一遍,而是从根节点开始,每一步推理「该往哪个分支走」,最终定位到几页。对比一下两种做法:
| 向量 RAG | PageIndex | |
|---|---|---|
| 索引 | 向量索引 | 树索引 |
| 检索 | 语义相似度搜索 | LLM 沿树推理 |
| 结果 | 不透明,像「感觉对」 | 可回溯到明确引用 |
| 上下文 | 只有 query 的 embedding | 完整上下文(对话历史、领域知识) |
索引和检索这两步,对模型的要求是相反的。树的结构本身是从文档版式里提取出来的,不需要 LLM 参与;索引模型只是做摘要和精修,所以用基础模型就够。检索模型才需要花大钱——它要沿树推理,官方建议「用你能负担的最强模型」。
本地跑起来
先装包,再用两个参数把「建树」和「走树」的模型分开指定:
import os
from pageindex import PageIndexClient
os.environ["OPENAI_API_KEY"] = "sk-xxx"
client = PageIndexClient(
index="gpt-6-luna", # 建树索引用基础模型,够用
chat="gpt-6-sol", # 沿树检索用最强的模型
)
doc_id = client.submit_document("annual_report.pdf")["doc_id"]
answer = client.chat("2024 年的经营现金流是多少?", doc_id=doc_id)
print(answer)
index 和 chat 分开,是这个 API 设计里最值得注意的一点。很多人一开始会把两个都设成最强模型,结果是索引阶段白白烧钱——树结构早就从版式里抽好了,索引模型只做润色。
云端模式
开源版擅长的是纯文本 PDF 和本地流程。遇到扫描件、图多的文档,本地就吃力了。云端模式把这部分搬走:解析、OCR、图像理解、树索引构建、存储全在云端,检索层仍用你自己的模型。切换只多一个 key:
import os
from pageindex import PageIndexClient
os.environ["PAGEINDEX_API_KEY"] = "pi-xxx"
os.environ["OPENAI_API_KEY"] = "sk-xxx"
client = PageIndexClient(
index="cloud", # 索引和存储在 PageIndex Cloud
chat="gpt-6-sol", # 检索仍走你自己的模型
)
doc_id = client.submit_document("contracts/nd_2025.pdf", wait=True)["doc_id"]
print(client.chat("违约金的计算基数是什么?", doc_id=doc_id))
两边的能力边界也清楚:本地只有页级引用(citation),云端有块级引用,还多出 OCR、元数据、文件夹和 MCP server。
索引一次、查询多次
成本结构是它相对向量 RAG 的一个实打实优势。建索引是一次性开销:官方测下来本地建树约 0.001 美元/页(用 gpt-5.6-luna),1000 页的教材也就一美元多一点、几分钟,之后每个问题都复用。查一次的成本不随文档变长而涨,因为它只读推理走到的那几个节点。
把「建索引」和「查问题」拆开写,就是下面这样:
# 建一次索引,之后每个问题只花检索成本,不再重复索引
from pageindex import PageIndexClient
client = PageIndexClient(index="gpt-6-luna", chat="gpt-6-sol")
docs = {
"fy2024": client.submit_document("fy2024_10k.pdf")["doc_id"],
"fy2023": client.submit_document("fy2023_10k.pdf")["doc_id"],
}
def ask(doc_key, question):
return client.chat(question, doc_id=docs[doc_key])
作为对照,另一种做法是每次把整份 PDF 塞给模型。官方测下来,同样答对的情况下,原生 PDF 输入在 52 页时贵 2.1 倍,420 页时贵 16.6 倍,到 805 页直接塞不进上下文窗口。
精度和边界
它在 FinanceBench(财务文档问答基准)上拿到 98.7% 的准确率,远高于向量 RAG 的 50%。这个数字来自它自己的评测,方向可信,但要记住基准场景是长而复杂的专业文档——财报、法条、监管文件、技术手册、医学文献、教材这类。
反过来看它的适用边界。如果你的文档短、语义单一、用户问法多变,向量检索仍然更成熟、生态更全,PageIndex 的树索引优势不明显。而且它和 LLM 强绑定——检索质量取决于 chat 模型强不强,选个弱模型,树走得再对,答案也还是不行。它也不是向量库的替代品,而是针对「长文档、要上下文理解、要多步推理」这类场景的另一种选择。
判断标准一句话:文档越长、越结构化、越要求答案可回溯到原文,PageIndex 越划算;文档短、追求快和稳,向量 RAG 仍够用。