Gemini · generateContent

Gemini API 接入教程:用 Tikway 调用 generateContent 协议

更新日期:

Gemini 原生协议将模型放在 URL 中,消息使用 contents[].parts[],与 OpenAI 的 model、messages 结构不同。本文演示最小 generateContent 请求、保存结果、提取候选文本,并说明多轮消息和流式动作路径。

本例用 Tikway API Key,通过 x-goog-api-key 鉴权。调用按使用量计费;先在模型目录确认所选型号的协议、输入输出能力和价格。

1. 把模型放入正确的 URL

采用协议文档中的示例:

text
POST https://api.tikway.ai/v1beta/models/gemini-2.5-flash:generateContent

gemini-2.5-flash 是文档示例标识,不代表任何时刻都可用。执行前确认目录中的当前调用 ID;如果模型资源名称已带 models/,不要与模板中的 /models/ 再拼接一次。指定命名空间或别名时按对应调用示例处理。

2. 保存请求并调用 generateContent

需要 Bash、curl 7.76 或更新版本,以及用于读取响应的 Python 3.10 或更新版本。Python 无需安装第三方包。在同一个终端设置 Key:

bash
export TIKWAY_API_KEY='替换为你的 Tikway API Key'

将以下内容保存为 04-gemini-request.json,或使用配套 JSON:

json
{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "请用三句话解释 API 网关在应用中的作用。"
        }
      ]
    }
  ]
}

在文件所在目录执行以下命令。它会发送真实模型请求,将响应体和响应头分别保存到本地;费用以账户及当前模型目录为准。

bash
: "${TIKWAY_API_KEY:?请先设置 Tikway API Key 环境变量}"

curl --fail-with-body --silent --show-error \
  --connect-timeout 10 --max-time 60 \
  --request POST \
  'https://api.tikway.ai/v1beta/models/gemini-2.5-flash:generateContent' \
  --header 'Content-Type: application/json' \
  --header "x-goog-api-key: ${TIKWAY_API_KEY}" \
  --data-binary '@04-gemini-request.json' \
  --dump-header 'gemini-response.headers' \
  --output 'gemini-response.json'

网关从 URL 取得模型,最小请求不需要根节点 model。如果应用同时传了该字段,应与路径保持一致,避免冲突。--fail-with-body 会保留 HTTP 错误响应并以非零退出码结束;网络失败时响应文件可能为空或残缺,所以读取前先确认请求状态。

3. 提取 candidates 中的普通文本

下面为人工构造的结构说明,不是实际模型输出:

json
{
  "candidates": [
    {
      "content": {"role": "model", "parts": [{"text": "示例回答。"}]},
      "finishReason": "STOP"
    }
  ],
  "usageMetadata": {"promptTokenCount": 100, "candidatesTokenCount": 40}
}

读取首个候选项的 content.parts,拼接其中普通 text 字段。有些响应包含工具调用或标记为 thought 的思考块,不能把所有块直接当作最终回答。候选项缺失时应读取反馈,而不是盲目索引 [0]。

保存以下代码为 04-read-gemini-response.py,或使用配套读取脚本。该脚本只读取本地 JSON:

python
"""Read a saved Gemini generateContent JSON response locally; no API calls."""

import json
from pathlib import Path
import sys


def extract_text(response):
    if not isinstance(response, dict):
        raise ValueError("响应必须是 JSON 对象。")
    if response.get("error"):
        raise ValueError("响应含有 error,请结合 HTTP 状态和响应头排查。")
    candidates = response.get("candidates")
    if not isinstance(candidates, list) or not candidates:
        feedback = response.get("promptFeedback", {})
        reason = feedback.get("blockReason", "未提供") if isinstance(feedback, dict) else "未提供"
        raise ValueError(f"没有候选项;promptFeedback.blockReason={reason}")
    candidate = candidates[0]
    if not isinstance(candidate, dict):
        raise ValueError("首个候选项不是 JSON 对象。")
    content = candidate.get("content", {})
    parts = content.get("parts", []) if isinstance(content, dict) else []
    if not isinstance(parts, list):
        raise ValueError("content.parts 不是数组。")
    text = "".join(
        part["text"] for part in parts
        if isinstance(part, dict) and part.get("thought") is not True
        and isinstance(part.get("text"), str)
    )
    if not text:
        raise ValueError(f"没有普通文本;finishReason={candidate.get('finishReason', 'unknown')}")
    return text


def main():
    path = Path(sys.argv[1] if len(sys.argv) > 1 else "gemini-response.json")
    response = json.loads(path.read_text(encoding="utf-8"))
    print(extract_text(response))
    reason = response["candidates"][0].get("finishReason")
    if reason == "MAX_TOKENS":
        print("注意:达到输出上限,回答可能被截断。", file=sys.stderr)


if __name__ == "__main__":
    try:
        main()
    except (OSError, ValueError, UnicodeError) as exc:
        print(f"读取失败:{exc}", file=sys.stderr)
        sys.exit(1)

执行:

bash
python 04-read-gemini-response.py gemini-response.json

finishReason == "MAX_TOKENS" 可能表示回答触及输出上限;空候选项时,脚本会显示 promptFeedback.blockReason(如果有)。这些分支有助于区分内容被限制、没有普通文本和文件结构错误。

4. 多轮内容与系统指令

Gemini 多轮回复使用 model 角色,用户使用 user。系统指令放入根节点 systemInstruction.parts。下面只展示需要合入完整请求的字段,助手内容为人工示例:

json
{
  "systemInstruction": {"parts": [{"text": "回答简洁,使用中文。"}]},
  "contents": [
    {"role": "user", "parts": [{"text": "什么是 API?"}]},
    {"role": "model", "parts": [{"text": "API 是程序之间交换数据或调用功能的接口。"}]},
    {"role": "user", "parts": [{"text": "用天气查询说明。"}]}
  ]
}

生成参数放在 generationConfig 中,例如型号支持时使用 maxOutputTokens 控制输出。它不是 Anthropic 的 max_tokens,也不是 OpenAI 的 max_completion_tokens。图片、工具及思考参数需按该型号单独确认。

5. 流式输出要改动作路径

支持流式能力的型号使用 :streamGenerateContent。如需按 SSE 获取事件,可指定 alt=sse;curl 的 --no-buffer 减少客户端输出缓存:

bash
: "${TIKWAY_API_KEY:?请先设置 Tikway API Key 环境变量}"

curl --fail-with-body --silent --show-error --no-buffer \
  --connect-timeout 10 --max-time 90 \
  --request POST \
  'https://api.tikway.ai/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse' \
  --header 'Content-Type: application/json' \
  --header "x-goog-api-key: ${TIKWAY_API_KEY}" \
  --data-binary '@04-gemini-request.json'

这也是一次真实调用,输入与普通请求相同。不要仅在 generateContent 请求体加 stream: true 期待切换动作。SSE 的每个 JSON 事件需要按 Gemini 结构读取;不能直接套用 Chat Completions 的 choices[].delta.content 或 [DONE] 完成判断。上面的普通 JSON 读取脚本不处理事件流。

常见错误怎么排查

现象 优先检查
鉴权失败 当前终端已设置 Tikway Key,并发送 x-goog-api-key。
未知动作或模型错误 检查 /v1beta/models/、准确 ID 和冒号后的动作名。
路径与请求体模型冲突 最小请求删除不必要的根节点 model,或保持一致。
输入结构被拒 contents 及每项 parts 都是非空数组,文本放在 parts[].text。
没有候选项 查看 promptFeedback 和错误响应,不假定一定有 candidates[0]。
429 / RESOURCE_EXHAUSTED 结合错误消息、账户状态与频率判断;它不只可能表示限流。
输出限制无效 核对 generationConfig.maxOutputTokens 和所选型号参数。
文本不完整或没有普通文本 查看 finishReason,确认是否达到上限、返回工具块或受限制。

排查时保留响应头 x-request-id,并记录所用模型与动作。运营方声明按请求型号提供调用;响应型号元数据和模型自报身份不能单独证明真实性。

继续阅读:模型与价格 · Gemini 协议 · 网关鉴权 · 错误码说明