ベクトル類似度はもうRAGの最善手ではないかもしれない。VectifyAI/PageIndex は埋め込みもベクトルDBもチャンキングも使わず、PDFを「目次のような階層ツリー」に変換してLLMが推論でノードを辿る、まったく新しいRAG設計だ。執筆時点でGitHubスター30,101、AlphaGo風のtree searchを応用した検索が強く、金融文書ベンチマークFinanceBenchで98.7%精度という現行SOTAを叩き出している。MCPサーバpageindex-mcpも提供され、Claude Desktop・Cursor・LangChain・OpenAI Agents SDKからそのまま長文PDFが読める。
この記事ではRAGに特化して解説します。RAG全般は RAGとは?仕組み・構築・ベクトルDB選定まで【2026年完全ガイド】 をご覧ください。
- PageIndexはベクトルDBを捨て、階層ツリーへのLLM推論で関連性を引き当てる新方式RAG。チャンキング・top-k・類似度しきい値の調整地獄から解放される
- FinanceBench 98.7%精度のSOTA実績。AlphaGo風tree searchで「類似はしているが答えていない」チャンクを根本から排除する
- CLI・Python API・MCPサーバ(`@pageindex/mcp`)まで揃い、Claude Desktop・Cursor・OpenAI Agents SDKから即接続できる
1. PageIndexとは:Embeddingを捨てるRAGの新しい設計
PageIndexは「類似度(similarity)」ではなく「関連性(relevance)」で長文ドキュメントを検索する、vectorless・reasoning-basedなRAGシステムだ。VectifyAIが2025年から開発し、2026年5月時点で30,101スター・MITライセンス・Python実装。専門ドキュメントの分析にフォーカスしており、金融・法律・規制・学術教科書など「ドメイン知識と多段推論が必要な文書」を主戦場に据えている。
従来のRAGは大まかに次のフローで動く。ドキュメントを固定長または意味単位でチャンク分割し、各チャンクを埋め込みモデルでベクトル化し、ベクトルDBに格納する。検索時はクエリも埋め込みベクトルに変換し、コサイン類似度上位k件を取り出してLLMに渡す。実装は素直で速いが、一つ大きな弱点がある。「意味は近いが質問の答えではないチャンク」を引いてしまう問題だ。PageIndex作者はこれをvibe retrieval(雰囲気検索)と揶揄している。
PageIndexの発想はまったく違う。長文PDFをそのまま「目次の木」に変換し、LLMに木構造を見せて「この質問ならどのノードを掘るべきか」を推論させる。木構造は人間がドキュメントを読むときの目次・章立てそのものなので、検索プロセスがそのまま人間の専門家の動きをシミュレートする。AlphaGoが盤面探索木をvalue networkで評価したのと同じ構造だ。
- No Vector DB:ベクトルDB不要。ドキュメント構造とLLM推論で検索する
- No Chunking:固定長チャンク分割なし。自然な節・章をそのままノード化
- 説明可能性とトレーサビリティ:検索結果はノードIDとページ番号で完全に追跡可能
- Context-Aware:会話履歴やドメイン知識を含む全文脈をLLM推論に統合できる
- Human-like Retrieval:人間専門家がドキュメントを読み解く順序を再現する
これが効くのは特に長文・専門領域だ。FinanceBenchという金融文書QAベンチマークで、PageIndexを採用したMafin2.5は98.7%精度を達成している。ベクトルRAG手法のSOTAを大きく上回る数字で、論文・規制文書・年次報告書のようにセマンティックな手がかりだけでは答えに届かないドキュメントで真価を発揮する。
論理構造的には、PageIndexは「Embedding時代に飲み込まれていたRAGの設計多様性を取り戻す」動きと言ってよい。LangChain登場以降、RAG≒ベクトル検索が当然視されすぎていたが、ここ1年でLightRAGのような知識グラフ系、PageIndexのようなツリー推論系、コードベース解析系(DeepWiki、graphify)など、非ベクトルRAGの選択肢が一気に広がった。RAGアーキテクチャの全体像はRAG進化系統樹2026:Naive RAGから自己進化型までで俯瞰している。
2. 仕組み:ツリーインデックスとLLMによる推論検索の二段構成
PageIndexの動作は2つのフェーズに分かれる。インデキシングフェーズで「目次の木」を作り、検索フェーズでLLMがその木を推論で辿る。それぞれ独立した工程なので、ツリー構築は事前バッチ、検索はオンデマンドで動かせる。
金融レポート・法律文書"] -->|PDFパーサ| B["ページ単位テキスト"] B -->|LLMで構造抽出| C["階層ツリーインデックス
章/節/小節をノード化"] C -->|node_id
start_index
end_index
summary| D["木構造JSON"] E["ユーザーの質問"] --> F["Agent: get_document"] F --> G["Agent: get_document_structure
ツリー全体をLLMに見せる"] G --> H["LLMがノードを推論で選択
tree search"] H --> I["Agent: get_page_content
選ばれたページ範囲だけ取得"] I --> J["LLM回答生成
引用ページ番号付き"] D --> G style C fill:#fef3c7 style H fill:#dbeafe
ツリー構造の各ノードは次のようなJSONで表現される。READMEに公開されているサンプルを引用しよう。
{
"title": "Financial Stability",
"node_id": "0006",
"start_index": 21,
"end_index": 22,
"summary": "The Federal Reserve ...",
"nodes": [
{
"title": "Monitoring Financial Vulnerabilities",
"node_id": "0007",
"start_index": 22,
"end_index": 28,
"summary": "The Federal Reserve's monitoring ..."
}
]
}
start_indexとend_indexがページ番号、summaryが節の要約、nodesが子ノードという素直な再帰構造だ。LLMはまずこのツリー全体をプロンプトで眺め、質問に答えるためにどのnode_idを掘るか決める。決めたらget_page_content("22-28")のように具体的なページを取得し、そこで初めて全文をLLMに見せる。トークンの使い方が極めて経済的で、長文ドキュメントでもコンテキスト窓が爆発しない。
ここでもう一つ重要なのが、PageIndexはtop-kという概念をそもそも持たない点だ。ベクトルRAGはコサイン類似度で並び替えてk個取るが、kの値は経験則で決めるしかない。多すぎればノイズが増え、少なすぎれば必要な情報が落ちる。PageIndexはLLM自身が「この質問なら3.2.1と4.5を読むべき」と推論で決めるので、固定top-kを設定する必要がない。質問ごとに最適な引用範囲が動的に決まる設計だ。
PageIndex作者はこれをAlphaGoのvalue/policy networkになぞらえている。AlphaGoは盤面の全候補手を読み切るのではなく、policy networkで「どの手が筋がよさそうか」を絞り、value networkで「その先がどう転ぶか」を評価しながらモンテカルロ木探索を回した。PageIndexのLLMはこれと同じことを文書ツリーの上で行う。各ノードのsummaryを見て「ここを掘れば質問に答えられそう」と判断し、必要に応じて隣接ノードまで広げる。
ツリー構築のチューニングポイント
run_pageindex.pyには主要な調整パラメータがある。--toc-check-pagesで目次の探索範囲、--max-pages-per-nodeで1ノードあたり最大ページ数、--max-tokens-per-nodeでノード要約のトークン上限を制御する。専門文書なら--if-add-node-summary yesを有効にして、各ノードに2〜3行のsummaryを付けておくと検索精度が大きく上がる。
3. ベクトル検索RAGとの違い:精度98.7%を生む構造的優位性
PageIndexがFinanceBench 98.7%を取った背景には、構造的なアドバンテージが3つある。順に整理しよう。第一に、関連性ベースの検索でvibe retrievalを排除できる。第二に、ノードIDとページ番号で引用が完全にトレース可能。第三に、コンテキストを質問ごとに動的に組める。次の比較表は従来ベクトルRAGとの違いを概観したものだ。
| 観点 | ベクトル検索RAG | PageIndex(Vectorless) |
|---|---|---|
| 検索の原理 | 埋め込みコサイン類似度(similarity) | ツリー上のLLM推論(relevance) |
| インフラ | ベクトルDB(Pinecone等)必須 | ベクトルDB不要 |
| 前処理 | 固定長チャンク分割+埋め込み生成 | PDF→階層ツリーJSON生成 |
| 検索粒度 | チャンク(top-k固定) | ノード単位(動的選択) |
| 説明可能性 | スコアのみ・なぜ引いたか不明 | ノードID・ページ番号で完全追跡 |
| top-k | 経験則で固定(5〜20が多い) | 不要・LLMが質問ごとに決定 |
| 長文性能 | チャンク分断で文脈崩壊しやすい | 章節構造を保ったまま検索 |
| 専門文書精度 | FinanceBench従来手法は60〜80%帯 | FinanceBench 98.7%(Mafin2.5) |
| 実装複雑度 | パイプライン要素が多い | ツリー生成1本+LLM推論のみ |
| 適性ドメイン | 短文FAQ・社内Wiki・口語会話 | 長文PDF・規制文書・学術論文 |
ここで誤解してほしくないのは、「ベクトルRAGが悪い」のではなく「ドメインで得意不得意が違う」という話だ。短文FAQやサポート問い合わせ、口語的なチャット履歴のような「意味の類似が答えに直結する」領域では、ベクトル検索は今でも合理的な選択。一方、200ページの規制文書から特定条項を引く、年次報告書のリスクファクターを横断比較する、論文の方法論セクションだけ精読する、といった長文+専門ドメインではPageIndexの設計が圧倒的に効く。
もう一点、PageIndexのアーキテクチャが本質的に強いのはコンテキスト・エンジニアリング親和性だ。LLMはツリー全体をプロンプトで一度見るので、その同じプロンプトに「会話履歴」「ユーザーのドメイン知識」「過去の引用ノード」をすべて詰め込める。動的にコンテキストを再構築しながら検索するため、対話を重ねるほど検索が賢くなる。ベクトルRAGはクエリ埋め込みが固定の語彙ベクトルなので、こうした文脈統合が原理的に苦手だ。
トレードオフも当然ある。PageIndexはツリー構築時にLLM呼び出しが必要なので、初回インデキシングのコストはベクトル埋め込みより高い。ただしツリーは一度作ればそのまま再利用でき、検索時の推論コストは埋め込み類似度計算より高くつくが、文書1件あたりの平均ノード数が数十〜数百なら実用域に収まる。ChunkingとEmbeddingのインフラ運用コスト(ベクトルDBのスケーリング、インデックス再構築、次元数選定など)が消えることを考えれば、長文ドメインでは総コストでも有利になりやすい。
4. インストールと使い方:CLIとPython APIで木を作る
セルフホスト版PageIndexは公式リポジトリから1分で立ち上がる。前提はPython 3とOpenAI APIキーのみ。標準PDFパーサーが入っているので、テキスト主体のPDFならそのまま動く。スキャン画像PDFは別途OCRが必要で、その場合はCloud Service版を使う流れになる。
git clone https://github.com/VectifyAI/PageIndex.git
cd PageIndex
pip3 install --upgrade -r requirements.txt
echo "OPENAI_API_KEY=sk-..." > .env
python3 run_pageindex.py --pdf_path /path/to/your/document.pdf
これだけでツリーJSONが生成される。チューニングパラメータを使うとノード分割の粒度を制御できる。長文専門文書では--max-pages-per-nodeを10〜15、--toc-check-pagesを20前後に設定すると安定する。
python3 run_pageindex.py --pdf_path /path/to/document.pdf \
--model gpt-4o-2024-11-20 \
--toc-check-pages 20 \
--max-pages-per-node 10 \
--max-tokens-per-node 20000 \
--if-add-node-id yes \
--if-add-node-summary yes \
--if-add-doc-description yes
Markdown文書もネイティブにサポートしており、--md_pathを指定すればOSSドキュメントや社内Wikiもそのまま木構造化できる。GitHub README、技術ブログのMarkdownバックアップ、SphinxやMkDocsの素材を統一フォーマットで扱えるのは運用上ありがたい。
PythonからAPIとして使う場合は、Cloud版ライブラリpageindex-clientを経由する。次のコードはAgentic Vectorless RAG demoの構成を簡略化したものだ。
from pageindex import PageIndexClient
from openai import OpenAI
client = PageIndexClient(api_key="your_pageindex_api_key")
openai_client = OpenAI()
# Step 1: ドキュメントをアップロードしてツリー化
doc = client.upload_document("/path/to/financial_report.pdf")
doc_id = doc["doc_id"]
# Step 2: ツリー構造をLLMに見せて、必要ノードを推論で選ばせる
tree = client.get_document_structure(doc_id)
resp = openai_client.responses.create(
model="gpt-4o",
input=f"以下の目次ツリーから「Liquidity Risk」に関連するnode_idを列挙してください。\n\n{tree}"
)
target_nodes = parse_node_ids(resp.output_text) # 例: ["0042", "0043"]
# Step 3: 選ばれたノードのページ範囲だけ取得
context = ""
for node_id in target_nodes:
pages = client.get_page_content(doc_id, node_id=node_id)
context += pages
# Step 4: コンテキストでQA
answer = openai_client.responses.create(
model="gpt-4o",
input=f"質問: 流動性リスクの主要因は?\n\nドキュメント:\n{context}"
)
print(answer.output_text)
このパターンの肝は「LLMにツリーを見せる→ノードを選ばせる→該当ページを取りに行く」の三段構成。ベクトルRAGのretrieve→rankとは思考順序が根本的に違う。retrieveが先にあるベクトルRAGは「ヒットしたチャンクを後付けで意味付け」する流れだが、PageIndexは「先に推論で当たりをつけ、その後で実テキストを見る」流れになる。人間が長文資料を読むときの自然な動作と同じだ。
5. Agentic Vectorless RAG:OpenAI Agents SDK統合デモ
PageIndexリポジトリにはexamples/agentic_vectorless_rag_demo.pyという公式デモが含まれている。OpenAI Agents SDKを使ってPageIndexを完全自律エージェント化したサンプルで、コードを読むとPageIndexの設計思想が一気に腹落ちする。エージェントには3つのツールが渡される。
get_document():ドキュメントのstatus・総ページ数・総行数を返すget_document_structure():本文テキストなしで階層ツリーだけを返すget_page_content(pages: str):「5-7」のようなページ範囲指定で本文を取得する
エージェントへのシステムプロンプトには「最初に必ずget_document()を呼んでstatusと範囲を確認し、次にget_document_structure()で関連ページ範囲を特定せよ」と書かれている。LLMは目次を見て「この質問ならpages 22-28」と判断し、get_page_content("22-28")で該当箇所だけ取り出す。次のサンプルコードは公式デモの本質部分を再構成したものだ。
import asyncio
from agents import Agent, Runner, function_tool
from pageindex import PageIndexClient
pi = PageIndexClient(api_key="your_pageindex_api_key")
DOC_ID = "doc_xxxxxxxxxxxx"
@function_tool
def get_document() -> dict:
"""ドキュメントメタデータ(status, page_count, line_count)"""
return pi.get_document(DOC_ID)
@function_tool
def get_document_structure() -> dict:
"""テキスト無しの階層ツリー(node_id, title, summary, page範囲)"""
return pi.get_document_structure(DOC_ID)
@function_tool
def get_page_content(pages: str) -> str:
"""ページ範囲指定でテキスト取得(例: "5-7")"""
return pi.get_page_content(DOC_ID, pages=pages)
agent = Agent(
name="VectorlessRAGAgent",
instructions=(
"Call get_document() first to confirm status and page/line count. "
"Call get_document_structure() to identify relevant page ranges. "
"Then call get_page_content() with tight specifications like '5-7'."
),
tools=[get_document, get_document_structure, get_page_content],
model="gpt-4o",
)
async def query_agent(question: str):
result = Runner.run_streamed(agent, question)
async for event in result.stream_events():
if hasattr(event, "data") and hasattr(event.data, "delta"):
print(event.data.delta, end="", flush=True)
asyncio.run(query_agent("Q: What does the paper say about residual attention?"))
このデモの何がエレガントかというと、エージェントが「いつどのツールを呼ぶか」を完全に推論で決めている点だ。固定パイプラインではなく、質問の性質に応じてget_document_structure()を1回で済ませることもあれば、最初に1ノード読んで補強情報が必要と判断したら別ノードを追加で取りに行くこともある。エージェントが「人間の専門家のように資料を読む」設計が、ここで実コードになって表れる。
6. PageIndex MCPサーバ:Claude DesktopやCursorに長文PDF読解力を足す
VectifyAI/pageindex-mcpはPageIndexをMCP(Model Context Protocol)プロトコル経由で公開するサーバ実装だ。執筆時点で326スター・TypeScript・MITライセンス。Claude Desktop・Cursor・OpenAI Agents SDK・Vercel AI SDK・LangChainといったMCP対応クライアントから、PDFアップロードとツリー検索を直接呼べる。
導入は3パターンある。最も手軽なのはAPIキー認証のホスト型で、https://api.pageindex.ai/mcpにBearerトークンで接続するだけ。OAuthフローも不要だ。
{
"mcpServers": {
"pageindex": {
"type": "http",
"url": "https://api.pageindex.ai/mcp",
"headers": {
"Authorization": "Bearer your_api_key"
}
}
}
}
PageIndex Chatアカウントを持っているユーザはOAuth経由のホスト型も使える。Claude Desktopなら.mcpbワンクリックインストールにも対応していて、Releasesからダウンロードしてダブルクリックすればそれで終わる。OAuthハンドリングは自動だ。
ローカルPDFをアップロードしたい場合は@pageindex/mcpパッケージを使うローカルMCPサーバを起動する。Node.js 18以上が必要で、設定はnpx経由で1行だ。
{
"mcpServers": {
"pageindex": {
"command": "npx",
"args": ["-y", "@pageindex/mcp"]
}
}
}
無料枠は1000ページ・会話無制限で、長文PDFを使ったプロトタイピングなら十分動く。コンテキスト窓の限界を理由にチャットを諦めていた厚い書籍・論文・規制文書も、PageIndex MCPを噛ませれば章節単位で取り出してLLMに渡せる。「Claude Desktopで巨大PDFを読ませる前提で資料を作る」というワークフローが現実的になるのは大きな転換だ。
OCRとセキュリティの注意点
セルフホストOSS版は標準PDFパーサーで動くため、画像主体PDFや手書き資料はOCR層が別途必要になる。Cloud Service版は強化OCRを内蔵しているが、機密文書を外部APIに送ることになる点はトレードオフ。エンタープライズ用途のオンプレ・プライベートデプロイも提供されているので、契約書・社外秘文書を扱う場合はそちらを検討したい。
7. 導入判断:PageIndexが向く案件と向かない案件
最後に、PageIndexを採用すべきかどうかの判断軸をまとめておく。長文・専門・トレーサビリティ重視ならPageIndex一択に近い。一方で短文FAQやリアルタイム性能を最優先する用途では、従来ベクトルRAGの方が素直に効く。次のチェックリストで自分のプロジェクトを評価してほしい。
- ✅ ドキュメントが100ページ以上の長文中心
- ✅ ドメインが金融・法務・医療・規制・学術など専門領域
- ✅ 回答ソースのページ番号引用が必須要件
- ✅ ベクトル類似度では拾いきれない関係性質問が多い
- ✅ Claude Desktop / Cursor等のMCPクライアントから直接使いたい
- ❌ 数百万件の短文FAQをミリ秒レイテンシで検索したい→ベクトルRAG
- ❌ 完全オフライン・LLM呼び出しゼロが要件→ベクトルRAGまたは知識グラフ
- ❌ チャットボットの口語応答に近い→ベクトルRAGまたはハイブリッド
長文・専門ドメインで詰まっているチームほど、PageIndexの設計はクリーンに刺さる。ベクトルDBインフラの運用を停止できる、top-kの調整地獄から解放される、引用がノードIDとページ番号で完全追跡できる、と実務上のメリットが厚い。ただし「ツリー生成のためにLLM呼び出しコストが先払い」になる点は事業計算に入れる必要がある。
ハイブリッド設計も現実的だ。例えば短文FAQはベクトルRAG・長文専門文書はPageIndexと用途別に切り分ける構成は、運用上もシンプルで失敗が少ない。LLMフロントエンドが質問を分類して片方のRAGに振り分ける、いわゆるRouter Patternは多くのプロダクションRAGですでに採られている。長文RAGの選択肢として、知識グラフ系のLightRAG・コードベース解析系のgraphify・PageIndexの3つを並行検討するのが2026年時点の定石だ。
LLMネイティブな検索体験は、ベクトル類似度の単一パラダイムから一気に多様化している。LLMの推論コストが下がり続ける限り、PageIndexのような「LLM自身がインデックスを読む」設計はむしろ追い風だ。GPT-4oクラスのモデルを安価に呼べる時代において、検索インフラを軽量化しながら精度を上げる発想は理にかなっている。長文ドメインでベクトルRAGの限界を感じているなら、PageIndexは数日で試せる強力な代替案になる。
まとめ
- RAG ≠ ベクトル検索という認識を業界に広げた。「Embeddingを捨てる選択肢」を本気で実装で示した最初級のOSS
- FinanceBench 98.7%という定量的SOTAで、ツリー推論RAGが長文・専門ドメインで実戦級だと証明した
- MCPサーバ提供により、Claude Desktop・Cursor・LangChainからPDF長文読解がそのまま使える運用基盤になった
- ベクトルDB・チャンキング・top-k・類似度しきい値の調整地獄から解放される設計シンプルさが、現場の心理的コストを劇的に下げる
ベクトルRAGはRAGの一部であって全部ではない、という当たり前を改めて示してくれる。PageIndexのGitHubリポジトリで実装を眺めながら、自分のドキュメントで30分試してみるのが一番早い。MCPサーバなら無料1000ページから入れる。長文ドキュメントを抱えるすべてのチームに、まずは触ってみることを薦めたい。