[[BigQuery]] の[[BigQuery マネージド AI 関数|マネージド AI 関数]] 3 つ ([[BigQuery AI.IF 関数|AI.IF()]] / [[BigQuery AI.SCORE 関数|AI.SCORE()]] / `AI.CLASSIFY()`) は、入力プロンプトに `NULL` を含む `STRUCT` を渡したときの挙動が一致しない。
このうち `AI.IF()` と `AI.CLASSIFY()` は LLM を呼ばずに `NULL` を返すのに対し、`AI.SCORE()` だけは LLM を呼んで何らかの数値を返す。
この非対称は戻り値型から逆算された設計と読める。`BOOL` (3 値論理) と `STRING` は `NULL` を自然に表せるため、データ品質エラーを `NULL` で返せる。
一方 `FLOAT64` は `NULL` 経路を [[Vertex AI]] 呼び出し失敗用に予約しており、データ品質エラーで占有すると API 失敗と区別できなくなる。
## 関数ごとの挙動
入力プロンプトに `NULL` フィールドを 1 つ含む `STRUCT` を渡したときの結果。
| 関数 | 戻り値型 | NULL 入力時 | LLM 呼び出し |
| --- | --- | --- | --- |
| `AI.IF()` | `BOOL` | `NULL` | なし |
| `AI.SCORE()` | `FLOAT64` | 数値 (例: スケール最小値) | あり |
| `AI.CLASSIFY()` | `STRING` | `NULL` | なし |
`AI.IF()` と `AI.CLASSIFY()` は `NULL` を含むプロンプトを LLM に送らない。`AI.SCORE()` は送り、LLM はスケール最小値や中央値などのデフォルト値を返す傾向がある。
## 戻り値型と NULL 経路の対応
`NULL` 入力時の挙動は、戻り値型に「データ品質エラー専用の値」を割り当てられるかで決まる。
- `BOOL`: `TRUE` / `FALSE` / `NULL` の 3 値論理を持つ。`NULL` を「未判定」として返せる
- `STRING`: `NULL` を含められる。`NULL` を「未分類」として返せる
- `FLOAT64`: `NULL` 経路を Vertex AI 呼び出し失敗に予約している。データ品質エラーで `NULL` を返すと、API 失敗との区別がつかなくなる
`AI.SCORE()` だけ「LLM を呼んで何らかの数値を返す」設計を採ったのは、`NULL` 経路を運用上の障害切り分けのために守る意図と整合する。
## 実務上の含意
`AI.SCORE()` は `NULL` 入力で LLM が呼ばれるため、SQL 層での防御が必須となる。
一方 `AI.IF()` / `AI.CLASSIFY()` は `NULL` 入力で LLM を呼ばずに `NULL` を返すため、入力 `NULL` の防御を関数側に任せられる。ただし運用上は `AI.SCORE()` の `NULL` 処理が他 2 関数と非対称なことを意識する必要がある。
`COALESCE` で空文字に倒す回避策は、判定基準が壊れた状態で LLM が値を返すリスクを生むため避けるべき。`NULL` を保持したい場合は `CASE` で SQL 層から分岐させ、除外したい場合は `WHERE` で先に弾く。具体的なクエリは各関数の Hub ノートに記載する。
## 関連ノート
- **[派生]** [[BigQuery AI 関数の料金は二重構造になる]]: BigQuery AI 関数の実装前に押さえるべき運用特性として、本ノートと並ぶ知見