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.355.41.88.121.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 或日志中暴露长期密钥。

官方来源

  1. Gemini Live API Official
  2. Gemini API Documentation Official
  3. Gemini Interactions API Overview Official
  4. Gemini API Getting Started Official
  5. Migrate to the Interactions API Official
  6. Gemini API Streaming Official
  7. Gemini Function Calling Official
  8. Gemini API Errors Official
  9. Gemini GenerateContent API Errors Official