はじめに
こんにちは!開発エンジニアのクリスです!
Google Cloud Next Tokyo 26 に参加してきました!今回は Day 1 の Google セッション「Python で挑む実践的 LLM アプリケーション開発: クリーン アーキテクチャによる堅牢な実装パターン」のレポートをお届けします。
生成 AI アプリって、デモまでは驚くほど早く作れるのに、いざ本番品質にしようとすると急に難易度が上がりますよね。プロンプトをちょっと変えたら挙動が壊れたり、ロジックがどんどん複雑になったり…。そんな「デモから本番品質への脱却」という壁に、クリーン アーキテクチャで挑む実践的なセッションでした!
セッション概要
タイトル:Python で挑む実践的 LLM アプリケーション開発: クリーン アーキテクチャによる堅牢な実装パターン
登壇者:
- 伊藤 裕一 氏(Google Cloud プロフェッショナル サービス アプリケーション開発コンサルタント)
日時・会場:Day 1(7 月 30 日)16:00 – 16:30 / Room 4(セッション ID:D1-APP-05)
公式カタログの紹介文は以下のとおりです。
生成 AI アプリ開発における「デモから本番品質への脱却」という壁。プロンプト変更による破損やロジックの複雑化に悩んでいませんか? 本セッションでは、数多くの顧客を支援した Google Cloud PSO の知見をもとに、Python でのクリーン アーキテクチャ適応パターンを解説します。LLM 特有の関心の分離や疎結合な実装など、保守性とテスト容易性を備えた堅牢なシステムを作る実践ノウハウをお届けします。

「高品質に作り直してくれない?」から始まった話

セッションはいきなり、登壇者ご本人が入社時に言われたという一言から始まりました。
AI エンジニアが開発した AI サービスを、高品質に作り直してくれない?
とんでもない無茶振りな話ですね!とご本人がおっしゃっていました(笑)。
プロトタイプとしては素晴らしく動いている。でも、そのまま本番に出すには足りないものが多すぎる。だから「作り直し」が必要になる。生成 AI が一気に広がったこの数年、いろいろな現場で起きている状況だと思います。
そこで示されたのが、このセッション全体を貫く一文でした。
AI 開発の仕事は、実は 8 割がソフトウェア エンジニアの仕事である。

具体的に挙げられたのが、こうした領域です。
- セキュリティ
- スケーラビリティ
- オブザーバビリティ
並べてみると、どれも生成 AI 特有の話ではありません。これまでの Web アプリやバックエンド開発でずっと向き合ってきたテーマそのものですよね。
つまり、AI アプリを本番品質にするうえで効いてくるのは、最新のモデルやプロンプトのテクニックよりも、これまで積み上げてきたソフトウェア エンジニアリングの基礎だということです。「AI だから特別」ではなく、「AI でも当たり前のことをやる」。ここが最初に提示されたのが、個人的にとても好みでした。
パラメータ設計 ― 気づけば 100 個を超えるパラメータ
では、実際に何から手を付けるのか。最初のテーマはパラメータ設計でした。

エージェント アプリを作っていると、設定すべきパラメータがとにかく多くなります。セッションで挙げられた例が、想像しやすくて秀逸でした。
- サブ エージェントが 10 個ある
- それぞれにパラメータが 5 〜 10 個ある
- これだけで 50 〜 100 個のパラメータになる
50 〜 100 個。しかもこれ、まだアプリ本体の設定は数えていない段階の話です。
さらに厄介なのが、変数の名前を決めるだけでも迷うという点。モデル名、温度、リトライ回数、タイムアウト、プロンプトのバージョン…。同じような設定が 10 個のエージェント分だけ並ぶので、命名がブレると一気に読めなくなります。「これ、どのエージェントの設定だっけ?」となった経験、皆さんもありませんか?

まずは YAML で構造化する
これに対する打ち手として、まず示されたのが YAML で環境変数を構造化することでした。
フラットな環境変数を大量に並べるのではなく、エージェントごと・機能ごとに階層を持たせて整理する。これだけで「どの設定がどこに効くのか」が構造として見えるようになります。
そして Pydantic で Config モデルにする
Config モデルを Pydantic で定義し、YAML から Config を生成するという形が推奨されていました。
セッション内では、実際に YAML から Config オブジェクトを組み立てるサンプル コードも紹介されていました。

Pydantic を選ぶ理由もはっきり語られていて、Python におけるデータ構造とバリデーションの事実上の標準だから、というものでした。確かに、最近の Python エコシステムで型付きの設定やスキーマを扱うとなると、まず名前が挙がるのは Pydantic ですよね。


ここが効いてくるのは、設定ミスの発見タイミングが変わるところだと思います。YAML をそのまま辞書として持ち回っていると、タイポや型の間違いは「その設定を実際に使う瞬間」まで気づけません。エージェントが 10 個あって、その中の 1 つだけ深いところで落ちる…というのが一番つらいパターンです。Config モデルにしておけば、起動時にまとめて弾けるわけですね。
補足:関数の引数も自動でバリデーションできる

補足として紹介されていたのが、Pydantic は関数の引数に対する自動バリデーションにも使えるという点です。
ただし、こちらは実行速度は落ちるというトレードオフもきちんと添えられていました。この「便利だけど遅くなる」という一言を省略しないところに、実案件を数多く支援してきた方の実感がにじんでいる気がします。全部に付ければいいというものではなく、境界となる部分に絞って使うのが良さそうですね。
疎結合化とテスト ― 家電 QA エージェントを例に
ここからがセッションの中心パートです。題材として使われたのが、家電商品の QA エージェントでした。
1 つのエージェントに全部やらせると、ハルシネーションしやすい
家電商品の問い合わせに答えるエージェントを作るとして、素直に考えると「全商品に対応する 1 つの QA エージェント」を作りたくなります。
ところが、商品は大量にあります。すべてを 1 つのエージェントに詰め込むと、ハルシネーションが起きやすくなるというのが現実でした。
そこで採られたのが、ドメインごとにエージェントを分散させるというアプローチです。
- テレビ QA エージェント
- エアコン QA エージェント
…といった形で、担当範囲を絞る。1 体に全知全能を求めず、扱う知識の範囲を狭くしてあげることで精度を保つわけですね。人間のサポート窓口でも、商品カテゴリごとに担当が分かれているのと同じ発想です。納得感があります。
分けたら分けたで、3 つの課題が出てくる

ただ、これで解決かというとそうではありません。分散させたことで、新しい課題が 3 つ出てきます。ここを正直に並べてくれたのがありがたかったです。
- テストがしづらい。エージェント フレームワークは手続き型のコードとは書き味が異なるため、ロジックを載せるほどテストが複雑になっていく。
- 量産の効率化。テレビ QA とエアコン QA は、中身がほとんど同じ。この「ほぼ同じもの」を増やしていく作業をどう効率化するか。
- テスト手段が乏しい。結局テストは 1 つだけ ― root エージェントを手動実行して目視で確認、という状態になりがち。
特に 3 つ目、耳が痛くないですか?(笑)エージェントを触り始めた頃って、どうしても「動かして、出力を眺めて、良さそうだから OK」になりがちなんですよね。でもそれだと、サブ エージェントが 10 個ある状態で回帰テストなんてとても回せません。
解決 1:複雑なことはエージェントに任せず、ヘルパー クラスに集約する
1 つ目の解決策は、責務の置き場所を変えることでした。

ADK のエージェントに複雑なことを任せるのではなく、ロジックをヘルパー クラスに集約する。そして、そのメソッドをエージェントに使わせる、という形です。
さらに、設計ルールとして親エージェントが子エージェントを作ることを禁止するという縛りも紹介されていました。
これ、地味に見えて効きますよね。生成の責務を外に出して構造を静的に保っておけば、「このアプリはこういう構成です」と図に描ける状態が維持できる。テスト以前に、まず読めるコードになります。
そして何より、ロジックが素の Python クラスに集まっていれば、そこは普通のユニット テストで守れるわけです。「AI 開発の 8 割はソフトウェア エンジニアの仕事」という冒頭のメッセージが、ここでちゃんと形になっていました。
解決 2:抽象ベース クラスを継承し、差分だけ実装する
2 つ目、「ほとんど同じエージェントをどう量産するか」への答えがこちらです。

Agent/AgentBuilder/Config というパスに置かれた抽象ベースクラス、これを継承して、差分だけを実装する。テレビ QA とエアコン QA で共通する部分はベース クラスに置き、違うところだけを子クラスで書く、という構成です。
解決 3:モックを使って、細かいパーツ単位でテストする
そして 3 つ目、「root エージェントの手動実行しかテストがない」問題への答えです。
方針は明快で、エージェントの細かなパーツに対して、モックでテストするというものでした。
具体的な方法として紹介されていたのが、Gemini 相当の SDK を自分で用意し、そのモック SDK に対してテストするというアプローチです。実際の API を叩かずに、期待どおりの呼び出しが行われているかを検証するわけですね。
みなさんが疑問に持つと思いますが、Gemini相当のSDKをどう用意するの?
しれっと語られたこの一言。
モックSDKも AI に作らせる。
確かに!そこは AI に任せて、人間は「何をテストすべきか」を考えることに集中する。AI アプリの開発現場らしい、実に合理的な割り切りだと思いました。
テストの粒度としては、こうした単位が挙げられていました。
- Test for prompt ― プロンプトが期待どおりに組み立てられているか
- Test for tool / callback ― ツールやコールバックが正しく呼ばれているか
この分け方、すごく実践的だと思いませんか? LLM の出力そのものを毎回評価しようとすると、どうしても不安定で高コストなテストになってしまいます。そうではなく、「入力の組み立て」と「呼び出しの制御」という決定的に検証できる部分を切り出して守る。
おすすめテクニック集
セッションの終盤では、すぐに使えるテクニックが 2 つ紹介されました。
1. Gemini の返り値を Pydantic オブジェクトにする

1 つ目は、Gemini の返り値を Pydantic オブジェクトとして受け取るというもの。理由がとても分かりやすかったです。
テキストは条件分岐できない。
言われてみればそのとおりで、自由文で返ってきた結果に対して if を書こうとすると、途端に文字列マッチのような脆いコードになってしまいます。「はい」「Yes」「承知しました」…と表現が揺れるたびに壊れる、あのパターンですね。
構造化されたフォーマットで受け取れれば、後続の処理は普通のオブジェクト操作になりますし、何よりテストしやすくなる。ここでも Pydantic が効いてくるわけです。設定の入口(YAML → Config)と、LLM の出口(レスポンス → オブジェクト)の両方を Pydantic で締めておく、という一貫した設計になっているのが気持ちいいですね。
2. Gemini への巨大な入力をキャッシュする
2 つ目はキャッシュの活用です。

前提として、巨大なインプット(例:PDF)をベースにした回答生成は、入力コストが重いという課題があります。マニュアルや仕様書のような大きなドキュメントを毎回丸ごと渡していると、そのぶんが毎回コストとしてのしかかるわけですね。
そこで、その巨大な入力をキャッシュしておき、キャッシュをベースに回答する。効果はコスト削減だけではなく、レイテンシの改善にもつながるとのことでした。
先ほどの家電 QA エージェントの例と重ねると、より腹落ちします。テレビ QA エージェントが参照するのはテレビのドキュメント群ですから、担当範囲を絞ったことでキャッシュ対象も自然と固定される。疎結合化がそのままキャッシュ効率にも効いてくるという関係になっているんですよね。設計判断が精度・保守性・コストの 3 方向に効いている、良い例だと思いました。
まとめ
今回のセッションのポイントを、3 つに整理してみます。
- AI 開発の 8 割はソフトウェア エンジニアの仕事。セキュリティ、スケーラビリティ、オブザーバビリティ ― 本番品質を決めるのは、AI 特有のテクニックではなく従来どおりのエンジニアリングである。
- 設定の責務は Config に集約する。サブ エージェント 10 体で 50 〜 100 個にもなるパラメータを、YAML で構造化し Pydantic の Config モデルとして受け取る。設定ミスは起動時に弾く。
- 挙動の責務はヘルパー クラスに集約し、継承とモックで支える。複雑なロジックはエージェントの外へ出し、Agent / AgentBuilder / Config の抽象ベース クラスを継承して差分だけ実装する。テストはモック SDK に対して prompt・tool / callback の単位で行う。
一言に凝縮するなら、設定と挙動の責務をそれぞれ集約し、継承とテストで支えるということですね。

さいごに
エージェント開発って、動くものを作るところまでは本当に速いんですよね。フレームワークが優秀なので、サブ エージェントを並べればそれらしいものはすぐできてしまいます。でも、そこから「何度も改修に耐えるもの」に育てようとした瞬間に、設定の山とテストできないコードが牙を剥いてくる…というのは、私自身も実感があるところです。
今回のセッションで一番刺さったのは、その答えが特別な何かではなかったことでした。責務を分ける、設定を型で守る、共通部分は継承にまとめる、外部依存はモックにする。どれも、これまでのアプリケーション開発でずっとやってきたことばかりです。
「AI 開発の 8 割はソフトウェア エンジニアの仕事」という冒頭の言葉が、最後まで一貫していたのが本当に見事でした。エージェント フレームワークの新しい書き方を覚えることよりも、これまで培ってきた設計の型をきちんと持ち込むこと。それが「デモから本番品質への脱却」の近道なんだと、改めて背中を押された気がします。
生成 AI アプリの本番化に悩んでいる方は、まず設定を Config に寄せるところから始めてみてはいかがでしょうか。パラメータが散らかっている状態を片付けるだけでも、見通しがかなり変わるはずです。ぜひ試してみてください!
最後までお読みいただき、ありがとうございました!