[[BigQuery]] の [`AI.IF()`](https://docs.cloud.google.com/bigquery/docs/reference/standard-sql/bigqueryml-syntax-ai-if) は、自然文で書かれた条件を LLM に判定させて真偽値を返す [[BigQuery マネージド AI 関数|マネージド AI 関数]] である。`WHERE` 句や `JOIN` の `ON` 条件に自然文フィルタを直接書けるのが中核となる設計。 マネージド AI 関数 3 兄弟 (`AI.IF()` / [[BigQuery AI.SCORE 関数|AI.SCORE()]] / `AI.CLASSIFY()`) の 1 つで、`BOOL` 戻り値の真偽判定を担う。モデルは自動選択され、プロンプトを内部で構造化して結果のブレを抑える。2025 年 11 月 Preview、2026 年 4 月 GA。 ## シグネチャ ```sql AI.IF( [ prompt => ] PROMPT [, examples => EXAMPLES ] [, connection_id => 'CONNECTION' ] [, endpoint => 'ENDPOINT' ] [, embeddings => EMBEDDINGS ] [, optimization_mode => 'OPTIMIZATION_MODE' ] [, max_error_ratio => MAX_ERROR_RATIO ] ) -> BOOL ``` 必須引数は `prompt` のみ。戻り値は `BOOL` (`TRUE` / `FALSE` / `NULL` の 3 値) で、[[Vertex AI]] の呼び出しに失敗したとき (quota 超過、モデル利用不可など) は `NULL` を返す。 ## 基本的な使い方 ```sql WITH tickets AS ( SELECT * FROM UNNEST (ARRAY<STRUCT<id INT64, message STRING>> [ (1, '先週届いた商品が動作しません。至急返金対応お願いします'), (2, 'パスワードのリセット方法を教えてください'), (3, '料金プランの違いが知りたいです'), (4, '3 回連続で配達遅延。もう解約したいです'), (5, NULL), (6, 'アカウントにログインできません。明日の会議で使う予定なので急ぎです'), (7, 'A プランから B プランへの変更手順を教えてください'), (8, '請求金額が二重に引き落とされています'), (9, '新機能の追加を検討いただけると嬉しいです'), (10, 'サーバーエラーで作業が止まっています。至急対応をお願いします') ]) ) SELECT *, AI.IF(('この問い合わせはクレームを含むか', message)) AS is_complaint FROM tickets ``` 実行結果: | id | message | is_complaint | | -- | ------------------------------------------------------------------ | ------------ | | 1 | 先週届いた商品が動作しません。至急返金対応お願いします | true | | 2 | パスワードのリセット方法を教えてください | false | | 3 | 料金プランの違いが知りたいです | false | | 4 | 3 回連続で配達遅延。もう解約したいです | true | | 5 | NULL | NULL | | 6 | アカウントにログインできません。明日の会議で使う予定なので急ぎです | true | | 7 | A プランから B プランへの変更手順を教えてください | false | | 8 | 請求金額が二重に引き落とされています | true | | 9 | 新機能の追加を検討いただけると嬉しいです | false | | 10 | サーバーエラーで作業が止まっています。至急対応をお願いします | true | > [!note] > - `id 5`: `message` が `NULL` のため、`AI.IF()` は LLM を呼ばずに `NULL` を返している > - `id 6, 10`: 丁寧な表現でも「機能不全の報告」がクレーム扱いされている > [!tip] > 判定が安定しないときは、以下を組み合わせて精度を高められる: > - **プロンプトに定義を埋め込む**: 「クレームとは『不満や非難の表明と、改善や補償の要求』を指し、機能不全の事実報告や質問は含まない」のように、判定基準をプロンプト内で固定する。曖昧なプロンプトほど効果が大きい > - **`examples` 引数を使う**: 主観的な境界事例は `FALSE` / `TRUE` の例を示すと、汎化により判定が安定する > [!info] > 実運用の前に、[[BigQuery AI 関数の料金は二重構造になる]] で BigQuery 側と Vertex AI 側で二重に発生する課金の構造を押さえておく。 ## NULL の扱い `AI.IF()` は、入力プロンプトを実効文字列に正規化したうえで、それが `NULL` または空文字 (`''`) のときに LLM を呼ばずに `NULL` を返す。 この正規化は `STRUCT` で渡した場合にも適用され、フィールドを `CONCAT` で連結した結果に対して判定される。そのため入力形式 (`STRING` / `STRUCT`) やフィールド数による違いはない。 | 入力 | 実効プロンプト | 結果 | | --- | --- | --- | | `NULL` | `NULL` | `NULL` (LLM 呼び出しなし) | | `''` (空文字) | `''` | `NULL` (LLM 呼び出しなし) | | `' '` (空白のみ) | `' '` | LLM 呼び出し → `false` に倒れがち | | `STRUCT('指示: ', NULL)` | `NULL` | `NULL` (LLM 呼び出しなし) | | `STRUCT('', '', '')` | `''` | `NULL` (LLM 呼び出しなし) | | `STRUCT('指示: ', '本文')` | `'指示: 本文'` | LLM 呼び出し → `true` / `false` | > [!warning] > `COALESCE(field, '')` で `NULL` を空文字に変換すると、LLM が常に呼び出されるようになる。 > > この変換では、プロンプトテンプレート部分のフィールドが `NULL` だった場合、判定指示が欠落したままでも LLM は感情的シグナルから真偽を返してしまい、サイレントな誤判定を生む。 > > `NULL` を「未判定」として保持したいなら、`CASE WHEN field IS NULL THEN NULL ELSE AI.IF(...) END` で SQL 層で分岐するか、`WHERE field IS NOT NULL` で先に除外する。 3 関数 (`AI.IF()` / `AI.SCORE()` / `AI.CLASSIFY()`) で `NULL` 入力時の挙動が異なる理由は [[BigQuery マネージド AI 関数は NULL 入力時の挙動が戻り値型で変わる]] で展開する。