株式会社ホコサキ

XERJで社内ドキュメントをRAG検索可能にする実装パターン

天京祐輔
天京祐輔
XERJで社内ドキュメントをRAG検索可能にする実装パターン

AIエージェントに社内のコードリポジトリやドキュメントフォルダを探索させるとき、よくある実装はgrepやcatをツールとして渡すパターンです。
シンプルに見えますが、これには構造的な問題があります。

AIエージェントがgrepを使うと何が起きるか

図書館で「顧客管理の仕様書」を探したいとき、全蔵書を端から読み上げていくようなイメージを思い浮かべてください。
答えはそこにあるかもしれませんが、辿り着くまでに膨大なコストがかかります。

AIエージェントがgrepでパターンにマッチするファイルを探す場合、マッチした行だけでなく、前後のコンテキストを確認するためにcatでファイルをまるごと読み込む流れになりがちです。
ファイルが数十件あれば、それだけのテキストがコンテキストウィンドウに流れ込みます。
大きなリポジトリや長めのドキュメント群を対象にした瞬間、トークン消費は爆発的に増えます。

XERJが掲げる「grepより40倍少ないトークン」という数字は、この構造に由来しています。
インデックスを事前に構築しておけば、エージェントは「このクエリに関係するチャンク」だけを少数のAPIコールで取り出せます。
ファイルを丸ごと引き渡す必要がないため、コンテキストへの不要な情報の流入を抑えられます。

ただし、精度の話は正直に見ておく必要があります。
公式サイトが開示しているベンチマークでは、518 MBのコーパスを使った10問テストで、XERJバックエンドのエージェントが9問正解+1問部分正解、grep/Pythonベースラインが10問正解という結果でした。
精度で圧倒するわけではなく、「スケールしたときの構造的な優位性」がXERJの本質です。
Elasticsearchとの直接比較では55勝26引き分け4敗、インジェスト速度1.72倍、ディスクフットプリント1.61倍小さいという結果が出ていますが、これはトークン削減の話とは別軸の数字です。

XERJが真価を発揮するのは、繰り返しクエリが飛ぶ・APIコストを抑えたい・コード以外にPDFやログも混在している、というシナリオです。
「まずgrepで試してから考えよう」という判断も十分合理的です。
ただ、選択肢として頭に入れておく価値はあります。

インストールから最初の検索まで

XERJはRust製の静的バイナリとして配布されています。
JVMもDockerも不要で、Linux・macOS・Windowsのx86-64とarm64向けにビルド済みのバイナリが用意されています。
ライセンスはApache-2.0で、起動はサブセカンドです。

Rustツールチェーンが手元にある場合はcargoから直接ビルドできます。

cargo install xerj

Rustなしで試したい場合は、公式サイト(xerj.org)からプラットフォーム別のビルド済みバイナリをダウンロードして展開するだけです。
curlワンライナーも用意されていますが、インストールスクリプトのURLは公式サイトで確認してください。

インストールが終わったら、インデックスを作成します。
対象ディレクトリを指定するだけで、XERJがファイル形式を推論してインデックスを構築します。

xerj autoindex ./docs

対応しているファイル形式は幅広く、ソースコード・Markdown・PDF・ログファイルに加えて、SQLite・DOCX・gzip圧縮のCSV・小数点にカンマを使うロケールのCSVにも対応しています。
形式が混在するフォルダでも1コマンドで処理が完了します。
インデックス作成はKILL -9に対してセーフで、途中で止めても再実行すると同じ結果に収束します。

インデックスが作成されたら、HTTPエンドポイントに検索クエリを投げます。
デフォルトでは9200番ポートで待ち受けます。

curl -X GET "http://localhost:9200/docs/_search" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "match": {
        "content": "認証フロー"
      }
    },
    "size": 5
  }'

レスポンスはElasticsearchと同じ構造で返ってきます。

{
  "hits": {
    "total": { "value": 12 },
    "hits": [
      {
        "_index": "docs",
        "_id": "auth/flow.md",
        "_score": 3.21,
        "_source": {
          "content": "認証フローはOAuth 2.0をベースに...",
          "file_path": "docs/auth/flow.md",
          "file_type": "markdown"
        }
      }
    ]
  }
}

hits.hits 配列にスコア順でドキュメントが並びます。
_score の高い上位N件だけをエージェントに渡せば、コンテキストを最小限に抑えられます。
_source にはファイルパスと本文テキストが入っているので、LLMへの受け渡しもシンプルです。

なお、デフォルト設定では認証なしで起動します。
ローカル開発用途であれば問題ありませんが、社内ネットワークに公開する場合はリバースプロキシやファイアウォールでの保護を忘れずに設定してください。

Elasticsearch互換APIをそのまま使う

「Elasticsearch互換」と聞いたとき、どのくらい信用できるのかは気になるところです。
XERJはElasticsearchの内部実装を再現したものでも、フォークでもありません。
APIレベルの互換性 に絞って設計されています。

適合テストスイートは1369ケースあり、コミットごとに実行されて現在1366ケースをパスしています。
残り3ケースのギャップはリポジトリの engine/tests/es-compat-yaml ディレクトリに正直に明示されているので、何が使えないかを事前に確認できます。

この互換性が実務で意味するのは、「ESクライアントのエンドポイントを差し替えるだけで動く」という点です。
PythonのElasticsearchクライアントを使っているコードがあれば、接続先URLをXERJに向けるだけで、インデックス操作もクエリ発行もそのまま動きます。

LangChainと繋ぐ場合も同じです。
ElasticsearchRetrieverのエンドポイントをXERJに向けるだけで、既存のRAGパイプラインをそのままローカルに移植できます。

from langchain_elasticsearch import ElasticsearchRetriever

def build_query(query: str) -> dict:
    return {
        "query": {
            "match": {
                "content": query
            }
        },
        "size": 5,
        "_source": ["content", "file_path"]
    }

retriever = ElasticsearchRetriever(
    es_url="http://localhost:9200",
    index_name="docs",
    body_func=build_query,
)

docs = retriever.invoke("認証フローの実装方法")
for doc in docs:
    print(doc.page_content)

クラウドのESクラスタや外部のベクトルDBに依存していたパイプラインを、そのままオフライン環境に持ち込める点は地味に効きます。
コンプライアンス上の理由でコードやドキュメントを外部サービスに送れないケースでは、選択肢として真っ先に検討できます。

一点補足しておくと、_cat 系のエンドポイントは一部が400を返すなど非互換が残っています。
search_after を使う高度なページネーションも制限のある領域です。
複雑な集計クエリやニッチなAPIを多用する場合は、対象のケースが通過済みかをリポジトリで事前確認する手間は省けません。

「使えなかったら戻る」が低コストで実現できるのは、互換APIを選んだ設計判断として十分に説得力があります。
既存の資産を捨てずに試せる、というのは実際に導入コストをかなり下げます。

ベクトル検索との使い分けと運用上の現実

XERJのような キーワード+構造化検索 が向いているのは、「意味より一致が重要」なケースです。
関数名・エラーコード・バージョン番号・固有の設定値・ログのパターンマッチングは、セマンティック検索より精度の高い全文検索のほうが信頼できます。
コードベースやインフラログを対象にしたRAGでは、ベクトル検索よりこちらが向くシナリオが少なくありません。

一方、「このドキュメントが意味的に似ている」「質問文の言い換えでも答えを見つけたい」という場合はベクトル検索の出番です。
自然言語のFAQや議事録・マニュアルのような文書で、ユーザーが予測不可能なフレーズで質問する可能性があるなら、エンベディングモデルと組み合わせたほうが頑健です。

使い分けの判断軸を整理します。

  • 意味の近さより文字の一致が重要か — コード・ログ・設定ファイルならXERJ、自然言語のQ&AならベクトルDB
  • データ種別の多様性 — コード・PDF・SQLiteが混在するフォルダを1コマンドで扱えるのはXERJの強みです
  • 更新頻度 — autoindex の再実行でインデックスを更新できますが、高頻度で変化するドキュメントへの自動追従はパイプラインへの組み込みが必要です
  • スケールと運用コスト — クライアントメモリはデータが5倍に増えても約250 MBでフラットに保たれることが公式で示されています。大規模なマルチテナント環境はElasticsearchクラスタの領域です
  • 認証・ネットワーク設計 — デフォルトは認証なしなので、本番投入前にネットワーク境界の設計を先に決めておいてください

「まずXERJを試す価値がある」のは、対象がコードリポジトリ・社内ログ・固有名詞の多い技術ドキュメントで、クラウドサービスにデータを送りたくない場面です。
「最初からベクトルDBを選ぶべき」なのは、意味的な類似検索が必要で、多言語対応やスケールの保証が求められる場面です。

どちらか一方に絞る必要はありません。
XERJで精度の高い候補をキーワードで絞り込んでから、ベクトルリランキングにかけるハイブリッド構成も現実的な選択肢です。
Elasticsearch互換APIを持っている以上、既存のRAGパイプラインへの差し込みは難しくありません。
まず手元で動かして判断する、という進め方が一番コストが低いと思います。


株式会社ホコサキは、山口県宇部を拠点にAI活用支援・業務システム開発・DX推進を手がけています。
「社内ドキュメントをAIに繋ぎたいが、どこから始めればいいかわからない」という相談も歓迎です。
お問い合わせ からお気軽にご連絡ください。