ADK mit multimodaler Tool-Interaktion : Teil 2 ( MCP-Toolset mit Tool-Callbacks)

1. 📖 Einführung

Im vorherigen Codelab haben Sie gelernt , wie Sie eine multimodale Dateninteraktion im ADK entwerfen. Als Nächstes sehen wir uns an, wie Sie mit dem MCP-Toolset eine multimodale Dateninteraktion mit dem MCP-Server entwerfen. Wir werden die Funktionen des zuvor entwickelten Agents für die Bearbeitung von Produktfotos erweitern, um kurze Videos mit dem Veo-Modell über den Veo-MCP-Server zu generieren.

In diesem Codelab gehen Sie schrittweise so vor:

  1. Google Cloud-Projekt und Basis-Agent-Verzeichnis vorbereiten
  2. MCP-Server konfigurieren, für die Dateidaten als Eingabe erforderlich sind
  3. ADK-Agent für die Verbindung mit dem MCP-Server vorbereiten
  4. Prompt-Strategie und Callback-Funktion zum Ändern von Funktionsaufrufanfragen an das MCP Toolset entwerfen
  5. Callback-Funktion für die Verarbeitung multimodaler Datenantworten aus dem MCP Toolset entwerfen

Architekturübersicht

Die gesamte Interaktion in diesem Codelab wird im folgenden Diagramm dargestellt.

93fe3107e0946ddd.jpeg

Voraussetzungen

  • Vertrautheit mit Python
  • (Optional) Grundlegende Codelabs zum Agent Development Kit (ADK)
  1. goo.gle/adk-foundation
  2. goo.gle/adk-using-tools

Lerninhalte

  • Kurzvideos mit Veo 3.1 erstellen – mit Prompt und Bild als Ausgangspunkt
  • Multimodalen MCP-Server mit FastMCP entwickeln
  • ADK für die Verwendung von MCP-Toolsets einrichten
  • Toolaufruf an MCP-Toolset über Tool-Callback ändern
  • Tool-Antwort aus dem MCP Toolset über Tool-Callback ändern

Voraussetzungen

  • Chrome-Webbrowser
  • Ein Gmail-Konto
  • Ein Cloud-Projekt mit aktiviertem Abrechnungskonto

In diesem Codelab, das sich an Entwickler aller Erfahrungsstufen (auch Anfänger) richtet, wird Python in der Beispielanwendung verwendet. Python-Kenntnisse sind jedoch nicht erforderlich, um die vorgestellten Konzepte zu verstehen.

2. 🚀 ( Optional) Workshop-Entwicklungsumgebung vorbereiten

Schritt 1: Aktives Projekt in der Cloud Console auswählen

Wählen Sie in der Google Cloud Console auf der Seite zur Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines (siehe oben links in der Console).

6069be756af6452b.png

Klicken Sie darauf, um eine Liste aller Ihrer Projekte aufzurufen, wie in diesem Beispiel:

dd8fcf0428ab868f.png

Der Wert, der durch das rote Kästchen angegeben wird, ist die PROJEKT-ID. Dieser Wert wird im gesamten Tutorial verwendet.

Die Abrechnung für das Cloud-Projekt muss aktiviert sein. Klicken Sie dazu links oben in der Leiste auf das Dreistrich-Menü ☰, um das Navigationsmenü aufzurufen, und suchen Sie nach dem Abrechnungsmenü.

db07810b26fc61d6.png

Wenn Sie unter dem Titel Abrechnung / Übersicht ( oben links in der Cloud Console ) das Google Cloud Platform-Testabrechnungskonto sehen, kann Ihr Projekt für diese Anleitung verwendet werden. Falls nicht, kehren Sie zum Anfang dieses Tutorials zurück und lösen Sie das Abrechnungskonto für den Testzeitraum ein.

45539d4ac57dd995.png

Schritt 2: Mit Cloud Shell vertraut machen

Für den Großteil der Anleitungen verwenden Sie Cloud Shell. Klicken Sie dazu oben in der Google Cloud Console auf „Cloud Shell aktivieren“. Wenn Sie zur Autorisierung aufgefordert werden, klicken Sie auf Autorisieren.

26f20e837ff06119.png

79b06cc89a99f840.png

Nachdem Sie eine Verbindung zu Cloud Shell hergestellt haben, müssen wir prüfen, ob die Shell ( oder das Terminal) bereits mit Ihrem Konto authentifiziert ist.

gcloud auth list

Wenn Sie Ihr privates Gmail-Konto wie im Beispiel unten sehen, ist alles in Ordnung.

Credentialed Accounts

ACTIVE: *
ACCOUNT: alvinprayuda@gmail.com

To set the active account, run:
    $ gcloud config set account `ACCOUNT`

Falls nicht, aktualisieren Sie Ihren Browser und klicken Sie auf Autorisieren, wenn Sie dazu aufgefordert werden. Möglicherweise wurde die Verbindung unterbrochen.

Als Nächstes müssen wir prüfen, ob die Shell bereits für die richtige PROJECT ID konfiguriert ist. Wenn im Terminal vor dem Symbol „$“ ein Wert in Klammern angezeigt wird (im Screenshot unten ist der Wert adk-multimodal-tool), gibt dieser Wert das konfigurierte Projekt für Ihre aktive Shell-Sitzung an.

10a99ff80839b635.png

Wenn der angezeigte Wert bereits korrekt ist, können Sie den nächsten Befehl überspringen. Wenn sie nicht korrekt ist oder fehlt, führen Sie den folgenden Befehl aus:

gcloud config set project <YOUR_PROJECT_ID>

Klonen Sie dann das Arbeitsverzeichnis der Vorlage für dieses Codelab von GitHub, indem Sie den folgenden Befehl ausführen. Das Arbeitsverzeichnis wird im Verzeichnis adk-multimodal-tool erstellt.

git clone https://github.com/alphinside/adk-mcp-multimodal.git adk-multimodal-tool

Schritt 3: Cloud Shell-Editor kennenlernen und Arbeitsverzeichnis der Anwendung einrichten

Jetzt können wir unseren Code-Editor für einige Programmieraufgaben einrichten. Dazu verwenden wir den Cloud Shell-Editor.

Klicken Sie auf die Schaltfläche Editor öffnen, um den Cloud Shell-Editor 168eacea651b086c.png zu öffnen.

Klicken Sie dann oben im Cloud Shell-Editor auf Datei > Ordner öffnen, suchen Sie nach dem Verzeichnis Nutzername und dann nach dem Verzeichnis adk-multimodal-tool und klicken Sie auf die Schaltfläche „OK“. Dadurch wird das ausgewählte Verzeichnis zum Hauptarbeitsverzeichnis. In diesem Beispiel ist der Nutzername alvinprayuda. Der Verzeichnispfad wird unten angezeigt.

8eb3f593141dbcbf.png

a4860f6be228d864.png

Ihr Cloud Shell-Editor-Arbeitsverzeichnis sollte nun so aussehen ( im Verzeichnis adk-multimodal-tool):

aa2edaf29303167f.png

Öffnen Sie nun das Terminal für den Editor. Klicken Sie dazu in der Menüleiste auf Terminal -> Neues Terminal oder verwenden Sie die Tastenkombination Strg + Umschalt + C. Dadurch wird unten im Browser ein Terminalfenster geöffnet.

74d314f6ff34965b.png

Ihr aktuelles aktives Terminal sollte sich im Arbeitsverzeichnis adk-multimodal-tool befinden. In diesem Codelab verwenden wir Python 3.12 und den uv-Python-Projektmanager, um das Erstellen und Verwalten von Python-Versionen und virtuellen Umgebungen zu vereinfachen. Das uv-Paket ist bereits in Cloud Shell vorinstalliert.

Führen Sie diesen Befehl aus, um die erforderlichen Abhängigkeiten in der virtuellen Umgebung im Verzeichnis .venv zu installieren.

uv sync --frozen

Sehen Sie sich die deklarierten Abhängigkeiten für diese Anleitung in der Datei pyproject.toml an. Sie lauten google-adk, and python-dotenv.

Jetzt müssen wir die erforderlichen APIs über den unten gezeigten Befehl aktivieren. Das kann etwas dauern.

gcloud services enable aiplatform.googleapis.com

Bei erfolgreicher Ausführung des Befehls sollte eine Meldung wie die unten gezeigte angezeigt werden:

Operation "operations/..." finished successfully.

Die Vorlagenstruktur für den Agent ist bereits im Verzeichnis part2_starter_agent des geklonten Repositorys enthalten. Für dieses Tutorial müssen wir sie zuerst umbenennen.

mv part1_ckpt_agent product_photo_editor

Kopieren Sie danach die Datei product_photo_editor/.env.example nach product_photo_editor/.env.

cp product_photo_editor/.env.example product_photo_editor/.env

Wenn Sie die Datei product_photo_editor/.env öffnen, sehen Sie Inhalte wie unten dargestellt.

GOOGLE_GENAI_USE_VERTEXAI=1
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_LOCATION=global

Anschließend müssen Sie den Wert your-project-id durch Ihre eigene Projekt-ID ersetzen. Jetzt sind wir bereit für den nächsten Schritt.

3. 🚀 Veo-MCP-Server initialisieren

Erstellen Sie zuerst das MCP-Dienstverzeichnis mit diesem Befehl:

mkdir veo_mcp

Erstellen Sie dann veo_mcp/main.py mit diesem Befehl.

touch veo_mcp/main.py

Kopieren Sie danach den folgenden Code in veo_mcp/main.py.

from fastmcp import FastMCP
from typing import Annotated
from pydantic import Field
import base64
import asyncio
import os
from google import genai
from google.genai import types
from dotenv import load_dotenv
import logging

# Load environment variables from .env file
load_dotenv()

mcp = FastMCP("Veo MCP Server")


@mcp.tool
async def generate_video_with_image(
    prompt: Annotated[
        str, Field(description="Text description of the video to generate")
    ],
    image_data: Annotated[
        str, Field(description="Base64-encoded image data to use as starting frame")
    ],
    negative_prompt: Annotated[
        str | None,
        Field(description="Things to avoid in the generated video"),
    ] = None,
) -> dict:
    """Generates a professional product marketing video from text prompt and starting image using Google's Veo API.

    This function uses an image as the first frame of the generated video and automatically
    enriches your prompt with professional video production quality guidelines to create
    high-quality marketing assets suitable for commercial use.

    AUTOMATIC ENHANCEMENTS APPLIED:
    - 4K cinematic quality with professional color grading
    - Smooth, stabilized camera movements
    - Professional studio lighting setup
    - Shallow depth of field for product focus
    - Commercial-grade production quality
    - Marketing-focused visual style

    PROMPT WRITING TIPS:
    Describe what you want to see in the video. Focus on:
    - Product actions/movements (e.g., "rotating slowly", "zooming into details")
    - Desired camera angles (e.g., "close-up of the product", "wide shot")
    - Background/environment (e.g., "minimalist white backdrop", "lifestyle setting")
    - Any specific details about the product presentation

    The system will automatically enhance your prompt with professional production quality.

    Args:
        prompt: Description of the video to generate. Focus on the core product presentation
                you want. The system will automatically add professional quality enhancements.
        image_data: Base64-encoded image data to use as the starting frame
        negative_prompt: Optional prompt describing what to avoid in the video

    Returns:
        dict: A dictionary containing:
            - status: 'success' or 'error'
            - message: Description of the result
            - video_data: Base64-encoded video data (on success only)
    """
    try:
        # Initialize the Gemini client
        client = genai.Client(
            vertexai=True,
            project=os.getenv("GOOGLE_CLOUD_PROJECT"),
            location=os.getenv("GOOGLE_CLOUD_LOCATION"),
        )

        # Decode the image
        image_bytes = base64.b64decode(image_data)
        print(f"Successfully decoded image data: {len(image_bytes)} bytes")

        # Create image object
        image = types.Image(image_bytes=image_bytes, mime_type="image/png")

        # Prepare the config
        config = types.GenerateVideosConfig(
            duration_seconds=8,
            number_of_videos=1,
        )

        if negative_prompt:
            config.negative_prompt = negative_prompt

        # Enrich the prompt for professional marketing quality
        enriched_prompt = enrich_prompt_for_marketing(prompt)

        # Generate the video (async operation)
        operation = client.models.generate_videos(
            model="veo-3.1-generate-preview",
            prompt=enriched_prompt,
            image=image,
            config=config,
        )

        # Poll until the operation is complete
        poll_count = 0
        while not operation.done:
            poll_count += 1
            print(f"Waiting for video generation to complete... (poll {poll_count})")
            await asyncio.sleep(5)
            operation = client.operations.get(operation)

        # Download the video and convert to base64
        video = operation.response.generated_videos[0]

        # Get video bytes and encode to base64
        video_bytes = video.video.video_bytes
        video_base64 = base64.b64encode(video_bytes).decode("utf-8")

        print(f"Video generated successfully: {len(video_bytes)} bytes")

        return {
            "status": "success",
            "message": f"Video with image generated successfully after {poll_count * 5} seconds",
            "complete_prompt": enriched_prompt,
            "video_data": video_base64,
        }
    except Exception as e:
        logging.error(e)
        return {
            "status": "error",
            "message": f"Error generating video with image: {str(e)}",
        }


def enrich_prompt_for_marketing(user_prompt: str) -> str:
    """Enriches user prompt with professional video production quality enhancements.

    Adds cinematic quality, professional lighting, smooth camera work, and marketing-focused
    elements to ensure high-quality product marketing videos.
    """
    enhancement_prefix = """Create a high-quality, professional product marketing video with the following characteristics:

TECHNICAL SPECIFICATIONS:
- 4K cinematic quality with professional color grading
- Smooth, stabilized camera movements
- Professional studio lighting setup with soft, even illumination
- Shallow depth of field for product focus
- High dynamic range (HDR) for vibrant colors

VISUAL STYLE:
- Clean, minimalist aesthetic suitable for premium brand marketing
- Elegant and sophisticated presentation
- Commercial-grade production quality
- Attention to detail in product showcase

USER'S SPECIFIC REQUIREMENTS:
"""

    enhancement_suffix = """

ADDITIONAL QUALITY GUIDELINES:
- Ensure smooth transitions and natural motion
- Maintain consistent lighting throughout
- Keep the product as the clear focal point
- Use professional camera techniques (slow pans, tracking shots, or dolly movements)
- Apply subtle motion blur for cinematic feel
- Ensure brand-appropriate tone and style"""

    return f"{enhancement_prefix}{user_prompt}{enhancement_suffix}"


if __name__ == "__main__":
    mcp.run()

Der folgende Code führt die folgenden Aktionen aus:

  1. Erstellt einen FastMCP-Server, der ein Veo 3.1-Tool zur Videogenerierung für ADK-Agents bereitstellt.
  2. Akzeptiert base64-codierte Bilder, Text-Prompts und negative Prompts als Eingabe.
  3. Erstellt asynchron 8‑sekündige Videos, indem Anfragen an die Veo 3.1 API gesendet und alle 5 Sekunden abgefragt werden, bis die Erstellung abgeschlossen ist.
  4. Gibt base64-codierte Videodaten zusammen mit dem angereicherten Prompt zurück

Für dieses Veo MCP-Tool ist dieselbe Umgebungsvariable wie für unseren Agent erforderlich. Wir können die .env-Datei also einfach kopieren und einfügen. Führen Sie dazu den folgenden Befehl aus:

cp product_photo_editor/.env veo_mcp/

Jetzt können wir mit diesem Befehl testen, ob der MCP-Server richtig ausgeführt wird:

uv run veo_mcp/main.py

Das Konsolenlog wird so angezeigt:

╭────────────────────────────────────────────────────────────────────────────╮
│                                                                            │
│        _ __ ___  _____           __  __  _____________    ____    ____     │
│       _ __ ___ .'____/___ ______/ /_/  |/  / ____/ __ \  |___ \  / __ \    │
│      _ __ ___ / /_  / __ `/ ___/ __/ /|_/ / /   / /_/ /  ___/ / / / / /    │
│     _ __ ___ / __/ / /_/ (__  ) /_/ /  / / /___/ ____/  /  __/_/ /_/ /     │
│    _ __ ___ /_/    \____/____/\__/_/  /_/\____/_/      /_____(*)____/      │
│                                                                            │
│                                                                            │
│                                FastMCP  2.0                                │
│                                                                            │
│                                                                            │
│                 🖥️  Server name:     Veo MCP Server                         │
│                 📦 Transport:       STDIO                                  │
│                                                                            │
│                 🏎️  FastMCP version: 2.12.5                                 │
│                 🤝 MCP SDK version: 1.16.0                                 │
│                                                                            │
│                 📚 Docs:            https://gofastmcp.com                  │
│                 🚀 Deploy:          https://fastmcp.cloud                  │
│                                                                            │
╰────────────────────────────────────────────────────────────────────────────╯


[10/22/25 08:28:53] INFO     Starting MCP server 'Veo MCP Server' with          server.py:1502
                             transport 'stdio'

Beenden Sie nun den MCP-Dienstprozess mit STRG+C. Dieser Befehl wird später über das ADK MCP Toolset aufgerufen. Wir können mit dem nächsten Schritt fortfahren, damit unser Kundenservicemitarbeiter dieses MCP-Tool verwenden kann.

4. 🚀 Veo-MCP-Server mit ADK-Agent verbinden

Verbinden wir nun den Veo-MCP-Server, damit er von unserem KI-Agenten verwendet werden kann. Erstellen wir zuerst ein anderes Skript, das das Toolset enthält. Führen Sie dazu den folgenden Befehl aus:

touch product_photo_editor/mcp_tools.py

Kopieren Sie dann den folgenden Code in product_photo_editor/mcp_tools.py.

from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
from mcp import StdioServerParameters


mcp_toolset = MCPToolset(
    connection_params=StdioConnectionParams(
        server_params=StdioServerParameters(
            command="uv",
            args=[
                "run",
                "veo_mcp/main.py",
            ],
        ),
        timeout=120, # seconds
    ),
)

# Option to connect to remote MCP server

# from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams

# mcp_toolset = MCPToolset(
#     connection_params=StreamableHTTPConnectionParams(
#         url="http://localhost:8000/mcp",
#         timeout=120,
#     ),
# )

Der obige Code zeigt, wie wir mit ADK MCPToolset eine Verbindung zu einem MCP-Server herstellen können. In diesem Beispiel stellen wir über den STDIO-Kommunikationskanal eine Verbindung zum MCP-Server her. Im Befehl geben wir an, wie der MCP-Server ausgeführt und der Zeitüberschreitungsparameter festgelegt werden kann.

5. 🚀 Änderung von Tool-Aufrufparametern

In der MCP-Server-Tooldeklaration haben wir das Tool generate_video_with_image entworfen, in dem Base64-String als Tool-Parameter angegeben wird. Wir können das LLM nicht darum bitten, das für uns zu erledigen. Daher müssen wir eine spezielle Strategie dafür entwickeln.

Im vorherigen Lab haben wir das vom Nutzer hochgeladene Bild und das Tool-Antwortbild in before_model_callback verarbeitet, um es als Artefakt zu speichern. Das wird auch in der zuvor vorbereiteten Agent-Vorlage berücksichtigt. Wir werden diese Informationen nutzen und folgende Strategien anwenden:

  1. Weisen Sie das LLM an, den Wert „artifact_id“ immer zu senden, wenn für bestimmte Tool-Parameter das Senden von Base64-Stringdaten erforderlich ist.
  2. Fangen Sie den Toolaufruf in before_tool_callback ab und wandeln Sie den Parameter von der artifact_id in den Byte-Inhalt um, indem Sie das Artefakt laden und die Toolargumente überschreiben.

Das folgende Bild zeigt den Teil, den wir abfangen.

2d6142cf5d96830e.png

Bereiten Sie zuerst die Funktion before_tool_callback vor. Erstellen Sie dazu mit dem folgenden Befehl eine neue Datei product_photo_editor/tool_callbacks.py.

touch product_photo_editor/tool_callbacks.py

Kopieren Sie dann den folgenden Code in die Datei.

# product_photo_editor/tool_callbacks.py

from google.genai.types import Part
from typing import Any
from google.adk.tools.tool_context import ToolContext
from google.adk.tools.base_tool import BaseTool
from google.adk.tools.mcp_tool.mcp_tool import McpTool
import base64
import logging
import json
from mcp.types import CallToolResult


async def before_tool_modifier(
    tool: BaseTool, args: dict[str, Any], tool_context: ToolContext
):
    # Identify which tool input should be modified
    if isinstance(tool, McpTool) and tool.name == "generate_video_with_image":
        logging.info("Modify tool args for artifact: %s", args["image_data"])
        # Get the artifact filename from the tool input argument
        artifact_filename = args["image_data"]
        artifact = await tool_context.load_artifact(filename=artifact_filename)
        file_data = artifact.inline_data.data

        # Convert byte data to base64 string
        base64_data = base64.b64encode(file_data).decode("utf-8")

        # Then modify the tool input argument
        args["image_data"] = base64_data

Der obige Code zeigt die folgenden Schritte:

  1. Prüfen Sie, ob das aufgerufene Tool ein McpTool-Objekt ist und ob es sich um den Ziel-Toolaufruf handelt, den wir ändern möchten.
  2. Rufen Sie den Wert von image_data-Argumenten ab, wobei das Argument in Base64-Format angefordert wird, aber wir das LLM bitten, die artifact_id dafür zurückzugeben.
  3. Laden Sie das Artefakt über den Artefaktdienst auf tool_context.
  4. image_data-Argumente mit den Base64-Daten überschreiben

Jetzt müssen wir diesen Callback dem Agent hinzufügen und die Anleitung leicht anpassen, damit der Agent die Base64-Tool-Argumente immer mit der Artefakt-ID füllt.

Öffnen Sie product_photo_editor/agent.py und ändern Sie den Inhalt mit dem folgenden Code.

# product_photo_editor/agent.py

from google.adk.agents.llm_agent import Agent
from product_photo_editor.custom_tools import edit_product_asset
from product_photo_editor.mcp_tools import mcp_toolset
from product_photo_editor.model_callbacks import before_model_modifier
from product_photo_editor.tool_callbacks import before_tool_modifier
from product_photo_editor.prompt import AGENT_INSTRUCTION

root_agent = Agent(
    model="gemini-2.5-flash",
    name="product_photo_editor",
    description="""A friendly product photo editor assistant that helps small business 
owners edit and enhance their product photos. Perfect for improving photos of handmade 
goods, food products, crafts, and small retail items""",
    instruction=AGENT_INSTRUCTION
    + """
**IMPORTANT: Base64 Argument Rule on Tool Call**

If you found any tool call arguments that requires base64 data,
ALWAYS provide the artifact_id of the referenced file to 
the tool call. NEVER ask user to provide base64 data. 
Base64 data encoding process is out of your 
responsibility and will be handled in another part of the system.
""",
    tools=[
        edit_product_asset,
        mcp_toolset,
    ],
    before_model_callback=before_model_modifier,
    before_tool_callback=before_tool_modifier,
)

Jetzt versuchen wir, mit dem Agent zu interagieren, um diese Änderung zu testen. Führen Sie den folgenden Befehl aus, um die Webentwickler-UI zu starten.

uv run adk web --port 8080

Es wird eine Ausgabe wie im folgenden Beispiel erzeugt. Das bedeutet, dass wir bereits auf die Weboberfläche zugreifen können.

INFO:     Started server process [xxxx]
INFO:     Waiting for application startup.

+-----------------------------------------------------------------------------+
| ADK Web Server started                                                      |
|                                                                             |
| For local testing, access at http://127.0.0.1:8080.                         |
+-----------------------------------------------------------------------------+

INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)

Um die URL zu prüfen, können Sie Strg+Klicken darauf ausführen oder oben im Cloud Shell Editor auf die Schaltfläche Webvorschau klicken und Vorschau auf Port 8080 auswählen.

edc73e971b9fc60c.png

Auf der folgenden Webseite können Sie oben links im Drop-down-Menü verfügbare Agents auswählen ( in unserem Fall sollte es product_photo_editor sein) und mit dem Bot interagieren.

Lade dann das folgende Bild hoch und bitte den Kundenservicemitarbeiter, daraus einen Werbeclip zu erstellen.

Generate a slow zoom in and moving from left and right animation

fede23931847cb7e.png

Der folgende Fehler tritt auf:

6728902ed0b7cc55.png

Warum? Da das Tool die Ergebnisse auch direkt in Form eines Base64-Strings zurückgegeben hat, wird das maximale Token überschritten. Im nächsten Abschnitt wird dieser Fehler behoben.

6. 🚀 Tool-Antworten ändern

In diesem Abschnitt verarbeiten wir die Tool-Antwort aus der MCP-Antwort. Wir werden Folgendes tun:

  1. Videoantwort des Tools im Artefaktdienst speichern
  2. Stattdessen die Artefakt-ID an den Agent zurückgeben

Zur Erinnerung: Wir werden uns während der folgenden Laufzeit des Kundenservicemitarbeiters melden.

2d6142cf5d96830e.png

Implementieren wir zuerst die Callback-Funktion. Öffnen Sie die Datei product_photo_editor/tool_callbacks.py und ändern Sie sie, um after_tool_modifier zu implementieren.

# product_photo_editor/tool_callbacks.py

from google.genai.types import Part
from typing import Any
from google.adk.tools.tool_context import ToolContext
from google.adk.tools.base_tool import BaseTool
from google.adk.tools.mcp_tool.mcp_tool import McpTool
import base64
import logging
import json
from mcp.types import CallToolResult


async def before_tool_modifier(
    tool: BaseTool, args: dict[str, Any], tool_context: ToolContext
):
    # Identify which tool input should be modified
    if isinstance(tool, McpTool) and tool.name == "generate_video_with_image":
        logging.info("Modify tool args for artifact: %s", args["image_data"])
        # Get the artifact filename from the tool input argument
        artifact_filename = args["image_data"]
        artifact = await tool_context.load_artifact(filename=artifact_filename)
        file_data = artifact.inline_data.data

        # Convert byte data to base64 string
        base64_data = base64.b64encode(file_data).decode("utf-8")

        # Then modify the tool input argument
        args["image_data"] = base64_data


async def after_tool_modifier(
    tool: BaseTool,
    args: dict[str, Any],
    tool_context: ToolContext,
    tool_response: dict | CallToolResult,
):
    if isinstance(tool, McpTool) and tool.name == "generate_video_with_image":
        tool_result = json.loads(tool_response.content[0].text)

        # Get the expected response field which contains the video data
        video_data = tool_result["video_data"]
        artifact_filename = f"video_{tool_context.function_call_id}.mp4"

        # Convert base64 string to byte data
        video_bytes = base64.b64decode(video_data)

        # Save the video as artifact
        await tool_context.save_artifact(
            filename=artifact_filename,
            artifact=Part(inline_data={"mime_type": "video/mp4", "data": video_bytes}),
        )

        # Remove the video data from the tool response
        tool_result.pop("video_data")

        # Then modify the tool response to include the artifact filename and remove the base64 string
        tool_result["video_artifact_id"] = artifact_filename
        logging.info(
            "Modify tool response for artifact: %s", tool_result["video_artifact_id"]
        )

        return tool_result

Danach müssen wir unseren Agenten mit dieser Funktion ausstatten. Öffnen Sie product_photo_editor/agent.py und ändern Sie den Code so:

# product_photo_editor/agent.py

from google.adk.agents.llm_agent import Agent
from product_photo_editor.custom_tools import edit_product_asset
from product_photo_editor.mcp_tools import mcp_toolset
from product_photo_editor.model_callbacks import before_model_modifier
from product_photo_editor.tool_callbacks import (
    before_tool_modifier,
    after_tool_modifier,
)
from product_photo_editor.prompt import AGENT_INSTRUCTION

root_agent = Agent(
    model="gemini-2.5-flash",
    name="product_photo_editor",
    description="""A friendly product photo editor assistant that helps small business 
owners edit and enhance their product photos. Perfect for improving photos of handmade 
goods, food products, crafts, and small retail items""",
    instruction=AGENT_INSTRUCTION
    + """
**IMPORTANT: Base64 Argument Rule on Tool Call**

If you found any tool call arguments that requires base64 data,
ALWAYS provide the artifact_id of the referenced file to 
the tool call. NEVER ask user to provide base64 data. 
Base64 data encoding process is out of your 
responsibility and will be handled in another part of the system.
""",
    tools=[
        edit_product_asset,
        mcp_toolset,
    ],
    before_model_callback=before_model_modifier,
    before_tool_callback=before_tool_modifier,
    after_tool_callback=after_tool_modifier,
)

Das war's. Jetzt können Sie den Agenten bitten, Ihnen nicht nur beim Bearbeiten des Fotos zu helfen, sondern auch ein Video für Sie zu generieren. Führen Sie den folgenden Befehl noch einmal aus.

uv run adk web --port 8080

Versuche dann, mit diesem Bild ein Video zu erstellen.

Generate a slow zoom in and moving from left and right animation

fede23931847cb7e.png

Das generierte Video wird wie im Beispiel unten angezeigt und bereits als Artefakt gespeichert.

29150fa84f85d2fd.png

7. ⭐ Zusammenfassung

Sehen wir uns noch einmal an, was wir in diesem Codelab bereits getan haben. Hier ist das Wichtigste:

  1. Verarbeitung multimodaler Daten (Tool-E/A): Die Strategie zur Verarbeitung multimodaler Daten (z. B. Bilder und Videos) für Tool-Ein- und ‑Ausgabe wurde durch die Verwendung des Artifacts-Dienstes des ADK und spezieller Callbacks anstelle der direkten Übergabe von Rohbytedaten verstärkt.
  2. Integration des MCP-Toolsets: Ein externer Veo-MCP-Server wurde mit FastMCP über das ADK-MCP-Toolset entwickelt und integriert, um dem Agenten Funktionen zur Videogenerierung hinzuzufügen.
  3. Änderung der Tool-Eingabe (before_tool_callback): Es wurde ein Callback implementiert, um den Aufruf des Tools „generate_video_with_image“ abzufangen und die vom LLM ausgewählte artifact_id der Datei in die erforderlichen base64-codierten Bilddaten für die Eingabe des MCP-Servers umzuwandeln.
  4. Änderung der Toolausgabe (after_tool_callback): Es wurde ein Callback implementiert, um die große base64-codierte Videoantwort vom MCP-Server abzufangen, das Video als neues Artefakt zu speichern und eine saubere video_artifact_id-Referenz an das LLM zurückzugeben.

8. 🧹 Bereinigen

So vermeiden Sie, dass Ihrem Google Cloud-Konto die in diesem Codelab verwendeten Ressourcen in Rechnung gestellt werden:

  1. Wechseln Sie in der Google Cloud Console zur Seite Ressourcen verwalten.
  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Beenden, um das Projekt zu löschen.