Gemini · generateContent
Gemini API 接入教程:用 Tikway 调用 generateContent 协议
更新日期:
Gemini 原生协议将模型放在 URL 中,消息使用 contents[].parts[],与 OpenAI 的 model、messages 结构不同。本文演示最小 generateContent 请求、保存结果、提取候选文本,并说明多轮消息和流式动作路径。
本例用 Tikway API Key,通过 x-goog-api-key 鉴权。调用按使用量计费;先在模型目录确认所选型号的协议、输入输出能力和价格。
1. 把模型放入正确的 URL
采用协议文档中的示例:
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:
export TIKWAY_API_KEY='替换为你的 Tikway API Key'
将以下内容保存为 04-gemini-request.json,或使用配套 JSON:
{
"contents": [
{
"role": "user",
"parts": [
{
"text": "请用三句话解释 API 网关在应用中的作用。"
}
]
}
]
}
在文件所在目录执行以下命令。它会发送真实模型请求,将响应体和响应头分别保存到本地;费用以账户及当前模型目录为准。
: "${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 中的普通文本
下面为人工构造的结构说明,不是实际模型输出:
{
"candidates": [
{
"content": {"role": "model", "parts": [{"text": "示例回答。"}]},
"finishReason": "STOP"
}
],
"usageMetadata": {"promptTokenCount": 100, "candidatesTokenCount": 40}
}
读取首个候选项的 content.parts,拼接其中普通 text 字段。有些响应包含工具调用或标记为 thought 的思考块,不能把所有块直接当作最终回答。候选项缺失时应读取反馈,而不是盲目索引 [0]。
保存以下代码为 04-read-gemini-response.py,或使用配套读取脚本。该脚本只读取本地 JSON:
"""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)
执行:
python 04-read-gemini-response.py gemini-response.json
finishReason == "MAX_TOKENS" 可能表示回答触及输出上限;空候选项时,脚本会显示 promptFeedback.blockReason(如果有)。这些分支有助于区分内容被限制、没有普通文本和文件结构错误。
4. 多轮内容与系统指令
Gemini 多轮回复使用 model 角色,用户使用 user。系统指令放入根节点 systemInstruction.parts。下面只展示需要合入完整请求的字段,助手内容为人工示例:
{
"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 减少客户端输出缓存:
: "${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,并记录所用模型与动作。运营方声明按请求型号提供调用;响应型号元数据和模型自报身份不能单独证明真实性。