1. 先说清楚:这个接口到底能干什么,适合谁来用
做后端或者写自动化脚本的朋友,应该都有过这种经历:调一个 AI 模型的接口,返回结果五花八门,你想让它输出一份规规矩矩的 JSON,结果它给你来一段散文,气得人想把键盘拆了。
gemini-1.5-pro-001 这个模型,是 Google Gemini 系列里比较能打的一个版本。它支持长上下文,多模态输入,最关键的是它提供了一个"JSON 输出模式",可以让模型严格按你定义的结构返回数据。这篇文章要讲的,就是如何用最朴素的方式——curl 命令行——把这个 JSON 输出功能调通,并且在实际项目里真正用起来。
适合谁看?三类人。第一类是刚接触 Gemini API、想快速验证一下能力的开发者;第二类是写脚本、写自动化工具、不想为了调一次接口就引入一整套 SDK 的人;第三类是已经在用 Gemini、但被非结构化输出折磨过的朋友。无论你是哪种,跟着这篇文章一步步走完,你至少能写出一个稳定返回 JSON 的 curl 调用,并且知道报错之后怎么排查。
我在文章里不会用任何代码框架,只讲 curl、JSON 和 Gemini 1.5 Pro 接口之间的配合逻辑。因为说白了,你理解了 HTTP 层面的请求和响应,后面不管换什么语言、什么 SDK,原理都是通的。JSON 格式也好,curl 的用法也好,本质上都是通用技能,学会了换到别的模型接口同样适用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 调用前的准备工作:API Key 与 curl 环境
2.1 拿到 API Key 才能上路
调用 Gemini API 需要 API Key。这个 Key 在 Google AI Studio 页面里申请,申请之后是一串类似 AIzaSy 开头的字符串。流程不复杂,跟着官方指引填几个表单就行,这里不展开讲,重点说说 Key 的使用习惯。
第一,不要把 Key 硬编码在脚本里。尤其是你打算把脚本分享出去、或者提交到代码仓库的时候,Key 一旦泄露,别人就能拿你的额度去调用。我个人的做法是存在环境变量里,调用的时候用 $GEMINI_API_KEY 引用,这样既安全,换 Key 的时候也不用改代码。
第二,Key 是有配额的。免费 Key 能调用的次数和每分钟请求数都有限制,生产环境需要另外开通计费。如果某天突然返回 403 或者 429,先查一下是不是 Key 的额度用完了,别一上来就怀疑是代码写错了。
第三,Key 是通过 URL 参数方式传递的,不是放在 Header 里。这一点跟很多其他 API 不一样,容易搞混。Gemini API 的请求格式是这样的:
code复制https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-001:generateContent?key=你的API_KEY
有些人习惯把认证信息往 Authorization Header 里塞,然后发现返回 400 或者 401,其实就是因为认证参数放错了位置。这个坑我见过不止一次,提醒一句:Gemini API 的 REST 接口认的是 URL 上的 key 参数。
2.2 curl 环境的三个注意点
curl 是现代操作系统里的标配工具。Windows 10 以上版本自带 curl.exe,macOS 和 Linux 更是天生就有。你真要在各种环境里用 curl 调接口,有几个细节值得注意。
第一是版本。Windows 自带的 curl 版本可能比较旧,某些新特性用不了。比如 --json 这个快捷参数是 curl 7.82.0 才有的功能,它会自动帮你设置 Content-Type: application/json,同时把请求体里的 JSON 原样发出去。如果版本太旧,就老老实实用 -H 和 -d 的组合。
第二是引号问题。在 Windows 的 CMD 或者 PowerShell 里写 JSON,单双引号的处理跟 Linux 完全不同。CMD 里 JSON 字符串里的双引号需要转义成 \",写起来非常痛苦。我踩过不少次这个坑,后来干脆把小 JSON 请求体写成一份 .json 文件,用 -d @request.json 的方式提交,既避免了转义地狱,又方便维护。
第三是单文件版 curl。有些老系统(比如网上经常有人提到的 32 位 Win7 环境)不自带 curl,需要自己下载一个独立的 curl.exe。这个单文件版本不需要安装,解压之后直接丢到 PATH 里就能用,对临时调试来说非常方便。下载时认准 curl 官网的官方版本就行,别去那些来路不明的下载站,否则容易拿到被植入广告或者恶意行为的假 curl。
注意:无论是哪种系统,调用 HTTPS 接口都需要 openssl 相关依赖。如果你遇到
curl: (35) TCP connection reset by peer或者 SSL 相关报错,优先检查系统时间、证书链和网络安全策略。
3. 最基础的 curl 调用:一次完整的 JSON 请求
3.1 请求 URL 的结构拆解
Gemini 1.5 Pro 的生成接口路径是固定的,本质上我们只需要关心四个部分:
v1beta:API 版本号。目前稳定的是 v1beta,v1 也有,但部分新功能只在 beta 里有。models/gemini-1.5-pro-001:模型 ID。注意这里的001是版本后缀,不是随便写的。如果你用的是新版本,模型 ID 可能变成gemini-1.5-pro-002,但gemini-1.5-pro-001在很长一段时间内都是可用的稳定版本。:generateContent:这是核心操作,表示"生成内容"。如果只要普通生成,就走这个路径;如果要流式输出,把冒号后面的动作改成streamGenerateContent。?key=...:认证参数。
所以一次最基础调用的完整 URL 长这样:
code复制https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-001:generateContent?key=${GEMINI_API_KEY}
把这四个部分拆开理解之后,你会发现这个接口的用法其实很直白。就好比你去餐厅点菜,URL 是餐厅的地址,模型 ID 是招牌菜的名字,generateContent 是"请给我做这道菜"这个动作,API Key 就是你的会员卡。组合起来,一次请求就发出去了。
3.2 请求体与响应体的 JSON 数据格式
Gemini API 的请求体是标准的 application/json,最小结构如下:
json复制{
"contents": [
{
"parts": [
{
"text": "用一句话解释什么是递归"
}
]
}
]
}
contents 是对话内容的数组,每个元素是一次参与者的发言。parts 是这次发言的具体内容片段,可以是文本,也可以是图片、视频等内联数据。最基础的情况下,一个 parts 数组中放一个带 text 的对象就够了。
响应体长什么样?如果你用浏览器或者类似 Postman 的工具直接看,会发现返回的 JSON 嵌套很深,核心内容在 candidates[0].content.parts[0].text 里。还有一堆 safetyRatings、usageMetadata、modelVersion 之类的元信息。
第一次调通的时候,你可能会被这个响应结构绕晕。别急,后面我会讲怎么用 jq 快速提取核心内容。
一个完整的调通示例:
bash复制curl -X POST \
-H "Content-Type: application/json" \
-d '{
"contents": [
{
"parts": [
{
"text": "用一句话解释什么是递归"
}
]
}
]
}' \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-001:generateContent?key=${GEMINI_API_KEY}"
如果返回的 HTTP 状态码是 200,并且响应体里的 candidates 数组不为空,恭喜你,你已经掌握了 Gemini API 的调用基本盘。接下来要做的,就是让它的输出变成严格的 JSON。
4. 让 Gemini 严格输出 JSON:responseMimeType 与 responseSchema
4.1 先试最简单的 JSON 输出模式
直接把上面那个请求发出去,你会发现 Gemini 虽然聪明,但不太守规矩。你让它"用 JSON 返回三本书的书名",它可能给你一段带解释的文本,JSON 混在 Markdown 代码块里。这在程序化调用场景下非常麻烦,因为你还需要额外的解析步骤,把代码块标记剥掉。
gemini-1.5-pro-001 提供了一种叫 responseMimeType 的配置,可以直接在 generationConfig 里指定:
json复制{
"contents": [
{
"parts": [
{
"text": "列出三本经典编程书籍,以 JSON 数组形式返回"
}
]
}
],
"generationConfig": {
"responseMimeType": "application/json"
}
}
加上这个配置之后,模型会尽量把自己的回答格式化为纯 JSON,而不是 Markdown 包装的代码块。这个改进对自动化流程来说非常有价值,省去了你从输出里剥离代码块标记的麻烦。
需要说明的是,responseMimeType 有两个合法值:text/plain 和 application/json。如果你不指定,默认是 text/plain,也就是自由文本。一旦指定为 application/json,模型就会切换到"严格 JSON 模式"。
我在实测中发现,指定了 responseMimeType: application/json 之后,模型输出的内容有两种表现。一种是纯 JSON:直接以 { 或 [ 开头,以一个匹配的 } 或 ] 结尾。另一种是偶尔在 JSON 前后带一些空行或换行符。空行不碍事,大多数解析器都能容忍。但如果出现 JSON 之外的说明文字,那多半是提示词写得不够具体,或者说模型开始跑偏了。这时候哪怕有再大的模型能力,也架不住你提示词里不把要求说清楚。
4.2 responseSchema 让输出结构完全可控
如果你只是想让输出是 JSON,那 responseMimeType 就够了。但如果你需要的是"输出格式完全符合我的数据结构",光靠 MimeType 还不够。万一模型少了一个字段、多了一层嵌套,下游代码可能直接崩。
这时候就要用到 responseSchema。它是 responseMimeType 的加强版,让你用 JSON Schema 的形式规定输出的具体结构。
举个例子,你需要一个表示人的 JSON 对象:
json复制{
"contents": [
{
"parts": [
{
"text": "虚构一个人物,包含姓名、年龄和职业"
}
]
}
],
"generationConfig": {
"responseMimeType": "application/json",
"responseSchema": {
"type": "OBJECT",
"properties": {
"name": {"type": "STRING"},
"age": {"type": "INTEGER"},
"occupation": {"type": "STRING"}
},
"required": ["name", "age", "occupation"]
}
}
}
看到区别了吗?responseSchema 相当于给模型下了一份"格式合同":必须有 name、age、occupation 三个字段,不能少。模型生成的时候会努力满足这套约束,你拿到手的就会是一个结构稳定的 JSON,下游解析起来非常省心。
这里我要说一个实战中的经验:responseSchema 里定义字段时,字段名最好用英文,并且提示词里也说清楚字段用的什么名字。Gemini 对中文字段名的支持虽然一直在改善,但实测下来,英文驼峰命名的字段,模型遵守得最好,返回结果也最稳定。如果你非要中文字段名,也建议在提示词里给出一个示范,比如"返回格式:{"姓名": "张三"}",否则模型很容易在字段名上自由发挥。
4.3 复杂 Schema 的实战写法
除了 OBJECT,responseSchema 还支持 ARRAY、ENUM 等类型。嵌套的写法稍微绕一点,但掌握规律之后并不难。
比如要返回一组人员列表,每个人包含姓名和职业,职业只能从"工程师、设计师、产品经理"里选:
json复制{
"contents": [
{
"parts": [
{
"text": "生成 3 个虚构人物,职业必须从给定选项中选"
}
]
}
],
"generationConfig": {
"responseMimeType": "application/json",
"responseSchema": {
"type": "ARRAY",
"items": {
"type": "OBJECT",
"properties": {
"name": {"type": "STRING"},
"role": {
"type": "STRING",
"enum": ["工程师", "设计师", "产品经理"]
}
},
"required": ["name", "role"]
}
}
}
}
这套结构可以套用到很多场景:抽取出差计划列表、解析用户输入的字段、把一段非结构化的文本转成可入库的数据表记录……本质上就是"正则表达式"的 AI 版——你用 Schema 定义规则,模型负责把自由文本映射到规则里。
我个人的建议是:能把 Schema 写具体就写具体。不要只写一层 OBJECT,必要的时候嵌套数组和子对象。约束越明确,模型越不会自由发挥。但也不要过度设计,字段能少就少,因为字段越多,模型出错概率越高,响应延迟也可能变大。这两者之间的平衡,需要你根据实际业务场景去试。
5. 实战案例:从文本生成到结构化数据
5.1 案例:生成一份待办事项 JSON
前面讲了不少理论,这里我放两个完整可直接复制的案例,你改一改请求体就能用。
案例一,让 Gemini 根据一句简短描述生成待办事项列表。
先准备好请求体文件 todo_request.json:
json复制{
"contents": [
{
"parts": [
{
"text": "我今天要完成三件事:调试登录接口、写周报、预约明天的会议室。请把这三个任务整理成 JSON 数组,每个任务有 title 和 due_date 两个字段,due_date 用 YYYY-MM-DD 格式。"
}
]
}
],
"generationConfig": {
"responseMimeType": "application/json",
"responseSchema": {
"type": "ARRAY",
"items": {
"type": "OBJECT",
"properties": {
"title": {"type": "STRING"},
"due_date": {"type": "STRING"}
},
"required": ["title", "due_date"]
}
}
}
}
然后用 curl 发送:
bash复制curl -X POST \
-H "Content-Type: application/json" \
-d @todo_request.json \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-001:generateContent?key=${GEMINI_API_KEY}"
响应里,你需要的 JSON 内容嵌套在 candidates 里面,可以用 jq 直接提取核心部分,命令如下:
bash复制curl -s -X POST \
-H "Content-Type: application/json" \
-d @todo_request.json \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-001:generateContent?key=${GEMINI_API_KEY}" \
| jq -r '.candidates[0].content.parts[0].text'
jq 的 -r 参数会去掉 JSON 字符串两侧的引号,直接输出纯文本。如果你的系统里没装 jq,也可以先把完整响应保存成文件,再用 Python 或者 grep 去提取。但说实话,命令行场景下 jq 是最顺手的,建议装一个。
5.2 案例:让 Gemini 返回产品信息数组
第二个案例更接近生产场景:你有一堆非结构化的商品描述,想让 Gemini 帮你抽取出品名、价格和库存。
json复制{
"contents": [
{
"parts": [
{
"text": "从下面的商品描述中提取结构化信息,输出 JSON 数组,每个元素包含 name(商品名)、price(数字,单位元)、stock_status(库存状态,只能是 in_stock 或 out_of_stock)。描述:白色蓝牙机械键盘,价格 299 元,现货。描述:USB-C 扩展坞,七合一,价格 159 元,暂时缺货。"
}
]
}
],
"generationConfig": {
"responseMimeType": "application/json",
"responseSchema": {
"type": "ARRAY",
"items": {
"type": "OBJECT",
"properties": {
"name": {"type": "STRING"},
"price": {"type": "NUMBER"},
"stock_status": {
"type": "STRING",
"enum": ["in_stock", "out_of_stock"]
}
},
"required": ["name", "price", "stock_status"]
}
}
}
}
像这种场景,放在实际项目里就是一条"数据清洗流水线":读文本 → 调 API → 拿 JSON → 写数据库。我用这种方案处理过不少产品信息,实测下来,只要提示词里把商品描述分隔清楚,Gemini 的识别准确率很高。
不过有一个点要提醒你:价格这种字段,如果描述里写的是"299 元",模型有时会返回 299,有时会返回 299.0,取决于它是按 STRING 还是 NUMBER 去理解。如果你对数据格式要求严格,建议在提示词里明确"不要带货币单位""用整数表示元"之类的话,否则你会看到各种意想不到的数值变体。
6. 常见报错与排查实录
6.1 HTTP 状态码速查表
调 API 最怕的就是报错看不懂。我整理了一份 Gemini API 的常见状态码对照,遇到问题可以先对号入座:
| 状态码 | 含义 | 常见原因 | 排查方向 |
|---|---|---|---|
| 200 | 成功 | 无 | 检查 candidates 是否为空 |
| 400 | 请求格式错误 | JSON 请求体语法不对、字段名拼错、Schema 不合法 | 用 JSON 校验工具格式化请求体 |
| 401 | 未授权 | Key 不存在、Key 传错位置 | 确认 URL 里的 key 参数 |
| 403 | 没有权限 | Key 被禁用、配额受限 | 检查 Key 状态与配额 |
| 404 | 资源不存在 | 模型 ID 拼写错误 | 核对 gemini-1.5-pro-001 这个 ID |
| 429 | 请求过多 | 免费额度用完、并发超限 | 加退避重试,降低调用频率 |
| 500 | 服务端错误 | 模型服务临时故障 | 稍后重试 |
这里头最常见的其实是 400 和 429。400 多半是请求体里多了个逗号、少了个引号,或者 responseSchema 的写法不合规。429 则意味着你要么免费额度真的用完了,要么某个时间窗口内请求太密集。遇到 429,我一般会做一个指数退避的重试逻辑,第一次等 1 秒,第二次等 2 秒,第三次等 4 秒,最多重试三到五次。这种策略在大多数 HTTP API 面前都通用,不只是 Gemini 一家适用。
6.2 curl 侧的问题排查
在调 Gemini API 的过程中,curl 本身也可能出问题。这几个报错在网上的出现频率最高,我挨个说:
curl: (6) Could not resolve host:域名解析失败。优先检查你的 DNS 配置,试试用nslookup generativelanguage.googleapis.com看能不能解析出 IP。如果解析正常但请求还是失败,那就需要检查本机网络环境、防火墙规则和相关安全策略。curl: (35) TCP connection reset by peer:连接被重置。这通常发生在 TLS 握手阶段。先查系统时间是否正确,时间偏了会导致证书验证失败;再查本地是否有安全软件或者网络设备干预了 TLS 流量。curl: (60) SSL certificate problem:证书验证失败。这可能是本地 CA 证书库太旧。解决办法是更新系统的 CA 证书,而不是直接加-k跳过验证。跳过验证一时爽,但会给自己埋雷,尤其在生产环境里,证书校验是 HTTPS 信任链的根基。
排查 curl 问题的时候,-v 参数是你的好朋友。curl -v 会打印出请求的完整握手过程、请求头、响应头,每一步卡在哪里一目了然。我在很多文章里看到过"curl -kv 命令详解"之类的标题,核心就是善用 -v 看详细交互,至于 -k 那个选项,我建议只在本地测试时临时用一用,线上千万别加。
还有人说 curl 在 Windows 上报 curl: (1) Unsupported protocol,这个大概率是系统里装了别人改坏的 curl,或者把某个工具误认成了 curl。建议直接用 Windows 自带的 curl.exe,或者从 curl 官网下载官方版本,别用第三方打包的奇奇怪怪版本。
6.3 响应 JSON 的解析与合法性校验
Gemini 返回的完整响应是一个嵌套很深的 JSON,直接用眼睛看容易懵。我强烈建议你配合 jq 使用,先看整体结构:
bash复制curl -s -X POST \
-H "Content-Type: application/json" \
-d @request.json \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-001:generateContent?key=${GEMINI_API_KEY}" \
| jq '.candidates[0].content.parts[0].text'
如果 jq 解析报错,提示某个 token 不合法,那多半是响应不完整,或者模型在某处把 JSON 写坏了。这种情况可以用 jq empty 做一次性校验,或者把响应保存成文件,用 Python 的 json.loads 打开看具体哪里不对。
如果在 Windows 上不想装 jq,也可以用 PowerShell 的 ConvertFrom-Json 来解析。但说实话,命令行的舒适度远不如 jq,能装还是装一个。jq 的学习曲线也不陡,你只需要掌握如何读 .candidates[0].content.parts[0].text 就够了,复杂的 JSON 转换逻辑可以后面再学。
提示:当你用
-s静默模式时,curl 会隐藏错误信息。排查问题的时候建议去掉-s,或者同时加-S把错误显示出来,这样你至少能知道 curl 是哪里卡住了。
7. 进阶技巧与我的经验收尾
7.1 流式输出与请求超时控制
如果回答比较长,Gemini 默认的 generateContent 会等到全部生成完毕再一次性返回。这在有些场景下体验很差,比如你想做打字机效果的聊天界面,或者你只是想看前几个 token 的输出。这时候可以改用流式接口。
把 URL 里的 generateContent 换成 streamGenerateContent,再加上 alt=sse 参数:
code复制https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-001:streamGenerateContent?alt=sse&key=${GEMINI_API_KEY}
curl 请求时加 -N(或者 --no-buffer)参数,让输出不要缓冲:
bash复制curl -N -X POST \
-H "Content-Type: application/json" \
-d @request.json \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-001:streamGenerateContent?alt=sse&key=${GEMINI_API_KEY}"
流式响应返回的是多行 SSE 格式,每一行以 data: 开头,后面跟着一个 JSON 片段。你需要逐行解析,每次取出 candidates[0].content.parts[0].text 里的增量文本,拼起来才是完整答案。SSE 格式本身不难理解,就是一行一个事件,但你得习惯这种"一段一段来"的节奏,跟你平时解析完整 JSON 的思路不太一样。
另外,请求超时也值得关注。免费 Key 的默认限制比较严格,复杂任务生成时间可能比较长。我给 curl 加 --max-time 60 来兜底,防止脚本挂死。如果你是写批处理脚本,建议再配合 --retry 3 --retry-delay 2,让 curl 对网络抖动做基础容错。
7.2 generationConfig 参数调优建议
最后分享几个 generationConfig 里的实用参数,这几个参数在官方文档里都有,但怎么用、用多少,文档不会告诉你。
temperature:控制随机性,范围 0 到 1。做结构化输出时,我建议设在 0.2 到 0.4 之间,太低会显得刻板,太高容易瞎编字段值。maxOutputTokens:控制最大输出长度。如果你的输出大概率超过默认值,把这个调大一点;反过来,如果你只需要简单 JSON,尽量调小,省得模型画蛇添足。topK和topP:这俩是采样策略参数,一般场景用默认值就行,不需要动。很多人喜欢折腾这两个参数,但在 JSON 结构化输出的场景里,它们的收益远不如把 temperature 调低来得实在。
还有一个很重要的提醒:如果你通过 responseSchema 定义了枚举值,一定要保证提示词与枚举值一致。比如你定义 enum: ["in_stock", "out_of_stock"],提示词里就不要只写"有货/无货",而是明确写"库存状态必须是 in_stock 或 out_of_stock"。模型不是你肚子里的蛔虫,你给的信息越一致,它越容易做对。别指望模型自己把"有货"映射到 in_stock,它可能会,也可能不会,而你赌不起这个"可能"。
7.3 我在实战里养成的几个习惯
写了这么多,把我在实际项目里踩坑总结出来的几个习惯放在最后,希望你能少走弯路。
第一,所有请求体都写进文件,用 -d @file.json 提交,而不是在命令行里手写长 JSON。这不仅解决了引号转义问题,还能让你随时把请求体分享给别人复现问题。命令行里的长 JSON 一旦出错,你连哪里少了逗号都找不出来。
第二,用 jq 提取结果之后,再做一次 jq . 管道的 JSON 合法性校验。AI 生成的 JSON 偶尔会有意外,多一步校验能挡住大部分脏数据。你可以在同一行命令里用 | jq -r '.candidates[0].content.parts[0].text' | jq .,前一个 jq 负责提取,后一个 jq 负责校验,两件事一起干,效率很高。
第三,模型 ID 尽量写成环境变量。gemini-1.5-pro-001 这种版本号后缀会随模型迭代而变化,你把模型 ID 抽出来统一配置,以后换版本只改一处就够了。我见过有人在几十个脚本里硬编码模型 ID,换版本的时候改到崩溃。
第四,别怕看官方文档,但要带着目的看。你只需要关心请求体结构、generationConfig 的字段说明、响应体结构这三块,其余大多用不到。官方文档信息密度高,但也不是每一行都要背下来,按需查阅就行。
说到底,用 curl 调 gemini-1.5-pro-001 拿到 JSON 输出,并不是一件多有门槛的事。你需要的只是搞清楚请求怎么发、响应怎么读、报错怎么查,剩下的就是反复测试。我刚开始调的时候也被那堆嵌套的 JSON 结构搞昏过头,后来把 responseSchema 用熟了,整套流程就变得非常顺畅。希望这篇文章能帮你把这三个环节一次理顺,少踩几个我踩过的坑。
