Snowflake Cortex Searchは、Snowflake内のテキストデータにベクトル検索とキーワード検索を組み合わせたハイブリッド検索をかけるマネージドサービスです。埋め込みの計算、インデックスの構築、元テーブルの変更に合わせた更新までをSnowflake側が受け持ち、利用者はSQL 1文でサービスを作成できます。用途はRAG(検索拡張生成)の検索エンジン、アプリ内の検索バー、Cortex Agentsの検索ツールの3つが中心です。
この記事では、CREATE CORTEX SEARCH SERVICEの書き方、Python・REST API・SEARCH_PREVIEWの使い分け、2026年5月18日に一般提供(GA)となったバッチ検索関数CORTEX_SEARCH_BATCH、料金の試算方法を、2026年10月8日時点の公式ドキュメントで確認した内容に沿って説明します。日本語データを扱うときの落とし穴にも触れます。
まとめ:Cortex Searchの要点
- 仕組み:ベクトル検索・キーワード検索・セマンティックリランキングの3段構成。チューニングなしで使える前提の設計です。
- 日本語データ:既定の埋め込みモデル
snowflake-arctic-embed-m-v1.5は英語専用です。日本語なら作成時にsnowflake-arctic-embed-l-v2.0などの多言語モデルを指定します。モデルは後から変更できません。 - 検索方法:アプリからはPython APIかREST API。
SEARCH_PREVIEWは動作確認用で、本番の検索には使いません。 - バッチ検索:
CORTEX_SEARCH_BATCHは名寄せ・重複排除など数千件以上のオフライン処理向けです。2,000件未満では通常の検索APIのほうが速い傾向があります。バッチ検索ではリランキングはサポートされません。 - 料金:検索待ち受けの費用が索引データ1GBあたり月6.3 AIクレジット。これに埋め込みトークン、ウェアハウス、ストレージの費用が加わります。検索がなくても待ち受け費用はかかるため、
AUTO_SUSPEND(2026年10月時点でプレビュー)で自動停止させるか、手動で停止します。 - 上限:ソースクエリの結果は4億行未満。検索は1サービス20QPS、アカウント全体で140QPSが目安で、超えるとHTTP 429が返ります。
Cortex Searchの検索方式と使われる場面
Cortex Searchは1回の検索で3つの処理を組み合わせます。意味の近い文書をベクトル検索で拾い、語の一致する文書をキーワード検索で拾い、最後にセマンティックリランキングで並べ替えます。言い換えに強いベクトル検索と、型番や固有名詞に強いキーワード検索を併用するため、どちらか一方だけの検索より取りこぼしが減ります。
公式ドキュメントが挙げる主な用途は、LLMチャットボットのRAGエンジン、アプリに埋め込む社内検索、Cortex Agentsの非構造化データ検索の3つです。エージェントから呼び出す構成はCortex AgentsのCREATE AGENTとツール設定で扱っています。Cortex AI全体の中での位置づけはSnowflake Cortex AIの機能一覧が参考になります。
リランキングは精度を上げる代わりに遅延が増えます。公式によれば、無効化すると平均100〜300ミリ秒速くなります。リランキングの考え方はクロスエンコーダとバイエンコーダの違いで詳しく説明しています。
CREATE CORTEX SEARCH SERVICEの書き方
必須パラメータと日本語FAQの作成例
単一インデックスのサービスでは、ON(検索対象のテキスト列)、WAREHOUSE(ソースクエリの実行と索引の更新に使うウェアハウス)、TARGET_LAG(元データからの許容遅延)を指定します。絞り込みに使う列は ATTRIBUTES に並べます。次の例は、日本語のFAQを検索するサービスを作るSQLです。
CREATE OR REPLACE CORTEX SEARCH SERVICE support_db.search.faq_search
ON body
PRIMARY KEY (doc_id)
ATTRIBUTES category, updated_at
WAREHOUSE = cortex_search_wh
TARGET_LAG = '1 hour'
EMBEDDING_MODEL = 'snowflake-arctic-embed-l-v2.0'
AUTO_SUSPEND = 3600
AS (
SELECT body, category, updated_at, doc_id, title
FROM support_db.raw.faq_chunks
);
省略時の既定値は REFRESH_MODE = INCREMENTAL(差分更新)、INITIALIZE = ON_CREATE(作成時に同期で初回更新)です。ON_CREATE では作成文の中で索引を作るため、行数とウェアハウスサイズによっては完了まで数時間かかります。PRIMARY KEY にはTEXT型の列を指定します。この例のdoc_idはチャンクごとに一意である必要があり、同じ文書を複数行に分割するときはチャンク単位のIDを用意します。主キーが重複する行は索引で無視されます。主キーを指定すると、更新時に変わった行だけを再埋め込みする経路が使えるので、毎日差分が入るテーブルでは付けておくべきです。
ウェアハウスは、サービスごとにMEDIUM以下の専用ウェアハウスを用意するよう公式が推奨しています。索引構築の時間の大半は埋め込み計算で、LARGEより大きくしてもほとんど速くなりません。
埋め込みモデル4種と日本語での選び方
日本語データでは、既定の埋め込みモデルに注意します。EMBEDDING_MODEL を省略すると snowflake-arctic-embed-m-v1.5 が使われますが、このモデルの対応言語は英語のみで、日本語の検索品質は保証されていません。しかも EMBEDDING_MODEL は作成後に変更できず、変えるには CREATE OR REPLACE で作り直して全行を再埋め込みすることになります。
| モデル | 次元 | 最大トークン | 言語 | 単価(100万トークン) | AWS東京 | Azure東日本 |
|---|---|---|---|---|---|---|
| snowflake-arctic-embed-m-v1.5(既定) | 768 | 512 | 英語のみ | 0.03 AIクレジット | ○ | ○ |
| snowflake-arctic-embed-l-v2.0 | 1024 | 512 | 多言語 | 0.05 AIクレジット | ○ | ○ |
| snowflake-arctic-embed-l-v2.0-8k | 1024 | 8192 | 多言語 | 公式表で確認 | ○ | ○ |
| voyage-multilingual-2 | 1024 | 32,000 | 多言語 | 0.07 AIクレジット | ○ | × |
単価は2026年10月2日発効のSnowflake Service Consumption Tableの値です。日本語なら、まず snowflake-arctic-embed-l-v2.0 を選ぶのが無難です。8kや32,000トークンのモデルがあっても長文をそのまま入れる必要はなく、公式は検索列を512トークン以下のチャンクに分けることを推奨しています。分割には SNOWFLAKE.CORTEX.SPLIT_TEXT_RECURSIVE_CHARACTER 関数を使えます。トークン数を超えた部分は埋め込みでは切り捨てられますが、キーワード検索には全文が使われます。日本語の埋め込みモデルを選ぶ基準は埋め込みモデルの仕組みと日本語モデルの選び方にまとめています。
リージョンについては、AWSの東京(ap-northeast-1)とAzureのJapan Eastでネイティブに提供されています。AWSの大阪はクロスリージョン推論を有効にした場合だけ使えます。
必要な権限とchange trackingの要件
作成するロールには、SNOWFLAKE.CORTEX_USER または SNOWFLAKE.CORTEX_EMBED_USER データベースロールのほか、次の3つの権限が要ります。スキーマへの CREATE CORTEX SEARCH SERVICE、元テーブルへの SELECT、ウェアハウスへの USAGE です。検索する側のロールには、サービス本体と、それが属するデータベース・スキーマの USAGE を付与します。
GRANT USAGE ON DATABASE support_db TO ROLE app_search;
GRANT USAGE ON SCHEMA support_db.search TO ROLE app_search;
GRANT USAGE ON CORTEX SEARCH SERVICE support_db.search.faq_search TO ROLE app_search;
差分更新には、参照するすべての元オブジェクトでchange trackingが有効になっている必要があります。作成時にSnowflakeが自動で有効化を試みますが、元テーブルを作り直すと解除されるので再設定が必要です。ソースクエリはDynamic Tableの差分更新の条件を満たす形で書く必要があり、Time Travelの保持期間(DATA_RETENTION_TIME_IN_DAYS)を0にすることもできません。
Cortex Searchサービスの検索方法
検索の入口は3つあり、パラメータは共通です。query、columns(返す列)、filter、scoring_config、limit(既定10、最大1000)を渡します。
| 方式 | 向く用途 | 主な制約 |
|---|---|---|
| Python API | アプリ・RAGの本番 | Snowflake Python APIs 0.8.0以降 |
| REST API | Snowflake外のアプリ | PAT・キーペア・OAuthで認証 |
| SEARCH_PREVIEW | 作成直後の動作確認 | 文字列リテラルのみ・高遅延 |
| CORTEX_SEARCH_BATCH | 数千件以上の一括照合 | 起動待ちあり・リランキングなし |
Python APIとREST APIでの呼び出し
Python APIは snowflake.core の Root からサービスを取得し、search メソッドを呼びます。次の例は、カテゴリが「請求」で2026年以降に更新された文書だけを対象に検索します。
from snowflake.core import Root
from snowflake.snowpark import Session
# CONNECTION_PARAMETERS は account・user・認証方式・role・warehouse を入れたdict
session = Session.builder.configs(CONNECTION_PARAMETERS).create()
svc = (Root(session)
.databases["support_db"]
.schemas["search"]
.cortex_search_services["faq_search"])
resp = svc.search(
query="請求書の再発行",
columns=["title", "body", "category"],
filter={"@and": [
{"@eq": {"category": "請求"}},
{"@gte": {"updated_at": "2026-01-01"}}
]},
limit=5,
)
print(resp.to_json())
REST APIのエンドポイントは https://<account_url>/api/v2/databases/<db>/schemas/<schema>/cortex-search-services/<service>:query の形式です。本文には同じJSONパラメータをPOSTで送ります。
curl -X POST "https://${ACCOUNT_URL}/api/v2/databases/support_db/schemas/search/cortex-search-services/faq_search:query" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer ${PAT}" \
--data '{"query": "請求書の再発行", "columns": ["title", "body"], "limit": 5}'
フィルタ演算子とscoring_configの調整
フィルタには ATTRIBUTES に指定した列だけを使えます。一致演算子は5つで、@eq(TEXT・数値の等価)、@contains(ARRAYに値を含む)、@gte・@lte(数値・日付の範囲)、@primarykey(主キーの一致)です。これらを @and・@or・@not で組み合わせます。
ランキングは scoring_config で調整します。数値列によるブースト、日付による減衰(新しい文書を上げる)、ベクトル・キーワード・リランカーの重み付け、リランキングの無効化("reranker": "none")が可能です。よく使う設定は ALTER CORTEX SEARCH SERVICE ... ADD SCORING PROFILE で名前を付けて保存し、クエリから scoring_profile で呼び出せます。
SEARCH_PREVIEWを本番で使わない理由
SNOWFLAKE.CORTEX.SEARCH_PREVIEW はワークシートやノートブックで結果を確かめるための関数です。引数には文字列リテラルしか渡せず、テーブルの列を入力にした一括検索はできません。遅延もREST・Pythonより大きいため、アプリの検索経路に組み込むのは避けます。
SELECT PARSE_JSON(
SNOWFLAKE.CORTEX.SEARCH_PREVIEW(
'support_db.search.faq_search',
'{"query": "請求書の再発行", "columns": ["title", "category"], "limit": 3}'
)
)['results'] AS results;
索引の中身を行単位で確認したいときは、テーブル関数 CORTEX_SEARCH_DATA_SCAN(SERVICE_NAME => '...') を使います。元の列に加え、_GENERATED_EMBEDDINGS_ で始まるベクトル列が返ります。実行にはサービスのOPERATE権限が必要です。
CORTEX_SEARCH_BATCHによる一括検索
LATERAL結合で書く名寄せの例
CORTEX_SEARCH_BATCH は、クエリを1列に並べたテーブルを入力にして、既存のCortex Searchサービスへまとめて問い合わせるテーブル関数です。2026年3月25日にパブリックプレビュー、2026年5月18日にGAとなりました。想定用途は、表記揺れのある社名や商品名を正規マスタへ突き合わせるエンティティ解決、重複排除、顧客データの突合(オーディエンスマッチング)、分類体系への振り分けです。
-- 取引先マスタ(正規名)を索引したサービスへ、表記揺れのある社名を一括照合
SELECT
q.raw_name,
r.*
FROM crm_db.staging.imported_accounts AS q,
LATERAL CORTEX_SEARCH_BATCH(
service_name => 'crm_db.search.account_master_search',
query => q.raw_name,
limit => 3
) AS r;
引数は service_name が必須で、query・multi_index_query・filter のどれか1つ以上を指定します。limit の既定は10です。options には行ごとの scoring_config と、検索に使う索引の複製数 replicas(既定2)を渡せます。名寄せでは、正解ラベル付きの照合データで誤統合率を評価してから、自動統合と人手確認の閾値を決めます。スコアの種類も区別します。ベクトル索引の cosine_similarity は-1〜1の範囲でクエリ間の比較ができますが、text_match はクエリ間で比較できないので、一律の閾値には使いません。
通常検索とバッチ検索の選択目安:2,000件
バッチ検索はジョブごとに専用の計算資源を立ち上げるため、起動の待ち時間がかかります。Batch Cortex Searchの公式ドキュメントは「2,000件未満なら、通常はインタラクティブなAPI(PythonまたはREST)のほうが速い」と明記しています。スループットは索引のデータ量とフィルタ条件の複雑さで変わり、クエリを投げる側のウェアハウスのサイズには左右されません。
「1万件で47倍速い」という数字は、Snowflake Developersのガイド「Getting Started with Batch Cortex Search」に載っているものです。読むときは前提に注意が必要です。
| クエリ数 | 通常検索(逐次) | バッチ | 倍率 |
|---|---|---|---|
| 100件 | 34.9秒 | 35.1秒 | 1.0倍 |
| 500件 | 2分55秒 | 56.0秒 | 3.1倍 |
| 1,000件 | 5分59秒 | 62.0秒 | 5.8倍 |
| 5,000件 | 約29分(推計) | 約68秒 | 25.7倍 |
| 10,000件 | 約58分(推計) | 73.6秒 | 47.5倍 |
公開されているノートブックを読むと、通常検索の側は for ループで1件ずつ順番に呼んでいます。毎秒約2.9件はサービスの上限ではなく、1回あたり約0.35秒の待ちを直列に積んだ結果です。5,000件と1万件の通常検索の時間は実測ではなく外挿で、ガイド自身が「公式の性能値ではない」と断っています。1サービス20QPSは公式が示す上限緩和の相談基準で、並列化による達成保証ではありません。数百件なら通常APIの並列呼び出しを候補にし、実際の処理時間と429の発生率を測って判断します。バッチ検索の利点がはっきり出るのは、数千件以上で、しかも並列化や429エラーへの再試行を自前で組みたくない場面です。
バッチ検索で変わる仕様
- リランキングなし:
scoring_configにリランカーの設定を書いても無視されます。通常検索と同じ順位にはならないので、閾値は必ずバッチの結果で決め直します。 - 停止中のサービスも検索できる:待ち受け(SERVING)を止めたサービスにも使えるため、バッチ専用の索引なら待ち受けを止めて費用を抑えられます。
- 通常検索と干渉しない:計算資源が別なので、バッチ実行中もアプリ側の検索性能は落ちません。
- ベクトル索引は1つまで:
multi_index_queryに含められるベクトル索引は現状1つです。 - クエリの埋め込みが有料:通常検索では無料のクエリ埋め込みに、バッチでは料金がかかります。
Cortex Searchの料金と試算方法
費用は6つの要素で決まります。どれが継続的にかかるかを押さえておくと見積もりで外しません。
| 費目 | 発生タイミング | 単価の目安 |
|---|---|---|
| サービング(待ち受け) | SERVING中は常時 | 6.3 AIクレジット/GB月 |
| 埋め込みトークン | 行の追加・更新時 | 0.03〜0.07 AIクレジット/100万 |
| 仮想ウェアハウス | 初回構築・更新・バッチ実行時 | ウェアハウスのクレジット |
| ストレージ | 常時 | TB単位の定額 |
| クラウドサービス | 変更検知時 | 日次でウェアハウス費の10%超のみ |
| バッチ検索 | ジョブ実行中 | 0.12 AIクレジット/GB時 |
公式の計算式に当てはめて試算します。日本語向けに1024次元の snowflake-arctic-embed-l-v2.0 を使い、100万行・1行あたり約1,000バイトの索引を作るとします。索引データは100万行×(1024次元×4バイト+1,000バイト)で約5.1GBなので、サービングは月約32 AIクレジットです。1行500トークンとすると、初回の埋め込みは5億トークン÷100万トークン×0.05 AIクレジットで、25 AIクレジットになります。同じ索引のバッチ検索では、サービング費用だけでジョブ実行1時間あたり約0.61 AIクレジット(起動時間は除く)です。別途、入力クエリの埋め込み費用と、ジョブを実行する仮想ウェアハウスの費用がかかります。replicas を増やすと早く終わりますが、総額は変わりません。
AIクレジットはウェアハウスのクレジットとは別建てで、オンデマンドの単価は1クレジット2.00ドル(Global)または2.20ドル(Regional)です。実績は SNOWFLAKE.ACCOUNT_USAGE.CORTEX_SEARCH_DAILY_USAGE_HISTORY で、サービスごと・費目ごとに日次で確認できます。
SELECT database_name, schema_name, service_name, service_id,
consumption_type, SUM(credits) AS credits
FROM snowflake.account_usage.cortex_search_daily_usage_history
WHERE usage_date >= DATEADD(day, -30, CURRENT_DATE())
GROUP BY database_name, schema_name, service_name, service_id, consumption_type
ORDER BY credits DESC;
CONSUMPTION_TYPE は SERVING・EMBED_TEXT_TOKENS・BATCH の3種です。ウェアハウス費はこのビューに入らないので、サービス専用のウェアハウスを割り当てて切り分けます。バッチのクエリ単位の明細は CORTEX_SEARCH_BATCH_QUERY_USAGE_HISTORY、時間単位のサービングは CORTEX_SEARCH_SERVING_USAGE_HISTORY にあります。
費用を抑える設定
- AUTO_SUSPEND:2026年10月8日時点ではプレビュー機能で、検索が一定時間ないとSERVINGを自動停止します。最小は1800秒(30分)です。停止中に最初の検索が来ると再開まで待たされ、同時に来た検索には
Retry-Afterヘッダ付きのHTTP 429が返るので、クライアント側に再試行を入れます。 - TARGET_LAGは利用頻度に合わせる:元データが5分ごとに更新されても、検索が1時間に1回なら1時間で十分です。
- 元テーブルはMERGEで更新する:
CREATE OR REPLACEで作り直すと全行が再埋め込みされます。行のどの列が変わっても検索列は再埋め込みされるので、更新はまとめて1回にします。 - 列は最初に多めに入れる:ソースクエリのスキーマを変えると全面更新になります。後で使いそうな列は作成時に含めておきます。
運用上の上限と障害対応
本番運用で問題になりやすい上限は次のとおりです。
- 行数:ソースクエリの結果は4億行未満。超えると作成がエラーになり、引き上げにはアカウントチームへの相談が必要です。古い資料にある1億行は現行の値ではありません。
- スループット:1サービス20QPS、アカウント全体140QPSを超える場合は相談が必要です。過負荷時はHTTP 429が返るので、指数バックオフ付きの再試行を実装します。
- クローン不可:Cortex Searchサービスはクローンに対応していません。開発環境を複製するときは作り直しになります。
- 元テーブルの変更:稼働中は元テーブルの削除や作り直しを避け、変更するときはサービスを止めてから行います。
ソースクエリの更新が5回続けて失敗すると、INDEXING(索引の更新)が自動で停止します。原因は DESCRIBE の INDEXING_ERROR 列に出ます。修正したら RESUME INDEXING で再開します。
DESCRIBE CORTEX SEARCH SERVICE support_db.search.faq_search;
-- INDEXING_STATE = SUSPENDED なら INDEXING_ERROR を確認して原因を直す
ALTER CORTEX SEARCH SERVICE support_db.search.faq_search RESUME INDEXING;
-- 開発期間など使わない間は待ち受けを止める(OPERATE権限が必要)
ALTER CORTEX SEARCH SERVICE support_db.search.faq_search SUSPEND SERVING;
Cortex Searchを選ばないほうがよい場面
次の条件に当てはまるなら、Cortex Search以外の手段を先に検討します。
- 完全一致の検索しかない:注文番号やSKUの完全一致だけなら、通常のSQLの条件指定で済みます。待ち受け費用を払う理由がありません。
- 検索の量が20QPSを常に超える:一般向けの検索サービスのように高いQPSが常時かかる用途では、上限緩和の交渉が前提になります。専用の検索エンジンと費用を比べてから決めます。
- データがSnowflakeの外にある:索引を作るためだけにデータをSnowflakeへ取り込むなら、取り込み・保管の費用と運用が増えます。
- 数値の集計やSQL生成が目的:売上の集計のような質問には、構造化データを自然言語からSQLに変換するCortex Analystが向いています。
逆に、Snowflake内に社内文書・問い合わせ履歴・商品マスタがあり、権限管理をSnowflakeのロールに揃えたい場合は、既存データを使って検索基盤を構築できるCortex Searchが候補になります。検索はサービス所有者の権限で実行されます。サービスの検索権限を持つユーザーは、自身が元テーブルで参照できない行も検索できるため、公開するデータ範囲とサービスへの権限付与を別途設計します。
2025〜2026年の主な機能追加
- 2025年4月28日:リランキングの無効化がGA
- 2025年10月3日:名前付きscoring profileがGA
- 2026年3月12日:マルチインデックス検索と、独自に計算したベクトルの持ち込みがGA
- 2026年5月18日:Batch Cortex Search(CORTEX_SEARCH_BATCH)がGA
- 2026年10月6日:データリネージに対応
マルチインデックスは、商品名やSKUはキーワード索引、説明文はベクトル索引というように、列ごとに検索方式を分ける機能です。作成時に ON の代わりに TEXT INDEXES と VECTOR INDEXES を指定します。
よくある質問
Cortex Searchは日本語に対応していますか?
対応しています。ただし既定の埋め込みモデルは英語専用なので、作成時に EMBEDDING_MODEL = 'snowflake-arctic-embed-l-v2.0' などの多言語モデルを指定してください。作成後にモデルは変更できません。
Cortex Search Serviceの作成にはどれくらい時間がかかりますか?
既定の INITIALIZE = ON_CREATE では作成文の中で索引を構築するため、データ量とウェアハウスのサイズによっては数時間かかります。すぐに返したい場合は ON_SCHEDULE を指定すると、次の定期更新で索引が作られます。
SEARCH_PREVIEWとREST APIはどう使い分けますか?
SEARCH_PREVIEW はSQLで結果を確かめるための関数で、本番アプリには使いません。アプリからはREST APIかPython APIで呼び出します。大量のクエリをまとめて処理するなら CORTEX_SEARCH_BATCH を使います。
CORTEX_SEARCH_BATCHはまだプレビューですか?
2026年5月18日にGAになりました。Cortex Searchが提供されているすべてのリージョンで使えます。プレビューと書かれた古い解説やLIMITEDACCESS配下のページは、現在の状態と異なります。
検索していない間も料金はかかりますか?
かかります。サービングの費用はSERVINGが有効な間、検索の有無に関係なく発生します。AUTO_SUSPEND を設定するか、ALTER CORTEX SEARCH SERVICE ... SUSPEND SERVING で止めてください。