【ADK】run_live() 深度解析

run_live() 深度解析

文档版本 : v1.0
最后更新 : 2026-01-26
适用对象 : 开发者、架构师
前置知识: Python async/await、WebSocket 基础


目录

  1. 一句话总结
  2. 本质:异步生成器
  3. [工作原理:双向 WebSocket 连接](#工作原理:双向 WebSocket 连接)
  4. 与其他方法对比
  5. 核心概念:LiveRequestQueue
  6. 完整的工作流程
  7. 实际代码示例
  8. 常见问题

1. 一句话总结

run_live() 是一个"双车道管道":

  • 上行车道 :你的应用 → Live API (通过 LiveRequestQueue)
  • 下行车道:Live API → 你的应用 (通过 async generator)

2. 本质:异步生成器

2.1 它是什么?

python 复制代码
async def run_live(
    self,
    user_id: str,
    session_id: str,
    live_request_queue: LiveRequestQueue,  # 上行通道
    run_config: RunConfig
) -> AsyncGenerator[Event, None]:  # ← 关键:返回异步生成器
    """
    这是一个异步生成器函数
    它会持续不断地"吐出" Event 对象
    """
    # 内部实现:
    # 1. 连接到 Gemini Live API (WebSocket)
    # 2. 持续监听 Live API 的消息
    # 3. 把消息转换成 Event 对象
    # 4. yield 出来给你的应用

    while live_api_is_connected:
        event = await receive_from_live_api()
        yield event  # ← 就像自来水管不断流出水

2.2 如何使用?

python 复制代码
# 你的代码
async for event in runner.run_live(
    user_id="user123",
    session_id="sess456",
    live_request_queue=queue,
    run_config=config
):
    # 每次迭代,都会收到一个新的 Event
    # 就像从水管接水一样

    print(f"收到事件: {event}")
    await websocket.send(event_json)

3. 工作原理:双向 WebSocket 连接

3.1 内部架构

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    你的应用 (FastAPI)                        │
│                                                               │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ WebSocket 端点                                      │    │
│  │                                                      │    │
│  │  ┌──────────────────┐      ┌─────────────────────┐ │    │
│  │  │ Upstream Task    │      │ Downstream Task     │ │    │
│  │  │ (接收客户端消息)  │      │ (发送给客户端)       │ │    │
│  │  └────────┬─────────┘      └──────────┬──────────┘ │    │
│  │           │                            │             │    │
│  │           ▼                            ▼             │    │
│  │  ┌──────────────────┐      ┌─────────────────────┐ │    │
│  │  │LiveRequestQueue  │      │  run_live()         │ │    │
│  │  │ send_content()   │      │  async for event    │ │    │
│  │  └────────┬─────────┘      └──────────┬──────────┘ │    │
│  └───────────┼───────────────────────────┼─────────────┘    │
│              │                           │                   │
└──────────────┼───────────────────────────┼───────────────────┘
               │                           │
               ▼                           ▼
┌─────────────────────────────────────────────────────────────┐
│                   ADK Runner 层                             │
│                                                               │
│         LiveRequestQueue         ←→         run_live()      │
│         (上行数据)                            (下行数据)      │
└─────────────────────────────────────────────────────────────┘
               │                           │
               ▼                           ▼
┌─────────────────────────────────────────────────────────────┐
│                  Gemini Live API                            │
│                  (WebSocket 服务器)                          │
└─────────────────────────────────────────────────────────────┘

3.2 数据流向详解

上行 (你的应用 → Live API)
python 复制代码
# 1. 客户端发送消息
websocket.send(JSON.stringify({
    type: "text",
    text: "你好"
}))

# 2. 服务端接收
message = await websocket.receive()
json_message = json.loads(message["text"])

# 3. 发送到 LiveRequestQueue
content = types.Content(
    parts=[types.Part(text=json_message["text"])]
)
live_request_queue.send_content(content)
# ↑ 这个数据被放入队列,等待 ADK 取走

# 4. ADK 内部自动处理
#    - 从 LiveRequestQueue 读取消息
#    - 转发给 Live API
#    - 你不需要写这部分代码
下行 (Live API → 你的应用)
python 复制代码
# 1. Live API 生成响应
#    (内部处理,你不需要管)

# 2. ADK 接收响应并转换成 Event

# 3. run_live() yield 出 Event
async for event in runner.run_live(...):
    # ← 这里每次迭代,都会收到一个新的 Event

    # 4. 你处理这个 Event
    if event.content:
        await websocket.send_text(event.model_dump_json())

4. 与其他方法对比

4.1 三种方法对比

特性 run() run_async() run_live()
连接方式 HTTP (一次请求) HTTP (异步) WebSocket (持久连接)
交互模式 一问一答 一问一答 持续双向流
输入 单个消息 单个消息 持续输入流
输出 生成器 异步生成器 异步生成器
音频支持 ❌ ❌ ✅
视频支持 ❌ ❌ ✅
打断功能 ❌ ❌ ✅
延迟 秒级 秒级 毫秒级
使用场景 测试脚本 Web 聊天 实时语音/视频

4.2 代码对比

run() - 同步版本
python 复制代码
# 适合简单的测试脚本
for event in runner.run(
    user_id="user123",
    session_id="sess456",
    new_message="你好"  # ← 一次性输入
):
    print(event.content)
# ← 发完就结束了
run_async() - 异步版本
python 复制代码
# 适合 Web 聊天应用
async for event in runner.run_async(
    user_id="user123",
    session_id="sess456",
    new_message="你好"  # ← 一次性输入
):
    await websocket.send_json(event)
# ← 发完就结束了
run_live() - 实时流式版本
python 复制代码
# 适合实时语音对话
queue = LiveRequestQueue()

# 持续发送音频
async def send_audio():
    while True:
        audio_chunk = await microphone.read()
        queue.send_realtime(audio_chunk)  # ← 持续输入

# 持续接收响应
async for event in runner.run_live(
    user_id="user123",
    session_id="sess456",
    live_request_queue=queue  # ← 持续输出
):
    if event.content:
        await speaker.play(event.content.audio)
# ← 一直运行,直到 queue.close()

5. 核心概念:LiveRequestQueue

5.1 它是做什么的?

LiveRequestQueue 是一个线程安全的队列 ,用于向 run_live() 发送数据。

5.2 为什么需要它?

因为 run_live() 是一个异步生成器,它一直在"吐出"数据,你无法直接往它里面"塞"数据。

所以需要一个队列作为缓冲区:

复制代码
你的应用 → [队列] → run_live() → Live API
         ↑
    LiveRequestQueue

5.3 如何使用?

python 复制代码
# 1. 创建队列
queue = LiveRequestQueue()

# 2. 发送文本
queue.send_content(
    types.Content(parts=[types.Part(text="你好")])
)

# 3. 发送音频
queue.send_realtime(
    types.Blob(
        mime_type="audio/pcm;rate=16000",
        data=audio_bytes
    )
)

# 4. 关闭队列(结束会话)
queue.close()

5.4 Queue 与 run_live() 的关系

Aspect LiveRequestQueue run_live()
方向 上行 (User → Model) 下行 (Model → User)
类型 同步方法 异步生成器
用法 send_content(), send_realtime() async for event in run_live()
生命周期 手动创建,手动关闭 自动启动,自动结束
并发性 线程安全 单次迭代

6. 完整的工作流程

6.1 时序图

复制代码
时间轴 →

0ms:    [你的代码] 创建 LiveRequestQueue
        queue = LiveRequestQueue()

10ms:   [你的代码] 启动 run_live()
        async for event in runner.run_live(..., queue):
            # ← 这里还没开始,因为 Live API 还没响应

20ms:   [ADK 内部] 连接到 Live API (WebSocket)
        ← WebSocket connection established →

30ms:   [你的代码] 客户端发送消息
        websocket.send("你好")

40ms:   [你的代码] 接收并转发到队列
        queue.send_content(Content(text="你好"))

50ms:   [ADK 内部] 从队列读取消息
        ← 队列 → Live API

60ms:   [Live API] Gemini 处理
        ... 理解文本,生成回复 ...

100ms:  [Live API] 开始返回响应
        → WebSocket: audio_chunk_1

110ms:  [run_live()] 收到响应,yield 出 Event
        async for event in runner.run_live(...):
            # ← 第一次迭代:event.content.audio = chunk_1

120ms:  [你的代码] 处理 Event
        await websocket.send(audio_chunk_1)

130ms:  [Live API] 继续返回
        → WebSocket: audio_chunk_2

140ms:  [run_live()] yield 第二个 Event
        async for event in runner.run_live(...):
            # ← 第二次迭代:event.content.audio = chunk_2

... 循环继续,直到对话结束 ...

最后:    [你的代码] 关闭队列
        queue.close()
        ← run_live() 停止迭代

6.2 并发执行模式

python 复制代码
# 关键:上行和下行必须并发执行

async def upstream_task():
    """上行任务:接收客户端消息,发送到队列"""
    while True:
        message = await websocket.receive()
        queue.send_content(message)

async def downstream_task():
    """下行任务:从 run_live() 接收事件,发送给客户端"""
    async for event in runner.run_live(..., queue):
        await websocket.send(event)

# 并行运行
await asyncio.gather(
    upstream_task(),
    downstream_task()
)

7. 实际代码示例

7.1 完整的 WebSocket 端点

python 复制代码
# main.py

from fastapi import WebSocket
from google.adk.agents.live_request_queue import LiveRequestQueue
from google.adk.runners import Runner
from google.genai import types
import json

@app.websocket("/ws/{user_id}/{session_id}")
async def websocket_endpoint(
    websocket: WebSocket,
    user_id: str,
    session_id: str
):
    await websocket.accept()

    # 1. 准备 LiveRequestQueue
    queue = LiveRequestQueue()

    # 2. 创建上行任务 (接收客户端消息)
    async def upstream():
        while True:
            message = await websocket.receive()

            if "text" in message:
                # 文本消息
                json_msg = json.loads(message["text"])
                content = types.Content(
                    parts=[types.Part(text=json_msg["text"])]
                )
                queue.send_content(content)

            elif "bytes" in message:
                # 音频数据
                blob = types.Blob(
                    mime_type="audio/pcm;rate=16000",
                    data=message["bytes"]
                )
                queue.send_realtime(blob)

    # 3. 启动 run_live() (下行任务)
    async def downstream():
        async for event in runner.run_live(
            user_id=user_id,
            session_id=session_id,
            live_request_queue=queue,
            run_config=run_config
        ):
            # 处理每个 Event
            event_json = event.model_dump_json(
                exclude_none=True,
                by_alias=True
            )
            await websocket.send_text(event_json)

    # 4. 并行运行两个任务
    try:
        await asyncio.gather(upstream(), downstream())
    except WebSocketDisconnect:
        print("客户端断开连接")
    finally:
        # 5. 清理资源
        queue.close()

7.2 带异步任务的版本

python 复制代码
@app.websocket("/ws/{user_id}/{session_id}")
async def websocket_endpoint(websocket: WebSocket, user_id: str, session_id: str):
    await websocket.accept()

    queue = LiveRequestQueue()

    # 上行任务
    async def upstream():
        while True:
            message = await websocket.receive()
            # ... 处理消息 ...
            queue.send_content(content)

    # 下行任务 + 异步任务
    async def downstream():
        async for event in runner.run_live(...):
            # 发送标准事件
            await websocket.send_text(event.model_dump_json())

            # 触发异步任务 (不阻塞主流程)
            if event.content:
                asyncio.create_task(
                    handle_async_tasks(event, user_id, websocket)
                )

    # 并行运行
    await asyncio.gather(upstream(), downstream())

async def handle_async_tasks(event, user_id: str, websocket: WebSocket):
    """并行执行异步任务"""

    # 提取响应文本
    response_text = ""
    if event.content and event.content.parts:
        response_text = event.content.parts[0].text

    # 并行执行多个任务
    tasks = [
        task_save_conversation(user_id, response_text),
        task_analyze_emotion(response_text),
        task_send_custom_event(response_text, websocket)
    ]

    await asyncio.gather(*tasks, return_exceptions=True)

async def task_save_conversation(user_id: str, text: str):
    """保存对话日志"""
    async with asyncpg.connect(DATABASE_URL) as conn:
        await conn.execute("""
            INSERT INTO logs (user_id, content, timestamp)
            VALUES ($1, $2, NOW())
        """, user_id, text)

async def task_send_custom_event(text: str, websocket: WebSocket):
    """发送自定义事件"""
    await websocket.send_text(json.dumps({
        "event_type": "text_content",
        "data": {"text": text}
    }))

8. 常见问题

8.1 为什么不能直接往 run_live() 发送数据?

答 : 因为 run_live() 是一个异步生成器,它只能"吐出"数据,不能"接收"数据(除了通过参数)。

所以需要一个中间队列 LiveRequestQueue 作为缓冲:

复制代码
你的应用 → [队列] → run_live()
         ↑
    只能往这里塞数据

8.2 什么时候关闭 LiveRequestQueue?

答 : 当会话结束时,必须调用 queue.close(),否则:

  • ❌ run_live() 会一直等待(永远不会结束)
  • ❌ WebSocket 连接会一直占用资源
  • ❌ 可能导致内存泄漏

正确做法:

python 复制代码
try:
    await asyncio.gather(upstream(), downstream())
finally:
    queue.close()  # ← 确保一定会关闭

8.3 run_live() 会阻塞吗?

答: 不会!

run_live() 是一个异步生成器,它:

  • ✅ 只在有事件时才 yield
  • ✅ 没有事件时自动 await(不占用 CPU)
  • ✅ 可以与其他任务并发执行
python 复制代码
# 这样不会阻塞
async def downstream():
    async for event in runner.run_live(...):
        # 只有收到事件时才执行
        await websocket.send(event)
        # 执行完立即返回,等待下一个事件

8.4 如何打断 AI 的回复?

答: 只需要发送新的消息!

复制代码
用户说话 → queue.send_content() → 自动打断 AI

Live API 会自动:

  1. 停止当前响应
  2. 清空输出缓冲
  3. 开始处理新输入

你不需要显式调用打断方法。

8.5 run_live() 支持哪些事件?

答: 支持以下事件类型:

事件类型 说明 示例
content 文本/音频内容 event.content.parts[0].text
input_transcription 用户语音转文字 event.input_transcription.text
output_transcription AI 语音转文字 event.output_transcription.text
tool_call 工具调用 event.tool_call.name
tool_call_result 工具返回结果 event.tool_call_result.result
turn_complete 轮次完成 event.turn_complete == True
interrupted 打断确认 event.interrupted == True
setup_complete 连接建立 event.setup_complete
error 错误信息 event.error

8.6 如何调试 run_live()?

答: 添加日志:

python 复制代码
async def downstream():
    async for event in runner.run_live(...):
        # 记录所有事件
        logger.info(f"收到事件: {event.model_dump()}")

        # 分类处理
        if event.content:
            logger.debug("Content 事件")
        elif event.setup_complete:
            logger.debug("连接建立")
        elif event.turn_complete:
            logger.debug("轮次完成")
        elif event.error:
            logger.error(f"错误: {event.error}")

9. 总结

9.1 核心要点

特性 答案
类型 异步生成器
输入 通过 LiveRequestQueue
输出 持续 yield Event 对象
连接 自动管理 WebSocket 到 Live API
生命周期 从调用到 queue.close()
特点 双向流式、低延迟、支持音频/视频

9.2 核心机制

  1. 双向通信:

    • 上行:LiveRequestQueue → Live API
    • 下行:Live API → async for event
  2. 自动管理:

    • WebSocket 连接自动建立
    • 消息自动转换
    • 断线自动重连 (如果配置了 session_resumption)
  3. 事件驱动:

    • 每次 Live API 有响应就 yield
    • 没有响应就等待 (不占用 CPU)

9.3 为什么用它?

✅ 实时语音对话 (像打电话)

✅ 超低延迟 (毫秒级)

✅ 支持打断 (随时说话打断 AI)

✅ 多模态 (音频 + 文本 + 视频)

✅ 持续会话 (可以聊几个小时)

9.4 关键代码模式

python 复制代码
# 标准模式
queue = LiveRequestQueue()

async def upstream():
    while True:
        message = await websocket.receive()
        queue.send_content(message)

async def downstream():
    async for event in runner.run_live(..., queue):
        await websocket.send(event)

await asyncio.gather(upstream(), downstream())

总结 :run_live() 就像一根双向水管,一端连接你的应用,一端连接 Live API,数据持续不断地流动。你只需要:

  1. 通过 LiveRequestQueue 往水管里"注水" (发送消息)
  2. 通过 async for 从水管里"接水" (接收事件)
  3. 用完记得 queue.close() 关闭水龙头

就这么简单! 🎉

相关推荐
NPE~3 个月前
[AI]Agent开发——ADK框架使用
人工智能·python·ai·教程·adk·agent开发
1941s7 个月前
Google Agent Development Kit (ADK) 指南 第五章:工具集成与自定义
人工智能·python·langchain·agent·adk
1941s7 个月前
Google Agent Development Kit (ADK) 指南 第六章:记忆与状态管理
人工智能·python·agent·adk·google agent
1941s7 个月前
Google Agent Development Kit (ADK) 指南 第四章:Agent 开发与编排
人工智能·python·langchain·agent·adk
1941s7 个月前
Google Agent Development Kit (ADK) 指南 第三章:核心概念与架构
人工智能·python·langchain·agent·adk
1941s7 个月前
Google Agent Development Kit (ADK) 指南 第二章:环境搭建与快速开始
人工智能·python·adk·google agent
zhojiew7 个月前
使用opensandbox结合ADK创建agent并测试沙盒环境
agent·sandbox·adk
wumingxiaoyao9 个月前
AI - Agent 到底怎么“接模型”?怎么认证?公司网关/本地模型怎么接?
agent·gemini·litellm·adk·ai model
wumingxiaoyao9 个月前
AI - ParallelAgent 实战:用并行工作流做一个「多主题 Web 调研」Agent
人工智能·ai·adk·parallelagent·workflow agent