Upgrade to Pro — share decks privately, control downloads, hide ads and more …

dbt Pythonモデルで実現するSnowflake活用術

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.
Avatar for Trs Trs
February 21, 2025

dbt Pythonモデルで実現するSnowflake活用術

Avatar for Trs

Trs

February 21, 2025
Tweet

More Decks by Trs

Other Decks in Programming

Transcript

  1. © LayerX Inc. 2 ⾃⼰紹介 平田 拓也 (@TrsNium) 株式会社LayerX バクラク事業部 機械学習・データ部

    DataOps チーム データエンジニア 2023年11月よりLayerXにjoin 普段はデータ基盤の運用開発を中心にやっています !
  2. 3 © LayerX Inc. 「すべての経済活動を、デジタル化する。」をミッションに、AI SaaSとAI DXの事業を展開 事業紹介 バクラク事業 企業活動のインフラとなる業務を

    効率化するクラウドサービス Fintech事業 ソフトウェアを駆使したアセットマネジ メント‧証券事業を合弁会社にて展開 AI‧LLM事業 社内のナレッジやノウハウをデータ ベース化するAIプラットフォーム AI SaaSドメイン AI DXドメイン
  3. ⽬次 Agenda • はじめに • Python Modelの基礎知識 • Python Modelの仕組み(Snowflake編)

    • Python Modelの便利な機能と使い⽅ • テストとパフォーマンスの課題 • Snowflake環境でのログ出⼒と重要性 • Future Works と今後の展望
  4. ⽬次 Agenda • はじめに • PythonModelの基礎知識 • Python Modelの仕組み(Snowflake編) •

    Python Modelの便利な機能と使い⽅ • テストとパフォーマンスの課題 • Snowflake環境でのログ出⼒と重要性 • Future Works と今後の展望
  5. © LayerX Inc. 6 dbt Python Model とは? はじめに 概要

    • dbt Python Model は、従来の SQL モデルに加え、Python でデータ変換ロジックを記述できる機能です。 def model(dbt, session): # モデル設定(例: テーブルとしてマテリアライズ) dbt.config(materialized='table') # シンプルなデータ取得処理 return session.sql("SELECT * FROM source_table") models/example_python_model.py source: source_table model: example_python_model model: other_sql_model
  6. © LayerX Inc. 7 dbt Python Model で実現できること はじめに 柔軟なデータ変換

    • Python の豊富なライブラリやロジックを利用可能 • API 連携や複雑なデータ整形が容易に実装できる dbt のエコシステムとの統合 • マクロ、テスト、ドキュメント生成など、既存の dbt機能をそのまま活用 • SQLモデルと同様に依存関係管理が可能
  7. © LayerX Inc. 8 対応プラットフォーム & 実⾏環境 はじめに 対応プラットフォーム •

    dbt Python Modelは、Snowflake, BigQuery, Databricksなどがサポートされている 各プラットフォームの実行環境 • Snowflake ◦ Warehouse 上で実行され、Snowpark API を利用して処理が行われる • BigQuery ◦ Python モデルは Dataproc 上の PySpark ジョブとして実行され、 BigQuery のテーブルやビューと連携して処理結果を返す • Databricks ◦ Databricks 環境上で PySpark コードとして実行される
  8. © LayerX Inc. 9 深堀する運⽤の課題と取り組み はじめに • 弊社では、dbt Python Model

    を部分的に活用していますが、その運用の中で様々な課題に直面してきました。 • まず、dbt Python Model がどのように動作する仕組みについてご紹介し、その内部処理の流れを明らかにします。 • 次に、Snowflake 環境における実行の特徴、特に Snowpark API を活用したデータ処理の詳細なプロセスについてご説明します • さらに、テスト、デバッグ 、ログ出力に関して、 実際に直面した課題と、それらに対する改善策や工夫のポイントをお話しします。 • 最後に、これらの課題を踏まえた今後の展望として、 Programmatic Invocations の活用やテスト環境の整備など、次のステップに向けた取 り組みについてご紹介します。
  9. ⽬次 Agenda • はじめに • Python Modelの基礎知識 • Python Modelの仕組み(Snowflake編)

    • Python Modelの便利な機能と使い⽅ • テストとパフォーマンスの課題 • Snowflake環境でのログ出⼒と重要性 • Future Works と今後の展望
  10. © LayerX Inc. 11 .py ファイルがモデルと認識されるまでの流れ Python Modelの基礎知識 全体の流れ Pythonモデル定義(.pyファイル)

    静的解析 (AST解析) コンパイル ターゲット環境(例: Snowflake)で実⾏ なぜ静的解析をするのか? • Pythonコードを実行せずに「構造」を把握することで、予期せぬ副作用や 実行時エラーを防止 • 再現性のある、信頼性の高いモデル定義を実現 静的解析のメリットと制約 • メリット : コードがどのように書かれているか(関数や変数の配置な ど)を、実際に実行する前にしっかり確認できる → これにより、予期せぬ動作やエラーを未然に防ぐことが可能に ! • 制約: 動的な変数(envやvarsなど)や実行時ロジックは解析対象外
  11. © LayerX Inc. 12 静的解析とASTの例 Python Modelの基礎知識 • Pythonの静的解析には astモジュールが利用可能

    • ast.parse(source_code)を使うと、ソースコードが抽象構文木 (AST)に変換され、コードをツリー構造で扱うことができる import ast source_code = """ x = 42 def hello(): message = "Hello, dbt!" print(message) """ tree = ast.parse(source_code) print(ast.dump(tree, indent=4)) Module( body=[ Assign(targets=[Name(id="x", ctx=Store())], value=Constant(value=42)), FunctionDef( name="hello", args=arguments( posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[] ), body=[ Assign( targets=[Name(id="message", ctx=Store())], value=Constant(value="Hello, dbt!"), ), Expr( value=Call( func=Name(id="print", ctx=Load()), args=[Name(id="message", ctx=Load())], keywords=[], ) ), ], decorator_list=[], ), ], type_ignores=[])
  12. © LayerX Inc. 13 AST解析実装の詳細 Python Modelの基礎知識 ソースコードの読み込みと AST生成 •

    ソースコードを文字列として読み込み、 ast.parse() によりASTを作成 カスタムAST Visitorの利用 • AST Visitor とは? : AST Visitor は、AST の各ノードを訪問し、必要な情報を取り出すためのクラス • ast.NodeVisitor を継承したクラスを用いて、 AST を巡回しながら、モデル定義に必要なノード(例 : 関数定義、変数代入、 dbt.ref や dbt.config などの dbt 特有の呼び出し)を抽出 参考 • dbt-core/core/dbt/parser/models.py
  13. © LayerX Inc. 14 コンパイルフェーズとターゲット環境での実⾏ Python Modelの基礎知識 コンパイルフェーズ • dbt

    は、PythonコードをAST解析により解析し、そこで抽出された定義を元にモデルオブジェクトを生成 • その過程で、dbt.ref や dbt.config などのdbt固有の関数が評価され、必要な変数の置換やマクロの展開が行われ、最終的に SQLやス トアドプロシージャに変換される ターゲット環境(Snowflake)での実行 • 変換されたモデルは、 Snowflake上でストアドプロシージャとしてラップされ、実際に実行される • 実行中は、プロシージャ内でデータの処理が行われ、その結果が最終的にテーブルなどに保存される
  14. © LayerX Inc. 15 マクロ評価と変数管理の注意点 Python Modelの基礎知識 マクロ評価の注意点 • コンパイル時はmodel()

    の実際の実行は行われず、コード構造から必要な情報のみが抽出される • 動的な変数(例 : env や vars)は直接扱えないため、代わりに schema.yml や静的な設定で値を渡す必要がある version: '2' models: - name: workspace__example__dbt_meetup config: schema: example database: "{{ var(target.name)['database'][workspace] }}" alias: dbt_meetup foo: "{{ var(target.name)[example][foo] }}" var: "{{ var(target.name)[example][foo] }}" def model(dbt, session): dbt.config(materialized='table') foo = dbt.config.get('foo') var = dbt.config.get('var') … return df
  15. ⽬次 Agenda • はじめに • Python Modelの基礎知識 • Python Modelの仕組み(Snowflake編)

    • Python Modelの便利な機能と使い⽅ • テストとパフォーマンスの課題 • Snowflake環境でのログ出⼒と重要性 • Future Works と今後の展望
  16. © LayerX Inc. 17 SnowflakeにおけるPython Modelの概要 Python Modelの仕組み(Snowflake編) Python Modelの実行方法

    • Pythonコードは、Snowflake上でストアドプロシージャとして実行 される • use_anonymous_sproc を True にした場合、匿名ストアドプロ シージャが利用される ◦ メリット : 実行が高速になり、クエリ履歴がクリーンに保た れる Snowflakeとの連携 • dbt のコンパイルフェーズで、 Pythonコードが AST 解析・変換さ れ、最終的な SQL やストアドプロシージャに変換される • 変換されたSQL/ストアドプロシージャーが Warehouse上で実行 される Pythonモデル定義(.pyファイル) 静的解析(AST)/コンパイル 変換されたSQL/ストアドプロシージャ Warehouse 上で実⾏ Snowpark API を利⽤したデータ処理 結果をテーブルに保存
  17. © LayerX Inc. 18 プロシージャ内での処理の詳細 Python Modelの仕組み(Snowflake編) データマテリアライズまでの流れ • dbt

    Python Model の model 関数が実行され、加工された データが得られる • 取得したデータは、 Snowparkのsave_as_table メソッドを 用いてSnowflake上にマテリアライズされる dbt内部ロジックとの統合 • dbt.ref, dbt.config, など、dbt固有の関数により、上流モデ ルの解決や差分更新、データ変換処理が統合され、 dbtモ デルとして動作する プロシージャーの実⾏ model(dbt, session) の実⾏ materialize() 呼び出し save_as_table でマテリアライズ
  18. © LayerX Inc. 19 コード例 ‒ Python Modelとdbt内部ロジック Python Modelの仕組み(Snowflake編)

    def model(dbt, session): dbt.config(materialized='table') # 上流モデルを参照(コンパイル時に⽣成されるロジックによりリソース解決) df = dbt.ref("my_first_dbt_model") return df # 以降のコードはコンパイル時に⾃動で⽣成される def ref(*args, dbt_load_df_function): refs = {"my_first_dbt_model": "DEMO_DB.DEMO_SCHEMA.my_first_dbt_model"} key = ".".join(args) return dbt_load_df_function(refs[key]) # 略: source(), config, this, dbtObj などの実装
  19. © LayerX Inc. 20 コード例 ‒ エントリーポイントとマテリアライズ処理 Python Modelの仕組み(Snowflake編) def

    materialize(session, df, target_relation): import pandas if isinstance(df, pandas.core.frame.DataFrame): df = session.createDataFrame(df) # テーブルとして結果を保存する df.write.mode("overwrite").save_as_table("DEMO_DB.DEMO_SCHEMA.my_first_python_model", table_type='') def main(session): dbt = dbtObj(session.table) df = model(dbt, session) materialize(session, df, dbt.this) return "OK"
  20. ⽬次 Agenda • はじめに • Python Modelの基礎知識 • Python Modelの仕組み(Snowflake編)

    • Python Modelの便利な機能と使い⽅ • テストとパフォーマンスの課題 • Snowparkライブラリに関する課題 • Snowflake環境でのログ出⼒と重要性 • Future Works と今後の展望
  21. © LayerX Inc. 22 Python Modelの便利な機能と使い⽅ Python Modelの便利な機能と使い⽅ • dbt

    Python Modelは、Pythonならではの柔軟なデータ操作が可能 • SQLモデルと同様に、 dbtの便利な関数・マクロを活用できる ◦ dbt.is_incremental, dbt.ref など • インクリメンタル更新により、差分のみの処理が実現できる
  22. © LayerX Inc. 23 dbt.is_incremental の活⽤ Python Modelの便利な機能と使い⽅ インクリメンタル実行の判定 •

    dbt.is_incremental を使い、現在の実行が初回か差分更新かを判定可能 実用例 • 既存テーブルから最新の更新時刻を取得 • その時刻を元に新たな開始時刻として設定し、新規データのみ取得 参考 • https://docs.getdbt.com/docs/build/incremental-models def model(dbt, session: Session) -> DataFrame: # モデルをインクリメンタルとして設定(unique_keyも指定) dbt.config(materialized='incremental', unique_key='id') # 初回実⾏時は固定の開始⽇時(例: 2024-09-01)からデータ取得 start_time = int(time.mktime(datetime.date(2024, 9, 1).timetuple())) # インクリメンタル実⾏時は、既存データの最終更新時刻から開始時刻を調整 if dbt.is_incremental: query = f"SELECT EXTRACT(EPOCH FROM MAX(updated_at)) FROM {dbt.this}" last_updated_at = int(session.sql(query).collect()[0][0]) start_time = last_updated_at - 60 # 1分前のタイムスタンプを設定 # APIからデータ取得(詳細は省略) data = fetch_data(start_time) schema = infer_schema(data) return session.create_dataframe(data, schema=schema)
  23. © LayerX Inc. 24 dbt.ref を使った依存関係管理 Python Modelの便利な機能と使い⽅ モデル間の参照 •

    dbt.ref("other_model") を使用して、他のモデルへの依存関係を明示 利点 • dbtが依存関係を自動解決し、正しい実行順序を保証 • コードの保守性と再利用性が向上 参考 • https://docs.getdbt.com/docs/build/python-models#referencing-other-models
  24. ⽬次 Agenda • はじめに • Python Modelの基礎知識 • Python Modelの仕組み(Snowflake編)

    • Python Modelの便利な機能と使い⽅ • テストとパフォーマンスの課題 • Snowparkライブラリに関する課題 • Snowflake環境でのログ出⼒と重要性 • Future Works と今後の展望
  25. © LayerX Inc. 26 弊社でのテストの⽅法とアプローチ テストとパフォーマンスの課題 テストフレームワーク • Pytest を利用

    ◦ ユニットテストと統合テストの両面から、 dbt Python Model の各処理(外部 API 呼び出し、データフレーム変換、フィルタ処理) の正確な動作を検証 • モック化手法の活用 ◦ monkeypatch を利用して、requests.get などの外部依存の挙動を固定のダミーデータで再現 ◦ モック化により、安定した再現性の高いテスト環境を実現 テスト環境のセットアップ • Snowflake セッションの再現 ◦ Snowflake セッションを再現するためのラッパー関数( new_session())を用意 ◦ 接続情報が未設定の場合は、ローカルテスト用の DummySession を使用して簡易な環境を構築
  26. © LayerX Inc. 27 モデル関数 (model) の例 テストとパフォーマンスの課題 ポイント •

    外部APIからユーザーデータを取得 • pandas.json_normalize によりネストされたデータをフラッ ト化 • 必要なカラムのみ抽出し、 create_dataframe に渡して DataFrame を生成 def model(dbt, session): dbt.config(materialized='table', packages=['requests']) url = "https://何かサービス/users" response = requests.get(url) response.raise_for_status() data = response.json() # APIデータをフラット化して必要なカラムを抽出 df_flat = pd.json_normalize(data) data_records = df_flat[['id', 'name', 'username']].to_dict(orient="records") # Snowflake の create_dataframe はリストの辞書形式を受け⼊れる df = session.create_dataframe(data_records) return df
  27. © LayerX Inc. 28 ダミーオブジェクトの定義 テストとパフォーマンスの課題 ポイント DummyDBT • dbt.config()

    の呼び出しを模倣して、設定内容を出力 DummySession • 実際の Snowflake セッションの代わりに、テスト用に pandas DataFrame を返すシンプルな実装 # ダミーの dbt オブジェクト class DummyDBT: def config(self, **kwargs): print("dbt.config called with:", kwargs) # ダミーの Session オブジェクト(テスト⽤) class DummySession: def create_dataframe(self, data): # Snowpark の create_dataframe の代わりに pandas DataFrame を返す return pd.DataFrame(data)
  28. © LayerX Inc. 29 テストクラスの環境構築部分 テストとパフォーマンスの課題 ポイント • new_session() で環境変数に応じてテスト用のダミーセッ

    ションまたは実際のセッションを生成 • テスト環境に応じたセッション管理を実現 class TestDataProcessingProcedure: @contextlib.contextmanager def new_session(self): if os.environ.get('DBT_SNOWFLAKE_ACCOUNT', 'fake_account') == 'fake_account': yield DummySession() else: snowflake_account = os.environ.get('DBT_SNOWFLAKE_ACCOUNT') snowflake_user = os.environ.get('DBT_SNOWFLAKE_USER') session = Session.builder.configs({ 'account': snowflake_account, 'user': snowflake_user, … # ロールやデータベース、スキーマなどを指定する }).create() try: yield session finally: session.close()
  29. © LayerX Inc. 30 外部APIモック テストとパフォーマンスの課題 ポイント • 外部API呼び出しをモックし、固定のダミーデータを返す def

    fake_requests_get(self, url): # ダミーの API データを返す dummy_data = [ {"id": 1, "name": "User One", "username": "userone", "email": "[email protected]"}, {"id": 2, "name": "User Two", "username": "usertwo", "email": "[email protected]"}, {"id": 3, "name": "User Three", "username": "userthree", "email": "[email protected]"} ] class DummyResponse: def __init__(self, data): self.data = data def raise_for_status(self): pass def json(self): return self.data return DummyResponse(dummy_data)
  30. © LayerX Inc. 31 テスト関数の実装 テストとパフォーマンスの課題 ポイント • モック化した requests.get

    を利用して model() を実行 • Snowpark の DataFrame を Pandas DataFrame に変換し、 カラム名を小文字化して期待値と比較 • check_dtype=False により、dtype の違いは無視して内容の み比較 def test_model_function(self, monkeypatch): # requests.get をモック化 monkeypatch.setattr(requests, "get", self.fake_requests_get) dbt = DummyDBT() with self.new_session() as session: df = model(dbt, session) # Snowpark DataFrameの場合は toPandas() で変換 if hasattr(df, "toPandas"): df = df.toPandas() # Snowpark ではカラム名が⼤⽂字になるので⼩⽂字に変換 df.columns = [col.lower() for col in df.columns] expected = pd.DataFrame([ {"id": 1, "name": "User One", "username": "userone"}, … ]) pd.testing.assert_frame_equal(df.reset_index(drop=True), expected, check_dtype=False)
  31. © LayerX Inc. 32 単体実⾏例 テストとパフォーマンスの課題 ポイント • テストクラス外で、ダミーの dbt

    と new_session() を使って model() を実行 • 結果の DataFrame をコンソールに出力し、動作確認可能 if __name__ == '__main__': dbt = DummyDBT() with TestDataProcessingProcedure().new_session() as session: result_df = model(dbt, session) print(result_df)
  32. © LayerX Inc. 33 内部モジュール (_snowflake) の概要 テストとパフォーマンスの課題 専用内部モジュールとしての _snowflake

    • _snowflake は、Snowflake の UDFやStreamlitなどに内部的に提供されるモジュール • 実行中にのみ利用され、セキュアなシークレットアクセスなどの用途で使用する
  33. © LayerX Inc. 34 テストにおける課題: 内部モジュール テストとパフォーマンスの課題 ローカル開発と本番環境のギャップ • ローカル開発環境では、

    _snowflake モジュールを利用する手段はない。 • 本番環境の実行時には、専用の内部モジュールとして _snowflake が用いられており、この内部実装は秘密管理などに特化した機 能を提供する。 ◦ ローカルでの挙動と本番での挙動は根本的に異なる。 モック/スタブの実装の必要性 • 本番環境の内部モジュールの動作を再現するため、ローカルテストでは専用のモックやスタブの構築が必要な場合がある。 • これにより、テストコードが複雑化し、保守性やデバッグが難しくなる可能性がある。
  34. © LayerX Inc. 35 テストにおける課題: 環境差異の問題 テストとパフォーマンスの課題 実環境とテスト環境の違い • 実際の

    Snowflake 環境とローカルテスト環境では、接続設定や権限、リソース管理などに差異があり、テスト結果に影響を与える データ型・カラム名の相違 • Snowpark は返す DataFrame のカラム名を大文字に変換するため、テスト時に変換処理が必要となる • データ型(例: int8 vs int64)の違いも検証を複雑にする 統合テストの再現性 • モックやスタブで完全に実環境を再現するのは難しく、実環境での統合テストとのギャップが発生する可能性
  35. © LayerX Inc. 36 パフォーマンスの課題と考慮点 テストとパフォーマンスの課題 データ処理の効率 • インクリメンタル更新により、必要なデータのみを処理しリソースの無駄を削減 •

    Snowparkを利用することで、分散処理により大規模データでも高速処理が可能 メモリ管理の課題 • Snowflakeの分散アーキテクチャにより、全データが一度にメモリに乗らない仕組み • ただし、データのロードやデータの処理の仕方が悪いことによりメモリリークが起こる可能性がある ログ出力とモニタリング • 実行時ログを活用して、パフォーマンスのボトルネックやエラー発生箇所を特定 • 定期的なパフォーマンステストにより、処理効率を継続的に評価
  36. ⽬次 Agenda • はじめに • Python Modelの基礎知識 • Python Modelの仕組み(Snowflake編)

    • Python Modelの便利な機能と使い⽅ • テストとパフォーマンスの課題 • Snowflake環境でのログ出⼒と重要性 • Future Works と今後の展望
  37. © LayerX Inc. 38 Snowflake のログ‧トレーシング設定 Snowflake環境でのログ出⼒と重要性 Telemetry Levels の概要

    • Snowflake では、ログ出力とトレーシングの詳細度を制御するために Telemetry Levels を設定できます。 • 設定により、エラー情報、警告、情報レベル、デバッグ情報などの出力を調整可能。 設定方法の参考 • 詳細は Snowflake Telemetry Levels を参照
  38. © LayerX Inc. 39 ログ&トレーシングを組み込む Snowflake環境でのログ出⼒と重要性 ポイント • logger.info で各処理段階の情報を出力

    • トレーシングで「 fetch_and_process_data」スパン内の処理 を記録し、デバッグを支援 import logging import requests from snowflake import telemetry from opentelemetry import trace logger, trace = logging.getLogger("example"), trace.get_tracer("example") def model(dbt, session): dbt.config(materialized='table', packages=['snowflake-telemetry-python', 'opentelemetry-api', 'requests']) logger.info("Model execution started") with tracer.start_as_current_span("fetch_and_process_data"): data = requests.get("https://何かサービス/users") logger.info("Data retrieved from API") … df = session.create_dataframe(data_records) logger.info("DataFrame created successfully") return df
  39. © LayerX Inc. 40 Snowflake ログ出⼒ & トレーシングの重要性 Snowflake環境でのログ出⼒と重要性 運用監視・デバッグ

    • リアルタイムでエラー・パフォーマンスを監視し、トラブルシューティングに役立 てる トレーシングによる処理の可視化 • 処理の各段階(データ取得、変換、 DataFrame作成)の流れを追跡し、ボトル ネックを特定
  40. ⽬次 Agenda • はじめに • Python Modelの基礎知識 • Python Modelの仕組み(Snowflake編)

    • Python Modelの便利な機能と使い⽅ • テストとパフォーマンスの課題 • Snowflake環境でのログ出⼒の重要性 • Future Works と今後の展望
  41. © LayerX Inc. 42 Programmatic Invocations の活⽤ Future Works と今後の展望

    概要 • Programmatic Invocations は、dbt モデルの実行をプログラム的に制御する仕組み 自動エラーハンドリング • モデル実行時にエラーが発生した場合、そのエラー情報を自動でキャプチャし、適切なハンドリングを実施 • エラー発生時の自動リトライや通知、あるいはエラーをログに記録することで、迅速な対処を実現 参考 • https://docs.getdbt.com/reference/programmatic-invocations
  42. © LayerX Inc. 43 テスト‧デバッグ環境の改善 Future Works と今後の展望 現状の課題 •

    共通で利用できるテスト環境が未整備のため、各モデルごとに個別のモックやスタブ実装が必要 • ローカルテストと本番環境の差異により、デバッグが困難な場合がある 今後の取り組み • ユニットテスト環境の提供 ◦ ローカルでのユニットテストが確実に動作するよう、共通のテストユーティリティやラッパー関数を整備し、再現性の高い環境 を構築 ◦ e2e テストは dbt test で十分なため、ユニットテストの精度向上に注力する • 共通ライブラリの整備と活用 ◦ 処理をUDF やストアドプロシージャとして切り出すか、 Snowflake に Zip でパッケージ化し置くか、共通ライブラリの配置・管 理方法を検討し、全モデルで再利用できる仕組みを構築する ◦ ログ出力、トレーシング、モック化のベストプラクティスを作成し、 CI/CD パイプラインに組み込むことで、環境差異の影響を最 小化
  43. © LayerX Inc. 45 まとめ • dbt Python Model の全体像について紹介し、

    PythonコードはAST解析を経てモデル定義に変換され、 Snowflake上で実行される仕 組みについて解説しました。 • Pytestを用いたユニットテスト環境とモック/スタブの工夫により、環境差異への対応やデバッグを容易にする方法について紹介しま した。 • 将来的には、Programmatic Invocationsによる自動エラーハンドリングや統一されたテスト・デバッグ環境の整備を目指しています。