1. はじめに
ドコモのデータプラットフォーム部の中村光宏と申します。
この記事では、GitHub CopilotのCustom Instructions を強化しチームのKPI管理に使っていた既存のSQLをdbtモデルに変換した経験と学びをまとめました。 まだベストプラクティスが少ない「Agentによるdbtモデル構築」について、実際に手を動かして得られた知見や工夫をまとめていますので、これからdbtの導入を検討している方や、Agentを活用して効率的にdbtモデルを作成したい方にとって、有益な情報となれば幸いです。
目指したゴール/目指さなかったゴール
今回の取り組みでは、Agentをdbtモデル構築の良い相棒に育てることを目的とし、Agentが完全自動でdbtモデルを生成することは目指しませんでした。 そのため、Agentからのアウトプットは少量を細かく受け取れるようにし、人間と都度レビューや相談をすることで、品質を担保しつつ効率的にモデルを構築することを重視しています。
理想としてはAgentにモデル実行までを任せることですが、モデル実行による既存のテーブルの破損リスクを避けるため、今回は完全な自動化は見送っています。
注意事項
文書内に記載されているファイル名は構成上一部実際のものから変更しています。実際に運用する際は適切な英名に置き換えてください。 AGENTS.mdは.github/copilot-instructions.mdを指しています。
2. dbtとは
dbt (data build tool) は、データウェアハウス上でのデータ変換(Transform)をSQLで定義・実行するためのフレームワークです。 本記事の取り組みにおいて、特に重要となるdbtの特徴は以下の3点です。
手軽な品質担保:unique や not_null といったテストプリセットが標準で用意されており、yamlファイルに記述するだけでデータパイプラインの基本的なテスト実装が容易。
DAG構築とデータ汚染の防止:SQLの参照関係から自動的にDAGが構築され、上流のテストが失敗した際に後続パイプラインの実行を自動で停止できる。
関心の分離による高い保守性:モデルをStaging(生データの抽象化)、Intermediate(中間集計)、Mart(最終成果物)の3層に分けて管理することで関心を分離し、ビジネスロジックの変更や指標の見直しにも柔軟に追従しやすい設計が可能。
なぜSQLをdbtモデルに変換するのか?
単発のSQL運用からdbtへ移行する最大のメリットは、データパイプラインが長期的に「強く」なることです。コードが適切に構造化・管理されることで、将来にわたってチームで安心してメンテナンスできる堅牢な基盤を築くことができます。
その理由は大きく分けて「外部要因」と「内部要因」への対応力にあります。 具体的には、dbtのテスト機能によって上流データの汚染といった不測の事態(外部要因)を検知・遮断できるだけでなく、ビジネスロジックから切り離された中間集計を共通部品として保持することで、仕様変更や分析観点の変化(内部要因)にも柔軟に対応できる再利用性の高さが得られるからです。
私たちが採用しているdbtのベストプラクティス
私たちのチームでは、dbt Labs社が提供するベストプラクティスガイドを参考に、プロジェクトの構造化を進めています。
このガイドラインは非常に包括的であり一読をお勧めしますが、今回の取り組みで特に重要となる「レイヤー構造」について簡単に解説します。
レイヤー構造 (Staging / Intermediate / Mart)
データを段階的に加工する3層構造を採用し、各モデルの役割を明確にすることで、保守性と再利用性を高めています。
Staging: ソースデータと1対1で対応する層。カラム名の統一や型変換のみを行い、この段階で上流データの汚染を食い止めます。
Intermediate: テーブルの結合や集計など、複雑な中間処理を行う層。特定のビジネスロジックを含まず、再利用可能な形でデータを整備します。
Mart: 最終的なビジネス利用に適した形に整える層。BIツールや分析用に最適化されたテーブルを提供します。
3. 実際にやってみた環境・方針
動作環境
本記事での検証・開発は以下の環境で実施しています。
- dbt coreを利用
- GitHub ActionsによるCI/CDパイプライン構築
- データベースは Snowflake
- 開発はVisual Studio Code上で実施
- AgentはGitHub Copilotを利用(GPT-4.1 もしくはClaude Sonnet4.5)
dbtに移行したクエリ
移行対象としたのは、チームで管理している自分たちのKPI(ツールやマートの利用回数)集計用のSQLです。 私たちのチームでは既にdbtを採用していますが、チーム内のKPI集計用SQLはまだdbtモデル化されていなかったため、失敗しても致命傷にならない良い題材として今回対象としています。
従来のSQLでは最終的な集計値のみを取得していましたが、dbtモデル化にあたり、
中間集計(Intermediate)やマート(Mart)を適切に設計することで、
新たな観点での分析や再利用性の高いデータマートの構築を目指しました。
Agentの育成方針
Agentの育成といっても実態はAGENTS.mdや手順書を作り込んでいくだけですが(汗)
基本方針として「目的・達成条件」と「達成条件に対する制約」を与えてアウトプットを絞り込んでいくイメージで構築しました。
dbtのように決まったフォーマットでのアウトプットが求められる場合、あれもこれもと指示を盛り込みすぎると肝心のアウトプットがブレるため、 コンテキストに盛り込む内容はゴールの制約条件に比重を置くように意識しています。
そのため、最初は最低限のルールだけを与えてモデル構築を依頼し、 出力されたモデルの問題点を洗い出してから、ルールの強化や手順書の整備を行う形でAgentを育成していきました。
4. 初手:最低限のルールでCopilotにdbtモデルを作ってもらう
最初からAgentにあれこれ指示を出すのもよくないため、まずはシンプルに以下の指示でモデル構築を指示しました。 dbtモデルに変換したいSQLは@target.sqlとして保存しています。
@target.sqlをdbtモデルに変換してください。 実装時の注意事項は以下のとおりです。 * dbtモデルは3層構造(Staging, Intermediate, Mart)で作成すること * Staging層では必要なカラムだけ抽出しカラムに対するdescriptionを付与すること。 * Intermediate層ではユーザもしくはアクション単位での集計とし、それ以上の粒度での集計はMart層で行うこと。 * Mart層では最終的なKPI集計のみを行うこと。 * モデルの接頭語には"STG_","INT_", "MRT_"をそれぞれ付与すること
この最低限のルールでCopilotにモデル構築を何度か依頼したところ、何れも3層構造のモデルが一通り生成されましたが、いくつかの問題がでていました。次はルールの強化やマニュアルによってこれを改善します。
出てきたモデルの問題点
出てきたモデルに多かった問題点は以下です。
- models.ymlのフォーマットが違う。
- models.ymlとStaging.sqlの対応が取れていない。
- 存在しない列を指定している(元のクエリのwith句内の列名を使っている?)
- 細かい集計ロジックが変わっている(元のクエリをwith句を途中で分断しているだけ)
- Staging,Intermediate,Martの責務がズレている。
models.ymlとsqlの不整合や、存在しない列の指定が出るのは想定外だったんですが、Agentの動作を見るとstaging.sql > intermediate.sql > mart.sqlでパイプラインを組んでからmodels.ymlを最後に生成しており、途中のsql作成中にコンテキストが混ざってしまっているように見えました。
5. 初手の結果を踏まえた改善
フォーマット間違いや責務の間違いはシンプルにサンプルコードを用意して解消し、 列名の間違いや集計ロジックのズレは作業を複数ステップに分けコンテキストを区切ることでAgentが内容を混同しないようにしました。
サンプルコードの用意
GA4を例にしたサンプルコードを4ファイル用意し、 models.yml, staging.sql, intermediate.sql, mart.sqlとして GitHub Copilotに見せることで、フォーマットのズレや責務のズレを解消しました。
チームのKPIのデータはGA4を使っているわけではないのですが、独自のサンプルコードを用意するよりもポピュラーでLLM内に知識があると想定されるGA4を使うことで、Agentが理解しやすくなると考えています。
■サンプルコード例

冒頭数行にコメントでファイルやモデルの責務やルールを書くとより効果的
手順書とステップの分割
サンプルコードの参照方法やモデルの構築ルール、チェック事項は手順書まとめて必要なもののみをステップ毎に参照させるようにしました。
dbtのベストプラクティスに従ったレイヤー構造を前提としており、各層の役割や設計方針を明確にすることで、Agentが適切なモデル設計を行いやすくしています。
最初は手順書を1つのマークダウンファイルにまとめていましたが、改善に伴い制約条件が増えていくとAgentが読み飛ばすことが多かったため、ステップごとにファイルを分けて参照させる形に変更しています。 ※前項で作ったサンプルコードはこの手順書内で引用させています。
初期Martは実際のところ作らなくてもモデル構築は自体はできるので蛇足かもしれませんが、Intermediate層の設計をユーザと議論しやすくし、最終Martの数値チェックの基準を作るためにあえて設けています。
ステップの運用例
VSCode上でChatのセッションを分けて、以下のようにステップ毎に指示出しをしていきます。

手順書
AGENTS.mdにそれぞれのファイルパスを記述し、ステップ毎にセッションを分けて実行する形でモデル構築を進めています。
| 手順書 | 役割・内容 |
|---|---|
| プランマニュアル.md | 受け取ったSQLに対する不足情報をユーザに問い合わせ、情報が揃ったら最終的なplan.mdファイルを作成する手順書 |
| staging作成マニュアル.md | plan.mdを参考にStaging層のymlとモデルsqlを作成し、チェックリストで品質を担保する手順書 |
| 初期Mart作成マニュアル.md | plan.mdを参考にIntermediate層を作らず直接Mart層を作成する手順書。stageを経由する以外は元のSQLをそのままdbt化 |
| Intermediate作成マニュアル.md | 初期Martのsqlを参考にIntermediate層の設計をユーザと議論し、議論結果とモデルを作成する手順書 |
| 最終Mart作成マニュアル.md | Intermediate層を使って初期Martと同等の結果が得られるMart層を作成する手順書 |
手順書の一例
以下はStaging層の作成手順書の冒頭抜粋です。 前半にStaging層の役割や設計方針を説明し、後半に具体的な手順とチェックリストを記載しています。
# Staging層作成マニュアル
この手順書では、plan.mdを参考にStaging層のdbtモデルを作成する手順を説明しています。
Staging層はソースデータと1対1で対応し、カラム名の統一や型変換のみを行う層です。
上流データの汚染や仕様変更を吸収し、Intermediate層以降のモデルが安定して動作することを目的としています。
この手順に従って作られたモデルは以下に保存されています。実装時はこれらのサンプルコードを参考にしたうえで着手して下さい。
.samplecodes/models.yml
.samplecodes/staging.sql
## 1. Staging層の定義(役割と制約)
* 役割: ソースシステム(Raw Data)とdbtプロジェクトの境界線。データのクリーニング、型変換、命名規則の統一のみを行う。
* Sourceの利用: 原則として `{{ source('source_name', 'table_name') }}` 関数を使用し、直接テーブル名を記述しないこと。
* 1対1の原則: 1つのソーステーブルにつき、1つのStagingモデルを作成する。
* カラムの説明: models.ymlに各カラムのdescriptionを必ず記載すること。descriptionはplan.mdの仕様を参考に作成すること。
~中略~
## 4. 品質チェックリスト
実装完了後、以下の項目を確認してください。
[ ] JOINの禁止: JOIN 句、UNION 句が含まれておらず、単一のソーステーブルのみを参照しているか?
[ ] 集計の禁止: GROUP BY, DISTINCT, 集計関数(SUM, COUNT, MAX 等)が使用されていないか?(粒度の変更は禁止)
[ ] ソース指定: FROM 句で生のテーブル名を直接書かず、必ず {{ source('...', '...') }} を使用しているか?
[ ] カラム命名: 全てのカラム名が英小文字のスネークケース(snake_case)にリネームされているか?(日本語や大文字の禁止)
[ ] データ型定義: 金額や日付などのフィールドに対し、CAST 関数で明示的に型変換が行われているか?
[ ] テスト定義: models.yml において、Primary Key に対する unique および not_null テストの記述が漏れていないか?
改善効果
手順書とプロンプトの改善により、基本的な構文エラーや列名の間違い、テスト記述の漏れはほぼ解消されました。
責務のズレに関しても、「Intermediate作成マニュアル.md」で設計を議論しながら進めるプロセスにしたことで、ビジネスロジックが誤って混入することは大幅に減少しました。ただし、深いドメイン知識が必要なケースなどでは、依然としてAgentが完全に自走できない場面も残っています。
特に、Intermediate層とMart層に分割する際、集計クエリのロジックを誤るケースが稀にあるため、現時点では「100%の自動化・自走」には至っていません。
6. Agentは良き相棒になったのか?
なりました!
面倒なyamlやテストの作成はほぼお任せできるようになり、Intermediate層の設計など元のSQLに思想が入っていない部分に関しては私と議論しながら観点を整理してくれる良き相棒になりました。 後述のTipsでも触れますが、dbt testを通じてデータ定義の情報を伝えることでAgentが的確なモデル設計の提案を行ってくれるためIntermediate層の設計が非常にスムーズになっています。
また、副次的な効果ですがIntermediate層の仕様や想定ユースケースをAgentと議論した結果が残るので、 新たなMartを作成する時はIntermediateと集計目的を指定するだけで適切なMartを提案してくれるdbt プロジェクトの専門家としての役割も果たしてくれています。
7. Tips
今回やってみて良かった細かいテクニックをいくつか紹介します。
dbt test/descriptionがデータ定義書になる
staging層のデータは仕様書は作らずともmodels.ymlを充実させることで仕様書として活用できます。 独自の定義書を用意するよりもAgentに仕様を伝えやすく、Agent自体に書いてもらうときも楽なので充実させることをお勧めします。
Intermediate層の仕様検討の際もAgentがこのymlでstagingの仕様を参照して提案してくれるため、定義確認や議論がスムーズに進みました。
Agentに渡すサンプルコードはGoogleAnalytics4などポピュラーなものを使う
Agentに渡すサンプルコードは、チームのドメインに近いものである必要はなく、GoogleAnalytics4などポピュラーでLLM内に知識があると想定されるものを使うと効果的です。 独自ドメインのサンプルコードを渡してしまうとAgentがデータ定義を勝手に解釈してしまい、Intermediate層の設計の際に正しく責務を理解してくれないケースがありました。
Intermediate層の設計議論結果をドキュメント化する
Intermediate層の設計をAgentと議論した結果は、ymlの定義とは別に議事のまとめとしてmdファイルに残すことで、Intermediateを流用する際の参考資料として活用できるようにしました。 集計粒度などの仕様に近い情報からモデルの想定ユースケース、モデルでカバーしないと決めたこと(他のモデルに任せること)などを残しておくようにしています。
yml自体には書ききれない設計意図やユースケースをまとめておくことで、ユーザ自身も設計の意図を振り返りやすくなりAgentも適切なIntermediateの提案がしやすくなるためお勧めです。

古典的ですが、モデルファイルにコメントで設計書のリンクを示してしまうと早い
期待通りのアウトプットが出なかった時は振り返りメモを残しておく
Agentが期待通りのアウトプットを出せなかった場合は、即座に手順書を修正するのではなく、まずは振り返りメモを残すことをお勧めします。 都度修正方法もありますが、断片的な記述が多くなり内容が把握しづらくなるのと、コミットログ自体も汚れるため振り返りメモとしてまとめておき、ある程度溜まってから作り直しをしたほうが効率的です。
8. まとめ
今回かなり試行錯誤しながらでしたが、Agentを育成することでdbtモデル構築の効率化が図れAgentをdbt モデルの専門家として活用できるようになりました。
現状はAgentによる直接クエリの実行を許可していないため、staging層のyml作成など本来人間が介入しなくてもいい部分にも人手がかかっていますが、 将来的には適切なガードレールを用意したうえでAgentにSnowflakeの参照やモデルの実行を許可することで、人側はIntermediate層の設計議論に集中し、Agentにモデル構築と実行を任せる形が理想と考えています。