- はじめに:SQLだけでLLMを呼び出せるAI_COMPLETE
- AI_COMPLETEとは?
- 旧SNOWFLAKE.CORTEX.COMPLETEとの違い
- いちばんシンプルなAI_COMPLETEの使い方
- テーブル列と組み合わせる
- プロンプトを作るときのコツ
- model_parametersで応答をコントロールする
- show_detailsでトークン使用量を確認する
- response_formatで構造化出力を返す
- JSON Schemaでより厳密に構造化する
- return_error_detailsでエラー詳細を確認する
- 生成結果をテーブルに保存する
- 差分処理で再実行コストを抑える
- AI_COMPLETEを使うための権限
- コスト・トークン・ウェアハウスの考え方
- 使用量を確認するSQL
- AI_COMPLETEに向いている用途・注意が必要な用途
- 専用AI関数との使い分け
- よくあるエラーと確認ポイント
- まとめ
- 参考リンク
- 関連記事
はじめに:SQLだけでLLMを呼び出せるAI_COMPLETE
Snowflakeの AI_COMPLETE は、SQLからLLMを呼び出して文章生成、要約、分類、抽出などを実行できるCortex AI関数です。以前は SNOWFLAKE.CORTEX.COMPLETE を使う例も多くありましたが、Snowflake公式では AI_COMPLETE が COMPLETE の更新版として案内されています。これから新しく実装する場合は、AI_COMPLETE を中心に覚えるのがおすすめです。
本記事では、初心者の方に向けて、最小例から構造化出力・エラー詳細・コスト管理・権限・専用関数との使い分けまで、実務で必要な範囲を整理します。Cortex全体の位置づけはSnowflake Cortex入門もあわせてご覧ください。
AI_COMPLETEとは?
AI_COMPLETE は、プロンプト(指示文)とモデル名を渡すと、LLMによる生成結果を返すCortex AI関数です。SQLの関数として呼び出せるため、外部APIキーやSDKを準備しなくても、Snowflake内のデータと直接組み合わせて使えます。
- 文章生成、要約、分類、抽出など幅広い用途に使える
- テーブル列を
||演算子で連結してプロンプトに渡せる - 構造化出力(
response_format)、トークン詳細(show_details)、エラー詳細(return_error_details)などのオプションがある - Snowflakeのロール・行レベルセキュリティ・マスキングがそのまま効く
旧SNOWFLAKE.CORTEX.COMPLETEとの違い
旧 SNOWFLAKE.CORTEX.COMPLETE は既存SQLや過去記事で見かけることがあります。公式では AI_COMPLETE が更新版として案内されているため、最新機能を使う場合や新規実装では AI_COMPLETE を使うのがおすすめです。既存SQLを保守する場合は、旧 COMPLETE の仕様も確認しておきましょう。
| 比較項目 | AI_COMPLETE | SNOWFLAKE.CORTEX.COMPLETE |
|---|---|---|
| 位置づけ | 現在のAI関数 | 旧Cortex関数として既存SQLで見かけることがある |
| 新規実装 | 推奨 | 基本的にはAI_COMPLETEへ寄せる |
| 入力 | テキスト、画像、Prompt objectなど | テキスト、会話形式、画像対応のバリエーションあり |
| 構造化出力 | response_formatで指定可能 | 旧形式の仕様を確認 |
| エラー詳細 | return_error_detailsが利用可能 | 仕様差分を確認 |
| 使い分け | 新しく書くSQL | 既存SQLの保守・読み替え |
非推奨化の正確な期限など、公式で明確にされていない情報は本記事では断定しません。最新の状態はSnowflake公式ドキュメントで確認してください。
いちばんシンプルなAI_COMPLETEの使い方
まずは1つのプロンプトをLLMに渡すだけの最小例です。モデル名とプロンプトの2つを指定します。
SELECT AI_COMPLETE(
'mistral-large2',
'Snowflakeを3行で初心者向けに紹介してください。'
) AS answer;
戻り値は文字列です。検証段階では、まずこの形で「ちゃんと動くか」「出力イメージはどうか」を確認するのがおすすめです。
テーブル列と組み合わせる
実務では、テーブル列の値をプロンプトに組み込んで使うのが基本パターンです。レビュー文を要約する例を見てみましょう。
SELECT
review_id,
AI_COMPLETE(
model => 'mistral-large2',
prompt => '次のレビューを30文字以内で要約してください。レビュー: ' || review_text
) AS summary
FROM customer_reviews
WHERE review_text IS NOT NULL
LIMIT 10;
LIMITを付けて少量で検証するWHERE条件で対象を絞る- 本番実行前に件数・文字数・トークン数を見積もる
- 同じ行に何度も実行すると、そのたびにコストが発生する
プロンプトを作るときのコツ
AI_COMPLETEで安定した結果を得るには、プロンプトの書き方がとても大切です。
- 何をしてほしいかを最初に書く
- 出力形式を明確にする(箇条書き、JSON、1文だけなど)
- 文字数や箇条書き数を指定する
- 入力データと指示文を「レビュー:」「問い合わせ:」などのラベルで区切る
- 重要な分類や抽出では、選択肢を明示する
- 機密情報や個人情報をむやみに渡さない
問い合わせを4カテゴリに分類する例:
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => '次の問い合わせを「配送」「返品」「請求」「その他」のいずれかに分類してください。回答はカテゴリ名だけにしてください。問い合わせ: ' || inquiry_text,
model_parameters => {'temperature': 0, 'max_tokens': 20}
) AS category
FROM inquiries
LIMIT 10;
model_parametersで応答をコントロールする
model_parameters を使うと、出力のランダム性や最大トークン数などを細かく調整できます。
| パラメータ | 役割 | 使い方の目安 |
|---|---|---|
| temperature | 出力のランダム性 | 要約・分類は低め、アイデア出しは高め |
| top_p | 出力候補の多様性 | 通常はtemperatureとどちらかを調整 |
| max_tokens | 最大出力トークン数 | 長すぎる回答を防ぐ。小さすぎると切れる |
| guardrails | Cortex Guardで有害応答をフィルタ | 外部向け・社内展開時に検討 |
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => 'Snowflakeのウェアハウスとは何か、初心者向けに説明してください。',
model_parameters => {
'temperature': 0.2,
'max_tokens': 200,
'guardrails': true
}
) AS answer;
temperatureを0に近づけると回答が安定しやすくなります。max_tokensが小さすぎると途中で切れることがあります。guardrails利用時はCortex Guard分の追加コストが発生する場合があります。- 重要用途ではAI出力を人手で確認するフローを入れましょう。
show_detailsでトークン使用量を確認する
show_details => TRUE を指定すると、回答だけでなく、モデル名や使用トークン数などの詳細情報を含むOBJECTが返ります。コスト感をつかむために、検証段階では show_details を使うと便利です。
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => 'Snowflakeを3行で説明してください。',
model_parameters => {'max_tokens': 100},
show_details => TRUE
) AS result;
トークン数を取り出して列にする例:
WITH result AS (
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => 'Snowflakeを3行で説明してください。',
show_details => TRUE
) AS res
)
SELECT
res:choices[0]:messages::string AS answer,
res:usage:prompt_tokens::number AS prompt_tokens,
res:usage:completion_tokens::number AS completion_tokens,
res:usage:total_tokens::number AS total_tokens
FROM result;
response_formatで構造化出力を返す
response_format を指定すると、文字列ではなく構造化されたOBJECTで結果を取り出せます。後続SQLで列に展開しやすくなり、AIを使った分類・抽出パイプラインを組みやすくなります。
戻り値の形は、response_format と show_details の組み合わせで変わります。
| 指定 | 戻り値の主な形 | 用途 |
|---|---|---|
response_format なし、show_details なし | 文字列 | 単純な回答生成 |
response_format なし、show_details => TRUE | choices / usage を含むOBJECT | トークン数確認 |
response_format あり、show_details なし | 指定した構造のOBJECT | 後続SQLで処理 |
response_format あり、show_details => TRUE | structured_output / usage を含むOBJECT | 構造化出力とトークン数を同時確認 |
TYPE OBJECT でフィールドを定義する書き方が、SQLとしてはいちばん読みやすくおすすめです。
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => '次の問い合わせを分類してください。内容: 商品が届かないので配送状況を確認したいです。',
model_parameters => {
'temperature': 0,
'max_tokens': 500
},
response_format => TYPE OBJECT(
category STRING,
urgency STRING,
summary STRING
)
) AS result;
JSON Schemaでより厳密に構造化する
より厳密に出力形式を指定したい場合は、response_format にJSON Schemaを渡せます。フィールドの型、必須項目、追加プロパティ可否などを細かく制御できます。
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => '次の問い合わせを分類してください。内容: 商品が届かないので配送状況を確認したいです。',
model_parameters => {
'temperature': 0,
'max_tokens': 500
},
response_format => {
'type': 'json',
'schema': {
'type': 'object',
'properties': {
'category': {'type': 'string'},
'urgency': {'type': 'string'},
'summary': {'type': 'string'}
},
'required': ['category', 'urgency', 'summary'],
'additionalProperties': false
}
}
) AS result;
- JSON Schemaを使うと、より厳密に出力形式を指定できます。
- OpenAI系モデルでは、すべてのプロパティを
requiredに含め、各ノードでadditionalProperties: falseが必要になる場合があります。 - 仕様は変わる可能性があるため、最新の公式ドキュメントを確認してください。
show_details => TRUE と response_format を同時に使うと、構造化出力に加えてトークン使用量も同時に取れます。
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => '次の問い合わせを分類してください。内容: 商品が届かないので配送状況を確認したいです。',
response_format => TYPE OBJECT(
category STRING,
urgency STRING,
summary STRING
),
show_details => TRUE
) AS result;
この場合、通常の choices ではなく、structured_output と usage を含むOBJECTが返ります。構造化結果は result:structured_output、トークン数は result:usage:total_tokens などで取り出せます。
return_error_detailsでエラー詳細を確認する
通常、処理できない入力ではNULLが返ることがあります。複数行クエリでは一部の行が失敗しても、クエリ全体が止まらない場合もあるため、本番でエラー行だけを再処理できる仕組みがあると安心です。return_error_details => TRUE を指定すると、成功時は value、失敗時は error を含むOBJECTが返ります。
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => 'Snowflakeを1文で説明してください。',
return_error_details => TRUE
) AS result;
- 通常、処理できない入力ではNULLが返ります。
- 複数行クエリでは一部の行が失敗しても、クエリ全体が止まらない場合があります。
return_error_details => TRUEを指定すると、成功時はvalue、失敗時はerrorを確認できます。- 本番ではエラー行だけを再処理できる設計にしておきましょう。
生成結果をテーブルに保存する
毎回AI関数を再実行するとコストが増えていきます。一度生成した結果はテーブルに保存し、後続クエリではそれを参照するのがおすすめです。
CREATE OR REPLACE TABLE customer_reviews_ai_summary AS
SELECT
review_id,
review_text,
AI_COMPLETE(
model => 'mistral-large2',
prompt => '次のレビューを30文字以内で要約してください。レビュー: ' || review_text
) AS summary,
CURRENT_TIMESTAMP() AS generated_at
FROM customer_reviews
WHERE review_text IS NOT NULL
LIMIT 10;
- 毎回AI関数を再実行するとコストが増えます。
- 一度生成した結果はテーブルに保存して使い回します。
- 更新日時やIDを使って、新規・変更分だけ再生成する設計にします。
- 本番では一時テーブルではなく、履歴管理できる正規のテーブルを検討します。
差分処理で再実行コストを抑える
未処理の行だけを対象にすれば、同じ行に対する重複実行を避けられます。次のSQLは「既存サマリテーブルに無いレビューだけを処理する」概念例です。
CREATE OR REPLACE TABLE customer_reviews_ai_summary AS
SELECT
r.review_id,
r.review_text,
AI_COMPLETE(
model => 'mistral-large2',
prompt => '次のレビューを30文字以内で要約してください。レビュー: ' || r.review_text
) AS summary,
CURRENT_TIMESTAMP() AS generated_at
FROM customer_reviews r
LEFT JOIN customer_reviews_ai_summary s
ON r.review_id = s.review_id
WHERE r.review_text IS NOT NULL
AND s.review_id IS NULL
LIMIT 100;
注意:上記は概念例です。実務では INSERT INTO や MERGE を使って、既存テーブルを上書きしない設計にしてください。Streamsや更新日時カラムと組み合わせると、増分処理をきれいに設計できます。
AI_COMPLETEを使うための権限
AI_COMPLETEを使うには、利用ロールにAI関数の利用権限と、必要なデータベースロールが必要です。検証環境では広めの権限、本番環境では必要な関数だけに絞る設計を検討します。
検証用ロール例(AI関数全体を許可):
USE ROLE ACCOUNTADMIN;
CREATE ROLE IF NOT EXISTS ai_user_role;
GRANT USE AI FUNCTIONS ON ACCOUNT TO ROLE ai_user_role;
GRANT DATABASE ROLE SNOWFLAKE.AI_FUNCTIONS_USER TO ROLE ai_user_role;
GRANT ROLE ai_user_role TO USER <USER_NAME>;
個別関数だけ許可する例(AI_COMPLETEのみ):
USE ROLE ACCOUNTADMIN;
CREATE ROLE IF NOT EXISTS ai_complete_user_role;
GRANT USE AI FUNCTION AI_COMPLETE
ON ACCOUNT TO ROLE ai_complete_user_role;
GRANT DATABASE ROLE SNOWFLAKE.AI_FUNCTIONS_USER
TO ROLE ai_complete_user_role;
GRANT ROLE ai_complete_user_role TO USER <USER_NAME>;
USE AI FUNCTIONSはAI関数全体を許可する権限です。USE AI FUNCTION AI_COMPLETEはAI_COMPLETEだけを許可する個別権限です。SNOWFLAKE.CORTEX_USERまたはSNOWFLAKE.AI_FUNCTIONS_USERのデータベースロールが必要になる場合があります。- 本番では必要な関数だけに絞ります。
PUBLICに広く付与しすぎないようにしてください。
コスト・トークン・ウェアハウスの考え方
- AI_COMPLETEは入力トークンと出力トークンの両方がコストに影響します。
show_detailsで実行後のトークン数を確認できます。AI_COUNT_TOKENSは事前見積もりに使えますが、実際の課金トークン数と完全一致しない場合があります。guardrails利用時はCortex Guard分の追加コストが発生する場合があります。- AI関数を呼び出すクエリでは、通常のウェアハウス稼働コストも発生します。
- 大きすぎるウェアハウスを使ってもAI関数自体の処理が速くなるとは限りません。
- まずは
LIMIT 10で少量検証してから本番投入します。 - 実行結果はテーブルに保存し、同じ行を何度も再実行しないようにします。
料金体系の全体像はSnowflakeの料金体系をやさしく解説、急な増加への対処はSnowflakeのクレジット急増の原因調査と削減方法もご覧ください。
使用量を確認するSQL
Cortex AI関数の利用量は、Account Usageの専用ビューで確認できます。まずは列構成を DESC VIEW で確認してから集計するのがおすすめです。
DESC VIEW SNOWFLAKE.ACCOUNT_USAGE.CORTEX_AI_FUNCTIONS_USAGE_HISTORY;
日別・関数別・モデル別のクレジット消費:
SELECT
DATE_TRUNC('day', START_TIME) AS usage_date,
FUNCTION_NAME,
MODEL_NAME,
SUM(CREDITS) AS total_credits,
COUNT(DISTINCT QUERY_ID) AS query_count
FROM SNOWFLAKE.ACCOUNT_USAGE.CORTEX_AI_FUNCTIONS_USAGE_HISTORY
WHERE START_TIME >= DATEADD('day', -30, CURRENT_TIMESTAMP())
GROUP BY 1, 2, 3
ORDER BY usage_date DESC, total_credits DESC;
ユーザー別のクレジット消費(直近3か月):
SELECT
DATE_TRUNC('month', h.START_TIME) AS usage_month,
u.NAME AS user_name,
u.EMAIL,
u.DEFAULT_ROLE,
SUM(h.CREDITS) AS total_credits,
COUNT(DISTINCT h.QUERY_ID) AS query_count
FROM SNOWFLAKE.ACCOUNT_USAGE.CORTEX_AI_FUNCTIONS_USAGE_HISTORY h
JOIN SNOWFLAKE.ACCOUNT_USAGE.USERS u
ON h.USER_ID = u.USER_ID
WHERE h.START_TIME >= DATEADD('month', -3, CURRENT_TIMESTAMP())
GROUP BY 1, 2, 3, 4
ORDER BY usage_month DESC, total_credits DESC;
CORTEX_AI_FUNCTIONS_USAGE_HISTORYはCortex AI関数の使用状況確認に使います。- ビューの列名や利用可否はSnowflakeの仕様変更で変わる可能性があります。
- 古い記事や環境では
CORTEX_FUNCTIONS_USAGE_HISTORYなどのビュー名を見かける場合があるため、最新の公式ドキュメントで確認してください。
AI_COMPLETEに向いている用途・注意が必要な用途
向いている用途
- 問い合わせ文の要約
- レビューの要約・分類
- メール文面の下書き
- SQLコメントやドキュメント文の生成
- ログや障害メモの一次整理
- 多言語テキストの下訳・要約
- ルールベースでは書きにくいテキスト処理
注意が必要な用途
- 法務・医療・金融など高リスク文書の最終判断
- 正確性が必須の確定処理
- 個人情報や機密情報を含むテキストの無制限投入
- 大量行への無検証実行
- AI出力をそのまま顧客へ返す処理
専用AI関数との使い分け
AI_COMPLETE は汎用的で便利ですが、翻訳・感情分析・分類・構造化抽出などは専用関数の方がシンプルで読みやすいSQLになる場合があります。何でも AI_COMPLETE で処理するのではなく、用途に合う専用関数があるか確認しましょう。
| やりたいこと | 優先候補 |
|---|---|
| 自由文生成・柔軟な指示 | AI_COMPLETE |
| 複数行の要約 | AI_SUMMARIZE_AGG |
| 複数行から洞察を出す | AI_AGG |
| 翻訳 | AI_TRANSLATE |
| 感情分析 | AI_SENTIMENT |
| 分類 | AI_CLASSIFY |
| 構造化抽出 | AI_EXTRACT |
| PIIマスキング | AI_REDACT |
各関数の詳細は、Snowflake Cortex AI関数まとめ、AI_TRANSLATE入門、AI_EXTRACT入門、SUMMARIZE / AI_SUMMARIZE_AGG入門も参考にしてください。
よくあるエラーと確認ポイント
| 症状 | 確認ポイント |
|---|---|
| 権限エラーになる | USE AI FUNCTIONS または USE AI FUNCTION AI_COMPLETE、AI_FUNCTIONS_USER / CORTEX_USER を確認 |
| NULLが返る | 入力が長すぎる、未対応形式、モデル・リージョン制限、エラー詳細を確認 |
| 出力が途中で切れる | max_tokens が小さすぎないか |
| JSONが崩れる | response_format を使う。JSON Schemaを見直す |
| コストが増える | LIMIT、WHERE、対象件数、出力長、モデル、Usage Historyを確認 |
| 結果が安定しない | temperature を下げる、選択肢を明示する |
| 不適切な出力が出る | guardrails、プロンプト改善、人手確認を検討 |
コスト超過を防ぐ仕組みはSnowflake Resource Monitor入門も参考になります。
まとめ
Snowflakeの AI_COMPLETE を使えば、SQLからLLMを呼び出して文章生成、要約、分類、抽出などをSnowflake上のデータ処理に組み込めます。新規実装では、旧 SNOWFLAKE.CORTEX.COMPLETE ではなく AI_COMPLETE を中心に使うのがおすすめです。実務利用では、model_parameters、show_details、response_format、return_error_details を使い分け、結果をテーブルに保存して再実行コストを抑えましょう。さらに、Usage Historyでコストを確認し、用途によっては AI_TRANSLATE、AI_SENTIMENT、AI_EXTRACT などの専用関数も検討すると、より安全で読みやすいSQLになります。
参考リンク
- Snowflake公式:AI_COMPLETE
- Snowflake公式:SNOWFLAKE.CORTEX.COMPLETE
- Snowflake公式:Cortex AISQL functions
- Snowflake公式:Cortex Guard
- Snowflake公式:Cortex functions usage history
- Snowflake公式:Service Consumption Table (PDF)
※リンク先のパスはSnowflake側の構成変更で変わる可能性があります。最新URLは公式ドキュメントのトップから検索してください。


