实时智能体的事件¶
实时智能体产生的所有内容都会作为 Event 传递到你的应用程序:模型在组合文本时的增量文本、原始音频字节、对话双方的转录、工具调用、Token 计数以及错误信息。一条语音回复可能会产生数十个事件,正确处理这些事件是让语音界面感觉即时响应而非延迟卡顿的关键。
Event 是 ADK 在所有地方使用的同一个类,文档参见 Events。实时会话会填充请求/响应智能体永远不会触及的字段——音频 Blob、转录、中断标志——并且持续不断地传递这些数据,而不是只传递一次。关于产出这些事件的循环,参见 Sessions。
实时智能体事件数据¶
Event 是一个继承自 LlmResponse 的 Pydantic 模型。实时会话使用以下字段:
| 字段 | 包含内容 |
|---|---|
content.parts[].text |
文本部分——在实时会话中,用于思维摘要和其他非语音内容 |
content.parts[].inline_data |
用于播放的原始音频字节(临时数据) |
content.parts[].file_data |
保存在制品服务中的音频引用(当 save_live_blob=True 时) |
content.parts[].function_call / function_response |
工具调用和结果(ADK 会自动执行这些) |
input_transcription / output_transcription |
用户和模型的语音转录文本 |
partial |
True 表示增量片段,False 表示合并后的完整结果 |
turn_complete |
当模型完成整个回复时为 True |
interrupted |
当用户在回复中途打断时为 True |
usage_metadata |
Token 计数,用于成本和配额跟踪 |
error_code / error_message |
故障诊断信息 |
author |
产生事件的来源(见下文) |
来源归属¶
在实时会话中,event.author 对于转录的用户语音为 "user",对于模型自身的输出为智能体的名称(而非字面量 "model")。当响应携带 input_transcription 或 content.role == 'user' 时,ADK 会设置 author="user";检查转录内容是确保归属可靠的关键,因为输入转录响应并不总是携带 role == 'user'(base_llm_flow.py)。
使用智能体名称可以让你在多智能体会话中按作者过滤事件:
事件类型¶
在实时会话中,智能体通过多种不同的事件类型传递其连续输出,包括增量文本、音频、语音转录、工具调用和 Token 使用元数据。以下各节描述这些事件类型。
文本¶
文本通过 event.content.parts[].text 传递。在实时会话中,这是思维摘要和其他非语音内容——模型的语音回复以输出转录的形式返回,而不是文本部分,因为 ADK 支持的每个实时模型都是接收音频输入并产生音频输出。
async for event in runner.run_live(...):
if event.content and event.content.parts:
for part in event.content.parts:
if part.text and not event.partial:
update_display(part.text)
遍历 parts,不要假设 parts[0]
单个事件可以携带多个部分,实时模型经常这样做。event.content.parts[0].text 会静默丢弃其余部分,并且当第一个部分不是文本时会出错(如思维摘要、函数调用、音频 Blob)。请遍历所有部分并根据设置的字段进行分支处理。
音频¶
当设置 response_modalities=["AUDIO"](实时模式的默认值)时,模型以 inline_data 的形式返回音频:
async for event in runner.run_live(...):
if event.content and event.content.parts:
for part in event.content.parts:
if part.inline_data: # 原始 PCM 字节
await play_audio(part.inline_data.data)
inline_data 是临时数据,不会被持久化。设置 save_live_blob=True 后,ADK 会将音频聚合到制品服务中的文件中,返回 file_data 引用而非原始字节(而非同时返回两者),以便你稍后检索音频。有关格式和播放的信息,参见音频和视频。
转录¶
当启用转录功能时(默认开启),用户和模型的语音通过 event.input_transcription 和 event.output_transcription 传递。它们以片段的形式流式传入:.text 包含最新的片段,.finished 标记当前轮次的最后一个片段,与 event.partial 相互对应。将片段拼接起来即可构建完整的转录文本。参见音频转录。
async for event in runner.run_live(...):
if event.input_transcription and event.input_transcription.text:
show_caption(event.input_transcription.text, is_user=True)
if event.output_transcription and event.output_transcription.text:
show_caption(event.output_transcription.text, is_user=False)
工具调用¶
模型通过 part.function_call 请求工具。ADK 会自动执行已注册的工具,因此你很少需要直接处理这些调用。参见自动工具执行。
元数据¶
event.usage_metadata 携带 Token 计数(prompt_token_count、candidates_token_count、total_token_count、cached_content_token_count),用于实时成本和配额跟踪。
流式标志¶
三个标志驱动实时 UI:partial、turn_complete 和 interrupted。partial 标志区分增量片段和合并结果:
partial=True:仅包含自上次事件以来的新文本。partial=False:当前片段的完整合并文本。
ADK 会为你累积这些片段(StreamingResponseAggregator),因此 partial=False 的事件已经包含了之前所有 partial=True 片段的总和。如果你不需要实时打字效果,可以忽略增量片段,只处理 partial=False 的事件。
Event 1: partial=True, text="Hello", turn_complete=False
Event 2: partial=True, text=" world", turn_complete=False
Event 3: partial=False, text="Hello world", turn_complete=False
Event 4: partial=False, text="", turn_complete=True
partial=False 在每轮中可能出现多次(例如每句话一次),而 turn_complete=True 在最后一个片段之后以单独的事件出现一次。
turn_complete 和 interrupted 告诉你的 UI 应进入什么状态:
| turn_complete | interrupted | 你的应用应执行 |
|---|---|---|
| True | False | 启用输入,显示"就绪" |
| False | True | 停止播放,清除增量内容 |
| True | True | 轮次结束;与正常完成相同 |
| False | False | 继续显示流式文本 |
async for event in runner.run_live(...):
if event.interrupted:
stop_audio_playback() # 用户打断;丢弃已排队的音频
clear_streaming_text()
if event.turn_complete:
enable_microphone() # 准备好接收下一轮
如果不处理 interrupted,已缓冲的音频会继续播放,盖过用户的声音。
错误处理¶
错误通过 event.error_code 和 event.error_message 呈现。需要做出的判断是模型的响应是否可以继续:当模型已停止时使用 break,当故障是临时性时使用 continue。
try:
async for event in runner.run_live(...):
if event.error_code:
logger.error("Model error: %s - %s", event.error_code, event.error_message)
if event.error_code in ("SAFETY", "PROHIBITED_CONTENT", "BLOCKLIST", "MAX_TOKENS"):
break # 模型已终止;本轮不再有事件。
continue # 临时性错误;流可能会恢复。
# ... 处理内容 ...
finally:
live_request_queue.close() # 无论是 break 还是正常结束都会执行。
| 错误代码 | 类别 | 操作 |
|---|---|---|
SAFETY、PROHIBITED_CONTENT、BLOCKLIST |
内容策略 | break——模型已终止响应 |
MAX_TOKENS |
限制 | break——模型已完成生成 |
UNAVAILABLE、DEADLINE_EXCEEDED |
临时性 | continue——网络或超时问题,可能会自行恢复 |
RESOURCE_EXHAUSTED |
速率限制 | continue 并使用指数退避 |
CANCELLED |
客户端 | break——进行清理 |
UNKNOWN |
系统 | continue 并记录日志 |
对于不到一秒的临时性错误,不要通知用户。对于 RESOURCE_EXHAUSTED,要进行退避并限制重试次数,以免无限循环。错误代码来自 Gemini API;参见 FinishReason 和 Agent Platform 参考文档。
向客户端发送事件¶
要将事件流式传输到浏览器或移动客户端,需要序列化事件并通过你的传输层发送。Event 是一个 Pydantic 模型,因此 model_dump_json() 即可完成序列化;base64 编码的音频会使 JSON 膨胀约 33%,因此应以二进制帧的形式发送音频。序列化模式和对应的客户端处理逻辑都包含在自定义服务器中。