ADK を使用してプロトタイプからエージェントへ

1. 概要

AI を利用した構築はどこから始めればよいですか?ほとんどの場合、まず「このモデルは、私が考えている問題を実際に解決できるのか?」というシンプルな疑問から始まります。そこで役立つのが Google AI Studio です。あらゆるものを迅速にプロトタイピングできる場所です。キッチンのリフォームを考えていますが、Gemini が役に立つと思います。私はエンジニアですが、ゼネコンではありません。規制や設備など、考慮すべきことがたくさんあるため、何を尋ねるべきかわかりません。そこで、この問題を分解して、Gemini に非常に詳細なプロンプトを生成してもらい、全面的な改修計画を生成して、リフォームを視覚化してもらいましょう。ただし、ここから、実際にビジネスの拡大を支援するにはどうすればよいでしょうか?エージェントを入力してください。

エージェントは、AI モデルと対話して、エージェントが持つツールとコンテキストを使用して目標ベースの操作を実行する自律型プログラムです。真実に基づいた自律的な意思決定を行うことができます。

Agent Development Kit(ADK)

Agent Development Kit(ADK)は、AI エージェントの開発とデプロイ用に設計された、柔軟性の高いモジュラー フレームワークです。ADK は、複数の個別のエージェント インスタンスをマルチエージェント システム(MAS)に構成して、高度なアプリケーションを構築することをサポートしています。

ADK では、マルチエージェント システムは、多くの場合階層を形成するさまざまなエージェントが連携または調整して、より大きな目標を達成するアプリケーションです。このようにアプリケーションを構造化すると、モジュール性、専門性、再利用性、保守性の向上や、専用のワークフロー エージェントを使用した構造化された制御フローの定義など、大きなメリットが得られます。

作成するアプリの概要

プロトタイプ PROMPT からエージェントの構築に移行する準備はできましたか?キッチン リフォーム プロジェクトの提案書を作成するエージェントを作成します。このラボでは、次の作業を行います。

  1. ADK を使用して改修提案書を生成するシンプルなエージェントを構築する
  2. 生成されたリフォーム提案書を Cloud Storage バケットに保存する
  3. Cloud Shell とエージェントのウェブ出力でエージェントをテストする

要件

  • ブラウザ(ChromeFirefox など)
  • 課金を有効にした Google Cloud プロジェクト

2. 始める前に

プロジェクトを作成する

  1. Google Cloud コンソールのプロジェクト選択ページで、Google Cloud プロジェクトを選択または作成します。
  2. Cloud プロジェクトに対して課金が有効になっていることを確認します。詳しくは、プロジェクトで課金が有効になっているかどうかを確認する方法をご覧ください。
  3. また、このページをご覧になっていて、Google Cloud の利用開始と ADK の使用に役立つクレジットをご希望の場合は、こちらのリンクからクレジットをご利用ください
  4. こちらの手順に沿ってご利用ください。なお、このリンクは 2025 年 7 月 15 日までのみ有効です。
  5. このリンクをクリックして、Cloud Shell をアクティブにします。Cloud Shell で対応するボタンをクリックすると、Cloud Shell ターミナル(クラウド コマンドの実行用)とエディタ(プロジェクトのビルド用)を切り替えることができます。
  6. Cloud Shell に接続したら、次のコマンドを使用して、すでに認証が完了しており、プロジェクトに各自のプロジェクト ID が設定されていることを確認します。
gcloud auth list
  1. Cloud Shell で次のコマンドを実行して、gcloud コマンドがプロジェクトを認識していることを確認します。
gcloud config list project
  1. プロジェクトが設定されていない場合は、次のコマンドを使用して設定します。
gcloud config set project <YOUR_PROJECT_ID>
  1. Python 3.9 以降がインストールされていることを確認する

他の gcloud コマンドとその使用方法については、ドキュメントをご覧ください。

3. プロトタイプ

Google AI Studio に移動します。プロンプトの入力を開始します。次のプロンプトを入力してみます。

I want to renovate my kitchen, basically just remodel it. I don't know where to start. So I want to use Gemini to generate a plan. For that I need a good prompt. Give me a short yet detailed prompt that I can use.

右側のパラメータを調整して構成し、最適なレスポンスを得ます。

この簡単な説明に基づいて、Gemini はリフォームを開始するための非常に詳細なプロンプトを作成してくれました。つまり、Gemini を使用して、AI Studio とモデルからさらに優れた回答を得ています。ユースケースに基づいて、使用するモデルを選択することもできます。

Gemini 2.5 Pro を選択しました。これは思考モデルであるため、長文の分析や詳細なドキュメントの場合、出力トークンがさらに多くなります(この場合は最大 65,000 個)。Gemini の思考ボックスは、ネイティブの推論機能を備え、長いコンテキスト リクエストを受け取ることができる Gemini 2.5 Pro を有効にすると表示されます。

レスポンスのスニペットを以下に示します。

4e4361663df80964.png

AI Studio がデータを分析し、キャビネット、カウンタートップ、バックスプラッシュ、床材、シンク、まとまり、カラーパレット、素材の選択などのすべての要素を生成しました。Gemini は情報源も引用しています。

別のプロンプトを使用して、アイデアが実現する様子を確認してみましょう。

  1. 次のプロンプトをコピーして、プロンプト エディタに貼り付けます。
Add flat and circular light accessories above the island area for my current kitchen in the attached image.
  1. 現在のキッチンの画像を添付します(キッチンのサンプル画像を使用することもできます)。
  2. モデルを [Gemini 2.0 Flash Preview Image Generation] に変更して、画像生成にアクセスできるようにします。

次のような出力が得られました。

fb33e7b1f6560a0c.png

これが Gemini の力です。

動画の理解、ネイティブな画像生成、Google 検索による実際の情報へのグラウンディングなどは Gemini でしか構築できないものです。

AI Studio でこのプロトタイプを作成し、API キーを取得して、Vertex AI ADK の機能を使用して完全なエージェント アプリケーションにスケーリングできます。

4. ADK のセットアップ

それでは、「始める前に」セクションで有効にした Cloud Shell ターミナルに移動しましょう。

  1. 仮想環境を作成して有効にする(推奨)

Cloud Shell ターミナルで、仮想環境を作成します。

python -m venv .venv

仮想環境をアクティブにします。

source .venv/bin/activate
  1. ADK をインストールする
pip install google-adk

5. プロジェクトの構造

  1. Cloud Shell ターミナルで、目的のプロジェクトの場所にエージェント アプリのルート ディレクトリを作成します。
mkdir agentic-apps
  1. メイン ディレクトリ内に、現在のプロジェクト専用のフォルダを 1 つ作成します。
mkdir renovation-agent
  1. Cloud Shell エディタに移動し、ファイルを作成して次のプロジェクト構造を作成します(最初は空のファイルを作成します)。
renovation-agent/
        __init__.py
        agent.py
        requirements.txt
        .env

6. ソースコード

  1. init.py」に移動し、次の内容で更新します。
from . import agent
  1. agent.py に移動し、次のパスから次の内容でファイルを更新します。

agent.py では、必要な依存関係をインポートし、.env ファイルから構成パラメータを取得して、提案ドキュメントを生成して Cloud Storage バケットに保存する root_agent を定義します。Cloud Storage の手順では、store_pdf というツールを使用します。

注: 現在、PDF はフォーマットされていません。コミュニティ デベロッパーの PR に基づき、次のスニペットがここに含められています [テストされていません]。store_pdf メソッド内で自由に調整してください。

doc = SimpleDocTemplate(
        pdf_buffer,
        pagesize=letter,
        rightMargin=0.75 * inch,
        leftMargin=0.75 * inch,
        topMargin=0.75 * inch,
        bottomMargin=0.75 * inch
    )

    styles = getSampleStyleSheet()
    story = []

    # --- CUSTOM STYLES FOR HEADERS ---
    # Define a new style for section headers
    styles.add(ParagraphStyle(name='SectionHeader',
                              parent=styles['Normal'],
                              fontName='Helvetica-Bold', # Make it bolder
                              fontSize=14,               # Make it slightly larger
                              leading=16,                # Line spacing
                              spaceAfter=0.15 * inch,    # Space after the header
                              spaceBefore=0.25 * inch,   # Space before the header
                              textColor=black            # Ensure color is bright/black (default is usually black, but explicit is good)
                             ))

    # Define a style for the main document title
    styles.add(ParagraphStyle(name='DocumentTitle',
                              parent=styles['Normal'],
                              fontName='Helvetica-Bold',
                              fontSize=20,
                              leading=24,
                              spaceAfter=0.25 * inch,
                              alignment=TA_CENTER, # Center align the title
                              textColor=black
                             ))
    # ---------------------------------

    paragraphs_raw = pdf_text.split('\n\n')

    # Heuristic for the garbled line issue (as before, temporary)
    if paragraphs_raw and len(paragraphs_raw[-1]) < 50 and any(char in paragraphs_raw[-1] for char in ['io', 'og', 'al', 'op']):
         logger.warning("Detected potentially garbled last paragraph. Attempting to trim/omit.")
         paragraphs_raw[-1] = "11. Entire Agreement:\nThis proposal constitutes the entire agreement between the parties and supersedes all prior discussions and agreements."


    for i, para_text in enumerate(paragraphs_raw):
        para_text = para_text.strip()
        if not para_text:
            continue

        # Special handling for the main document title (PROPOSAL DOCUMENT)
        if i == 0 and "PROPOSAL DOCUMENT" in para_text.upper():
            p = Paragraph("PROPOSAL DOCUMENT", styles['DocumentTitle'])
            story.append(p)
            story.append(Spacer(1, 0.15 * inch)) # Add space after the title
            # Skip the rest of this initial block if it's just the title
            remaining_text_lines = para_text.splitlines()[1:]
            if remaining_text_lines:
                formatted_text = "<br/>".join(remaining_text_lines)
                p = Paragraph(formatted_text, styles['Normal'])
                story.append(p)
                story.append(Spacer(1, 0.1 * inch))
            continue # Move to the next paragraph

        # Check if the paragraph looks like a section header (e.g., starts with a number and dot or just bold text)
        # This is a heuristic and might need fine-tuning based on actual proposal content variability.
        is_section_header = False
        # Check for numbered sections (e.g., "1. Scope of Work:")
        if para_text.startswith(('1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.', '10.', '11.')):
            is_section_header = True
        # Check for Exhibit headers (e.g., "Exhibit A: Cabinet Design") or Roman numeral headings
        elif para_text.startswith(('Exhibit ', 'I.', 'II.', 'III.', 'IV.', 'V.', 'VI.', 'VII.')):
            is_section_header = True
        # Check for specific known headers
        elif para_text.strip().upper() in ["IN WITNESS WHEREOF,", "EXHIBITS:"]:
            is_section_header = True


        if is_section_header:
            p = Paragraph(para_text, styles['SectionHeader'])
            story.append(p)
            # No additional Spacer here, as SectionHeader style has spaceAfter
        else:
            formatted_text = para_text.replace('\n', '<br/>')
            p = Paragraph(formatted_text, styles['Normal'])
            story.append(p)
            story.append(Spacer(1, 0.1 * inch)) # Standard space after body paragraphs

    doc.build(story)

    pdf_buffer.seek(0)

    # Upload the PDF to GCS
    storage_client = storage.Client()
    bucket = storage_client.bucket(STORAGE_BUCKET)
    blob = bucket.blob(PROPOSAL_DOCUMENT_FILE_NAME)

    blob.upload_from_file(pdf_buffer, content_type="application/pdf")

    logger.info(f"Successfully uploaded PDF to gs://{STORAGE_BUCKET}/{PROPOSAL_DOCUMENT_FILE_NAME}")

except Exception as e:
    logger.error(f"Error writing text to PDF and uploading: {e}")
    raise
finally:
    if 'pdf_buffer' in locals():
        pdf_buffer.close()
return "Successfully uploaded PDF to GCS!!"
  1. Cloud Storage バケットがあることを確認する

これは、エージェントが生成する提案書を保存するためのものです。Vertex AI で作成したエージェント システムがアクセスできるように、作成してアクセス権をプロビジョニングします。手順は次のとおりです。

https://cloud.google.com/storage/docs/creating-buckets#console

バケットに「next-demo-store」という名前を付けます。別の名前を付ける場合は、.env ファイル(環境変数の設定ステップ)で STORAGE_BUCKET の値を更新してください。

  1. バケットへのアクセスを設定するには、Cloud Storage のコンソールに移動し、ストレージ バケットに移動します(この例では、バケット名は「next-demo-storage」です。https://console.cloud.google.com/storage/browser/next-demo-storage)。

[権限] -> [プリンシパルを表示] -> [アクセスを許可] に移動します。プリンシパルに「allUsers」、ロールに「Storage オブジェクト ユーザー」を選択します。

Make sure to not enable "prevent public access". Since this is a demo/study application we are going with a public bucket. Remember to configure permission settings appropriately when you are building your application.
  1. 依存関係リストを作成する

requirements.txt にすべての依存関係をリストします。これは repo からコピーできます。

単一エージェントのシステム ソースコードの説明

agent.py ファイルは、Agent Development Kit(ADK)を使用して、キッチン リフォームのマルチエージェント システムの構造と動作を定義します。主なコンポーネントを詳しく見ていきましょう。

エージェントの定義

ルート エージェント(オーケストレーター): proposal_agent

root_agent は、この単一エージェント システムのオーケストレーターとして機能します。最初のリノベーション リクエストを受け取り、リクエストのニーズに基づいて呼び出すツールを決定します。

root_agent はツールからのレスポンスを収集し、それらを組み合わせてユーザーに包括的なレスポンスを提供します。この例では、ツールは「store_pdf」の 1 つだけです。

7. データフローと主なコンセプト

ユーザーが ADK インターフェース(端末またはウェブ UI)を介してリクエストを開始します。

  1. リクエストは root_agent によって受信されます。
  2. root_agent はリクエストを分析し、必要に応じてツールにルーティングします。
  3. ツール「store_pdf」は、更新されたテキスト コンテンツを PDF ファイルに書き込み、Google Cloud Storage にアップロードするように設計されています。
  4. その後、レスポンスが root_agent に返されます。
  5. root_agent はレスポンスを組み合わせて、ユーザーに最終的な出力を提供します。

LLM(大規模言語モデル)

エージェントは、テキストの生成、質問への回答、推論タスクの実行を LLM に大きく依存しています。LLM は、エージェントがユーザーのリクエストを理解して応答する能力の背後にある「頭脳」です。このアプリケーションでは Gemini 2.5 を使用しています。

Google Cloud Storage

生成されたリフォーム提案書を保存するために使用されます。バケットを作成し、エージェントがアクセスするために必要な権限を付与する必要があります。

Cloud Run(省略可)

OrderingAgent は、Cloud Run 関数を使用して AlloyDB とのインターフェースをとります。Cloud Run は、HTTP リクエストに応答してコードを実行するサーバーレス環境を提供します。

AlloyDB

OrderingAgent を使用している場合は、注文情報を保存するように AlloyDB データベースを設定する必要があります。

.env ファイル

.env ファイルには、API キー、データベース認証情報、バケット名などの機密情報が保存されます。このファイルは安全に保管し、リポジトリに commit しないことが重要です。また、エージェントと Google Cloud プロジェクトの構成設定も保存します。通常、root_agent またはサポート関数はこのファイルから値を読み取ります。.env ファイルで必要な変数がすべて正しく設定されていることを確認します。これには、Cloud Storage バケット名が含まれます。

8. モデルの設定

エージェントがユーザー リクエストを理解して回答を生成する機能は、大規模言語モデル(LLM)によって実現されています。エージェントは、この外部 LLM サービスに安全な呼び出しを行う必要があります。これには認証情報が必要です。有効な認証がないと、LLM サービスはエージェントのリクエストを拒否し、エージェントは機能できません。

  1. Google AI Studio から API キーを取得します。
  2. 次の手順で .env ファイルを設定する際に、<<your API KEY>> を実際の API キーの値に置き換えます。

9. 環境変数の設定

  1. この リポジトリのテンプレート .env ファイルで、パラメータの値を設定します。この例では、.env に次の変数が含まれています。
GOOGLE_GENAI_USE_VERTEXAI=FALSE
GOOGLE_API_KEY=<<your API KEY>>
GOOGLE_CLOUD_LOCATION = us-central1 <<or your region>>
GOOGLE_CLOUD_PROJECT = <<your project id>>
PROJECT_ID = <<your project id>>
GOOGLE_CLOUD_REGION=us-central1 <<or your region>>
STORAGE_BUCKET = next-demo-store <<or your storage bucket name>>

プレースホルダを実際の値に置き換えます。

10. エージェントを実行する

  1. ターミナルを使用して、エージェント プロジェクトの親ディレクトリに移動します。
cd agentic-apps/renovation-agent
  1. すべての依存関係をインストールする
pip install -r requirements.txt
  1. Cloud Shell ターミナルで次のコマンドを実行して、エージェントを実行できます。
adk run .
  1. ADK プロビジョニングされたウェブ UI で実行するには、次のコマンドを実行します。

注: このコマンドは、エージェント プロジェクト フォルダの外から実行する必要があります。フォルダから 1 つ上の階層に移動してから実行してください。

adk web
  1. 次のプロンプトでテストします。
user>> 

Hello. Generate Proposal Document for the kitchen remodel requirement in a proper format that applies to a renovation contract. Remember this text will eventually be stored as a pdf file so make sure to have the formatting appropriate. I have no other specification.

11. 結果

コマンド adk run . の場合、結果は次のとおりです」

2703603a907329ae.png

ae56b38cc6da9afe.png

...

91452a4de933a75b.png

改修提案書ドキュメントが Cloud Storage バケットに作成されているかどうかを確認できます。

12. Cloud Run にデプロイする

  1. プロジェクトのルート フォルダ内に Dockerfile という名前のファイルを作成します。
cd agentic-apps/renovation-agent
  1. GitHub リポジトリからコンテンツをコピーする
https://github.com/AbiramiSukumaran/adk-renovation-single-agent/blob/main/Dockerfile

この Dockerfile ファイルにコピーします。

  1. 次のコマンドを使用して Cloud Run にデプロイします。
adk deploy cloud_run --project=abis-345004 --region=us-central1 --service_name=renovation-agent --app_name=renovation-app --with_ui .

以上です。デプロイが完了すると、ターミナルにエンドポイントが表示され、使用できるようになります。

13. クリーンアップ

この投稿で使用したリソースについて、Google Cloud アカウントに課金されないようにするには、次の手順を行います。

  1. Google Cloud コンソールで、[リソースの管理] ページに移動します。
  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

14. 完了

おめでとうございます!これで、ADK を使用してマルチエージェント アプリを作成し、操作することができました。マルチエージェント システムは、提案書の作成、許可証の確認、注文ステータスの追跡などのタスクを自動化することで、キッチンのリフォーム プロセスを効率化するように設計されています。各エージェントには特定のロールがあり、root_agent がアクティビティを調整して包括的なソリューションを提供します。このシステムは、LLM、Google Cloud サービス、場合によっては外部 API を活用して機能を提供します。プロダクトのドキュメントへのリンクはこちらです。