Claude CodeでGreat Expectationsのデータ品質検査を作る手順
Great Expectations 1.21以降はClaude Code用スキルを同梱します。同梱スキルが実測から期待値を決めない理由と、サンプルの実測を人が確認して検査に変え、Checkpointで回す流れをまとめます。
Great Expectations(GX)の1.21.0以降は、Claude Code向けのスキルをパッケージに同梱しています。python -m great_expectations skills install を実行すると、データ接続・期待値・Checkpointの3つのスキルが .claude/skills/ に入ります。ただし同梱の期待値スキルは、データを測って閾値を決める作業をあえて引き受けません。サンプルの実測からExpectation Suiteを作る流れは、測る側を自分で用意して組み合わせます。
この記事は、500行の注文CSVを例に、実測の確認からExpectation Suiteの登録、Checkpointでの再実行までを通します。コードと出力はGX 1.24.1の手元実行です。
同梱スキルはClaude Codeの .claude/skillsに入る
GX CoreはPythonライブラリで、同梱スキルはパッケージ内のテキストファイルです。インストールコマンドが、コーディングエージェントの読むディレクトリへそれをコピーします。前提はPython 3.10から3.14とGX Core 1.21.0以上で、それ以前のバージョンにはスキルが含まれません。
pip install great_expectations
python -m great_expectations skills installプロジェクトのルートで実行すると、次のように6つのディレクトリが作られます(パスは省略)。
Great Expectations 1.24.1 skills in /path/to/my_project
Installed (6)
.agents/skills/gx-configure-checkpoint
.agents/skills/gx-configure-data-source
.agents/skills/gx-configure-expectations
.claude/skills/gx-configure-checkpoint
.claude/skills/gx-configure-data-source
.claude/skills/gx-configure-expectations.agents/skills/ はCodexとCursor、.claude/skills/ はClaude CodeとCursorが読む場所です。Claude Codeだけなら --target claude で片方に絞れます。再実行は安全で、GXが置いていないディレクトリは上書きされません。GXをアップグレードしてもプロジェクト内のコピーは古いままなので、更新後にもう一度インストールを実行します。skills list で、どのバージョンが入っているかを確認できます。
Claude Code側は、.claude/skills/<名前>/SKILL.md を置けばスキルとして読み込みます。SKILL.mdの書き方はClaude Code SkillsのSKILL.mdとfrontmatterにまとめています。
3つのスキルは直列の一本道です。
同梱スキル3本の受け渡し
- 1
gx-configure-data-source
Data Source、Data Asset、Batch Definitionを作り、実際に行を読んで接続を確かめます。
- 2
gx-configure-expectations
話した内容をExpectationに変え、Suiteに保存して、実データに対して実行します。
- 3
gx-configure-checkpoint
Validation DefinitionとCheckpointを保存し、エージェントなしで再実行するスクリプトを渡します。
各スキルは自分の前提が揃っているかを先に確かめ、足りなければ前段のスキル名を挙げて止まります。途中のスキルから呼んでも、入口へ戻されるだけです。
同梱の期待値スキルは「実測から決める」ことをしない
インストールされた gx-configure-expectations のSKILL.mdを読むと、このタイトルの作業と逆向きの規則が書かれています。要点は3つです。
- ユーザーが述べた検査だけを作る。データをプロファイリングして主張できそうな項目を探さない
- 列ごとにスキーマを走査して、検査を1本ずつ提案しない
- 「金額は妥当な範囲」のように曖昧な指示には、データから境界を拾わず、どの境界かを聞き返す
理由も同じ文書に書かれています。頼まれていない検査はエージェントの推測であり、後で失敗したときに本物のデータ品質の問題として読まれるからです。データを見て「質問を作る材料にする」のは許されますが、観察したものを頼まれずに期待値へ変えることは許されません。
だから「サンプルデータの分布から値域・欠損率・一意性の期待値を生成する」という使い方では、同梱スキルに丸投げできません。分布を測る段と、境界を人が決める段を、別の手順として足す必要があります。測る段まではClaude Codeに任せ、境界の確定で人が入る形です。この分担は、同梱スキルの方針とも矛盾しません。
実測を表にする自作スキルを足す
測る段はpandasの数行で済みます。ここでは profile.py として置き、欠損率・一意率・数値の分布・カテゴリの度数を出します。
import sys
import pandas as pd
df = pd.read_csv(sys.argv[1])
print(df.dtypes.to_string())
print("rows", len(df))
print("null_rate\n", df.isna().mean().round(3).to_string())
print("unique_rate\n", (df.nunique() / len(df)).round(3).to_string())
print(df["amount"].describe(percentiles=[.01, .99]).round(0).to_string())
print(df["status"].value_counts().to_string())例に使う orders_sample.csv は、order_id、customer_id、amount、status、coupon_code の5列で500行です。実行すると、次の実測が得られます。
| 列 | 欠損率 | 一意率 | 補足 |
|---|---|---|---|
| order_id | 欠損率0 | 一意率1.000 | 補足全行が異なる |
| customer_id | 欠損率0 | 一意率0.238 | 補足同じ顧客が複数回注文 |
| amount | 欠損率0 | 一意率0.988 | 補足最小812、1%点2,260、99%点24,324、最大41,709 |
| status | 欠損率0 | 一意率0.006 | 補足paid 411、pending 63、refunded 26 |
| coupon_code | 欠損率0.71 | 一意率0.002 | 補足値は1種類のみで、29%の行にだけ付く |
この表をClaudeに読ませ、候補を出させるところまでをスキルにします。disable-model-invocation: true を付けると、Claudeが自動で呼ぶことはなく、/ で自分が呼んだときだけ動きます。allowed-tools は、そのターンだけ実行を事前承認するフィールドです。権限を広げる設定なので、リポジトリに入れる前に中身を確認してください。
---
name: gx-sample-profile
description: サンプルCSVの実測(欠損率・一意率・値域・カテゴリ)を表にし、Expectationの候補と境界案を提示する。Suiteは保存しない
disable-model-invocation: true
allowed-tools: Bash(python profile.py *)
---
`python profile.py $ARGUMENTS` を実行し、列ごとに次の表を作る。
| 列 | 実測 | 候補のExpectation | 境界案と根拠 |
- 実測の最小・最大をそのまま境界にしない。桁違いの異常を拾える幅を案にする
- 業務ルールで決まる列(一意、非NULL、取りうる値)は、実測が満たしていても
業務側の根拠を人に尋ねる
- 保存はしない。表を見せ、人が境界を確定したら gx-configure-expectations に渡す$ARGUMENTS には呼び出し時に続けた文字列が入ります。/gx-sample-profile data/orders_sample.csv のように使います。スキルは保存まで進まず、境界が確定してから gx-configure-expectations へ手渡す構成です。
実測を境界に変えるときに人が決めること
実測と採用する期待値は一対一ではありません。例の表は、次のように変換しました。
| 列 | 実測 | 採用したExpectation | 決めた理由 |
|---|---|---|---|
| order_id | 実測重複なし | 採用したExpectationExpectColumnValuesToBeUnique | 決めた理由注文IDは仕様上一意。実測が偶然満たしているだけではない |
| customer_id | 実測欠損なし | 採用したExpectationExpectColumnValuesToNotBeNull | 決めた理由顧客不明の注文は業務上ありえない |
| amount | 実測812〜41,709 | 採用したExpectationExpectColumnValuesToBeBetween(1, 1,000,000) | 決めた理由実測の最大に貼ると、次の高額注文で誤検知になる。桁違いと負値を拾う幅にする |
| status | 実測3種類 | 採用したExpectationExpectColumnValuesToBeInSet | 決めた理由業務で定義済みの状態。実測の度数は使わない |
| coupon_code | 実測欠損71% | 採用したExpectationExpectColumnValuesToNotBeNull(mostly=0.2, severity="warning") | 決めた理由付与率の実測は29%。2割を割ったら警告にする |
| 行数 | 実測500 | 採用したExpectationExpectTableRowCountToBeBetween(100, 5000) | 決めた理由欠落と二重取り込みを拾う幅 |
mostly は割合で許容する引数で、指定した割合以上の行が成功すればExpectation全体を成功とします。severity は critical、warning、info から選び、省略すると critical です。
実測が使えるのは、境界を決める手がかりまでです。amount の下限を実測の812にすると、翌月に800円の注文が1件入っただけで検査が落ちます。逆に業務ルールで決まる列は、実測が満たしていても根拠は仕様にあります。この区別を人が判断するので、同梱スキルは勝手に境界を決めません。
Suiteを登録して期待値を足す
境界が決まったら、gx-configure-expectations に渡すか、同じことを自分でコードにします。ここでは後者を示します。最初にファイル型のData Contextを作り、Data SourceとBatch Definitionを登録します。
import great_expectations as gx
from great_expectations import expectations as gxe
context = gx.get_context(mode="file", project_root_dir=".")
ds = context.data_sources.add_pandas_filesystem(
name="orders_files", base_directory="data"
)
asset = ds.add_csv_asset(name="orders_csv")
bd = asset.add_batch_definition_path(name="today", path="orders_today.csv")
suite = context.suites.add(gx.ExpectationSuite(name="orders_quality"))
suite.add_expectation(gxe.ExpectColumnValuesToBeUnique(column="order_id"))
suite.add_expectation(gxe.ExpectColumnValuesToNotBeNull(column="customer_id"))
suite.add_expectation(gxe.ExpectColumnValuesToBeBetween(
column="amount", min_value=1, max_value=1_000_000))
suite.add_expectation(gxe.ExpectColumnValuesToBeInSet(
column="status", value_set=["paid", "pending", "refunded"]))
suite.add_expectation(gxe.ExpectTableRowCountToBeBetween(
min_value=100, max_value=5000))
suite.add_expectation(gxe.ExpectColumnValuesToNotBeNull(
column="coupon_code", mostly=0.2, severity="warning"))順序が重要です。context.suites.add(...) を先に呼び、戻り値のSuiteに add_expectation します。この順なら追加のたびに gx/expectations/orders_quality.json へ書き込まれます。Suiteを登録せずに期待値を足して batch.validate(suite) を呼んでも検証は走りますが、何も保存されません。後から context.suites.get(...) を呼ぶと見つからず、積み上げた期待値は消えます。
context.suites.add_or_update() にも注意が必要です。同名のSuiteがすでにあると、マージせずに中身を置き換えます。3本入ったSuiteに空のSuiteを渡すと、0本になります。既存のSuiteに足すときは context.suites.get(name) で取り出してから追加します。
保存されたJSONは、type と kwargs と severity を持つ期待値の配列です。Gitで差分レビューできる形なので、境界を変えたときの履歴は通常のコードレビューに載せられます。Suite名にドットは使えません。orders.quality は gx/expectations/orders/quality.json に展開され、ディレクトリが枝分かれするためです。orders_quality のようにアンダースコアかハイフンで区切ります。
Validation DefinitionとCheckpointで再実行する
Suiteは「何を検査するか」、Batch Definitionは「どのデータか」です。この2つをValidation Definitionで結び、Checkpointにまとめると、名前を指定して再実行できる単位になります。Checkpointは複数のValidation Definitionを束ね、実行後にActionを走らせられます。
vd = context.validation_definitions.add(
gx.ValidationDefinition(name="orders_vd", data=bd, suite=suite)
)
cp = context.checkpoints.add(
gx.Checkpoint(name="orders_checkpoint", validation_definitions=[vd])
)
result = cp.run()
print("success:", result.success)ActionはCheckpointの actions に渡します。Slack通知とData Docsの更新が代表例で、notify_on に failure、critical、warning などを選べます。ここではActionなしで、Suiteの判定だけを見ます。
手元では、先に壊したデータを orders_today.csv として置いて実行しました。壊した箇所は、order_id の重複1件、customer_id の欠損1件、amount の -500、status の cancelled です。出力は次のとおりです(要点のみ)。
FAIL expect_column_values_to_be_unique order_id unexpected_count 2 [1022, 1022]
FAIL expect_column_values_to_not_be_null customer_id unexpected_count 1 [nan]
FAIL expect_column_values_to_be_between amount unexpected_count 1 [-500.0]
FAIL expect_column_values_to_be_in_set status unexpected_count 1 ['cancelled']
PASS expect_table_row_count_to_be_between observed_value 500
PASS expect_column_values_to_not_be_null coupon_code (mostly=0.2, 未入力 71.0%)
success: False重複した 1022 は2行ぶんで数えられ、unexpected_count が2になります。壊した値が partial_unexpected_list にそのまま出るので、どの行を直すかの手がかりになります。
結果の並びにも注意が必要です。入力した順ではなく、列ごとにまとめられます。上の出力は偶然 order_id から始まっていますが、位置で期待値と結果を対応づけず、各結果の expectation_config を読むのが安全です。
エージェントなしで回すスクリプトと、結果の置き場
Checkpointは名前で取り出せるので、定期実行用のスクリプトは短く書けます。success が偽なら終了コードを1にします。
import sys
import great_expectations as gx
context = gx.get_context(mode="file", project_root_dir="/abs/path/to/project")
result = context.checkpoints.get("orders_checkpoint").run()
sys.exit(0 if result.success else 1)壊したデータでは終了コードが1、元のサンプルに差し替えると0になりました。cron、Airflow、CIのジョブなど、どこから呼ぶかは運用側の選択です。同梱の gx-configure-checkpoint スキルも、同様の再実行スクリプトを渡すところで終わり、スケジューラへの組み込みは範囲に含めていません。
もう1つ、結果の置き場に注意してください。検証結果は gx/uncommitted/validations/ に保存され、生成された gx/.gitignore は uncommitted/ を除外しています。SuiteとCheckpointの定義はGitに載りますが、実行結果は載りません。結果を残したいならData Docsの更新などのActionを足すか、別の保管先を決めます。
severity="warning" の失敗でも success は偽になります。実際に、coupon_code に mostly=0.9 のwarningを置いて実行すると success は False で、最大の重大度は WARNING でした。警告だけでジョブを止めたくないなら、success ではなく重大度を見て終了コードを分けます。GXのガイドは判定用のヘルパー名を get_maximum_severity_failure と書いていますが、1.24.1に入っているメソッドは get_max_severity_failure でした。バージョンが上がると名前が変わる可能性があるため、使う前に dir() で確かめます。
よくあるつまずき
落とし穴
サンプルの最小・最大を境界にして、翌日から誤検知が続く
実測の分布は、検査で守りたい業務ルールとは別物です。実測値は幅を決める手がかりにとどめ、上限と下限は仕様と運用の許容に合わせます。
- メトリクスのエラーを「データの失敗」と読む: 列名の打ち間違いなど設定の誤りでは、
successが偽でもresultが空になります。データが悪いのではなく、検査が走っていません。exception_infoに原因の文面が入ります - すべて欠損の列に範囲検査を足して安心する: 同梱スキルの文書によると、SQLバッチでは全行が欠損の列でも
ExpectColumnValuesToBeBetweenは成功します。欠損は違反に数えられないためで、非NULLの検査と対にします - 平均の検査が空の表で落ちる理由を取り違える: 同梱スキルの文書では、空の表に対する平均の検査は
observed_valueがNoneになって失敗します。計算する値がないだけで、範囲外という意味ではありません - ClaudeにSuite全体の作り直しを頼む: 既存のSuiteを置き換える操作は、同梱スキルでも明示の依頼がない限り避ける設計です。Claude Codeに任せるときも、追加か置換かを指示に書いておきます
- 新しいバージョンのGXで同梱スキルが古いまま: アップグレードしても、プロジェクトに入っているスキルは更新されません。更新後に
skills installを再実行します
まとめ
GXの同梱スキルは、頼まれた検査を作って実行し、Checkpointにして渡すところまでを担います。分布から境界を決める作業は含まれないので、そこは実測の表を作る自作スキルと、境界を確定する人の判断で補います。Suiteは登録してから期待値を足し、Checkpointは終了コードを返す短いスクリプトにして、運用側のスケジューラから呼びます。
この流れのデータ読み取りは、DuckDBプラグインでCSVをその場で見る方法とも組み合わせられます。dbt側の変換検証と合わせて組む場合は、Data Agent Kitの記事が参考になります。コード品質側のゲートについてはSonarQubeプラグインを参照してください。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。
- code.claude.com/docs/en/skills
- docs.greatexpectations.io/docs/core/set_up_a_gx_environment/install_agent_skills
- docs.greatexpectations.io/docs/core/define_expectations/create_an_expectation
- docs.greatexpectations.io/docs/core/define_expectations/organize_expectation_suites
- docs.greatexpectations.io/docs/core/trigger_actions_based_on_results/create_a_checkp…
- docs.greatexpectations.io/docs/core/trigger_actions_based_on_results/run_a_checkpoint