Google 官方指南:用 ADK 和 Gemini 3.8 Live 构建不会冷场的语音智能体
Step by Step guide: Build a Voice Agent That Never Goes Silent using ADK
Google 开发者博客发布分步指南,介绍如何用开源 ADK 与 gemini-3.8-live 实现非阻塞语音工具调用。在销售报告基准流程中,把阻塞式工具改为流式工具后,智能体首次开口时间从 14.4 秒降到 1.2 秒。指南给出流式工具、单次 yield 后台执行和 response_scheduling 三种模式,并说明 stop_streaming 取消与用户插话不中断工具的行为。
原文给出流式工具与响应调度的具体代码和基准数据,读者可据此改造语音智能体的阻塞式工具调用。
2026年10月19日
Stephen Allen 解决方案架构师 AI 应用与平台
Kaz Sato Staff 开发者关系倡导者
使用 ADK 和 Gemini 3.8 Live 构建永不沉默的语音智能体
一份关于 流式工具、非阻塞函数调用以及 Agent Development Kit(ADK)Gemini Live API Toolkit 中响应调度的指南。
在文本聊天中,一次耗时 13 秒的数据库查询或 RAG 流水线会显示加载旋转图标。而在实时语音对话中,13 秒的阻塞式工具执行会产生 13 秒的静默,导致用户以为通话已断开而挂断。
你无法总是让后端查询在亚秒级完成,因此修复方案必须从架构层面入手:在慢速工具于后台运行的同时,保持智能体的对话能力。开源 ADK 和 gemini-3.8-live 无需你自行编写客户端 WebSocket 事件循环来管理异步工具状态,而是让你用 Python 原生实现非阻塞语音工具。在我们基于 gemini-3.8-live 的基准销售报告工作流中,从阻塞式工具切换为流式工具后,智能体说出第一句话的时间从 14.4 秒缩短至 1.2 秒。
本指南涵盖如何使用流式工具、非阻塞 FunctionTool 定义以及 response_scheduling 来构建响应迅速、自然的语音智能体。
观看 1 分钟的 ADK Live AI Concierge 演示视频,亲耳听听调度响应如何运作:
抱歉,你的浏览器不支持此视频的播放
ADK Live AI Concierge 演示视频解释了调度响应如何运作
非阻塞工具能带来什么
在 ADK 中实现非阻塞工具调用可带来多项立竿见影的用户体验优势:
- 零静默:智能体不会在对话中途卡住,而是可以立即确认请求(“正在查看那些数字……”)并让对话保持活跃。
- 可听见的进度更新:对于多阶段工作流(例如连接、查询和核对),智能体可以播报中间里程碑,让等待听起来像是在积极推进。
- 多任务与并行对话:用户可以在长时间查询于后台执行时提出后续或无关问题,并及时获得回答,而不会让会话停滞。
- 细粒度交付控制:你可以决定最终结果何时以及如何进入对话:等待自然的说话停顿、为关键警报立即打断,或静默更新对话上下文。
基线:为什么标准工具会阻塞对话
在标准智能体开发中,工具被定义为一个 async def 函数,它完成工作并返回单个值:
from typing import Any
async def query_sales_database(region: str) -> dict[str, Any]:
"""Runs the quarterly sales report query for a region.
Args:
region: Sales region to report on, e.g. "EMEA" or "APAC".
Returns:
A dict containing report metrics upon completion.
"""
report = await run_expensive_query(region) # e.g., 13 seconds of work
return {"status": "success", "report": report} # Single return at the end纯文本
已复制
当直接传递给 ADK 中的 Agent 时,该函数会创建一个阻塞式工具调用。在 Gemini Live API 双向语音会话(run_live)期间,执行循环会等待工具返回,然后才生成后续的模型轮次。
如果 query_sales_database 耗时 13 秒,智能体就会产生 13 秒的完全沉默。诸如“告诉用户你正在处理”之类的提示指令无法解决这个问题,因为模型的轮次被阻塞,正在等待函数响应负载。
为了让对话保持流畅,你可以根据期望的用户体验,在两种主要的非阻塞模式之间进行选择。
模式 1:流式工具(当进度本身有故事可讲时)
当一项操作由多个有意义的阶段组成(例如连接数据库、聚合记录以及格式化结果)时,流式工具可以让智能体在进度发生时进行叙述。
在 ADK 中,将标准工具转换为流式工具只需把 return 改为 yield。通过将函数定义为 AsyncGenerator,ADK 会自动将工具声明标记为 NON_BLOCKING,并向模型返回一个即时的状态确认。
import asyncio
from typing import AsyncGenerator
from google.adk.agents import Agent
async def query_sales_database(region: str) -> AsyncGenerator[str, None]:
"""Runs the quarterly sales report. Call this ONCE; it pushes updates automatically.
Args:
region: Sales region to report on, e.g. "EMEA" or "APAC".
Yields:
Progress updates while the query runs, followed by the final summary.
"""
stages = [
("Connecting to the sales warehouse...", 1.5),
("Scanning order records...", 3.5),
("Aggregating revenue by product line...", 3.5),
("Formatting the report...", 1.5),
]
for message, delay in stages:
yield f"PROGRESS: {message}"
await asyncio.sleep(delay)
yield f"DONE: {region} Q2 revenue reached $4.81M, up 12.4% year over year."
agent = Agent(
name="sales_analyst",
model="gemini-3.8-live",
tools=[query_sales_database],
instruction=(
"You are a helpful sales assistant. When the user asks about sales data, "
"call query_sales_database once. Acknowledge the request immediately, "
"briefly paraphrase each progress update, and present the final figures "
"when completed. Keep answers conversational and concise."
),
)纯文本
已复制
实际工作原理
- 即时确认:当模型触发
query_sales_database时,ADK 会立即向模型派发一个异步状态事件。智能体会立刻开口(“正在为你查看 EMEA 的数据……”)。 - 部分更新:每次
yield都会向上游发送一个部分函数响应。模型会在每个更新到达时对其进行转述(“正在连接数据仓库……”、“正在聚合产品线数据……”),不过叙述可能会比工具滞后数秒。 - 无缝多线程:如果用户在查询运行期间提出一个无关的问题,智能体可以回答该问题,而不会取消或中断后台任务。
流式工具的生产环境注意事项
- 单次工具调用:由于流式工具会跨多个轮次推送更新,如果被反复提示,模型偶尔可能会尝试再次调用该工具。请在 docstring 中保留明确的“仅调用一次”指令,并考虑在工具实现中加入内存闩锁或幂等键,以确保生产环境安全。
- 使用 `stop_streaming` 实现用户取消:ADK 为长时间运行的流式工具提供了内置的应急出口。通过在智能体的工具列表中包含一个名为
stop_streaming(function_name: str)的函数,ADK 会拦截该调用并取消生成器底层的asyncio.Task,在你的函数中抛出asyncio.CancelledError,以便你可以干净地关闭数据库连接或文件句柄。 - 用户插话不会取消工具:当用户打断智能体时,Live API 会停止模型的输出,ADK 会发出一个带有
interrupted=True的事件,以便你的客户端停止音频播放。正在运行的流式工具或后台工具不会被取消,其结果仍会被传递。要停止某个工具,模型必须调用stop_streaming。
模式 2:后台执行(当等待就只是等待时)
持续不断的进度叙述并不总是合适的。如果用户请求一份报告后立即转向另一个话题,那么一个不断用状态更新打断的智能体会让人感到烦扰。
当等待过程中没有故事可讲时,就在后台安静地运行任务。
方法 A:单次产出生成器(最快设置)
要获得即时的口头确认而不产生持续的进度絮叨,一个有效的方法是使用仅在完成时产出一次的异步生成器:
async def query_sales_database(region: str) -> AsyncGenerator[dict, None]:
"""Runs the quarterly sales report. Call this ONCE; results arrive when ready."""
report = await run_expensive_query(region) # Background execution (~13s)
yield report # Single yield upon completion纯文本
已复制
由于这是一个异步生成器,ADK 会立即发出一个待处理响应。智能体会迅速确认请求(“我正在为你整理那份报告”),在计算期间保持沉默,并在准备就绪时或用户说完话后呈现数据。
方法 B:使用 `response_scheduling` 的 `FunctionTool`
如果你希望保留标准协程(async def ... -> dict),请将函数包装在 FunctionTool 中并配置 response_scheduling:
from google.adk.tools import FunctionTool
from google.genai import types
sales_tool = FunctionTool(query_sales_database)
sales_tool.response_scheduling = types.FunctionResponseScheduling.WHEN_IDLE
(Note: In ADK 2.10+, you can also set `sales_tool.behavior = types.Behavior.NON_BLOCKING` to run a tool in the background without modifying scheduling mode.)纯文本
已复制
理解权衡: 当使用 response_scheduling 搭配只返回一次结果的协程时,模型在整个函数完成之前不会收到任何数据。因此,除非用户继续说话,模型通常不会先给出一次口头确认。当用户紧接着继续提出其他问题,而你希望结果在下一个自然停顿时融入对话时,这种模式最为理想。
使用 `response_scheduling` 控制结果投递
response_scheduling 属性控制模型何时对传入的工具响应做出反应。WHEN_IDLE 绝不会打断进行中的轮次:如果模型仍在说话或用户仍在讲话,结果会等到该轮次结束后再处理,模型会在下一个自然停顿时做出响应。ADK 支持三种调度行为:
在 gemini-3.8-live 的基准测试会话中:
- 在
WHEN_IDLE下,传入的销售报告礼貌地等待模型完成回答另一个 20 秒的问题,然后无缝地给出数据。 - 在
INTERRUPT下,传入的结果在句子中间打断了当前语音;随后模型完成了回答并追加了数据。 - 在
SILENT下,结果被添加到对话历史中,可供后续轮次使用,而不会触发未经请求的播报。
结合流式与调度
执行形态(流式与单次返回)和投递时机(response_scheduling)相互独立,可以组合使用。在流式工具上设置 response_scheduling 会为每个部分更新打上该行为标记。
此外,在 ADK 2.8 及更新版本中,流式工具可以通过产出 types.FunctionResponse 为每次更新动态指定调度:
# Acknowledge and keep intermediate progress quiet:
yield types.FunctionResponse(
response={"stage": "aggregating"},
scheduling=types.FunctionResponseScheduling.SILENT,
)
# Deliver the final result when the speaker finishes:
yield types.FunctionResponse(
response={"report": final_data},
scheduling=types.FunctionResponseScheduling.WHEN_IDLE,
)纯文本
已复制
架构总结:选择合适的工具模式
经验法则:如果等待有故事可讲,就流式呈现。如果等待只是等待,就在后台运行,让结果在空闲时落地。
结论
非阻塞工具并不会减少后端执行时间:一个 13 秒的查询仍然需要 13 秒。改变的是你的智能体在对话中的存在感。
一个需要十秒才能加载的网页应用会被认为很慢;一个沉默十秒的对话式语音智能体则会被认为出了故障。通过将 ADK 的流式工具和灵活的响应调度与 gemini-3.8-live 的实时能力相结合,你可以构建出在复杂操作全程中始终保持专注、自然且在场的语音助手。
亲自试一试: 交互式演示 在 gemini-3.8-live 上通过每种工具模式和 response_scheduling 模式运行同一个 13 秒查询,让你能听出差异。
要求与后续步骤
- 框架:
google-adk >= 2.10.0 - 模型:
gemini-3.8-live在 Gemini Enterprise 上 - API 环境: 流式工具和实时调度需要双向实时会话(
run_live)。
参考资料
上一页
下一页
来源:Google Developers Blog · developers.googleblog.com