n8n RAG チュートリアル:Supabase ベクトルデータベース設定ガイド

n8n RAG チュートリアル:Supabase ベクトルデータベース設定ガイド

セクション1: はじめに:RAGシステムとSupabaseベクトルデータベースの概要

RAGシステムとは何か

近年、大規模言語モデル(LLM)の進化は目覚ましいものがありますが、その一方で「幻覚(ハルシネーション)」と呼ばれる、事実に基づかない情報を生成してしまう課題も抱えています。この課題を解決し、より信頼性の高い情報生成を可能にする技術が、RAG(Retrieval Augmented Generation:検索拡張生成)システムです。RAGは、ユーザーの質問に対して、事前に用意されたドキュメントの中から関連性の高い情報を検索し、その情報を基にLLMが回答を生成する仕組みを指します。これにより、LLMは最新かつ正確な情報に基づいて応答できるようになります。

n8nでRAGシステムを構築するメリットは多岐にわたります。n8nはノーコード・ローコードのワークフロー自動化ツールであり、複雑なプログラミングなしに様々なサービスと連携できます。これにより、データの取り込みからベクトル化、Supabaseへの保存、そしてLLMへの連携までの一連のRAGワークフローを視覚的に、かつ迅速に構築することが可能です。私自身も、複雑なデータパイプラインを構築する際にn8nの柔軟性に助けられてきました。特に、異なるAPIやデータベースとの連携が容易な点は、開発スピードを格段に向上させると感じています。

RAGシステムにおいて、関連ドキュメントを効率的に検索するためには、ベクトルデータベースが不可欠です。今回、私がSupabaseを選択した理由は、その手軽さと強力な機能にあります。Supabaseは、PostgreSQLをベースとしたオープンソースのBaaS(Backend as a Service)であり、リアルタイムデータベース、認証、ストレージなど、Webアプリケーション開発に必要なバックエンド機能を包括的に提供しています。特に、PostgreSQLの拡張機能である「pgvector」を利用することで、ベクトルデータを効率的に保存し、高速な類似度検索を実現できます。テキストを数値のベクトルに変換する「ベクトル埋め込み」は、意味が似たテキストほどベクトル空間で近い位置に配置されるため、このpgvectorエクステンションがなければ、ベクトル操作自体ができません。Supabaseとpgvectorの組み合わせは、RAGシステムにおける「知識の棚」を構築するための最適な選択肢と言えるでしょう。

セクション2: Supabaseでのベクトルデータベースセットアップ手順

pgvectorエクステンションの有効化

Supabaseでベクトルデータベースを構築する最初のステップは、pgvectorエクステンションを有効にすることです。pgvectorは、PostgreSQLでベクトルデータを効率的に保存し、コサイン類似度などのベクトル検索を実行するための重要な拡張機能です。このエクステンションがなければ、ベクトル型のカラムを定義したり、ベクトル間の距離計算を行ったりすることはできません。Supabaseのプロジェクトダッシュボードから「SQL Editor」を開き、以下のSQLコマンドを実行することで有効化できます。

create extension vector;

【注意点】 このコマンドは、pgvectorエクステンションがまだ有効になっていない場合にのみ実行してください。もし既に有効になっている状態で再度実行すると、「pgvector extension already exists」というエラーが発生します。その場合は、このcreate extension vector;の行を削除して、次のステップに進んでください。一度有効化すれば、プロジェクト内で永続的に利用可能になります。

ドキュメント保存テーブルの作成

次に、RAGシステムで利用するドキュメントを保存するためのテーブルを作成します。このテーブルには、ドキュメントの実際のテキスト内容、メタデータ、そして最も重要な「埋め込みベクトル」を格納します。埋め込みベクトルの次元数(vector(次元数))は、使用する埋め込みモデルによって異なるため、非常に重要です。この次元数が、後述するベクトル検索用関数のクエリ埋め込み次元と一致していないと、「dimension mismatch」エラーが発生し、検索が動作しません。ここでは、主要な3つのケースを比較しながら解説します。

ケース1:Gemini Embedding 3072次元(高品質)
本格的なRAGシステムや高精度が求められるビジネスアプリケーション向けです。最も詳細な意味情報を保持できます。

create table documents (
    id bigserial primary key,
    content text, -- ドキュメントの実際のテキスト内容
    metadata jsonb, -- ドキュメントのメタデータ(作成日、出典など)
    embedding vector(3072) -- Gemini Embedding のデフォルト次元(高品質)
);

ケース2:Gemini Embedding 1536次元(バランス型)
中程度の品質要件で、ストレージコストや検索速度とのバランスを取りたい場合に適しています。MRL(Matryoshka Representation Learning)技術により、次元削減後も高い意味情報を保持します。

create table documents (
    id bigserial primary key,
    content text,
    metadata jsonb,
    embedding vector(1536) -- Gemini Embedding のバランス次元
);

ケース3:Gemini Embedding 768次元(軽量)
小規模なデモンストレーション、プロトタイピング、またはコスト・スピードを最重視する場合に最適です。n8nのオフィシャルチュートリアルでもこの設定が採用されています。

create table documents (
    id bigserial primary key,
    content text,
    metadata jsonb,
    embedding vector(768) -- Gemini Embedding の軽量次元
);

各ケースで注目すべきは、embedding vector(次元数)の部分です。この次元数が、後でn8nで設定する埋め込みモデルの出力次元と完全に一致している必要があります。例えば、Gemini Embeddingのデフォルト出力は3072次元ですが、APIリクエストで明示的に1536や768次元を指定することで、より軽量なベクトルを生成できます。これにより、ストレージ容量の削減や検索速度の向上が期待できますが、その分、意味情報の保持能力は低下する可能性があります。ご自身のユースケースと要件に合わせて、最適な次元数を選択してください。

ベクトル検索用関数の作成

ドキュメントテーブルが作成できたら、次にベクトル検索を実行するためのSQL関数を作成します。この関数は、与えられたクエリの埋め込みベクトルと、データベースに保存されているドキュメントの埋め込みベクトルとの類似度を計算し、最も関連性の高いドキュメントを返します。ここでは、match_documentsという関数を作成します。

create function match_documents (
    query_embedding vector(3072), -- クエリのベクトル表現
    match_count int default null, -- 返す結果の最大件数
    filter jsonb DEFAULT '{}' -- メタデータフィルタ
)
returns table (
    id bigint,
    content text,
    metadata jsonb,
    similarity float -- 類似度スコア(0-1、1に近いほど類似)
)
language plpgsql as $$
#variable_conflict use_column
begin
    return query select
        documents.id,
        documents.content,
        documents.metadata,
        1 - (documents.embedding <=> query_embedding) as similarity
    from documents
    where metadata @> filter
    order by documents.embedding <=> query_embedding
    limit match_count;
end;
$$;

このmatch_documents関数は、以下の重要な役割を担っています。

  1. query_embedding vector(3072): 検索クエリの埋め込みベクトルを受け取ります。ここでの次元数も、ドキュメントテーブルのembeddingカラムの次元数と必ず一致させる必要があります。上記の例では3072次元ですが、テーブル定義に合わせて適宜変更してください。
  2. match_count int default null: 返す結果の最大件数を指定します。nullの場合は制限なしとなりますが、通常は上位数件を指定して効率化を図ります。
  3. filter jsonb DEFAULT '{}': ドキュメントのメタデータに基づいて検索結果を絞り込むためのフィルタリング条件です。例えば、特定のカテゴリや作成日のドキュメントのみを検索対象としたい場合に利用します。デフォルトの'{}'は、すべてのメタデータにマッチすることを意味します。

関数の処理の流れは以下の通りです。

  • documents.embedding <=> query_embedding: ここがベクトル検索の核心です。<=>演算子は、2つのベクトル間のコサイン距離を計算します。コサイン距離は、ベクトルの方向の類似度を示す指標であり、値が小さいほど類似度が高いことを意味します。
  • 1 - (documents.embedding <=> query_embedding) as similarity: コサイン距離は0から2の範囲で値を取りますが、RAGシステムでは通常、0から1の範囲で「類似度スコア」として表現されることが多いため、1 - 距離という計算で類似度スコアに変換しています。これにより、1に近いほど類似度が高いと直感的に理解できるようになります。
  • where metadata @> filter: jsonb型のmetadataカラムに対して、@>演算子を使ってフィルタリング条件を適用します。これにより、特定のメタデータを持つドキュメントのみを検索対象とすることができます。
  • order by documents.embedding <=> query_embedding limit match_count: 計算されたコサイン距離が小さい(つまり類似度が高い)順にドキュメントを並べ替え、match_countで指定された件数だけ上位のドキュメントを取得します。

この関数を一度作成すれば、n8nからSQLノードなどを介して簡単に呼び出し、RAGシステムの中核となる検索機能を実現できます。関数のSQLコードは、テーブル作成と同じSQLセッションで実行することが推奨されます。そうしないと、「function match_documents does not exist」というエラーが発生する可能性があります。

セクション3: 埋め込みモデル別セットアップケース詳細

ケース1:Gemini Embedding 3072次元(高品質)

用途と推奨シナリオ

この3072次元のGemini Embeddingは、Googleが提供する埋め込みモデルのデフォルト出力であり、最も高品質なベクトル表現を提供します。そのため、本格的なRAGシステムを構築する際や、高い精度が求められるビジネスアプリケーションに最適です。特に、複数の言語を扱う必要がある場合や、ドキュメントの意味的なニュアンスを非常に細かく捉えたい場合に推奨されます。予算に余裕があり、最高の検索精度を追求したい場合に、まず検討すべき選択肢と言えるでしょう。

SQLコードの特徴

SQLコードは、セクション2で解説した基本構造とほぼ同じですが、embeddingカラムとquery_embedding関数のパラメータがvector(3072)と明示的に定義されている点が特徴です。この次元数が、Gemini Embeddingのデフォルト出力と完全に一致しています。

-- pgvectorエクステンション有効化
-- (既に有効になっている場合はエラーが出るため、最初の行を削除して実行)
create extension vector;

-- ドキュメント保存テーブル
create table documents (
    id bigserial primary key,
    content text, -- ドキュメントの実際のテキスト内容
    metadata jsonb, -- ドキュメントのメタデータ(作成日、出典など)
    embedding vector(3072) -- Gemini Embedding のデフォルト次元(高品質)
);

-- ベクトル検索用関数
create function match_documents (
    query_embedding vector(3072), -- クエリのベクトル表現
    match_count int default null, -- 返す結果の最大件数
    filter jsonb DEFAULT '{}' -- メタデータフィルタ
)
returns table (
    id bigint,
    content text,
    metadata jsonb,
    similarity float -- 類似度スコア(0-1、1に近いほど類似)
)
language plpgsql as $$
#variable_conflict use_column
begin
    return query select
        documents.id,
        documents.content,
        documents.metadata,
        1 - (documents.embedding <=> query_embedding) as similarity
    from documents
    where metadata @> filter
    order by documents.embedding <=> query_embedding
    limit match_count;
end;
$$;

n8nでの設定方法

  • モデル: gemini-embedding-001
  • 出力次元: 3072(これは通常、デフォルト設定で問題ありません)

これにより、n8nで生成された3072次元の埋め込みベクトルがSupabaseに保存され、match_documents関数で正しく検索できるようになります。

ケース2:Gemini Embedding 1536次元(バランス型)

性能とコストのバランス

Gemini Embeddingの1536次元は、品質とコスト、そして検索速度のバランスを重視するアプリケーションに非常に適しています。GoogleのMRL(Matryoshka Representation Learning)技術により、次元を削減しても意味情報の保持能力が比較的高いのが特徴です。私自身、多くのドキュメントを扱うシステムでは、ストレージコストと検索速度がボトルネックになりがちなので、このバランス型は非常に魅力的に感じます。調査データによると、ストレージ必要容量は約50%削減され、検索処理速度は約30-40%高速化されるとされています。API呼び出しコスト自体は3072次元と同じですが、データベース側のリソース消費を抑えられるため、全体的な運用コスト削減に貢献します。

SQLコードの違い

3072次元のケースとの主な違いは、embeddingカラムとquery_embedding関数のパラメータがvector(1536)に変更されている点です。この変更により、データベースに保存されるベクトルのサイズが小さくなり、ストレージ効率と検索パフォーマンスが向上します。

-- pgvectorエクステンション有効化
create extension vector;

-- ドキュメント保存テーブル
create table documents (
    id bigserial primary key,
    content text,
    metadata jsonb,
    embedding vector(1536) -- Gemini Embedding のバランス次元
);

-- ベクトル検索用関数
create function match_documents (
    query_embedding vector(1536), -- ← 1536次元に変更
    match_count int default null,
    filter jsonb DEFAULT '{}'
)
returns table (
    id bigint,
    content text,
    metadata jsonb,
    similarity float
)
language plpgsql as $$
#variable_conflict use_column
begin
    return query select
        documents.id,
        documents.content,
        documents.metadata,
        1 - (documents.embedding <=> query_embedding) as similarity
    from documents
    where metadata @> filter
    order by documents.embedding <=> query_embedding
    limit match_count;
end;
$$;

n8nでの設定ポイント

  • モデル: gemini-embedding-001
  • 出力次元: 1536(APIリクエストで明示的に指定が必要な場合があります)

この設定により、n8nが生成する埋め込みベクトルの次元が1536となり、Supabaseのテーブル定義と一致させることができます。

ケース3:Gemini Embedding 768次元(軽量)

軽量モデルの利点と用途

Gemini Embeddingの768次元は、最も軽量なベクトル表現を提供します。このモデルの最大の利点は、リソース消費が最小限に抑えられる点です。そのため、小規模なデモンストレーション、プロトタイピング、またはテスト環境での利用に最適です。私自身も、新しいRAGシステムのアイデアを検証する際には、まずこの軽量モデルから始めることが多いです。また、極めてコストやスピードを重視する用途、例えばエッジデバイスやモバイルデバイスでの限定的な利用も考えられます。特に、n8nのオフィシャルチュートリアルでもこの設定が採用されており、RAGシステム構築の入門としては非常に分かりやすい選択肢です。

SQLコードの特徴

このケースのSQLコードは、embeddingカラムとquery_embedding関数のパラメータがvector(768)と定義されている点が特徴です。これにより、データベースに保存されるベクトルのサイズがさらに小さくなり、ストレージと計算コストを大幅に削減できます。

-- pgvectorエクステンション有効化
create extension vector;

-- ドキュメント保存テーブル
create table documents (
    id bigserial primary key,
    content text,
    metadata jsonb,
    embedding vector(768) -- Gemini Embedding の軽量次元
);

-- ベクトル検索用関数
create function match_documents (
    query_embedding vector(768), -- ← 768次元に変更
    match_count int default null,
    filter jsonb DEFAULT '{}'
)
returns table (
    id bigint,
    content text,
    metadata jsonb,
    similarity float
)
language plpgsql as $$
#variable_conflict use_column
begin
    return query select
        documents.id,
        documents.content,
        documents.metadata,
        1 - (documents.embedding <=> query_embedding) as similarity
    from documents
    where metadata @> filter
    order by documents.embedding <=> query_embedding
    limit match_count;
end;
$$;

n8nオフィシャルチュートリアルとの関連

n8nのオフィシャルチュートリアルでは、この768次元のGemini Embeddingが使用されています。これは、初心者がRAGシステムを構築する際の障壁を低くし、迅速に動作するプロトタイプを作成できるようにするためです。初期段階の検証や、数千ドキュメント以下の小規模なナレッジベースであれば、この設定でも十分な性能を発揮することが多いでしょう。

ケース4:OpenAI text-embedding-3-small(1536次元)

OpenAIモデルの特徴と推奨用途

OpenAIのtext-embedding-3-smallモデルは、その高い性能とコスト効率から広く利用されています。既にOpenAI APIを他のプロジェクトで使用している場合や、OpenAIのエコシステムに統一したい場合に特に推奨されます。確立された実績と豊富なコミュニティサポートがあるため、安心して利用できるモデルの一つです。このモデルは、汎用的なテキスト埋め込みタスクにおいて非常に優れたパフォーマンスを発揮します。

SQLコードのポイント

OpenAIのtext-embedding-3-smallモデルは、固定で1536次元のベクトルを出力します。そのため、Supabaseのテーブル定義と検索関数も、この1536次元に合わせて設定する必要があります。

-- pgvectorエクステンション有効化
create extension vector;

-- ドキュメント保存テーブル
create table documents (
    id bigserial primary key,
    content text,
    metadata jsonb,
    embedding vector(1536) -- OpenAI text-embedding-3-small の次元
);

-- ベクトル検索用関数
create function match_documents (
    query_embedding vector(1536),
    match_count int default null,
    filter jsonb DEFAULT '{}'
)
returns table (
    id bigint,
    content text,
    metadata jsonb,
    similarity float
)
language plpgsql as $$
#variable_conflict use_column
begin
    return query select
        documents.id,
        documents.content,
        documents.metadata,
        1 - (documents.embedding <=> query_embedding) as similarity
    from documents
    where metadata @> filter
    order by documents.embedding <=> query_embedding
    limit match_count;
end;
$$;

n8nでのAPIキー設定など

  • モデル: text-embedding-3-small
  • OpenAI API キー: n8nのクレデンシャルで設定したAPIキーを選択
  • 次元: 1536(このモデルは固定出力のため、通常は自動的に設定されますが、確認は必要です)

OpenAIのAPIを利用する際は、APIキーの管理と利用規約の遵守に特に注意を払う必要があります。セキュリティ対策を怠ると、予期せぬコスト発生や情報漏洩のリスクにつながる可能性があります。

セクション4: パフォーマンス最適化とトラブルシューティング

インデックスの追加による高速化

RAGシステムが扱うドキュメントの量が増えるにつれて、ベクトル検索のパフォーマンスは重要な課題となります。特に大規模なデータセットでは、インデックスの追加が検索速度を劇的に向上させる鍵となります。pgvectorでは、ベクトル検索を高速化するためのivfflatインデックスと、メタデータ検索を高速化するためのginインデックスが利用できます。

-- ベクトル検索を高速化するインデックス
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

-- メタデータ検索を高速化するインデックス
CREATE INDEX ON documents USING gin (metadata);

ivfflatインデックスは、コサイン類似度検索に特化したインデックスであり、vector_cosine_opsを指定することでコサイン距離計算を最適化します。WITH (lists = 100)は、インデックスの分割数を指定するパラメータで、データ量や検索精度に応じて調整が必要です。一般的に、listsの値を大きくすると検索精度は向上しますが、インデックス構築時間と検索時間は増加します。数万件以上のドキュメントを扱う場合は、このインデックスの導入を強く推奨します。

また、ginインデックスは、jsonb型のmetadataカラムに対する検索を高速化します。RAGシステムでは、特定のカテゴリや作成日などでドキュメントをフィルタリングすることが多いため、このインデックスはメタデータフィルタリングのパフォーマンス向上に大きく貢献します。これらのインデックスを適切に設定することで、ユーザーエクスペリエンスを損なうことなく、RAGシステムを大規模に運用することが可能になります。

よくあるエラーと対処法

RAGシステムのセットアップ中に遭遇しやすいエラーとその対処法をまとめました。私自身もこれらのエラーに何度も遭遇し、その都度解決策を探してきました。事前に知っておくことで、スムーズな開発につながるはずです。

  • 「pgvector extension already exists」エラーが出た場合
    このエラーは、create extension vector;コマンドを、既にpgvectorエクステンションが有効になっているデータベースで実行しようとした際に発生します。対処法は簡単で、SQLスクリプトの最初の行にあるcreate extension vector;を削除して、再度実行してください。pgvectorは一度有効化すれば、そのデータベース内で永続的に利用可能です。
  • 「dimension mismatch」エラーが出た場合
    これは最も重要なエラーの一つで、テーブルのembeddingカラムで定義されているベクトル次元と、match_documents関数に渡されるquery_embeddingの次元が一致していない場合に発生します。例えば、テーブルがvector(1536)なのに、関数がquery_embedding vector(3072)を受け取ろうとしている、といったケースです。このエラーが発生したら、必ずテーブル定義と関数のパラメータ定義、そしてn8nの埋め込みモデルの出力次元がすべて一致しているかを確認してください。私の経験上、この不一致が原因で多くの時間が費やされることがあります。
  • 「function match_documents does not exist」が出た場合
    このエラーは、match_documents関数がまだデータベースに作成されていないか、または関数を作成したSQLセッションとは異なるセッションで呼び出そうとしている場合に発生します。テーブル作成後、同じSQL Editorのセッション内で関数定義のSQLも実行し、関数が正しく作成されていることを確認してください。SupabaseのSQL Editorでは、複数のSQL文をまとめて実行できます。
  • 検索結果が返されない場合
    match_documents関数を実行しても期待する検索結果が返されない場合、いくつかの原因が考えられます。まず、filterパラメータに指定したJSONB条件が、データベースに保存されているドキュメントのmetadataと一致しているかを確認してください。特に、キー名や値の型(文字列、数値など)が完全に一致している必要があります。テストとして、filter := '{}'::jsonbのように空のJSONBオブジェクトを渡して、すべてのメタデータにマッチする状態で検索が機能するかを確認するのが良いでしょう。また、そもそもドキュメントがテーブルに正しく挿入されているか、埋め込みベクトルがNULLになっていないかなども確認ポイントです。

セクション5: まとめと本番環境への推奨設定

段階的な導入ステップ

RAGシステムを構築する際、どの埋め込みモデルの次元数を選ぶべきか迷うことがあるかもしれません。私の経験から、以下の段階的な導入ステップを推奨します。これにより、開発の初期段階でのリソース消費を抑えつつ、最終的に最適なパフォーマンスと精度を実現できます。

  • プロトタイピングは768次元から始める: 開発の初期段階や概念実証(PoC)では、Gemini Embeddingの768次元モデルから始めることを強くお勧めします。これは最も軽量であり、リソース消費が少ないため、迅速なイテレーションとテストが可能です。n8nのオフィシャルチュートリアルでも採用されている設定であり、手軽にRAGシステムの全体像を把握できます。
  • 精度検証後は1536次元でスケールアップ: プロトタイタイプで基本的な機能が動作することを確認したら、次にGemini Embeddingの1536次元モデルに移行し、精度とパフォーマンスのバランスを検証します。多くの実用的なアプリケーションでは、この1536次元が最適な選択となることが多いです。ストレージ効率と検索速度の向上を実感できるでしょう。
  • 本番運用前に3072次元で最終評価: 最高の精度が求められる本番環境への導入を検討する際は、Gemini Embeddingの3072次元モデルで最終的な評価を行います。このモデルは最も高品質な埋め込みを提供するため、ビジネス要件が非常に厳しい場合にその真価を発揮します。ただし、ストレージと計算コストが増加する点には注意が必要です。
  • リソース制約時の1536次元採用の推奨: Googleの公式ドキュメントでも「品質を最大化するなら3072、バランス型なら1536」と推奨されています。もし、予算やインフラのリソースに制約がある場合は、1536次元モデルを本番環境で採用することも非常に有効な選択肢です。多くのユースケースで十分な精度を提供しつつ、運用コストを抑えることができます。

各モデルの比較表の活用方法

RAGシステムの構築において、適切な埋め込みモデルを選択することは非常に重要です。以下の比較表は、各モデルの品質、コスト、ストレージ効率を視覚的に理解するのに役立ちます。この表を活用し、ご自身のプロジェクトの要件に最も合致するモデルを見つけてください。

モデル デフォルト次元 推奨次元 品質 コスト ストレージ効率
Gemini Embedding 3072 3072 3072 ⭐⭐⭐⭐⭐ $0.15/1M tokens
Gemini Embedding 1536 3072 1536 ⭐⭐⭐⭐ $0.15/1M tokens
Gemini Embedding 768 3072 768 ⭐⭐⭐ $0.15/1M tokens
OpenAI small 1536 1536 1536 ⭐⭐⭐⭐ $0.02/1M tokens
OpenAI large 3072 3072 3072 ⭐⭐⭐⭐⭐ $0.13/1M tokens

この表からわかるように、各モデルには一長一短があります。例えば、最高の品質を求めるならGemini Embedding 3072やOpenAI large 3072が候補になりますが、コストやストレージ効率は低くなります。一方で、OpenAI small 1536は非常にコスト効率が良く、中程度の品質を提供します。ご自身のプロジェクトの「品質・コスト・ストレージ効率」のバランスを理解し、最も適切なモデルを選択することが成功への鍵です。特に、大規模なRAGシステムを構築する際は、初期段階でのモデル選択が長期的な運用コストに大きく影響するため、慎重な検討が必要です。最終的には、n8nでのテストを通じて、実際にどのモデルが最も要件を満たすかを確認することをお勧めします。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール