api
Gemini API 调用指南:GenerateContent、Live 与 Interactions
直接答案本站提供 GenerateContent 与 gemini-3.8-live 的 /v1beta/live 原生 WebSocket;Live 使用 setup 与 serverContent 协议,只支持语音输出,可开启配套转写。
更新 · 审核信息
先区分官方推荐与本站兼容范围
Google 已在 2026 年 6 月把 Interactions API 设为 GA,并推荐所有新项目使用;原 generateContent 仍受支持,但已归为 Legacy。本站是否支持某个端点取决于网关适配,不能因为 Google 已发布 /v1beta/interactions 就假设本站也已开放。本站已适配 GenerateContent,以及下文 gemini-3.8-live 的原生 Live WebSocket;它们分别使用 HTTP 与 WebSocket 协议。
通过本站调用 GenerateContent
本站推荐使用 Authorization: Bearer;为兼容 Gemini 原生客户端,/v1beta/models/... 路径也接受本站密钥形式的 x-goog-api-key。模型名位于 URL 中并应来自模型广场。contents[].parts 可组合文本和模型支持的多模态输入;不要发送官方单模型页未声明的内容类型。
调用前从模型广场复制明确列出 GenerateContent 路径的精确模型 ID:
export BASE_URL="https://api.tu-zi.com"
export API_KEY="你的本站 API Key"
export GEMINI_MODEL_NAME="支持 GenerateContent 的精确模型 ID"
curl "$BASE_URL/v1beta/models/$GEMINI_MODEL_NAME:generateContent" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"contents":[{"role":"user","parts":[{"text":"只回复:连接成功"}]}]}'
直连 Google 的 Interactions 形态
直连 Google 使用 x-goog-api-key。Interactions 调用 POST /v1beta/interactions,把 model 和 input 放入 JSON;响应包含 id、status、usage 与类型化 steps[]。SDK 的 output_text 适合读取简单最终文本,遇到思考、工具、图片或音频交错时必须按 type 遍历 steps。
多轮可用 previous_interaction_id,但工具、system instruction 和 generation config 不会自动继承,应在后续请求中重发。store=false 适合不希望服务端保存交互的场景,但不能再依赖已存储交互续接。
两套响应和流式事件不能混用
GenerateContent 返回 candidates、finishReason、promptFeedback 和安全信息;Interactions 使用 interaction.created、step.start/delta/stop 与 interaction.completed 等类型化 SSE 事件。函数调用在新协议中也是独立 step,不再是简单替换 function response part。HTTP 200 后仍可能出现流内错误,客户端要容忍未知事件并等待明确完成事件。
错误与重试
401 检查认证,403 检查权限,404 检查模型或资源,429 可能是速率或配额。先读错误 status 和 message 再处理 5xx:过载或临时服务故障可有限退避,但 GenerateContent 的输入或上下文过长也可能表现为 500 或 504,此时必须缩短请求而不是重试。参数、安全拦截和配额问题同样需要修正请求或账户。保存请求 ID、错误 code、status 与最终 step,参见 API 报错排查。
迁移前必须核验的边界
Interactions 尚未覆盖 GenerateContent 的全部能力,例如部分视频 metadata、Batch、显式缓存和自定义安全设置。迁移前逐项核验目标型号、/v1 或 /v1beta、文件限制、工具、结构化输出、存储策略与数据政策,并分别保留两套契约测试;不要只改 URL。
gemini-3.8-live 原生 WebSocket
Gemini 类型渠道的 Base URL 只填写上游根地址,渠道模型填写 gemini-3.8-live;令牌分组也必须启用该模型。调用本站 GET /v1beta/live?model=gemini-3.8-live 升级为 WebSocket,例如 wss://api.tu-zi.com/v1beta/live?model=gemini-3.8-live。握手使用本站 Authorization: Bearer。这是 Gemini Live 原生消息协议,不接受 GenerateContent 请求体、OpenAI Realtime 事件或 HTTP POST/SSE 调用;本站不提供省略 model 的 Google SDK WebSocket 路径。
第一条消息必须为 setup,其中 model 为 models/gemini-3.8-live。该型号只支持 responseModalities: ["AUDIO"];设置 outputAudioTranscription: {} 后可同时读取 serverContent.outputTranscription.text。必须等到 setupComplete 才发送输入。
最小 Python 示例:文本输入、WAV 和转写输出
Python 3.11+,在可信服务端执行。依赖:python3 -m pip install "websockets>=14,<16"。配置 BASE_URL 为本站 HTTPS 根地址,API_KEY 为本站令牌:
export BASE_URL="https://api.tu-zi.com"
export API_KEY="你的本站 API Key"
脚本逐块写入 reply.wav(24 kHz、PCM16、单声道)和 reply.txt,不在内存中缓存完整音频或文字。单条消息最大 2 MiB、接收队列最多 4 条、总时限 120 秒;异常退出时文件可能不完整,调用方应标记未完成。
import asyncio, base64, json, os, wave
from urllib.parse import urlencode, urlsplit, urlunsplit
from websockets.asyncio.client import connect
async def main():
base = urlsplit(os.environ["BASE_URL"].rstrip("/"))
if base.scheme not in ("https", "http", "wss", "ws"):
raise ValueError("BASE_URL must be an HTTP(S) or WS(S) origin")
scheme = "wss" if base.scheme in ("https", "wss") else "ws"
url = urlunsplit((scheme, base.netloc, "/v1beta/live", urlencode({"model": "gemini-3.8-live"}), ""))
headers = {"Authorization": "Bearer " + os.environ["API_KEY"]}
async with asyncio.timeout(120):
async with connect(url, additional_headers=headers, max_size=2*1024*1024,
max_queue=4, compression=None, open_timeout=10,
close_timeout=5, ping_interval=20, ping_timeout=20) as ws:
await ws.send(json.dumps({"setup": {"model": "models/gemini-3.8-live",
"generationConfig": {"responseModalities": ["AUDIO"]},
"outputAudioTranscription": {}}}))
while True:
event = json.loads(await asyncio.wait_for(ws.recv(), timeout=30))
if "error" in event:
raise RuntimeError(event["error"])
if "setupComplete" in event:
break
await ws.send(json.dumps({"clientContent": {"turns": [{"role": "user",
"parts": [{"text": "Say hello briefly."}]}], "turnComplete": True}}))
with wave.open("reply.wav", "wb") as audio, open("reply.txt", "w", encoding="utf-8") as transcript:
audio.setnchannels(1)
audio.setsampwidth(2)
audio.setframerate(24000)
while True:
event = json.loads(await asyncio.wait_for(ws.recv(), timeout=30))
if "error" in event:
raise RuntimeError(event["error"])
content = event.get("serverContent", {})
for part in content.get("modelTurn", {}).get("parts", []):
inline = part.get("inlineData", {})
if inline.get("mimeType", "").startswith("audio/pcm"):
audio.writeframesraw(base64.b64decode(inline["data"], validate=True))
text = content.get("outputTranscription", {}).get("text", "")
if text:
transcript.write(text)
transcript.flush()
if content.get("turnComplete"):
print(json.dumps(event.get("usageMetadata", {}), ensure_ascii=False))
break
asyncio.run(main())
图片、实时音频与视频帧输入
文字和 JPEG 图片可组合在 clientContent.turns[].parts 中:
{"clientContent":{"turns":[{"role":"user","parts":[{"text":"描述这张图片"},{"inlineData":{"mimeType":"image/jpeg","data":"<JPEG_BASE64>"}}]}],"turnComplete":true}}
会话内也可以发送 realtimeInput。音频为 16 kHz、PCM16 单声道,分小块 Base64 编码;视频按帧发送 JPEG,限制帧率、分辨率和帧大小。音频流结束时发送 audioStreamEnd。持续接收模型输出,不要把整段音视频读入内存。
{"realtimeInput":{"audio":{"mimeType":"audio/pcm;rate=16000","data":"<PCM16_BASE64>"}}}
{"realtimeInput":{"video":{"mimeType":"image/jpeg","data":"<JPEG_BASE64>"}}}
{"realtimeInput":{"audioStreamEnd":true}}
MOV 长视频:按时间顺序解说
安装 ffmpeg 后,在上方 Python 示例中添加以下函数;将文本输入的 await ws.send(...) 替换为 await send_video(ws, os.environ["VIDEO_PATH"]),并将外层总时限改为 asyncio.timeout(600)。仍先等待 setupComplete,然后沿用现有接收循环逐块写 WAV 和转写;不要在发完视频后提前关闭连接。
export VIDEO_PATH="/path/to/movie.mov"
async def send_video(ws, video_path):
import pathlib, tempfile
# Bound video input to 400 JPEG frames, 640 px wide, at 5-second intervals.
with tempfile.TemporaryDirectory(prefix="gemini-live-") as directory:
ffmpeg = await asyncio.create_subprocess_exec(
"ffmpeg", "-v", "error", "-threads", "2", "-i", video_path,
"-vf", "fps=1/5:start_time=0,scale=640:-2", "-frames:v", "400",
"-threads", "2", "-q:v", "5", directory + "/frame_%05d.jpg",
stdout=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.DEVNULL)
try:
returncode = await asyncio.wait_for(ffmpeg.wait(), timeout=120)
finally:
if ffmpeg.returncode is None:
ffmpeg.kill()
await ffmpeg.wait()
if returncode:
raise RuntimeError("ffmpeg extraction failed")
frames = sorted(pathlib.Path(directory).glob("frame_*.jpg"))
if not frames:
raise ValueError("No video frames extracted")
for index, filename in enumerate(frames):
# Only one bounded image and one JSON message are resident at a time.
with filename.open("rb") as image:
jpeg = image.read(1024 * 1024 + 1)
if len(jpeg) > 1024 * 1024:
raise ValueError("JPEG exceeds 1 MiB; reduce resolution")
seconds = index * 5
timestamp = f"{seconds // 3600:02d}:{seconds % 3600 // 60:02d}:{seconds % 60:02d}"
await ws.send(json.dumps({"clientContent": {"turns": [{
"role": "user", "parts": [{"text": f"[{timestamp}]"},
{"inlineData": {"mimeType": "image/jpeg",
"data": base64.b64encode(jpeg).decode("ascii")}}]}],
"turnComplete": False}}))
await ws.send(json.dumps({"clientContent": {"turns": [{"role": "user",
"parts": [{"text": "以上是一段视频按时间顺序每 5 秒截取的画面,方括号里是该画面在视频中的时间。请按时间顺序解说这段视频。"}]}],
"turnComplete": True}}))
示例按 5 秒间隔抽取整个视频画面,最多 400 帧(约 33 分钟;更长视频应分段或调大间隔)。所有中间消息均为 turnComplete: false,仅最后的提示词结束该轮。它分析抽样画面,不发送原视频音轨;若需要理解对白,需另行按 16 kHz PCM16 小块发送音频。结果以模型实际返回为准,可能只是简短概述;需要逐段详细解说时应明确指定分段粒度。
逐轮用量与计费
一个 WebSocket 连接计一次请求。每轮仅在 serverContent.turnComplete=true 时按该轮 usageMetadata 结算;中途断开、未完成的轮次不收费,已完成轮次仍收费。每轮输入含此前会话上下文,因此上下文会在后续轮次重复计入。用量字段包括 promptTokenCount、promptTokensDetails、responseTokenCount、responseTokensDetails,可能还含 thoughtsTokenCount;按模态核对,不要把逐轮字段误当累计快照重复相加。
总数与模态明细可能不相等:未细分的输入余额按文本计价,thoughtsTokenCount 另计文本输出,不因 totalTokenCount 未包含思考而丢弃。账单以归一化后的五类 token、冻结价格和实际分组倍率为准。
以下为上游参考价格,单位为美元 / 百万 tokens;本站实际价格、分组倍率和优惠以管理台与使用日志为准。后台可使用现有内置计费表达式:
| Text input / 文本输入 | Audio input / 音频输入 | Image / video input / 图像视频输入 | Text output with thinking / 文本含思考输出 | Audio output / 音频输出 |
|---|---|---|---|---|
| 1.35 | 5.4 | 1.8 | 8.1 | 21.6 |
tier("live", p*1.35 + c*8.1 + img*1.8 + ai*5.4 + ao*21.6)
如部署启用了 versioned authoritative 计费,需在管理台创建、保存并发布该模型的计费版本后才能调用;表达式示例只是参考,不会自动创建或发布价格。
错误与关闭
握手失败时检查本站令牌、余额、模型可见性和渠道可用性。会话中处理错误消息及关闭码:1008 常见于额度不足或 setup/model 不合法,1011 为上游不可用,1001 为服务关闭。断线后停止输入并关闭资源;重连是新请求,不要自动重放已完成轮次。
适用场景
- 多模态理解
- 长上下文推理
- 文本与媒体生成
API 协议
/v1beta/models/{model}:generateContent/v1beta/models/{model}:streamGenerateContent/v1beta/live?model=gemini-3.8-live
FAQ
通过本站调用 Gemini 应该使用哪个端点?
GenerateContent 使用模型广场列出的原生 HTTP 路径;gemini-3.8-live 使用 GET /v1beta/live?model=gemini-3.8-live 升级为 WebSocket。本站当前不提供 /v1beta/interactions。
能否只把 generateContent 的 URL 改成 /interactions?
不能。Interactions 把 model 与 input 放入请求体,响应改为 status、steps 和 usage,流式事件与函数结果也不同,必须更换请求构造和解析器。
generateContent 已经不能使用了吗?
不是。Google 将其标为 Legacy,但仍明确表示继续支持;已有项目可按业务节奏迁移,并先核验 Interactions 尚未覆盖的能力。
为什么 HTTP 200 却没有可见文本?
检查 Interactions 的 status 与各类 steps,或 GenerateContent 的 candidates、finishReason、promptFeedback 和安全反馈;工具调用、拦截和流内错误都可能没有普通文本。
本站和直连 Google 的认证请求头一样吗?
本站推荐统一使用 Authorization: Bearer,也兼容 Gemini 原生路径的 x-goog-api-key;直连 Google 使用 x-goog-api-key。两种方式都不要在浏览器代码、URL 或日志中暴露长期密钥。
关联指南
官方来源
- Gemini Live API Official
- Gemini API Documentation Official
- Gemini Interactions API Overview Official
- Gemini API Getting Started Official
- Migrate to the Interactions API Official
- Gemini API Streaming Official
- Gemini Function Calling Official
- Gemini API Errors Official
- Gemini GenerateContent API Errors Official
兔子API