run_live() 深度解析
文档版本 : v1.0
最后更新 : 2026-01-26
适用对象 : 开发者、架构师
前置知识: Python async/await、WebSocket 基础
目录
- 一句话总结
- 本质:异步生成器
- [工作原理:双向 WebSocket 连接](#工作原理:双向 WebSocket 连接)
- 与其他方法对比
- 核心概念:LiveRequestQueue
- 完整的工作流程
- 实际代码示例
- 常见问题
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 会自动:
- 停止当前响应
- 清空输出缓冲
- 开始处理新输入
你不需要显式调用打断方法。
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 核心机制
-
双向通信:
- 上行:
LiveRequestQueue→ Live API - 下行:Live API →
async for event
- 上行:
-
自动管理:
- WebSocket 连接自动建立
- 消息自动转换
- 断线自动重连 (如果配置了 session_resumption)
-
事件驱动:
- 每次 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,数据持续不断地流动。你只需要:
- 通过
LiveRequestQueue往水管里"注水" (发送消息) - 通过
async for从水管里"接水" (接收事件) - 用完记得
queue.close()关闭水龙头
就这么简单! 🎉