多くの RAG アーキテクチャ図には、ユーザー、ベクトルデータベース、LLM の 3 つの箱しかありません。会議でアイデアを説明するにはそれで十分です。しかし、システムを運用する人の役には立ちません。回答の品質と安全性を決める判断が描かれていないからです。ドキュメントをどうチャンクに分けるのか、どの埋め込みモデルを使うのか、誰がどのチャンクを見られるのか、結果をどう順位付けするのか、回答が良いことを誰がどうやって知るのか、といった判断です。
このガイドでは、検索拡張生成(RAG)アプリの全体像を、共有のモデルとストアを囲む 2 つの経路として描きます。そのうえで、何にラベルを付けるべきか、よくある間違いは何かを説明します。
経路は 1 つではなく 2 つ
この用語は Lewis らの論文(2020 年)に由来します。この論文は、生成時にリトリーバーが検索する Wikipedia の密なベクトルインデックスと言語モデルを組み合わせました。本番環境の多くの RAG アプリは、部品を一緒に学習させることはせず、考え方だけを受け継いでいます。関連する文章を検索してプロンプトに入れ、それに基づいて答えるようモデルに頼むのです。
そのため、どの RAG アプリにも 2 つの経路があります。
- **取り込み(インジェスト)**は、バックグラウンドで継続的に動きます。ドキュメントをチャンク、ベクトル、メタデータに変換します。
- 問い合わせは、質問ごとに 1 回動きます。質問を、出典付きの回答に変換します。
2 つの経路は共有部分で合流します。両方の経路で同じバージョンでなければならない埋め込みモデル、ベクトルインデックス、メタデータストアです。問い合わせの経路しか描かない図は、システムの半分と、バグの大半を隠してしまいます。
取り込みの経路
- ソースと変更検知。 コネクター、Webhook、クローラーが新規・変更・削除されたドキュメントを検知し、キューにジョブを積みます。削除と権限の変更もジョブです。
- 解析。 構造を保ったままテキストを取り出します。見出し、リスト、表、そしてタイトル、URL、オーナー、最終更新日時、アクセス制御リストなどのメタデータです。
- チャンク分割。 まずドキュメントの構造に沿って分け、次にサイズで分けます。チャンクサイズと重なりは、普遍的な数値ではなく、自分たちの質問に合わせて調整するパラメーターです。段落が単独でも意味を持つよう、各チャンクに見出しのパスを残しましょう。
- 埋め込み。 埋め込みモデルは 1 つだけ呼び出し、バージョンを固定します。モデル名とバージョンは、すべてのベクトルと一緒に保存します。異なるモデルのベクトルは比較できないため、モデルを変えるならコーパス全体の埋め込みを作り直すことになります。
- インデックス作成。 各チャンクを、ベクトル、ドキュメント ID、チャンク番号、バージョン、アクセスタグと一緒に upsert します。ハイブリッド検索を予定しているなら、ベクトルインデックスの隣にキーワードインデックスも用意します。
- 更新と削除。 upsert を冪等にし、削除されたドキュメントのチャンクを消し、鮮度を追跡します。ソースの変更がインデックスに届くまでにかかる時間です。
問い合わせの経路
- ユーザーを特定する。 検索を始める前に、ユーザーの所属グループを解決します。
- 質問を書き換える(任意)。 「では業務委託の人は?」のような追加の質問は、会話の内容を使って、単独で成り立つクエリに変える必要があります。
- 検索する。 ベクトル検索は意味の近い文章を、キーワード検索はエラーコードや製品名のような完全一致の語を見つけます。ハイブリッド検索は両方を実行して結果を統合します。よく使われるのが Reciprocal Rank Fusion で、各ドキュメントに、各リストでの 1/(k + 順位) を合計したスコアを与えます。RRF の元論文では k を 60 に固定していました。
- クエリの中で権限によって絞り込む。 ユーザーのグループをアクセスタグに対するフィルターとして渡し、本人が見られないチャンクが決してプロンプトに入らないようにします。ストアがフィルターをどう適用するかは確認しておきましょう。pgvector のドキュメントは、近似インデックスではインデックスの走査後にフィルターが適用されると説明しています。HNSW の
ef_searchがデフォルトの 40 のとき、行の 10% に一致するフィルターは平均して約 4 行しか返しません。反復スキャン、部分インデックス、パーティション分割で解決できます。 - リランキングする。 たとえば 50 件の候補を多めに取得し、リランキングモデルに質問との関連度で採点させます。上位の数件、たとえば 8 件を残します。
- プロンプトを組み立てる。 指示、ID とタイトル付きの選ばれたチャンク、質問を、トークン予算の範囲に収めます。チャンク ID での出典の明示を求めます。
- 生成して出典を示す。 LLM を呼び出し、回答をストリーミングで返し、チャンク ID をユーザーが確認できるリンクに変換します。
2 つの経路の周辺
- 検索したテキストは信頼できない入力として扱う。 ドキュメントには、モデル宛ての指示が含まれていることがあります。OWASP はこれを間接プロンプトインジェクションとして挙げています。モデルが使えるツールやデータは、ユーザー本人のアクセス権と同じ範囲に絞りましょう。
- すべてのリクエストをトレースする。 書き換え後のクエリ、検索およびリランキングされたチャンク ID とスコア、プロンプトのバージョン、モデルのバージョン、各ステップのレイテンシを記録します。
- 固定の質問セットで評価する。 チャンク分割、モデル、検索、プロンプトを変えるたびに実行します。RAGAS の論文は、検索した文脈への忠実性、回答の関連性、文脈の関連性という 3 つの有用な観点を挙げています。期待する出典が上位の結果に入っているかどうかも確認しましょう。
- キャッシュは慎重に。 クエリの埋め込みのキャッシュは通常は安全です。回答のキャッシュが安全なのは、権限の組み合わせごとに分けた場合だけです。
図
ダイアグラムの内容
- Ingestion path · continuous
- Source documents
- Ingestion jobs
- Ingestion worker
- Embedding model (pinned)
- Vector index
- Metadata + ACLs
- LLM
- Query path · per request
- User
- RAG API
- Reranker
- change events
- jobs
- embed chunks
- upsert chunks + ACL tags
- document metadata
- question · answer + sources
- embed question
- hybrid search + ACL filter
- user's groups
- top 50
- prompt with top 8
図の読み方は次のとおりです。
- 左側、取り込み。 ソースの変更はキューのジョブになります。取り込みワーカーが各ドキュメントを解析してチャンクに分け、埋め込みモデルを呼び出し、チャンクとベクトルをアクセスタグ付きでベクトルインデックスに upsert し、ドキュメントのメタデータを Postgres に書き込みます。
- 中央、共有のモデルとストア。 バージョンを固定した同じ埋め込みモデルが、両方の経路を担います。LLM もここにあり、問い合わせの経路からだけ呼び出されます。
- 右側、問い合わせ。 RAG API はユーザーのグループを調べ、質問を埋め込みに変換し、権限フィルター付きのハイブリッド検索を実行し、50 件の候補をリランカーに送り、上位 8 件のチャンクで LLM を呼び出します。
- 左下。 ステップごとのレイテンシ予算の例です。遅い回答を、予算を超えたステップまでたどれるようにします。
ラベルを付けるべきもの
- すべてのモデルノードのバージョン。埋め込みモデルも含みます。
- チャンク分割のパラメーター。取り込みワーカーかメモに書きます。
- 権限が適用される場所。脚注ではなく、検索のエッジ上に書きます。
- ステップ間を流れる結果の件数。検索で 50 件、プロンプトに 8 件です。
- 鮮度とレイテンシの目標。 ソースの変更が検索可能になるまでの時間と、問い合わせの各ステップの予算です。
よくある間違い
- 「AI」という名前の箱が 1 つだけ。 検索、リランキング、生成は異なる形で失敗するので、別々の箱が必要です。
- 取り込みの経路がない。 古い回答や欠けた回答は、多くの場合そこから始まります。
- 権限がない。 インデックスに対象読者の異なるドキュメントが入っているなら、アクセスタグのない図はデータ漏洩を描いていることになります。
- 評価がない。 質問セットがなければ、プロンプトやチャンクサイズの変更はすべて当て推量です。
- 埋め込みのバージョンが混在している。 コーパスの半分だけを新しいモデルで埋め込み直すと、検索は気づかないうちに壊れます。
RAG が答えにならないとき
- 答えが構造化データにある。 テーブルの行を埋め込むのではなく、データベースや API に問い合わせましょう。
- タスクにドキュメント全体が必要。 1 つの契約書を要約するなら、収まる限り契約書全体をプロンプトに入れたほうがうまくいきます。
- コーパスがごく小さい。 数ページなら、そのままプロンプトに入れられます。
- 必要なのは知識の追加ではなく、振る舞いの変更。 それはプロンプトかファインチューニングの問題です。
キューを使ったイベント駆動の取り込みについては、イベント駆動アーキテクチャ図のガイドを参照してください。
Archboard で作る
- Google または GitHub でサインインし、図を作成します。
- 基本パックの フレーム を 2 つ追加し、左を「Ingestion path」、右を「Query path」とします。
- 取り込みのフレームに、ソースドキュメント用としてデータパックの オブジェクトストア、取り込みジョブ用としてデータパックの キュー、取り込みワーカー用として C4 モデルパックの コンテナ を追加します。
- 中央に、埋め込みモデル用として ML パックの モデル、ML パックの ベクトルインデックス、メタデータと権限用としてデータパックの Postgres、LLM 用として 2 つ目の モデル を追加します。
- 問い合わせのフレームに、ユーザー用として C4 モデルパックの 人物、RAG API 用として コンテナ、リランカー用として ML パックの モデル を追加します。
- 各エッジに、「top 50」と「top 8」も含めて流れるもののラベルを付け、レイテンシ予算を基本パックの Markdown ブロックに書きます。
この例で使うダイアグラムノードは 13 個で、Free プランの範囲に収まります。AI エージェントを使っているなら、2 つの経路の説明から、MCP 経由で最初のバージョンを描いてもらえます。AIエージェントで構成図を作る方法を参照してください。一般的な手順はソフトウェアアーキテクチャ図の描き方を参照してください。
よくある質問
RAG には専用のベクトルデータベースが必要ですか? 必要ありません。すでに運用しているデータベースのベクトル拡張(PostgreSQL の pgvector など)や、ベクトル検索に対応した検索エンジンで十分なアプリは多くあります。コーパスの規模、フィルタリングの要件、チームがうまく運用できるものを基準に選びましょう。
チャンクサイズはどうすればよいですか? 普遍的な数値はありません。見出しと段落で分けたうえで、いくつかのサイズと重なりを、自分たちの質問セットで比較しましょう。
権限チェックはどこで行うべきですか? 取り込み時に各ドキュメントのアクセスリストをチャンクにコピーし、検索時にそれで絞り込みます。内容を隠すのにプロンプトを頼ってはいけません。
RAG アプリはどう評価すればよいですか? 期待する出典付きの、実際の質問からなる固定のセットを用意します。検索がその出典を見つけられるか、回答が検索した文脈に忠実で、質問に関連しているかを測ります。
RAG の代わりに LLM にツールを呼ばせるべきですか? ライブデータや構造化データについてはそうです。正となるシステムに問い合わせるツールは、コピーを集めたインデックスに勝ります。多くのアプリは両方を使っています。