3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【入門】ADK Web サンプルを改造して Word/Excel を Markdown に変換する(コールバックで前処理)

Posted at

はじめに

Google が提供している ADK (Agent Development Kit) を使うと、簡単にチ
ャットエージェントを作れます。
ただし、ADK Web のサンプルコードをそのまま使うと、Excel や Word ファイルを添付したときにエラーになることがあります。

本記事では、次の流れで問題を解決します。

  1. 現象の再現(どんなエラーになるか)
  2. 原因(なぜエラーになるのか)
  3. コールバックの基礎(コールバックって何?)
  4. 修正方法(コールバックで Markdown に変換する)

1. 現象の再現(どんなエラーになるか)

参考:初期サンプル(公式ドキュメント)

注記
この記事内で掲載する「初期サンプル」は、上記の公式サンプルをベースに、説明に不要な部分を一部省略しています(可読性のため)。処理の流れや意図が変わらない範囲で簡略化しています。

まずは ADK Web のサンプルを(上記をベースに)そのまま動かします。

import datetime
from zoneinfo import ZoneInfo
from google.adk.agents import Agent

def get_weather(city: str) -> dict:
    if city.lower() == "new york":
        return {"status": "success", "report": "The weather in New York is sunny."}
    else:
        return {"status": "error", "error_message": f"No data for {city}"}

def get_current_time(city: str) -> dict:
    if city.lower() == "new york":
        tz = ZoneInfo("America/New_York")
        now = datetime.datetime.now(tz)
        return {"status": "success", "report": now.strftime("%Y-%m-%d %H:%M:%S")}
    else:
        return {"status": "error", "error_message": f"No timezone for {city}"}

root_agent = Agent(
    name="weather_time_agent",
    model="gemini-2.0-flash",
    description="Agent to answer time and weather.",
    instruction="You are a helpful agent.",
    tools=[get_weather, get_current_time],
)

この状態でチャットにテキスト文を直接打つと、正しく挙動しますが、同じテキスト文を記載した Excel ファイル をチャットに添付すると、次のようなエラーが出ます。

スクリーンショット 2025-09-07 204452.png

400 INVALID_ARGUMENT.
Unable to submit request because it has a mimeType parameter with value
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, which is not supported.

2. 原因(なぜエラーになるのか)

Gemini などの LLM は バイナリの Excel/Word ファイルを直接理解できません
添付ファイルは inline_data として mimeType がそのまま LLM に渡されるのですが、Gemini は application/vnd.openxmlformats-officedocument.* 系を受け付けないため、400 エラー になります。

つまり:

  • サンプルコードには 「添付ファイルをテキストに変換する処理」 が入っていない
  • そのため バイナリがそのまま投げられてエラー になる

コールバックの基礎(コールバックって何?)

一言でいうと

「フレームワークの処理の前後で、開発者が差し込みたい関数を“呼び戻してもらう”仕組み」です。

  • 例:ボタンをクリックしたら実行される関数 → クリックイベントのコールバック
  • ADK では、Agent / Model / Tools それぞれの 前後 にコールバックを差し込めます

コールバックの全体像(図とリファレンス)

callback_flow.png

図1: ADK におけるコールバック呼び出しポイント(Agent/Model/Tools の前後)
出典: Google ADK Docs — Callbacks: Introduction

  • before_agent_callback:LLM 呼び出し直前。入力の整形権限制御添付のテキスト化(今回)
  • after_agent_callback:最終応答直後。監査ログ要約保存メトリクス送信
  • before/after_model_callback:モデル入出力の最終整形安全フィルタ
  • before/after_tool_callback:ツール引数の検証結果整形キャッシュ

関数のシグネチャ(ADK 1.13 以降)

キーワード引数で呼ばれるため、引数名に注意します。

from google.adk.agents.callback_context import CallbackContext

def before_agent_callback(*, callback_context: CallbackContext) -> None:
    ...

補足:callback_context という 引数名が要求されます。違う名前にすると
unexpected keyword argument 'callback_context' で落ちるため注意。


4. 修正方法(コールバックで Markdown に変換する)

LLM に渡す前に添付ファイルをテキスト(Markdown)へ変換すれば OK です。
ADK の before_agent_callback フックで、DOCX/XLSX を Markdown に変換し、バイナリは捨ててテキストだけ残すようにします。

依存の導入(重要)markitdown は拡張子ごとに追加依存が必要です。
Word/Excel を扱う場合は以下をインストールしてください。

pip install --upgrade pip
pip install "markitdown[docx]" "markitdown[xlsx]"
# まとめて入れる場合は
# pip install "markitdown[all]"

コールバック実装

import io
from google.adk.agents.callback_context import CallbackContext
from google.genai import types
from markitdown import MarkItDown  # バイナリ→Markdown 変換ライブラリ

# 変換対象の MIME タイプ(Word/Excel の OpenXML 形式)
DOC_MIMES = {
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",  # .docx
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",        # .xlsx
}

def before_agent_callback(*, callback_context: CallbackContext) -> None:
    """LLM 実行前のフック。DOCX/XLSX を Markdown に変換し、バイナリは渡さない。"""

    # 1) ユーザーの発話のみを対象にする(system/assistant には介入しない)
    uc = getattr(callback_context, "user_content", None)
    if not uc or getattr(uc, "role", "") != "user":
        return None

    # 2) メッセージが Part(複数要素)で構成されていなければ何もしない
    parts = getattr(uc, "parts", None)
    if not parts:
        return None

    # 3) 変換器を用意(バイナリ→Markdown)
    converter = MarkItDown()

    # 4) 新しい Part 配列を作成(対象だけ置換、その他はそのまま)
    new_parts = []
    for part in parts:
        inline = getattr(part, "inline_data", None)               # バイナリを持つ Part か
        mime = getattr(inline, "mime_type", "") if inline else "" # MIME を取得
        data = getattr(inline, "data", None) if inline else None  # バイナリ(bytes 前提)

        # 4-1) DOCX/XLSX かつバイナリあり → 変換
        if inline and mime in DOC_MIMES and data:
            try:
                md_text = converter.convert(io.BytesIO(data)).text_content
                # バイナリ Part は捨てて、Markdown テキスト Part に置換
                new_parts.append(types.Part(text=md_text))
            except Exception:
                # 失敗してもバイナリは渡さず、失敗メッセージのテキストに置換
                new_parts.append(types.Part(text="[変換失敗: 添付ファイルを読み取れませんでした]"))
        else:
            # 4-2) 対象外(通常テキストや画像など)はそのまま通す
            new_parts.append(part)

    # 5) 差し替えた Part を反映(以降、LLM にはテキストのみが渡る)
    callback_context.user_content.parts = new_parts
    return None

Agent へ組み込み

root_agent = Agent(
    name="weather_time_agent",
    model="gemini-2.0-flash",
    description="Agent to answer time and weather.",
    instruction="You are a helpful agent.",
    tools=[get_weather, get_current_time],
    before_agent_callback=before_agent_callback,  # ← 追加
)

実行結果

この修正版を使うと、Excel や Word を添付してもエラーにならず、
ファイルの中身が Markdown テキストとして LLM に渡されます

スクリーンショット 2025-09-07 205746.png

スクリーンショット 2025-09-07 205946.png


まとめ

  • ADK Web のサンプルをそのまま使うと、Word/Excel 添付で mimeType が LLM に渡りエラーになる
  • before_agent_callback を使って Markdown に変換し、バイナリは渡さないようにすれば解決
  • 依存は markitdown[docx] / markitdown[xlsx](または markitdown[all])を導入

参考リンク

本記事のサンプルは、上記の公式コードをベースに 一部を省略 しています(可読性のため)。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?