Skip to content

实时智能体的事件

Supported in ADKPython v0.1.0

实时智能体产生的所有内容都会作为 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_transcriptioncontent.role == 'user' 时,ADK 会设置 author="user";检查转录内容是确保归属可靠的关键,因为输入转录响应并不总是携带 role == 'user'base_llm_flow.py)。

使用智能体名称可以让你在多智能体会话中按作者过滤事件:

events = [e for e in stream if e.author == "billing_agent"]

事件类型

在实时会话中,智能体通过多种不同的事件类型传递其连续输出,包括增量文本、音频、语音转录、工具调用和 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_transcriptionevent.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_countcandidates_token_counttotal_token_countcached_content_token_count),用于实时成本和配额跟踪。

流式标志

三个标志驱动实时 UI:partialturn_completeinterruptedpartial 标志区分增量片段和合并结果:

  • 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_completeinterrupted 告诉你的 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_codeevent.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 还是正常结束都会执行。
错误代码 类别 操作
SAFETYPROHIBITED_CONTENTBLOCKLIST 内容策略 break——模型已终止响应
MAX_TOKENS 限制 break——模型已完成生成
UNAVAILABLEDEADLINE_EXCEEDED 临时性 continue——网络或超时问题,可能会自行恢复
RESOURCE_EXHAUSTED 速率限制 continue 并使用指数退避
CANCELLED 客户端 break——进行清理
UNKNOWN 系统 continue 并记录日志

对于不到一秒的临时性错误,不要通知用户。对于 RESOURCE_EXHAUSTED,要进行退避并限制重试次数,以免无限循环。错误代码来自 Gemini API;参见 FinishReasonAgent Platform 参考文档

向客户端发送事件

要将事件流式传输到浏览器或移动客户端,需要序列化事件并通过你的传输层发送。Event 是一个 Pydantic 模型,因此 model_dump_json() 即可完成序列化;base64 编码的音频会使 JSON 膨胀约 33%,因此应以二进制帧的形式发送音频。序列化模式和对应的客户端处理逻辑都包含在自定义服务器中。