iT邦幫忙

2026 iThome 鐵人賽

DAY 21
0

Day 21 | Live and Voice Agents:跟 Gemini 講電話

主張:ADK 的 Session(對話紀錄)跟 Live API session(即時連線)是兩個完全不同的東西,搞混這件事,你寫出來的語音 agent 遲早會在某個深夜炸掉。
讀完能做到:分清楚 ADK Session 與 Live API session 的差異,並用 adk web 跑起一個能講話、能看鏡頭的 agent。

https://ithelp.ithome.com.tw/upload/images/20260919/2018376236Yqravh8Q.png

前幾天你可能已經在 adk web 裡跟自己寫的 agent 打過幾百輪文字對話。今天開始的三天,主題換成語音跟視訊——你的 agent 終於可以真的「聽」你講話、「看」你的鏡頭,而不是只會讀你打的字。

聲音會斷,記憶不會斷

在你動手之前,先講一個幾乎所有人第一次碰 ADK 語音功能都會踩到的坑。假設你做了一個客服語音 agent,用 DatabaseSessionService 把對話存進 PostgreSQL,理論上使用者掛斷再打進來,agent 應該記得剛剛聊到哪。結果你發現——每次連線大概撐個十分鐘就斷了,斷線之後好像「失憶」了一部分,對話變得怪怪的。你開始懷疑是不是資料庫寫入有問題,或者是不是 session 沒存好。

答案跟你的資料庫一點關係都沒有。你撞到的是 ADK Session 和 Live API session 是兩個不同生命週期的東西——這是官方開發指南在 Part 1 跟 Part 4 都特別拉出來強調的一點,因為幾乎每個人都會搞混。

ADK Session(由 SessionService 管理)是持久的對話儲存——它可以橫跨好幾個小時、好幾天,甚至好幾個月,存放對話歷史、事件、狀態。它在你呼叫 run_live() 之前就存在,結束之後也還在。

Live API session(由 Google 的 Live API 後端管理)則是一次性的串流連線上下文——只在 run_live() 的事件迴圈跑著的時候存在,你呼叫 LiveRequestQueue.close() 它就沒了。它會受平台的連線時長限制(後面會講到大概 10 分鐘這件事)。

兩者怎麼合作?當你呼叫 run_live()

  1. ADK 從 SessionService 把既有的 ADK Session 讀出來
  2. session.events 裡的對話歷史去初始化一個新的 Live API session
  3. 雙向串流事件,同時把新事件寫回 ADK Session
  4. run_live() 結束時,Live API session 消失,但 ADK Session 繼續留著
  5. 下次再呼叫 run_live()(或應用程式重啟後),ADK 從 Session 讀歷史,建一個全新的 Live API session

所以你資料庫裡的對話紀錄一直都是完整的——斷線斷的只是那條「即時通話線路」,不是你的記憶。真正會讓你在生產環境吃虧的,是不知道這件事之後,把兩者的責任混在一起處理:比如試圖手動「保存」Live API session 的狀態,或者以為 ADK Session 也有那個十分鐘的限制而過度設計重連邏輯。這個區分,會在明天講 RunConfig 的時候繼續深挖——因為 Live API session 本身還可以透過 session resumption 跨多個連線延續。今天,你只需要記住這個心智模型就夠了。

Bidi-streaming 到底是什麼

官方文件把 ADK 的語音/視訊層叫做 ADK Gemini Live API Toolkit,用一句話講完:它把 Gemini Live API 的低延遲雙向語音視訊能力,包成 ADK 的宣告式設定。

「雙向」(Bidi, Bidirectional)是這裡的關鍵字。傳統的 AI 互動像寄 email——你送出一則完整訊息,等對方回一則完整訊息。Bidi-streaming 像講電話——雙方都能同時說、同時聽,而且你可以打斷對方。你正在聽 agent 解釋量子物理,突然插一句「等等,什麼是電子?」,它會立刻停下來回答你,而不是把準備好的長篇大論講完。

這件事之所以難做,是因為背後牽涉到管理 WebSocket 連線與重連邏輯、協調工具呼叫與回應處理、跨 session 保存對話狀態、協調多模態輸入的並發資料流、還要處理開發環境跟正式環境的平台差異。Raw Live API(google-genai SDK)把這些全部丟給你自己處理;ADK Gemini Live API Toolkit 把它們變成宣告式設定——這是官方文件用一張表格直接對比的重點,ADK 幫你做掉的部分包括:

  • 自動工具執行
  • 自動重連與 session resumption
  • 統一的事件模型
  • LiveRequestQueuerun_live() 這組非同步協調框架
  • 跨 SQL 資料庫/Agent Platform/記憶體的 session 持久化

而不是你自己土法煉鋼。

兩個平台,一套 API

Live API 有兩種存取路徑,官方在後續文件裡統稱它們為 "Live API",只有在講平台專屬差異時才會分開講:

Gemini Live API Gemini Live API (Agent Platform)
存取方式 Google AI Studio Google Cloud
認證 API key(GOOGLE_API_KEY Google Cloud 憑證
適合 快速原型、開發、實驗 正式部署、企業應用
Setup Complexity 最低(只要一把 API key) 需要建好 Google Cloud 專案
Session Duration 純語音 15 分鐘/加視訊 2 分鐘 兩種模式都是 10 分鐘
API 版本 v1beta v1beta1
端點 generativelanguage.googleapis.com {location}-aiplatform.googleapis.com

表格裡的 Session Duration 就是開頭那個十分鐘的來源——Agent Platform 兩種模式都是 10 分鐘,AI Studio 則是純語音 15 分鐘、加上視訊只剩 2 分鐘。怎麼突破這個上限,明天講 RunConfig 時會拆。

切換平台不需要改程式碼,只要改環境變數 GOOGLE_GENAI_USE_ENTERPRISEFALSE 用 AI Studio,TRUE 用 Agent Platform)。這代表你可以本機用免費 API key 開發,上線再切到企業級基礎設施,程式碼一行都不用動——只是這個切換帶來的 session 時長差異,明天講 RunConfig 的時候會看到,兩個平台的限制其實不太一樣。

架構:誰負責什麼

官方文件用一張表把責任分得很清楚:

  • 你負責:前端(Web/Mobile)、Transport 層(WebSocket/SSE 伺服器,通常是 FastAPI)、Agent 定義(model、tools、instruction)
  • ADK 負責LiveRequestQueue(緩衝並排序使用者訊息)、Runner(協調 session、提供 run_live())、RunConfig(串流行為設定)、以及內部的 LLM Flow 與協定轉譯
  • Live API 負責:真正的模型推論、多模態理解、function calling

資料流長這樣:

https://ithelp.ithome.com.tw/upload/images/20260919/20183762kgoqr6kPZT.png

Client 透過 WebSocket 連到你的 Transport 層,一路往下交給 LiveRequestQueueRunner.run_live()agent.run_live(),最後由 LLM Flow 呼叫 llm.connect() 接上 Gemini。回程反過來,一路 yield Event 回到你的 Transport 層。

四階段生命週期

ADK 把一個語音 session 的生命週期切成四個階段:

https://ithelp.ithome.com.tw/upload/images/20260919/20183762g7rBnOyJTQ.png

Phase 1:應用初始化(整個應用程式啟動時做一次)——建立 Agent、建立 SessionService、建立 Runner。這三個物件是無狀態且可重用的,只建一次,所有使用者的 session 共用。

Phase 2:Session 初始化(每個使用者連線做一次)——用 get-or-create 模式拿到 ADK Session、建立這次連線專用的 RunConfig、建立一個全新LiveRequestQueue。官方特別提醒:LiveRequestQueue 絕對不能跨 session 重用,每次 run_live() 呼叫都要一個新的 queue,重用會導致訊息順序錯亂與狀態汙染。

Phase 3:run_live() 事件迴圈——這是真正雙向串流的地方:一個 upstream task 把使用者訊息送進 queue,一個 downstream task 從 run_live()Event 往外送,兩者用 asyncio.gather() 並發跑。

Phase 4:終止——呼叫 live_request_queue.close(),讓 run_live() 的 async generator 優雅結束,Live API session 隨之銷毀,但 ADK Session 留下來。

動手做:第一個能講話的 agent

上面那四個階段,等你要自己寫 FastAPI server 時每一步都得自己接。但今天先不用——adk web 已經把 Transport 層跟四階段生命週期都包好了,你只要負責 Agent 定義那一塊(自己寫 transport 的完整範例在官方 dev guide Part 1)。

理論講完,來跑一個真的能講話的 agent。跟 Day 2 一樣的流程,先裝好環境:

python3 -m venv .venv
source .venv/bin/activate   # macOS/Linux
pip install google-adk

專案結構跟平常一樣簡單:

adk-streaming/
└── app/
    ├── .env
    └── google_search_agent/
        ├── __init__.py
        └── agent.py

agent.py 幾乎跟 Day 3 你寫過的 agent 一模一樣——這是 ADK 語音功能最讓人安心的地方,不需要為了支援語音重寫 agent 定義,差異全部在後面的 RunConfig 上:

from google.adk.agents import Agent
from google.adk.tools import google_search  # Import the tool

root_agent = Agent(
   # A unique name for the agent.
   name="basic_search_agent",
   # The Large Language Model (LLM) that agent will use.
   # 兩個平台的 native audio 模型 ID 不同,AI Studio 用這個:
   model="gemini-2.5-flash-native-audio-preview-12-2025",
   # 若走 Agent Platform,改成 "gemini-live-2.5-flash-native-audio"
   # A short description of the agent's purpose.
   description="Agent to answer questions using Google Search.",
   # Instructions to set the agent's behavior.
   instruction="You are an expert researcher. You always stick to the facts.",
   # Add google_search tool to perform grounding with Google search.
   tools=[google_search]
)

model 這欄不能留空也不能隨便填——不是每個 Gemini 模型都支援 Live API。上面兩個 ID 是官方開發指南 Part 1 列出的預設值(ADK 文件 2026-08 版本),模型汰換很快,跑不動時去 Gemini API 模型清單 查標了 Live API 的最新 ID。填錯的話 adk web 會回 500,log 裡是 ValueError: Model xxx not found.(Day 23 會細講 Native Audio 與 Half-Cascade 兩種模型架構的差異)。

.env 設定跟平台選擇綁在一起。用 AI Studio 的話,先去 Google AI Studio 按 Create API key 拿一把(免費額度足夠跑完今天的範例),貼進 .env

GOOGLE_GENAI_USE_ENTERPRISE=FALSE
GOOGLE_API_KEY=PASTE_YOUR_ACTUAL_API_KEY_HERE

用 Agent Platform 的前置作業比較多:要有一個 Google Cloud 專案、裝好 gcloud CLI、跑 gcloud auth login、再到 Console 啟用 Agent Platform(aiplatform)API。第一次碰 GCP 的話這段會花掉你半小時以上,想快點聽到聲音就先用上面的 AI Studio 路線。

GOOGLE_GENAI_USE_ENTERPRISE=TRUE
GOOGLE_CLOUD_PROJECT=PASTE_YOUR_ACTUAL_PROJECT_ID
GOOGLE_CLOUD_LOCATION=us-central1

你在網路上會看到很多教學寫 GOOGLE_GENAI_USE_VERTEXAI,那是舊名,現在還能用但會噴 deprecation warning,GOOGLE_GENAI_USE_ENTERPRISE 是目前的正式名稱。

跑之前有一個容易被忽略的小步驟——設定 SSL_CERT_FILE,語音跟視訊測試都需要它。macOS/Linux:

export SSL_CERT_FILE=$(python3 -m certifi)

Windows PowerShell 要用另一種寫法:

$env:SSL_CERT_FILE = (python3 -m certifi)

Windows 使用者如果 adk web_make_subprocess_transport NotImplementedError,改用 adk web --no-reload

然後跟平常一樣啟動 dev UI:

cd app
adk web

提醒一下,adk web 只是開發用的 dev UI,別直接拿去接正式流量。

打開瀏覽器(http://127.0.0.1:8000),選 google_search_agent重新整理一次頁面,再點麥克風圖示啟用語音輸入,問「What is the weather in New York?」——你會聽到 agent 用語音即時回答,而且答案是它真的呼叫 google_search 查出來的,不是憑空編的。切換到視訊同樣要先重新整理,再點攝影機圖示,問「What do you see?」,它會描述鏡頭看到的畫面。

注意:native-audio 模型不能同時用文字聊天——adk web 上打字會直接報錯,這不是 bug,是模型架構的限制(Day 23 會解釋為什麼)。

一個容易被忽略的行為差異

串流(語音/視訊)模式跟平常的文字模式比起來,少了一部分你可能習慣依賴的掛鉤點:

  • before_model_callbackafter_model_callback 這兩個不會被觸發,只有文字模式的 run_async 路徑才會執行它們。
  • agent callback(before_agent_callbackafter_agent_callback)跟 tool callback(before_tool_callbackafter_tool_callback)照常運作,LongRunningFunctionToolExampleTool 也是。
  • workflow agents 目前只有 SequentialAgent 能接語音;LoopAgentParallelAgent 接上串流會直接丟出 NotImplementedError

白話講:如果你在 Day 12 的 model callback 裡放了 guardrail(例如過濾模型輸出的敏感字詞),語音模式下這段邏輯根本不會跑。要保住這層防護,就把邏輯搬到 agent callback 或 tool callback。同理,架構裡用了 LoopAgentParallelAgent 的話,語音功能現在還接不上去。

銜接

今天先建立了兩個心智模型:ADK Session 與 Live API session 是分開的,以及四階段生命週期把責任切得很乾淨。你也跑起了第一個能講話、能看鏡頭的 agent——但它用的都是 RunConfig 的預設值。明天要拆開 RunConfig 這個黑盒子:為什麼 session 十分鐘就斷、要不要開 session resumption、context window compression 什麼時候該用、以及一個幾乎所有人上線前都會忽略的成本控管陷阱——max_llm_calls 對 BIDI 串流完全不生效。


Google ADK 官方網站
GitHub - Agent Development Kit (ADK) 2.0

GitHub 開源實作:https://github.com/SeanLinH/adk_tutor


上一篇
Day 20 - 動手接上遠端 Agent:A2A Exposing 與 Consuming
系列文
Google ADK Agent 教戰:30 天從原型到可上線的 AI Agent 系統21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言