Gaudiy Tech Blog

Gaudiyの技術、開発組織、カルチャーについてお伝えするブログです

Gaudiy流AI駆動開発: AI-DLCを3ヶ月実践してみて

この記事は#GauDev Advent Calendar 2025の15日目です。

はじめに

こんにちは、Gaudiyでエンジニアをしている@mrskiroです。

私が所属するチームでは、新規事業の複数立ち上げに向けてより高速に開発を行える、AIネイティブな開発フローを模索していました。AI駆動開発というキーワードは盛り上がり始めていましたが、コード生成の話が中心で開発プロセス全体をどう回すかのベストプラクティスはまだ見当たりませんでした。

そんな中で出会ったのがAWSの提唱する「AI-DLC(AI-Driven Development Lifecycle)」です。この記事では、AI-DLCを実践したこの3ヶ月間の取り組みについて紹介します。

AI-DLCとは

AI-DLC(AI-Driven Development Lifecycle)は、AWSが提唱した開発ライフサイクルの考え方です。

aws.amazon.com

AI-DLCでは、「人間が主導し、AIがアシストする」という従来の形を逆転させています。AIが開発を主導し、人間はAIの成果物をレビューして意思決定に責任を持つという考え方です。

また、AIを中心にプロダクト開発を進めると、AIのアウトプット速度に対して人間のレビュー・意思決定がボトルネックになりやすいため、チーム内での同期コミュニケーションが重視されています。

AI-DLCはInception、Construction、Operationの3つのフェーズで構成されています。

出典: AI 駆動開発ライフサイクル:ソフトウェアエンジニアリングの再構築

フェーズ AIの役割 人間の役割
Inception 人間との対話を通じて仕様書・ユーザーストーリーを生成し、構想を具体化 ビジネスの目的や作りたい機能の概要をインプット
Construction 設計・コード・テストなどを自律的に生成 AIが生成した成果物を専門家としてレビュー・検証
Operation パフォーマンス監視、エラー検知、ログ分析を実施。問題発見時は原因分析と修正案を提示 AIからの報告や提案を評価し、本番適用の可否を最終判断

具体化するまで

AI-DLCはあくまで考え方や方法論なので、実際にどうやるかは自分たちで決める必要がありました。そのため、まずはホワイトペーパーの末尾にあるプロンプト例をClaude Codeのスラッシュコマンドとして実装し、Inceptionを試したのちに、ドキュメントの構造や保存方法を検討していきました。

0→1で作るときはプロトタイプでチーム内認識を合わせるフェーズがほしかったので、α ConstructionやInception Refiningといった独自のフェーズも追加することにしました。一方で、AI-DLCには「Bolt」というイテレーションの単位が定義されていますが、聞き慣れない用語を増やすよりシンプルにフェーズ単位で進める方が浸透しやすいと判断し、導入しませんでした。

フェーズを検討していた時期は、チームメンバーが整理してくれたFigJamを見ながら「次は何なんだっけ」「ここに戻ってもいいんだっけ」といった議論を都度していました。Unitの依存関係やデザインを反映するタイミングなど、実際に回しながら考えることも多かったです。

figjam

ツール面での苦労もあり、スラッシュコマンドを改善するたびにClaude Codeで動作確認が必要で、レートリミットに達したりAIの出力に揺れがあったりして難航することもありました。

現在運用中のフロー

Inceptionの進め方

Inceptionでは、PdM・エンジニア・デザイナーなどプロジェクトメンバーが参加し、ビジネス目標を元にIntentとユーザーストーリーを作ります。

スラッシュコマンドを実行すると、AIが「このプロダクトで解決したい課題は?」「ターゲットユーザーは?」といった質問を投げかけてきます。対話を通じて要件を具体化し、Intent(プロダクトの目的、スコープ、成功指標などをまとめたPRDに近いドキュメント)とユーザーストーリーを生成します。

実際の進め方としては、PdMが要件の言語化に集中し、エンジニアは技術的な観点から補足を入れる形で対話しながら進めています。1セッションは1時間程度で終わることが多いです。

雰囲気を伝えるため、実際のスラッシュコマンドを抜粋して紹介します。

# AI-DLC Inception Phase

現在のプロジェクトのインセプションフェーズを実行します。
AI が主導して、Intent 発見から User Stories 作成、Units 分解まで一連の流れで進めます。

## このセッションの概要

AI-DLC 思想に基づき、以下を一連の流れで実施します:

1. **ビジネス価値の明確化** - 段階的な対話で真の Intent を発見
2. **Intent Statement 生成** - 価値、ユーザー、メトリクス、MVP を統合
3. **User Stories 作成** - Intent を User Stories に分解
4. **Units 分解** - User Stories を独立開発可能な Units に設計
5. **計画承認プロセス** - 各ステップでチェックボックス付き計画をレビュー・承認

## Phase 1: Intent Discovery

### Intent番号の確認

1. `aidlc-docs/requirements/` ディレクトリ内の最新Intent番号(3桁: 001, 002...)を確認
2. 最新Intent番号+1を `NEXT_INTENT_NUM` とする
3. 既存Intentがあれば Brown-Field、なければ Green-Field として判定

### Intent Discovery プロセス

私(AI)がファシリテーターとなり、対話を通じてあなたの Intent を明確にしていきます。

状況に応じて適切な質問を行いながら、以下の要素を明確化していきます:

- 解決すべき問題または追求する機会
- 価値を受けるターゲットユーザー
- 成功の測定方法
- MVP(数日〜1 週間で実現可能なもの)

Green-Field(新規開発)でも Brown-Field(既存改善)でも、まずはあなたが取り組みたいことについて自由にお話しください。私が適切な質問で詳細を引き出していきます。

### Intent Statement 生成

対話を通じて収集した情報を基に、Intent Statement を生成します。以下の形式でまとめます:

# Intent-XXX: [簡潔で明確なビジネス目標]

## Business Value
[実現するビジネス価値]

## Target Users
[価値を受けるターゲットユーザー]

## Success Metrics
[成功を測定する指標]

## MVP Scope
[最初に実現する最小限の機能 - 数日〜1 週間で実現可能]

(以下、Phase 2: User Stories 作成、Phase 3: Units 分解と続く)

α Constructionの進め方

α Constructionでは、PdM・エンジニア・デザイナーなどプロジェクトメンバーが参加し、Inceptionの内容を元にプロトタイプを作成します。

プロトタイプ用の設計を生成し、Next.jsやExpo等でプロトタイプを自動生成します。ここで作るのは「動くもの」であって、「完成品」ではありません。実際に触ってみて「これで合ってる?」を確認するのが目的です。

Inception Refiningの進め方

プロトタイプを触ると、だいたい何かしら課題が見つかります。「この画面遷移、分かりにくいな」「この機能、実は要らないかも」といった気づきです。

そういった課題をインプットすると、AIが対話を通じてIntentとユーザーストーリーを更新します。α ConstructionとInception Refiningは2〜3回繰り返すことが多いです。

β Constructionの進め方

β Constructionでは、クローズドベータ向けのプロダクトを構築します。ここからは本番を見据えた実装になります。

設計フェーズでは、まず全体設計としてDDD戦略的設計とUnit分割を行います。その後、Unitごとにドメイン設計、DB設計、API設計、UIフロー設計を進めます。設計ドキュメントは実装時に参照することが多いため、時間をかけてレビューします。

最初はチーム全員で進めていましたが、慣れてきた後半からはエンジニアのみで行うようになりました。InceptionやRefiningと違って、技術的な判断が中心になるためです。

実装フェーズでは、設計ドキュメントを参照しながらClaude CodeのSubAgentなどを用いて各メンバーが実装を進めます。いわゆる仕様駆動開発(SDD)と同様のアプローチで、Inceptionで作成したIntentやユーザーストーリーが仕様として機能するので、ある程度精度よく実装を進められます。デザインがある場合は、MCPでFigmaデータを参照しながら実装します。こちらについては@minamiが記事を書いているので、ぜひご覧ください。

zenn.dev

なお、フローチャートに記載したConstruction以降のフェーズは、AI主体で実行していく部分をまだ模索中です。

プラグイン化

AI-DLCを実践していく中でやり方が固まってきましたが、プロジェクト固有のスラッシュコマンドとして実装していたため、他のプロジェクトでも使えるようにClaude Codeのプラグインとして切り出しました。

Claude Codeは他のCLIツールと比べてチームで設定を共有する機能が豊富で、プラグイン化に手間がかからなかったのはありがたいポイントでした。「aidlc-kit」という名前で、各フェーズに対応した以下のスラッシュコマンドを用意しています。

コマンド 説明
setup プロジェクトのセットアップ
inception-new 新規Intent作成
inception-fix Intent修正
alpha-design プロトタイプ用設計
prototype プロトタイプ生成
design-overview 全体設計
design-unit Unit個別設計
task 実装タスク生成

将来的にはPluginはOSSにするかもしれません

実践してみて

プロジェクトメンバーが同期でAIと一緒に要件を整理していくので、機能の実装優先度や仕様の背景について認識が揃いやすくなっています。

実際に、エンジニア3名で開発を始め、途中から1名が加わり計4名で約2ヶ月と少し、Web・モバイル・バックエンドを含むプロダクトをデリバリー直前の状態まで開発しました(リリース時期はビジネス上の理由で調整中)。

α Constructionでプロトタイプ作成とユーザーテストを何度も繰り返したおかげで、β Construction以降はほとんど手戻りなく進められました。また、AI-DLCのフロー構築自体も並行して検証していたのもあり、フローが固まった今後はもっと短縮できる余地があると考えています。

課題

AIのアウトプットが早く多いため、人間のレビューがボトルネックになりやすいです。β Constructionの設計ドキュメントのレビューだけで1〜2時間かかることもあり、レビューしやすいフォーマットの検討を進めています。

現状はClaude Codeのスラッシュコマンドやプラグインとして実装していますが、今後AIモデルや開発ツールが変わる可能性もあります。MCPやシェルスクリプトを活用し、ツールに依存しない形への切り出しも検討しています。

他チームへの展開の難しさも課題の一つです。AI-DLCの用語や、カスタマイズしたフローの学習コストが一定存在します。途中のフェーズからでも使える形や、通常の開発でも便利なコマンドを一緒に配布するなど、小さく始められる状態を目指しています。

おわりに

AI-DLCを3ヶ月実践してきました。ホワイトペーパーの概念を自分たちなりに解釈し、Claude Codeで運用できるようにしました。まだまだ課題は多いですが、従来よりスピード感を持って開発を進められています。

AI-DLCの実践に関心がある方や、すでに取り組まれている方にとって、一つの事例として参考になれば嬉しいです。

なお、私たちが独自に実践を始めた後、AWSからAI-DLCのワークフローがオープンソースとして公開されました。これから始める方はこちらも参考になると思います。

github.com

GaudiyではAI駆動開発に興味のあるエンジニアを募集しています。カジュアル面談も歓迎ですので、気になった方はぜひお声がけください。

special.gaudiy.com

次回の#GauDev Advent Calendar 2025はameさんの記事です。お楽しみに!


参考

AI活用で開発加速!GCP×AWS マルチクラウドリバースETLの技術選定と実装

はじめに

この記事はGaudiy Engineers Advent Calendar 2025の6日目の記事です。

はじめまして。Gaudiy でデータエンジニアをしている miyataku です。

私たちのプロジェクトでは、「BigQuery で分析したデータをアプリケーション側で使いたい」というニーズがありました。
課題となったのは、データ分析基盤が GCP (BigQuery)、アプリケーションが AWS で動いているというマルチクラウド構成 です。プラットフォームをどちらかに統一する案も出ましたが、コストやリスクを試算すると現実的ではありませんでした。

そこで、マルチクラウド環境を維持したまま、BigQuery のデータを AWS 側で活用する仕組み を構築する方針を選択しました。

技術選定から開発まで進めていった結果、いい感じに要件を満たす構成がまとまって、わずか3 週間で完了まで持っていくことができました
これほど短期間で仕上げられた大きな理由は、技術選定から開発まで Claude Code を積極的に活用した点にあります。

本記事では、そのときの意思決定や構成、AIをどう活用したかなどの裏側をまとめて紹介します。

🔍 この記事のハイライト

  • 🔄 データ連携基盤構築:BigQuery → S3 → アプリケーションのデータ連携を短期間で実現
  • ⏱️ 開発工数削減:Cloud Run Jobs の採用で 6〜9 日短縮。Go / Docker の既存スキルのみで対応可能
  • 🔐 セキュアな認証:Cloud Run OIDC × AWS STS でマルチクラウド認証を実現(WIF構築不要)
  • 💰 高いコスト効率:数百万ユーザー規模のデータを月額コスト数ドルで処理
  • 🤖 AI 活用の工夫:技術選定や実装パターン検討で効率化を実現
  • 📚 ドキュメント戦略:spec / research の分離で新メンバーのキャッチアップを大幅に高速化

1. 背景と課題

実現したかったことはシンプルで、BigQuery で分析・集計したユーザーの記録や行動履歴を、API 経由でアプリケーションに提供することです。
これができれば、よりパーソナライズされた体験を提供できます。

ただ、実際にやろうとすると、2 つの壁にぶつかりました。

マルチクラウド認証をどうするか

データは GCP(BigQuery)にあって、アプリケーションインフラは AWS(ECS/S3)にある。
つまり GCP 側から AWS の S3 に安全にアクセスする必要があります。

アクセスキーみたいな長期秘密情報は管理リスクが高いので避けたいですし、かといって認証基盤を新たに構築・運用するコストもできるだけ抑えたい。

数百万ユーザー分のデータを高速に処理するには

1 日 1 回、数百万ユーザー分のデータを全件更新する必要があります。
Cloud Run のメモリ・CPU 制限の中で、並列処理をうまく使って高速化しつつ、コストも最適化しないといけません。

マルチクラウドのままいくか、統合するか

最初はチーム内でも「マルチクラウドって運用複雑じゃない?どっちかに統合した方がいいのでは?」という意見がありました。

AWS 統合案(BigQuery → Redshift に移行)や GCP 統合案(ECS → Cloud Run に移行)も一応検討しましたが、どちらも既存システムの大規模な移行が必要で、移行期間やビジネスリスクを考えると現実的ではありませんでした。

となると、マルチクラウドのままでいくことになるのですが、「アクセスキーの管理コストが心配…」という懸念がありました。
でも実際に検証してみたら Cloud Run のデフォルト OIDC だけで AWS 認証が完結して、Workload Identity Federation(WIF)の構築も不要で、意外とシンプルに構築できました。

この検証結果を受けて、マルチクラウド構成を維持する方針を確定。
データ連携基盤の構築を進めて、短期間で完了まで持っていくことができました。

2. 技術選定とアーキテクチャ

今回構築したのは、BigQuery(データウェアハウス)から S3(データストア)を経由してアプリケーションにデータを提供する、リバースETL(Reverse ETL) の仕組みです。
要するに、BigQuery で分析・集計したデータを、アプリケーション側で使えるようにする流れになります。

全体構成

先ほど挙げた 2 つの課題(マルチクラウド認証と大量データ処理)を解決するために、こちらの構成を選定しました。

 

 


データストアは S3 + ElastiCache に決定

データストア選定では、2つの観点を重視しました。
1つ目は、アプリケーション側が ECS に決まっているため、ECS から参照しやすい形であること。
2つ目は、BigQuery から取得したデータを転送する集計ジョブからの一括更新のしやすさです。

データストアの候補としては S3、DynamoDB、Aurora の 3 つを検討しました。最終的に選んだのは S3 + ElastiCache for Redis の組み合わせです。

データ特性を整理してみると、読み取り専用・日次更新・検索不要というパターンなので、RDB の利点(トランザクション、JOIN)も DynamoDB の利点(低レイテンシ書き込み)もあまり活かせないことが分かりました。
むしろ「安価に大量データを長期保管する仕組み」が最適です。

当初は「数百万ユーザーのデータ扱うなら RDB(Aurora)の方が安全ではないか?」という意見もありましたが、よくデータ特性を整理すると以下のことがわかりました。

  • トランザクション不要:1 日 1 回の全件置換だし、ACID 特性いらない
  • JOIN 不要:もう BigQuery で集計済みのデータを転送するだけ
  • スキーマ変更の柔軟性:S3 なら JSON 形式でスキーマフリー。RDB だと ALTER TABLE が必要
  • コスト:Aurora は月額数十ドル〜、S3 は月額数ドル程度

こういった理由で RDB は選択肢から外れました。
さらに、「RDB だと高速だが、S3 だと遅い」という懸念も、ElastiCache をキャッシュとして使うことで解決できました。

観点 S3 の優位性
ストレージコスト DynamoDB と比較して大幅に安価。数百万ユーザー規模でも月額コストを抑えられる
アクセスパターン 日次全件更新・読取専用に最適
バージョン管理 S3 標準機能で過去データ保持が容易(RDB は履歴テーブル設計が必要)
レイテンシ S3 単体では数十〜百ms程度だが、ElastiCache(サブミリ秒)で十分カバー可能と判断

ジョブ実行環境は Cloud Run Jobs で

ジョブ実行環境はデータソースとして BigQuery がメインになるだろうとは想定していましたが、将来的に他のデータソースも扱う可能性があったため、特定のデータソースに依存しない柔軟な仕組みを選びたいと考えました。

ジョブ実行環境の候補は Cloud Run Jobs と Vertex AI Pipelines の 2 つ。最終的に選んだのは Cloud Run Jobs です。

観点 Cloud Run Jobs Vertex AI Pipelines 判定
開発工数 短期間で対応可能 パイプライン定義・DSL学習が必要 ✅ 大幅に削減
コスト 低コスト 相対的に高コスト ✅ 圧倒的に低コスト
アーキテクチャ コンテナ 1 つで完結(修正も容易) DSL 学習が必要 ✅ シンプル

チームが既に持っている Go / Docker のスキルを中心に開発できて、パイプライン定義や DSL の学習が不要だったのが決め手でした。
結果として、開発工数を大幅に削減できました。

3. 実現のポイント

BigQuery からデータをストリーミングで取得

数百万ユーザーのデータを一気にメモリに読み込んだら、OOM(Out Of Memory)で落ちるリスクがあります。
なので、Query.Read() が返す RowIterator を使って、ストリーミングで 1 件ずつ処理する方式にしました。

// BigQuery クエリを実行
query := client.Query("SELECT ...")
it, err := query.Read(ctx)
if err != nil {
    return fmt.Errorf("failed to read query: %w", err)
}

// RowIterator でレコードを1件ずつ処理(全件メモリに載せない)
for {
    var row MyStruct
    err := it.Next(&row)
    if err == iterator.Done {
        break
    }
    if err != nil {
        return fmt.Errorf("iterator error: %w", err)
    }
    if err := processRow(ctx, row); err != nil {
        return err
    }
}

ポイント:

  • RowIteratorは内部でページングを行うため、数百万レコードでも全データをメモリに保持する必要がない
  • 常に処理中の1レコード分のみがメモリに載るため、Cloud Runのメモリ制限内で安定した処理が可能

マルチクラウド認証は意外とシンプルだった

結論から言うと、Cloud Run のデフォルト OIDC トークン(Issuer: accounts.google.com)を使うだけで完結しました。
最初は「Workload Identity Federation(WIF)の構築が必須では?」と思ってましたが、AWS は accounts.google.com を WebIdentity Provider として直接扱えるため、WIF Pool の作成は不要でした。

AWS 側の IAM Role(Trust Policy)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "accounts.google.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        // Cloud Run の ID Token を検証する条件を設定
        // 詳細は AWS STS と Google Cloud OIDC のドキュメントを参照
      }
    }
  ]
}

ポイントは "Federated": "accounts.google.com" の設定で、これだけで Cloud Run のデフォルト OIDC を使えます。
Condition 部分は環境によって異なるため、AWS STS と Google Cloud OIDC のドキュメントを参照してください。

認証の有効期限について: AWS STS が発行する一時クレデンシャルは 1 時間で期限切れになります。
今回はクレデンシャルをキャッシュし、期限切れ 10 分前に自動更新する仕組みを実装しました。

並列アップロードで高速化

BigQuery からのストリーミング取得と S3 への並列アップロードを組み合わせて、errgroup.WithContext で制御しました。
並列数を 10〜100 で調整して、1 日 1 回の全件更新という要件を十分満たせる性能を実現できました。

ただ、高並列で S3 にアクセスする場合、Go のデフォルト HTTP クライアント設定がボトルネックになるので、接続プールを拡張しました。

httpClient := &http.Client{
    Transport: &http.Transport{
        MaxIdleConns:        200,
        MaxIdleConnsPerHost: 200,
        IdleConnTimeout:     90 * time.Second,
    },
}

ポイント:

  • MaxIdleConnsPerHost:デフォルトは2ですが、並列数に合わせて200に拡張
  • これにより、接続の再利用(Keep-Alive)が効き、高速かつ安定した処理が可能になります

4. AI をどう活用したか

今回のマルチクラウド構成のデータ連携基盤では、Claude Code を活用して、技術選定から開発まで短期間で完了できました。

技術選定の壁打ち相手として

技術選定を AI に相談するとき、一番大事なのは前提となる要件を明確に伝えることです。
データ特性、アクセスパターン、パフォーマンス要件を構造化して提示すると、AI が適切な比較軸で回答を返してくれます。

# 技術選定:データストアの選定

## 集計対象の情報
- 対象ユーザー数: 数百万
- 更新頻度: 1日1回(全件更新)
- アクセスパターン: 読み取り専用

## 比較観点
1. 月額コスト
2. 開発工数
3. 性能

## 比較対象
- S3 + ElastiCache
- DynamoDB
- Aurora

この形式で依頼すると、AI が各候補を一貫した観点で比較してくれて、意思決定の質が上がりました。
マルチクラウド認証の構築など、初めて扱う技術要素についても、ベストプラクティスを含むサンプルコード付きで回答が返ってくるので、方針を素早く固められました。

AI 活用で重要なのは、入力の厳格さと出力への深掘りの両立です。
生成されたコードやアーキテクチャ案に対して「このエラーケースは?」「パフォーマンスは?」「この情報は本当に正しい?」と繰り返し問いかけ、公式ドキュメントで裏取りしながら品質を高めていきました。
AI はハルシネーション(誤った情報の生成)もあるため、出力を鵜呑みにせず、批判的に検証することが不可欠です。

意思決定に "唯一の正解" はありません。だからこそ、意思決定理由を明確にし、曖昧さを残さないことが重要です。
AI は選択肢の整理や情報収集を効率化してくれる存在として活用しました。

コード生成の活用

AI によるコード生成は、errgroup による並列処理や HTTP クライアントの接続プール設定など、Go のベストプラクティスに沿った実装パターンを素早く提案してくれるため、調査時間の短縮に役立ちました。
ただし、初期段階では失敗もありました。

設計方針を明確にせず「BigQuery から S3 にデータを転送する処理を書いて」とだけ依頼したら、レビューで大量の修正が発生。
エラーハンドリング、ログ出力、インターフェース設計が曖昧なまま生成されたので、開発時間よりもレビュー対応時間の方が長くなってしまいました。

対策として、開発前にエラーハンドリングポリシー、ログ出力ポリシー、インターフェース設計などを CLAUDE.md に記載するようにしたところ、レビューコメントが大幅に削減されました。
プロジェクト固有のルールを事前に伝えることで、最初から本番投入レベルのコードが生成されるようになりました。

ドキュメント管理の工夫

AI を活用して技術選定を行うと、どうしても膨大な検討ログが生成されます。
これをそのままチームに共有すると、必要な結論が埋もれ、読むのに時間がかかり、レビューが難しくなります。

解決策:spec / research 分離

  • docs/spec/(開発仕様書:結論): A4 1〜2 枚に収まる簡潔な仕様書。What(何を作るか)、Why(なぜ必要か)、How(どう作るか)のみを記載。検討過程は含めない。
  • docs/research/(根拠の記録): 詳細な検討記録。候補技術の比較、コスト試算、実測ベンチマーク、AI との議論ログ、不採用案の理由を記載。

効果:

  • 新メンバーは spec(A4 1-2 枚)を読めば全体を把握でき、research を最初から読む必要がない
  • 意思決定の理由が research に残るので透明性が保たれる
  • PR では spec だけ確認すればよく、詳細は必要に応じて research を参照
  • 方針転換の判断がしやすい:research に「なぜその選択をしたか」が残っているので、状況が変わったときに「前提条件が崩れた」と気づきやすく、適切なタイミングで方針を見直せる

この分離により、「新メンバーは spec を読むだけで開発開始」「マネージャーは結論だけ見れば十分」「詳細が必要な時のみ research を参照」という効率的なワークフローが実現しました。
さらに、意思決定の根拠が明確に残るので、半年後・1年後に「なぜこの構成にしたんだっけ?」と振り返るときにも役立ちます。

5. やってみて分かったこと

マルチクラウドは思ったよりシンプルだった

Cloud Run のデフォルト OIDC と AWS STS を組み合わせることで、追加インフラなしでクラウド間の安全な認証を実現できました。
Workload Identity Federation(WIF)が不要だったのは大きな発見で、構築も思ったよりずっとシンプルでした。

ストリーミング取得 × 並列処理で高速化

BigQuery のストリーミング取得(RowIterator)でメモリ使用量を抑えつつ、S3 へのアップロードは worker pool と errgroup を組み合わせて並列化。
さらに HTTP コネクションプールを適切に調整したことで、安定して高いスループットが出せるようになりました。

ドキュメント管理とAI活用はセットで考える

spec / research の分離で、チーム全体での情報共有の質が大幅に向上しました。
新メンバーが短時間で追いつけるし、認識のズレもなくなるし、仕様変更点も分かりやすくなりました。
AI を使うと検討ログが膨大になりがちですが、この分離戦略で整理することで、必要な情報に素早くアクセスできるようになります

6. おわりに

マルチクラウド構成って複雑そう…と最初は不安でしたが、実際に検証してみると Cloud Run の OIDC + AWS STS だけでクラウド間認証が完結して、思ったよりシンプルに構築できました。

データストアは S3 + ElastiCache、ジョブは Cloud Run Jobs という構成で、短期間で完了できました。
Go と Docker の既存スキルだけで対応できたのも大きかったです。

Claude Code を技術選定の壁打ちに活用したことで開発スピードが上がりました。
要件を構造化して AI に相談することで選択肢の整理が効率化され、意思決定の材料を素早く集められました。
ただし設計方針や最終判断は人間が行う必要があります。
最初から完璧な指示を出すのではなく、AI の出力に対して疑問を投げかけ、改善を重ねていく対話プロセスが、品質の高い成果物につながります。

マルチクラウドや AI 活用の開発プロセスで悩んでいる方の参考になれば幸いです。

#GauDev Advent Calendar 2025、明日の担当はYuseiWhiteさんです!

少人数の新規チームでAI駆動開発を1ヶ月半実践してみた

こんにちは。Gaudiyでバックエンドエンジニアをしているryio1010です。

突然ですが、皆さんの開発チームは何人構成でしょうか?

私の所属する新たに新設されたチームは、PdM、デザイナーが0.5人(他PJと兼務)、エンジニアがフロントエンドとバックエンド各1人です。

Gaudiyの他の開発チームはPdMとデザイナーそれぞれ1人、エンジニアが4-5人ほどのチームが多いので、従来よりも少ないチーム構成となっています。

詳細は後述しますが、そんな構成のチームの中で私たちに与えられたミッションは、「AIをフル活用して、他チームと同等のパフォーマンスを出す」というものでした。

従来のやり方にとらわれない自由な開発が許されている環境で、ここ1ヶ月半ほどAI駆動開発に本格的に取り組んできました。

この記事ではこのミッションのもと「AI駆動開発」に取り組んでみて得た学びや、実践と試行錯誤の過程をお届けします。

1. なぜAI駆動開発を始めたのか

チームが発足した当初、Dev代表からこんなミッションをもらいました。

デリバリーする機能の開発が最優先だが、それを担保した上でAIをフル活用して従来のやり方にとらわれない自由な開発の仕方を模索してほしい。 AIをチームでどう活用できるかを考えてほしい。

エンジニア一人当たり4万円/月まで会社負担でAIツールを自由に利用できる福利厚生も整備されてきており、これまでももちろんエンジニアは個人としてAIツールを開発に取り入れていましたが、改めてチームとしてAIを利用した上での開発フローの確立をしたいという意図がありました。

フロントエンド・バックエンドエンジニア各1名という構成で、AIツールの活用を前提としたチーム作りは、ある程度チームとして戦略立ててAIを使っていかなければ個人で使用していた以上の成果を出すことは難しいだろうと考えていました。

そこでまず、チームとして使うAIツールの選定から始めることにしました。

2. チームとして実践したこと

実践内容 詳細
AIツールの統一 • ナレッジの共有や蓄積のしやすさを重視
• その他のツールは各自の責任で自由に利用可能
MCPサーバーの活用 filesystem MCPで別リポジトリを参照
• 社内Go共通実装リポジトリのMCPを活用
• その他MCPの活用
AI Daily Sync 毎朝5〜10分のカジュアルな情報交換
• フロントエンド・バックエンドの異なるアプローチを共有

チームとして取り組むという観点から話し合った結果、ナレッジの共有や蓄積のしやすさ考えてメインで使うツールはなるべく統一したほうがよいだろう、という結論になりました。

CursorやChatGPTなどいくつかのツールを試していたとき、ちょうどClaude Codeがサブスクプランで使えるようになり、世間的に盛り上がりを見せていたタイミングでした。

そこで私たちのチームは Claude (Code/Desktop) をメインとし、設計の壁打ち相手やClaude以外の視点を入れる目的として Gemini を併用するスタイルに落ち着きました。それ以外のAIツールについてはセキュリティーなどは考慮しつつも各自の責任で好きなものを開発に利用することとしました。

また、MCPサーバーも積極的に活用しました。filesystem MCPで別リポジトリを参照できるようにしたり、別チームが開発を進めていた社内のGo共通実装リポジトリの MCPなども活用し、開発を進めていきました。

そしてツール以上に効果的だったのが、毎日実践した「AI Daily Sync」です。これは朝会の前にほんの5〜10分、AIについてカジュアルに情報交換する時間です。

「昨日Gemini CLIを試したら、結構良くて…」 「CLAUDE.mdの管理、どうしてる?」

フロントエンドとバックエンドの開発の取り組みについては後述しますが、両者では使うツールも違えば、AIへのアプローチも細かい部分では異なっていました。だからこそ短い時間でのsync会が、次の取り組みへのヒントになることも多くありました。実際にCLAUDE.mdの管理方法やdocsディレクトリの構成の再検討などを実践する機会となりました。

何より「それいいね!他のrepoでも試してみよう」と、互いの知見を取り入れることでAI活用の知見をチームとして高められた点が特に良かったと感じています。

また毎日話す機会を作ることで、各人が自然とAIについて調査する時間を意識的にとるようにもなっていました。この取り組みは今でも続けています。

Syncの内容をSlackでシェアしてる様子

3. ドキュメント駆動開発を軸にする

AI駆動開発を進めていく中で、どのAIツールを使う上でも共通していることがあると感じていました。

AIのパフォーマンスは、インプットされるコンテキストの質で決まる」

AIに質の高い仕事やアウトプットをしてもらうには、私たちが質の高い情報、すなわち「ドキュメント」をコンテキストとして整備して与えてあげることが不可欠だと感じています。ドキュメントがない場合とのアウトプットの質にかなり差があることはこれまでのAIを活用した開発でも感じていた部分でした。

実践を通して私たちは以下の理由からAIと協働する時にはドキュメント駆動開発を導入した方が良いと考えるようになりました。

3-1. 共通認識としてのドキュメント

開発において最もコストがかかるのは、後から発覚する仕様の認識齟齬による手戻りだと感じています。ドキュメントを起点に開発を進めることで、そのドキュメントをもとに仕様の確認ができるため認識齟齬を減らすことができています。

何を作るべきか、なぜ作るのかが明文化されるため、開発者間はもちろんAIとの間でも認識がズレるリスクを大幅に低減できました。また実装の前にドキュメントのレビューを行うことで、各人が共通認識を持った上で適切なコンテキストのもとでAI開発を行うための土台になっています。

実際のドキュメントの一例

├── docs
│   ├── 01-requirements ★要件定義書など
│   ├── 02-architecture ★ドメイン設計・データ設計など
│   ├── 03-api-reference ★各RPCの仕様書
│   ├── 04-development ★開発ガイドラインなど
│   ├── 05-decisions ★ADR

3-2. 「とりあえず実装」からの脱却

ドキュメントを作成する過程は、要件や設計の曖昧な点を強制的に洗い出すプロセスでもありました。

「とりあえず実装しながら考えよう」という進め方では見落としがちなエッジケースや考慮すべき点を事前に特定できるため、結果的により具体的なプロンプトをAIに与えることができるようになり、AIが生成するコードの質を向上させることができたと感じています。

3-3. チームの生産性向上

AIのために整備を進めたドキュメントはAIだけのものではなく、むしろチームにとっての資産になります。

新しいメンバーが参加した際、コードを隅々まで読まなくてもドキュメントを読めばプロジェクトの全体像や過去の意思決定(なぜこの技術を採用したのか等)をキャッチアップできます。

そして何より「AIに正確に指示を出すため」という明確な目的が、これまで後回しにされがちだったドキュメント作成のモチベーションになっていると感じています。

結果として、ドキュメント駆動開発は「AIのため」ではなく、「チーム全体の開発プロセスを最適化する手法」であるという結論に至り、この開発手法を導入しています。

4. バックエンド開発 with AI

ここからはバックエンド/フロントエンドそれぞれでの取り組みを簡単に紹介します。

バックエンド開発はAIとの協業を成功させるために、まず「AIが迷わず、最高のアウトプットを出せる土台をどう作るか」という点から考え始めました。

4-1. AI駆動開発の3つの指針

AI駆動開発を始める前に、AIと開発するための3つの指針を考えました。

ドキュメント駆動

「3. ドキュメント駆動開発を軸にする」でもご説明した考え方です。AIのパフォーマンスはインプットされるコンテキストの質に大きく左右されます。人間とAIが同じドキュメントを「共通のコンテキスト」として参照することで、双方の認識のズレを防ぎ、精度の高い実装を目指しました。

テストファースト

AIは高速なコード生成は可能ですが、人間が見ればすぐに気づくような単純なミスや、考慮漏れなどのハレーションを起こすことが少なくありません。AIの生成物を盲信せずに必ずテストで品質を担保することを目指しました。

DDDとレイヤードアーキテクチャ

ドメイン駆動設計(DDD)の考え方を取り入れ、責務を明確に分離するレイヤードアーキテクチャを採用しました。これにより、各レイヤーのインターフェースを定義すれば、AIが実装を並列で進められるようになることを目指しました。

4-2. Claude Slash Commandsを利用した標準化

上記の指針を、日々の開発作業に落とし込むためにClaude Slash Commandsをベースとした開発フローを実践しました。AIへの指示が個人のプロンプトスキルに依存するのを防ぎ、「誰がやっても同じ品質」の開発の「型」を作りました。

コマンド 目的 実現される効果
implrpc RPCエンドポイントの実装 ドキュメント存在確認を強制(仕様書がないと実装不可)
• テスト駆動開発(TDD)の徹底
• analyze → 人間実装 → implementの段階的実装
commit-ready コミット前の品質チェック • モック生成・フォーマット・Lint・テストを順次実行
• エラーが完全に解決するまで継続的にAIが修正
• コミットされるコードの品質を一定以上に保証
create-pr PR作成の自動化 • PRテンプレートに従った一貫性のあるPR作成
• 修正内容に適したPRタイトル・説明の自動生成
• セルフレビューコメントの自動追加

implrpcコマンド

RPCのエンドポイントを実装するためのコマンドです。実行時にまず対応するRPC仕様書の存在を確認し、「ドキュメントをもとに必ず開発する」というルールをコマンドで強制する仕組みです。またドメインレイヤーや各レイヤーを繋ぐInterfaceは人間が主導して実装することでAIによるハレーションを減らすようにしています。

# Implement RPC

**このコマンドの目的**: テスト駆動開発)手法に従って新しいRPCエンドポイントを実装します。

## Overview

Implement a new RPC endpoint following strict TDD (Test-Driven Development) methodology.

⚠️ **IMPORTANT NOTICE** ⚠️

Always carefully consider before executing commands from this file. Before implementation, verify:
- The RPC specification is clearly defined
- You understand the impact on the existing codebase
- You understand the Test-Driven Development process

## Arguments

- `rpc_name` (required): The name of the RPC method to implement
- `phase` (optional): The execution phase - "analyze" (default) or "implement"

## Prerequisites (MANDATORY)

**→ For detailed implementation standards, refer to the following sections in `coding-standards.md`:**
- RPC Implementation Standards
- Documentation Standards
- Test-Driven Development (TDD)

### Mandatory Pre-implementation Checklist:

1. **Verify RPC Specification**
2. **Study Project Documentation**
3. **Validate Proto Definition**
4. **Reference Existing Implementations**
5. **Check Auto-generated Files**

## Execution Modes

This command operates in two phases:

### Phase: "analyze" (Default)
When executed without phase argument or with `phase=analyze`:
- Analyzes the proto definition
- Provides guidance and templates for human implementation
- Shows examples of interfaces and tests to write

### Phase: "implement"
When executed with `phase=implement`:
- Reads human-written code from Phase 1
- Executes three parallel implementation tasks
- Generates complete implementation based on interfaces

## RPC Implementation Specification

Based on the provided `rpc_name` argument, the command will:

### RPC Details
- **RPC Name**: `{rpc_name}`
- **Functionality**: {Analyze proto definition to determine functionality}
- **Proto Messages**: {Extract Request/Response types from proto definition}

## Implementation Process

### When phase="analyze" (First Execution)

The command will:

1. **Review Documentation** (MANDATORY)
2. **Analyze Proto Definition**
3. **Generate E2E Test Implementation**
5. **Provide Implementation Guidance**
6. **Output Templates and Implementation**
   **E2E Test Implementation** (`test/e2e/{snake_case_rpc_name}_test.go`):

### Human Implementation Phase (Between Commands)

**After reviewing the analysis and AI-generated E2E tests, humans should implement:**

1. **Layer Interfaces**
2. **Domain Layer** (if new domain concepts are needed)

### When phase="implement" (Second Execution)

**After human implementation is complete, the command will:**

1. **First, review all documentation** (CRITICAL)
   - **MUST** re-read docs to ensure implementation follows project standards
   - **MUST** verify patterns match architecture documentation
   - **MUST** check RPC specification in `docs/03-api-reference/{snake_case_rpc_name}.md`
   - **MUST** follow patterns from `docs/architecture/` and `docs/technical/`
   - **MUST** ensure all implementations align with documentation

2. **Then execute parallel tasks following strict TDD methodology:**

#### Parallel Task 1: Interface Layer Implementation (RPC Handler) - TDD Process
**MANDATORY TDD Steps:**
1. **FIRST: Write failing unit tests**
2. **SECOND: Implement minimal code** to make tests pass (green phase)
3. **THIRD: Refactor** while keeping tests green

#### Parallel Task 2: UseCase Layer Implementation - TDD Process
**MANDATORY TDD Steps:**
1. **FIRST: Write failing unit tests**
2. **SECOND: Implement minimal code** to make tests pass (green phase)
3. **THIRD: Refactor** while keeping tests green

#### Parallel Task 3: Infrastructure Layer Implementation - TDD Process
**MANDATORY TDD Steps:**
1. **FIRST: Write failing unit tests**
2. **SECOND: Implement minimal code** to make tests pass (green phase)
3. **THIRD: Refactor** while keeping tests green

commit-readyコマンド

コミット前に必ず実行する、品質チェック用のコマンドです。mock生成、フォーマット、Lint、テストを順番に実行し、問題があればAIが可能な範囲で自動修正まで行います。これにより、コミットされるコードの品質を一定以上に保ちます。

# Commit Ready

**このコマンドの目的**: コードをコミットする前に、フォーマット、リント、テストのチェックを実行してコードベースを準備します。

## Overview

Prepare the codebase for commit by running format, lint, and test checks. This command runs the essential pre-commit checks to ensure code quality and correctness before committing changes.

## CRITICAL REQUIREMENTS

**IMPORTANT**: This command MUST execute ALL of the following commands in order:
1. `make gomock`
2. `make fmt`
3. `make lint`
4. `make test`

**MANDATORY**: The command MUST continue fixing all lint and test errors until they are completely resolved. Do not stop at the first error - continue iterating and fixing issues until all checks pass successfully.

## What this command does

1. **Generate mocks** (`make gomock`) - MUST be run first to regenerate all mock files ensuring they're up to date with current interfaces
2. **Format code** (`make fmt`) - Formats all Go code using goimportz and gofumpt
3. **Lint code** (`make lint`) - Runs golangci-lint to check for code quality issues
4. **Check test coverage** (`make coverage`) - Generates test coverage report and ensures adequate coverage
5. **Verify test implementation** - Checks that all layers have proper tests:
6. **Run tests** (`make test`) - Executes all tests with race detection

## Execution Order and Error Handling

The commands are executed in this specific order:
1. `make gomock` - ALWAYS runs first to ensure mocks are up to date
2. `make fmt` - Formats the code (including newly generated mocks)
3. `make lint` - Checks code quality
   - If lint errors are found, fix them and re-run `make lint`
   - Continue fixing and re-running until all lint errors are resolved
4. `make coverage` - Checks test coverage
5. `make test` - Runs all tests
   - If test failures occur, fix them and re-run `make test`
   - Continue fixing and re-running until all tests pass

**IMPORTANT**: Do NOT stop at the first error. Keep fixing issues and re-running the failed command until it passes, then continue with the next command.

## Prerequisites

- All source code files should be saved
- Docker should be running (for Spanner emulator during tests)
- No ongoing file modifications

## Output

The command will:
- Show formatting results
- Display any lint issues that need to be fixed
- Display test coverage report with percentages per package
- Identify any missing tests or low coverage areas
- Run the full test suite and report results
- Indicate if the code is ready for commit

## Exit behavior

- If any step fails, the command will stop and show the error
- Only when all checks pass is the code considered commit-ready
- Fix any reported issues before attempting to commit

## Common issues and solutions

### Mock generation issues
- **Outdated mocks**: If interfaces have changed but mocks haven't been regenerated, tests will fail
- **Missing mocks**: New interfaces need mock generation before tests can be written
- **Solution**: Always run `make gomock` before committing to ensure all mocks are up to date

### Lint errors
- **godot**: Comments should end with a period
- **gofmt**: File formatting issues (automatically fixed by `make fmt`)
- **goimports**: Import organization issues (automatically fixed by `make fmt`)

### Test failures
- Check test output for specific failure details
- Ensure all mocks are properly configured
- Verify database schema is up to date

### Coverage issues
- **Missing E2E tests**: Check that all RPC endpoints have corresponding tests in `test/e2e/`
- **Low use case coverage**: Ensure all use case methods have unit tests with both success and error scenarios
- **Missing converter tests**: Add tests for all conversion and validation functions
- **Repository coverage**: Verify integration tests cover all repository methods

### Format issues
- Usually auto-fixed by `make fmt`
- Ensure consistent indentation and import grouping

create-prコマンド

開発が完了した際にcommitからPRの作成までを行うコマンドです。PRのテンプレートを参照し、修正内容にあったPRを作成します。

# Create PR

**このコマンドの目的**: CLAUDE.mdのルールに従ってプルリクエストを作成し、レビューコメントを追加します。

## Overview

This Claude Code project command creates a PR following the rules in CLAUDE.md and adds a review comment.

## Functionality

1. Ensures commits follow Semantic Git commits convention using `npx git-cz`
2. Checks current branch changes using git commands
3. Creates PR following CLAUDE.md PR creation rules:
4. After PR creation, adds self-review comment:

## Implementation

This command is executed internally by Claude Code, not as a bash script. When invoked via `/create-pr`, Claude will:

1. Use `npx git-cz` for creating semantic commits if there are uncommitted changes
2. Run `git status`, `git diff`, and `git log` to analyze changes
3. Use `gh pr create` with proper template structure and semantic title
4. Add review comment using `gh pr comment`

## Commit Convention

All commits must follow Semantic Git commits format using `npx git-cz`:

- **feat**: A new feature
- **fix**: A bug fix
- **docs**: Documentation only changes
- **style**: Changes that do not affect the meaning of the code
- **refactor**: A code change that neither fixes a bug nor adds a feature
- **test**: Adding missing tests or correcting existing tests
- **chore**: Changes to the build process or auxiliary tools

PR titles should match the primary commit type and scope.

## Prerequisites

- Changes must be pushed to remote branch beforehand
- Must be executed from a branch other than main
- Ensure commits exist before execution
- GitHub CLI (`gh`) must be configured and authenticated

これらのコマンドを整備したことで、「AIにどう指示すればいいか迷う時間」がほぼゼロになりある程度実装の型を作ることができました。

4-3. コンテキスト管理と役割分担

またAIの能力を最大限に引き出すため、コンテキスト管理や役割分担も工夫しました。

ドキュメントとコンテキストの分離・整備

AIに与える情報は質だけでなく量も重要です。要件定義からDB設計・ADRなど、必要だと思ったドキュメントはまずAIにたたき台を作らせ、人間がレビューする方針で網羅的に整備しました。 またコンテキストファイルは役割を分け、AI向けのCLAUDE.mdはAIの理解度・精度が高い英語で、人間が主に参照するドキュメントは可読性を重視して日本語で管理しています。

AIと人間の役割分担

アプリケーションの核となるドメインロジックや、レイヤー間の繋がりを定義するインターフェース部分は人間が主体となって実装するようにしています。重要な部分の実装をAIに全て任せてしまうとどれだけ適切にコンテキストを与えることができていたとしても、手戻りが大きくなるリスクがあるためです。

5. フロントエンド開発 with AI

(この章はFEのAI駆動開発を推進したエンジニアのhan sanに寄稿いただきました。)

フロントエンド開発は、バックエンドとはまた違ったアプローチでの開発を実施しました。

5-1. 目的ごとのツール群とMCPサーバーの活用

フロントエンドではより細かく用途によるAIツールの使い分け、MCPの活用を行いました。

  • メインツール: Claude (Code/Desktop)
  • 自動化・サブツール: GitHub Copilot Agent
  • 壁打ち・相談役: Gemini Pro

特に開発効率を大きく向上させたのが、MCPサーバーの活用です。

  • context7: OSSのドキュメントなどを参照させ、Mantine UIのようなライブラリの正しい使い方をAIに学習させる。
  • playwright: AI自身に画面を操作・確認させ、E2EテストやUIの動作確認を行わせる。
  • figma: デザインデータからUIプロパティを直接取得させ、デザインと実装の乖離を防ぐ。

5-2. 2つの開発パターン

日々の開発では、タスクの複雑さに応じてAIへの指示の出し方を2つのパターンで使い分けていました。

パターン1: シンプルなタスクは「直接指示」

「テキストのvariantを変えてください」や「実装に合わせてStorybookのケースを増やしてください」などは直接指示でも十分期待通りの結果を出せます。

パターン2: 複雑なタスクは「Opus 4で計画、Sonnet 4で実行」

一機能の実装計画を立てる場合はまず思考能力の高いClaude Opus 4に「フロントエンドのエキスパート」として詳細な実装計画を立て、その計画をClaude Sonnet 4に渡してコーディングを行います。 Opus 4 はセッションあたりのトークン上限が Sonnet 4 より小さいため、コスト効率の観点から「計画=Opus 4」「実装=Sonnet 4」と使い分けています。計画の精度が高ければ、実装品質は Sonnet 4 で十分に担保できます。

5-3. AIと協働するための工夫

並列作業の実現

Git worktreeとClaude CodeなどのAI CLIツールを組み合わせることで、複数の、ターミナルセッションで作業を同時に進められることができます。これによってエンジニア一人で複数のタスクを分担処理でき、作業の効率化を図ることができました。

エンジニアがUIデザインまで担う自由度

UIライブラリで構築された管理画面の実装を行う際、Context7 MCPを活用することで、AIがUIライブラリの仕様を把握でき、一貫性のあるモダンなUIを迅速に作成することができました。

6. 見えてきた課題と今後取り組みたいこと

AI駆動開発を本格的に実践したからこそ、現実的な課題も見えてきました。ここでは、私たちが直面した主な課題と、今後どのような取り組みをしていきたいと考えているかについてお伝えします。

6-1. AIレビューの限界

私たちは当初、コードレビューもAIが行うことで開発がうまく回るのではないかと考えていました。しかし、1ヶ月半試行錯誤した上での正直な感想は、「どれだけpromptを改善しても、レビュアーとしてはまだ物足りない」というものでした。

以下は試したAIコードレビューの一例です。

AIツール 手法 結果
Github Copilot PRのレビュアーに入れることによる自動レビュー typoの修正や部分ごとの実装の修正提案はあるが、PRコード全体でのレビューは難しい。
Devin slackでのコミュニケーションによるレビュー PR全体でのレビューは可能だが、設計思想やドメインを理解した上でのレビューは難しい。
Claude Code Action カスタムプロンプトによるGHAを使用した自動レビュー プロンプトのチューニングも行ったが、Devinとほぼ同じ結果であった。PR全体でのレビューは可能だが、設計思想やドメインを理解した上でのレビューは難しい。

AIはコーディング規約違反や単純なロジックミスといったレビューであれば問題なく対応できます。しかし、私たちがレビューで本当に求めているのはそこだけではありません。

  • この設計は、半年後の機能拡張に耐えられるか?
  • ビジネスのドメインルールを正しくコードに反映できているか?
  • パフォーマンス上の懸念や、よりシンプルな代替案はないか?

こういった背景知識や将来のプロダクト展開までを考慮した「設計の妥当性」に関するレビューは、やはり経験を積んだ人間のエンジニアが行う必要があると感じました。

この経験から、あくまで我々がトライした条件下の中ではありますが、人間によるコードレビューは品質を担保する上で今後も不可欠であると感じています。

6-2. コンテキストを十分に与えられない分野でのコード生成

AIは一般的なWeb開発の知識は豊富ですが、特定のデータベースでの最適化などはそのままでは難しいのではないかと感じました。

特にSpannerのような比較的新しいDBを扱う上で例えば、

  • interleaveで親子関係を設計したテーブルに対する、効率的なJOINクエリ
  • クエリの実行計画を考慮した、パフォーマンスの最適化
  • Spanner特有のトランザクション管理やインデックスのベストプラクティス

といった内容はAIが生成するコードだけでは不十分なケースが多くありました。

6-3. 「AIの実装しやすさ」と「人間のレビューしやすさ」のジレンマ

アーキテクチャの選定においても、課題を感じる部分がありました。例えば今回バックエンドで採用したレイヤードアーキテクチャは、責務が明確に分離されているため、AIに対して「このレイヤーを実装して」と指示を出しやすく、並列でのコード生成も可能という点で「AIフレンドリー」と感じたため採用しました。

しかしその一方でコード量が増加し、人間のレビュー負荷が高まるというデメリットも生じました。例えば一つの簡単な機能追加のために、各レイヤーの複数のファイルにまたがって変更が必要になり、レビュー時に全体像を把握するのが難しくなっていました。

AIの生産性を最大化するアーキテクチャと、人間が保守・レビューしやすいアーキテクチャをどう両立させるかは今後より実践を重ねていきたいと思っています。

6-4. 今後取り組みたいこと

今回AI駆動開発を実践してみて、これからチームとして取り組んでいきたいことがいくつか見えてきました。今後はチームとして特に下記2つを意識してAI駆動開発に取り組んでいきたいと考えています。

1つ目は、開発の専門領域を超えた動きをしていくことです。 フロントエンドとバックエンドそれぞれの領域でAI駆動開発の実践によってAIを利用した開発の型が定まり、AIを活用することで専門でない分野のキャッチアップも容易になってきています。

そのため「フロントエンドだから」、「バックエンドだから」と役割を限定せず、互いの領域をどんどん越境して助け合えるようにしていきたいと考えています。少数チームであってもAIを活用することでお互いの領域の手助けをし合えるチームにしていきたいです。

2つ目は、開発に閉じない動きをしていくことです。 今後はこれまで以上にエンジニアも企画や要件定義の段階からどんどん首を突っ込んでいきたいです。

AIと開発することでこれまでよりも確実に開発スピードは上がってくると思っています。そんな状況の中で、「どう作るか」だけでなく、「なぜ作るのか」、「何を作るのか」から一緒に考えていけるようにしていきたいです。

7. おわりに

それぞれの開発フェーズでAIと人間の役割をまとめてみました。

開発フェーズ AIの役割 人間の役割
設計・計画 アイデアの壁打ち、たたき台作成 要件定義、アーキテクチャの最終決定
実装 定型コードの生成、並列実装、翻訳 複雑なビジネスロジックの実装、設計判断
テスト 単体テストコードの生成、E2Eテストの実行 テストケースの設計、仕様に基づいた検証
レビュー Lint、コーディング規約の自動チェック 設計の妥当性評価、拡張性・保守性のレビュー
ドキュメント テンプレートからの生成、議事録の要約 仕様の明確化、全体像の記述

今回は私たちなりのAI駆動開発という取り組みを紹介させていただきました。この記事が同じようにAIと共に新しい開発の型を模索している皆さんの何かしらのヒントになれば幸いです。

Gaudiyでは私たちと新しいことに積極的に向き合って新しい型を作っていく仲間を積極的に募集しています!この記事を読んで、Gaudiyの開発スタイルに少しでも興味を持ってくださった方、ぜひ一度カジュアルにお話ししませんか?

ご応募、お待ちしています!

site.gaudiy.com

ECチームでの分散トランザクションの課題とOutboxV2の利用について

はじめまして。ファンと共に時代を進める、Web3スタートアップのGaudiyでエンジニアをしている miyamoto です。

Gaudiyでは、ファンコミュニティサービスの「Gaudiy Fanlink」にマイクロサービスアーキテクチャを採用しています。

マイクロサービスアーキテクチャは、サービスの独立性を高め、チームが自律的に開発を進められる強力なパラダイムです。しかし、その分散的な性質から、サービス間のデータ一貫性を保つことには特有の難しさが伴います。

この分散トランザクションにおいて、Gaudiyでは主に Outbox(Transactional Outbox Pattern) を利用していますが、ECチームである課題にぶつかり、その解決策としてGaudiyが独自に開発・改良したOutboxV2という新しい仕組みを導入しました。

今回のブログでは、Outboxの仕組みから、ECチームで生じた問題とその解決策としてのOutboxV2の導入までを詳しくご紹介していきます。マイクロサービスアーキテクチャの一事例として、ご参考になれば嬉しいです。

1. Outboxの仕組み

Outboxは、マイクロサービスアーキテクチャで、データベースの更新とメッセージの送信を確実に両立させるためのデザインです。これにより、サービス間のデータ一貫性を保ちます。

(出典元: microservices.io)

1-1.アトミックな書き込み

サービスは、自身のビジネスデータ(例: Orderテーブル)の更新と、送信したいメッセージの内容を「Outboxテーブル」に書き込む処理を、単一のデータベーストランザクション内で行います。 これにより、データベースの更新と「メッセージを送る」という記録が必ずセットで行われ、どちらか一方が失敗することがなくなります。

1-2.メッセージリレー

メッセージリレーと呼ばれる独立したプロセスが、Outboxテーブルを監視します。Gaudiyでは、テーブルの監視をpollingで実現していました。未送信のメッセージを見つけると、それをメッセージブローカーへ送信し、送信が完了したらテーブルからそのレコードを削除するか、処理済みとしてマークします。

1-3. Outboxテーブル:メッセージの状態管理

// OutboxEventテーブル
CREATE TABLE OutboxEvent (
  OutboxEventId STRING(64) NOT NULL,          -- 一意なイベントID
  Topic STRING(255) NOT NULL,                 -- Pub/Subトピック名
  Data outbox.task.Event NOT NULL,            -- Protocol Buffersのメッセージデータ
  CreatedAt TIMESTAMP NOT NULL OPTIONS (      -- レコード作成時刻
    allow_commit_timestamp = true
  ),
  SentAt TIMESTAMP OPTIONS (                  -- メッセージ送信完了時刻(NULLなら未送信)
    allow_commit_timestamp = true
  ),
  -- FallbackPoller用のシャードID(OutboxEventIdのハッシュ値を8で割った余り)
  -- FallbackPollerについては後述する
  ShardId INT64 NOT NULL AS (MOD(ABS(FARM_FINGERPRINT(OutboxEventId)), 8)) STORED,
) PRIMARY KEY(OutboxEventId);

-- インデックス:未送信メッセージの効率的な検索用
CREATE INDEX OutboxEventBySentAt ON OutboxEvent(OutboxEventId, SentAt);
CREATE INDEX OutboxEventByShardIdSentAtCreatedAt ON OutboxEvent(ShardId, SentAt, CreatedAt);

2. ECサイトの裏側で起きていた「コインが買えていない?」問題

ECチームでは、決済システムや、デジタルグッズ・通常の商品を販売するストア機能の開発を担当しています。 このストアでは、クレジットカードなどの現金決済のほかに、独自の「コイン」でも支払いが可能です。このコインは現金で購入できます。

2-1. どんな問題が起きていたか?

ユーザーがコインを購入する際、複数のマイクロサービスをまたいで行われるため、データの整合性を保つためにOutboxを利用していました。

しかし、この方法では、データベース(Spanner)に「処理は完了したか?」と確認(polling)するために最大で5秒以上かかってしまうことがありました。5sというのは、Gaudiyでpollingするためにデータベース負荷的に許容できる最低間隔が5sだからです。

このタイムラグのせいで、ユーザーがコインを購入してもすぐには反映されず、「購入に失敗したのかな?」と勘違いして、もう一度コイン購入を試みるという問題が発生していました。

2-2. どのように解決したか?

この問題を解決するために、Gaudiyが独自に開発・改良した「OutboxV2」という新しい仕組みを導入しました。

このOutboxV2によって、コイン購入後のタイムラグを短縮し、ユーザーが「コインが買えていない」と誤解することなく、スムーズに買い物ができるようになりました。

次から、OutboxV2について詳しく説明していきます。

3. OutboxV2とは

(便宜上、この後は元々使われていたOutboxをOutboxV1と呼びます)

OutboxV2で最も重要な技術的要素は、Cloud SpannerのChange Streamsを活用したリアルタイムなデータ変更検知です。従来のpolling方式と比べて、劇的な処理時間短縮を実現しています。

3-1. Change Streamsとは

Cloud SpannerのChange Streamsは、データベース内のデータ変更(INSERT、UPDATE、DELETE)をリアルタイムに近い形で捉え、ストリームとして外部に提供する機能です。

OutboxV2では、OutboxEventテーブルへの新しいレコード挿入を即座に検知し、メッセージ処理を開始します。

ref: https://cloud.google.com/spanner/docs/change-streams?hl=ja

// Change Streamsの設定(SQL)
CREATE CHANGE STREAM OutboxEventStream FOR OutboxEvent 
OPTIONS ( 
  value_capture_type = 'OLD_AND_NEW_VALUES',  // 変更前後の値を取得
  retention_period = '7d',                    // 7日間のデータ保持
  exclude_update = true,                      // UPDATE操作は除外
  exclude_delete = true                       // DELETE操作は除外
);
  • exclude_update = true:OutboxEventのSentAt更新は検知する必要がないため除外
  • exclude_delete = true:OutboxEventレコードは通常削除せず、履歴として保持

Cloud Spannerのchange streamsをOutboxテーブルに設定し、データの変更をリアルタイムに近い形でメッセージリレーがsubscribeし、メッセージブローカーへメッセージをpublishします。こうすることで、OutboxV1では実現できなかった高速な分散トランザクションを実現することができました。

3-2. リアルタイム検知の仕組み

OutboxEventテーブルにレコードが挿入されると、Change Streamsが即座にイベントを発火します。このイベントをChangeStreamsWatcherが受信し、処理を開始します。

(以下のtype ChangeRecordについてはこちらを参照してください)

type ChangeStreamsWatcher struct {
    cs       *changestreams.ChangeStreams  // Change Streamsクライアント
    client   database.SpannerClient        // Spannerクライアント
    kch      channel.Channel               // イベントID送信用チャネル
    done     chan struct{}                 // 終了シグナル
}

type Result struct {
    PartitionToken string          `spanner:"-" json:"partition_token"`
    ChangeRecords  []*ChangeRecord `spanner:"ChangeRecord" json:"change_record"`
}

// Change Streamsのイベントを監視・処理
func (cs *ChangeStreamsWatcher) Watch(ctx context.Context, result *Result) error {
    select {
    case <-cs.done:
        cs.Stop()
        return nil
    default:
        // 各変更レコードを処理
        for _, cr := range result.ChangeRecords {
            if cr.HasDataChangeRecords() {  // データ変更イベントの場合
                for _, dcr := range cr.DataChangeRecords {
                    // 重要:分散ロックによる重複処理防止
                    locked, err := cs.Lock(ctx, dcr.ServerTransactionID)
                    if err != nil {
                        return err
                    }
                    if locked {
                        // 既に他のインスタンスが処理中の場合はスキップ
                        return nil
                    }

                    // 変更されたOutboxEventのIDを取得
                    for _, key := range dcr.Mods.GetValues("OutboxEventId") {
                        if eventID, ok := key.(string); ok {
                            // 処理用チャネルにイベントIDを送信(ここがリアルタイム!)
                            cs.kch.Input(eventID)
                        }
                    }
                }
            }
        }
    }
    return nil
}

3-3. 分散ロック機構による重複処理防止

マイクロサービス環境では、同じOutboxEventが複数のインスタンスで同時に処理される可能性があります。インスタンスごとに未処理のOutboxイベントを処理するsubscribeが実行されているので、同じレコードを処理しようとしてトランザクションの解放待ちにならないようにServerTransactionIDを使った分散ロック機構を実装しています。

SpannerのServerTransactionIDは、Cloud Spannerが生成するデータベースレベルで一意なトランザクション識別子です。トランザクションを一意に識別するため、これをロックキーとして使用することで確実に重複処理を防げます。

<OutboxLockテーブル:分散ロックを管理>

CREATE TABLE OutboxLock (
  TransactionId STRING(28) NOT NULL,          -- SpannerのServerTransactionID
  CreatedAt TIMESTAMP NOT NULL OPTIONS (      -- ロック取得時刻
    allow_commit_timestamp = true
  ),
) PRIMARY KEY(TransactionId),
-- 1日経過したロックレコードは自動削除(メモリリーク防止)
ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 1 DAY));
  • TransactionId(ServerTransactionID)をプライマリキーとすることで、同じトランザクションによる重複ロック取得を防止
  • ROW DELETION POLICYにより古いロックレコードが自動削除され、テーブルサイズが無制限に増加することを防ぐ
  • ロック期間は通常数秒〜数分程度だが、障害時の安全マージンとして1日間保持
// 分散ロック機構 - OutboxLockテーブルを使用
func (cs *ChangeStreamsWatcher) Lock(ctx context.Context, serverTxID string) (bool, error) {
    // OutboxLockテーブルにTransactionIDを挿入してロックを取得
    ms := []*spanner.Mutation{
        spanner.Insert("OutboxLock",
            []string{"TransactionId", "CreatedAt"},
            []any{serverTxID, spanner.CommitTimestamp},
        ),
    }
    
    _, err := cs.client.Apply(ctx, ms)
    if err != nil {
        // AlreadyExistsエラー = 既に他のインスタンスがロック取得済み
        if grpcerrors.IsAlreadyExists(err) {
            return true, nil  // ロック済みを示すフラグを返す
        }
        return false, fmt.Errorf("failed to acquire lock: %w", err)
    }
    
    // ロック取得成功
    return false, nil
}

3-4. 全体シーケンス

4. OutboxV2の導入結果と課題への対処

OutboxV1では、OutboxテーブルにレコードがINSERTされてからメッセージブローカーにメッセージをpublishするまでに、最長5000ms程度かかっていました。今回、OutboxV2を導入したことで、レコードがINSERTされてからメッセージがpublishするまでに200ms程度で済むようになり、分散トランザクションの大幅な時間短縮に繋げることができました。

一方で、OutboxV2は強力ですが、万能ではありません。Change Streamsはリアルタイム性に優れる一方で、イベントの伝達が保証されない(At-Most-Once)という特性があります。ネットワークの問題やごく稀なクラウド側の障害で、変更イベントをメッセージブローカーへ送信しそこなう可能性がゼロではありません。これにより、「Outboxテーブルへの書き込み」と「メッセージブローカーへのpublish」の間の整合性に課題が生じます。

そこで我々は、この課題を克服するために、Change Streamsのイベントを取りこぼした場合でも最終的な整合性を担保するリカバリー機構として、OutboxV1で利用していた定期的にOutboxテーブルを監視するポーリング処理(Fallback Poller)も併用しています。このハイブリッドなアプローチにより、リアルタイム性を享受しつつ、メッセージ伝達の最終的な到達保証(At-Least-Once)も実現しています。

5. まとめ

Gaudiyで新しく開発したマイクロサービスアーキテクチャについて紹介させていただきました。

今回は「コインの配布ができていない」という誤解をユーザーに与えないように、リアルタイムに近い処理ができるOutboxV2を採用しました。OutboxV1に比べて分散トランザクションが高速になる一方で、アトミック性を確保できないという課題がありましたが、リカバリー機構を導入することでアトミック性を担保するようにしました。分散トランザクションを扱う上で、どうしても高速に処理したいというシーンはさまざまなプロダクトで発生するかと思うので、ぜひ参考にしてみてください。

GaudiyのECチームではこれから、決済基盤の追加開発や海外展開へ向けてのストア開発などやることが山積みの状態です。特に、巨大なIPを抱えての海外展開はこれからという段階で、ハードな開発がまさに始まろうとしています。

決済、ストア、物流など考えることが山積みでたくさんの困難に挑戦できる機会があるので、興味がある方はぜひお話しましょう!

site.gaudiy.com

site.gaudiy.com

Appendix

Outboxについて microservices.io

以下のメルカリさんの記事がわかりやすいです。 engineering.mercari.com

ファン国家のために"人類の失敗"を代替する。Gaudiyがデータサイエンスと機械学習をやっていく話

はじめまして。ファンと共に時代を進める、Web3スタートアップ Gaudiy の tatsuki (@tatsukiiine)と申します。

Gaudiyは、誰もが好きや夢中で生きられる社会「ファン国家」の創造をビジョンに掲げ、その実現をエンタメ領域から目指しています。

note.com

この「ファン国家」は、単一の巨大なコミュニティというよりは、多様な価値観や熱量をもっている無数の「マイクロコミュニティ」が相互に繋がり合うネットワークのようなものです。それは、情熱、創造性、ビジネス、テクノロジー、そして多くの人々の相互作用が渦巻く、複雑でダイナミックなエコシステムになるはずです。

しかし、このようなマイクロコミュニティが中心となるエコシステムの運営は、よく行われるプロダクト運営の手法では太刀打ちできないと考えています。

そこで本記事では、そんな課題に対する解決手段として、社会シミュレーションの技術と、それを支えるデータサイエンス・機械学習について書いていこうと思います。

意図と結果はよくずれる

まず前提として、社会をある結果に導くべく行動し、想定しない結果をもたらしたといった事象は、歴史上無数に存在します。

たとえば、コブラ効果はかなり擦られている話ではありますが、これは史実ではなく、経済学を説明するためにホルスト・ジーベルトにより導入された寓話、というのが真実に近いそうです。もちろんコブラ効果で括られる実話は数多く存在します。

インドを統治していた英国のインド総督府は、デリーにおける多くの毒ヘビ特にコブラの害を脅威と看做し[3]、コブラの死骸を役所に持ち込めば報酬を与えることにした。

最初のうちは報酬目当てに多くの蛇が捕獲されたので巧くいくと思われていたが、蛇の死骸を多く持ち込めば収入が多くなるのなら蛇を捕獲するよりは蛇を飼って増やせば良いと目先の利く連中がコブラの飼育を始めてしまうことになった。

蛇を減らす目的の筈が反って蛇を増やす原因になったことを重く見て、この施策は取り止めになった。

この結果報酬目当てに繁殖していたコブラが野に放たれ、コブラの数は施策が行われる以前よりも増加してしまった。一見正しそうな問題解決策は、状況をさらに悪化させた[2][4]

ja.wikipedia.org

また少しスケールが小さくなりますが、ストライサンド効果も類似の例として挙げられます。 ja.wikipedia.org

僕はこの手のうんちくを収集するのが好きなのですが、どの例をとっても想定通りいかなかった介入者の哀愁が垣間見えておもしろいです。

複雑なエコシステム

このような意図と結果の乖離には様々な要因が絡んでいますが、ここでは2つに着目したいと思います。

ひとつめは、社会が単なる部品の寄せ集めではなく、無数の要素が相互に影響し合い、非線形かつ予測不能な振る舞いを見せる複雑系であるということ。

こうした複雑系において特に注目されるのが、完全な秩序と無秩序なカオスとの狭間、いわゆる「カオスの縁」と呼ばれる状態です。この領域では、系は硬直化することなく、かといって崩壊するほどの不安定さもなく、自己組織化や新たなパターンの創発、そして環境への適応が最も活発に行われるとされています。

このような状態は、創造性や革新性が生まれやすい、社会のあるべき姿であるといえますが、絶妙なパラメータの調整が必要であり、狙って実現することは困難を極めます。

www.kodansha.co.jp

ふたつめに、そのシステムを構成し、ときに介入しようとする我々人間自身も、単純ではない「複雑な合理性」の中で動いているということ。

この点は、ノーベル経済学賞を受賞したハーバート・サイモンが提唱した「限定合理性」の概念によって深く掘り下げられています。人間は完全な情報や無限の計算能力を持つわけではなく、自身の認知能力の限界の中で意思決定を行っており、必ずしも常に「最適な」選択をするわけではない。むしろ、利用可能な情報の中から満足のいく結果をもたらしそうな選択肢を探索し、決定に至るという、より現実的なプロセスを辿ります。

ja.wikipedia.org

さらに、我々の意思決定は、経験則(ヒューリスティクス)に頼る一方で、それはいわゆる「認知バイアス」を生む原因ともなり得ます。たとえば、自分の信念を支持する情報を優先的に集めてしまう確証バイアスや、最初に提示された情報に過度に影響されるアンカリング効果などが知られています。

このように、人間の合理性は、認知的な制約や心理的な傾向によって、多層的に「複雑」なものとなっているのです。

高速なエコシステム

またIPコンテンツのマイクロコミュニティにおいては、システムが変化するその時間的速度についても言及すべきことがあります。それは、IPコンテンツのライフサイクルが早まりつつあるという言説です。

『コンテンツビジネスのデザイン』 https://www.unijapan.org/producer/pdf/producer_304.pdf

以前までは、製作委員会方式やメディアミックス戦略への言及が多かったようですが、近年ではさらに、グローバルプラットフォームによる流行の高速化や推薦アルゴリズムへの適合によるショート化が要因として加わり、そのスピードはますます早くなっています。(動画コンテンツを主題としていますが面白い議論があります)

www.cogitatiopress.com

ここで言いたいことは、「スピードを遅めよう」ということではありません。

クリエイターがしなければならない重要な意思決定が、コンテンツのライフスパンが短縮されたことにより、短いタームで押し寄せてくる状況にある、ということです。

意思決定のためのAgent Based Modeling

この意思決定において、人類は、その質を科学で進化させてきました。基本となるのは、小学校でやった「対照実験」です。

アサガオの鉢を用意して、葉の一部をアルミニウムはくで覆って1日暗所で寝かせる。その後日光に十分当てた後に脱色してヨウ素液につけると、はくで覆っていた場所以外が青紫色になるというアレです。

この「比較して確かめる」というシンプルな知恵は、近代、かのR・A・フィッシャーによって統計的な厳密さを与えられ、RCT(ランダム化比較試験)、つまり現代でよく言うところのA/Bテストへと発展しました。

bookplus.nikkei.com

重要な意思決定で致命的なミスを犯さないためには、A/Bテストが有効です。

名だたるTech企業の成功は、A/Bテストを徹底的に活用し、データに基づいてサービスを改善し続けたことに依る部分も大きいです。ソフトウェアプロダクトの世界では、ある新機能を試したいと思ったら、ユーザーの半分を「アルミはくで覆い」、残り半分に新機能を提供して、どちらが良いかを比較検証することができます。

www.uber.com

しかしながら、IPコンテンツはそれとは異なります。なぜなら、一つ一つの施策は本番であり、短いライフスパンの中で意思決定が連続するからです。

またファンコミュニティは、個々のファンが様々な相互に繋がり合う世界です。そのため、半分のユーザにだけイベントを実施したり、グッズを販売することができず、実験をすることが難しいという特徴があります。

そんなIPコンテンツにも、もしかしたら実験環境が用意できるかもしれません。それは、LLMの進歩によって現実味を帯びてきた「Agent Based Modeling(ABM)」による社会シミュレーションです。

もし、IPエコシステムを構成するファンやクリエイターの振る舞いをリアルに再現できるAgentを作り出し、彼らが相互作用する仮想世界を構築できたなら、 そこは、現実では不可能な「実験」を心ゆくまで行える、理想的なテストベッドになるはずです。

www.nature.com

この「社会シミュレーション」という手段は、Gaudiy AI Teamが以前から追いかけているテーマでもあります。

実は、Generative Agentsの取り組みは、自分が元々やりたいと思っていたことにかなり近いんです。それは、社会のシミュレーションを実現すること。金融のキャリアのなかで「社会科学系の実験のしづらさ」を感じていて、社会の実現するパスがひとつしかないために検証ができないのはバグだと思っていました。 (中略) この問題は、Generative Agentsを使った社会のシミュレーションを実現できれば解決に近づきます。そのために、人間の感情までも再現できるようにしていきたいと考えています。

note.gaudiy.com

この記事を書いている最中にも、いくつかの大学とAmazonの共同研究チームからある論文が出ました。

ユーザペルソナからLLM Agentの母集団を生成し、サンプリングしてControl/Treatmentに割り振り、Amazon.comのサイドバーにデザイン差分を作る。現実でも同じように当ててみて、現実と仮想における反応の傾向を見たら概ね一致していたという内容です。(ただしアウトカムに至るまでのアクション回数などは大きく傾向が違うそうで、これも考察のしがいがあります)

あくまで複雑系ではなく、個人とプロダクトのインタラクションにおける集団としての傾向を一致させられるということですが、部分的にはすでに実現できる可能性が高いといえます。

arxiv.org

人類の偉大な発明やイノベーションは、常にそれ以前の人類の能力の限界を代替してきました。馬車が自動車に、算盤がコンピュータに。

僕たちは、社会シミュレーションによって、IPコンテンツにおける「実験できない」という制約、そしてそこから生まれる「あり得たかもしれない」失敗を代替したいと考えています。

社会シミュレーションとデータサイエンス

僕はGaudiyの「ファン国家」の創造というビジョンのために、Gaudiyにデータサイエンスチームを立ち上げることにしました。

前章までで述べたように、IPコンテンツとファンが織りなす複雑でダイナミックなエコシステムにおいて、より良い意思決定を行うためには、社会シミュレーションが有望なアプローチとなります。

特にファン国家においては、単に経済圏を機能させるだけでなく、個々のファン活動がコミュニティ全体のIPコンテンツ認知や消費へ与える正負の影響――経済学でいうところの「外部性」を捉え、それが当人のインセンティブとして適切に評価される仕組みも重要になります。

この点で、ファンひとりひとりの貢献を計測・可視化しうる社会シミュレーションは、ファン国家の基盤技術とも言えると考えています。

そして、この信頼性の高い社会シミュレーションを実現するためには、データサイエンスや機械学習の力が不可欠となります。

なぜなら、シミュレーションを実現するという目標は、突き詰めれば、従来のデータサイエンスおよび機械学習が長年取り組んできた核心的な技術課題と深く重なり合っているからです。具体的には、以下の3つの技術要素が不可欠だと考えています。

image: Flaticon.com

第一に、「解釈」の技術。シミュレーションの根幹をなすAgentが現実の人間の複雑な振る舞いを、どれだけ忠実に再現できるかが肝です。

そのためには、人間の行動を深く「解釈」する能力が求められます。効果検証や因果推論の技術をベースに、心理学・社会学などを取り込んだMixed Methodsのような形が望ましいと考えています。

正直なところ、因果推論 x Mixed Methodsの全体像はまだ描けていない(業界でのコンセンサスもまだ薄い)ですが、いわゆる「定性リサーチ」の部分については、LLMを使ったプロダクトが台頭してきており、これから熱い領域になると思っています。

wondering.com

第二に、その解釈に基づいて未来を「予測」する技術。個体としてのAgentの行動(e.g. あるファンが次にどんなコンテンツに興味を持つか)から、それらが相互作用した結果として創発するマクロな現象(e.g. 特定のIPに関する話題がコミュニティでどれだけ拡散するか、市場全体のトレンドがどう変化するか)まで、様々なレベルでの予測が求められます。

個体レベルについては、短期的なコンバージョンの推定から始め、サロゲートインデックスなどを経由して長期指標の予測へと移っていくことになると思われます。

developers.cyberagent.co.jp

コミュニティレベルになると、相互作用を加味した予測が必要となるため、集団としての振る舞いから始めるのが良いと思います。グラフニューラルネットワークなどが先駆けとなっていると思われるので、そのキャッチアップに励んでいます。

そして第三に、予測に基づいてコミュニティを健全な成長へ導くための「最適化」の技術。これは画一的なトップダウン制御を意味せず、前述の「カオスの縁」のようなダイナミクスにより、全体のポテンシャルを最大化することを目指していきます。

これは冒頭のGaudiy CEOのnoteでも触れられていますが、人の多様性(参加人数)とコラボレーションの深度を両立した社会に求められる、適応型の行政システムと考えていただくと良いかと思います。

wrl.co.jp

こちらも個体レベルから始め、情報推薦や強化学習の知見を活かしていきます。コミュニティレベルでは、近年最適化自体をモデル学習の対象とする、Learn-to-Optimizeなども来ていそうです。

academic.oup.com

これらの「解釈」「予測」「最適化」という技術的要求に応えるためには、多様な専門性を持つデータサイエンティストと機械学習エンジニアが集い、協働することが不可欠です。要求される技術スタックは広範であり、その多くは日進月歩で進化しています。

正直に言って、たとえば人間の感情や文化といった本質的に捉えにくい要素のモデル化のように、未だ確立された方法論が存在しない領域も多く含まれていると認識しています。我々自身も、常に最新の知見をキャッチアップし、未知の領域を自ら切り拓いていく必要があると思っています。

データサイエンスチームがいまからやること

ここまでの話を読んで、どんな夢想家だ。と思われてしまっているハズなので、最後に直近の話もさせてください。

僕自身は、どちらかというとリアリストです。ファン国家というGaudiyのビジョンを成し遂げるためにも、足元のアウトカムについて重めに考えています。やるべきことは大きく2つあります。

ひとつめは、Gaudiyのプロダクトと組織へのデータサイエンスのインストールです。

Gaudiyのプロダクトは、新機能開発によるシード期から、既存機能の磨き上げとUXの追求というグロース期へと移行すべき段階にきています。効果検証・レコメンデーション・時系列解析など、グロース期に必要なものを、まずはプロダクトと組織にインストールするところから始めたいです。

ふたつめは、既にGaudiyでGenerative AIを中心に活動してきたGaudiy AI Teamとのコラボレーションです。

ABMでの連携は今まで論じてきた通りではありますが、データサイエンスチームが持つ統計解析、情報推薦、予測モデリング、最適化といった強みと、AIチームが持つGenerative AIの技術を組み合わせることで、単独では実現できない大きな価値を生み出せると考えています。

note.com

かくいう僕自身も、PdMとしてAIプロダクトを鋭意開発中ですが、Gen AIの技術だけでプロダクトインすることの難しさを感じています。

特にビッグデータのハンドリングにおいては、従来のML・DSの技術を積極的に活用した方がいいです。例えば、RAGの検索精度がAIプロダクトそのものの性能に大きく影響することは開発者であれば骨身にしみるところですが、検索結果をRe-Rankingする部分に、精度とパフォーマンスの観点で従来のLearning-to-Rankは今再注目を浴びています。

www.elastic.co

情報の解釈に関しても、構造/非構造化データに関わらず、データ量が多い場合には教師なし学習のアプローチを使った圧縮が有効であると思います。

aclanthology.org

長くなってしまいましたが、これが僕たちが描く構想とその現在地になります。

人類の未来を、データサイエンスと機械学習、そしてAIの力で明るくしたい。この挑戦に、少しでもワクワクしてくれたなら、ぜひカジュアルに一度お話しさせてください。

Gaudiyには、正社員でも副業でも、どんな形からでもコミットできる企業文化があります。

site.gaudiy.com

special.gaudiy.com