0
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でLive/Voice Agentを評価する前に整理する - 音声対話をCIでどこまで確認できるか

0
Posted at

はじめに

overview.png

Google Developers Blogで、Agent Development Kit(ADK)のLive / Voice agentを評価する方法が紹介されていました。

音声で対話するagentは「動かしてみて、聞いて、良さそう」で終わりがちです。この記事では、実際に作り始める前に、ADKの評価機能で音声対話のどこまでを自動で採点できて、どこからは人間の聴感確認に任せるのか を整理します。

※本記事は個人の整理メモです。確認日は 2026-08-30 です。裏取りは公式ドキュメントのソース(google/adk-docs)と、ADK Python本体(google/adk-python)の実装・公式サンプルに当たって行いました。モデル名・料金・リージョン・提供段階は変わりやすいので、実装前に公式情報を確認してください。

fig_01

先に結論

ADKのLive/Voice評価は、テキストagentの評価と同じ adk eval の枠組みの上に、「音声で喋るシミュレーテッドユーザー」を載せたもの と捉えると整理しやすいです。

新しい評価フレームワークが別に生えたわけではなく、既存の評価設定(EvalConfig)に次の2つが足されている、という構造になっています。

追加されたもの 役割
user_simulator_configtype: "llm_audio" シミュレーテッドユーザーの発話を 音声に合成して agentに入力する
live_model_config Live API(双方向ストリーミング)モードで動かす際の タイムアウト設定

ここで最初に押さえておきたい点が3つあります。

  1. type: "llm_audio" を書かないと音声にならない。 audio_model だけ書いても、テキストのシミュレーテッドユーザーとして動きます(後述)。
  2. live_model_config は音声品質の設定ではない。 現時点のフィールドは timeout_seconds だけです。
  3. 採点しているのは「会話の中身」であって「音の良し悪し」ではない。 rubric(採点基準)をLLMが判定する仕組みなので、声色・間・音質そのものを点数化しているわけではありません。

つまり、CIで自動化できるのは「音声で会話させたときに、agentが手順どおりに振る舞ったか」までです。標準のrubric評価は声の自然さそのものを直接採点しないため、最終的な聴感確認は人間が行うのが現実的です。

fig_02

なぜLive/Voice agentの評価は難しいのか

テキストのagentであれば、「この入力に対してこの出力」という固定のテストが書けます。音声対話ではこれが崩れます。

理由は大きく2つです。

1. 会話の分岐が固定できない

agentが2つの値を必要とするとき、1つずつ聞くこともあれば、まとめて聞くこともあります。公式ドキュメントでもこれが「固定のユーザープロンプトが実用的でない」理由として挙げられています。

固定スクリプトでテストすると、agentが少し違う順序で聞いただけでテストが壊れます。これは実装の劣化ではなくテストの脆さです。

2. 音声はモダリティが増えるぶん失敗の種類が増える

Live APIは低遅延の双方向ストリーミングで、ユーザーがagentの発話に割り込めます。テキストにはない「割り込み」「無音」「言い直し」といった状態が入ってきます。

ADKの LiveModelConfigtimeout_seconds(デフォルト300秒)があるのは、この「応答が返ってこないまま止まる」ケースを打ち切るためです。

この2つに対するADKの答えが、次に見る ユーザーシミュレーション です。

fig_03

ADKの評価の全体像

Live/Voiceの話に入る前に、土台になる評価の枠組みを整理します。ADKの評価は次の4つの入口があります。

入口 コマンド 主な用途
Web UI adk web 対話的にテストケースを作る・結果を見る
プログラム pytest + AgentEvaluator 統合テストに組み込む
CLI adk eval CI/CDから回す
適合テスト adk conformance 記録済みベースラインとの差分検知

Live/Voiceの評価で使うのは主に adk eval です。構成要素は次の3つです。

  • eval set*.evalset.json): 評価ケースの集合。1ケースが1つの会話に対応します
  • 評価設定ファイル: criteria(採点基準としきい値)などをまとめたJSON。--config_file_path で渡します
  • agentディレクトリ: __init__.pyagent モジュールを公開し、その中に root_agent があるディレクトリ

adk eval のコマンド構文は次のとおりです。

adk eval \
    <AGENT_MODULE_FILE_PATH> \
    <EVAL_SET_FILE_PATH_OR_ID>... \
    [--config_file_path=<PATH_TO_TEST_JSON_CONFIG_FILE>] \
    [--print_detailed_results]

AGENT_MODULE_FILE_PATHファイルではなくディレクトリ を指す点に注意してください(公式ドキュメントにも明記されています)。

設定ファイルの名前は決まっていない

ここは少し紛らわしいところです。公式の中でも呼び名が揺れています。

  • 評価ドキュメントの説明: test_config.json
  • ユーザーシミュレーションのドキュメントの例: eval_config.json
  • 公式サンプル live_workflow: test_config.json

いずれも --config_file_path に渡すパスなので、ファイル名は任意 です。ただし、テストファイルをフォルダにまとめて使う場合は、そのフォルダ内の test_config.json が自動的に評価基準として読まれる、という別の仕組みがあります。CLIで明示的に渡す場合と混同しないようにします。

fig_04

ユーザーシミュレーション: 会話を固定せずに作る

固定スクリプトの代わりに、ADKは ConversationScenario を使います。「何を達成したいユーザーなのか」を書いておくと、LLMがその場で発話を生成します。

ConversationScenario のフィールドは3つです。

  • starting_prompt: 会話を始める最初の固定プロンプト
  • conversation_plan: ユーザーが達成すべきゴールの大まかな指示
  • user_persona: ユーザーの人物像(任意)

最小の例は次のようになります。

{
  "starting_prompt": "What can you do for me?",
  "conversation_plan": "Ask the agent to roll a 20-sided die. After you get the result, ask the agent to check if it is prime.",
  "user_persona": "NOVICE"
}

conversation_plan が「何を達成するか」、user_persona が「どう振る舞うか」を決める、という分担です。

組み込みペルソナは3種類

user_persona には文字列で組み込みペルソナを指定できます。公式ドキュメントの整理は次のとおりです。

振る舞い EXPERT NOVICE EVALUATOR
情報の出し方 先回りして詳細を出す 聞かれるまで出さない 先回りして詳細を出す
質問への回答 関連する質問のみ すべての質問に答える 関連する質問のみ
agentの誤りを訂正するか する しない しない
agentのエラーに付き合うか 1回だけ しない しない
トーン プロフェッショナル 会話的 会話的

「NOVICEだと情報を先に出さないので、agentがちゃんと聞き返すかを試せる」といった使い分けができます。独自のペルソナを UserPersona オブジェクトとして定義することもできます。

eval setへの登録

シナリオはCLIでeval setに追加できます。

# (任意)新しいeval setを作る
adk eval_set create \
  contributing/samples/core/hello_world \
  eval_set_with_scenarios

# シナリオを新しいeval caseとして追加する
adk eval_set add_eval_case \
  contributing/samples/core/hello_world \
  eval_set_with_scenarios \
  --scenarios_file contributing/samples/core/hello_world/conversation_scenarios.json \
  --session_input_file contributing/samples/core/hello_world/session_input.json

--session_input_file には app_nameuser_id を持つJSONを渡します。

fig_05

音声で評価する: type: "llm_audio" が要

ここからがLive/Voice固有の部分です。

シミュレーテッドユーザーの設定は EvalConfig.user_simulator_config に書きます。実装上、この設定は type フィールドで実装クラスを選ぶ判別つきユニオン(discriminated union) になっています。

type クラス 動作
llm_backed(デフォルト) LlmBackedUserSimulatorConfig テキストで発話を生成する
llm_audio LlmAudioUserSimulatorConfig テキスト生成に加えて 音声に合成する

ここが最大の落とし穴

type を省略した設定は、後方互換のために llm_backed として扱われます(ADK本体の EvalConfig にそのためのバリデータが入っています)。

つまり、次のように書いても音声評価になりません。

{
  "user_simulator_config": {
    "model": "gemini-3.5-flash",
    "audio_model": "gemini-3.1-flash-tts-preview"
  }
}

llm_backed として解釈されるため、audio_model は使われないまま無視されます。設定クラスが追加フィールドを許容する(extra="allow")ので、その場でバリデーションエラーにもなりません。音声で評価したいなら "type": "llm_audio" を明示します。

{
  "user_simulator_config": {
    "type": "llm_audio",
    "model": "gemini-3.5-flash",
    "max_allowed_invocations": 10,
    "audio_model": "gemini-3.1-flash-tts-preview",
    "audio_model_configuration": {
      "response_modalities": ["AUDIO"],
      "speech_config": {
        "voice_config": {
          "prebuilt_voice_config": { "voice_name": "Kore" }
        },
        "language_code": "en-US"
      }
    }
  }
}

主なフィールド

  • model: 発話テキストを生成するモデル
  • audio_model: 音声合成に使うモデル。デフォルトは cloud_tts(Google Cloud Text-to-Speech)で、gemini-2.5-flash-preview-tts のようなモデル名を指定するとGemini TTSを使います
  • audio_model_configuration: 音声の設定。声は speech_config で選びます。ネイティブ音声モデルを使う場合は response_modalities: ["AUDIO"] も指定します
  • max_allowed_invocations: 会話が無限ループするのを防ぐ上限(デフォルト20、最初の固定プロンプトも1回に数えます)
  • include_text_with_audio: 音声パートと一緒にテキストパートも入れるか(デフォルト true

voice_namelanguage_code を変えれば、違う声や言語でagentの挙動を確かめられます。

Live側の設定

Live APIモデル(gemini-*-live-*)を評価するときは live_model_config が必要です。現時点のフィールドは1つだけです。

{
  "live_model_config": {
    "timeout_seconds": 300
  }
}

timeout_seconds は「モデルのターン完了を待つ秒数」で、デフォルトは300秒です。音声の設定はここには入りません。

fig_06

何を採点するか: rubricベースの評価基準

音声で会話させたあと、何を見て合否を決めるかが criteria です。

Live/Voiceの文脈でよく使うのは、rubric(採点基準)を自然言語で書いて、LLMに判定させる タイプの3つです。

基準 見る対象
rubric_based_final_response_quality_v1 最終応答の品質
rubric_based_tool_use_quality_v1 ツール呼び出しの妥当性
rubric_based_multi_turn_trajectory_quality_v1 会話全体の進み方

rubric_based_multi_turn_trajectory_quality_v1 は、会話履歴(ユーザー発話・agent発話・ツール呼び出し)をすべて積み上げて、rubricごとに yes(1.0)/ no(0.0)で判定します。最後のターンに集計スコアが載り、それ以前のターンは NOT_EVALUATED になります。

rubricの書き方は「こうなっているべき」という宣言文です。

{
  "rubric_id": "verifies_identity_first",
  "rubric_content": {
    "text_property": "Across the call, the agent confirms the caller's name and validates their date of birth before disclosing any appointment details."
  }
}

ユーザーシミュレーションと併用できない基準がある

ここは事前に知っておかないとハマります。期待するツール呼び出しや期待する応答文を必要とする基準は、ユーザーシミュレーションと併用できません。

公式ドキュメントで明記されているのは次の3つです。

  • tool_trajectory_avg_score
  • response_match_score
  • final_response_match_v2

会話が動的に生成される以上、「期待される正解の応答」を先に書けないので当然ではあるのですが、adk eval のデフォルト基準がまさに tool_trajectory_avg_score(1.0)と response_match_score(0.8) です。設定ファイルを渡し忘れると、この組み合わせで走ろうとします。

シナリオを使うなら、criteria は必ず明示的に指定します。

rubricが空だとエラーになる

rubric系の基準は、有効なrubricのリストが空だと実行時に ValueError になります。設定ファイル側のrubricを空にする場合は、eval case側(EvalCase.rubrics)で型の一致するrubricを供給する必要があります。

なお、eval case側のrubricは設定ファイル側の 置き換えではなく追加 です。rubric_based_multi_turn_trajectory_quality_v1 の場合、eval case側は type"TRAJECTORY_QUALITY" のものだけがマージされます。

基準によって必要な認証が違う

ここも分けて把握しておくと迷いません。基準によって、評価を実行する場所が違います。

種類 実行される場所 必要なもの
rubric_based_* の3種 ADK内部のLLM-as-a-Judge judge_model_options で指定するjudgeモデル
safety_v1 / multi_turn_task_success_v1 / multi_turn_trajectory_quality_v1 / multi_turn_tool_use_quality_v1 Agent Platform Eval SDKへ委譲 Google Cloud Project(GOOGLE_CLOUD_PROJECTGOOGLE_CLOUD_LOCATION

rubricベースの基準はADK側で判定するため、judgeモデルが呼べれば動きます。一方、委譲するタイプの基準は公式ドキュメントで Google Cloud Projectが必要 とされており、.env に環境変数を置く前提で説明されています。

CIで動かす場合、後者を使うなら認証情報の受け渡しが最初の関門になります。

fig_07

公式サンプルで通しで見る

ADK Python公式リポジトリの contributing/samples/live/live_workflow に、Live評価の一式が揃っています。断片ではなく通しで動く例なので、ここを読むのが早いです。

agent側の構成

3つの単機能な音声agentをグラフ型のworkflowにつないだ、コールセンター風のサンプルです。

  1. greeter_agent — 挨拶して発信者の名前を確認する
  2. dob_verifier_agent — 生年月日を聞き取り、validate_date_of_birth ツールで検証する
  3. goals_agent — 本人確認が済んだら要件(予約情報)を伝えて会話を締める

各段は mode='task' で動き、GreeterOutput / DobOutput という型付きの結果を次の段に渡します。ライブモデルには gemini-live-2.5-flash-native-audio が使われています。

eval set側

live_workflow.evalset.json は、eval caseの中に conversation_scenariosession_input を持ちます。

{
  "eval_id": "verified_patient_scenario",
  "conversation_scenario": {
    "starting_prompt": "Hello?",
    "conversation_plan": "You are John Doe. Confirm your name when greeted. When asked for your date of birth, give July 12th, 1985, and confirm it when read back. Listen to the appointment details, ask what you should bring to the visit, then say you have no other questions and let the call wrap up.",
    "user_persona": "NOVICE"
  },
  "session_input": {
    "app_name": "live_workflow",
    "user_id": "test_user_id",
    "state": {}
  }
}

「本人確認が済むまで予約情報を出さない」ことを試したいので、NOVICE(聞かれるまで情報を出さない)ペルソナを選んでいる、という読み方ができます。

実行

# 評価用の追加依存を入れる
uv pip install -e ".[eval]"

# 評価を実行する
uv run adk eval \
  contributing/samples/live/live_workflow \
  contributing/samples/live/live_workflow/live_workflow.evalset.json \
  --config_file_path contributing/samples/live/live_workflow/test_config.json

READMEには、Live APIとGemini TTSの両方にアクセスできるVertex AIの認証情報 をこのディレクトリの .env に置く、と書かれています。片方だけでは通りません。

ADK Web で対話的に動かす場合は次のとおりです。

uv run adk web contributing/samples/live/live_workflow

fig_08

CI/CDへの組み込みと、その境界

adk eval はCLIなので、CIから呼ぶこと自体は難しくありません。ただし、どこまでをCIに置くかは分けて考えたほうがよさそうです。

CIで自動化しやすいもの

  • 会話が手順どおりに進んだか(rubricベースのマルチターン評価)
  • 順序の制約が守られたか(本人確認の前に情報を出していないか)
  • ツールが正しい形式・タイミングで呼ばれたか(rubricベースのツール評価)
  • 会話が打ち切られずに完了したかmax_allowed_invocations / timeout_seconds

CIに置きづらいもの

  • 声の自然さ、間の取り方、聞き取りやすさ
  • 割り込みの体感的な滑らかさ
  • 実際のユーザーが「話しやすい」と感じるか

rubricベースの評価はLLM判定なので、判定自体にゆらぎがあります。公式実装では判定モデルを複数回サンプリングして多数決を取る仕組みになっていますが(num_samples)、サンプル数を増やせばそのぶん時間とコストがかかります。公式サンプルが num_samples: 1 にしているのは、おそらくサンプルとしての実行時間を抑えるためです。

現実的な置き方

公式ドキュメントは、CI/CDや回帰テストには tool_trajectory_avg_scoreresponse_match_score のような 速くて予測可能な基準 を勧めています。一方でこれらはユーザーシミュレーションと併用できません。

そこで、次のように段を分けるのが素直だと思います。

内容 頻度
PRごと テキストのagentロジックを固定テストで検証 毎回
マージ後 / 夜間 音声のシミュレーテッドユーザーでrubric評価 定期
リリース前 人間が実際に喋って確認 手動

音声評価ではLive APIとTTSの呼び出しが加わるため、固定テストより実行時間・コストが大きくなりやすいです。毎PRで回す前提にはしないほうが無難です。

fig_09

つまずきやすい点

ここまでで触れたものも含めて、事前に知っておくと事故りにくい点をまとめます。

1. type: "llm_audio" の書き忘れ

一番静かに失敗します。エラーにならず、テキストのシミュレーテッドユーザーとして普通に動いてしまうためです。ADK側はログに「type の判別子がないので llm_backed を既定にした」という情報ログを出すので、音声で動いているつもりのときはログを確認します。

2. 設定ファイルを渡し忘れる

--config_file_path を渡さないと、デフォルト基準(tool_trajectory_avg_score: 1.0, response_match_score: 0.8)が使われます。シナリオベースの評価とは併用できない基準なので、意図しない結果になります。

3. AGENT_MODULE_FILE_PATH にファイルを指定する

引数名が ..._FILE_PATH ですが、渡すのは ディレクトリ です。

4. max_allowed_invocations-1 にする

上限が外れるので、agentとシミュレーテッドユーザーが延々と喋り続ける可能性があります。公式ドキュメントでも非推奨とされています。CIで回すなら特に、必ず有限値にします。

5. モデル名をそのままコピーする

公式サンプルの gemini-3.1-flash-tts-previewgemini-live-2.5-flash-native-audio は、preview を含むものも含めて 入れ替わります。サンプルの値は「こういう場所にモデル名を書く」という位置の参考として読み、実際の対応モデルは公式ドキュメントで確認します。

6. 音声データの取り扱い

評価では合成音声とはいえ音声データが生成され、会話の中身がログや評価結果に残ります。実際の顧客の会話内容をシナリオにそのまま書き写さない、評価結果の保存先を確認する、といった配慮は必要です。

fig_10

始める前のチェックリスト

環境まわり

  • ADK Pythonのバージョンを確認する(音声のシミュレーテッドユーザーは v2.6.0 以降)
  • 評価用の追加依存を入れる(pip install -e ".[eval]" 相当)
  • Live APIとTTSモデルの 両方 にアクセスできる認証情報を用意する
  • safety_v1 やマルチターン系など Agent Platform Eval SDK に委譲する基準を使うなら、Google Cloud Projectの設定を用意する

設定まわり

  • user_simulator_config.type"llm_audio" を書いたか
  • criteria を明示的に指定したか(デフォルトのままにしていないか)
  • rubricのリストが空になっていないか
  • max_allowed_invocations に有限値を入れたか
  • Live APIモデルなら live_model_config.timeout_seconds を設定したか

運用まわり

  • 音声評価を毎PRで回す想定になっていないか(時間・コスト)
  • 判定のゆらぎを踏まえて、しきい値と num_samples を決めたか
  • 「人間が聞いて確認する」工程を残したか
  • 評価に使う会話内容に、実データを持ち込んでいないか

バージョンと提供段階

確認日(2026-08-30)時点で確認できた範囲では次のとおりです。

項目 状況
ADK Python 最新 v2.8.0(PyPI公開 2026-08-26)
ユーザーシミュレーション Python v1.18.0 以降
ユーザーペルソナ Python v1.26.0 以降
音声のシミュレーテッドユーザー v2.6.0(PyPI公開 2026-07-30)で追加
ADK Gemini Live API Toolkit Python v0.5.0 以降 / Experimental

Live API Toolkit自体がExperimental表記なので、本番前提の断定は避けたほうがよさそうです。音声のシミュレーテッドユーザーの実装にも実験的機能のマーカーが付いています。

fig_11

まとめ

  • ADKのLive/Voice評価は、既存の adk eval音声で喋るシミュレーテッドユーザー を足したもの
  • 会話を固定スクリプトではなく ConversationScenario(ゴールとペルソナ)で書く
  • 音声にするには user_simulator_config"type": "llm_audio" が必須。書き忘れても静かにテキストで動く
  • live_model_config は音声設定ではなく、timeout_seconds を持つだけ
  • 採点はrubricベースのLLM判定。tool_trajectory_avg_score などの 正解ありき の基準はシナリオと併用できない
  • CIで確認できるのは「手順どおりに振る舞ったか」まで。声の自然さは標準のrubricでは直接採点されないので、聴感確認は人間が担う
  • Live APIとTTSの呼び出しが加わるぶん実行時間・コストが増えやすいので、毎PRではなく定期実行に置くのが現実的

「音声agentのテストを自動化する」と言うと全部機械任せにできそうに聞こえますが、実際にはテストしやすい部分をきちんと切り出す作業に近い、というのが整理した感想です。

参考(公式情報)

確認方法についての注記

本記事はGoogle Developers Blogに加え、ADK公式ドキュメントおよびADK Python本体の実装コード・公式サンプルgoogle/adk-docs / google/adk-python)を確認して整理しています。

特に type: "llm_audio" の扱いのように、実装を読まないと分かりにくい挙動については、判別つきユニオンの定義とバリデータを直接確認しました。バージョンの公開日はPyPIの公開日に揃えています。

また、Live API / Gemini TTS / adk eval の実API実行による検証は行っていません。コマンドと設定は公式ドキュメントおよび公式サンプルの記述に基づくものです。

0
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
0
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?