【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() 关闭水龙头

就这么简单! 🎉

相关推荐
wumingxiaoyao1 个月前
AI - Agent 到底怎么“接模型”?怎么认证?公司网关/本地模型怎么接?
agent·gemini·litellm·adk·ai model
wumingxiaoyao1 个月前
AI - ParallelAgent 实战:用并行工作流做一个「多主题 Web 调研」Agent
人工智能·ai·adk·parallelagent·workflow agent
casterQ2 个月前
4. Agent Quality ——【Google 5-Day AI Agents】
人工智能·llm·agent·langgraph·adk
技术便签8 个月前
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
人工智能·python·ai编程·agi·多智能体·智能体·adk