mcpField
P
postgres-semantic-search

postgres-semantic-search

github.com/laguaguデータ・データベース70218 回インストール2 日前に更新
リポジトリ

postgres-semantic-search について

PostgreSQL Semantic Searchは、Postgres検索の設計と評価に使うスキルおよび検証済みSQL部品です。ベクトル検索、キーワード検索、ハイブリッド検索、再ランキングを選ぶ開発者向けに、評価方法とエラーにならない失敗要因を解説します。

主な機能

  1. 01ベクトル・キーワード検索の指針
  2. 02RRFによるハイブリッド検索
  3. 03検索用SQL関数
  4. 04評価とチューニングの実践
  5. 05pgvectorインデックスの指針
  6. 06再ランキングのトレードオフ

活用シーン

  1. 01文書のセマンティック検索構築
  2. 02キーワード検索とベクトル検索の併用
  3. 03検索結果の欠落や低品質の診断
  4. 04再ランキングやクエリ変更の評価

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/laguagu/claude-code-nextjs-skills --skill postgres-semantic-search

インストール後、Claude Code で/postgres-semantic-searchを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
evaluation.md
hybrid-search.md
keyword-search.md
pgvector.md
reranking.md
scripts/
fuzzy_search.sql
hybrid_search_bm25.sql
hybrid_search_fts.sql
indexes.sql
prefix_tsquery.sql
semantic_search.sql
setup.sql

詳細

PostgreSQL セマンティック検索

Postgres を使った検索の意思決定、実測結果、気づきにくい失敗モードと、scripts/ にある検証済みの SQL 構成要素をまとめています。公式ドキュメントで十分に説明されている構文はそちらに任せます。ここで取り上げるのは、エラーが出ないまま問題になるものです。

構築の順序

  1. ユーザーが実際に何を入力するかを確認します。識別子やコード、1〜3 語の用語、完全な質問、使用言語などです。これによって必要な検索方式が決まります。
  2. それらのクエリに適した、最もシンプルな検索から始めます。完全一致/キーワード検索、またはベクトル検索です。近似ベクトル検索を使う場合は、再現率の基準として完全ベクトル検索を残します。
  3. 調整を始める前に、長い質問と短い用語の 2 つの評価セットを作成します(evaluation.md)。
  4. ベースラインが必要なクエリで改善する場合に限り、検索方式を追加して RRF で統合します(hybrid-search.md)。
  5. リランカーを追加するのは最後にし、精度の高い候補リストの上位部分に適用します(reranking.md)。

一度に変更するのは 1 つだけにします。合格基準をあらかじめ定めてください。デフォルトでは、対象のクエリ群で実測した実行ごとのばらつきを超える改善を求め、もう一方の評価セットで重大な低下がないことを条件にします。クエリごとの改善と悪化を確認し、合意したレイテンシとコストの予算を守れることも条件にします。 以下の数値による結果は特定のコーパスでの観察であり、普遍的な性能保証ではありません。

選択

  • 次元数に応じてカラム型を選びます。プロバイダーでは決まりません。vector(N) は最大 2,000 次元までインデックス化できます。最大 4,000 次元までは halfvec にキャストしてインデックス化するか、halfvec カラムを使います。それを超える場合は、バイナリ量子化または Matryoshka による切り詰めを使います。
  • デフォルトでは HNSW を使い、メモリやビルド時間の制約で HNSW が使えない場合は IVFFlat を使います。行数だけでは決めず、完全検索と比較して再現率とレイテンシを測定します。
  • モデルは数カ月ごとに変わります。 プロバイダーの最新ドキュメントを参考に、埋め込みモデルとリランカーモデルを選びます。英語以外のテキストには多言語モデルを優先し、採用を決める前に対象言語で評価します。
  • ユーザーが質問と用語の両方を入力する場合はハイブリッド検索を使います。 フィンランド語の文字起こしコーパスでは、キーワード検索のみでは長い質問に弱く(最初に正解した割合は 24 %)、ベクトル検索のみでは短い用語に弱い結果でした(53 %)。ハイブリッド検索は、長い質問ではベクトル検索と同等、短い用語ではそれを上回りました(それぞれ 53 % と 93 %。詳細はhybrid-search.mdを参照)。
  • BM25 はどこでも利用できるわけではありません。マネージドホストによって異なり(Neon は pg_search を削除しました)、以下の修正を加えた標準の FTS で十分な場合がよくあります。

暗黙の失敗

エラーにはならず、返される行数が少なくなる、0 行になる、またはもっともらしい結果が返されます。

pgvector(pgvector.md):

  • 反復スキャンを有効にしない場合、HNSW の結果は hnsw.ef_search(デフォルトは 40)によって制限されます。LIMIT の値が大きい場合や、絞り込み条件の WHERE がある場合、結果が黙って少なくなります。hnsw.iterative_scan(デフォルトではオフ)を有効にするか、部分インデックスまたはパーティションを使用してください。
  • デフォルトの ef_search は、警告なしに再現率を下げることがあります。厳密検索との再現率が変わらなくなるまで値を上げてください。EXPLAIN にシーケンシャルスキャンが表示される場合、調整しても効果はなく、シーケンシャルスキャンのほうが速い実行計画である可能性もあります。
  • 距離のしきい値は MATERIALIZED CTE の外に置き、その他のフィルターはその中に置いてください。
  • SET は接続ごとに適用されます。トランザクションプーラーの背後では、同じトランザクション内で SET LOCAL を使用するか、関数レベルの SET を使用してください。
  • 類似度のしきい値はモデル固有です。あるモデル向けに調整したしきい値では、別のモデルで結果が返らないことがあります。1 - (a <#> b) はコサイン類似度ではありません。
  • クライアントは JS 配列を不正に処理します。node-postgres は {0.1,0.2} を送信し、Drizzle の sql テンプレートは配列を ($1, $2, ...) に展開しますが、どちらもベクトル入力では拒否されます。JSON.stringify(embedding) で送信し、::vector でキャストしてください。

キーワード(keyword-search.md):

  • plainto_tsquery と、websearch_to_tsquery に引用符なしで渡す通常の入力は、単語を AND で結合するため、長い質問では何も一致しないことがあります。通常の入力で検索する場合は、OR と ts_rank_cd を検討し、明示的なウェブ検索の引用符、OR、除外指定はそのまま保持してください。
  • unaccent はフィンランド語、スウェーデン語、ドイツ語、トルコ語で異なる単語を同一化します。装飾的なアクセントだけを、テキスト検索設定の中で除去してください。
  • 不要なゼロ幅文字がエクスポート時に混入すると、トークンに結合して語幹処理を妨げることがあります。意味のある ZWNJ/ZWJ は削除せず、一貫した方法で正規化してください。
  • ORM のマイグレーション後、生成された tsvector 列が通常の NULL 列に暗黙のうちに変わることがあります。ハイブリッド検索とベクトルのみの検索で結果一覧が同じ場合、キーワード側の検索が欠落しているか機能していない可能性があります。原因を調べる前に、NULL 件数とデータ投入済み件数、およびキーワードのみの検索結果を確認してください。
  • 屈折語のある言語での前方一致では、語を OR で結合し、ハイフンでつながれたトークンを展開する必要があります(prefix_tsquery.sql)。
  • % は文字列全体を比較します。長いテキストに対する前方一致や短いクエリには <% を使用してください。

非英語テキストとチャンク分割

  • 選択した埋め込みモデルのトークナイザーと入力上限に合わせて、チャンクのサイズを制限してください。比率はモデルとテキストによって異なります。OpenAI の cl100k_base(text-embedding-3)では、フィンランド語は英語の 4 文字以上に対して、1 トークンあたり約 2.5 文字です。そのため、英語向けに調整した文字数上限は約 2 倍も緩すぎます。エンドポイントはチャンクを拒否し、バッチ全体が失敗することもよくあります。
  • クエリの言語が検索対象と異なる場合、語彙検索側の効果が薄れ、多言語埋め込みモデルを使っていてもスコアが約 12 ポイント下がることがあります。キーワードの羅列ではなく文に翻訳してください(あるコーパスでは、キーワードの羅列を翻訳した場合、翻訳しない場合よりスコアが 14 ポイント低くなりました)。2 段階の融合も検討してください。
  • 複数語の同義語展開は、独立した単語として tsquery に追加され、汎用的な単語が順位付けを支配します。同じフレーズ内の他の語より桁違いに一般的な部分を削除してください。
  • 屈折語:あるフィンランド語コーパスでは、埋め込みが屈折を吸収しました。語幹処理側(Postgres の finnish)はキーワードのみの検索を向上させましたが、ハイブリッド検索は向上させませんでした。辞書ベースのレンマ化は、口語テキストと専門分野のテキストで語幹処理より性能が低く、複合語の分割は検索側にノイズを大量に増やしました。対象のテキストで再測定してください。前方一致では、屈折形のクエリから基本形にたどり着けません(keyword-search.md)。
  • 文字起こし:チャンクの長さを変えても、見つかる動画はほとんど変わりませんでした。一致したセグメント内の字幕キューを対象に、より細かい 2 回目の検索を行うと、該当する場面を特定できます。

詳細と測定結果:hybrid-search.md。

再ランキング

  • まず約30件の候補リストの先頭(上位5件)のみを再ランキングし、 短い語のセットが維持される場合に限り、対象を広げます。クロスエンコーダーは「Xについて述べている」ものを評価し、「Xを主題としている」ものではないため、短い語への言及が通過して上位に来ることがあります。フィンランド語の文字起こしコーパスでは、30件すべてを並べ替えると短い語の Hit@1 は 0.93 から 0.60~0.80 に低下し、上位10件では一部のモデルで語が1~2個失われましたが、上位5件では6つの再ランキングモデルのいずれも語を失いませんでした。対象のデータセットで深さを再測定してください。
  • 関連性を問う文言は、モデルより重要な場合があります(あるコーパスでは約30ポイントの差がありました)。すべての基準は送信するテキストから確認可能である必要があり、再ランキングモデルへの入力にはソースのタイトルを含めてください。
  • 実行可能な場所と、テキストがネットワークの外部に送信されてもよいかどうかで選択します。 2コア CPU での測定では、約1億2,000万パラメーターの多言語クロスエンコーダーは約0.3秒追加する一方、5億6,800万~6億パラメーターのモデルは10~25秒かかりました。データセンターの GPU では、Qwen3-Reranker-8B だけが、この小型 CPU モデルより明らかに高い精度を示しました(reranking.md)。ホスト型の再ランキングモデルはインフラストラクチャーが不要ですが、候補テキストを受け取ります。GPU を利用できるときはまず GPU で処理し、タイムアウト時のフォールバックとして CPU モデルを使う方法もあります。実際のハードウェアで p95 を測定してください。
  • まず候補リストの上限を測定してください。第1段階で取りこぼしたものを、再ランキングで取り戻すことはできません。

数値、モデルのトレードオフ、導入ルールについては、reranking.md を参照してください。

スクリプト

新しいサンプルデータベースでは、setup.sql、続けて indexes.sql と選択した関数ファイルを使用します。既存のデータベースでは、これらをマイグレーションとして確認・調整してください。これらは documents(id, title, content, metadata, embedding vector(1536)) と chunks(id, document_id, chunk_index, content, embedding) を前提としているため、すべてのファイルで名前と次元を一緒に変更してください。一部の関数ファイルは古いシグネチャを削除するため、適用前に依存するオブジェクトを確認してください。これらは構成要素であり、本番環境向けの完全なマイグレーション/認可ポリシーではありません。hnsw.ef_search を設定するものはありません。設定は呼び出し元で行います。

ファイル関数(実際のパラメーター名)
semantic_search.sqlmatch_documents(query_embedding, match_threshold, match_count)、match_documents_filtered(query_embedding, filter_metadata, match_threshold, match_count)、match_chunks(query_embedding, match_threshold, match_count)。match_threshold が NULL の場合は、通常の top-k です
hybrid_search_fts.sqlhybrid_search_fts(query_embedding, query_text, match_count, rrf_k, fts_language, vector_weight, keyword_weight)。どちらの入力も NULL にできます
hybrid_search_bm25.sqlhybrid_search_bm25(...)、hybrid_search_chunks_bm25(...)。fts_language を除いた同じパラメーターを使用します。pg_search が必要です
fuzzy_search.sqlfuzzy_search_trigram(query_text, similarity_threshold, max_results)、autocomplete_search(search_prefix, max_results)、hybrid_search_fuzzy_semantic(query_text, query_embedding, max_results, rrf_k)
prefix_tsquery.sqlprefix_tsquery(config, text [, join])
setup.sql、indexes.sql拡張機能、テーブルの例、インデックス

Supabase の .rpc() は引数を名前でバインドするため、キーのスペルを間違えると呼び出し時に失敗します。

const { data, error } = await supabase.rpc('hybrid_search_fts', {
  query_embedding: embedding, // number[]
  query_text: userQuery,
  match_count: 10,
  fts_language: 'simple',
});

// Drizzle or node-postgres: send the vector as text and cast it
await db.execute(sql`SELECT * FROM match_documents(${JSON.stringify(embedding)}::vector, NULL, 10)`);

参考資料

  • pgvector.md:型と次元数の上限、HNSW のチューニング、プランナーの選択、フィルターとしきい値、プーラー、ビルド、演算子。
  • keyword-search.md:tsquery の解析、アクセントと言語設定、不要なキーワード分岐、前方一致、トライグラム、ParadeDB。
  • hybrid-search.md:融合、ハイブリッド検索とベクトル検索の比較、チャンク化、トランスクリプト、異なる言語でのクエリ、同義語の拡張。
  • reranking.md:選択、深さ、ハードウェア、関連性の問題、余裕、導入。
  • evaluation.md:2 つの評価セット、バイアス、ノイズ、測定結果を誤らせる 4 つの要因。

バージョン

  • pgvector:SELECT extversion FROM pg_extension WHERE extname = 'vector' を実行して、インストール済みのバージョンを確認します。反復スキャンには 0.8.0 以降が必要です。現在の安定版リリースを確認してください。0.8.2 では並列 HNSW ビルドでのバッファーオーバーフローが、0.8.3 と 0.8.4 では HNSW のバキューム時の破損とエラーが、0.8.7 では IVFFlat ビルドでのバッファーオーバーフローが修正されています。アップグレードする前に、最新のインデックス修正とアップグレードに関する注意事項をお読みください。リリースは Git タグのみで公開されるため、空の Releases タブではなく、CHANGELOG をお読みください。
  • pg_search:0.25 以降は pgvector に依存するため、先に pgvector をインストールしてください。ParadeDB の API は頻繁に変更されるため、keyword-search.md を参照してください。

関連するスキル

必要なものスキル
Postgres の一般的なスキーマ、インデックス、RLS、プーリングsupabase-postgres-best-practices
チャットボットのオーケストレーション、セッション、ツール呼び出しnextjs-chatbot
AI SDK を介した埋め込みの呼び出しai-sdk

コメント