# 余额查询 API Source: https://docs.apiyi.com/api-capabilities/balance-query 获取账户余额、已使用额度和请求次数等信息,实现主动余额告警控制 ## 接口概述 余额查询接口用于获取当前账户的额度使用情况,包括总配额、已使用额度、剩余额度和请求次数等信息。 这个接口可帮助客户以简单的方式获取账号余额,以便更主动、自由地控制余额告警。 ## 如何获取 Authorization 本接口用**个人访问令牌**认证(`nap_` 开头),与调用模型的 API Key 不是一回事。 访问 `api.apiyi.com/account/profile` 个人中心,在「账户选项」里点击 **个人访问令牌** → **创建令牌** 本接口只需勾选 **个人资料 · 读取**。到期时间默认 30 天,建议选久一点 创建后立即复制保存,填进请求头 `Authorization` 创建个人访问令牌:名称、到期时间、IP 白名单与权限 **旧系统令牌计划于 2026-11-11 (UTC+8) 停用。** 原来在「账号选项 - 系统令牌」生成的 AccessToken 在此之前照常可用,之后会返回 401 `ACCESS_TOKEN_LEGACY_RETIRED`。 平台会视使用情况决定是否延期,建议尽早换成个人访问令牌:替换请求头里的值即可,代码不用改。 到期时间、IP 白名单与权限的详细说明见 [什么是个人访问令牌?](/faq/personal-access-token) ## 接口信息 | 项目 | 说明 | | - | - | | **接口URL** | `https://api.apiyi.com/api/user/self` | | **请求方法** | `GET` | | **认证方式** | Authorization Header(个人访问令牌,`Bearer ` 前缀可加可不加) | | **响应格式** | JSON | ## 请求说明 ### 请求 Headers | Header名称 | 必填 | 说明 | | - | - | - | | `Authorization` | 是 | 个人访问令牌,直接填写令牌字符串 | | `Accept` | 否 | 建议设置为 `application/json` | | `Content-Type` | 否 | 建议设置为 `application/json` | ### 请求参数 该接口为 GET 请求,**不需要**传递任何请求体(body)参数。 ## 响应说明 ### 成功响应示例 ```json theme={null} { "success": true, "message": null, "data": { "id": 19489, "username": "testnano", "display_name": "testnano", "role": 1, "status": 1, "email": "", "quota": 24997909, "used_quota": 10027091, "request_count": 339, "group": "ceshi", "aff_code": "ZM0H", "inviter_id": 0, "access_token": "...", "ModelFixedPrice": [...] } } ``` ### 核心响应字段说明 | 字段名 | 类型 | 说明 | | - | - | - | | `success` | Boolean | 请求是否成功 | | `message` | String | 错误信息(成功时为null) | | `data.username` | String | 用户名 | | `data.display_name` | String | 显示名称 | | `data.quota` | Integer | **剩余额度**(当前可用余额,单位:额度) | | `data.used_quota` | Integer | **已使用额度**(单位:额度) | | `data.request_count` | Integer | **总请求次数** | | `data.group` | String | 用户所属组 | | `data.ModelFixedPrice` | Array | 模型价格列表(可忽略) | ### 额度换算说明 500,000 额度 = \$1.00 美金 (USD) **计算公式:** * 美金金额 = 额度 ÷ 500,000 * 剩余额度 = quota(quota 本身就是当前剩余余额) * 剩余美金 = quota ÷ 500,000 **示例:** * `quota: 24997909` → \$49.99 USD(当前剩余余额) * `used_quota: 10027091` → \$20.05 USD(已使用) ## 错误响应 ### HTTP 401 - 认证失败 ```json theme={null} { "success": false, "message": "Unauthorized" } ``` **原因:** 令牌无效、已过期或已删除。修改密码、两步验证或通行密钥会撤销全部个人访问令牌。 **解决方法:** 回控制台重新创建个人访问令牌。 另外两种 401 会带明确的报文: | 报文 | 原因 | | - | - | | `ACCESS_TOKEN_LEGACY_RETIRED` | 仍在使用已停用的旧系统令牌,需换成个人访问令牌 | | `This access token may not be used from the current IP address.` | 请求来源不在令牌的 IP 白名单内;白名单不能修改,需新建令牌 | ### HTTP 403 - 权限不足 ```json theme={null} { "code": "ACCESS_TOKEN_SCOPE_DENIED", "message": "This endpoint is outside this access token’s granted permissions.", "success": false } ``` **原因:** 个人访问令牌没有勾选 **个人资料 · 读取**。 **解决方法:** 新建一把勾选了该权限的令牌(权限创建后不能修改)。 ## 代码示例 ### cURL 示例 ```bash theme={null} curl --compressed 'https://api.apiyi.com/api/user/self' \ -H 'Accept: application/json' \ -H 'Authorization: YOUR_TOKEN_HERE' \ -H 'Content-Type: application/json' ``` **重要提示:** 必须添加 `--compressed` 选项,因为API返回的是gzip压缩内容,否则会得到乱码。 **快速测试(替换YOUR\_TOKEN\_HERE):** ```bash theme={null} export APIYI_TOKEN='YOUR_TOKEN_HERE' curl --compressed -s 'https://api.apiyi.com/api/user/self' \ -H 'Accept: application/json' \ -H "Authorization: $APIYI_TOKEN" \ -H 'Content-Type: application/json' | \ jq '.data | {quota, used_quota, request_count}' ``` 说明:`-s` 选项隐藏进度条,`--compressed` 自动解压gzip响应 ### Python 示例(基础版) ```python theme={null} import requests # 配置 url = "https://api.apiyi.com/api/user/self" authorization = "YOUR_TOKEN_HERE" # 替换为你的令牌 # 请求头 headers = { 'Accept': 'application/json', 'Authorization': authorization, 'Content-Type': 'application/json' } # 发送请求 response = requests.get(url, headers=headers, timeout=10) # 检查响应 if response.status_code == 200: data = response.json() user_data = data['data'] # 提取核心信息 quota = user_data['quota'] used_quota = user_data['used_quota'] request_count = user_data['request_count'] # 计算美金金额(注意:quota 就是当前剩余余额) remaining_usd = quota / 500000 used_usd = used_quota / 500000 # 打印结果 print(f"剩余额度:${remaining_usd:.2f} USD ({quota:,} 额度)") print(f"已使用:${used_usd:.2f} USD ({used_quota:,} 额度)") print(f"请求次数:{request_count:,} 次") else: print(f"请求失败:HTTP {response.status_code}") print(response.text) ``` ### Python 示例(完整优化版) 在基础版之上增加了以下特性: 完整的异常处理和错误捕获 安全管理令牌,避免硬编码 美观的表格展示和数字格式化 自动计算美金金额 把下面的代码保存为 `quota.py` 即可直接运行: ```python theme={null} import os import sys import requests URL = "https://api.apiyi.com/api/user/self" QUOTA_PER_USD = 500_000 def fetch_quota(token): """查询账户余额,返回 data 字段。""" headers = { "Accept": "application/json", "Authorization": token, "Content-Type": "application/json", } try: resp = requests.get(URL, headers=headers, timeout=10) except requests.exceptions.Timeout: sys.exit("请求超时,请检查网络后重试") except requests.exceptions.RequestException as exc: sys.exit(f"请求失败:{exc}") if resp.status_code == 401: sys.exit("认证失败:令牌无效、已过期或 IP 不在白名单,请回控制台重新创建个人访问令牌") if resp.status_code != 200: sys.exit(f"请求失败:HTTP {resp.status_code}\n{resp.text[:200]}") body = resp.json() if not body.get("success"): sys.exit(f"接口报错:{body.get('message')}") return body["data"] def report(data): quota = data.get("quota", 0) used = data.get("used_quota", 0) count = data.get("request_count", 0) print("=" * 60) print("📊 APIYI 账户余额信息") print("=" * 60) print(f"用户名称:{data.get('username')} ({data.get('display_name')})") print("-" * 60) print(f"剩余额度:{quota:,} 额度 (${quota / QUOTA_PER_USD:.2f} USD)") print(f"已使用: {used:,} 额度 (${used / QUOTA_PER_USD:.2f} USD)") print(f"请求次数:{count:,} 次") print("=" * 60) print(f"💡 换算说明:{QUOTA_PER_USD:,} 额度 = $1.00 USD") print("=" * 60) if __name__ == "__main__": # 优先读命令行参数,其次读环境变量,避免把令牌硬编码进代码 token = sys.argv[1] if len(sys.argv) > 1 else os.environ.get("APIYI_TOKEN") if not token: sys.exit("请提供令牌:设置环境变量 APIYI_TOKEN,或作为第一个命令行参数传入") report(fetch_quota(token)) ``` **使用方法:** ```bash theme={null} # 方式1:使用环境变量(推荐) export APIYI_TOKEN='YOUR_TOKEN_HERE' python quota.py # 方式2:命令行参数 python quota.py 'YOUR_TOKEN_HERE' ``` **输出示例:** ``` ============================================================ 📊 APIYI 账户余额信息 ============================================================ 用户名称:testnano (testnano) ------------------------------------------------------------ 剩余额度:24,997,909 额度 ($49.99 USD) 已使用: 10,027,091 额度 ($20.05 USD) 请求次数:339 次 ============================================================ 💡 换算说明:500,000 额度 = $1.00 USD ============================================================ ``` ## 常见问题 请参考上方「如何获取 Authorization」的步骤,在个人中心创建个人访问令牌并勾选「个人资料 · 读取」。旧系统令牌计划于 2026-11-11 (UTC+8) 停用,详见 [什么是个人访问令牌?](/faq/personal-access-token) 否,查询账户余额接口不会消耗您的配额额度。 建议查询间隔不少于1秒,避免频繁请求触发限流。 该字段返回各个AI模型的定价信息。如果您只关心余额信息,可以忽略该字段。 `quota` 字段就是当前的剩余额度。如果 `quota` 为 0 或接近 0,说明账户余额不足,需要及时充值。 **问题:** 执行curl命令时得到乱码,或者jq报错 "Invalid numeric literal" **原因:** API返回的是gzip压缩内容(`Content-Encoding: gzip`),curl没有自动解压。 **解决方案:** 添加 `--compressed` 选项让curl自动解压: ```bash theme={null} # ✅ 正确(带 --compressed) curl --compressed 'https://api.apiyi.com/api/user/self' \ -H 'Authorization: YOUR_TOKEN' | jq # ❌ 错误(缺少 --compressed) curl 'https://api.apiyi.com/api/user/self' \ -H 'Authorization: YOUR_TOKEN' | jq ``` ## 注意事项 **安全性提醒** * 切勿在代码中硬编码 Authorization 令牌 * 建议使用环境变量或配置文件管理敏感信息 * 不要将包含令牌的代码提交到公开仓库 **请求限制** * 建议设置合理的请求超时时间(推荐10秒) * 避免过于频繁的查询请求 * 务必处理网络异常、超时、认证失败等错误情况 * 建议记录错误日志便于排查问题 * API返回gzip压缩内容,使用curl时必须添加 `--compressed` 选项 * Python的requests库会自动处理gzip解压,无需额外配置 # Claude API 调用基础说明 Source: https://docs.apiyi.com/api-capabilities/claude API易 提供纯正官转的 AWS Claude + Claude Official API 双通道接入,约 85 折,稳定可靠、按量计费。 通道与计费亮点: * **默认通道:AWS Claude**(AWS Bedrock 官方接入)——稳定性高、缓存命中好。 * **替补通道:Claude Official**(Anthropic 官网官 Key 直连)——AWS 通道异常时自动兜底。 * **两条通道都是纯官转**,按量计费、不限速,综合成本约官网价 **85 折**(含充值加赠后区间为 79–86 折)。 低价逆向的我们不做,**只做可靠稳定的质量和服务**。 市场上 Claude 的接入渠道有点混乱,价格越低往往越不透明:那些低价通道你用了,根本不知道对方在里面掺了什么——可能是逆向破解、共享账号、降智或被替换的模型;更要紧的是,你的对话数据被转卖了你也无从察觉。API易 只做纯正官转(AWS Bedrock + Anthropic 官方 Key),通道可溯源、不留存数据,宁可贵一点也要稳、要干净。 ## 获取 API Key 在后台创建 / 查看令牌: `https://api.apiyi.com/token` * 使用**默认令牌**即可直接调用。 * 若新建令牌时选择 **ClaudeCode 分组**,可享 **95 折**(5% off)。 * 该分组折扣可与**充值活动 10%–20% 加赠**叠加,综合下来实际成本约为官网价的 **79 折–86 折**(即标题所说的"约 85 折"区间)。 * 不限速,比官网价格更低,使用方便。 API 按量计费(非包月套餐),余额从充值账户实时扣费。 ## 接入信息 | 项目 | 值 | | - | - | | **Base URL** | `https://api.apiyi.com` | | **Anthropic 原生端点** | `https://api.apiyi.com/v1/messages` | | **OpenAI 兼容端点** | `https://api.apiyi.com/v1/chat/completions` | ## 可用模型 以下 3 个为各系列最新版本,推荐直接使用: | 系列 | 模型名 | 适用场景 | | - | - | - | | **Opus** | `claude-opus-4-8` | 复杂编程、深度推理 | | **Sonnet** | `claude-sonnet-4-6` | 通用智能、日常代码 | | **Haiku** | `claude-haiku-4-5-20251001` | 快速响应、高并发 | ## 调用方式:原生 vs OpenAI 兼容 我们**同时支持** Anthropic 原生格式和 OpenAI 兼容格式,但请按场景选择: 端点:`/v1/messages` **凡是用 Claude Code、Cline、Cursor 等深度依赖 Claude 的客户端,请务必使用原生格式。** 只有原生格式才能正确触发 **Prompt Cache(缓存计费)**,长上下文/重复 system prompt 场景下账单可大幅降低。 端点:`/v1/chat/completions` 若你的项目原本就是 OpenAI SDK 写的、**且不在意缓存计费**,可以直接切到 Claude 模型,迁移成本几乎为零。 适合一次性脚本、轻量调用、SDK 已固化的存量项目。 **缓存计费只在 Anthropic 原生格式下生效。** 在 Claude Code 等高频、长上下文场景中,使用 OpenAI 兼容格式可能让账单显著偏高——这不是 API 易的问题,是上游协议本身的限制。 ## 调用示例 ### Anthropic 原生格式(推荐) ```bash theme={null} curl https://api.apiyi.com/v1/messages \ -H "x-api-key: your-apiyi-key" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [ {"role": "user", "content": "你好,介绍一下你自己。"} ] }' ``` ```python theme={null} import anthropic client = anthropic.Anthropic( api_key="your-apiyi-key", base_url="https://api.apiyi.com" ) message = client.messages.create( model="claude-opus-4-8", max_tokens=1024, messages=[ {"role": "user", "content": "写一个 Python 快速排序示例。"} ] ) print(message.content[0].text) ``` ### OpenAI 兼容格式(通用迁移用) ```python theme={null} from openai import OpenAI client = OpenAI( api_key="your-apiyi-key", base_url="https://api.apiyi.com/v1" ) response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[ {"role": "user", "content": "你好,介绍一下你自己。"} ] ) print(response.choices[0].message.content) ``` ## 关于 Opus 价格的提示 **Opus 价格相对较高**。日常对话消耗一般,但在编程场景里,由于大量 Tokens 输入和输出,账单不低。建议先用 **\$10 小额度**测试实际消耗,再决定是否长期使用。 日常使用建议: * **大多数场景**:优先 `claude-sonnet-4-6`,性价比最高。 * **简单/高频任务**:用 `claude-haiku-4-5-20251001`,速度快、成本低。 * **复杂编程/推理**:再切到 `claude-opus-4-8`。 ## 常见问题 因为低价的代价你看不见。逆向破解、共享账号、降智或被悄悄替换的模型,都能把价格压下来,但用的时候你完全不知道对方在通道里掺了什么——输出质量时好时坏、随时可能断供,甚至你的对话数据被转卖你也察觉不到。 我们只做**纯正官转**:默认走 AWS Bedrock 官方接入,替补走 Anthropic 官方 Key 直连,两条通道都可溯源、按量计费、不留存你的数据。综合成本约官网价 **85 折**,我们认为这是「稳定可靠」与「价格合理」之间最该守住的那条线——宁可贵一点,也不碰来路不明的便宜货。 这是经 AWS(Bedrock)通道调用 Opus 4.7 / 4.8 时最常见的 400 报错,完整信息形如: ``` ValidationException: "thinking.type.enabled" is not supported for this model. Use "thinking.type.adaptive" and "output_config.effort" to control thinking behavior. ``` **原因**:请求体里传了旧版固定预算思考写法 `thinking: { "type": "enabled", "budget_tokens": N }`。Opus 4.7 / 4.8 已移除这种写法,只支持自适应思考。 **解决**:删掉 `type: "enabled"` 和 `budget_tokens`,改用 `thinking: { "type": "adaptive" }` + `output_config.effort` 控制思考深度。同理 `temperature` / `top_p` / `top_k` 在这些模型上也已移除,传了会 400。详见 [Claude Effort 思考指南](/api-capabilities/claude-effort-thinking)。 ## 相关链接 * 获取 / 管理令牌:`https://api.apiyi.com/token` * 充值与活动:`https://api.apiyi.com` # Claude Effort 努力档位与思考指南 Source: https://docs.apiyi.com/api-capabilities/claude-effort-thinking Anthropic 原生格式下 output_config.effort 努力档位与 adaptive thinking 自适应思考的正确用法,附完整可运行示例。 本文说明如何用 **Anthropic 原生 Messages API** 调用 Claude(经 API易 网关转 AWS Bedrock),以及 `output_config.effort`(努力档位)和 `thinking`(自适应思考)的正确用法。 基础的通道、计费与接入信息请先看 [Claude API 基础说明](/api-capabilities/claude)。 适用模型:Claude Opus 4.8 / 4.7 / 4.6、Sonnet 4.6 等。本文以 **Opus 4.8** 为例。 ## 在线测试工具 不想写代码?可以先用 API易 在线推理测试工具体验:选择模型与 effort 档位、设置 Max Tokens、勾选「请求返回思考摘要」,即可直接对比不同档位的推理效果。 网页端直接调用 Claude(也支持 GPT / Gemini)推理测试,无需写代码,填入 API易 令牌即可使用。 API易 在线推理测试工具:选择 claude-opus-4-8 模型与 effort 档位 ## 请求的基本结构 ### Endpoint 与请求头 ``` POST https://api.apiyi.com/v1/messages ``` | Header | 值 | 说明 | | - | - | - | | `content-type` | `application/json` | 固定 | | `anthropic-version` | `2023-06-01` | Anthropic 原生版本头,必填 | | `x-api-key` | `your-apiyi-key` | Anthropic 原生鉴权方式 | 经 API易 转 Bedrock 时,**客户端仍用 Anthropic 原生格式**(`x-api-key` + `/v1/messages`),网关内部负责翻译成 Bedrock 的 `bedrock-2023-05-31`。你不需要写 `anthropic_version: bedrock-2023-05-31`。 ### 最小请求体 ```json theme={null} { "model": "claude-opus-4-8", "max_tokens": 16000, "messages": [ { "role": "user", "content": "你的问题" } ] } ``` ## effort 努力档位 `effort` 控制 Claude 愿意花多少 token 来产出结果,在「彻底程度」和「速度/成本」之间权衡。它影响**全部** token 消耗:正文、工具调用、以及扩展思考。 **关键规则** 1. `effort` 必须放在顶层独立的 `output_config` 对象里,**不能**放进 `thinking` 里 —— 放错会直接 `ValidationException` / 400。 2. **无需 beta 头**。effort 现已对所有支持的模型开放,不再需要 `anthropic-beta: effort-2025-11-24`。 3. 默认值是 `high`;设成 `"high"` 与完全不传 `effort` 行为一致。 ### 带 effort 的请求体 ```json theme={null} { "model": "claude-opus-4-8", "max_tokens": 16000, "output_config": { "effort": "medium" }, "messages": [ { "role": "user", "content": "分析微服务与单体架构的取舍" } ] } ``` ### 档位一览 | 档位 | 说明 | 典型场景 | | - | - | - | | `low` | 最省。显著节省 token,能力略降。 | 简单任务、高并发、子 agent | | `medium` | 平衡。中等 token 节省。 | 多数 agentic 工作流的折中默认 | | `high` | 默认值。高能力。 | 复杂推理、难编码、对质量敏感的任务 | | `xhigh` | 长程扩展能力,介于 high 和 max 之间。 | 长时编码 / agentic 任务(超 30 分钟) | | `max` | 无约束的最强能力。 | 真正前沿的难题,最深推理 | **Opus 4.8 推荐**:编码 / agentic 用 `xhigh` 起步,其它智力敏感任务用 `high`,只有在 eval 验证过质量不掉的前提下才下调到 `medium` / `low`。 跑 `xhigh` / `max` 时把 `max_tokens` 设大(64k 起步),给模型留足思考 + 输出空间。 ### 各模型支持的档位 不是所有模型都支持全部档位。`xhigh` 是 Opus 4.7 才新增的,`max` 不支持 Sonnet: | 档位 | Opus 4.6 | Opus 4.7 / 4.8 | Sonnet 4.6 | | - | :-: | :-: | :-: | | `low` / `medium` / `high` | ✅ | ✅ | ✅ | | `xhigh` | ❌ | ✅ | ❌ | | `max` | ✅ | ✅ | ❌ | 常见误用:`claude-opus-4-6` 配 `effort: "xhigh"`。4.6 没有 `xhigh` 档 —— 请改用 `high` / `max`,或把模型换成 `claude-opus-4-8` 再用 `xhigh`。 ## thinking 自适应思考 Opus 4.7 / 4.8 用**自适应思考**:由模型自己决定何时思考、思考多少,配合 effort 控制深度。 ```json theme={null} { "model": "claude-opus-4-8", "max_tokens": 16000, "thinking": { "type": "adaptive", "display": "summarized" }, "output_config": { "effort": "xhigh" }, "messages": [ { "role": "user", "content": "一步步定位这个生产环境 Bug 的根因" } ] } ``` * `thinking.type: "adaptive"` —— 开启自适应思考(不传则不思考)。 * `thinking.display: "summarized"` —— 让响应里返回**思考摘要**块;不需要展示可删掉。 * effort 与思考的关系:`high` / `xhigh` / `max` 几乎总会深度思考;`low` / `medium` 在简单题上可能跳过思考。 * `display` 默认值随模型不同:**Opus 4.6 默认 `summarized`**,Opus 4.7 / 4.8 默认 `omitted`(思考块仍在,但 `thinking` 文本为空,前端表现为「正文前一段停顿」)。想稳定拿到摘要就显式写 `display: "summarized"`。 * 没有 `-thinking` 后缀的原生模型。是否思考由 `thinking` **参数**决定,不是靠模型名后缀;`xxx-thinking` 都是第三方别名,直接用基础模型 ID + `thinking` 参数即可。 Opus 4.7 / 4.8 **不支持** `thinking.type: "enabled"` + `budget_tokens`(会 400)。请改用 adaptive + effort。 ### thinking 摘要的本质(重要) * 摘要由 **Anthropic 官方(模型/服务层)生成**,不是网关或第三方模型二次加工;原始思维链(raw CoT)永不原文返回,你拿到的就是官方摘要。 * **不能用 system prompt 定制 thinking 摘要的语气/格式**。`system` 影响的是模型「怎么思考」和「最终回答」的风格,摘要只是内部推理的可读呈现。语气、排版、风格等要求请放进对**最终回答**的约束,让它体现在 `text` 块里。 * 不要在 prompt 里要求模型把内部推理「原文」输出到回答中 —— 可能触发拒答(`stop_reason: "refusal"`,`stop_details.category` 可能为 `reasoning_extraction`)。需要看推理就读 `display: "summarized"` 的摘要。 多轮对话在**同一模型**上继续时,要把上一轮收到的 thinking 块原样回传(含签名、含空文本块)—— API 拒绝被**修改过**的 thinking 块。展示摘要没问题,但不要编辑后再回传。 ## 解析响应 响应的 `content` 是一个块数组,按 `type` 区分: ```python theme={null} for block in data["content"]: if block["type"] == "thinking": print("[思考摘要]", block["thinking"]) elif block["type"] == "text": print("[回答]", block["text"]) ``` token 用量在 `usage` 字段: ```json theme={null} { "usage": { "input_tokens": 164, "output_tokens": 11056, "service_tier": "standard" } } ``` 若 `stop_reason` 为 `max_tokens`,说明输出被 `max_tokens` 截断(高 effort 下思考容易吃满),此时正文可能为空 —— 把 `max_tokens` 调大即可。 ## 流式(stream)下的 thinking 字段 `stream: true` 时,思考内容**不走** `delta.text`,而是专门的事件序列: | 事件 | 字段 | 说明 | | - | - | - | | `content_block_start` | `content_block.type = "thinking"` | 思考块开始 | | `content_block_delta` | `delta.type = "thinking_delta"` → `delta.thinking` | 思考摘要增量文本(不是 `delta.text`) | | `content_block_delta` | `delta.type = "signature_delta"` | 思考块签名,多轮回传时需原样保留 | | `content_block_stop` | — | 思考块结束,之后才是正文 `text` 块 | 正文文本仍走 `delta.type = "text_delta"` → `delta.text`。若 `display: "omitted"`,思考块照常出现但 `delta.thinking` 为空字符串。 ## 完整可运行示例 ```python theme={null} import os import requests from dotenv import load_dotenv load_dotenv() API_KEY = os.getenv("APIYI_API_KEY") BASE_URL = "https://api.apiyi.com" resp = requests.post( f"{BASE_URL}/v1/messages", headers={ "content-type": "application/json", "anthropic-version": "2023-06-01", "x-api-key": API_KEY, }, json={ "model": "claude-opus-4-8", "max_tokens": 16000, "thinking": {"type": "adaptive", "display": "summarized"}, "output_config": {"effort": "xhigh"}, "messages": [ {"role": "user", "content": "分析微服务与单体架构的取舍"} ], }, timeout=300, ) data = resp.json() print("status:", resp.status_code, "| usage:", data.get("usage")) for block in data.get("content", []): if block.get("type") == "thinking": print("\n[思考摘要]\n", block.get("thinking", "")) elif block.get("type") == "text": print("\n[回答]\n", block.get("text", "")) ``` ```bash theme={null} curl https://api.apiyi.com/v1/messages \ -H "content-type: application/json" \ -H "anthropic-version: 2023-06-01" \ -H "x-api-key: your-apiyi-key" \ -d '{ "model": "claude-opus-4-8", "max_tokens": 16000, "thinking": { "type": "adaptive" }, "output_config": { "effort": "xhigh" }, "messages": [{ "role": "user", "content": "分析微服务与单体架构的取舍" }] }' ``` ## 经 Bedrock 时的注意事项 | 项 | 说明 | | - | - | | `output_config` | **必须放行**。网关若有 `delete output_config` 覆盖规则,effort 会被静默吞掉(返回 200 但无效)。 | | `effort` 位置 | 顶层 `output_config` 内,不能放进 `thinking`。 | | beta 头 | effort 不需要;adaptive thinking 也不需要。 | | `temperature` / `top_p` | Opus 4.7 / 4.8 配自适应思考时应使用默认采样;网关通常会为这两个模型剥离这两个参数,属正常,客户端不必设置。 | | 非法 effort 值 | Bedrock 对未知值**宽容降级**(返回 200),不报 400。所以不能用「invalid 是否报错」判断是否透传 —— 要看不同档位的 token 是否拉开差距。 | ## 常见报错排查 ### `"thinking.type.enabled" is not supported for this model` 经 AWS(Bedrock)通道调用 Opus 4.7 / 4.8 时,最常见的一个 400 报错: ``` status_code=400, InvokeModelWithResponseStream: ... Bedrock Runtime, StatusCode: 400, ValidationException: "thinking.type.enabled" is not supported for this model. Use "thinking.type.adaptive" and "output_config.effort" to control thinking behavior. ``` **原因**:请求体里传了旧版的固定预算思考写法 `thinking: { "type": "enabled", "budget_tokens": N }`。Opus 4.7 / 4.8(以及更新的模型)已**移除**这种写法,只支持自适应思考;AWS 上游会直接返回 `ValidationException` 400。这与 [thinking 自适应思考](#thinking-自适应思考) 章节的提示一致。 报错里的 `thinking.type.enabled` 就是你请求体里的 `thinking.type` 字段值为 `"enabled"`。同理,`budget_tokens` 也已不被支持;`temperature` / `top_p` / `top_k` 在这些模型上一并被移除,传了也会 400。 **解决**:删掉 `type: "enabled"` 和 `budget_tokens`,改用 `adaptive` + `output_config.effort` 控制思考深度。 ```json theme={null} { "model": "claude-opus-4-8", "max_tokens": 16000, "thinking": { "type": "adaptive", "display": "summarized" }, "output_config": { "effort": "xhigh" }, "messages": [ { "role": "user", "content": "你的问题" } ] } ``` | 旧写法(会 400) | 新写法 | | - | - | | `"thinking": { "type": "enabled", "budget_tokens": 8000 }` | `"thinking": { "type": "adaptive" }` | | 用 `budget_tokens` 控制思考量 | 用 `output_config.effort`(`low` \~ `max`)控制 | | `temperature` / `top_p` / `top_k` | 删除即可,用 prompt 引导,无需采样参数 | 不想思考时:Opus 4.7 / 4.8 可传 `thinking: { "type": "disabled" }`,或干脆不传 `thinking` 字段(不传即不思考)。 ## 参考 * Anthropic — Effort 文档:`platform.claude.com/docs/en/build-with-claude/effort` * AWS Bedrock — Adaptive thinking:`docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-adaptive-thinking.html` * AWS Bedrock — Claude Opus 4.8:`docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-opus-4-8.html` # Claude Prompt Caching 缓存计费指南 Source: https://docs.apiyi.com/api-capabilities/claude-prompt-caching Claude 原生格式的 Prompt Cache 入门:怎么写出会缓存的请求、怎么看账单、为什么没命中。看完能少花一半钱。 如果你用 Claude Code、Cline、Cursor,或者自己写代码调 Claude API,**Prompt Cache 是把账单打下来最直接的一件事**——命中缓存的部分只按 **0.1×** 计费,相当于打 1 折。 本页基于 Anthropic 官方文档整理(`platform.claude.com/docs/en/build-with-claude/prompt-caching`),并按 API易 的接入方式给出可直接复制的示例。 ## 一句话理解 把一段**反复使用的长 prompt**(系统说明 / 长文档 / few-shot 示例)打上 `cache_control` 标记,服务器会把它存起来。下次相同前缀的请求来,服务器跳过重复处理,**便宜约 10 倍、也更快**。一段时间内没人再用就会过期。 ## 为什么要用 —— 看账单倍率 以模型原始输入 token 价为 **1×** 计: | 类型 | 价格 | 说明 | | - | - | - | | 普通输入 | **1×** | 没缓存的部分,原价 | | 缓存写入(5 分钟 TTL) | **1.25×** | 第一次写入贵 25% | | 缓存写入(1 小时 TTL) | **2×** | 想存更久要付更多 | | **缓存读取(命中)** | **0.1×** | 后续每次便宜 90% | **回本点:** * **5 分钟 TTL**:只需 **2 次**复用同一前缀即可回本(1.25 + 0.1 = 1.35,比两次不缓存的 2.0 便宜)。 * **1 小时 TTL**:需要 **3 次**才回本(2 + 0.2 = 2.2,比 3.0 便宜)。 TTL 是**滑动窗口**:每次命中都会把过期时间重置,因此活跃的会话不会平白过期。只有真正闲置超过 TTL 才会失效。 ### 适合场景 * 同一份长系统提示词被多次调用(Agent、客服机器人) * 多轮对话(每加一轮,前面的历史都能复用) * 批量处理同一份文档(一份合同问 50 个问题) * RAG 把检索到的稳定文档块作为前缀 ### 不适合场景 * 每次 prompt 从第一个字开始都不一样 * 整体很短,根本到不了最小 token 阈值(见下) * 聊天客户端里**每轮都重新组织上传附件**的对话(原因见下方「图片和文件也能缓存」) ## 触发缓存的三个硬条件 缺一不可。 ### 1. 必须显式打标记 `cache_control` `content` 不能是纯字符串,必须是 **content block 数组**,在要缓存的那一块上加 `cache_control`: ```python theme={null} # ❌ 错:纯字符串永不缓存 "content": "一大段长文..." # ✅ 对:content block + cache_control "content": [ { "type": "text", "text": "一大段长文...", "cache_control": {"type": "ephemeral"}, }, {"type": "text", "text": "问题"}, ] ``` ### 2. 长度必须达到最小阈值 短于阈值的内容,**就算打了标记也不会缓存**(不报错,静默忽略)。按模型不同: | 最小 tokens | 模型 | | - | - | | **512** | Opus 5、Fable 5 / 5.1、Mythos 5 | | **1024** | Opus 4.8、Sonnet 5、Sonnet 4.6、Sonnet 4.5、Sonnet 4、Opus 4.1、Opus 4 | | **2048** | Opus 4.7、Haiku 3.5 | | **4096** | Opus 4.6、Opus 4.5、Haiku 4.5 | **这个阈值不随版本号单调下降,别凭直觉猜。** 最典型的反直觉组合:Opus 5 只要 **512**,而更早的 Opus 4.6 / 4.5 要 **4096**,整整差 8 倍;Haiku 4.5 也是 4096,比它更老的 Haiku 3.5(2048)还高。所以「新模型门槛更低」「小模型门槛更低」这两个推断都不成立,换模型时务必查表。 中文 1 个字大约 0.5–1 token。换算下来:Opus 5 大约 **500 字**以上就能缓存,Sonnet 5 / Sonnet 4.6 需要 **1000 字**左右,而 Opus 4.6 / Haiku 4.5 要到 **4000 字**才有意义。阈值可能随官方版本变化,**以 Anthropic 官方文档为准**。 **实测校验(2026-07-29,API易 站内)。** 我们用逐档递增的固定前缀实测了写入起点:`claude-opus-5` 在 301 tokens 时不产生缓存写入、614 tokens 时产生,落点与官方 **512** 一致;`claude-sonnet-5` 在 612 tokens 时不写入、1250 tokens 时写入,落点与官方 **1024** 一致。两者均与上表吻合。 ### 3. 前缀必须逐字节相同 缓存按**前缀匹配**:从请求开头一直到 `cache_control` 标记位置,这段字节流必须和上一次**完全一样**。改任何一个字符——哪怕是空格、JSON 字段顺序、时间戳——都算"新前缀",会重新写入而不是命中。 **实践含义:稳定的东西放前面,易变的东西放后面。** ```python theme={null} # ❌ 错:每换一个问题前缀就变了,永远命中不了 content = [ {"type": "text", "text": "请回答下面问题: " + 问题}, # 这块在变 {"type": "text", "text": 长文, "cache_control": {"type": "ephemeral"}}, ] # ✅ 对:长文在前打标记,问题在后不打标记 content = [ {"type": "text", "text": 长文, "cache_control": {"type": "ephemeral"}}, # 稳定 {"type": "text", "text": 问题}, # 随便变 ] ``` ## 最小可运行示例 跑两次同一段长文 + 不同问题,第一次写入缓存,第二次命中: ```python theme={null} import json, os, requests URL = "https://api.apiyi.com/v1/messages" KEY = os.environ["APIYI_API_KEY"] HEADERS = { "content-type": "application/json", "x-api-key": KEY, "anthropic-version": "2023-06-01", } # 必须足够长。Sonnet 4.6 至少 1024 tokens,大约 1000+ 中文字。 LONG_TEXT = open("long_document_zh.txt").read() def ask(question: str, label: str): payload = { "model": "claude-sonnet-4-6", "max_tokens": 256, "messages": [{ "role": "user", "content": [ {"type": "text", "text": LONG_TEXT, "cache_control": {"type": "ephemeral"}}, {"type": "text", "text": question}, ], }], } r = requests.post(URL, headers=HEADERS, data=json.dumps(payload), timeout=120) u = r.json().get("usage", {}) print(f"[{label}] input={u.get('input_tokens')} " f"write={u.get('cache_creation_input_tokens')} " f"read={u.get('cache_read_input_tokens')}") ask("请概括主旨", "第1次") # 期望 write>0, read=0 ask("请给3个关键词", "第2次") # 期望 write=0, read>0 ``` 期望看到的输出: ```text theme={null} [第1次] input=35 write=6512 read=0 [第2次] input=22 write=0 read=6512 ``` 第 2 次的 `read` ≈ 第 1 次的 `write`,说明同一段前缀被命中复用了。 ## 怎么判断命中没命中 —— 看三个字段 每次响应的 `usage` 里: | 字段 | 含义 | 计费倍率 | | - | - | - | | `input_tokens` | 没被缓存的剩余输入 token | 1× | | `cache_creation_input_tokens` | 这次写入缓存的 token 数 | 1.25× 或 2× | | `cache_read_input_tokens` | 这次从缓存读取的 token 数 | **0.1×** | **输入总量 = 三者之和。** 只要 `cache_read_input_tokens > 0`,你就在省钱。 **一个很好用的自检判据**:真正命中时,本轮的 `cache_read_input_tokens` 会**精确等于上一轮的 `cache_creation_input_tokens`**——上一轮写进去多少,这一轮就整块读回来多少,本轮新增的内容才计入本轮的 write。 所以:`read` 约等于上一轮的 `write` = 前缀完整命中;`read` 明显小于上一轮的 `write` = 只命中了更靠前的一段,中间有东西变过,顺着那段去找就能定位到是什么在变。 ## 最常见的踩坑 | 现象 | 原因 | | - | - | | `write` 永远是 `0` 或字段不存在 | 没打 `cache_control` 标记 / 长度没到阈值 / 用了 OpenAI 兼容格式 | | 第 2 次 `write` 又 > 0,`read` 还是 0 | 前缀变了。常见:prompt 里有 `datetime.now()`、UUID、变化的用户 ID;JSON 序列化顺序不稳定;带时间戳的 system 提示 | | 隔了一会儿再调又变成写入 | 闲置超过 TTL 过期。要常驻可加 `{"type": "ephemeral", "ttl": "1h"}` | | 同一份 prompt 切换模型后没命中 | 缓存按模型隔离,换模型相当于换 key | | 用 Fable 5 / 5.1 时某一轮 `read` 突然归 0,响应 `model` 变成 `claude-opus-4-8`(或 `claude-opus-5`),或 `stop_reason` 为 `refusal` | 触发了内容安全回退 / 拒答。回退等于换模型换缓存键,本轮读不到之前的缓存;被拒那一轮写入的缓存后续也不会被读取。见下方说明 | | 长对话最近几轮不命中 | 单次请求最多 **4 个** `cache_control` 断点;且每个断点的**前缀查找窗口为 20 个 block**,更久远的内容不会被纳入命中检查 | | 对话里带了上传的文件或大图,命中忽高忽低 | **不是 Claude 的图片/文件缓存有问题**,是客户端每轮重新组织附件让前缀变了。见下方「图片和文件也能缓存」 | **Fable 系列的安全回退会打断缓存。** `claude-fable-5` / `claude-fable-5-1` 内建安全分类器,命中高风险内容时会**拒答**(HTTP 200 + `stop_reason: "refusal"`)或**安全回退**到 Opus 系列处理,响应顶层的 `model` 会如实回显回退后的模型。这是模型侧的正常行为,不是中转层问题。 对缓存的影响:回退等于换模型、换缓存键,这一轮读不到前面轮次写的缓存,下一轮回到 Fable 后才能继续命中;被拒的那一轮即使报了 `cache_creation_input_tokens`,这份写入后续不会被读取。多轮 agent 会话里表现为**个别轮次 `read` 归 0、`write` 重新变大**。 怎么办:判断命中先看响应 `model` 和 `stop_reason`,别只看 usage;被拒的输入调整后再发,不要原样重试;对话内容保持规范就能把这类 miss 压到最低。拒答、回退与计费的细节见 [Fable 5.1 上线说明](/news/claude-fable-5-1-launch)。 **Prompt Cache 只在 Anthropic 原生格式(`/v1/messages`)下生效。** 用 OpenAI 兼容格式(`/v1/chat/completions`)调 Claude 时,无论你怎么传,都拿不到缓存计费。Claude Code、Cline、Cursor 等深度场景请务必走原生格式。 ## 图片和文件也能缓存 —— 但附件场景命中会不稳 先澄清一个常见误解:**不是 Claude 对图片/文件的缓存"有问题"**。Anthropic 官方明确说明**文本、图片、文档都可以进入 Prompt Cache**,三者一视同仁。 真正的原因还是上面那条硬条件:缓存匹配的是**请求的完整前缀**,而图片和文件的那串字节同样算在前缀里。只要客户端每轮对附件的组织方式有任何变化,前缀就断了。官方还特别提到一点:**改变 prompt 里是否包含图片,会让对应的缓存失效**。 ### 两类场景的差别 | | Coding / 纯文本 | 聊天客户端 + 附件 | | - | - | - | | 典型形态 | 固定 system prompt + 代码上下文 + 不断向后追加的历史 | 每轮可能重新组织 PDF / 大图附件 | | 前缀怎么变 | 只在末尾追加,前面原样不动 | 附件重新上传 / 重新编码、content block 顺序或结构变化 | | 命中表现 | **稳定** —— 正是 prefix caching 最擅长的形态 | **忽高忽低** | Claude Code、Cline、Cursor 这类编码客户端属于前一类,命中通常很稳。Chatbox、Cherry Studio 这类 GUI 客户端在**纯文本对话**时同样稳,只有在**对话里夹了上传文件或较大图片附件**时,才容易出现命中率掉下去的情况。 ### 附件场景怎么把命中率救回来 1. **附件放最前面、顺序固定** —— 它们是最大块也最稳定的内容,天然该占据前缀 2. **同一份附件只编码一次,之后复用同一份 base64**,不要每轮重新读文件重新编码(重新编码出来的字节不保证完全一致) 3. **不要每轮重排 content block**,尤其不要"这轮图在前、下轮图在后" 4. **`cache_control` 打在附件之后**,让附件整体落进缓存 5. **对话中途尽量不增删附件** —— 加一张图、去一张图,都会让它之后的整段前缀失效 **要跟非技术同事或客户解释时,可以这么说:** Claude 的缓存本身支持文本、图片和文件。不过在 Chatbox 这类客户端中,如果对话里包含上传文件或较大的图片附件,客户端每轮请求对附件和上下文的组织方式可能会发生变化,因此实际缓存命中率会相对不稳定。在 Coding 这类以纯文本、代码上下文为主的场景中,请求上下文通常比较稳定,所以缓存命中一般是正常且比较稳定的。 这样说的好处是把原因落在**客户端怎么组织请求**上,不会被理解成"Claude 官方的图片/文件缓存有 Bug"。 ## 进阶:多轮对话怎么打 把 `cache_control` 打在**最近一条 user 消息的最后一个 content block** 上。每加一轮,缓存读取范围会自动延伸到上一轮结束的位置: ```python theme={null} # 每一轮请求构造时 messages[-1]["content"][-1]["cache_control"] = {"type": "ephemeral"} ``` 两个硬限制要注意: * 单次请求最多 **4 个** `cache_control` 断点。 * 每个断点的**前缀查找只回溯最近 20 个 content block**——超出 20 个 block 的更久远内容不会再被检索去拼命中。换言之:很长的多轮对话靠"最末一次打标记"是兜不住前面所有历史的。 实践建议:在工具定义/系统提示/长文档/最近一轮对话各打一个断点,正好用满 4 个槽位,让不同变化频率的内容互不影响彼此的命中。 ## API易 关于缓存的说明 **API易完整透传缓存字段。** 你在请求里写的 `cache_control` 会原样转发给上游 Claude(AWS Claude 或 Claude Official),响应里的 `cache_creation_input_tokens` / `cache_read_input_tokens` 也会原样回吐给你——所以你的代码无需为中转层做任何额外适配。 如何自检: 1. 第一次发送时观察响应 `usage.cache_creation_input_tokens > 0`(写入成功)。 2. 几秒后用相同前缀再发一次,应看到 `usage.cache_read_input_tokens > 0`(命中)。 3. 后台账单里会单独显示**缓存写入** / **缓存读取**两类计费项,倍率与官方一致(1.25× / 2× / 0.1×)。 ## 要点回顾 `cache_control: {"type": "ephemeral"}` 加在 content block 上,**纯字符串 content 永不缓存**。 Opus 5 ≥ 512、Sonnet 5 / Sonnet 4.6 ≥ 1024、Opus 4.7 ≥ 2048、Opus 4.6 / Haiku 4.5 ≥ 4096 tokens,否则静默忽略。 稳定内容在前、易变内容在后;任何一个字符变化都会让缓存失效。 `cache_read_input_tokens > 0` 才说明真的省钱了;它应约等于上一轮的 `cache_creation_input_tokens`。 图片和文件同样能缓存,但每轮重新组织附件会让前缀失效——附件放前面、顺序固定、只编码一次。 ## 相关链接 * 父页面:[Claude API 调用基础说明](/api-capabilities/claude) * 客户端配置教程:[Claude Code 接入指南](/scenarios/programming/claude-code) · [Cherry Studio 接入指南](/scenarios/chat/cherry-studio) * 获取 / 管理令牌:`https://api.apiyi.com/token` * Anthropic 官方文档:`platform.claude.com/docs/en/build-with-claude/prompt-caching` # Claude 原生格式:流式与非流式响应 Source: https://docs.apiyi.com/api-capabilities/claude-response-handling Anthropic 原生 /v1/messages 的流式与非流式响应结构:content 块数组、具名事件 SSE 协议,附解析方法与各字段说明。 通过 [Claude 原生格式](/api-capabilities/claude)(`/v1/messages`)调用时,响应结构与 OpenAI 兼容格式**完全不同**:正文是按类型区分的 `content` 块数组,流式则用 Anthropic 的**具名事件 SSE 协议**。本页讲清两种模式怎么解析。 请求侧(端点、`anthropic-version` 头、`x-api-key` 鉴权、effort / thinking 参数)见 [Claude API 基础说明](/api-capabilities/claude) 与 [Claude Effort 思考指南](/api-capabilities/claude-effort-thinking)。本页只讲**响应侧**。示例用轻量模型 `claude-haiku-4-5-20251001`。 ## 非流式响应 顶层是一个 `message` 对象,**正文在 `content` 数组**里,按 `type` 区分块: ```json theme={null} { "id": "msg_bdrk_xxx", "type": "message", "role": "assistant", "model": "claude-haiku-4-5-20251001", "content": [ { "type": "text", "text": "1+1等于2。" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 26, "output_tokens": 11, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0 } } ``` 取正文要**遍历 `content` 数组**——不能像 OpenAI 那样直接取一个字符串字段: ```python Python theme={null} import requests resp = requests.post( "https://api.apiyi.com/v1/messages", headers={ "content-type": "application/json", "anthropic-version": "2023-06-01", "x-api-key": "YOUR_API_KEY", }, json={ "model": "claude-haiku-4-5-20251001", "max_tokens": 100, "messages": [{"role": "user", "content": "1+1等于几?"}], }, timeout=60, ) data = resp.json() for block in data["content"]: if block["type"] == "text": print(block["text"]) elif block["type"] == "thinking": # 开启思考时才有 print("[思考]", block["thinking"]) print(data["usage"]) ``` ```bash cURL theme={null} curl https://api.apiyi.com/v1/messages \ -H "content-type: application/json" \ -H "anthropic-version: 2023-06-01" \ -H "x-api-key: YOUR_API_KEY" \ -d '{ "model": "claude-haiku-4-5-20251001", "max_tokens": 100, "messages": [{"role": "user", "content": "1+1等于几?"}] }' ``` `stop_reason` 取值:`end_turn`(正常结束)、`max_tokens`(被 `max_tokens` 截断,正文可能为空,调大即可)、`stop_sequence`、`tool_use`(要调用工具)、`refusal`(被安全策略拒答,`content` 为空数组,见 [Claude 拒答为什么返回空内容](/faq/claude-content-safety-refusal))。开启思考后,`content` 数组里会多出 `type: "thinking"` 的块,排在 `text` 块之前。 ## 流式响应(具名事件 SSE) Claude 流式用的是 **Anthropic 事件协议**:每条消息有 `event:` 名称 + `data:` 负载,需要**按事件类型分发**,而不是像 OpenAI 那样每块都同构。 ```text theme={null} event: message_start data: {"type":"message_start","message":{"id":"...","content":[],"usage":{"input_tokens":26,"output_tokens":8}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"1+1等于2。"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":11}} event: message_stop data: {"type":"message_stop"} ``` 事件流的固定顺序与职责: | 事件 | 作用 | | - | - | | `message_start` | 消息骨架;`usage.input_tokens` 和初始 `output_tokens` 在此 | | `content_block_start` | 一个内容块开始(`index` + 块类型 text / thinking) | | `content_block_delta` | 增量;正文是 `delta.type == "text_delta"` 的 `delta.text` | | `content_block_stop` | 当前块结束 | | `message_delta` | 最终 `stop_reason` + **累计的 `output_tokens`** 在此 | | `message_stop` | 整条消息结束(**无 `[DONE]`,以此事件为终止信号**) | 解析的核心是**累加 `content_block_delta` 里的 `text_delta`**: ```python theme={null} import json, requests resp = requests.post( "https://api.apiyi.com/v1/messages", headers={ "content-type": "application/json", "anthropic-version": "2023-06-01", "x-api-key": "YOUR_API_KEY", }, json={ "model": "claude-haiku-4-5-20251001", "max_tokens": 100, "stream": True, "messages": [{"role": "user", "content": "写一首短诗"}], }, stream=True, timeout=120, ) text, usage = "", {} for line in resp.iter_lines(decode_unicode=True): if not line or not line.startswith("data: "): continue # event: 行可跳过,类型也在 data 的 "type" 里 evt = json.loads(line[6:]) t = evt["type"] if t == "message_start": usage.update(evt["message"]["usage"]) elif t == "content_block_delta" and evt["delta"]["type"] == "text_delta": piece = evt["delta"]["text"] text += piece print(piece, end="", flush=True) elif t == "message_delta": usage.update(evt["usage"]) # 最终 output_tokens elif t == "message_stop": break # 终止信号,无 [DONE] ``` 事件类型在 `event:` 行和 `data:` 负载的 `"type"` 字段里**都有**,按任一个分发都行。用官方 `anthropic` SDK 时,把 base\_url 指向 `https://api.apiyi.com` 即可,SDK 会自动处理事件流,无需手写上面的循环。 开启思考(adaptive thinking)时,会先出现 `type: "thinking"` 的内容块,其增量是 `thinking_delta`,并在块结束前出现一个 `signature_delta`(思考块签名)。展示思考时把 `thinking_delta` 与 `text_delta` 分流渲染即可。思考用法见 [Claude Effort 思考指南](/api-capabilities/claude-effort-thinking)。 ## 与 OpenAI 兼容格式的关键差异 | 维度 | Claude 原生(`/v1/messages`) | OpenAI 兼容(`/v1/chat/completions`) | | - | - | - | | 正文位置 | `content` **块数组**,按 `type` 区分 | `choices[0].message.content` 字符串 | | 流式协议 | 具名事件(`event:` + `data:`) | 同构 chunk 对象 | | 流式终止 | `message_stop` 事件,**无 `[DONE]`** | `data: [DONE]` | | 增量字段 | `content_block_delta.delta.text` | `choices[0].delta.content` | | usage 字段 | `input_tokens` / `output_tokens`(分散在 message\_start 与 message\_delta) | `prompt_tokens` / `completion_tokens` / `total_tokens` | | 结束原因 | `stop_reason`(`end_turn` 等) | `finish_reason`(`stop` 等) | | `max_tokens` | **必填** | 选填 | 迁移最容易踩的两点:① 正文是**数组**不是字符串,必须遍历 `content` 取 `type=="text"` 的块;② 流式**没有 `[DONE]`**,要用 `message_stop` 事件判结束。 ## usage 与计费 * 非流式:`usage` 随结果返回,含 `input_tokens`、`output_tokens`、`cache_creation_input_tokens`、`cache_read_input_tokens`。 * 流式:`input_tokens` 在 `message_start`,最终 `output_tokens` 在 `message_delta`,需**两处合并**。 * 缓存命中字段(`cache_read_input_tokens`)的折扣与用法见 [Claude 缓存计费](/api-capabilities/claude-prompt-caching)。 ## 相关链接 * 同组页面:[Claude API 基础说明](/api-capabilities/claude) · [Claude 缓存计费](/api-capabilities/claude-prompt-caching) · [Claude Effort 思考指南](/api-capabilities/claude-effort-thinking) * 兼容格式对照:[OpenAI 兼容模式响应数据处理](/api-capabilities/openai/response-handling) * 获取 / 管理令牌:`https://api.apiyi.com/token` # Claude API 联网搜索实现指南 Source: https://docs.apiyi.com/api-capabilities/claude-web-search 默认分组(AWS Claude)不支持原生 web_search 工具,本文给出两条实测可行的联网方案:ClaudeOfficial 内测分组与自定义搜索工具。 本文说明在 API易 上使用 Claude 系列模型实现联网搜索的两条路径,基于 2026年6月 实测验证。基础的通道、计费与接入信息请先看 [Claude API 基础说明](/api-capabilities/claude)。 ## 一句话结论 **API易 Claude 系列默认分组为 AWS Claude 官方直转(Amazon Bedrock),AWS 原厂不支持 Claude 原生联网搜索**——这是 Bedrock 的架构限制,不是中转配置问题。如需联网,您有两条路: | 路径 | 适用场景 | 稳定性 | | - | - | - | | **方式一:ClaudeOfficial 内测分组**(原生 `web_search` 工具) | 强烈需要 Anthropic 原生搜索体验、可接受内测稳定性 | ⚠️ 内测,不如默认分组 | | **方式二:自定义搜索工具**(默认分组即可用,推荐) | 生产环境、需要稳定可控 | ✅ 与默认分组同级 | **容易踩的坑**:在默认分组带上 `web_search` 工具发请求,**不会报错**——网关会兼容处理,请求正常返回 HTTP 200,但搜索没有发生,模型只是凭训练数据回答。请不要以"没报错"判断联网已生效,判断方法见文末 FAQ。 ## 为什么默认分组不支持? Claude 的 `web_search` / `web_fetch` 属于 **server-side tools**:搜索动作由 Anthropic 自己的服务端基础设施执行。AWS Bedrock 只提供模型推理,没有这套搜索后端,因此 Bedrock 接口在校验层就会拒绝这类工具。Bedrock 支持的工具类型白名单仅包含 client-side 工具: ``` bash_20250124, custom, memory_20250818, text_editor_*(20250124/0429/0728), tool_search_tool_bm25(_20251119), tool_search_tool_regex(_20251119) ``` 同理,Anthropic 的 **MCP Connector(`mcp_servers` 参数)也属于服务端功能,默认分组不支持**。 ## 方式一:ClaudeOfficial 内测分组(原生 web\_search) API易 提供一个内测分组 **ClaudeOfficial**(Anthropic 官方渠道直连),支持 Claude 原生 `web_search` / `web_fetch` 工具。 * **开通方式:联系客服对接**,将您的 key 加入 ClaudeOfficial 分组 * **稳定性提示**:内测分组,稳定性不如默认分组,重要业务请做好降级(可降级到方式二) ### 请求示例 ```bash theme={null} curl https://api.apiyi.com/v1/messages \ -H "content-type: application/json" \ -H "x-api-key: 你的APIYI_KEY(ClaudeOfficial分组)" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 2048, "tools": [{"type": "web_search_20260209", "name": "web_search"}], "messages": [{"role": "user", "content": "Anthropic 最近发布了什么新模型?请搜索并给出来源"}] }' ``` 工具版本说明: | 工具 | type | 说明 | | - | - | - | | Web Search(推荐) | `web_search_20260209` | 带动态过滤,4.6+ 模型 | | Web Search(基础) | `web_search_20250305` | 兼容更早模型 | | Web Fetch | `web_fetch_20260209`(旧版 `web_fetch_20250910`) | 抓取指定 URL 内容 | 可选参数:`max_uses`(限制搜索次数)、`allowed_domains` / `blocked_domains`(域名过滤)。 ### 怎么确认搜索真的执行了 成功响应的 `content` 中会出现 `server_tool_use` 和 `web_search_tool_result` 块,正文带引用;`usage` 中出现计次字段: ```json theme={null} "usage": { "server_tool_use": { "web_search_requests": 2 } } ``` 若响应只有 `text` 块、`usage` 里没有 `server_tool_use`,说明这条请求没有走到支持搜索的渠道。 ### 计费 * **工具名:`web_search`、`web_fetch`** * web\_search:**\$10 / 1000 次搜索**(\$0.01/次,按 `usage.server_tool_use.web_search_requests` 计次——一次回答可能触发多次搜索)+ 正常 token 费;失败的搜索不计费 * web\_fetch:不额外按次收费,抓取内容按 input token 计费 * 单次联网问答参考成本(sonnet):约 \$0.02–0.08 ## 方式二:自定义搜索工具(默认分组可用,推荐生产使用) 默认分组(Bedrock)**完整支持标准 function calling(custom tools)**。您只需定义一个搜索工具,由您的客户端执行实际搜索(接 Tavily / Brave / Serper / Bing 等搜索 API),把结果回传给模型。实测 Claude 会主动调用、自动改写中英文查询词、多轮搜索后给出带来源的回答。 ### 完整示例(Python) ```python theme={null} import requests API_KEY = "你的APIYI_KEY" # 默认分组即可 URL = "https://api.apiyi.com/v1/messages" HEADERS = { "content-type": "application/json", "x-api-key": API_KEY, "anthropic-version": "2023-06-01", } SEARCH_TOOL = { "name": "web_search", "description": ("Search the web for current information. Call this whenever " "the user asks about recent events or anything after your " "knowledge cutoff. You may call it multiple times."), "input_schema": { "type": "object", "properties": {"query": {"type": "string", "description": "搜索关键词"}}, "required": ["query"], }, } def do_search(query: str) -> str: """接您选用的搜索 API(Tavily/Brave/Serper 等),返回结果文本。""" # 以 Tavily 为例: # r = requests.post("https://api.tavily.com/search", # json={"api_key": TAVILY_KEY, "query": query, "max_results": 5}) # return "\n".join(f"- {x['title']}\n {x['url']}\n {x['content'][:200]}" # for x in r.json()["results"]) ... messages = [{"role": "user", "content": "Anthropic 最近发布了什么新模型?请搜索后回答并附来源"}] for _ in range(5): # 工具循环,最多 5 轮 resp = requests.post(URL, headers=HEADERS, json={ "model": "claude-sonnet-4-6", "max_tokens": 2048, "tools": [SEARCH_TOOL], "messages": messages, }, timeout=180).json() if resp.get("stop_reason") != "tool_use": print(next(b["text"] for b in resp["content"] if b["type"] == "text")) break messages.append({"role": "assistant", "content": resp["content"]}) results = [{"type": "tool_result", "tool_use_id": b["id"], "content": do_search(b["input"]["query"])} for b in resp["content"] if b["type"] == "tool_use"] messages.append({"role": "user", "content": results}) ``` ### 成本参考 * 模型 token 费:一次多轮搜索问答约 1 万 input + 1–2 千 output token(sonnet 约 \$0.05) * 搜索 API 费:Tavily 免费档 1000 次/月(付费约 \$0.008/次)、Brave \$3/1000 次——与官方 web\_search 同量级 * 优势:渠道无关、搜索源可控可缓存、保留默认分组的稳定性与缓存计费优势 ### 进阶:MCP 搜索源 若您已有 MCP 生态(如 Tavily MCP、Brave MCP),可在**客户端**连接 MCP server,把其工具转换成上面的 custom tools 传给模型——原理完全相同。注意:直接在请求里传 `mcp_servers` 参数(服务端 MCP)在默认分组**不可用**。 ## FAQ **Q:我在默认分组带了 web\_search 工具,没报错,是不是就支持了?** A:不是。默认分组对 server tools 做兼容处理(忽略),请求返回 200 但搜索未发生。判断方法:看响应 `content` 是否有 `server_tool_use` 块、`usage` 是否有 `server_tool_use.web_search_requests` 字段——没有就是没执行。 **Q:传 `mcp_servers` 参数呢?** A:同样不支持(也是 Anthropic 服务端功能)。特别提醒:这种情况下模型可能在正文中生成看似真实的"工具调用结果"文本,那是模型的幻觉,并非真实数据,请勿采信。 **Q:两种方式怎么选?** A:生产环境优先方式二(稳定、可控);需要 Anthropic 原生搜索质量、引用格式(citations)或不想自建搜索的,联系客服开通 ClaudeOfficial 内测分组,并准备好降级方案。 ## 相关文档 通道说明、模型列表、接入与计费基础信息 多轮搜索问答场景配合缓存可显著降本 # 废弃模型列表 Source: https://docs.apiyi.com/api-capabilities/deprecated-models 查看即将下线和已废弃的模型列表,及时迁移到推荐替代模型。 ## 说明 本页面列出即将下线和已废弃的模型,帮助您了解模型生命周期,提前做好迁移准备。 如果您正在使用即将废弃的模型,请尽快迁移到推荐的替代模型,以避免服务中断。 ## ⚠️ 废弃模型预告 以下模型即将下线,请在预计下线日期前完成迁移。 | 模型名称 | 模型ID | 预计下线日期 | 推荐替代模型 | 备注 | | - | - | - | - | - | | Gemini 3 Pro Preview | `gemini-3-pro-preview` | 已透明替换(2026-05) | `gemini-3.1-pro-preview` | **与谷歌官方同步**:考虑到已有客户在线上使用旧 ID,调用 `gemini-3-pro-preview` 会自动路由到 `gemini-3.1-pro-preview`,避免业务中断;建议尽快迁移到新 ID | | Gemini 3.1 Flash Lite Preview | `gemini-3.1-flash-lite-preview` | 已透明替换(2026-05) | `gemini-3.1-flash-lite` | **与谷歌官方同步**:谷歌已于 2026-05-25 下线该 Preview,调用旧 ID 会自动路由到 `gemini-3.1-flash-lite`,**价格相同**、跟随官网变化,避免业务中断;建议尽快迁移到新 ID | | Gemini 2.0 Flash Lite | `gemini-2.0-flash-lite` | 待定 | `gemini-2.5-flash` | Gemini 2.0 系列整体下线 | | Gemini 2.0 Flash Lite 001 | `gemini-2.0-flash-lite-001` | 待定 | `gemini-2.5-flash` | Gemini 2.0 系列整体下线 | | Gemini 2.0 Flash | `gemini-2.0-flash` | 待定 | `gemini-2.5-flash` | Gemini 2.0 系列整体下线 | | Gemini 2.0 Flash 001 | `gemini-2.0-flash-001` | 待定 | `gemini-2.5-flash` | Gemini 2.0 系列整体下线 | | Gemini 2.0 Flash Lite Preview 02-05 | `gemini-2.0-flash-lite-preview-02-05` | 待定 | `gemini-2.5-flash` | Preview 版本 | 谷歌 Gemini 模型废弃详情请参考官方文档:`ai.google.dev/gemini-api/docs/deprecations` ## 🚫 已下线模型 以下模型已废弃,不再可用。如果您的应用仍在调用这些模型,请尽快切换到替代模型。 | 模型名称 | 模型ID | 下线日期 | 替代模型 | 备注 | | - | - | - | - | - | | Kimi K2 | `kimi-k2` | 2026-05-28 | `kimi-k2.6` | 旧版 Kimi K2 系列,请迁移到最新的 K2.6 | | Kimi K2 128K | `kimi-k2-128k` | 2026-05-28 | `kimi-k2.6` | 旧版长上下文 K2,K2.6 已默认 256K 上下文 | | Kimi K2 Instruct | `kimi-k2-instruct` | 2026-05-28 | `kimi-k2.6` | 旧版指令微调 K2,请迁移到 K2.6 | | DeepSeek V3.1 | `deepseek-v3-1-250821` | 2026-05-28 | `deepseek-v4-pro` / `deepseek-v4-flash` | DeepSeek V3 系列下线,请迁移至 V4:复杂推理选 Pro,高并发/性价比选 Flash | | DeepSeek V3.1 Terminus | `deepseek-v3.1-terminus` | 2026-05-28 | `deepseek-v4-pro` / `deepseek-v4-flash` | DeepSeek V3 系列下线,请迁移至 V4 | | DeepSeek R1 (250528) | `deepseek-r1-250528` | 2026-05-28 | `deepseek-v4-pro` / `deepseek-v4-flash` | R1 推理模型已被 V4 系列取代,推理能力请用 V4 Pro | | GPT-4 | `gpt-4` | 2026-02-27 | `gpt-5.2` | 一代目强者,已停用 | | GPT-4(32K) | `gpt-4-32k` | 2026-02-27 | `gpt-5.2` | 一代目强者,已停用 | | Grok 4 | `grok-4` | 2026-03-12 | `grok-4-1-fast-reasoning` | 已下线,请迁移至新版 | | Grok 4 0709 | `grok-4-0709` | 2026-03-12 | `grok-4-1-fast-reasoning` | 已下线,请迁移至新版 | | Sora 2 视频生成(官转) | `sora-2` / `sora-2-pro` / `sora-2-remix` | 2026-07-01 | `doubao-seedance-2-0` / `wan2.7-t2v` | **Sora 2 全系已下线**。OpenAI 官方算力持续倾斜导致超时严重,官方定于 9 月正式下线,本站提前于 7 月 1 日下线 `Sora2Official` 分组,该分组现已停用。详见 [服务通知](/live/2026-07/sora2-official-offline) | | Sora 2 视频生成(逆向) | `sora_video2` / `sora_video2-landscape` / `sora_video2-15s` / `sora_video2-landscape-15s` | 2026-04-26 | `doubao-seedance-2-0` / `wan2.7-t2v` | 逆向通道下线。其后继的官转 Sora 2 也已于 2026-07-01 下线,请直接迁移到 [SeeDance 2.0](/api-capabilities/seedance2/overview) 或 [Wan2.7](/api-capabilities/wan/overview) | | Sora 2 角色生成 | `sora-character` | 2026-04-26 | `doubao-seedance-2-0` | 逆向通道下线。人物一致性场景请改用 [Seedance 素材库](/api-capabilities/seedance2/asset-library) | | Sora Image 生图/编辑(逆向) | `sora_image` | 2026-04-26 | `gpt-image-2-all`(新版官逆) | 逆向通道下线,请迁移至 [GPT-Image-2-All](/api-capabilities/gpt-image-2-all/overview) | | GPT-4o Image 生图/编辑(逆向) | `gpt-4o-image` | 2026-04-26 | `gpt-image-2-all`(新版官逆) | 逆向通道下线,请迁移至 [GPT-Image-2-All](/api-capabilities/gpt-image-2-all/overview) | ## 🔗 各大模型官方弃用页面 各模型供应商的官方模型生命周期与弃用公告页面,便于对照查询最新动态。点击卡片直接跳转到官方文档。 GPT 系列模型弃用列表与替代建议 Claude 模型弃用计划(中文官方文档) Gemini 系列模型下线时间表与迁移指引 Grok 模型迁移说明与历次下线公告 百炼平台 Qwen 模型上下架与更新公告 DeepSeek API 更新日志与模型变更记录 以上链接将跳转至各厂商官方文档,建议收藏并定期查看,以掌握最新的模型下线安排。 本页面会持续更新,建议定期查看以获取最新的模型废弃信息。 # Gemini 生图 API 错误处理指南 Source: https://docs.apiyi.com/api-capabilities/gemini-image-error-handling gemini-3-pro-image-preview(Nano Banana Pro)出图失败的三大判断指标、谷歌内容审核政策与 C 端友好提示方案,帮助开发者把技术错误转化为可操作的用户提示。 ## 概述 `gemini-3-pro-image-preview`(即 Nano Banana Pro)对内容安全有严格控制,会在多个层级拒绝不合规的请求。简单的「生成失败」提示无法帮助用户理解问题,一套好的错误处理需要做到: * **精准识别拒绝原因** —— 区分内容违规、知识库限制、技术错误 * **友好的用户提示** —— 把技术错误转化为可理解的说明 * **可操作的建议** —— 告诉用户怎么改才能成功 * **完整的技术信息** —— 供开发者调试排查 当请求返回 **HTTP 200 但没有图片** 时,这通常是谷歌侧的安全判定。API易 透明代理只是如实转发结果——我们同样希望客户成功出图。判断与文案处理需要在你的应用侧完成。 ## 谷歌内容审核政策(2026 更新) 谷歌的图片生成采用**两层安全机制**: 1. **可调节过滤器**:覆盖骚扰、仇恨言论、露骨色情、危险内容等四类,可通过 `safetySettings` 调整 2. **内置保护**:针对核心危害(如儿童安全)始终生效,**无法通过参数关闭** 明确禁止的内容包括:儿童性虐待与剥削(CSAE)、暴力极端主义/恐怖主义、未经同意的私密影像(NCII)、自残、露骨色情、仇恨言论、骚扰与霸凌。 **2026 年 2 月,Nano Banana 2 上线后谷歌显著收紧了人物与版权相关策略**,新增/强化了以下高频拒绝场景(数据截至 2026 年 5 月 (UTC+8)): * **公众人物 / 名人**:照片级、可识别的真实人物 * **换脸(faceswap)** * **真人换装 / 改脸** * **金融、订单信息篡改** * **知名 IP**(如迪士尼,2026 年 1 月 23 日起) * **去水印**、**未成年人**相关内容 仍然可以生成:虚构角色、风格化肖像、插画类人物。 谷歌官方政策原文(请自行复制访问): * 生成式 AI 禁止使用政策:`policies.google.com/terms/generative-ai/use-policy` * 生成式内容常见错误说明:`ai.google.dev/api/generate-content` ## 三个核心判断指标 按**优先级从高到低**依次检查: ### 1. candidatesTokenCount(最高优先级)⭐ * **位置**:`response.usageMetadata.candidatesTokenCount` * **含义**:API 生成的候选内容 token 数 * **规则**:等于 `0` 表示在**内容审核阶段就被直接拒绝**,连候选内容都没生成,这是最严格的拒绝 ```json theme={null} { "candidates": null, "usageMetadata": { "promptTokenCount": 271, "candidatesTokenCount": 0, "totalTokenCount": 271 } } ``` `candidatesTokenCount` 为 `0` 并不都是内容违规,还有两种常见情况需要区分: * **响应里有 `promptFeedback.blockReason`、没有 `candidates`**:输入在生成前被原厂检查拦截,`OTHER` 常由某一张参考图触发,见 [blockReason: OTHER 排查](/faq/gemini-image-input-blocked) * **`finishReason` 为 `NO_IMAGE`**:往往是提示词在要求模型写方案或分析、出图意图不明确,见 [NO\_IMAGE 排查](/faq/gemini-no-image) ### 2. finishReason(次优先级) * **位置**:`response.candidates[0].finishReason` * **规则**:不等于 `STOP` 即为非正常结束,需要特殊处理 最新的图片相关 `finishReason` 取值(注意 Nano Banana 系列新增了 `IMAGE_` 前缀的图片专用值): | finishReason | 含义 | 用户友好文案 | | - | - | - | | `STOP` | 正常结束 | - | | `IMAGE_SAFETY` | 输出侧图片安全过滤 | 内容触发了图片安全策略 | | `PROHIBITED_CONTENT` / `IMAGE_PROHIBITED_CONTENT` | 违禁内容 | 内容违反安全策略,已被拒绝 | | `SAFETY` | 安全过滤 | 内容触发了安全过滤器 | | `RECITATION` / `IMAGE_RECITATION` | 引用/版权限制 | 内容可能涉及版权问题 | | `IMAGE_OTHER` / `NO_IMAGE` | 未出图/其他 | 未能生成图片,请调整提示词后重试 | | `MAX_TOKENS` | 长度超限 | 内容长度超出限制 | ### 3. 文本拒绝说明(重要) * **位置**:`response.candidates[0].content.parts[].text` * **规则**:`finishReason` 为 `STOP`,但 `parts` 里只有 `text`、没有图片数据时,说明 API 返回的是**拒绝说明**而非图片。文案可能是中文或英文,例如: ```text theme={null} 我不能为你创建带有色情、不雅或冒犯性内容的图像。这违反了我们的安全政策。 I can't generate images that are sexually explicit. ``` ## 错误场景速查 | 场景 | 检测条件 | 典型原因 | | - | - | - | | 内容审核拒绝 | `candidatesTokenCount === 0` | 提示词/参考图含敏感内容,最早期拒绝 | | 输入被拦截 | 有 `promptFeedback.blockReason`、无 `candidates` | `OTHER` 多由某张参考图触发,见 [blockReason: OTHER 排查](/faq/gemini-image-input-blocked) | | 出图意图不明确 | `finishReason === 'NO_IMAGE'`,时有时无 | 提示词在要求文字产出,见 [NO\_IMAGE 排查](/faq/gemini-no-image) | | 生成过程拒绝 | `finishReason !== 'STOP'` 且 `parts` 为空 | 违禁内容、安全过滤 | | 文本拒绝说明 | `finishReason === 'STOP'`,有 text 无图片 | 露骨色情、违规请求 | | 知识库限制 | text 提到未来年份(2026+)或未发布产品 | 知识库更新至 2025 年 1 月 | | 禁止功能 | text 含 `watermark`、`faceswap`、换装、名人等关键词 | 去水印/换脸/换装/名人等被禁功能 | ## 处理流程(决策顺序) ```text theme={null} 收到 API 响应 ├─ ① candidatesTokenCount === 0 ─→ 内容审核拒绝 ├─ ② candidates 为空 ───────────→ API 格式错误(系统问题) ├─ ③ finishReason !== 'STOP' ───→ 生成过程拒绝(查映射表) ├─ ④ content.parts 为空 ────────→ 内容为空(同 finishReason 处理) ├─ ⑤ 遍历 parts 收集 text 与图片 ├─ ⑥ 有图片 ────────────────────→ ✅ 成功返回 └─ ⑦ 无图片但有 text ──────────→ 展示拒绝说明(可选关键词识别) └─ 无 text ──────────────→ 通用错误 + 保留完整响应 ``` ## 代码实现(核心) 将上面的判断顺序整合为一个解析函数: ```javascript theme={null} async function processGeminiResponse(data) { // ① 最高优先级:内容审核阶段直接拒绝 if (data.usageMetadata?.candidatesTokenCount === 0) { return { success: false, errorType: 'ZERO_CANDIDATES_TOKEN', userMessage: '您的请求在内容审核阶段被拒绝,请修改后重试', devMessage: 'candidatesTokenCount: 0 - 谷歌内容审核拒绝', rawResponse: data, }; } // ② candidates 为空 —— 通常是系统/格式问题 if (!data.candidates || !data.candidates.length) { return { success: false, errorType: 'NO_CANDIDATES', userMessage: '系统出错,请稍后重试', devMessage: 'candidates 为 null 或空数组', rawResponse: data, }; } const candidate = data.candidates[0]; // ③ finishReason 非 STOP —— 生成过程被拒 if (candidate.finishReason && candidate.finishReason !== 'STOP') { const reasonMessages = { PROHIBITED_CONTENT: '内容违反安全策略,已被拒绝处理', IMAGE_PROHIBITED_CONTENT: '内容违反安全策略,已被拒绝处理', SAFETY: '内容触发了安全过滤器', IMAGE_SAFETY: '内容触发了图片安全策略', RECITATION: '内容可能涉及版权问题', IMAGE_RECITATION: '内容可能涉及版权问题', NO_IMAGE: '未能生成图片,请调整提示词后重试', IMAGE_OTHER: '未能生成图片,请调整提示词后重试', MAX_TOKENS: '内容长度超出限制', }; return { success: false, errorType: 'FINISH_REASON', finishReason: candidate.finishReason, userMessage: reasonMessages[candidate.finishReason] || `请求被拒绝:${candidate.finishReason}`, devMessage: `finishReason: ${candidate.finishReason}`, rawResponse: data, }; } // ④ content.parts 为空 if (!candidate.content?.parts) { return { success: false, errorType: 'NO_PARTS', userMessage: '生成失败,请重试', devMessage: 'candidate.content.parts 为空', rawResponse: data, }; } // ⑤ 遍历 parts:⚠️ 务必先收集 text,再判断 thoughtSignature const images = []; const texts = []; for (const part of candidate.content.parts) { if (part.text && !part.text.startsWith('data:image/')) { texts.push(part.text); } if (part.inlineData?.data) { images.push(`data:${part.inlineData.mimeType};base64,${part.inlineData.data}`); } } // ⑥ 有图片即成功 if (images.length > 0) { return { success: true, images, texts }; } // ⑦ 无图片但有文本 —— 展示拒绝说明 if (texts.length > 0) { const textContent = texts.join('\n'); return { success: false, errorType: 'TEXT_RESPONSE', userMessage: textContent, // 直接使用 API 返回的文本 detectedType: detectContentType(textContent), apiText: textContent, rawResponse: data, }; } // ⑧ 兜底:永远不要只说「未知错误」 return { success: false, errorType: 'UNKNOWN', userMessage: '生成失败,请检查提示词后重试', devMessage: '未找到图片数据或文本响应', rawResponse: data, }; } ``` 关键词智能识别(可选,用于给出更具体的提示): ```javascript theme={null} function detectContentType(text) { const t = text.toLowerCase(); const isRejection = t.includes("i can't generate") || t.includes('i cannot create') || t.includes("i'm just a language model") || t.includes('我不能') || t.includes('无法生成'); if (!isRejection) return null; if (t.includes('watermark')) return 'watermark_removal'; if (t.includes('faceswap') || t.includes('face swap')) return 'faceswap'; if (t.includes('sexually') || t.includes('explicit') || t.includes('色情') || t.includes('不雅')) return 'nsfw'; return 'general_rejection'; } ``` **最易踩的坑**:带 `thoughtSignature` 的 part 仍可能包含重要的 `text`。一定要**先收集 text,再决定是否跳过**——否则拒绝说明会丢失,用户只能看到「生成失败」。 ## C 端友好提示文案 设计原则:**简洁明了、正面引导、可操作、避免指责**。推荐模板: ```text theme={null} ❌ 内容不符合要求 您的请求包含不适当内容,无法生成图片。 💡 建议:使用健康、正面的描述;避免敏感话题;重新调整提示词后再试。 ❌ 功能暂不支持 该功能(如去水印/换脸)暂不支持,请尝试其他编辑方式。 ❌ 内容超出范围 您提到的内容可能超出了 AI 的知识范围(更新至 2025 年 1 月)。 💡 建议:使用常见物品/概念,避免引用未来的产品。 ``` 展示分层建议: * **C 端用户**:默认只显示友好说明 + 修改建议 * **B 端 / 工具服务商**:默认展开技术详情(`finishReason`、`candidatesTokenCount` 等) * **开发者**:提供「展开/收起」查看完整 JSON 响应 ## 最佳实践 1. **严格按优先级检测**:`candidatesTokenCount` → `finishReason` → `parts` → 提取数据 → 关键词识别 2. **先收集 text 再判断 thoughtSignature**,避免拒绝说明丢失 3. **保留完整响应**:开发/测试工具务必保存原始 JSON,便于排查 4. **支持中英文拒绝文案**:谷歌可能返回中文或英文,关键词匹配两者都要覆盖 5. **友好降级**:能智能识别就给具体提示,否则直接展示 API 文本,再否则用 `finishReason` 友好名称,最后才是通用提示 6. **永不显示「未知错误」**:始终带上可操作建议或完整响应 ## 常见问题 FAQ 谷歌的安全过滤存在随机性和上下文相关性:参考图内容、提示词组合方式都会影响判断。建议调整描述方式、使用更委婉的表达。 `candidatesTokenCount: 0` 或 `finishReason: PROHIBITED_CONTENT` → 内容问题;`Failed to fetch` 或 HTTP 错误 → 技术问题;有 API 文本说明 → 通常是内容问题。 分层展示:默认显示友好说明 + 修改建议;可选展开技术详情;开发模式下显示完整 JSON 响应。 不需要。用映射表 + 通用兜底即可:`reasonMessages[finishReason] || ` + 显示原始值。 ## 相关阅读 * [Nano Banana 系列价格总览](/api-capabilities/nano-banana-pricing) * [Nano Banana Pro 出图失败包补计划](/api-capabilities/nano-banana-pro-guarantee) * [Nano Banana OSS 分组](/api-capabilities/nano-banana-oss-group) # 图片编辑 API 参考 Source: https://docs.apiyi.com/api-capabilities/gemini-nano-banana-2.1/image-edit api-reference/gemini-nano-banana-2.1-edit-openapi.yaml POST /v1beta/models/gemini-nano-banana-2.1:generateContent Nano Banana 2.1 图片编辑 API 参考与在线调试 — 输入图片 + 指令生成新图片 右侧的交互式 Playground 支持下拉选择参数。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),即可一键发送请求测试。 **场景说明**:本页用于「图片编辑」,必须上传一张待编辑的图片(base64 编码)+ 编辑指令。如果只想根据文本生成新图片,请使用 [文生图接口](/api-capabilities/gemini-nano-banana-2.1/text-to-image)。 **🖥️ 浏览器 Playground 限制(重要)** 本接口的响应里包含 base64 编码的图片(`inlineData.data`,数 MB 量级)。受浏览器渲染限制,右侧 Playground 在收到响应后**可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法把这么长的 base64 显示出来。 **推荐做法**(小白零踩坑): * **直接复制下方"代码示例"中的 Python / Node.js / cURL 到本地运行**,代码会自动 `base64.b64decode` 并把图片**保存为本地文件**。 * 如要在浏览器里试 Playground,**用极小的参考图(\< 50KB)** 并把 `imageSize` 设为最小档(`1K`)。 **⚠️ `parts` 数组结构(重要,多图编辑必看)** 每个 `part` **只能是 `text` 或 `inlineData` 中的一个**,二者不能同时出现在同一个 part 里。这与谷歌官方 `gemini-nano-banana-2.1` 的契约一致。 **正确**:一个 text part(编辑指令)+ N 个 inlineData part(每张图一个): ```json theme={null} "contents": [{ "parts": [ {"text": "把这两张图里的人物合成到同一个办公室场景中"}, {"inlineData": {"mimeType": "image/png", "data": ""}}, {"inlineData": {"mimeType": "image/png", "data": ""}} ] }] ``` **错误**(每个 part 同时塞了 text 和 inlineData,会导致非预期行为): ```json theme={null} "contents": [{ "parts": [ {"inlineData": {...}, "text": "这是提示词吗 1"}, {"inlineData": {...}, "text": "这是提示词吗 2"} ] }] ``` **🖼️ 关于 `inlineData.data` 字段** 本接口是 **JSON 格式**(非 multipart 文件上传),所以 Playground 无法直接选择本地文件,需要先把图片转成 **Base64 字符串**再粘贴到 `data` 输入框。 **一行命令转换 + 自动复制到剪贴板**: ```bash theme={null} # macOS base64 -i your-image.jpg | tr -d '\n' | pbcopy # Linux base64 -w0 your-image.jpg | xclip -selection clipboard # Windows PowerShell [Convert]::ToBase64String([IO.File]::ReadAllBytes("your-image.jpg")) | Set-Clipboard ``` 执行后直接在 Playground 的 `data` 字段 `Cmd+V` / `Ctrl+V` 粘贴即可。同时记得把 `mimeType` 切换为对应的 `image/jpeg` 或 `image/png`。 **建议**:测试用小图(\< 200KB),避免 base64 字符串过长导致浏览器卡顿。频繁测试图片编辑更推荐用下方代码示例直接在本地运行。 ## 代码示例 ### Python ```python theme={null} import requests import base64 API_KEY = "sk-your-api-key" # 读取待编辑的图片 with open("input.jpg", "rb") as f: image_b64 = base64.b64encode(f.read()).decode() response = requests.post( "https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent", headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json={ "contents": [{ "parts": [ {"text": "请把背景模糊化,突出前景的人物"}, {"inlineData": {"mimeType": "image/jpeg", "data": image_b64}} ] }], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }, timeout=300 ).json() img_data = [p for p in response["candidates"][0]["content"]["parts"] if "inlineData" in p][-1]["inlineData"]["data"] with open("edited.png", 'wb') as f: f.write(base64.b64decode(img_data)) print("编辑后的图片已保存至 edited.png") ``` ### Node.js ```javascript theme={null} import fs from "fs"; const API_KEY = "sk-your-api-key"; const imageB64 = fs.readFileSync("input.jpg").toString("base64"); const response = await fetch( "https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [ { text: "请把背景模糊化,突出前景的人物" }, { inlineData: { mimeType: "image/jpeg", data: imageB64 } } ] }], generationConfig: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "16:9", imageSize: "2K" } } }) } ); const data = await response.json(); const imgBase64 = data.candidates[0].content.parts.filter((p) => p.inlineData).at(-1).inlineData.data; fs.writeFileSync("edited.png", Buffer.from(imgBase64, "base64")); ``` ### cURL ```bash theme={null} # 注意:需要先将图片转为 base64 字符串 # IMAGE_B64=$(base64 -i input.jpg | tr -d '\n') curl -X POST "https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "contents": [{ "parts": [ {"text": "请把背景模糊化,突出前景的人物"}, {"inlineData": {"mimeType": "image/jpeg", "data": "'"$IMAGE_B64"'"}} ] }], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }' ``` ## 多图编辑示例 把多张图作为输入合成或对比时,**只用一个 `text` part**(编辑指令),后面追加多个 `inlineData` part(每张图一个)。 ### Python(多图) ```python theme={null} import requests import base64 API_KEY = "sk-your-api-key" def to_b64(path): with open(path, "rb") as f: return base64.b64encode(f.read()).decode() # 准备多张图(这里以 2 张为例,最多可上传多张) images = ["person1.png", "person2.png"] parts = [{"text": "把这两张图里的人物合成到同一个办公室场景中,做着搞怪表情"}] for path in images: parts.append({"inlineData": {"mimeType": "image/png", "data": to_b64(path)}}) response = requests.post( "https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent", headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json={ "contents": [{"parts": parts}], "generationConfig": { "responseModalities": ["TEXT", "IMAGE"], "imageConfig": {"aspectRatio": "5:4", "imageSize": "2K"} } }, timeout=300 ).json() img_data = [p for p in response["candidates"][0]["content"]["parts"] if "inlineData" in p][-1]["inlineData"]["data"] with open("merged.png", "wb") as f: f.write(base64.b64decode(img_data)) ``` ### cURL(多图,对齐谷歌官方格式) ```bash theme={null} curl -X POST "https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "contents": [{ "parts": [ {"text": "An office group photo of these people, they are making funny faces."}, {"inlineData": {"mimeType": "image/png", "data": ""}}, {"inlineData": {"mimeType": "image/png", "data": ""}}, {"inlineData": {"mimeType": "image/png", "data": ""}} ] }], "generationConfig": { "responseModalities": ["TEXT", "IMAGE"], "imageConfig": {"aspectRatio": "5:4", "imageSize": "2K"} } }' ``` ## 参数说明速查 | 参数 | 类型 | 必填 | 说明 | | - | - | - | - | | `contents[].parts` | array | 是 | 由「**1 个 text part + N 个 inlineData part**」组成。每个 part 只能含 `text` 或 `inlineData` 之一,不可同时出现 | | `contents[].parts[].text` | string | 是 | 编辑指令(建议只放在第一个 part 中) | | `contents[].parts[].inlineData.mimeType` | string | 是 | `image/jpeg` 或 `image/png` | | `contents[].parts[].inlineData.data` | string | 是 | 图片的 Base64 编码(多图编辑时重复多个 inlineData part) | | `generationConfig.responseModalities` | array | 是 | 通常为 `["IMAGE"]` | | `generationConfig.imageConfig.aspectRatio` | string | 否 | 14 种宽高比;不传时由模型按内容自选,建议显式传 | | `generationConfig.imageConfig.imageSize` | string | 否 | `1K` / `2K` / `4K`(不支持 `512`),默认 `1K` | | `generationConfig.thinkingConfig.thinkingLevel` | string | 否 | `minimal` / `medium` / `high`,默认 `medium` | | `generationConfig.thinkingConfig.includeThoughts` | boolean | 否 | 是否返回思维过程文本 | ## 多轮对话式编辑 Nano Banana 2.1(`gemini-nano-banana-2.1`)支持**真正的对话式多轮编辑**:把模型每一轮产出的图片,作为 **`role: "model"` 的 `inlineData`** 追加回 `contents`,再发下一条 user 指令。模型会基于**完整对话历史**继续修改并**累积效果**(例如先改沙发颜色、再加配饰,前一步的改动会保留)。 这与"逆向"图像模型不同:原生 Gemini 格式会真正读取对话历史里 `model` 角色的图片。要保持跨轮一致性、逐步精调,推荐用下面这种**对话历史回填**的写法。 ```python theme={null} import requests, base64 API_KEY = "sk-your-api-key" URL = "https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent" H = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"} CFG = {"responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "1:1", "imageSize": "2K"}} contents = [] # 全程维护同一个对话历史 def turn(instruction, save_to): contents.append({"role": "user", "parts": [{"text": instruction}]}) data = requests.post(URL, headers=H, json={"contents": contents, "generationConfig": CFG}, timeout=300).json() part = next(p for p in data["candidates"][0]["content"]["parts"] if "inlineData" in p) contents.append({"role": "model", "parts": [part]}) # 关键:把产出的图回填进历史 with open(save_to, "wb") as f: f.write(base64.b64decode(part["inlineData"]["data"])) return part turn("生成一只橙色的猫,坐在蓝色沙发上,简笔画风格", "step1.png") turn("把沙发改成红色,猫和构图保持不变", "step2.png") # 基于上一轮的图修改 turn("给猫戴一顶黄色小帽子,其它保持不变", "step3.png") # 继续累积,红沙发会保留 ``` **从已有图片开始多轮**:第一轮的 user 消息里放 `inlineData`(你自己的图)+ 指令即可编辑现有照片,之后每轮照样把模型产出回填进 `contents`。 **两种多轮写法**: * **对话历史回填(上面,推荐)**:`contents` 保留 user/model 交替历史,跨轮累积修改、画面更稳。 * **重新喂图(简化版)**:每轮只发单条 user 消息(`text` + 上一张图的 `inlineData`),做单步编辑也可以,但不携带更早的上下文。 # Nano Banana 2.1 生图/编辑 Source: https://docs.apiyi.com/api-capabilities/gemini-nano-banana-2.1/overview 谷歌 Nano Banana 2.1(gemini-nano-banana-2.1)正式版:Nano Banana 2 的升级版,画质、文字渲染、多轮一致性提升。按次 $0.045/次(4K 同价),按量输入 $0.66、输出 $13.2 每百万 tokens。 ## 概述 **Nano Banana 2.1** 是谷歌于 2026 年 10 月 6 日发布的图像生成模型,模型 ID 为 **`gemini-nano-banana-2.1`**,发布即为正式版(GA)。它是 [Nano Banana 2](/api-capabilities/nano-banana-2-image/overview)(`gemini-3.1-flash-image`)的升级版,保持 Flash 级速度,同时提升了画质、图中文字渲染和多轮编辑的一致性,并修复了上一代的平铺伪影。谷歌官方推荐新项目直接使用 2.1。 **🆕 2026 年 10 月 6 日发布**:API易 已同步上线,支持**按次**(\$0.045/次,1K / 2K / 4K 同价,2026-10-13 起由 \$0.05 降价)和**按量**(输入 \$0.66、输出 \$13.2 每百万 tokens)两种计费。Nano Banana 2 继续可用、价格不变,谷歌也尚未公布它的下线日期。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 输入文本提示词生成图片,带交互式 Playground 在线调试。 上传图片 + 编辑指令生成新图片,带交互式 Playground 在线调试。 ## 让 AI Agent 帮你接入 帮我在当前项目里接入 / 排查 Nano Banana 2.1(`gemini-nano-banana-2.1`)的「文生图 + 图片编辑」。 先读文档再动手:抓 [https://docs.apiyi.com/api-capabilities/gemini-nano-banana-2.1/overview.md](https://docs.apiyi.com/api-capabilities/gemini-nano-banana-2.1/overview.md) 拿到本页纯文本版;需要更细的参数说明时,text-to-image 和 image-edit 两页同样在地址后加 `.md` 即可。 接入要求: 1. 超时:走 Gemini 原生格式 `POST https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent`,客户端 timeout 提到 360 秒兜底。图片接口是同步调用,没有任务 ID,客户端一断连结果就丢了、但这次请求照样计费。反向代理、网关、Serverless 执行上限这些中间层也要一起放宽,任何一层小于生成时间都会掐断请求。如果你用 Node,注意 undici 有三个独立的超时设置,SDK 的 `timeout` 并不覆盖它们。 2. 返回解析(**最容易写错的一条**):图片是 base64,在 `candidates[0].content.parts[]` 里的 `inlineData.data`。但 `parts` 是**异构数组,段数和顺序都不保证**——前面可能挂一个文本段,图片就落到下标 1 而不是 0。所以**绝对不要写死 `parts[0]` 或 `parts[1]`**。正确写法:遍历 `parts`、筛出所有含 `inlineData` 的段,取**最后一张**(复杂任务会返回多张中间稿,最后一张才是终稿)。`mimeType` 也从响应里读,不要写死成 `image/png`。拿到后渲染展示并提供「保存到本地」。 3. 上传压缩:编辑时把参考图 base64 塞进 `inlineData`。上传前先压缩——超过 1.5MB 才处理,长边等比缩到 2048px 以内(不放大小图),以质量 0.9 重编码、保持原格式;多图时合计控制在 6MB 以内。base64 编码后体积还会再膨胀约三分之一,所以单图尽量压到 5MB 以内留足余量。某张图压缩失败就回退用原图继续,不要因为压缩失败中断整个请求。另外注意:**同一个 part 里只能放 `text` 或 `inlineData` 其中一个**,不能两个字段并存,正确结构是 1 个文本段 + N 个图片段。 4. 分辨率参数:**必须显式传** `generationConfig.imageConfig.imageSize`(`1K` / `2K` / `4K`,默认 `1K`;**本模型不支持 `512`,传了会直接报 400**)和 `aspectRatio`(本页列了 14 个合法比例)。不传 `aspectRatio` 时模型会按内容自己选比例,结果不可控。按量计费时**分辨率直接决定单价**。前台界面把分辨率和比例都做成下拉。 5. 错误处理:内容审核拦截时 HTTP 仍然是 200,但 `candidates[0].content.parts` 为空。判断顺序是先看 `candidatesTokenCount` 是否为 0,再看 `finishReason` 是否非 `STOP`。`IMAGE_SAFETY` 这类拦截**不计费**,原样重试 1-2 次往往就成功了,建议在代码里对它做自动重试。 6. Key 从环境变量 `APIYI_API_KEY` 读,用 `Authorization` 头加 `Bearer` 前缀传,不要硬编码进代码、也不要提交进 git。 7. 改完真跑一次文生图 + 一次图片编辑,把出图结果和这两次调用的花费贴给我。 | 要求 | 挡掉的坑 | | - | - | | 不写死 `parts` 下标 | `parts` 段数和顺序都不保证,写死下标一定会间歇性失败。详见 [Nano Banana 开发指南](/api-capabilities/nano-banana-dev-guide) | | 取最后一张图片段 | 复杂编辑任务会返回多张中间稿,最后一张才是终稿 | | 不传 `512` | 2.1 去掉了 512 档,沿用 Nano Banana 2 的代码会直接 400 | | 显式传 `imageSize` 和 `aspectRatio` | 按量计费下分辨率决定单价;不传比例时模型自己选,同一提示词可能出横图也可能出竖图 | | 上传前压缩 | base64 后体积还会再膨胀约三分之一。压缩标准见 [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution) | | 对 `IMAGE_SAFETY` 自动重试 | 审核拦截返回 200 但没有图,且不计费,原样重试往往就过了。详见 [Gemini 出图错误处理](/api-capabilities/gemini-image-error-handling) | ## 核心特性 视觉质量较 Nano Banana 2 提升,并修复了上一代的平铺伪影 图中文字更清晰、错字更少,适合海报、营销物料、信息图 对话式多轮编辑时角色和画面一致性更好,逐步精调不易跑偏 支持 minimal / medium / high 三档,默认 medium,比上一代多了中间档 1K / 2K / 4K 三档分辨率,按次计费 4K 与 1K 同价 含 1:4、4:1、1:8、8:1 超长/超宽比例,官方称宽幅格式在高分辨率下效果更好 最多 10 张物体参考图 + 4 张角色参考图 + 3 张风格参考图 可挂 `googleSearch` 工具,画天气卡、行情图等需要实时信息的图片 ## 与 Nano Banana 2 的区别 | 对比项 | **Nano Banana 2.1** | Nano Banana 2 | | - | - | - | | 模型 ID | `gemini-nano-banana-2.1` | `gemini-3.1-flash-image` | | 状态 | 正式版,谷歌推荐新项目使用 | 正式版,继续可用 | | 画质 / 文字渲染 / 多轮一致性 | 更好 | 好 | | 分辨率 | 1K / 2K / 4K | 512 / 1K / 2K / 4K | | 4K 单张输出 tokens | 3780 | 2520 | | 思考档位 | minimal / medium / high,默认 medium | minimal / high,默认 minimal | | 官方价:输入 | \$1.50 / M | \$0.50 / M | | 官方价:图片输出 | \$30 / M | \$60 / M | | 官方价:4K 每张 | \$0.113 | \$0.151 | | **API易 按次** | **\$0.045 / 次** | \$0.055 / 次 | | **API易 按量** | 输入 \$0.66 / 输出 \$13.2 每 M | 输入 \$0.18 / 输出 \$21.6 每 M | **怎么选**: * **新项目** → 直接用 Nano Banana 2.1,画质更好,按次也更便宜 * **已在用 Nano Banana 2** → 改一个模型名即可切换;但如果代码里用了 `512` 档,要先改成 `1K` * **需要 512px 缩略图** → 继续用 Nano Banana 2,或用 [Nano Banana 2 Lite](/api-capabilities/nano-banana-lite-image/overview) * **追求画质上限** → [Nano Banana Pro](/api-capabilities/nano-banana-image/overview)(\$0.09/次) ## 模型定价 **计费模式选择**:Nano Banana 2.1 支持两种计费方式,通过创建令牌时的「Billing model」设置选择: * 选择 **Pay-as-you-go**(按量计费)或 **Pay-as-you-go Priority**(按量优先)→ 按量计费 * 选择 **Pay-per-request**(按次计费)或 **Pay-per-request Priority**(按次优先)→ 按次计费 * ⚠️ **请勿选择 Hybrid billing(混合计费)** ### 按次计费 | 模型 | API易定价 | 谷歌官方 4K 定价 | 相对官方 | | - | - | - | - | | **Nano Banana 2.1** `gemini-nano-banana-2.1` | **\$0.045/次**(1K / 2K / 4K 同价) | \$0.113/张 | **约 40%** | **2026-10-13 起降价 10%:\$0.05 → \$0.045/次**(1K / 2K / 4K 同价)。 * **为什么降**:按量计费下 1K / 2K 单张本来就只要 \$0.02–\$0.04,按次 \$0.05 显得偏贵,所以把按次价也降下来。出 4K 的话,按次比按量(\$0.06 以上)便宜近三成。 * **按量价不变**:输入 \$0.66、输出 \$13.2 每百万 tokens。 * **还能更低**:叠加充值活动加赠 10%–20%,按次实际约 \$0.038–\$0.041/次。 ### 按量计费 | 计费项目 | Google 官方 | API易 | 相对官方 | | - | - | - | - | | 输入 | \$1.50/M tokens | \$0.66/M tokens | **44%** | | 输出(图片、文本、思考统一计价) | 图片 \$30/M,文本与思考 \$7.50/M | \$13.2/M tokens | 图片部分 **44%** | 模型价格与官网对齐,且可能随官网调整;上表仅供参考,具体以顶部导航「模型价格」栏目为准:[模型价格](/models/index)。 ### 按量计费:线上真实扣费预览 Nano Banana 2.1 **默认就会先思考再出图**(默认档 medium),每张图除了图片 tokens,还会产生约 400–1300 个思考和其它输出 tokens,它们与图片一起按 \$13.2/M 计费。所以按量计费的单张费用**不是定值,有高有低**。下表摘自 2026-10-07 线上按量计费的真实请求,金额为控制台实际扣费(分辨率按输出 tokens 推断): | 场景 | 输入 tokens | 输出 tokens | 实际扣费 | | - | - | - | - | | 1K 文生图 | 36 | 1,970 | \$0.0260 | | 1K 单图编辑 | 1,137 | 2,015 | \$0.0273 | | 1K 单图编辑(思考较多) | 1,132 | 2,409 | \$0.0325 | | 2K 文生图 | 13 | 2,623 | \$0.0346 | | 2K 单图编辑 | 1,188 | 2,957 | \$0.0398 | | 2K 14 张参考图融合 | 15,735 | 2,827 | \$0.0581 | | 4K 文生图 | 13 | 4,703 | \$0.0621 | | 4K 复杂提示词 | 72 | 4,933 | \$0.0652 | 当天 60 次按量请求的分布: | 分辨率 | 请求数 | 最低 | 中位 | 最高 | 对比按次 | | - | - | - | - | - | - | | 1K | 31 | \$0.021 | \$0.029 | \$0.033 | \$0.045 | | 2K | 19 | \$0.033 | \$0.036 | \$0.058 | \$0.045 | | 4K | 10 | \$0.061 | \$0.063 | \$0.067 | \$0.045 | **怎么选计费方式**: * **出 1K / 2K → 选按量**,单张通常 \$0.02–\$0.04,比按次 \$0.045 更低 * **出 4K → 选按次**,固定 \$0.045/次,按量要 \$0.06 以上 * **一次塞很多参考图**(如 10 张以上)时,输入 tokens 会让按量费用接近甚至超过 \$0.045,这类请求也建议走按次 结合 [充值加赠活动](/faq/recharge-promotions),实际成本更低。 **为什么不能只按图片 tokens 估成本**:`usageMetadata` 里的 `thoughtsTokenCount`(思考 tokens)不包含在 `candidatesTokenCount` 里,但会计入 API易 日志的输出 tokens 一起计费。按 1120 / 1680 / 3780 估算会低估 20%–40%。对账请以控制台日志的扣费为准。 ## 影响计费的参数 | 参数 | 对单次账单的影响 | 说明 | | - | - | - | | `imageConfig.imageSize` | 1K 约 \$0.026 → 4K 约 \$0.062(按量) | 最大的杠杆;按次计费不受影响 | | `thinkingConfig.thinkingLevel` | `high` 比默认多约 30% 思考 tokens,4K 单张约 +6% | 影响很小,按需开 | | `tools: [{"googleSearch": {}}]` | 每次检索另收 \$0.014,实测每次出图检索 2 次 | 开了之后单次成本会明显上升 | ### thinkingLevel:默认 medium,high 只贵一点 | 设置 | 思考 tokens(中位) | 4K 单张费用(按量) | | - | - | - | | 不传(默认 medium) | 约 690 | 约 \$0.062 | | `high` | 约 940 | 约 \$0.066 | 与 Nano Banana 2 不同(它默认 minimal、开 `high` 要贵 54%),2.1 默认就已经在思考,`high` 只是多想一点。对画面内文字排版、数据图表比例有硬要求时可以放心开。 ### Google 搜索接地:按检索次数另收费 需要实时信息才能画对的场景(天气卡、行情图、近期活动海报),可以挂 `googleSearch` 工具: ```json theme={null} { "contents": [{ "parts": [{ "text": "画一张东京今天天气的卡片海报" }] }], "tools": [{ "googleSearch": {} }] } ``` 实测 3/3 触发接地,每次请求模型自主发起 2 次检索,每次检索收 \$0.014(即 \$14 / 1000 次)。**检索次数由模型决定,无法预先控制**,做成本预估时按每次请求 2–3 次检索算。 **按次计费下检索费照样另收**:\$0.045/次只覆盖出图本身,挂了 `googleSearch` 后检索费按次数叠加在上面。 ## 分组介绍 Nano Banana 2.1 在 API易 提供两个分组,可在后台「令牌设置」中切换: | 分组 | 倍率 | 适用场景 | | - | - | - | | `Default` 默认分组 | 1.0x | 基础通道,与定价表一致;默认推荐 | | `NB-Enterprise` 企业分组 | 1.4x | 兜底通道,默认分组紧张或超时高发时切换,稳定优先 | **令牌「计费模式」推荐**:选 `按量优先`(Pay-as-you-go Priority)——同时兼容 Nano Banana 2 / 2.1 的按量计费和 Nano Banana Pro 的按次计费,**一把令牌跑全系列**。主分组放 `Default`、兜底分组挂上 `NB-Enterprise`,主分组 429 时会自动回退继续出图。 ## 支持的分辨率与宽高比 ### 输出分辨率 | 分辨率 | 说明 | 推荐场景 | | - | - | - | | 1K | 默认 | 社交媒体、网页展示 | | 2K | 高清 | 高清显示、打印材料 | | 4K | 超高清 | 专业设计、商业海报 | **不支持 `512`**:传 `"imageSize": "512"` 会直接返回 400 `Image size 512 is not supported for this model`(不计费)。从 Nano Banana 2 迁移时注意改掉。 ### 支持的宽高比(14 种) `1:1`、`1:4`、`4:1`、`1:8`、`8:1`、`2:3`、`3:2`、`3:4`、`4:3`、`4:5`、`5:4`、`9:16`、`16:9`、`21:9` **不传 `aspectRatio` 时,模型会按内容自己选比例**:实测风景类提示词多出 16:9,海报类多出 2:3 或 3:4。需要固定比例请显式传。 ### 实测输出尺寸(像素) | 宽高比 | 1K | 2K | 4K | | - | - | - | - | | **16:9** | 1376×768 | 2752×1536 | 5504×3072 | | **2:3** | 848×1264 | — | 3392×5056 | | **3:4** | 896×1200 | — | — | | **8:1** | 2928×352 | — | — | 以上为 2026-10-07 实测值,「—」为尚未实测。注意 8:1 在 1K 下是 2928×352,与 Nano Banana 2 的 3072×384 不同,**不要照搬 Nano Banana 2 的尺寸表**做前端预留。 ## 常见问题 不是。2.1 是独立的新模型 ID `gemini-nano-banana-2.1`,画质、文字渲染和多轮一致性更好,计费结构也不同(输入更贵、图片输出更便宜、4K 单张 tokens 更多)。两个模型在 API易 同时可用,互不影响。 1. 模型名从 `gemini-3.1-flash-image`(或 `-preview`)改为 `gemini-nano-banana-2.1` 2. 如果用了 `"imageSize": "512"`,改成 `1K` 3. 如果你按 tokens 估算成本,重新按上方「每张实际多少钱」的表核一遍 请求格式、返回结构、多图编辑和多轮编辑的写法都不用改。 截至 2026 年 10 月 7 日,谷歌没有公布 `gemini-3.1-flash-image` 的下线日期,只是把 2.1 列为推荐替代。API易 上的 Nano Banana 2 继续可用、价格不变。如有变化我们会提前通知。 因为 2.1 默认就会思考,每张图多出约 400–1300 个思考和其它输出 tokens,与图片一起按输出价计费。这部分在返回的 `thoughtsTokenCount` 里能看到。如果你出 4K 为主,改用按次计费(\$0.045/次)更划算。 **API 不限制并发**,可以放心自行并发调用,请求之间不排队、不互相阻塞。真正要注意的是 `timeout`:4K 或高峰时单次耗时可能较长(实测 4K 多在 30–50 秒,个别超过 2 分钟),**建议客户端超时设到 360 秒**。偶发 429 时,把令牌兜底分组挂上 `NB-Enterprise` 即可。 所有输出图片都带有 SynthID 隐形数字水印(Google 的 AI 生成内容标识技术),肉眼不可见,不影响使用。 ## 相关文档 * [Nano Banana 2 生图/编辑](/api-capabilities/nano-banana-2-image/overview) - 上一代,支持 512px * [Nano Banana Pro 生图/编辑](/api-capabilities/nano-banana-image/overview) - 画质上限 * [Nano Banana 系列开发指南](/api-capabilities/nano-banana-dev-guide) - 尺寸控制、输入图片要求、URL 传图 * [Nano Banana 系列价格](/api-capabilities/nano-banana-pricing) * [Gemini 出图错误处理](/api-capabilities/gemini-image-error-handling) # Nano Banana 2.1 Agent 技能 Source: https://docs.apiyi.com/api-capabilities/gemini-nano-banana-2.1/skills 把 Nano Banana 2.1(gemini-nano-banana-2.1)封装成开箱即用的 Agent Skill,丢进 Codex、OpenClaw、hermes-agent、Claude Code 等任意编码 Agent,一句话即可调用 API易 平台完成文生图与图片编辑。 本页提供一个**开箱即用的 Agent 技能(Skill)**:把它放进你正在用的编码 Agent,就能用自然语言(或显式命令)直接调用 API易 平台的 **Nano Banana 2.1**(`gemini-nano-banana-2.1`)完成生图与改图。整套东西就两个文件,复制即用。 如果你想要的是 Nano Banana **Pro**(`gemini-3-pro-image`,极致画质)的技能,见 [Nano Banana Pro Agent 技能](/api-capabilities/nano-banana-image/skills)。本页是 **Nano Banana 2.1**(Nano Banana 2 的升级版,画质与文字渲染更好)。还在用上一代的话,见 [Nano Banana 2 Agent 技能](/api-capabilities/nano-banana-2-image/skills)。 ## 这个技能能做什么 一个合并技能,脚本会根据**是否传入图片**自动判断是「文生图」还是「图片编辑」: 只给提示词 → 生成全新图片,支持 **14 种宽高比**与 1K/2K/4K 分辨率。 传入一张图 + 指令 → 局部编辑、风格迁移、背景替换等。 传入多张图 + 一条指令 → 多图合成、对比、换装等高级玩法。 相比 Pro,Nano Banana 2.1 多了 `1:4 / 4:1 / 1:8 / 8:1` 四种**超长/超宽比例**,按次仅 \$0.045/张(1K / 2K / 4K 同价),更适合走量。注意它**不支持 512px**,需要缩略图档请用 Nano Banana 2。 ## 能用在哪些 Agent 一个 Skill 本质上就是**一个文件夹**:一份写给 Agent 看的说明(`SKILL.md`)+ 一个干活的脚本。所以**凡是能读取本地文件、执行命令行的编码 Agent 都能用上**——比如 **Codex、OpenClaw、hermes-agent、Claude Code** 等。 唯一要求:跑 Agent 的那台机器(你的电脑或服务器)装了 **Python 3** 并且**能联网**(脚本要直连 `api.apiyi.com`)。仅此而已,不挑具体哪家 Agent。 ## 三步装好 ### ① 建目录、贴文件 新建一个技能文件夹,放入下面两个文件(完整内容见后两节): ``` nano-banana-2-1/ ├── SKILL.md ├── scripts/ │ └── nano_banana_2_1.py └── .env # 第②步创建,放你的 Key ``` ### ② 同目录写 Key 在 `nano-banana-2-1/.env` 里写上你的 **API易 API Key**(在 `api.apiyi.com` 控制台创建): ```bash theme={null} APIYI_API_KEY=sk-your-api-key ``` 脚本会自动从这个 `.env` 读取 Key,**无需任何额外配置或环境变量**。 `.env` 里是你的密钥。如果这个技能要随项目仓库共享,**务必把 `.env` 加进 `.gitignore`,不要提交到 git**。 ### ③ 交给 Agent * **支持技能自动发现的 Agent**(如 Claude Code):把整个 `nano-banana-2-1/` 目录放进它的技能目录——个人级 `~/.claude/skills/`,或项目级 `.claude/skills/`(随仓库共享)。 * **其他 Agent**:按它各自的技能/插件约定放置;或者最简单——**直接让 Agent「读一下这个文件夹里的 SKILL.md,并照着执行」** 即可。 装好后就能用了,跳到 [怎么用](#怎么用) 看示例。 ## SKILL.md 新建 `nano-banana-2-1/SKILL.md`,完整内容如下(`description` 写清「做什么 + 何时用」,Agent 会据此自动触发): ````markdown theme={null} --- name: nano-banana-2-1 description: Generate or edit images via APIYI's Nano Banana 2.1 (gemini-nano-banana-2.1) model. Use this when the user asks to create, draw, render, or generate an image/illustration/poster, or to edit, retouch, restyle, or composite existing images. allowed-tools: Bash(python3 *) --- # Nano Banana 2.1 出图技能 通过 API易 平台调用 Nano Banana 2.1(`gemini-nano-banana-2.1`)生成或编辑图片。Nano Banana 2 的升级版,画质、文字渲染与多轮一致性更好;不支持 512 分辨率。 ## Key 配置 脚本会自动读取技能目录下 `.env` 文件里的 `APIYI_API_KEY`(也支持同名环境变量)。 若脚本报「未找到 Key」,提示用户在 `.env` 里写一行 `APIYI_API_KEY=sk-xxx`。 ## 用法 调用同目录下的脚本,第一个参数是提示词;编辑图片时用 `-i` 传入一张或多张本地图片路径: ```bash # 文生图(默认出 1 张) python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana_2_1.py "一只戴着宇航头盔的柴犬,电影感打光" -o dog.png --size 2K --aspect 16:9 # 超宽横幅(Nano Banana 2.1 独有的 8:1 / 4:1 等超宽比例) python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana_2_1.py "国风山水长卷横幅" -o banner.png --aspect 8:1 --size 2K # 图片编辑(传 1 张) python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana_2_1.py "把背景换成赛博朋克城市夜景" -i input.jpg -o edited.png # 多图合成(传多张,重复 -i) python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana_2_1.py "把这两个人合到同一张办公室合影里" -i a.png -i b.png -o merged.png # 一次出多张同主题变体(最多 5 张,并发生成) python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana_2_1.py "国风山水插画" -o shanshui.png -n 3 --aspect 16:9 ``` 参数说明: - 第 1 个位置参数:提示词(必填)。 - `-i / --image`:输入图片路径,可重复多次;不传 = 文生图,传 = 图片编辑。 - `-o / --out`:输出文件名,默认 `output.png`。 - `-n / --count`:一次出几张,**默认 1**,最多 5(脚本内并发生成,超过会自动截到 5)。`-n>1` 时文件名自动加 `-1` `-2`… 后缀。 - `--aspect`:宽高比,14 选 1(`1:1` `1:4` `4:1` `1:8` `8:1` `2:3` `3:2` `3:4` `4:3` `4:5` `5:4` `9:16` `16:9` `21:9`),默认 `1:1`。 - `--size`:分辨率 `1K` / `2K` / `4K`,默认 `2K`。 ## 出图张数(重要) - **默认只出 1 张**:用户没有明确要求多张时,`-n` 保持默认(即不传),只生成 1 张。 - **指定多张才出多张**:用户说「出 3 张 / 来几张 / 多给几个版本」时才用 `-n`,且**一次不超过 5 张**。需要更多时分多次调用,不要试图绕过上限。 - 多张是同一提示词的并发变体(模型有随机性,结果各不相同)。 ## 输出位置(重要) - `-o` 传**纯文件名**(如 `dog.png`)时,图片统一保存到**项目根目录下的 `nano-banana-2-1-output/` 文件夹**,方便用户在项目里直接找到。 - `-o` 传**带目录的路径**(相对或绝对,如 `images/dog.png` 或 `/abs/path/dog.png`)时,按给定路径保存(相对路径相对当前工作目录)。 - 不要把图片写到 `/tmp`、scratchpad 等临时目录,用户会找不到。 ## 完成后 脚本会为每张图打印一行完整路径,把它们如实回报给用户。若脚本报告被内容安全策略拒绝,把拒绝原因如实转达,不要重试同一提示词。 ```` `name` 必须是小写字母 + 连字符,且**不能含 `claude` / `anthropic`** 等保留词。在支持斜杠命令的 Agent 里,目录名就是命令名——叫 `nano-banana-2-1` 即 `/nano-banana-2`。`${CLAUDE_SKILL_DIR}` 是 Claude Code 提供的技能目录变量;其他 Agent 直接用脚本的实际路径即可。 ## scripts/nano\_banana\_2\_1.py 新建 `nano-banana-2-1/scripts/nano_banana_2_1.py`,使用 Gemini 原生格式(与本站文生图 / 图片编辑接口代码一致、已验证可跑)。**纯 Python 标准库,无需 `pip install` 任何东西**: ```python theme={null} #!/usr/bin/env python3 """通过 API易 调用 Nano Banana 2.1(gemini-nano-banana-2.1)生成 / 编辑图片。纯标准库,零依赖。""" import argparse import base64 import json import os import sys import urllib.error import urllib.request from concurrent.futures import ThreadPoolExecutor # 一次调用最多并发出几张图(边界,避免一次性打太多请求) MAX_COUNT = 5 def load_api_key(): """优先读环境变量;否则在脚本所在目录及其父目录找 .env。""" key = os.environ.get("APIYI_API_KEY") if key: return key here = os.path.dirname(os.path.abspath(__file__)) for d in (here, os.path.dirname(here)): env_path = os.path.join(d, ".env") if os.path.exists(env_path): with open(env_path, encoding="utf-8") as f: for line in f: line = line.strip() if line.startswith("APIYI_API_KEY") and "=" in line: return line.split("=", 1)[1].strip().strip('"').strip("'") return None def project_root(): """从脚本位置向上找包含 .git 或 .claude 的目录,作为项目根目录;找不到则用当前工作目录。""" d = os.path.dirname(os.path.abspath(__file__)) while True: if os.path.isdir(os.path.join(d, ".git")) or os.path.isdir(os.path.join(d, ".claude")): return d parent = os.path.dirname(d) if parent == d: return os.getcwd() d = parent def to_b64(path): with open(path, "rb") as f: return base64.b64encode(f.read()).decode() def mime_of(path): return "image/png" if path.lower().endswith(".png") else "image/jpeg" def generate(api_key, endpoint, prompt, images, aspect, size): """发一次请求,返回图片字节;失败抛 RuntimeError。""" parts = [{"text": prompt}] for path in images: parts.append({"inlineData": {"mimeType": mime_of(path), "data": to_b64(path)}}) payload = json.dumps({ "contents": [{"parts": parts}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": aspect, "imageSize": size}, }, }).encode() req = urllib.request.Request( endpoint, data=payload, method="POST", headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}, ) try: with urllib.request.urlopen(req, timeout=360) as r: resp = json.loads(r.read()) except urllib.error.HTTPError as e: raise RuntimeError(f"请求失败 HTTP {e.code}:{e.read().decode(errors='replace')}") candidates = resp.get("candidates") if not candidates: raise RuntimeError(f"未返回候选内容(可能被内容安全策略拒绝):{resp}") cand = candidates[0] # 内容审核拦截:finishReason 非 STOP,或只返回了文字说明 if cand.get("finishReason") not in (None, "STOP"): text = next((p.get("text") for p in cand["content"]["parts"] if p.get("text")), "") raise RuntimeError(f"请求被拒绝(finishReason={cand.get('finishReason')}):{text}") image_part = next((p for p in cand["content"]["parts"] if p.get("inlineData")), None) if not image_part: text = next((p.get("text") for p in cand["content"]["parts"] if p.get("text")), "") raise RuntimeError(f"未返回图片,模型回复:{text}") return base64.b64decode(image_part["inlineData"]["data"]) def resolve_paths(out, count): """决定输出路径列表。 - 若 out 带目录成分(相对/绝对),按用户给定的路径处理(相对则相对当前工作目录)。 - 若 out 是纯文件名,统一存到 <项目根>/nano-banana-2-1-output/ 下,确保好找。 count>1 时给文件名加 -1 / -2 … 后缀。 """ if os.path.dirname(out): base_path = os.path.abspath(out) else: out_dir = os.path.join(project_root(), "nano-banana-2-1-output") os.makedirs(out_dir, exist_ok=True) base_path = os.path.join(out_dir, out) if count == 1: return [base_path] base, ext = os.path.splitext(base_path) return [f"{base}-{i}{ext}" for i in range(1, count + 1)] def main(): api_key = load_api_key() if not api_key: sys.exit("未找到 API Key:请在技能目录的 .env 写一行 APIYI_API_KEY=sk-xxx") model = os.environ.get("APIYI_IMAGE_MODEL", "gemini-nano-banana-2.1") endpoint = f"https://api.apiyi.com/v1beta/models/{model}:generateContent" parser = argparse.ArgumentParser(description="Nano Banana 2.1 出图") parser.add_argument("prompt", help="提示词 / 编辑指令") parser.add_argument("-i", "--image", action="append", default=[], help="输入图片路径(可重复,传入即为编辑模式)") parser.add_argument("-o", "--out", default="output.png", help="输出文件名") parser.add_argument("-n", "--count", type=int, default=1, help=f"一次出几张,默认 1,最多 {MAX_COUNT}(并发生成)") parser.add_argument("--aspect", default="1:1", help="宽高比,14 选 1,如 16:9 / 1:4 / 8:1") parser.add_argument("--size", default="2K", help="分辨率 1K / 2K / 4K") args = parser.parse_args() count = args.count if count < 1: count = 1 if count > MAX_COUNT: print(f"提示:一次最多 {MAX_COUNT} 张,已将 {args.count} 限制为 {MAX_COUNT}。", file=sys.stderr) count = MAX_COUNT paths = resolve_paths(args.out, count) def task(path): data = generate(api_key, endpoint, args.prompt, args.image, args.aspect, args.size) with open(path, "wb") as f: f.write(data) return os.path.abspath(path) failures = 0 with ThreadPoolExecutor(max_workers=count) as pool: for path, result in zip(paths, pool.map(lambda p: _safe(task, p), paths)): ok, value = result if ok: print(f"图片已保存至 {value}") else: failures += 1 print(f"第 {os.path.basename(path)} 张生成失败:{value}", file=sys.stderr) if failures == count: sys.exit("全部生成失败。") def _safe(fn, arg): try: return True, fn(arg) except Exception as e: # noqa: BLE001 — 单张失败不影响其它并发任务 return False, str(e) if __name__ == "__main__": main() ``` 模型名默认 `gemini-nano-banana-2.1`。如需临时切回上一代,设环境变量 `APIYI_IMAGE_MODEL=gemini-3.1-flash-image` 即可(上一代额外支持 `--size 512`)。 ## 为什么一句话就能出图 很多人好奇:我又没敲命令,怎么说句"画只猫"它就出图了? 原理是这样:Agent 启动时会**先读取每个技能 `SKILL.md` 里的 `description`**(一段很短的元数据,说明「这个技能做什么、什么时候该用」)。当你说出的需求**匹配上**这段描述的场景(比如"画/生成/渲染一张图""把这张图改成…"),Agent 就**自动决定调用这个技能**,去读完整的 `SKILL.md` 并运行脚本——整个过程你不用记任何命令。 所以: * **写得好的 `description` = 更准的自动触发**。本技能的描述已覆盖"生成/绘制/编辑/合成图片"等说法。 * 不想靠 Agent 猜、想要**百分百可控**时,用下面的**显性调用**。 ## 怎么用 ### 自然语言(隐式触发) 装好后直接对 Agent 说话即可: | 你说 | 技能行为 | | - | - | | "用 nano banana 2 画一张 16:9 的雪山日出海报" | 调脚本(无 `-i`),出 1 张 png | | "做一张 8:1 的超宽国风横幅" | 调脚本 `--aspect 8:1`,出 1 张超宽 png | | "国风山水来 3 张不同的" | 调脚本 `-n 3`,并发出 3 张 png | | "把 photo.jpg 的背景虚化,突出人物" | 调脚本 `-i photo.jpg`,出 1 张 png | ### 显性调用(更可控) 不想让 Agent 自己判断时,两种显式方式: * **支持斜杠命令的 Agent**(如 Claude Code): ```text theme={null} /nano-banana-2-1 一只在花园里打盹的橘猫,油画风格 --size 2K --aspect 3:2 ``` * **任意 Agent / 直接命令它跑脚本**(最通用): ```text theme={null} 运行 python3 nano-banana-2-1/scripts/nano_banana_2_1.py "一只在花园里打盹的橘猫,油画风格" --size 2K --aspect 3:2 ``` **怎么控制比例和清晰度**:用 `--aspect` 选宽高比(14 选 1,含 Pro 没有的 `1:4 / 4:1 / 1:8 / 8:1` 超长超宽),用 `--size` 选分辨率(`1K` / `2K` / `4K`,不支持 `512`)。直接说"竖屏 9:16""出 4K""来个超宽横幅",Agent 也会自动带上这两个参数。 ## 生成的图片在哪里 * 当 `-o` 只传**文件名**(如 `-o dog.png`)时,图片统一存到**项目根目录下的 `nano-banana-2-1-output/` 文件夹**(脚本自动创建),所以你在项目里的这个文件夹就能直接找到。 * 「项目根目录」= 脚本从自身位置向上找到的第一个含 `.git` 或 `.claude` 的目录——**不管 Agent 在哪个目录运行,图都落在项目里**,不会跑进临时目录害你找不到。 * 脚本会**为每张图打印一行完整绝对路径**,例如 `图片已保存至 /Users/you/project/nano-banana-2-1-output/dog.png`。 * 默认**只出 1 张**;`-n 3` 一次出 3 张(最多 5,超过自动截到 5),文件名自动加 `-1`、`-2`、`-3` 后缀。 * 传**带目录的路径**(如 `-o images/dog.png` 或绝对路径)时,按你给的路径存(相对路径相对当前工作目录),不进 `nano-banana-2-1-output/`。 * 图片编辑同理:输出是新文件,**不会覆盖你的原图**。 Nano Banana 2.1 有严格的内容安全管控。若脚本提示 `finishReason` 非 `STOP` 或返回的是拒绝说明文本,请按提示调整内容,不要对同一违规提示词反复重试。 ## 相关文档 * [Nano Banana 2.1 图片生成总览](/api-capabilities/gemini-nano-banana-2.1/overview) * [文生图 API 参考](/api-capabilities/gemini-nano-banana-2.1/text-to-image) * [图片编辑 API 参考](/api-capabilities/gemini-nano-banana-2.1/image-edit) * [Nano Banana Pro Agent 技能](/api-capabilities/nano-banana-image/skills) * [Nano Banana 定价说明](/api-capabilities/nano-banana-pricing) # 文生图 API 参考 Source: https://docs.apiyi.com/api-capabilities/gemini-nano-banana-2.1/text-to-image api-reference/gemini-nano-banana-2.1-generate-openapi.yaml POST /v1beta/models/gemini-nano-banana-2.1:generateContent Nano Banana 2.1 文生图 API 参考与在线调试 — 输入文本描述生成图片 右侧的交互式 Playground 支持下拉选择参数(宽高比、分辨率、响应类型等)。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),即可一键发送请求测试。 **场景说明**:本页用于「文本生成图片」。只需输入提示词即可,无需上传任何图片。如需根据现有图片做编辑,请使用 [图片编辑接口](/api-capabilities/gemini-nano-banana-2.1/image-edit)。 **🖥️ 浏览器 Playground 限制(重要)** 本接口的响应里包含 base64 编码的图片(`inlineData.data`,数 MB 量级)。受浏览器渲染限制,右侧 Playground 在收到响应后**可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法把这么长的 base64 显示出来。 **推荐做法**(小白零踩坑): * **直接复制下方"代码示例"中的 Python / Node.js / cURL 到本地运行**,代码会自动 `base64.b64decode` 并把图片**保存为本地文件**。 * 如要在浏览器里试 Playground,把 `imageSize` 设为最小档(`1K`),缩小响应体积。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 ## 代码示例 ### Python ```python theme={null} import requests import base64 API_KEY = "sk-your-api-key" PROMPT = "一只可爱的柴犬坐在樱花树下,水彩画风格,高清细节" response = requests.post( "https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent", headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json={ "contents": [{"parts": [{"text": PROMPT}]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }, timeout=300 ).json() img_data = [p for p in response["candidates"][0]["content"]["parts"] if "inlineData" in p][-1]["inlineData"]["data"] with open("output.png", 'wb') as f: f.write(base64.b64decode(img_data)) print("图片已保存至 output.png") ``` ### cURL ```bash theme={null} curl -X POST "https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "contents": [{"parts": [{"text": "未来主义的城市夜景,霓虹灯,赛博朋克风格"}]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }' ``` ### Node.js ```javascript theme={null} import fs from "fs"; const API_KEY = "sk-your-api-key"; const response = await fetch( "https://api.apiyi.com/v1beta/models/gemini-nano-banana-2.1:generateContent", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [{ text: "未来主义的城市夜景,霓虹灯,赛博朋克风格" }] }], generationConfig: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "16:9", imageSize: "2K" } } }) } ); const data = await response.json(); const imgBase64 = data.candidates[0].content.parts.filter((p) => p.inlineData).at(-1).inlineData.data; fs.writeFileSync("output.png", Buffer.from(imgBase64, "base64")); ``` ## 参数说明速查 | 参数 | 类型 | 必填 | 说明 | | - | - | - | - | | `contents[].parts[].text` | string | 是 | 文本提示词 | | `generationConfig.responseModalities` | array | 是 | `["IMAGE"]` 或 `["TEXT","IMAGE"]` | | `generationConfig.imageConfig.aspectRatio` | string | 否 | 14 种宽高比;不传时由模型按内容自选,建议显式传 | | `generationConfig.imageConfig.imageSize` | string | 否 | `1K` / `2K` / `4K`(不支持 `512`),默认 `1K` | | `generationConfig.thinkingConfig.thinkingLevel` | string | 否 | `minimal` / `medium` / `high`,默认 `medium` | | `generationConfig.thinkingConfig.includeThoughts` | boolean | 否 | 是否返回思维过程文本,默认 `false` | 详细的参数文档、可选值和默认值请查看右侧 Playground 中的字段说明,所有 enum 类型字段(如 `aspectRatio`、`imageSize`、`thinkingLevel`)都支持下拉选择,无需手动输入。 **Google 搜索接地**:可在请求顶层加 `"tools": [{"googleSearch": {}}]`,每次检索另收 \$0.014,检索次数由模型决定。详见 [Nano Banana 2.1 概览](/api-capabilities/gemini-nano-banana-2.1/overview)。 # Gemini Function Calling 函数调用指南 Source: https://docs.apiyi.com/api-capabilities/gemini/function-calling function_declarations 定义、AUTO/ANY/NONE 调用模式、function_response 回传,以及 Gemini 3 思维签名的回传要求。 Gemini 原生格式完整支持 Function Calling:模型输出"想调哪个函数 + 参数",你本地执行后把结果回传,模型给出最终回答。整体循环与 [OpenAI 的 FC](/api-capabilities/openai/function-calling) 一致,但**字段格式完全不同**,不能混用。 本页基于 Google 官方文档整理(`ai.google.dev/gemini-api/docs/function-calling`,2026年6月数据)。 ## 与 OpenAI 格式的差异速查 | | Gemini 原生 | OpenAI | | - | - | - | | 工具定义 | `tools: [{"function_declarations": [...]}]` | `tools: [{"type": "function", ...}]` | | 调用返回 | part 里的 `function_call`(`name` + `args` 对象) | `tool_calls` / `function_call` item(`arguments` 是 JSON 字符串) | | 结果回传 | `Part(function_response=...)` | `role:"tool"` 消息 / `function_call_output` item | | 调用策略 | `tool_config.function_calling_config.mode`:`AUTO` / `ANY` / `NONE` | `tool_choice`:`auto` / `required` / `none` | | 多轮推理状态 | **Gemini 3 需回传思维签名** | 无此要求 | 注意一个易错点:Gemini 的 `function_call.args` 是**结构化对象**,不是 JSON 字符串,不需要 `json.loads`。 ## 完整调用循环 ```python theme={null} from google import genai from google.genai import types client = genai.Client( api_key="YOUR_API_KEY", http_options={"base_url": "https://api.apiyi.com"} ) # 1. 定义工具 tools = [{ "function_declarations": [{ "name": "get_weather", "description": "获取指定城市的当前天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名,如:北京"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]} }, "required": ["city"] } }] }] # 2. 第一次请求:模型决定调函数 contents = [types.Content(role="user", parts=[types.Part(text="北京现在多少度?")])] r1 = client.models.generate_content( model="gemini-3.5-flash", contents=contents, config={"tools": tools} ) call = r1.candidates[0].content.parts[0].function_call print(f"模型想调用: {call.name}, 参数: {dict(call.args)}") # 3. 本地执行(这里用假数据代替真实查询) weather = {"city": call.args["city"], "temp": 26, "condition": "晴"} # 4. 回传结果:把模型的回复(含 function_call 及思维签名)和函数结果一起加进历史 contents.append(r1.candidates[0].content) contents.append(types.Content( role="user", parts=[types.Part( function_response=types.FunctionResponse(name=call.name, response=weather) )] )) r2 = client.models.generate_content( model="gemini-3.5-flash", contents=contents, config={"tools": tools} ) print(r2.text) ``` **Gemini 3 系列的思维签名(thought signature)必须回传**:模型返回的 `function_call` part 里带有加密的 `thought_signature`,第二次请求时要把**整个模型回复 Content 原样加进历史**(如上例第 4 步),签名缺失会导致推理链断裂甚至请求报错。用官方 `google-genai` SDK 按上面的写法即可自动带上;手写 REST 请求时不要剥掉该字段。报 `missing a thought_signature` 或 `Invalid thought signature` 时的区分与处理见 [Gemini 工具调用报思维签名 400 怎么办?](/faq/gemini-thought-signature-missing)。 ## 调用模式(mode) ```python theme={null} config = { "tools": tools, "tool_config": {"function_calling_config": {"mode": "AUTO"}} } ``` | 模式 | 行为 | | - | - | | `AUTO`(默认推荐) | 模型自行决定调不调 | | `ANY` | 强制必须调用某个函数;可配 `allowed_function_names` 限定范围 | | `NONE` | 禁止调用,只生成文本 | ```python theme={null} # 强制只能调 get_weather config = { "tools": tools, "tool_config": { "function_calling_config": { "mode": "ANY", "allowed_function_names": ["get_weather"] } } } ``` ## 并行与多步调用 * **并行调用**:一轮里模型可能返回多个 `function_call` part(如同时查两个城市),逐个执行后把全部 `function_response` 一起回传 * **多步调用**:模型可以"调函数 → 看结果 → 再调下一个"链式推进,循环处理直到响应里不再有 `function_call`。给循环设最大轮数,避免失控烧钱 ```python theme={null} # 通用 Agent 循环骨架 MAX_ROUNDS = 5 for _ in range(MAX_ROUNDS): response = client.models.generate_content( model="gemini-3.5-flash", contents=contents, config={"tools": tools} ) parts = response.candidates[0].content.parts calls = [p.function_call for p in parts if getattr(p, "function_call", None)] if not calls: print(response.text) # 没有函数调用了,输出最终回答 break contents.append(response.candidates[0].content) # 含思维签名,原样入历史 result_parts = [ types.Part(function_response=types.FunctionResponse( name=c.name, response=execute(c.name, dict(c.args)) )) for c in calls ] contents.append(types.Content(role="user", parts=result_parts)) ``` ## 最佳实践 * **description 写给模型看**:说清"什么时候该调我",参数能用 `enum` 收窄就别用自由字符串 * **工具定义保持稳定**:参与缓存前缀匹配,频繁变动会破坏 [缓存命中](/api-capabilities/gemini/prompt-caching) * **需要确定性 JSON 输出而非调用外部工具时**,考虑用 `response_schema` 结构化输出代替 FC(见 [原生调用](/api-capabilities/gemini/native) 参数表) * 沙箱计算类任务可以直接用 [code\_execution](/api-capabilities/gemini/multimodal) 工具,不必自己实现计算函数 ## 常见踩坑 | 现象 | 处理 | | - | - | | 第二轮报错 / 回答前后矛盾 | 模型回复(含思维签名)没有原样加进历史 —— 整个 `candidates[0].content` 都要 append | | 对 `args` 做 `json.loads` 报错 | Gemini 的 `args` 是对象不是字符串,直接 `dict(call.args)` | | 模型不调函数 | description 不够明确;或改用 `mode: "ANY"` 强制 | | 用 OpenAI 格式的 tools 定义报错 | 两套格式不能混用,按上文 `function_declarations` 格式改写 | ## 相关链接 * 同组页面:[原生调用](/api-capabilities/gemini/native) · [多模态与代码执行](/api-capabilities/gemini/multimodal) · [缓存计费](/api-capabilities/gemini/prompt-caching) * OpenAI 侧对照:[OpenAI FC函数调用](/api-capabilities/openai/function-calling) * Google 官方文档:`ai.google.dev/gemini-api/docs/function-calling` # Interactions API 与 generateContent 对比 Source: https://docs.apiyi.com/api-capabilities/gemini/interactions-api 详解谷歌 Gemini 两种 API 范式的差异:端点、请求响应结构、状态管理、思考呈现与计费字段,附 API易 网关兼容性实测结论 2026 年 6 月起,谷歌将 **Interactions API** 定为 GA(正式可用)并推荐所有新项目使用,经典的 **generateContent API** 转为 legacy 但仍完全支持。官方文档(如 Nano Banana 图片生成页)已提供两种范式的切换开关,很多开发者因此困惑:两者差异是什么?经 API易 网关该用哪个?本文给出详细对比与实测结论。 **API易 网关当前状态(2026 年 7 月 4 日实测)**:暂不支持 Interactions API 中转——`/v1beta2/interactions` 与 `/v1beta/interactions` 路径均返回 404。经 API易 调用 Gemini 请继续使用 [generateContent 原生格式](/api-capabilities/gemini/native),本站全部 Gemini 文档均基于该格式;后续网关支持 Interactions API 时会更新本页。 ## 两种范式是什么 **generateContent** 是经典的无状态接口:一次请求带全部上下文,一次响应返回全部结果,端点为 `POST /v1beta/models/{模型名}:generateContent`。谷歌称它"虽已被视为 legacy,但仍获得完整支持"。 **Interactions API** 是谷歌 2026 年 6 月 GA 的新接口,端点为 `POST /v1beta2/interactions`。它围绕核心资源 `Interaction`(一次完整的对话轮次或任务)设计,响应是一条按时间排列的**执行步骤(steps)时间线**——模型思考、工具调用与结果、最终输出都是显式的 step。官方明确:**今后主线模型之外的新模型、新 Agent 能力将优先在 Interactions API 上发布**(来源:`ai.google.dev/gemini-api/docs/interactions-overview`)。 ## 核心差异总览 | 维度 | generateContent(经典) | Interactions API(新) | | - | - | - | | 端点 | `POST /v1beta/models/{模型名}:generateContent` | `POST /v1beta2/interactions` | | 输入结构 | `contents[].parts[]`(按角色组织的多模态 part) | `input`(字符串或内容块,模型名放请求体) | | 输出结构 | `candidates[0].content.parts[]` | `steps[]` 时间线:`user_input` / `thought` / `function_call` / `function_result` / `model_output` | | 多轮对话 | 客户端自行拼接**全量历史**逐轮重发 | `previous_interaction_id` 服务端续接(也可自带历史走无状态) | | 思考呈现 | `thoughtsTokenCount` 计数;图片模型的思考中间稿混在图片 parts 里返回 | 以 `steps`(`type: "thought"`)显式返回,含思考文本与临时图片 | | 流式输出 | 专用端点 `:streamGenerateContent` | 同一端点,请求体加 `"stream": true` | | 后台执行 | 不支持 | `"background": true`,适合长任务 | | 缓存 | 显式缓存 + 隐式缓存 | 无显式缓存;`previous_interaction_id` 可显著提升隐式缓存命中率 | | 服务端数据保留 | 不存储请求 | 默认 `store: true`:付费层保留 **55 天**、免费层 1 天,可主动删除;`store: false` 退回无状态(但与 background、previous\_interaction\_id 不兼容) | | usage 字段 | `promptTokenCount` / `candidatesTokenCount` / `thoughtsTokenCount` / `totalTokenCount` | `total_thought_tokens` / `total_output_tokens` 等(蛇形命名) | | Agent 调用 | 不支持 | 同一接口可直接调用 Deep Research、Antigravity 等官方 Agent | | 尚不支持的能力 | —(功能最全) | Batch API、显式缓存、`video_metadata`、Python 自动函数调用、Gemini 3 远程 MCP | | SDK 入口 | `client.models.generate_content`(google-genai) | `client.interactions.create`(google-genai ≥ 2.3.0 / @google/genai ≥ 2.3.0) | Interactions API 的服务端状态管理有一个易踩的坑:`previous_interaction_id` **只续接对话历史**,`tools`、`system_instruction`、`generation_config`(含 `thinking_level`、`temperature` 等)都是"单次交互作用域",每一轮都要重新传,否则不生效。 ## 请求与响应结构对比(文本单轮) generateContent 示例可直接在 API易 网关使用;Interactions API 示例为官方直连端点(API易 暂不支持): ```bash generateContent(API易 可用) theme={null} curl -X POST "https://api.apiyi.com/v1beta/models/gemini-2.5-flash:generateContent" \ -H "Authorization: Bearer $APIYI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [{ "parts": [{ "text": "Tell me a joke." }] }] }' ``` ```bash Interactions API(官方直连) theme={null} curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \ -H "x-goog-api-key: $GEMINI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gemini-3.5-flash", "input": "Tell me a joke." }' ``` 两者的响应结构差异(同一请求的两种返回形态): ```json generateContent 响应 theme={null} { "candidates": [ { "content": { "parts": [{ "text": "Why did the chicken cross the road? ..." }], "role": "model" }, "finishReason": "STOP", "index": 0 } ], "usageMetadata": { "promptTokenCount": 4, "candidatesTokenCount": 12, "totalTokenCount": 16 } } ``` ```json Interactions API 响应 theme={null} { "id": "int_123", "status": "completed", "steps": [ { "type": "user_input", "status": "done", "content": [{ "type": "text", "text": "Tell me a joke." }] }, { "type": "model_output", "status": "done", "content": [{ "type": "text", "text": "Why did the chicken cross the road?" }] } ] } ``` ## 多轮对话对比 这是两者体验差异最大的地方。generateContent 每一轮都要把**完整历史**重新发一遍;Interactions API 只需带上一轮的 `id`: ```bash generateContent(全量重发历史) theme={null} curl -X POST "https://api.apiyi.com/v1beta/models/gemini-2.5-flash:generateContent" \ -H "Authorization: Bearer $APIYI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [ { "role": "user", "parts": [{ "text": "Hi, my name is Phil." }] }, { "role": "model", "parts": [{ "text": "Hello Phil! How can I help?" }] }, { "role": "user", "parts": [{ "text": "What is my name?" }] } ] }' ``` ```bash Interactions API(服务端续接) theme={null} curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \ -H "x-goog-api-key: $GEMINI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gemini-3.5-flash", "previous_interaction_id": "int_123", "input": "What is my name?" }' ``` 服务端续接除了省去拼历史的代码,还能让隐式缓存更容易命中对话前缀,官方称可降低多轮场景的 token 成本。代价是数据默认存储在谷歌侧(付费层 55 天),对数据合规敏感的业务需评估 `store` 语义。 ## 图片模型场景的差异 Gemini 3 系图片模型(如 `gemini-3-pro-image`)默认带思考过程,两种范式对"思考中间稿图片"的呈现完全不同: * **generateContent(API易 网关现行格式)**:思考中间稿以**普通图片 part** 混在 `candidates[0].content.parts` 里返回(带 `thoughtSignature`、无 `thought` 标记),实测一次可返回 2–10 张、每张按 1120/2000 tokens 计入输出——解析时务必遍历 parts 并**取最后一张为最终稿**。完整实测与对账口径见 [usage 字段与输出解读](/api-capabilities/nano-banana-usage-metadata)。 * **Interactions API**:思考被显式化为 `type: "thought"` 的 steps(含思考文本与临时图片),最终图在 `model_output` step 中;SDK 另提供 `.output_image` / `.output_text` 便捷属性。交错图文输出(如图文并茂的故事)仍需手动遍历 steps。 ## API易 网关兼容性实测 2026 年 7 月 4 日以测试 key 对 `api.apiyi.com` 的探测结果: | 测试项 | 请求 | 结果 | | - | - | - | | `POST /v1beta2/interactions` + Bearer 认证 | `gemini-2.5-flash` 最小请求 | ❌ 404(Invalid URL) | | `POST /v1beta/interactions` + Bearer 认证 | 同上 | ❌ 404(Invalid URL) | | `POST /v1beta2/interactions` + `x-goog-api-key` 认证 | 同上 | ❌ 404(Invalid URL) | | `POST /v1beta/models/{模型名}:generateContent` | 文本/图片各模型 | ✅ 正常(本站全部 Gemini 文档基于此) | **结论:API易 网关暂未开通 Interactions API 转发**,多轮续接、Agent 调用、后台执行等 Interactions 独有能力现阶段无法经网关使用。 ## 开发者建议 1. **经 API易 调用:继续用 generateContent**。它功能最全(Batch、显式缓存、video\_metadata 反而只有它支持),且 generateContent 被官方承诺持续完整支持,短期内没有停用风险。 2. **多轮对话在 generateContent 下的写法**:客户端拼接历史即可,参考 [Gemini 原生格式调用](/api-capabilities/gemini/native) 与 [多轮对话](/api-capabilities/multi-turn-conversation)。 3. **如果你直连官方并考虑迁移到 Interactions API**,注意四点:`tools` / `system_instruction` / `generation_config` 每轮需重传;`store` 默认开启、付费层数据保留 55 天;Batch API 与显式缓存尚不可用;SDK 需升级 google-genai / @google/genai 到 2.3.0 以上。 4. **值得关注 Interactions API 的时机**:需要官方 Agent(Deep Research、Antigravity)、`background: true` 长任务、或多轮场景想靠服务端状态省 token 时。API易 支持后本页会第一时间更新。 ## 相关文档 经 API易 使用 generateContent 原生格式的完整指南 candidates、parts、finishReason 的解析要点 图片模型 usageMetadata 字段口径与思考中间稿实测 无状态接口下的多轮对话实现方式 # Gemini 多模态输入与代码执行 Source: https://docs.apiyi.com/api-capabilities/gemini/multimodal 图片、音频、视频内联传入 Gemini:20MB 限制、media_resolution 控费、code_execution 沙箱执行 Python。 Gemini 原生格式支持直接传入图片、音频、视频做理解分析,并内置 `code_execution` 工具在沙箱里跑 Python。本页示例默认使用 [原生调用](/api-capabilities/gemini/native) 的客户端配置。 **API易 通道的两个硬限制**: 1. **不支持 Files API**(`client.files.upload()` 仅 Google 官方端点可用),媒体一律**内联传入** 2. 内联媒体**单文件不超过 20MB**,超过请先压缩或抽帧 ## 图片理解 图片可以直接传 PIL Image 对象,SDK 自动处理编码: ```python theme={null} from google import genai from PIL import Image client = genai.Client( api_key="YOUR_API_KEY", http_options={"base_url": "https://api.apiyi.com"} ) img = Image.open("photo.jpg") response = client.models.generate_content( model="gemini-3.5-flash", contents=[ "请详细描述这张图片的内容,包括主要元素、颜色、构图。", img ] ) print(response.text) ``` 也可以用 `types.Part.from_bytes` 显式传入: ```python theme={null} from google.genai import types with open("photo.jpg", "rb") as f: image_bytes = f.read() response = client.models.generate_content( model="gemini-3.5-flash", contents=[ types.Part.from_bytes(data=image_bytes, mime_type="image/jpeg"), "图里有什么?" ] ) ``` ## 音频理解 ```python theme={null} from google.genai import types with open("meeting.mp3", "rb") as f: audio_bytes = f.read() response = client.models.generate_content( model="gemini-3.5-flash", contents=[ types.Part.from_bytes(data=audio_bytes, mime_type="audio/mp3"), "请转录这段音频,并总结主要话题。" ] ) print(response.text) ``` ## 视频理解 ```python theme={null} from google.genai import types with open("demo.mp4", "rb") as f: video_bytes = f.read() response = client.models.generate_content( model="gemini-3.5-flash", contents=[ types.Part.from_bytes(data=video_bytes, mime_type="video/mp4"), "总结这个视频的主要内容和关键信息。" ] ) print(response.text) ``` 视频按帧 + 音轨计 tokens,较长视频费用可观 —— 更多视频场景实践见 [视频理解](/api-capabilities/video-understanding)。 ## media\_resolution 控费 媒体输入的 token 消耗和分辨率挂钩。对"看个大概"的任务(分类、是否包含某元素),降分辨率能明显省钱: ```python theme={null} from google.genai import types response = client.models.generate_content( model="gemini-3.5-flash", contents=["这张图片的主题是什么?", img], config=types.GenerateContentConfig( media_resolution="MEDIA_RESOLUTION_LOW" # LOW / MEDIUM / HIGH ) ) ``` | 档位 | 适用 | | - | - | | `LOW` | 分类、粗粒度识别,最省 | | `MEDIUM` | 一般描述与理解(默认均衡选择) | | `HIGH` | OCR、小字、细节密集型任务 | ## 支持的格式 | 类型 | 格式 | 传入方式 | | - | - | - | | 图片 | JPG、PNG、WebP | PIL Image 或 `Part.from_bytes` | | 音频 | MP3、WAV | `Part.from_bytes` | | 视频 | MP4、MOV | `Part.from_bytes` | 均为内联传入,单文件不超过 20MB。 ## 代码执行(code\_execution) 声明 `code_execution` 工具后,模型会自己写 Python、在沙箱里执行、再基于结果回答 —— 适合计算、数据分析类任务: ```python theme={null} response = client.models.generate_content( model="gemini-3.5-flash", contents=""" 有以下销售数据:产品A 100件×50元、产品B 200件×30元、产品C 150件×40元。 请计算总销售额和平均单价,并给出各产品销售额占比。 """, config={"tools": [{"code_execution": {}}]} ) for part in response.candidates[0].content.parts: if getattr(part, "executable_code", None): print(f"[执行的代码]\n{part.executable_code.code}") if getattr(part, "code_execution_result", None): print(f"[执行结果]\n{part.code_execution_result.output}") if getattr(part, "text", None): print(f"[说明]\n{part.text}") ``` 代码执行的限制:仅 Python;沙箱环境无法访问网络和你的文件系统;有执行时长上限。需要调用你自己的外部服务时,用 [FC函数调用](/api-capabilities/gemini/function-calling)。 ## 相关链接 * 同组页面:[原生调用](/api-capabilities/gemini/native) · [缓存计费](/api-capabilities/gemini/prompt-caching) · [FC函数调用](/api-capabilities/gemini/function-calling) * 应用场景:[视频理解](/api-capabilities/video-understanding) · [视觉理解](/api-capabilities/vision-understanding) * Google 官方文档:`ai.google.dev/gemini-api/docs/vision` # Gemini 原生格式调用指南 Source: https://docs.apiyi.com/api-capabilities/gemini/native 通过 API易 调用 Gemini 官方 generateContent 格式:google-genai SDK 配置、流式输出、thinking_level 思考控制、思维签名。 API易 完整支持 **Gemini 官方原生格式**(`/v1beta` generateContent 端点):把 base\_url 指向 `https://api.apiyi.com`,现有 Gemini 代码和官方 SDK 即可无缝迁移,无需任何格式转换。 本页基于 Google 官方文档整理(`ai.google.dev/gemini-api/docs`,2026年6月数据),示例均可直接复制运行。 ## 为什么用原生格式 OpenAI 兼容格式也能调 Gemini,但以下能力**只有原生格式有**: * **完整思考控制**:`thinking_level`(Gemini 3 系列)/ `thinking_budget`(2.5 系列)、思考摘要、思维签名 * **原生多模态 Part**:图片 / 音频 / 视频直接内联传入,支持 `media_resolution` 控费 —— 见 [多模态与代码执行](/api-capabilities/gemini/multimodal) * **代码执行工具**:`code_execution` 沙箱跑 Python * **精细的用量字段**:`thoughts_token_count`、`cached_content_token_count` 等 简单文本对话、或要和其它厂商模型共用一套代码 → 用 [OpenAI 兼容模式调用](/api-capabilities/openai/compatible) 即可。 ## 快速开始 推荐使用 Google 官方统一 SDK `google-genai`(旧版 `google-generative-ai` 已于 2025年11月30日 停止支持): ```bash theme={null} pip install google-genai ``` ```python Python theme={null} from google import genai client = genai.Client( api_key="YOUR_API_KEY", # 你的 API易 密钥 http_options={"base_url": "https://api.apiyi.com"} ) response = client.models.generate_content( model="gemini-3.5-flash", contents="用一句话介绍你自己" ) print(response.text) ``` ```javascript Node.js theme={null} import { GoogleGenAI } from '@google/genai'; const ai = new GoogleGenAI({ apiKey: 'YOUR_API_KEY', httpOptions: { baseUrl: 'https://api.apiyi.com' } }); const response = await ai.models.generateContent({ model: 'gemini-3.5-flash', contents: '用一句话介绍你自己' }); console.log(response.text); ``` ```bash cURL theme={null} curl "https://api.apiyi.com/v1beta/models/gemini-3.5-flash:generateContent" \ -H "Content-Type: application/json" \ -H "x-goog-api-key: YOUR_API_KEY" \ -d '{ "contents": [{ "parts": [{"text": "用一句话介绍你自己"}] }] }' ``` 注意 base\_url 是 `https://api.apiyi.com`(**不带** `/v1`),与 OpenAI 兼容格式的 `https://api.apiyi.com/v1` 不同。请使用 **API易 密钥**,不是 Google AI Studio 的密钥。 ## 流式输出 ```python theme={null} stream = client.models.generate_content_stream( model="gemini-3.5-flash", contents="写一篇关于量子计算的短文" ) for chunk in stream: print(chunk.text, end="", flush=True) ``` ## 思考控制 Gemini 是思考型模型,**两代参数不一样,混用会直接报错**: | 模型系列 | 参数 | 取值 | | - | - | - | | Gemini 3 / 3.1 / 3.5 | `thinking_level` | `minimal`(仅 Flash 系)/ `low` / `high`(默认) | | Gemini 2.5 | `thinking_budget` | token 数上限(如 0–8192),不设则模型自动控制 | 对 Gemini 3 系列模型**同时传 `thinking_level` 和 `thinking_budget` 会返回错误**,只能二选一(Gemini 3 系列请用 `thinking_level`)。 ```python theme={null} from google.genai import types # Gemini 3 系列:按档位控制 response = client.models.generate_content( model="gemini-3.5-flash", contents="证明根号2是无理数", config=types.GenerateContentConfig( thinking_config=types.ThinkingConfig(thinking_level="high") ) ) # Gemini 2.5 系列:按 token 预算控制 response = client.models.generate_content( model="gemini-2.5-flash", contents="简单问题,要快", config=types.GenerateContentConfig( thinking_config=types.ThinkingConfig(thinking_budget=0) # 关闭思考 ) ) ``` 档位选型:`minimal` 适合低延迟简单任务(分类、抽取);`low` 适合常规对话;`high` 适合复杂推理和代码 —— 思考 token 按**输出价**计费,档位越高账单越贵。 ### 思考摘要与思维签名 * **思考摘要**:`include_thoughts=True` 可在响应中返回思考过程摘要(`part.thought` 为 `True` 的 part) * **思维签名(thought signatures)**:Gemini 3 引入的加密推理状态。多轮对话(尤其函数调用)时要把响应里的 `thought_signature` 原样回传,模型才能延续推理链。**官方 SDK 自动处理**,手写 REST 请求时注意不要丢弃该字段 —— 详见 [FC函数调用](/api-capabilities/gemini/function-calling) ```python theme={null} response = client.models.generate_content( model="gemini-3.5-flash", contents="分析这段代码的时间复杂度:def fib(n): return n if n <= 1 else fib(n-1) + fib(n-2)", config=types.GenerateContentConfig( thinking_config=types.ThinkingConfig(thinking_level="high", include_thoughts=True) ) ) for part in response.candidates[0].content.parts: if getattr(part, "thought", False): print(f"[思考摘要] {part.text}") else: print(f"[最终答案] {part.text}") ``` ## 常用配置参数 通过 `config`(`GenerateContentConfig`)传入: | 参数 | 说明 | | - | - | | `system_instruction` | 系统指令 | | `temperature` | 0–2。**Gemini 3 系列官方建议保持默认 1.0**,调低反而可能损伤推理质量 | | `max_output_tokens` | 最大输出(含思考 token) | | `thinking_config` | 思考控制,见上文 | | `response_mime_type` | 设为 `application/json` 强制 JSON 输出 | | `response_schema` | 配合 JSON 输出的结构约束(结构化输出) | | `tools` | 函数声明 / `code_execution` 等工具 | | `media_resolution` | 多模态输入分辨率控费,见 [多模态页](/api-capabilities/gemini/multimodal) | ## 用量字段(usage\_metadata) ```python theme={null} usage = response.usage_metadata print(f"输入: {usage.prompt_token_count}") print(f"输出: {usage.candidates_token_count}") print(f"思考: {usage.thoughts_token_count}") print(f"缓存命中: {usage.cached_content_token_count}") ``` | 字段 | 说明 | 计费 | | - | - | - | | `prompt_token_count` | 输入 tokens | 按输入价 | | `candidates_token_count` | 输出 tokens | 按输出价 | | `thoughts_token_count` | 思考 tokens | **按输出价**,控档位可省 | | `cached_content_token_count` | 缓存命中 tokens | 按官方折扣,见 [缓存计费](/api-capabilities/gemini/prompt-caching) | | `total_token_count` | 总量 | — | ## 支持的模型与价格 | 模型 | 输入(每 1M tokens) | 输出(每 1M tokens) | 说明 | | - | - | - | - | | `gemini-3.5-flash` | \$1.50 | \$9.00 | 当前主力,多项指标反超 3.1 Pro,1M 上下文 | | `gemini-3.1-pro-preview` | \$1.80 | \$10.80 | Pro 旗舰 | | `gemini-3-pro-preview` | \$1.80 | \$10.80 | 上代 Pro | | `gemini-3-flash-preview` | \$0.44 | \$2.64 | 轻量快速 | | `gemini-3.1-flash-lite` | \$0.25 | \$1.50 | 超低价 | | `gemini-2.5-pro` | \$1.25 | \$10.00 | 2.5 系 Pro | | `gemini-2.5-flash` | \$0.30 | \$2.40 | 2.5 系主力 | | `gemini-2.5-flash-lite` | \$0.10 | \$0.40 | 最便宜 | 部分模型提供 `-thinking` / `-nothinking` 后缀别名(如 `gemini-3-flash-preview-nothinking`),固定开启/关闭思考,适合不方便改请求参数的客户端。完整列表见 [模型与价格总览](/api-capabilities/model-info)。 ## 与 OpenAI 兼容格式对比 | 特性 | Gemini 原生格式 | OpenAI 兼容格式 | | - | - | - | | base\_url | `https://api.apiyi.com` | `https://api.apiyi.com/v1` | | SDK | `google-genai` | `openai` | | 思考控制 | `thinking_level` / `thinking_budget` | `reasoning_effort` | | 思考摘要 / 思维签名 | ✅ | ❌ | | 代码执行工具 | ✅ | ❌ | | 媒体输入 | 原生 Part 内联(PIL / bytes) | Base64 image\_url | | 缓存命中字段 | `cached_content_token_count` | `cached_tokens` | ## 注意事项 * **不支持 Files API**(`client.files.upload()`),媒体一律内联传入且**单文件不超过 20MB** —— 详见 [多模态与代码执行](/api-capabilities/gemini/multimodal) * 媒体、长上下文的缓存折扣与命中率说明见 [缓存计费](/api-capabilities/gemini/prompt-caching) ## 相关链接 * 同组页面:[多模态与代码执行](/api-capabilities/gemini/multimodal) · [缓存计费](/api-capabilities/gemini/prompt-caching) · [FC函数调用](/api-capabilities/gemini/function-calling) * 获取 / 管理令牌:`https://api.apiyi.com/token` * Google 官方文档:`ai.google.dev/gemini-api/docs` # Gemini 隐式缓存计费指南 Source: https://docs.apiyi.com/api-capabilities/gemini/prompt-caching Gemini 隐式缓存自动启用,命中按官方折扣计费。但命中率不如 Claude/OpenAI,做成本预算请按无缓存价格估算。 API易 的 Gemini 通道自动启用**隐式上下文缓存**(implicit caching):请求前缀命中时按官方折扣计费,`cached_content_token_count` 字段原样回吐,零代码改动。 先说结论:**Gemini 缓存"有,但别指望"** —— 隐式缓存的命中行为由上游控制,实际命中率明显不如 [OpenAI](/api-capabilities/openai/prompt-caching) 和 [Claude](/api-capabilities/claude-prompt-caching)。把它当作"有则更好"的额外优惠,**做成本测算时一律按无缓存价格估算**。 本页基于 Google 官方文档整理(`ai.google.dev/gemini-api/docs/caching`,2026年6月数据)。 ## 机制一句话 请求开头部分(前缀)与近期请求相同且达到最小长度时,上游自动复用缓存:命中部分按官方折扣计费(官方口径**最高可省 90%**),无需打任何标记。 ## 触发条件 | 条件 | 要求 | | - | - | | 最小前缀长度 | **Gemini 3 / 3.1 / 3.5 系列:4096 tokens**;2.5 系列:2048 tokens | | 前缀稳定 | 从第一个字符起逐字节相同,动态内容(时间戳、随机 ID)会切断匹配 | | 时间窗口 | 缓存会在闲置一段时间后过期,短时间内的连续请求更容易命中 | 注意 Gemini 的起缓阈值(4096)比 OpenAI(1024)高不少 —— **短系统提示词在 Gemini 上基本不会命中**,这是"Gemini 缓存体感差"的原因之一。 ## 怎么判断命中 看 `usage_metadata.cached_content_token_count`: ```python theme={null} response = client.models.generate_content( model="gemini-3.5-flash", contents=[LONG_STABLE_PREFIX, question] ) usage = response.usage_metadata print(f"输入: {usage.prompt_token_count}") print(f"缓存命中: {usage.cached_content_token_count}") # > 0 即命中 ``` 命中部分在后台账单中按折扣倍率单列计费项;REST 响应里的对应字段为 `usageMetadata.cachedContentTokenCount`。 ## 尽量提高命中率 方法论和 OpenAI 一致(详细解释见 [OpenAI 缓存计费指南](/api-capabilities/openai/prompt-caching)): * **稳定内容放前面**:长系统指令、文档、few-shot 示例在前;用户输入、时间戳在后 * **前缀做长**:不足 4096 tokens(Gemini 3 系)的前缀永远不会命中 * **短时间内集中复用**:批量任务连续发,不要拉开间隔 * 多轮对话天然是追加式前缀,相对容易命中 即便全部做对,**也不保证命中** —— 隐式缓存是 best-effort 的,这点和 OpenAI/Claude 的确定性行为不同。 ## 显式缓存(cachedContents) Google 官方还有显式缓存 API(`cachedContents`,创建一个有 TTL 的缓存对象再引用)。该接口是**有状态的服务端资源**,API易 通道**暂不支持**,请使用隐式缓存。 ## 与其它通道对比 | | Gemini | OpenAI | Claude | | - | - | - | - | | 触发 | 隐式自动 | 全自动 | 手动标记 | | 最小阈值 | **4096**(3 系)/ 2048(2.5 系) | 1024 | 1024–4096 | | 命中折扣 | 官方口径最高省 90% | 命中 0.1× | 命中 0.1× | | 命中稳定性 | ⚠️ 不保证,体感一般 | ✅ 稳定 | ✅ 稳定 | | 命中字段 | `cached_content_token_count` | `cached_tokens` | `cache_read_input_tokens` | **缓存敏感的高频长前缀业务(Agent、RAG、批量文档),建议优先选 OpenAI 或 Claude 通道。** 全平台缓存支持总览见 [缓存计费 FAQ](/faq/cache-billing)。 ## 相关链接 * 同组页面:[原生调用](/api-capabilities/gemini/native) · [多模态与代码执行](/api-capabilities/gemini/multimodal) · [FC函数调用](/api-capabilities/gemini/function-calling) * 其它通道:[OpenAI 缓存计费](/api-capabilities/openai/prompt-caching) · [Claude 缓存计费](/api-capabilities/claude-prompt-caching) * Google 官方文档:`ai.google.dev/gemini-api/docs/caching` # Gemini 原生格式:流式与非流式响应 Source: https://docs.apiyi.com/api-capabilities/gemini/response-handling Gemini 官方 generateContent / streamGenerateContent 的响应结构:candidates/parts、thoughtSignature、SSE 流式,附解析方法与字段说明。 通过 [Gemini 原生格式](/api-capabilities/gemini/native)(`/v1beta` generateContent)调用时,响应是 Google 的 `candidates / parts` 结构,与 OpenAI 兼容格式不同。本页讲清非流式(`generateContent`)与流式(`streamGenerateContent`)两种响应怎么解析。 请求侧(base\_url 为 `https://api.apiyi.com` 不带 `/v1`、`x-goog-api-key` 鉴权、`thinking_level` 思考控制)见 [Gemini 原生格式调用指南](/api-capabilities/gemini/native)。本页只讲**响应侧**。示例用轻量模型 `gemini-3.1-flash-lite`。 ## 非流式响应 端点 `…:generateContent`,**正文在 `candidates[0].content.parts[]`**: ```json theme={null} { "candidates": [{ "content": { "role": "model", "parts": [ { "text": "1+1等于2。", "thoughtSignature": "EjQKMgEM…" } ] }, "finishReason": "STOP", "index": 0 }], "usageMetadata": { "promptTokenCount": 15, "candidatesTokenCount": 6, "totalTokenCount": 21 }, "modelVersion": "gemini-3.1-flash-lite", "responseId": "Il0taoSYJ5Cez7…" } ``` 取正文要**遍历 `parts`** 拼接每个 `text`: ```python Python theme={null} import requests resp = requests.post( "https://api.apiyi.com/v1beta/models/gemini-3.1-flash-lite:generateContent", headers={"Content-Type": "application/json", "x-goog-api-key": "YOUR_API_KEY"}, json={"contents": [{"parts": [{"text": "1+1等于几?"}]}]}, timeout=60, ) data = resp.json() parts = data["candidates"][0]["content"]["parts"] text = "".join(p["text"] for p in parts if "text" in p) print(text) print(data["usageMetadata"]) ``` ```bash cURL theme={null} curl "https://api.apiyi.com/v1beta/models/gemini-3.1-flash-lite:generateContent" \ -H "Content-Type: application/json" \ -H "x-goog-api-key: YOUR_API_KEY" \ -d '{"contents":[{"parts":[{"text":"1+1等于几?"}]}]}' ``` `finishReason` 是**大写** `STOP`(不是 OpenAI 的小写 `stop`),其它取值如 `MAX_TOKENS`、`SAFETY`。一个 `part` 可能只含 `thoughtSignature` 而无 `text`,遍历时要用 `if "text" in p` 过滤,否则会 KeyError。 ## thoughtSignature(思维签名) Gemini 3 系列会在 part 上附带 `thoughtSignature`(加密的推理状态)——**实测连轻量的 `gemini-3.1-flash-lite` 也会返回**。 * **单轮**:用不到,忽略即可。 * **多轮 / 函数调用**:要把上一轮响应里的 `thoughtSignature` **原样回传**到下一轮的 `contents` 中,模型才能延续推理链。**官方 `google-genai` SDK 自动处理**;手写 REST 时注意不要丢弃该字段。详见 [Gemini 函数调用](/api-capabilities/gemini/function-calling)。 这正是原生格式与 [OpenAI 兼容模式](/api-capabilities/openai/reasoning-models) 的关键区别:兼容模式下推理模型无状态、不暴露签名;原生格式才有 `thoughtSignature` 且多轮需回传。 ## 流式响应(SSE) 端点 `…:streamGenerateContent`,每行 `data: {...}`,每块的增量在 `candidates[0].content.parts[0].text`: ```text theme={null} data: {"candidates":[{"content":{"parts":[{"text":"1"}]},"finishReason":"","index":0}],"usageMetadata":{...}} data: {"candidates":[{"content":{"parts":[{"text":"+1等于2。"}]},"finishReason":"","index":0}],"usageMetadata":{...}} data: {"candidates":[{"content":{"parts":[{"thoughtSignature":"EjQK…"}]},"finishReason":"STOP","index":0}],"usageMetadata":{...}} ``` **经 API易 网关,流式统一返回 SSE 的 `data:` 行**(加不加 `?alt=sse` 都一样),**没有 `[DONE]` 终止符**——以 `finishReason == "STOP"` 的那一块为结束。最后一块通常**只含 `thoughtSignature` 而无 `text`**。 ```python theme={null} import json, requests resp = requests.post( "https://api.apiyi.com/v1beta/models/gemini-3.1-flash-lite:streamGenerateContent?alt=sse", headers={"Content-Type": "application/json", "x-goog-api-key": "YOUR_API_KEY"}, json={"contents": [{"parts": [{"text": "写一首短诗"}]}]}, stream=True, timeout=120, ) text, usage = "", None for line in resp.iter_lines(decode_unicode=True): if not line or not line.startswith("data: "): continue chunk = json.loads(line[6:]) usage = chunk.get("usageMetadata", usage) # 累计值,后到的覆盖 for cand in chunk.get("candidates", []): for p in cand.get("content", {}).get("parts", []): if "text" in p: # 跳过只含 thoughtSignature 的块 text += p["text"] print(p["text"], end="", flush=True) print("\n", usage) ``` `usageMetadata` **每块都带,且是累计值**(`candidatesTokenCount` 随输出增长)——以**最后一块**为准即可,无需自己累加。 ## 与 OpenAI 兼容格式的关键差异 | 维度 | Gemini 原生(`/v1beta`) | OpenAI 兼容(`/v1/chat/completions`) | | - | - | - | | base\_url | `https://api.apiyi.com`(不带 `/v1`) | `https://api.apiyi.com/v1` | | 鉴权头 | `x-goog-api-key` | `Authorization: Bearer` | | 正文位置 | `candidates[0].content.parts[].text` | `choices[0].message.content` | | 流式增量 | 各块 `parts[].text` | `choices[0].delta.content` | | 流式终止 | `finishReason == "STOP"`,**无 `[DONE]`** | `data: [DONE]` | | 结束原因 | 大写 `STOP` / `MAX_TOKENS` | 小写 `stop` | | 思维签名 | ✅ `thoughtSignature`(多轮需回传) | ❌ 不暴露 | | usage | `usageMetadata`(流式每块累计) | `usage`(流式尾部一次性) | ## usage 与计费 ```python theme={null} u = data["usageMetadata"] # promptTokenCount 输入 / candidatesTokenCount 输出 / thoughtsTokenCount 思考 / totalTokenCount 总量 ``` * `thoughtsTokenCount`(思考 token)按**输出价**计费,可用 `thinking_level` 控档省钱。 * 缓存命中 `cachedContentTokenCount` 的折扣见 [Gemini 缓存计费](/api-capabilities/gemini/prompt-caching)。 * 各字段完整说明见 [Gemini 原生格式调用指南](/api-capabilities/gemini/native) 的「用量字段」一节。 ## 相关链接 * 同组页面:[Gemini 原生格式调用指南](/api-capabilities/gemini/native) · [多模态与代码执行](/api-capabilities/gemini/multimodal) · [函数调用](/api-capabilities/gemini/function-calling) * 兼容格式对照:[OpenAI 兼容模式响应数据处理](/api-capabilities/openai/response-handling) * 获取 / 管理令牌:`https://api.apiyi.com/token` # Gemini API 联网搜索使用指南 Source: https://docs.apiyi.com/api-capabilities/gemini/web-search 原生 generateContent + google_search 工具即可联网,默认分组 KEY 直接可用,三个 Gemini 模型实测通过;OpenAI 兼容模式不支持,附判别方法与计费说明($14/1K 次)。 本文说明在 API易 上使用 Gemini 系列模型实现联网搜索(Grounding with Google Search)的方法,基于 2026年6月 实测验证(3 模型 × 2 模式 × 多种工具声明,21 条请求证据)。Gemini 原生格式的基础接入请先看 [Gemini 原生调用](/api-capabilities/gemini/native)。 ## 一句话结论 **API易 Gemini 原生端点完整支持 Google 官方联网搜索**:使用 **`/v1beta` generateContent + `google_search` 工具**,gemini-3.5-flash、gemini-3.1-flash-lite、gemini-3.1-pro-preview 实测均真实联网、返回带来源引用的最新信息。**默认分组的 KEY 即可使用,无需任何特殊开通。** ``` 端点: POST https://api.apiyi.com/v1beta/models/{模型名}:generateContent 工具: tools: [{"google_search": {}}] 模型: gemini-3.5-flash / gemini-3.1-flash-lite / gemini-3.1-pro-preview(实测验证) ``` **OpenAI 兼容模式(`/v1/chat/completions`)不支持联网搜索**。实测 `web_search_options`、透传 `google_search`、`tools: [{"type": "web_search"}]` 三种声明全部返回 HTTP 200 但被静默忽略——模型只是凭训练数据回答。请不要以"没报错"判断联网已生效,判别方法见下文。 ## 真实可用性(实测数据,2026-06-11) | 模型 | 联网结果 | groundingMetadata | 单次问答搜索次数 | 延迟 | | - | - | - | - | - | | gemini-3.5-flash | ✅ 当周真实新闻,多查询交叉检索 | ✅ 完整 | 4–7 次 | 24–45s | | gemini-3.1-flash-lite | ✅ 当周真实新闻 | ✅(偶发缺失,见注意事项) | 2 次 | \~5s | | gemini-3.1-pro-preview | ✅ 当周真实新闻,深思考后精准检索 | ✅ | 1 次 | \~45s | 选型建议:**对延迟敏感、高频调用选 gemini-3.1-flash-lite(约 5 秒);追求检索广度和答案质量选 gemini-3.5-flash**(多查询交叉验证,thinking 消耗大、延迟高,见计费一节)。 ## 快速上手 ### cURL ```bash theme={null} curl "https://api.apiyi.com/v1beta/models/gemini-3.5-flash:generateContent" \ -H "content-type: application/json" \ -H "x-goog-api-key: 你的APIYI_KEY" \ -d '{ "contents": [{"parts": [{"text": "过去一周 AI 领域有什么重要新闻?请搜索后列出 3 条并附来源网址。"}]}], "generationConfig": {"maxOutputTokens": 4096}, "tools": [{"google_search": {}}] }' ``` ### Python(google-genai SDK) ```python theme={null} from google import genai from google.genai import types client = genai.Client( api_key="你的APIYI_KEY", # 默认分组即可 http_options={"base_url": "https://api.apiyi.com"}, # 注意:不带 /v1 ) resp = client.models.generate_content( model="gemini-3.5-flash", contents="过去一周 AI 领域有什么重要新闻?请搜索后列出 3 条并附来源网址。", config=types.GenerateContentConfig( tools=[types.Tool(google_search=types.GoogleSearch())], max_output_tokens=4096, ), ) # 1) 最终回答文本 print(resp.text) # 2) 联网证据:实际执行的搜索词与来源 gm = resp.candidates[0].grounding_metadata if gm: print("搜索词:", gm.web_search_queries) for chunk in gm.grounding_chunks or []: print("来源:", chunk.web.title, chunk.web.uri) else: print("⚠️ 本次未触发联网搜索") ``` ### 怎么确认搜索真的执行了 成功联网时,响应 `candidates[0].groundingMetadata` 包含以下字段;**没有这些字段就是没搜**: | 字段 | 含义 | | - | - | | `webSearchQueries` | 模型实际执行的搜索词数组(数组长度 = 搜索次数) | | `groundingChunks` | 检索到的来源(URI + 标题) | | `groundingSupports` | 回答正文片段与来源的对应关系(startIndex/endIndex) | | `searchEntryPoint` | 渲染 Google 搜索建议所需的 HTML/CSS | 对照参考:同一问题不带工具时,模型一致回答"知识截止 2025年1月,无法提供最新信息";带工具后准确给出训练截止之后发生的真实事件。 ## 计费说明(重要) 联网搜索**会收取工具调用费用**,由两部分组成: | 项目 | 价格 | 说明 | | - | - | - | | **工具调用费** | **\$14 / 1000 次**(\$0.014/次) | **工具名:`google_search`**;按实际执行的搜索次数计费,即 `groundingMetadata.webSearchQueries` 数组长度——一次提问可能触发多次搜索(实测 pro-preview 1 次、flash-lite 2 次、3.5-flash 4–7 次) | | **模型 token 费** | 按模型标准价 | 与 OpenAI 联网不同,**检索内容不注入 input token**(实测联网前后 promptTokenCount 几乎不变,31–43 token);大头是 **thinking + output token**(3.5-flash 一次深度联网问答消耗 thoughts 3500–4900 token,按 output 价计费) | 单次联网问答总开销参考(搜索费 + token 费):flash-lite ≈ \$0.03;3.5-flash ≈ \$0.08–0.16;3.1-pro-preview ≈ \$0.06。如需控制成本,可在提示词中约束搜索行为(如"最多搜索 2 次"),或选用搜索次数少的模型。 **可能享受免费减免**:Gemini API 官方为联网搜索提供一定免费配额(Gemini 3 系列每月 5,000 条提示免费,超出后按 \$14/1K 次计)。上游命中免费配额时,该次搜索费可获减免(实测出现过整次未扣搜索费的情况);有扣费则按上表价格正常计费。实际以控制台计费明细为准。 ## 注意事项 1. **必须走原生端点**:OpenAI 兼容模式的所有搜索声明都被静默忽略且不报错。用 OpenAI SDK 的项目改用 google-genai SDK(`base_url` 设为 `https://api.apiyi.com`,不带 `/v1`)即可。 2. **以 groundingMetadata 为准判断联网**:实测 flash-lite 偶发(4 次中 1 次)不返回 groundingMetadata。严格场景请校验该字段存在性,缺失时重试。 3. **思考型模型给足 `maxOutputTokens`**(建议 ≥4096):3.5-flash / 3.1-pro-preview 联网时 thinking 消耗 1900–4900 token,上限过小会截断回答。 4. 工具声明 `{"google_search": {}}` 与 camelCase `{"googleSearch": {}}` 均可用;旧版 `google_search_retrieval` 是 Gemini 1.5 时代的工具,当前模型一律用 `google_search`。 5. 联网搜索可与 URL Context 等工具组合使用(Google 官方文档:`ai.google.dev/gemini-api/docs/google-search`)。 ## FAQ **Q:怎么确认这次回答真的联网了?** A:检查 `candidates[0].groundingMetadata` 是否存在、`webSearchQueries` 是否非空、`groundingChunks` 是否包含来源 URI。只有正文、没有这些字段的,是模型凭训练数据回答。 **Q:需要换分组或特殊 KEY 吗?** A:不需要。Gemini 系列模型使用默认分组的 KEY 即可直接调用联网搜索(与 OpenAI 联网搜索一致;区别于 Claude 原生搜索需要 ClaudeOfficial 内测分组)。 **Q:搜索次数怎么看、不同模型差异大吗?** A:数 `groundingMetadata.webSearchQueries` 数组长度。同一问题实测差异很大:pro-preview 1 次、flash-lite 2 次、3.5-flash 4–7 次。 **Q:支持哪些模型?** A:gemini-3.5-flash、gemini-3.1-flash-lite、gemini-3.1-pro-preview 已实测验证。其他 Gemini 2.5+ 模型理论上同样支持 `google_search` 工具,使用前建议按上面 FAQ 的方法做一次验证。 ## 相关文档 google-genai SDK 配置、流式输出、thinking 控制 自定义工具调用,可与联网搜索组合使用 # 图片编辑 API 参考 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2-all/image-edit api-reference/gpt-image-2-all-edit-openapi.yaml POST /v1/images/edits gpt-image-2-all 图片编辑 API 参考与在线调试 — 上传参考图 + 指令进行单图改图或多图融合 右侧的交互式 Playground 支持直接上传本地图片。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),选择图片与填入 `prompt`、`model` 后一键发送即可。 **场景说明**:本页用于「基于一张或多张参考图改图 / 融合生成」。请求为 `multipart/form-data` 格式。如需纯文本生成图片,请使用 [文生图接口](/api-capabilities/gpt-image-2-all/text-to-image)。 **🖥️ 浏览器 Playground 限制(默认 b64\_json 模式)** 本端点**默认 `response_format: "b64_json"`**,响应会包含数 MB 的 base64 字符串,浏览器 Playground **可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法显示这么长的 base64。 **推荐做法**: * 只想在 Playground 里看图:**显式传 `"response_format": "url"`**,响应是单条 R2 链接,浏览器渲染正常。 * 想要 base64 或要传超大参考图:**复制下方"代码示例"到本地运行**,代码会自动处理上传与解码。 **📎 多图融合顺序有意义** `image` 字段可重复传入多张参考图,**顺序将作为 prompt 中「图1/图2/图3」的引用依据**。建议在 prompt 中显式指代,例如: > 把图1的人物放进图2的场景,参考图3的画风 推荐单张 **≤ 10MB**,格式 `png` / `jpg` / `webp`,过大的图可能触发网关限制。 **🎯 保形改图小技巧**:本端点的输出尺寸**跟随 prompt 里点名要修改的那张图的比例**——多图场景下**不一定是第一张**。 例如 prompt 写"**修改图2**,把图2 的衣服和帽子改成图1 里的样子",那图2 是 1:1,则输出也是 1:1(即便图1 是横版 16:9)。 对换装、加帽子、修图等保形场景特别好用。**`size` 字段在本模型不生效**(传入静默忽略,要严格锁尺寸请用 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/image-edit));prompt 没明确指代时由模型自行判断。 ## 代码示例 ### Python **单图编辑**: ```python theme={null} import requests API_KEY = "sk-your-api-key" with open("photo.png", "rb") as f: response = requests.post( "https://api.apiyi.com/v1/images/edits", headers={"Authorization": f"Bearer {API_KEY}"}, data={ "model": "gpt-image-2.5-all", "prompt": "把背景换成海边黄昏", "response_format": "url" }, files=[ ("image", ("photo.png", f, "image/png")) ], timeout=300 # 保守值,吸收长尾 + 图片上传/下载耗时 ).json() print(response["data"][0]["url"]) ``` **多图融合**: ```python theme={null} import requests with open("ref1.png", "rb") as f1, \ open("ref2.png", "rb") as f2, \ open("ref3.png", "rb") as f3: response = requests.post( "https://api.apiyi.com/v1/images/edits", headers={"Authorization": "Bearer sk-your-api-key"}, data={ "model": "gpt-image-2.5-all", "prompt": "把图1的人物放进图2的场景,参考图3的画风", "response_format": "b64_json" }, files=[ ("image", ("ref1.png", f1, "image/png")), ("image", ("ref2.png", f2, "image/png")), ("image", ("ref3.png", f3, "image/png")) ], timeout=300 # 保守值,吸收长尾 + 图片上传/下载耗时 ).json() # 实测(2026-07)b64_json 为纯 base64(无 data: 前缀);历史版本曾含前缀,做个检测最稳 import base64 b64 = response["data"][0]["b64_json"] if b64.startswith("data:"): b64 = b64.split(",", 1)[1] with open("edited.png", "wb") as f: f.write(base64.b64decode(b64)) ``` ### cURL **单图编辑**: ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/images/edits" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=gpt-image-2.5-all" \ -F "prompt=把背景换成海边黄昏" \ -F "response_format=url" \ -F "image=@./photo.png" ``` **多图融合**: ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/images/edits" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=gpt-image-2.5-all" \ -F "prompt=把图1的人物放进图2的场景,参考图3的画风" \ -F "response_format=b64_json" \ -F "image=@./ref1.png" \ -F "image=@./ref2.png" \ -F "image=@./ref3.png" ``` ### Node.js(原生 fetch + FormData) ```javascript theme={null} import fs from 'node:fs'; import { Agent, setGlobalDispatcher } from 'undici'; // 图片编辑要上传参考图、又要等 MB 级响应体,而 undici 默认建连超时只有 10 秒, // 且不受下面 AbortSignal.timeout 控制(两者是不同层),必须单独放宽 setGlobalDispatcher(new Agent({ connect: { timeout: 30_000 }, headersTimeout: 300_000, bodyTimeout: 300_000, })); const form = new FormData(); form.append('model', 'gpt-image-2.5-all'); form.append('prompt', '把背景换成太空'); form.append('response_format', 'url'); form.append( 'image', new Blob([fs.readFileSync('./photo.png')]), 'photo.png' ); const resp = await fetch('https://api.apiyi.com/v1/images/edits', { method: 'POST', signal: AbortSignal.timeout(300_000), // 总超时,与上面三个是不同层 headers: { 'Authorization': 'Bearer sk-your-api-key' }, body: form }); const data = await resp.json(); console.log(data.data[0].url); ``` 上面的 `undici` 需要单独安装(`npm i undici`)——它虽然是内置 `fetch` 的底层实现,但没有以模块名对外暴露;装出来的这份调用 `setGlobalDispatcher` 会同时影响内置 `fetch`。 `maxRetries`、`connectTimeout` 与「总超时」分属不同层,配错层是 Node 侧最常见的踩坑;连接被重置、`UND_ERR_CONNECT_TIMEOUT`、挂代理导致大响应体收不全等情况,排查方法见[图片 API 连接中断排查](/api-capabilities/image-connection-drops)。 ### 浏览器 JavaScript(File 对象) ```javascript theme={null} // const files = document.getElementById('fileInput').files; const form = new FormData(); form.append('model', 'gpt-image-2.5-all'); form.append('prompt', '把这几张图融合成一张海报'); form.append('response_format', 'url'); for (const f of files) form.append('image', f); const resp = await fetch('https://api.apiyi.com/v1/images/edits', { method: 'POST', headers: { 'Authorization': 'Bearer sk-your-api-key' }, body: form }); const { data } = await resp.json(); document.getElementById('result').src = data[0].url; ``` ## 参数说明速查 | 字段 | 类型 | 必填 | 说明 | | - | - | - | - | | `model` | text | 是 | `gpt-image-2.5-all` 或 `gpt-image-2-all`(同价同行为) | | `prompt` | text | 是 | 改图/融合的自然语言描述 | | `image` | file | 是 | 参考图,可重复多次(数组字段) | | `size` | text | 否 | **本字段不生效,传入会被静默忽略**。输出尺寸**跟随 prompt 里点名要修改的那张图的比例**(多图下不一定是第一张)。要严格锁尺寸请用 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/image-edit) | | `response_format` | text | 否 | `b64_json`(默认)或 `url` | **多轮迭代**:把上一次的输出图片作为下一次的 `image` 输入,配合新的编辑指令,可逐步精调画面效果。 ## 响应格式 与文生图接口一致:**`data[0]` 中只会出现 `url` 或 `b64_json` 之一**(取决于 `response_format`),不会两者都返回。本端点**默认返回 `b64_json`**。 **`b64_json` 模式**(默认): ```json theme={null} { "data": [ { "b64_json": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." } ], "created": 1778037127, "usage": { "input_tokens": 98, "output_tokens": 1185, "total_tokens": 1283 } } ``` **`url` 模式**(需显式 `"response_format": "url"`): ```json theme={null} { "data": [ { "url": "https://r2cdn.copilotbase.com/r2cdn2/0e82148a-bec0-4b42-bbca-117c6b42581b.png" } ], "created": 1778037331, "usage": { "input_tokens": 30, "output_tokens": 2074, "total_tokens": 2104 } } ``` 2026-07 实测 `b64_json` 字段为**纯 base64(不含 `data:` 前缀)**,需解码或自行拼接前缀后使用;**历史版本曾直接带前缀**。请先做 `startsWith('data:')` 检测再处理,兼容两种形态。 # GPT-Image-2.5-All 生图/编辑 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2-all/overview GPT 图像生成 ChatGPT 网页版官逆模型 gpt-image-2-all,$0.025/张按次计费,约 30–60 秒出图,支持文生图、多图融合编辑、自然语言改图,文字还原度高、中文提示词友好。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 ## 概述 **gpt-image-2.5-all** 与 **gpt-image-2-all** 是 API易 平台的 **GPT 图像生成官逆模型**(逆向 ChatGPT 网页版)。ChatGPT 网页版已整体升级到 Images 2.5,所以 `gpt-image-2-all` 现在出的就是 2.5 的图;新增的 `gpt-image-2.5-all` 只是把这次升级表达在模型名上,**两个名字同价、同行为、可互换**,新项目直接用 `gpt-image-2.5-all`。以 **\$0.025/张** 的极具竞争力的按次计费定价(2026-10-10 起由 \$0.03 降价),**约 30–60 秒出图**,支持 **文生图 / 单图编辑 / 多图融合 / 自然语言改图**,文字还原度高、原生支持中文提示词。 **🎨 核心亮点**:官逆通道稳定、定价统一每张 \$0.025,无需关心 size/quality/n 等参数,尺寸与风格全部写进 prompt 即可——最适合"开箱即用"的图像生成场景。统一使用 OpenAI Images API 标准端点:`/v1/images/generations`(文生图)与 `/v1/images/edits`(图片编辑)。 **需要锁定输出尺寸或 4K?** 请改用姐妹模型 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/overview)——调用方式与本模型完全一致,仅多一个 `size` 字段。 `/v1/images/generations`,输入文本提示词生成图片。 `/v1/images/edits`,multipart 上传参考图 + 编辑/融合指令。 ## 让 AI Agent 帮你接入 帮我在当前项目里接入 / 排查 gpt-image-2-all 的「文生图 + 图片编辑」。 先读文档再动手:抓 [https://docs.apiyi.com/api-capabilities/gpt-image-2-all/overview.md](https://docs.apiyi.com/api-capabilities/gpt-image-2-all/overview.md) 拿到本页纯文本版;需要更细的参数说明时,text-to-image 和 image-edit 两页同样在地址后加 `.md` 即可。 接入要求: 1. 超时:客户端 timeout 提到 360 秒兜底。图片接口是同步调用,没有任务 ID,客户端一断连结果就丢了、但这次请求照样计费,所以宁可多等也不要过早超时。反向代理、网关、Serverless 执行上限这些中间层也要一起放宽,任何一层小于生成时间都会掐断请求。 2. 返回渲染:默认返回 base64(`b64_json`,不带 `data:` 前缀),也可以显式传 `response_format: "url"` 拿 CDN 直链。**请显式传 `response_format`,不要依赖默认值**——历史上默认行为随分组和负载变化过。走 base64 就要能渲染展示并提供「保存到本地」;走 url 则要注意链接约 24 小时后失效,必须服务端立即下载转存。同一条 `data[]` 里只会有 `url` 和 `b64_json` 其中一个,解析时两种都要兜住。 3. 上传压缩:调 `/v1/images/edits`(multipart)前先压缩参考图——超过 1.5MB 才处理,长边等比缩到 2048px 以内(不放大小图),以质量 0.9 重编码、保持原格式;多图时合计控制在 6MB 以内,单图不要超过 10MB。某张图压缩失败就回退用原图继续,不要因为压缩失败中断整个请求。 4. 参数红线:本模型**不接受** `size` / `quality` / `n` / `aspect_ratio`,请求里一个都不要带。特别注意两点:传 `n=3` 会按 3 张扣费但仍然只返回 1 张图;OpenAI SDK 的 `client.images.generate()` 会默认带上 `size` 和 `n`,所以这里**直接发原始 HTTP 请求更稳妥**。输出尺寸靠提示词前缀控制(比如在提示词开头写「横版 16:9」),本页有验证过的「提示词到实际分辨率」对照表,照着用。 5. Key 从环境变量 `APIYI_API_KEY` 读,base\_url 用 [https://api.apiyi.com/v1,不要硬编码进代码、也不要提交进](https://api.apiyi.com/v1,不要硬编码进代码、也不要提交进) git。 6. 改完真跑一次文生图 + 一次图片编辑,把出图结果和这两次调用的花费贴给我。 | 要求 | 挡掉的坑 | | - | - | | timeout 提到 360 秒 | 主流 HTTP 客户端默认 30-60 秒超时,会在服务端还在正常出图时掐断请求,而**断连的请求照常计费**。详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices) | | 显式传 `response_format` | 默认值历史上变过;不显式指定就得同时兜住 `url` 和 `b64_json` 两种形态 | | 不传 `n` | 传 `n=3` 会按 3 张扣费,但仍然只返回 1 张图 | | 不传 `size` / `quality` | 本模型不接受这两个参数,尺寸靠提示词前缀控制 | | 上传前压缩 | 手机原图动辄 4-5MB,base64 编码后还会再膨胀约 33%。压缩标准见 [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution) | ## 核心特性 统一按次计费 \$0.025/张,无分辨率阶梯,出图成本可预测 图内中英文、招牌、海报文字还原稳定,适合信息图与营销物料 原生理解中文描述,无需翻译即可获得高质量输出 支持多张参考图同时输入,prompt 中可用「图1/图2/图3」指代 约 30–60 秒出图,比 `gpt-image-2-vip` 和官转 `gpt-image-2` 都更快 显式传 `response_format: "url"` 返回 R2 CDN 链接,全球低延迟下载 支持通过对话描述直接改图,无需蒙版,可多轮迭代 兼容 OpenAI Images API 标准端点 `/images/generations`、`/images/edits` ## 模型定价 | 模型名 | 计费方式 | 价格 | 输出 | | - | - | - | - | | `gpt-image-2-all` | 按次计费 | **\$0.025 / 张** | 单次返回 1 张图片 | | `gpt-image-2.5-all` | 按次计费 | **\$0.025 / 张**(与 -all 相同) | 单次返回 1 张图片,模型为 GPT-Image 2.5 | **计费说明**: * 统一定价,不区分分辨率、质量或提示词长度 * 失败请求不计费(如鉴权失败、参数校验失败) * 如需生成 N 张,客户端并行调用 N 次 模型价格与官网对齐,且可能随官网调整;上表仅供参考,具体以顶部导航「模型价格」栏目为准:[模型价格](/models/index)。 **2026-10-10 起降价:\$0.03 → \$0.025/张**(`gpt-image-2-all` 与 `gpt-image-2.5-all` 同步)。 * **为什么降**:-all 系列本身出图质量高,而且比 -vip 系列更稳定——-vip 受原厂风控影响波动较大(我们一直在维护、持续加资源,-vip 照常可用)。降价是希望更多常规出图场景直接用上 -all。 * **唯一短板**:只出 1K–1.5K,无法出 2K / 4K。常规出图完全够用;要锁尺寸或 4K 再选 -vip / 官转。 * **还能更低**:\$0.025 是折扣前价格,叠加充值活动加赠 10%–20%,实际成本更低。 **姐妹模型**:[`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/overview)(Adobe 官逆线路(Firefly))\$0.03/张,支持 30 档常见 size(含 4K),调用方式与本模型完全一致——需要锁定输出尺寸或 4K 时切过去即可。 ## 分组介绍 `gpt-image-2-all` **放在 `Default` 默认分组**即可,不需要额外切分组。逆向通道目前供给稳定,不存在像官转那样需要"企业分组"过渡的场景。 | 模型 | 分组 | 备注 | | - | - | - | | `gpt-image-2-all` | `Default` | 逆向 ChatGPT 官网线路,统一 \$0.025/张,约 30–60 秒出图 | | `gpt-image-2.5-all` | `Default` | 同上线路的 GPT-Image 2.5 版本,统一 \$0.025/张,分组与 -all 相同 | | `gpt-image-2-all` | `image2_OSS` | **1x 倍率(不加价)**,确定性 URL 输出——默认分组资源紧张时不会降级为 base64 | ### 需要确定性 URL 输出 → 切到 `image2_OSS` 分组 `gpt-image-2-all`(及 `gpt-image-2-vip`)在默认分组下**实测(2026-07)不传 `response_format` 时返回 `b64_json`**;显式传 `response_format: "url"` 可拿到图片 URL。但默认分组的输出格式**不做承诺**——历史上曾默认返回 `url`、资源紧张时降级为 `b64_json`,行为随负载与渠道版本变化过。 如果你的业务**强依赖 URL 输出**(直接把 URL 落库、前端按 URL 渲染、不接受 base64),请把令牌分组切到 **`image2_OSS`**——这是专为 **URL 输出确定性**设计的分组,**1x 倍率(不加价)**,对 `gpt-image-2-all` 和 `gpt-image-2-vip` 两个官逆模型都生效,保证响应稳定输出图片 URL,不会降级为 base64。 令牌创建界面:计费模式「按量优先」,选择分组 image2_OSS(1x 倍率),支持输出为图片 URL 的分组,适合 gpt-image-2-all 与 gpt-image-2-vip **进阶玩法(同时使用 `gpt-image-2-vip` 与官转 `gpt-image-2`)**:如果你的令牌同时覆盖逆向两模与官转 `gpt-image-2`,可以在令牌的「分组优先级」里这样配—— * **第一优先级**:`image2Enterprise`(1.2x 企业分组,官转专用稳定通道) * **默认(兜底)**:`Default`(逆向两模都在这里,按模型路由) 这样官转 `gpt-image-2` 走企业分组保稳,逆向两模仍走默认分组——一把令牌覆盖三种模型,互不干扰。 📖 关于 `image2Enterprise` 企业分组:[/live/2026-04/image2-enterprise](/live/2026-04/image2-enterprise) ## 技术规格 | 维度 | 参数 | | - | - | | **模型名** | `gpt-image-2-all` | | **渠道性质** | 官方逆向(逆向 ChatGPT 官网) | | **定价** | \$0.025 / 张,按次计费 | | **出图速度** | 约 30–60 秒 | | **输出分辨率** | 无显式 size 参数,由模型自适应(建议在 prompt 中描述) | | **默认响应格式** | `b64_json`(纯 base64,**无 `data:` 前缀**,2026-07 实测;建议显式传 `response_format`) | | **可选响应格式** | `url`(R2 CDN 加速链接,**默认 1 天有效期**,需显式传 `response_format: "url"`) | | **中文提示词** | ✅ 原生支持 | | **支持能力** | 文生图、单图编辑、多图融合、自然语言改图 | 本模型为**自适应输出尺寸**,不等同于官转 `gpt-image-2` API。需要严格锁定输出尺寸或 4K 时,请改用 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/overview)(Adobe 官逆线路(Firefly),30 档 size 含 4K);需要官方完全一致字段时,请使用 [`gpt-image-2`](/api-capabilities/gpt-image-2/overview)(官转)。 **⏰ 图片 URL 有效期:默认 1 天** `url` 模式响应的 `url` 字段是 R2 CDN 加速链接,**有效期约 24 小时**,过期后访问会 404。对于需要长期保存的图片(商品图、用户作品、历史记录等),请**在生成后尽快转存到自己的对象存储 / CDN / 数据库**。 两种常见做法: * **服务端立即下载并入库**:收到响应后用 `requests` / `fetch` 把图片拉回来存到 S3 / OSS / R2 / 本地磁盘 * **改用 `b64_json` 响应格式**:直接拿到 base64 图片数据,省一次跨域下载,适合前端直接渲染或写入文件 ## 端点一览 | 端点 | 用途 | Content-Type | 适用场景 | | - | - | - | - | | `POST /v1/images/generations` | 文生图 | `application/json` | OpenAI Images API 标准格式,方便同一套代码同时调用官转与官逆 | | `POST /v1/images/edits` | 图片编辑(单图 / 多图) | `multipart/form-data` | OpenAI Images API 标准格式,方便同一套代码同时调用官转与官逆 | **统一使用 OpenAI Images API**(`/v1/images/generations` + `/v1/images/edits`),理由有二: 1. **更稳定**:上游对 Images API 通道的资源供给更充足,调用成功率更高 2. **兼容官转,便于切换**:与官转 [`gpt-image-2`](/api-capabilities/gpt-image-2/overview) 调用方式、`size` 等参数完全兼容——遇到官逆通道风控异常时,**只需更换 `model` 名即可切换**,业务代码零改动 另有对话式端点(`/v1/chat/completions`,不主推),见下方「常见问题」。 **域名选择**:`api.apiyi.com` 为主域名,也可使用 `b.apiyi.com` 等平台提供的其他网关域名,响应行为一致。 **想用 `size` 参数锁定输出尺寸?** 改用姐妹模型 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/overview)——端点完全一致,仅多一个 `size` 字段(30 档常见 size,含 4K)。 ## 尺寸与比例控制(写进 prompt) `gpt-image-2-all` 没有 `size` 参数,尺寸通过 prompt 描述。如果你需要严格锁定输出尺寸(电商主图、海报模板、4K 壁纸等),请改用 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/overview)。 ### 经过验证的「提示词 → 实际分辨率」对照表 下表是实测复现稳定的 8 种写法。把第一列的描述放在 prompt **最前面**,就能拿到第二列的分辨率(输出全部在 1.5K 像素量级): | 提示词包含描述 | 实测分辨率 | 文件体积 | 实际比例 | | - | - | - | - | | `横版 16:9` | 1672 × 941 | \~1.9 MB | 16:9 | | `竖屏 9:16` | 941 × 1672 | \~2.1 MB | 9:16 | | `4:3` | 1448 × 1086 | \~2.3 MB | 4:3 | | `3:4` | 1086 × 1448 | \~2.5 MB | 3:4 | | `3:2 尺寸` | 1536 × 1024 | \~2.9 MB | 3:2 | | `2:3 尺寸` | 1024 × 1536 | \~3.0 MB | 2:3 | | `2:5 竖屏` | 793 × 1983 | \~1.9 MB | 2:5 | | `5:2 横屏` | 1983 × 793 | \~1.9 MB | 5:2 | **使用须知**: * 输出统一在 \~1.5K 量级(最长边 1500–2000 px),**不是真正的"任意分辨率"**——所有 8 种写法都属于"约 1.5K"水平的模型上限 * prompt 里**只**包含表中描述词时复现度最高;和其它构图词混写会发生偏离 ### 风格化补充写法(无固定分辨率) 下面这些写法没有稳定的实测分辨率,仅作风格修饰用,搭配上表使用: | 需求 | 写法(仅风格参考,不保证分辨率) | | - | - | | 方形 | `1024×1024 方图` / `1:1 方形构图` | | 超宽横幅 | `横幅 21:9 超宽银幕` | | 画幅风格 | `电影画幅` / `手机海报` / `方形构图` | **技巧**:在 prompt **开头** 描述尺寸/构图,模型遵循度更高。 ### 把这张表暴露给终端用户 虽然 `gpt-image-2-all` 没有 `size` 参数,但接入方完全可以在前端加一个「尺寸 / 比例」下拉框,给用户**和官方 size 一样的体验**: * 每个选项的 value 直接用上表的 prompt 前缀(如 `横版 16:9`) * label 同时展示**预期分辨率**(如 `横版 16:9 (1672×941)`),让用户对最终输出有数 * 后端把选中的 prefix 拼到用户原始 prompt 的最前面再发给 API ```js theme={null} const SIZE_OPTIONS = [ { label: "横版 16:9 (1672×941)", prefix: "横版 16:9" }, { label: "竖屏 9:16 (941×1672)", prefix: "竖屏 9:16" }, { label: "4:3 (1448×1086)", prefix: "4:3" }, { label: "3:4 (1086×1448)", prefix: "3:4" }, { label: "3:2 (1536×1024)", prefix: "3:2 尺寸" }, { label: "2:3 (1024×1536)", prefix: "2:3 尺寸" }, { label: "2:5 竖屏 (793×1983)", prefix: "2:5 竖屏" }, { label: "5:2 横屏 (1983×793)", prefix: "5:2 横屏" }, ]; const finalPrompt = `${selected.prefix},${userPrompt}`; ``` 底层模型仍是**自适应**——返回分辨率允许 ±少量像素偏差,请不要在 UI 上向用户承诺"像素级精确"。**需要严格锁死输出尺寸**(电商主图、海报模板、4K 壁纸等),请改用姐妹模型 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/overview)——同价、同套调用代码,仅多一个 `size` 字段。 ## 最佳实践 上传给接口的每张图先压到 **1.5MB 以内**(JPEG 质量 80-90 / 分辨率适当下调),多图融合时也按这个标准逐张控制。偶发的服务端错误大多就是图片体积过大触发的,压一下请求成功率和出图速度都会明显改善。**输出分辨率由 prompt 的画幅描述决定,与输入图体积无关**——压小输入只会提速、不会损画质。提示词里光写 `4K` / `8K` 这类词也不会真给你高清;要稳定拿到大图请用上文「经过验证的『提示词 → 实际分辨率』对照表」里的写法。 把比例、分辨率、画幅描述放在提示词最前面,模型遵循度更高。 该模型文字还原度是主要卖点,招牌、海报、信息图都可直接写中英文文字。 重复传入的同名 `image` 字段顺序有意义,在 prompt 里可用「图1/图2/图3」明确指代。 Web 应用直接渲染用 `b64_json`,服务端中转存储用 `url`。 典型 30–60s,但叠加图片上传 / 下载、官逆高峰长尾后实际耗时波动较大。**保守按 300 秒配**,避免大量误超时。 `gpt-image-2-all` 不接受 `size`、`n`、`quality`、`aspect_ratio`,传入可能触发参数校验错误——请把它们从请求里去掉。需要传 `size` 时改用 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/overview)。 ## 错误码与重试 | 状态码 | 含义 | 建议 | | - | - | - | | `401` | 令牌无效 | 检查 Bearer Token | | `429` | 限流/额度不足 | 指数退避重试 | | `5xx` | 网关/后端临时错误 | 重试 1–2 次 | | 超时 | 官逆高峰偶发 + 图片上传/下载长尾 | 客户端设置 **≥ 300s 超时**(保守值) | **建议客户端**: * 请求超时 **300 秒** 起步(保守值;典型 30–60s,但叠加图片上传 / 下载、官逆高峰长尾后波动大,按 120s 配置容易误超时) * 对 5xx 与超时做 **指数退避重试**(建议 2–3 次) * 记录响应头 `request-id` 方便排查 ## 常见问题 两者都是逆向通道,**调用方式完全一致**;价格上 -all 为 \$0.025/次、-vip 为 \$0.03/次,差异主要在 `size`、出图速度与稳定性: * **不需要严格控尺寸、追求出图速度与稳定性** → `gpt-image-2-all`(约 30–60s 出图,尺寸写进 prompt,输出 1K–1.5K)。 * **要锁死输出尺寸或要 4K** → [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/overview)(约 90–150s 出图,30 档常见 size 含 4K)。 * **需要画质参数 `quality` 或 OpenAI 官方完全对齐字段** → 改用官方版 [`gpt-image-2`](/api-capabilities/gpt-image-2/overview)。 本模型单次返回 1 张。如需 N 张,请客户端并行调用 N 次。每张独立按 \$0.025 计费。 **不支持。** 本模型单次只返回 1 张图,请通过**重复调用 / 并发调用**的方式生成多张。 ⚠️ **重要**:如果在请求里传入 `n=3`,**计费会按 0.025 × 3 = \$0.075 扣费**,但**实际上仍然只返回 1 张图**。请务必把 `n` 字段从请求里去掉,避免被多扣费。 官逆是**同步对话式返回**,结果分两种情况,**计费规则不同**: **1) 返回 5xx 状态码 → 不计费** 上游内容策略明确拦截时会返回类似: ```json theme={null} { "error": { "message": "没有按照预期生成图片,请重新调整提示词后重试(traceid: 0672821c6951af183dbf847130caaf16)", "localized_message": "Unknown error", "type": "invalid_request_error", "param": "", "code": null } } ``` 这种"明确报错"调用**不计费**,引导用户调整提示词重试即可。 **2) 返回 200 状态码(模型用文字软拒绝)→ 计费** 模型在对话里软拒绝、用文字回复(例如「我不能做到这个需求」「抱歉,这个请求涉及……」),从协议层看就是一次正常的对话返回,**这种情况会被计费**。官逆目前没有办法在协议层提前识别"这一段是拒绝文字而不是图片"。 **为什么不能直接对软拒绝免单?** 强行对所有"软拒绝"都不计费意味着平台要为每次失败承担上游成本;更关键的是,**频繁触发上游内容安全会让供应方账号更容易被封号**——这部分供给侧的硬成本也无法完全规避。 **给接入方的建议** * ✅ **前置内容过滤 / 风险提示**:在前端或接入层先做一道关键词与场景过滤(如真实姓名、版权角色、敏感题材),并在 UI 上提示"涉及名人/版权题材时上游限制较严,可能失败也会计费",能显著降低误扣率。 * ✅ **C 端产品月度补发**:理解 C 端产品无法完全控制用户输入。如果你的月用量较大(**月消费 \$1000+ 起**),可以**按月汇总日志**(短耗时调用通常对应软拒绝)联系客服一次性人工补发,无须逐条申诉。 📖 相关:[官逆 500 多为内容违规](/live/2026-04/gpt-image-2-all-500-content-policy) **先检测再处理**。2026-07 实测返回的 `b64_json` 为纯 base64(不带前缀),需要解码写文件或自行拼接前缀后再渲染;但**历史版本曾直接带前缀**。请在代码里做 `startsWith('data:')` 检测:有前缀直接用作 `img src`,无前缀先解码,避免双重拼接或带前缀解码产出损坏的图片。 自适应模型对尺寸描述是"参考"不是"强制"。提升遵循度的写法:把尺寸/画幅词放在 prompt 最前面,并配合画幅风格词(如 `电影画幅`、`手机海报`、`方形构图`)。 具体能稳定复现的写法和对应分辨率,参见上文「尺寸与比例控制 → 经过验证的『提示词 → 实际分辨率』对照表」。 **强烈建议压**。单张输入图压到 **1.5MB 以内**(JPEG 质量 80-90 / 分辨率适当下调):偶发的服务端错误大多就是图片体积过大触发的,压一下请求成功率和出图速度都会明显改善。注意 1.5MB 是**推荐上限**(追求稳定性与速度),上面 FAQ 写的 10MB 是网关硬上限。 **别担心压输入会损画质**——本模型输出分辨率由 prompt 的画幅描述决定,跟你上传图的体积没关系。压小输入只会提速、不会损画质。 **提示词光写 `4K` / `8K` 这类词也不会真给你高清**——这些只是修饰词,模型不会因此提分辨率。要稳定拿到大图,请用上文「经过验证的『提示词 → 实际分辨率』对照表」里验证过的写法(如 `电影画幅`、`手机海报`、`方形构图`);需要严格锁尺寸或 4K 请改用 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/overview)(30 档 size,含 4K,同价 \$0.03/张)。 推荐 **单张 ≤ 10MB**,格式 `png` / `jpg` / `webp`。过大的图可能触发网关限制。多图融合时每张都需满足此限制。 `url` 模式响应的 `url` 字段是 **R2 CDN 加速链接,有效期约 1 天(24 小时)**,过期后会 404。 **强烈建议**:生成后尽快把图片 **转存到自己的对象存储(S3 / OSS / R2)、CDN 或数据库**,不要长期直接引用本服务返回的 URL。 **两种推荐做法**: * **服务端中转**:收到响应后立即 `requests.get(url)` 把图片拉回来存到你自己的存储,把你自己的 URL 返回给前端; * **改用 b64\_json**:请求时加 `"response_format": "b64_json"`,直接拿到 base64 图片数据,少一次跨域下载,适合前端直接渲染或写入文件。 如果只是短期展示(如单次会话预览),可以直接用 R2 URL 无需转存。 本模型为一次性出图,不支持 stream 输出。如果对响应延迟敏感,建议客户端显示"生成中"进度提示,并合理配置 **300s 超时**(保守值)。 可以。把 `base_url` 指向 `https://api.apiyi.com/v1`,`api_key` 设为 API易 令牌即可。但 `client.images.generate()` 方法默认带 `size`/`n` 参数——本模型不接受这两个参数,建议直接用 `requests` / `fetch` 发原生 HTTP 请求调用 `/v1/images/generations` 与 `/v1/images/edits`。 本模型原生支持中文,两者效果接近。对中文特有的场景(如中式书法、传统节日元素)中文表达更自然。 可以,端点仍然可用,但**不再主推**——推荐统一使用 `/v1/images/generations` 与 `/v1/images/edits`(更稳定、与官转 `gpt-image-2` 同套代码)。 对话方式仅适合两类场景:多轮迭代改图、需要直接传在线图片 URL。注意出图意图不够明确时可能返回纯文字而不是图片(可在提示词开头加「生成图片:」前缀强化)。 详细参数见 [对话式调用说明](/api-capabilities/gpt-image-2-all/chat-completions)。 ## 相关文档 * [⚖️ 官转 vs 官逆 对比](/api-capabilities/gpt-image-2/vs-gpt-image-2-all) - 与官方版 `gpt-image-2` 选型对照表 * [文生图 Playground](/api-capabilities/gpt-image-2-all/text-to-image) - `/v1/images/generations` 兼容端点 * [图片编辑 Playground](/api-capabilities/gpt-image-2-all/image-edit) - `/v1/images/edits` 多图融合与改图 * [GPT-Image-2-VIP(同价、支持 size 和 4K)](/api-capabilities/gpt-image-2-vip/overview) - 同价位姐妹模型,30 档常见 size(含 4K),调用方式与本模型完全一致 * [GPT-Image-2 官方版(按 token 计费)](/api-capabilities/gpt-image-2/overview) - 需要 `quality` 参数 / mask 局部重绘 / OpenAI 官方对齐字段时的选择 * [GPT-Image 系列总览](/api-capabilities/gpt-image-series) - 官方 GPT-Image 系列对比 * [社区贡献:Luck GPT-Image 2 ComfyUI 节点](/scenarios/ecosystem/luckgpt2-comfyui) - 在 ComfyUI 中一键调用 `gpt-image-2-all`(支持 chat\_completions / images\_api 双端点) * [社区贡献:APIYI GPT-Image 2 Skills](/scenarios/ecosystem/apiyi-gpt-image-skills) - 在 Codex CLI / Cursor / Gemini CLI 等 AI 编程工具中一句话调用 * [API 使用手册](/api-manual) - 通用调用规范 gpt-image-2-all 属于官逆通道,行为对齐但定价/能力与官方版本不完全一致。如需官方直连版本,请参考 [GPT-Image-1.5](/api-capabilities/gpt-image-1-5)。 # GPT-Image-2.5-All Agent 技能 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2-all/skills 在 Agent 里用 gpt-image-2.5-all(官逆·ChatGPT 线,最快、flat $0.025/张;gpt-image-2-all 同调用)出图——与 gpt-image-2、gpt-image-2-vip 共用同一个 gpt-image-2 技能,只需把 --model 设为 gpt-image-2.5-all。 **gpt-image-2-all 不需要单独的技能**。它和 gpt-image-2(官转)、gpt-image-2-vip 共用同一个 **gpt-image-2 系列技能**——三者都走同一套 OpenAI Images API,只是 `--model` 不同。完整的安装、`SKILL.md` 与脚本,见 [**GPT-Image-2 系列 Agent 技能**](/api-capabilities/gpt-image-2/skills)。 ## 这个模型适合什么 官逆 ChatGPT 线,约 30–60 秒,是三条通道里最快的。 flat \$0.025/张,不分尺寸/画质档,走量友好。 支持文生图与最多 16 张的多图融合(不支持掩码局部重绘)。 ## 在技能里怎么用 装好 [gpt-image-2 系列技能](/api-capabilities/gpt-image-2/skills) 后,把 `--model` 设为 `gpt-image-2.5-all`(或 `gpt-image-2-all`,两者同价同行为)即可: ```bash theme={null} python3 gpt-image-2/scripts/gpt_image.py "扁平插画风节日海报,竖版 2:3" -o poster.png --model gpt-image-2.5-all ``` 想把它设成默认通道(不必每次加 `--model`),在 `gpt-image-2/.env` 里加一行: ```bash theme={null} APIYI_IMAGE_MODEL=gpt-image-2.5-all ``` **红线**:gpt-image-2-all **不接受 `size` / `quality` / `n`**—— * 尺寸/比例请**写进提示词**(如「竖版 2:3」「16:9 横幅」),传 `size` 会被忽略或报错; * 传 `n>1` **仍只出 1 张但会按张数扣费**。 技能脚本已对本模型自动「不传 size/quality/n」,所以正常用 `--model gpt-image-2-all` 不会踩坑;只有你绕过脚本手搓请求时需要注意。需要锁尺寸/4K 请改用 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/skills),需要画质档/掩码请用官转 `gpt-image-2`。 ## 相关文档 * [GPT-Image-2 系列 Agent 技能(主页 · 完整脚本)](/api-capabilities/gpt-image-2/skills) * [GPT-Image-2-VIP Agent 技能](/api-capabilities/gpt-image-2-vip/skills) * [GPT-Image-2-All 图片生成总览](/api-capabilities/gpt-image-2-all/overview) # 文生图 API 参考 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2-all/text-to-image api-reference/gpt-image-2-all-generate-openapi.yaml POST /v1/images/generations gpt-image-2-all 文生图 API 参考与在线调试 — 输入文本描述生成图片,$0.025/张 右侧的交互式 Playground 支持直接在线调试。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),输入 prompt 后一键发送即可。 **场景说明**:本页用于「文本生成图片」。只需输入提示词即可,无需上传任何图片。如需根据现有图片做编辑或融合,请使用 [图片编辑接口](/api-capabilities/gpt-image-2-all/image-edit)。 **🖥️ 浏览器 Playground 限制(默认 b64\_json 模式)** 本端点**默认 `response_format: "b64_json"`**,响应会包含数 MB 的 base64 字符串,浏览器 Playground **可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法显示这么长的 base64。 **推荐做法**: * 只想在 Playground 里看图:**显式传 `"response_format": "url"`**,响应是单条 R2 链接,浏览器渲染正常。 * 想要 base64:**复制下方"代码示例"到本地运行**,代码会自动解码并把图片保存为本地文件。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 **⚠️ 参数支持情况** * **`size`**:**字段不生效**——传 `auto` 或具体尺寸都不会报错,但会被服务端**静默忽略**。最终尺寸完全由 prompt 决定: * prompt 写了尺寸/比例(如"横版 16:9")→ 模型会遵循 prompt * prompt 没写尺寸 → 同一 prompt 多次调用会**抽卡式**出现多样化尺寸,适合看多种构图变体 * 需要严格锁尺寸请改用 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/text-to-image)(支持 `auto` + 30 档锁定) * **`n` / `quality` / `aspect_ratio`**:❌ 不接受,传入可能触发参数校验错误。 尺寸与比例请直接写进 `prompt`,例如: * `横版 16:9 电影画幅,黄昏时的海边老灯塔` * `竖版 9:16 手机壁纸,赛博朋克城市雨夜` * `1024×1024 方形 LOGO,极简猫咪线条` **建议把尺寸描述放在 prompt 最前面**,模型遵循度更高。 ## 代码示例 ### Python ```python theme={null} import requests API_KEY = "sk-your-api-key" response = requests.post( "https://api.apiyi.com/v1/images/generations", headers={"Authorization": f"Bearer {API_KEY}"}, json={ "model": "gpt-image-2.5-all", "prompt": "横版 16:9 日落海边的老灯塔,电影画幅,写实风格", "response_format": "url" }, timeout=300 # 保守值,吸收长尾 + 图片下载耗时 ).json() image_url = response["data"][0]["url"] print(image_url) ``` **b64\_json 模式(返回 base64 图片数据)**: ```python theme={null} import base64 import requests response = requests.post( "https://api.apiyi.com/v1/images/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "gpt-image-2.5-all", "prompt": "1024x1024 方形 LOGO,极简猫咪线条", "response_format": "b64_json" }, timeout=300 # 保守值,吸收长尾 + 图片下载耗时 ).json() # 实测(2026-07)b64_json 为纯 base64(无 data: 前缀);历史版本曾含前缀,做个检测最稳 b64 = response["data"][0]["b64_json"] if b64.startswith("data:"): b64 = b64.split(",", 1)[1] with open("output.png", "wb") as f: f.write(base64.b64decode(b64)) ``` ### cURL ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/images/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2.5-all", "prompt": "横版 16:9 电影画幅,黄昏时的海边老灯塔", "response_format": "url" }' ``` ### Node.js ```javascript theme={null} import { Agent, setGlobalDispatcher } from "undici"; import { writeFile } from "node:fs/promises"; const API_KEY = "sk-your-api-key"; // 图片接口是「长时间静默 + MB 级响应体」,而 undici 的默认建连超时只有 10 秒, // 且不受下面 AbortSignal.timeout 控制(两者是不同层),必须单独放宽 setGlobalDispatcher(new Agent({ connect: { timeout: 30_000 }, headersTimeout: 300_000, bodyTimeout: 300_000, })); const response = await fetch( "https://api.apiyi.com/v1/images/generations", { method: "POST", signal: AbortSignal.timeout(300_000), // 总超时,与上面三个是不同层 headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: "gpt-image-2.5-all", prompt: "1024x1024 方形 LOGO,极简猫咪线条", response_format: "b64_json" }) } ); const data = await response.json(); // 实测为纯 base64(无 data: 前缀);历史版本曾自带前缀,做个检测最稳 let b64 = data.data[0].b64_json; if (b64.startsWith("data:")) b64 = b64.slice(b64.indexOf(",") + 1); await writeFile("output.png", Buffer.from(b64, "base64")); ``` 上面的 `undici` 需要单独安装(`npm i undici`)——它虽然是内置 `fetch` 的底层实现,但没有以模块名对外暴露;装出来的这份调用 `setGlobalDispatcher` 会同时影响内置 `fetch`。 `maxRetries`、`connectTimeout` 与「总超时」分属不同层,配错层是 Node 侧最常见的踩坑;连接被重置、`UND_ERR_CONNECT_TIMEOUT`、挂代理导致大响应体收不全等情况,排查方法见[图片 API 连接中断排查](/api-capabilities/image-connection-drops)。 ### 浏览器 JavaScript(Fetch) ```javascript theme={null} const resp = await fetch('https://api.apiyi.com/v1/images/generations', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer sk-your-api-key' }, body: JSON.stringify({ model: 'gpt-image-2.5-all', prompt: '竖版 9:16 手机壁纸,赛博朋克城市雨夜', response_format: 'b64_json' }) }); const { data } = await resp.json(); let b64 = data[0].b64_json; if (!b64.startsWith('data:')) b64 = `data:image/png;base64,${b64}`; document.getElementById('result').src = b64; ``` ## 参数说明速查 | 参数 | 类型 | 必填 | 说明 | | - | - | - | - | | `model` | string | 是 | `gpt-image-2.5-all` 或 `gpt-image-2-all`(同价同行为) | | `prompt` | string | 是 | 提示词,尺寸/比例/风格请写在此处(**唯一控制尺寸的方式**) | | `response_format` | string | 否 | `b64_json`(默认,纯 base64)或 `url`(返回 R2 CDN 链接);建议始终显式传值 | 本模型**不支持 `size` 入参**——即便传入也会被静默忽略,不会报错;如需用 `size` 字段锁定 30 档尺寸,请改用 [`gpt-image-2-vip`](/api-capabilities/gpt-image-2-vip/text-to-image)。 详细的参数约束和可选值请查看右侧 Playground 中的字段说明,`response_format` 字段支持下拉选择。 ## 响应格式 **`data[0]` 中只会出现 `url` 或 `b64_json` 之一**(取决于 `response_format`),不会两者都返回。本端点**默认返回 `b64_json`**。 **`b64_json` 模式**(默认): ```json theme={null} { "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA..." } ], "created": 1778037127, "usage": { "input_tokens": 98, "output_tokens": 1185, "total_tokens": 1283 } } ``` **`url` 模式**(需显式 `"response_format": "url"`,R2 CDN 全球加速): ```json theme={null} { "data": [ { "url": "https://r2cdn.copilotbase.com/r2cdn2/0e82148a-bec0-4b42-bbca-117c6b42581b.png" } ], "created": 1778037331, "usage": { "input_tokens": 30, "output_tokens": 2074, "total_tokens": 2104 } } ``` **兼容性提示**:2026-07 实测 `b64_json` 字段为**纯 base64(不含 `data:` 前缀)**,需解码写文件或自行拼接前缀后渲染;**历史版本曾直接带前缀**。请在代码里做 `startsWith('data:')` 检测后再处理,兼容两种形态。 # 图片编辑 API 参考 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2-vip/image-edit api-reference/gpt-image-2-vip-edit-openapi.yaml POST /v1/images/edits gpt-image-2-vip 图片编辑 API 参考与在线调试 — 上传参考图 + 指令进行单图改图或多图融合,可用 size 锁定输出尺寸 右侧的交互式 Playground 支持直接上传本地图片。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),选择图片与填入 `prompt`、`model`、`size` 后一键发送即可。 **场景说明**:本页用于「基于一张或多张参考图改图 / 融合生成」。请求为 `multipart/form-data` 格式。如需纯文本生成图片,请使用 [文生图接口](/api-capabilities/gpt-image-2-vip/text-to-image)。 **与 `gpt-image-2-all` 的区别**:调用结构完全一致,只多一个 `size` 字段;不需要锁尺寸、追求出图速度时改用 [`gpt-image-2-all`](/api-capabilities/gpt-image-2-all/image-edit)。 **🖥️ 浏览器 Playground 限制** 本端点**默认返回 base64 字符串(`b64_json`)**,体积可达数 MB,浏览器 Playground **可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法显示这么长的 base64。 **推荐做法**:想要 base64 或要传超大参考图时,**复制下方"代码示例"到本地运行**,代码会自动处理上传与解码。 **📎 多图融合顺序有意义** `image` 字段可重复传入多张参考图,**顺序将作为 prompt 中「图1/图2/图3」的引用依据**。建议在 prompt 中显式指代,例如: > 把图1的人物放进图2的场景,参考图3的画风 推荐单张 **≤ 10MB**,格式 `png` / `jpg` / `webp`,过大的图可能触发网关限制。 **🎯 保形改图小技巧**:传 `size=auto`(或不传 `size`)时,输出会**跟随 prompt 里点名要修改的那张图的尺寸比例**——多图场景下**不一定是第一张**。 例如 prompt 写"**修改图2**,把图2 的衣服和帽子改成图1 里的样子",那图2 是 1:1,则输出也是 1:1(即便图1 是横版 16:9)。 对换装、加帽子、修图等保形场景特别好用。如果 prompt 没明确指代要改哪张,模型会自行判断;需要切换到 30 档锁定尺寸时再显式传 `size`。 **⚠️ 关键参数说明** * **`size`**:**改图建议传 `auto`(或不传)**——模型会**根据 prompt 里点名要修改的那张图的尺寸比例**输出,多图场景下不一定是第一张。例如 prompt 写"修改图2,把图2 的衣服换成图1 的样子",输出比例就跟图2 一致;prompt 没明确指代时由模型自行判断。需要强制改变尺寸时,从 30 档常见尺寸里选;写法用半角小写 `x`,如 `2048x1360`、`3840x2160`。完整表见 [概览页](/api-capabilities/gpt-image-2-vip/overview#支持的-size30-档完整对照表)。 * **`quality`**:可选,实测生效(属渠道行为不承诺,以实际返回为准)。2.5 两款(`gpt-image-2.5-vip` = `gpt-image-2.5-sunburst-vip`、`gpt-image-2.5-flare-vip`)六档全开:`auto` / `low` / `medium` / `high` / `xhigh` / `max`;`gpt-image-2-vip` 只到 `high`,传 `xhigh` / `max` 会被拒。同名档位不等价:2.5 的 `high` 只相当于 `gpt-image-2-vip` 的 `medium`,2.5 的 `max` 才等于它的 `high`。按次 \$0.03 不随档位变,但档位越高耗时越长。 * **`n`**:❌ 不接受,单次仅返回 1 张图。 * **`response_format`**:不传默认返回 base64(纯 base64 无前缀,2026-07 实测);传 `"url"` 可返回图片 URL。**强依赖 URL 输出**的业务建议把令牌分组切到 `image2_OSS`,稳定输出 URL、不降级为 base64。 ## 代码示例 ### Python **单图编辑**: ```python theme={null} import requests API_KEY = "sk-your-api-key" with open("photo.png", "rb") as f: response = requests.post( "https://api.apiyi.com/v1/images/edits", headers={"Authorization": f"Bearer {API_KEY}"}, data={ "model": "gpt-image-2.5-vip", "prompt": "把背景换成海边黄昏", "size": "2048x1360" # 3:2 2K Recommended }, files=[ ("image", ("photo.png", f, "image/png")) ], timeout=300 # 保守值,吸收长尾 + 图片上传/下载耗时 ).json() print(response["data"][0]["url"]) ``` **多图融合**: ```python theme={null} import requests with open("ref1.png", "rb") as f1, \ open("ref2.png", "rb") as f2, \ open("ref3.png", "rb") as f3: response = requests.post( "https://api.apiyi.com/v1/images/edits", headers={"Authorization": "Bearer sk-your-api-key"}, data={ "model": "gpt-image-2.5-vip", "prompt": "把图1的人物放进图2的场景,参考图3的画风", "size": "2048x2048" # 1:1 2K Recommended }, files=[ ("image", ("ref1.png", f1, "image/png")), ("image", ("ref2.png", f2, "image/png")), ("image", ("ref3.png", f3, "image/png")) ], timeout=300 # 保守值,吸收长尾 + 图片上传/下载耗时 ).json() # 实测(2026-07)b64_json 为纯 base64(无 data: 前缀);历史版本曾含前缀,做个检测最稳 import base64 b64 = response["data"][0]["b64_json"] if b64.startswith("data:"): b64 = b64.split(",", 1)[1] with open("edited.png", "wb") as f: f.write(base64.b64decode(b64)) ``` ### cURL **单图编辑**: ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/images/edits" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=gpt-image-2.5-vip" \ -F "prompt=把背景换成海边黄昏" \ -F "size=2048x1360" \ -F "image=@./photo.png" ``` **多图融合**: ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/images/edits" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=gpt-image-2.5-vip" \ -F "prompt=把图1的人物放进图2的场景,参考图3的画风" \ -F "size=2048x2048" \ -F "image=@./ref1.png" \ -F "image=@./ref2.png" \ -F "image=@./ref3.png" ``` ### Node.js(原生 fetch + FormData) ```javascript theme={null} import fs from 'node:fs'; const form = new FormData(); form.append('model', 'gpt-image-2.5-vip'); form.append('prompt', '把背景换成太空'); form.append('size', '2048x1360'); form.append( 'image', new Blob([fs.readFileSync('./photo.png')]), 'photo.png' ); const resp = await fetch('https://api.apiyi.com/v1/images/edits', { method: 'POST', headers: { 'Authorization': 'Bearer sk-your-api-key' }, body: form }); const data = await resp.json(); console.log(data.data[0].url); ``` ### 浏览器 JavaScript(File 对象) ```javascript theme={null} // const files = document.getElementById('fileInput').files; const form = new FormData(); form.append('model', 'gpt-image-2.5-vip'); form.append('prompt', '把这几张图融合成一张海报'); form.append('size', '2048x2048'); for (const f of files) form.append('image', f); const resp = await fetch('https://api.apiyi.com/v1/images/edits', { method: 'POST', headers: { 'Authorization': 'Bearer sk-your-api-key' }, body: form }); const { data } = await resp.json(); document.getElementById('result').src = data[0].url; ``` ## 参数说明速查 | 字段 | 类型 | 必填 | 说明 | | - | - | - | - | | `model` | text | 是 | `gpt-image-2.5-vip`(= `gpt-image-2.5-sunburst-vip`)/ `gpt-image-2.5-flare-vip` / 上一代 `gpt-image-2-vip`,同价同调用;`mask` 三款都只做整图重绘 | | `prompt` | text | 是 | 改图/融合的自然语言描述 | | `image` | file | 是 | 参考图,可重复多次(数组字段) | | `size` | text | 否 | 输出尺寸:`auto`(默认,**跟随 prompt 里点名要修改的那张图的比例**,多图下不一定是第一张)或 30 档之一;写法 `宽x高`(半角小写 `x`) | **多轮迭代**:把上一次的输出图片作为下一次的 `image` 输入,配合新的编辑指令,可逐步精调画面效果。每一轮都可以独立指定 `size`。 ## 响应格式 与文生图接口一致:**默认返回 base64**(`data[0].b64_json`,纯 base64 无前缀,2026-07 实测)。如需 **图片 URL**:显式传 `response_format: "url"` 即可;**强依赖 URL 输出**的业务建议把令牌分组切到 **`image2_OSS`**,稳定输出 URL、不降级为 base64。`data[0]` 中只会出现 `url` 或 `b64_json` 之一,不会两者都返回。 **`b64_json` 模式**(默认): ```json theme={null} { "data": [ { "b64_json": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." } ], "created": 1778037127, "usage": { "input_tokens": 98, "output_tokens": 1185, "total_tokens": 1283 } } ``` **`url` 模式**(显式传 `response_format: "url"`;强依赖 URL 建议用 `image2_OSS` 分组): ```json theme={null} { "data": [ { "url": "https://r2cdn.copilotbase.com/r2cdn2/0e82148a-bec0-4b42-bbca-117c6b42581b.png" } ], "created": 1778037331, "usage": { "input_tokens": 30, "output_tokens": 2074, "total_tokens": 2104 } } ``` 2026-07 实测 `b64_json` 字段为**纯 base64(不含 `data:` 前缀)**,需解码或自行拼接前缀后使用;**历史版本曾直接带前缀**。请先做 `startsWith('data:')` 检测再处理,兼容两种形态。 ## 相关资源 30 档 size 完整对照表、定价、技术规格 `/v1/images/generations` 兼容端点 不需要锁尺寸时调用方式一致,出图更快 # GPT-Image-2.5-VIP 生图/编辑 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2-vip/overview GPT 图像生成 Adobe 官逆模型(Firefly 线路) gpt-image-2-vip 及 2.5 姐妹模型 gpt-image-2.5-flare-vip / gpt-image-2.5-sunburst-vip,$0.03/张统一价,支持 10 比例 × 3 档分辨率(1K/2K/4K)共 30 档常见 size,调用方式与 gpt-image-2-all 一致;约 90–150 秒出图,适合需要稳定锁定输出尺寸的场景。 **`size` 参数已恢复可用**(2026-07-22 更新):显式传入 `size` 即可正常锁定输出尺寸,本页 30 档对照表恢复生效。注意:`size` 仅在 `/v1/images/generations` 与 `/v1/images/edits` 端点生效,**`/v1/chat/completions` 聊天补全端点不支持 `size` 参数**,对话方式出图无法锁尺寸。最新状态以 [实时动态](/live) 栏目为准。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 ## 概述 **gpt-image-2.5-vip**(别名,指向 `gpt-image-2.5-sunburst-vip`)、**gpt-image-2.5-flare-vip** 与上一代 **gpt-image-2-vip** 是 API易 平台的 **GPT 图像生成 Adobe 官逆模型(Firefly 线路)**——高质量的 GPT-Image 2.5 逆向资源,不是低质量的超分。定价 **\$0.03/张**,与 [`gpt-image-2.5-all`](/api-capabilities/gpt-image-2-all/overview)(\$0.025/张)**调用方式完全一致**,最大区别是 **支持 `size` 参数**——覆盖 **10 比例 × 3 分辨率档(1K Fast / 2K Recommended / 4K Detail)共 30 档常见尺寸**,含 4K。 **🎨 核心定位**:当你需要**锁定输出尺寸**(电商主图、海报模板、视频封面、4K 壁纸等)时使用 `gpt-image-2.5-vip`。请求体里只需把 `model` 改成 `gpt-image-2.5-vip`、加一个 `size` 字段,其它代码与 `gpt-image-2.5-all` **完全相同**。 **三款 -vip 一家人**:`gpt-image-2.5-vip`(别名,指向 `gpt-image-2.5-sunburst-vip`)、`gpt-image-2.5-flare-vip` 与上一代 `gpt-image-2-vip` 走同一条 Adobe 官逆线路,**价格(\$0.03/张按次)、分组(`Default` / `image2_OSS` / `svip`)、端点、调用方式完全相同**,换 `model` 即可互切。flare-vip 更快、画面偏软;sunburst-vip 画质与编辑精度更高,人眼与 `gpt-image-2-vip` 接近。三款的参数边界与实测差异见下方「三款 -vip 对比」一节。 `/v1/images/generations`,输入文本提示词 + `size` 生成指定尺寸图片。 `/v1/images/edits`,multipart 上传参考图 + 编辑/融合指令。 ## 让 AI Agent 帮你接入 帮我在当前项目里接入 / 排查 gpt-image-2.5-vip 的「文生图 + 图片编辑」(`gpt-image-2.5-flare-vip` / `gpt-image-2-vip` 同价同调用,模型名做成配置项方便切换)。 先读文档再动手:抓 [https://docs.apiyi.com/api-capabilities/gpt-image-2-vip/overview.md](https://docs.apiyi.com/api-capabilities/gpt-image-2-vip/overview.md) 拿到本页纯文本版;需要更细的参数说明时,text-to-image 和 image-edit 两页同样在地址后加 `.md` 即可。 接入要求: 1. 超时:客户端 timeout 提到 360 秒兜底。图片接口是同步调用,没有任务 ID,客户端一断连结果就丢了、但这次请求照样计费,所以宁可多等也不要过早超时。反向代理、网关、Serverless 执行上限这些中间层也要一起放宽,任何一层小于生成时间都会掐断请求。 2. 返回渲染:默认返回 base64(`b64_json`,不带 `data:` 前缀),也可以显式传 `response_format: "url"` 拿 CDN 直链。**请显式传 `response_format`,不要依赖默认值**——历史上默认行为随分组和负载变化过。走 base64 就要能渲染展示并提供「保存到本地」;走 url 则要注意链接约 24 小时后失效,必须服务端立即下载转存。同一条 `data[]` 里只会有 `url` 和 `b64_json` 其中一个,解析时两种都要兜住。 3. 上传压缩:调 `/v1/images/edits`(multipart)前先压缩参考图——超过 1.5MB 才处理,长边等比缩到 2048px 以内(不放大小图),以质量 0.9 重编码、保持原格式;多图时合计控制在 6MB 以内,单图不要超过 10MB。某张图压缩失败就回退用原图继续,不要因为压缩失败中断整个请求。 4. 尺寸参数:`size` 只能填本页列出的 **30 个档位之一**(1K / 2K / 4K 各 10 档)或者 `auto`,写法是小写半角 `x`,比如 `1536x1024`——不要写成全角乘号、也不要大写 `X`,表外的尺寸不会报错但会被对齐改写(16 倍数对齐、过小抬到最小边),拿到的尺寸可能与请求不同。请把这 30 档做成前台的分辨率下拉,别让用户自由填。`quality` 可以传 `low` / `medium` / `high` / `xhigh` / `max`(`xhigh` / `max` 只有 2.5 两款接受,`gpt-image-2-vip` 传了会被拒;不要依赖 `auto`;注意 2.5 的 `high` 只相当于 `gpt-image-2-vip` 的 `medium`、2.5 的 `max` 才等于它的 `high`);`n` / `aspect_ratio` 不要带(传 `n=3` 会按 3 张扣费但仍然只返回 1 张图);`mask` 不做精确局部重绘,需要就走官转。如果你需要控制 `size`,就必须走 `/v1/images/generations` 或 `/v1/images/edits`,`/v1/chat/completions` 端点不支持 `size`。 5. Key 从环境变量 `APIYI_API_KEY` 读,base\_url 用 [https://api.apiyi.com/v1,不要硬编码进代码、也不要提交进](https://api.apiyi.com/v1,不要硬编码进代码、也不要提交进) git。 6. 限流:RPM 500 以内不用做并发限制。遇到 429 先看 error.message:参数被拒就改参数;上游饱和就退避重试,或查看 [https://docs.apiyi.com/live。](https://docs.apiyi.com/live。) 7. 改完真跑一次文生图 + 一次图片编辑,把出图结果和这两次调用的花费贴给我。 | 要求 | 挡掉的坑 | | - | - | | timeout 提到 360 秒 | 主流 HTTP 客户端默认 30-60 秒超时,会在服务端还在正常出图时掐断请求,而**断连的请求照常计费**。详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices) | | 显式传 `response_format` | 默认值历史上变过;不显式指定就得同时兜住 `url` 和 `b64_json` 两种形态 | | `size` 锁定 30 档 | 表外尺寸会被改写成别的尺寸;写成全角乘号或大写 `X` 不合法 | | `quality` 按模型选档、不传 `n` | 2.5 两款六档全开,`gpt-image-2-vip` 传 `xhigh` / `max` 会被拒;2.5 的 `high` 只等于 `gpt-image-2-vip` 的 `medium`;传 `n=3` 会按 3 张扣费但只返回 1 张图 | | 429 先看 `error.message` | 参数被拒和上游饱和共用 429,处理方式不同 | | 上传前压缩 | 手机原图动辄 4-5MB,base64 编码后还会再膨胀约 33%。压缩标准见 [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution) | ## 与 `gpt-image-2-all` 的关键差异 `gpt-image-2-vip` 与 [`gpt-image-2-all`](/api-capabilities/gpt-image-2-all/overview) 同属逆向通道、同套调用代码(-all \$0.025/张、-vip \$0.03/张)。**互相映射**——把同一段请求里的 `model` 字段从一个换成另一个,行为整体一致,差异如下: | 维度 | `gpt-image-2-all` | `gpt-image-2-vip` | | - | - | - | | **渠道** | 逆向 ChatGPT 官网 | Adobe 官逆线路(Firefly) | | **价格** | \$0.025 / 张 | \$0.03 / 张(所有 size 统一价) | | **`size` 参数** | ❌ 不接受(写进 prompt) | ✅ 30 档 size,含 4K | | **4K(如 `3840x2160`)** | ❌ | ✅ 4K Detail 档 | | **出图速度** | 约 30–60 秒 | 约 90–150 秒(与官转 `gpt-image-2` 持平) | | **`quality` 参数** | ❌ 不接受 | ✅ 实测生效、不承诺:2.5 两款 `auto` / `low` / `medium` / `high` / `xhigh` / `max` 六档全开(`xhigh` / `max` 2026-09-10 放开),`gpt-image-2-vip` 到 `high` | | **支持端点** | `/images/generations` + `/images/edits` | 同左(一模一样) | | **响应格式** | `b64_json`(默认,纯 base64 无前缀)/ `url`(显式传参) | 同左 | | **适合场景** | 提示词驱动、对尺寸不敏感 | 需要稳定指定输出尺寸(含 4K) | **一句话决策**:**不需要严格控尺寸、追求出图速度** → `gpt-image-2-all`;**要锁死输出尺寸或要 4K** → `gpt-image-2-vip`;**需要画质参数 `quality` 或 OpenAI 官方完全对齐的字段** → 改用官方版 [`gpt-image-2`](/api-capabilities/gpt-image-2/overview)。 ## 三款 -vip 对比(2026-09-09 实测) 同渠道、同令牌、只换模型名的三臂对比 253 次,加串行边界用例 26 次。三款契约逐格一致,差异只在下表标出的几行;`quality` / 透明背景这两项 `gpt-image-2-vip` 历史上不接受、本次实测已接受,**属渠道行为、不作承诺,以实际返回为准**。 | 项 | `gpt-image-2-vip` | `gpt-image-2.5-flare-vip` | `gpt-image-2.5-sunburst-vip`(别名 `gpt-image-2.5-vip`) | | - | - | - | - | | 定位 | 上一代 | 2.5 速度优先 | 2.5 画质与编辑精度优先 | | 价格 / 分组 | \$0.03/张按次;`Default` / `image2_OSS` / `svip` | 同左 | 同左 | | `quality` | ✅ `auto` / `low` / `medium` / `high`;`xhigh` / `max` ❌ | ✅ 六档全开:`auto` / `low` / `medium` / `high` / `xhigh` / `max`(`xhigh` / `max` 2026-09-10 复测放开) | 同 flare-vip | | 输出 token(2048×1152) | low 157 / medium 1,413 / high 5,650 | low 157 / medium 367 / high 1,413 / xhigh 2,511 / max 5,650 | 同 flare-vip | | 档位怎么对齐 | — | 2.5 的 `high` = 2-vip 的 `medium`,2.5 的 `max` = 2-vip 的 `high`;三款最高档 token 相同 | 同 flare-vip | | 缺省 `size` | 2048×2048 | **1024×1536 竖版** | 2048×2048 | | 30 档 `size` | 30/30 逐像素命中 | 30/30 | 30/30 | | 表外 `size` | 不报错:16 倍数原样,非 16 倍数对齐(`1920x1080` → 1920×1088),过小抬到最小边(`512x512` → 816×816) | 同左 | 同左 | | `mask` | ⚠️ 接受但整图重绘,真实照片三次内外改动比 ≈1 | 同左 | 同左 | | `background: "transparent"` | ✅ 返回带 alpha 的 PNG | ✅ 单物体提示词四角全透明 | ✅ 同 flare-vip | | `output_format: "jpeg"` | 静默忽略,仍回 PNG | 同左 | 同左 | | `n` | 只回 1 张,不要传 | 同左 | 同左 | | `response_format: "url"` | ✅ | ✅ | ✅ | | 编辑端点 | 单图沿用输入尺寸,多图以第一张定画幅 | 同左 | 同左 | | 画质(人眼,6 组同尺寸提示词) | 基准 | 偏软、装饰细节少 | 与 2-vip 接近 | | 1024² 耗时(串行) | 约 90–150 秒 | 22~138 秒 | 37~120 秒 | **怎么选**:日常文生图默认 `gpt-image-2.5-vip`;要最快选 `gpt-image-2.5-flare-vip`;要最高 token 档 2.5 传 `max`、`gpt-image-2-vip` 传 `high`(token 量相同)。三款都不做精确 mask,这一项走官转 [GPT-Image-2.5 / 2](/api-capabilities/gpt-image-2/overview)。默认尺寸随上游变动过,要锁尺寸一律显式传 `size`。 ## 核心特性 `size` 字段直接接受 30 档常见尺寸,电商主图、海报模板、4K 壁纸都能严格输出 4K Detail 档支持 2880×2880 / 3840×2160 / 3840×1632 等,适合大尺寸交付物 1K / 2K / 4K 所有档位统一 \$0.03/张,4K 不额外加价 请求结构、字段、响应字段与 `gpt-image-2-all` 完全一致,可秒级切换模型名 图内中英文、招牌、海报文字还原稳定,适合信息图与营销物料 原生理解中文描述,无需翻译即可获得高质量输出 支持通过对话描述直接改图,无需蒙版,可多轮迭代 兼容 OpenAI Images API 标准端点 `/images/generations`、`/images/edits` ## 模型定价 | 模型名 | 计费方式 | 价格 | 输出 | | - | - | - | - | | `gpt-image-2-vip` | 按次计费 | **\$0.03 / 张** | 单次返回 1 张图片,`size` 字段锁定输出尺寸 | | `gpt-image-2.5-flare-vip` | 按次计费 | **\$0.03 / 张** | GPT-Image 2.5 速度优先版,参数面同 `-vip`(`quality` 六档全开) | | `gpt-image-2.5-sunburst-vip`(别名 `gpt-image-2.5-vip`) | 按次计费 | **\$0.03 / 张** | GPT-Image 2.5 画质与编辑精度优先版,参数面同 flare-vip | **计费说明**: * **所有 30 档 size 统一定价 \$0.03/张**——4K Detail 不加价 * 失败请求不计费(如鉴权失败、参数校验失败) * 如需生成 N 张,客户端并行调用 N 次 ## 分组介绍 `gpt-image-2-vip` 放在 **`Default` 默认分组** 即可,不需要额外切分组。逆向通道目前供给稳定,不存在像官转那样需要"企业分组"过渡的场景。 | 模型 | 分组 | 备注 | | - | - | - | | `gpt-image-2-vip` | `Default` | Adobe 官逆线路(Firefly),统一 \$0.03/张,约 90–150 秒出图 | | `gpt-image-2-vip` | `image2_OSS` | **1x 倍率(不加价)**,确定性 URL 输出——默认分组资源紧张时不会降级为 base64 | | `gpt-image-2.5-flare-vip` / `gpt-image-2.5-sunburst-vip` / `gpt-image-2.5-vip` | `Default` / `image2_OSS` / `svip` | 与 `gpt-image-2-vip` 分组完全相同 | ### 需要确定性 URL 输出 → 切到 `image2_OSS` 分组 `gpt-image-2-vip`(及 `gpt-image-2-all`)在默认分组下**实测(2026-07)不传 `response_format` 时返回 `b64_json`**;显式传 `response_format: "url"` 可拿到图片 URL。但默认分组的输出格式**不做承诺**——历史上曾默认返回 `url`、资源紧张时降级为 `b64_json`,行为随负载与渠道版本变化过。 如果你的业务**强依赖 URL 输出**(直接把 URL 落库、前端按 URL 渲染、不接受 base64),请把令牌分组切到 **`image2_OSS`**——这是专为 **URL 输出确定性**设计的分组,**1x 倍率(不加价)**,对 `gpt-image-2-vip` 和 `gpt-image-2-all` 两个官逆模型都生效,保证响应稳定输出图片 URL,不会降级为 base64。 令牌创建界面:计费模式「按量优先」,选择分组 image2_OSS(1x 倍率),支持输出为图片 URL 的分组,适合 gpt-image-2-all 与 gpt-image-2-vip **进阶玩法(同时使用 `gpt-image-2-all` 与官转 `gpt-image-2`)**:如果你的令牌同时覆盖逆向两模与官转 `gpt-image-2`,可以在令牌的「分组优先级」里这样配—— * **第一优先级**:`image2Enterprise`(1.2x 企业分组,官转专用稳定通道) * **默认(兜底)**:`Default`(逆向两模都在这里,按模型路由) 这样官转 `gpt-image-2` 走企业分组保稳,逆向两模仍走默认分组——一把令牌覆盖三种模型,互不干扰。 📖 关于 `image2Enterprise` 企业分组:[/live/2026-04/image2-enterprise](/live/2026-04/image2-enterprise) ## 技术规格 | 维度 | 参数 | | - | - | | **模型名** | `gpt-image-2-vip`;2.5 版本 `gpt-image-2.5-flare-vip` / `gpt-image-2.5-sunburst-vip`(别名 `gpt-image-2.5-vip`) | | **渠道性质** | 官方逆向(Adobe 官逆线路(Firefly)) | | **定价** | \$0.03 / 张,按次计费(所有 size 统一价) | | **出图速度** | `gpt-image-2-vip` 约 **90–150 秒**;2.5 两款 1024² 串行实测 22~140 秒,波动大。RPM 500 以内无需考虑并发。若遇到 `429`,先看 `error.message`:参数被拒(`quality must be one of…`)改参数即可;`当前分组上游负载已饱和` 属偶发,退避重试即可,或联系客服咨询模型状态、查看 [实时动态](/live) | | **`size` 参数** | ✅ 30 档:10 比例 × 3 分辨率档(1K Fast / 2K Recommended / 4K Detail) | | **4K 支持** | ✅ 4K Detail 档(如 `3840x2160` / `2880x2880`) | | **`quality` 参数** | 三款 ✅ 实测生效,属渠道行为不承诺:2.5 两款 `auto` / `low` / `medium` / `high` / `xhigh` / `max` 六档全开(`xhigh` / `max` 2026-09-10 放开),`gpt-image-2-vip` 到 `high`;同名档位 token 量 2.5 低一档(见上方对照表) | | **`mask` 参数** | ⚠️ 三款接受但不保证只改蒙版区(真实照片实测整图重绘),精确局部重绘走官转 | | **透明背景** | ✅ 三款 `background: "transparent"` 返回带 alpha 的 PNG(不承诺) | | **缺省 `size`** | `gpt-image-2-vip` / sunburst-vip 2048×2048,flare-vip 1024×1536;随上游变动,以实际返回为准 | | **`n` 参数** | ❌ 不支持,单次仅返回 1 张 | | **默认响应格式** | `b64_json`(纯 base64,**无 `data:` 前缀**,2026-07 实测;建议显式传 `response_format`) | | **可选响应格式** | `url`(R2 CDN 加速链接,**默认 1 天有效期**,需显式传 `response_format: "url"`) | | **中文提示词** | ✅ 原生支持 | | **支持能力** | 文生图、单图编辑、多图融合、自然语言改图 | **⏰ 图片 URL 有效期:默认 1 天** `url` 模式响应的 `url` 字段是 R2 CDN 加速链接,**有效期约 24 小时**,过期后访问会 404。需要长期保存的图片请**在生成后尽快转存到自己的对象存储 / CDN / 数据库**,或改用 `b64_json` 响应格式。 ## 端点一览 `gpt-image-2-vip` 与 `gpt-image-2-all` 兼容**完全相同**的两个端点。把 `model` 字段换掉、按需加上 `size` 即可: | 端点 | 用途 | Content-Type | 适用场景 | | - | - | - | - | | `POST /v1/images/generations` | 文生图 | `application/json` | OpenAI Images API 标准格式,方便同一套代码同时调用官转与官逆 | | `POST /v1/images/edits` | 图片编辑(单图 / 多图) | `multipart/form-data` | OpenAI Images API 标准格式,方便同一套代码同时调用官转与官逆 | **统一使用 OpenAI Images API**(`/v1/images/generations` + `/v1/images/edits`),理由有二: 1. **更稳定**:上游对 Images API 通道的资源供给更充足,调用成功率更高 2. **兼容官转,便于切换**:与官转 [`gpt-image-2`](/api-capabilities/gpt-image-2/overview) 调用方式、`size` 等参数完全兼容——遇到官逆通道风控异常时,**只需更换 `model` 名即可切换**,业务代码零改动 另有对话式端点(`/v1/chat/completions`,不主推),见下方「常见问题」。 **域名选择**:`api.apiyi.com` 为主域名,也可使用 `b.apiyi.com` 等平台提供的其他网关域名,响应行为一致。 ## 支持的 size(30 档完整对照表) `gpt-image-2-vip` 支持 **10 个比例 × 3 个分辨率档 = 30 档** 常见尺寸。请求体直接传 `size: "宽x高"`(半角小写 `x`)。 ### 1K Fast — 草稿与低成本试稿 | 比例 | 命名 | 像素 | | - | - | - | | 1:1 | Square | `1280x1280` | | 2:3 | Portrait | `848x1280` | | 3:2 | Photo | `1280x848` | | 3:4 | Portrait | `960x1280` | | 4:3 | Standard | `1280x960` | | 4:5 | Social | `1024x1280` | | 5:4 | Large | `1280x1024` | | 9:16 | Story | `720x1280` | | 16:9 | Wide | `1280x720` | | 21:9 | Cinema | `1280x544` | ### 2K Recommended — 默认推荐档(多数终稿) | 比例 | 命名 | 像素 | | - | - | - | | 1:1 | Square | `2048x2048` | | 2:3 | Portrait | `1360x2048` | | 3:2 | Photo | `2048x1360` | | 3:4 | Portrait | `1536x2048` | | 4:3 | Standard | `2048x1536` | | 4:5 | Social | `1632x2048` | | 5:4 | Large | `2048x1632` | | 9:16 | Story | `1152x2048` | | 16:9 | Wide | `2048x1152` | | 21:9 | Cinema | `2048x864` | ### 4K Detail — 大尺寸交付物 | 比例 | 命名 | 像素 | | - | - | - | | 1:1 | Square | `2880x2880` | | 2:3 | Portrait | `2336x3520` | | 3:2 | Photo | `3520x2336` | | 3:4 | Portrait | `2480x3312` | | 4:3 | Standard | `3312x2480` | | 4:5 | Social | `2560x3216` | | 5:4 | Large | `3216x2560` | | 9:16 | Story | `2160x3840` | | 16:9 | Wide | `3840x2160` | | 21:9 | Cinema | `3840x1632` | **30 档统一价**:所有档位都是 \$0.03/张,4K Detail 不额外加价。 **怎么选档位**: * **1K Fast**:用于草稿、缩略图、A/B 测试,省时(也不省钱,价格统一),出图最快。 * **2K Recommended**:**默认档**,覆盖大部分终稿场景(电商主图、海报、信息图)。 * **4K Detail**:印刷、大屏壁纸、视频封面、桌面 / 户外大图。 **最小调用示例**(只传 `size`;`quality` 可选,见上文): ```bash theme={null} curl "https://api.apiyi.com/v1/images/generations" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $YI_API_KEY" \ -d '{ "model": "gpt-image-2.5-vip", "prompt": "生成一张白色陶瓷马克杯放在灰色桌面上的产品图,柔和自然光,简洁背景", "size": "2048x1360" }' ``` ## 最佳实践 上传给接口的每张图先压到 **1.5MB 以内**(JPEG 质量 80-90 / 分辨率适当下调),多图融合时也按这个标准逐张控制。偶发的 `shell_api_error` / `Unknown error` 大多就是图片体积过大触发的,压一下请求成功率和出图速度都会明显改善。**输出分辨率由 `size` 字段决定,与输入图体积无关**——压小输入只会提速、不会损画质。提示词里光写 `4K` / `8K` 这类词也不会真给你 4K,画质看 `size`,不看 prompt 修饰词。 草稿用 1K Fast、终稿用 2K Recommended、印刷/大屏用 4K Detail。所有档位统一价,按需要选。 请求体写 `"size": "1536x1024"`,不是 `1536×1024`、不是大写 `X`。 三款 -vip 实测接受 `quality`(不承诺):2.5 两款六档全开(`xhigh` / `max` 2026-09-10 放开),`gpt-image-2-vip` 到 `high`,传 `xhigh` / `max` 会被拒;2.5 的 `high` 只相当于 `gpt-image-2-vip` 的 `medium`,2.5 的 `max` 才等于它的 `high`。`n` 单次仅返回 1 张图,多张请客户端并行调用。 出图典型 90–150s,叠加图片上传/下载与高峰长尾,**保守按 300s 配**,避免大量误超时。 Web 应用直接渲染用 `b64_json`,服务端中转存储用 `url`。 同一套调用代码,把 `model` 在 `gpt-image-2-all` ↔ `gpt-image-2-vip` 之间切换即可。需要锁尺寸时切到 -vip,需要更快出图时切回 -all。 ## 错误码与重试 | 状态码 | 含义 | 建议 | | - | - | - | | `400` | size 取值不在 30 档内或格式错误 | 用上表中的精确字符串 | | `401` | 令牌无效 | 检查 Bearer Token | | `429` | 参数被拒 / 上游偶发饱和 / 额度不足 | RPM 500 以内无需考虑并发。若遇到 `429`,先看 `error.message`:参数被拒(`quality must be one of…`)改参数即可;`当前分组上游负载已饱和` 属偶发,退避重试即可,或联系客服咨询模型状态、查看 [实时动态](/live) | | `500`(4K 偶发) | OpenAI 上游算力波动,4K Detail 档较易触发 | **优先降到 2K Recommended 档**重试;必须 4K 时改用官转 [`gpt-image-2`](/api-capabilities/gpt-image-2/overview) + `image2Enterprise` 企业分组 | | `5xx`(其它) | 网关/后端临时错误 | 重试 1–2 次 | | 超时 | 上游高峰 + 4K 长尾 | 客户端设置 **≥ 300s 超时**(保守值) | **建议客户端**: * 请求超时 **300 秒** 起步(保守值;典型 90–150s,但 4K Detail + 高峰长尾会更长) * 对 5xx 与超时做 **指数退避重试**(建议 2–3 次) * 记录响应头 `request-id` 方便排查 ## 常见问题 **可以,几乎完全一样。** 两个端点(`/v1/images/generations`、`/v1/images/edits`)的请求字段、响应字段、`b64_json` 前缀行为都一致。差异只有两处: 1. `model` 字段:`gpt-image-2-vip` ↔ `gpt-image-2-all` 2. `size` 字段:vip 接受 30 档常见尺寸;-all 不接受 `size`,尺寸要写进 prompt 实际工程实践:保留同一套代码,做一个 `if model == 'vip': payload['size'] = ...` 的开关即可。 `gpt-image-2-vip` 走的是 Adobe 逆向通道(Firefly),**典型 90–150 秒**,与官转 `gpt-image-2`(100–120 秒)持平,比 ChatGPT 网页线路的 `gpt-image-2-all`(约 30–60 秒)慢。如果对**响应延迟敏感**,建议优先用 `gpt-image-2-all`;只在**必须锁尺寸或 4K** 时切换到 vip。 **建议严格用表里 30 档之一**。2026-09-09 实测表外尺寸不再报错,而是被改写后出图:16 倍数的会原样保留(如 `1024x1024` / `1600x1600`),不是 16 倍数的对齐到 16(`1920x1080` → 1920×1088),过小的抬到最小边(`512x512` → 816×816)。拿到的尺寸可能与请求不同,需要严格尺寸时仍按 30 档传。 **现象**:在 4K Detail 档(如 `3840x2160` / `2880x2880`)较容易触发 `status_code: 500` 错误,上游返回 `invalid_request_error`: ```json theme={null} { "status_code": 500, "error": { "message": "An error occurred while processing your request. ... Please include the request ID xxxxxxxx in your message.", "type": "invalid_request_error", "code": null } } ``` **根因**:**OpenAI 算力波动**,与请求参数本身无关——同一段请求换 2K 档大概率就过了。逆向通道对 4K 这种大输出的负载更敏感,高峰期更明显。 **应对建议**(按性价比排序): 1. **优先改用 2K Recommended 档**(如 `2048x1360` / `2048x2048`)—— 2K 成功率显著更高,价格同样 **\$0.03/张** 2. **图生图 / 多图融合少传输入图** —— 逆向链路对多入图请求处理压力大,会进一步增加 4K 失败率;单张输入图先压到 **1.5MB 以内**也有帮助 3. **如必须稳定出 4K** —— 切换到官转模型 [`gpt-image-2`](/api-capabilities/gpt-image-2/overview) + **`image2Enterprise` 企业分组**。官转 4K 价格更高(**约 \$0.3+/张**),但稳定性显著更好,适合对 4K 交付有硬要求的场景。 📖 经验来源:[/live/2026-05/gpt-image-2-vip-4k-tips](/live/2026-05/gpt-image-2-vip-4k-tips) **强烈建议压**。单张输入图压到 **1.5MB 以内**(JPEG 质量 80-90 / 分辨率适当下调):偶发的 `shell_api_error` / `Unknown error` 大多就是图片体积过大触发的,压一下请求成功率和出图速度都会明显改善。注意 1.5MB 是**推荐上限**(追求稳定性与速度),上面 FAQ 写的 10MB 是网关硬上限。 **别担心压输入会损画质**——本模型输出分辨率由 `size` 参数决定,跟你上传图的体积没关系。压小输入只会提速、不会损画质。 **提示词里光写 `4K` / `8K` 这类词也不会真给你 4K**。如果 prompt 写 `8K 超清` 但 `size` 选 `1024x1024`,最终拿到的就是 1K 水平。**要 4K 请在 `size` 字段里指定**——30 档里 1K / 2K / 4K 同价 \$0.03/张,按需要直接选。 📖 排错来源:[/live/2026-05/gpt-image-2-vip-unknown-error](/live/2026-05/gpt-image-2-vip-unknown-error) **不加价**,4K Detail 档(`3840x2160` / `2880x2880` 等)与 1K / 2K 同价 \$0.03/张。 **不支持。** 本模型单次只返回 1 张图,请通过**重复调用 / 并发调用**的方式生成多张。 ⚠️ **重要**:如果在请求里传入 `n=3`,**计费会按 0.03 × 3 = \$0.09 扣费**,但**实际上仍然只返回 1 张图**。请务必把 `n` 字段从请求里去掉,避免被多扣费。 官逆是**同步对话式返回**,结果分两种情况,**计费规则不同**: **1) 返回 5xx 状态码 → 不计费** 上游内容策略明确拦截时会返回类似: ```json theme={null} { "error": { "message": "没有按照预期生成图片,请重新调整提示词后重试(traceid: 0672821c6951af183dbf847130caaf16)", "localized_message": "Unknown error", "type": "invalid_request_error", "param": "", "code": null } } ``` 这种"明确报错"调用**不计费**,引导用户调整提示词重试即可。 **2) 返回 200 状态码(模型用文字软拒绝)→ 计费** 模型在对话里软拒绝、用文字回复(例如「我不能做到这个需求」「抱歉,这个请求涉及……」),从协议层看就是一次正常的对话返回,**这种情况会被计费**。官逆目前没有办法在协议层提前识别"这一段是拒绝文字而不是图片"。 **为什么不能直接对软拒绝免单?** 强行对所有"软拒绝"都不计费意味着平台要为每次失败承担上游成本;更关键的是,**频繁触发上游内容安全会让供应方账号更容易被封号**——这部分供给侧的硬成本也无法完全规避。 **给接入方的建议** * ✅ **前置内容过滤 / 风险提示**:在前端或接入层先做一道关键词与场景过滤(如真实姓名、版权角色、敏感题材),并在 UI 上提示"涉及名人/版权题材时上游限制较严,可能失败也会计费",能显著降低误扣率。 * ✅ **C 端产品月度补发**:理解 C 端产品无法完全控制用户输入。如果你的月用量较大(**月消费 \$1000+ 起**),可以**按月汇总日志**(短耗时调用通常对应软拒绝)联系客服一次性人工补发,无须逐条申诉。 📖 相关:[官逆 500 多为内容违规](/live/2026-04/gpt-image-2-all-500-content-policy) **先检测再处理**。2026-07 实测返回的 `b64_json` 为纯 base64(不带前缀),需要解码写文件或自行拼接前缀后再渲染;但**历史版本曾直接带前缀**。请在代码里做 `startsWith('data:')` 检测:有前缀直接用作 `img src`,无前缀先解码,避免双重拼接或带前缀解码产出损坏的图片。 推荐 **单张 ≤ 10MB**,格式 `png` / `jpg` / `webp`。过大的图可能触发网关限制。多图融合时每张都需满足此限制。 `url` 模式响应的 `url` 字段是 **R2 CDN 加速链接,有效期约 1 天(24 小时)**,过期后会 404。 **强烈建议**:生成后尽快把图片 **转存到自己的对象存储(S3 / OSS / R2)、CDN 或数据库**,不要长期直接引用本服务返回的 URL。 本模型为一次性出图,不支持 stream 输出。如果对响应延迟敏感,建议客户端显示"生成中"进度提示,并合理配置 **300s 超时**(保守值)。 可以。把 `base_url` 指向 `https://api.apiyi.com/v1`,`api_key` 设为 API易 令牌即可。`client.images.generate(model="gpt-image-2.5-vip", size="2048x1360", prompt=...)` 直接可用。 可以,端点仍然可用,但**不再主推**——推荐统一使用 `/v1/images/generations` 与 `/v1/images/edits`(更稳定、与官转 `gpt-image-2` 同套代码)。 对话方式仅适合两类场景:多轮迭代改图、需要直接传在线图片 URL。注意出图意图不够明确时可能返回纯文字而不是图片(可在提示词开头加「生成图片:」前缀强化)。 详细参数见 [对话式调用说明](/api-capabilities/gpt-image-2-vip/chat-completions)。 需要**精确**的 mask 局部重绘、需要 OpenAI 官方完全一致的字段行为(含官方承诺的 `quality` 六档)时,改用官转 [`gpt-image-2.5-flare` / `sunburst` / `gpt-image-2`](/api-capabilities/gpt-image-2/overview)。详见 [官转 vs 官逆 对比](/api-capabilities/gpt-image-2/vs-gpt-image-2-all)。 ## 相关文档 * [GPT-Image-2-All 概览](/api-capabilities/gpt-image-2-all/overview) - 更便宜(\$0.025/张)、出图更快更稳的姐妹模型,适合不需要锁尺寸、1K–1.5K 够用的场景 * [⚖️ 官转 vs 官逆 对比](/api-capabilities/gpt-image-2/vs-gpt-image-2-all) - 与官方版 `gpt-image-2`(含 `-all` / `-vip`)的选型对照表 * [文生图 Playground](/api-capabilities/gpt-image-2-vip/text-to-image) - `/v1/images/generations` 兼容端点,传 `size` 锁定尺寸 * [图片编辑 Playground](/api-capabilities/gpt-image-2-vip/image-edit) - `/v1/images/edits` 多图融合与改图 * [GPT-Image-2.5 / 2 官方版](/api-capabilities/gpt-image-2/overview) - 需要精确 mask 局部重绘 / OpenAI 官方对齐字段时的选择 * [深度解读:GPT-image-2.5 上线](/news/gpt-image-2-5-launch) - 2.5 双模型发布说明 * [GPT-Image 系列总览](/api-capabilities/gpt-image-series) - 官方 GPT-Image 系列对比 * [API 使用手册](/api-manual) - 通用调用规范 gpt-image-2-vip 属于官逆通道(Adobe 线路,Firefly),行为对齐但定价/能力与官方版本不完全一致。需要官方完全一致字段时,请使用 [`gpt-image-2`](/api-capabilities/gpt-image-2/overview)。 # GPT-Image-2.5-VIP Agent 技能 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2-vip/skills 在 Agent 里用 gpt-image-2.5-vip(官逆·Adobe 线,可锁尺寸/4K,flat $0.03/张;gpt-image-2-vip 同调用)出图——与 gpt-image-2、gpt-image-2-all 共用同一个 gpt-image-2 技能,只需把 --model 设为 gpt-image-2.5-vip。 **gpt-image-2-vip 不需要单独的技能**。它和 gpt-image-2(官转)、gpt-image-2-all 共用同一个 **gpt-image-2 系列技能**——三者都走同一套 OpenAI Images API,只是 `--model` 不同。完整的安装、`SKILL.md` 与脚本,见 [**GPT-Image-2 系列 Agent 技能**](/api-capabilities/gpt-image-2/skills)。 ## 这个模型适合什么 官逆 Adobe 线,支持 30 档 `size`(含 3840×2160 等 4K),尺寸可控。 flat \$0.03/张,所有尺寸同价,4K 也不加价。 支持文生图与最多 16 张的多图融合(不支持画质档/掩码)。 ## 在技能里怎么用 装好 [gpt-image-2 系列技能](/api-capabilities/gpt-image-2/skills) 后,把 `--model` 设为 `gpt-image-2.5-vip`(或 `gpt-image-2.5-flare-vip` / `gpt-image-2-vip`),并用 `--size` 锁定尺寸: ```bash theme={null} python3 gpt-image-2/scripts/gpt_image.py "城市夜景航拍" -o city.png --model gpt-image-2.5-vip --size 3840x2160 ``` 想把它设成默认通道,在 `gpt-image-2/.env` 里加一行: ```bash theme={null} APIYI_IMAGE_MODEL=gpt-image-2.5-vip ``` **红线**:`quality` 按模型选档——2.5 两款六档全开(`xhigh` / `max` 2026-09-10 放开),`gpt-image-2-vip` 只到 `high`,传 `xhigh` / `max` 会被拒;2.5 的 `high` 只等于 `gpt-image-2-vip` 的 `medium`、2.5 的 `max` 才等于它的 `high`。**不要传 `n`**(传 `n>1` 仍只出 1 张但会按张数扣费),**掩码只是整图重绘**(要精确局部重绘请用官转)。技能脚本已对本模型自动门控,正常 `--model gpt-image-2.5-vip` 不会踩坑。 另:`size` 偶有上游临时停用、被强制自适应 1K 的情况;此时把尺寸/比例**也写进提示词**(如「16:9」)兜底即可。 ## 相关文档 * [GPT-Image-2 系列 Agent 技能(主页 · 完整脚本)](/api-capabilities/gpt-image-2/skills) * [GPT-Image-2-All Agent 技能](/api-capabilities/gpt-image-2-all/skills) * [GPT-Image-2-VIP 图片生成总览](/api-capabilities/gpt-image-2-vip/overview) # 文生图 API 参考 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2-vip/text-to-image api-reference/gpt-image-2-vip-generate-openapi.yaml POST /v1/images/generations gpt-image-2-vip 文生图 API 参考与在线调试 — 输入文本描述 + size 锁定输出尺寸生成图片,$0.03/张统一价 右侧的交互式 Playground 支持直接在线调试。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),输入 `prompt` 与 `size` 后一键发送即可。 **场景说明**:本页用于「文本生成图片」。只需输入提示词与 `size` 即可,无需上传任何图片。如需根据现有图片做编辑或融合,请使用 [图片编辑接口](/api-capabilities/gpt-image-2-vip/image-edit)。 **与 `gpt-image-2-all` 的区别**:调用结构完全一致,只多一个 `size` 字段;不需要锁尺寸、追求出图速度时改用 [`gpt-image-2-all`](/api-capabilities/gpt-image-2-all/text-to-image) 即可。 **🖥️ 浏览器 Playground 限制** 本端点**默认返回 base64 字符串(`b64_json`)**,体积可达数 MB,浏览器 Playground **可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法显示这么长的 base64。 **推荐做法**:**复制下方"代码示例"到本地运行**,代码会自动解码并把图片保存为本地文件。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 **⚠️ 关键参数说明** * **`size`**:可传 `auto` 让模型自动决定尺寸(vip 在同一提示词下尺寸相对**收敛/固定**),或从 30 档常见尺寸里选(10 比例 × 1K Fast / 2K Recommended / 4K Detail,详见 [概览页 size 完整表](/api-capabilities/gpt-image-2-vip/overview#支持的-size30-档完整对照表))严格锁尺寸。**写法用半角小写 `x`**,例如 `2048x1360`、`3840x2160`,不要用 `×` 或大写 `X`。 * **`quality`**:可选,实测生效(属渠道行为不承诺,以实际返回为准)。2.5 两款(`gpt-image-2.5-vip` = `gpt-image-2.5-sunburst-vip`、`gpt-image-2.5-flare-vip`)六档全开:`auto` / `low` / `medium` / `high` / `xhigh` / `max`;`gpt-image-2-vip` 只到 `high`,传 `xhigh` / `max` 会被拒。同名档位不等价:2.5 的 `high` 只相当于 `gpt-image-2-vip` 的 `medium`,2.5 的 `max` 才等于它的 `high`。按次 \$0.03 不随档位变,但档位越高耗时越长。 * **`n`**:❌ 不接受,单次仅返回 1 张图。**传 n=3 会按 0.09 \$ 扣费但只返回 1 张**,请把 `n` 字段从请求里去掉。 * **`aspect_ratio`**:❌ 不接受。比例直接由 `size` 决定。 * **`response_format`**:不传默认返回 base64(纯 base64 无前缀,2026-07 实测);传 `"url"` 可返回图片 URL。**强依赖 URL 输出**的业务建议把令牌分组切到 `image2_OSS`,稳定输出 URL、不降级为 base64。 ## 代码示例 ### Python ```python theme={null} import requests API_KEY = "sk-your-api-key" response = requests.post( "https://api.apiyi.com/v1/images/generations", headers={"Authorization": f"Bearer {API_KEY}"}, json={ "model": "gpt-image-2.5-vip", "prompt": "黄昏时的海边老灯塔,电影画幅,写实风格", "size": "2048x1152", # 16:9 2K Recommended "response_format": "url" # 默认返回 base64,读 url 字段需显式传 }, timeout=300 # 保守值,吸收长尾 + 图片下载耗时 ).json() image_url = response["data"][0]["url"] print(image_url) ``` **4K Detail 档示例(壁纸 / 印刷)**: ```python theme={null} import requests response = requests.post( "https://api.apiyi.com/v1/images/generations", headers={"Authorization": "Bearer sk-your-api-key"}, json={ "model": "gpt-image-2.5-vip", "prompt": "桌面壁纸,赛博朋克城市夜景,霓虹招牌,雨后倒影", "size": "3840x2160" # 16:9 4K Detail }, timeout=300 ).json() # 实测(2026-07)b64_json 为纯 base64(无 data: 前缀);历史版本曾含前缀,做个检测最稳 import base64 b64 = response["data"][0]["b64_json"] if b64.startswith("data:"): b64 = b64.split(",", 1)[1] with open("wallpaper.png", "wb") as f: f.write(base64.b64decode(b64)) ``` ### cURL ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/images/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2.5-vip", "prompt": "白色陶瓷马克杯放在灰色桌面上的产品图,柔和自然光", "size": "2048x1360" }' ``` ### Node.js ```javascript theme={null} const API_KEY = "sk-your-api-key"; const response = await fetch( "https://api.apiyi.com/v1/images/generations", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: "gpt-image-2.5-vip", prompt: "1:1 方形 LOGO,极简猫咪线条", size: "2048x2048" // 1:1 2K Recommended }) } ); const data = await response.json(); // 实测为纯 base64(无 data: 前缀),渲染前需拼接;历史版本曾自带前缀,做个检测最稳 let b64 = data.data[0].b64_json; if (!b64.startsWith("data:")) b64 = `data:image/png;base64,${b64}`; document.getElementById("result").src = b64; ``` ### OpenAI SDK(Python,推荐) ```python theme={null} from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1" ) resp = client.images.generate( model="gpt-image-2.5-vip", prompt="水墨山水,国画风格,纵向构图", size="1536x2048", # 3:4 2K Portrait ) print(resp.data[0].url) ``` ## 参数说明速查 | 参数 | 类型 | 必填 | 说明 | | - | - | - | - | | `model` | string | 是 | `gpt-image-2.5-vip`(= `gpt-image-2.5-sunburst-vip`)/ `gpt-image-2.5-flare-vip` / 上一代 `gpt-image-2-vip`,同价同调用 | | `prompt` | string | 是 | 提示词,描述画面内容、风格、光线等 | | `size` | string | **强烈建议** | 输出尺寸:`auto`(模型自动决定,**vip 在同一提示词下尺寸相对固定**)或 30 档之一;写法 `宽x高`(半角小写 `x`);省略时等同 `auto` | **size 速查**:常用挑这几个就够: * 电商主图:`2048x1360` (3:2 2K) / `2048x2048` (1:1 2K) * 海报竖图:`1536x2048` (3:4 2K) / `2480x3312` (3:4 4K) * 视频封面:`2048x1152` (16:9 2K) / `3840x2160` (16:9 4K) * 故事/手机壁纸:`1152x2048` (9:16 2K) / `2160x3840` (9:16 4K) 完整 30 档表见 [概览页](/api-capabilities/gpt-image-2-vip/overview#支持的-size30-档完整对照表)。 ## 响应格式 **默认返回 base64**(`data[0].b64_json`,纯 base64 无前缀,2026-07 实测)。如需 **图片 URL**:显式传 `response_format: "url"` 即可;**强依赖 URL 输出**的业务建议把令牌分组切到 **`image2_OSS`**,稳定输出 URL、不降级为 base64。`data[0]` 中只会出现 `url` 或 `b64_json` 之一,不会两者都返回。 **`b64_json` 模式**(默认): ```json theme={null} { "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA..." } ], "created": 1778037127, "usage": { "input_tokens": 98, "output_tokens": 1185, "total_tokens": 1283 } } ``` **`url` 模式**(显式传 `response_format: "url"`;强依赖 URL 建议用 `image2_OSS` 分组,R2 CDN 全球加速): ```json theme={null} { "data": [ { "url": "https://r2cdn.copilotbase.com/r2cdn2/0e82148a-bec0-4b42-bbca-117c6b42581b.png" } ], "created": 1778037331, "usage": { "input_tokens": 30, "output_tokens": 2074, "total_tokens": 2104 } } ``` **兼容性提示**:2026-07 实测 `b64_json` 字段为**纯 base64(不含 `data:` 前缀)**,需解码写文件或自行拼接前缀后渲染;**历史版本曾直接带前缀**。请在代码里做 `startsWith('data:')` 检测后再处理,兼容两种形态。 ## 相关资源 30 档 size 完整对照表、定价、技术规格 `/v1/images/edits` 多图融合与改图 不需要锁尺寸时调用方式一致,出图更快(约 30–60s) # GPT-Image 历史版本 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2/historical-versions GPT-Image 系列版本一览:现役 gpt-image-2.5-flare / gpt-image-2.5-sunburst / gpt-image-2 与历史版 GPT-Image-1 / 1-mini / 1.5 的模型 ID、发布时间、计费、端点,以及迁移到 2.5 / 2-All 的建议。 新项目请直接使用 [GPT-Image-2.5 / 2](/api-capabilities/gpt-image-2/overview)(官方版,文生图选 `gpt-image-2.5-flare`、改图选 `gpt-image-2.5-sunburst`)或 [GPT-Image-2-All](/api-capabilities/gpt-image-2-all/overview)(官逆,\$0.025/张统一价)。本页面仅保留历史版本的核心参数,方便老项目排查与迁移。 ## 版本一览 | 模型 ID | 发布时间 | 端点 | 价格特点 | 状态 | | - | - | - | - | - | | `gpt-image-2.5-flare`(快照 `-2026-09-08`) | 2026年9月 | `/v1/images/generations`、`/v1/images/edits` | 输入 \$5.00 / 输出 \$30.00 / 百万 tokens,与 gpt-image-2 相同 | **现役**,速度优先,文生图默认 | | `gpt-image-2.5-sunburst`(快照 `-2026-09-08`) | 2026年9月 | `/v1/images/generations`、`/v1/images/edits` | 同上 | **现役**,画质与编辑精度优先 | | `gpt-image-2`(快照 `-2026-04-21`) | 2026年4月 | `/v1/images/generations`、`/v1/images/edits` | 输入 \$5.00 / 输出 \$30.00 / 百万 tokens | 现役,上一代,`quality` 只到 `high` | | `gpt-image-1.5` | 2025年12月 | `/v1/images/generations` | 输入 \$5.00 / 输出 \$10.00 / 百万 tokens | 仍可用,建议升级至 `gpt-image-2.5-flare` / `sunburst` | | `gpt-image-1` | 2025年4月 | `/v1/images/generations`、`/v1/images/edits` | 输入 \$2.50 / 输出 \$8.00 / 百万 tokens | 仍可用,建议升级至 `gpt-image-2.5-flare` / `sunburst` | | `gpt-image-1-mini` | 2025年4月 | `/v1/images/generations` | 较 `gpt-image-1` 更低 | 仍可用 | **直接迁移**:把 `model` 改成 `gpt-image-2` 或 `gpt-image-2-all` 即可,参数(`size` / `quality` / `output_format` 等)大体兼容,无需改代码结构。 ## 通用参数(生图) 历史版本共享一套生图参数: | 参数 | 说明 | | - | - | | `model` | `gpt-image-1.5` / `gpt-image-1` / `gpt-image-1-mini` | | `prompt` | 图像描述,最长 32,000 字符(原厂上限,按字符计) | | `size` | `1024x1024` / `1536x1024` / `1024x1536` / `auto` | | `quality` | `low` / `medium` / `high` / `auto`(2.5 系列另有 `xhigh` / `max`) | | `output_format` | `png`(默认) / `jpeg` / `webp` | | `output_compression` | 仅 JPEG / WebP,0–100% | | `background` | `transparent` / `opaque` / `auto` | | `n` | 生成数量(1–10) | ## 快速调用示例 ```python theme={null} from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1" ) response = client.images.generate( model="gpt-image-1.5", {/* 也可填 gpt-image-1 / gpt-image-1-mini */} prompt="A professional product photo on white background, soft studio lighting", size="1024x1024", quality="high" ) print(response.data[0].url) ``` ## 按图片计费参考 GPT-Image-1 / 1.5 同时支持 Token 计费与按图片计费,系统自动取较优方式: ### GPT-Image-1.5 | 质量 | 1024×1024 | 1024×1536 / 1536×1024 | | - | - | - | | Low | \$0.009 | \$0.013 | | Medium | \$0.034 | \$0.050 | | High | \$0.133 | \$0.200 | ### GPT-Image-1 | 质量 | 1024×1024 | 1024×1536 / 1536×1024 | | - | - | - | | Low | \$0.005 | \$0.006 | | Medium | \$0.011 | \$0.015 | | High | \$0.036 | \$0.052 | 按图片计费仅作参考。实际扣费以系统自动选择的更优方式(Token 或按图片)为准。 ## 图像编辑(仅 `gpt-image-1`) `gpt-image-1` 支持基于遮罩的局部编辑端点 `/v1/images/edits`: ```python theme={null} response = client.images.edit( model="gpt-image-1", image=open("original.png", "rb"), mask=open("mask.png", "rb"), prompt="A modern glass skyscraper with reflective windows", size="1024x1024" ) ``` | 参数 | 说明 | | - | - | | `image` | 原图,PNG/WebP/JPG,单张 \< 50MB,最多 16 张 | | `mask` | 遮罩图,alpha=0 的区域将被重绘 | | `prompt` | 描述要在遮罩区生成的内容 | 新项目编辑场景推荐使用 [GPT-Image-2 编辑](/api-capabilities/gpt-image-2/image-edit)(官方)或 [GPT-Image-2-All 编辑](/api-capabilities/gpt-image-2-all/image-edit)(官逆,最多 16 张多图融合)。 ## 迁移到 GPT-Image-2.5 / 2-All | 你的诉求 | 推荐迁移到 | | - | - | | 沿用 OpenAI 官方端点、需要精确尺寸/质量控制 | `gpt-image-2.5-flare`(文生图)/ `gpt-image-2.5-sunburst`(编辑),官转 | | 想要可预测的统一价(\$0.025/张)、提示词遵循更优 | `gpt-image-2-all`(官逆) | | 仍需图像编辑 + 遮罩 | `gpt-image-2-all` 编辑端点(支持最多 16 张) | OpenAI 官方现役三款,端点 / 参数与历史版兼容 官逆通道,\$0.025/张统一价,速度更快 一文看懂两者的差异和选型建议 # 图片编辑 API 参考 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2/image-edit api-reference/gpt-image-2-edit-openapi.yaml POST /v1/images/edits gpt-image-2.5-sunburst / gpt-image-2.5-flare / gpt-image-2 图片编辑 API 参考与在线调试 — 上传参考图(最多 16 张)+ 指令进行单图改图、多图融合或 mask 局部重绘 右侧的交互式 Playground 支持直接上传本地图片。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),选择 image / mask 文件并填入 `prompt`、`model` 后一键发送即可。 **场景说明**:本页用于「基于一张或多张参考图改图 / 融合生成 / mask 局部重绘」。请求为 `multipart/form-data` 格式。如需纯文本生成图片,请使用 [文生图接口](/api-capabilities/gpt-image-2/text-to-image)。 **🖥️ 浏览器 Playground 限制(重要)** 本接口的响应包含**纯 base64 字符串**(数 MB 量级)。受浏览器渲染限制,右侧 Playground 在收到响应后**可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法把这么长的 base64 显示出来。 **推荐做法**(小白零踩坑): * **直接复制下方"代码示例"中的 Python / Node.js / cURL 到本地运行**,代码会自动 `base64.b64decode` 并把图片**保存为本地文件**。 * 如要在浏览器里试 Playground,**用极小的参考图(\< 50KB)** 并把 `size` 设为最小档(如 `1024x1024`)、`quality` 改为 `low`。 **⚠️ 关键差异(从 gpt-image-1.5 迁移注意)** * **不要传 `input_fidelity`** —— 三款模型均强制启用高保真,传了会 400 报错(2.5 实测 2026-09-09 同样 400) * **编辑请求的输入 token 明显更高** —— 因为参考图按 Vision 计费规则换算成大量 token,预算要留足 * **多图融合最多 16 张** —— `image[]` 字段重复传入,超过会报错 **📎 多图融合顺序有意义** `image[]` 字段可重复传入多张参考图,**顺序将作为 prompt 中「图1/图2/图3」的引用依据**。建议在 prompt 中显式指代,例如: > 把图1的人物放进图2的场景,沿用图3的色彩风格 单张文件上限 **50MB**(multipart 文件上传),格式 `png` / `jpg` / `webp`;实践中建议先压到 **1.5MB 以内**再上传(详见下方「上传大小限制速查」)。 ## 代码示例 ### Python(OpenAI SDK · 单图编辑) ```python theme={null} from openai import OpenAI import base64 client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1" ) resp = client.images.edit( model="gpt-image-2.5-sunburst", image=open("photo.png", "rb"), prompt="把背景换成海边黄昏,保留人物细节", size="1536x1024", quality="high" ) # b64_json 是纯 base64(无前缀),需自己 decode with open("edited.png", "wb") as f: f.write(base64.b64decode(resp.data[0].b64_json)) ``` ### Python(OpenAI SDK · 多图融合) ```python theme={null} resp = client.images.edit( model="gpt-image-2.5-sunburst", image=[ open("person.png", "rb"), open("scene.png", "rb"), open("style.png", "rb"), ], prompt="把图1人物放进图2场景,沿用图3的色彩风格,保持光线一致", size="1536x1024", quality="high" ) with open("fused.png", "wb") as f: f.write(base64.b64decode(resp.data[0].b64_json)) ``` ### cURL(多图融合) ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/images/edits" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=gpt-image-2.5-sunburst" \ -F "prompt=把图1的人物放进图2的场景,保留图3的色彩风格" \ -F "size=1536x1024" \ -F "quality=high" \ -F "image[]=@person.png" \ -F "image[]=@scene.png" \ -F "image[]=@style.png" ``` ### cURL(mask 局部重绘) ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/images/edits" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=gpt-image-2.5-sunburst" \ -F "prompt=把天空换成粉色晚霞" \ -F "size=1024x1024" \ -F "quality=high" \ -F "image[]=@photo.png" \ -F "mask=@mask.png" \ | jq -r '.data[0].b64_json' | base64 -d > photo_edited.png ``` ### Node.js(原生 fetch + FormData · 多图融合) ```javascript theme={null} import fs from 'node:fs'; const form = new FormData(); form.append('model', 'gpt-image-2.5-sunburst'); form.append('prompt', '把图1的人物放进图2的场景'); form.append('size', '1536x1024'); form.append('quality', 'high'); form.append('image[]', new Blob([fs.readFileSync('./person.png')]), 'person.png'); form.append('image[]', new Blob([fs.readFileSync('./scene.png')]), 'scene.png'); const resp = await fetch('https://api.apiyi.com/v1/images/edits', { method: 'POST', headers: { 'Authorization': 'Bearer sk-your-api-key' }, body: form }); const { data } = await resp.json(); fs.writeFileSync('fused.png', Buffer.from(data[0].b64_json, 'base64')); ``` ## 参数说明速查 | 字段 | 类型 | 必填 | 默认 | 说明 | | - | - | - | - | - | | `model` | text | 是 | — | `gpt-image-2.5-sunburst`(编辑精度优先,改图首选)/ `gpt-image-2.5-flare`(速度优先)/ `gpt-image-2`(上一代,仍可用);生产环境可锁日期快照 `gpt-image-2.5-sunburst-2026-09-08` / `gpt-image-2.5-flare-2026-09-08`。三款同价同参数 | | `prompt` | text | 是 | — | 编辑 / 融合指令 | | `image[]` | file | 是 | — | 参考图,可重复多次(**最多 16 张**) | | `mask` | file | 否 | — | 掩码图(仅对第一张 image 生效,要求带 alpha 通道) | | `size` | text | 否 | `auto` | 输出尺寸,同文生图 | | `quality` | text | 否 | `auto` | `low` / `medium` / `high` / `xhigh` / `max` / `auto`;`xhigh` / `max` 是 2.5 新增档位,`gpt-image-2` 只到 `high` | | `output_format` | text | 否 | `png` | `png` / `jpeg` / `webp` | | `output_compression` | text | 否 | — | 0–100,仅 `jpeg` / `webp` 生效 | | `background` | text | 否 | `auto` | `transparent` / `opaque` / `auto`;`transparent` 仅 2.5 两款支持(`gpt-image-2` 自 2026-09-30 起被官方拒绝,返回 400),且 `output_format` 必须是 `png` 或 `webp`。注意编辑接口的透明是**重绘去背**而非精确抠像,见 [透明背景 FAQ](/faq/image-transparent-background) | **`quality` 不要传旧版 DALL·E 的 `standard` / `hd`。** 只接受 `low` / `medium` / `high` / `xhigh` / `max` / `auto` 六个官方枚举值(`xhigh` / `max` 仅 2.5 两款接受)。旧值在不同后端渠道下行为不一致:有时直接 400 报错(`invalid_value`),有时被静默忽略、按 `auto` 档跑出结果(费用不可控)。请始终显式传官方枚举值之一。 ## 上传大小限制速查 | 项目 | 限制 | 说明 | | - | - | - | | 参考图数量 | 最多 **16 张** | `image[]` 字段重复传入 | | 单张图片(multipart 文件上传) | 每张**小于 50MB** | 支持 `png` / `jpg` / `webp` | | 单张图片(base64 data URL 方式) | 字段长度约 **20MiB** | 这是 URL/base64 **字符串字段**的长度限制(schema `maxLength: 20971520`),**不等同于** multipart 文件上传的 50MB 上限;base64 编码会膨胀约 1/3,**实际原图建议控制在 15MB 以内** | | mask 文件 | **PNG 且小于 4MB** | 须与原图相同尺寸、带 alpha 通道 | **总请求体积别顶满**:虽然单图上限 50MB、最多 16 张,但多张大图同时接近上限时,单个请求体会非常大,容易在网关 / CDN / 超时层面失败。实践中建议**每张先压到 1.5MB 以内**(JPEG 质量 80-90),成功率和出图速度都会明显更好——输出画质与输入图体积无关。 ## 参考图格式要求与预处理 `/v1/images/edits` 只接受 **png / jpg / webp** 三种标准格式。如果收到下面这个 400: ```json theme={null} { "error": { "message": "Invalid image file or mode for image 1, please check your image file. ...", "type": "shell_api_error", "code": "invalid_image_file" } } ``` 大概率是参考图**并非标准 JPEG/PNG**。最常见的坑是手机原拍照片的 **MPO 格式**(Multi-Picture Object,多帧 JPEG 容器):华为 Mate 系列等机型直出的 `.jpg` 内嵌 HDR 增益图副帧,实为 MPO。这类文件的文件头同为 `FFD8`,**扩展名和 `file` 命令都显示 JPEG**,肉眼无法分辨,只有按帧解析(如 Pillow)才能识别。报错里的「image 1」指第 N 张参考图(序号从 1 开始),可按序号定位问题图。 **2026-07 实测**:MPO 图 5 次上传全部 400;同一批图重编码为标准 JPEG/PNG 后,**保持 3072×4096 原分辨率**上传全部成功——问题出在格式,不在尺寸/体积。该错误在入口校验阶段快速返回(约 4 秒),**不计费**。 **判别与修复**:`Image.open(f).format` 返回 `"MPO"` 即需转换。上传链路统一做一次重编码即可,顺带兼容 HEIC 等其它手机格式: ```python theme={null} from PIL import Image import io def normalize_image(path: str) -> bytes: """手机图片(MPO/HDR 多帧等)转标准 JPEG,通过 edits 格式校验""" im = Image.open(path) im.load() # MPO 只取第一帧(全尺寸主图) if im.mode not in ("RGB", "RGBA"): im = im.convert("RGB") out = io.BytesIO() im.save(out, format="JPEG", quality=92) # 或 format="PNG" return out.getvalue() ``` 业务是「用户上传实拍图」的场景(家装效果图、商品实拍等),建议在服务端上传链路**统一重编码**,而不是逐张排查——手机 HDR 照片会持续出现。更多输入图片处理技巧见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 ## mask 局部重绘要求 * 与原图**相同尺寸**,**PNG 格式**,单张**小于 4MB** * **必须带 alpha 通道**:透明区域(alpha=0)= 要重绘的部分,不透明区域 = 保留 * mask 仅对**第一张** image 生效 * mask 作为「软引导」而非精确边界,模型可能在蒙版周围扩展 / 收敛 **多轮迭代**:把上一次的输出作为下一次的 `image[]` 输入,配合新的编辑指令,可逐步精调画面。每一轮都按 token 实计,预算时留意累计成本。 ## 响应格式 ```json theme={null} { "created": 1776832476, "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA..." } ], "usage": { "input_tokens": 848, "input_tokens_details": { "image_tokens": 832, "text_tokens": 16 }, "output_tokens": 196, "output_tokens_details": { "image_tokens": 196, "text_tokens": 0 }, "total_tokens": 1044 } } ``` `b64_json` 字段是**纯 base64**,**不含** `data:image/...;base64,` 前缀,与 `gpt-image-2-all` 不同。客户端需自行 decode 写文件,或在浏览器端拼前缀渲染。 编辑请求的 `input_tokens` 通常**显著高于**同尺寸文生图,原因是参考图按 Vision 计费规则换算——具体消耗多少可以直接读 `usage.input_tokens_details.image_tokens`,与文本部分(`text_tokens`)是分开计的。多图融合时 `image_tokens` 会随参考图数量**严格线性增加**(2026-07 实测:4 张 1024² = 4 × 1024 tokens),量化数据见 [多图输入的价格影响](/api-capabilities/gpt-image-2/overview#多图输入的价格影响2026-07-实测)。详细字段说明见 [概览页「如何查看每次调用的真实 token 数」](/api-capabilities/gpt-image-2/overview#如何查看每次调用的真实-token-数)。 # 蒙版局部重绘指南 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2/mask-editing gpt-image-2 蒙版(mask)局部重绘完整指南 — Alpha 通道原理、蒙版制作与校验、Python/cURL/Node.js 示例、常见报错排查 本页是 `gpt-image-2` 通过 `POST /v1/images/edits` 上传**原图 + 蒙版(mask)+ 提示词**实现局部重绘的实操指南。接口参数与在线调试请见 [图片编辑 API 参考](/api-capabilities/gpt-image-2/image-edit)。 ## 核心原理:Alpha 通道决定编辑区域 一次局部编辑请求由三部分组成: ```text theme={null} 原始图片 image + 蒙版图片 mask + 编辑指令 prompt = 编辑后的完整图片 ``` 蒙版通过 PNG 的 **Alpha 透明通道**标记修改区域: | 蒙版区域 | Alpha 值 | 作用 | | - | -: | - | | 完全透明 | 0 | 允许模型编辑 | | 完全不透明 | 255 | 尽量保留原图 | | 半透明 | 1~254 | 过渡区域,不建议作为精确规则依赖 | **最容易搞反的一点**:决定编辑区域的是 **Alpha 通道**,不是肉眼看到的黑色或白色。 ```text theme={null} 透明区域 = 要修改的区域 不透明区域 = 尽量不要修改的区域 ``` 一张"看起来黑白分明"的 PNG,如果没有 Alpha 通道,直接上传会报 `invalid_image_file`。 ### 一个直观例子 假设原图尺寸是 1024×1024: ```text theme={null} ┌──────────────────────┐ │ 不透明区域 │ │ 尽量保持原图不变 │ │ │ │ ┌──────┐ │ │ │透明区│ │ │ │换成花│ │ │ └──────┘ │ └──────────────────────┘ ``` 配合提示词: ```text theme={null} 把透明区域中的水杯替换成一束白色郁金香, 其他区域保持不变,保持原有光线、视角和摄影风格。 ``` ## Mask 不是硬裁剪 GPT Image 的蒙版**不是** Photoshop 那种绝对像素级的硬限制。官方说明其本质仍是**基于提示词的引导式编辑**:模型会参考蒙版,但不保证严格按每一个像素边界执行。 因此可能出现: * 蒙版外的阴影略微变化 * 物体边缘向外扩展 * 光照和反射发生联动 * 背景细节被轻微重绘 * 蒙版边界附近出现过渡 这对自然融合是好事,但不适合要求绝对像素不变的场景(解决方案见下文「严格保持蒙版外不变」)。 ### 提高局部编辑稳定性 提示词不要只写「换成红色衣服」,建议写成: ```text theme={null} 仅修改蒙版透明区域,将人物上衣替换成纯红色圆领棉质短袖。 保持人物脸部、头发、身体姿势、手臂、背景、构图、相机视角、 光照方向和图片尺寸不变。新衣服需要自然贴合人物身体,并保持 原图的真实摄影质感。 ``` 实务建议: 1. 蒙版比目标物体边缘稍微扩大一些 2. 不要只遮住物体中心,要覆盖物体边缘、阴影和反射 3. 明确写出哪些内容必须保持不变 4. 编辑区域过小时,适当扩大蒙版 5. 要求绝对不变时,最后自行做一次像素合成(见下文) ## 要不要用 Mask?与纯提示词编辑的取舍 一个常见疑问:**现在的 AI 不是已经"指哪打哪"了吗,为什么还要费劲做蒙版?** 确实,`gpt-image-2` 不传 mask、只靠一句"把左边桌上的杯子换成花",多数时候就能改对地方——指令遵循能力已经很强,日常随手改图完全够用。但 mask 解决的是**提示词说不清、或者说清了也不保险**的问题: | 场景 | 纯提示词 | 提示词 + Mask | | - | - | - | | 画面只有一个目标物 | ✅ 够用,mask 属于多余 | 不必要 | | 多个相似物体只改其一(三个人只换中间那位的衣服) | ⚠️ 容易误伤同类目标 | ✅ 空间指定,零歧义 | | 边界要求严格(商品图 / UI 截图 / 证件版式) | ❌ 整图重生成,蒙版外也会漂移 | ✅ 配合像素合成可做到严格不变 | | 批量流水线(同一版式反复替换某区域) | ⚠️ 每次结果不稳定 | ✅ 蒙版可程序化生成,结果可复现 | | 精确形状 / 位置控制(把物体挪到指定坐标) | ❌ 语言难以描述像素级位置 | ✅ 蒙版就是像素级坐标 | 学术研究也支持这个分工:mask-free(纯文字驱动)的编辑方法难以精确控制空间位置和形状——比如 prompt-to-prompt 类方法无法在画面中**空间移动**一个物体,编辑区域覆盖不准时还会"该改的没改、不该改的改了";而 mask-based 方法以牺牲一点便利为代价,换来明确的空间控制精度。 **一句话结论**:mask 不是被淘汰的旧技术,而是从"必需品"变成了"精度控制工具"。聊天式随手改图 → 直接用提示词;生产环境要求可复现、可控、边界严格 → 用 mask。另外别忘了:`gpt-image-2` 纯提示词编辑本质是**整图重生成**,未指定区域同样可能变化——这正是 mask + 像素合成存在的意义。 ## 文件要求速查 | 项目 | 要求 | | - | - | | 原图格式 | PNG / JPG / WebP,单张小于 50MB | | 输入图数量 | 最多 16 张(`image[]` 重复传入) | | 蒙版格式 | **PNG,必须带 Alpha 通道** | | 蒙版尺寸 | 与**第一张**原图宽高**完全一致**(差 1 像素也不行) | | 蒙版大小 | 小于 **4MB** | | 蒙版作用范围 | 仅作用于 `image[0]`(第一张图) | 多图编辑时的角色分配: ```text theme={null} image[0] = 主要编辑画布 image[1…] = 参考图 mask = 只作用于 image[0] ``` 「尺寸完全一致」听起来麻烦,实际不需要手动对齐——蒙版都是**从原图上派生**出来的(在原图副本上擦除 / 涂抹 / 分割),同尺寸自动满足,详见下文 [蒙版从哪来](#蒙版从哪来五种常见制作方式)。 ## Python 调用示例 ```python theme={null} import base64 from pathlib import Path from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1" ) with open("input.png", "rb") as image_file, \ open("mask.png", "rb") as mask_file: result = client.images.edit( model="gpt-image-2.5-sunburst", image=image_file, mask=mask_file, prompt=( "仅修改蒙版透明区域。" "把桌面上的白色水杯替换成一束白色郁金香," "保持桌面、背景、相机视角、构图和光线不变。" "花束需要自然放置在原水杯的位置,并产生符合原图光线的阴影。" ), size="1536x1024", quality="high", output_format="png", n=1, ) image_bytes = base64.b64decode(result.data[0].b64_json) Path("edited.png").write_bytes(image_bytes) print("图片已保存:edited.png") ``` **不要传 `input_fidelity="high"`** —— 三款模型对输入图默认始终高保真处理,API 不允许调整该参数,传了会 400 报错(2.5 实测 2026-09-09 同样 400),直接省略即可。 ## cURL 调用示例 图片编辑接口必须使用 `multipart/form-data`,不能把原图和蒙版作为普通 JSON 字段提交: ```bash theme={null} curl -s \ -X POST "https://api.apiyi.com/v1/images/edits" \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=gpt-image-2.5-sunburst" \ -F "image[]=@input.png;type=image/png" \ -F "mask=@mask.png;type=image/png" \ -F "prompt=仅修改蒙版透明区域,把桌上的水杯替换成一束白色郁金香,其他区域保持不变,保持原有构图、光线和真实摄影风格。" \ -F "size=1536x1024" \ -F "quality=high" \ -F "output_format=png" \ | jq -r '.data[0].b64_json' \ | base64 --decode > edited.png ``` 即便只有一张图片,也建议按官方示例使用 `image[]` 字段名。 使用 `-F` 时**不要手动设置** `-H "Content-Type: multipart/form-data"`。curl 需要自动生成 `boundary`,手动设置会丢失 boundary,导致服务器无法识别文件。 ## Node.js 调用示例 ```javascript theme={null} import fs from "fs"; import OpenAI, { toFile } from "openai"; const client = new OpenAI({ apiKey: "sk-your-api-key", baseURL: "https://api.apiyi.com/v1", }); const image = await toFile( fs.createReadStream("input.png"), "input.png", { type: "image/png" } ); const mask = await toFile( fs.createReadStream("mask.png"), "mask.png", { type: "image/png" } ); const result = await client.images.edit({ model: "gpt-image-2.5-sunburst", image, mask, prompt: "仅修改蒙版透明区域。把桌面上的白色水杯替换成一束白色郁金香," + "保持背景、构图、镜头视角和光线不变。", size: "1536x1024", quality: "high", output_format: "png", }); const imageBuffer = Buffer.from(result.data[0].b64_json, "base64"); fs.writeFileSync("edited.png", imageBuffer); console.log("图片已保存:edited.png"); ``` ## 蒙版从哪来?五种常见制作方式 很多人觉得做蒙版麻烦——"还得跟原图一模一样的尺寸"。其实有一个关键认知:**蒙版几乎从不'另画一张',而是从原图上派生出来的**。无论用代码、修图软件还是网页画布,流程都是「打开原图 → 在原图上标记区域 → 导出」,尺寸一致是**自动满足**的,不需要手动对齐。 | 方式 | 适合场景 | 上手成本 | | - | - | - | | ① 代码生成(PIL 画区域) | 固定版式批量处理、坐标已知 | 低(几行代码) | | ② 修图软件手动擦除 | 一次性精修、形状复杂 | 低(会用选区即可) | | ③ 网页涂抹画布 | 自建产品给用户"刷一下"就改 | 中(前端 Canvas) | | ④ AI 自动分割(SAM 系) | 点一下 / 一句话就出精确蒙版 | 中(需部署或调用分割服务) | | ⑤ 黑白蒙版转 Alpha | 承接其它工具输出的黑白 mask | 低(几行代码) | ### 方法一:程序直接生成透明蒙版 把矩形区域设为透明(允许编辑): ```python theme={null} from PIL import Image, ImageDraw original = Image.open("input.png").convert("RGBA") # 默认整张蒙版完全不透明:尽量保留 mask = Image.new("RGBA", original.size, (255, 255, 255, 255)) draw = ImageDraw.Draw(mask) # 把指定区域设为完全透明:允许编辑 # 坐标格式:(左, 上, 右, 下) draw.rectangle((300, 250, 750, 800), fill=(0, 0, 0, 0)) mask.save("mask.png") print("蒙版尺寸:", mask.size) ``` * `(255, 255, 255, 255)` = 不透明,保留区 * `(0, 0, 0, 0)` = 透明,编辑区 ### 方法二:修图软件手动擦除 任何支持透明 PNG 的修图软件(Photoshop、GIMP、Krita、Photopea 等)都能做蒙版,本质就一步:**把要编辑的区域"擦"成透明**。以 Photoshop 为例: 1. 打开**原图副本**(直接在原图上操作,尺寸天然一致) 2. 如果图层是「背景」,先双击解锁为普通图层(背景图层不支持透明) 3. 用套索 / 快速选择 / 对象选择工具框选要修改的区域 4. 按 Delete 删除选区内容 → 露出透明棋盘格 5. 「导出为 PNG」(勾选透明度),得到的就是合格的 Alpha 蒙版 GIMP 同理:`图层 → 透明 → 添加 Alpha 通道`,选区后 Delete,导出 PNG。 形状完全不限于矩形——套索沿着物体轮廓走、快速选择一键选中主体,擦出来的透明区就是任意不规则形状。建议选区比物体轮廓**稍微外扩几个像素**(Photoshop:`选择 → 修改 → 扩展`),把阴影和边缘一并覆盖。 ### 方法三:网页涂抹画布 各类 AI 修图产品里"用笔刷涂一下要改的地方"的交互,就是在浏览器里动态生成 Alpha 蒙版,核心只有一个 Canvas API 属性。实现原理见下文 [涂抹式修图的实现原理](#蒙版形状与涂抹式修图的实现原理)。 ### 方法四:AI 自动分割一键出蒙版 手动涂抹也嫌麻烦?可以让分割模型代劳。Meta 开源的 **SAM(Segment Anything Model)** 系列是目前的主流方案: * **点选出蒙版**:在物体上点一下,模型输出该物体的像素级精确轮廓(连头发丝边缘都能贴合) * **文字出蒙版**:2025 年 11 月开源的 **SAM 3** 支持概念级文字提示,如「所有黄色出租车」「穿红色球衣的球员」,一次返回所有匹配实例的蒙版(模型与代码见 `github.com/facebookresearch`,介绍见 `ai.meta.com`) * **抠主体 / 抠背景**:`rembg` 这类开源工具一行命令分离主体与背景,背景区域直接可以当"只改背景"的蒙版用 拿到分割结果(通常是黑白位图)后,用下面「方法五」转成 Alpha 蒙版即可。Stable Diffusion 社区的 **Inpaint Anything** 插件、ComfyUI 的 Mask Editor 就是「SAM 分割 + 笔刷微调 → 蒙版 → 局部重绘」这套流水线的成熟实现,思路可以直接借鉴。 ### 方法五:把黑白蒙版转换成 Alpha 蒙版 如果你已有一张「黑色 = 编辑,白色 = 保留」的黑白蒙版: ```python theme={null} from PIL import Image bw_mask = Image.open("mask_bw.png").convert("L") rgba_mask = Image.new("RGBA", bw_mask.size, (255, 255, 255, 255)) # 黑色值 0 → Alpha 0 → 透明 → 编辑 # 白色值 255 → Alpha 255 → 不透明 → 保留 rgba_mask.putalpha(bw_mask) rgba_mask.save("mask.png") ``` ### 上传前先校验蒙版 很多 `invalid_image_file` 都是因为文件扩展名是 `.png`,实际却只有 RGB 没有 Alpha。上传前跑一遍: ```python theme={null} from PIL import Image image = Image.open("input.png") mask = Image.open("mask.png") print("蒙版格式:", mask.format) print("蒙版模式:", mask.mode) print("蒙版尺寸:", mask.size) assert mask.format == "PNG", "蒙版必须是 PNG" assert mask.mode in ("RGBA", "LA"), "蒙版必须包含 Alpha 通道" assert image.size == mask.size, "原图和蒙版尺寸必须完全一致" alpha = mask.getchannel("A") assert alpha.getextrema()[0] == 0, "蒙版中没有完全透明的编辑区域" print("蒙版检查通过") ``` ## 蒙版形状与涂抹式修图的实现原理 ### 蒙版可以是任意不规则形状 蒙版本质是一张**逐像素的位图**,不是几何图形——每个像素独立记录一个 Alpha 值。所以: * 矩形、圆形只是最简单的示例 * 沿人物轮廓的剪影、头发丝边缘、随手涂鸦的一团、不连通的多块区域,全部合法 * 实践中**大多数蒙版都是不规则的**:跟着目标物体的轮廓走,再略微外扩 唯一的"形状建议"与规则无关,与效果有关:透明区要**完整覆盖物体本体 + 边缘 + 阴影 + 反射**,宁可多圈一点,让模型有空间做自然融合。 ### 涂抹式修图是怎么实现的 各类修图 App 里"笔刷涂哪改哪"的交互,前端实现出奇地简单:**两层画布叠加,笔刷把上层"擦"成透明**。 ```text theme={null} 底层 显示原图(仅供用户参考,不参与导出) 上层 与原图等宽高,初始整张填充为不透明 笔刷经过处 → 像素变透明 导出 canvas → 就是一张合格的 Alpha 蒙版 PNG ``` 核心只有一行——把 Canvas 合成模式设为 `destination-out`(新笔迹从已有像素中"挖掉"内容): ```javascript theme={null} const canvas = document.getElementById("mask-canvas"); const ctx = canvas.getContext("2d"); // 1. 画布尺寸 = 原图原始像素尺寸(不是 CSS 显示尺寸),同尺寸自动满足 canvas.width = image.naturalWidth; canvas.height = image.naturalHeight; // 2. 初始整张不透明 = 全部保留 ctx.fillStyle = "rgba(255, 255, 255, 1)"; ctx.fillRect(0, 0, canvas.width, canvas.height); // 3. 关键一行:笔刷改为"擦除"模式,画过的地方变透明 ctx.globalCompositeOperation = "destination-out"; ctx.lineWidth = 40; // 笔刷粗细 ctx.lineCap = "round"; ctx.strokeStyle = "rgba(0, 0, 0, 1)"; // 4. 监听鼠标 / 触摸事件画线(注意把显示坐标换算回原始像素坐标) canvas.addEventListener("pointermove", (event) => { if (!drawing) return; const scaleX = canvas.width / canvas.clientWidth; const scaleY = canvas.height / canvas.clientHeight; ctx.lineTo(event.offsetX * scaleX, event.offsetY * scaleY); ctx.stroke(); }); // 5. 导出:直接得到带 Alpha 通道的 PNG 蒙版 canvas.toBlob((blob) => { const formData = new FormData(); formData.append("mask", blob, "mask.png"); // 连同原图、prompt 一起 POST /v1/images/edits }, "image/png"); ``` 几个工程细节: 1. **坐标换算**:画布在页面上通常被 CSS 缩小显示,笔迹坐标要按 `原始宽 / 显示宽` 的比例换算回去,否则蒙版错位 2. **撤销**:每笔开始前 `ctx.getImageData()` 存快照,撤销时 `putImageData()` 恢复 3. **蒙版膨胀(dilate)**:用户涂抹往往只盖住物体中心,提交前程序性外扩几个像素(专业工具里的「Expand Mask」按钮就是这个),Python 端可用 `PIL.ImageFilter.MaxFilter` 或 OpenCV `cv2.dilate` 实现 4. **半透明预览**:给用户看的涂抹高亮(如红色半透明)画在**另一个预览层**上,导出的蒙版层保持纯粹的"不透明 / 透明"二值 ### 进阶:点选 / 文字自动出蒙版 涂抹式再往前一步,就是把"人手涂"换成"模型算": ```text theme={null} 用户点一下物体 → SAM 输出该物体的像素级轮廓蒙版 用户输入「左边的杯子」 → SAM 3 按文字概念找出所有匹配实例 程序自动 dilate + 羽化 → 提交 /v1/images/edits ``` 这正是 Inpaint Anything、ComfyUI Mask Editor 等工具的做法:**分割模型负责"准",笔刷负责"改"**——先一键生成精确蒙版,再用笔刷做加减微调(Add / Trim mask by sketch)。自建产品时,把 SAM 部署为后端服务、前端保留涂抹画布做兜底微调,是当前体验最好的组合。 ## 多参考图 + 蒙版 典型场景:换装(第一张是人物原图,后面是款式 / 材质参考图,蒙版标记衣服区域): ```python theme={null} import base64 from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1" ) with open("person.png", "rb") as person, \ open("clothes_reference.png", "rb") as clothes, \ open("fabric_reference.png", "rb") as fabric, \ open("mask.png", "rb") as mask: result = client.images.edit( model="gpt-image-2.5-sunburst", image=[person, clothes, fabric], mask=mask, prompt=( "第一张图片是需要编辑的主体。" "仅修改第一张图片蒙版透明区域中的衣服。" "使用第二张图片的服装款式和第三张图片的面料质感," "保持第一张图片的人脸、发型、姿势、身体比例、背景和光线不变。" ), quality="high", size="1024x1536", output_format="png", ) with open("result.png", "wb") as output: output.write(base64.b64decode(result.data[0].b64_json)) ``` 多图时必须在 prompt 中**清楚描述每张图的用途**(第一张是主体、第二张是款式参考、第三张是材质参考),否则模型可能混淆图片角色。 ## 严格保持蒙版外不变(像素级后处理) 由于模型可能轻微修改蒙版外内容,对像素精度要求高的场景(商品图、证件版式、固定 UI 截图),可以在生成后把蒙版外区域强制替换回原图: ```python theme={null} from PIL import Image, ImageFilter original = Image.open("input.png").convert("RGBA") edited = Image.open("edited.png").convert("RGBA") mask = Image.open("mask.png").convert("RGBA") if edited.size != original.size: edited = edited.resize(original.size, Image.Resampling.LANCZOS) # 原蒙版 Alpha:0 = 编辑区,255 = 保留区 alpha = mask.getchannel("A") # 反转后:255 = 使用编辑结果,0 = 使用原图 edit_area = alpha.point(lambda value: 255 - value) # 轻微羽化,避免边缘太硬 edit_area = edit_area.filter(ImageFilter.GaussianBlur(radius=3)) final = Image.composite(edited, original, edit_area) final.save("final.png") ``` 最终效果:蒙版内部采用 AI 编辑结果,蒙版外部恢复成原始图片,边界轻微羽化融合。 ## 常见错误排查 常见原因: * 蒙版不是有效 PNG,或文件内容损坏 * 扩展名是 PNG,实际编码不是 PNG * 图片模式异常(CMYK、调色板模式、缺 Alpha) * 上传时 MIME 类型错误 * 文件流在请求前已被读取完毕或关闭 统一转码可解决大多数问题: ```python theme={null} from PIL import Image Image.open("input_source.jpg").convert("RGBA").save("input.png") Image.open("mask_source.png").convert("RGBA").save("mask.png") ``` 哪怕只差 1 像素也会报错。修正: ```python theme={null} from PIL import Image image = Image.open("input.png") mask = Image.open("mask.png").convert("RGBA") mask = mask.resize(image.size, Image.Resampling.NEAREST) mask.save("mask_fixed.png") ``` `RGB` / `L` / `P` 模式都不行,必须是 `RGBA`。用上文「方法二」把黑白蒙版转换成 Alpha 蒙版。 这是两个不同的透明,容易混: ```text theme={null} 蒙版透明:标记「这块允许模型改」,由 mask 文件的 alpha 通道表达 输出透明:成图本身带 alpha 通道,由 background 参数控制 ``` 两者**可以同时用**:一边传带 alpha 的 `mask` 标记编辑区,一边传 `background: "transparent"` 让成图输出透明底,实测不冲突。输出透明只有 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` 支持,`gpt-image-2` 自 2026-09-30 起已被官方关停。 输出透明要求 `output_format` 是 `png` 或 `webp`,配 `jpeg` 会 400(jpeg 没有 alpha 通道)。完整说明见 [怎么生成透明背景的图片](/faq/image-transparent-background)。 GPT Image 系列固定返回 Base64 数据,`response_format` 只适用于旧的 DALL·E 2 行为。正确读取方式: ```python theme={null} result.data[0].b64_json ``` 通常是手动设置了 `Content-Type` 头导致 boundary 丢失,或中间层把 multipart 请求解析成 JSON 后再转发。让 HTTP 客户端自动生成 multipart 头即可。 ## 尺寸参数 `gpt-image-2` 支持灵活尺寸,需同时满足: ```text theme={null} 宽和高都必须是 16 的倍数 最长边不超过 3840px 长宽比不超过 3:1 总像素不低于 655,360 总像素不超过 8,294,400 ``` 常用尺寸:`1024x1024`、`1536x1024`、`1024x1536`、`2048x2048`、`2048x1152`、`3840x2160`、`2160x3840`、`auto`。方形图片通常生成更快。 ## 生产环境请求模板 ```python theme={null} result = client.images.edit( model="gpt-image-2.5-sunburst", image=open("input.png", "rb"), mask=open("mask.png", "rb"), prompt=""" 仅编辑蒙版透明区域。 编辑任务: 把透明区域内的白色马克杯替换成一束白色郁金香。 必须保持: - 原有构图不变 - 相机位置和镜头透视不变 - 桌面及背景不变 - 光线方向和色温不变 - 蒙版外人物和物品不变 - 输出保持真实摄影风格 融合要求: 花束需要自然位于原马克杯所在位置,产生符合现场光线的阴影、 反射和接触关系,不要增加其他物体。 """, size="1536x1024", quality="high", output_format="png", ) ``` ## 相关页面 完整参数说明与在线调试 Playground 模型能力、定价与版本说明 官方参考资料(请复制到浏览器访问): * 模型说明:`developers.openai.com/api/docs/models/gpt-image-2.5-sunburst`、`developers.openai.com/api/docs/models/gpt-image-2` * 图像编辑 API Reference:`developers.openai.com/api/reference/python/resources/images/methods/edit/` * 图像生成指南:`developers.openai.com/api/docs/guides/image-generation` # GPT-Image-2.5 / 2 生图/编辑 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2/overview OpenAI GPT-Image 2.5 / 2 系列官转接入:gpt-image-2.5-flare 速度优先、gpt-image-2.5-sunburst 编辑精度优先、gpt-image-2 仍可用,三款同价同参数。原生 2K/4K 任意分辨率,参考图自动高保真,支持文生图、参考图编辑、多图融合、mask 局部重绘。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 ## 概述 本页覆盖 OpenAI **GPT-Image 2.5 / 2 系列**三款官转模型:**`gpt-image-2.5-flare`**(速度优先)、**`gpt-image-2.5-sunburst`**(画质与编辑精度优先)与上一代 **`gpt-image-2`**。2.5 于 2026-09-08 发布,画质高于 gpt-image-2、编辑更准、`quality` 新增 `xhigh` / `max` 两档,**价格与参数与 gpt-image-2 完全相同**。系列共同特性:**任意合法分辨率(含 2K / 3840×2160 4K)**、**参考图自动高保真**、**按 token 计费**。API易 网关完整兼容 OpenAI Images API,OpenAI 官方 SDK 把 `base_url` 指过来即可零代码改动直连。 **🎨 核心亮点**:原生支持任意合法分辨率(最大 3840×2160 4K)+ 参考图编辑自动启用 high-fidelity + 中文提示词原生支持 + 2.5 新增 `xhigh` / `max` 画质档。**适合需要精确控制 size / quality、要求与 OpenAI 官方一致、要 4K 出图**的生产场景;文生图默认选 `gpt-image-2.5-flare`,改图选 `gpt-image-2.5-sunburst`。 `/v1/images/generations`,输入文本提示词生成图片,支持 size / quality / output\_format。 `/v1/images/edits`,multipart 上传参考图(最多 16 张)+ 编辑/融合指令,支持 mask 局部重绘。 ## 让 AI Agent 帮你接入 帮我在当前项目里接入 / 排查 GPT-Image 2.5 系列的「文生图 + 图片编辑」:文生图用 `gpt-image-2.5-flare`,图片编辑用 `gpt-image-2.5-sunburst`(两款与 `gpt-image-2` 同价同参数,模型名做成配置项方便切换)。 先读文档再动手:抓 [https://docs.apiyi.com/api-capabilities/gpt-image-2/overview.md](https://docs.apiyi.com/api-capabilities/gpt-image-2/overview.md) 拿到本页纯文本版;需要更细的参数说明时,text-to-image 和 image-edit 两页同样在地址后加 `.md` 即可。 接入要求: 1. 超时:客户端 timeout 提到 360 秒兜底。图片接口是同步调用,没有任务 ID,客户端一断连结果就丢了、但这次请求照样计费,所以宁可多等也不要过早超时。反向代理、网关、Serverless 执行上限这些中间层也要一起放宽,任何一层小于生成时间都会掐断请求。 2. 返回渲染:`/v1/images/generations` 固定返回 base64(`b64_json`,不带 `data:` 前缀),不是 URL。前端要能把 base64 渲染成图片展示,并提供「保存到本地」。不要传 `response_format`,传了直接报 400;也不要传 `input_fidelity`。 3. 上传压缩:调 `/v1/images/edits`(multipart,最多 16 张参考图)前先压缩参考图——超过 1.5MB 才处理,长边等比缩到 2048px 以内(不放大小图),以质量 0.9 重编码、保持原格式;多图时合计控制在 6MB 以内。某张图压缩失败就回退用原图继续,不要因为压缩失败中断整个请求。 4. 质量参数:`quality` 必传,默认 `medium`,前台界面加一个质量下拉(`low` / `medium` / `high` / `xhigh` / `max`,后两档仅 2.5 模型接受、成本更高)。不要传 `auto`——它是动态推理档,费用和耗时都会漂移。注意 2.5 的档位重新分过级:2.5 的 `high` 只等于 `gpt-image-2` 的 `medium`,从 `gpt-image-2` 迁移时不要原样照搬 `quality`,同预算要改传 `max`。 5. 生产环境把模型名锁到日期快照 `gpt-image-2.5-flare-2026-09-08` / `gpt-image-2.5-sunburst-2026-09-08`,别名指向变化时不会被动跟着变。 6. Key 从环境变量 `APIYI_API_KEY` 读,base\_url 用 [https://api.apiyi.com/v1,不要硬编码进代码、也不要提交进](https://api.apiyi.com/v1,不要硬编码进代码、也不要提交进) git。 7. 改完真跑一次文生图 + 一次图片编辑,把出图结果和这两次调用的花费贴给我。 | 要求 | 挡掉的坑 | | - | - | | timeout 提到 360 秒 | 主流 HTTP 客户端默认 30-60 秒超时,会在服务端还在正常出图时掐断请求,而**断连的请求照常计费**。详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices) | | 渲染 base64 | 本模型不返回 URL,按 `data[0].url` 取值只会拿到空值 | | 不传 `response_format` | 目前最高频的 400 报错,见本页[常见问题](#常见问题) | | 上传前压缩 | 手机原图动辄 4-5MB,base64 编码后还会再膨胀约 33%。压缩标准见 [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution) | | 显式传 `quality` | `auto` 是动态推理档,同一条提示词的费用和耗时会在档位之间漂移 | | 迁移不照搬 `quality` | 2.5 的 `high` = `gpt-image-2` 的 `medium`,照搬会掉一档 | | 锁日期快照 | 别名指向新快照时行为可能变,生产环境不该被动跟随 | ## 为什么选 API易 的 GPT-image-2 官转? 对标 OpenAI 官方通道,针对企业生产场景在 **稳定性**、**成本**、**接入体验** 三方面做了深度优化: 严格走 OpenAI 官方转发链路,请求和响应 **100% 与 OpenAI 官方一致**——字段、错误码、模型行为完全相同,质量无损、无偷跑风险。 不受 OpenAI 官方 **Tier 等级** 对 RPM / TPM 的硬限,企业量级请求可线性放大,批量生图与高峰场景更从容。 默认单价与 OpenAI 官方一致,叠加 [充值加赠活动](/faq/recharge-promotions) **最低可享 85 折**,长期使用成本显著下降。 **无需海外服务器或代理**,国内机房、家宽网络、海外节点均可直连 `api.apiyi.com`,延迟稳定、免去出海改造。 官逆 [`gpt-image-2-all`](/api-capabilities/gpt-image-2-all/overview)(\$0.025/张统一价)可无缝切换,另有性价比标杆 [Nano Banana Pro / 2](/api-capabilities/nano-banana-2-image/overview),按场景自由组合。 团队深耕图像生成场景,具备丰富的选型、调优与集成经验,可为企业客户提供从 PoC 到生产上线的完整技术支持。 ## 模型选型:flare / sunburst / gpt-image-2 2026-09-08 起,本组文档覆盖 OpenAI GPT-Image 2.5 / 2 三款模型。**三款同价、同参数、同分组、同端点**,切换只改 `model` 字段: | 对比项 | `gpt-image-2.5-flare` | `gpt-image-2.5-sunburst` | `gpt-image-2` | | - | - | - | - | | 定位 | 速度优先的小模型,OpenAI 建议的日常默认 | 画质优先的基座模型,OpenAI 目前最强的生成 / 编辑模型 | 上一代单一旗舰,仍可用 | | 画质 | 高于 gpt-image-2 | 两款中最高 | 基准 | | 时延(1024² 实测 2026-09-09) | `low` 10 秒 / `high` 21 秒 / `max` 45 秒;OpenAI 称比 gpt-image-2 最多低 50% | `low` 14 秒 / `high` 42 秒 / `max` 142 秒 | `low` 21 秒 / `high` 224 秒 | | 编辑与多轮一致性 | 提升 | 提升更明显,主体保留、多轮记忆更好 | — | | `quality` 档位 | 六档,新增 `xhigh` / `max` | 六档,新增 `xhigh` / `max` | `low` / `medium` / `high` / `auto` | | 当前快照 | `gpt-image-2.5-flare-2026-09-08` | `gpt-image-2.5-sunburst-2026-09-08` | `gpt-image-2-2026-04-21` | | 适合 | 批量出图、社交内容、商品图、时延敏感场景 | 多轮精修、成品级营销物料、多图融合 | 已接入且不想改动的存量项目 | **怎么选**:文生图默认用 `gpt-image-2.5-flare`,编辑 / 多图融合用 `gpt-image-2.5-sunburst`;生产环境锁定日期快照,避免别名指向变化时被动跟着变。已在用 `gpt-image-2` 的代码只需改一个模型名即可升级,其余参数、价格、超时经验都沿用。发布解读见 [GPT-image-2.5 上线:Flare 更快、Sunburst 更准](/news/gpt-image-2-5-launch)。 ## 核心特性 支持任意合法尺寸输出,预设涵盖 1K / 2K / 3840×2160 4K,自定义尺寸只需满足边长 16 倍数、比例 ≤ 3:1 等基本约束。 编辑场景下自动启用 high-fidelity,参考图细节、人物身份、文字内容保留度大幅提升。**无需也不能**再传 `input_fidelity`。 1024×1024 高画质从 1.5 时代的 \$0.25 级别降到 \$0.211/张,2K/4K 按 token 实计但同样下行,长期使用成本明显降低。 中文提示词原生支持,招牌、海报、UI 截图等场景的中英文文字渲染稳定,`high` 档位下精细文字几乎不糊。 `image[]` 数组最多接受 16 张参考图,prompt 中可用「图1/图2/图3」明确指代。 支持上传带 alpha 通道的 mask 图,透明区域为重绘区,不透明区域保留原图。 支持 png(默认)/ jpeg / webp,jpeg/webp 可设 `output_compression` 控制体积。 把 `base_url` 指向 `https://api.apiyi.com/v1` 即可用 OpenAI 官方 SDK 直接调用,零代码改动迁移。 ## 模型定价 API易 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` / `gpt-image-2` 三款(Default 分组)**单价相同,且与 OpenAI 官网完全一致**,折扣体现在充值活动上——**充值 100 美金即送 10%,最多可送 20%**,📖 [了解充值活动](/faq/recharge-promotions)。 ### 按 token 计费单价(与官网一致) 按 token 计费,一次请求 = 文本输入 + 图片输入 + 图片输出三段 token 之和: | 计费项 | 单价(每 1M tokens) | 说明 | | - | - | - | | 文本输入(Text Input) | \$5.00 | prompt 文字部分 | | 图片输入(Image Input) | \$8.00 | 编辑/融合场景的参考图,按 Vision 规则换算 token | | 图片输出(Image Output) | \$30.00 | **成本大头**,由 size × quality 决定 token 量 | | 缓存输入(Cached Input) | 文本 \$1.25 / 图片 \$2.00 | 已配置,但高并发下命中率有限,详见 [FAQ](#常见问题) | **图片输入为什么更贵?** 图片输入单价 \$8.00 / 1M tokens,是文本输入 \$5.00 / 1M tokens 的 **1.6 倍**(官方定价如此)。这也是为什么带参考图的编辑 / 多图融合请求,输入成本会明显高于纯文生图——参考图本身按 Vision 规则换算成大量 image token,且每个 token 的单价还比文本 token 高六成。 ### 每张成本速查(官方按量定价表,gpt-image-2) 1K 预设尺寸下,`gpt-image-2` 每张输出图的典型成本(2.5 两款同名档位的 token 量不同,见下一节): | 画质 | 1024×1024 | 1024×1536 | 1536×1024 | | - | - | - | - | | Low | \$0.006 | \$0.005 | \$0.005 | | Medium | \$0.053 | \$0.041 | \$0.041 | | High | \$0.211 | \$0.165 | \$0.165 | **计费说明**: * 单价与 OpenAI 官网一致,叠加 [充值加赠](/faq/recharge-promotions)(充 \$100 送 10%,最高 20%)后实际成本低于官方直连 * 2K / 4K 无固定每张价,按输入 + 输出 token 实计 * 编辑场景因强制高保真,输入 token 明显高于纯文生图 * 流式出图(`stream: true` + `partial_images: N`)每张 partial 额外消耗 100 个输出 image token * 对比 `gpt-image-1.5`,同档同尺寸 `gpt-image-2` 成本低约 20-30% ### 2.5 两款的画质档位与实测成本(2026-09-09 实测) **同名档位在 2.5 与 gpt-image-2 上的 token 量不同**:2.5 把画质梯子重新分了级,`low` 不变,2.5 的 `high` 等于 gpt-image-2 的 `medium`,2.5 的 `max` 才等于 gpt-image-2 的 `high`。下表为 1024×1024 文生图、同一条提示词、串行各跑一次的 `usage.output_tokens` 与按 \$30 / 1M 折算的输出费用(flare 与 sunburst 的 token 量逐档相同,只有耗时不同): | quality | gpt-image-2 | gpt-image-2.5-flare | gpt-image-2.5-sunburst | | - | - | - | - | | `low` | 196 tokens ≈ \$0.006(21 秒) | 196 tokens ≈ \$0.006(10 秒) | 196 tokens ≈ \$0.006(14 秒) | | `medium` | 1,756 tokens ≈ \$0.053(63 秒) | 439 tokens ≈ \$0.013(10 秒) | 439 tokens ≈ \$0.013(19 秒) | | `high` | 7,024 tokens ≈ \$0.211(224 秒) | 1,756 tokens ≈ \$0.053(21 秒) | 1,756 tokens ≈ \$0.053(42 秒) | | `xhigh` | ❌ 400 | 3,122 tokens ≈ \$0.094(31 秒) | 3,122 tokens ≈ \$0.094(60 秒) | | `max` | ❌ 400 | 7,024 tokens ≈ \$0.211(45 秒) | 7,024 tokens ≈ \$0.211(142 秒) | **从 gpt-image-2 迁移时不要原样照搬 `quality`**:同样写 `high`,2.5 的输出 token 只有 gpt-image-2 的四分之一,画质档位也对应更低;想要与 gpt-image-2 `high` 同等的 token 预算,2.5 要传 `max`。反过来,同样的预算下 2.5 的 `high` / `xhigh` 给了两个更便宜的中间档。上生产前用自己的提示词跑一轮 `usage.output_tokens` 核对,2K / 4K 尺寸按像素比例外推。 ### 多图输入的价格影响(2026-07 实测) 客户常问:「参考图是每张定量收费,还是图片越大消耗越多?」答案是**两者都影响,且张数严格线性累加**。`gpt-image-2` 对输入图固定高保真处理(`input_fidelity` 不可调,传了直接 400),每张参考图按尺寸/宽高比换算成 image token。以下为控制变量实测(编辑接口,2026-07-15): | 参考图输入 | `image_tokens` | 输入费用(\$8/M) | | - | - | - | | 1 张 512×512 | 1024 | ≈\$0.0082 | | 1 张 1024×1024 | 1024 | ≈\$0.0082 | | 1 张 2048×2048 | 1521 | ≈\$0.0122 | | 1 张 4096×4096 | 1521 | ≈\$0.0122 | | 1 张 1024×1536(竖版) | 1536 | ≈\$0.0123 | | **4 张 1024×1024** | **4096(= 4 × 1024)** | ≈\$0.0328 | 三个规律: 1. **张数严格线性**:N 张参考图 ≈ N × 单张 token。16 张 1024² 参考图 ≈ 16384 tokens ≈ \$0.13——已与一张 `high` 输出(\$0.211)同量级,多图融合时不可忽略。 2. **尺寸有下限也有封顶**:小于等于 1024² 的方图统一按 1024 tokens 计(把图缩到 512 **不省钱**);2048² 与 4096² 同为 1521 tokens(超大图先缩放再换算,**封顶**)。单张参考图的 token 大致在 800-1600 区间浮动(含宽高比影响)。 3. **token 由像素尺寸决定,与文件体积无关**:把图压到 1.5MB 是为了上传稳定和速度,**不会减少 image token**;反过来,费用也不会因为你传了 50MB 的原图而爆炸(有封顶)。 成本视角的直觉:`low` 输出(196 tokens ≈ \$0.006)时,1 张参考图的输入费(≈\$0.008)反而比输出还贵;`high` 输出(≈\$0.211)时 1 张参考图只占约 4%。**输出的尺寸和画质永远是价格的最大变量**,参考图张数是第二变量。 ### 2K/4K 成本预估(像素比例外推,⚠️ 非官方固定价) OpenAI 官方只公布了 1K 尺寸的固定每张单价表,2K/4K **没有官方逐尺寸定价**。下表是 API易 按「像素总数比例」从上方 1K 官方单价**外推**的预估值,仅供预算参考: | 画质 | 2048×2048(2K 方形) | 2048×1152(2K 横版) | 3840×2160 / 2160×3840(4K) | | - | - | - | - | | Low | ≈\$0.024 | ≈\$0.008 | ≈\$0.026 | | Medium | ≈\$0.212 | ≈\$0.062 | ≈\$0.216 | | High | ≈\$0.844 | ≈\$0.248 | ≈\$0.870 | **这不是官方定价表,是估算值。** 计算方法:以 1K 官方表中同长宽比的行为基准,按目标尺寸与基准尺寸的像素总数比例线性外推(如 2048×2048 像素数是 1024×1024 的 4 倍,估算成本也 ×4)。实际出图的 image token 数由模型按内容复杂度动态决定,并非严格线性,**真实成本务必以每次调用响应里的 `usage.output_tokens` 为准**(见下方「如何查看每次调用的真实 token 数」)。`high` + 超过 2560×1440 的尺寸目前还是官方标记的实验档位,估算误差可能更大。 ### 与 SaaS 套餐 / 积分制计费方式的区别 图像生成类工具厂商常见两种计费模式: * **包月套餐(订阅制)**:固定月费换取"每月可生成 N 张"的额度。这个额度背后是运营商按平均用量预估的**超卖定价**——套餐设计时就假设不是所有人都会用满,宣传的"单张成本"只是套餐价除以额度上限的理论值,跟你真实一张图的实际生成成本没有必然关系。 * **积分包 / 动态点数计费**:把不同画质、尺寸的生成任务换算成不透明的"积分"消耗,本质上也是按量计费,只是用积分做了一层包装,掩盖了底层真实的 token 用量。 API易走的是**官转 API + 按 token 实际用量计费**:没有套餐额度,没有积分模糊层,每次调用的成本 = 实际消耗的 input/output token × 官方单价,可以精确核算到每一次调用,不存在套餐"多退少补"或"超额限流"的问题。 按量计费的代价是需要自己估算 / 监控用量,不像套餐那样有固定月度总价的确定性——好处是用多少花多少,没有闲置浪费。下面教你怎么从响应里直接拿到每次调用的真实 token 数,自己核算成本。 ### 如何查看每次调用的真实 token 数 `/v1/images/generations` 和 `/v1/images/edits` 的响应都带 `usage` 字段,**图片输入 token 和文本输入 token 是分开返回的**,不用估算,直接读字段就能精确核算这一次调用的真实成本。下面是一次真实编辑请求(带 1 张参考图)返回的完整 `usage`(实测抓包): ```json theme={null} { "data": [ { "b64_json": "..." } ], "usage": { "input_tokens": 848, "input_tokens_details": { "image_tokens": 832, "text_tokens": 16 }, "output_tokens": 196, "output_tokens_details": { "image_tokens": 196, "text_tokens": 0 }, "total_tokens": 1044 } } ``` | 字段 | 含义 | | - | - | | `usage.input_tokens_details.text_tokens` | 输入 prompt 文本消耗的 token 数,按 \$5.00 / 1M 计 | | `usage.input_tokens_details.image_tokens` | 参考图按 Vision 规则换算出的 token 数,按 \$8.00 / 1M 计;纯文生图(无参考图)时恒为 0 | | `usage.input_tokens` | 上面两项之和 | | `usage.output_tokens` | 输出图片的 token 数,由 `quality × size` 共同决定,是**成本大头**,按 \$30.00 / 1M 计,2K/4K 请求应重点关注这个值(`output_tokens_details.image_tokens` 与其相同,`text_tokens` 恒为 0) | | `usage.total_tokens` | 输入 + 输出总和 | 自行核算公式(精确版): ``` 成本 ≈ input_tokens_details.text_tokens × \$5.00 / 1,000,000 + input_tokens_details.image_tokens × \$8.00 / 1,000,000 + output_tokens × \$30.00 / 1,000,000 ``` 想看历史调用的真实 token 消耗和计费明细,也可以直接去控制台「日志」页面查:📖 [如何查看调用记录](/faq/call-logs)——日志详情里会把「输入价格 / 图片输入价格 / 输出价格」和对应的 token 数都列出来,跟接口里 `usage.input_tokens_details` / `usage.output_tokens_details` 是对应的。 ## 分组介绍 GPT-Image 2.5 / 2 系列三款模型的官转分组完全相同,可在后台「令牌设置 → 分组」中切换: | 分组 | 倍率 | 适用场景 | | - | - | - | | `Default` 默认分组 | 1.0x | 与 OpenAI 官方同价,资源宽裕时首选;高峰期可能并发紧张、出现 429 | | `image2Enterprise` 企业分组 | 1.2x | 默认分组紧张时的稳定过渡通道,稳定优先 | | `svip` 分组 | 见控制台 | 同样开放三款模型,倍率以控制台令牌页显示为准 | **1.2x 倍率怎么来的?** 基于"3000 美金单次充值大客户加赠 20% 后约等官网原价"的口径设定——平台不计税务成本,不赚钱也优先保障供给。默认分组不稳定时,把令牌切到 `image2Enterprise` 即可临时过渡使用。 令牌创建界面:计费模式选「按量优先」,分组选 image2Enterprise(1.2x),高速正价的 GPT image 2 企业分组 📖 分组上线公告:[/live/2026-04/image2-enterprise](/live/2026-04/image2-enterprise) ## 技术规格 | 维度 | 参数 | | - | - | | **模型名** | `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` / `gpt-image-2`(三款同参数,见上方「模型选型」一节) | | **速度** | 1024² 实测(2026-09-09):`gpt-image-2.5-flare` `low` 10 秒 / `high` 21 秒 / `max` 45 秒;`gpt-image-2.5-sunburst` 14 / 42 / 142 秒;`gpt-image-2` `low` 21 秒 / `high` 224 秒。高画质 + 2K/4K 更长 | | **输出分辨率** | 任意合法尺寸(1K/2K/4K,最大 3840×2160) | | **画质档位** | `auto` / `low` / `medium` / `high` / `xhigh` / `max`(`xhigh` / `max` 仅 2.5 两款接受) | | **输出格式** | `png`(默认)/ `jpeg` / `webp` | | **中文提示词** | ✅ 原生支持 | | **提示词上限** | 32,000 字符(原厂上限,按字符计),见 [长提示词篇](/api-capabilities/image-long-prompt) | | **单次出图数量** | 1 张(`n=1`) | | **参考图上限** | 16 张(`image[]`) | | **单图大小上限** | multipart 文件每张小于 50MB(png/jpg/webp);base64 data URL 字段约 20MiB,原图建议 15MB 以内 | | **mask 局部重绘** | ✅ 支持(要求带 alpha 通道,PNG 且小于 4MB) | | **透明背景** | ✅ 仅 2.5 两款支持(`background: "transparent"` + `png` / `webp`;`jpeg` 无 alpha 通道,与透明互斥;提示词别描述场景,否则参数压不过)。❌ `gpt-image-2` 已于 2026-09-30 被 OpenAI 官方关停,传了返回 400,见 [透明背景 FAQ](/faq/image-transparent-background) | | **响应字段** | `b64_json`(**纯 base64,无前缀**);**不接受 `response_format` 参数**,传了直接 400 | ## 端点一览 | 端点 | 用途 | Content-Type | | - | - | - | | `POST /v1/images/generations` | 文生图 | `application/json` | | `POST /v1/images/edits` | 参考图编辑 / 多图融合 / mask 重绘 | `multipart/form-data` | **域名选择**:`api.apiyi.com` 为主域名,也可使用 `b.apiyi.com` 等平台提供的其他网关域名,响应行为一致。 ## 尺寸(size)详解 ### 预设尺寸 | size | 含义 | 像素 | | - | - | - | | `auto` | 自适应(默认) | 模型决定 | | `1024x1024` | 方形 1:1 | 1K | | `1536x1024` | 横版 3:2 | 1K | | `1024x1536` | 竖版 2:3 | 1K | | `2048x2048` | 方形 1:1 | 2K | | `2048x1152` | 横版 16:9 | 2K | | `3840x2160` | 横版 16:9 | 4K | | `2160x3840` | 竖版 9:16 | 4K | ### 自定义尺寸约束 `gpt-image-2` 接受**任意合法尺寸**,只需同时满足: 1. **最大边 ≤ 3840px** 2. **两条边都是 16 的倍数** 3. **长短边比例 ≤ 3:1** 4. **总像素数 ∈ \[655,360, 8,294,400]**(下限约 0.65MP,上限约 8.3MP) **合法示例**:`1600x1200`、`1792x1024`、`2048x1536`、`3200x1800` **非法示例**:`1000x1000`(非 16 倍数)、`4000x4000`(超上限)、`3840x1000`(比例超 3:1) 超过 `2560×1440`(约 3.69MP)的输出目前官方标记为**实验性**,可能不稳定或出现质量波动。生产环境建议优先用预设尺寸:`2048x1152` / `2048x2048` / `3840x2160` 等。 ## 画质(quality)详解 ### 可选档位 | quality | 含义 | 说明 | | - | - | - | | `auto` | 自动(**默认**) | 不传 `quality` 时即为此值,由模型自动选档 | | `low` | 低画质 | 速度最快、成本最低,适合草稿 / 批量 | | `medium` | 中画质 | 日常 / 终稿的均衡选择 | | `high` | 高画质 | 文字、精细纹理、印刷场景;`gpt-image-2` 的最高档 | | `xhigh` | 超高画质(**2.5 新增**) | 仅 `gpt-image-2.5-flare` / `sunburst` 接受,输出 token 高于 `high` | | `max` | 最高画质(**2.5 新增**) | 仅 2.5 两款接受,成本与耗时最高,用于成品级终稿 | **默认是 `auto`,不是 `medium`。** 不传 `quality` 等同于传 `"quality": "auto"`,由模型自动选择合适的画质档位,**官方没有承诺它固定等同于 `medium`**。`auto` 选中的档位不可控,会直接影响出图成本、响应速度与计费稳定性。**需要控制成本和可预期性时,请显式传入 `low` / `medium` / `high` / `xhigh` / `max`,不要依赖 `auto`。** **不要传旧版 DALL·E 的 `standard` / `hd`。** `quality` 只接受 `low` / `medium` / `high` / `xhigh` / `max` / `auto` 六个官方枚举值,其中 `xhigh` / `max` 仅 2.5 两款接受。旧版 DALL·E 3 的 `standard` / `hd` 在不同后端渠道下行为不一致:有时直接 400 报错(`invalid_value`),有时被静默忽略、按 `auto` 档跑出结果(费用不可控)。请始终显式传官方枚举值之一。 **`quality` 是影响价格最大的参数,比 `size` 更显著。** 输出图片 token 量由 `quality × size` 共同决定,但 `quality` 的权重明显更高——同一尺寸下从 `low` 到 `high`,每张成本可相差 **30 倍以上**(参见上方「每张成本速查」表:gpt-image-2 的 1024×1024 从 `low` \$0.006 到 `high` \$0.211;2.5 两款从 `low` \$0.006 到 `max` \$0.211)。预算和选档时应**优先按 `quality` 评估成本**,再叠加 `size` 的影响。 ## 最佳实践 **对接经验:先用 `low` 跑通,再按需升档** 实测有客户首次接入就直接拉满 `quality=high` + 高分辨率,**单张耗时 ≈ 235 秒(约 4 分钟)**,一度误以为是接口卡住。`high` 模式推理复杂度最高,4K 场景甚至接近 5 分钟。**正式上线前请先用 `quality=low` 跑通整条链路**(鉴权、SDK、参数、超时、错误处理),确认功能 OK 后再按业务对画质的实际需求逐档升到 `medium` / `high`。 新接入时**优先用 `quality=low` + 预设尺寸**跑通整条链路(鉴权、参数、超时、错误处理)。`low` 速度比 `high` 快数倍,能快速暴露所有非画质相关的问题,避免被长耗时干扰排查。 8 个预设尺寸经过官方优化,速度和质量更稳定;自定义尺寸留给真有比例需求的场景。 草稿 / 批量 → `low`;默认 / 终稿 → `medium`;文字、精细纹理、印刷 → `high`。**注意 `low` ↔ `high` 不仅是画面精美度差异,还包含推理复杂度差异**——耗时差距可达数倍。 对最终展示无特别要求时,`output_format=jpeg` + `output_compression=85` 比 PNG 快且体积小一半以上。 文字渲染是主要卖点,但 low/medium 仍可能糊;招牌、海报类场景锁 `quality=high`。 单张上限 50MB(建议压到 1.5MB 以内),PNG/JPEG/WebP 均可;最多 16 张;prompt 里用「图1/图2」指代顺序。 透明背景只有 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` 支持(`gpt-image-2` 自 2026-09-30 起被官方拒绝,返回 400)。`background: "transparent"` + `png` / `webp` 只保证输出带 alpha 通道;**提示词或参考图一旦描述了完整场景(森林、房间、街道),模型会按语义把场景画出来**,参数拦不住,这就是「透明时好时坏」的主因,与 `quality` 无关(2026-09-11 实测:主体类提示词 16/16 透明,写了场景的 0/3,medium 与 high 一样)。句尾固定加 `isolated subject on a transparent background, no scenery, no ground`;参考图带背景时在编辑提示词里写明 `remove the background entirely, keep only the subject`。详见 [透明背景 FAQ](/faq/image-transparent-background)。 影响出图耗时最大的是 **`quality`** 与 **`size`**,尤其是 `quality`。建议按档位配置客户端超时: | quality | 推荐客户端超时 | 实测耗时区间 | | - | - | - | | `low` | ≥ **120 秒** | 通常 10–40 秒 | | `medium` | ≥ **240 秒** | 通常 30–90 秒 | | `high` | ≥ **600 秒**(兜底) | gpt-image-2 2K/4K 实测 3–5 分钟,长尾可达 235 秒以上;2.5 两款 1024² 实测 21–42 秒 | | `xhigh`(仅 2.5) | ≥ **300 秒** | 1024² 实测 31–60 秒(2026-09-09),2K/4K 更长 | | `max`(仅 2.5) | ≥ **600 秒**(兜底) | 1024² 实测 45–142 秒,token 量与 gpt-image-2 `high` 相同,2K/4K 按 gpt-image-2 `high` 的经验预留 | **`high` 模式务必配 600 秒兜底**,覆盖排队 / 长尾 / 服务抖动等各种异常情况;前端务必给进度反馈;服务端建议用任务队列解耦。 从 `gpt-image-1.5` 迁移:删掉 `input_fidelity`(强制高保真,传了会报错);要透明背景请迁到 2.5 两款(`gpt-image-2` 自 2026-09-30 起已不支持 `background: transparent`)。从 DALL·E 2/3 老代码迁移:**删掉 `response_format`**(GPT Image 系列不接受此参数,传了直接 400 `Unknown parameter: 'response_format'`,返回固定为 `b64_json`)。 ## 错误码与重试 | 状态码 | 含义 | 处理建议 | | - | - | - | | `400` | 参数非法(size 不合约束、传了不支持的字段等) | 按尺寸约束章节校验;**注意不要传** `response_format` / `input_fidelity`;`background: transparent` 配 `output_format: jpeg` 也会 400(jpeg 无 alpha 通道),改用 `png` / `webp`;`gpt-image-2` 传透明报 `Transparent background is not supported for this model.` 是官方已关停,换 2.5 两款;报 `Unknown parameter: 'response_format'` 直接删掉该参数即可,见 [FAQ](#常见问题);编辑接口报 `invalid_image_file` 多为手机原拍 MPO 图,见 [FAQ](#常见问题) | | `413` | 请求体过大(编辑接口多图 / 超大 PNG,实测请求体 80MB 以上开始出现,同体积并非必现) | 报错体是 `message: openai_error, code: bad_response_status_code`,由原厂链路返回,网关不会自动重试;把参考图压到单张 10MB 以内、总体 50MB 以内(原厂单图上限 50MB),也能把上传等待从几分钟降下来 | | `401` | 令牌无效 | 检查 Bearer Token | | `400` + `safety_violations=[...]` | 内容审核拦截(实测状态码是 400,不是 403) | 多数发生在成图之后、同一 prompt 时过时不过;`moderation: low` 只影响提示词侧审核。先给人物加明确服装,再一次只改一个词,见 [内容安全排查篇](/api-capabilities/image-safety-troubleshooting) | | `429` | 限流 / 余额不足 | 指数退避重试 | | `5xx` | 网关 / 后端错误 | 重试 1–2 次 | | 超时 | 长尾 | 客户端超时按 `quality` 分档:`low` ≥ **120 秒** / `medium` ≥ **240 秒** / `high` ≥ **600 秒**(high + 2K/4K 实测 3–5 分钟,长尾可达 235 秒以上) | **建议客户端**: * 请求超时按 `quality` 分档配置:`low` ≥ **120 秒** / `medium` ≥ **240 秒** / **`high` ≥ 600 秒**(兜底;实测 3–5 分钟,按 120/360 秒配会大量误超时) * **新接入先用 `quality=low` 跑通链路**,再按需升到 `medium` / `high` * 对 5xx 与超时做 **指数退避重试**(建议 2 次) * 记录响应头 `x-request-id` 方便排查 ## 常见问题 **删掉 `response_format` 参数即可,这是目前最高频的 400 报错。** `gpt-image-2`(及整个 GPT Image 系列)**不接受** `response_format`——返回格式固定为 `b64_json`,无需也不能指定。传了就会报: ```json theme={null} { "error": { "message": "Unknown parameter: 'response_format'.", "type": "invalid_request_error", "param": "response_format", "code": "unknown_parameter" } } ``` 这个参数是 **DALL·E 2/3 时代**的遗留(当年可选 `url` / `b64_json`),网上大量旧示例代码和部分第三方库默认会带上它。迁移到 `gpt-image-2` 时把这个字段删掉,直接读 `data[0].b64_json`(纯 base64,decode 后即为图片文件)。该错误在入口参数校验阶段返回,**不计费**。 如果业务确实需要拿到**图片 URL** 而不是 base64: * 官方 `gpt-image-2` 没有 URL 输出,需自行 decode 后上传到自己的对象存储 * 或改用官逆 [`gpt-image-2-all`](/api-capabilities/gpt-image-2-all/overview),它支持 `response_format: "url"`,返回 24 小时有效的 CDN 链接 **要**。`gpt-image-2` 返回的是**纯 base64 字符串**(无前缀),与 `gpt-image-2-all` 不同。客户端有两种用法: * **写文件**:`base64.b64decode(b64_str)` 后写入磁盘 * **浏览器渲染**:`img.src = 'data:image/png;base64,' + b64_str` 自行拼前缀 若你的代码沿用了 1.5 时代的"已含前缀"假设,会拿到损坏的 data URL,请显式判断。 `gpt-image-2` **强制启用** high-fidelity 处理参考图,不再接受 `input_fidelity` 参数。从 1.5 迁移时把这个字段移除即可,无需替换。 用 `gpt-image-2.5-flare` 或 `gpt-image-2.5-sunburst`,传 `background: "transparent"`,配合 `output_format` 为 `png` 或 `webp`,返回的就是带 alpha 通道的透明底图,不需要任何后处理抠图。文生图、图片编辑两条路都支持。**`gpt-image-2` 不行**:OpenAI 已于 2026-09-30 关停它的透明背景,传了直接 400,换模型名即可,其它参数不用改。 两个边界:`jpeg` 没有 alpha 通道,与透明互斥(会 400);编辑接口传透明时是**重绘去背**而不是沿原图轮廓精确抠像,主体细节会有变化,要像素级还原请自行用 `rembg` / `PIL` / `sharp` 处理。 第三个边界最常被忽略:**提示词或参考图描述了场景,模型就会把场景画出来**,参数压不过,升 `quality` 也没用。只写主体,句尾加 `isolated subject on a transparent background, no scenery, no ground`。 完整说明与各模型支持情况见 [怎么生成透明背景的图片](/faq/image-transparent-background)。 1 张(`n=1`)。如需 N 张请客户端并行 N 次调用。每次独立按 token 计费。 输出分辨率越高、画质档位越高,需要生成的 image token 越多,自然耗时越长。**实测有客户在 `quality=high` + 高分辨率下耗时 ≈ 235 秒(约 4 分钟)单张**,`3840×2160` + `high` 长尾可接近 5 分钟。建议: * **新接入先用 `quality=low` 跑通链路**,确认正常后再按业务需求升档 * 客户端超时按档位配:`low` ≥ **120 秒** / `medium` ≥ **240 秒** / **`high` ≥ 600 秒**(兜底) * 前端显示"生成中"进度反馈 * 不需要 4K 时仍用 1024×1024 / 1536×1024 等 1K 预设 因为 `gpt-image-2` 对参考图自动启用 high-fidelity 处理,参考图本身会按 Vision 计费规则换算成大量输入 token。带图编辑的输入 token 明显高于文生图,预算时要留足。 **根因:`quality` 传了 `auto`(或没传)。** 有客户反馈「尺寸、分辨率、参考图完全一样,价格却忽高忽低」,定位后发现请求里 `size` 和 `quality` 都用了 `auto`。 **问题出在 `quality: auto`**:自动模式下,模型会**自行理解需求、临时选择不同的质量档位**去创作。档位不同 → 输出的 image token 数量不同 → 价格自然不同。下面是三次「输入完全一致(input 都是 1061 token)」却价格相差数倍的真实账单: | 耗时 | 输入 token | 输出 token | 单次价格 | | - | - | - | - | | 53 秒 | 1061 | 1286 | \$0.055082 | | 135 秒 | 1061 | **5146** | **\$0.194042** | | 68 秒 | 1061 | 1287 | \$0.055118 | 第二次 `auto` 被模型判定为更高画质,输出 token 飙到 5146,价格也随之涨到约 3.5 倍。 **解决办法:不要让 `quality` 用 `auto`,显式传 `low` / `medium` / `high`。** 固定档位后,相同输入的输出 token 量和价格才稳定可预期。详见上方「画质(quality)详解」章节。 **已配置,但请勿把缓存折扣纳入成本预算。** 官方缓存单价为文本 \$1.25 / 图片 \$2.00(每 1M tokens),API易 通道同样配置了缓存计费,命中时按缓存价结算。 但需要如实同步一个客观限制:API易 为承载高并发,请求会分散到多个 OpenAI 上游账号(单个 OpenAI Tier-5 账号的 RPM 仅 250)。OpenAI 的提示词缓存不跨账号共享,高并发下同一前缀的请求未必落在同一账号上,**缓存可能命中不了**。 好在影响很小:图像生成的成本大头是图片输出 token(\$30 / 1M),缓存折扣只作用于输入端,对单张图总成本的影响本就式微。建议按**全正价输入**做预算,缓存命中时视为额外节省。 `gpt-image-2` 图片编辑接口(`/v1/images/edits`)最多支持上传 **16 张**参考图: * **multipart/form-data 文件上传**:每张图片**小于 50MB**,支持 `png` / `jpg` / `webp` * **base64 data URL 方式**:字段长度限制约 **20MiB**(schema `maxLength: 20971520`,是字符串字段限制,**不等同于** multipart 的 50MB 上限),实际原图建议控制在 **15MB 以内** * **mask 文件**:单独限制为 **PNG 且小于 4MB** 实践建议:不要多张大图同时顶满上限——请求体过大易在网关 / 超时层面失败,每张先压到 **1.5MB 以内**最稳,且输出画质与输入体积无关。 这个报错(`code: invalid_image_file`)的含义是:**第 N 张参考图不是标准的 png / jpg / webp 格式**(序号从 1 开始,按序号定位问题图)。 最常见的根因是手机原拍照片的 **MPO 格式**:华为 Mate 系列等机型直出的 `.jpg` 内嵌 HDR 增益图副帧,实为多帧 JPEG 容器(MPO)。文件头同为 `FFD8`,扩展名和 `file` 命令都显示 JPEG,肉眼无法分辨——2026-07 实测 MPO 图必被拒,重编码为标准 JPEG/PNG 后**原分辨率上传即成功**(与尺寸、`image[]` 字段名、`quality`/`size` 参数均无关)。该错误在入口校验阶段返回,**不计费**。 **修复**:上传前用 Pillow 重编码(`Image.open(f).format` 返回 `"MPO"` 即需转换): ```python theme={null} from PIL import Image im = Image.open("photo.jpg") im.load() # MPO 只取第一帧 im.convert("RGB").save("photo_fixed.jpg", quality=92) ``` 完整说明与判别方法见 [图片编辑 API「参考图格式要求与预处理」](/api-capabilities/gpt-image-2/image-edit#参考图格式要求与预处理)。 * 与原图**相同尺寸**,**PNG 格式**,单张**小于 4MB** * **必须带 alpha 通道**:透明区域(alpha=0)= 要重绘的部分,不透明区域 = 保留 * 仅对第一张 image 生效 * mask 是"软引导"非精确边界,模型可能在蒙版周围扩展 / 收敛 | 选 | 场景 | | - | - | | **gpt-image-2.5-flare / sunburst / gpt-image-2**(官方) | 需要精确控制 size / quality、要求与 OpenAI 官方完全一致、要 4K 出图、要 mask 局部重绘;三款中 flare 最快、sunburst 编辑最准 | | **gpt-image-2-all**(官逆) | 追求统一价 \$0.025/张、约 30–60 秒出图、参数极简、对一致性 / 中文文字要求高 | 可以,零代码改动。把 `base_url` 指向 `https://api.apiyi.com/v1`,`api_key` 设为 API易 令牌即可: ```python theme={null} from openai import OpenAI client = OpenAI(api_key="sk-your-key", base_url="https://api.apiyi.com/v1") resp = client.images.generate(model="gpt-image-2.5-flare", prompt="...", size="2048x1152", quality="high") ``` **不支持**。`gpt-image-2` 走 OpenAI 官方同步端点,请求一旦提交就会跑到结束,无法发出"取消"指令。客户端即使断开连接,服务端仍会把这次生成完整跑完并照常计费。建议在客户端做好超时控制,不要依赖"断连就不收费"的假设。 默认 **100 RPM**(每分钟 100 次请求)。实际可用 RPM 还会受**全平台总并发**动态调整。如果你的业务需要更高配额,请联系我们告知预估 QPS / RPM,可单独申请扩容资源。 **不支持**。`gpt-image-2` 严格与 OpenAI 官方一致——只有同步调用,发起请求后阻塞等待结果(`high` 档 + 4K 实测 1–2 分钟)。如需异步队列、回调通知等能力: * 在业务层用任务队列(Celery / BullMQ 等)自行封装异步 * 或改用 [`gpt-image-2-all`](/api-capabilities/gpt-image-2-all/overview),出图约 30–60 秒,更适合前端轮询 有,**32,000 字符**,是 OpenAI Images API 的原厂上限,按字符不按 token,API易 官转不额外收紧。网页版看起来没限制,是因为贴进去的资料由对话模型先读、再自己写一条短提示词去调图片工具,图片模型从没直面那份资料。有品牌手册、包装规格这类长资料时,先用文本模型提炼成 1K~3K 字符的结构化提示词再出图,做法见 [长提示词篇](/api-capabilities/image-long-prompt)。 这类拦截多数发生在**成图之后**:图先生成,再被输出侧分类器拦下,所以失败耗时和成功一样长,同一 prompt 也会时过时不过。`moderation: low` 只降低提示词侧审核强度,对它无效。最常见的原因是人物只写了气质没写服装,模型自由发挥后越线:**先给人物加一句明确的服装描述**(其余不动),不行再一次只改一个词做消融。完整案例与 20 次实测记录见 [内容安全排查篇](/api-capabilities/image-safety-troubleshooting)。 **不会**。OpenAI 自带内容安全审核,触发审核或参数非法时会直接返回 `400` 错误并**不计费**。典型响应: ```json theme={null} { "status_code": 400, "error": { "message": "Your request was rejected by the safety system. ...", "type": "shell_api_error", "code": "moderation_blocked" } } ``` 其它常见的 0 计费错误:`401`(令牌无效)、`429`(限流)。**只有请求实际进入模型生成阶段(即收到 `200` + `b64_json`)才会按 token 计费**。 ## 相关文档 * [⚖️ 官转 vs 官逆 对比](/api-capabilities/gpt-image-2/vs-gpt-image-2-all) - 选型对照表,帮你决定用哪个 * [文生图 Playground](/api-capabilities/gpt-image-2/text-to-image) - `/v1/images/generations` 在线调试 * [图片编辑 Playground](/api-capabilities/gpt-image-2/image-edit) - `/v1/images/edits` 多图融合 + mask * [深度解读:GPT-image-2.5 上线,Flare 更快、Sunburst 更准](/news/gpt-image-2-5-launch) - News 文章 * [深度解读:gpt-image-2 上线说明](/news/gpt-image-2-launch) - News 文章 * [完整接入文档(中文)](/api-capabilities/gpt-image-2/overview) - 完整 API 参考 * [GPT-Image-2-All(官逆版本)](/api-capabilities/gpt-image-2-all/overview) - 更便宜、更快的备选方案 * [社区贡献:Luck GPT-Image 2 ComfyUI 节点](/scenarios/ecosystem/luckgpt2-comfyui) - 在 ComfyUI 中一键调用 `gpt-image-2` / `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst`(含 mask / 16 图输入 / 六档画质 / 自定义尺寸) * [社区贡献:APIYI GPT-Image 2 Skills](/scenarios/ecosystem/apiyi-gpt-image-skills) - 在 Codex CLI / Cursor / Gemini CLI 等 AI 编程工具中一句话调用 * [API 使用手册](/api-manual) - 通用调用规范 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` / `gpt-image-2` 是 OpenAI 官方模型,按 token 实计;如果你更看重统一定价(\$0.025/张)和出图速度(30–60s),可参考 [gpt-image-2-all](/api-capabilities/gpt-image-2-all/overview)。 # GPT-Image-2.5 / 2 系列 Agent 技能 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2/skills 把 gpt-image-2.5-flare / gpt-image-2.5-sunburst / gpt-image-2(官转)与 gpt-image-2.5-all / gpt-image-2-all / gpt-image-2-vip(官逆)封装成一个开箱即用的 Agent Skill,丢进 Codex、OpenClaw、hermes-agent、Claude Code 等任意编码 Agent,用 --model 一键切换六个模型完成文生图、多图融合与局部重绘。 本页提供一个**开箱即用的 Agent 技能(Skill)**:一个脚本同时覆盖 **gpt-image-2.5-flare / gpt-image-2.5-sunburst / gpt-image-2(官转)** 与 **gpt-image-2.5-all / gpt-image-2-all / gpt-image-2-vip(官逆)** 六个模型——它们都走同一套 OpenAI Images API,只是 `--model` 不同。把它放进你正在用的编码 Agent,一句话即可出图,整套东西就两个文件。 ## 这个技能能做什么 一个合并技能,脚本会根据**是否传入图片**自动判断走「文生图」还是「图片编辑」: 只给提示词 → 生成全新图片,文字渲染、写实质感强。 传入多张图(最多 16 张)+ 一条指令 → 把图1的人放进图2的场景、保留图3的风格等。 传入一张图 + `--mask` 掩码 + 指令 → 只改掩码覆盖的区域(**仅官转三款支持**,推荐 `gpt-image-2.5-sunburst`)。 ## 六个模型怎么选 六个模型**调用方式完全一致**,区别只在通道来源、价格/速度和支持的参数。官转三款(2.5-flare / 2.5-sunburst / 2)同价同参数,官逆三款同价同参数。脚本已按 `--model` 自动处理这些差异: | 模型(`--model`) | 通道 | 价格 | 速度 | `size` | `quality` / `mask` | 适合 | | - | - | - | - | - | - | - | | `gpt-image-2.5-flare`(默认) | 官转(官方直连) | 按量 \~\$0.03–0.2/张 | **官转最快**(1K low 实测约 10s) | ✅ 任意预设 | ✅ 支持,含 `xhigh` / `max` | 日常文生图默认、要画质档、要透明背景 | | `gpt-image-2.5-sunburst` | 官转(官方直连) | 按量,同 flare | 比 flare 慢 | ✅ 任意预设 | ✅ 支持,含 `xhigh` / `max` | 改图 / 多图融合 / 掩码局部重绘,编辑精度最高 | | `gpt-image-2` | 官转(官方直连) | 按量,同 flare | \~100–120s | ✅ 任意预设 | ✅ 支持(到 `high`) | 上一代,存量项目沿用 | | `gpt-image-2.5-all` | 官逆(ChatGPT 网页版 2.5) | flat \$0.025/张 | 约 30–90s | ❌ 写进 prompt | ❌ | 走量、要快、要 2.5 画质,尺寸用提示词描述即可 | | `gpt-image-2-all` | 官逆(ChatGPT 线) | flat \$0.025/张 | **最快 \~30–60s** | ❌ 写进 prompt | ❌ | 走量、要快、尺寸用提示词描述即可 | | `gpt-image-2-vip` | 官逆(Adobe 线) | flat \$0.03/张 | \~90–150s | ✅ 30 档含 **4K** | ❌ | 要锁定输出尺寸 / 4K,价格还想便宜 | | `gpt-image-2.5-flare-vip` / `gpt-image-2.5-sunburst-vip` | 官逆(Adobe 线 2.5) | flat \$0.03/张 | \~20–140s | ✅ 30 档含 **4K** | `quality` 六档全开(含 `xhigh` / `max`)、透明背景 ✅;`mask` 只整图重绘 ❌ | 便宜价拿到 2.5 画质,尺寸可锁 | 简单记:**日常文生图** → `gpt-image-2.5-flare`;**改图 / 掩码局部重绘** → `gpt-image-2.5-sunburst`;**要快又便宜** → `gpt-image-2.5-all` / `gpt-image-2-all`;**要锁尺寸/4K 又便宜** → `gpt-image-2-vip`。 ## 能用在哪些 Agent 一个 Skill 本质上就是**一个文件夹**:一份写给 Agent 看的说明(`SKILL.md`)+ 一个干活的脚本。所以**凡是能读取本地文件、执行命令行的编码 Agent 都能用上**——比如 **Codex、OpenClaw、hermes-agent、Claude Code** 等。 唯一要求:跑 Agent 的那台机器(你的电脑或服务器)装了 **Python 3** 并且**能联网**(脚本要直连 `api.apiyi.com`)。仅此而已,不挑具体哪家 Agent。 ## 三步装好 ### ① 建目录、贴文件、装依赖 新建一个技能文件夹,放入下面两个文件(完整内容见后两节)。本技能用 OpenAI SDK 调用,需要先装一个依赖: ```bash theme={null} pip install openai ``` ``` gpt-image-2/ ├── SKILL.md ├── scripts/ │ └── gpt_image.py └── .env # 第②步创建,放你的 Key ``` ### ② 同目录写 Key 在 `gpt-image-2/.env` 里写上你的 **API易 API Key**(在 `api.apiyi.com` 控制台创建): ```bash theme={null} APIYI_API_KEY=sk-your-api-key ``` 脚本会自动从这个 `.env` 读取 Key,**无需任何额外配置或环境变量**。 `.env` 里是你的密钥。如果这个技能要随项目仓库共享,**务必把 `.env` 加进 `.gitignore`,不要提交到 git**。 ### ③ 交给 Agent * **支持技能自动发现的 Agent**(如 Claude Code):把整个 `gpt-image-2/` 目录放进它的技能目录——个人级 `~/.claude/skills/`,或项目级 `.claude/skills/`(随仓库共享)。 * **其他 Agent**:按它各自的技能/插件约定放置;或者最简单——**直接让 Agent「读一下这个文件夹里的 SKILL.md,并照着执行」** 即可。 装好后就能用了,跳到 [怎么用](#怎么用) 看示例。 ## SKILL.md 新建 `gpt-image-2/SKILL.md`,完整内容如下(`description` 写清「做什么 + 何时用」,Agent 会据此自动触发): ````markdown theme={null} --- name: gpt-image-2 description: Generate or edit images via APIYI's gpt-image-2 (official) and gpt-image-2-all / gpt-image-2-vip (reverse) models. Use this when the user asks to create, draw, render, or generate an image/illustration/poster, or to edit, retouch, restyle, fuse, or inpaint existing images. allowed-tools: Bash(python3 *) --- # GPT-Image-2 系列出图技能 通过 API易 平台调用 GPT-Image 2.5 / 2 系列生成或编辑图片。一个脚本覆盖六个模型,用 `--model` 切换: - `gpt-image-2.5-flare`(默认,官转):速度优先,支持 `size` / `quality`(含 `xhigh` / `max`)/ `mask` 局部重绘,按量计费。 - `gpt-image-2.5-sunburst`(官转):编辑精度优先,参数同 flare,改图 / 多图融合首选。 - `gpt-image-2`(官转):上一代,参数同上(`quality` 只到 `high`)。 - `gpt-image-2.5-all`(官逆):ChatGPT 网页版 2.5 逆向,flat \$0.025/张,参数同 `-all`。 - `gpt-image-2.5-flare-vip` / `gpt-image-2.5-sunburst-vip`(官逆·Adobe 线 2.5):flat \$0.03/张,可锁 `size`,支持 `quality` 六档(含 `xhigh` / `max`)与透明背景;`mask` 只整图重绘。 - `gpt-image-2-all`(官逆·ChatGPT 线):最快、flat \$0.025/张;**不支持 `size`/`quality`**,尺寸写进提示词。 - `gpt-image-2-vip`(官逆·Adobe 线):可锁 `size`(30 档含 4K)、flat \$0.03/张;**不支持 `quality`/`mask`**。 ## Key 配置 脚本会自动读取技能目录下 `.env` 文件里的 `APIYI_API_KEY`(也支持同名环境变量)。 脚本依赖 OpenAI SDK,若报缺包,提示用户 `pip install openai`。 ## 用法 第一个参数是提示词;编辑/融合时用 `-i` 传入一张或多张本地图片: ```bash # 文生图(默认 gpt-image-2.5-flare 官转,可带画质) python3 ${CLAUDE_SKILL_DIR}/scripts/gpt_image.py "一只戴墨镜的橘猫坐在海边吧台,电影画幅" -o cat.png --size 1536x1024 --quality high # 最快、最省:用官逆 all(尺寸写进提示词,别传 size) python3 ${CLAUDE_SKILL_DIR}/scripts/gpt_image.py "扁平插画风节日海报,竖版 2:3" -o poster.png --model gpt-image-2-all # 要锁 4K:用官逆 vip python3 ${CLAUDE_SKILL_DIR}/scripts/gpt_image.py "城市夜景航拍" -o city.png --model gpt-image-2-vip --size 3840x2160 # 多图融合(最多 16 张,重复 -i) python3 ${CLAUDE_SKILL_DIR}/scripts/gpt_image.py "把图1的人物放进图2的场景,保留图3的色彩风格" -i person.png -i scene.png -i style.png -o fused.png # 局部重绘(掩码,仅官转三款;推荐 gpt-image-2.5-sunburst) python3 ${CLAUDE_SKILL_DIR}/scripts/gpt_image.py "把蒙版区域换成一扇圆窗" -i room.png --mask mask.png -o edited.png # 一次出多张(最多 5 张,并发) python3 ${CLAUDE_SKILL_DIR}/scripts/gpt_image.py "节日海报草图" -o draft.png -n 3 --model gpt-image-2-all ``` 参数说明: - 第 1 个位置参数:提示词(必填)。 - `--model`:`gpt-image-2`(默认)/ `gpt-image-2-all` / `gpt-image-2-vip`。也可在 `.env` 写 `APIYI_IMAGE_MODEL=...` 设默认。 - `-i / --image`:输入图片路径,可重复(最多 16 张);不传 = 文生图,传 = 编辑/融合。 - `-o / --out`:输出文件名,默认 `output.png`。 - `-n / --count`:一次出几张,**默认 1**,最多 5(客户端并发;脚本一律不传 `n`,避免官逆通道按张数超扣费)。 - `--size`:尺寸,默认 `auto`(`gpt-image-2-all` 会忽略,请把尺寸/比例写进提示词)。 - `--quality`:`low`/`medium`/`high`/`xhigh`/`max`/`auto`,**官转三款与 2.5 两款 -vip 生效**(`xhigh` / `max` 只有 2.5 系列接受;脚本对 `-all` / `gpt-image-2-vip` 自动不传)。 - `--format`:`png`/`jpeg`/`webp`,仅官转生效。 - `--mask`:掩码图,仅官转编辑生效(PNG 带 alpha,对第一张图)。 - `--background`:`transparent`/`opaque`/`auto`,`transparent` 仅官转 2.5 两款生效(`gpt-image-2` 已于 2026-09-30 被官方关停)。传 `transparent` 时 `--format` 必须是 `png` 或 `webp`。 ## 选型与红线(重要) - **默认 `gpt-image-2.5-flare`(官转)**:日常文生图;改图 / 掩码局部重绘换 `gpt-image-2.5-sunburst`。 - 要**快/便宜** → `gpt-image-2-all`;要**锁尺寸/4K** → `gpt-image-2-vip`。 - 官逆 `-all` / `gpt-image-2-vip` **不要传 `quality`**(2.5 两款 -vip 可传);**`gpt-image-2-all` 不要传 `size`**(写进提示词)。脚本已自动门控,但你直接调脚本时也请遵守。 - **透明背景**只有官转 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` 支持:加 `--background transparent` 即可出带 alpha 通道的透明底图(`--format` 需为 `png`/`webp`)。`gpt-image-2` 的透明背景已于 2026-09-30 被 OpenAI 官方关停,传了会 400,脚本会在本地先拦下。官逆 all/vip 没有这个参数,只能在提示词里要求,偶现不稳定。 ## 出图张数与成本 - **默认只出 1 张**;用户明确要多张才用 `-n`,一次不超过 5。 - 官逆 -all 系列 flat \$0.025/张、-vip 系列 flat \$0.03/张;官转三款按量、`--quality high` 较贵(1K 档约 \$0.05~0.21/张,`xhigh` / `max` 更贵),预算敏感就降 `medium`/`low` 或改用官逆。 ## 输出位置(重要) - `-o` 传**纯文件名**时存到**项目根目录下的 `gpt-image-output/`**;传**带目录路径**时按给定路径存。 - 不要把图片写到 `/tmp`、scratchpad 等临时目录,用户会找不到。 ## 完成后 脚本会为每张图打印一行完整路径,如实回报给用户。若被内容审核拒绝,把原因如实转达,不要重试同一提示词。 ```` `name` 必须是小写字母 + 连字符。在支持斜杠命令的 Agent 里,目录名就是命令名——叫 `gpt-image-2` 即 `/gpt-image-2`。`${CLAUDE_SKILL_DIR}` 是 Claude Code 提供的技能目录变量;其他 Agent 直接用脚本的实际路径即可。 ## scripts/gpt\_image.py 新建 `gpt-image-2/scripts/gpt_image.py`,使用 OpenAI SDK 指向 API易(`base_url="https://api.apiyi.com/v1"`): ```python theme={null} #!/usr/bin/env python3 """通过 API易 调用 GPT-Image 2.5 / 2 系列(gpt-image-2.5-flare / gpt-image-2.5-sunburst / gpt-image-2 官转,gpt-image-2.5-all / gpt-image-2-all / gpt-image-2-vip 官逆)生成 / 编辑图片。 都走 OpenAI Images API(/v1/images/generations + /v1/images/edits),用 --model 切换。需要:pip install openai""" import argparse import base64 import os import sys import urllib.request from concurrent.futures import ThreadPoolExecutor from openai import OpenAI # 一次调用最多并发出几张图(服务端 n 只出 1 张,这里用客户端并发模拟多张) MAX_COUNT = 5 # 各模型能力门控:是否接受这些参数(不接受的一律不传,避免报错或超扣费) MODEL_CAPS = { "gpt-image-2.5-flare": {"size": True, "quality": True, "output_format": True, "mask": True, "background": True}, # 官转·速度优先 "gpt-image-2.5-sunburst": {"size": True, "quality": True, "output_format": True, "mask": True, "background": True}, # 官转·编辑优先 "gpt-image-2": {"size": True, "quality": True, "output_format": True, "mask": True, "background": True}, # 官转·上一代 "gpt-image-2.5-all": {"size": False, "quality": False, "output_format": False, "mask": False, "background": False}, # 官逆·ChatGPT 2.5 "gpt-image-2-all": {"size": False, "quality": False, "output_format": False, "mask": False, "background": False}, # 官逆·ChatGPT "gpt-image-2-vip": {"size": True, "quality": False, "output_format": False, "mask": False, "background": False}, # 官逆·Adobe "gpt-image-2.5-flare-vip": {"size": True, "quality": True, "output_format": False, "mask": False, "background": True}, # 官逆·Adobe 2.5(quality 六档全开;mask 不做局部重绘) "gpt-image-2.5-sunburst-vip": {"size": True, "quality": True, "output_format": False, "mask": False, "background": True}, # 官逆·Adobe 2.5 "gpt-image-2.5-vip": {"size": True, "quality": True, "output_format": False, "mask": False, "background": True}, # = sunburst-vip 别名 } def caps_of(model): # 未知模型回落到官转能力集 return MODEL_CAPS.get(model, MODEL_CAPS["gpt-image-2.5-flare"]) def load_api_key(): """优先读环境变量;否则在脚本所在目录及其父目录找 .env。""" key = os.environ.get("APIYI_API_KEY") if key: return key here = os.path.dirname(os.path.abspath(__file__)) for d in (here, os.path.dirname(here)): env_path = os.path.join(d, ".env") if os.path.exists(env_path): with open(env_path, encoding="utf-8") as f: for line in f: line = line.strip() if line.startswith("APIYI_API_KEY") and "=" in line: return line.split("=", 1)[1].strip().strip('"').strip("'") return None def project_root(): """从脚本位置向上找包含 .git 或 .claude 的目录,作为项目根目录;找不到则用当前工作目录。""" d = os.path.dirname(os.path.abspath(__file__)) while True: if os.path.isdir(os.path.join(d, ".git")) or os.path.isdir(os.path.join(d, ".claude")): return d parent = os.path.dirname(d) if parent == d: return os.getcwd() d = parent def resolve_paths(out, count): """决定输出路径列表。纯文件名 → 存到 <项目根>/gpt-image-output/;带目录则按给定路径。""" if os.path.dirname(out): base_path = os.path.abspath(out) else: out_dir = os.path.join(project_root(), "gpt-image-output") os.makedirs(out_dir, exist_ok=True) base_path = os.path.join(out_dir, out) if count == 1: return [base_path] base, ext = os.path.splitext(base_path) return [f"{base}-{i}{ext}" for i in range(1, count + 1)] def decode_image(item): """统一取图字节:b64_json 可能是纯 base64 或带 data:image 前缀的 data URL(官逆模型);也可能只给 url。""" raw = getattr(item, "b64_json", None) if raw: if raw.startswith("data:"): raw = raw.split(",", 1)[1] # 剥掉 data:image/png;base64, 前缀 return base64.b64decode(raw) url = getattr(item, "url", None) if url: with urllib.request.urlopen(url, timeout=360) as r: return r.read() raise RuntimeError("响应里既没有 b64_json 也没有 url") def one_image(client, model, args): """发一次请求,返回图片字节;失败抛异常(由 _safe 兜住)。一律不传 n(默认 1 张,多张靠客户端并发)。""" cap = caps_of(model) if args.image: # 编辑 / 多图融合:每次重新 open 文件,避免线程间共享句柄 files = [open(p, "rb") for p in args.image] try: kwargs = dict(model=model, image=files if len(files) > 1 else files[0], prompt=args.prompt) if cap["size"] and args.size: kwargs["size"] = args.size if cap["quality"] and args.quality: kwargs["quality"] = args.quality if cap["output_format"] and args.format: kwargs["output_format"] = args.format if cap["background"] and args.background and args.background != "auto": kwargs["background"] = args.background if cap["mask"] and args.mask: kwargs["mask"] = open(args.mask, "rb") resp = client.images.edit(**kwargs) finally: for fh in files: fh.close() else: # 文生图 kwargs = dict(model=model, prompt=args.prompt) if cap["size"] and args.size: kwargs["size"] = args.size if cap["quality"] and args.quality: kwargs["quality"] = args.quality if cap["output_format"] and args.format: kwargs["output_format"] = args.format if cap["background"] and args.background and args.background != "auto": kwargs["background"] = args.background resp = client.images.generate(**kwargs) return decode_image(resp.data[0]) def main(): api_key = load_api_key() if not api_key: sys.exit("未找到 API Key:请在技能目录的 .env 写一行 APIYI_API_KEY=sk-xxx") default_model = os.environ.get("APIYI_IMAGE_MODEL", "gpt-image-2.5-flare") # 同步阻塞调用,图片生成慢,超时给足 360s client = OpenAI(api_key=api_key, base_url="https://api.apiyi.com/v1", timeout=360) parser = argparse.ArgumentParser(description="GPT-Image 2.5 / 2 系列出图") parser.add_argument("prompt", help="提示词 / 编辑指令") parser.add_argument("--model", default=default_model, help="gpt-image-2.5-flare(官转·默认) / gpt-image-2.5-sunburst(官转·编辑) / gpt-image-2(官转·上一代) / gpt-image-2.5-all / gpt-image-2-all(官逆·最快) / gpt-image-2-vip(官逆·可锁尺寸) / gpt-image-2.5-flare-vip / gpt-image-2.5-sunburst-vip(官逆·2.5·可锁尺寸+quality)") parser.add_argument("-i", "--image", action="append", default=[], help="输入图片路径(可重复,最多 16 张;传入即为编辑/融合模式)") parser.add_argument("-o", "--out", default="output.png", help="输出文件名") parser.add_argument("-n", "--count", type=int, default=1, help=f"一次出几张,默认 1,最多 {MAX_COUNT}(客户端并发)") parser.add_argument("--size", default="auto", help="尺寸,如 1024x1024 / 2048x1152 / auto(gpt-image-2-all 不支持,写进 prompt)") parser.add_argument("--quality", default="high", help="画质 low / medium / high / xhigh / max / auto(仅官转生效;xhigh/max 仅 2.5)") parser.add_argument("--format", default="png", help="输出格式 png / jpeg / webp(仅官转生效)") parser.add_argument("--mask", help="掩码图(仅官转编辑,PNG 带 alpha,对第一张图生效)") parser.add_argument("--background", default="auto", choices=["transparent", "opaque", "auto"], help="背景,transparent 出透明底(仅官转 2.5 两款,需配 --format png/webp)") args = parser.parse_args() # jpeg 没有 alpha 通道,与透明背景互斥,先在本地拦掉,别把 400 甩给用户 if args.background == "transparent" and args.format == "jpeg": sys.exit("--background transparent 不能配 --format jpeg(jpeg 无 alpha 通道),请改用 png 或 webp") # gpt-image-2 的透明背景 2026-09-30 被 OpenAI 关停,传了官方直接 400 if args.background == "transparent" and args.model == "gpt-image-2": sys.exit("gpt-image-2 已不支持透明背景(OpenAI 2026-09-30 关停),请改用 --model gpt-image-2.5-flare 或 gpt-image-2.5-sunburst") count = args.count if count < 1: count = 1 if count > MAX_COUNT: print(f"提示:一次最多 {MAX_COUNT} 张,已将 {args.count} 限制为 {MAX_COUNT}。", file=sys.stderr) count = MAX_COUNT paths = resolve_paths(args.out, count) def task(path): data = one_image(client, args.model, args) with open(path, "wb") as f: f.write(data) return os.path.abspath(path) failures = 0 with ThreadPoolExecutor(max_workers=count) as pool: for path, result in zip(paths, pool.map(lambda p: _safe(task, p), paths)): ok, value = result if ok: print(f"图片已保存至 {value}") else: failures += 1 print(f"第 {os.path.basename(path)} 张生成失败:{value}", file=sys.stderr) if failures == count: sys.exit("全部生成失败。") def _safe(fn, arg): try: return True, fn(arg) except Exception as e: # noqa: BLE001 — 单张失败不影响其它并发任务 return False, str(e) if __name__ == "__main__": main() ``` ## 怎么切换模型 切换模型**只需改 `--model`**,六个值任选: ```text theme={null} ... gpt_image.py "提示词" # 默认 gpt-image-2.5-flare(官转·速度优先) ... gpt_image.py "改成水彩" -i in.png --model gpt-image-2.5-sunburst # 官转·编辑精度优先 ... gpt_image.py "提示词" --model gpt-image-2 # 官转·上一代 ... gpt_image.py "提示词" --model gpt-image-2.5-all # 官逆·ChatGPT 网页版 2.5 ... gpt_image.py "提示词" --model gpt-image-2-all # 官逆·最快·最省 ... gpt_image.py "提示词" --model gpt-image-2-vip --size 3840x2160 # 官逆·锁 4K ``` 想改默认通道(不每次都加 `--model`),在 `gpt-image-2/.env` 里加一行: ```bash theme={null} APIYI_IMAGE_MODEL=gpt-image-2-all ``` 脚本已**自动兼容** base64 与 url 两种返回(官逆模型的 `b64_json` 带 `data:image;base64,` 前缀,脚本会自动剥除)。只有当你**强依赖 URL 输出**时,才需要在 API易 控制台把令牌「分组」切到 `image2_OSS`——普通出图无需关心。 ## 为什么一句话就能出图 很多人好奇:我又没敲命令,怎么说句"画只猫"它就出图了? 原理是这样:Agent 启动时会**先读取每个技能 `SKILL.md` 里的 `description`**(一段很短的元数据,说明「这个技能做什么、什么时候该用」)。当你说出的需求**匹配上**这段描述的场景(比如"画/生成/渲染一张图""把这几张图融合一下"),Agent 就**自动决定调用这个技能**,去读完整的 `SKILL.md` 并运行脚本——整个过程你不用记任何命令。 不想靠 Agent 猜、想要**百分百可控**时,用下面的**显性调用**。 ## 怎么用 ### 自然语言(隐式触发) 装好后直接对 Agent 说话即可: | 你说 | 技能行为 | | - | - | | "画一张电影画幅的猫" | 默认 gpt-image-2.5-flare,出 1 张 png | | "快点、便宜点出张海报" | Agent 会带 `--model gpt-image-2-all` | | "出一张 4K 的城市夜景" | Agent 会带 `--model gpt-image-2-vip --size 3840x2160` | | "把 person.png 的人放进 scene.png" | 调 `-i person.png -i scene.png`,融合出图 | ### 显性调用(更可控) * **支持斜杠命令的 Agent**(如 Claude Code): ```text theme={null} /gpt-image-2 赛博朋克城市雨夜,霓虹招牌特写 --model gpt-image-2-vip --size 2048x1152 ``` * **任意 Agent / 直接命令它跑脚本**(最通用): ```text theme={null} 运行 python3 gpt-image-2/scripts/gpt_image.py "赛博朋克城市雨夜,霓虹招牌特写" --model gpt-image-2-all ``` ## 生成的图片在哪里 * 当 `-o` 只传**文件名**(如 `-o cat.png`)时,图片统一存到**项目根目录下的 `gpt-image-output/` 文件夹**(脚本自动创建),所以你在项目里的这个文件夹就能直接找到。 * 「项目根目录」= 脚本从自身位置向上找到的第一个含 `.git` 或 `.claude` 的目录——**不管 Agent 在哪个目录运行,图都落在项目里**,不会跑进临时目录害你找不到。 * 脚本会**为每张图打印一行完整绝对路径**,例如 `图片已保存至 /Users/you/project/gpt-image-output/cat.png`。 * 默认**只出 1 张**;`-n 3` 一次出 3 张(最多 5),文件名自动加 `-1`、`-2`、`-3` 后缀。 * 传**带目录的路径**(如 `-o images/cat.png` 或绝对路径)时,按你给的路径存,不进 `gpt-image-output/`。 * 编辑 / 融合同理:输出是新文件,**不会覆盖你的原图**。 ## 相关文档 * [GPT-Image-2-All Agent 技能](/api-capabilities/gpt-image-2-all/skills)(官逆·最快) * [GPT-Image-2-VIP Agent 技能](/api-capabilities/gpt-image-2-vip/skills)(官逆·锁 4K) * [GPT-Image-2 图片生成总览](/api-capabilities/gpt-image-2/overview) * [Nano Banana Pro Agent 技能](/api-capabilities/nano-banana-image/skills) # 文生图 API 参考 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2/text-to-image api-reference/gpt-image-2-generate-openapi.yaml POST /v1/images/generations gpt-image-2.5-flare / gpt-image-2.5-sunburst / gpt-image-2 文生图 API 参考与在线调试 — 任意合法尺寸(含 4K),按 token 计费,三款同价同参数 右侧的交互式 Playground 支持直接在线调试。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),输入 prompt、选择 size / quality 后一键发送即可。 **场景说明**:本页用于「文本生成图片」。只需输入提示词即可,无需上传任何图片。如需根据现有图片做编辑、多图融合或 mask 局部重绘,请使用 [图片编辑接口](/api-capabilities/gpt-image-2/image-edit)。 **🖥️ 浏览器 Playground 限制(重要)** 本接口的响应包含**纯 base64 字符串**(数 MB 量级)。受浏览器渲染限制,右侧 Playground 在收到响应后**可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法把这么长的 base64 显示出来。 **推荐做法**(小白零踩坑): * **直接复制下方"代码示例"中的 Python / Node.js / cURL 到本地运行**,代码会自动 `base64.b64decode` 并把图片**保存为本地文件**。 * 如要在浏览器里试 Playground,把 `size` 设为最小档(如 `1024x1024`)、`quality` 改为 `low`,缩小响应体积。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 **⚠️ 不支持的参数** * `input_fidelity` —— 三款模型均强制启用高保真,传了会 400 报错(2.5 实测 2026-09-09:`does not support the 'input_fidelity' parameter`;从 1.5 迁移时直接删掉这一行) **超过 `2560×1440` 的输出仍属实验性**,生产环境建议优先用预设尺寸:`2048x1152` / `2048x2048` / `3840x2160`。 ## 代码示例 ### Python(OpenAI SDK 直连) ```python theme={null} from openai import OpenAI import base64 client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1" ) resp = client.images.generate( model="gpt-image-2.5-flare", prompt="赛博朋克城市雨夜,霓虹招牌特写,电影画幅", size="2048x1152", quality="high", output_format="jpeg", output_compression=85 ) # b64_json 是纯 base64(无前缀),需要自己 decode 写文件 with open("out.jpg", "wb") as f: f.write(base64.b64decode(resp.data[0].b64_json)) ``` ### Python(原生 requests) ```python theme={null} import requests import base64 API_KEY = "sk-your-api-key" response = requests.post( "https://api.apiyi.com/v1/images/generations", headers={ "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" }, json={ "model": "gpt-image-2.5-flare", "prompt": "横版 2K 海边日落老灯塔,电影画幅", "size": "2048x1152", "quality": "high" }, timeout=360 # high + 2K/4K 实测可能 3-5 分钟,按 120s 配会大量误超时 ).json() with open("out.png", "wb") as f: f.write(base64.b64decode(response["data"][0]["b64_json"])) ``` ### cURL ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/images/generations" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2.5-flare", "prompt": "一只戴墨镜的橘猫坐在海边吧台,电影画幅", "size": "2048x1152", "quality": "high", "output_format": "jpeg", "output_compression": 85 }' ``` ### Node.js(原生 fetch) ```javascript theme={null} import fs from 'node:fs'; const resp = await fetch('https://api.apiyi.com/v1/images/generations', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer sk-your-api-key' }, body: JSON.stringify({ model: 'gpt-image-2.5-flare', prompt: '极简线条风格的猫咪 LOGO', size: '1024x1024', quality: 'medium' }) }); const { data } = await resp.json(); // b64_json 是纯 base64(无前缀),需自己 decode fs.writeFileSync('logo.png', Buffer.from(data[0].b64_json, 'base64')); ``` ### 浏览器 JavaScript(直接渲染) ```javascript theme={null} const resp = await fetch('https://api.apiyi.com/v1/images/generations', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer sk-your-api-key' }, body: JSON.stringify({ model: 'gpt-image-2.5-flare', prompt: '水彩风的北欧极光', size: '1536x1024', quality: 'high' }) }); const { data } = await resp.json(); // 浏览器渲染需自己拼 data URL 前缀 document.getElementById('img').src = `data:image/png;base64,${data[0].b64_json}`; ``` ## 参数说明速查 | 参数 | 类型 | 必填 | 默认 | 说明 | | - | - | - | - | - | | `model` | string | 是 | — | `gpt-image-2.5-flare`(速度优先,日常首选)/ `gpt-image-2.5-sunburst`(画质与编辑精度优先)/ `gpt-image-2`(上一代,仍可用);生产环境可锁日期快照 `gpt-image-2.5-flare-2026-09-08` / `gpt-image-2.5-sunburst-2026-09-08`。三款同价同参数 | | `prompt` | string | 是 | — | 提示词,支持中英文;最长 32,000 字符(原厂上限,按字符不按 token),长资料先提炼再传,见 [长提示词篇](/api-capabilities/image-long-prompt) | | `size` | string | 否 | `auto` | 输出尺寸,预设或满足约束的自定义尺寸 | | `quality` | string | 否 | `auto` | `low` / `medium` / `high` / `xhigh` / `max` / `auto`;`xhigh` / `max` 是 2.5 新增档位,`gpt-image-2` 只到 `high` | | `output_format` | string | 否 | `png` | `png` / `jpeg` / `webp` | | `output_compression` | int | 否 | — | 0–100,仅 `jpeg` / `webp` 生效 | | `background` | string | 否 | `auto` | `transparent` / `opaque` / `auto`;`transparent` 仅 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` 支持(`gpt-image-2` 自 2026-09-30 起被官方拒绝,返回 400),且 `output_format` 必须是 `png` 或 `webp`,配 `jpeg` 会 400,见 [透明背景 FAQ](/faq/image-transparent-background) | | `moderation` | string | 否 | `auto` | `auto` / `low`(低强度审核) | | `n` | int | 否 | 1 | 仅支持 1 | **`quality` 不要传旧版 DALL·E 的 `standard` / `hd`。** 只接受 `low` / `medium` / `high` / `xhigh` / `max` / `auto` 六个官方枚举值(`xhigh` / `max` 仅 2.5 两款接受)。旧值在不同后端渠道下行为不一致:有时直接 400 报错(`invalid_value`),有时被静默忽略、按 `auto` 档跑出结果(费用不可控)。请始终显式传官方枚举值之一。 详细的参数约束、可选值、示例请查看右侧 Playground 中的字段说明,所有 enum 字段均支持下拉选择。 ## 响应格式 ```json theme={null} { "created": 1776832476, "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA..." } ], "usage": { "input_tokens": 17, "input_tokens_details": { "image_tokens": 0, "text_tokens": 17 }, "output_tokens": 196, "output_tokens_details": { "image_tokens": 196, "text_tokens": 0 }, "total_tokens": 213 } } ``` **⚠️ b64\_json 字段是纯 base64**,**不含** `data:image/...;base64,` 前缀。客户端需要: * **写文件**:`base64.b64decode(b64_str)` → 写入磁盘 * **浏览器渲染**:自行拼前缀 `data:image/png;base64,` + b64 `gpt-image-2-all` / `gpt-image-2-vip` 实测(2026-07)同样返回纯 base64,但其历史版本曾带前缀——跨模型复用代码时建议统一做 `startsWith('data:')` 检测。 `usage` 字段反映本次实际计费的 token 数,`input_tokens_details` / `output_tokens_details` 把文本、图片两段 token 拆开列出(纯文生图时 `image_tokens` 恒为 0)。详细的字段说明和自行核算公式见 [概览页「如何查看每次调用的真实 token 数」](/api-capabilities/gpt-image-2/overview#如何查看每次调用的真实-token-数)。 # GPT-image-2.5 / 2 官转 vs 官逆 对比 Source: https://docs.apiyi.com/api-capabilities/gpt-image-2/vs-gpt-image-2-all 官转 gpt-image-2.5-flare / gpt-image-2.5-sunburst / gpt-image-2 与官逆姐妹模型 gpt-image-2.5-all / gpt-image-2-all / gpt-image-2.5-vip / gpt-image-2-vip 对比:性质、计费、端点、上传/输出格式、速度与画质定位、指令遵循等差异,帮你选对模型。 ## 一句话结论 | 你需要 | 选这个 | | - | - | | **`quality` 画质参数 / mask 局部重绘 / 任意自定义尺寸(不限 30 档) / OpenAI 官方完全对齐字段** | 官转按量计费:`gpt-image-2.5-flare`(速度优先)/ `gpt-image-2.5-sunburst`(编辑精度优先)/ `gpt-image-2`(上一代) | | **可预测的统一价(\$0.025/张)+ 出图快且稳(快就是优势)** | `gpt-image-2-all` / `gpt-image-2.5-all`(官逆,ChatGPT 网页线,\~90s;2.5-all 背后是 Images 2.5) | | **可预测的统一价 + 锁尺寸(30 档含 4K)** | `gpt-image-2-vip` / `gpt-image-2.5-flare-vip` / `gpt-image-2.5-sunburst-vip`(官逆,Adobe 线;2.5 两款 `quality` 六档全开,`gpt-image-2-vip` 到 `high`) | 八个模型**底层都是 OpenAI GPT-Image 2.5 / 2 系列**,差别在通道性质(官方直连 vs 逆向)、计费方式、参数粒度。官转三款同价同参数,官逆五款同套调用代码(`-all` 两款 \$0.025/张、`-vip` 三款 \$0.03/张)。 **官逆三线(-all / 2.5-all / -vip 三款)**:本页"官逆"列同时覆盖 **`gpt-image-2-all`**、**`gpt-image-2.5-all`** 与 **`gpt-image-2-vip` / `gpt-image-2.5-flare-vip` / `gpt-image-2.5-sunburst-vip`**(别名 `gpt-image-2.5-vip` = sunburst-vip)。同套调用代码(`-vip` 三款额外支持 `size`),按次计费:`-all` 两款 \$0.025/张、`-vip` 三款 \$0.03/张: * `gpt-image-2-all` / `gpt-image-2.5-all`:ChatGPT 网页线,**约 90 秒** 出图——**快就是优势**;2.5-all 背后是 Images 2.5 * `gpt-image-2-vip` 与 2.5 两款 -vip:Adobe 线(Firefly),**支持 `size` 锁尺寸(30 档含 4K)**;三款都接受 `quality`(属渠道行为不承诺):2.5 两款 2026-09-10 复测 `xhigh` / `max` 也已放开、六档全开,`gpt-image-2-vip` 到 `high`;都能出透明背景;flare-vip 最快、画面偏软,sunburst-vip 与 `gpt-image-2-vip` 画质接近 * 共同点:都不支持 `n`;`mask` 都只是整图重绘,不保证只改蒙版区 需要精确的 mask 局部重绘、30 档以外的任意自定义尺寸或 `n` 多图时,走官转(`gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` / `gpt-image-2`)。 **关于速度**:当前 `-all` / `-vip` 出图速度**比刚上线时慢一些**,这是 **OpenAI 官方算力波动** 导致的全链路放缓——APIYI 的号池和运维侧并无问题,所有逆向通道用户都会感受到。建议把超时设置在 300 秒以上,复杂场景预留更多。 ## 完整对比表 | 维度 | **gpt-image-2-all / 2.5-all / -vip**(官逆,高性价比) | **gpt-image-2.5-flare / sunburst / gpt-image-2**(官转,正式版) | | - | - | - | | **模型名** | `gpt-image-2-all`(出图最快) / `gpt-image-2.5-all`(同线路的 2.5 版) / `gpt-image-2-vip`(画质优先、可锁尺寸) / `gpt-image-2.5-flare-vip` · `gpt-image-2.5-sunburst-vip`(同线路的 2.5 版,别名 `gpt-image-2.5-vip`) | `gpt-image-2.5-flare`(速度优先) / `gpt-image-2.5-sunburst`(编辑精度优先) / `gpt-image-2`(上一代) | | **通道性质** | `-all` / `2.5-all`:逆向 ChatGPT 官网线路
`-vip` 三款:Adobe 官逆线路(Firefly,高质量 GPT-Image 2.5 逆向资源,非超分) | 官方直连(OpenAI Images API),三款同价同参数 | | **计费方式** | **按次计费**:`-all` 两款固定 \$0.025/次、`-vip` 三款固定 \$0.03/次 | **按量计费**:按 token 实计,官网同价;本站充值加赠后约 **8.5 折** | | **典型成本/张** | \$0.025(`-all`)/ \$0.03(`-vip`),不区分尺寸 / 画质 | 实测 **\$0.03 – \$0.2**(与提示词长度、size、quality 正相关) | | **令牌分组** | 默认分组(Default) | 默认分组(Default) | | **令牌类型** | **按次计费** 或 **按量优先** 均可 | **仅支持按量优先**(本模型按 token 计费,按次计费令牌不可用) | | **推荐端点** | **`/v1/images/generations` + `/v1/images/edits`**(更稳定、上游供给更足,且同套代码兼容官转,风控异常时换 `model` 名即可切换) | `/v1/images/generations` + `/v1/images/edits` | | **上传图片格式** | multipart file(edits 端点) | multipart file(编辑接口) | | **输出图片格式** | `b64_json`(默认,**纯 base64 无前缀**,2026-07 实测;历史版本曾带前缀)或 `url`(R2 CDN) | `b64_json`(**纯 base64,无前缀**) | | **上传图片数(编辑)** | 多张 | **最多 16 张**(`image[]`) | | **mask 局部重绘** | `-all` / `2.5-all`:❌ 不支持
`-vip` 三款:⚠️ 接受但整图重绘,真实照片三次实测蒙版内外改动比 ≈1(2026-09-09),不保证只改蒙版区 | ✅ 支持(要求带 alpha 通道) | | **指令遵循** | 好 | **优秀** | | **生成速度** | `-all`:约 **90 秒**(快就是优势)
`gpt-image-2-vip`:约 **90–150 秒**
2.5 两款 -vip:1024² 串行实测 flare 22~138 秒、sunburst 37~120 秒,波动大;RPM 500 以内无需考虑并发,偶发 429 退避重试或看 [实时动态](/live)
📌 当前比刚上线时慢——OpenAI 官方算力波动所致,非 APIYI 侧问题 | `gpt-image-2.5-flare`:官转最快,1K `low` 实测约 10 秒(2026-09-09)
`gpt-image-2.5-sunburst`:1K `low` 实测约 14 秒,高画质更慢
`gpt-image-2`:约 **100-120 秒**,复杂场景 + 4K 可达 3-5 分钟 | | **画质倾向** | `-all` / `2.5-all`:好(同一线路,两个名字出的是同一批图)
`-vip`:同尺寸 6 组提示词人眼对比,sunburst-vip 与 `gpt-image-2-vip` 接近,flare-vip 偏软、装饰细节少;三款中文标题笔画都正确 | 稳定;2.5 两款高于 gpt-image-2,sunburst 最高,且可用 `quality=xhigh` / `max` 拉满 | | **`size` 参数** | `-all` / `2.5-all`:❌ 不接受(写进 prompt)
`-vip` 三款:✅ **已恢复**(2026-07-22 起),支持 30 档常见尺寸(含 4K);仅 images 端点生效,chat 端点不支持 | ✅ 任意合法尺寸 | | **`size = auto` 行为** | `-all`:— 不接受 `size` 字段
`-vip`:不传 `size` 时 `gpt-image-2-vip` / sunburst-vip 出 2048×2048,flare-vip 固定出 1024×1536;默认值随上游变动过,要锁尺寸请显式传 30 档之一 | ✅ 默认值,OpenAI 官方语义"按 prompt 智能选";**社区实测偏向 1:1 方形(1024×1024)**,要其它比例请显式传 `size` | | **支持 4K** | `-all` / `2.5-all`:❌
`-vip` 三款:✅ 4K Detail 档(如 `3840x2160` / `2880x2880`),不加价 | ✅ 含 `3840×2160` | | **常见输出尺寸** | `-all`:16:9 → 1672×941、9:16 → 941×1672、1:1 → 1254×1254(自适应)
`-vip` 三款:30 档(10 比例 × 1K/2K/4K),见 [30 档完整对照表](/api-capabilities/gpt-image-2-vip/overview#支持的-size30-档完整对照表);表外尺寸不报错但会被对齐到 16 倍数或抬到最小边 | 8 个预设 + 任意合法自定义尺寸 | | **画质参数 `quality`** | `-all` / `2.5-all`:❌ 不支持(不要传)
`-vip` 三款:✅ 实测生效,属渠道行为不承诺——2.5 两款 `auto` / `low` / `medium` / `high` / `xhigh` / `max` 六档全开(`xhigh` / `max` 2026-09-10 放开),`gpt-image-2-vip` 到 `high` | ✅ `low` / `medium` / `high` / `xhigh` / `max` / `auto`(`xhigh` / `max` 仅 2.5 两款) | | **`quality` 档位对齐** | 2048×1152 输出 token:`gpt-image-2-vip` low 157 / medium 1,413 / high 5,650;2.5 两款 -vip low 157 / medium 367 / high 1,413 / xhigh 2,511 / max 5,650——**2.5 的 `high` = `gpt-image-2-vip` 的 `medium`,2.5 的 `max` = `gpt-image-2-vip` 的 `high`**,与官转 2.5 对 gpt-image-2 的关系一致;按次计费,档位不影响价格 | 1024² 输出 token:官转 2.5 low 196 / medium 439 / high 1,756 / xhigh 3,122 / max 7,024;`gpt-image-2` low 196 / medium 1,756 / high 7,024——官转 2.5 的 `high` = `gpt-image-2` 的 `medium`;按 token 计费 | | **`n` 参数** | ❌ 官逆五款均不支持(单次仅返回 1 张) | ✅ 支持 | | **透明背景** | `-all` / `2.5-all`:⚠️ 无 `background` 参数,只能在提示词里要求,偶现不稳定
`-vip` 三款:✅ `background: "transparent"` 实测返回带 alpha 的 PNG(不承诺) | ✅ 2.5 两款参数控制稳定 —— `background: "transparent"` + `png` / `webp`
❌ `gpt-image-2` 已于 2026-09-30 被 OpenAI 官方关停,传了 400 | | **中文提示词** | ✅ 原生 | ✅ 原生 | | **文字渲染** | 高还原度 | 高还原度(`high` 档位最强) | | **API 文档** | [GPT-Image-2.5-All 概览](/api-capabilities/gpt-image-2-all/overview) / [GPT-Image-2.5-VIP 概览](/api-capabilities/gpt-image-2-vip/overview) | [GPT-Image-2.5 / 2 概览](/api-capabilities/gpt-image-2/overview) | 🔑 **如何创建或管理令牌**:[https://api.apiyi.com/token](https://api.apiyi.com/token)\ 在控制台创建令牌时可以选择分组(`Default` 默认即可)和令牌类型(**按次计费** / **按量优先**)。**调用官转三款(`gpt-image-2.5-flare` / `sunburst` / `gpt-image-2`)必须使用「按量优先」类型的令牌**,否则会因计费方式不匹配被拒。 ## 选型场景 ### 选 `gpt-image-2-all` / `gpt-image-2.5-all`(官逆)的场景 单价稳定 \$0.025/张(2026-10-10 起由 \$0.03 降价),无尺寸 / 画质阶梯,叠加充值加赠成本更低,**适合大批量生产、成本必须封顶的场景**(信息图、营销物料、电商素材批量)。 约 90 秒出图,**比 `-vip` 和官转都略快**,前端实时交互体验更好。 Images API 标准格式,与 `-vip` 三款和官转三款 **同套代码**——改个 `model` 名即可互切或兜底。`gpt-image-2-all` 与 `gpt-image-2.5-all` 同价同行为,新名只是把 ChatGPT 网页版升级到 Images 2.5 这件事表达在模型名上。 中文提示词原生友好、招牌 / 海报 / 信息图文字还原度高,**适合面向中文用户的内容生产**。 ### 选 `-vip` 三款(官逆,锁尺寸)的场景 `gpt-image-2.5-flare-vip` / `gpt-image-2.5-sunburst-vip` 实测接受 `auto` 到 `max` 六档(`xhigh` / `max` 2026-09-10 放开),`gpt-image-2-vip` 到 `high`,都属渠道行为不承诺。档位对齐:2.5 的 `high` 只等于 `gpt-image-2-vip` 的 `medium`,2.5 的 `max` 才等于 `gpt-image-2-vip` 的 `high`;按次 \$0.03 不随档位变。 flare-vip 最快、画面偏软;sunburst-vip 画质与编辑精度更高、人眼与 `gpt-image-2-vip` 接近;三款最高档 token 相同(2.5 传 `max`、`gpt-image-2-vip` 传 `high`)。都比 `-all` 慢,`max` 档 1024² 实测 80~160 秒,接受更长等待时选。 `size` 参数**已恢复**(2026-07-22 起):支持 30 档常见尺寸(10 比例 × 1K/2K/4K),电商主图、海报模板、4K 壁纸可严格输出,统一 \$0.03/张、4K 不加价。 调用结构与 `-all` 一致(仅多一个 `size` 字段)——一套代码五个官逆模型来回切,随时按速度 / 画质偏好换 `model` 名。 `-vip` 的 `size` 仅在 `/v1/images/generations` 与 `/v1/images/edits` 端点生效,**`/v1/chat/completions` 对话端点不支持 `size`**。需要 30 档以外的任意自定义尺寸或精确的 mask 局部重绘时走官转(`gpt-image-2.5-flare` / `sunburst`)。该参数可用性随上游变动,最新状态以 [实时动态](/live) 为准。 ### 选官转(`gpt-image-2.5-flare` / `sunburst` / `gpt-image-2`)的场景 `quality` 六档全开且**官方承诺**,档位与 token 量按官方规格稳定;官逆 -vip 2.5 两款虽然也已放开六档(2026-09-10 实测),但属渠道行为不承诺,`gpt-image-2-vip` 仍只到 `high`。 支持 alpha 通道蒙版,**精准修改图片局部区域而保留其余部分**——官逆各款只做整图重绘,不保证只改蒙版区。 `size` 参数接受**任意合法尺寸**(含 4K),不限预设档位。`-vip` 三款只保证 30 档,表外尺寸会被改写,**要严格的自定义尺寸走官转**。 走官方 Images API,字段与行为完全与官方一致。**已有基于 OpenAI 官方 SDK 的代码 / 系统**可零改动迁移,长期更稳。 ## 关键差异详解 ### 1. `b64_json` 格式差异(迁移坑!) 2026-07 实测官转与官逆各款都返回**纯 base64(无 `data:` 前缀)**,但 `gpt-image-2-all` 历史版本曾直接带前缀——跨模型 / 跨版本复用代码时,统一做前缀检测最稳: ```python theme={null} # 通用写法:先检测前缀再处理,官转三款与官逆五款均适用 b64 = resp["data"][0]["b64_json"] if b64.startswith("data:"): # 兼容曾出现过的带前缀响应 b64 = b64.split(",", 1)[1] with open("out.png", "wb") as f: f.write(base64.b64decode(b64)) # ✅ 写文件 img_tag = f'' # ✅ 浏览器渲染 ``` 从一个切到另一个时,**`b64_json` 处理代码必须改**,否则会拿到损坏的 data URL 或 decode 失败。 ### 2. 分辨率控制方式 **`gpt-image-2-all` / `gpt-image-2.5-all`**(不接受 `size`,画幅写在 prompt 里,两个名字行为相同): ``` "横版 16:9 电影画幅,黄昏时的海边老灯塔" → 输出约 1672×941 "竖版 9:16 手机壁纸,赛博朋克城市雨夜" → 输出约 941×1672 "1024×1024 方形 LOGO,极简猫咪线条" → 输出约 1254×1254 ``` **`-vip` 三款**(`gpt-image-2.5-vip` / `gpt-image-2.5-flare-vip` / `gpt-image-2-vip`,`size` 已恢复,2026-07-22 起): 支持 **30 档常见尺寸**(10 比例 × 1K/2K/4K),请求体直接传 `size: "宽x高"`(须为 30 档之一,完整清单见 [30 档对照表](/api-capabilities/gpt-image-2-vip/overview#支持的-size30-档完整对照表))。三款实测也都接受 `quality`(属渠道行为不承诺):2.5 两款六档全开(`xhigh` / `max` 2026-09-10 放开),`gpt-image-2-vip` 到 `high`;2.5 两款的 `high` 输出 token 只等于 `gpt-image-2-vip` 的 `medium`,`max` 才等于它的 `high`: ```python theme={null} client.images.generate( model="gpt-image-2.5-vip", # 别名 = gpt-image-2.5-sunburst-vip;换 gpt-image-2.5-flare-vip / gpt-image-2-vip 只改这一行 prompt="...", size="3840x2160", # ✅ 30 档之一,仅 images 端点生效(chat 端点不支持) quality="max" # 可选;2.5 两款六档全开,gpt-image-2-vip 只到 high;按次 $0.03 不随档位变 ) ``` **官转三款**(`size` 参数严格控制 + `quality` 档位,示例用 `gpt-image-2.5-flare`,换 `sunburst` / `gpt-image-2` 只改模型名): ```python theme={null} client.images.generate( model="gpt-image-2.5-flare", prompt="...", size="2048x1152", # ✅ 精确按此输出 quality="max" # 六档全开;xhigh / max 仅 2.5 两款;2.5 的 high 只等于 gpt-image-2 的 medium ) ``` ### 3. 上传 / 输出格式差异 | 操作 | 官逆五款(`-all` / `2.5-all` / `-vip` 三款) | 官转三款(`2.5-flare` / `2.5-sunburst` / `gpt-image-2`) | | - | - | - | | **上传参考图** | multipart `image` 文件字段(edits 端点) | multipart `image[]` 文件字段 | | **下载生成图** | 默认 `b64_json`(纯 base64,2026-07 实测),显式传 `response_format: "url"` 得 R2 CDN 链接(**24 小时有效期**) | `b64_json`(**纯 base64**,需 decode) | | **多图融合** | edits 端点 `image` 字段重复传入多张 | `image[]` 数组重复传入,**最多 16 张** | ### 4. 价格示例(粗算) | 场景 | 官逆五款(`-all` / `2.5-all` \$0.025;`-vip` 三款 \$0.03) | 官转 `gpt-image-2.5-flare` / `sunburst` | 官转 `gpt-image-2` | | - | - | - | - | | 1024×1024 草图(`low`) | \$0.025 / \$0.03 | \~\$0.006(196 tok) | \~\$0.006(196 tok) | | 1024×1024 中等画质(`medium`) | \$0.025 / \$0.03 | \~\$0.013(439 tok) | \~\$0.053(1,756 tok) | | 1024×1024 高画质(`high`) | \$0.025 / \$0.03 | \~\$0.053(1,756 tok) | \~\$0.211(7,024 tok) | | 1024×1024 `xhigh` / `max` | \$0.03(`-vip` 2.5 两款可传,`gpt-image-2-vip` 只到 `high`,`-all` 不接受 `quality`) | \~\$0.094(3,122 tok)/ \~\$0.211(7,024 tok) | ❌ 不支持 | | 2048×1152 高画质 | \$0.03(`-vip`;`-all` 只出 1K–1.5K) | 按 token 实计,`high` 约 `gpt-image-2` `medium` 的量级 | \~\$0.20+(按 token 实计) | | 3840×2160 4K 高画质 | \$0.03(`-vip` 4K Detail 档,不加价;`-all` / `2.5-all` 不支持 4K) | 按 token 实计,**显著高于 1K** | 按 token 实计,**显著高于 1K** | | 编辑 / 多图融合 | \$0.025 / \$0.03 | 输入 token 显著上升,单次成本可达 \$0.1+ | 输入 token 显著上升,单次成本可达 \$0.1+ | 以上官转数值按 2026-09-09 实测输出 token × \$30 / 百万粗算,未含提示词输入 token(通常不到 \$0.001)。 模型价格与官网对齐,且可能随官网调整;上表仅供参考,具体以顶部导航「模型价格」栏目为准:[模型价格](/models/index)。 **结论**:批量、低画质场景用官逆不一定省(草图 1K `low` 在官转上反而更便宜,2.5 两款连 `medium` / `high` 都只要 \~\$0.013 / \~\$0.053);**中-高画质区段**(`gpt-image-2` 的 `medium` 以上、2.5 两款的 `xhigh` 以上)才是官逆 \$0.025–0.03 的甜点区。**需要 `quality` 档位 / mask 局部重绘 / 锁尺寸、4K / OpenAI 官方完全对齐字段** 时选官转(按量计费)。 ## 客户端调用建议 | 设置项 | 官逆五款(`-all` / `2.5-all` / `-vip` 三款) | 官转三款(`2.5-flare` / `2.5-sunburst` / `gpt-image-2`) | | - | - | - | | **超时(保守值)** | `-all` / `2.5-all`:**300 秒**(典型 \~90s)
`-vip` 三款:**300 秒**(典型 120–200s) | 2.5 两款 `low` / `medium`:**240 秒**;`high` / `xhigh`:**300 秒**;`max` 与 `gpt-image-2` `high`:**600 秒** 兜底(4K 高画质实测可达 3-5 分钟),分档表见 [官转概览](/api-capabilities/gpt-image-2/overview) | | **重试策略** | 5xx 与超时指数退避 2 次 | 同左 | | **并发** | 单次仅返回 1 张,多张请并发 | 单次 1 张,需要多张请并发 | | **请求 ID** | `request-id` 响应头 | `x-request-id` 响应头 | **八个模型通用:图生图 / 多图融合时,单张输入图先压到 1.5MB 以内**(JPEG 质量 80-90 / 分辨率适当下调)。偶发的 `shell_api_error` / `Unknown error` 大多就是图片体积过大触发的,压一下请求成功率和出图速度都会明显改善。**输出分辨率与输入图体积无关**——画质看输出端配置(官转看 `size` + `quality`;`-vip` 三款看 `size` 档位 + `quality`;`-all` / `2.5-all` 看 prompt 画幅描述),不看输入端体积。 ## 常见问题 **强烈建议压**。八个模型上传给接口的图都先压到 **1.5MB 以内**(JPEG 质量 80-90 / 分辨率适当下调):偶发的 `shell_api_error` / `Unknown error` 大多就是图片体积过大触发的,压一下请求成功率和出图速度都会明显改善。 **别担心压输入会损画质**——输出分辨率与输入图体积无关,三类模型的"输出端"控制方式不同: * `gpt-image-2-all` / `gpt-image-2.5-all`:用 prompt 的画幅描述控制(参见 [-all 概览页的「经过验证的『提示词 → 实际分辨率』对照表」](/api-capabilities/gpt-image-2-all/overview)),prompt 光写 `4K` / `8K` 不算数 * `-vip` 三款(`gpt-image-2.5-vip` / `gpt-image-2.5-flare-vip` / `gpt-image-2-vip`):用 `size` 字段控制(30 档常见尺寸,含 4K),可再叠 `quality`(2.5 两款六档、`gpt-image-2-vip` 到 `high`);prompt 光写 `4K` / `8K` 同样不算数 * 官转三款(`gpt-image-2.5-flare` / `sunburst` / `gpt-image-2`):用 `size` + `quality` 字段控制(任意合法尺寸) 总结:压输入只会提速,不会损画质——画质看输出端配置,不看输入端体积。 可以。八款都走默认分组(Default),同一个 API Key 同时调用即可,无需额外配置。注意:调用官转三款需要「按量优先」类型的令牌;`-all` / `-vip` 两种令牌类型都能用。 **统一使用 OpenAI Images API**(`/v1/images/generations` 文生图 + `/v1/images/edits` 图片编辑),理由有二: 1. **更稳定**:上游对 Images API 通道的资源供给更充足,调用成功率更高 2. **兼容官转,便于切换**:与官转三款(`gpt-image-2.5-flare` / `sunburst` / `gpt-image-2`)调用方式、参数格式完全兼容——官逆通道遇到风控异常时,**只需更换 `model` 名即可切到官转**,业务代码零改动 另有对话式端点(`/v1/chat/completions`,**不主推**),仅适合多轮迭代改图、直接传在线图片 URL 的场景;注意出图意图不够明确时可能返回纯文字而不是图片(可在提示词开头加「生成图片:」前缀强化)。详细参数见 [-all 对话式调用说明](/api-capabilities/gpt-image-2-all/chat-completions) / [-vip 对话式调用说明](/api-capabilities/gpt-image-2-vip/chat-completions)。 两条线都是逆向通道、**调用方式一致**(`-all` \$0.025/张、`-vip` \$0.03/张)(`-vip` 三款额外支持 `size` 锁尺寸与 `quality`,2.5 两款六档全开),差异是**速度 vs 画质 + 是否锁尺寸**: * **出图速度**:`-all` / `2.5-all` 约 90 秒——**快就是优势**;`-vip` 三款约 120–200 秒。当前比刚上线时慢,源自 OpenAI 官方算力波动 * **画质**:`-vip`(Adobe 线)细节表现**有时更高**,适合不赶时间的精品图;三款里 sunburst-vip 与 `gpt-image-2-vip` 接近,flare-vip 偏软 * **锁尺寸**:`-vip` 三款支持 30 档 `size`(含 4K);`-all` / `2.5-all` 不接受 `size`,画幅写进 prompt 决策:追求出图速度 → `-all` / `2.5-all`;要锁尺寸 / 4K → `-vip` 三款;要 30 档以外的自定义尺寸或精确 mask → 官转。详见 [GPT-Image-2.5-VIP 概览](/api-capabilities/gpt-image-2-vip/overview)。 三款同价、同分组、同调用(2026-09-09 同渠道同令牌 253 次三臂对比,契约逐格一致),差异只有三处: * **档位**:2.5 两款六档全开(`xhigh` / `max` 2026-09-10 放开),`gpt-image-2-vip` 到 `high`;同名档位不等价——2048×1152 下 2.5 的 `high` 1,413 = `gpt-image-2-vip` 的 `medium`,2.5 的 `max` 5,650 = `gpt-image-2-vip` 的 `high`。三款能到的最高 token 档相同 * **画质与速度**:flare-vip 最快、画面偏软、装饰细节少;sunburst-vip 人眼与 `gpt-image-2-vip` 接近 * **缺省尺寸**:flare-vip 固定 1024×1536,另外两款 2048×2048;要锁尺寸一律显式传 `size` 别名 `gpt-image-2.5-vip` 就是 sunburst-vip。逐项对照表见 [GPT-Image-2.5-VIP 概览「三款 -vip 对比」](/api-capabilities/gpt-image-2-vip/overview)。 首选 `-vip` 三款(默认 `gpt-image-2.5-vip`,要最高 token 档位选 `gpt-image-2-vip` + `high`):`size` 参数已于 2026-07-22 恢复,支持 **30 档常见尺寸(10 比例 × 1K/2K/4K)**,统一 \$0.03/张、4K 不加价。注意 `size` 仅在 images 端点生效,且须为 30 档之一。 需要 **30 档以外的任意合法尺寸**、**官方承诺的 `quality` 六档**(`-vip` 的档位属渠道行为不承诺)、**精确的 mask 局部重绘**(alpha 通道蒙版)或 **OpenAI 官方完全对齐字段**(已有官方 SDK 代码零改动迁移)时,走官转(`gpt-image-2.5-flare` / `sunburst` / `gpt-image-2`,按量计费、按 token 实计)。 * **沿用官方 SDK / 要求与 OpenAI 官方一致,或要 30 档以外的任意自定义尺寸**:选官转(文生图 `gpt-image-2.5-flare`、改图 `gpt-image-2.5-sunburst`),需要删掉 `input_fidelity`,其它字段不动(`background: transparent` 照常可用) * **想顺便降低成本,追求出图速度**:选 `gpt-image-2.5-all`(官逆,\~90s;与 `gpt-image-2-all` 同价同行为) * **想顺便降低成本,画质优先或要锁尺寸 / 4K**:选 `gpt-image-2.5-vip`(官逆,\~120–200s,30 档 `size` 含 4K,`quality` 六档全开;`gpt-image-2-vip` 只到 `high`) 可以。常见做法:**主用 `2.5-all` 或 `2.5-vip`**(成本可预测,按速度 / 画质偏好选),**兜底用官转 `gpt-image-2.5-flare` / `sunburst`**(需要 `quality` 档位 / mask / 30 档以外自定义尺寸时切过去)。官转和官逆两类模型响应字段不同,业务层做一次格式归一即可。 详见 [下载 CDN 图片/视频很慢怎么办?](/faq/cdn-download-slow) ## 相关文档 * [GPT-Image-2.5 / 2 概览](/api-capabilities/gpt-image-2/overview) - 官转三款完整接入文档 * [GPT-Image-2.5-All 概览](/api-capabilities/gpt-image-2-all/overview) - 官逆 ChatGPT 网页线(出图最快,`gpt-image-2.5-all` / `gpt-image-2-all`)完整接入文档 * [GPT-Image-2.5-VIP 概览](/api-capabilities/gpt-image-2-vip/overview) - 官逆 Adobe 线(`gpt-image-2.5-vip` 系列 + `gpt-image-2-vip`,支持 `size` 锁尺寸、`quality` 档位)完整接入文档 * [深度解读:GPT-image-2.5 上线](/news/gpt-image-2-5-launch) - 2.5 双模型上线说明 * [深度解读:gpt-image-2 上线](/news/gpt-image-2-launch) - 官转上线说明 * [深度解读:gpt-image-2-all 上线](/news/gpt-image-2-all-launch) - 官逆上线说明 * [社区贡献:Luck GPT-Image 2 ComfyUI 节点](/scenarios/ecosystem/luckgpt2-comfyui) - 多模型合一的 ComfyUI 节点包 * [社区贡献:APIYI GPT-Image 2 Skills](/scenarios/ecosystem/apiyi-gpt-image-skills) - 多模型合一的 AI Agent Skill 包 * [充值优惠活动](/faq/recharge-promotions) - 充值加赠政策 # Grok 对话与推理调用指南 Source: https://docs.apiyi.com/api-capabilities/grok/chat Grok 系列在 API易 的 Chat Completions 全能力实测:流式输出、思维链 reasoning_content 与计费、结构化输出 json_schema、函数调用、视觉输入与自动缓存。 本文覆盖 Grok 系列在 `/v1/chat/completions` 端点上的全部对话类能力,所有结论基于 2026年7月13日 (UTC+8) 在 API易 网关的实测。 ## 基础对话与流式输出 全系 6 个模型均支持标准 OpenAI 格式与流式输出,`stream_options: {"include_usage": true}` 实测可用(末尾 chunk 返回完整 usage): ```python theme={null} from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1" ) stream = client.chat.completions.create( model="grok-4.3", messages=[{"role": "user", "content": "从1数到5,每个数字一行"}], stream=True, stream_options={"include_usage": True}, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") if chunk.usage: print(f"\n用量: {chunk.usage.total_tokens} tokens") ``` 实测流式首 token 延迟 1.5–2.3 秒(全模型),非流式短问答整体延迟 1.7–5.1 秒。 ## 思维链(Reasoning) 这是 Grok 系列最容易被误解的计费点,务必读完本节。 ### 哪些模型输出思维链 | 模型 | 思维链行为 | | - | - | | `grok-4.5` / `grok-4.3` / `grok-build-0.1` | **默认开启**,响应含 `reasoning_content` 字段,无法关闭 | | `grok-4.20-0309-reasoning` | 开启,含 `reasoning_content` | | `grok-4.20-0309-non-reasoning` | 关闭,`reasoning_tokens` 为 0,直接快答 | | `grok-4.20-multi-agent-beta-0309` | 内部推理不外露,但 `reasoning_tokens` 照常计费 | **推理 tokens 计入输出计费**。实测一条短问答:可见回答仅 30 tokens,实际计费输出 586 tokens(其中推理 556 tokens)。高频短问答场景选 `grok-4.20-0309-non-reasoning` 可显著省成本。 ### 读取思维链与推理用量 ```python theme={null} resp = client.chat.completions.create( model="grok-4.20-0309-reasoning", messages=[{"role": "user", "content": "A管8小时注满水池,B管12小时,同开需几小时?"}] ) msg = resp.choices[0].message print("回答:", msg.content) print("思维链:", msg.reasoning_content) # 推理过程文本 print("推理 tokens:", resp.usage.completion_tokens_details.reasoning_tokens) ``` ### reasoning\_effort 参数 `reasoning_effort`(如 `"low"` / `"high"`)**仅 `grok-4.5` 接受**;`grok-4.20-0309-reasoning` 会明确报错 `Model ... does not support parameter reasoningEffort`(400)。跨模型代码请勿硬编码该参数。 ## 结构化输出(Structured Outputs) 支持 OpenAI 标准的 `response_format: json_schema`(strict 模式),实测 `grok-4.5` / `grok-4.3` / `grok-build-0.1` / `grok-4.20-0309-reasoning` / multi-agent 模型全部通过,返回严格符合 schema 的 JSON: ```python theme={null} resp = client.chat.completions.create( model="grok-4.5", messages=[{"role": "user", "content": "张三今年25岁,住在杭州。请抽取信息。"}], response_format={ "type": "json_schema", "json_schema": { "name": "user_info", "strict": True, "schema": { "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer"}, "city": {"type": "string"} }, "required": ["name", "age", "city"], "additionalProperties": False } } } ) print(resp.choices[0].message.content) # {"name":"张三","age":25,"city":"杭州"} ``` ## 函数调用(Function Calling) 支持 OpenAI 标准的 `tools` / `tool_choice` 字段与完整的两轮工具调用流程(实测 `grok-4.5` / `grok-4.3` / `grok-build-0.1` 通过): ```python theme={null} tools = [{ "type": "function", "function": { "name": "get_weather", "description": "查询指定城市当前天气", "parameters": { "type": "object", "properties": {"city": {"type": "string", "description": "城市名"}}, "required": ["city"] } } }] messages = [{"role": "user", "content": "上海现在天气怎么样?"}] # 第一轮:模型决定调用工具 resp = client.chat.completions.create(model="grok-4.5", messages=messages, tools=tools) msg = resp.choices[0].message tool_call = msg.tool_calls[0] print(tool_call.function.name, tool_call.function.arguments) # get_weather {"city":"上海"} # 第二轮:喂回工具执行结果 messages += [ msg, {"role": "tool", "tool_call_id": tool_call.id, "content": '{"city": "上海", "temp_c": 31, "condition": "晴"}'} ] resp2 = client.chat.completions.create(model="grok-4.5", messages=messages, tools=tools) print(resp2.choices[0].message.content) # 上海现在天气晴朗,气温31℃。 ``` `tool_choice` 强制调用(`{"type": "function", "function": {"name": "get_weather"}}`)实测同样可用。 这里说的是**客户端函数调用**(工具由你的代码执行)。如果想让 xAI 服务端替你执行搜索 / 跑代码 / 连 MCP,请走 Responses API,见 [联网搜索与 X 搜索](/api-capabilities/grok/web-search) 和 [代码执行与 MCP](/api-capabilities/grok/code-execution-mcp)。 ## 视觉输入(图片理解) Grok 4.x 对话模型支持图片输入(jpg / png,单图不大于 20MiB),使用 OpenAI Vision 兼容格式。实测 `grok-4.5` / `grok-4.3` / `grok-4.20-0309-non-reasoning` 均正确识别图形与颜色: ```python theme={null} import base64 with open("image.jpg", "rb") as f: b64 = base64.b64encode(f.read()).decode() resp = client.chat.completions.create( model="grok-4.5", messages=[{ "role": "user", "content": [ {"type": "text", "text": "图里有什么?"}, {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}} ] }] ) print(resp.choices[0].message.content) ``` **优先使用 base64 data URL**。传外链 URL 时,图片由 xAI 上游服务器直接抓取——实测部分图床(如维基媒体)会对服务器抓取返回错误,导致请求失败(`image_download_error`)。若必须用外链,请确保图床对服务端请求开放且 URL 直接指向图片文件。 ## Prompt Caching(自动缓存) Grok 前缀缓存**自动生效,无需任何配置**,命中部分按缓存折扣价计费(`grok-4.6` 为 0.25×,省 75%): ```python theme={null} resp = client.chat.completions.create(model="grok-4.6", messages=messages) print("缓存命中:", resp.usage.prompt_tokens_details.cached_tokens) ``` 优化建议:把稳定不变的 system prompt / few-shot 示例放在消息最前面,可变内容放最后,最大化前缀命中。xAI 官方明确缓存条目可能被驱逐、不保证 100% 命中,**做成本测算请按无缓存价格打底**。 命中判读、128 token 块粒度、长对话该走哪个端点等完整口径,见 [Grok 缓存计费指南](/api-capabilities/grok/prompt-caching)。 ## 常见问题 关不掉。`grok-4.5` / `grok-4.3` / `grok-build-0.1` 的内部推理是模型固有行为。若不需要思维链、追求快答低成本,直接改用 `grok-4.20-0309-non-reasoning`。 多轮对话回传历史时,只需回传 `content`(和工具调用相关字段),不要把 `reasoning_content` 塞回 messages——它不是标准字段,回传徒增输入 tokens。 推理模型的思维链也消耗输出配额,`max_tokens` 给小了会导致思维链吃满配额、正文被截断。带推理的模型建议 `max_tokens` 至少 2048 起步。 可以正常传入。注意推理类模型对采样参数的敏感度低于传统模型,调优价值有限。 ## 相关文档 模型阵容、定价与能力矩阵 命中省 75%、判读口径与长对话接法 server-side 联网工具实战 # Grok 代码执行与 Remote MCP 使用指南 Source: https://docs.apiyi.com/api-capabilities/grok/code-execution-mcp Grok 在 API易 的 code_interpreter 服务端代码执行与 Remote MCP 工具实测:Python 沙箱真实运行、外部 MCP server 成功连通,附调用示例与响应结构。 除联网搜索外,Grok 的 Responses API 还提供两类 server-side 工具:**代码执行**(`code_interpreter`,服务端 Python 沙箱)与 **Remote MCP**(让 xAI 服务端直连你指定的 MCP server)。两者在 API易 上均实测可用(2026年7月13日 (UTC+8)),默认分组 KEY 直接调用。 ## 代码执行(Code Execution) 模型自动编写 Python 代码并在 xAI 服务端沙箱中真实运行,适合精确计算、数据处理等场景。实测让模型计算 2 的 100 次方,模型执行 `print(2 ** 100)` 后返回了精确值(大整数运算,纯语言模型极易算错,代码执行则完全精确): ```python theme={null} from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1", ) resp = client.responses.create( model="grok-4.5", tools=[{"type": "code_interpreter"}], input="用代码计算 2 的 100 次方,给出精确值", ) print(resp.output_text) # 2^100 = 1267650600228229401496703205376 # 查看模型实际执行的代码 for item in resp.output: if item.type == "code_interpreter_call": print("执行的代码:", item.code) ``` 实测单次代码执行任务延迟约 6 秒。usage 的 `server_side_tool_usage_details.code_interpreter_calls` 记录执行次数。 ## Remote MCP 工具 在请求中声明外部 MCP server,xAI 服务端会自动连接、列出其工具并按需调用——你无需在本地跑任何 MCP 客户端。实测成功连通公开 MCP server 并完成工具调用(延迟约 16 秒): ```python theme={null} resp = client.responses.create( model="grok-4.5", tools=[{ "type": "mcp", "server_label": "deepwiki", "server_url": "https://mcp.deepwiki.com/mcp", "require_approval": "never" }], input="使用 deepwiki 查询 openai/openai-python 仓库是做什么的,一句话", ) print(resp.output_text) # 查看 MCP 调用明细 for item in resp.output: if item.type == "mcp_call": print("工具:", item.name, "| 输出:", item.output[:200]) ``` | 参数 | 说明 | | - | - | | `server_label` | 自定义标签,用于区分多个 MCP server | | `server_url` | MCP server 地址(需公网可达,xAI 服务端直连) | | `require_approval` | `"never"` 为自动调用;默认会返回审批请求,需要二次确认 | * **MCP server 必须公网可达**:连接由 xAI 服务端发起,内网 / localhost 地址不可用。 * **注意数据安全**:你的对话内容会经 xAI 服务端发送到该 MCP server,请只接入可信服务。 * 外部 server 的可用性不受 API易 控制,调用失败时请先自查 server 状态。 ## Collections Search(RAG)不可用 xAI 官方还有一个 `collections_search`(知识库检索 / file\_search)工具,**在 API易 上不适用**:它依赖在 xAI 控制台预先上传文件并建立知识库,而 API易 为号池模式、用户无法访问上游控制台。实测请求可透传但检索必然失败(`file_search_call` 返回 failed)。 需要 RAG 场景请在业务侧自建检索(向量库 + 把召回内容拼入 prompt),配合 Grok 的 1M 上下文与 [自动缓存](/faq/cache-billing) 使用。 ## 常见问题 可以。`tools` 数组可同时包含 `web_search` / `x_search` / `code_interpreter` / `mcp`,模型按任务自行决定调用哪个。usage 的 `server_side_tool_usage_details` 会分项计数。 沙箱面向计算场景(Python 运算 / 数据处理),不能访问你的本地文件;需要外部数据请配合 web\_search 或 MCP 工具。 模型推理消耗的 tokens 正常计费。工具本身的调用费以 API易 工具定价与账单实际扣费为准,建议开发期先小量验证再放量。 ## 相关文档 同为 Responses API 的联网类工具 模型阵容、定价与能力边界总表 # Grok Multi-Agent 多智能体模型指南 Source: https://docs.apiyi.com/api-capabilities/grok/multi-agent grok-4.20-multi-agent-beta-0309 多智能体协作模型在 API易 的实测:多 agent 并行解题、适合复杂研究任务,重点说明其内部 agent 流量全额计费的成本放大特性。 `grok-4.20-multi-agent-beta-0309` 是 xAI 的多智能体协作模型:一次请求内部会启动**多个 agent 并行工作**(模型自称 Oppie,「协作型 AI 团队领导者」),由领导 agent 汇总产出最终回答。适合复杂研究、多角度对比分析类任务。API易 已上架,OpenAI 兼容格式直接调用。 ## 计费特性(务必先读) **内部 agent 的全部流量都计入你的账单**。这是该模型与普通模型最大的差异: * 实测一个约 40 tokens 的普通提问,实际计费 **39,263 prompt tokens + 9,997 completion tokens**(内部多 agent 的往返流量全部计入) * 即使最简单的一句话请求,也有约 **3,900 prompt tokens 起步**的固定开销 * 单价虽与 grok-4.3 相同(\$1.25 / \$2.50 每 1M tokens),**单次请求实际成本可达普通模型的几十倍** 简单任务请勿使用该模型——普通问答用 `grok-4.3` 或 `grok-4.20-0309-reasoning` 即可。 好消息是内部流量的缓存命中率很高(实测 39K prompt tokens 中 26.8K 命中缓存折扣价 —— 这是多次内部调用汇总后的口径,不是单次请求的命中量),实际成本低于名义 token 数的直接换算,但仍显著高于普通模型。缓存本身的规则见 [Grok 缓存计费指南](/api-capabilities/grok/prompt-caching)。 ## 调用方式 与普通模型完全一致,仅 `model` 字段不同: ```python theme={null} from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1", ) resp = client.chat.completions.create( model="grok-4.20-multi-agent-beta-0309", messages=[{ "role": "user", "content": "比较 Rust 和 Go 在构建高并发网络服务时的优劣,各给3点,最后给一句结论" }], ) print(resp.choices[0].message.content) print("实际计费 tokens:", resp.usage.total_tokens) ``` 多智能体协作是**服务端内部行为**,无需任何额外参数;流式输出、结构化输出(`json_schema`)实测同样支持。 ## 实测特征(2026-07-13) | 维度 | 实测值 | | - | - | | 中等复杂度任务延迟 | \~29 秒 | | 简单问答延迟 | \~5 秒 | | 简单问答固定开销 | \~3,900 prompt tokens | | 中等任务 token 消耗 | \~39K prompt + \~10K completion | | 内部缓存命中 | 约 2/3 的 prompt tokens 命中缓存折扣 | | 思维链外露 | 不外露(`reasoning_tokens` 照常计费) | ## 选型建议 多角度深度对比分析、复杂研究类问题、需要多条思路交叉验证的开放性任务——多 agent 并行探索能明显提升答案的全面性。 日常问答、翻译、摘要、代码补全等单线任务——效果与普通模型接近,成本却放大几十倍。这些场景用 grok-4.3 / grok-4.5 即可。 上线前建议先用少量真实任务对比 `grok-4.20-0309-reasoning` 与 multi-agent 模型的输出质量差异,再决定是否为质量增量支付成本放大——多数场景下推理变体已经够用。 ## 常见问题 这是该模型的内置人设(多 agent 团队的领导者角色),属正常现象。验证模型身份以请求与响应的 `model` 字段为准。 不能。多智能体编排是 xAI 服务端内部行为,对外不暴露任何控制参数。 建议放宽(如 8192),该模型内部推理消耗大,配额过小容易截断最终回答。 ## 相关文档 全系模型阵容与定价 普通模型的思维链与计费说明 # Grok 系列模型调用指南 Source: https://docs.apiyi.com/api-capabilities/grok/overview xAI Grok 4.x 系列(grok-4.7 / grok-4.6 / grok-4.5 / grok-4.3 / grok-4.20 / grok-build-0.1)API易 调用总览:OpenAI 兼容 + Responses API 双端点,联网搜索 / X 搜索 / 代码执行 / MCP 四大 server-side 工具实测可用,定价与 xAI 官网一致,默认分组即官转线路。 Grok 是 xAI 推出的旗舰大模型家族。当前一代(Grok 4.x)覆盖旗舰通用、长上下文标准档、推理/非推理双变体、代码专用与多智能体协作五条产品线,全部已上架 API易。**xAI 官方 API 本身就是 OpenAI 兼容格式**(Chat Completions + Responses API),没有独立的私有协议——因此在 API易 上用 OpenAI SDK 即可调用全部能力,包括官方的 server-side 工具(联网搜索、X 搜索、代码执行、Remote MCP)。 本组文档基于 2026年7月13日 (UTC+8) 在 API易 网关上的全量实测(56 组请求/响应日志)编写,能力边界均有实测依据。 **🚀 核心亮点**:`grok-4.7` 是 SpaceXAI(xAI)于 2026年9月21日 发布的最新旗舰,换用 2.1T 参数新基座,编码 Agent 提升最明显,**定价与 grok-4.6 完全相同**;grok-4.3 与 grok-4.20 系列提供 **100 万 tokens 上下文**;Responses API 的 **web\_search / x\_search / code\_interpreter / MCP 四大工具在 API易 实测真实可用**,其中 X 搜索是 Grok 独有的差异化能力;原生 responses 协议还意味着 Grok 可以[直接接入 OpenAI Codex](/scenarios/programming/codex-cli)。Grok 全系在默认分组即走 xAI 官转线路,定价与官网一致,详见下方「分组与折扣」。 ## 模型阵容 **最新旗舰 · 代码与 Agent** 2026/9/21 发布,500K 上下文。2.1T 参数新基座,与 grok-4.6 同价,长任务编码提升明显。 **上一代旗舰 · 同价** 2026/8/7 发布,500K 上下文。与 grok-4.5 同基座,长周期任务的自检与验证更强。 **上一代旗舰 · 同价** 500K 上下文,与 grok-4.6 输入输出同价,存量业务可继续使用。 **标准主力** 1M 上下文,价格是旗舰的六折出头,日常对话与中等推理的均衡之选。 **推理 / 非推理可选** `-reasoning` 与 `-non-reasoning` 同价同上下文(1M),按需选择是否输出思维链。 **代码专用** 256K 上下文,全系最低单价,适合高频代码补全与轻量编程任务。 **多智能体协作** 多个 agent 并行协作解题,适合复杂研究任务。计费特性特殊,详见 [Multi-Agent 模型](/api-capabilities/grok/multi-agent)。 对话/推理/视觉见 [对话与推理](/api-capabilities/grok/chat);联网见 [联网搜索与 X 搜索](/api-capabilities/grok/web-search)。 ## 模型定价 定价与 xAI 官网一致(已于 2026-07-13 经 API易 定价接口逐一核对,`grok-4.6` 于 2026-08-13、`grok-4.7` 于 2026-09-24 补核),可叠加 [充值加赠活动](/faq/recharge-promotions)。 下表为 **0 – 200K 上下文档位**的定价(Grok 全系按上下文长度阶梯计费,超过 200K 的档位见下方说明): | 模型 ID | 上下文 | 提示价格(输入) | 补全价格(输出) | 定位 | | - | - | - | - | - | | `grok-4.7` | 500K | \$2.00 / 1M tokens | \$6.00 / 1M tokens | **最新旗舰**,代码/Agent/通用 | | `grok-4.6` | 500K | \$2.00 / 1M tokens | \$6.00 / 1M tokens | 上一代旗舰,与 4.7 同价 | | `grok-4.5` | 500K | \$2.00 / 1M tokens | \$6.00 / 1M tokens | 上一代旗舰,与 4.6 同价 | | `grok-4.3` | 1M | \$1.25 / 1M tokens | \$2.50 / 1M tokens | 标准主力 | | `grok-4.20-0309-reasoning` | 1M | \$1.25 / 1M tokens | \$2.50 / 1M tokens | 推理变体 | | `grok-4.20-0309-non-reasoning` | 1M | \$1.25 / 1M tokens | \$2.50 / 1M tokens | 非推理变体(快答/低成本) | | `grok-4.20-multi-agent-beta-0309` | 1M | \$1.25 / 1M tokens | \$2.50 / 1M tokens | 多智能体(注意计费放大) | | `grok-build-0.1` | 256K | \$1.00 / 1M tokens | \$2.00 / 1M tokens | 代码专用 | ### 阶梯计费与缓存价 Grok 全系按**单次请求的上下文长度**分两档计费,分档点为 200K tokens(即 200Ki = 204,800)。超过该点的请求,输入与输出单价翻倍: | 模型 | 档位 | 输入 | 输出 | 缓存读取 | | - | - | - | - | - | | `grok-4.7` | 0 – 200K | \$2.00 | \$6.00 | \$0.50 | | `grok-4.7` | 200K – 500K | \$4.00 | \$12.00 | \$1.00 | | `grok-4.6` | 0 – 200K | \$2.00 | \$6.00 | \$0.50 | | `grok-4.6` | 200K – 512K | \$4.00 | \$12.00 | \$1.00 | | `grok-4.5` | 0 – 200K | \$2.00 | \$6.00 | \$0.30 | | `grok-4.5` | 200K – 512K | \$4.00 | \$12.00 | \$0.60 | | `grok-4.3` / `grok-4.20` 系列 | 0 – 200K | \$1.25 | \$2.50 | \$0.20 | | `grok-4.3` / `grok-4.20` 系列 | 200K – 1M | \$2.50 | \$5.00 | \$0.40 | | `grok-build-0.1` | 0 – 200K | \$1.00 | \$2.00 | \$0.20 | | `grok-build-0.1` | 200K – 256K | \$2.00 | \$4.00 | \$0.40 | 单位均为每 1M tokens。`grok-4.7` 与 `grok-4.6` 四项计费完全相同;**`grok-4.6` 与 `grok-4.5` 唯一的价格差异在缓存读取**(\$0.50 vs \$0.30),输入输出完全相同。 其中 `grok-4.7` 与 `grok-4.6` 两档的输入 / 输出 / 缓存读取价均已逐项核对(`grok-4.7` 的分档点为 200,000 tokens);其余模型第二档的缓存读取价按官方「第二档单价翻倍」口径推算,以 [模型价格页](/api-capabilities/model-info) 的实时定价为准。 * 别名 `grok-code-fast` / `grok-code-fast-1` 亦可调用(实测连通),定价以 [模型价格页](/api-capabilities/model-info) 为准。 * 缓存命中的输入 tokens 享受上表的缓存读取价,Grok 前缀缓存**自动生效、无需配置**,双端点与流式 / 非流式均已实测覆盖,详见 [Grok 缓存计费指南](/api-capabilities/grok/prompt-caching)。 * 长上下文任务请留意分档点:一次 210K tokens 的请求,全部 tokens 都按第二档计价,而不是只有超出的 10K 按第二档。拆分请求可以避开这一跳变。 ## 分组与折扣 | 分组 | 倍率 | 说明 | | - | - | - | | `default` | 1.0x | 默认分组,即 xAI 官转线路,定价与官网一致 | | `CodexResponses` | 1.0x | 面向 Codex 等 Responses 客户端,模型与定价同默认分组 | Grok 系列在默认分组就走 xAI 官转线路,不再单独设官转分组。此前的 `GrokOfficial` 分组已不对外展示,倍率同为 1x,已在使用该分组的令牌无需改动。 如需进一步降低成本,可叠加 [充值加赠](/faq/recharge-promotions),按单次充值金额分档。 ## 实测能力矩阵 以下矩阵来自 2026-07-13 (UTC+8) 在 API易 网关的实测(✅ 实测通过;◐ 未实测、同架构预期一致;— 未覆盖,同架构预期一致): | 能力 | grok-4.6 | grok-4.5 | grok-4.3 | 4.20-reasoning | 4.20-non-reasoning | grok-build-0.1 | multi-agent | | - | :-: | :-: | :-: | :-: | :-: | :-: | :-: | | 基础对话 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | 流式输出(含 usage) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | 思维链输出 `reasoning_content` | ◐ 默认开启 | ✅ 默认开启 | ✅ 默认开启 | ✅ | ❌ 按设计关闭 | ✅ 默认开启 | 内部推理不外露 | | `reasoning_effort` 参数 | ◐ | ✅ | — | ❌ 明确拒绝 | — | — | — | | 结构化输出(json\_schema) | ◐ | ✅ | ✅ | ✅ | — | ✅ | ✅ | | 函数调用 / Tool Use | ◐ | ✅ | ✅ | — | — | ✅ | — | | 视觉输入(图片理解) | ◐ | ✅ | ✅ | — | ✅ | — | — | | Prompt Caching(自动) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Responses API + server-side 工具 | ◐ | ✅ | — | — | — | — | — | **`grok-4.6` 那一列为什么还有 ◐**:这套 56 组请求的实测跑于 2026-07-13,当时 4.6 尚未发布。2026-08-19 我们对 4.6 补测了**基础对话、流式输出(含 usage)与 Prompt Caching** 三项(覆盖 `/v1/chat/completions` 与 `/v1/responses` 双端点、流式与非流式,并逐条核对了账单),已改为实测结论。其余仍为 ◐ 的项目沿用同架构预期一致的判断:4.6 与 4.5 同为 1.5T 参数 V9 基座、同一套 API 协议与端点,上游也未公告任何参数层面的破坏性变更。生产接入前建议先在自己的用例上做一次小样验证。 `grok-4.7`(2026-09-21 发布)尚未纳入本矩阵。它换用了 2.1T 新基座,不适用上面「同基座预期一致」的推断,接入前请先在自己的用例上小样验证。 ## 端点一览 | 端点 | 方法 | 用途 | | - | - | - | | `/v1/chat/completions` | `POST` | 对话 / 推理 / 函数调用 / 结构化输出 / 视觉(全系共用,`model` 字段区分) | | `/v1/responses` | `POST` | Responses API:联网搜索、X 搜索、代码执行、Remote MCP 等 server-side 工具 | ### 在 Codex 中直接使用 因为原生支持 `/v1/responses`,Grok 是少数能在 **OpenAI Codex**(桌面客户端 / IDE 插件 / CLI)里以 responses 原生协议直接使用的非 OpenAI 模型——`config.toml` 里 `model = "grok-4.6"`、`wire_api = "responses"` 即可,5 分钟接上,Codex 的工具调用、推理条目等 Agent 能力全走原生协议。对比之下,Claude / Gemini 在 API易 上只能走 OpenAI 兼容 chat 模式(`wire_api = "chat"` 兜底),在 Codex / Agent 场景存在协议不兼容。完整接入步骤见 [Codex 接入教程](/scenarios/programming/codex-cli)。 **以下能力在 API易 上不支持**(实测确认,避免踩坑): * **Legacy Completions(`/v1/completions`)**:上游拒绝——Grok 4.x 全系为推理架构,官方层面即不支持文本补全模式 * **联网旧入口 `search_parameters`**:已被 xAI 下线(实测返回 410),联网一律走 Responses API 工具,见 [联网搜索](/api-capabilities/grok/web-search) * **Batch API / Files 文件能力**:网关不路由,号池模式不适用 * **Deferred Completions(`deferred: true`)**:参数会被**静默忽略**,请求按同步执行并正常计费,请勿依赖 * **Collections Search(RAG / file\_search)**:需在 xAI 控制台预建知识库,号池模式不适用 * **Context Compaction(`/v1/responses/compact`)**、**Priority Processing(`service_tier: "priority"`,实测回落 default)**、**WebSocket 模式**、**mTLS 认证**:均不支持 ## 快速开始 ```bash cURL theme={null} curl -X POST "https://api.apiyi.com/v1/chat/completions" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "grok-4.6", "messages": [ {"role": "user", "content": "用一句话介绍你自己"} ] }' ``` ```python Python theme={null} from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1" ) resp = client.chat.completions.create( model="grok-4.6", messages=[{"role": "user", "content": "用一句话介绍你自己"}] ) print(resp.choices[0].message.content) ``` ```javascript Node.js theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apiyi.com/v1', }); const resp = await client.chat.completions.create({ model: 'grok-4.6', messages: [{ role: 'user', content: '用一句话介绍你自己' }], }); console.log(resp.choices[0].message.content); ``` **模型怎么选**:默认用 `grok-4.3`(1M 上下文、价格均衡);代码 Agent / 复杂任务升级 `grok-4.7`(最新旗舰,与 `grok-4.6` 同价,但单任务输出 tokens 更多,建议先按真实任务对比成本);追求快答低成本用 `grok-4.20-0309-non-reasoning`(无思维链、输出 tokens 最省);高频代码补全用 `grok-build-0.1`;复杂研究类任务才考虑 multi-agent 模型(注意其计费放大特性)。想进一步降低成本,可叠加充值加赠。 ## 计费注意:思维链 tokens `grok-4.7` / `grok-4.6` / `grok-4.5` / `grok-4.3` / `grok-build-0.1` **默认带内部推理**:响应中返回 `reasoning_content`,且推理 tokens 计入输出计费。实测短问答中可见文本仅 30 tokens、实际计费输出 586 tokens(含 556 推理 tokens)。对成本敏感的短问答场景,建议改用 `grok-4.20-0309-non-reasoning`。详细说明见 [对话与推理](/api-capabilities/grok/chat)。 ## 常见问题 没有独立私有协议。xAI 官方 REST API 就是 OpenAI 兼容格式:`/v1/chat/completions`(对话)+ `/v1/responses`(Responses API 与 server-side 工具)。在 API易 上用 OpenAI SDK 把 `base_url` 指向 `https://api.apiyi.com/v1` 即为全功能调用,不存在"兼容模式阉割"。 走 Responses API:`tools: [{"type": "web_search"}]`(或 `x_search`)。旧版 Chat Completions 的 `search_parameters` 参数已被 xAI 下线(实测 410),不要再使用。详见 [联网搜索与 X 搜索](/api-capabilities/grok/web-search)。 正常现象。Grok 4.x 全系的自我认知均为「Grok 4」(multi-agent 模型自称 Oppie),不会精确报出 4.6 / 4.5 / 4.3 等版本号。验证模型身份请以请求的 `model` 字段和响应 `model` 字段为准,而非模型的自我介绍。 不需要。Grok 前缀缓存自动生效,响应 `usage.prompt_tokens_details.cached_tokens` 可自查命中量(`/v1/responses` 端点看 `usage.input_tokens_details.cached_tokens`)。命中量按 128 token 取整,两轮实测都吻合:8802 token 的前缀命中 8704、2735 token 的前缀命中 2688。xAI 官方明确缓存条目可能被驱逐、不保证 100% 命中,**做成本测算请按无缓存价格打底**。完整口径见 [Grok 缓存计费指南](/api-capabilities/grok/prompt-caching)。 返回 400 错误。注意各档上限不同:grok-4.7、grok-4.6 与 grok-4.5 为 500K,grok-4.3 与 4.20 系列为 1M,grok-build-0.1 为 256K。超长内容建议先做摘要 / 分段 / RAG 检索。 4xx 类客户端错误(参数错误 / 鉴权失败)不计费;已成功返回 tokens 的请求按实际消耗计费。注意 `deferred: true` 会被静默忽略——请求实际同步执行并正常计费。 ## 相关文档 流式、思维链、结构化输出、函数调用、视觉输入、缓存 命中省 75%、128 token 块粒度、长对话该走哪个端点 Responses API 的 web\_search / x\_search 工具实战 服务端 Python 沙箱与 Remote MCP 工具接入 多智能体协作模型的能力与计费特性 原生 responses 协议直连 Codex,5 分钟接上 xAI 最新旗舰的基准、定价与迁移建议 上一代旗舰的深度解读 查看所有可用模型及分组 # Grok Prompt Caching 缓存计费指南 Source: https://docs.apiyi.com/api-capabilities/grok/prompt-caching Grok 缓存全自动、无写入费:命中部分按输入价 0.25x 计费。128 token 块粒度、怎么写出会命中的请求、怎么看 cached_tokens、长对话为什么该走 responses 链式。 用 Grok 跑 Agent、长系统提示词、多轮对话时,Prompt Caching 能把**命中部分**的输入账单打到 **0.25×**(省 75%),而且**什么代码都不用改**——缓存是全自动的。 先把预期说在前面:xAI 官方明确缓存条目可能因负载、重启、路由变化被驱逐,**不保证 100% 命中**。把缓存折扣当作「有则更好」的额外优惠,**做成本测算时按无缓存价格打底**。 本页基于 xAI 官方文档(`docs.x.ai/developers/advanced-api-usage/prompt-caching`)整理,并以 **2026-08-19 在 API易 网关上对 `grok-4.6` 的实测**为准(124 次调用,逐条与后台账单核对)。 ## 一句话理解 只要请求的**开头部分(前缀)与近期某次请求逐字相同**,上游就自动跳过重复处理:命中部分按 **0.25×** 计费,不需要任何参数、不需要打标记。 和另外两家的差别: * **对比 Claude**:不用打 `cache_control` 标记,达到条件自动生效 * **对比 OpenAI**:同样全自动、同样没有写入费,但 Grok 没有 `prompt_cache_key` 这类由你控制路由的手段 ## 为什么要用 —— 看账单倍率 以模型原始输入 token 价为 **1×** 计: | 类型 | 价格 | 说明 | | - | - | - | | 普通输入 | **1×** | 没命中的部分,原价 | | 缓存写入 | **0×(免费)** | 自动发生,不收钱 | | **缓存命中** | **0.25×** | 命中部分便宜 75% | **回本点:第 2 次请求就净省。** 没有写入成本要摊,同一前缀只要被复用一次,省下的就是纯收益。 按 `grok-4.6` 的挂牌价换算(每 1M tokens,两个上下文档位): | 档位 | 普通输入 | 缓存命中 | | - | - | - | | 0 – 200K | \$2.00 | **\$0.50** | | 200K – 512K | \$4.00 | **\$1.00** | 其余 Grok 型号的分档与缓存读取价见 [Grok 概览的阶梯计费表](/api-capabilities/grok/overview)。 ### 适合场景 * 同一份长系统提示词 + 工具定义被反复调用(Agent、客服机器人) * 批量处理同一份文档(一份合同问 50 个问题) * RAG 把稳定的文档块放在 prompt 前部 * 多轮对话(注意:Grok 上 chat 与 responses 两种接法的效果差别很大,见下文) ### 不适合场景 * 每次请求从第一个字开始就不一样 * 整个 prompt 在**千 token 量级以下**——实测这种请求反复调用也形不成可复用的缓存 ## 两个端点、流式与非流式都已核对 `/v1/chat/completions` 与 `/v1/responses`,各自的流式与非流式,**四种组合我们于 2026-08-19 逐条核对过后台账单**,命中部分均按缓存价单列计费: | | 非流式 | 流式 | | - | - | - | | `/v1/chat/completions` | 已核对 | 已核对 | | `/v1/responses` | 已核对 | 已核对 | **代码无需为中转层做任何适配。** 缓存相关行为原样转发上游,`cached_tokens` 原样回吐,后台账单把命中部分单列为「缓存读取」计费项。 ## 触发条件 | 条件 | 要求 | | - | - | | 触发方式 | **全自动**,无参数、无标记 | | 匹配起点 | 从 `messages` 数组**开头**逐字比对 | | 只能追加 | 修改 / 删除 / 重排历史消息会让缓存作废;**尾部追加不影响** | | 块粒度 | **128 token**(见下) | | 长度 | 官方未公布最小门槛;实测千 token 量级以下形不成可复用缓存 | | 时间窗 | 官方明确随时可能驱逐,**间隔越短越稳** | ### 命中量按 128 token 取整 ```text theme={null} 命中量 = ⌊匹配上的前缀长度 ÷ 128⌋ × 128 ``` 两轮实测都吻合:8802 token 的前缀命中 8704(= 68 × 128),更早一轮 2735 token 的前缀命中 2688(= 21 × 128)。**所以 `cached_tokens` 通常略小于你的稳定前缀总长,是正常现象。** ### 只能追加:改历史即失效 同一段前缀贴着连发,只改动其中一次: | 操作 | `cached_tokens` | | - | - | | 不改动 | 8704 | | **把前缀首字符改掉** | 128(等于没命中) | | **在前缀末尾追加一行** | 8704(不受影响) | | 再发一次原前缀 | 8704 | **实践含义:稳定的东西放前面,易变的东西放后面。** ```python theme={null} # ❌ 错:动态内容拼在 system 开头,前缀每次都变,永远命中不了 messages = [ {"role": "system", "content": f"当前时间 {datetime.now()}。你是一个助手。" + 长指令}, {"role": "user", "content": 问题}, ] # ✅ 对:长指令和工具定义在前保持稳定,动态内容放 user 消息里 messages = [ {"role": "system", "content": 长指令}, # 稳定,会命中 {"role": "user", "content": f"当前时间 {datetime.now()}。{问题}"}, # 易变,放最后 ] ``` ## 最小可运行示例 同一段长前缀发两次不同问题,第一次自动写入,第二次命中: ```python theme={null} import os from openai import OpenAI client = OpenAI( api_key=os.environ["APIYI_API_KEY"], base_url="https://api.apiyi.com/v1" ) # 前缀要足够长:千 token 量级以下基本吃不到缓存 LONG_SYSTEM = open("long_instructions_zh.txt").read() def ask(question: str, label: str): r = client.chat.completions.create( model="grok-4.6", messages=[ {"role": "system", "content": LONG_SYSTEM}, {"role": "user", "content": question}, ], ) cached = r.usage.prompt_tokens_details.cached_tokens print(f"[{label}] input={r.usage.prompt_tokens} cached={cached}") ask("请概括要点", "第1次") # 冷启动,cached 为 0 或极小值 ask("请给3个关键词", "第2次") # 期望 cached 接近前缀长度 ``` 期望看到的输出: ```text theme={null} [第1次] input=8804 cached=128 [第2次] input=8804 cached=8704 ``` 第 2 次的 `cached` 接近系统提示词长度(按 128 取整),这部分按 0.25× 计费。 `/v1/responses` 端点同样自动生效,字段换成 `usage.input_tokens_details.cached_tokens`,机制完全一致。**长对话在这个端点上还有额外优势**,见下文「长对话优先走 responses 链式」。 ## 怎么判断命中 —— 看 usage 字段 | 端点 | 命中字段 | | - | - | | `/v1/chat/completions` | `usage.prompt_tokens_details.cached_tokens` | | `/v1/responses` | `usage.input_tokens_details.cached_tokens` | ### 判读口径:小值不算命中 不要只看「大于 0」。**拿 `cached_tokens` 和你的稳定前缀长度做比**: | `cached_tokens` | 判读 | | - | - | | `0` | 未命中 | | 相对前缀长度只占零头(几十、一两百这样的小值) | **同样按未命中看待** | | 上千,且接近前缀长度按 128 取整后的值 | 真命中 | 实测冷启动的首次调用也可能回显一个一两百的小值,别被它骗到 —— 那不代表你的前缀被缓存了。 ### 对账:控制台的缓存计费详情 后台单条调用日志里会**单列缓存读取的 token 数与对应的折扣倍率**,可以直接和响应里的 `cached_tokens` 对上。需要精确核算某一次调用到底怎么计费时,以那里为准。 自检三步: 1. 构造一个千 token 以上的稳定前缀,连续发 2 次请求 2. 第 2 次响应应看到 `cached_tokens` 明显上千 3. 后台 [调用日志](/faq/call-logs) 里对应请求出现「缓存读取」计费项,输入费用明显低于第 1 次 ## 提高命中率 ### 稳定前缀工程化 * 长指令、few-shot 示例、工具定义放最前面;用户输入、时间戳放最后 * 工具定义的顺序与 JSON 序列化方式保持固定(别让序列化库随机排序字段) * 图片输入也参与前缀比对,复用图片时保持 base64 / URL 与参数一致 * 同一前缀**集中连续复用**,不要拉开间隔 方法论与 OpenAI 一致,展开解释见 [OpenAI 缓存计费指南](/api-capabilities/openai/prompt-caching)。 ### 长对话优先走 responses 链式 这是 Grok 上一个容易被忽略的差别: | 接法 | 实测表现 | | - | - | | `/v1/chat/completions` 追加式多轮 | 连追 5 轮、prompt 从 8.8K 涨到 10K,`cached_tokens` **始终停在最初那段静态前缀的量级** —— 每轮新增的问答没有被复用起来 | | `/v1/responses` + `previous_response_id` | 命中量**随轮次增长**(实测第 2 轮 8704 → 第 3 轮 9344) | 所以长对话、Agent 多步骤这类场景,优先用 Responses API 的链式接法: ```python theme={null} r1 = client.responses.create( model="grok-4.6", input=[{"role": "system", "content": LONG_SYSTEM}, {"role": "user", "content": "第一个问题"}], store=True, ) r2 = client.responses.create( model="grok-4.6", previous_response_id=r1.id, # 只传新增的一句,历史由上游接续 input=[{"role": "user", "content": "追问"}], store=True, ) print(r2.usage.input_tokens_details.cached_tokens) ``` 端点差异详见 [Grok 概览的端点一览](/api-capabilities/grok/overview)。 ### 关于 `x-grok-conv-id` xAI 官方最佳实践建议每次请求带上 `x-grok-conv-id` 请求头(UUID 或会话 ID)以提高命中率。我们在 API易 上做了对称 A/B(带与不带各若干组独立前缀、各若干次复用),**两组的命中表现没有可观测的差异**。带上它无害,但不要把命中率的指望押在这个请求头上。 ## 命中率与预期管理 **缓存命中不保证。** xAI 官方文档写明缓存条目可能因内存压力、服务重启、请求被路由到另一台服务器而失效。 实测在**稳定前缀 + 连续复用**的场景下多数请求能命中,但确实存在抖动,且抖动来自上游侧、无法由调用方控制。**做成本测算请一律按无缓存价格打底,把命中当作额外优惠。** 还有一点值得提前说清楚:**缓存的价值在成本,不在速度**。实测命中与未命中的首字延迟差距只有百毫秒量级 —— 别指望靠缓存把长上下文请求变快。 ## 最常见的踩坑 | 现象 | 原因 | | - | - | | `cached_tokens` 恒为 0 或恒是极小值 | prompt 太短(千 token 量级以下)/ 前缀开头有时间戳、UUID、随机 ID | | 时有时无 | 上游驱逐,属正常现象;缩短复用间隔、批量任务连续发 | | 命中数比前缀短一截 | 128 token 取整,正常 | | 多轮对话 `cached_tokens` 不涨 | chat/completions 只复用最初那段静态前缀,长对话改走 responses 链式 | | 改了历史消息就不命中了 | 缓存只能追加,改 / 删 / 重排历史即作废 | | 换模型后不命中 | 缓存按**模型隔离**,`grok-4.6` 与 `grok-4.5` 互不共享 | ## 与其它通道的差异速查 | | Grok | OpenAI | Gemini | Claude | | - | - | - | - | - | | 触发方式 | **全自动** | **全自动** | 隐式自动 | 手动打 `cache_control` | | 写入费 | **免费** | **免费** | 免费 | 1.25× / 2× | | 命中价 | 0.25× | 0.1× | 官方口径最高省 90% | 0.1× | | 最小阈值 | 官方未公布,实测千 token 以下不起缓 | 1024 tokens | 4096(3 系)/ 2048(2.5 系) | 1024–4096 | | 块粒度 | 128 token | 128 token | — | — | | 命中稳定性 | ✅ 命中确定,但上游不保证 | ✅ 稳定 | ⚠️ 不保证,体感一般 | ✅ 稳定 | | 命中字段 | `cached_tokens` | `cached_tokens` | `cachedContentTokenCount` | `cache_read_input_tokens` | 全平台缓存支持总览见 [缓存计费 FAQ](/faq/cache-billing)。 **本页数据基于 `grok-4.6`(2026-08-19 实测)。** xAI 官方称全部 Grok 语言模型都支持前缀缓存,其余型号我们未逐一实打;块粒度、短 prompt 行为等细节以你自己用例上的实测为准。 若你发现同一前缀下的账单与上面的口径明显不符,请带上响应头里的 request-id 联系客服。 ## 要点回顾 不用打标记、没有写入费,达到条件自动缓存,第 2 次复用就是纯省钱。 从 messages 开头逐字匹配,改历史即作废;命中量按 128 token 台阶取整。 chat 多轮只复用最初的静态前缀;responses + previous\_response\_id 的命中量随轮次增长。 官方不保证命中,成本测算按无缓存价打底,命中当作额外优惠。 ## 相关链接 * 同组页面:[Grok 概览](/api-capabilities/grok/overview) · [对话与推理](/api-capabilities/grok/chat) · [联网搜索与 X 搜索](/api-capabilities/grok/web-search) · [代码执行与 MCP](/api-capabilities/grok/code-execution-mcp) * 其它通道缓存:[OpenAI 缓存计费](/api-capabilities/openai/prompt-caching) · [Gemini 缓存计费](/api-capabilities/gemini/prompt-caching) · [Claude 缓存计费](/api-capabilities/claude-prompt-caching) * 全平台总览:[缓存计费 FAQ](/faq/cache-billing) * 获取 / 管理令牌:`https://api.apiyi.com/token` * xAI 官方文档:`docs.x.ai/developers/advanced-api-usage/prompt-caching` # Grok 联网搜索与 X 搜索使用指南 Source: https://docs.apiyi.com/api-capabilities/grok/web-search Grok 在 API易 的联网能力实测:Responses API + web_search / x_search 工具真实联网、返回带引用的时新结果,X 搜索是 Grok 独有的差异化能力,附响应结构与计费说明。 本文说明在 API易 上使用 Grok 模型联网搜索(Web Search)与 X 平台搜索(X Search)的方法,基于 2026年7月13日 (UTC+8) 实测验证。 ## 一句话结论 **API易 完整支持 Grok 官方 server-side 联网工具**:使用 **Responses API(`/v1/responses`)+ `web_search` / `x_search` 工具**,`grok-4.5` 实测真实联网、返回带来源引用的最新信息。默认分组的 KEY 直接可用。 ``` 端点: POST https://api.apiyi.com/v1/responses 工具: tools: [{"type": "web_search"}] 或 [{"type": "x_search"}] 模型: grok-4.5(实测验证) ``` **旧入口已下线**:Chat Completions 端点的 `search_parameters` 参数(旧版 Live Search)已被 xAI 移除,实测返回 410。存量代码请迁移到 Responses API 工具写法。 ## 真实可用性(实测数据,2026-07-13) | 工具 | 联网结果 | 单次问答搜索次数 | 延迟 | | - | - | - | - | | `web_search` | ✅ 准确返回当周新闻(正确检索到 7月8日 Grok 4.5 发布公告),带来源引用 | 5 次 | \~12s | | `x_search` | ✅ 准确返回 X 平台指定账号最新帖子及线程内容 | 24 次 | \~45s | **X 搜索是 Grok 的差异化能力**:可直接检索 X(推特)平台的实时帖子、账号动态与话题讨论,这是其他厂商联网工具覆盖不到的信息源。适合舆情监控、热点追踪、KOL 动态分析等场景。注意 x\_search 搜索轮次多、延迟明显更长(实测约 45 秒),客户端超时建议设 120 秒以上。 ## 快速上手 ### cURL ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/responses" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "grok-4.5", "tools": [{"type": "web_search"}], "input": "xAI 最近一周发布了什么新闻?请搜索并附来源链接" }' ``` ### Python(OpenAI SDK) ```python theme={null} from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1", ) resp = client.responses.create( model="grok-4.5", tools=[{"type": "web_search"}], # X 搜索改为 {"type": "x_search"} input="xAI 最近一周发布了什么新闻?请搜索并附来源链接", ) # 1) 最终回答 print(resp.output_text) # 2) 实际搜索次数 searches = [i for i in resp.output if i.type == "web_search_call"] print(f"本次搜索 {len(searches)} 次") # 3) 工具用量明细(计量自查) print(resp.usage.server_side_tool_usage_details) ``` ### X 搜索示例 ```python theme={null} resp = client.responses.create( model="grok-4.5", tools=[{"type": "x_search"}], input="在X上搜索 xAI 官方账号最近的帖子,总结主题", ) print(resp.output_text) ``` ## 响应结构说明 `output` 数组按执行顺序包含: | item type | 含义 | | - | - | | `reasoning` | 模型推理过程(规划搜索策略) | | `web_search_call` | 一次实际执行的 Web 搜索(`x_search` 对应条目类似) | | `message` | 最终回答,正文内嵌来源引用链接 | usage 中的 `server_side_tool_usage_details` 逐项给出各工具的实际调用次数(`web_search_calls` / `x_search_calls` / `code_interpreter_calls` / `mcp_calls` 等),建议在业务侧记录用于成本核对。 ## 计费说明 联网问答的开销由两部分组成: | 项目 | 说明 | | - | - | | **检索内容 token 费** | 搜索结果注入模型上下文,按模型标准 input 价计费。**这部分是大头**:实测 web\_search 单次问答约 27K input tokens(其中约 11K 命中缓存享折扣价) | | **工具调用费** | server-side 工具可能按次收取工具调用费,以 API易 工具定价与账单实际扣费为准 | x\_search 搜索轮次多(实测单次问答 24 次搜索),token 注入量与延迟均高于 web\_search,请按业务问答量预估成本。响应 usage 中的搜索次数与 `cached_tokens` 均可自查。 ## 注意事项 1. **只走 Responses API**:Chat Completions 的 `search_parameters` 已下线(410),不要再用。 2. **延迟预期**:web\_search 约 12 秒、x\_search 约 45 秒(实测值,随任务复杂度浮动),客户端超时建议 ≥ 120 秒。 3. **控制成本**:可在提示词中约束搜索行为(如「最多搜索 2 次」),或在业务侧监控 `server_side_tool_usage_details`。 4. **验证真实联网**:检查 `output` 中是否存在 `web_search_call` / 对应搜索条目——只有正文、没有搜索条目的回答是模型凭训练数据作答。 ## 相关文档 模型阵容、定价与能力矩阵 同为 Responses API 的另外两大 server-side 工具 联网注入的大量 input tokens 可配合自动缓存降本 GPT 系列的联网搜索用法对照 # 出图进阶:工作流编排与去 AI 味 Source: https://docs.apiyi.com/api-capabilities/image-advanced-workflow 同一个模型,C 端出图产品比裸调 API 好在哪:提示词改写层、参考图锚定、并发采样加视觉模型择优、分步精修。附去 AI 味的可复制词表、实测对比图与成本账。 [如何生成满意的图片](/api-capabilities/image-generation-success-tips) 解决的是「这一次没出好,怎么救」。这一篇解决下一个问题:**怎么让每一次都稳定地出好**。 一个反复被问到的现象:`freepik.com`、`higgsfield.ai` 这类 C 端出图产品,用的是和你一样的底层模型——同一批 Nano Banana、GPT-Image、FLUX——但出来的图看着就是更「成品」。差距不在模型权重,**在模型外面那一层**。那一层是可以自己搭的,本文讲怎么搭。 ## 一、C 端出图产品在模型外面套了什么 把这类产品拆开,模型之外大致是这八层。每一层都能在 API 上复刻: | 产品侧做的事 | 解决什么问题 | 在 API 上怎么复刻 | | - | - | - | | **提示词改写层**(Prompt Enhancer) | 用户写的是口语,模型要的是结构化描述 | 先调一次文本模型改写,再进图片模型(见第二节) | | **风格预设**(几十个可点选的 preset,可保存自己的) | 把审美固化,用户不必懂摄影术语 | 预设 = 你代码里的提示词片段常量 + 固定的参考图 | | **身份锚定**(如 Higgsfield 的 `Soul ID`,用 20–80 张照片训练一个持久身份) | 同一个人跨次生成不漂移 | 用参考图逼近(见下方边界说明) | | **多次采样 + 择优** | 用户只看到最好那张,感知成功率接近 100% | 并发出 N 张 + 视觉模型打分自动挑(见第三节 Step 3/4) | | **分步编辑** | 一次下达复合指令必然掉链子 | 先定构图,再局部改,最后加文字 | | **放大与后处理**(Freepik 2024 年收购 Magnific,提供 2×–16× 的创造性放大) | 把小图补成可印刷尺寸 | API易 无此接口,改为直接出高分辨率档(见下方边界说明) | | **负面词与安全兜底** | 避开模型的常见坏习惯、拦下会被审核打回的请求 | 提示词模板里内置固定的负面描述 + 审核失败的降级路径 | | **素材库与转存** | 用户的图不会过期丢失 | 拿到结果立刻转存自己的对象存储 | **两条必须先说清的平台边界**,别按竞品的功能表照抄: 1. **API易 没有超分 / 抠图 / 修脸接口**。要大图就在生成时直接选高分辨率档(`gpt-image-2` 的 4K、Nano Banana Pro 的 4K),不要指望事后放大;要透明底用官转 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst`,传 `background: "transparent"` 直接出带 alpha 通道的 PNG(`gpt-image-2` 的透明背景已于 2026-09-30 被官方关停)(`seedream-5-0` / `seedream-5-0-pro` 只能靠提示词要求,不保证每次都有 alpha),见 [怎么生成透明背景的图片](/faq/image-transparent-background)。 2. **API易 不提供 LoRA / 身份训练**。`Soul ID` 那种「训练一次、永久锁脸」的能力,用参考图只能逼近:同一角色换场景、换光线时仍会漂移,越接近正脸、越接近原始光照条件,保持得越好。做需要严格一致的商业角色,要预留人工挑图的环节。 ## 二、第一层就能拉开差距:把口语变成结构化提示词 这是投入产出比最高的一层,也是最容易被跳过的一层。 ### 实测:同一个模型,同一个需求,两种提示词 需求是「给咖啡出一张电商产品图」。左边是用户会写的原话,右边是补齐要素之后的版本,都用 `gemini-3-pro-image`(Nano Banana Pro)、`2K`、`1:1`,各出一次: 口语提示词生成的咖啡图:木桌、磨豆机、麻布袋等大量道具,画面暖调,杯身被模型自行编造了一个品牌名 结构化提示词生成的咖啡图:浅灰台面上一只哑光黑陶瓷杯,背景干净虚化,光影方向明确,画面留白充足 左图不难看,但**不可用**:模型替你做了一堆你没授权的决定——加了磨豆机和麻布袋、把色调定成怀旧暖调,还在杯身上编了一个并不存在的品牌名(这种自动生成的文字在商用场景基本等于废片)。右图是能直接进商详页的图:中性背景、光位可复述、留白够放文案。 **「好看」和「可用」是两件事**。口语提示词只能约束前者。 ### 六要素:改写层要补齐的东西 改写不是把提示词写长,是把缺失的决策补上。图片提示词的骨架就六项: | 要素 | 缺了会怎样 | 写法示例 | | - | - | - | | **主体** | 模型自由发挥,加你不想要的道具 | 「一只哑光黑色陶瓷手冲杯,盛八分满黑咖啡」 | | **环境** | 背景随机,同系列图对不上 | 「浅灰色微水泥台面,同色系墙面并虚化」 | | **光线** | 全局均匀光,一眼假 | 「左上方 45 度柔光箱主光,右侧白色反光板补光」 | | **镜头与视角** | 透视和景深不可控 | 「85mm 微距,f/5.6,正面略俯视 15 度」 | | **色调与介质** | 默认高饱和「渲染感」 | 「冷调中性白平衡,整体偏低饱和」 | | **构图** | 主体永远居中 | 「杯子位于画面左三分之一,右侧大面积留白」 | 分辨率**不是**第六要素。输出分辨率只由 `size` / `imageSize` 这类参数决定,在提示词里写「4K」「8K」不会提高实际像素——详见 [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution)。 ### 把改写层写成代码 用一个便宜快的文本模型做这件事就够了,成本相对出图可以忽略: ```python theme={null} import os import requests BASE = "https://api.apiyi.com/v1" API_KEY = os.environ["APIYI_API_KEY"] # 绝不要把 Key 写死在代码里 REWRITE_SYSTEM = """你是图片提示词工程师。把用户的口语需求改写成一段结构化的中文出图提示词。 必须补齐六要素,缺什么补什么,不要询问用户: 1 主体:具体到材质、颜色、数量、状态 2 环境:背景是什么、虚实关系 3 光线:光源方向、软硬、有没有补光,必须是可指认的单一主光 4 镜头与视角:焦段、光圈、机位高度、俯仰角 5 色调与介质:白平衡倾向、饱和度、胶片或数码质感 6 构图:主体在画面什么位置,留白在哪 规则: - 只输出提示词正文,不要解释、不要分点、不要加标题 - 不要出现品牌名、商标、可识别的文字内容,除非用户明确要求 - 不要使用 8K、超高清、杰作、完美 这类空泛的质量词 - 用户明确指定过的要素原样保留,不要改写""" def rewrite(user_prompt: str) -> str: r = requests.post( f"{BASE}/chat/completions", headers={"Authorization": f"Bearer {API_KEY}"}, json={ "model": "gemini-3.5-flash", "messages": [ {"role": "system", "content": REWRITE_SYSTEM}, {"role": "user", "content": user_prompt}, ], }, timeout=60, ) r.raise_for_status() return r.json()["choices"][0]["message"]["content"].strip() ``` 注意 system prompt 里那条「不要使用 8K、超高清、杰作、完美 这类空泛的质量词」——原因见第四节。 ## 三、一条可落地的五步流水线 改写层之外,把剩下四层串起来就是一条完整流水线: 见第二节。这一步还顺便承担「把用户输入里的敏感内容中和掉」的职责,能显著降低后面被审核拦下的比例。 风格靠两样东西固定:**一段每次都拼进去的风格常量**(你的 preset),和**一组固定的参考图**。 各系列的参考图上限差别很大,设计流水线前先确认: | 模型系列 | 参考图上限 | 备注 | | - | - | - | | Nano Banana 全系 | **14 张**(实测) | 见 [多图融合测试指南](/api-capabilities/multi-image-fusion-testing) | | `gpt-image-2` 系列 | **16 张** | 重复传 `image[]` | | Seedream | **10 张** | 输入 + 输出总数不超过 15 | | FLUX.2 pro / max / flex | **8 张** | `input_image_2` … `input_image_8`;klein 4 张,Kontext 仅 1 张 | | Grok Imagine | **1–4 张** | 传第 5 张直接 400 | 两个必须遵守的约定:**提示词里的「图1 / 图2」严格按数组顺序对应**,要显式写出来指代谁;**Grok 的参考图只在 `/v1/images/edits` 生效**,传给 `/v1/images/generations` 会被静默丢弃并照常计费。 C 端产品「一发就中」的观感,本质是它替你抽了好几次卡。 但**服务端的 `n` 参数在多数图片模型上不生效**(Seedream 明确静默忽略)。要多张就在客户端并发发多次请求——站内 skills 页给出的做法是一次最多 5 并发。并发数按渠道调,有的渠道并发 2 就开始 429,加指数退避。 有了 N 张就要自动挑,否则等于把选择成本转嫁给用户。 做法是把候选图回传给一个视觉模型打分,走标准的 `/v1/chat/completions` 图片输入即可,可选模型见 [视觉理解](/api-capabilities/vision-understanding)。评分维度建议固定成五项、要求返回 JSON:指令符合度、结构与解剖、文字正确性、质感真实度、构图。 不要用 `/v1/rerank` 做这件事。`bge-reranker-v2-m3` 是**纯文本**重排模型,不接受图片输入。图片打分只能用视觉理解模型。 定稿之后再做局部调整,比一次性下达复合指令成功率高得多: * **像素级可控的局部重绘**:只有**官转 `gpt-image-2`** 支持蒙版,见 [蒙版局部重绘指南](/api-capabilities/gpt-image-2/mask-editing); * **多轮对话式累积编辑**:Nano Banana 系列的**原生 Gemini 端点**支持(把上一轮的图以 `role: "model"` 回填),逆向线路不支持; * **立刻转存**:所有平台返回的 URL 都是临时链接(FLUX 约 10 分钟且无 CORS,Seedream 与 R2 约 24 小时),拿到就下载进自己的对象存储。 ### 串起来的最小实现 ```python theme={null} import base64 import json import os from concurrent.futures import ThreadPoolExecutor import requests BASE = "https://api.apiyi.com" API_KEY = os.environ["APIYI_API_KEY"] HEAD = {"Authorization": f"Bearer {API_KEY}"} STYLE_CONST = "冷调中性白平衡,整体偏低饱和,画面干净留白充足。" # 你的 preset def draw(prompt: str, size: str = "2K", aspect: str = "1:1") -> bytes: """出一张图(Nano Banana Pro 原生 Gemini 端点)""" url = f"{BASE}/v1beta/models/gemini-3-pro-image:generateContent" body = { "contents": [{"parts": [{"text": prompt}]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": aspect, "imageSize": size}, }, } r = requests.post(url, headers=HEAD, json=body, timeout=600) # 4K 按 600s 留余量 r.raise_for_status() parts = r.json()["candidates"][0]["content"]["parts"] part = next((p for p in parts if p.get("inlineData")), None) if part is None: # HTTP 200 但没图 = 多半被审核拦了 raise RuntimeError("未返回图片:" + json.dumps(parts, ensure_ascii=False)[:300]) return base64.b64decode(part["inlineData"]["data"]) def score(image: bytes, prompt: str) -> dict: """用视觉模型给候选图打分,返回 {总分, 各维度分, 一句话讲问题}""" data_url = "data:image/png;base64," + base64.b64encode(image).decode() rubric = ( "给这张图打分,严格返回 JSON:" '{"instruction":0-10,"anatomy":0-10,"text":0-10,"texture":0-10,' '"composition":0-10,"total":0-50,"issue":"一句话"}。' "instruction=是否满足下面这条需求;anatomy=手指、肢体、物体结构有无错误;" "text=画面内文字是否正确(没有文字给 10);texture=质感是否像真实拍摄而非渲染;" "composition=构图是否可用。需求如下:\n" + prompt ) r = requests.post( f"{BASE}/v1/chat/completions", headers=HEAD, json={ "model": "gemini-3.5-flash", "messages": [{"role": "user", "content": [ {"type": "text", "text": rubric}, {"type": "image_url", "image_url": {"url": data_url}}, ]}], "response_format": {"type": "json_object"}, }, timeout=120, ) r.raise_for_status() return json.loads(r.json()["choices"][0]["message"]["content"]) def best_of(user_input: str, n: int = 4) -> bytes: prompt = rewrite(user_input) + "\n" + STYLE_CONST # Step 1 + Step 2 with ThreadPoolExecutor(max_workers=n) as pool: # Step 3:客户端并发,不用 n 参数 results = list(pool.map(lambda _: _safe(draw, prompt), range(n))) cands = [img for ok, img in results if ok] if not cands: raise RuntimeError("全部失败,检查审核拦截或降级到别的模型") with ThreadPoolExecutor(max_workers=len(cands)) as pool: # Step 4:并发打分 scores = list(pool.map(lambda im: score(im, prompt), cands)) ranked = sorted(zip(cands, scores), key=lambda x: x[1]["total"], reverse=True) return ranked[0][0] # Step 5 的精修与转存按业务接上 def _safe(fn, *args): try: return True, fn(*args) except Exception as e: # 单张失败不拖垮整批 return False, str(e) ``` ## 四、去 AI 味:让图看起来不像 AI 生成 「AI 味」不是玄学,是**一组可以逐条消掉的具体特征**。 ### 实测对比 同一个模型(`gemini-3-pro-image`)、同一个题材,两种写法各出两张,各取第一张: 裸提示词生成的人像:人物正对镜头且居中,全画面光线均匀没有明确方向,背景元素齐整,整体是通用图库照片的观感 控制词生成的人像:单一侧向窗光,半边脸落进阴影,可见毛孔与面部绒毛、颊上小痣与碎发,胶片色调,人物偏画面右侧 左图并不差——底模已经足够强,裸提示词也能出「好看的图」。但它带着一整套典型特征:**人物死死居中、光线均匀到找不出光源在哪、每样东西都恰到好处**。而且这不是偶然:那一轮出的两张,构图和布光模式几乎一样。 右图换了一套写法之后:光有明确方向、半边脸敢丢进阴影、皮肤上有油光和毛孔、颊上有痣、碎发没梳齐、人物偏在画面右侧。它看起来像**某个人在某个具体时刻被拍到了**,而不是「一张咖啡馆女性微笑素材」。 这里也说明了流水线真正的价值:它不是把丑图变好看,而是**把「碰运气的好看」变成「你指定的、可复述、可复现的好看」**。左图换个需求方来看未必更差,但你说不出它为什么长这样,也无法要求下一张跟它保持一致。 ### 症状 → 对策 → 反面写法 | AI 味的症状 | 对策(写进提示词) | 反面写法(别这么写) | | - | - | - | | 主体永远居中、构图对称 | 指定位置:「人物偏画面右侧,左侧留白」 | 「完美构图」「黄金分割」 | | 皮肤塑料感、零毛孔 | 「自然肤质,可见毛孔与面部绒毛,鼻翼一点油光,不做磨皮」 | 「皮肤细腻」「精致」「唯美」 | | 光线均匀、找不到光源 | 指定唯一主光的方向与软硬:「左侧窗光是唯一光源,右半边脸落进阴影」 | 「完美光线」「柔和打光」 | | 景深假、虚化像贴上去的 | 给焦段和光圈:「85mm,f/2.8,焦点在近侧眼睛」 | 「背景虚化」「电影感」 | | 颜色过饱和、发光 | 给介质和白平衡:「Kodak Portra 400 质感,高光偏暖阴影偏青,低饱和」 | 「色彩鲜艳」「HDR」 | | 一切都是新的、无磨损 | 主动加瑕疵:「毛衣起球,桌面有水渍和面包屑」 | 「干净整洁」「高级质感」 | | 整体像海报、像渲染图 | 指定拍摄情境:「抓拍」「从邻桌高度平视」 | 「8K」「超高清」「杰作」「大师作品」 | **`8K`、`超高清`、`超精细`、`杰作`、`完美` 这类空泛质量词是负资产**。它们既不提高分辨率(分辨率只由参数决定),又会把模型推向过锐、过饱和的渲染风格——正好是「AI 味」的核心来源。上面左图的提示词里塞满了这些词,结果就是那个样子。要质量就写具体的光、镜头和介质。 ### 四组可直接复制的控制词块 按需拼进提示词,一般四组各取一到两句就够: 左侧窗光是画面里唯一的光源 / 午后三点的侧后方硬光 / 逆光,发丝出现轮廓光 / 桌面台灯作为画面内实用光源 / 阴天的散射光,没有明显投影 35mm f/2.0 平视抓拍 / 85mm f/2.8,焦点在近侧眼睛 / 24mm 低机位,边缘有轻微畸变 / 长焦压缩空间,背景层次被压平 / 画面四角有轻微暗角 Kodak Portra 400 胶片质感,细腻颗粒 / 高光偏暖、阴影偏青 / 宝丽来即时成像,反差低、边缘发虚 / 早期 CCD 数码相机的噪点与偏色 / 整体低饱和,不做锐化 自然肤质,可见毛孔与面部绒毛 / 额前几缕碎发没有梳齐 / 毛衣起球,袖口有磨损 / 桌面有水渍、指纹和面包屑 / 构图不居中,边缘被裁掉一部分 ### 三个场景的完整示例 半身人像抓拍:一位二十多岁的女性在咖啡馆窗边,侧身看向窗外,嘴角有一点没收住的笑。窗光是画面里唯一的光源,来自左侧,右半边脸落进阴影,鼻梁下有一小块硬边阴影。85mm 镜头,f/2.8,平视,焦点落在近侧眼睛。柯达 Portra 400 胶片质感,可见细腻颗粒,高光偏暖、阴影偏青,整体低饱和。自然肤质:可见毛孔与面部绒毛,鼻翼有一点油光,左颊有一颗小痣,眉毛有几根杂乱散毛,额前几缕碎发没有梳齐。不做磨皮,不做美颜,不做锐化。构图偏右,左侧留白。 电商主图:一只哑光黑色陶瓷手冲咖啡杯,杯中盛八分满的黑咖啡,表面有一圈细密油脂。放在浅灰色微水泥台面上,背景是同色系墙面并虚化。主光为左上方 45 度柔光箱,右侧用白色反光板补光,杯身右缘留一条细窄的高光;台面上落一道柔和的杯体投影,向右后方延伸。85mm 微距镜头,f/5.6,正面略俯视 15 度,全杯清晰。冷调中性白平衡,整体偏低饱和。陶瓷表面有细微的手工釉面不均和一处极小的窑变斑点,杯沿有一道极浅的使用痕迹。画面留白充足,杯子位于画面左三分之一处。不要出现任何品牌名或文字。 傍晚六点的老城区窄街,刚下过雨,地面积水倒映着两侧店铺的灯箱。唯一的主光是街口一盏偏暖的路灯,其余靠店铺灯光补,天空还剩一点冷调余晖,形成暖冷对比。28mm 镜头,f/4,机位在人的视线高度,略微仰拍。整体低饱和,暗部保留噪点,不做提亮。墙面有水渍、旧海报的撕痕和空调外机,电线在画面上方横过。画面中没有人正对镜头,行人都是背影和运动模糊。 ## 五、几个会打乱流水线设计的事实 设计之前先知道这些,能省掉一轮返工: | 事实 | 对流水线的影响 | | - | - | | **seed 在这条产品线上基本不可用**:Nano Banana 全系与 GPT-Image 全系不暴露 seed,Seedream 4.x / 5.x 的 seed 实测不生效,Grok Imagine 明确不支持 | 别把「复现」建立在 seed 上。**唯一可靠的复现是把成功那次的完整请求体存档**(提示词、参考图、全部参数),下次原样重放 | | **服务端 `n` 参数在多数图片模型上不生效** | 多候选必须客户端并发,并发数要按渠道压测,配指数退避 | | **图片 API 全部同步,没有任务 ID,断连仍计费** | 流水线必须自建任务队列,见 [自实现异步队列](/api-capabilities/image-async-queue) 与 [图片 API 调用须知](/api-capabilities/image-api-best-practices) | | **没有超分 / 抠图 / 修脸接口** | 尺寸在生成时一次到位;透明底走 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` 的 `background: "transparent"` 参数(不是抠图接口,是生成时直接出透明底) | | **HTTP 200 但没图,通常是内容审核拦截** | 择优逻辑要能区分「没出图」和「出了但不好」,前者见 [Gemini 生图错误处理](/api-capabilities/gemini-image-error-handling) | | **返回的图片 URL 都是临时链接** | 拿到结果立刻转存,别把上游 URL 直接落库 | | **GPT-Image 系列提示词上限 32,000 字符**(原厂上限,按字符计) | 品牌手册、包装规格这类资料不要直接塞图片模型,先经文本模型提炼成 1K~3K 字符的结构化提示词,见 [长提示词篇](/api-capabilities/image-long-prompt) | ## 六、成本账:什么时候值得上流水线 流水线是拿钱换成功率。出图成本的大头永远是**输出**(`gpt-image-2` 输出 \$30 / 百万 tokens),改写和打分用的文本 / 视觉模型相对可以忽略。所以成本基本等于「你出了几张候选」。 按场景分三档用,不要一刀切: | 档位 | 组成 | 相对成本 | 适用 | | - | - | - | - | | **草稿档** | Lite 模型单张直出 | 1× | 内部预览、批量占位图、用户随手试 | | **标准档** | 改写 + 2 张候选 + 打分挑 1 张 | 约 2× | C 端产品的默认路径 | | **精品档** | 改写 + 4 张候选 + 打分 + 一次局部精修 | 约 5× | 商详主图、投放素材、要对外交付的图 | 判断标准很简单:**这张图会不会被外部的人看到**。会,就值得上标准档以上;只是内部看一眼,草稿档足够。中间还可以加一道闸门——打分低于阈值才追加候选,大部分请求两张就收敛了。 ## 速查总结 * 差距不在模型权重,**在模型外面那八层**:改写、预设、锚定、采样、择优、分步编辑、后处理、转存。 * **改写层性价比最高**:补齐主体 / 环境 / 光线 / 镜头 / 色调 / 构图六要素,「好看」才会变成「可用」。 * **多候选 + 视觉模型打分**是 C 端产品高成功率观感的真正来源;`n` 参数不生效,要客户端并发;`/v1/rerank` 不能给图片打分。 * **去 AI 味靠加具体,不靠加形容词**:指定唯一光源、给焦段光圈、指定介质与颗粒、主动加瑕疵、把主体挪出画面中心。 * **`8K` / `杰作` / `完美光线` 这类词是负资产**,既不提分辨率又把画面推向渲染感。 * **别指望 seed 复现**,存完整请求体才是复现;图片 API 全同步、断连仍计费,流水线必须配队列。 * API易 没有超分 / 抠图 / 身份训练接口,涉及这几层要在方案里提前绕开。 ## 相关文档 单次调用失败怎么救:改提示词、重试、换模型、用测试工具定位 同步调用、timeout 分档、计费口径、base64 处理、输入图预处理 像素级可控的局部修改,官转 gpt-image-2 专属 参考图上限实测方法与 14 图融合结果 可用于给候选图打分的视觉模型清单与调用方式 把同步出图包进任务队列,支撑多候选流水线 # 图片 API 调用须知与最佳实践 Source: https://docs.apiyi.com/api-capabilities/image-api-best-practices API易 图片 API 全部为同步调用:没有异步任务 ID,断开连接结果丢失但仍会计费。提供各模型 timeout 推荐值、base64 与 URL 输出对照表。 **一句话结论**:API易 所有图片模型均为**同步调用**——发出请求后保持连接等待,生成结果直接在响应里返回。没有异步任务 ID、没有轮询接口;客户端提前断开,这次的结果就拿不回来了,但请求仍会计费。因此,**留足 timeout 是图片 API 开发的第一原则**。 ## 三个必须先知道的事实 一次 HTTP 请求全程阻塞等待,与官方接口形态一致,没有「提交任务 → 轮询结果」模式。部分上游本身是异步的(如 FLUX),也已被网关封装成同步,无需自己写轮询。 不存在 task\_id 查询接口,也无法凭 request\_id 事后找回图片。API易 原厂透传、不存储生成结果,断开连接后结果不可恢复。 客户端超时主动断开后,服务端与上游的生成仍会跑完,该次请求**照常计费**。timeout 设得太小 = 花了钱却拿不到图。 ## 模型系列速查表 各图片模型系列的推荐 timeout、输出格式与 URL 支持一览: | 模型系列 | 端点 | 推荐 timeout | 输出格式 | URL 输出与有效期 | | - | - | - | - | - | | [GPT-Image-2.5 / 2(官转,flare / sunburst / 2)](/api-capabilities/gpt-image-2/overview) | `/v1/images/generations`、`/v1/images/edits` | **360 秒**(gpt-image-2 high + 2K/4K 实测 3-5 分钟;2.5 更快但 `xhigh` / `max` 同样建议留足) | 纯 b64\_json(**无 `data:` 前缀**) | ❌ 不支持(传 `response_format` 直接 400;`image2_OSS` 分组暂不支持官转) | | [GPT-Image-2-All(官逆)](/api-capabilities/gpt-image-2-all/overview) | 同上 | **300 秒** | 默认 `b64_json`(**无 `data:` 前缀**,2026-07 实测);显式 `response_format: "url"` 可切换 | ✅ 显式 `url`:R2 CDN 约 24 小时;强依赖 URL 用 `image2_OSS` 分组 | | [GPT-Image-2-VIP](/api-capabilities/gpt-image-2-vip/overview) | 同上 | **300 秒** | 同 All(默认 `b64_json` 无前缀,2026-07 实测) | ✅ 同 All(显式 `url` 或 `image2_OSS` 分组) | | [Nano Banana Pro](/api-capabilities/nano-banana-image/overview) | Gemini 原生 `:generateContent` | 1K/2K **300 秒**、4K **600 秒**、多图参考 5 分钟以上 | `inlineData.data` 纯 base64 | `NB_OSS` 内测分组(见下文) | | [Nano Banana 2](/api-capabilities/nano-banana-2-image/overview) | 同上 | **360 秒** | 同上 | `NB_OSS` 覆盖范围请咨询客服 | | [Nano Banana Lite](/api-capabilities/nano-banana-lite-image/overview) | 同上 | **300 秒**(常规约 4 秒出图,为高峰拥塞留余量) | 同上 | `NB_OSS` 覆盖范围请咨询客服 | | [FLUX](/api-capabilities/flux/overview) | `/v1/images/generations` | **60-120 秒**,flex 系列建议 180 秒 | 仅 `data[0].url`(**原厂默认即 URL**) | ⚠️ 仅约 **10 分钟**有效、无 CORS,必须服务端立即转存 | | [Seedream](/api-capabilities/seedream-image/overview) | `/v1/images/generations`(生成/编辑统一端点) | **60 秒**(4K + hd 约 30-60 秒) | 默认 `url`(**原厂默认即 URL**);可选 `b64_json`(纯 base64 无前缀) | ✅ BytePlus TOS,约 24 小时 | | [MAI-Image 2.6](/api-capabilities/mai-image/overview) | `/v1/images/generations`、`/v1/images/edits` | Flash **120 秒**、2.6 **180 秒**(1024×1024 约 17 / 30 秒) | 纯 b64\_json(**无 `data:` 前缀**),PNG | ❌ 不支持(传 `response_format` 直接 400) | `response_format` 参数的**适用面很窄**:仅 GPT-Image-2-All / VIP 和 Seedream 支持;官转 GPT-Image-2 传了会直接返回 400 `unknown_parameter`,MAI-Image 2.6 传了同样返回 400。支持该参数的模型建议**始终显式传值**,不要依赖默认行为——历史上默认值随分组和负载变化过。 ## 计费与价格影响 新手最常问的计费问题:「参考图是每张定量,还是图片越大消耗 token 越多?」先建立三个直觉: 以 gpt-image-2 为例:文本输入 \$5/M、图片输入 \$8/M、**输出 \$30/M**。影响价格最大的永远是**输出的尺寸和画质**(quality × size),其次才是参考图张数。 GPT 系输入图按**尺寸/宽高比映射**成 tokens(越大越多,但有下限也有封顶),**张数严格线性累加**。Gemini 系则相反——输出图按分辨率档固定 token/张。 输入/输出 token 都在响应里:GPT 系看 `usage.input_tokens_details.image_tokens`,Gemini 系看 `usageMetadata.promptTokensDetails`。对账、核价都以此为准,不要按张数估。 ### 两大体系的 token 口径对照 | 体系 | 输入图 tokens | 输出图 tokens | | - | - | - | | **GPT 系**(gpt-image-2 等) | 按尺寸动态换算:≤1024² 方图统一 1024,2048² 以上封顶 1521(2026-07 实测);**N 张 = N × 单张** | 由 `size` × `quality` 决定:1024² 从 low 196 到 high 数千 tokens | | **Gemini 系**(Nano Banana 全系) | 计入 `promptTokensDetails` 的 IMAGE 模态 | **按分辨率档固定**:1K/2K 每张 1120、4K 每张 2000,与宽高比无关 | ### 多图输入的费用直觉 * 单张参考图约 **800-1600 image tokens ≈ \$0.008-0.012**(gpt-image-2 实测,含尺寸/宽高比浮动); * 张数线性累加:**16 张 ≈ \$0.13**,与一张 `high` 输出(≈\$0.21)同量级——多图融合场景输入成本不可忽略; * **token 由像素尺寸决定、与文件体积无关**:压缩体积是为上传稳定,不省 token;省 token 靠减少张数(超大图有封顶,不必担心费用爆炸)。 完整实测数据表见 [gpt-image-2 多图输入的价格影响](/api-capabilities/gpt-image-2/overview#多图输入的价格影响2026-07-实测);Gemini 系 token 口径详见 [usageMetadata 解读](/api-capabilities/nano-banana-usage-metadata) 与 [Nano Banana 价格说明](/api-capabilities/nano-banana-pricing)。 ## timeout 配置建议 ### 为什么默认 timeout 会误伤 主流 HTTP 客户端的默认超时普遍在 30-60 秒(`requests` 甚至默认不限时但常被框架包一层 30 秒),而图片生成是真正的「长请求」: * GPT-Image-2 在 `high` 画质 + 2K/4K 分辨率下,实测整体耗时 **3-5 分钟**; * Nano Banana 系列 4K 出图约 50 秒起步,高峰期更久; * 多图融合、图片编辑类请求普遍比文生图更慢。 按默认值配置,请求会在服务端还在正常生成时被客户端掐断——表现为大量「超时失败」,实际上是**自己断开了本来会成功的请求**,而且这些请求照常计费。 ### 按模型分档设置 ```python theme={null} # 按模型设置客户端超时(秒),而不是全局一个值 IMAGE_TIMEOUTS = { "gpt-image-2": 360, # high + 2K/4K 实测 3-5 分钟 "gpt-image-2.5-flare": 360, # 2.5 同价同参数,超时经验沿用 "gpt-image-2.5-sunburst": 360, "gpt-image-2-all": 300, "gpt-image-2-vip": 300, "gemini-3-pro-image": 600, # Nano Banana Pro,4K 按 600s 兜底 "gemini-3.1-flash-image-preview": 360, # Nano Banana 2 "gemini-3.1-flash-lite-image": 300, # Nano Banana Lite "flux": 120, # flex 系列建议 180 "seedream": 60, # 4K + hd 约 30-60 秒 "MAI-Image-2.6-Flash": 120, # 1024×1024 约 17 秒 "MAI-Image-2.6": 180, # 1024×1024 约 30 秒 } import requests def generate_image(model: str, payload: dict, api_key: str) -> dict: resp = requests.post( "https://api.apiyi.com/v1/images/generations", headers={"Authorization": f"Bearer {api_key}"}, json={"model": model, **payload}, timeout=IMAGE_TIMEOUTS.get(model, 300), # 未知模型给 300s 兜底 ) resp.raise_for_status() return resp.json() ``` ### 重试策略 不是所有失败都值得重试,先分清计费口径: | 失败情况 | 是否计费 | 建议做法 | | - | - | - | | 429 / 503(限流、上游过载) | 不计费 | 指数退避后重试(如 5s、15s、45s) | | 客户端超时主动断开 | **仍计费** | 优先加大 timeout;确需重试时谨慎控制次数 | | 400 / 403(参数、权限错误) | 不计费 | 修正请求再发,盲目重试无意义 | | 内容审核拦截 | **分情况**(见下方说明) | 修改 prompt 后再试,原样重发大概率再次被拦 | **内容审核拦截的计费分情况**:按 token 计费的模型(gpt-image-2 官转等)触发审核通常直接返回 400 错误,**不计费**;**仅按次计费的 Nano Banana Pro** 会遇到「HTTP 200 但出图失败」的谷歌侧拦截,该次**会计费**——API易 对此类非主观失败提供 [出图失败包补计划](/api-capabilities/nano-banana-pro-guarantee),按条数核算后补发额度。 ## base64 数据处理要点 ### 前缀差异对照 不同系列返回的 base64 字段格式**并不统一**,这是新接入时最常见的坑: | 模型系列 | base64 所在字段 | 是否含 `data:image/...;base64,` 前缀 | | - | - | - | | GPT-Image-2(官转) | `data[0].b64_json` | 无前缀(纯 base64) | | GPT-Image-2-All / VIP | `data[0].b64_json` | 无前缀(2026-07 实测;**历史版本曾含前缀**) | | Nano Banana 系列 | `candidates[0].content.parts[].inlineData.data` | 无前缀(纯 base64) | | Seedream(`b64_json` 模式) | `data[0].b64_json` | 无前缀(纯 base64) | 前缀行为随渠道版本变化过,写代码时**务必先做 `startsWith("data:")` 检测**:有前缀的剥掉前缀再解码(或直接用作 `img src`),无前缀的直接解码,避免「双重拼接」或「带前缀解码」产出损坏的图片。 ### 解码写文件 ```python theme={null} import base64 b64 = response["data"][0]["b64_json"] if b64.startswith("data:"): # 防御性剥前缀,兼容曾出现过的 data: 前缀响应 b64 = b64.split(",", 1)[1] with open("output.png", "wb") as f: f.write(base64.b64decode(b64)) ``` ```javascript theme={null} let b64 = response.data[0].b64_json; if (b64.startsWith("data:")) { b64 = b64.slice(b64.indexOf(",") + 1); } require("fs").writeFileSync("output.png", Buffer.from(b64, "base64")); ``` ### Playground 渲染限制 base64 模式的响应往往有数 MB,浏览器 Playground 可能弹出 `请求时发生错误: unable to complete request`——这**不代表请求失败**,实际请求已成功并已计费,只是浏览器无法渲染这么长的字符串。验证效果请用代码调用,或改用支持 `url` 输出的模型/参数。 ## 输入图片格式预处理 图片编辑 / 参考图类接口(如 gpt-image-2 的 `/v1/images/edits`)对输入图片只接受 **png / jpg / webp** 三种标准格式。「用户上传实拍图」类业务最容易踩一个隐蔽的坑:**手机原拍照片经常不是标准 JPEG**。 ### 典型症状:400 invalid\_image\_file ```json theme={null} { "error": { "message": "Invalid image file or mode for image 1, please check your image file. ...", "code": "invalid_image_file" } } ``` 根因通常是 **MPO 格式**(Multi-Picture Object,多帧 JPEG 容器):华为 Mate 系列等机型直出的 `.jpg` 内嵌 HDR 增益图副帧,实为 MPO。这类文件的隐蔽性在于——文件头同为 `FFD8`,**扩展名、HTTP Content-Type、`file` 命令全都显示 JPEG**,只有按帧解析才能识别: ```python theme={null} from PIL import Image Image.open("photo.jpg").format # 返回 "MPO" 即中招;标准图返回 "JPEG"/"PNG" ``` 2026-07 实测(gpt-image-2 编辑接口):MPO 图必被拒;同一张图重编码为标准 JPEG/PNG 后**保持原分辨率(3072×4096)上传即成功**——问题在格式,不在尺寸。此类 400 在入口校验阶段快速返回,**不计费**。 ### 建议:服务端统一重编码 与其逐张排查,不如在上传链路统一做一次重编码,顺带兼容 HEIC、CMYK 等其它非标准输入: ```python theme={null} from PIL import Image import io def normalize_image(raw: bytes) -> bytes: """任意来源图片 → 标准 JPEG,通过各图片编辑接口的格式校验""" im = Image.open(io.BytesIO(raw)) im.load() # 多帧格式(MPO 等)只取第一帧 if im.mode not in ("RGB", "RGBA"): im = im.convert("RGB") # CMYK / P 等模式统一转 RGB out = io.BytesIO() im.save(out, format="JPEG", quality=92) return out.getvalue() ``` 重编码时顺手把体积也压下来(长边 ≤ 4096、JPEG 质量 80-92),单张控制在 1.5MB 以内,上传成功率和出图速度都会更好——输出画质与输入图体积无关。详见 [gpt-image-2 图片编辑「参考图格式要求与预处理」](/api-capabilities/gpt-image-2/image-edit#参考图格式要求与预处理)。 ## 需要 URL 输出怎么办 一共三条路径,按可靠程度排序: 1. **原厂默认就是 URL**:FLUX(仅约 10 分钟有效且无 CORS 头,必须服务端立即下载转存)和 Seedream(BytePlus TOS,约 24 小时)的原厂输出格式本身就是 URL,无需任何配置。 2. **OSS 分组(确定性 URL 输出,推荐生产使用)**: * `image2_OSS` 分组:适用于 **GPT-Image-2-All / VIP**(1x 倍率、不加价),令牌分组切换后稳定输出 URL、不降级为 base64;**官转 GPT-Image-2 暂不支持**。 * `NB_OSS` 内测分组:适用于 Nano Banana 系列,图片 URL 出现在 `text` 字段中,详见 [NB-OSS 分组说明](/api-capabilities/nano-banana-oss-group)。 3. **显式传 `response_format: "url"`**:仅 GPT-Image-2-All / VIP(R2 CDN,约 24 小时)和 Seedream 支持,**适用面窄**——官转 GPT-Image-2 传了直接 400。默认分组下这是逐请求切换,强依赖 URL 的业务建议直接用 OSS 分组。 **GPT-Image-2(官转)目前没有任何 URL 输出途径**,仅 base64。 所有平台返回的图片 URL 都是**临时链接**(10 分钟到 24 小时不等),过期后 404。任何需要长期保存的场景(商品图、用户作品、历史记录),都必须在拿到结果后**立即转存到自己的对象存储 / CDN**,再把自己的 URL 落库。 ## 超时与断连排查 如果你已经把 SDK timeout 调大了却仍然频繁「超时」,按这个顺序排查: 有些框架会在 HTTP 客户端外再包一层超时(如任务队列的 worker 超时、Serverless 函数的执行上限),任何一层小于模型生成时间都会掐断请求。 自建反向代理的 `proxy_read_timeout`、云负载均衡的空闲连接超时、CDN 的回源超时**默认值普遍是 60 秒**,会先于你的客户端断开连接。长请求链路上的每一跳都要放宽。 长时间无字节传输的连接可能被 NAT / 防火墙静默回收,开启 TCP keep-alive 或 HTTP keep-alive 可显著降低概率。 记录响应头中的 `x-request-id`,再到 API易 后台的调用日志中核对:如果日志里能查到这次调用,说明服务端已完成生成并计费,问题出在你这一侧的连接被提前断开。 ## 想做异步任务管理? 平台不提供异步接口,但你完全可以在同步接口之上自建异步外壳: FAQ:图片生成有异步接口吗?支持任务 ID 查询结果吗? 工程实践:把同步调用包进任务队列,自己生成 task\_id、落库、重试 Nano Banana 系列改为 URL 输出,减轻 base64 传输压力 # 图片压缩与输出分辨率说明 Source: https://docs.apiyi.com/api-capabilities/image-compression-resolution 澄清图像 API 调用中两个最常被混淆的问题:输出图的分辨率由什么决定?对输入参考图做压缩,会不会让出图变糊? 本文面向通过 API 调用图像生成/编辑模型的开发者,澄清两个最常被混淆的问题:**① 输出图的分辨率由什么决定?② 对输入参考图做压缩,会不会让出图变糊?** 结论适用于 Nano Banana、GPT 图像、SeeDream、Flux 等各类图像模型,与具体产品界面无关。 ## 先分清两件完全不同的事 调用图像模型时,有两个「分辨率」,它们在请求里是**互相独立的两个字段**,不要混为一谈: | | 输入图分辨率 / 压缩 | 输出图分辨率 | | - | - | - | | 指什么 | 你上传的**参考图 / 待编辑图**的大小、像素 | 模型**生成出来**那张图的大小、像素 | | 由谁决定 | 你在上传/请求前对图片做的压缩处理 | 请求里的**尺寸参数**(`size` / `imageSize` / `aspect_ratio`) | | 在请求里的位置 | 图片数据字段(如 `inline_data.data`、`image[]`、`input_image`) | 尺寸参数字段,与图片数据**毫不相干** | **一句话**:压缩的是「你喂进去的图」,分辨率参数控制的是「模型吐出来的图」。两者各管各的。 ## 压缩质量还是压缩尺寸?先搞懂「压缩」压的是什么 「压缩」这个词经常被笼统使用,实际上图片有两个互相独立的「大小」,对应两种不同的压缩手段: | | 像素尺寸(分辨率) | 文件体积 | | - | - | - | | 指什么 | 图片长宽各多少像素,如 `4284×5712` | 文件占多少磁盘/带宽,如 4.6 MB | | 由什么决定 | 拍摄/生成时的分辨率设置 | 像素数 × 编码质量 × 画面复杂度 | | 对应的压缩手段 | **缩尺寸**:等比缩小长边,像素变少 | **降质量**:JPEG/WebP 有损重编码,像素不变、体积变小 | 两者可以严重不成比例。一个真实例子(经验值,具体随编码实现变化): * iPhone 16 Pro 拍的一张照片,尺寸 **4284×5712**(约 2400 万像素,很大),文件体积却只有 **4.6 MB**——因为系统保存时已经做了高效率的有损编码; * 同样像素的照片如果以高质量直出,体积可能接近 **30 MB**。 所以「这张图要不要压」不能只看像素,也不能只看体积——两者影响的环节不同: * **像素尺寸**决定模型「看图」能获得的信息量上限,以及解码/理解的处理成本; * **文件体积**决定传输环节的成本:Base64 编码膨胀约 33%、上传耗时、单文件 20 MB 上限,冲着的都是体积。 **实践建议是双管齐下、按序执行**:先限像素(长边 ≤ 2048px 等比缩小),再限质量(重编码质量 0.9);而**是否触发处理看体积**(大于 1.5 MB 才处理)。上面那张 4.6 MB 的照片两步都会做:4284px 长边缩到 2048px、再以 0.9 质量重编码,体积通常降到 1 MB 以内,对模型理解毫无影响。 ## 输出分辨率由「尺寸参数」决定,不由提示词决定 这是最常见的误解,结论先行: **在 prompt 里写「4K」「高清」「超清」「8K」并不会让输出变成 4K。** 实际输出分辨率**只取决于请求里的尺寸参数**。提示词只负责「画什么内容」,不负责「出多大尺寸」。 不同模型用不同的尺寸参数,常见的几类: | 模型类别 | 控制输出尺寸的参数 | 取值形式 | 示例 | | - | - | - | - | | **Gemini 图像系列**(如 `gemini-3-pro-image`) | `imageConfig.imageSize` + `imageConfig.aspectRatio` | **档位字符串** + 比例 | `imageSize: "4K"`、`aspectRatio: "16:9"` | | **GPT 图像系列**(gpt-image 等) | `size` | **像素字符串 `宽x高`** | `size: "2048x2048"` | | **SeeDream 系列** | `size` | 像素字符串 / 档位 | `size: "2048x2048"` | | **Flux 系列** | `aspect_ratio` 或 `width` + `height` | 比例字符串 / 像素 | `aspect_ratio: "16:9"` | ### 以 gemini-3-pro-image 为例 它通过 **`imageSize`** 档位控制输出分辨率,可选 **`1K` / `2K` / `4K`**(不传时默认 `1K`),同时用 `aspectRatio` 控制画幅比例: ```json theme={null} { "contents": [ /* prompt 文本 + 输入图(如有)*/ ], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": { "aspectRatio": "16:9", "imageSize": "4K" } } } ``` 其中 `imageSize` 才是真正决定输出分辨率的字段。不同比例 + 档位对应的精确像素是固定映射,例如 1:1 在 1K/2K/4K 分别约为 `1024×1024 / 2048×2048 / 4096×4096`,16:9 约为 `1376×768 / 2752×1536 / 5504×3072`。 ### GPT 图像系列以 size 像素串控制 ```json theme={null} { "model": "gpt-image-...", "prompt": "...", "size": "2048x2048" } ``` **要点**:想要 4K,就把尺寸参数设成对应档位/像素(如 `imageSize:"4K"` 或 `size:"4096x4096"`),**而不是在 prompt 里写「4K」**。提示词和尺寸参数在请求里是两个独立字段,引擎不会去解析 prompt 里的「4K」字样来调整分辨率。 个别模型(如某些自适应出图的型号)**不接受尺寸参数**,输出分辨率由模型自行决定(通常约 1–1.5K)。这类模型即使想要 4K 也无法通过参数强制,更不可能靠提示词实现。以各模型文档/能力声明为准。 **同一模型系列内部,`imageSize` 可选档位也不统一**:以 Gemini 图像系列为例,`gemini-3-pro-image` 支持 `1K`/`2K`/`4K`,但 Nano Banana 2 Lite(`gemini-3.1-flash-lite-image`)**只接受 `1K`**,传 `2K`/`4K` 会报错。切换模型时请核对该模型自己的档位支持范围,不要照搬同系列其它模型的参数。 ## 压缩输入图,会不会影响出图清晰度?基本不会 结论:**对绝大多数场景,合理压缩输入参考图,几乎不影响输出图的清晰度。** 原因有三: 1. **输出是「重新生成」的,不是把你的图放大。** 模型按你指定的尺寸参数**新画一张**,输出分辨率只看 `imageSize`/`size`,跟输入图原本多少像素无关。输入图 3000px 还是压到 2000px,你选 4K 出来就是 4K。 2. **输入图压缩字段与输出尺寸字段彼此独立。** 压缩只改变请求里「图片数据」那个字段的体积/像素,**完全不碰**尺寸参数字段。两者在请求里没有任何关联。 3. **推荐的压缩力度本就很轻,远高于模型「看图」所需。** 实践中把参考图**最长边压到约 2048px、JPEG 质量 0.9 左右**,对模型理解构图、配色、风格、主体细节已经绰绰有余——这些模型内部本就会把输入图缩到不高的分辨率再编码理解。 ### 严谨地说一句「边界」 在**图生图 / 精细编辑**(要求严格保留输入图某块区域的微小纹理、细小文字)这类任务里,如果把输入图压得**过狠**(例如长边压到几百像素、质量压到 0.5 以下),理论上可能丢失一些细节,间接影响编辑结果对原图的还原度。 但只要遵循「长边 ≤ 2048px、质量 ≥ 0.85」这类温和标准,这种影响在实际使用中**可忽略**。所以更准确的表述是: > **合理压缩**(长边 2048px、质量 0.9)→ 对输出清晰度**无可感知影响**; > 只有**极端过度压缩**才可能在精细编辑场景里造成细节损失。 ## 输入图压缩的实践经验 如果你也要在调用前压缩输入图,建议采用以下温和标准,既省带宽又不损失有效信息: | 项目 | 建议值 | 说明 | | - | - | - | | 触发压缩的阈值 | 原图 > **1.5 MB** | 小图无需压缩,直接传 | | 最长边上限 | **2048 px** | 等比缩放、保持宽高比,**不放大**小图 | | 压缩质量 | **0.9**(0\~1) | 偏高画质,肉眼几乎无损 | | 输出格式 | **保持原格式**(JPG/PNG/WebP) | 不强制转格式;含透明通道用 PNG/WebP | | 多图合计体积 | 控制在 **约 6 MB** 以内 | 多张参考图时,按张数自适应分摊单张目标体积 | | 单文件上限 | **≤ 20 MB** | 超大文件先压再传,避免上传超时/被拒 | 多图自适应思路:`单张目标体积 = clamp(总预算 ÷ 张数, 0.3MB, 1.5MB)`。张数越多、每张分摊越小,保证合计可控;已达标的图原样通过、不二次压缩。 **容错建议**:压缩属于「锦上添花」,请保留兜底——**某张图压缩失败时回退用原图继续**,不要因为压缩环节失败而中断整个生成请求。 ## 生成图用于下游工作流:同样建议先处理 API 产出的图片往往比想象中大。以 Nano Banana Pro 的 4K 档位为例(实测经验值,具体随渠道编码实现变化): | 渠道 | 4K 单张典型体积 | | - | - | | AI Studio 渠道 | 约 **9 MB** | | Vertex 渠道 | 约 **18 MB** | 同为 4K 档位,不同渠道的编码实现不同,体积可以差一倍。 如果要把产出图作为下一环节的输入(再编辑、多图合成、当参考图),**按输入图的同一标准先压缩再传**(长边 2048px、质量 0.9)。否则一张 18 MB 的图经 Base64 膨胀约 33% 后约 24 MB,很容易触顶请求体/单文件限制,还拖慢上传。Base64 膨胀细节见 [Nano Banana 系列开发指南](/api-capabilities/nano-banana-dev-guide)。 下游用图 ≠ 需要保真原图。工作流的中间图按「模型看得懂」的标准压即可;如果终稿需要 4K 交付,只在**最后一步**出 4K,中间迭代用 1K/2K,又快又省。 如果产出图只用于展示/存档、不回传模型,可考虑 [Nano Banana OSS 分组](/api-capabilities/nano-banana-oss-group):图片以 URL 形式输出,省去 Base64 传输压力。 ## 更多图片处理最佳实践 除了压缩,以下几件事在 API 调用场景同样建议在上传前做好: * **EXIF 方向先「烘焙」进像素**:手机照片的横竖方向常常存在 EXIF Orientation 标记里,而不是像素本身。部分处理链路会忽略这个标记,导致模型看到横竖颠倒的图。上传前先把旋转应用到像素上(多数压缩库在重编码时会自动完成)。 * **上传前剥离 EXIF 隐私信息**:原图 EXIF 常包含 GPS 经纬度、设备型号、拍摄时间等敏感信息。把用户照片发给第三方 API 前建议剥离元数据——重编码压缩通常会顺带完成这一步,但注意顺序:**先应用方向、再剥离**。 * **格式兼容**:iPhone 默认的 HEIC/HEIF 格式多数图像 API 不支持,上传前先转 JPEG/PNG;带透明通道的图用 PNG/WebP;GIF 动图通常只有首帧会被读取。 * **色彩空间转 sRGB**:苹果设备照片常用 Display P3 色彩空间,部分处理链路不识别色彩描述文件会产生色偏,建议上传前转成 sRGB。 * **传输方式按场景选**:输入侧 Base64 最稳,URL(`fileUri`)上传对图床/CDN 要求高,取舍见 [Nano Banana 系列开发指南](/api-capabilities/nano-banana-dev-guide);输出侧不想处理 Base64 可用 [Nano Banana OSS 分组](/api-capabilities/nano-banana-oss-group) 直接拿 URL。 * **按需选择输出档位**:不需要 4K 交付就别请求 4K——生成更慢、体积更大、下游传输和处理成本更高。中间迭代用 1K/2K,确认满意后终稿再上 4K。 * **URL 输出及时转存**:以 URL 形式返回的图片链接有时效,拿到后及时转存到自有存储,不要把临时 URL 当永久资源引用。 ## 速查总结 * **输出分辨率 = 尺寸参数**(`imageSize` / `size` / `aspect_ratio`),**不是 prompt 里的文字**。想要 4K,请设参数,别写在提示词里。 * `gemini-3-pro-image` 用 `imageSize`,档位 **1K / 2K / 4K**(默认 1K);GPT 图像系列用 `size` 像素串。 * **输入图压缩 与 输出图分辨率 互不相干**,是请求里两个独立字段。 * **像素尺寸和文件体积是两回事**:压缩 = 先缩边(长边 2048px)再降质(0.9),是否处理看体积(大于 1.5MB 才压)。 * **合理压缩输入图(长边 2048px、质量 0.9)不影响输出清晰度**;只有极端过度压缩才可能在精细编辑里掉细节。 * 输入压缩推荐:大于 1.5MB 才压、长边 ≤2048px、质量 0.9、保持原格式、多图合计 ≤6MB、单文件 ≤20MB、失败回退原图。 * **生成图进下游工作流前也要先压缩**:Nano Banana Pro 4K 单张约 9–18 MB(随渠道而异),直接回传很容易触顶限制。 * **上传前处理好 EXIF 与格式**:方向烘焙进像素、剥离 GPS 等隐私元数据、HEIC 转 JPEG、Display P3 转 sRGB。 ## 相关文档 * [Nano Banana 系列开发指南](/api-capabilities/nano-banana-dev-guide) * [usage 字段与输出解读](/api-capabilities/nano-banana-usage-metadata) * [Gemini 生图 API 错误处理指南](/api-capabilities/gemini-image-error-handling) # 图片 API 连接中断排查 Source: https://docs.apiyi.com/api-capabilities/image-connection-drops connection reset by peer、write_response_body_failed、SSL EOF、200 后 body 传一半断开这类报错的排查方法。平台返回 500 write_response_body_failed 不计费;含 macOS / Linux 服务器分别要查什么、线路丢包自查、Node.js undici 三个超时、以及本地代理判别矩阵。 ### 先说计费:`write_response_body_failed` 这类 500 **不收费** 网关返回 `500` + `write_response_body_failed` / `connection reset by peer` 时,平台侧**已经自动内部重试了 2-3 次**,全部失败才把错误抛给你。**这种情况不产生任何费用。** 所以哪怕你在日志里看到一连串这样的报错,**账单上不会有对应扣费**——不用担心「失败了还被收钱」。会计费的是另一种情况(客户端自己提前断开),详见下面的「计费影响」一节。 **一句话结论**:图片 API 的响应体动辄十几到几十 MB,断点几乎总在**下载响应数据**这一程(**不是**请求体太大——纯文生图同样会中断)。排查时先看后台日志属于哪一类,再按 macOS / Linux / Node.js 分别自查。 ## 报错长什么样 同一个根因,在网关侧和客户端侧会呈现成两副完全不同的面孔。 ### 网关侧返回 ```json theme={null} { "status_code": 500, "error": { "message": "write tcp 10.0.0.1:443->203.0.113.5:52310: write: connection reset by peer", "type": "shell_api_error", "code": "write_response_body_failed" } } ``` ### 客户端侧抛出 | 语言 / 库 | 典型异常 | | - | - | | Python `requests` / `urllib3` | `SSLError(SSLEOFError(8, 'EOF occurred in violation of protocol'))`、`ChunkedEncodingError`、`ConnectionResetError`、`IncompleteRead` | | Java(Spring WebClient / Reactor Netty) | `PrematureCloseException: Connection prematurely closed DURING response` | | Python `httpx` | `RemoteProtocolError`、`ReadError` | | Node.js(undici / 内置 fetch) | `UND_ERR_CONNECT_TIMEOUT`、`UND_ERR_HEADERS_TIMEOUT`、`UND_ERR_BODY_TIMEOUT`、`SocketError: other side closed` | | Node.js(其它栈) | `read ECONNRESET`、`ERR_STREAM_PREMATURE_CLOSE`、`socket hang up` | | Go | `unexpected EOF`、`http2: server sent GOAWAY` | | curl | `curl: (56) Recv failure`、`curl: (18) transfer closed with outstanding read data remaining` | **Node.js 的报错要分清「掐」和「关」**:`ECONNRESET` 表示收到了 TCP RST,是连接被中间设备**掐断**,指向网络路径上的某一跳;`SocketError: other side closed` / `ERR_STREAM_PREMATURE_CLOSE` 表示对端**优雅关闭**(FIN),指向服务端收尾问题(如少发了 chunked 终止块)。两者方向完全不同,不要混为一谈。 另外 `UND_ERR_*` 只可能来自 undici(Node 18+ 的内置 `fetch` 底层),`read ECONNRESET` 则是 libuv 的顶层文案,`axios` / `node-fetch` / `http` 模块都会给。**如果两类错误同时出现,先确认你的应用里是不是有两条不同的 HTTP 路径**——那样的话它们根本不是同一件事。 ## 先判断方向:是谁先断的 `write_response_body_failed` 这个 code 是关键线索——它的含义是**网关在向调用方回写响应体的过程中失败**,属于**下行方向**,不是上游模型报错。换句话说,结果已经生成出来了,正往你这边推的时候连接断了。 **这不是「请求体太大」造成的。** 图片编辑要上传参考图,容易让人误以为是上传体积的问题;但**纯文生图(请求体只有几百字节)同样会中断**。断点在**下载响应数据**的这一程——图片响应动辄十几到几十 MB,是全链路上最脆弱的一段。 `write_response_body_failed`、`connection reset by peer`、客户端 SSL EOF。 网关在推 body 时连接消失。**平台返回 500 的这一类不计费**,详见下面的计费一节。 上游超时、`upstream_error`、5xx 带上游原始报文,或 HTTP 200 但 `finishReason` 异常。 这类才是渠道问题,可以拿 `x-request-id` 找客服核查。 ### 最强判据:后台日志里这次是什么状态 在动手排查之前先看后台调用日志。这条零成本,而且比任何客户端操作都更快缩小范围: | 日志表现 | 含义 | 是否计费 | 下一步 | | - | - | - | - | | **正常的计费记录** | 请求到达、上游跑完、网关认为已交付完成 | 计费 | 走下面的四步自证,重点查应用层误读与客户端提前断开 | | **500 `write_response_body_failed`** | 网关向你回写响应体失败,且平台已**内部重试 2-3 次**仍未成功 | **不计费** | 属于下行链路问题,带 request-id 找客服 | | **完全没有记录** | 请求**压根没发出去** | 不计费 | 建连阶段,跳到下面的「Node.js 三个超时」与「本地代理 / VPN」两节 | 由此可以推出一条经常被搞反的结论:**`UND_ERR_CONNECT_TIMEOUT` 这类建连阶段的失败不可能产生计费**,因为请求根本没到网关。所以如果你看到「大量 connect timeout」同时又「被扣了很多次费」,**这两件事一定不是同一批请求**,必须分开排查,不要用一个根因去解释全部现象。 ### 四步自证 按这个顺序做,绝大多数情况在前两步就能定位: 「没收到图」往往是**代码抛异常后被 catch 成「请求失败」**的结论,而不是真的没收到字节。最常见的一种:`gpt-image-2-all` 默认返回 `b64_json` 且**不带 `data:` 前缀**,代码若按 `data[0].url` 取值会拿到 `undefined`,后续处理直接抛错 → 被判定为失败 → 触发重试 → **重复计费**。 现象与网络故障一模一样,但根本不需要任何网络故障。先打印一行自查: ```javascript theme={null} console.log(Object.keys(resp.data[0]), resp.data[0].b64_json?.length); ``` 各系列的字段与前缀差异见 [base64 前缀差异对照](/api-capabilities/image-api-best-practices#前缀差异对照)。 同一时间窗内,如果你在测的**两个不同渠道、不同模型都在报同一个错**,那几乎可以直接排除渠道特异性——上游不会这么整齐地同时出问题。 Python 看 TLS 栈版本,Node.js 看 undici 的三个超时——见下面两节。这是实测中最高频的根因,且完全在你本地,一条命令就能确认。 把并发降到 1-2、并关掉 VPN 或代理重跑同样的请求。如果这样完全不复现,问题在客户端的连接管理、本地资源(连接池、文件描述符、内存)或网络路径,而不是渠道。 ## 头号元凶:客户端 TLS 栈(macOS 尤其高发) **macOS 系统自带的 Python(`/usr/bin/python3`)链接的是 LibreSSL 2.8.3**,而不是 OpenSSL。这个组合在配合 urllib3 v2 做**并发大响应体下载**时会稳定抛出 `SSLEOFError`,表现为客户端单方面断连——于是网关侧记录下一片 `connection reset by peer`。 ### 一条命令自查 ```bash theme={null} python3 -c "import ssl; print(ssl.OPENSSL_VERSION)" ``` | 输出 | 判断 | | - | - | | `LibreSSL 2.8.3` | ⚠️ **高危**,并发大响应体场景下会假报连接错误 | | `OpenSSL 1.1.1x` 及以上 | ✅ 正常 | 导入 `requests` 时如果看到这行告警,同样是中招信号: ``` NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'LibreSSL 2.8.3' ``` ### 修复:换一个解释器 不要去降级 urllib3,直接换用带正常 OpenSSL 的 Python: ```bash theme={null} # macOS:用 Homebrew 的 Python 建虚拟环境 brew install python@3.13 python3.13 -m venv venv venv/bin/pip install requests pillow venv/bin/python -c "import ssl; print(ssl.OPENSSL_VERSION)" # 应输出 OpenSSL 3.x ``` ### 实测对照(2026-07-29,UTC+8) 在 Nano Banana 系列(`gemini-3-pro-image` / `gemini-3.1-flash-image`)上做双渠道对比测试时的真实数据: | 解释器 | 场景 | 传输层异常率 | | - | - | - | | 系统 python3.9(LibreSSL 2.8.3) | 并发 12 跑图 | **大面积报错,两个渠道同时出现** | | Homebrew python3.13(OpenSSL 3.6.1) | 同样的 108 次调用 | 3 次(2.8%),均为 4K 大响应体,**重试一次即成功** | | Homebrew python3.13(OpenSSL 3.6.1) | 80 次专项复现(含并发 24 小响应体、并发 12 的 4K、以及串行 4K) | **0 次** | 结论很清楚:**换解释器前后差了一个数量级**,且换之前两个渠道同时报错这一点本身就说明与渠道无关。 ## Linux 服务器要查什么(和 macOS 完全不是一回事) 上一节的 TLS 栈自查在 Linux 上**基本都会通过**——各发行版自带的 Python 链的都是正常 OpenSSL,不存在 LibreSSL 那个坑。**所以别在这里停下**:服务器环境的坑在**出网路径**和**容器限制**上,和本机开发完全是两套问题。 ### 1. 云 NAT 网关 / 负载均衡的空闲超时(服务器上最高频) 这是生产环境 `connection reset by peer` 的头号来源。以 **AWS NAT Gateway** 为例:它有一个**固定 350 秒**、不可调的空闲超时,而且超时后**发的是 RST 不是 FIN**——于是客户端拿到的正好就是 `ECONNRESET`。 坑在于它会**连锁**:连接池里的连接闲置超过 350 秒后集体失效,你发请求时第一条被 RST,客户端自动重试换池里下一条——**那条也闲置超时了,照样 RST**。表现就是「一段时间没调用,然后突然连续几发全挂,之后又恢复正常」。 这和前面 Node.js 一节讲的「keep-alive 复用死连接」是同一个机制,只不过在服务器上元凶通常是**云厂商的 NAT 网关**,而不是本地代理软件。 修法(任选,推荐前两个): * **把 TCP keepalive 调到小于 350 秒**,让静默期也有包在走; * **限制连接池的空闲存活时间**,让它主动丢弃可能已失效的连接(Node:`new Agent({ keepAliveTimeout: 60_000 })`;Python `requests` 用 `HTTPAdapter` 控制连接池); * 走 VPC 端点等绕开 NAT 网关的路径。 其它云厂商与自建 LB 的空闲超时值各不相同,但**思路一样:找出链路上最短的那个空闲超时,把 keepalive 调得比它更小**。 ### 2. TCP keepalive 默认值等于没开 Linux 的 `tcp_keepalive_time` 默认是 **7200 秒(2 小时)**,远大于上面任何一个空闲超时,等于完全不起作用: ```bash theme={null} # 查当前值 sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probes # 临时调整(容器里需要 --sysctl 或特权,生产建议写进 sysctl.d 或应用层设置 SO_KEEPALIVE) sudo sysctl -w net.ipv4.tcp_keepalive_time=60 sudo sysctl -w net.ipv4.tcp_keepalive_intvl=15 ``` 更稳妥的做法是**在应用层的 HTTP 客户端上开 keepalive**,不依赖全局 sysctl——容器里改内核参数往往受限。 ### 3. 容器网络的 MTU Docker / K8s 的 overlay 网络(flannel VXLAN 等)MTU 常被设成 **1450** 而不是 1500,一旦和路径上的 PMTUD 黑洞叠加,就是典型的「小请求全正常、大响应必挂」: ```bash theme={null} ip link show # 看容器网卡 MTU # 用逐步增大的包长探测实际可通过的 MTU(不分片) ping -M do -s 1400 api.apiyi.com ``` ### 4. 容器内存上限 → 进程被 OOMKilled 4K 出图的 base64 单条可达 20-30MB,`resp.json()` 一次性载入再叠加并发,很容易超过容器的 memory limit 被内核杀掉,**表现同样是「连接莫名其妙断了」**: ```bash theme={null} # 容器是不是被 OOM 杀的 dmesg -T | grep -i -E "oom|killed process" kubectl describe pod | grep -A3 "Last State" # 看是否 OOMKilled ``` 对策见下面的「流式读取,别一次性载入」。 ### 5. 环境变量里的代理(服务器上最隐蔽的一个) 服务器上经常有全局 `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`(写在 `/etc/environment`、systemd unit 或 Dockerfile 里),你自己都忘了它的存在。**更麻烦的是各语言对它的处理并不一致**: | 客户端 | 是否自动读取 `HTTPS_PROXY` | | - | - | | Python `requests` / `httpx` | ✅ 默认读取 | | curl | ✅ 默认读取 | | **Node.js 内置 `fetch`(undici)** | ❌ **默认不读**,要显式用 `ProxyAgent` / `EnvHttpProxyAgent` | 这个不一致会造成非常迷惑的现象:**同一台机器上 curl 和 Python 走代理、Node 直连**(或反过来),两者行为不同,排查时容易得出矛盾结论。先确认一下: ```bash theme={null} env | grep -i -E "proxy|no_proxy" ``` API易 国内可直连,**服务器上通常应该把 `api.apiyi.com` 加进 `NO_PROXY`**,或干脆确认没有代理变量。 ### 服务器侧一键自查 ```bash theme={null} echo "--- 代理变量 ---"; env | grep -i proxy || echo "无" echo "--- keepalive ---"; sysctl net.ipv4.tcp_keepalive_time echo "--- MTU ---"; ip link show | grep mtu echo "--- fd 上限 ---"; ulimit -n echo "--- DNS 解析 ---"; getent hosts api.apiyi.com echo "--- 连通性与耗时 ---" curl -sS -o /dev/null -w 'connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} ip=%{remote_ip}\n' \ https://api.apiyi.com/v1/models -H "Authorization: Bearer $KEY" ``` ## 线路丢包:响应头到了,body 传一半就断 服务器直连 API易 时,如果这台机器(或它所在机房)的出口线路质量差,会出现一种很好认的断法:**HTTP 200 的响应头已经收到,body 下载到一小部分后连接就被关闭了**。图片其实已经生成成功,是在把结果传回你服务器的这段公网上断的。 ### 典型报错 | 客户端 | 报错 | | - | - | | curl | `curl: (18) transfer closed with 3129248 bytes remaining to read`,同时 `-w` 打出的状态码是 `200` | | Java Spring WebClient(Reactor Netty) | `WebClientResponseException: 200 OK from POST https://api.apiyi.com/v1/images/edits, but response failed with cause: PrematureCloseException: Connection prematurely closed DURING response` | | Python | `IncompleteRead(134335 bytes read, 3129248 more expected)`(`requests` 会把它包在 `ChunkedEncodingError` 里抛出) | ### 怎么认出它:三个特征 1. **状态码是 200,而且报出了「还差多少字节」**。报得出剩余字节数,说明响应带了 `Content-Length`。也就是说,网关是拿到了完整结果、知道总大小之后才开始发送的。**生成已经成功,断点只可能在下行传输上。** 2. **总耗时远大于生成耗时**。curl 的 `time_starttransfer`(收到首字节的时间)大致等于生成耗时。如果 `time_total` 比它多出几分钟,而 `size_download` 只有几十到几百 KB,说明数据传不动。 3. **按时段发作,换一台机器就正常**。同一台服务器在白天某几个小时集中失败,其它时段正常;同一时间用另一台机器(或你的本地电脑)跑同样的请求,却完全正常。 为什么最后是「连接被关闭」,而不是一直卡着:线路严重丢包时,数据包和确认包都在大量丢失,服务端**长时间收不到客户端的接收确认,就会关闭连接**。所以它看起来像超时,根因却是丢包。**把你这边的超时调大解决不了问题。** ### 自查步骤 ```bash theme={null} curl -sS -X POST https://api.apiyi.com/v1/images/edits \ -H "Authorization: Bearer $APIYI_API_KEY" \ -F model=gpt-image-2 -F prompt=换个背景 -F n=1 \ -F 'image=@ref.jpg;type=image/jpeg' \ -D headers.txt -o resp.json \ -w 'http=%{http_code} ttfb=%{time_starttransfer} total=%{time_total} size=%{size_download} speed=%{speed_download}\n' grep -i -E "content-length|x-shellapi-request-id" headers.txt ``` 判读:`http=200`、`size` 明显小于 `Content-Length`、`total` 远大于 `ttfb`,就是下行传输出了问题,继续做下一步。 ```bash theme={null} ping -c 100 api.apiyi.com mtr -rwc 100 api.apiyi.com # 没装的话:yum install mtr 或 apt install mtr ``` 正常线路的丢包应该接近 0%。只看**一直延续到最后一跳**的丢包:中间某一跳单独显示丢包、后面几跳又恢复正常的,通常只是那台路由器不优先回应 ping,不算数。**最好在失败高发的时段跑**,线路好的时候测不出问题。 同一时间,在另一台服务器(最好是不同机房或不同云厂商)或你的本地电脑上跑第 1 步的同一条命令。对照机正常、只有这台失败,问题就在这台机器的网络出口,不在接口本身。 把每次调用的时间、`http_code`、`size`、`total` 记下来,看失败是不是集中在某几个时段。按时段发作是线路拥塞的典型特征。 **一个真实案例(2026-09-27)**:出图 48 秒就完成了,完整的响应也已经送到出口节点,但客户服务器所在机房的线路当时丢包 17%\~37%(同一时刻,同一批节点到其它国内服务器的丢包是 0%),客户最终只收到约 4% 的数据。同一时段、同一批节点上的其他客户全部正常收完。 ### 缓解办法 1. **把响应体变小(立刻见效)**:gpt-image 系列加上 `output_format=jpeg` 和 `output_compression=85`。2026-09-27 实测:n=1 的响应从约 2.4 MB 降到约 0.5 MB,n=5 从约 12 MB 降到约 2.6 MB,**小了约 4.7 倍**。要传的数据越少,在丢包线路上越容易传完。支持 `response_format: "url"` 的模型(如 `gpt-image-2-vip`、`gpt-image-2-all`)可以直接改为返回链接,响应体只剩几百字节。 2. **把 n>1 拆成多个并发的 n=1**:n=5 的响应是 n=1 的 5 倍大,更容易断,而且一断就整批作废。拆开后每个响应都小,失败只影响一张。 3. **换出口(根治)**:把调用 API易 的服务放到线路质量好的机器上(比如主流云厂商的 BGP 或精品线路机型),或者经这类机器中转。也可以把 mtr 结果交给你的机房或运营商排查。 4. **客户端配置**:修不了丢包,但能避免线路恢复后出现别的问题。以 Java WebClient 为例,`maxInMemorySize` 默认只有 256 KB,而 n>1 的响应动辄十几 MB,需要调到 32 MB 以上,否则会报 `DataBufferLimitException`;`responseTimeout` 至少设 300 秒: ```java theme={null} HttpClient httpClient = HttpClient.create() .responseTimeout(Duration.ofSeconds(300)); WebClient client = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .codecs(c -> c.defaultCodecs().maxInMemorySize(32 * 1024 * 1024)) .build(); ``` **重试前先想清楚:这类失败照常计费。** 连接断开时图片已经生成,平台也已完成交付,后台日志里是一条正常的计费记录。所以每重试一次,就是再生成一次、再付一次钱。线路没有改善之前,盲目重试只会让费用成倍增加,先做上面第 1\~3 条。 需要找客服核查时,请提供: * 失败时间,精确到分钟并带时区,如 `2026-09-27 13:32 (UTC+8)` * 服务器的出口 IP(可以用 `curl -s ipinfo.io/ip` 查) * 响应头里的 `X-Shellapi-Request-Id`(第 1 步命令会打印出来) * 第 1 步 curl `-w` 输出的那一行 * `mtr` 的结果 想进一步测出你的服务器到我们接口的实际带宽,见[日志显示已完成但客户端收不到响应](/faq/log-duration-vs-client-wait)。 ## Node.js:三个超时互相独立,SDK 的 timeout 管不到 Node 18+ 的内置 `fetch` 底层是 undici,它有**三个各自独立的超时**,分别对应请求的三个阶段。「我 timeout 设了 5 分钟」通常只调到了其中一个都不是的第四个值: | 错误码 | 发生阶段 | undici 默认值 | 由什么控制 | 会被计费吗 | | - | - | - | - | - | | `UND_ERR_CONNECT_TIMEOUT` | 建连(TCP + TLS 握手) | **10 秒** | `connect.timeout` | **不会**(请求没到网关) | | `UND_ERR_HEADERS_TIMEOUT` | 等首个响应头 | 300 秒 | `headersTimeout` | 会 | | `UND_ERR_BODY_TIMEOUT` | **相邻两个 body chunk 之间**的间隔 | 300 秒 | `bodyTimeout` | 会 | **openai-node 的 `timeout` 选项是基于 AbortController 的「总请求超时」,不会传导到上面三个中的任何一个。** 你把 `timeout` 从 60 秒调到 300 秒,`connectTimeout` 依然是 10 秒。裸 `fetch()` 的 `AbortSignal.timeout()` 同理。 这就是「明明 timeout 设得很大却还是报超时」最常见的原因——调错了层。 ### 正确的配置写法 要放宽 undici 的三个超时,必须配 `Agent`(全局或按请求): ```javascript theme={null} import { Agent, setGlobalDispatcher } from "undici"; import OpenAI from "openai"; // 图片接口是「长时间静默 + MB 级响应体」,三个超时都要单独放宽 setGlobalDispatcher(new Agent({ connect: { timeout: 30_000 }, // 建连 30s,默认只有 10s headersTimeout: 300_000, // 等首包 300s bodyTimeout: 300_000, // chunk 间隔 300s })); const client = new OpenAI({ apiKey: process.env.APIYI_API_KEY, baseURL: "https://api.apiyi.com/v1", timeout: 300_000, // 总超时,与上面三个是不同层,都要设 maxRetries: 0, // 关键:见下 }); ``` ### `maxRetries` 默认是 2,而且会自动重试连接错误 openai-node **默认 `maxRetries: 2`,并且连接错误和超时都在自动重试范围内**。也就是说一次业务调用最多会产生 **3 次实际请求**(其中会不会计费,取决于每一次分别属于下面「计费影响」的哪一类),而你的代码里可能一次重试都没写。 图片接口单价高、又是同步长请求,**一律显式设 `maxRetries: 0`,把重试收到自己手里**,配合自己的退避与次数上限。计费口径见 [重试策略](/api-capabilities/image-api-best-practices#重试策略)。 排查时先确认你到底用的是什么栈:`node -v`、`npm ls openai undici axios node-fetch`。`UND_ERR_*` 只证明底层走的是 undici,**不能证明你用的是 openai SDK**——裸 `fetch()` 同样会抛这些错误码,而裸 `fetch()` 没有 `maxRetries` 这回事。 ### keep-alive 复用了一条已经死掉的连接 undici 默认启用连接池 + keep-alive。VPN、NAT、代理软件把空闲连接静默回收之后,客户端并不知情,仍然从池里取出这条连接来发下一个请求——**写入的瞬间收到 RST,表现就是 `read ECONNRESET`**。 这是 `ECONNRESET` 在长间隔调用场景下最常见的来源,也能解释「错误在某个时间窗内密集出现」「重试的第一发也失败」。验证方法是关掉复用再跑: ```javascript theme={null} const agent = new Agent({ pipelining: 0, keepAliveTimeout: 1_000 }); // 错误随之消失 ⇒ 就是死连接复用 ``` ## 本地代理 / VPN:图片接口最容易暴露的一跳 **API易 国内可直连,不需要代理或 VPN**(见 [使用 API 接口需要代理网络吗?](/faq/network-proxy))。所以排查时,**关掉代理直连复测是成本最低、信息量最大的一发**。 但要说清楚:代理只是**嫌疑最大的变量之一**,不等于根因。下面的判别矩阵才是用来定位的。 图片接口有两个特征让它比文本接口敏感得多:**生成期有 30-60 秒零字节流动**,以及**响应体是 MB 级的一次性突发**。普通聊天接口跑得好好的,图片接口挂掉,往往就卡在这两点上。 代理软件的 fake-ip 模式下,若规则没命中,会连到 `198.18.x.x` 这类不可路由地址,表现是**精确 10 秒**的 connect timeout。注意这不是「建连慢」,是**根本没有路由**——放大 `connect.timeout` 也救不回来。务必记录实际连到的 `remote_ip`。 请求发出后有 30-60 秒零字节流动,代理按空闲连接策略回收。特征是**失败时刻是 30 / 60 / 120 这类圆整值**,且与图片大小无关。 隧道 MTU 小于路径 MTU,而 ICMP「需要分片」被丢弃导致 PMTUD 失效。典型表现是**小请求全正常、大响应必挂**,已收字节停在几 KB 到几十 KB 就不动了。把隧道 MTU 降到 1400 左右常能解决。 开了 HTTPS 解密的代理常对大 body 做整体缓冲,可能撞上体积上限;也可能把 chunked 重写成 `Content-Length` 而长度算错,直接 RST。同样只打图片这种 MB 级响应,不打文本调用。 ### 判别矩阵 这是本节的核心。判别轴只有两条:**失败发生在首字节之前还是之后**、**已经收到了多少字节**。 | 观测项 | 建连超时 | 代理空闲回收 | MTU 黑洞 | 服务端缺 chunked 终止块 | | - | - | - | - | - | | TTFB(首字节) | 永远没有 | 永远没有 | 有 | **正常**(与生成耗时一致) | | 已收字节数 | 0 | 0 | **0 \< N ≪ 全量** | **= 全量,JSON 可完整解析** | | 失败时刻 | **≈10.0 秒,极稳定** | 圆整值,与图大小无关 | 不定 | last-byte 之后 **+300 秒被断开**;也可能**无限期挂住**(实测等 330 秒仍无变化) | | 连接终态 | ConnectTimeout | RST | 挂死或 RST | FIN(优雅关闭);也可能**既无终止块也无 FIN,连接一直开着** | | 是否计费 | **否**(没到网关) | 见「计费影响」一节 | 见「计费影响」一节 | 见「计费影响」一节 | | 换 `response_format: "url"` | 仍失败 | 仍失败 | **变正常** | **变正常** | 最后一行是性价比最高的一发:把响应体从数 MB 压到 1KB 左右,**如果 URL 模式稳定成功而 base64 模式稳定失败,就说明问题与传输体量相关**,可以直接排掉建连和空闲回收两列。 最右边那一列(**服务端缺 chunked 终止块**)近期出现了新的形态:**既不发终止块、也不关连接,无限期挂住**,不再是过去那种「+300 秒后被优雅关闭」。两种形态的共同点是**数据已经完整、图片可以直接用**,处理方式也一样 —— 在客户端主动收尾,详见[请求收尾卡住](/api-capabilities/image-tail-stall)。 ### 一条命令看清时间剖面 ```bash theme={null} curl -sS -o /tmp/out.json --trace-time \ -w '\nconnect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total} bytes=%{size_download} code=%{http_code} ip=%{remote_ip}\n' \ -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \ -d '{"model":"gpt-image-2-all","prompt":"a red cube on a white table"}' \ https://api.apiyi.com/v1/images/generations ``` 读法对着上面的矩阵:`connect` 没值 → 建连阶段;`ttfb` 没值且 `total` 是圆整数 → 空闲回收;`bytes` 是全量但 `total ≈ ttfb + 300` 并报 `curl: (18)` → 服务端没发终止块;`bytes` 卡在几十 KB → MTU。 **做代理开 / 关的 A/B 对比时必须交替执行,不能分块。** 先连跑 5 次代理、再连跑 5 次直连,这种顺序分组会被**时间窗性质的故障**污染出完全错误的结论——实测中确实遇到过某段时间全挂、隔几分钟全好、再过一会儿又复发的情况。正确做法是 `代理 → 直连 → 代理 → 直连` 交替,并记录每次的 `remote_ip`。 ## 其它常见诱因 调试时 Ctrl+C、重启进程、热重载、kill 掉正在跑的脚本——所有正在传输的大响应体都会在网关侧留下一条 `write_response_body_failed`。这是最容易被误读成「渠道不稳」的假警报。 任务队列 worker 超时、Serverless 函数执行上限、网关/CDN 的回源超时(默认普遍 60 秒)。任何一层小于生成时间都会先掐断连接,详见[必读&最佳实践](/api-capabilities/image-api-best-practices#超时与断连排查)。 连接池上限、本地文件描述符上限、NAT / 防火墙对长连接的静默回收。大响应体持续时间长,撞上这些限制的概率远高于文本接口。 4K 出图的 base64 单条可达 20-30MB,一次性 `resp.json()` 全量载入再加并发,容器内存打满会导致进程被 OOM 杀掉,表现同样是「连接莫名断开」。 ## 计费影响:哪些断连收费,哪些不收费 这两种情况经常被混为一谈,但计费结果完全相反: ### 平台返回 500 `write_response_body_failed` —— **不计费** 这个错误表示网关在向你回写图片数据时连接断了。**平台侧会自动内部重试 2-3 次**,重试全部失败之后才把 500 抛给你。 **这种情况不产生任何费用。** 所以即使你在日志里看到一连串这样的报错、而且是同一个请求反复失败,**账单上不会有对应的扣费**,不用担心「失败了还被收钱」。 ### 客户端自己提前走开 —— **照常计费** 另一种情况是网关正常完成了交付,是**你这边先断的**:客户端 timeout 到点主动断开、调试时 Ctrl+C、进程被重启或被 OOM 杀掉。 这类请求服务端与上游的生成**已经完成**,**照常计费**——「我没拿到图」不等于「没花钱」。所以排查期间反复重试大图请求,这部分账单是实打实在走的。 区分方法就是上面那张表:**看后台日志记的是正常调用还是 500 `write_response_body_failed`**。 重试策略也要相应克制:传输层异常值得重试,但**每次重试都可能是一次新的计费**(取决于它属于上面哪一类)。不要写无上限的重试循环。 ## 正确的重试写法 关键原则:**只对传输层异常重试,不对 HTTP 层错误重试**。4xx 重发一万次也还是 4xx,而且浪费时间。 ```python theme={null} import time import requests TRANSPORT_ERRORS = ( requests.exceptions.SSLError, requests.exceptions.ConnectionError, requests.exceptions.ChunkedEncodingError, requests.exceptions.ReadTimeout, ) def call_image_api(url, headers, body, timeout=360, retries=2): """传输层异常最多重试 retries 次;HTTP 4xx/5xx 一律不重试,直接交给上层判断。 注意:每次重试都可能是一次新的计费请求,retries 不要设大。 """ attempts = [] for i in range(retries + 1): try: resp = requests.post(url, headers=headers, json=body, stream=True, timeout=(10, timeout)) raw = b"".join(resp.iter_content(chunk_size=8192)) attempts.append({"attempt": i + 1, "status": resp.status_code}) return resp.status_code, raw, attempts # 含 4xx/5xx,交上层处理 except TRANSPORT_ERRORS as e: attempts.append({"attempt": i + 1, "error": repr(e)}) if i == retries: raise time.sleep(2 + 3 * i) # 2s、5s 退避 ``` **把每次尝试单独记下来**(上面的 `attempts`)。否则客户端重试成功后,日志里只剩一条漂亮的 200,你会永远看不到底层到底断了多少次——排查渠道质量时这份数据是关键,也能避免把自己的重试误读成渠道行为。 ### 流式读取,别一次性载入 大响应体建议用 `stream=True` 逐块读取,既能降低内存峰值,也能在出问题时看清**是在传输的哪个阶段断的**: ```python theme={null} resp = requests.post(url, headers=headers, json=body, stream=True, timeout=(10, 360)) chunks, total = [], 0 for chunk in resp.iter_content(chunk_size=8192): total += len(chunk) chunks.append(chunk) raw = b"".join(chunks) # total 远小于 Content-Length ⇒ 传到一半断了 # total 完整但连接不关 ⇒ 上游缺 chunked 终止块,属于渠道问题 ``` 后一种情况**不要重试**:数据已经完整,图片可以直接用。客户端主动收尾的完整写法见[请求收尾卡住](/api-capabilities/image-tail-stall)。 ## 什么时候才该找客服 自证走完之后,如果满足下面**任一条**,就带着材料找客服核查: * 换了正常 OpenSSL 的解释器、并发降到串行,**仍然稳定复现**; * 只有**某一个特定渠道 / 模型**在报,其它渠道同时段正常; * 响应体**已经完整收到**(字节数对得上 `Content-Length`)但连接迟迟不关闭,直到超时——这是上游缺 chunked 终止块,属于渠道侧问题。**先按[请求收尾卡住](/api-capabilities/image-tail-stall)加一层主动收尾把图取出来**,仍有问题再提工单; * 报错是明确的上游方向(`upstream_error`、上游 5xx 原文)。 提工单时附上:`x-request-id`、调用时间(**带时区**,如 `2026-07-29 14:32 (UTC+8)`)、模型名、`imageSize` 等关键参数、客户端异常原文、以及你已经做过的自证步骤。 ## 相关文档 图已传完但连接不结束时,在客户端主动收尾的兼容写法 同步调用、timeout 分档配置、base64 处理、断连计费口径 把同步调用包进任务队列,用重试与落库消化偶发断连 API易 国内直连、无需代理;证书与 DNS 类问题的自查方法 Gemini 系出图的错误码与 finishReason 处理 怎么把上面这些报错完整打印下来,以及提工单该带哪些字段 # 如何生成满意的图片 Source: https://docs.apiyi.com/api-capabilities/image-generation-success-tips 用一个真实的图片编辑案例,讲清网页版与 API 的本质差异、单次调用波动的来源,以及改提示词、重试、换模型、用测试工具定位四个实用策略。 一次出图不满意是常态,**不满意 ≠ 模型不行,更 ≠ 通道不行**。本文用一个真实客户案例,讲清「同一个模型,为什么网页版和 API 表现不一样」,波动到底来自哪里,以及四个能实际提高出图成功率的策略。 ## 案例复盘:一次「颜色改错」的图片编辑 任务是一张玩具茶具套装的产品图,图中左下角「Cups」的两只杯子(一灰一绿)被红框框住。提示词: > 把红框中的物品换成黑色,并去除红框,其他不变 玩具茶具套装产品图,左下角两只杯子被红色方框标注 客户用 `gemini-3.1-flash-image`(Nano Banana 2)通过 API 调用,得到的结果是: 编辑失败的结果图:红框内两只杯子变成绿色而非要求的黑色,红框仍然存在 **颜色改错了**——要求换黑色,结果出来是两只绿杯,红框还在。而客户在 gemini 网页版(`gemini.google.com`)用同一个模型做同样的编辑,效果很好。客户的反馈是: > API 和官方(网页版)输出的内容完全不一样,感觉 API 理解层面没有那么到位。 这个感受很真实,但归因需要修正。下面逐层拆解。 ## 先讲清:网页版是 Agent,API 是单次原子调用 拿 `gemini.google.com` 的效果直接对比裸 API,本身就不是同一条链路的对比: | | Gemini 网页版 | API 直调 | | - | - | - | | 产品形态 | **综合 Agent** | **单次原子调用** | | 你的提示词 | 可能被系统**改写、扩充、增强**后再进模型 | **原样**进模型 | | 执行过程 | 可能有多步编排、内部重试/择优 | 一次采样、直接返回 | | 底层模型 | gemini-3.1-flash-image | gemini-3.1-flash-image(相同) | 同一个模型、两种产品形态。网页版帮你把「口语化指令」加工成了模型更容易执行好的形式,这层加工在 API 里**需要你自己做**(这也正是 API 的价值:一切可控、可复现、可集成)。 所以「网页版效果更好」的主要来源是**链路差异**,不能直接得出「API 理解不到位」的结论。API 拿到的是没有任何加工的原始提示词,表现自然更依赖提示词本身的质量。 ## 单次调用有波动,是生成模型的固有属性 我们用**完全相同的提示词 + 图片**,在 [imagen.apiyi.com](https://imagen.apiyi.com) 测试工具上重试了这个任务:**一次就编辑对了**——杯子变黑、红框去除、其他不变。 需要澄清:imagen.apiyi.com 与裸 API 调用的唯一区别,是内置了一个「生成图片」的意图提示词。它有助于让模型明确「要输出图片」这个意图,但与本案例「精准编辑是否成功」没有关系——**工具上成功并不是因为工具「加了料」**。 同样的输入、同样的模型、同样的通道,一次失败一次成功,说明什么? **生成式模型的单次输出本身就有随机性**。每次调用都是一次独立采样,复杂指令(框选定位 + 换色 + 去框 + 保持其他)恰好是容易在个别采样中「掉链子」的类型。这不是通道问题,也不是 API 被「降智」,而是模型固有的波动。 理解了波动来源,应对策略就清晰了——下面四个,按性价比排序。 ## 策略一:改进提示词 提示词写得越少歧义、越可执行,单次成功率越高。以本案例为例: **原提示词**(口语化,依赖模型自行推断): > 把红框中的物品换成黑色,并去除红框,其他不变 **改进方向**: | 技巧 | 原写法 | 改进写法 | | - | - | - | | 用具体名词替代指代 | 「红框中的物品」 | 「红框内的**两只杯子**」 | | 颜色写具体 | 「换成黑色」 | 「改为**哑光纯黑色**,保留原有材质质感」 | | 保持项逐条列出 | 「其他不变」 | 「画面其余所有物品的颜色、位置、文字标注**全部保持不变**」 | | 动作拆解明确 | 混在一句里 | 「完成两件事:① 两只杯子改为黑色;② 删除红色方框线」 | **改进后的完整提示词示例**: > 编辑这张图片,完成两件事:① 把红框内的两只杯子改为哑光纯黑色,保留原有材质质感和形状;② 删除红色方框线本身。画面其余所有物品的颜色、位置、尺寸标注和文字全部保持不变。 通用原则:**一次只改一类东西**。如果编辑动作很多(换色 + 换背景 + 加文字),拆成多轮编辑,每轮的成功率都会显著高于一次性下达复合指令。 ### 不知道怎么改?让 AI 帮你改 改提示词本身也可以交给 AI——方法很简单,把三样材料一起发给当下知名可靠的 AI 对话产品(如 `chatgpt.com`、`gemini.google.com`): 1. **原始提示词**(原样贴上); 2. **问题描述**(例如「要求换黑色,结果改成了绿色,红框也没去掉」); 3. **出图效果对比**(原图 + 实际出图,一起上传)。 然后让它「针对这个失败结果,改写出一个更精准、少歧义的图片编辑提示词」,通常一轮就能拿到明显更好的版本。 如果无法访问外网,用 API易 同样能满足 AI 对话需求:搭配 **Cherry Studio** 或 **Chatbox** 等对话客户端接入我们的 API 即可,教程见文档中心「应用场景 - 对话」: * [Cherry Studio 接入教程](/scenarios/chat/cherry-studio) * [Chatbox 接入教程](/scenarios/chat/chatbox) ## 策略二:失败就重试 既然失败来自单次采样的波动,**重试本身就是有效手段**——同样的请求再发一次,很可能就对了(本案例正是如此)。 * 建议在业务代码里对「结果不符合预期」保留 1–2 次自动重试的预算; * 注意区分「出图了但改错」和「没出图」两类失败:后者(HTTP 200 但无图片)通常是内容审核拦截,处理方式见 [Gemini 生图 API 错误处理指南](/api-capabilities/gemini-image-error-handling)。 ## 策略三:换模型 本案例中我们用同样的提示词 + 图片实测了其他模型,**全部一次成功**: | 模型 | 结果 | | - | - | | `gemini-3-pro-image`(Nano Banana Pro) | ✅ 一次成功 | | `gemini-3.1-flash-lite-image` | ✅ 一次成功 | | `gpt-image-2` 系列 | ✅ 一次成功 | 不同模型对同一类指令的「擅长程度」不同,某个模型反复失败的任务,换一个模型可能一次就过。在 API易 聚合网关下,换模型只需改 `model` 参数(同一个 key、同一个端点),成本极低——**把「换模型」纳入你的出图工作流,是达成出图目标的正经策略,不是妥协**。 实践中可以按「先快后强」组织一个梯队: 1. 首选快而便宜的模型(如 `gemini-3.1-flash-image`)跑常规任务; 2. 精准编辑类任务失败 1–2 次后,自动升级到 `gemini-3-pro-image` 或 `gpt-image-2` 系列重试; 3. 都不理想再回头改提示词。 ### 特例:小字糊了先调质量档,别急着换模型 海报、UI 截图、招牌、图表标注这类含文字的图,小字发糊多数不是模型能力不行,而是**质量档给低了**。先看手上的模型能调到哪一档: | 模型 | `quality` 可用档位 | 含文字场景建议 | | - | - | - | | `gpt-image-2`(官转) | `low` / `medium` / `high` / `auto` | 至少 `high` | | gpt-image-2.5 系列 | 上述之外另有 `xhigh` / `max` | 密集中英文小字用 `xhigh` 或 `max` | **升档要算成本**:`quality` 直接决定输出 tokens。1024×1024 实测,`gpt-image-2` 从 `low` 到 `high`,输出 tokens 由 196 涨到 7,024(约 36 倍),费用同步上涨;`auto` 是动态档,同一条提示词的费用还会在档位之间漂移。所以不要全局设最高档,只在含文字的任务上升档,或者按任务类型分两套配置。 **同名档位跨模型不等价**:2.5 系把画质梯子重新分了级,2.5 的 `high` 约等于 `gpt-image-2` 的 `medium`,2.5 的 `max` 才等于 `gpt-image-2` 的 `high`。从 `gpt-image-2` 迁到 2.5 时照搬 `quality` 会静默降档,看起来就像「换了模型之后小字更糊」。逐档数据见 [GPT-Image-2.5 / 2 生图/编辑](/api-capabilities/gpt-image-2/overview)。 Gemini 系(Nano Banana 2 / 2 Lite)不用 `quality` 参数,能调的是分辨率和 `thinkingLevel`:对画面内文字排版、构图逻辑有硬要求时开 `thinkingLevel: "high"`,代价约 +54% 费用和 +55% 耗时;`gemini-3-pro-image` 不支持该调节。机制见 [usage 字段与输出解读](/api-capabilities/nano-banana-usage-metadata)。 ## 策略四:先用测试工具定位问题 排查「为什么出图不对」时,先把变量隔离开。[imagen.apiyi.com](https://imagen.apiyi.com) 可以免代码快速验证「提示词 + 图片」这个组合本身: * **工具上也失败** → 大概率是提示词/任务本身的问题,回到策略一改提示词,或按策略三换模型; * **工具上成功、自己代码里失败** → 检查代码:图片是否完整上传、参数是否正确、提示词是否被截断或转义出错; * **时好时坏** → 就是采样波动,按策略二加重试。 这样能避免把「提示词问题」误判成「通道问题」,少走弯路。 ## 速查总结 * **网页版 ≠ API**:网页版是带提示词改写和多步编排的综合 Agent,API 是原始提示词的单次原子调用——观感差异主要来自链路,不是「API 理解不行」。 * **单次输出有随机波动**是生成模型的固有属性,一次失败不代表模型/通道有问题。 * **策略一改提示词**:具体名词、具体颜色、保持项列清楚、动作拆解编号,一次只改一类东西。 * **策略二重试**:为「改错」保留 1–2 次重试预算;「无图」是另一类问题(见错误处理指南)。 * **策略三换模型**:本案例 `gemini-3-pro-image`、`gemini-3.1-flash-lite-image`、`gpt-image-2` 系列均一次成功;聚合网关下换模型只改一个参数。 * **小字糊先调质量档**:含文字的图 `quality` 至少 `high`(2.5 系用 `xhigh` / `max`),升档前先算 tokens 成本;Gemini 系改用 `thinkingLevel: "high"`。 * **策略四用测试工具定位**:imagen.apiyi.com 先验证「提示词+图」,区分提示词问题、代码问题和采样波动。 ## 相关文档 * [Gemini 生图 API 错误处理指南](/api-capabilities/gemini-image-error-handling) * [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution) * [Nano Banana 系列开发指南](/api-capabilities/nano-banana-dev-guide) # 长提示词与两段式出图:32K 上限从哪来,广告图资料怎么进图片模型 Source: https://docs.apiyi.com/api-capabilities/image-long-prompt 客户问「你们 prompt 限了 32K,ChatGPT 没有」。32,000 字符是 OpenAI Images API 的官方上限,按字符不按 token;网页版能吃长资料是对话模型先替你提炼。讲清上限、成本、为什么长不等于遵循度高,以及资料先进文本模型提炼、再进图片模型出图的两段式做法。 「你们的 prompt 长度和 ChatGPT 不一样,好像限了 32K」。这个 32K 不是 API易 加的,是 OpenAI Images API 的官方上限,而且按**字符**计不按 token。但真正值得讨论的不是「能不能塞 32K」,而是**该不该把 32K 资料原样塞给图片模型**。本文讲清上限从哪来、网页版为什么看起来没有限制、长提示词的两笔账,以及广告图这类「要求很多」的场景该怎么组织提示词。 ## 客户的问题:为什么 API 限 32K,ChatGPT 不限 对话原样(已脱敏): > 客户:你们的 prompt 长度和 ChatGPT 不一样,好像限了 32K。 > 我们:是有限制的。谁会有 32K 输入提示词的出图场景? > 客户:肯定有呀,广告图,包装的尺寸……自己 call codex cli 算了。 > 我们:确认是 32000 个 tokens 的提示词吗? > 客户:是呀,32000 英文其实不长呀。 这几句话里混了三个概念,先拆开: | 概念 | 是什么 | 这个案例里的值 | | - | - | - | | **字符数** | 提示词字符串的长度 | Images API 上限 **32,000 字符** | | **token 数** | 模型实际计费与理解的单位 | 32,000 英文字符约 8K tokens;中文每字符消耗更多 token | | **上下文窗口** | 文本模型一次能装下的全部内容 | 文本模型动辄几十万到上百万 tokens,与图片模型的提示词上限是两回事 | 客户说「32000 英文其实不长」,说的是字符数,这个判断本身没错。但他拿来对比的 ChatGPT 网页版,走的根本不是同一条链路。 ## 32K 上限从哪来 OpenAI Images API 对 `prompt` 字段的官方上限(`/v1/images/generations` 与 `/v1/images/edits` 相同): | 模型 | prompt 上限 | 单位 | | - | - | - | | gpt-image 系列(含 `gpt-image-2.5-flare` / `sunburst` / `gpt-image-2`) | **32,000** | 字符 | | DALL·E 3 | 4,000 | 字符 | | DALL·E 2 | 1,000 | 字符 | 出处:OpenAI API 参考 `developers.openai.com/api/reference/resources/images/methods/generate`。 API易 官转链路不在这个上限之上再收紧。超过 32,000 字符由原厂返回 400,报错原文以原厂为准,本页未做逐字节的边界实测;要精确到哪一个字符开始报错,用你自己的 Key 试一次即可,被拒的请求不计费。 **字符不等于 token**。计费和模型理解都按 token 走,`usage.input_tokens_details.text_tokens` 是每次调用实际消耗的文本 token 数。同样 32,000 字符,英文约 8K tokens,中文因为每个字符对应的 token 更多,会明显高于这个数,装的信息量也更大。所以「32K 英文不长」和「32K 中文很长」可以同时成立。 ## 为什么 ChatGPT 网页版「没有限制」 和 [如何生成满意的图片](/api-capabilities/image-generation-success-tips)、[内容安全排查篇](/api-capabilities/image-safety-troubleshooting) 讲的是同一件事:**网页版是 Agent,API 是单次原子调用**。 * 在 ChatGPT 里贴一份很长的资料,读它的是对话模型,不是图片模型。对话模型读完后**自己写一条短的出图提示词**去调用图片工具,图片模型拿到的从来不是那份原始资料。粘贴超过 10,000 字符时网页版还会自动转成附件(OpenAI 帮助中心 `help.openai.com`),更说明那是给对话模型看的。 * API 是你直接对图片模型说话,中间没有人替你读资料、做取舍。上限是图片模型这一层的上限,网页版从来没有让图片模型直面这个上限。 * 客户最后说「自己 call codex cli 算了」,这个直觉是对的:让一个文本模型先读资料,再产出出图提示词,正是网页版在后台做的事。下文把它写成可以跑的两段式。 ## 长提示词的两笔账 先算钱,再算效果。 **钱**:gpt-image 系列的文本输入按 token 计费(`gpt-image-2.5` 为 \$5.00 / 百万 tokens,见 [概览定价表](/api-capabilities/gpt-image-2/overview))。一条 32,000 英文字符的提示词约 8K tokens,单次约 \$0.04;中文 token 更多,费用更高。这个数单看不大,量产要乘张数,而且每一次重试都会再付一遍。提示词里 90% 是原始资料而不是画面描述时,这笔钱大部分是白花的。 **效果**:更多细节不等于更高遵循度。几百条要求同时摆在图片模型面前时会互相竞争,真正重要的硬约束(Logo 不变形、包装文字准确、人物数量)反而容易被埋没。OpenAI 自己对出图提示词的建议是 1~3 句清晰描述起步,再补必要的构图、光线和硬性约束(`openai.com/academy/image-generation`)。图片模型需要的是**优先级明确的信息密度**,不是字数。 所以「专业广告要求多」是对的,但**细不等于长**。[出图进阶篇](/api-capabilities/image-advanced-workflow) 的六要素和 [提示词诊断技能](/api-capabilities/image-prompt-doctor) 里「不要堆形容词把提示词写长」讲的都是这一条。 ## 两段式:资料进文本模型,提示词进图片模型 真有 32K 的东西要交给出图,它多半是品牌手册、包装规格、广告 brief、角色设定库。这类资料应该先进文本模型,由它提炼成一条高密度提示词,再进图片模型: | 阶段 | 输入 | 模型 | 输出 | | - | - | - | - | | ① 提炼 | 品牌手册 / 包装规格 / 广告 brief,长度不限 | `gpt-5.6` 等文本模型 | 1K~3K 字符的结构化出图提示词 | | ② 出图 | 上一步的提示词(远在 32K 之内) | `gpt-image-2.5-sunburst` | 图片 | 提炼层的输出模板,在六要素之外加上广告图特有的三项: | 段落 | 要写什么 | | - | - | | **硬约束(放最前)** | Logo 不变形、包装上的文字逐字给出、人物数量、画面比例、背景色值 | | **目的与投放位** | 这是什么广告、放在哪里、要让消费者感受到什么 | | **主体与必须保留的元素** | 产品是什么、包装上哪些元素必须准确出现 | | **构图与留白** | 产品位置、人物位置、景别、给文案留的空白区域 | | **视觉** | 场景、色调、光线、材质、摄影风格 | | **禁止项** | 不要增加什么、不要改变什么 | 品牌手册、规格表、brief 不用预处理,文本模型的上下文足够大。system prompt 里写清输出模板、字符上限(建议 2,500 字符以内)和「硬约束放最前」。 检查 `len(prompt)`,超过 32,000 就让文本模型再压缩一轮。正常情况下提炼结果只有一两千字符,这一步是防御。 提炼结果存下来,出图只重放这条提示词。重试、换尺寸、换模型都不需要重新读资料,也不会再为资料付 token。 包装改版、brief 更新时重跑提炼,出图这一段的代码和参数不动。 最小实现(OpenAI SDK,两段共用一个 Key): ```python theme={null} import os from openai import OpenAI client = OpenAI( api_key=os.environ["APIYI_API_KEY"], base_url="https://api.apiyi.com/v1", ) DISTILL_SYSTEM = """你是广告图提示词工程师。读完用户给的全部资料,输出一条可以直接交给图片模型的中文提示词。 按这个顺序写,每段一行: 1 硬约束:Logo 不变形、包装文字逐字给出、人物数量、画面比例、背景色值 2 目的与投放位 3 主体与必须保留的包装元素 4 构图与留白:产品位置、人物位置、景别、文案留白区域 5 视觉:场景、色调、单一可指认的主光、材质、摄影风格 6 禁止项 规则: - 总长不超过 2500 字符,只输出提示词正文,不要解释、不要标题 - 资料里没有的信息不要编造,尤其是品牌名和包装文字 - 不要使用 8K、超高清、杰作、完美 这类空泛的质量词 - 资料明确规定的内容原样保留,不要改写""" PROMPT_LIMIT = 32000 # OpenAI Images API 的 prompt 上限,按字符计 def distill(brief: str, model: str = "gpt-5.6") -> str: resp = client.chat.completions.create( model=model, messages=[ {"role": "system", "content": DISTILL_SYSTEM}, {"role": "user", "content": brief}, ], ) prompt = resp.choices[0].message.content.strip() if len(prompt) > PROMPT_LIMIT: # 极少发生,发生了就再压一轮 prompt = distill(f"把下面这条提示词压缩到 2500 字符以内,保留全部硬约束:\n\n{prompt}", model) return prompt def generate(prompt: str, size: str = "1536x1024", quality: str = "high") -> bytes: import base64 resp = client.images.generate( model="gpt-image-2.5-sunburst", prompt=prompt, size=size, quality=quality, timeout=600, ) return base64.b64decode(resp.data[0].b64_json) if __name__ == "__main__": brief = open("brief.md", encoding="utf-8").read() # 品牌手册 + 包装规格 + 广告需求,多长都行 prompt = distill(brief) open("prompt.txt", "w", encoding="utf-8").write(prompt) # 落库,出图只重放这条 open("ad.png", "wb").write(generate(prompt)) ``` 提炼结果和出图参数一起存档,是这条产品线上**唯一可靠的复现方式**(GPT-Image 系列不暴露 seed),详见 [出图进阶篇](/api-capabilities/image-advanced-workflow) 第五节。 ## 什么时候真的需要长提示词 有几类场景提示词确实会长一些,但都远够不到 32K: * **多图编辑**:用「图1 / 图2 / 图3」逐一指代参考图,说明各取什么,几百字。 * **画面内文字**:招牌、海报、包装上的文字要逐字给出,别让模型编,几十到几百字。 * **系列图的固定前缀**:同一批图共用的风格、光线、构图段落,一千字以内。 这些加起来通常也就两三千字符。提示词真的逼近 32K,先怀疑是不是把资料塞进去了。 ## 速查总结 * **32,000 字符是 OpenAI Images API 的官方上限**,按字符不按 token,`/generations` 与 `/edits` 相同,API易 官转不额外收紧。 * **字符、token、上下文窗口是三件事**:32K 英文约 8K tokens,中文更多;文本模型的上下文窗口和图片模型的提示词上限无关。 * **网页版没有限制是错觉**:对话模型先读资料、再自己写短提示词调图片工具,图片模型从没直面那 32K。 * **细不等于长**:文本输入按 token 计费且每次重试再付一遍;几百条要求互相竞争,硬约束反而被埋。 * **两段式**:资料进文本模型提炼成 1K~3K 字符的结构化提示词,硬约束放最前,落库后只重放提示词出图。 ## 相关文档 * [如何生成满意的图片](/api-capabilities/image-generation-success-tips) * [内容安全排查篇](/api-capabilities/image-safety-troubleshooting) * [出图进阶:工作流编排与去 AI 味](/api-capabilities/image-advanced-workflow) * [出图提示词诊断技能](/api-capabilities/image-prompt-doctor) * [文生图 API 参考](/api-capabilities/gpt-image-2/text-to-image) # 出图提示词诊断技能 Source: https://docs.apiyi.com/api-capabilities/image-prompt-doctor 把出图前的提示词审阅封装成开箱即用的 Agent Skill,丢进 Codex、OpenClaw、hermes-agent、Claude Code 等任意编码 Agent:按六要素体检、揪出拉低质量的空泛词、输出可直接使用的优化提示词,也能拿实际出图反向复诊。默认用 gpt-5.6-luna。 本页提供一个**开箱即用的 Agent 技能(Skill)**:出图**之前**先把提示词过一遍体检,补齐缺失要素、删掉会拉低质量的空泛词,再拿优化后的版本去出图。整套东西就两个文件,**零第三方依赖**。 出图不满意,八成问题出在提示词,而不是模型或通道。这个技能把 [出图进阶篇](/api-capabilities/image-advanced-workflow) 里讲的「改写层」做成了可以丢进任意编码 Agent 的技能。 ## 这个技能能做什么 按主体 / 环境 / 光线 / 镜头 / 色调 / 构图六要素逐项判定,给 0-100 分,列出风险项,输出一条可直接复制使用的优化提示词。 把实际出图连同原提示词一起传进去,模型看图对照,指出**提示词里哪一句没有被执行**、模型自作主张加了什么,再针对性重写。 指定 `-t` 之后会追加该系列特有的提醒:参考图上限、蒙版支持情况、分辨率参数叫什么名字。 人像查肤质与光位,产品图查背景与文字禁令,插画则自动降低写实系检查的权重。 ## 实测:一条口语提示词的完整诊断 输入是用户会随手写的原话,外加它实际出的图: ```bash theme={null} python3 scripts/prompt_doctor.py "帮我出一张咖啡的产品图,好看一点,要高级感" \ -i result.jpg -t nano-banana -s product ``` 口语提示词生成的咖啡图:木桌、磨豆机、麻布袋等大量道具,杯身被模型编造了品牌名 诊断输出(节选真实运行结果): ```text theme={null} 【诊断】18/100 —— 主体可辨,但缺少产品摄影约束,画面被模型扩展成生活方式场景 六要素:主体⚠️ 环境❌ 光线❌ 镜头❌ 色调⚠️ 构图❌ 风险项: · 提示词中的"咖啡的产品图"没有具体说明产品形态、材质、颜色和数量,模型自行生成了带拉花的咖啡杯。 · 提示词没有指定中性、可控的背景,模型自行加入了窗户、木桌、咖啡磨豆机、咖啡豆、布袋和书本, 导致画面更像生活方式摄影而非电商产品图。 · 提示词没有写主光方向、光线软硬或补光,成片采用了左侧窗光和较重的暖色阴影,光线不可控。 · "好看一点"和"高级感"属于抽象质量描述,没有转化为具体的色温、饱和度和摄影介质。 · 提示词没有声明禁止文字,模型自行生成了咖啡杯上的"AURA COFFEE ROASTERS"、磨豆机标牌 以及右下角书本上的"AURA"字样,商用时存在错误品牌和乱码风险。 【优化后的提示词】 制作一张电商产品摄影风格的咖啡产品图:画面主体是一只单独的哑光暖白色陶瓷咖啡杯…… (完整正文略) 【参数建议】size=2K aspect=1:1;电商方图适合1:1;若需要横版广告,可改用4:3并保留右侧文案留白。 ``` 拿这条优化后的提示词原样重出一次,同一个模型(`gemini-3-pro-image`): 优化提示词重出的咖啡图:中性浅灰背景上一只暖白陶瓷拿铁杯,光线方向明确,投影落向右后方,画面无任何文字,四周留白充足 道具全部清掉、背景变成可控的中性灰、投影方向明确、没有任何编造的品牌名,留白也够放文案了。**模型没变,变的只是提示词。** ## 什么时候该跑诊断 不是每次出图都要审一遍。按需求本身的具体程度决定: | 你的需求长什么样 | 该怎么做 | | - | - | | 口语化、只有主体(「来张咖啡的产品图,好看点」) | **先诊断再出图**,收益最大 | | 出了图但和预期差很远 | **复诊模式**(`-i` 传实际出图),看图找原因 | | 已经写清了光位、焦段、构图 | 不必多此一举,直接出图 | | 要批量出同一系列的图 | 先诊断定稿一条,之后复用,不必每张都审 | 这个技能只改提示词,**不负责出图**。出图交给 [Nano Banana Pro 技能](/api-capabilities/nano-banana-image/skills) 或 [GPT-Image-2 系列技能](/api-capabilities/gpt-image-2/skills),两者串起来就是完整的「先审后出」流程。 ## 能用在哪些 Agent 一个 Skill 本质上就是**一个文件夹**:一份写给 Agent 看的说明(`SKILL.md`)+ 一个干活的脚本。所以**凡是能读取本地文件、执行命令行的编码 Agent 都能用上**——比如 **Codex、OpenClaw、hermes-agent、Claude Code** 等。 唯一要求:跑 Agent 的那台机器装了 **Python 3** 并且**能联网**(脚本要直连 `api.apiyi.com`)。本技能**只用 Python 标准库,不需要 pip 装任何包**。 ## 三步装好 ### ① 建目录、贴文件 新建一个技能文件夹,放入下面两个文件(完整内容见后两节): ``` image-prompt-doctor/ ├── SKILL.md ├── scripts/ │ └── prompt_doctor.py └── .env # 第②步创建,放你的 Key ``` 不需要 `pip install` 任何东西。 ### ② 同目录写 Key 在 `image-prompt-doctor/.env` 里写上你的 **API易 API Key**(在 `api.apiyi.com` 控制台创建): ```bash theme={null} APIYI_API_KEY=sk-your-api-key ``` 脚本会自动从这个 `.env` 读取 Key,**无需任何额外配置或环境变量**。 `.env` 里是你的密钥。如果这个技能要随项目仓库共享,**务必把 `.env` 加进 `.gitignore`,不要提交到 git**。 ### ③ 交给 Agent * **支持技能自动发现的 Agent**(如 Claude Code):把整个 `image-prompt-doctor/` 目录放进它的技能目录——个人级 `~/.claude/skills/`,或项目级 `.claude/skills/`(随仓库共享)。 * **其他 Agent**:按它各自的技能/插件约定放置;或者最简单——**直接让 Agent「读一下这个文件夹里的 SKILL.md,并照着执行」** 即可。 装好后就能用了,跳到 [怎么用](#怎么用) 看示例。 ## SKILL.md 新建 `image-prompt-doctor/SKILL.md`,完整内容如下(`description` 写清「做什么 + 何时用」,Agent 会据此自动触发): ````markdown theme={null} --- name: image-prompt-doctor description: Diagnose and optimize an image-generation prompt before generating, or review a disappointing result against the prompt that produced it. Use this whenever the user is about to generate an image from a casual or vague prompt, asks why an image came out wrong, or asks to improve/rewrite an image prompt. allowed-tools: Bash(python3 *) --- # 出图提示词诊断 在真正出图**之前**先审一遍提示词,补齐缺失要素、删掉会拉低质量的空泛词,再拿优化后的版本去出图。 也可以在出图**之后**把实际结果回传,让模型对照提示词指出哪一条没被执行。 API 调用是单次原子调用,提示词原样进模型,没有网页版那种自动改写兜底——所以提示词质量直接决定成功率。 ## 什么时候用 - 用户给的出图需求是口语化的(「来张咖啡的产品图,好看点」),**先诊断再出图**; - 用户抱怨出图不对、和预期差很远,**用复诊模式看图找原因**; - 用户直接要求「帮我优化这个提示词」。 需求本身已经写得很具体(光位、焦段、构图都有)时不必多此一举,直接出图。 ## 两种运行方式 ### 方式一:调脚本(默认,用 gpt-5.6-luna) ```bash # 出图前诊断 python3 ${CLAUDE_SKILL_DIR}/scripts/prompt_doctor.py "帮我出一张咖啡的产品图,好一点" -t nano-banana -s product # 出图后复诊:把实际出图一起传进去 python3 ${CLAUDE_SKILL_DIR}/scripts/prompt_doctor.py "把红框里的杯子改成黑色,其他不变" -i result.png # 给程序消费的 JSON python3 ${CLAUDE_SKILL_DIR}/scripts/prompt_doctor.py "国风山水插画" -s illustration --json ``` 参数: - 第 1 个位置参数:待诊断的提示词(必填)。 - `-i / --image`:实际出图的路径,可重复,最多 4 张;**传了就进入复诊模式**。 - `-t / --target`:目标图片模型,`nano-banana` / `gpt-image` / `seedream` / `flux` / `grok`,会追加该系列特有的提醒(参考图上限、蒙版支持、参数名等)。不确定就不传。 - `-s / --scene`:题材,`portrait` / `product` / `scene` / `illustration`,默认 `auto`。插画类会自动降低写实系检查的权重。 - `--model`:诊断用的文本模型,默认 `gpt-5.6-luna`(便宜、支持图像输入)。也可用 `APIYI_TEXT_MODEL` 环境变量覆盖。 - `--json`:输出原始 JSON。 Key:脚本自动读技能目录下 `.env` 里的 `APIYI_API_KEY`,也支持同名环境变量。 报「未找到 Key」时提示用户在 `.env` 写一行 `APIYI_API_KEY=sk-xxx`。 ### 方式二:你自己来(没有 Key,或不想额外花钱时) 诊断量表本身没有秘密,你可以直接按下面这套标准自己审,不调任何 API。 输出格式与脚本保持一致,用户看到的东西一样。 ## 诊断量表 **六要素**,逐项判 ✅ 写清楚了 / ⚠️ 提到但含糊 / ❌ 完全没写: | 要素 | 判定标准 | |---|---| | 主体 | 材质、颜色、数量、状态是否具体 | | 环境 | 背景是什么、虚实关系 | | 光线 | 光源方向、软硬、有无补光——**必须存在一个可指认的主光** | | 镜头 | 焦段、光圈、机位高度、俯仰角 | | 色调 | 白平衡倾向、饱和度、胶片或数码质感 | | 构图 | 主体在画面什么位置、留白在哪 | **必须报出的风险项**: - **空泛质量词**(8K / 超高清 / 超精细 / 杰作 / 大师作品 / 完美):不提高分辨率,反而把画面推向过锐过饱和的渲染感,是「AI 味」的主要来源。建议删除,换成具体的光、镜头、介质。 - **在提示词里写分辨率**:无效。分辨率只由 `size` / `imageSize` 之类的参数决定。 - **一句话塞多个编辑动作**:单次成功率显著下降,建议拆成多轮,一次只改一类东西。 - **用「这个」「红框里的东西」指代**:编辑类任务最常见的失败原因,要点名具体物体。 - **没有声明画面内文字**:模型会自行编造品牌名和文案,商用等于废片。要么写清出现什么字,要么明确禁止出现文字。 - **真人、名人、受版权保护的角色、未成年人、暴力或成人内容**:会被上游审核拦截,要提前改写。 **优化原则**:补齐缺失要素,不要堆形容词把提示词写长;用户已明确指定的要素原样保留; 要写实质感就加具体光位、焦段光圈、介质和主动添加的瑕疵(毛孔、碎发、磨损、水渍), 不要用「真实感」「高级感」这类抽象词;不要输出独立的负面提示词字段,把「不要什么」直接写进正文。 ## 输出与后续动作 把诊断结果如实转述给用户——分数、缺了哪些要素、风险项、优化后的提示词、改了什么、参数建议。 然后**征求用户确认再出图**:优化版可能改变了原意(比如把「咖啡」定成了「拿铁」), 让用户看一眼再决定。用户认可后,用优化后的提示词调出图技能(如 `nano-banana-pro`), 并按「参数建议」里的 `size` / `aspect` 传参。 用户明确说「不用问了直接出」时,就用优化版直接出图,把诊断摘要和图一起给他。 ## 边界 - 这个技能只改提示词,**不出图**,也不做审核预判之外的合规判断。 - 优化后的提示词仍然可能一次不中——生成模型单次采样本身有波动,失败就重试或换模型。 - 分辨率、宽高比、参考图这些只能靠参数解决的东西,诊断只会提醒,不会写进提示词正文。 ```` ## scripts/prompt\_doctor.py 新建 `image-prompt-doctor/scripts/prompt_doctor.py`,完整内容如下(纯 Python 标准库,无需安装依赖): ````python theme={null} #!/usr/bin/env python3 """出图提示词诊断:审阅提示词、指出缺失要素、给出优化版本。 通过 API易 调用文本模型(默认 gpt-5.6-luna)。纯标准库,零依赖。 支持两种模式: 1) 出图前诊断 —— 只给提示词 2) 出图后复诊 —— 同时给提示词和实际出图(模型看图反推哪条要素没落实) """ import argparse import base64 import json import os import sys import urllib.error import urllib.request DEFAULT_MODEL = "gpt-5.6-luna" BASE_URL = "https://api.apiyi.com/v1/chat/completions" MAX_IMAGES = 4 # 各目标模型的额外提醒,只在 --target 指定时追加 TARGET_NOTES = { "nano-banana": "目标模型是 Nano Banana(Gemini 系):自然语言长句友好,可以写成连贯段落而非关键词堆砌;" "参考图最多 14 张;分辨率走 imageSize 参数(1K/2K/4K),宽高比走 aspectRatio。", "gpt-image": "目标模型是 GPT-Image 系:指令遵循强、画面内文字渲染准确,可以放心指定要出现的文字内容;" "参考图最多 16 张;只有官转支持蒙版局部重绘;background=transparent 透明背景只有官转 gpt-image-2.5-flare / sunburst 支持(gpt-image-2 已被官方关停);" "分辨率走 size 参数。", "seedream": "目标模型是 Seedream:中文语境理解好;参考图最多 10 张(输入+输出不超过 15);" "5.0 与 5.0-pro 可以在提示词里要求输出透明背景的 PNG。", "flux": "目标模型是 FLUX:偏好结构清晰的描述;FLUX.2 pro/max/flex 参考图最多 8 张,Kontext 只有 1 张。", "grok": "目标模型是 Grok Imagine:参考图只有走 /v1/images/edits 才生效," "传给 /v1/images/generations 会被静默丢弃且照常计费;参考图最多 4 张。", } SCENE_NOTES = { "portrait": "这是人像。重点检查:是否指定了唯一主光的方向与软硬、焦段与光圈、是否要求了自然肤质" "(毛孔、绒毛、油光)、是否禁用了磨皮美颜、主体是否被挪出画面正中。", "product": "这是产品图/电商图。重点检查:背景是否被指定为中性可控、光位与补光是否写清、" "投影方向、是否明确禁止出现品牌名和文字(否则模型会自行编造)、是否留出放文案的空白。", "scene": "这是环境场景。重点检查:具体时间与天气、唯一主光来源、机位高度与焦段、" "是否加入了磨损与杂物等真实痕迹、画面里的人是否被要求不正对镜头。", "illustration": "这是插画/非写实。去 AI 味的那套写实手法要降权,改为检查:画风是否被具体指认" "(媒材、笔触、年代、参考流派)、配色方案、线条与上色方式、构图与留白。", } SYSTEM = """你是出图提示词诊断专家,服务于通过 API 直接调用图片模型的开发者和设计师。 API 调用是单次原子调用,提示词原样进模型,没有任何网页版那样的自动改写兜底,所以提示词质量直接决定成功率。 ## 诊断量表 先按「六要素」逐项判定 ok(写清楚了)/ weak(提到但含糊)/ missing(完全没写): 1 subject 主体:材质、颜色、数量、状态是否具体 2 environment 环境:背景是什么、虚实关系 3 light 光线:光源方向、软硬、有无补光——必须存在一个可指认的主光 4 lens 镜头与视角:焦段、光圈、机位高度、俯仰角 5 tone 色调与介质:白平衡倾向、饱和度、胶片或数码质感 6 composition 构图:主体在画面什么位置、留白在哪 ## 必须报出的风险项 - 出现 8K / 超高清 / 超精细 / 杰作 / 大师作品 / 完美 这类空泛质量词:它们不提高分辨率, 反而把画面推向过锐过饱和的渲染感,是「AI 味」的主要来源,必须建议删除并换成具体的光、镜头、介质描述。 - 在提示词里写分辨率(4K/8K/高清):无效。分辨率只由 size / imageSize 之类的参数决定。 - 一句话里塞了多个互不相关的编辑动作:单次成功率会显著下降,建议拆成多轮。 - 用「这个」「红框里的东西」等指代而不点名具体物体:编辑类任务最常见的失败原因。 - 没有声明画面内文字:模型可能自行编造品牌名或文案,商用场景等于废片。要么写清要出现什么字,要么明确禁止出现文字。 - 涉及真人、名人、受版权保护的角色、未成年人、暴力或成人内容:会被上游审核拦截,需要提示改写。 ## 优化原则 - 补齐缺失要素,不要靠堆砌形容词把提示词写长。 - 用户已经明确指定过的要素原样保留,不要擅自改写。 - 要写实质感就加具体的光位、焦段光圈、胶片或数码介质、以及主动添加的瑕疵(毛孔、碎发、磨损、水渍), 不要用「真实感」「高级感」这类抽象词。 - 不要输出负面提示词语法(多数图片模型不支持独立的 negative prompt 字段),把「不要什么」直接写进正文。 - optimized_prompt 与 changes 必须与用户原提示词使用同一种语言,中文提示词就全中文,不要中英混写。 ## 输出 严格输出以下 JSON,不要加代码围栏,不要额外解释: { "score": 0-100 的整数,表示这条提示词单次出图的可用程度, "verdict": "一句话总评,不超过 40 字", "elements": {"subject":"ok|weak|missing","environment":"...","light":"...","lens":"...","tone":"...","composition":"..."}, "risks": ["每条一句话,说明问题和后果;没有风险则给空数组"], "optimized_prompt": "优化后的完整提示词正文,可直接复制使用", "changes": ["逐条说明改了什么、为什么"], "suggested_params": {"size":"1K|2K|4K","aspect":"如 1:1 / 16:9","note":"参数上的建议,没有则空字符串"} }""" REVIEW_EXTRA = """ ## 本次是出图后复诊 用户已经用下面这条提示词出了图,实际结果附在后面。请对照提示词逐条核对: 哪些要求落实了、哪些没落实、模型自作主张加了什么。 risks 里要明确写出「提示词的哪一句没有被执行」,optimized_prompt 要针对这些偏差重写, 而不是泛泛地补要素。""" def load_api_key(): """优先读环境变量;否则在脚本所在目录及其父目录找 .env。""" key = os.environ.get("APIYI_API_KEY") if key: return key here = os.path.dirname(os.path.abspath(__file__)) for d in (here, os.path.dirname(here)): env_path = os.path.join(d, ".env") if os.path.exists(env_path): with open(env_path, encoding="utf-8") as f: for line in f: line = line.strip() if line.startswith("APIYI_API_KEY") and "=" in line: return line.split("=", 1)[1].strip().strip('"').strip("'") return None def image_data_url(path): mime = "image/png" if path.lower().endswith(".png") else "image/jpeg" with open(path, "rb") as f: return f"data:{mime};base64," + base64.b64encode(f.read()).decode() def build_messages(prompt, images, target, scene): system = SYSTEM if images: system += REVIEW_EXTRA extras = [TARGET_NOTES[target]] if target else [] if scene and scene != "auto": extras.append(SCENE_NOTES[scene]) if extras: system += "\n\n## 本次的额外约束\n\n" + "\n".join("- " + e for e in extras) content = [{"type": "text", "text": "待诊断的提示词:\n\n" + prompt}] for path in images: content.append({"type": "image_url", "image_url": {"url": image_data_url(path)}}) return [{"role": "system", "content": system}, {"role": "user", "content": content}] def diagnose(api_key, model, messages): payload = json.dumps({ "model": model, "messages": messages, "response_format": {"type": "json_object"}, }).encode() req = urllib.request.Request( BASE_URL, data=payload, method="POST", headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}, ) try: with urllib.request.urlopen(req, timeout=180) as r: resp = json.loads(r.read()) except urllib.error.HTTPError as e: raise RuntimeError(f"请求失败 HTTP {e.code}:{e.read().decode(errors='replace')[:500]}") text = resp["choices"][0]["message"]["content"].strip() if text.startswith("```"): # 防御:个别模型仍会套代码围栏 text = text.split("\n", 1)[1].rsplit("```", 1)[0] try: return json.loads(text) except json.JSONDecodeError: raise RuntimeError("模型未返回合法 JSON,原始输出:\n" + text[:800]) MARK = {"ok": "✅", "weak": "⚠️", "missing": "❌"} LABEL = {"subject": "主体", "environment": "环境", "light": "光线", "lens": "镜头", "tone": "色调", "composition": "构图"} def render(r): out = [f"【诊断】{r.get('score', '?')}/100 —— {r.get('verdict', '')}", ""] els = r.get("elements", {}) out.append("六要素:" + " ".join( f"{LABEL.get(k, k)}{MARK.get(v, '?')}" for k, v in els.items())) risks = r.get("risks") or [] if risks: out += ["", "风险项:"] + [f" · {x}" for x in risks] else: out += ["", "风险项:无"] out += ["", "【优化后的提示词】", "", r.get("optimized_prompt", "")] changes = r.get("changes") or [] if changes: out += ["", "【改了什么】"] + [f" - {x}" for x in changes] p = r.get("suggested_params") or {} bits = [] if p.get("size"): bits.append(f"size={p['size']}") if p.get("aspect"): bits.append(f"aspect={p['aspect']}") line = " ".join(bits) if p.get("note"): line = (line + ";" if line else "") + p["note"] if line: out += ["", "【参数建议】" + line] return "\n".join(out) def main(): parser = argparse.ArgumentParser(description="出图提示词诊断与优化") parser.add_argument("prompt", help="待诊断的提示词") parser.add_argument("-i", "--image", action="append", default=[], help=f"实际出图的路径,可重复(最多 {MAX_IMAGES} 张);传入即进入出图后复诊模式") parser.add_argument("-t", "--target", choices=sorted(TARGET_NOTES), help="目标图片模型,用于追加该系列特有的提醒") parser.add_argument("-s", "--scene", choices=["auto"] + sorted(SCENE_NOTES), default="auto", help="题材,默认 auto(不追加题材专项检查)") parser.add_argument("--model", default=os.environ.get("APIYI_TEXT_MODEL", DEFAULT_MODEL), help=f"诊断用的文本模型,默认 {DEFAULT_MODEL}") parser.add_argument("--json", action="store_true", help="输出原始 JSON,便于程序消费") args = parser.parse_args() api_key = load_api_key() if not api_key: sys.exit("未找到 API Key:请在技能目录的 .env 写一行 APIYI_API_KEY=sk-xxx,或设置同名环境变量") if len(args.image) > MAX_IMAGES: sys.exit(f"最多 {MAX_IMAGES} 张出图,收到 {len(args.image)} 张") for path in args.image: if not os.path.exists(path): sys.exit(f"图片不存在:{path}") messages = build_messages(args.prompt, args.image, args.target, args.scene) try: result = diagnose(api_key, args.model, messages) except RuntimeError as e: sys.exit(str(e)) print(json.dumps(result, ensure_ascii=False, indent=2) if args.json else render(result)) if __name__ == "__main__": main() ```` ## 怎么换诊断模型 默认用 `gpt-5.6-luna`——便宜(输入 \$0.2 / 输出 \$1.2 每百万 tokens)、支持图像输入,复诊模式要看图,正好够用。想换成别的模型有两种方式: ```bash theme={null} # 单次覆盖 ... prompt_doctor.py "提示词" --model gemini-3.5-flash # 改默认值:在 image-prompt-doctor/.env 里加一行 APIYI_TEXT_MODEL=gemini-3.5-flash ``` 换模型时注意两点:**复诊模式必须选支持图像输入的模型**(纯文本模型传图会报错),可选清单见 [视觉理解](/api-capabilities/vision-understanding);另外脚本用了 `response_format: {"type": "json_object"}`,不支持该参数的模型可能返回带代码围栏的文本(脚本已做剥离兜底,但仍以支持 JSON 模式的模型为准)。 ## 为什么一句话就会自动诊断 很多人好奇:我又没敲命令,怎么说句「帮我画张图」它就先去审提示词了? 原理是这样:Agent 启动时会**先读取每个技能 `SKILL.md` 里的 `description`**(一段很短的元数据,说明「这个技能做什么、什么时候该用」)。当你说出的需求**匹配上**这段描述的场景(比如「画一张……」「这图为什么不对」「优化下提示词」),Agent 就**自动决定调用这个技能**,去读完整的 `SKILL.md` 并运行脚本——整个过程你不用记任何命令。 SKILL.md 里还写明了「需求已经很具体时不必多此一举」,所以它不会对每条提示词都动手。想要**百分百可控**时,用下面的**显性调用**。 ## 怎么用 ### 自然语言(隐式触发) 装好后直接对 Agent 说话即可: | 你说 | 技能行为 | | - | - | | "帮我画张咖啡的产品图,好看一点" | 需求口语化 → 先诊断,报结果,确认后再出图 | | "这张图为什么不对?"(附上图) | 走复诊模式 `-i`,看图指出哪句没被执行 | | "优化一下这个提示词" | 只诊断不出图 | | "用 gpt-image-2 出图,帮我先审下词" | 带 `-t gpt-image`,追加该系列特有提醒 | | "不用诊断,直接出图" | 跳过本技能,直接调出图技能 | ### 显性调用(更可控) * **支持斜杠命令的 Agent**(如 Claude Code): ```text theme={null} /image-prompt-doctor 一位女性在咖啡馆窗边微笑 -s portrait ``` * **任意 Agent / 直接命令它跑脚本**(最通用): ```text theme={null} 运行 python3 image-prompt-doctor/scripts/prompt_doctor.py "一位女性在咖啡馆窗边微笑" -s portrait ``` ## 诊断结果在哪里 * 这个技能**不产出文件**,结果直接打印到终端,Agent 会把它转述给你——分数、六要素标记、风险项、优化后的提示词、改了什么、参数建议。 * 需要把结果接进自己的程序时加 `--json`,输出是一个结构化对象(`score` / `elements` / `risks` / `optimized_prompt` / `changes` / `suggested_params`),重定向落盘即可: ```bash theme={null} python3 image-prompt-doctor/scripts/prompt_doctor.py "提示词" --json > diagnosis.json ``` * **优化后的提示词需要你确认再用**:改写可能顺带改变原意(比如把「咖啡」定成了「拿铁」),SKILL.md 里已经要求 Agent 先问一句再出图。 * 复诊模式传的图**不会被修改或覆盖**,只作为只读输入。 ## 成本 一次诊断的开销是几千 tokens 级别,按 `gpt-5.6-luna` 的标价折算不到一分钱,而一次 `high` 画质的出图是它的几十倍以上。**先诊断再出图,省下的重试费用远超诊断本身。** 复诊模式要传图,图片按输入 token 计费,成本略高但仍远低于一次出图。 ## 相关文档 * [出图进阶:工作流编排与去 AI 味](/api-capabilities/image-advanced-workflow)(这个技能在整条流水线里的位置) * [如何生成满意的图片](/api-capabilities/image-generation-success-tips)(单次调用失败怎么救) * [Nano Banana Pro Agent 技能](/api-capabilities/nano-banana-image/skills)(配套出图技能,诊断完直接接上) * [GPT-Image-2 系列 Agent 技能](/api-capabilities/gpt-image-2/skills)(同上,GPT 线) * [GPT-5.6 Luna](/models/gpt-5-6-luna)(诊断默认使用的模型:规格、定价与端点支持) # 内容安全拦截排查:用分词消融法找到触发词 Source: https://docs.apiyi.com/api-capabilities/image-safety-troubleshooting 一个真实案例:同一段提示词网页版能出图、API 报 safety_violations=[sexual]。讲清拦截发生在哪一层、为什么 moderation: low 无效、网页版为何能过,以及用分词消融法 20 次实测定位触发词并给出零改意的改法。 被内容安全拦下,**不等于提示词写了违规内容**。更常见的情况是:提示词里某一个词把「成图」推过了审核线,而你自己读提示词根本看不出问题。本文用一个真实客户案例,讲清拦截发生在哪一层、网页版为什么能过、`moderation: low` 为什么没用,以及一套 20 次调用就能定位触发词的方法。 ## 案例复盘:网页版能出、API 报 sexual 客户用 `gpt-image-2.5-sunburst` 生成一张角色设定图(左侧脸部特写、右侧正面 / 侧面 / 背面三视图)。请求体: ```json theme={null} { "model": "gpt-image-2.5-sunburst-2026-09-08", "prompt": "帮我生成一个妈感美艳女主(外国人),皮肤通透有自然光泽,无过度磨皮;皮肤保留自然原生纹理、皮肤通透有自然光泽;整体画面自然真实,8K超高清,细节丰富,真实人像摄影质感,纯白背景#一张综合角色设定图,左侧为脸部大特写,右侧为全身标准三视图,露出完整的头部,脸部特写和全身三视图要在一张图。全身三视图:右侧依次排列全身的正面视角、90 度纯侧面视角(头部完全侧转)、背面视角。", "size": "2736x1536", "quality": "xhigh", "background": "auto", "n": 1 } ``` API 稳定返回 400: ```json theme={null} { "status_code": 400, "error": { "message": "Your request was rejected by the safety system. If you believe this is an error, contact us at ***.***.com and include the request ID req_e97b3571a1e2433da4b154fe7ea7a82c. safety_violations=[sexual]." } } ``` 客户的反馈很有代表性: > 我理解是内容安全,但我在 ChatGPT 网页版又能成功,你得告诉我,为啥网页版能成。 提示词里没有任何露骨内容,只是「美艳女主 + 三视图 + 纯白背景」。这类「我没写什么却被拦」的工单,排查思路和「我写了什么被拦」完全不同。 ## 先分清拦截发生在哪一层 出图模型的内容安全有两道关,触发的层不同,排查方法也不同: | | 提示词层拦截 | 图像输出层拦截 | | - | - | - | | 审核对象 | 你发的文本 | 生成出来的那张图 | | 响应耗时 | **几秒内**返回 400 | **与成功请求相同**(图先生成,再被分类器拦下) | | 同一提示词多次调用 | 结果稳定:每次都拒 | **有随机性**:每次成图不同,时过时不过 | | 改法 | 删掉明显违规词 | 找到把成图「推过线」的那个词 | **判据就看耗时**。本案例失败请求耗时 42 到 51 秒,成功请求 46 到 57 秒,两者没有区别,说明图已经生成完了,是输出侧分类器判了 sexual。后面的消融实验也印证了这一点:同一句提示词跑 3 次,出现过 1 过 2 拒。 提示词层拦截和参数错误一样几乎秒回。如果你的 400 等了半分钟以上才来,先按「输出层拦截」来查,别急着删提示词里的词。 ## 为什么网页版能成 这和 [如何生成满意的图片](/api-capabilities/image-generation-success-tips) 里讲的是同一件事:**网页版是 Agent,API 是单次原子调用**。 * ChatGPT 网页版不会把你这段话原样喂给出图模型。中间的对话模型会先**改写、扩充**提示词,通常会自己补上服装、场景、光线等细节。等于替你做了下文「加服装」那一步。 * API 是原文直达。你没写穿什么,模型就自由发挥;在「美艳 + 全身三视图 + 纯白背景」这个组合下,它倾向画成贴身或少衣的角色设定图,成图就容易越线。 * 再叠加输出层审核本身的随机性,网页版跑一次成功、API 跑一次失败并不矛盾。 所以「网页版能过」不能推出「API 审核更严」,两条链路送进模型的提示词根本不是同一段。 ## 分词消融法:一次只动一个词 提示词里有十几个形容词和构图约束,靠猜哪个词有问题效率很低。有效的办法是**消融**:每次只删或替换一个词块,其余一字不动,看哪一次结果翻转。 分三类:人物形容词(美艳、性感、妩媚……)、身体相关动词(露出、展示、贴身……)、构图约束(全身、三视图、纯白背景……)。构图约束单独看没问题,但会和形容词组合出效果。 删掉或换成中性词,其余原样。同时也跑一组「不动词、只加约束」的变体,比如给人物加一句明确的服装描述。 输出层拦截有随机性,单次通过不算数。命中的变体至少再跑 2 次,全过才算找到。 目标不是「能过」,而是「改意最小地能过」。优先选不动原词、只加约束的方案。 变体之间互不依赖,**并行跑**。本案例 6 个变体一批发出,50 秒左右全部返回;串行等要五分钟。 本案例的完整消融记录(共 20 次调用,同尺寸同 quality): | 改法 | 结果 | | - | - | | 原句(跑 2 次) | 400 sexual ×2 | | 原句 + `moderation: "low"` | 400 sexual | | 只把「露出完整的头部」改成「头部完整入画」 | 400 sexual | | 只删「妈感」,保留「美艳」 | 400 sexual | | 只删「美艳」,保留「妈感」 | 200 | | 「妈感美艳女主」改成「成熟优雅的欧美女性角色」(跑 3 次) | 200 ×3 | | 改成「成熟优雅、面容精致的欧美女性角色」(跑 3 次) | 400 / 200 / 400 | | **原句一字不动,只加「身穿米色高领针织衫和深色长裤」(跑 3 次)** | **200 ×3** | 从表里能直接读出三件事:触发词是「美艳」(删「妈感」没用、删「美艳」就过);「露出」不是原因;「面容精致」那组 1 过 2 拒,证明审核对象是每次都不同的成图而不是文本。 ## 结论与推荐改法 **触发机制**:「美艳」让模型把人物往性感方向渲染,「全身三视图 + 纯白背景」又是典型的设定图构图,两者叠加,成图容易被输出侧分类器判为 sexual。提示词本身没有一个字违规。 **推荐改法**:保留全部原文,只在人物描述后加一句明确的服装。实测 3/3 通过,人物气质和构图都和原意一致: ```text theme={null} 帮我生成一个妈感美艳女主(外国人),身穿米色高领针织衫和深色长裤,皮肤通透有自然光泽,无过度磨皮;皮肤保留自然原生纹理、皮肤通透有自然光泽;整体画面自然真实,8K超高清,细节丰富,真实人像摄影质感,纯白背景#一张综合角色设定图,左侧为脸部大特写,右侧为全身标准三视图,露出完整的头部,脸部特写和全身三视图要在一张图。全身三视图:右侧依次排列全身的正面视角、90 度纯侧面视角(头部完全侧转)、背面视角。 ``` 欧美女性角色设定图,左侧脸部特写,右侧正面、侧面、背面三视图,人物着米色高领针织衫与深色长裤,纯白背景 服装换成任何得体的日常装都可以,关键是**要写**,不要留给模型自己决定。 **备选改法**:把「美艳」换成「成熟优雅」,同样 3/3 通过,但脸部气质会偏温和,离原意远一些。 ## moderation: low 为什么没用 `moderation` 参数(见 [文生图参数表](/api-capabilities/gpt-image-2/text-to-image))取值 `auto` / `low`,作用是降低**提示词侧**的审核强度。本案例传了 `low` 照样 400,因为拦截发生在输出层,这个参数管不到成图的 sexual 分类。 在网关之外无法区分「参数没透传」和「参数透传了但不生效」,但对结论没有影响:**遇到输出层拦截,改参数没用,得改提示词**。 ## 通用排查清单 秒回是提示词层,等了和正常出图差不多的时间是输出层。前者删明显违规词;后者用分词消融法找「推过线」的那个词,并接受同一提示词会有随机性。 人物类提示词只写气质、不写服装,是输出层拦截最常见的来源。加一句明确的服装描述,往往一步就解决,而且不动原意。 同时改三个词,过了也不知道是哪个起作用,下次换个提示词又要从头猜。每次只动一个词块,命中后回归 2 到 3 次。 按 token 计费的 gpt-image 系列触发审核返回 400 时不计费,口径见 [gpt-image-2 常见问题](/api-capabilities/gpt-image-2/overview#常见问题)「生成失败会扣费吗」。所以消融实验本身不花钱,只花时间。 ## 速查总结 * **看耗时判层级**:400 秒回是提示词层;等了和出图一样久才 400,是成图被输出层分类器拦下。 * **输出层拦截有随机性**:同一提示词时过时不过,单次结果不能下结论,要回归 2 到 3 次。 * **网页版能过不代表 API 更严**:网页版会先改写扩充提示词(补服装、场景),API 是原文直达。 * **分词消融法**:一次只动一个词块,并行跑变体,取改意最小的方案。本案例触发词是「美艳」。 * **人物提示词写清服装**:只加一句服装描述、其余不动,实测 3/3 通过;`moderation: low` 对输出层拦截无效。 ## 相关文档 * [如何生成满意的图片](/api-capabilities/image-generation-success-tips) * [出图提示词诊断技能](/api-capabilities/image-prompt-doctor) * [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices) * [gpt-image-2 常见问题](/api-capabilities/gpt-image-2/overview#常见问题) # 出图请求传完却不返回的兼容处理 Source: https://docs.apiyi.com/api-capabilities/image-tail-stall 图片其实已经完整传完,但连接迟迟不结束,客户端一直等到自己超时。说明成因、判别方法,并给出在客户端主动收尾的兼容代码 —— 是在现有调用上加一层保护,不是替换调用方式。 **一句话结论**:图片数据**是完整的**,能正常解码出图,卡住的只是 HTTP 传输的最后一个动作 ——「告诉客户端传完了」。所以正确的处理不是把超时调长、也不是重试,而是**在数据已经到齐时主动收尾,把图取出来用**。 ### 这是**兼容**,不是**替换** 下面给的代码是在你**现有调用逻辑之外加一层保护**,不是让你换一套接入方式: * **不需要**换端点、换模型、换 SDK,也不需要改请求参数; * 正常请求走的还是原来的路径,**行为完全不变** —— 这段兼容逻辑在正常请求下根本不会触发; * 只有当「数据已经到齐、但连接迟迟不结束」时,它才会介入,把已经拿到的图交给你。 换句话说:\*\*加上它,坏的情况能救回来;不加它,坏的情况只能等到超时报错。\*\*其余一切照旧。 ## 现象 调用原生出图接口(`POST /v1beta/models/{model}:generateContent`)时,可能遇到这样一组现象: * 后台调用日志显示请求**成功**、也**已经计费**; * 客户端却一直挂着,直到自己的读超时才报错; * 报错形如 `Read timed out`、`ETIMEDOUT`、`UND_ERR_BODY_TIMEOUT`。 体感就是「后台日志里 30 秒就好了,我这边 5 分钟都拿不到图」。 **同一套代码以前一直是好用的,现在会在收尾这一步卡住不放。** 这是近期新出现的场景,不是你的集成方式一直有问题 —— 所以你不需要怀疑自己的调用写法,只需要按下面的方式加一层兼容。 ### 它是成时间窗发作的 这一点很重要,直接决定你怎么复现、怎么判断: * **窗内**:连续多次调用**全部**卡住,不分先后; * **窗外**:几十次连续调用**一次都不出现**,完全正常。 所以它既不是「必现」,也不是「小概率偶发」。如果你测的时候刚好错开了窗口,会 100% 正常,很容易得出「已经好了」的错误结论。反过来,如果你正好撞进窗口,会觉得「全挂了」。**两种体感都是真的,别用其中一次的结果去下长期结论。** **流式请求**(`:streamGenerateContent`)和纯文本模型一般不受影响。本页针对的是**非流式出图**这一类响应体很大的请求 —— 一张 2K 图的 JSON 响应体在 13 MB 量级。 ## 成因 出图响应用 `Transfer-Encoding: chunked` 分块传输。按 HTTP/1.1 规范,服务端把最后一个数据块发完之后,还必须再发一个**终止块**(长度为 0 的块),用来告诉客户端「到此为止,传完了」。 问题就出在这一步:**数据块全部到齐了,终止块却没有发出来,连接也没有关闭。** 于是客户端手里握着一份**完整可用的 JSON**(图片能正常 base64 解码),但它无从知道这份数据已经收完了,只能继续等 —— 一直等到自己的读超时。 打个比方:**快递已经放到你门口了,但快递员忘了点「已送达」。** 你守在系统前等状态更新,东西其实就在门外。 链路在某些时间窗内没有给响应做这个收尾动作。**服务端侧的根治我们会继续推进**,本页给的是在此之前的**客户端兜底方案**。 这层兜底有它自己独立的价值,而且**不需要等服务端修好再回滚**:有结束信号时它永远不会被触发,服务端修复之后会自动静默,零开销、零维护负担。 三个关键判断,直接决定该怎么处理: 不是丢包、不是网络质量问题、更不是传到一半断了。已收到的字节能完整解析,图片可以正常使用。 卡住之后服务端**一个字节都不会再发**。实测持续等待 **330 秒**仍无任何变化,把超时调到几百秒只是白白拖长故障感知时间。 故障窗内多个落点**同时**出现、又**同时**恢复,所以换域名、换入口都绕不开,只能在客户端处理。 ## 判别方法 同时满足下面三条,基本可以确定就是这个场景: 这说明响应体的长度不是预先声明的,客户端只能靠终止块判断「传完了」。 把已收字节做一次 `json.loads`,能成功;并且里面的 `inlineData.data` 做 base64 解码后是一张完整可用的图片。 既没有收到终止块,连接也没有被关闭 —— 它就那样一直开着。 ### 与另外两种形态的区别 三种情况报错很像,但根因和处理方式完全不同,**不要混用同一套判据**: | 观测项 | 本页场景(无限期挂住) | 被掐断(`ECONNRESET`) | 收尾迟到后被断开 | | - | - | - | - | | 已收字节数 | **= 全量,JSON 可完整解析** | 通常不足全量 | = 全量 | | 连接终态 | **既无终止块、也无 FIN,一直开着** | 收到 TCP RST | FIN(优雅关闭) | | 失败时刻 | 无限期,等到客户端自己放弃(实测 330 秒仍无变化) | 不定 | last-byte 之后 **+300 秒** | | 该怎么办 | **把已拿到的图直接用掉**(本页) | 排查网络路径与客户端 | 同本页,数据也是完整的 | 如果你的报错是 `ECONNRESET`,那属于另一类问题,判别方式见[连接中断排查](/api-capabilities/image-connection-drops)。 ## 兼容改造:客户端主动收尾 思路很简单:**不要死等连接结束,而是在已收数据能被完整解析时就主动收尾。** ### 关键:必须留一个宽限期 不能一解析成功就立刻收尾。正常情况下,终止块往往就在下一个 TCP 分段里,只差几毫秒。如果解析成功就马上断开,会把「终止块晚到几毫秒」误判成「服务端没发」。 正确做法是:解析成功后**再等一小段时间**(建议 3\~5 秒)。这期间收到任何字节就按正常流程继续走;等不到才判定为卡住并主动收尾。 **这一步不是可选优化。** 省掉宽限期会让判定完全失真 —— **每一个正常请求都会被误判成故障**。我们在实测中第一版就踩了这个坑,一整批正常请求全被误报。 ### 收尾前要过的几道判定 按从便宜到昂贵的顺序排,**任何一条不满足就继续等,不要收尾**: | # | 判定 | 为什么 | | - | - | - | | 1 | 连接仍处于「接收中」,没有正常结束 | 已经正常结束的走原有成功路径即可 | | 2 | HTTP 状态码是 2xx | 非 2xx 交给你原有的错误处理 | | 3 | 响应头没有 `Content-Length` | 有明确长度就说明不是这个场景,交给客户端库自己结束 | | 4 | 已收数据达到一个最小体积 | 挡掉小体积的错误响应 | | 5 | 数据量相比上次尝试有变化 | 避免对十几 MB 的内容反复做无谓解析 | | 6 | 最后一个非空白字符是 `}` | 极便宜的预筛。图片的 base64 编码里不含 `}`,传输没到头几乎必定在这里被挡掉 | | 7 | 完整 JSON 解析成功 | 最终裁决 | 第 6、7 条合起来让**误判概率接近于零**:响应是单个 JSON 对象,数据没收全时解析必然失败。换句话说,**只有真的收全了才可能收尾**。 ### Python 实现 用后台线程做流式读取,主线程靠**队列超时**来实现宽限期: ```python theme={null} import json import time import base64 import queue import threading import requests MIN_BYTES = 1024 # 判定 4:比这还小不可能是一张图 def _pump(raw, q): """后台线程:只负责把收到的块塞进队列。""" try: for chunk in raw.stream(65536, decode_content=True): q.put(chunk) q.put(None) # 服务端正常收尾了 except Exception as exc: # 传输异常,交给主线程抛 q.put(exc) def _try_parse(buf): if len(buf) < MIN_BYTES: # 判定 4 return None if not buf.rstrip().endswith(b"}"): # 判定 6:极便宜的预筛 return None try: return json.loads(buf.decode("utf-8")) # 判定 7:最终裁决 except ValueError: return None def generate_image(url, headers, payload, ttfb_timeout=180, term_grace=5, body_timeout=60): """非流式出图,带主动收尾。 ttfb_timeout: 等首字节。这一段是上游在生成,慢不等于故障,要留够。 term_grace: 数据到齐后,再等多久结束信号。等不到就主动收尾。 body_timeout: 首字节之后,整个响应体最多再花多久。 """ resp = requests.post(url, headers=headers, json=payload, stream=True, timeout=(10, ttfb_timeout)) resp.raise_for_status() # 判定 2 if resp.headers.get("Content-Length"): # 判定 3 return resp.json() # 长度已声明,交给库自己结束 q = queue.Queue() threading.Thread(target=_pump, args=(resp.raw, q), daemon=True).start() buf = bytearray() deadline = time.monotonic() + body_timeout while True: try: item = q.get(timeout=term_grace) except queue.Empty: # 宽限期内没有任何新数据 obj = _try_parse(buf) if obj is not None: resp.close() # 数据已到齐,主动收尾 return obj if time.monotonic() > deadline: raise TimeoutError("响应不完整,且长时间没有新数据") continue # 还不完整,继续等 if item is None: # 正常路径:服务端自己收了尾 break if isinstance(item, Exception): raise item buf += item obj = _try_parse(buf) if obj is None: raise ValueError("响应不完整") return obj def extract_image(obj): for cand in obj.get("candidates", []): for part in cand.get("content", {}).get("parts", []): if "inlineData" in part: return base64.b64decode(part["inlineData"]["data"]) return None ``` **为什么要多起一个线程?** 因为 `requests` 的读超时**只有一个值**,它同时管着「等首字节」和「块间等待」这两段。而卡住时读循环会一直阻塞在下一次读上,宽限期根本没有机会跑 —— 直接写成 `for chunk in ...` 加计时的版本,在真正卡住时是**不会触发**的。 用后台线程读、主线程 `q.get(timeout=term_grace)`,才能把这两段超时真正拆开。这个坑我们自己踩过:一个超时值管两件事,会把「生成慢」和「不收尾」混成同一种失败,根本没法归因。 这个写法只在**宽限期到点时解析一次**,而不是每收到一块就试一次,天然满足上表第 5 条 —— 十几 MB 的内容不会被反复解析。 ### Node.js 实现 Node.js 这边不需要额外线程 —— `reader.read()` 本身就是 Promise,用 `Promise.race` 就能给「等下一块」加上宽限期上限: ```javascript theme={null} const TERM_GRACE_MS = 5000; // 数据到齐后再等多久结束信号 const TOTAL_TIMEOUT_MS = 180000; // 总超时:必须留够上游生成的时间 const MIN_BYTES = 1024; async function generateImage(url, headers, payload) { const resp = await fetch(url, { method: "POST", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify(payload), signal: AbortSignal.timeout(TOTAL_TIMEOUT_MS), }); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); // 判定 2 if (resp.headers.get("content-length")) return resp.json(); // 判定 3 const reader = resp.body.getReader(); const chunks = []; let total = 0; while (true) { // 用 Promise.race 给「等下一块数据」加一个宽限期上限 const next = reader.read(); const timer = new Promise((r) => setTimeout(() => r("GRACE"), TERM_GRACE_MS)); const winner = await Promise.race([next, timer]); if (winner === "GRACE") { const parsed = tryParse(chunks, total); if (parsed) { // 数据已完整,是服务端没收尾 reader.cancel().catch(() => {}); return parsed; } continue; // 数据还不完整,继续等 } const { done, value } = winner; if (done) break; // 正常路径:服务端收了尾 chunks.push(value); total += value.length; } const parsed = tryParse(chunks, total); if (!parsed) throw new Error("响应不完整"); return parsed; } function tryParse(chunks, total) { if (total < MIN_BYTES) return null; // 判定 4 const buf = new Uint8Array(total); let off = 0; for (const c of chunks) { buf.set(c, off); off += c.length; } const text = new TextDecoder().decode(buf).trimEnd(); if (!text.endsWith("}")) return null; // 判定 6 try { return JSON.parse(text); } catch { return null; } // 判定 7 } ``` 两段代码里都留意一下**正常路径**那一行注释:服务端正常收尾时,循环靠 `done` / 迭代结束自然退出,宽限期分支根本不会进。这就是「兼容而不是替换」的具体含义 —— 你原来的成功路径一个字节都没变。 ## 超时怎么设 最容易踩的坑是**用一个超时值管两件事**:「等上游把图生成出来」和「首字节之后两块数据之间的静默」。这两段的正常时长差着一个数量级,混成一个值,要么把生成慢误杀成故障,要么让真正卡住的请求白等好几分钟。 | 这一段 | 正常时长 | 建议值 | 为什么 | | - | - | - | - | | **等首字节**(上游在生成) | 2K 约 20\~30 秒,4K 更久 | **120\~180 秒** | 这段慢不等于故障,要留够,切短了会误杀正常请求 | | **首字节之后的块间静默** | 毫秒级 | **3\~5 秒**(宽限期) | 到这个量级还没动静,就该判定并主动收尾 | 两段分开设:首字节留足生成时间, 块间静默压到几秒,靠上面的主动收尾兜底。 故障几秒内就能感知,正常请求一个都不误伤。 用一个 300 秒的大超时兜一切「以防万一」。 卡住时服务端一个字节都不会再发, 等多久都一样,只是白白拖长故障感知时间。 如果你的产品里有 4K 这类更耗时的档位:**总超时可以更长**(模型确实要算那么久),但**块间静默的判定不该跟着变长** —— 这是两件事,不要一起放大。 Node.js 用户注意:undici(Node 18+ 内置 `fetch` 的底层)有**三个互相独立的超时**,SDK 的 `timeout` 选项管不到它们。配置写法见[连接中断排查](/api-capabilities/image-connection-drops)的「Node.js:三个超时互相独立」一节。 ## 重试与计费 判定为「服务端没有收尾」之后,按这个顺序处理: 数据是完整的,图片可以正常使用,**不需要重试**。这既是最省事的路径,也避免了重复计费。 如果已收字节真的解析不出完整 JSON(数据确实不完整),再重试。建议换一条新连接,并在重试之间留 2\~3 秒间隔。 该问题成时间窗发作,短时间内连续重试很可能仍然落在同一个窗口内。若连续 3 次都卡住,建议退避到 30 秒后再试。 **计费口径**:这类请求上游已经把图生成出来、也开始正常回传了,属于**交付已完成**,会**照常计费**。所以「客户端超时报错」不等于「没花钱」—— 这正是第一步「直接用已拿到的图」价值最大的地方:图已经付过费了,白白丢掉才是真的浪费。 完整的断连计费对照(哪些收费、哪些不收费)见[连接中断排查](/api-capabilities/image-connection-drops)的「计费影响」一节。 ## 工程落地建议 下面这些与语言、框架无关,是我们自己落地时总结的: * **落在网络层的统一入口,不要散在业务调用点。** 把它做成「发请求」这个动作的一部分。这样所有出图路径一次性覆盖,业务代码完全无感知,将来服务端修好了也只需要动一个地方。 * **你真正需要的是「增量读取」能力。** 关键前提是能在响应还没结束时就看到已收到的内容。绝大多数 HTTP 客户端都提供这个能力(流式读取、分块回调、进度事件),但**默认用法通常不是** —— 默认那个「直接拿完整响应体」恰恰就是会卡死的那条路。**这是改造的主要工作量所在。** * **计时以「最后一次收到数据」为准,不是请求开始时间。** 每收到一块数据就重置宽限期计时。这样既不会误伤慢速网络,也能准确捕捉「彻底不动了」的状态。 * **加一个开关。** 把这层行为放在一个可以随时关闭的开关后面。上线初期出现任何非预期情况,关掉即可回到原有行为,不用紧急发版。 * **加埋点。** 每次触发收尾都记一条(时间、数据量、等待时长)。它有三个用途:量化故障实际发生频率、验证这层逻辑确实在起作用、以及在服务端修复之后确认埋点归零 —— 这是判断「可以下线这层逻辑」的唯一客观依据。 * **顺带可以改善的体验。** 既然已经拿到了增量读取能力,就可以顺便把「正在接收数据 X.X MB」这类真实进度展示给用户。大响应体下载期间的等待,原本对用户是完全黑盒的。 ## 我们自己的落地情况 这套改造我们已经在自家的 AI 图片大师(`imagen.apiyi.com`)上完成并验证。用一个会复现该故障的模拟服务(完整发完数据后既不发结束信号、也不关连接)跑了一组对照: | 场景 | 结果 | | - | - | | 正常响应(有结束信号) | 走原有路径正常返回,**不触发收尾** —— 无误判 | | 数据到齐但无结束信号 | 宽限期后主动收尾,数据完整可用 | | 同上,但开关关闭 | 维持旧行为(一直等) —— 开关有效,可随时回退 | | 数据只收到一半就挂起 | **不会**误收尾,继续等待 | | 完整数据但响应头带 `Content-Length` | 被护栏拦住,不收尾 | 结论:**正常请求零影响,故障请求从「等到超时然后失败」变成「几秒内正常出图」。** ## 常见疑问 不会。收尾的前提是已收数据能解析成一份**完整的 JSON** —— 数据没收全时解析必然失败。再加上 3\~5 秒的宽限期,正常请求不会被误判。上面「我们自己的落地情况」那张表里,前两行就是这两种情况的对照。 不会。判定的是**整个响应体**的完整性,不是图片本身。JSON 解析通过就意味着图片数据是完整的 —— 半张图对应的是解析失败,那种情况不会触发收尾。 不算。它不替代服务端修复,只是把**已经产生、并且已经计费**的结果交付到用户手里,同时避免了盲目重试带来的重复扣费。埋点数据反过来还能帮助定位故障的发作规律。 不需要急着拆。有结束信号时这段逻辑永远不会被触发,零开销。可以等埋点连续归零一段时间之后再考虑清理。 ## 什么时候找客服 加了上面的兼容之后,如果仍然满足下面任一条,带材料找客服核查: * 已收字节**始终解析不出完整 JSON**(说明不是本页场景,是真的传输中断); * 加了主动收尾之后**仍然长时间拿不到任何响应头**(那是上游还没开始回传,属于生成慢或上游故障,不是收尾问题); * 卡住的比例**持续偏高**,不是集中在某个时间窗内,而是长时间稳定复现。 提工单时附上:`x-request-id`、调用时间(**带时区**,如 `2026-08-03 13:15 (UTC+8)`)、模型名与 `imageSize` 等关键参数、客户端异常原文,以及卡住时已收到的字节数。 ## 相关文档 `ECONNRESET`、SSL EOF、undici 三个超时与本地代理判别矩阵 同步调用、timeout 分档配置、base64 处理、断连计费口径 把同步调用包进任务队列,用重试与落库消化偶发异常 # 图像与视频生成模型 Source: https://docs.apiyi.com/api-capabilities/image-video-models 查看支持的图像生成和视频生成 AI 模型,包括定价和使用说明。 API易 支持多种图像和视频生成模型,本页面提供详细的模型信息、定价和使用说明。 查看文本和多模态模型请访问 [当下热门模型](/api-capabilities/model-info)。 ## 🎨 图像生成模型 | 模型名称 | 状态 | 特点 | 分辨率/规格 | 价格 | | - | - | - | - | - | | [**Nano Banana Pro**](/api-capabilities/nano-banana-image-edit) 🔥 | 热卖中,本站最强 | 知识理解、中文字支持好、精准编辑 | 1K/2K/4K,支持原比例改图 | \$0.09/张 | | [**Nano Banana 2**](/api-capabilities/nano-banana-2-image) 🔥 | 热卖中,快速 | 同 Pro,支持按量计费 | 0.5K-4K,新增 1:8 和 8:1(可制作长图) | \$0.055/张(按量 \$0.025-0.07) | | [**Nano Banana Lite**](/api-capabilities/nano-banana-lite-image/overview) 🆕 | 新上线,最快最省 | 谷歌最快最省、约 4s 出图、比 NB2 快约 2.7 倍、专注 1K | 1K,14 种宽高比 | \$0.025/张(按量约 \$0.018) | | [**gpt-image-2.5-flare**](/api-capabilities/gpt-image-2/overview) 🆕 | 2026-09 新上线,官转 | OpenAI GPT-Image 2.5 速度优先版:画质高于 gpt-image-2、时延最多低 50%,size/quality 精确控(新增 `xhigh` / `max`),参考图自动高保真、支持 mask 局部重绘 | 1K/2K/**4K**(任意合法尺寸) | 按 token 计费,与 gpt-image-2 同价,充值活动 85 折左右 | | [**gpt-image-2.5-sunburst**](/api-capabilities/gpt-image-2/overview) 🆕 | 2026-09 新上线,官转 | OpenAI GPT-Image 2.5 画质与编辑精度优先版:多轮编辑、主体保留更强,参数同 flare | 1K/2K/**4K**(任意合法尺寸) | 按 token 计费,与 gpt-image-2 同价 | | [**gpt-image-2**](/api-capabilities/gpt-image-2/overview) | 官转,上一代 | OpenAI 官转、size/quality 精确控(到 `high`)、参考图自动高保真、支持 mask 局部重绘 | 1K/2K/**4K**(任意合法尺寸) | 按 token 计费,默认原价,充值活动 85 折左右 | | [**gpt-image-2.5-all / 2-all**](/api-capabilities/gpt-image-2-all/overview) 🔥 | 热卖中,官逆 | GPT 官逆 ChatGPT 网页线,网页版已升级到 Images 2.5,两个名字同价同行为、新项目用 `gpt-image-2.5-all`;文字还原好、中文原生、约 30–60s 出图较快、尺寸写进 prompt | 1K–1.5K(prompt 控制) | \$0.025/张(按次) | | [**gpt-image-2.5-flare-vip / sunburst-vip**](/api-capabilities/gpt-image-2-vip/overview) 🆕 | 2026-09 新上线,官逆 | GPT-Image 2.5 的 Adobe 线逆向版:调用同 -vip、`size` 锁 30 档含 **4K**,另支持 `quality` 六档(含 `xhigh` / `max`)与透明背景;别名 `gpt-image-2.5-vip` | 1K/2K/**4K**(30 档统一价) | \$0.03/张(按次) | | [**gpt-image-2-vip**](/api-capabilities/gpt-image-2-vip/overview) | 官逆 | GPT 官逆 Adobe 线(Firefly)、调用同 -all、`size` 字段锁尺寸、30 档常见 size 含 **4K**、约 90–150s 出图 | 1K/2K/**4K**(30 档统一价) | \$0.03/张(按次,4K 不加价) | | [**Nano Banana**](/api-capabilities/nano-banana-image) | 正常可用 | 速度快、一致性佳、电商改图 | 多种尺寸 | \$0.02/张 | | [**Seedream 5.0 Pro**](/api-capabilities/seedream-image/overview) 🆕 | 新上线,专业版 | 全系最强画质与复杂指令遵循、交互式编辑(坐标/选框/箭头)、最多 10 张参考图融合、png 输出;约 2 分钟出图,常规场景仍建议 5.0 Lite | 1K/2K(总像素 ≤ 4.19M,无 3K/4K;不支持组图与流式) | \$0.12/次 | | [**Seedream 5.0 Flash**](/api-capabilities/seedream-image/overview) 🆕 | 2026-09 新上线,全系最便宜 | BytePlus 快速出图版,约 15–20 秒出图、中文排版准确,参考图不收费;不支持组图与流式,默认带水印(传 `watermark: false` 关闭) | 多种尺寸 | \$0.018/张 | | [**Seedream 5.0 Lite**](/api-capabilities/seedream-image) | 正常可用 | 价格优势、速度快、输出 URL | 2K/3K | \$0.035/张 | | [**Seedream 4.5**](/api-capabilities/seedream-image) | 正常可用 | 价格优势、速度快、输出 URL | 2K/4K | \$0.04/张 | | [**Seedream 4.0**](/api-capabilities/seedream-image) | 正常可用 | 价格优势、速度快、输出 URL | 2K | \$0.035/张 | | [**GPT Image 1.5**](/news/gpt-image-1-5-launch) 🔥 | 官方系列 | 精准编辑,速度提升 4 倍,文本渲染增强 | 低/中/高质量 | 按量计费 | | [**GPT Image 1**](/api-capabilities/gpt-image-1) | 官方系列 | 精准编辑 | 多种尺寸 | 按量计费 | | **GPT Image 1-Mini** | 官方系列 | 可替代 sora\_image | 多种尺寸 | 按量计费 | | [**Grok Imagine 2**](/api-capabilities/grok-imagine-image/overview) 🆕 | 新上线,🔒 需申请开通 | xAI 第二代图像模型,标准版 `grok-imagine-image` / 高质量版 `grok-imagine-image-quality`;5 种宽高比、单次最多 10 张、真参考图编辑;需开通 `Grok_imagine` 专属分组 | 1K/2K(同价) | \$0.02 / \$0.045 每张 | | [**flux-2-max**](/api-capabilities/flux/overview) 🆕 | 最新一代(FLUX.2) | FLUX.2 旗舰,高质量出图 | 多种尺寸 | \$0.07/次 | | [**flux-2-pro**](/api-capabilities/flux/overview) 🆕 | 最新一代(FLUX.2) | FLUX.2 专业版,均衡质价 | 多种尺寸 | \$0.03/次 | | [**flux-2-flex**](/api-capabilities/flux/overview) 🆕 | 最新一代(FLUX.2) | FLUX.2 灵活版,参数可调 | 多种尺寸 | \$0.06/次 | | [**flux-2-klein-4b / 9b**](/api-capabilities/flux/overview) | 最新一代(FLUX.2) | FLUX.2 轻量版,亚秒级出图,最多 4 张参考图 | 多种尺寸 | \$0.01/次 | | [**Flux Kontext Pro**](/api-capabilities/flux-image-generation) | 正常可用 | 图像编辑 | 多种尺寸 | 详见文档 | | [**Flux Kontext Max**](/api-capabilities/flux-image-generation) | 正常可用 | 高质量图像编辑 | 多种尺寸 | 详见文档 | 模型价格与官网对齐,且可能随官网调整;上表仅供参考,具体以顶部导航「模型价格」栏目为准:[模型价格](/models/index)。 **Nano Banana Pro**:1K-4K 所有分辨率统一价格 \$0.09/张,官方 4K 价格为 \$0.24/张,约官网 38%!企业高可用分组 `NanoBananaEnterprise`(1.4x)可作为兜底。[查看详情](/news/nano-banana-pro-launch) **图像生成测试工具** * 国内访问:image.apiyi.com * 全球访问:imagen.apiyi.com 详细文档: * [Nano Banana Pro 文档](/api-capabilities/nano-banana-image-edit) - 本站最强,知识理解+精准编辑 * [Nano Banana 2 文档](/api-capabilities/nano-banana-2-image) - 支持按量计费,新增超长比例 * [gpt-image-2.5-all / 2-all 文档](/api-capabilities/gpt-image-2-all/overview) - GPT 官逆 ChatGPT 网页线(已升级 Images 2.5),\$0.025/张、约 30–60s 出图较快 * [gpt-image-2-vip 文档](/api-capabilities/gpt-image-2-vip/overview) - GPT 官逆 Adobe 线(Firefly),\$0.03/张、30 档 size 含 4K、约 90–150s 出图 * [GPT-Image-2.5 / 2 文档](/api-capabilities/gpt-image-2/overview) - OpenAI 官转三款(2.5-flare / 2.5-sunburst / 2)、原生 4K、size/quality 精确控 * [⚖️ 官转 vs 官逆 对比](/api-capabilities/gpt-image-2/vs-gpt-image-2-all) - gpt-image-2 与官逆姐妹模型 -all / -vip 选型对照 * [Nano Banana 文档](/api-capabilities/nano-banana-image) - 速度快、一致性佳 * [Nano Banana Lite 文档](/api-capabilities/nano-banana-lite-image/overview) - 最快最省,约 4s 出图,按次 \$0.025/张 * [Seedream 文档](/api-capabilities/seedream-image) - 价格优势、速度快;含 5.0 Flash(\$0.018/张、约 15–20 秒)与 5.0 Pro 专业版(\$0.12/次、约 2 分钟出图) * [Grok Imagine 2 文档](/api-capabilities/grok-imagine-image/overview) - xAI 第二代图像模型,\$0.02 / \$0.045 每张,需申请开通专属分组 * [GPT Image 1.5 文档](/news/gpt-image-1-5-launch) - 速度提升 4 倍,精准编辑 * [GPT Image 1 文档](/api-capabilities/gpt-image-1) - 官方图像生成 * [FLUX.2 文档](/api-capabilities/flux/overview) - klein / pro / flex / max 全系,文生图与多图融合 * [Flux Kontext 文档](/api-capabilities/flux-image-generation) - 图像编辑 ## 🎬 视频生成模型 | 模型名称 | 状态 | 特点 | 时长 | 价格 | | - | - | - | - | - | | [**Seedance 2.5 / 2.0 系列**](/api-capabilities/seedance2/overview) 🔥 | 已上线,热卖中 | 字节跳动,火山引擎国内版官方资源;**2.5** + 2.0 标准版 / 极速版 `fast` / 轻量版 `mini` 四档并行,文生/首尾帧/多模态参考(图+视频+音频)/视频编辑与延长,**默认输出同步音频**,私域素材库免费 | 2.5 为 4–30s,2.0 系为 4–15s(480p/720p/1080p,1080p 仅 2.5 与标准版) | 按 token 计费(面积×时长);720p/5s 实测 \$0.45(mini)/ \$0.73(fast)/ \$0.91(标准)/ \$1.35(2.5) | | [**Wan2.7 视频生成**](/api-capabilities/wan/overview) 🆕 | 新上线,推荐 | 阿里云万象,文生/图生/参考生/视频编辑,i2v 支持音频驱动 | 按秒(最长 12s) | \$0.084-0.14/秒(官网 98%) | | [**Wan2.6 视频生成**](/api-capabilities/wan/historical-versions) | 正常可用 | 与 Wan2.7 共用端点,含 `r2v-flash` 低延迟档 | 按秒 | 以控制台为准 | | [**VEO 3.1 官转(Official)**](/api-capabilities/veo-3-1-official/overview) 🔥 | 已上线,热卖中(官逆替代方案) | 透传 Google AI Studio 官方端点,声画同步、支持真人、默认分组即可调用 | 4/6/8s | 按次 \$0.3 / \$1.2(720p/1080p/4k 同价) | | **Veo 3.1 官逆** | ⏸️ 暂停(谷歌风控) | 谷歌 Flow 官逆;暂停期间请改用 **VEO 3.1 官转** | 固定 8s | 暂不可用 | | **Sora 2 官转** | ⏸️ 已下线 | OpenAI 官转、专业创作、稳定性高、支持 sora-2-pro,不支持「角色」引用 | 4/8/12s | 暂不可用 | | [**HappyHorse 1.1**](/api-capabilities/happyhorse/overview) 🆕 | 新上线 | 阿里云,多参考图主体保持(最多 9 张),与 Wan 共用分组 | 按秒(最长 12s) | \$0.126-0.224/秒(官网 98%) | | [**MiniMax H3**](/api-capabilities/minimax-h3/overview) 🆕 | 2026-09 新上线,开源自部署 | 海螺 3.0 开源权重自部署,一个端点覆盖文生、首尾帧与图/视频/音频混合参考,自带立体声,768P;参考素材不另收费,默认分组可用 | 4–15s | \$0.03/秒 | | [**Oxygen**](/api-capabilities/oxygen/overview) 🆕 | 2026-10 新上线,便宜走量 | AZ8 自研全能视频模型,OpenAI Videos 兼容接口,文生/首帧/首尾帧/参考图+视频+音频生视频,自带音轨,480p / 768p 同价;默认分组可用 | 4–15s | \$0.02/秒 | | ~~**Sora 2 官逆**~~ | ❌ 已下线 | 原电商带货 / 动漫漫剧场景,请改用 Sora 2 官转 | — | — | 模型价格与官网对齐,且可能随官网调整;上表仅供参考,具体以顶部导航「模型价格」栏目为准:[模型价格](/models/index)。 **视频模型核心特性**: * **Seedance 2.5 / 2.0 系列(字节跳动)**:`doubao-seedance-2-5-260628`(2.5,最长 30 秒、最多 30 张参考图、支持 mov 输出)/ `doubao-seedance-2-0-260128`(标准版)/ `-fast-260128`(极速版)/ `-mini-260615`(轻量版),价格与速度不同(mini \< fast \< 标准版 \< 2.5,2.5 约为标准版 1.5 倍),mini 与 fast 最高 720p。**四个模型同走 `SeeDance2` 分组**(0.18x),一把令牌通吃;令牌计费模式须选「按量优先」或「按量计费」 * **VEO 3.1 官转(Official)**:透传 Google AI Studio 官方端点,业界领先的声画同步,支持真人出镜,默认分组 + 按次/按量优先令牌即可调用,是 Veo 3.1 官逆暂停期间的推荐替代 * **Veo 3.1 官逆**:因谷歌风控**暂停中**,恢复时间另行通知,期间请改用 VEO 3.1 官转 * **Sora 2 官转**:已下线,暂不可用 * **Wan2.7 / Wan2.6(阿里云万象)**:默认价约官网 98%,两个系列与 HappyHorse 共用 `Wan&HappyHorse` 分组,一把令牌通用;Wan2.6 与 Wan2.7 同端点同 schema,只改 `model` 名即可迁移 * **HappyHorse 1.1(阿里云)**:擅长多参考图主体保持,与 Wan 共用 `Wan&HappyHorse` 分组与端点 * **MiniMax H3(开源自部署)**:`MiniMax-H3`,走 `POST /hailuo/v2/video_generation`,最多 9 张参考图 + 3 段参考视频 + 3 段参考音频,按请求时长计费;暂不支持 2K,`Idempotency-Key` 不生效,需在业务层做幂等 * **Oxygen(AZ8)**:`oxygen-1.0`,走 `POST /v1/videos`;每次显式传 `size`(不传默认竖版),首尾帧、参考素材、768p、1:1 等高级参数写进 `input_reference` 的 JSON 信封;失败自动全额退款 * AI 视频工具(在线测试):icover.ai **即将上线的视频模型**: * Kling 3.0 敬请期待!关注我们获取最新上线通知。 ## 💰 定价说明 * **按量计费**:根据实际使用计费,无最低消费 * **余额有效期**:自充值之日起 365 天内有效,再次充值自动重置(详见[充值活动说明](/faq/recharge-promotions)) * 访问 [API易控制台定价页面](https://www.apiyi.com/account/pricing) 查看所有模型的最新价格 # 日志查询 API Source: https://docs.apiyi.com/api-capabilities/log-query 用程序拉取调用日志,获取每次请求的模型、实际扣费、耗时与错误码,实现自动对账与故障自查 ## 接口概述 日志查询接口返回你账号下**每一次 API 调用**的明细记录,包括调用的模型、实际扣费、耗时、 是否流式、以及失败时的错误码。 它和[余额查询 API](/api-capabilities/balance-query) 是互补的:余额查询告诉你「现在还剩多少钱」, 日志查询告诉你「钱花在哪了」。 三类典型用途: 按时间段、按模型统计实际消费,与自己的业务账单核对 查失败请求的错误码,定位是参数问题还是上游问题 拿到 `request_id` 给客服,能精确定位到那一次调用 控制台也能看日志(「日志」页面)。本接口是同一份数据的程序化入口,适合需要自动化对账、 定时导出、或接入自己监控系统的场景。手动查看请直接用控制台,见 [如何查看我的调用记录](/faq/call-logs)。 **推荐用法:每天同步一次,把日志落到你自己的库里。** 这个接口是为「定时增量导出」设计的,不适合当成实时查询接口反复调用: * **每天跑一次**,只拉「上次同步之后」的新记录,写进你自己的数据库或 CSV * **一次多取一些**:`pageSize` 最大 5000,别用默认的 10 —— 详见下方参数说明里的常见陷阱 * **不要**用它做全量回溯(例如一次性拉三个月),也不要在页面上做实时翻页 * **不要并发**调用,串行一页一页拉,页与页之间留 1 秒左右间隔 * 时间窗跨度**建议不超过 1 天**;调用量大的账号按小时切 原因见下方「性能须知」一节:时间窗越老、翻页越深,单次请求的开销涨得很快, 超过服务端上限会直接返回错误,**重试同样的参数也不会变快**。 本页的 Python 示例已经按这个模式写好,可以直接拿去当每日定时任务用。 ## 如何获取个人访问令牌 日志接口用**个人访问令牌**认证(`nap_` 开头),与 API Key 不是一回事(详见文末注意事项)。 访问 `api.apiyi.com/account/profile` 个人中心,在「账户选项」里点击 **个人访问令牌** → **创建令牌** 本接口只需勾选 **用量与个人记录 · 读取**。每日定时任务建议把到期时间选久一点(默认 30 天,到期后脚本会直接 401) 同步脚本跑在固定 IP 的服务器上时建议填上。白名单创建后不能修改,可以先不填调通,再新建一把带白名单的正式令牌 创建个人访问令牌:名称、到期时间、IP 白名单与权限 **旧系统令牌计划于 2026-11-11 (UTC+8) 停用。** 原来在「账号选项 - 系统令牌」生成的 AccessToken 在此之前照常可用,之后会返回 401 `ACCESS_TOKEN_LEGACY_RETIRED`。 平台会视使用情况决定是否延期,建议尽早换成个人访问令牌:把环境变量 `APIYI_SYS_TOKEN` 的值换掉即可,下方脚本不用改。 详见 [什么是个人访问令牌?](/faq/personal-access-token) ## 接口信息 | 项目 | 说明 | | - | - | | **接口URL** | `https://api.apiyi.com/api/log/self` | | **请求方法** | `GET` | | **认证方式** | Authorization Header(个人访问令牌,`Bearer ` 前缀可加可不加) | | **响应格式** | JSON(gzip 压缩) | | **数据范围** | 仅本账号自己的日志 | ## 请求说明 ### 请求 Headers | Header名称 | 必填 | 说明 | | - | - | - | | `Authorization` | 是 | 个人访问令牌,直接填写令牌字符串 | | `Accept` | 否 | 建议设置为 `application/json` | ### 查询参数 | 参数 | 类型 | 必填 | 说明 | | - | - | - | - | | `p` | Integer | 否 | 页码,**从 0 开始**(不是 1)。偏移量 = `p` × `pageSize` | | `pageSize` | Integer | 否 | 每页条数,默认 10,**最大 5000**。⚠️ **注意是驼峰**,见下方提示 | | `type` | Integer | 否 | 日志类型,对账请传 `2`,取值见下表 | | `model_name` | String | 否 | 按模型名精确过滤,如 `gpt-5.6` | | `token_name` | String | 否 | 按令牌名过滤 | | `request_id` | String | 否 | 按请求 ID 精确查单条,命中时只返回那一条 | | `start_timestamp` | Integer | **必传** | 起始时间,Unix 秒。见下方说明 | | `end_timestamp` | Integer | **必传** | 结束时间,Unix 秒。与 `start_timestamp` 的跨度建议不超过 1 天 | | `group` | String | 否 | 按分组过滤 | **时间窗请当成必填参数。** 接口本身**不强制**这两个参数 —— 不传也能调通。但不传意味着让服务端在你账号的**全部历史**里 从最新往回找,**历史调用量大的账号会直接撞上服务端上限、返回错误而不是慢一点**。 这是本页最容易踩、后果也最直接的一个坑。我们把它标成「必传」不是因为服务端会拒绝, 而是因为**不传的失败方式很难自己看出来**:它不报「参数缺失」,只报超时。 **`pageSize` 是本接口唯一使用驼峰命名的参数,写成 `page_size` 会被静默忽略。** 其余参数(`model_name`、`token_name`、`start_timestamp`、`request_id` …)都是下划线命名, 只有这一个不是。写错不会报错,服务端会**当作没传、回落到默认的每页 10 条** —— 很容易被误认为「上限就是 10」或者「我这段时间只有 10 次调用」。 ``` ?pageSize=1000 ✅ 返回 1000 条 ?page_size=1000 ❌ 静默返回 10 条 ``` 超过 5000 会明确报错(`每页条数不能超过 5000`),不会静默截断。 **把 `pageSize` 调大是这个接口最有效的一个优化。** 按每页 10 条拉, 一个每天 50 万次调用的账号需要请求 5 万次;按每页 5000 条只要 **100 次**。 请求数少两个数量级,翻页深度也随之降下来 —— 参见下方「性能须知」。 实测每页 5000 条的响应约 700 KB(gzip 后)、2.5 秒左右。 如果你的网络或内存吃紧,`1000` 是个稳妥的折中值。 ### 性能须知 单次请求的开销**不是固定的**,取决于三个因素。按这三条来用,这个接口很快; 反着用,会直接撞上服务端 60 秒的单条查询上限并返回错误。 | 因素 | 便宜 | 昂贵 | | - | - | - | | **时间窗的位置** | 最近几天 | 几十天以前 | | **时间窗的跨度** | 1 小时 \~ 1 天 | 一个月,或者干脆不传时间窗 | | **偏移量(`p` × `pageSize`)** | 前几万条 | 几十万条以上 | 四条实用规则: 1. **一定要传 `start_timestamp` 和 `end_timestamp`。** 不传时间窗是最贵的用法。 2. **`pageSize` 调大。** 这是最省事的一条:每页 10 条改成每页 1000\~5000 条, 请求数直接降两个数量级,偏移量也跟着降。 3. **窗口切小、而不是把偏移量翻深。** 真正贵的不是「第几页」而是「跳过了多少条」—— 这个开销是超线性增长的。与其在一个大窗口里一路翻下去, 不如切成 24 个一小时的小窗口,每个窗口的偏移量都从 0 开始。 4. **拉历史数据一次拉完就落库,之后只做增量。** 老数据的查询成本比新数据高得多, 反复回查同一段历史是纯浪费。 如果某个窗口用 `pageSize=1000` 还翻了几十页才结束,说明这段时间你的调用量很大 —— **把窗口对半切开分别拉**,比继续往深处翻页快得多。下方 Python 示例里的 `MAX_PAGES` 就是干这件事的。 #### 60 秒是硬上限,超了返回的是错误 服务端对单次查询有 **60 秒**的执行上限。超过这个时间,你拿到的**不是一个慢响应, 而是一个错误** —— 已经花掉的时间不会给你任何数据。 以下三种用法**很可能**触发它,请直接避开,不要靠重试去碰运气: | 用法 | 为什么 | | - | - | | **不传时间窗** | 服务端要在你的全部历史里回溯 | | **时间窗落在一个月以前**,且账号调用量大 | 越老的数据检索成本越高 | | **偏移量翻到几十万条以上** | 跳过的记录数越多越慢,且是超线性的 | **重试同样的参数不会变快**,只会再等 60 秒。正确的反应是**把时间窗缩小**、 或者把 `pageSize` 调大以减少翻页 —— 换句话说,让服务端每次要处理的数据量变小。 ### 日志类型 type | 取值 | 含义 | 说明 | | - | - | - | | `1` | 充值 | 记录充值前后余额,`quota` 为 0 | | `2` | **消费** | **对账只需要这一类**,`quota` 是实际扣费 | | `3` | 管理 | 账号信息变更等操作记录,`quota` 为 0 | | `4` | 系统 | 系统赠送额度等,`quota` 为 0 | | `11` | 异步任务退款 | 视频等异步任务失败时全额退回预扣额,`quota` 为**负数**,`content` 里带 task\_id | **统计花费时务必带上 `type=2`。** 不传 `type` 会把充值、系统赠送记录一起返回, 这些记录的 `quota` 虽然是 0,但 `model_name` 与 `token_name` 也是空的, 直接遍历求和或按模型分组会得到错误结果。 用到视频类异步模型(Seedance 等)的账号,请**再拉一遍 `type=11`**:任务失败时预扣额以负数退款行退回,只算 `type=2` 会把失败单的预扣额当成消费。 ## 响应说明 ### 成功响应示例 ```json theme={null} { "success": true, "message": "", "data": [ { "request_id": "2026080114481936471351696e93ae3FTV8WKfk", "created_at": 1785595715, "type": 2, "content": "模型固定价格 0.015,分组倍率 1", "username": "your-account", "token_name": "生产环境-主力", "token_group": "default", "model_name": "gpt-5.6", "quota": 7500, "prompt_tokens": 1000, "completion_tokens": 0, "duration_for_view": 16, "is_stream": false, "error_code": "", "other": "{\"billing_type\":\"by_count\",\"request_path\":\"/v1/images/generations\",\"group_ratio\":1,\"model_ratio\":1,\"usage\":{}}" } ] } ``` ### 关键响应字段 | 字段名 | 类型 | 说明 | | - | - | - | | `quota` | Integer | **本次实际扣费**(额度),÷ 500,000 = 美元 | | `content` | String | 人类可读的计价说明,如「模型固定价格 0.015,分组倍率 1」 | | `model_name` | String | 实际计费的模型名 | | `token_name` | String | 哪一把令牌发起的 | | `token_group` | String | 令牌所属分组(注意是分组标识,见常见问题) | | `prompt_tokens` / `completion_tokens` | Integer | 输入 / 输出 token 数 | | `duration_for_view` | Integer | 本次调用耗时(秒) | | `is_stream` | Boolean | 是否为流式调用 | | `error_code` | String | 失败原因码,成功时为空字符串 | | `created_at` | Integer | 调用时间,Unix 秒 | | `request_id` | String | **请求 ID,报障提工单时提供这个** | | `other` | String | 计费与请求的补充信息,**是 JSON 字符串,需要二次解析** | `other` 字段存的是一段 JSON **字符串**而不是嵌套对象,取用前需要再解析一次 (Python 用 `json.loads()`,JavaScript 用 `JSON.parse()`)。里面包含 `billing_type`(计费方式)、`request_path`(实际调用的端点)、`group_ratio`(分组倍率)、 `model_ratio`(模型倍率)、`usage`(用量明细)等。 视频类异步任务的结算记录里另有 `final_quota`(该任务最终总额)、`original_quota`(提交时的预扣额)、 `adjustment_quota`(本条记录的差额)、`actual_tokens`(实际用量),见下方常见问题。 ### 额度换算 500,000 额度 = \$1.00 美金 (USD) **计算公式:** 美金金额 = `quota` ÷ 500,000 **示例:** * `quota: 7500` → \$0.015 USD * `quota: 22500` → \$0.045 USD * `quota: 18` → \$0.000036 USD 这与[余额查询 API](/api-capabilities/balance-query) 是同一套换算口径,可以直接对齐。 ## 错误响应 ### HTTP 401 - 认证失败 ```json theme={null} { "success": false, "message": "You are not authorized to perform this operation. The access token is invalid." } ``` **原因:** 令牌无效、已过期或已删除,或误把 API Key(`sk-` 开头)当成个人访问令牌使用。修改密码、两步验证或通行密钥会撤销全部个人访问令牌。 **解决方法:** 回控制台重新创建个人访问令牌。 另外两种 401 会带明确的报文: | 报文 | 原因 | | - | - | | `ACCESS_TOKEN_LEGACY_RETIRED` | 仍在使用已停用的旧系统令牌,需换成个人访问令牌 | | `This access token may not be used from the current IP address.` | 请求来源不在令牌的 IP 白名单内;白名单不能修改,需新建令牌 | ### HTTP 403 - 权限不足 ```json theme={null} { "code": "ACCESS_TOKEN_SCOPE_DENIED", "message": "This endpoint is outside this access token’s granted permissions.", "success": false } ``` **原因:** 个人访问令牌没有勾选 **用量与个人记录 · 读取**。新建一把勾选了该权限的令牌即可(权限创建后不能修改)。 ## 代码示例 ### cURL 示例(单页,快速验证) ```bash theme={null} export APIYI_SYS_TOKEN='nap_YOUR_PERSONAL_ACCESS_TOKEN' # 个人访问令牌 # 只看最近 1 小时。start/end 一定要带上,不带时间窗的查询很可能超时 END=$(date +%s) START=$((END - 3600)) curl --compressed -s "https://api.apiyi.com/api/log/self?p=0&pageSize=1000&type=2&start_timestamp=$START&end_timestamp=$END" \ -H "Authorization: $APIYI_SYS_TOKEN" \ -H 'Accept: application/json' | jq '.data[] | {created_at, model_name, quota, request_id}' ``` **必须添加 `--compressed` 选项**,因为 API 返回的是 gzip 压缩内容,否则会得到乱码。 这条命令用来验证令牌配好了没有。真正的对账请用下面的每日同步脚本。 ### Python 示例:每日增量同步(可直接当定时任务用) 下面这段是**推荐的标准用法**:每天跑一次,只拉上次同步之后的新记录,写进本地 SQLite。 重复运行是安全的(按 `request_id` 去重),中断之后重跑会从断点继续。 ```python theme={null} """APIYI 调用日志每日增量同步。建议每天跑一次,例如 crontab: 30 2 * * *""" import json import os import sqlite3 import time import requests BASE = "https://api.apiyi.com" TOKEN = os.environ["APIYI_SYS_TOKEN"] QUOTA_PER_USD = 500_000 DB_PATH = "apiyi_logs.db" WINDOW = 3600 # 单个时间窗的跨度(秒)。1 小时对绝大多数账号都够用 PAGE_SIZE = 1000 # 每页条数,服务端最大 5000。⚠️ 参数名是驼峰 pageSize MAX_PAGES = 50 # 单窗最多翻多少页;超过就把窗口对半切,避免偏移量太深 SLEEP = 1.0 # 每页之间的间隔,串行调用不要并发 FIRST_RUN_DAYS = 7 # 首次运行往回补多少天 HEADERS = {"Authorization": TOKEN, "Accept": "application/json"} def open_db(): con = sqlite3.connect(DB_PATH) con.execute(""" CREATE TABLE IF NOT EXISTS logs ( request_id TEXT PRIMARY KEY, created_at INTEGER, model_name TEXT, token_name TEXT, quota INTEGER, error_code TEXT, raw TEXT )""") con.execute("CREATE INDEX IF NOT EXISTS idx_created ON logs(created_at)") con.commit() return con def get_page(start, end, page): resp = requests.get( f"{BASE}/api/log/self", headers=HEADERS, params={ "p": page, "pageSize": PAGE_SIZE, # 驼峰!写成 page_size 会被忽略、每页只给 10 条 "type": 2, # 2 = 消费,对账只看这一类 "start_timestamp": start, "end_timestamp": end, }, timeout=60, # 服务端单条查询上限也是 60 秒 ) resp.raise_for_status() return resp.json().get("data") or [] def fetch_window(start, end): """拉取 [start, end] 这一段。翻页过深时自动把窗口对半切。""" rows, seen, page = [], set(), 0 while page < MAX_PAGES: data = get_page(start, end, page) if not data: return rows # 翻到空数组即这一窗结束 fresh = [r for r in data if r.get("request_id") not in seen] if not fresh: return rows # 整页都是重复,防御性退出 seen.update(r["request_id"] for r in fresh) rows.extend(fresh) page += 1 time.sleep(SLEEP) if end - start <= 1: return rows mid = (start + end) // 2 # 这段时间调用量太大,切成两半分别拉 return fetch_window(start, mid) + fetch_window(mid + 1, end) def sync(): con = open_db() row = con.execute("SELECT MAX(created_at) FROM logs").fetchone() cursor = (row[0] + 1) if row and row[0] else int(time.time()) - FIRST_RUN_DAYS * 86400 now = int(time.time()) total = 0 while cursor < now: end = min(cursor + WINDOW, now) rows = fetch_window(cursor, end) con.executemany( "INSERT OR IGNORE INTO logs VALUES (?,?,?,?,?,?,?)", [(r.get("request_id"), r.get("created_at"), r.get("model_name"), r.get("token_name"), r.get("quota"), r.get("error_code"), json.dumps(r, ensure_ascii=False)) for r in rows]) con.commit() total += len(rows) cursor = end + 1 print(f"本次同步 {total} 条,已落库到 {DB_PATH}") return con def report(con, days=1): since = int(time.time()) - days * 86400 print(f"{'模型':32s} {'次数':>6s} {'花费(USD)':>12s}") rows = con.execute( "SELECT model_name, COUNT(*), SUM(quota) FROM logs " "WHERE created_at >= ? GROUP BY model_name ORDER BY SUM(quota) DESC", (since,)).fetchall() for model, cnt, quota in rows: print(f"{model or '(无)':32s} {cnt:6d} {(quota or 0) / QUOTA_PER_USD:12.4f}") total = sum((q or 0) for _, _, q in rows) print(f"\n合计 ${total / QUOTA_PER_USD:.4f} USD") if __name__ == "__main__": report(sync(), days=1) ``` **输出示例:** ``` 本次同步 570 条,已落库到 apiyi_logs.db 模型 次数 花费(USD) gpt-5.6 128 2.3850 gemini-3-pro-image 30 1.3500 deepseek-chat 412 0.0148 合计 $3.7348 USD ``` 落库之后,按模型、按天、按令牌的各种统计都在你自己的库里做,**不需要再回头查接口**。 这既快得多,也避免了「日志保留期到了、想查的数据已经不在」的问题。 ### Node.js 示例(单个时间窗) 同样的思路:按小时切窗口、串行翻页、翻太深就把窗口切小。 ```javascript theme={null} const BASE = "https://api.apiyi.com"; const TOKEN = process.env.APIYI_SYS_TOKEN; const QUOTA_PER_USD = 500_000; const PAGE_SIZE = 1000; // 服务端最大 5000 const MAX_PAGES = 50; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); async function getPage(start, end, page) { const qs = new URLSearchParams({ p: String(page), pageSize: String(PAGE_SIZE), // 驼峰!写成 page_size 会被忽略、每页只给 10 条 type: "2", // 2 = 消费 start_timestamp: String(start), end_timestamp: String(end), }); const resp = await fetch(`${BASE}/api/log/self?${qs}`, { headers: { Authorization: TOKEN, Accept: "application/json" }, }); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); const { data } = await resp.json(); return data || []; } // 拉取 [start, end],翻页过深时把窗口对半切 async function fetchWindow(start, end) { const rows = []; const seen = new Set(); for (let page = 0; page < MAX_PAGES; page += 1) { const data = await getPage(start, end, page); if (data.length === 0) return rows; const fresh = data.filter((r) => !seen.has(r.request_id)); if (fresh.length === 0) return rows; fresh.forEach((r) => seen.add(r.request_id)); rows.push(...fresh); await sleep(1000); // 串行,不要并发 } if (end - start <= 1) return rows; const mid = Math.floor((start + end) / 2); return [...(await fetchWindow(start, mid)), ...(await fetchWindow(mid + 1, end))]; } const end = Math.floor(Date.now() / 1000); const logs = await fetchWindow(end - 3600, end); // 最近 1 小时 const total = logs.reduce((sum, r) => sum + (r.quota || 0), 0); console.log(`${logs.length} 次调用,合计 $${(total / QUOTA_PER_USD).toFixed(4)} USD`); ``` `fetch` 会自动处理 gzip 解压,Node.js 侧无需额外配置(Python 的 requests 同理)。 只有 curl 需要显式加 `--compressed`。 ## 典型场景 ### 场景一:每日对账(推荐做法) 把上面的 Python 脚本挂成每天一次的定时任务,日志落到本地库; 统计花费、按模型分组、按令牌拆分这些事,全部在**你自己的库里**用 SQL 做。 这样做有三个好处:查询是本地的所以很快;不受日志保留期影响; 也不会因为反复回查历史数据而拖慢接口。 如果只想同步某个模型的记录,在请求参数里加 `model_name` 即可。 ### 场景二:找出失败的调用 落库之后直接查本地表: ```sql theme={null} SELECT created_at, model_name, error_code, request_id FROM logs WHERE error_code != '' AND created_at >= strftime('%s', 'now', '-1 day') ORDER BY created_at DESC; ``` 被网关拒绝的请求(参数错误等)`quota` 为 0,**不产生扣费**。日志里能看到 `error_code`, 方便你区分「调用失败了」和「调用成功但结果不满意」。 ### 场景三:报障时提供 request\_id 在日志里定位到出问题的那次调用,把 `request_id` 提供给客服,可以精确查到该次请求的 完整链路。这比描述「大概几点调用 xx 模型失败了」高效得多。 ## 常见问题 先检查这三件事,绝大多数慢查询都出在这里: 1. **有没有传 `start_timestamp` / `end_timestamp`?** 不传时间窗是最贵的用法, 服务端会在你的全部历史里回溯。 2. **时间窗是不是太老或太宽?** 查一个月前的数据比查昨天贵得多; 跨度也建议压到 1 天以内,量大的账号按小时切。 3. **`p` 是不是已经翻到几千页?** 翻页开销是超线性增长的。 正确的做法是**把时间窗切小、让每个窗口只翻几十页**,而不是在一个大窗口里往深处翻。 **重试同样的参数不会变快。** 遇到超时请按上面三条调整参数, 而不是原样重试 —— 原样重试只会再等一次。 **十有八九是参数名写成了下划线的 `page_size`。** 正确的写法是驼峰 `pageSize`。这是本接口唯一一个驼峰参数,其余(`model_name`、 `token_name`、`start_timestamp` 等)都是下划线,很容易顺手写错。 写错不会报错,服务端会当作没传、回落到默认的每页 10 条。 ``` ?pageSize=1000 ✅ 1000 条 ?page_size=1000 ❌ 10 条 ``` 最大 5000,超过会明确报错。数据量大时仍然需要翻页(`p=0`、`p=1` …), 翻到返回空数组为止 —— 上面的 Python 与 Node.js 示例已经封装好这个逻辑。 可以,传 `request_id` 即可,命中时只返回那一条: ```bash theme={null} curl --compressed -s "https://api.apiyi.com/api/log/self?request_id=YOUR_REQUEST_ID" \ -H "Authorization: $APIYI_SYS_TOKEN" ``` 排查某一次具体调用时,这比按时间段拉一堆记录再筛要快得多。 不是。部分字段属于平台内部信息,普通账号视角下为空或 0,属正常现象, 不影响对账与排障需要的字段(`quota`、`model_name`、`error_code`、`request_id` 等都是完整的)。 接口返回的是分组的**标识符**,控制台显示的是分组的**展示名**,两者可能不同。 例如接口返回 `default`,控制台显示「Default」。 完整的对应关系可以从公开接口 `https://api.apiyi.com/api/pricing` 的 `usable_group` 字段获取,它是一个「标识符 → 展示名」的映射表。如果你要让自己的报表和控制台显示一致, 需要自己做一次映射。 以 `quota` 为准。`quota` 是本次调用**实际扣除的额度**,是唯一可用于对账的字段。 响应体里的 token 数在部分按次计费的模型(如出图、视频类)上可能是占位值, 不参与计价 —— 这类模型的计价方式在 `other.billing_type` 里会标为 `by_count`。 **请按「只有最近 30 天可查」来设计你的同步逻辑。** 实际可查询的范围通常比 30 天更长,但我们**不对保留期做承诺** —— 它会随日志清理策略调整,而且不会单独通知。把 30 天当成规划下限, 你的对账流程就不会因为清理策略变化而断掉。 另外,**越老的时间窗查询成本越高**,即使数据还在,查起来也慢得多。 所以正确的用法是**每天同步一次、把数据落到你自己的库里**,历史统计在本地做。 需要长期留存的账单数据,请务必自行归档,不要依赖这个接口回查。 **原因:** API 返回的是 gzip 压缩内容(`Content-Encoding: gzip`),curl 没有自动解压。 **解决方案:** 添加 `--compressed` 选项: ```bash theme={null} curl --compressed 'https://api.apiyi.com/api/log/self?p=0' \ -H "Authorization: $APIYI_SYS_TOKEN" | jq ``` Python 的 requests 与 Node.js 的 fetch 会自动解压,无需额外配置。 Seedance 等异步视频任务按「提交时预扣、完成后多退少补」计费,所以一条视频在日志里是两条记录: 预扣行(`completion_tokens` 为 0、有 `request_id`)和结算行(`completion_tokens` 是实际用量、 **`request_id` 为空**、`quota` 只记差额)。**两条记录里都没有 `task_id`**,本接口没法按任务逐单配对。 要查一条视频的真实消费,请用任务接口按 `task_id` 查,返回的 `quota` 就是两条之和: ```bash theme={null} curl --compressed -s "https://api.apiyi.com/api/task/self?p=1&page_size=1&task_id=YOUR_TASK_ID" \ -H "Authorization: $APIYI_SYS_TOKEN" ``` 注意它的分页参数是下划线 `page_size`、`p` 从 1 开始,与本接口相反。 失败的任务 `quota` 仍显示预扣额,真实消费为 0(日志里有 `type=11` 的负数退款行)。 完整说明见 [如何按 task\_id 查一条 Seedance 视频的真实消费](/faq/seedance-task-cost-lookup)。 不会。日志查询接口不消耗任何配额。 ## 注意事项 **个人访问令牌不是 API Key,两者不能互换** * **API Key**(`sk-` 开头)用于 `/v1/*` 推理端点,拿去调 `/api/log/self` 会返回 401 * **个人访问令牌**(`nap_` 开头)用于 `/api/*` 管理端点,不能调用模型 对账脚本的令牌**只勾「用量与个人记录 · 读取」**,不要顺手勾其它权限; 存进密钥管理工具而不是代码,不要提交进代码仓库,到期前及时换新。 **日志响应里含有你自己的 API Key 明文** 日志记录会返回发起该次调用的令牌信息。**不要把日志的原始响应直接贴到公开场合、 截图发群、或转交给第三方**,导出前先剔除敏感字段。 特别提醒:这段明文不带 `sk-` 前缀,**常见的密钥扫描工具可能扫不出来**, 不要依赖自动化检查兜底。 **建议的调用节奏** * **每天同步一次**即可,不需要更频繁;同步的是「上次之后的增量」 * **`pageSize` 用 1000\~5000**,不要用默认的 10 —— 这一条比其它几条加起来都管用 * **串行调用**,页与页之间间隔 1 秒左右,**不要并发** * 客户端超时设成 60 秒(服务端单条查询的上限也是 60 秒) * 每个时间窗的跨度不超过 1 天;调用量大的账号按小时切 * 遇到超时**先缩小时间窗再重试**,原样重试不会变快 这些不是硬性配额,而是能让你自己拿到结果最快的用法。 按这个节奏用,一个普通账号同步一天的日志通常在一分钟内跑完; 即使是每天几十万次调用的重度账号,一天也只需要一百次左右的请求。 **我们保留未来对本接口引入调用频率限制的权利。** 目前没有对它设置频率限制,但请**不要按「永远不限」来设计你的定时任务**。 按上面这个节奏(每天一次、串行、`pageSize` 调大)用,即使将来加了限流也不会影响到你。 * [余额查询 API](/api-capabilities/balance-query) —— 查账号剩余额度 * [令牌管理 API](/api-capabilities/token-management) —— 程序化创建与管理 API Key * [如何查看我的调用记录](/faq/call-logs) —— 控制台手动查看 * [日志与账单怎么看](/faq/log-billing-explained) —— 计费字段解读 # 长文输出实战建议 Source: https://docs.apiyi.com/api-capabilities/long-form-output-practices 漫剧脚本、文学创作、万字长文等长输出场景怎么稳定拿到结果:用流式、read timeout 按事件间隔设、max_tokens 给足、查 stop_reason。附 Claude 原生调用示例。 **一句话结论**:让大模型一次产出上万字(分集大纲、长篇小说、长翻译、大段代码)时,**用流式、不要用非流式**;客户端 read timeout 按「两次数据事件之间的间隔」设(几十秒即可,建议 90\~120 秒),不要按「整段生成的总时长」设;`max_tokens` 给足;拿到响应后先看 `stop_reason` 再用正文。做到这四点,长文场景就不会「拿不到结果」。 本页面向所有大模型通用(OpenAI、Claude、Gemini、Grok 等),代码示例以 Claude 原生 `/v1/messages` 为主,OpenAI 兼容格式的差异单独标注。 ## 三个先知道的事实 1. **出万字长文,真实生成 10\~20 分钟是常态**。模型要逐 token 产出上万字,叠加推理/思考阶段,端到端耗时本就很长。这不是网关慢,是生成本身慢。 2. **非流式要「整段攒齐」才回写**。非流式(`stream` 不传或为 `false`)下,服务端必须等模型把整段生成完,再一次性把响应体回传给你。这几分钟里你的客户端 read timeout 一直在和它赛跑,生成越久越容易在拿到结果前先断开——断开时异常信息经常是空的(`httpx.ReadError` 的 `str(e)` 为空),看不出病因。 3. **断连仍然计费,盲目重试是重复计费**。只要服务端已经产出,哪怕最后没送达你,这次调用也照常计费。已经收到部分正文再断开的情况,重试等于让模型再跑一遍、再付一次钱。 ## 用流式,不要用非流式 流式(`stream: true`)下,首字节几秒内就到,之后每隔几十秒必有一个数据事件。你的 read timeout 只需覆盖「两次事件之间的间隔」,而不是覆盖长达十几分钟的整段生成——这是流式能稳定拿到长文结果的根本原因。 两种协议的**结束信号不同**,别混用: | 协议 | 结束信号 | 正文取法 | | - | - | - | | Claude 原生 `/v1/messages` | `event: message_stop`(**没有 `[DONE]`**) | `content_block_delta` 里 `delta.type == "text_delta"` 的 `delta.text` | | OpenAI 兼容 `/v1/chat/completions` | `data: [DONE]` | `choices[0].delta.content` | Claude 原生开启自适应思考时,会**先**出一个 `type: "thinking"` 的思考块(增量是 `thinking_delta`),再出 `text` 正文块。渲染时把 `thinking_delta` 和 `text_delta` 分流即可,思考增量不拼进正文。 Claude 原生 `/v1/messages` 流式最小可用示例(纯 httpx,逐行解析 SSE): ```python theme={null} import json import httpx def generate_long_text(prompt, api_key, model="claude-opus-5", max_tokens=64000): url = "https://api.apiyi.com/v1/messages" headers = { "x-api-key": api_key, "anthropic-version": "2023-06-01", "content-type": "application/json", "accept": "text/event-stream", } payload = { "model": model, "max_tokens": max_tokens, "stream": True, # ← 关键:长输出必须流式 "thinking": {"type": "adaptive"}, # 自适应思考,模型自行决定思考深度 "messages": [{"role": "user", "content": prompt}], } # 三段式超时:read 只覆盖事件间隔,不覆盖整段生成 timeout = httpx.Timeout(connect=30, write=120, read=120, pool=30) text, stop_reason = [], None with httpx.Client(timeout=timeout) as c: with c.stream("POST", url, json=payload, headers=headers) as r: if r.status_code != 200: raise RuntimeError(f"HTTP {r.status_code}: {r.read()[:400]}") event, data = None, [] for line in r.iter_lines(): if line == "": # 事件以空行分隔 if data: d = json.loads("\n".join(data)) t = d.get("type") or event if t == "content_block_delta" and d.get("delta", {}).get("type") == "text_delta": text.append(d["delta"]["text"]) elif t == "message_delta": stop_reason = d.get("delta", {}).get("stop_reason") or stop_reason event, data = None, [] continue if line.startswith("event:"): event = line[6:].strip() elif line.startswith("data:"): data.append(line[5:].strip()) # Claude 流以 message_stop 结束,没有 [DONE] if stop_reason == "max_tokens": raise RuntimeError(f"被 max_tokens 截断,已产出 {len(''.join(text))} 字,调大 max_tokens 后重试") return "".join(text).strip() ``` 如果你用官方 anthropic SDK,把 `base_url` 指到 `https://api.apiyi.com`,再用 `client.messages.stream(...).get_final_message()` 即可一步到位——SDK 自带了 SSE 解析、超时处理、`stop_reason` 判定。上面的 httpx 版本是给不想引 SDK 的场景。 ## read timeout 按事件间隔设,不是按总时长 很多人把 read timeout 设成一个能兜住整段生成的巨大值(比如 1800 秒),结果照样超时——因为非流式下这个值要和整段生成竞速,稍有波动就断。正确做法是流式 + 按事件间隔设 read timeout。 实测参考(`claude-opus-5` 出约 2 万字分集大纲,输入约 1.5 万字符): | 指标 | 实测值 | | - | - | | 首字节到达 | 3 \~ 130 秒 | | 思考阶段最大静默间隔 | 约 42 秒(且期间有 keepalive ping) | | 端到端总耗时 | 9 \~ 12 分钟 | 所以 read timeout 设 **90 \~ 120 秒**足以覆盖最大事件间隔并留余量,不必设成十几分钟。三段式超时把三个阶段拆开,各自设值: ```python theme={null} # connect:建连;write:上行发请求体;read:两次读之间的上限 timeout = httpx.Timeout(connect=30, write=120, read=120, pool=30) ``` ## max\_tokens 给足,并检查 stop\_reason 长输出容易撞到 `max_tokens` 上限被截断。尤其是 Claude 这类**开了思考的模型,思考本身也占 `max_tokens` 预算**,一份长文很容易把预算吃满。 * **`max_tokens` 建议 64000 起步**(开高 effort / 深度思考时更要给足;`claude-opus-5` 输出上限 128K)。 * **拿到响应先看 `stop_reason`**: * `end_turn`——正常结束,正文完整,这才算成功。 * `max_tokens`——被截断,正文可能不完整甚至为空。这是**被截断**不是「空结果」,把 `max_tokens` 调大后重试即可。 * `refusal`——被安全策略拒绝,单独处理。 只用 `str(e)` 或「正文为空」来判断成败会误导——空正文的真实原因往往是 `max_tokens` 截断。 ## 重试策略 长文场景的重试要克制,别让「失败重试」变成「重复计费 + 重复长跑」: * **只对「拿到响应头之前的失败」和 `5xx` / `429` 重试**(退避、最多 2 次)。这类是建连/瞬时问题,重试有意义。 * **已经收到部分正文再断流的,不要盲目重试**。服务端已经产出并计费,重试是让它再跑一遍、再付一次钱。 * 记录响应头里的 request id,方便对账和排查。 ## 场景速查 | 场景 | 典型输出量 | max\_tokens 建议 | read timeout 建议 | | - | - | - | - | | 漫剧 / 短剧分集大纲 | 1 \~ 3 万字 | 64000 | 90 \~ 120 秒 | | 文学创作(长篇小说/章节) | 1 \~ 5 万字 | 64000 \~ 128000 | 90 \~ 120 秒 | | 长篇翻译 | 随原文长度 | 按原文 token 估算 ×1.5 | 90 \~ 120 秒 | | 大段代码生成 | 数千行 | 32000 \~ 64000 | 90 \~ 120 秒 | 全部走流式;节点用 `api.apiyi.com`(或备用 `b.apiyi.com`),**不要用 `api-cf.apiyi.com`**(CDN 节点约 100 秒就 `524`,扛不住长请求)。 ## 相关链接 分场景的 timeout 推荐值 两种模式的差异与选型 自适应思考、effort 档位、max\_tokens 与截断 原生响应结构、SSE 事件、stop\_reason # 当下热门模型(保持更新) Source: https://docs.apiyi.com/api-capabilities/model-info 查看支持的400+AI模型详细信息,推荐给你当下热门、好用的 AI 大模型。 API易 支持 400+ 主流 AI 模型,本页面提供详细的模型信息、定价和使用说明。 **企业级专业稳定的AI大模型API中转站**\ 本站均为官方源头转发,价格八折(叠加充值加赠和汇率优势),聚合各种优秀大模型。不限速,不惧封号,按量计费,长期可靠服务。 ## 🔥 当前推荐模型 以下为当前稳定供给的热门模型,完整模型列表和实时价格请访问 [API易控制台定价页面](https://www.apiyi.com/account/pricing),也可查看站内 [模型价格总览](/models)。 **模型升级建议**:我们推荐优先使用最新模型以获得最佳性能,但请注意: 1. **上线初期可能不稳定**:新模型刚发布时,原厂商算力可能不足,导致响应慢、超时或偶发错误,通常数天至数周后趋于稳定 2. **注意参数兼容性**:新模型可能新增或变更参数(如 `max_completion_tokens` 替代 `max_tokens`),升级前请检查 API 参数是否兼容老模型 3. **务必先测试再上线**:在将新模型用于生产环境前,务必在测试环境充分验证,确保输出质量和接口兼容性符合预期 表格中上下文一栏标注为「—」的,表示厂商暂未公布明确数值,实际以控制台与官方文档为准。价格均为每 100 万 tokens 的定价(输入/输出),可叠加充值加赠优惠。 ## 模型分类 ### 🤖 OpenAI 系列 #### 🆕 最新模型 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **GPT-6 Astra** 🔥 | `gpt-6-astra` | 1.05M | 6 代旗舰,主打电脑操作与长程 Agent,推理力度五档(新增 `xhigh` / `max`),Terminal-Bench 4.0 **57.9%**、ScreenSpot-Pro 92.7%;\$10/\$50 | 顶级 Agent、电脑操作、科研 | | **GPT-6 Sol** 🔥 | `gpt-6-sol` | 1.05M | 6 代中档,价格约为 5.6 Sol 的一半且为长期价,128K 输出;\$2/\$10 | 写功能、审代码、数据分析 | | **GPT-6 Luna** 🔥 | `gpt-6-luna` | 1.05M | 6 代轻量档,\$0.10/\$0.50,适合摘要、抽取等大批量任务 | 高并发、成本敏感 | | **GPT-5.5 Pro** | `gpt-5.5-pro` | 1M | 深度推理专用,Terminal-Bench 2.0 82.7%;**仅 `/v1/responses` 端点 + SVIP 分组**,价格高昂 | 顶级推理、科研(专业需求) | | **chat-latest** | `chat-latest` | 400K | 去版本号别名,始终指向最新 ChatGPT Instant 模型 | 快速写作、对话 | **GPT-6 系列供给**:`default` / `svip` 官转分组四项计费与官网一致,Chat Completions 与 Responses 双端点实测可用(含函数调用),从 5.6 迁移只需改 `model` 字段;Astra 另有 `Codex_Reverse` 分组按 0.5 折扣。Sol / Luna 单次上下文超过 272K 会整单进入更高一档计费。详见 [GPT-6 Astra 上线说明](/news/gpt-6-astra-launch) 与 [GPT-6 Sol / Luna 上线说明](/news/gpt-6-sol-luna-launch)。 **GPT Pro 系列(如 `gpt-5.5-pro`、`gpt-5.4-pro`)使用须知**: 1. **仅支持 `/v1/responses` 端点**:不能走 `/v1/chat/completions`,调用前请确认 SDK / 代码已切换到 Responses API 2. **价格高昂**:单次调用可能消耗数美金,且仅对 SVIP 分组开放,避免 Default 分组误用 3. **非专业需求不建议使用**:日常任务用 GPT-6 Sol 或 GPT-5.6 Sol 即可,Pro 仅适合对推理深度有极致要求的科研、顶级任务 #### ✅ 稳定/经典系列 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **GPT-5.6 Sol** ⭐ | `gpt-5.6-sol` | 1M | 5.6 代旗舰,Terminal-Bench 2.1 88.8%,`gpt-5.6` 别名指向它;\$4/\$20(9/3 起降价) | 前沿智能、Agent 任务 | | **GPT-5.6 Terra** | `gpt-5.6-terra` | 1M | 5.6 代主力档,\$2/\$12 | 日常开发 | | **GPT-5.6 Luna** | `gpt-5.6-luna` | 1M | 5.6 代轻量档,\$0.20/\$1.20;256K 以上超长上下文表现较弱 | 高并发、成本敏感 | | **GPT-5.5** ⭐ | `gpt-5.5` | 1M | 上代旗舰,SWE-bench Verified 88.7%,`xhigh` 推理档 | 复杂智能体、专业工作流 | | **GPT-5.4** ⭐ | `gpt-5.4` | 1M | 上代主力,原生计算机操控,GDPval 83% | 复杂智能体、专业工作流 | | **GPT-5.4 Mini** | `gpt-5.4-mini` | 1M | 轻量高性价比 | 平衡性能与成本 | | **GPT-5.2** | `gpt-5.2` | 400K | GDPval 70.9% 超越专业人士 | 编程规划、结构化任务 | | **GPT-5.1** | `gpt-5.1` | 128K | 智能与速度平衡,SWE-bench 76.3%,24h 缓存 | 综合应用、编程 | | **GPT-5.1 Codex** | `gpt-5.1-codex-high` | 128K | Codex 编程专用系列(含 medium / mini 档) | 编程、智能体任务 | | **GPT-5** | `gpt-5` | 128K | 经典旗舰稳定版 | 顶级推理、复杂任务 | | **GPT-5 Mini** | `gpt-5-mini` | 128K | GPT-5 轻量版,性能优异 | 平衡性能与成本 | | **o3** | `o3` | 200K | 推理模型,已大幅降价,性价比极高 | 复杂推理、数学、编程 | | **o4-mini** | `o4-mini` | 200K | 轻量级推理模型 | 编程任务 | | **GPT-4.1** | `gpt-4.1` | 128K | 速度快,老牌主力模型 | 综合应用 | | **GPT-4o** | `gpt-4o` | 128K | 综合能力平衡,多模态支持 | 通用场景 | | **GPT-4o Mini** | `gpt-4o-mini` | 128K | 轻量快速版本 | 快速响应 | **GPT-5 及以上系列使用注意事项**: 1. 温度参数 `temperature` 必须设置为 1(只支持 1) 2. 使用 `max_completion_tokens` 替代 `max_tokens` 3. 不要传递 `top_p` 参数 **图像和视频生成模型**已移至专属页面,请访问 [图像与视频生成模型](/api-capabilities/image-video-models) 查看完整列表和定价。 ### 🎭 Claude 系列 (Anthropic) #### 🆕 最新模型 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **Claude Opus 5.5** 🔥 | `claude-opus-5-5` | 1M | Terminal-Bench 4.0 **66.4%** 超过 Fable 5.1(55.8%),输出速度比 Opus 5 快 30%+;\$4/\$20,比 Opus 5 更便宜 | 顶级编程、复杂智能体 | | **Claude Opus 5.5 Thinking** 🔥 | `claude-opus-5-5-thinking` | 1M | Opus 5.5 显式思考版本 | 顶级推理任务 | | **Claude Fable 5.1** 🔥 | `claude-fable-5-1` | 1M | Mythos 级旗舰同价升级,Terminal-Bench-Science 24.7% → **52.6%**,含 `-thinking` 变体;\$10/\$50 | 顶级任务、长任务异步执行 | | **Claude Sonnet 5** | `claude-sonnet-5` | — | SWE-bench Verified **85.2%**、Terminal-Bench 2.1 80.4%,AWS 纯官转高缓存命中;\$2/\$10 | 编程首选、智能体开发 | | **Claude Opus 5** | `claude-opus-5` | 1M | Frontier Bench v0.1 43.3%,128K 输出,思考默认开启 + effort 五档,含 `-thinking` 变体;\$5/\$25 | 顶级编程、复杂智能体 | **Opus 5.5 / Fable 5.1 的破坏性变更**:强制工具调用(`tool_choice` 为 `any` / `tool`)直接报 400,思考块与模型绑定、编辑历史消息会让思考块失效;Opus 5.5 另有**思考无法关闭**、effort 默认从 `high` 降为 `medium`。从旧版迁移前请先看 [Opus 5.5 上线说明](/news/claude-opus-5-5-launch) 与 [Fable 5.1 上线说明](/news/claude-fable-5-1-launch)。 **Fable 5 / Fable 5.1 / Mythos 5 的 30 天数据保留合规**:因模型能力先进,Bedrock 与 Anthropic 对高风险滥用执行额外检查,这两款模型的**输入与输出将保留 30 天**用于严重滥用检测,默认仅自动化安全系统访问,人工审查仅在系统标记潜在危害时发生。保留发生在 AWS / Anthropic 官方侧,API易 为纯透明代理、自身不保留数据;**其他 Claude 模型不受影响**。详见 [Fable 5.1 上线说明](/news/claude-fable-5-1-launch)。 #### ✅ 稳定/经典系列 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **Claude Fable 5** | `claude-fable-5` | — | 上代 Mythos 级旗舰,\$10/\$50 | 顶级任务 | | **Claude Opus 4.8** | `claude-opus-4-8` | 1M (Beta) | Agentic 编程 69.2%,支持动态工作流 | 顶级编程、复杂智能体 | | **Claude Opus 4.7** ⭐ | `claude-opus-4-7` | 1M (Beta) | 编程基准较 4.6 +13%,工具错误降至 1/3,含 `-thinking` 变体 | 顶级编程、复杂智能体 | | **Claude Opus 4.6** | `claude-opus-4-6` | 1M (Beta) | Terminal-Bench 2.0 登顶,128K 输出 | 顶级编程、复杂智能体 | | **Claude Sonnet 4.6** ⭐ | `claude-sonnet-4-6` | 1M (Beta) | 性能媲美 Opus 4.5,性价比极高 | 编程、智能体开发 | | **Claude Opus 4.5** | `claude-opus-4-5-20251101` | 200K | SWE-bench 80.9%,价格降至前代 1/3 | 复杂编程、顶级推理 | | **Claude Sonnet 4.5** | `claude-sonnet-4-5-20250929` | 200K | 世界级编码模型,SWE-bench 77.2%,含 `-thinking` 变体 | 代码生成、智能体开发 | | **Claude Haiku 4.5** ⭐ | `claude-haiku-4-5-20251001` | 200K | 高性价比编码模型,SWE-bench 73.3%,速度 2 倍 | 实时聊天、结对编程 | | **Claude 4 Sonnet** | `claude-sonnet-4-20250514` | 200K | 老牌稳定版本 | 代码生成、分析 | **最新推荐**:Claude Opus 5.5 多数编程与知识工作基准超过 Fable 5.1,价格却比 Opus 5 还低(\$4/\$20),官方称典型负载综合成本比 Opus 5 低约 40%。Sonnet 5 性能逼近 Opus 4.8 而更快更省。**稳定首选**:Opus 4.7 / Sonnet 4.6 经过充分验证,适合已上线的生产工作流;Haiku 4.5 速度快 2 倍、性价比高。 ### 🌟 Google Gemini 系列 #### 🆕 最新模型 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **Gemini 3.8 Flash** 🔥 | `gemini-3.8-flash` | — | 谷歌 9/2 推出的新一版 Flash,官方规格尚未公布;150 例双协议实测与 3.7 逐项对齐;\$0.75/\$3.75 与 3.7 同价平迁 | 编程、Agent、性价比 | | **Gemini 3.7 Flash** 🔥 | `gemini-3.7-flash` | 1M | 64K 输出,Terminal-bench 2.1 **85.8%**、DeepSWE 65.3%;\$0.75/\$3.75 为官方限时价(至 2026-12-31,之后 \$1.50/\$7.50) | 编程、Agent、多模态 | | **Gemini 3.1 Pro Preview** | `gemini-3.1-pro-preview` | 1M | ARC-AGI-2 77.1%(3 Pro 的 2 倍+),谷歌最强推理模型 | 复杂推理、多模态分析 | | **Gemini 3.1 Flash Lite** | `gemini-3.1-flash-lite` | 1M | GA 版,比 2.5 Flash 快 64%,\$0.25/\$1.50 | 高并发、大批量、低成本 | **注意**:Gemini 3 Pro Preview 已于 2026 年 3 月 9 日停止服务,请迁移至 Gemini 3.1 Pro Preview 或 Gemini 3.8 Flash。 #### ✅ 稳定/经典系列 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **Gemini 3.6 Flash** | `gemini-3.6-flash` | 1M | 多模态 Flash,默认开思考(四档可控),原生工具全放通;\$1.50/\$7.50 | 编程、Agent、多模态 | | **Gemini 3.5 Flash-Lite** | `gemini-3.5-flash-lite` | 1M | 默认零思考、约 2 秒极速响应;\$0.30/\$2.50 | 高并发、低延迟、批量 | | **Gemini 3.5 Flash** | `gemini-3.5-flash` | 1M | Terminal-Bench 2.1 76.2%;\$1.50/\$9.00 | 编程 | | **Gemini 3 Flash Preview** | `gemini-3-flash-preview` | 1M | SWE-bench 78%,含 thinking / nothinking 变体 | 编程、性价比 | | **Gemini 2.5 Pro** ⭐ | `gemini-2.5-pro` | 2M | 正式版,编程优势,多模态能力强 | 长文本、编程、多模态 | | **Gemini 2.5 Flash** ⭐ | `gemini-2.5-flash` | 1M | 速度快,成本低,正式版 | 快速响应场景 | | **Gemini 2.5 Flash Lite** | `gemini-2.5-flash-lite` | 1M | 超轻量版本,更快更便宜 | 大批量简单任务 | **最新推荐**:Gemini 3.8 Flash 与 3.7 Flash 同价,编码与 Agent 能力较 3.6 Flash 大幅提升、价格只有一半,Gemini 原生格式与 OpenAI 兼容格式双端点可用,详见 [3.8 Flash 上线说明](/news/gemini-3-8-flash-launch) 与 [3.7 Flash 上线说明](/news/gemini-3-7-flash-launch)。高并发低延迟场景可选 [3.5 Flash-Lite](/api-capabilities/gemini-3-5-flash-lite/overview)。**稳定首选**:Gemini 2.5 Pro(2M 超长上下文)和 Gemini 2.5 Flash 适合已定型的生产环境。 ### 🚀 xAI Grok 系列 #### 🆕 最新模型 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **Grok 4.7** 🔥 | `grok-4.7` | 500K | 换用 2.1T 新基座,Terminal-Bench 4.0 18% → **33%**、DeepSWE 73%;\$2/\$6 与上代同价,但单任务输出 tokens 约为 4.6 的两倍 | 编程 Agent、长任务 | | **Grok 4.6** 🔥 | `grok-4.6` | 500K | Artificial Analysis 智能指数 **61**,追平 GPT-5.6 Sol Max;\$2/\$6 | 综合任务、编程 | | **Grok 4.5** | `grok-4.5` | 500K | 与 Cursor 联合训练,Agentic 工具调用单项登顶,输出 token 效率高;\$2/\$6 | 编程、Agent 任务 | | **Grok 4.3** | `grok-4.3` | 1M | Intelligence Index 53、τ²-Bench 98%、IFBench 81%;\$1.25/\$2.50,200K 后阶梯翻倍 | 复杂推理、综合任务 | | **Grok 4 Fast Reasoning** | `grok-4-fast-reasoning` | 200K | 推理模式,显示思考过程,降价 93%+ | 复杂推理任务 | | **Grok Code Fast 1** | `grok-code-fast-1` | 256K | SWE-bench 70.8%,高速生成 | 代码生成、智能体编程 | #### ✅ 稳定/经典系列 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **Grok 4.20 Beta** | `grok-4.20-beta` | 标准 | 含推理 / 多智能体 / 非推理多个变体 | 综合任务 | | **Grok 4** | `grok-4` | 标准 | 官方版本,可选 `grok-4-all` 原生联网 | 综合任务、实时信息 | | **Grok 3** | `grok-3` | 标准 | 官方稳定版本 | 日常使用 | | **Grok 3 All** | `grok-3-all` | 标准 | 原生联网增强版 | 新闻资讯、市场分析 | | **Grok 3 Mini** | `grok-3-mini` | 标准 | 带推理能力的小模型 | 轻量任务 | **按任务估算 Grok 成本**:4.5 / 4.6 / 4.7 单价相同(\$2/\$6),但 Grok 4.7 完成同一批任务的输出 tokens 约为 4.6 的两倍,按任务算的实际花费会上升;Grok 4.5 输出最省。超过 200K tokens 的请求按官方第二档费率计费。详见 [Grok 4.7 上线说明](/news/grok-4-7-launch)。 ### 🔍 DeepSeek 系列 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **DeepSeek V4 Pro** 🔥 | `deepseek-v4-pro` | 1M | 1.6T/49B 激活,SWE-Verified 80.6 接近 Claude/Gemini,Hybrid Attention | 复杂推理、编程、智能体 | | **DeepSeek V4.1 Flash** 🔥 | `deepseek-v4.1-flash` | — | DeepSeek 最新 Flash,OpenAI 与 Anthropic 双格式可用(Anthropic 格式走 `ClaudeCode` 分组),缓存命中低至 \$0.006;\$0.30/\$1.20 | 高并发、编程、Agent | | **DeepSeek V3.2** | `deepseek-v3.2` | 128K | 性能比肩 GPT-5,工具调用融入推理 | 复杂推理、编程 | | **DeepSeek V3.1** ⭐ | `deepseek-v3-1-250821` | 128K | 混合推理模式,Think/Non-Think 双模式 | 智能推理、编程 | | **DeepSeek R1** | `deepseek-r1` | 64K | 推理模型 | 数学、推理 | | **DeepSeek V3** | `deepseek-v3` | 128K | 综合能力强 | 通用场景 | ### 🐘 国产模型系列 #### 智谱 AI (GLM) **🆕 最新**:GLM-5.3、GLM-5.3-Flash | **✅ 稳定/经典**:GLM-5.2、GLM-5.1、GLM-5 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **GLM-5.3** 🔥 | `glm-5.3` | 1M | 沿用 5.2 的 753B MoE 基座只扩后训练,Z.ai Code Bench 比 5.2 提升 **50%**;\$1.40/\$4.396 | 项目级编程、长程智能体 | | **GLM-5.3-Flash** 🔥 | `glm-5.3-flash` | 1M | GLM-5 系列首个原生多模态(文本/图片/视频/文件),Terminal-Bench 2.1 **84.3** 逼近 Claude Opus 4.8;\$0.15/\$0.50 | 多模态、高性价比编程 | | **GLM-5.2** | `glm-5.2` | 1M | 阿里云官转直连,AA 智能指数 **51** 开源第一,Terminal-Bench 2.1 81.0、SWE-bench Pro 62.1;\$1.142/\$3.997 | 项目级编程、长程智能体 | | **GLM-5.1** ⭐ | `glm-5.1` | 200K | SWE-Bench Pro 58.4,744B MoE,MIT 开源 | 复杂编程、智能体 | | **GLM-5** | `glm-5` | 200K | 744B 参数(40B 激活),编程对齐 Claude Opus 4.5 | 复杂编程、系统工程 | | **GLM-4.6** | `glm-4.6` | 200K | 代码与推理增强版,稳定可靠 | 编程、推理、智能体 | | **GLM-4.5** | `glm-4.5` | 128K | 标准版本,综合能力强 | 通用场景 | **GLM-5.3 系列要点**: * 旗舰 GLM-5.3 与 5.2 同基座、只扩后训练,编程与网络安全能力(ExploitBench 54.4%,5.2 为 24.4%)大幅提升 * GLM-5.3-Flash 是 320B-A18B 混合稀疏 + 线性注意力的原生多模态模型,价格约为旗舰的 1/10 * 定价与智谱官网逐项一致,`default` / `svip` 分组可用;长任务建议把 timeout 调到 600 秒以上,详见 [GLM-5.3 上线说明](/news/glm-5-3-launch) #### 阿里通义千问 (Qwen) **🆕 最新**:Qwen3.8-Max | **✅ 稳定/经典**:Qwen3.7 系列、Qwen3.6 系列、Qwen Max / Plus / Turbo | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **Qwen3.8-Max** 🔥 | `qwen3.8-max` | 1M | 2.4T 参数稀疏 MoE,131K 输出,GPQA Diamond **92.6**、PaperBench 93.0,原生图片/视频输入;思考默认开启,暂不支持 `/v1/responses`;\$1.65/\$4.95 | 智能体、多模态、长文本 | | **Qwen3.7-Max** | `qwen3.7-max` | 1M | AA Intelligence Index 56.6 全球前五、国产第一,agent 长程 35 小时自主任务;\$1.714/\$5.142 | 智能体、多语言、长文本 | | **Qwen3.7-Plus** | `qwen3.7-plus` | 1M | 3.7 代性价比档,\$0.60/\$2.40 | 综合任务、批量处理 | | **Qwen3.6-Max-Preview** | `qwen3.6-max-preview` | 1M | 6 项编程基准登顶(AIME 2025 93%、GPQA 86%) | 编程、推理 | | **Qwen3.6-Plus** | `qwen3.6-plus` | 1M | MoE 72B / 18B 激活,Terminal-Bench 61.6,始终开启思维链 | 编程智能体 | | **Qwen3.6-Flash** | `qwen3.6-flash` | 1M | 35B-A3B MoE,多模态,\$0.17/\$1.02 | 高性价比、多模态 | | **Qwen3.6 开源双模** | `qwen3.6-27b` / `qwen3.6-35b-a3b` | 1M | API易 官转托管开源权重,免去租 GPU 与运维推理框架 | 私有化替代、成本敏感 | | **Qwen Max** | `qwen-max` | 32K | 老牌稳定版本 | 综合任务 | | **Qwen Plus** | `qwen-plus` | 32K | 增强版本 | 性价比场景 | | **Qwen Turbo** | `qwen-turbo` | 32K | 快速版本 | 低延迟场景 | #### Moonshot Kimi 系列 **🆕 最新**:Kimi K3 | **✅ 稳定/经典**:Kimi K2.6、K2.5、K2 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **Kimi K3** 🔥 | `kimi-k3` | 1M | 2.8 万亿参数开源旗舰,Kimi Delta Attention,LMArena Frontend Code Arena **登顶第一**,GPQA Diamond 93.5%、Terminal-Bench 2.1 88.3%;\$3/\$15 全区间统一计价,缓存命中低至 \$0.30 | 前端编程、长程智能体 | | **Kimi K2.7 Code** | `kimi-k2.7-code` | 256K | 编程专用档,\$0.95/\$3.99 | 编程任务 | | **Kimi K2.6** ⭐ | `kimi-k2.6` | 256K | 1T MoE / 32B 激活,SWE-Bench Pro 58.6;\$0.60/\$2.40 约官网 6 折 | 编程、智能体 | | **Kimi K2.5** | `kimi-k2.5` | 200K | 原生多模态,Agent Swarm 100 智能体协作 | 多模态、智能体 | | **Kimi K2 正式版** | `kimi-k2-250711` | 200K | 火山引擎官方合作,稳定性强 | 生产环境 | **Kimi K3 使用要点**: * 精确 1,048,576 tokens 上下文,**全区间统一计价**,长代码库 / 长文档可整体塞入,不担心跨档涨价 * 最大输出默认 131,072、最高可调至 1,048,576 tokens;思考模式常开满档,请预留输出预算 * 自动上下文缓存,命中部分输入按 \$0.30/1M 计费(未命中价的 1/10),多轮对话收益显著 #### 字节跳动 Seed 系列 **🆕 最新**:Seed 2.1 Turbo | **✅ 稳定/经典**:Seed 2.0 Pro / Lite / Mini | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **Seed 2.1 Turbo** 🔥 | `dola-seed-2-1-turbo-260628` | 256K | 高频生产型文本模型,Chat Completions 与 Responses **双端点 15/15 用例实测通过**,思考开关可控 + 隐式/显式双层缓存;\$0.50/\$2.50 | 高并发生产、批量抽取 | | **Seed 2.0 Pro** | `seed-2-0-pro-260328` | — | AIME 2025 98.3、Codeforces 3020,Agent 能力出色;\$0.50/\$3.00 | 复杂推理、编程 | | **Seed 2.0 Lite** | `seed-2-0-lite-260428` | — | 高性价比企业级,支持图片/视频/文本多模态输入;\$0.25/\$2.00 | 大规模生产场景 | | **Seed 2.0 Mini** | `seed-2-0-mini-260428` | — | 超轻量档,\$0.10/\$0.40 | 大批量简单任务 | **Seed 2.1 Turbo 默认开启深度思考**:不传任何参数时,一句话的简单问题也会先输出数百 tokens 的思考内容(实测一句话自我介绍消耗 444 输出 tokens,其中思考 409),且思考内容按输出 tokens 正常计费。高频短问答请把 `thinking: {"type": "disabled"}` 作为基线配置。详见 [接入文档](/api-capabilities/dola-seed-2-1-turbo/overview)。 ### 🌐 MiniMax 系列 **🆕 最新**:MiniMax-M3 | **✅ 稳定/经典**:MiniMax-M2.7、M2.5 | 模型名称 | 模型ID | 上下文长度 | 特点 | 推荐场景 | | - | - | - | - | - | | **MiniMax-M3** 🔥 | `MiniMax-M3` | 1M | 首个集前沿 Agent 编程、百万上下文与原生多模态于一身的开源旗舰,SWE-Bench Pro **59.0** 反超 GPT-5.5 与 Gemini 3.1 Pro,MSA 稀疏注意力把长上下文推理成本降至前代 1/20 | 编程、智能体、长上下文 | | **MiniMax-M2.7** | `MiniMax-M2.7` | 标准 | 10B 参数 SWE-bench Pro 56.22%,自进化能力,最小 Tier-1 模型 | 编程、智能体 | | **MiniMax-M2.5** | `MiniMax-M2.5` | 标准 | 230B(10B 激活),SWE-bench 80.2%,极致性价比 | 编程、智能体、办公自动化 | **MiniMax-M3 计费说明**: * 采用阶梯计费,0-512K 区间 \$0.30 输入 / \$1.20 输出(每 1M tokens),超出部分按更高档单价计费 * 注意模型名大小写:`MiniMax-M3`(同系列 `MiniMax-M2.7-highspeed` 等亦区分大小写) * 做百万级长上下文任务前请预估好成本 模型价格与官网对齐,且可能随官网调整;上表仅供参考,具体以顶部导航「模型价格」栏目为准:[模型价格](/models/index)。 ## 💰 定价说明 ### 计费方式 * **按量计费**:根据实际使用的 Token 数量计费 * **无最低消费**:充多少用多少 * **余额有效期**:自充值之日起 365 天内有效,再次充值自动重置(详见[充值活动说明](/faq/recharge-promotions)) * **实时扣费**:每次调用后立即从余额扣除费用 ### 价格优势 * 官方源头转发,价格略有优势 * 批量使用可联系客服获取更优惠价格 * 新用户注册后账号自带 \$0.05 试用额度,可直接跑通接入验证 ### 查看实时价格 访问 [API易控制台定价页面](https://www.apiyi.com/account/pricing) 查看所有模型的最新价格,或查看站内 [模型价格总览](/models)。 ## 🛠️ 使用建议 ### 模型选择指南 **编程开发** * 顶级性能:Claude Opus 5.5(Terminal-Bench 4.0 66.4%)、GPT-6 Astra(Terminal-Bench 4.0 57.9%)、Claude Sonnet 5(SWE-bench 85.2%)、Kimi K3 * 高性价比:GPT-6 Sol(约 5.6 代半价)、Gemini 3.8 Flash / 3.7 Flash、GLM-5.3 / 5.3-Flash、MiniMax-M3、DeepSeek V4.1 Flash * 备选:Grok 4.7 / 4.6、Qwen3.8-Max、GPT-5.6 Sol、Kimi K2.6 **文本创作** * 首选:GPT-6 Sol、Claude Opus 5.5、Claude Sonnet 5、Gemini 3.8 Flash * 备选:chat-latest、GPT-5.6 Terra、Claude Sonnet 4.6、GLM-5.3 **快速响应** * 首选:Gemini 3.5 Flash-Lite(默认零思考、约 2 秒)、Claude Haiku 4.5(速度快 2 倍)、GPT-6 Luna * 备选:Gemini 3.1 Flash Lite、Gemini 2.5 Flash、Seed 2.1 Turbo(需显式关闭思考) **长文本处理** * 超长上下文:Gemini 2.5 Pro(2M)、Kimi K3(1M 全区间统一计价)、MiniMax-M3(1M)、GLM-5.3(1M)、GPT-6 系列(1.05M)、Claude Opus 5.5(1M) * 注意:部分模型超过一定长度会按更高档阶梯计费(如 Grok 4.5 / 4.6 / 4.7 超 200K、GPT-6 Sol / Luna 超 272K、MiniMax-M3 超 512K),长任务前请预估成本 **图像生成** * 最新推荐:gpt-image-2.5-flare / gpt-image-2.5-sunburst(2026-09 上线,原生 4K、size/quality 精确控制,与 gpt-image-2 同价,速度 / 编辑精度分别更强)、Nano Banana Pro(4K 高清,最佳文本渲染) * 高性价比:Nano Banana 2(\$0.055/张,按量低至 \$0.025)、Nano Banana Lite(约 4 秒出图,\$0.025/张) * 专业设计:Seedream 5.0 Pro(\$0.12/次,交互式编辑 + 最多 10 张参考图)、Seedream 5.0 Lite(\$0.035/张) * 快速出图:Seedream 5.0 Flash(2026-09 上线,\$0.018/张,15–20 秒出图,中文排版准确) * 最便宜:gpt-image-2-all(官逆,\$0.025/张) **视频生成** * 官转首选:VEO 3.1 Official(按次 \$0.3 / \$1.2,4/6/8 秒,原生同步音轨) * 国产主力:Seedance 2.5 与 2.0 系列(2.5 / 标准 / fast / mini 四档)、Wan2.7、HappyHorse 1.1 * 注意:Sora 2 相关通道已下线,请改用上述通道,详见 [图像与视频生成模型](/api-capabilities/image-video-models) **联网搜索** * 原生联网:Grok 4 All、Grok 3 All(无需工具调用) * 搜索 grounding:Gemini 3.8 Flash / 3.7 Flash / 3.6 Flash / 3.5 Flash-Lite ### 成本优化建议 1. **分级使用**:简单任务用便宜模型,复杂任务用高级模型 2. **测试优化**:先用小模型测试,确定需求后再用大模型 3. **批量处理**:大量相似任务可以选择 Luna / Lite / Mini 档位 4. **缓存复用**:善用各家的上下文缓存(Kimi K3、Seed 2.1 Turbo、Claude 系列命中后输入价可低至 1/10) 5. **关闭不必要的思考**:部分模型默认开启深度思考(如 Seed 2.1 Turbo、Gemini Flash 系列、Qwen3.8-Max),高频短问答显式关闭可同时省钱提速 ## 🔗 相关资源 * [模型价格总览](/models) - 全量模型与实时价格 * [模型对比测试](https://imagen.apiyi.com/) - 图像生成效果对比 * [实时价格查询](https://www.apiyi.com/account/pricing) - 最新定价信息 * [API 文档](/api-manual) - 详细接口说明 * [快速开始](/getting-started) - 集成指南 模型列表持续更新中,我们会及时添加最新发布的优秀模型。最新上新动态请关注 [网站公告](/changelog)。如需使用特定模型或有批量需求,请联系客服。 # 多图融合测试指南 Source: https://docs.apiyi.com/api-capabilities/multi-image-fusion-testing 客户常问:参考图数量是否和官方一致(最多14张)?本文给出两套可自行复现的测试方法,并公开一次14图融合的完整实测结果。 不少客户会问:**"你们支持的参考图数量和官方一致吗?"** 谷歌官方 Gemini 图像模型的参考图上限是**每次请求最多 14 张**,这也是目前业内能拿到的最高上限。答案是**支持**,但"支持"不该只是一句口头承诺——本文给出一套你可以自己动手复现的测试方法,并附上一次真实的 14 图融合实测结果。 ## 为什么值得自己测一遍 14 张参考图是**极限场景**,实际业务里未必天天用到,但一旦用到(比如把多个独立设计的元素合成同一张海报/大片),你需要确认两件事: 1. **调用本身能不能成功**:14 张图叠在一起,请求体积会明显变大,会不会因为体积过大被拒绝? 2. **融合结果是否合理**:这么多张图一起塞给模型,会不会丢图、错位、张冠李戴? 下面两种方法,分别针对这两个问题设计,且都不依赖主观审美判断——结果一眼就能看出对不对。 ## 方法一:客观标记法(推荐先做这个) **思路**:不用复杂的业务场景,而是生成 N 张**彼此风格迥异、内容可数**的卡片(最简单的是 1 到 14 的数字),再要求模型把它们拼贴/融合成一张图。 * 每张卡片用完全不同的配色和材质风格(比如霓虹灯管、拉丝金属、粉笔字、像素风、木刻雕花……),保证融合结果里**每个数字都能凭颜色和风格反查到对应的输入图**; * 融合后人眼一眼核对:**1 到 14 是否全部出现、有没有重复或缺失**,不需要判断"好不好看",只需要判断"全不全、对不对"。 14张创意数字卡片融合成一张3行5列网格海报,每个数字保留独特配色和材质风格 这套方法的价值在于**排除干扰项**:如果连"数字对不对"这种最基础的核验都能通过,说明模型确实在认真处理每一张输入图,而不是随便挑几张应付了事。 ## 方法二:真实场景拆解法 **思路**:把你实际要用的业务场景,拆解成 N 个独立元素分别生成,再要求模型把它们融合回同一个场景。这更贴近真实使用方式——比如角色、服装、道具、背景分开管理,再合成一张成片。 以一个时尚大片场景为例,拆解成 14 个独立元素:模特人像、外套、载具、背景板、宠物/配饰若干、包袋、饰品、鞋履、行李箱等,每个元素单独生成一张图,风格基调保持统一(比如都用"浅灰影棚背景、写实摄影")。 14个独立时尚元素融合成一张完整的时尚大片场景,模特斜倚粉色轿车旁,鹦鹉、宠物犬、手提包等元素齐全 核验重点:**14 个元素是否全部入镜**、位置和比例是否协调、有没有明显的元素丢失或变形。真实场景的融合天然比"数字拼贴"更难(不同元素的光影、透视需要重新统一),所以这一步能更真实地反映复杂业务场景下的融合质量。 两种方法建议**都做**:方法一负责回答"模型有没有认真处理全部输入图",方法二负责回答"复杂真实场景下融合是否够用"。只做方法二,一旦出问题很难判断是"模型没看到某张图"还是"构图效果不理想"这两类完全不同的问题。 ## 请求格式:14 张图怎么塞进一次请求 Gemini 原生格式下,多图融合的规则很简单:**一个 `text` part(融合指令)+ N 个 `inlineData` part(每张参考图一个)**,每个 part 只能是 `text` 或 `inlineData` 其中一种,不能混在一起。 ```python theme={null} import requests import base64 API_KEY = "sk-your-api-key" def to_b64(path): with open(path, "rb") as f: return base64.b64encode(f.read()).decode() # 最多可放 14 张参考图 image_paths = ["01.png", "02.png", "03.png", "..."] # 最多14张 parts = [{"text": "请把这些图片的元素合理融合成一张场景图,保持风格统一、构图协调"}] for path in image_paths: parts.append({"inlineData": {"mimeType": "image/png", "data": to_b64(path)}}) response = requests.post( "https://api.apiyi.com/v1beta/models/gemini-3.1-flash-image:generateContent", headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json={ "contents": [{"parts": parts}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "1:1", "imageSize": "2K"} } }, timeout=600 # 图片数量多、请求体大,建议放宽超时 ).json() img_data = response["candidates"][0]["content"]["parts"][0]["inlineData"]["data"] with open("fused.png", "wb") as f: f.write(base64.b64decode(img_data)) ``` 更完整的多图编辑格式说明(`parts` 结构、常见报错)见 [图片编辑 API 参考](/api-capabilities/nano-banana-image/image-edit) 和 [Nano Banana 系列开发指南](/api-capabilities/nano-banana-dev-guide)。 ## 客户最关心的问题:图片这么多,请求会不会太大被拒绝 14 张原图不压缩直接传,请求体积确实会明显变大。我们实测过一组真实数据(14 张 2K 分辨率的图片,未做任何压缩): | 项目 | 实测数值 | | - | - | | 单张原图体积 | 约 1.7MB – 4.0MB | | 14 张合计(Base64 编码后) | **约 42–43MB** | | 请求结果 | **全部成功**,未出现因体积被拒绝的情况 | API易对单次请求的图片总量上限是 **100MB**(同步调用,避免内存占用过大);单张图片则遵循谷歌官方规则,不超过 **7MB**。本次 14 张 2K 图合计 42–43MB,在两条规则的安全范围内,因此顺利调用成功。 **结论**:即便不压缩,14 张 2K 参考图通常也不会撞到体积上限。但**压缩仍然是推荐做法**——不是因为不压会被拒绝,而是压缩后**耗时明显更短**(实测同样的融合任务,压缩后请求耗时约为原图直传的 1/2 到 1/3,因为省去了大体积数据的网络传输和服务端解码时间)。具体压缩参数建议(长边像素、JPEG 质量、多图合计体积目标)见 [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution)。 ## 遇到"没出图",先看是不是安全拦截 多图融合任务偶尔会命中 `finishReason: IMAGE_SAFETY`(HTTP 状态码仍是 200,但 `content.parts` 为空)。实测发现,**同样的输入原样重试 1-2 次,很可能就成功了**——这类拦截存在一定随机性,不代表输入内容真的有问题。 安全拦截的图片**不计费**,建议在业务代码里对 `IMAGE_SAFETY` 做自动重试,而不是直接判定为失败。更多错误类型(安全拦截、内容审核、超时等)的识别和处理方式,见 [Gemini 生图 API 错误处理指南](/api-capabilities/gemini-image-error-handling)。 ## 速查总结 * 谷歌官方上限**每次请求最多 14 张参考图**,API易已验证完全支持,实测调用成功、融合合理。 * 自测时建议**两种方法都做**:数字卡片法验证"全不全",真实场景拆解法验证"合不合理"。 * 14 张 2K 原图合计约 40MB 级别,**在 API易 100MB / 谷歌单图 7MB 的限制范围内不会被拒绝**,但压缩上传耗时更短,仍是推荐做法。 * 多图请求的 `parts` 结构:**1 个 text + N 个 inlineData**,二者不能混在同一个 part 里。 * 遇到 `IMAGE_SAFETY` 空图返回,先重试 1-2 次,往往就能成功,且不计费。 ## 相关文档 * [Nano Banana 系列开发指南](/api-capabilities/nano-banana-dev-guide) * [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution) * [Gemini 生图 API 错误处理指南](/api-capabilities/gemini-image-error-handling) * [如何生成满意的图片](/api-capabilities/image-generation-success-tips) # 多轮对话实现指南 Source: https://docs.apiyi.com/api-capabilities/multi-turn-conversation 在 API易 实现多轮对话:OpenAI 兼容模式(多模型通用)、OpenAI/Gemini/Anthropic 原生格式的历史维护方式、对比与常见问题。 大模型本身**没有记忆**——它不会记得你上一句说了什么。所谓"多轮对话",本质是**每次请求都把完整的对话历史一起发给模型**。本指南讲清在 API易 平台上,四种调用格式各自怎么维护历史、有哪些坑。 本文示例端点统一为 `https://api.apiyi.com`,密钥用你的 [API易 令牌](https://api.apiyi.com/token)。涉及模型:`gpt-5.4-mini`、`deepseek-v4-pro`、`gemini-3.5-flash`、`claude-sonnet-4-6`。 ## 核心原理:自己维护历史 记住一句话就够了:**模型无状态,对话历史由你(客户端)维护,每轮把全部历史重新发一遍。** ```text theme={null} 第1轮:发 [用户问1] → 得 [回复1] 第2轮:发 [用户问1, 回复1, 用户问2] → 得 [回复2] 第3轮:发 [用户问1, 回复1, 用户问2, 回复2, 用户问3] → 得 [回复3] ``` 每多一轮,就把上一轮的"用户提问"和"模型回复"**追加**到历史数组末尾,再整体发出。四种格式的差异只是**历史数组叫什么名字、角色怎么写**而已。 **在 API易 平台,请一律采用"自己维护历史"的方式。** 不要依赖任何服务端会话状态(如 OpenAI Responses 的 `previous_response_id`)——经网关转发后该机制不保证生效,详见下文 OpenAI 原生一节。 ## OpenAI 兼容模式(多模型通用) 最通用的方式,端点 `/v1/chat/completions`。历史放在 `messages` 数组里,每条带 `role`(`system` / `user` / `assistant`)。**换个 `model` 名就能用同一套代码调不同模型**(gpt、deepseek、claude、gemini…)。 ```python Python theme={null} from openai import OpenAI client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1") messages = [{"role": "system", "content": "你是一个友好的助手。"}] def chat(user_input, model="gpt-5.4-mini"): messages.append({"role": "user", "content": user_input}) resp = client.chat.completions.create(model=model, messages=messages) reply = resp.choices[0].message.content messages.append({"role": "assistant", "content": reply}) # 追加回复进历史 return reply print(chat("我叫小明,今年28岁,请记住。")) print(chat("我今年多少岁?再加5岁是多少?")) # 模型记得 → 28,33 ``` ```javascript Node.js theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'YOUR_API_KEY', baseURL: 'https://api.apiyi.com/v1' }); const messages = [{ role: 'system', content: '你是一个友好的助手。' }]; async function chat(userInput, model = 'gpt-5.4-mini') { messages.push({ role: 'user', content: userInput }); const resp = await client.chat.completions.create({ model, messages }); const reply = resp.choices[0].message.content; messages.push({ role: 'assistant', content: reply }); // 追加回复进历史 return reply; } console.log(await chat('我叫小明,今年28岁,请记住。')); console.log(await chat('我今年多少岁?')); ``` ```bash cURL theme={null} {/* 第2轮:把第1轮的问与答都带上 */} curl https://api.apiyi.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '{ "model": "deepseek-v4-pro", "messages": [ {"role": "user", "content": "我叫小明,今年28岁,请记住。"}, {"role": "assistant", "content": "好的,我记住了:你叫小明,今年28岁。"}, {"role": "user", "content": "我今年多少岁?"} ] }' ``` **一套代码多模型**:把上面的 `model` 换成 `deepseek-v4-pro`、`claude-sonnet-4-6`、`gemini-3.5-flash` 等任意模型,多轮逻辑完全不变。模型清单见 [模型与价格总览](/api-capabilities/model-info)。 ### 推理模型的历史处理 像 `deepseek-v4-pro` 这样的推理模型,响应里会多一个 `reasoning_content`(思考过程)字段。 **历史里只放 `content`,不要回传 `reasoning_content`。** 思考过程只是本轮的中间产物,回传它既浪费 token,也不符合上游规范(DeepSeek 官方直连甚至会因此报 400)。正确做法是追加历史时只取 `content`: ```python theme={null} messages.append({"role": "assistant", "content": resp.choices[0].message.content}) # 不要把 resp.choices[0].message.reasoning_content 放进去 ``` 推理模型在响应解析上的更多细节,见 [推理模型输出](/api-capabilities/openai/reasoning-models)。 ## OpenAI 原生格式(Responses API) 端点 `/v1/responses`。多轮时**把完整历史作为 `input` 数组**传入(每条带 `role` / `content`),用法与兼容模式同理: ```python theme={null} from openai import OpenAI client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1") resp = client.responses.create( model="gpt-5.4-mini", input=[ {"role": "user", "content": "记住暗号:紫色大象。"}, {"role": "assistant", "content": "记住了,暗号是紫色大象。"}, {"role": "user", "content": "暗号是什么?"}, ], ) print(resp.output_text) # 暗号是:紫色大象。 ``` **不要依赖 `previous_response_id` / `conversation` / `store` 等服务端状态。** 经 API易 网关实测:传 `previous_response_id` 不报错(返回 200),但下一轮**并不会记得**上一轮内容,`GET /v1/responses/{id}` 也不可用。因此在 API易 平台,Responses API 也请按上面的**自管理历史**(`input` 数组)方式使用。 ## Gemini 原生格式 端点 `/v1beta/models/{model}:generateContent`。历史放在 `contents` 数组里,注意 **role 取值是 `user` / `model`**(不是 `assistant`),每条的内容在 `parts` 里。 ```python theme={null} from google import genai from google.genai import types client = genai.Client(api_key="YOUR_API_KEY", http_options={"base_url": "https://api.apiyi.com"}) contents = [ {"role": "user", "parts": [{"text": "记住暗号:紫色大象。"}]}, {"role": "model", "parts": [{"text": "好的,我记住了:紫色大象。"}]}, {"role": "user", "parts": [{"text": "暗号是什么?"}]}, ] resp = client.models.generate_content(model="gemini-3.5-flash", contents=contents) print(resp.text) # 暗号是:紫色大象。 ``` **更省事的写法**:官方 `google-genai` SDK 的 `client.chats.create(...)` 会自动维护 `contents` 历史,你只管 `send_message`,无需手动拼接。 Gemini 3 系列响应的 part 上会带 `thoughtSignature`(思维签名)。**普通文本多轮只回传 `text` 即可记住上下文**(更省 token);只有在**函数调用**等需要严格推理连续性的场景才需把 `thoughtSignature` 原样回传——官方 SDK 会自动处理。详见 [Gemini 原生调用](/api-capabilities/gemini/native) 与 [函数调用](/api-capabilities/gemini/function-calling)。 ## Anthropic 原生格式 端点 `/v1/messages`。历史放在 `messages` 数组里,role 取值 `user` / `assistant`,`content` 用字符串简写即可。注意 **`max_tokens` 必填**。 ```python theme={null} import requests def chat(messages): r = requests.post( "https://api.apiyi.com/v1/messages", headers={ "content-type": "application/json", "anthropic-version": "2023-06-01", "x-api-key": "YOUR_API_KEY", }, json={"model": "claude-sonnet-4-6", "max_tokens": 200, "messages": messages}, timeout=60, ) return "".join(b["text"] for b in r.json()["content"] if b["type"] == "text") messages = [{"role": "user", "content": "记住暗号:紫色大象。"}] reply = chat(messages) messages.append({"role": "assistant", "content": reply}) # 追加回复 messages.append({"role": "user", "content": "暗号是什么?"}) print(chat(messages)) # 暗号是:紫色大象。 ``` 也可以用官方 `anthropic` SDK,把 base\_url 指向 `https://api.apiyi.com` 即可。响应是 `content` 块数组,解析细节见 [Claude 流式与非流式响应](/api-capabilities/claude-response-handling)。 ## 四种格式对比 | 维度 | OpenAI 兼容 | OpenAI 原生(Responses) | Gemini 原生 | Anthropic 原生 | | - | - | - | - | - | | 端点 | `/v1/chat/completions` | `/v1/responses` | `/v1beta/…:generateContent` | `/v1/messages` | | 历史字段 | `messages` | `input` | `contents` | `messages` | | role 取值 | system/user/assistant | user/assistant | **user/model** | user/assistant | | 内容写法 | `content` 字符串 | `content` 字符串 | `parts: [{text}]` | `content` 字符串 | | 历史维护方 | 自己拼 | 自己拼 | 自己拼 | 自己拼 | | 服务端会话状态 | 无 | ⚠️ 不可用 | 无 | 无 | | 多模型通用 | ✅ 换 model 名即可 | 仅 OpenAI 系 | 仅 Gemini | 仅 Claude | **选型建议**:要用同一套代码调多家模型 → 首选 **OpenAI 兼容模式**;要用某家原生独有能力(Gemini 思维签名 / 代码执行、Claude 思考块与缓存、OpenAI 内置工具)→ 用对应**原生格式**。 ## 常见问题 是。每轮都要把完整历史重新发送,所以**输入 token 随轮数增长**,费用也随之上升。省钱主要靠**上下文缓存**:相同的历史前缀会自动命中缓存价(远低于原价)。各家缓存见 [OpenAI 缓存](/api-capabilities/openai/prompt-caching)、[Claude 缓存](/api-capabilities/claude-prompt-caching)、[Gemini 缓存](/api-capabilities/gemini/prompt-caching)。 没有硬性要求,但历史越长越贵、也可能超出模型上下文窗口。常见策略:① **滑动窗口**——只保留最近 N 轮;② **摘要压缩**——把早期对话总结成一段话放进 system;③ 始终保留 system 指令 + 最近若干轮。按业务对"记忆深度"的需要权衡。 OpenAI 兼容与 Anthropic:放在 `messages` 最前面(兼容模式用 `role:"system"`;Anthropic 用顶层 `system` 字段或首条消息)。Gemini:用 `config.system_instruction`。系统指令**只需设置一次**,不必每轮重复追加。 **不要。** 思考过程是本轮中间产物,历史里只回传最终 `content`(Gemini 只回传 `text`)。回传思考既费 token,部分上游还会报错。函数调用场景下 Gemini 的 `thoughtSignature` 是例外——官方 SDK 会自动处理。 在 API易 平台**不建议依赖服务端会话状态**。OpenAI Responses 的 `previous_response_id` 经网关转发后不保证生效(实测不记忆)。请统一采用客户端自维护历史的方式,行为最稳定、跨模型一致。 ## 相关链接 * 调用基础:[OpenAI 兼容模式调用](/api-capabilities/openai/compatible) · [OpenAI 原生调用](/api-capabilities/openai/native) · [Gemini 原生调用](/api-capabilities/gemini/native) · [Claude API 基础](/api-capabilities/claude) * 响应解析:[OpenAI 响应数据处理](/api-capabilities/openai/response-handling) · [推理模型输出](/api-capabilities/openai/reasoning-models) · [Claude 流式与非流式响应](/api-capabilities/claude-response-handling) · [Gemini 流式与非流式响应](/api-capabilities/gemini/response-handling) * 模型与价格:[模型与价格总览](/api-capabilities/model-info) * 获取 / 管理令牌:`https://api.apiyi.com/token` # Nano Banana 2 生图/编辑 Source: https://docs.apiyi.com/api-capabilities/nano-banana-2-image/overview 谷歌最新图像生成模型 Nano Banana 2 (gemini-3.1-flash-image-preview),Pro级画质、Flash级速度,支持4K输出、14种宽高比、图像搜索Grounding,按次 $0.055/次,按量低至 $0.025/张。 ## 概述 **Nano Banana 2**(代号)是谷歌于 2026 年 2 月 26 日发布的最新图像生成模型,模型 ID 为 `gemini-3.1-flash-image-preview`。它以 **Pro 级画质 + Flash 级速度和成本** 重新定义了图像生成的性价比,是 Nano Banana 系列的最新旗舰。 **🔥 2026年2月26日发布**:Nano Banana 2 上线!Pro 级画质、Flash 级速度,支持按量计费(低至官网 36%),512px 低至 \$0.025/张!支持 4K 输出、14 种宽高比、图像搜索 Grounding 等独家特性。 **🆕 2026年5月29日更新(去掉 `-preview`)**:谷歌更新了官方文档,推出去掉 `-preview` 的正式模型名 **`gemini-3.1-flash-image`**,API易已同步上线支持。 * **原模型名仍可用**:`gemini-3.1-flash-image-preview` 继续正常调用,**价格不变**,现有代码无需改动。 * **两个名字都能跑**:可按需选用新名 `gemini-3.1-flash-image` 或原 `-preview` 名。 话说在前面:正式版相比 preview 版在效果表现、安全审核机制等方面是否存在差异,谷歌官方暂未明确说明,欢迎大家在实际使用中一起测试反馈。 **🆕 2026 年 10 月 6 日:Nano Banana 2.1 发布**(`gemini-nano-banana-2.1`)。谷歌推荐新项目使用 2.1:画质、文字渲染和多轮一致性更好,API易 按次 \$0.045/次(10-13 起由 \$0.05 降价)。Nano Banana 2 继续可用、价格不变。详见 [Nano Banana 2.1 生图/编辑](/api-capabilities/gemini-nano-banana-2.1/overview)。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 输入文本提示词生成图片,带交互式 Playground 在线调试。 上传图片 + 编辑指令生成新图片,带交互式 Playground 在线调试。 ## 让 AI Agent 帮你接入 帮我在当前项目里接入 / 排查 Nano Banana 2(`gemini-3.1-flash-image`)的「文生图 + 图片编辑」。 先读文档再动手:抓 [https://docs.apiyi.com/api-capabilities/nano-banana-2-image/overview.md](https://docs.apiyi.com/api-capabilities/nano-banana-2-image/overview.md) 拿到本页纯文本版;需要更细的参数说明时,text-to-image 和 image-edit 两页同样在地址后加 `.md` 即可。 接入要求: 1. 超时:走 Gemini 原生格式 `POST https://api.apiyi.com/v1beta/models/gemini-3.1-flash-image:generateContent`,客户端 timeout 提到 360 秒兜底。图片接口是同步调用,没有任务 ID,客户端一断连结果就丢了、但这次请求照样计费。反向代理、网关、Serverless 执行上限这些中间层也要一起放宽,任何一层小于生成时间都会掐断请求。如果你用 Node,注意 undici 有三个独立的超时设置,SDK 的 `timeout` 并不覆盖它们。 2. 返回解析(**最容易写错的一条**):图片是 base64,在 `candidates[0].content.parts[]` 里的 `inlineData.data`。但 `parts` 是**异构数组,段数和顺序都不保证**——前面可能挂一个文本段,图片就落到下标 1 而不是 0。本模型尤其容易出现这种情况,因为它支持返回思维过程文本。所以**绝对不要写死 `parts[0]` 或 `parts[1]`**,在两者之间来回改是解决不了问题的。正确写法:遍历 `parts`、筛出所有含 `inlineData` 的段,取**最后一张**(复杂任务会返回多张中间稿,最后一张才是终稿)。`mimeType` 也从响应里读,不要写死成 `image/png`。拿到后渲染展示并提供「保存到本地」。 3. 上传压缩:编辑时把参考图 base64 塞进 `inlineData`。上传前先压缩——超过 1.5MB 才处理,长边等比缩到 2048px 以内(不放大小图),以质量 0.9 重编码、保持原格式;多图时合计控制在 6MB 以内。官方硬限制是单图 7MB、每次请求最多 14 张,而 base64 编码后体积还会再膨胀约三分之一,所以单图尽量压到 5MB 以内留足余量。某张图压缩失败就回退用原图继续,不要因为压缩失败中断整个请求。另外注意:**同一个 part 里只能放 `text` 或 `inlineData` 其中一个**,不能两个字段并存,正确结构是 1 个文本段 + N 个图片段。 4. 分辨率参数:**必须显式传** `generationConfig.imageConfig.imageSize`(`512` / `1K` / `2K` / `4K`,默认 `1K`)和 `aspectRatio`(本页列了 14 个合法比例)。本模型按量计费时**分辨率直接决定单价**,靠默认值会让成本不可控。前台界面把分辨率和比例都做成下拉。另外:代码里模型名统一用 `gemini-3.1-flash-image`,**不要用带 `-4k` 后缀的那个名字**(那是给聊天客户端用的),需要 4K 直接在 `imageSize` 里指定即可。本模型不支持 Google 搜索接地(`tools` 里的 `google_search`),不要传。 5. 错误处理:内容审核拦截时 HTTP 仍然是 200,但 `candidates[0].content.parts` 为空。判断顺序是先看 `candidatesTokenCount` 是否为 0,再看 `finishReason` 是否非 `STOP`。`IMAGE_SAFETY` 这类拦截**不计费**,原样重试 1-2 次往往就成功了,建议在代码里对它做自动重试。 6. Key 从环境变量 `APIYI_API_KEY` 读,用 `Authorization` 头加 `Bearer` 前缀传,不要硬编码进代码、也不要提交进 git。 7. 改完真跑一次文生图 + 一次图片编辑,把出图结果和这两次调用的花费贴给我。 | 要求 | 挡掉的坑 | | - | - | | 不写死 `parts` 下标 | `parts` 段数和顺序都不保证,写死下标一定会间歇性失败;本模型会返回思维过程文本,更容易把图片挤到下标 1。详见 [Nano Banana 开发指南](/api-capabilities/nano-banana-dev-guide) | | 取最后一张图片段 | 复杂编辑任务会返回多张中间稿,最后一张才是终稿 | | 显式传 `imageSize` | 按量计费下分辨率直接决定单价,靠默认值成本不可控 | | 模型名不带 `-4k` 后缀 | 带后缀的名字是给聊天客户端用的,代码调用统一用通用名更稳定 | | 上传前压缩 | 官方单图上限 7MB,base64 后还会再膨胀约三分之一。压缩标准见 [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution) | | 对 `IMAGE_SAFETY` 自动重试 | 审核拦截返回 200 但没有图,且不计费,原样重试往往就过了。详见 [Gemini 出图错误处理](/api-capabilities/gemini-image-error-handling) | ## 为什么选 API易 的 Nano Banana 2 **Nano Banana Pro / 2 是 API易 消耗量排名第一的图像模型**——稳定、可靠、速度快。如果你期望跟专业的团队合作,那选 API易 就对了。 针对 Nano Banana 2 这种谷歌官方刚发布、产能仍紧张的新模型,API易 在**稳定性**、**成本**、**接入体验**三方面做了深度优化: 完全兼容谷歌官方 Gemini API 格式(`/v1beta/models/.../generateContent`),同时支持 OpenAI SDK 模式,请求体、响应字段、错误码与官方一致,迁移零改造。 无谷歌 AI Studio 的 RPM/RPD 硬限制,企业批量出图、高峰流量都能线性放大,避免被官方配额打断。 按次 \$0.055/张(官方 \$0.151)、按量 512px 低至 \$0.025/张(官方 \$0.045),叠加 [充值加赠活动](/faq/recharge-promotions) 最低可达官方 30.3%。 **无需海外服务器或代理**,国内机房、家宽网络、海外节点均可直连 `api.apiyi.com`,延迟稳定、免去出海改造。 同系列覆盖 [Nano Banana Pro](/api-capabilities/nano-banana-image/overview)(极致画质)、Nano Banana 2(性价比)、Nano Banana 第一代(基础版),按场景自由组合。 团队深耕图像生成场景,具备丰富的选型、调优与集成经验,可为企业客户提供从 PoC 到生产上线的完整技术支持。 ## 核心特性 生动光影、丰富纹理、锐利细节,画质媲美 Nano Banana Pro,速度却快得多 支持 512px、1K、2K、4K 四档分辨率,最高 4096×4096 新增 1:4、4:1、1:8、8:1,共支持 14 种宽高比,覆盖更多场景 Nano Banana 2 独家功能,可从 Google 图片搜索拉取视觉上下文 图片内文字清晰可读,支持多语言文本渲染,适合海报、营销物料 支持多轮对话式图像编辑,逐步精调画面效果 可配置 minimal 或 high 思维级别,处理复杂提示词更精准 最多保持 5 个角色的相貌一致性,14 个参考物体的高保真度 ## 版本对比 | 特性 | **Nano Banana 2** | Nano Banana 2.1 | Nano Banana Pro | Nano Banana | | - | - | - | - | - | | 模型 ID | `gemini-3.1-flash-image-preview` | `gemini-nano-banana-2.1` | `gemini-3-pro-image-preview` | `gemini-2.5-flash-image` | | 画质 | ⭐⭐⭐⭐⭐ Pro 级 | ⭐⭐⭐⭐⭐ 更好 | ⭐⭐⭐⭐⭐ 最高 | ⭐⭐⭐⭐ 优秀 | | 速度 | 🚀 最快 | 🚀 快 | 🐢 较慢 | ⚡ 快速 | | 最高分辨率 | 4K | 4K(无 512px) | 4K | 1K | | 宽高比数量 | 14 种 | 14 种 | 10 种 | 10 种 | | 图像搜索 Grounding | ✅ 独家 | ✅ | ❌ | ❌ | | API易定价 | **\$0.055/次(按次)** | \$0.045/次 | \$0.09/次 | \$0.02/次 | | 状态 | Preview | GA | Preview | GA | **选择建议**: * 🔥 **追求性价比** → Nano Banana 2(按量计费低至 \$0.025/张,Pro 级画质 + Flash 级速度) * 🎨 **追求极致画质** → Nano Banana Pro(\$0.09/次,最高保真度) * ⚡ **追求最低成本** → Nano Banana(\$0.02/次,快速稳定) ## 模型定价 **计费模式选择**:Nano Banana 2 支持两种计费方式,通过创建令牌时的「Billing model」设置选择: * 选择 **Pay-as-you-go**(按量计费)或 **Pay-as-you-go Priority**(按量优先)→ 按量计费 * 选择 **Pay-per-request**(按次计费)或 **Pay-per-request Priority**(按次优先)→ 按次计费(与 Nano Banana Pro 相同) * ⚠️ **请勿选择 Hybrid billing(混合计费)** ### 按次计费 | 模型 | API易定价 | 谷歌官方 4K 定价 | 折扣 | | - | - | - | - | | **Nano Banana 2** `gemini-3.1-flash-image-preview` | **\$0.055/次** | \$0.151/次 | **🔥 约 3.6 折** | | Nano Banana Pro `gemini-3-pro-image-preview` | \$0.09/次 | \$0.151/次 | **约 6 折** | | Nano Banana `gemini-2.5-flash-image` | \$0.020/次 | \$0.039/次(仅1K) | 约 5 折 | Nano Banana Pro 另提供 `NanoBananaEnterprise` 企业 HA 通道(1.4 倍费率,即 \$0.126/次),适合对可用性有更高要求的场景。 ### 按量计费(Nano Banana 2 专属) | 计费项目 | Google 官方 | API易 | 官网折扣 | | - | - | - | - | | Input | \$0.50/M tokens | \$0.18/M tokens | **36%** | | Output(图片和文本统一) | 图片 \$60/M, 文本 \$1.5/M | \$21.6/M tokens | **36%** | ### 按量计费价格预估 | 分辨率 | Google 官方 | API易 | Fal AI | | - | - | - | - | | 512px | \$0.045 | **\~\$0.025** | \$0.06 | | 1K | \$0.067 | **\~\$0.035** | \$0.08 | | 2K | \$0.101 | **\~\$0.045** | \$0.12 | | 4K | \$0.151 | **\~\$0.07** | \$0.16 | **💰 按量计费更省钱!** 使用按量计费,512px 低至 \$0.025/张,仅为官方 36%!低分辨率场景下比按次计费(\$0.055/次)更实惠。4K 场景按量计费约 \$0.07/张,仍远低于谷歌官方 \$0.151/次。结合充值加赠活动,实际成本更低。 ## 影响计费的三个参数 以下结论来自 2026-08-27 对生产网关的实测(每个条件 12–20 次,全部以后台实际扣费为准,非估算)。三个参数对账单的影响差了两个数量级,优化顺序应该按下表来。 | 参数 | 对单次账单的影响 | 值不值得调 | | - | - | - | | `imageConfig.imageSize` | 1K \$0.033 → 4K \$0.064,**+90%** | ✅ 最大杠杆 | | `thinkingConfig.thinkingLevel` | 默认 \$0.033 → `high` \$0.052,**+54%** | ✅ 按需开 | | `responseModalities` | −2.8%,统计上不显著 | ❌ 省不到钱 | ### thinkingLevel:默认就是 minimal,high 贵一半 `gemini-3.1-flash-image` 支持思考等级调节(Pro 不支持)。实测同一提示词各 20 次: | 设置 | 输出 tokens 均值 | `thoughtsTokenCount` | 单次扣费 | 耗时中位 | | - | - | - | - | - | | 不传(默认) | 1548.7 | 不出现该字段 | \$0.0335 | 13.3s | | `minimal` | 1546.0 | 不出现该字段 | \$0.0334 | 13.6s | | `high` | 1557.5 | 中位 784.5 | **\$0.0516** | 20.7s | * **默认档就等于 `minimal`**,两者统计上不可区分(p=0.64),显式传 `minimal` 没有任何收益。 * `high` 档下 `thoughtsTokenCount` 才作为独立字段出现,并**计入 `completion_tokens` 一起按图片价计费**。 * **只有开了 `high`,复杂提示词才会烧更多 tokens**:默认档下,3 tokens 的极简提示词与 98 tokens 的推理型提示词,输出 tokens 无显著差异(p=0.37);开了 `high` 之后,推理型提示词的思考 tokens 比极简提示词多 **126%**,单次账单贵 **38%**。 日常出图**不用动这个参数**。只有对构图逻辑、画面内文字排版、数据图表比例准确性有硬要求时才开 `high`,代价是 +54% 费用和 +55% 耗时。 ### Google 搜索接地:能力可用,但按检索次数另收费 需要实时信息才能画对的场景(天气卡、行情图、近期活动海报),可以挂 `googleSearch` 工具。实测 12/12 触发接地。 ```json theme={null} { "contents": [{ "parts": [{ "text": "画一张东京今天天气的卡片海报" }] }], "tools": [{ "googleSearch": {} }] } ``` **输出 tokens 几乎不受影响**(扣掉搜索费后与无工具对照差 −2.1%,不显著),成本全部来自检索调用本身:**\$0.014/次**。 | 条件 | 接地触发 | 检索次数中位 | 单次账单 | | - | - | - | - | | 不挂工具 | 0/12 | 0 | \$0.036 | | `googleSearch: {}` | 12/12 | 2 | **\$0.062(+73%)** | **检索次数由模型自己决定,客户无法预先控制**。实测一次出图请求会自主发起 1–3 次检索,所以开了这个工具后单次成本是 **\$0.050–\$0.078** 的区间,不是定值。做成本预估时按上限算。 ### 图片搜索接地:Nano Banana 2 独有,且不额外收费 `searchTypes.imageSearch` 让模型从 Google 图片搜索拉取视觉参考,适合「用真实照片做拼贴」「照着实物画」这类需求。 ```json theme={null} { "tools": [{ "googleSearch": { "searchTypes": { "imageSearch": {} } } }] } ``` | 条件 | 接地触发 | 计搜索费 | 单次账单 | | - | - | - | - | | 不挂工具 | 0/12 | 0/12 | \$0.036 | | `imageSearch` 单开 | 7/12 | **0/12** | **\$0.032** | * 生效证据是 `groundingMetadata.imageSearchQueries` 出现,实测值形如 `["current weather in Tokyo"]`。 * **目前不额外收费**,单次账单甚至略低于无工具对照。 * 12 次里 7 次触发——**是否检索由模型自己判断**,不是每次都查。 * 网页搜索和图片搜索**可以同时开**,但选用哪条由模型决定,费用也就跟着不确定。 **`searchTypes` 必须写成对象,不能写成数组。** ✅ `{"searchTypes": {"imageSearch": {}}}` — 生效 ❌ `{"searchTypes": ["imageSearch"]}` — **不报错、返回 200、图也照出,但图片搜索一次都不会触发**,只看返回体察觉不到。上游对数组写法的报错是 `Proto field is not repeating, cannot start list`。 ### responseModalities:省不到钱,但能去掉多余文本 `responseModalities: ["IMAGE"]` 声明只要图片。实测各 20 次: | 设置 | 输出 tokens 均值 | 单次扣费 | | - | - | - | | 不传(默认) | 1548.7 | \$0.0335 | | `["IMAGE"]` | 1535.0 | \$0.0332 | | `["TEXT","IMAGE"]` | 1542.8 | \$0.0333 | * **三者差异都在 1% 以内**,`["TEXT","IMAGE"]` 与不传完全等价(p=0.90)。 * 普通出图提示词下 **60/60 本来就只返回一个图片 part、一个字文本都没有**,所以模态开关无从体现。只有推理型提示词(要求画信息图、数据图表)才有约 1/3 概率附一段总结文字,此时 `["IMAGE"]` 能把它抑制掉,省约 **2.8%**(p=0.09,不显著)。 **为什么省不下来**:输出 tokens 的构成不是「图片 + 文本」,而是 **图片 1120 + 约 400 tokens 的不可见开销**。后者不体现在 `candidatesTokensDetails` 的任何字段里,但**照价计费,占单次账单的 28%**,且不随提示词长度变化。文本那点量在它面前微不足道。 所以按 1120 tokens 估算单张成本会**低估约 28%**,实际请按 `candidatesTokenCount` 或 `totalTokenCount` 对账。 ## 分组介绍 Nano Banana 2 在 API易提供两个分组,可在后台「令牌设置」中切换: | 分组 | 倍率 | 适用场景 | | - | - | - | | `Default` 默认分组 | 1.0x | 基础通道,与定价表一致;默认推荐 | | `NanoBananaEnterprise` 企业分组 | 1.4x | 兜底通道,默认紧张/超时高发时手动切换,稳定优先 | **1.4x 倍率怎么来的?** 1.4x 后仍约等于谷歌官方 5 折水平,远低于官网原价。这是面向更高并发需求、应对意外风控时的兜底方案,为企业客户提供高可用性保障。默认分组紧张时,把令牌切到 `NanoBananaEnterprise` 即可临时过渡。 **令牌「计费模式」推荐**:选 `按量优先`(Pay-as-you-go Priority)—— 同时兼容 Nano Banana 2 的按量计费 和 Nano Banana Pro 的按次计费,**一把令牌跑全系列**。 令牌创建界面:计费模式『按量优先』兼容 NB2 按量 + NB Pro 按次;主分组 Default + 兜底分组 NanoBananaEnterprise(1.4x 兜底通道) **拓展玩法**:如果你的令牌还覆盖其它图像模型(如 GPT-image-2),把更稳的 Default 分组放主位、`NanoBananaEnterprise` 放兜底位即可,主分组 429 会自动回退到企业分组继续出图,无需切换 token。 ## 支持的分辨率与宽高比 ### 输出分辨率 | 分辨率 | 说明 | 推荐场景 | | - | - | - | | 512px | 低分辨率 | 缩略图、快速预览 | | 1K | 默认 | 社交媒体、网页展示 | | 2K | 高清 | 高清显示、打印材料 | | 4K | 超高清 | 专业设计、商业海报 | ### 支持的宽高比(14 种) `1:1`、`1:4`、`4:1`、`1:8`、`8:1`、`2:3`、`3:2`、`3:4`、`4:3`、`4:5`、`5:4`、`9:16`、`16:9`、`21:9` ### 各宽高比的输出尺寸(像素) 下表为 Nano Banana 2 在 512px / 1K / 2K / 4K 四档分辨率下、14 种宽高比对应的实际输出像素尺寸(数据来源:谷歌官方文档)。在请求中通过 `aspect_ratio` 指定宽高比、`image_size`(或 `resolution`)指定分辨率即可: | 宽高比 | 512px | 1K | 2K | 4K | | - | - | - | - | - | | **1:1** | 512×512 | 1024×1024 | 2048×2048 | 4096×4096 | | **1:4** | 256×1024 | 512×2048 | 1024×4096 | 2048×8192 | | **1:8** | 192×1536 | 384×3072 | 768×6144 | 1536×12288 | | **2:3** | 424×632 | 848×1264 | 1696×2528 | 3392×5056 | | **3:2** | 632×424 | 1264×848 | 2528×1696 | 5056×3392 | | **3:4** | 448×600 | 896×1200 | 1792×2400 | 3584×4800 | | **4:1** | 1024×256 | 2048×512 | 4096×1024 | 8192×2048 | | **4:3** | 600×448 | 1200×896 | 2400×1792 | 4800×3584 | | **4:5** | 464×576 | 928×1152 | 1856×2304 | 3712×4608 | | **5:4** | 576×464 | 1152×928 | 2304×1856 | 4608×3712 | | **8:1** | 1536×192 | 3072×384 | 6144×768 | 12288×1536 | | **9:16** | 384×688 | 768×1376 | 1536×2752 | 3072×5504 | | **16:9** | 688×384 | 1376×768 | 2752×1536 | 5504×3072 | | **21:9** | 792×168 | 1584×672 | 3168×1344 | 6336×2688 | 相比 Nano Banana Pro 的 10 种宽高比,Nano Banana 2 新增了 `1:4`、`4:1`、`1:8`、`8:1` 四种超长/超宽比例,适合长图、信息图等特殊场景;并独有 512px 低分辨率档位,适合缩略图与快速预览。 ## 常见问题 **Nano Banana 2** (`gemini-3.1-flash-image-preview`) 基于 Gemini 3.1 Flash,**Nano Banana Pro** (`gemini-3-pro-image-preview`) 基于 Gemini 3 Pro。主要区别: * ✅ **速度**:Nano Banana 2 更快(Flash 级速度) * ✅ **价格**:Nano Banana 2 按量计费更便宜(低至 \$0.025 vs \$0.09) * ✅ **宽高比**:Nano Banana 2 支持 14 种(多 4 种) * ✅ **图像搜索 Grounding**:Nano Banana 2 独家 * ⚠️ **极致画质**:Nano Banana Pro 仍略优 **推荐切换**!Nano Banana 2 以更低的价格提供了接近 Pro 级别的画质,速度还更快。除非你对画质有极致要求,否则 Nano Banana 2 是更好的选择。 只需将模型名称从 `gemini-3-pro-image-preview` 改为 `gemini-3.1-flash-image-preview` 即可。 **代码 / API 调用,建议统一用不带 `-4k` 后缀的通用模型名 `gemini-3.1-flash-image-preview`,而不是 `gemini-3.1-flash-image-preview-4k`。** * **官方命名不带 `-4k`**:谷歌官方的模型名就是 `gemini-3.1-flash-image-preview`。这也是我们投入最多资源持续维护的通用通道,稳定性和兼容性最有保障。 * **`-4k` 的由来**:`gemini-3.1-flash-image-preview-4k` 当初是为 Chatbox 等对话式客户端的「对话出图」场景准备的配置——在聊天界面里直接对话生成图片时使用。 * **代码 + Gemini 原生格式更推荐通用名**:如果你是通过代码调用、走 Gemini 原生格式(`/v1beta/models/.../generateContent`),用常规模型名 `gemini-3.1-flash-image-preview` 会更稳定。 需要 4K 分辨率输出时,无需依赖 `-4k` 模型名——直接在请求参数里指定 4K 分辨率即可(见上方「支持的分辨率与宽高比」)。 图像搜索 Grounding 是 Nano Banana 2 的独家功能。它可以从 Google 图片搜索拉取视觉上下文,生成更贴合现实世界的图像。例如,当你要求生成某个真实地标的图片时,它能参考搜索结果来提高准确性。 思维模式(Thinking Mode)让模型在生成图像前进行推理分析,对复杂提示词的理解更准确。设置为 `high` 时效果最好,但会稍微增加生成时间。适合需要精确构图、包含文字、或涉及复杂场景的生成任务。 生成时间取决于分辨率和是否启用思维模式: * **1K 分辨率**:约 5-10 秒 * **2K 分辨率**:约 10-15 秒 * **4K 分辨率**:约 15-25 秒 * 启用高级思维模式会额外增加几秒 建议设置较长的超时时间(至少 360 秒)以应对偶发延迟和高峰拥塞。 **API 不限制并发,也不是串行处理。** 你可以放心地自行并发调用,请求之间不用排队、不会互相阻塞——20 个用户同时调用直接发起 20 个并发请求即可,无需额外申请配额或做限流。 与谷歌 AI Studio 不同,API易 的通道没有 RPM/RPD 硬限制,企业批量出图、高峰流量都能线性放大。 **真正要注意的是 `timeout` 超时时间**:图像生成(尤其 4K 或高峰拥塞时)单次耗时可能较长,**建议把客户端超时设到 360 秒**,避免请求还在正常处理就被本地超时掐断。 如果偶发 429(并发过高被限流),可把令牌的兜底分组挂上 `NanoBananaEnterprise`(见上方「分组介绍」),主分组打满时自动回退,进一步提升高并发下的成功率。 支持 `image/png` 和 `image/jpeg` 格式。可以通过 base64 编码或 Files API 上传。 所有输出图片都带有 SynthID 隐形数字水印(Google 的 AI 生成内容标识技术),肉眼不可见,不影响使用。 完整报错形如: ```text theme={null} [&{{write tcp ip:port->ip:port: write: connection reset by peer Unknown error shell_api_error write_response_body_failed} 500 }] ``` 这种错误**往往是上传的图片体积过大,请求体超限把连接压崩了**。请按以下最佳实践处理: * **控制图片张数**:保持在官方规则内(每个提示最多 14 张图),不要堆图。 * **控制单图体积**:每张图尽量不要超过 5MB——官方单图上限为 7MB,且 base64 编码后体积还会膨胀约 1/3,原图请留足余量。 * **前端先压缩再上传**:在前端(或服务端中转层)压缩后再提交给接口,常见做法是限制最长边、转 JPEG/WebP 并控制质量参数。 * **改用 URL 传图**:Gemini 原生格式支持 `fileData.fileUri` 直接传图片 URL,可避开 base64 请求体过大的问题,详见 [Nano Banana 开发指南](/api-capabilities/nano-banana-dev-guide)。 ## 相关文档 * [Nano Banana Pro 图片生成](/api-capabilities/nano-banana-image) - 上一代旗舰版 * [Nano Banana 图片编辑](/api-capabilities/nano-banana-image-edit) - 图片编辑功能 * [图像生成对比测试](https://imagen.apiyi.com/) * [API 使用手册](/api-manual) Nano Banana 2 目前处于 Preview 状态,功能和定价可能会有调整。建议关注文档更新获取最新信息。 # 文生图 API 参考 Source: https://docs.apiyi.com/api-capabilities/nano-banana-2-image/text-to-image api-reference/nano-banana-2-generate-openapi.yaml POST /v1beta/models/gemini-3.1-flash-image-preview:generateContent Nano Banana 2 文生图 API 参考与在线调试 — 输入文本描述生成图片 右侧的交互式 Playground 支持下拉选择参数(宽高比、分辨率、响应类型等)。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),即可一键发送请求测试。 **场景说明**:本页用于「文本生成图片」。只需输入提示词即可,无需上传任何图片。如需根据现有图片做编辑,请使用 [图片编辑接口](/api-capabilities/nano-banana-2-image/image-edit)。 **🖥️ 浏览器 Playground 限制(重要)** 本接口的响应里包含 base64 编码的图片(`inlineData.data`,数 MB 量级)。受浏览器渲染限制,右侧 Playground 在收到响应后**可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法把这么长的 base64 显示出来。 **推荐做法**(小白零踩坑): * **直接复制下方"代码示例"中的 Python / Node.js / cURL 到本地运行**,代码会自动 `base64.b64decode` 并把图片**保存为本地文件**。 * 如要在浏览器里试 Playground,把 `imageSize` 设为最小档(如 `512` / `1K`),缩小响应体积。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 ## 代码示例 ### Python ```python theme={null} import requests import base64 API_KEY = "sk-your-api-key" PROMPT = "一只可爱的柴犬坐在樱花树下,水彩画风格,高清细节" response = requests.post( "https://api.apiyi.com/v1beta/models/gemini-3.1-flash-image-preview:generateContent", headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json={ "contents": [{"parts": [{"text": PROMPT}]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }, timeout=300 ).json() img_data = response["candidates"][0]["content"]["parts"][0]["inlineData"]["data"] with open("output.png", 'wb') as f: f.write(base64.b64decode(img_data)) print("图片已保存至 output.png") ``` ### cURL ```bash theme={null} curl -X POST "https://api.apiyi.com/v1beta/models/gemini-3.1-flash-image-preview:generateContent" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "contents": [{"parts": [{"text": "未来主义的城市夜景,霓虹灯,赛博朋克风格"}]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }' ``` ### Node.js ```javascript theme={null} import fs from "fs"; const API_KEY = "sk-your-api-key"; const response = await fetch( "https://api.apiyi.com/v1beta/models/gemini-3.1-flash-image-preview:generateContent", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [{ text: "未来主义的城市夜景,霓虹灯,赛博朋克风格" }] }], generationConfig: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "16:9", imageSize: "2K" } } }) } ); const data = await response.json(); const imgBase64 = data.candidates[0].content.parts[0].inlineData.data; fs.writeFileSync("output.png", Buffer.from(imgBase64, "base64")); ``` ## 参数说明速查 | 参数 | 类型 | 必填 | 说明 | | - | - | - | - | | `contents[].parts[].text` | string | 是 | 文本提示词 | | `generationConfig.responseModalities` | array | 是 | `["IMAGE"]` 或 `["TEXT","IMAGE"]` | | `generationConfig.imageConfig.aspectRatio` | string | 否 | 14 种宽高比,默认 `1:1` | | `generationConfig.imageConfig.imageSize` | string | 否 | `512` / `1K` / `2K` / `4K`,默认 `1K` | | `generationConfig.thinkingConfig.thinkingLevel` | string | 否 | `minimal`(快速)/ `High`(深度推理),默认 `minimal` | | `generationConfig.thinkingConfig.includeThoughts` | boolean | 否 | 是否返回思维过程文本,默认 `false` | 详细的参数文档、可选值和默认值请查看右侧 Playground 中的字段说明,所有 enum 类型字段(如 `aspectRatio`、`imageSize`、`thinkingLevel`)都支持下拉选择,无需手动输入。 **不支持的功能** 以下谷歌官方功能在 API易 中**不支持**,需要单独计费: * **Grounding with Google Search**(谷歌搜索增强):通过 `tools: [{"google_search": {}}]` 调用实时搜索信息 * **Image Search Grounding**(图片搜索增强,Nano Banana 2 独有):通过谷歌图片搜索获取视觉上下文 这些功能需要额外的谷歌搜索 API 费用,暂不纳入 API易 转发范围。 # Nano Banana 系列开发指南 Source: https://docs.apiyi.com/api-capabilities/nano-banana-dev-guide Nano Banana 系列(Pro / 2 / 2 Lite / 第一代)的模型选型、计费、接入端点、开发格式与常见问题一站式指南,帮助开发者快速上手 Gemini 生图 API。 ## 模型卡 | 模型 | 官方模型名 | 计费 | 备注 | | - | - | - | - | | **Nano Banana Pro** | `gemini-3-pro-image-preview` | 固定按次 **\$0.09/次**(约 0.63 元;叠加充值活动后约 0.55 元) | 质量最高 | | **Nano Banana 2.1** | `gemini-nano-banana-2.1` | 按次 **\$0.045/次**(推荐 4K 出图使用);或按量动态计费,1K 约 **\$0.026**、4K 约 **\$0.062** | 2026-10 新版,画质与文字渲染更好,不支持 512px | | **Nano Banana 2** | `gemini-3.1-flash-image-preview` | 按次 **\$0.055/次**(推荐 4K 出图使用);或按量动态计费,2K 约 **\$0.04** | 性价比 | | **Nano Banana 2 Lite** | `gemini-3.1-flash-lite-image` | 固定按次 **\$0.025/次**;或按量约 **\$0.018/次**(官网 4 折) | 最快最省,仅 1K | | **Nano Banana**(第一代) | `gemini-2.5-flash-image` | 固定按次 **\$0.02/次** | 最便宜 | 完整价格对比、按次/按量计费与令牌选择建议,见 [Nano Banana 系列价格总览](/api-capabilities/nano-banana-pricing)。 ### 尺寸控制 * **遵循原图比例**:不传 `aspectRatio` 即可;在多图编辑场景里,以**最后一张图的尺寸**为准 * **分辨率 `imageSize`**:支持 `1K` / `2K` / `4K` * Nano Banana(第一代)**仅支持 1K** * Nano Banana 2 **新增 512px** * Nano Banana 2 Lite **仅支持 1K**(不支持 2K/4K/512px) 用同一套代码调用第一代 `gemini-2.5-flash-image` 时,**必须去掉 `imageSize` 参数**(它不支持 `2K` / `4K`),否则会调用失败。 ## 接入方式 ### 官方文档 * 谷歌官方文档:`ai.google.dev/gemini-api/docs/image-generation` * 接入 API易 只需把**请求地址 + KEY 替换为 API易 的**即可,其余参数与官方一致 ### 官方状态查询(排查上游故障) Nano Banana 系列底层依赖谷歌 AIStudio / Gemini API。少数情况下 **2K / 4K 出图变糊或报错**,可能是**谷歌官方侧**的问题、而非接入层——可在谷歌官方状态页核对(请自行复制访问):`aistudio.google.com/status`。 例如 2026 年 6 月 19 日,该页报道过「Issues with Nano Banana」:Gemini API 与 AI Studio 上的 Nano Banana 2 / Pro 在 2K 或 4K 分辨率下出现问题。遇到类似现象,先比对官方状态页即可快速判断是否为上游故障。 API易 为 Nano Banana 系列提供 **AIStudio + Vertex 双通道**冗余:官方单通道异常时可由另一通道顶上,尽量保障服务可用性。 ### 端点支持 * **推荐端点**(Gemini 原生):`https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent` * 支持 **OpenAI 兼容模式**调用(`/v1/chat/completions`,可用 `image_url` 传 Base64 或公网链接) * **不支持** `/v1/image/generations` ### 开发格式(默认推荐) * **【推荐】使用谷歌原生端点格式** * 图片:**Base64 上传、下载转存** * 调用方式:**同步多线程调用**,暂不支持异步调用 ## 输入图片要求 * **单图不能超过 7MB**(谷歌规则);若通过 Google Cloud Storage 导入,单文件上限 30MB * **每个提示最多 14 张图** * **支持的 MIME 类型**:`image/png`、`image/jpeg`、`image/webp`、`image/heic`、`image/heif`(`jpg` 格式 API易 已兼容) * **Base64 体积膨胀**:图片转 Base64 后体积增加约 **33.3%**(7MB 的图约为 9.3MB) * **API易 限制**:单次请求上传图片总量需**低于 100MB**——均为同步调用,过大会导致内存爆炸 谷歌 Gemini 3 Pro Image 官方技术规范表:单图上限 7MB,每个提示最多 14 张图,支持的宽高比与 MIME 类型 Base64 体积计算说明:7MB 原图按 4/3 比例编码后约 9.33MB **最佳实践**:传给接口前对图片做**无损压缩**,避免超大分辨率拖慢请求速度。 谷歌官方规格说明(请自行复制访问):`docs.cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro-image` ## URL 图片输入说明 除了 Base64,Gemini 原生端点还支持用 `fileData.fileUri` 直接传图片 URL,请求体更小,也省去自己下载、转码的步骤。 **URL 传图时,图片是由谷歌的服务器去下载的,不是 API易。** 所以能不能用、快不快,取决于谷歌访问你的图床顺不顺,跟你自己的浏览器能不能打开没有关系。下面这些要求和排查方法,都是从实际工单和实测(2026-10-16,NB2 / Pro,三条线路)里总结出来的。 **条件允许的话,优先用 Base64(`inlineData`)。** 图片由你自己下载后放进请求体,不依赖谷歌去拉图,是稳定性最高的方式。URL 传图适合图片本来就放在海外全球 CDN 上、又想控制请求体积的场景。 ### 什么样的链接能用 | 要求 | 说明 | | - | - | | 直接指向图片文件 | 不能是网盘分享页、图片预览页这类网页。指向网页时谷歌会把整页内容读进来,几秒内报 400 `The input token count exceeds the maximum number of tokens allowed` | | 公网匿名可读 | 不需要登录、Cookie 或 Referer。私有桶、没有权限的链接实测会在约 60 秒后报 400 拉图失败 | | 谷歌服务器访问要快 | 谷歌拉图的时间上限约 60 秒。只走国内 CDN 的图床(例如腾讯云 COS 默认配置)常常拉不到;海外对象存储也可能很慢:实测一个 AWS S3 美西的存储桶,约 500KB 的图有一半以上在 60 秒时超时 | | 签名链接有效期覆盖整个请求 | 预签名 URL、临时链接不要设得太短,也不要限制下载次数 | | 原始直链,不带图片处理参数 | 带 `?x-oss-process=...` 这类处理参数会引起格式不符,见 [MIME type 不匹配](/faq/image-mime-type-mismatch-with-query) | | 单图不超过 7MB | 与 Base64 的限制相同,见上文 [输入图片要求](#输入图片要求) | `http://` 和 `https://` 链接实测都可以。`gs://`(Google Cloud Storage)只有部分线路能读,不建议通过 API易 使用。 ### 请求写法 ```json theme={null} { "contents": [{ "role": "user", "parts": [ { "fileData": { "fileUri": "https://your-cdn.example.com/a.png", "mimeType": "image/png" } }, { "fileData": { "fileUri": "https://your-cdn.example.com/b.jpg", "mimeType": "image/jpeg" } }, { "text": "把第二张图里的外套穿到第一张图的人物身上" } ] }], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": { "imageSize": "2K" } } } ``` * **多张图**:每张一个 `fileData`,按顺序放在 `parts` 里。也可以和 `inlineData` 混用,实测 2 张 URL 加 1 张 Base64 一次请求正常出图 * **`mimeType`** 按图片的真实格式写。实测写错或不写,当前线路也能识别,但不要依赖这一点 * **字段命名**:推荐与谷歌官方一致的驼峰写法 `fileData` / `fileUri` / `mimeType`。实测下划线写法 `file_data` / `file_uri` 目前也能识别 ### 确认参考图真的被读到了 **链接拉取失败时,有的线路不会报错**:返回 200、照常出图并计费,但结果完全没参考你的图。所以不能只看状态码,要看响应里的 `usageMetadata.promptTokensDetails`: ```json theme={null} "promptTokensDetails": [ { "modality": "TEXT", "tokenCount": 17 }, { "modality": "IMAGE", "tokenCount": 1120 } ] ``` 有 `IMAGE` 这一项,说明图片读到了(实测默认设置下 Nano Banana 2 每张约 1120 tokens,Pro 每张约 560 tokens);**只有 `TEXT`,说明谷歌没拿到图**,这次结果不会参考原图。下面的 Python 示例已经内置了这道检查。 ### 拉图失败的几种表现 | 表现 | 原因 | 计费 | | - | - | - | | 200 正常出图,但不参考原图;`promptTokensDetails` 只有 `TEXT` | 链接 404 等取不到图,部分线路会静默跳过这张图 | 计费 | | 几秒内 400 `Cannot fetch content from the provided URL ... Status: URL_ERROR-ERROR_NOT_FOUND` | 链接 404,另一部分线路会直接报错 | 不计费 | | 约 60 秒后 400 `Cannot fetch content from the provided URL`,`Status` 为 `URL_REJECTED-REJECTED_FC_TIMEOUT` 或 `URL_REJECTED-REJECTED_SCHEDULE_TIME_OVER_DEADLINE`,或提示 `The request to crawl the page timed out` | 谷歌下载超时:图床对谷歌太慢,或链接没有访问权限 | 不计费 | | 几秒内 400 `The input token count exceeds the maximum number of tokens allowed` | 链接指向的是网页,不是图片文件 | 不计费 | **同一个链接重试会变快,不代表问题解决了。** 谷歌会缓存拉过的图片,第二次请求同一个 URL 往往直接命中缓存;换一张新上传的图,还会再超时。自己测试时,可以在链接后加一个随机参数(如 `?v=随机数`)避开缓存,才能测出真实的拉图速度。 超时问题的完整案例见 [图片编辑报 Unable to process input image 怎么办](/faq/gemini-empty-inlinedata)(同页也讲了 Base64 为空导致的报错,两者经常被混淆)。 ### Curl 示例(fileUri) ```bash theme={null} curl --location 'https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent' \ --header "Authorization: Bearer $APIYI_API_KEY" \ --header 'Content-Type: application/json' \ --data '{ "contents": [{ "role": "user", "parts": [ { "fileData": { "fileUri": "https://raw.githubusercontent.com/apiyi-api/ai-api-code-samples/refs/heads/main/Vision-API-OpenAI/otter.png", "mimeType": "image/png" } }, { "text": "add five dogs" } ] }], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": { "aspectRatio": "16:9", "imageSize": "2K" } } }' > output.json ``` ### Python 示例(fileUri) ```python theme={null} import base64, os, requests API_KEY = os.environ["APIYI_API_KEY"] MODEL = "gemini-3.1-flash-image-preview" URL = f"https://api.apiyi.com/v1beta/models/{MODEL}:generateContent" IMAGE_URLS = [ "https://raw.githubusercontent.com/apiyi-api/ai-pics/refs/heads/main/1762260696217_dd0352c1f9604540.png", ] PROMPT = "把人物的衣服换成蓝色夹克,其余保持不变" payload = { "contents": [{ "role": "user", "parts": [ *[{"fileData": {"fileUri": u, "mimeType": "image/png"}} for u in IMAGE_URLS], {"text": PROMPT}, ], }], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"imageSize": "2K"}, }, } resp = requests.post(URL, json=payload, timeout=300, headers={"Authorization": f"Bearer {API_KEY}"}) if resp.status_code != 200: raise SystemExit(f"HTTP {resp.status_code}: {resp.text[:500]}") data = resp.json() # 1. 确认参考图真的被读到了:promptTokensDetails 里要有 IMAGE details = data.get("usageMetadata", {}).get("promptTokensDetails", []) image_tokens = sum(d.get("tokenCount", 0) for d in details if d.get("modality") == "IMAGE") if image_tokens == 0: print("⚠️ 参考图没有被读到(谷歌没拉到图),这张结果不会参考原图") # 2. 遍历 parts 取图,不要按下标 for part in data["candidates"][0]["content"]["parts"]: inline = part.get("inlineData") if inline: with open("output.png", "wb") as f: f.write(base64.b64decode(inline["data"])) print(f"✅ 已保存 output.png(参考图 {image_tokens} tokens)") break else: print("响应中没有图片:", data["candidates"][0].get("finishReason")) ``` ### OpenAI 兼容端点能传 URL 吗 可以。`/v1/chat/completions` 里用 `image_url` 传公网链接,实测 Nano Banana 2 与 Pro 都能正常读到图片并出图。链接要求与上文相同;需要多轮编辑、精确控制分辨率等能力时,仍推荐原生端点。 ## 计费基础(重要) * **同步调用耗时**:Pro / 2 在 4K 下的合理生成时间约 **30–150s** * **超时主动断开仍计费**:例如生成需 120s,但客户端把超时设为 100s 主动断开,仍会计费 * **429 / 503 不收费**:请求不通时不计费(我们尽量不让客户久等、不卡死迟迟不出图) * **内容安全拒绝仍计费**:客户输入存在内容安全问题、谷歌拒绝出图时,**状态码 200 仍会计费**——详见下方错误处理与保障计划 ## Google 搜索接地会在按次价之外另收费 Pro 支持 `googleSearch` 工具(实测 3/3 触发接地,返回完整 `groundingMetadata`),用来画天气卡、股价图这类需要实时信息的图。 **但搜索调用费是加在 \$0.09 按次价之上的,不含在里面**: | 场景 | 单次账单 | | - | - | | 普通出图(无工具) | \$0.09 | | 带搜索、模型查了 1 次 | \$0.09 + \$0.014 = **\$0.104** | | 带搜索、模型查了 2 次 | \$0.09 + \$0.028 = **\$0.118** | ```json theme={null} { "contents": [{ "parts": [{ "text": "画一张东京今天天气的卡片海报" }] }], "tools": [{ "googleSearch": {} }] } ``` **搜索次数由模型自己决定,客户无法预先控制**。实测一次出图请求会自主发起 1–3 次检索,所以开了这个工具之后单次成本是一个区间(\$0.104–\$0.132),不再是固定的 \$0.09。做成本预估时按上限算。 **图片搜索接地(`searchTypes.imageSearch`)在 Pro 上不生效**,实测 0/2 未触发,`groundingMetadata` 里不出现 `imageSearchQueries`。这是 Nano Banana 2(`gemini-3.1-flash-image`)的独有能力,用法见 [Nano Banana 2 · 影响计费的三个参数](/api-capabilities/nano-banana-2-image/overview#影响计费的三个参数)。 ## thinkingLevel 对 Pro 无效,不要照搬 NB2 的写法 `generationConfig.thinkingConfig.thinkingLevel` 是 **Nano Banana 2 系列独有**的开关。给 Pro 传 `high`: * **不报错**,请求正常返回 200 * **但完全不生效**:实测 `thoughtsTokenCount` 在 108–156 之间,与不传时的 130–159 完全重叠 * Pro 的思考恒开、无法调节,官方文档亦如此说明 而且 **Pro 按次固定计价,思考多少都不进账单**——所以在 Pro 上调这个参数既无效果、也无成本意义。需要控制思考开销请改用 Nano Banana 2 的按量计费。 ## 超时设置(重要) 4K 出图的整体耗时较长,包含**图片上传、API 处理、Base64 图片下载**等环节(我们后台按 **API 处理用时**计费)。正常情况下 4K 用时约 **50s**(不含轮询),但客户端若把超时设得过短,就会在出图完成前**主动断开**并报错: ```text theme={null} API Connection Error: HTTPSConnectionPool(host='api.apiyi.com', port=443): Read timed out. (read timeout=120) ``` 调用日志:gemini-3-pro 4K 出图首字节耗时 43 到 61 秒 为更保险,建议按分辨率设置超时时间: ```python theme={null} timeout = { "1K": 300, # 5 分钟 - 快速预览 "2K": 300, # 5 分钟 - 推荐使用 "4K": 600, # 10 分钟 - 超高清 } ``` ## 多轮对话式编辑(原生支持,逆向不支持) Nano Banana 系列走 **Gemini 原生格式**,支持**真正的对话式多轮编辑**:把模型每一轮产出的图作为 **`role: "model"` 的 `inlineData`** 回填进 `contents`,再发下一条 user 指令,模型会基于**完整对话历史**继续修改并**累积效果**(如先改沙发颜色、再加配饰,上一步的改动会保留)。 这一点与"逆向"图像模型有本质区别,接入前务必分清: | 维度 | Nano Banana(Gemini 原生) | 逆向模型(如 `gpt-image-2-all`) | | - | - | - | | 端点 | `/v1beta/...:generateContent` | `/v1/chat/completions`(对话式) | | 多轮机制 | ✅ **真·对话式**:`contents` 回填 `role:model` 图,模型读历史 | ❌ 无对话状态:`assistant` 历史里的图**被忽略** | | 跨轮累积修改 | ✅ 支持(红沙发→再加帽子,红沙发保留) | ⚠️ 只能"重新喂图"做单步改图 | | 正确改上一张图的姿势 | 把上轮产出作为 `model` 图回填进对话历史 | 把上一张图 URL 作为**新一轮 user 的参考图**重新传 | 实测:把上一张图放进 `model` 角色回填,Nano Banana 2(`gemini-3.1-flash-image-preview`)能正确基于它继续编辑并累积修改;而逆向模型只认**最后一条 user 消息里的参考图**,靠保留对话历史做多轮在逆向上无效。 最小示例(每轮把产出图回填进同一个 `contents`): ```python theme={null} import requests, base64 API_KEY = "sk-your-api-key" URL = "https://api.apiyi.com/v1beta/models/gemini-3.1-flash-image-preview:generateContent" H = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"} CFG = {"responseModalities": ["IMAGE"], "imageConfig": {"imageSize": "2K"}} contents = [] # 全程维护同一个对话历史 def turn(instruction, save_to): contents.append({"role": "user", "parts": [{"text": instruction}]}) data = requests.post(URL, headers=H, json={"contents": contents, "generationConfig": CFG}, timeout=300).json() part = next(p for p in data["candidates"][0]["content"]["parts"] if "inlineData" in p) contents.append({"role": "model", "parts": [part]}) # 关键:把产出图回填进历史 with open(save_to, "wb") as f: f.write(base64.b64decode(part["inlineData"]["data"])) turn("生成一只橙色的猫,坐在蓝色沙发上,简笔画风格", "step1.png") turn("把沙发改成红色,猫和构图保持不变", "step2.png") # 基于上一轮的图 turn("给猫戴一顶黄色小帽子,其它保持不变", "step3.png") # 继续累积,红沙发会保留 ``` 完整说明(含"对话历史回填" vs "重新喂图"两种写法、从已有图片开始多轮)见 [图片编辑 API · 多轮对话式编辑](/api-capabilities/nano-banana-2-image/image-edit#多轮对话式编辑)。 ## 取图必须遍历 parts,不要按下标 `parts` 是一个**异构数组**:里面可能只有图片段,也可能文本段与图片段混排,**段数和顺序都不做保证**。因此 `parts[0]` / `parts[1]` 这类写死下标的取法一定会间歇性失败。 实测出现过的三种排列: | parts 结构 | 长度 | 图片下标 | | - | - | - | | `inlineData` | 1 | `0` | | `text` + `inlineData` | 2 | **`1`** | | `inlineData` + `text` | 2 | **`0`** | 文本段的来源不止一种:`responseModalities` 里含 `TEXT`、提示词要求模型给出文字说明,都会让模型在图片之外再返回一段文本;文本段排在图片前还是图片后也不固定。**因此图片落在哪个下标并非定值**,同一份代码在不同请求下可能拿到不同结构。 写死下标的两种写法是**互补**的:图片必落在 `[0]` 或 `[1]`,无论选哪个,都存在拿不到图的请求。**在 `[0]` 和 `[1]` 之间来回改解决不了问题**,只有按字段特征筛选才稳定。 正确写法是**按字段特征筛选**,而不是按位置索引。注意取的是**最后一个** `inlineData` 而非第一个——复杂任务下模型会返回多张图,最后一张才是最终稿(见下一节): ```python theme={null} cand = (resp.get("candidates") or [{}])[0] parts = (cand.get("content") or {}).get("parts") or [] # 兼容 parts=null images = [p["inlineData"] for p in parts if "inlineData" in p] if not images: raise RuntimeError(f"未返回图片,finishReason={cand.get('finishReason')}") final = images[-1] # 多图时最后一张为最终稿 image_bytes = base64.b64decode(final["data"]) mime = final["mimeType"] # 以响应为准,别写死 image/png ``` ```javascript theme={null} const parts = resp?.candidates?.[0]?.content?.parts ?? []; const images = parts.filter((p) => p.inlineData?.data); // ✅ 不要写 parts[1] if (images.length === 0) throw new Error("Gemini 未返回图片数据"); const { data, mimeType } = images[images.length - 1].inlineData; // 最后一张为最终稿 ``` **加固手段**:在 `generationConfig` 里显式声明 `responseModalities: ["IMAGE"]`,表明只要图片,可减少多余文本段。 但这是加固、**不是替代**——遍历筛选仍需先落地。反向并不成立:传 `["TEXT","IMAGE"]` 并**不保证**一定返回文本段,模型也可能只给图片。 **`mimeType` 同样不要写死**。响应里的图片格式并不恒定,`image/png` 与 `image/jpeg` 都出现过。若按固定 `.png` 后缀落盘,会得到扩展名与实际内容不符的文件——**一律以响应里的 `mimeType` 为准**决定后缀。 ## 偶现多图输出是怎么回事 调用 `gemini-3-pro-image` 时,偶尔会看到**同一个响应里返回多张图片 part(实测 2–10 张)**,日志里对应偶发的 6000+ 乃至上万的输出 tokens。这不是异常:谷歌官方文档说明 Gemini 3 图片模型默认启用"思考"(无法在 API 中关闭),模型会生成临时图片来测试构图和逻辑,这些中间稿与最终稿一并出现在 `parts` 里,且"思考中的最后一张图片也是最终渲染的图片"(官方文档:`ai.google.dev/gemini-api/docs/image-generation`)。基于 2026 年 7 月实测(Google 原生 `generateContent` 格式): | 场景 | 返回图片数 | | - | - | | 纯文生图 | 恒为 1 张(即使提示词明确要求"输出多张图") | | 简单图片编辑(加饰品/换背景/换风格) | 恒为 1 张 | | 复杂任务型编辑(如"人物四视图 + 换装 + 白底"等多重约束) | 2–10 张,必现 | 触发因素是**提示词的任务复杂度**,不是"图片编辑"本身。多张图仍在**同一个 candidate** 内(不是多 candidates),每张都是完整的成图——它们是思考过程中对同一设计的逐稿修正(构图相同、细节略有差异),**最后一张 part 即最终稿**。这些中间稿以普通图片 part 返回(带 `thoughtSignature` 字段、无 `thought: true` 标记);官方称思考最多生成两张临时图片,实测复杂任务下最多见 10 张。 **对计费的影响**:每张图按固定 tokens 计费(1K/2K 分辨率每张 1120 tokens,4K 每张 2000 tokens),输出 tokens 随图片数严格线性增长。日志里偶发的 6000+(极端可达 1.3 万+)输出 tokens 就是 4–10 图响应,**不是异常计费**。 **下游代码建议**: ```python theme={null} parts = response["candidates"][0]["content"]["parts"] or [] # 安全拒绝时 parts 为 null images = [p["inlineData"]["data"] for p in parts if "inlineData" in p] if images: final_image = images[-1] # 最后一张 = 最终稿 ``` * **必须遍历 parts**,不要假设单响应单图;按张计数、落盘的逻辑要以实际 part 数为准 * **只要一张时取最后一张**:前面的迭代稿细节未修完,质量略低,不建议取第一张 * **提示词控制张数基本无效**(实测"只输出一张"类指令不敏感),请在代码层处理 * 多图响应耗时 35–142s(1K 分辨率,张数越多越久),显著长于单图,超时请沿用上文建议(≥ 5 分钟) usageMetadata 各字段的完整口径(details 与总量的差值、拒绝响应的计数特例等)见 [usage 字段与输出解读](/api-capabilities/nano-banana-usage-metadata)。 ## 常见问题 出图失败的三大判断指标、内容审核政策与友好提示方案 出图失败排查与常见疑问 非主观原因导致的失败,按条数核算后补发额度 完整报错形如: ```text theme={null} [&{{write tcp ip:port->ip:port: write: connection reset by peer Unknown error shell_api_error write_response_body_failed} 500 }] ``` 这种错误**往往是上传的图片体积过大,请求体超限把连接压崩了**。请按以下最佳实践处理: * **控制图片张数**:保持在官方规则内(每个提示最多 14 张图,见上方官方技术规范)。 * **控制单图体积**:每张图尽量不要超过 5MB——官方单图上限为 7MB,且 base64 编码后体积还会膨胀约 1/3,原图请留足余量。 * **前端先压缩再上传**:在前端(或服务端中转层)压缩后再提交给接口,常见做法是限制最长边、转 JPEG/WebP 并控制质量参数。 * **改用 URL 传图**:Gemini 原生格式支持 `fileData.fileUri` 直接传图片 URL,可避开 base64 请求体过大的问题,见上文 [URL 图片输入说明](#url-图片输入说明)。 ## 应用场景 * **AI 对话客户端**:[Cherry Studio](/scenarios/chat/cherry-studio) 等客户端可直接配置 API易 出图 * **出图测试**:可在对话客户端或控制台快速验证模型效果 ## 高级需求 * **图片上传想用 URL?** Gemini 原生端点用 `fileData.fileUri` 传入,OpenAI 兼容端点用 `image_url` 传入;图片由谷歌服务器下载,对图床要求较高。链接要求、拉图失败的判断与代码示例见上文 [URL 图片输入说明](#url-图片输入说明)。 * **图片下载想直接拿到 URL(而非 Base64)?** 使用 NB-OSS 分组——详见 [Nano Banana OSS 分组](/api-capabilities/nano-banana-oss-group)。 # 图片编辑 API 参考 Source: https://docs.apiyi.com/api-capabilities/nano-banana-image/image-edit api-reference/nano-banana-pro-edit-openapi.yaml POST /v1beta/models/gemini-3-pro-image-preview:generateContent Nano Banana Pro 图片编辑 API 参考与在线调试 — 输入图片 + 指令生成新图片 右侧的交互式 Playground 支持下拉选择参数。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),即可一键发送请求测试。 **场景说明**:本页用于「图片编辑」,必须上传一张待编辑的图片(base64 编码)+ 编辑指令。如果只想根据文本生成新图片,请使用 [文生图接口](/api-capabilities/nano-banana-image/text-to-image)。 **🖥️ 浏览器 Playground 限制(重要)** 本接口的响应里包含 base64 编码的图片(`inlineData.data`,数 MB 量级)。受浏览器渲染限制,右侧 Playground 在收到响应后**可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法把这么长的 base64 显示出来。 **推荐做法**(小白零踩坑): * **直接复制下方"代码示例"中的 Python / Node.js / cURL 到本地运行**,代码会自动 `base64.b64decode` 并把图片**保存为本地文件**。 * 如要在浏览器里试 Playground,**用极小的参考图(\< 50KB)** 并把 `imageSize` 设为最小档(如 `1K`)。 **⚠️ `parts` 数组结构(重要,多图编辑必看)** 每个 `part` **只能是 `text` 或 `inlineData` 中的一个**,二者不能同时出现在同一个 part 里。这与谷歌官方 `gemini-3-pro-image-preview` 的契约一致。 **正确**:一个 text part(编辑指令)+ N 个 inlineData part(每张图一个): ```json theme={null} "contents": [{ "parts": [ {"text": "把这两张图里的人物合成到同一个办公室场景中"}, {"inlineData": {"mimeType": "image/png", "data": ""}}, {"inlineData": {"mimeType": "image/png", "data": ""}} ] }] ``` **错误**(每个 part 同时塞了 text 和 inlineData,会导致非预期行为): ```json theme={null} "contents": [{ "parts": [ {"inlineData": {...}, "text": "这是提示词吗 1"}, {"inlineData": {...}, "text": "这是提示词吗 2"} ] }] ``` **关于 `inlineData.data` 字段** 本接口是 **JSON 格式**(非 multipart 文件上传),所以 Playground 无法直接选择本地文件,需要先把图片转成 **Base64 字符串**再粘贴到 `data` 输入框。 **一行命令转换 + 自动复制到剪贴板**: ```bash theme={null} # macOS base64 -i your-image.jpg | tr -d '\n' | pbcopy # Linux base64 -w0 your-image.jpg | xclip -selection clipboard # Windows PowerShell [Convert]::ToBase64String([IO.File]::ReadAllBytes("your-image.jpg")) | Set-Clipboard ``` 执行后直接在 Playground 的 `data` 字段 `Cmd+V` / `Ctrl+V` 粘贴即可。同时记得把 `mimeType` 切换为对应的 `image/jpeg` 或 `image/png`。 **建议**:测试用小图(少于 200KB),避免 base64 字符串过长导致浏览器卡顿。频繁测试图片编辑更推荐用下方代码示例直接在本地运行。 ## 代码示例 ### Python ```python theme={null} import requests import base64 API_KEY = "sk-your-api-key" # 读取待编辑的图片 with open("input.jpg", "rb") as f: image_b64 = base64.b64encode(f.read()).decode() response = requests.post( "https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent", headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json={ "contents": [{ "parts": [ {"text": "请把背景模糊化,突出前景的人物"}, {"inlineData": {"mimeType": "image/jpeg", "data": image_b64}} ] }], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }, timeout=300 ).json() img_data = response["candidates"][0]["content"]["parts"][0]["inlineData"]["data"] with open("edited.png", 'wb') as f: f.write(base64.b64decode(img_data)) print("编辑后的图片已保存至 edited.png") ``` ### Node.js ```javascript theme={null} import fs from "fs"; const API_KEY = "sk-your-api-key"; const imageB64 = fs.readFileSync("input.jpg").toString("base64"); const response = await fetch( "https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [ { text: "请把背景模糊化,突出前景的人物" }, { inlineData: { mimeType: "image/jpeg", data: imageB64 } } ] }], generationConfig: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "16:9", imageSize: "2K" } } }) } ); const data = await response.json(); const imgBase64 = data.candidates[0].content.parts[0].inlineData.data; fs.writeFileSync("edited.png", Buffer.from(imgBase64, "base64")); ``` ### cURL ```bash theme={null} # 注意:需要先将图片转为 base64 字符串 # IMAGE_B64=$(base64 -i input.jpg | tr -d '\n') curl -X POST "https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "contents": [{ "parts": [ {"text": "请把背景模糊化,突出前景的人物"}, {"inlineData": {"mimeType": "image/jpeg", "data": "'"$IMAGE_B64"'"}} ] }], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }' ``` ## 多图编辑示例 把多张图作为输入合成或对比时,**只用一个 `text` part**(编辑指令),后面追加多个 `inlineData` part(每张图一个)。 ### Python(多图) ```python theme={null} import requests import base64 API_KEY = "sk-your-api-key" def to_b64(path): with open(path, "rb") as f: return base64.b64encode(f.read()).decode() # 准备多张图(这里以 2 张为例,最多可上传多张) images = ["person1.png", "person2.png"] parts = [{"text": "把这两张图里的人物合成到同一个办公室场景中,做着搞怪表情"}] for path in images: parts.append({"inlineData": {"mimeType": "image/png", "data": to_b64(path)}}) response = requests.post( "https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent", headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json={ "contents": [{"parts": parts}], "generationConfig": { "responseModalities": ["TEXT", "IMAGE"], "imageConfig": {"aspectRatio": "5:4", "imageSize": "2K"} } }, timeout=300 ).json() img_data = response["candidates"][0]["content"]["parts"][0]["inlineData"]["data"] with open("merged.png", "wb") as f: f.write(base64.b64decode(img_data)) ``` ### cURL(多图,对齐谷歌官方格式) ```bash theme={null} curl -X POST "https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "contents": [{ "parts": [ {"text": "An office group photo of these people, they are making funny faces."}, {"inlineData": {"mimeType": "image/png", "data": ""}}, {"inlineData": {"mimeType": "image/png", "data": ""}}, {"inlineData": {"mimeType": "image/png", "data": ""}} ] }], "generationConfig": { "responseModalities": ["TEXT", "IMAGE"], "imageConfig": {"aspectRatio": "5:4", "imageSize": "2K"} } }' ``` ## 参数说明速查 | 参数 | 类型 | 必填 | 说明 | | - | - | - | - | | `contents[].parts` | array | 是 | 由「**1 个 text part + N 个 inlineData part**」组成。每个 part 只能含 `text` 或 `inlineData` 之一,不可同时出现 | | `contents[].parts[].text` | string | 是 | 编辑指令(建议只放在第一个 part 中) | | `contents[].parts[].inlineData.mimeType` | string | 是 | `image/jpeg` 或 `image/png` | | `contents[].parts[].inlineData.data` | string | 是 | 图片的 Base64 编码(多图编辑时重复多个 inlineData part) | | `generationConfig.responseModalities` | array | 是 | 通常为 `["IMAGE"]` | | `generationConfig.imageConfig.aspectRatio` | string | 否 | 10 种宽高比,默认 `1:1` | | `generationConfig.imageConfig.imageSize` | string | 否 | `1K` / `2K` / `4K`,默认 `1K` | Nano Banana Pro 支持多图编辑:可以同时输入多张图片和编辑指令,实现多图合成、风格迁移等高级编辑功能。 # Nano Banana Pro 图片生成 Source: https://docs.apiyi.com/api-capabilities/nano-banana-image/overview 谷歌最新、最强的图像生成模型 gemini-3-pro-image-preview,俗称 Nano Banana Pro。支持自定义分辨率 1K、2K、4K 输出、10种宽高比,低至官网 2 折优惠。 **🔥 最新发布**:谷歌于 2026年2月26日发布了 **Nano Banana 2** (`gemini-3.1-flash-image-preview`),Pro 级画质 + Flash 级速度,按量低至 \$0.025/张![查看 Nano Banana 2 文档](/api-capabilities/nano-banana-2-image/overview) **🆕 2026年5月29日更新(去掉 `-preview`)**:谷歌更新了官方文档,推出去掉 `-preview` 的正式模型名 **`gemini-3-pro-image`**,API易已同步上线支持。 * **原模型名仍可用**:`gemini-3-pro-image-preview` 继续正常调用,**价格不变**,现有代码无需改动。 * **两个名字都能跑**:可按需选用新名 `gemini-3-pro-image` 或原 `-preview` 名。 话说在前面:正式版相比 preview 版在效果表现、安全审核机制等方面是否存在差异,谷歌官方暂未明确说明,欢迎大家在实际使用中一起测试反馈。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 **Nano Banana Pro**(代号)是谷歌图像生成模型系列,目前有以下版本可用: ### 最新版本 * **Nano Banana 2.1**:`gemini-nano-banana-2.1`(🆕 2026年10月6日上线,Nano Banana 2 的升级版)— [查看详情](/api-capabilities/gemini-nano-banana-2.1/overview) * **Nano Banana 2**:`gemini-3.1-flash-image-preview`(🔥 2026年2月26日上线)— [查看详情](/api-capabilities/nano-banana-2-image/overview) * **Nano Banana Pro**:`gemini-3-pro-image-preview`(2025年11月20日上线) ### 前代版本 * **正式版**:`gemini-2.5-flash-image`(稳定版,支持 10 种宽高比) * **预览版**:`gemini-2.5-flash-image-preview`(⚠️ 已于2025年10月30日下线) **核心优势** * 🔥 **Nano Banana Pro 新特性**: * 🎯 **4K 高清支持**:支持 1K、2K、4K 三种分辨率,最高可达 4096×4096 * 📝 **文本渲染之王**:图像中的文字清晰可读,适合海报、广告等场景 * ✨ **局部编辑**:支持摄像机角度、焦点、色彩分级、场景照明调整 * 🧠 **智能推理**:基于 Gemini 3 Pro,更好地理解复杂提示词 * 🚀 **通用优势**: * ⚡ **生成速度**:Nano Banana Pro 约 20 秒,前代版本约 10 秒 * 💰 **价格实惠**:结合充值优惠,性价比极高 * 🔄 **完全兼容**:完全兼容谷歌官方 Gemini API 格式 * 🎨 **谷歌技术**:基于谷歌最新、最强的图像生成/编辑技术 ## 在线调试 API 输入文本提示词生成图片,带交互式 Playground 在线调试。 上传图片 + 编辑指令生成新图片,带交互式 Playground 在线调试。 ## 让 AI Agent 帮你接入 帮我在当前项目里接入 / 排查 Nano Banana Pro(`gemini-3-pro-image`)的「文生图 + 图片编辑」。 先读文档再动手:抓 [https://docs.apiyi.com/api-capabilities/nano-banana-image/overview.md](https://docs.apiyi.com/api-capabilities/nano-banana-image/overview.md) 拿到本页纯文本版;需要更细的参数说明时,text-to-image 和 image-edit 两页同样在地址后加 `.md` 即可。 接入要求: 1. 超时:走 Gemini 原生格式 `POST https://api.apiyi.com/v1beta/models/gemini-3-pro-image:generateContent`。客户端 timeout 按分辨率分档:1K / 2K 用 300 秒,**4K 用 600 秒兜底**。图片接口是同步调用,没有任务 ID,客户端一断连结果就丢了、但这次请求照样计费。反向代理、网关、Serverless 执行上限这些中间层也要一起放宽,任何一层小于生成时间都会掐断请求。如果你用 Node,注意 undici 有三个独立的超时设置,SDK 的 `timeout` 并不覆盖它们。 2. 返回解析(**最容易写错的一条**):图片是 base64,在 `candidates[0].content.parts[]` 里的 `inlineData.data`。但 `parts` 是**异构数组,段数和顺序都不保证**——前面可能挂一个文本段,图片就落到下标 1 而不是 0。所以**绝对不要写死 `parts[0]` 或 `parts[1]`**,在两者之间来回改是解决不了问题的。正确写法:遍历 `parts`、筛出所有含 `inlineData` 的段,取**最后一张**(复杂任务会返回多张中间稿,最后一张才是终稿)。`mimeType` 也从响应里读,不要写死成 `image/png`。拿到后渲染展示并提供「保存到本地」。 3. 上传压缩:编辑时把参考图 base64 塞进 `inlineData`。上传前先压缩——超过 1.5MB 才处理,长边等比缩到 2048px 以内(不放大小图),以质量 0.9 重编码、保持原格式;多图时合计控制在 6MB 以内。官方硬限制是单图 7MB、每次请求最多 14 张、单次上传总量低于 100MB,而 base64 编码后体积还会再膨胀约三分之一,所以单图尽量压到 5MB 以内留足余量。某张图压缩失败就回退用原图继续,不要因为压缩失败中断整个请求。另外注意:**同一个 part 里只能放 `text` 或 `inlineData` 其中一个**,不能两个字段并存,正确结构是 1 个文本段 + N 个图片段。 4. 分辨率参数:显式传 `generationConfig.imageConfig.imageSize`(`1K` / `2K` / `4K`,默认 `1K`)和 `aspectRatio`(本页列了 10 个合法比例),别靠默认值。前台界面把分辨率和比例都做成下拉。选了 4K 记得把 timeout 一起提到 600 秒。本模型**不支持** `thinkingConfig`,也不支持 Google 搜索接地(`tools` 里的 `google_search`),不要传。 5. 错误处理:内容审核拦截时 HTTP 仍然是 200,但 `candidates[0].content.parts` 为空。判断顺序是先看 `candidatesTokenCount` 是否为 0,再看 `finishReason` 是否非 `STOP`。`IMAGE_SAFETY` 这类拦截**不计费**,原样重试 1-2 次往往就成功了,建议在代码里对它做自动重试。 6. Key 从环境变量 `APIYI_API_KEY` 读,用 `Authorization: Bearer` 头传,不要硬编码进代码、也不要提交进 git。 7. 改完真跑一次文生图 + 一次图片编辑,把出图结果和这两次调用的花费贴给我。 | 要求 | 挡掉的坑 | | - | - | | 不写死 `parts` 下标 | `parts` 段数和顺序都不保证,写死下标一定会间歇性失败,且在下标 0 和 1 之间来回改解决不了问题。详见 [Nano Banana 开发指南](/api-capabilities/nano-banana-dev-guide) | | 取最后一张图片段 | 复杂编辑任务会返回多张中间稿,最后一张才是终稿 | | 超时按分辨率分档 | 4K 按 300 秒配会误超时,而**断连的请求照常计费**。详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices) | | 上传前压缩 | 官方单图上限 7MB,base64 后还会再膨胀约三分之一,直传手机原图容易触发 500。压缩标准见 [图片压缩与输出分辨率说明](/api-capabilities/image-compression-resolution) | | 对 `IMAGE_SAFETY` 自动重试 | 审核拦截返回 200 但没有图,且不计费,原样重试往往就过了。详见 [Gemini 出图错误处理](/api-capabilities/gemini-image-error-handling) | ## 为什么选 API易 的 Nano Banana Pro **Nano Banana Pro / 2 是 API易 消耗量排名第一的图像模型**——稳定、可靠、速度快。如果你期望跟专业的团队合作,那选 API易 就对了。 针对 Nano Banana Pro 这种谷歌最新旗舰、内容安全管控严格的模型,API易 在**稳定性**、**成本**、**接入体验**三方面做了深度优化: 完全兼容谷歌官方 Gemini API 格式(`/v1beta/models/.../generateContent`),同时支持 OpenAI SDK 模式,请求体、响应字段、错误码与官方一致,迁移零改造。 无谷歌 AI Studio 的 RPM/RPD 硬限制,企业批量出图、高峰流量都能线性放大,避免被官方配额打断。 按次 \$0.09/张(官方 4K \$0.24),叠加 [充值加赠活动](/faq/recharge-promotions) 最低可达官方 31.25%——4K 场景最多省 68.75%。 **无需海外服务器或代理**,国内机房、家宽网络、海外节点均可直连 `api.apiyi.com`,延迟稳定、免去出海改造。 同系列覆盖 [Nano Banana 2](/api-capabilities/nano-banana-2-image/overview)(性价比 + Flash 速度)、Nano Banana Pro(极致画质)、Nano Banana 第一代(基础版),按场景自由组合。 团队深耕图像生成场景,具备丰富的选型、调优与集成经验,可为企业客户提供从 PoC 到生产上线的完整技术支持。 ## 调用方式 使用谷歌原生 Gemini API 格式调用: ``` POST /v1beta/models/gemini-3-pro-image-preview:generateContent ``` * ✅ 支持 4K 高清分辨率(1K / 2K / 4K) * ✅ 10 种宽高比自由选择 * ✅ 业界最佳文字渲染 * ✅ 支持高级局部编辑 * 📖 完全兼容谷歌官方 API 格式,参考 `ai.google.dev/gemini-api/docs/image-generation` ### 不支持的功能 以下谷歌官方功能在 API易 中**不支持**,需要单独计费: * **Grounding with Google Search**(谷歌搜索增强):通过 `tools: [{"google_search": {}}]` 调用 * **thinkingConfig**(思维模式):仅 Nano Banana 2 支持,Nano Banana Pro 不支持 * **Image Search Grounding**(图片搜索增强):仅 Nano Banana 2 独有 其他图片生成和编辑能力均正常支持。 ## 价格对比 | 模型 | 定价 | 优势 | | - | - | - | | **Nano Banana Pro** (4K) | \$0.09/张(约¥0.52) | 🔥 4K 支持,约官方 38% | | **Nano Banana Pro** (1K-2K) | \$0.09/张(约¥0.52) | 🔥 高清出图,约官方 67% | | **Nano Banana** | \$0.025/张(约¥0.15) | ⭐ 快速生成,官方 52% | | gpt-image-1 | 较高 | - | | flux-kontext-pro | \$0.035/张 | 持平 | **性价比推荐**: * **Nano Banana Pro**:4K 支持,文字渲染最强,定价约官方 38-67% * **NanoBananaEnterprise**:企业 HA 通道 1.4 倍率(\$0.126/张),适合高可用需求 * **Nano Banana**:快速生成(约10秒),官方 52% 价格,适合常规高质量图像生成 ## 分组介绍 Nano Banana Pro 在 API易提供两个分组,可在后台「令牌设置」中切换: | 分组 | 倍率 | 适用场景 | | - | - | - | | `Default` 默认分组 | 1.0x | 基础通道,按次 \$0.09/张;默认推荐 | | `NanoBananaEnterprise` 企业分组 | 1.4x | 兜底通道,按次 \$0.126/张,默认紧张/超时高发时手动切换,稳定优先 | **1.4x 倍率怎么来的?** 1.4x 后仍约等于谷歌官方 5 折水平,远低于官网原价。这是面向更高并发需求、应对意外风控时的兜底方案,为企业客户提供高可用性保障。默认分组紧张时,把令牌切到 `NanoBananaEnterprise` 即可临时过渡。 **令牌「计费模式」推荐**:选 `按量优先`(Pay-as-you-go Priority)—— 同时兼容 Nano Banana Pro 的按次计费 和 Nano Banana 2 的按量计费,**一把令牌跑全系列**。 令牌创建界面:计费模式『按量优先』兼容 NB Pro 按次 + NB2 按量;主分组 Default + 兜底分组 NanoBananaEnterprise(1.4x 兜底通道) **拓展玩法**:如果你的令牌还覆盖其它图像模型(如 GPT-image-2),把更稳的 Default 分组放主位、`NanoBananaEnterprise` 放兜底位即可,主分组 429 会自动回退到企业分组继续出图,无需切换 token。 ## 兼容性说明 如果你之前使用过以下模型,可以直接替换模型名称: ### 升级到最新版本 * 任何旧版本 → `gemini-3-pro-image-preview`(🔥 推荐,Nano Banana Pro) * 支持 4K 高清出图 * 文本渲染能力最强 * 局部编辑功能 ### 使用稳定版本 * `gpt-4o-image` → `gemini-2.5-flash-image` * `sora_image` → `gemini-2.5-flash-image` * 旧版 Nano Banana → `gemini-2.5-flash-image` 其他参数保持不变,即可无缝切换使用。 ## 支持的分辨率与宽高比 ### 输出分辨率 Nano Banana Pro 支持 1K、2K、4K 三档分辨率(不含 Nano Banana 2 独有的 512px): | 分辨率 | 说明 | 推荐场景 | | - | - | - | | 1K | 默认 | 社交媒体、网页展示 | | 2K | 高清 | 高清显示、打印材料 | | 4K | 超高清 | 专业设计、商业海报 | ### 各宽高比的输出尺寸(像素) 下表为 Nano Banana Pro 在 1K / 2K / 4K 三档分辨率下、10 种宽高比对应的实际输出像素尺寸(数据来源:谷歌官方文档)。在请求中通过 `aspect_ratio` 指定宽高比、`image_size`(或 `resolution`)指定分辨率即可: | 宽高比 | 1K | 2K | 4K | | - | - | - | - | | **1:1** | 1024×1024 | 2048×2048 | 4096×4096 | | **2:3** | 848×1264 | 1696×2528 | 3392×5056 | | **3:2** | 1264×848 | 2528×1696 | 5056×3392 | | **3:4** | 896×1200 | 1792×2400 | 3584×4800 | | **4:3** | 1200×896 | 2400×1792 | 4800×3584 | | **4:5** | 928×1152 | 1856×2304 | 3712×4608 | | **5:4** | 1152×928 | 2304×1856 | 4608×3712 | | **9:16** | 768×1376 | 1536×2752 | 3072×5504 | | **16:9** | 1376×768 | 2752×1536 | 5504×3072 | | **21:9** | 1584×672 | 3168×1344 | 6336×2688 | 需要 `1:4`、`4:1`、`1:8`、`8:1` 这类超长/超宽比例,或 512px 低分辨率档位时,请使用 [Nano Banana 2](/api-capabilities/nano-banana-2-image/overview)(支持 14 种宽高比 + 512px)。 ## API 错误处理指南 Nano Banana Pro 有严格的内容安全管控,可能在多个层级拒绝不合规请求。 **最高优先级** 值为 0 时,内容在审核阶段被拒绝,未生成任何候选内容。 **次要优先级** 值非 `STOP`(如 `PROHIBITED_CONTENT`、`SAFETY`)时,表示生成过程中被拒绝。 **拒绝说明** API 返回拒绝说明文本而非图片数据时,应将这些说明展示给用户。 ## FAQ **追求性价比**推荐 **Nano Banana 2** (`gemini-3.1-flash-image-preview`): * Pro 级画质 + Flash 级速度 * 按量计费低至 \$0.025/张 * 支持 14 种宽高比(比 Pro 多 4 种) * 独有:思维模式、图片搜索 Grounding **追求极致画质**选 **Nano Banana Pro** (`gemini-3-pro-image-preview`): * 最高保真度 * 定价 \$0.09/次 详情参考 [Nano Banana 2 文档](/api-capabilities/nano-banana-2-image/overview)。 **新项目推荐:Nano Banana Pro** (`gemini-3-pro-image-preview`) ✅ **Pro 版优势**: * 支持 4K 超高清分辨率 * 业界最佳文本渲染质量 * 高级局部编辑功能 * 定价仅约官方 38-67% ⚡ **前代版本** (`gemini-2.5-flash-image`) 适合: * 预算敏感场景(约\$0.025/张 vs \$0.09/张) * 需要快速生成(10s vs 20s) * 现有项目迁移 只需将模型名称从 `gpt-4o-image` 或 `sora_image` 更改为 `gemini-3-pro-image-preview`(推荐 Pro)或 `gemini-2.5-flash-image`(前代),其他参数保持不变。 模型返回 base64 编码的图片数据,通常为 PNG 或 JPEG 格式。代码会自动检测格式并保存为对应的文件类型。 是的,Nano Banana Pro 支持图片生成和图片编辑。请查看 [图片编辑 API 参考](/api-capabilities/nano-banana-image/image-edit)。 完整报错形如: ```text theme={null} [&{{write tcp ip:port->ip:port: write: connection reset by peer Unknown error shell_api_error write_response_body_failed} 500 }] ``` 这种错误**往往是上传的图片体积过大,请求体超限把连接压崩了**。请按以下最佳实践处理: * **控制图片张数**:保持在官方规则内(每个提示最多 14 张图),不要堆图。 * **控制单图体积**:每张图尽量不要超过 5MB——官方单图上限为 7MB,且 base64 编码后体积还会膨胀约 1/3,原图请留足余量。 * **前端先压缩再上传**:在前端(或服务端中转层)压缩后再提交给接口,常见做法是限制最长边、转 JPEG/WebP 并控制质量参数。 * **改用 URL 传图**:Gemini 原生格式支持 `fileData.fileUri` 直接传图片 URL,可避开 base64 请求体过大的问题,详见 [Nano Banana 开发指南](/api-capabilities/nano-banana-dev-guide)。 ## 相关文档 * [Nano Banana 2 图片生成](/api-capabilities/nano-banana-2-image/overview) * [Nano Banana 定价说明](/api-capabilities/nano-banana-pricing) * [其他图像生成模型](/api-capabilities/gpt-image-1) * [API 使用手册](/api-manual) # Nano Banana Pro Agent 技能 Source: https://docs.apiyi.com/api-capabilities/nano-banana-image/skills 把 Nano Banana Pro(gemini-3-pro-image)封装成开箱即用的 Agent Skill,丢进 Codex、OpenClaw、hermes-agent、Claude Code 等任意编码 Agent,一句话即可调用 API易 平台完成文生图与图片编辑。 本页提供一个**开箱即用的 Agent 技能(Skill)**:把它放进你正在用的编码 Agent,就能用自然语言(或显式命令)直接调用 API易 平台的 **Nano Banana Pro**(`gemini-3-pro-image`)完成生图与改图。整套东西就两个文件,复制即用。 ## 这个技能能做什么 一个合并技能,脚本会根据**是否传入图片**自动判断是「文生图」还是「图片编辑」: 只给提示词 → 生成全新图片,支持 10 种宽高比与 1K/2K/4K 分辨率。 传入一张图 + 指令 → 局部编辑、风格迁移、背景替换等。 传入多张图 + 一条指令 → 多图合成、对比、换装等高级玩法。 ## 能用在哪些 Agent 一个 Skill 本质上就是**一个文件夹**:一份写给 Agent 看的说明(`SKILL.md`)+ 一个干活的脚本。所以**凡是能读取本地文件、执行命令行的编码 Agent 都能用上**——比如 **Codex、OpenClaw、hermes-agent、Claude Code** 等。 唯一要求:跑 Agent 的那台机器(你的电脑或服务器)装了 **Python 3** 并且**能联网**(脚本要直连 `api.apiyi.com`)。仅此而已,不挑具体哪家 Agent。 ## 三步装好 ### ① 建目录、贴文件 新建一个技能文件夹,放入下面两个文件(完整内容见后两节): ``` nano-banana-pro/ ├── SKILL.md ├── scripts/ │ └── nano_banana.py └── .env # 第②步创建,放你的 Key ``` ### ② 同目录写 Key 在 `nano-banana-pro/.env` 里写上你的 **API易 API Key**(在 `api.apiyi.com` 控制台创建): ```bash theme={null} APIYI_API_KEY=sk-your-api-key ``` 脚本会自动从这个 `.env` 读取 Key,**无需任何额外配置或环境变量**。 `.env` 里是你的密钥。如果这个技能要随项目仓库共享,**务必把 `.env` 加进 `.gitignore`,不要提交到 git**。 ### ③ 交给 Agent * **支持技能自动发现的 Agent**(如 Claude Code):把整个 `nano-banana-pro/` 目录放进它的技能目录——个人级 `~/.claude/skills/`,或项目级 `.claude/skills/`(随仓库共享)。 * **其他 Agent**:按它各自的技能/插件约定放置;或者最简单——**直接让 Agent「读一下这个文件夹里的 SKILL.md,并照着执行」** 即可。 装好后就能用了,跳到 [怎么用](#怎么用) 看示例。 ## SKILL.md 新建 `nano-banana-pro/SKILL.md`,完整内容如下(`description` 写清「做什么 + 何时用」,Agent 会据此自动触发): ````markdown theme={null} --- name: nano-banana-pro description: Generate or edit images via APIYI's Nano Banana Pro (gemini-3-pro-image) model. Use this when the user asks to create, draw, render, or generate an image/illustration/poster, or to edit, retouch, restyle, or composite existing images. allowed-tools: Bash(python3 *) --- # Nano Banana Pro 出图技能 通过 API易 平台调用 Nano Banana Pro(`gemini-3-pro-image`)生成或编辑图片。 ## Key 配置 脚本会自动读取技能目录下 `.env` 文件里的 `APIYI_API_KEY`(也支持同名环境变量)。 若脚本报「未找到 Key」,提示用户在 `.env` 里写一行 `APIYI_API_KEY=sk-xxx`。 ## 用法 调用同目录下的脚本,第一个参数是提示词;编辑图片时用 `-i` 传入一张或多张本地图片路径: ```bash # 文生图(默认出 1 张) python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana.py "一只戴着宇航头盔的柴犬,电影感打光" -o dog.png --size 2K --aspect 16:9 # 图片编辑(传 1 张) python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana.py "把背景换成赛博朋克城市夜景" -i input.jpg -o edited.png # 多图合成(传多张,重复 -i) python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana.py "把这两个人合到同一张办公室合影里" -i a.png -i b.png -o merged.png # 一次出多张同主题变体(最多 5 张,并发生成) python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana.py "国风山水插画" -o shanshui.png -n 3 --aspect 16:9 ``` 参数说明: - 第 1 个位置参数:提示词(必填)。 - `-i / --image`:输入图片路径,可重复多次;不传 = 文生图,传 = 图片编辑。 - `-o / --out`:输出文件名,默认 `output.png`。 - `-n / --count`:一次出几张,**默认 1**,最多 5(脚本内并发生成,超过会自动截到 5)。`-n>1` 时文件名自动加 `-1` `-2`… 后缀。 - `--aspect`:宽高比,10 选 1(`1:1` `2:3` `3:2` `3:4` `4:3` `4:5` `5:4` `9:16` `16:9` `21:9`),默认 `1:1`。 - `--size`:分辨率 `1K` / `2K` / `4K`,默认 `2K`。 ## 出图张数(重要) - **默认只出 1 张**:用户没有明确要求多张时,`-n` 保持默认(即不传),只生成 1 张。 - **指定多张才出多张**:用户说「出 3 张 / 来几张 / 多给几个版本」时才用 `-n`,且**一次不超过 5 张**。需要更多时分多次调用,不要试图绕过上限。 - 多张是同一提示词的并发变体(模型有随机性,结果各不相同)。 ## 输出位置(重要) - `-o` 传**纯文件名**(如 `dog.png`)时,图片统一保存到**项目根目录下的 `nano-banana-output/` 文件夹**,方便用户在项目里直接找到。 - `-o` 传**带目录的路径**(相对或绝对,如 `images/dog.png` 或 `/abs/path/dog.png`)时,按给定路径保存(相对路径相对当前工作目录)。 - 不要把图片写到 `/tmp`、scratchpad 等临时目录,用户会找不到。 ## 完成后 脚本会为每张图打印一行完整路径,把它们如实回报给用户。若脚本报告被内容安全策略拒绝,把拒绝原因如实转达,不要重试同一提示词。 ```` `name` 必须是小写字母 + 连字符,且**不能含 `claude` / `anthropic`** 等保留词。在支持斜杠命令的 Agent 里,目录名就是命令名——叫 `nano-banana-pro` 即 `/nano-banana-pro`。`${CLAUDE_SKILL_DIR}` 是 Claude Code 提供的技能目录变量;其他 Agent 直接用脚本的实际路径即可。 ## scripts/nano\_banana.py 新建 `nano-banana-pro/scripts/nano_banana.py`,使用 Gemini 原生格式(与本站文生图 / 图片编辑接口代码一致、已验证可跑)。**纯 Python 标准库,无需 `pip install` 任何东西**: ```python theme={null} #!/usr/bin/env python3 """通过 API易 调用 Nano Banana Pro(gemini-3-pro-image)生成 / 编辑图片。纯标准库,零依赖。""" import argparse import base64 import json import os import sys import urllib.error import urllib.request from concurrent.futures import ThreadPoolExecutor # 一次调用最多并发出几张图(边界,避免一次性打太多请求) MAX_COUNT = 5 def load_api_key(): """优先读环境变量;否则在脚本所在目录及其父目录找 .env。""" key = os.environ.get("APIYI_API_KEY") if key: return key here = os.path.dirname(os.path.abspath(__file__)) for d in (here, os.path.dirname(here)): env_path = os.path.join(d, ".env") if os.path.exists(env_path): with open(env_path, encoding="utf-8") as f: for line in f: line = line.strip() if line.startswith("APIYI_API_KEY") and "=" in line: return line.split("=", 1)[1].strip().strip('"').strip("'") return None def project_root(): """从脚本位置向上找包含 .git 或 .claude 的目录,作为项目根目录;找不到则用当前工作目录。""" d = os.path.dirname(os.path.abspath(__file__)) while True: if os.path.isdir(os.path.join(d, ".git")) or os.path.isdir(os.path.join(d, ".claude")): return d parent = os.path.dirname(d) if parent == d: return os.getcwd() d = parent def to_b64(path): with open(path, "rb") as f: return base64.b64encode(f.read()).decode() def mime_of(path): return "image/png" if path.lower().endswith(".png") else "image/jpeg" def generate(api_key, endpoint, prompt, images, aspect, size): """发一次请求,返回图片字节;失败抛 RuntimeError。""" parts = [{"text": prompt}] for path in images: parts.append({"inlineData": {"mimeType": mime_of(path), "data": to_b64(path)}}) payload = json.dumps({ "contents": [{"parts": parts}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": aspect, "imageSize": size}, }, }).encode() req = urllib.request.Request( endpoint, data=payload, method="POST", headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}, ) try: with urllib.request.urlopen(req, timeout=360) as r: resp = json.loads(r.read()) except urllib.error.HTTPError as e: raise RuntimeError(f"请求失败 HTTP {e.code}:{e.read().decode(errors='replace')}") candidates = resp.get("candidates") if not candidates: raise RuntimeError(f"未返回候选内容(可能被内容安全策略拒绝):{resp}") cand = candidates[0] # 内容审核拦截:finishReason 非 STOP,或只返回了文字说明 if cand.get("finishReason") not in (None, "STOP"): text = next((p.get("text") for p in cand["content"]["parts"] if p.get("text")), "") raise RuntimeError(f"请求被拒绝(finishReason={cand.get('finishReason')}):{text}") image_part = next((p for p in cand["content"]["parts"] if p.get("inlineData")), None) if not image_part: text = next((p.get("text") for p in cand["content"]["parts"] if p.get("text")), "") raise RuntimeError(f"未返回图片,模型回复:{text}") return base64.b64decode(image_part["inlineData"]["data"]) def resolve_paths(out, count): """决定输出路径列表。 - 若 out 带目录成分(相对/绝对),按用户给定的路径处理(相对则相对当前工作目录)。 - 若 out 是纯文件名,统一存到 <项目根>/nano-banana-output/ 下,确保好找。 count>1 时给文件名加 -1 / -2 … 后缀。 """ if os.path.dirname(out): base_path = os.path.abspath(out) else: out_dir = os.path.join(project_root(), "nano-banana-output") os.makedirs(out_dir, exist_ok=True) base_path = os.path.join(out_dir, out) if count == 1: return [base_path] base, ext = os.path.splitext(base_path) return [f"{base}-{i}{ext}" for i in range(1, count + 1)] def main(): api_key = load_api_key() if not api_key: sys.exit("未找到 API Key:请在技能目录的 .env 写一行 APIYI_API_KEY=sk-xxx") model = os.environ.get("APIYI_IMAGE_MODEL", "gemini-3-pro-image") endpoint = f"https://api.apiyi.com/v1beta/models/{model}:generateContent" parser = argparse.ArgumentParser(description="Nano Banana Pro 出图") parser.add_argument("prompt", help="提示词 / 编辑指令") parser.add_argument("-i", "--image", action="append", default=[], help="输入图片路径(可重复,传入即为编辑模式)") parser.add_argument("-o", "--out", default="output.png", help="输出文件名") parser.add_argument("-n", "--count", type=int, default=1, help=f"一次出几张,默认 1,最多 {MAX_COUNT}(并发生成)") parser.add_argument("--aspect", default="1:1", help="宽高比,如 16:9") parser.add_argument("--size", default="2K", help="分辨率 1K / 2K / 4K") args = parser.parse_args() count = args.count if count < 1: count = 1 if count > MAX_COUNT: print(f"提示:一次最多 {MAX_COUNT} 张,已将 {args.count} 限制为 {MAX_COUNT}。", file=sys.stderr) count = MAX_COUNT paths = resolve_paths(args.out, count) def task(path): data = generate(api_key, endpoint, args.prompt, args.image, args.aspect, args.size) with open(path, "wb") as f: f.write(data) return os.path.abspath(path) failures = 0 with ThreadPoolExecutor(max_workers=count) as pool: for path, result in zip(paths, pool.map(lambda p: _safe(task, p), paths)): ok, value = result if ok: print(f"图片已保存至 {value}") else: failures += 1 print(f"第 {os.path.basename(path)} 张生成失败:{value}", file=sys.stderr) if failures == count: sys.exit("全部生成失败。") def _safe(fn, arg): try: return True, fn(arg) except Exception as e: # noqa: BLE001 — 单张失败不影响其它并发任务 return False, str(e) if __name__ == "__main__": main() ``` 模型名默认 `gemini-3-pro-image`(正式名)。如需用原 `gemini-3-pro-image-preview`,设环境变量 `APIYI_IMAGE_MODEL=gemini-3-pro-image-preview` 即可,两者价格一致、效果等价。 ## 为什么一句话就能出图 很多人好奇:我又没敲命令,怎么说句"画只猫"它就出图了? 原理是这样:Agent 启动时会**先读取每个技能 `SKILL.md` 里的 `description`**(一段很短的元数据,说明「这个技能做什么、什么时候该用」)。当你说出的需求**匹配上**这段描述的场景(比如"画/生成/渲染一张图""把这张图改成…"),Agent 就**自动决定调用这个技能**,去读完整的 `SKILL.md` 并运行脚本——整个过程你不用记任何命令。 所以: * **写得好的 `description` = 更准的自动触发**。本技能的描述已覆盖"生成/绘制/编辑/合成图片"等说法。 * 不想靠 Agent 猜、想要**百分百可控**时,用下面的**显性调用**。 ## 怎么用 ### 自然语言(隐式触发) 装好后直接对 Agent 说话即可: | 你说 | 技能行为 | | - | - | | "用 nano banana 画一张 16:9 的雪山日出海报" | 调脚本(无 `-i`),出 1 张 png | | "把 photo.jpg 的背景虚化,突出人物" | 调脚本 `-i photo.jpg`,出 1 张 png | | "国风山水来 3 张不同的" | 调脚本 `-n 3`,并发出 3 张 png | | "把 a.png 和 b.png 里的人合成一张合影" | 调脚本 `-i a.png -i b.png`,出 1 张 png | ### 显性调用(更可控) 不想让 Agent 自己判断时,两种显式方式: * **支持斜杠命令的 Agent**(如 Claude Code): ```text theme={null} /nano-banana-pro 一只在花园里打盹的橘猫,油画风格 --size 4K --aspect 3:2 ``` * **任意 Agent / 直接命令它跑脚本**(最通用): ```text theme={null} 运行 python3 nano-banana-pro/scripts/nano_banana.py "一只在花园里打盹的橘猫,油画风格" --size 4K --aspect 3:2 ``` **怎么控制比例和清晰度**:用 `--aspect` 选宽高比(如 `--aspect 16:9`,10 选 1),用 `--size` 选分辨率(`1K` / `2K` / `4K`)。直接说"竖屏 9:16""画方图""出 4K",Agent 也会自动带上这两个参数。 ## 生成的图片在哪里 * 当 `-o` 只传**文件名**(如 `-o dog.png`)时,图片统一存到**项目根目录下的 `nano-banana-output/` 文件夹**(脚本自动创建),所以你在项目里的这个文件夹就能直接找到。 * 「项目根目录」= 脚本从自身位置向上找到的第一个含 `.git` 或 `.claude` 的目录——**不管 Agent 在哪个目录运行,图都落在项目里**,不会跑进临时目录害你找不到。 * 脚本会**为每张图打印一行完整绝对路径**,例如 `图片已保存至 /Users/you/project/nano-banana-output/dog.png`。 * 默认**只出 1 张**;`-n 3` 一次出 3 张(最多 5,超过自动截到 5),文件名自动加 `-1`、`-2`、`-3` 后缀。 * 传**带目录的路径**(如 `-o images/dog.png` 或绝对路径)时,按你给的路径存(相对路径相对当前工作目录),不进 `nano-banana-output/`。 * 图片编辑同理:输出是新文件,**不会覆盖你的原图**。 Nano Banana Pro 有严格的内容安全管控。若脚本提示 `finishReason` 非 `STOP` 或返回的是拒绝说明文本,请按提示调整内容,不要对同一违规提示词反复重试。详见 [生图接口的错误处理指南](/api-capabilities/nano-banana-image/overview#api-错误处理指南)。 ## 相关文档 * [Nano Banana Pro 图片生成总览](/api-capabilities/nano-banana-image/overview) * [文生图 API 参考](/api-capabilities/nano-banana-image/text-to-image) * [图片编辑 API 参考](/api-capabilities/nano-banana-image/image-edit) * [Nano Banana 定价说明](/api-capabilities/nano-banana-pricing) # 文生图 API 参考 Source: https://docs.apiyi.com/api-capabilities/nano-banana-image/text-to-image api-reference/nano-banana-pro-generate-openapi.yaml POST /v1beta/models/gemini-3-pro-image-preview:generateContent Nano Banana Pro 文生图 API 参考与在线调试 — 输入文本描述生成图片 右侧的交互式 Playground 支持下拉选择参数(宽高比、分辨率、响应类型等)。请在 **Authorization** 中填入你的 API Key(格式:`Bearer sk-xxx`),即可一键发送请求测试。 **场景说明**:本页用于「文本生成图片」。只需输入提示词即可,无需上传任何图片。如需根据现有图片做编辑,请使用 [图片编辑接口](/api-capabilities/nano-banana-image/image-edit)。 **🖥️ 浏览器 Playground 限制(重要)** 本接口的响应里包含 base64 编码的图片(`inlineData.data`,数 MB 量级)。受浏览器渲染限制,右侧 Playground 在收到响应后**可能弹出** `请求时发生错误: unable to complete request` ——**实际请求已经成功**,只是浏览器无法把这么长的 base64 显示出来。 **推荐做法**(小白零踩坑): * **直接复制下方"代码示例"中的 Python / Node.js / cURL 到本地运行**,代码会自动 `base64.b64decode` 并把图片**保存为本地文件**。 * 如要在浏览器里试 Playground,把 `imageSize` 设为最小档(如 `1K`),缩小响应体积。 图片 API 全部为**同步调用**:没有异步任务 ID,客户端断开连接结果即丢失、但请求仍会计费。请为本模型设置足够大的 timeout,详见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices)。 ## 代码示例 ### Python ```python theme={null} import requests import base64 API_KEY = "sk-your-api-key" PROMPT = "一只可爱的小猫坐在花园里,油画风格,高清,细节丰富" response = requests.post( "https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent", headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json={ "contents": [{"parts": [{"text": PROMPT}]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }, timeout=300 ).json() img_data = response["candidates"][0]["content"]["parts"][0]["inlineData"]["data"] with open("output.png", 'wb') as f: f.write(base64.b64decode(img_data)) print("图片已保存至 output.png") ``` ### cURL ```bash theme={null} curl -X POST "https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "contents": [{"parts": [{"text": "未来主义的城市夜景,霓虹灯,赛博朋克风格"}]}], "generationConfig": { "responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"} } }' ``` ### Node.js ```javascript theme={null} import fs from "fs"; const API_KEY = "sk-your-api-key"; const response = await fetch( "https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [{ text: "未来主义的城市夜景,霓虹灯,赛博朋克风格" }] }], generationConfig: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "16:9", imageSize: "2K" } } }) } ); const data = await response.json(); const imgBase64 = data.candidates[0].content.parts[0].inlineData.data; fs.writeFileSync("output.png", Buffer.from(imgBase64, "base64")); ``` ## 参数说明速查 | 参数 | 类型 | 必填 | 说明 | | - | - | - | - | | `contents[].parts[].text` | string | 是 | 文本提示词 | | `generationConfig.responseModalities` | array | 是 | `["IMAGE"]` 或 `["TEXT","IMAGE"]` | | `generationConfig.imageConfig.aspectRatio` | string | 否 | 10 种宽高比,默认 `1:1` | | `generationConfig.imageConfig.imageSize` | string | 否 | `1K` / `2K` / `4K`,默认 `1K` | 详细的参数文档、可选值和默认值请查看右侧 Playground 中的字段说明,所有 enum 类型字段(如 `aspectRatio`、`imageSize`)都支持下拉选择,无需手动输入。 **不支持的功能** 以下谷歌官方功能在 API易 中**不支持**,需要单独计费: * **Grounding with Google Search**(谷歌搜索增强):通过 `tools: [{"google_search": {}}]` 调用实时搜索信息 * **thinkingConfig**(思维模式):仅 Nano Banana 2 支持,Nano Banana Pro 不支持此参数 如需搜索增强功能,请联系客服了解单独计费方案。 # Nano Banana OSS 分组 Source: https://docs.apiyi.com/api-capabilities/nano-banana-oss-group Nano Banana OSS(NB-OSS)内测分组:图片输出为 URL 地址而非 Base64,减轻传输压力、提升体验,适合直接使用 URL 的场景。 ## 背景必读 **特点**:本分组为内测,可输出的图片为 **URL 地址**,而非 Base64,可减轻 Base64 传输压力,提升客户体验。**适合直接用 URL 的场景**。如果没有特别需求、能处理 Base64 输出的图片格式,则仍然建议使用「正常默认分组」或「NanoBanana 企业分组」。 **支持模型**(Nano Banana Pro 和第一代): * `gemini-3-pro-image-preview` * `gemini-3.1-flash-image-preview` * `gemini-2.5-flash-image` ## 如何开始 联系管理员为你的账号开通 NB-OSS 可见分组(在「编辑用户信息 → 额外可见分组」中添加 NB-OSS)。 编辑用户信息界面,在额外可见分组中添加 NB-OSS 分组 新建令牌时,计费模式选「按次计费」,分组选 **NB-OSS**(Nano Banana PRO,输出图片为 URL,替代 Base64)。只换令牌,请求方式不变。 创建令牌界面:计费模式选择按次计费,选择分组 NB-OSS,输出图片为 URL 替代 Base64 替换令牌进行测试。**代码层面需要兼容 URL 输出的解析** —— 不要直接替换 Base64,两者兼容最佳。 ## 示例代码 ```bash theme={null} curl --location 'https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent' \ --header 'Authorization: Bearer sk-' \ --header 'Content-Type: application/json' \ --data '{ "contents": [ { "parts": [ { "fileData": { "fileUri": "https://raw.githubusercontent.com/apiyi-api/ai-api-code-samples/refs/heads/main/Vision-API-OpenAI/otter.png", "mimeType": "image/png" } }, { "text": "add five dogs" } ], "role": "user" } ], "generationConfig": {"responseModalities": ["IMAGE"], "imageConfig": { "aspectRatio": "16:9", "imageSize": "2K" }}, "safetySettings": [] }' > output.json ``` ### 输出示例 在 `text` 字段里有图片的地址,下方的 `thoughtSignature` 是推理过程的 base64。 API 响应 JSON 示例,text 字段包含图片 URL 地址,thoughtSignature 字段为推理的 base64 ## OSS 存储节点与下载速度 ### 图片存储在哪里? 输出的图片 URL 存储在**阿里云 OSS 洛杉矶节点(美西 us-west-1,北美区域)**,URL 形如: ``` https://.oss-us-west-1.aliyuncs.com/xxxx.png ``` URL 的二级域名(`` 部分,如 `mycdn-gg`)**可能会变化,请不要在代码或防火墙规则中写死完整域名**。如需做域名判断或白名单,请匹配后缀 `oss-us-west-1.aliyuncs.com`(阿里云 OSS 官方域名),或更宽松地放行 `*.aliyuncs.com`。 ### 下载慢怎么办? 由于存储节点在北美,从中国大陆等地直连下载时速度可能受限,常见原因和建议: * **公司网络对海外流量限速 / 白名单拦截**:请联系网络管理员对 `*.oss-us-west-1.aliyuncs.com`(或 `*.aliyuncs.com`)解除限速、加入白名单。 * **及时转存**:拿到 URL 后建议尽快下载并转存到自己的存储 / CDN,再分发给终端用户,不要把 OSS URL 直接长期暴露给最终用户。 * **服务器代下**:如果本地网络下载慢,可以用海外或网络出口较好的服务器先下载再中转。 更多网络链路排查思路(DNS、路由、跨境带宽等),可参考 FAQ:[下载 CDN 图片/视频很慢怎么办?](/faq/cdn-download-slow) ### 网址打不开? 核心是把输出内容 JSON 里的转义字符 `\u0026` 还原成正常的 `&`,同时忽略 `thoughtSignature` 后面的 base64 内容。 说明图:图片链接在 text 字段,需将 JSON 转义符 反斜杠 u0026 还原成 & 号,忽略 thoughtSignature 后的 base64 内容 # Nano Banana 系列价格总览 Source: https://docs.apiyi.com/api-capabilities/nano-banana-pricing Nano Banana 系列图片模型(Pro / 2.1 / 2 / 2 Lite / 第一代)完整价格对比,包括按次计费、按量计费、充值优惠及令牌选择建议。最低可达官方 30.3% 的价格。 ## 概述 Nano Banana 系列包含五款图片生成模型,均支持通过 API易 以远低于官方的价格调用。本页汇总所有 Nano Banana 模型的定价信息,帮助你快速选择最适合的计费方案。 在 API 调用时,所使用的\*\*「令牌类型」\*\*决定计费方式。Nano Banana Pro、2.1、2、Lite 都有两种计费方式:**按次计费**、**按量计费**。且仅有两个分组:默认稳定分组、企业高可用分组。 ## 五款模型速览 | 特性 | **Nano Banana Pro** | **Nano Banana 2.1** | **Nano Banana 2** | **Nano Banana 2 Lite** | **Nano Banana(第一代)** | | - | - | - | - | - | - | | 模型 ID | `gemini-3-pro-image-preview` | `gemini-nano-banana-2.1` | `gemini-3.1-flash-image-preview` | `gemini-3.1-flash-lite-image` | `gemini-2.5-flash-image` | | 最高分辨率 | 4K | 4K | 4K | 1K | 1K | | 按次计费 | \$0.09/次 | \$0.045/次 | \$0.055/次 | \$0.025/次 | \$0.02/次 | | 按量计费 | 内测中 | ✅ 已开放 | ✅ 已开放 | ✅ 已开放 | ❌ 不支持 | | 最低折扣 | 官方 31.25% | 官方 33.2% | 官方 30.3% | 官网 4 折 | 官方 50% | ## Nano Banana Pro 定价 模型名:`gemini-3-pro-image-preview` ### 按次计费 \$0.09 美金/次,1-4K 统一按次价格,计费简单、4K 图片更省。 | 计费项 | API易默认 | 充值加赠 +10% | 充值加赠 +20% | 谷歌官方 | | - | - | - | - | - | | 单张价格(\$) | \$0.09 | \$0.0818 | \$0.075 | \$0.24 | | 单张价格(¥) | ¥0.63 | ¥0.573 | ¥0.525 | ¥1.68 | | 相对官方 | 37.5% | 34.0% | **31.25%** | / | 4K 图片最多省 **68.75%**!按次计费不区分分辨率,4K 和 1K 同价,分辨率越高越划算。 ### 按量计费(未开放,可内测) | 计费项 | API易 | 谷歌官方 | | - | - | - | | 输入价格 | \$0.88/M tokens | \$2/M tokens | | 输出价格 | \$52.8/M tokens | \$120/M tokens | | 相对官方 | **44%** | / | 按量计费预估单张价格: | 分辨率 | API易预估 | 谷歌官方预估 | | - | - | - | | 1-2K | \~\$0.075 | \~\$0.134 | | 4K | \~\$0.11 | \~\$0.24 | Tokens 计费因输入和输出图片的 Tokens 会动态变化,以上为预估值。按量计费适合 1-2K 图片需求,价格更低;4K 场景建议使用按次计费更划算。 ## Nano Banana 2.1 定价 模型名:`gemini-nano-banana-2.1`(2026 年 10 月 6 日发布,Nano Banana 2 的升级版,详见 [Nano Banana 2.1 生图/编辑](/api-capabilities/gemini-nano-banana-2.1/overview)) ### 按次计费 \$0.045 美金/次(2026-10-13 起由 \$0.05 降价),1-4K 统一按次价格,4K 图片更省。 | 计费项 | API易默认 | 充值加赠 +10% | 充值加赠 +20% | 谷歌官方(4K) | | - | - | - | - | - | | 单张价格(\$) | \$0.045 | \$0.0409 | \$0.0375 | \$0.113 | | 单张价格(¥) | ¥0.315 | ¥0.286 | ¥0.263 | ¥0.791 | | 相对官方 | **39.8%** | **36.2%** | **33.2%** | / | ### 按量计费 | 计费项 | API易 | 谷歌官方 | | - | - | - | | 输入价格 | \$0.66/M tokens | \$1.5/M tokens | | 输出价格(图片、文本、思考统一) | \$13.2/M tokens | 图片 \$30/M,文本与思考 \$7.5/M | | 相对官方 | **44%**(图片部分) | / | 按量计费实测单张价格(2026-10-07,含默认思考 tokens): | 分辨率 | API易实测 | 谷歌官方 | | - | - | - | | 1K | \~\$0.026 | \~\$0.040 | | 2K | \~\$0.035 | \~\$0.058 | | 4K | \~\$0.062 | \~\$0.120 | Nano Banana 2.1 默认就会思考,每张图另有约 400–1300 个思考 tokens 与图片一起按输出价计费,按量单张有高有低;1K / 2K 选按量更低,4K 选按次(\$0.045)更省。不支持 512px。 模型价格与官网对齐,且可能随官网调整;上表仅供参考,具体以顶部导航「模型价格」栏目为准:[模型价格](/models/index)。 ## Nano Banana 2 定价 模型名:`gemini-3.1-flash-image-preview` ### 按次计费 \$0.055 美金/次,1-4K 统一按次价格,计费简单、4K 图片更省。 | 计费项 | API易默认 | 充值加赠 +10% | 充值加赠 +20% | 谷歌官方 | | - | - | - | - | - | | 单张价格(\$) | \$0.055 | \$0.05 | \$0.0458 | \$0.151 | | 单张价格(¥) | ¥0.385 | ¥0.35 | ¥0.32 | ¥1.68 | | 相对官方 | **36.4%** | **33.1%** | **30.3%** | / | ### 按量计费 | 计费项 | API易 | 谷歌官方 | | - | - | - | | 输入价格 | \$0.18/M tokens | \$0.5/M tokens | | 输出价格 | \$21.6/M tokens | \$60/M tokens | | 相对官方 | **36%** | / | 按量计费预估单张价格: | 分辨率 | API易预估 | 谷歌官方 | | - | - | - | | 512px | \~\$0.024 | \$0.045 | | 1K | \~\$0.03 | \$0.067 | | 2K | \~\$0.036 | \$0.101 | | 4K | \~\$0.06 | \$0.151 | 以上为预估值,实际按输入和输出的 tokens 计算。可叠加充值活动最多送 20% 额度,进一步降低成本。 ## Nano Banana 2 Lite 定价 模型名:`gemini-3.1-flash-lite-image` 最快最省的一档,专注 1K 画布、约 4 秒出图。**推荐按量优先**:实测单价更低且可预期。 ### 按量计费(推荐) | 计费项 | API易 | 谷歌官方 | 相对官方 | | - | - | - | - | | 输入价格 | \$0.10/M tokens | \$0.25/M tokens | **官网 4 折** | | 输出价格 | \$12/M tokens | \$30/M tokens | **官网 4 折** | 按量计费预估单张价格: | 分辨率 | API易预估 | 谷歌官方 | | - | - | - | | 1K | **\~\$0.018** | \~\$0.034 | **实测可预期**:1K 出图按量(按 tokens)**实测平均约 \$0.018/次**(常见区间约 \$0.016–\$0.019,输出 token 决定单价),比按次固定价 \$0.025/次更省。可叠加充值活动进一步降本。 ### 按次计费 | 计费项 | API易 | 谷歌官方 | 说明 | | - | - | - | - | | 单张价格 | \$0.025/次 | 约 \$0.034/张 | 固定单价,后续可能下调、暂时不动 | **计费模式怎么选**:Lite **首选「按量优先」**(Pay-as-you-go Priority)——实测约 \$0.018/次、比按次 \$0.025 更省且可预期,同一把令牌还兼容 Nano Banana Pro / 2 的按次计费,一把跑全系列。按次 \$0.025/次为固定单价,后续可能下调、暂时不动。 ## Nano Banana 第一代定价 模型名:`gemini-2.5-flash-image` 仅支持 1K 图片,固定按次计费。 | 计费项 | API易 | 谷歌官方 | 折扣 | | - | - | - | - | | 单张价格 | \$0.02/次 | \$0.039/次 | **约 5 折** | 同样参与充值活动可进一步降低成本。 ## 充值活动:更能降本 通过参与充值加赠活动,可在已有优惠基础上进一步降低成本。 查看当前充值加赠活动详情和各档位加赠比例。 前往管理后台创建和管理 API 令牌。 ## 令牌类型选择建议 选择令牌类型时,令牌的「Billing model」设置决定了计费方式。请根据你的主要使用场景选择: | 模型 | 使用场景 | 推荐令牌类型 | | - | - | - | | Nano Banana Pro | 1-4K 混合使用 | 按量优先 | | Nano Banana Pro | 只跑 4K | 按次计费 | | Nano Banana 2 | 0.5K-4K 混合使用 | 按量优先 | | Nano Banana 2 | 只用 4K | 按次计费 | | Nano Banana 2 Lite | 1K 走量出图 | 按量优先 | **简单原则**:如果你主要生成 4K 图片,选**按次计费**更划算(不区分分辨率统一价格);如果分辨率混合使用,选**按量计费**可以在低分辨率时节省更多。 ## 账单波动不等于调价 同一款模型两笔账单差很多,先别归因于调价:**按量计费**下单次扣费本身就随参数浮动。实测影响最大的两项: | 参数 | 对单次账单的影响 | 适用范围 | | - | - | - | | 输出分辨率 | 1K 到 4K 约 **+90%**(\$0.033 → \$0.064,Nano Banana 2 按量实测;Pro 按量 1-2K 到 4K 约 +47%) | 仅按量计费;2 Lite 与第一代只出 1K,不受影响 | | `thinkingLevel`(思考等级) | 默认档到 `high` 约 **+54%** | 仅 Nano Banana 2 / 2 Lite;Pro 思考恒开、不可调 | 同一个模型昨天约 \$0.033、今天约 \$0.052,通常是参数不同造成的,不是价格调整。 两个参数的逐档实测(tokens、扣费、耗时)见 [Nano Banana 2 生图/编辑](/api-capabilities/nano-banana-2-image/overview) 与 [usage 字段与输出解读](/api-capabilities/nano-banana-usage-metadata)。 **要成本可预期就选按次计费**:1-4K 统一单价(Pro \$0.09/次、Nano Banana 2 \$0.055/次),不随分辨率和 tokens 波动。 ## 相关文档 * [Nano Banana 2 生图/编辑](/api-capabilities/nano-banana-2-image/overview) - Nano Banana 2 完整使用指南 * [Nano Banana Pro 图片生成](/api-capabilities/nano-banana-image) - Nano Banana Pro 使用指南 * [Nano Banana Pro 图片编辑](/api-capabilities/nano-banana-image-edit) - 图片编辑功能 * [充值加赠活动](/faq/recharge-promotions) - 充值优惠详情 # Nano Banana Pro 出图失败包补计划 Source: https://docs.apiyi.com/api-capabilities/nano-banana-pro-guarantee API易 针对按次计费的 Nano Banana Pro 推出出图失败包补计划:因谷歌风控导致的非主观出图失败,按条数核算后补发额度。 ## 计划简介 为了更好地服务客户,API易 推出 **Nano Banana Pro 出图失败包补计划**。 保障模型仅为**按次计费的 Nano Banana Pro**。Nano Banana 2 可选择**按量计费**的令牌,失败的调用计费极少、可忽略。 当请求返回**状态码 200 但出图失败**时,这是谷歌侧的反馈,API易 透明代理只是直接转发结果——我们同样希望客户成功出图。本计划即针对这类**非主观原因**导致的失败进行额度补发。 ## 什么情况下 Nano Banana Pro 不出图? 谷歌的内容风控策略在持续收紧,常见触发拒绝的情形: * **内容安全**:NSFW、未成年人相关内容 * **去水印**:较为特殊的一类 * **知名 IP**(2026 年 1 月 23 日新增):如迪士尼等——谷歌疑似调整了新的风控政策 * **更严格的安全机制**(2 月 27 日,Nano Banana 2 上线后):知名人物、金融/订单信息修改、人物换装/换脸、隐性 Sex 等,均会返回文案报错,类似「我不能完成 xxx 的修改」 ## 出图失败的表现特征 谷歌返回一段文字,例如:「我无法协助完成这个工作」 / `I'm just a language model and can't help with that.` 直接拒绝出图,报错为空。接口数据中关键指标为 `"candidatesTokenCount": 0` 日志列表,gemini-3-pro-image-preview 模型的补全 Tokens 列出现 173、176 等极小值,标记为出图失败 ## 为什么失败仍会扣费? * 谷歌会扣配额:不修改提示词反复请求,会浪费谷歌的 RPD 配额 * 系统暂时做不到「输出为空即不扣费」 * 违规内容请求更容易导致我们的官方 KEY 被封——这是不可挽回的损失 ## 如何参与? 面向客户范围: 1. 每月消耗 **1000 美金起**(不限站内模型)——门槛定得很低,小额度测试、自用出现失败的概率也不高 2. 面向**工具服务商**:因为客户侧并不好控制用户的输入内容 3. 仅限**非主观原因**:恶意请求相同/相似的违规内容,拒绝补发 4. 时间范围:**5 月 1 日 (UTC+8) 起** 5. 陆续沟通,加入我们内部名单登记 ## 如何补发? 进入**日志**栏目,点击右上角的【导出】,选择时间范围(比如上个月),选择【异步导出】;提交后在顶部导航菜单的【异步任务】中查看进度,并下载最终的 Excel 数据结果。 日志栏目工具栏,右上角导出按钮被高亮标记 **补发额度**:统计出具体的失败条数,按 `条数 × 模型价格 / 折扣系数` 进行补发(例如充值赠送 15%,则除以 1.15)。 **补发时间**:每个月底统计上个月的数据,大概在**下月初的 5 个工作日内**补发到账。 # usage 字段与输出解读 Source: https://docs.apiyi.com/api-capabilities/nano-banana-usage-metadata 解读 gemini-3-pro-image 响应 JSON 的输出结构与 usageMetadata 各字段含义,解释三个看似异常、实际是模型固有行为的计数现象 本文面向通过 API易 调用 `gemini-3-pro-image`(Nano Banana Pro)的开发者,解释响应 JSON 的输出结构与 `usageMetadata` 各字段的实际含义,并说明几个**看起来像异常、实际是模型固有行为**的计数现象。全部结论来自对生产网关的实测(48 次文生图 + 18 次图片编辑),并与谷歌官方文档(`ai.google.dev/gemini-api/docs/image-generation`)交叉核对,非推测。 ## 响应的整体结构 API易 的 nano banana 系列走 Google 原生格式,响应顶层固定四个字段: ```json theme={null} { "candidates": [ ... ], // 生成结果(图片/文本 parts) "usageMetadata": { ... }, // token 用量 "modelVersion": "gemini-3-pro-image", "responseId": "..." } ``` ### 成功出图时 ```json theme={null} "candidates": [{ "content": { "role": "model", "parts": [ { "inlineData": { "mimeType": "image/jpeg", "data": "" } } ] }, "finishReason": "STOP", "index": 0 }] ``` **parts 里可能不止一张图**。当提示词是复杂任务型(如"人物四视图/角色设定图"这类多约束任务)时,模型可能一次返回多张图片 part(实测 2–10 张)——它们是模型"思考过程"生成的中间稿加最终稿,官方文档明确"思考中的最后一张图片也是最终渲染的图片",**取最后一张即可**。纯文生图和简单编辑(加饰品/换背景/换风格)通常只返回 1 张。无论哪种情况,解析时都请遍历 parts,需要单图时取最后一个 `inlineData`。现象详解见 [开发指南 · 偶现多图输出是怎么回事](/api-capabilities/nano-banana-dev-guide#偶现多图输出是怎么回事)。 #### parts 里还可能混有文本段 上面示例里 `parts` 只有一个图片段,但这**不是固定结构**。`parts` 是异构数组,实测出现过三种排列: | parts 结构 | 长度 | 图片下标 | | - | - | - | | `inlineData` | 1 | `0` | | `text` + `inlineData` | 2 | **`1`** | | `inlineData` + `text` | 2 | **`0`** | `responseModalities` 里含 `TEXT`、提示词要求模型给出文字说明,都会让响应里多出文本段;文本段排在图片前还是图片后也不固定。**因此图片落在哪个下标并非定值。** 写死下标的两种写法是**互补**的:图片必落在 `[0]` 或 `[1]`,写死任一个都存在拿不到图的请求。正确做法见下文 [解析与对账最佳实践](#解析与对账最佳实践);也可在 `generationConfig` 里显式声明 `responseModalities: ["IMAGE"]` 表明只要图片作为加固,但**不能替代**遍历筛选。 ### 被安全策略拦截时 HTTP 状态码**仍是 200**,区别在 candidate 内部: ```json theme={null} "candidates": [{ "content": { "parts": null }, // ⚠️ parts 为 null,不是空数组 "finishReason": "IMAGE_SAFETY", // 或 NO_IMAGE / PROHIBITED_CONTENT "finishMessage": "Unable to show the generated image. ...", // 仅部分场景携带 "index": 0 }] ``` * `finishReason` 取值实测有三种:`IMAGE_SAFETY`(输出图违规)、`PROHIBITED_CONTENT`(触发禁用政策,附带 `finishMessage` 说明)、`NO_IMAGE`(未生成图片,通常秒回)。 * 拒绝说明放在 `finishMessage` 字段里,**不会**以文本 part 形式出现在 `parts` 中。 * 解析代码务必兼容 `parts` 为 `null` 的情况,否则拦截响应会导致报错。 各类失败的判断指标、内容审核政策与友好提示方案,见 [Gemini 出图错误处理指南](/api-capabilities/gemini-image-error-handling)。 ## usageMetadata 字段含义 成功出图时固定 6 个字段: ```json theme={null} "usageMetadata": { "promptTokenCount": 615, // 输入总 tokens(文本 + 输入图片) "candidatesTokenCount": 2478, // 输出总 tokens(含图片 + 内部生成 tokens) "thoughtsTokenCount": 208, // 思考(推理)tokens "totalTokenCount": 3301, // 本次请求计费总量 "promptTokensDetails": [ { "modality": "TEXT", "tokenCount": 99 }, { "modality": "IMAGE", "tokenCount": 516 } ], "candidatesTokensDetails": [ { "modality": "IMAGE", "tokenCount": 2240 } ] } ``` | 字段 | 含义 | 可靠性 | | - | - | - | | `promptTokenCount` | 输入侧总量 | ✅ 恒等于 `promptTokensDetails` 之和 | | `candidatesTokenCount` | 输出侧总量 | ✅ 计费口径;**但大于 details 之和,见下文现象一** | | `thoughtsTokenCount` | 思考 tokens,实测通常 50–350 | ✅ | | `totalTokenCount` | 总量 | ✅ 出图时恒等于前三项之和;**拒绝时例外,见下文现象二** | | `promptTokensDetails` | 输入按模态分解 | ✅ 完整分解 | | `candidatesTokensDetails` | 输出按模态分解 | ⚠️ **只是图片部分,不是完整分解** | **图片 tokens 由分辨率档决定,与宽高比无关**:1K 与 2K 档均为 **1120 tokens/张**,4K 档为 **2000 tokens/张**;宽高比只改变像素尺寸,不改变 token 数。一次返回 N 张图则 details 精确等于 N × 单张值。 下表为谷歌官方给出的 Pro Image 宽高比与图片大小对照(来源:`ai.google.dev/gemini-api/docs/image-generation`),与我们对 `gemini-3-pro-image` 的实测完全一致: | 宽高比 | 1K 尺寸 | 1K tokens | 2K 尺寸 | 2K tokens | 4K 尺寸 | 4K tokens | | - | - | - | - | - | - | - | | 1:1 | 1024x1024 | 1120 | 2048x2048 | 1120 | 4096x4096 | 2000 | | 2:3 | 848x1264 | 1120 | 1696x2528 | 1120 | 3392x5056 | 2000 | | 3:2 | 1264x848 | 1120 | 2528x1696 | 1120 | 5056x3392 | 2000 | | 3:4 | 896x1200 | 1120 | 1792x2400 | 1120 | 3584x4800 | 2000 | | 4:3 | 1200x896 | 1120 | 2400x1792 | 1120 | 4800x3584 | 2000 | | 4:5 | 928x1152 | 1120 | 1856x2304 | 1120 | 3712x4608 | 2000 | | 5:4 | 1152x928 | 1120 | 2304x1856 | 1120 | 4608x3712 | 2000 | | 9:16 | 768x1376 | 1120 | 1536x2752 | 1120 | 3072x5504 | 2000 | | 16:9 | 1376x768 | 1120 | 2752x1536 | 1120 | 5504x3072 | 2000 | | 21:9 | 1584x672 | 1120 | 3168x1344 | 1120 | 6336x2688 | 2000 | 官方**中文版**文档把英文表头 `1K tokens`(即"1K 档的 token 数")直译成了「1,000 个 token」,容易被误读成"每张 1000 tokens"——实际按张计的 token 数以单元格数值为准:1K/2K 档每张 1120,4K 档每张 2000。另外 512px 档(747 tokens/张)仅 Flash 系列图片模型支持,`gemini-3-pro-image` 只有 1K/2K/4K 三档;Nano Banana 2 Lite(`gemini-3.1-flash-lite-image`)比较特殊,本身**只有 1K 一档**,不含 512px。 ## 三个"看起来像异常"的现象及解释 ### 现象一:candidatesTokenCount ≠ candidatesTokensDetails 之和 —— 正常,必然如此 实测 **100%**(49/49 成功出图样本)满足:`candidatesTokenCount` 比 details 之和**大 88–630 tokens**(提示词越复杂、返回图片越多,差值越大)。 原因:`candidatesTokensDetails` 只统计**图片本体**(固定 1120/2000 每张);而 `candidatesTokenCount` 还包含图像生成过程伴随的内部 tokens,这部分没有对应的 modality 条目。这是 Gemini 原生计数口径,API易 透传不做改写。 **结论:请勿把 details 当作 `candidatesTokenCount` 的完整分解来校验;对账、计费一律以 `candidatesTokenCount` / `totalTokenCount` 为准,details 仅用于估算图片部分的占比。** ### 现象二:totalTokenCount ≠ prompt + candidates + thoughts —— 只发生在无图输出的响应上 * 正常出图时,等式**严格成立**(49/49):`total = promptTokenCount + candidatesTokenCount + thoughtsTokenCount`。 * 被安全拦截(无图输出)时,等式**必然不成立**(6/6),且模式固定: ```text theme={null} candidatesTokenCount == thoughtsTokenCount // 思考 tokens 被同时写入两个字段 totalTokenCount == promptTokenCount + thoughtsTokenCount // total 只计一次,是正确的 ``` 即拒绝响应中 `candidatesTokenCount` 是 `thoughtsTokenCount` 的镜像值,三项相加会把思考多算一份。这同样是上游固有行为。**`totalTokenCount` 本身是准的,直接用它即可**;如果你的日志里有约 10% 的响应"等式不平",请核对这些响应是否 `parts` 为空——大概率正是安全拦截样本。 ### 现象三:输出 tokens 偶尔高达 6000+ —— 来自思考过程返回的多张图片 part 谷歌官方文档说明,Gemini 3 图片模型是思考型模型:默认启用"思考"且无法在 API 中关闭,模型会生成临时图片来测试构图和逻辑,且"思考中的最后一张图片也是最终渲染的图片"(来源:`ai.google.dev/gemini-api/docs/image-generation` 思考过程章节)。 实测中,这些思考中间稿在原生 `generateContent` 响应里以**普通图片 part** 的形式返回:每个 part 都带 `thoughtSignature` 字段、但没有 `thought: true` 标记,并且**每张都按 1120 tokens 计入 `candidatesTokensDetails`**。官方称思考最多生成两张临时图片,但复杂任务型提示词下实测单次返回最多见 **10 张** part。usage 随图片数严格线性增长: | 返回图片数 | candidatesTokensDetails | candidatesTokenCount | totalTokenCount | | - | - | - | - | | 1(文生图,1K) | 1120 | \~1210–1275 | \~1350–1450 | | 2 | 2240 | \~2500 | \~3300 | | 3 | 3360 | \~3800 | \~4600 | | 4 | 4480 | \~5000 | \~5900 | | 5 | 5600 | \~6200 | \~7000 | | 10 | 11200 | \~12700 | \~13500 | 而 `thoughtsTokenCount` 字段只统计**文本思考**,实测从未超过 400——高输出 tokens 的来源是图片 part 的张数,不是这个字段。看到 6000+ 甚至上万的输出 tokens 时,请检查该响应的 parts 数量——几乎可以确定是多图响应,属于正常计费(对账仍以 `totalTokenCount` 为准)。 ## 思考等级与两种 API 范式 ### thinkingLevel 对 tokens 的影响 思考等级控制仅 **Gemini 3.1 Flash Image / Flash Lite Image** 支持(`generationConfig.thinkingConfig.thinkingLevel`,默认 `minimal`,可选 `high`);`gemini-3-pro-image` 的思考恒开、无法调节。实测(同一提示词、1K 文生图,经 API易 网关): | 模型 / 设置 | thoughtsTokenCount | 图片 tokens | totalTokenCount | 耗时 | | - | - | - | - | - | | gemini-3.1-flash-image · minimal(默认) | 无该字段 | 1120 | \~1534–1554 | \~12–13s | | gemini-3.1-flash-image · high | 700–792 | 1120 | \~2243–2375 | \~18–23s | | gemini-3-pro-image · 传入 high | 181–214(与默认区间无异) | 1120 | \~1427–1471 | \~23s | * **high 只增加思考 tokens 与延迟,不改变图片 tokens**(仍为每张 1120)。 * 给 `gemini-3-pro-image` 传 `thinkingLevel` 不会报错,但实测无效果,思考 tokens 仍在默认区间。 * `includeThoughts: true` 实测不改变返回结构与计费;官方明确:无论是否查看思考过程,思考 tokens 都默认计费。 * 官方说明"最少思考并不意味着模型完全不进行思考"——minimal 下只是 usage 里不再单列 `thoughtsTokenCount` 字段。 Nano Banana 2 Lite(`gemini-3.1-flash-lite-image`)与 Nano Banana 2 同属 3.1 Flash 系列,同样支持 `thinkingLevel` 调节,机制与上表一致;但暂未单独实测收录进上表,具体价格明细见 [Nano Banana 系列价格总览](/api-capabilities/nano-banana-pricing)。 ### 图片模型与文本模型的思考 tokens 有何不同 * **文本思考模型**:思考产物是文本,`thoughtsTokenCount` 可达数千,按输出 token 价计费;官方定价按模型内部生成的**完整思考**计,即使 API 只返回思考摘要(来源:`ai.google.dev/gemini-api/docs/thinking` 价格章节)。 * **图片思考模型**:思考产物有两类——少量**文本思考**计入 `thoughtsTokenCount`(实测 Pro 不超过 400、Flash high 档约 800),以及**中间稿图片**,后者以普通图片 part 返回、按每张 1120/2000 tokens 计入 `candidatesTokenCount`。因此图片模型"思考的成本"主要体现在图片 part 的张数上,而不是 `thoughtsTokenCount` 字段(见上文现象三)。 ### 两种 API 范式 谷歌的图片模型文档现有两个版本:经典的 **generateContent API**(无状态)与新推荐的 **Interactions API**(面向 Agent 与工具调用)。API易 网关走 **Google 原生 generateContent 格式,本文全部结构与字段均以此为准**。两者的思考相关差异: | | generateContent(本文) | Interactions API | | - | - | - | | 思考等级参数 | `generationConfig.thinkingConfig.thinkingLevel` | `generation_config.thinking_level` | | 思考内容返回 | 有 `includeThoughts` 开关(实测对图片模型无可见效果,中间稿总是以普通图片 part 返回) | 以 `steps`(`type: "thought"`)显式返回,无 includeThoughts 开关 | | usage 字段名 | `thoughtsTokenCount` / `candidatesTokenCount` / `totalTokenCount` | `total_thought_tokens` / `total_output_tokens` | 两种范式的完整对比(端点、状态管理、数据保留、API易 网关兼容性实测)见 [Interactions API 与 generateContent 对比](/api-capabilities/gemini/interactions-api)。 ## 解析与对账最佳实践 ```python theme={null} data = resp.json() cand = (data.get("candidates") or [{}])[0] parts = (cand.get("content") or {}).get("parts") or [] # 兼容 parts=null # 按字段特征筛选,不要用 parts[0] / parts[1]——文本段可能排在图片前面 images = [p["inlineData"] for p in parts if "inlineData" in p] if images: final_image = images[-1]["data"] # 多图时最后一张为最终稿 mime = images[-1]["mimeType"] # 以响应为准,别写死 image/png else: reason = cand.get("finishReason") # IMAGE_SAFETY / NO_IMAGE / PROHIBITED_CONTENT message = cand.get("finishMessage", "") # 可能为空 ``` 1. **计费对账用 `totalTokenCount`**(拒绝场景下它也是准的),不要自行用三项相加或 details 求和去校验。 2. **遍历 parts,不假设单图**;按张计数的业务以实际 `inlineData` part 数为准。 3. **兼容 `parts = null` + HTTP 200** 的拦截响应,按 `finishReason` 分流。 4. 简单编辑耗时 \~22–25s,复杂任务(多图响应)35–142s,张数越多越久;客户端超时建议设置 ≥ 5 分钟(含代理层)。 ## 相关文档 接入方式、输入图片要求、计费基础、超时设置与偶现多图说明 出图失败的三大判断指标、内容审核政策与友好提示方案 非主观原因导致的失败,按条数核算后补发额度 各分辨率与各模型档位的出图价格 # OpenAI Chat Completions 兼容模式调用 Source: https://docs.apiyi.com/api-capabilities/openai/compatible /v1/chat/completions 业界标准端点:各语言 OpenAI SDK 一行 base_url 切换到 API易,同一套代码调用全平台模型。 `/v1/chat/completions` 是大模型行业的事实标准接口 —— 几乎所有框架、客户端、SDK 默认支持它。通过 API易,这一个端点可以统一调用 OpenAI、Claude、Gemini、DeepSeek 等全部 400+ 模型,切换模型只需要换一个字符串。 **怎么选端点**:用现成框架/客户端、或要用同一套代码调多家模型 → 用本页的兼容模式;要内置工具(联网搜索、代码解释器)或调 Pro 系列模型 → 用 [原生调用(/v1/responses)](/api-capabilities/openai/native)。OpenAI 官方对 Chat Completions 的定位是"长期支持,但新项目推荐 Responses"。多轮对话两种端点都需自己维护历史,见 [多轮对话实现指南](/api-capabilities/multi-turn-conversation)。 ## 快速开始 ```bash cURL theme={null} curl https://api.apiyi.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '{ "model": "gpt-5.4", "messages": [ {"role": "user", "content": "用一句话介绍你自己"} ] }' ``` ```python Python theme={null} from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "用一句话介绍你自己"}] ) print(response.choices[0].message.content) ``` ```javascript Node.js theme={null} import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: 'YOUR_API_KEY', baseURL: 'https://api.apiyi.com/v1' }); const response = await openai.chat.completions.create({ model: 'gpt-5.4', messages: [{ role: 'user', content: '用一句话介绍你自己' }] }); console.log(response.choices[0].message.content); ``` ## 一个接口调用全平台模型 这是兼容模式最大的价值:**换模型只换字符串,代码一行不动**。 ```python theme={null} def ask(message: str, model: str) -> str: response = client.chat.completions.create( model=model, messages=[{"role": "user", "content": message}] ) return response.choices[0].message.content print(ask("解释量子纠缠", "gpt-5.4")) # OpenAI print(ask("解释量子纠缠", "claude-sonnet-4-6")) # Anthropic print(ask("解释量子纠缠", "gemini-3-pro-preview")) # Google print(ask("解释量子纠缠", "deepseek-chat")) # DeepSeek ``` 各家模型的完整名称和价格见 [模型与价格总览](/api-capabilities/model-info)。注意:用兼容格式调 Claude 时拿不到 Claude 的 Prompt Cache 优惠,深度使用 Claude 请走 [Claude 原生调用](/api-capabilities/claude)。 ## 各语言 SDK 配置 所有官方 SDK 都支持自定义 base\_url,配置一次即可。 ### Python ```bash theme={null} pip install openai ``` ```python theme={null} from openai import OpenAI, AsyncOpenAI # 同步客户端 client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1" ) # 异步客户端 async_client = AsyncOpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1" ) ``` 也可以用环境变量,代码里零配置: ```bash theme={null} export OPENAI_API_KEY="YOUR_API_KEY" export OPENAI_BASE_URL="https://api.apiyi.com/v1" ``` ```python theme={null} from openai import OpenAI client = OpenAI() # 自动读取环境变量 ``` ### Node.js / TypeScript ```bash theme={null} npm install openai ``` ```typescript theme={null} import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, baseURL: 'https://api.apiyi.com/v1' }); const response = await openai.chat.completions.create({ model: 'gpt-5.4-mini', messages: [{ role: 'user', content: 'Hello!' }], temperature: 0.7 }); ``` ### .NET ```bash theme={null} dotnet add package OpenAI ``` ```csharp theme={null} using OpenAI; using OpenAI.Chat; var client = new OpenAIClient( new System.ClientModel.ApiKeyCredential("YOUR_API_KEY"), new OpenAIClientOptions { Endpoint = new Uri("https://api.apiyi.com/v1") } ); var chatClient = client.GetChatClient("gpt-5.4"); var response = await chatClient.CompleteChatAsync("Hello!"); Console.WriteLine(response.Value.Content[0].Text); ``` ### Go 使用 OpenAI 官方 Go SDK(`github.com/openai/openai-go`): ```bash theme={null} go get github.com/openai/openai-go ``` ```go theme={null} package main import ( "context" "fmt" "github.com/openai/openai-go" "github.com/openai/openai-go/option" ) func main() { client := openai.NewClient( option.WithAPIKey("YOUR_API_KEY"), option.WithBaseURL("https://api.apiyi.com/v1"), ) completion, err := client.Chat.Completions.New(context.TODO(), openai.ChatCompletionNewParams{ Model: "gpt-5.4", Messages: []openai.ChatCompletionMessageParamUnion{ openai.UserMessage("Hello!"), }, }) if err != nil { panic(err) } fmt.Println(completion.Choices[0].Message.Content) } ``` ### Java 使用 OpenAI 官方 Java SDK(`com.openai:openai-java`): ```xml theme={null} com.openai openai-java LATEST ``` ```java theme={null} import com.openai.client.OpenAIClient; import com.openai.client.okhttp.OpenAIOkHttpClient; import com.openai.models.chat.completions.ChatCompletion; import com.openai.models.chat.completions.ChatCompletionCreateParams; OpenAIClient client = OpenAIOkHttpClient.builder() .apiKey("YOUR_API_KEY") .baseUrl("https://api.apiyi.com/v1") .build(); ChatCompletionCreateParams params = ChatCompletionCreateParams.builder() .model("gpt-5.4") .addUserMessage("Hello!") .build(); ChatCompletion completion = client.chat().completions().create(params); System.out.println(completion.choices().get(0).message().content().orElse("")); ``` 老项目如果还在用第三方库(Go 的 `sashabaranov/go-openai`、Java 的 `theokanning` 系列),改 base\_url 同样能跑通,但建议迁移到上面的官方 SDK —— 第三方库对新模型参数(如 `reasoning_effort`)跟进较慢。 ## 常用功能 ### 流式输出 ```python theme={null} stream = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "写一首关于秋天的短诗"}], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ### 推理控制 Chat Completions 端点用**顶层** `reasoning_effort` 参数(注意与 Responses 端点的嵌套写法不同): ```python theme={null} response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "证明根号2是无理数"}], reasoning_effort="high" # none / low / medium / high / xhigh ) ``` **GPT-5.4 及之后的模型(含 gpt-5.6 系列)在本端点上 `tools` 与显式的 `reasoning_effort` 可能互斥**:显式传了非 `none` 的 `reasoning_effort` 又携带 `tools` 时会报 400 `Function tools with reasoning_effort are not supported for ... in /v1/chat/completions`(`low` / `medium` / `high` / `xhigh` 四档实测都会触发,不传该参数则不触发)。是否触发取决于请求落到哪条上游链路,同一模型可能时灵时不灵。这是 OpenAI 官方限制——需要推理 + 工具调用请改用 [Responses 端点](/api-capabilities/openai/native),或显式设置 `reasoning_effort="none"`;怎么判断、怎么迁见 [端点选型与迁移](/api-capabilities/openai/responses-migration)。 gpt-5 系列推理模型在该端点同样**不支持 `temperature` / `top_p`**,传了会报错。 ### 图像输入 ```python theme={null} response = client.chat.completions.create( model="gpt-5.4", messages=[ { "role": "user", "content": [ {"type": "text", "text": "这张图片里有什么?"}, {"type": "image_url", "image_url": {"url": "https://example.com/image.jpg"}} ] } ] ) ``` ### Embeddings ```python theme={null} response = client.embeddings.create( model="text-embedding-3-small", input="要嵌入的文本内容" ) embedding = response.data[0].embedding ``` ## 错误处理与重试 官方 SDK 内建自动重试(默认 2 次,针对 429 / 5xx / 连接错误),优先用它而不是自己写循环: ```python theme={null} client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1", max_retries=3, # 内建指数退避重试 timeout=60.0 ) ``` 需要精细处理时按异常类型捕获: ```python theme={null} from openai import ( APIError, APIConnectionError, RateLimitError, InternalServerError, ) try: response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello"}] ) except RateLimitError: print("请求频率超限,稍后重试") except APIConnectionError: print("网络连接错误,检查网络或代理") except InternalServerError: print("上游服务错误,建议重试") except APIError as e: print(f"API 错误:{e}") ``` ## 兼容模式的能力边界 | 能力 | 兼容模式 | 说明 | | - | - | - | | 基础对话 / 流式 / 多模态输入 | ✅ | 完整支持 | | 函数调用(FC) | ✅ | 见 [FC函数调用](/api-capabilities/openai/function-calling) | | Prompt 缓存折扣 | ✅ | OpenAI 模型自动生效,见 [缓存计费](/api-capabilities/openai/prompt-caching) | | 内置工具(联网搜索、代码解释器等) | ❌ | 仅 [原生调用](/api-capabilities/openai/native) | | 多轮对话 | ✅ | 自己维护 `messages` 历史(原生 Responses 同样需自管理,见 [多轮对话指南](/api-capabilities/multi-turn-conversation)) | | `verbosity` 输出控制 | ❌ | 仅原生调用 | | Pro 系列模型(gpt-5.4-pro 等) | ❌ | 实务上仅原生调用可用 | ## 从 OpenAI 官方迁移 已经在用 OpenAI 官方服务的项目,迁移只需两步、代码零改动: 1. **换 base\_url 和 key** ```python theme={null} # 原来 client = OpenAI(api_key="sk-...") # 改为 client = OpenAI( api_key="YOUR_APIYI_KEY", base_url="https://api.apiyi.com/v1" ) ``` 2. **或者只改环境变量**(代码完全不动) ```bash theme={null} export OPENAI_API_KEY="YOUR_APIYI_KEY" export OPENAI_BASE_URL="https://api.apiyi.com/v1" ``` 方法调用、参数格式、响应结构全部保持一致。 ## 相关链接 * 同组页面:[原生调用](/api-capabilities/openai/native) · [端点选型与迁移](/api-capabilities/openai/responses-migration) · [缓存计费](/api-capabilities/openai/prompt-caching) · [FC函数调用](/api-capabilities/openai/function-calling) * 模型与价格:[模型与价格总览](/api-capabilities/model-info) * 获取 / 管理令牌:`https://api.apiyi.com/token` * OpenAI 官方 SDK 列表:`platform.openai.com/docs/libraries` # OpenAI Function Calling 函数调用指南 Source: https://docs.apiyi.com/api-capabilities/openai/function-calling tools 定义、strict 严格模式、并行调用、流式拼装,以及 responses 与 chat/completions 两个端点的格式差异。 Function Calling(函数调用,简称 FC)是构建 Agent 的基础能力:**模型不执行函数,只输出"想调哪个函数 + 什么参数"**,执行在你自己的代码里完成,把结果回传后模型再给出最终回答。 本页基于 OpenAI 官方文档整理(`developers.openai.com/api/docs/guides/function-calling`,2026年6月数据),两个端点的示例均可直接复制运行。 ## 完整调用循环 把函数名、用途描述、参数 JSON Schema 随请求发给模型 模型判断需要调用时,返回函数名和 JSON 格式的参数 你的代码解析参数、真正执行函数(查数据库、调外部 API……) 把执行结果连同历史一起再发一次请求,模型基于结果生成最终回答 ## 两个端点的关键格式差异 同一个功能,`/v1/chat/completions` 和 `/v1/responses` 的字段格式**不一样**,这是接入时最容易踩的坑: | | Chat Completions | Responses | | - | - | - | | tools 定义 | 嵌套:`{"type": "function", "function": {name, parameters, ...}}` | 扁平:`{"type": "function", "name": ..., "parameters": ...}` | | 调用返回 | `message.tool_calls[]`(含 `id`) | 顶层 output item:`{"type": "function_call", "call_id", "name", "arguments"}` | | 结果回传 | `{"role": "tool", "tool_call_id": ..., "content": ...}` | `{"type": "function_call_output", "call_id": ..., "output": ...}` | | strict 模式 | 需显式 `"strict": true` | 服务端会尽量自动归一化为 strict | 两套格式**不能混用**。把 Chat Completions 的嵌套 `function: {...}` 定义发给 `/v1/responses`(或反过来)是 SDK 报"参数无效"最常见的原因。 需要把现有的工具调用从 Chat Completions 整体搬到 Responses(比如撞上了 GPT-5.4+ 的 `tools` 与 `reasoning_effort` 互斥限制),见 [端点选型与迁移](/api-capabilities/openai/responses-migration),那里有完整的前后代码对照。 ## Chat Completions 完整示例 以查天气为例,走完"定义 → 调用 → 执行 → 回传"全循环: ```python Python theme={null} import json from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1" ) tools = [{ "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的当前天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名,如:北京"} }, "required": ["city"], "additionalProperties": False }, "strict": True } }] messages = [{"role": "user", "content": "北京天气怎么样?"}] # 第 1 次请求:模型决定调函数 r1 = client.chat.completions.create( model="gpt-5.4", messages=messages, tools=tools ) tool_call = r1.choices[0].message.tool_calls[0] args = json.loads(tool_call.function.arguments) # 本地执行(这里用假数据代替真实查询) weather = {"city": args["city"], "temp": "26°C", "condition": "晴"} # 第 2 次请求:回传结果,模型生成最终回答 messages.append(r1.choices[0].message) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(weather, ensure_ascii=False) }) r2 = client.chat.completions.create( model="gpt-5.4", messages=messages, tools=tools ) print(r2.choices[0].message.content) ``` ```javascript Node.js theme={null} import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: 'YOUR_API_KEY', baseURL: 'https://api.apiyi.com/v1' }); const tools = [{ type: 'function', function: { name: 'get_weather', description: '获取指定城市的当前天气', parameters: { type: 'object', properties: { city: { type: 'string', description: '城市名,如:北京' } }, required: ['city'], additionalProperties: false }, strict: true } }]; const messages = [{ role: 'user', content: '北京天气怎么样?' }]; const r1 = await openai.chat.completions.create({ model: 'gpt-5.4', messages, tools }); const toolCall = r1.choices[0].message.tool_calls[0]; const args = JSON.parse(toolCall.function.arguments); const weather = { city: args.city, temp: '26°C', condition: '晴' }; messages.push(r1.choices[0].message); messages.push({ role: 'tool', tool_call_id: toolCall.id, content: JSON.stringify(weather) }); const r2 = await openai.chat.completions.create({ model: 'gpt-5.4', messages, tools }); console.log(r2.choices[0].message.content); ``` ```bash cURL(第 1 次请求) theme={null} curl https://api.apiyi.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "北京天气怎么样?"}], "tools": [{ "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的当前天气", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], "additionalProperties": false }, "strict": true } }] }' ``` ## Responses 完整示例 注意三处不同:tools 定义是**扁平**的、调用以顶层 `function_call` item 返回、回传用 `function_call_output`。配合 `previous_response_id`,第 2 次请求不必重发全部历史: ```python theme={null} import json from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1" ) tools = [{ "type": "function", # 扁平定义,没有嵌套的 function 字段 "name": "get_weather", "description": "获取指定城市的当前天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名,如:北京"} }, "required": ["city"], "additionalProperties": False } }] # 第 1 次请求 r1 = client.responses.create( model="gpt-5.4", input="北京天气怎么样?", tools=tools ) # 从 output 数组里找 function_call item call = next(item for item in r1.output if item.type == "function_call") args = json.loads(call.arguments) weather = {"city": args["city"], "temp": "26°C", "condition": "晴"} # 第 2 次请求:用 previous_response_id 接上文,只回传函数结果 r2 = client.responses.create( model="gpt-5.4", previous_response_id=r1.id, input=[{ "type": "function_call_output", "call_id": call.call_id, "output": json.dumps(weather, ensure_ascii=False) }], tools=tools ) print(r2.output_text) ``` ## strict 严格模式(结构化输出) `strict: true` 让模型输出的参数**严格符合你的 JSON Schema**,杜绝幻觉字段和缺字段。三个要求: 1. Schema 里必须有 `"additionalProperties": false` 2. 所有字段都要出现在 `required` 里(可选语义用 `"type": ["string", "null"]` 表达) 3. 只能用受支持的 JSON Schema 子集(基本类型、enum、数组、嵌套对象等) ```json theme={null} // ✅ 合法的 strict schema { "type": "object", "properties": { "city": {"type": "string"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, "date": {"type": ["string", "null"], "description": "可选,默认今天"} }, "required": ["city", "unit", "date"], "additionalProperties": false } ``` ```json theme={null} // ❌ 不合法:缺 additionalProperties,date 不在 required 里 { "type": "object", "properties": { "city": {"type": "string"}, "date": {"type": "string"} }, "required": ["city"] } ``` **strict 模式与并行函数调用不兼容**:需要严格 schema 保证时,请同时设置 `parallel_tool_calls: false`。 ## parallel\_tool\_calls 与 tool\_choice ### 并行调用 `parallel_tool_calls` 默认开启,模型可以在一轮里同时请求多个函数(如同时查北京和上海的天气)。逐个执行后**全部回传**再发起下一次请求,每个结果都要带对应的 `call_id`(responses)或 `tool_call_id`(chat)配对。 ### tool\_choice 控制策略 | 取值 | 行为 | | - | - | | `"auto"`(默认) | 模型自行决定调不调、调哪个 | | `"required"` | 必须调用至少一个函数 | | `{"type": "function", "name": "get_weather"}` | 强制调用指定函数 | | `"none"` | 禁止调用,只生成文本 | ### allowed\_tools 限定子集 工具很多但本轮只想开放一部分时,用 `tool_choice` 的 `allowed_tools` 形式限定可调用子集 —— 它**不改变 tools 列表本身**,因此不破坏 [缓存](/api-capabilities/openai/prompt-caching) 的稳定前缀: ```python theme={null} tool_choice={ "type": "allowed_tools", "mode": "auto", "tools": [{"type": "function", "name": "get_weather"}] } ``` ## 流式中的函数调用 ### Chat Completions:按 index 拼接 函数参数在流式里是分片下发的,按 `index` 累加 `arguments` 字符串,流结束后再 `json.loads`: ```python theme={null} stream = client.chat.completions.create( model="gpt-5.4", messages=messages, tools=tools, stream=True ) calls = {} # index -> {name, arguments} for chunk in stream: delta = chunk.choices[0].delta if chunk.choices else None if delta and delta.tool_calls: for tc in delta.tool_calls: entry = calls.setdefault(tc.index, {"name": "", "arguments": ""}) if tc.function.name: entry["name"] = tc.function.name if tc.function.arguments: entry["arguments"] += tc.function.arguments print(calls) # 流结束后每个 entry 的 arguments 才是完整 JSON ``` ### Responses:监听语义事件 `response.function_call_arguments.delta` 事件携带参数增量,`response.function_call_arguments.done` 给出完整参数,无需自己按 index 拼。 ## 最佳实践与踩坑 写好工具定义: * **函数名和 description 是写给模型看的**:说清楚"什么时候该调我",比如 `"获取实时天气,仅当用户明确询问天气时调用"` * **参数用 enum 收窄**:能枚举就别用自由字符串,幻觉参数会少一大半 * **工具定义放 prompt 前部且保持稳定**:tools 会参与缓存前缀比对,定义稳定 = 输入费打 1 折(见 [缓存计费](/api-capabilities/openai/prompt-caching)) * **Agent 循环设最大轮数**:避免模型在"调函数 → 回传 → 又调函数"里打转烧钱 常见踩坑: | 现象 | 处理 | | - | - | | `arguments` 不是合法 JSON | 上 `strict: true`,从根上解决 | | 模型调了不存在的函数名 | 用 `tool_choice` 收紧;检查 description 是否误导 | | 并行调用后报 `call_id` 不匹配 | 每个结果必须和对应调用的 `call_id` / `tool_call_id` 一一配对,缺一个都会报错 | | 两个端点格式混用报参数错误 | 对照上文差异表,确认 tools 定义嵌套/扁平与端点匹配 | ## 模型支持与选型 gpt-5 全系支持函数调用,按场景选: | 场景 | 推荐模型 | 理由 | | - | - | - | | 日常 Agent / 工具调用 | `gpt-5.4`(\$2.50 / \$15.00 每 1M) | 能力与成本均衡 | | 高频轻量工具路由 | `gpt-5.4-mini`(\$0.75 / \$4.50 每 1M) | 便宜,简单分发够用 | | 复杂多步推理 Agent | `gpt-5.5`(\$5.00 / \$30.00 每 1M) | 长链路规划更稳 | ## 相关链接 * 同组页面:[原生调用](/api-capabilities/openai/native) · [兼容模式调用](/api-capabilities/openai/compatible) · [缓存计费](/api-capabilities/openai/prompt-caching) * 获取 / 管理令牌:`https://api.apiyi.com/token` * OpenAI 官方文档:`developers.openai.com/api/docs/guides/function-calling` # OpenAI Responses API 原生调用指南 Source: https://docs.apiyi.com/api-capabilities/openai/native 通过 API易 调用 /v1/responses:状态管理、推理参数、内置工具、语义化流式事件。OpenAI 官方推荐新项目首选的端点。 `/v1/responses` 是 OpenAI 当前的原生主力端点。官方原话:"While Chat Completions remains supported, **Responses is recommended for all new projects**." API易 完整支持该端点,base\_url 换成 `https://api.apiyi.com/v1` 即可。 本页基于 OpenAI 官方文档整理(`developers.openai.com/api/docs`,2026年6月数据),示例均可直接复制运行。 ## 为什么用 Responses 相比 Chat Completions,官方给出的三个硬数字: * **推理更强**:同一个推理模型走 Responses 端点,SWE-bench 成绩提升约 3%(推理状态跨轮保持) * **缓存更省**:缓存利用率比 Chat Completions 高 40%–80%(官方内部测试),输入账单直接受益 * **工具更多**:`web_search`、`code_interpreter` 等内置工具只在 Responses 提供 什么时候仍然选 Chat Completions:你在用现成框架(LangChain、各类客户端默认走 `/v1/chat/completions`),或需要用同一套代码调 Claude、Gemini 等非 OpenAI 模型 —— 见 [兼容模式调用](/api-capabilities/openai/compatible)。 被弃用的是 **Assistants API**(官方计划 2026年8月26日 关停),不是 Chat Completions。两个端点都会长期支持,只是新功能优先落在 Responses。 ## 快速开始 ```bash cURL theme={null} curl https://api.apiyi.com/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '{ "model": "gpt-5.4", "input": "用一句话介绍你自己", "instructions": "你是一个简洁的助手" }' ``` ```python Python theme={null} from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1" ) response = client.responses.create( model="gpt-5.4", input="用一句话介绍你自己", instructions="你是一个简洁的助手" ) print(response.output_text) # SDK 提供的便捷字段,自动拼接文本输出 ``` ```javascript Node.js theme={null} import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: 'YOUR_API_KEY', baseURL: 'https://api.apiyi.com/v1' }); const response = await openai.responses.create({ model: 'gpt-5.4', input: '用一句话介绍你自己', instructions: '你是一个简洁的助手' }); console.log(response.output_text); ``` 取结果优先用 `response.output_text`,不要手写 `output[0].content[0].text` —— 推理模型的 `output` 数组第一项往往是 `reasoning` 而不是 `message`,手写下标会取错。 ## 请求参数速查表 | 参数 | 类型 | 默认值 | 说明 | | - | - | - | - | | `model` | string | 必填 | 如 `gpt-5.4`、`gpt-5.5` | | `input` | string / array | 必填 | 用户输入,支持多模态 content 数组 | | `instructions` | string | null | 系统指令(相当于 system prompt) | | `max_output_tokens` | int | null | 最大输出 token(含推理 token) | | `reasoning` | object | medium | `{"effort": "none/low/medium/high/xhigh"}` | | `text` | object | — | `format`(输出格式)、`verbosity`(low/medium/high) | | `tools` | array | \[] | 函数 + 内置工具 | | `tool_choice` | string | "auto" | `auto` / `required` / `none` / 指定工具 | | `parallel_tool_calls` | boolean | true | 是否允许并行工具调用 | | `store` | boolean | true | 服务端保留响应对象 —— ⚠️ **API易 下不可用**,见下方多轮对话一节 | | `previous_response_id` | string | null | 链式引用上一个响应 —— ⚠️ **API易 下不生效**,多轮请用 `input` 数组自带历史 | | `conversation` | string | null | 持久会话对象 —— ⚠️ **API易 下不支持**(`/v1/conversations` 返回 404) | | `background` | boolean | false | 异步后台执行(长任务 / Pro 模型) | | `stream` | boolean | false | 流式输出(语义化事件) | | `prompt_cache_key` | string | null | 缓存路由键,提高命中率,见 [缓存计费](/api-capabilities/openai/prompt-caching) | | `metadata` | object | 自定义元数据 | | gpt-5 系列推理模型**不支持 `temperature` / `top_p`**,传了会报错。控制输出风格请改用 `reasoning.effort` 和 `text.verbosity`。 ## 响应结构 `output` 是一个 item 数组,常见三种类型:`reasoning`(推理摘要)、`message`(文本回复)、`function_call`(函数调用请求)。精简后的响应示例: ```json theme={null} { "id": "resp_abc123", "object": "response", "status": "completed", "model": "gpt-5.4-2026-03-05", "output": [ { "type": "reasoning", "summary": [] }, { "type": "message", "role": "assistant", "content": [{ "type": "output_text", "text": "你好!我是一个 AI 助手。" }] } ], "usage": { "input_tokens": 24, "input_tokens_details": { "cached_tokens": 0 }, "output_tokens": 58, "output_tokens_details": { "reasoning_tokens": 40 }, "total_tokens": 82 } } ``` `usage` 里两个值得盯的字段: * `input_tokens_details.cached_tokens`:命中缓存的输入量(按 0.1× 计费) * `output_tokens_details.reasoning_tokens`:推理消耗(按输出价计费,调低 `reasoning.effort` 可控) ## 多轮对话:自己维护历史 经 API易 调用 Responses API,多轮请**把完整历史作为 `input` 数组传入**(每条带 `role` / `content`),与 Chat Completions 的做法一致: ```python theme={null} resp = client.responses.create( model="gpt-5.4", input=[ {"role": "user", "content": "我叫 Alice,请记住。"}, {"role": "assistant", "content": "好的,我记住了,你叫 Alice。"}, {"role": "user", "content": "我叫什么名字?"}, ], ) print(resp.output_text) # 会回答 Alice ``` **服务端会话状态在 API易 下不可用,请勿依赖。** 经网关实测(多模型、含延迟重试): * `previous_response_id`:传了不报错(返回 200),但下一轮**不会记得**上一轮内容(`input_tokens` 仅为本轮量,未带入历史); * `GET /v1/responses/{id}`:返回 400,**无法取回**已存响应; * `conversation` 持久会话对象(`/v1/conversations`):返回 404,**不支持**。 因此 `store` / `previous_response_id` / `conversation` 这几个服务端状态参数在 API易 上均**不要使用**,请统一采用上面的「`input` 数组自管理历史」方式。完整跨格式说明见 [多轮对话实现指南](/api-capabilities/multi-turn-conversation)。 **多轮不省输入费**:每轮把完整历史重新发送,全部上下文按输入 token 全量计费。长对话省钱靠的是缓存折扣(历史前缀自动命中 0.1× 缓存价)—— 详见 [缓存计费](/api-capabilities/openai/prompt-caching)。 ## 推理与输出控制 ### reasoning.effort 档位选型 | 档位 | 适用场景 | | - | - | | `none` | 简单问答、格式转换,要快要便宜 | | `low` | 常规对话、摘要 | | `medium`(默认) | 日常开发的均衡选择 | | `high` | 复杂代码、多步推理 | | `xhigh` | 最难的题,配合 `gpt-5.5` / `gpt-5.4` 使用 | ```python theme={null} response = client.responses.create( model="gpt-5.5", input="证明根号2是无理数", reasoning={"effort": "xhigh"} ) ``` ### text.verbosity 输出长度 `low` / `medium`(默认)/ `high` 控制回答详略,仅 Responses 端点支持: ```python theme={null} response = client.responses.create( model="gpt-5.4", input="解释什么是闭包", text={"verbosity": "low"} # 给简短版本 ) ``` ## 流式输出 Responses 的流式是**语义化事件**,不是 Chat Completions 那种 `choices[0].delta` 通用块。核心事件: | 事件 | 含义 | | - | - | | `response.created` | 响应开始 | | `response.output_item.added` | 新增一个 output item(message / function\_call 等) | | `response.output_text.delta` | 文本增量 | | `response.function_call_arguments.delta` | 函数参数增量 | | `response.completed` | 全部完成(含最终 usage) | | `error` | 出错 | ```python theme={null} stream = client.responses.create( model="gpt-5.4", input="写一首关于秋天的短诗", stream=True ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) elif event.type == "response.completed": print("\n\n用量:", event.response.usage) ``` ## 内置工具一览 内置工具是 Responses 独有能力,在 `tools` 数组里声明即可,无需自己实现执行逻辑: | 工具 | type 值 | 说明 | | - | - | - | | 网页搜索 | `web_search` | 模型自主联网检索 | | 文件搜索 | `file_search` | 检索已上传的向量库 | | 代码解释器 | `code_interpreter` | 沙箱里跑 Python | | 计算机使用 | `computer_use` | 操作虚拟桌面 | | 远程 MCP | `mcp` | 连接远程 MCP 服务器 | | 图像生成 | `image_generation` | 内嵌生图。**API易 不建议**用此工具出图(按次固定收费、稳定性无法保证),请走 [Images API](/api-capabilities/gpt-image-2/text-to-image) 按量计费 | | 工具搜索 | `tool_search` | 海量工具动态检索(gpt-5.4 及之后模型) | `web_search` 最小示例: ```python theme={null} response = client.responses.create( model="gpt-5.4", input="今天有哪些重要的 AI 新闻?", tools=[{"type": "web_search"}] ) print(response.output_text) ``` 内置工具依赖 OpenAI 服务端执行,API易 通道对各内置工具的透传支持情况以实测为准。函数调用(自定义工具)完整支持,见 [FC函数调用](/api-capabilities/openai/function-calling)。 ## Pro 模型与 background 模式 `gpt-5.4-pro`、`gpt-5.5-pro` 是面向专业场景的深度推理模型(\$30 / \$180 每百万 tokens,仅 **svip 分组**可用),实务上**仅通过 `/v1/responses` 调用**。单次请求耗时可达分钟级,建议配合 `background: true` 异步执行: ```python theme={null} # 提交后台任务 response = client.responses.create( model="gpt-5.4-pro", input="对这份架构方案做深度评审:...", background=True ) # 轮询取回结果 import time while response.status in ("queued", "in_progress"): time.sleep(10) response = client.responses.retrieve(response.id) print(response.output_text) ``` Pro 模型价格高、速度慢,定位是"花几分钟换一个更靠谱的答案"。日常开发请用 `gpt-5.4` / `gpt-5.5`,没有明确的深度推理需求不建议上 Pro。 ## 支持的模型与价格 | 模型 | 输入(每 1M tokens) | 输出(每 1M tokens) | 说明 | | - | - | - | - | | `gpt-6-astra` | \$10.00 | \$50.00 | GPT-6 旗舰,电脑操作与长程 Agent;函数调用仅 responses | | `gpt-6-sol` | \$2.00 | \$10.00 | GPT-6 日常主力,1.05M 上下文 | | `gpt-6-luna` | \$0.10 | \$0.50 | GPT-6 轻量款,适合大批量任务 | | `gpt-5.6-sol` | \$4.00 | \$20.00 | 5.6 系列旗舰,1M 上下文,`gpt-5.6` 别名指向它;9/3 起降价,优惠期至少到 2026/11/21 | | `gpt-5.6-terra` | \$2.00 | \$12.00 | 5.6 系列均衡主力 | | `gpt-5.6-luna` | \$0.20 | \$1.20 | 5.6 系列轻量款 | | `gpt-5.4` | \$2.50 | \$15.00 | 上代主力,1M 上下文 | | `gpt-5.4-mini` | \$0.75 | \$4.50 | 轻量高性价比 | | `gpt-5.5` | \$5.00 | \$30.00 | 上代旗舰,复杂推理 | | `gpt-5.2` | \$1.75 | \$14.00 | 上代主力 | | `gpt-5.1` / `gpt-5` | \$1.25 | \$10.00 | 价格友好 | | `gpt-5.4-pro` | \$30.00 | \$180.00 | 仅 svip,仅 responses,专业场景 | | `gpt-5.5-pro` | \$30.00 | \$180.00 | 仅 svip,仅 responses,专业场景 | 日期固定版本(如 `gpt-5.4-2026-03-05`)同步在售,价格与主版本一致。完整列表见 [模型与价格总览](/api-capabilities/model-info)。 模型价格与官网对齐,且可能随官网调整;上表仅供参考,具体以顶部导航「模型价格」栏目为准:[模型价格](/models/index)。 ## 与 Chat Completions 对照 **GPT-5.4 及之后的模型(含 `gpt-5.6-sol` / `gpt-5.6-terra` / `gpt-5.6-luna`)在 `/v1/chat/completions` 上,「工具调用 + 显式推理档位」可能被上游拒绝**:请求带 `tools` 且**显式**传了非 `none` 的 `reasoning_effort` 时会报 400 `Function tools with reasoning_effort are not supported for ... in /v1/chat/completions`。这是 OpenAI 的官方限制,本页的 `/v1/responses` 端点没有此限制——这类模型做工具调用请直接用 Responses。 是否触发**取决于请求落到哪条上游链路**,同一个模型可能这次 200、下次 400,所以不能靠「我这次没报错」判断安全。实测数据、两条出路的取舍与完整迁移步骤见 [端点选型与迁移](/api-capabilities/openai/responses-migration)。 从 `/v1/chat/completions` 迁移过来的字段映射: | Chat Completions | Responses | 说明 | | - | - | - | | `messages` 数组 | `input` | 简单场景直接传字符串 | | `messages[0]` 的 system | `instructions` | 独立参数 | | `max_tokens` / `max_completion_tokens` | `max_output_tokens` | — | | `response_format` | `text.format` | — | | 顶层 `reasoning_effort` | `reasoning.effort` | Responses 里是嵌套对象 | | `choices[0].message.content` | `output_text` | 取结果 | | 无状态,自己拼历史 | 同样自己拼历史(`input` 数组)⚠️ 服务端状态在 API易 下不可用 | — | | `usage.prompt_tokens` | `usage.input_tokens` | 字段名不同 | ```python Chat Completions(旧) theme={null} response = client.chat.completions.create( model="gpt-5.4", messages=[ {"role": "system", "content": "你是一个简洁的助手"}, {"role": "user", "content": "你好"} ] ) content = response.choices[0].message.content ``` ```python Responses(新) theme={null} response = client.responses.create( model="gpt-5.4", input="你好", instructions="你是一个简洁的助手" ) content = response.output_text ``` ## 客户端支持现状 为什么 Cline、Trae 等 VS Code 系 IDE / 插件大多只支持 `/v1/chat/completions`,不支持本页的 Responses 端点? * **chat/completions 是事实上的行业通用协议**:第三方网关、本地推理框架(Ollama / vLLM / LM Studio)、各家非 OpenAI 厂商全都实现它,客户端写一套处理逻辑就能接几百家供应商;而 `/v1/responses` 目前基本是 OpenAI 专属方言 * **Responses 不是「换个 URL」**:语义化事件流(不是 delta 拼接)、item 化输出、推理状态传递都与 chat/completions 完全不同,客户端需要重写整个 agent 循环,维护成本高 * **鸡生蛋问题**:客户端不做,是因为大多数自定义端点(网关)不支持 responses;网关反过来也不急着做。API易 已托管 `/v1/responses`(即本页),不存在网关侧障碍 截至 2026 年 7 月的主流客户端支持情况: | 客户端 | Responses 支持 | 说明 | | - | - | - | | [Codex CLI](/scenarios/programming/codex-cli) | ✅ 原生 | OpenAI 官方出品,agent 循环整个建在 Responses 上,2026 年初已弃用 chat/completions | | [opencode](/scenarios/programming/opencode) | ✅ | OpenAI provider 默认走 Responses | | [Roo Code](/scenarios/programming/roo-code) | ✅(止步 gpt-5.4) | 「OpenAI」provider 走 Responses 且支持自定义 Base URL(「OpenAI Compatible」仍是 chat/completions);已停更,预置模型止步 `gpt-5.4`;可作为插件装进 Trae 等 VS Code 系 IDE | | Continue | ✅ | gpt-5 / o 系默认走 responses;已被 Cursor 收购,独立产品收尾中 | | [Cline](/scenarios/programming/cline) | ❌ | OpenAI Compatible 方式固定 chat/completions,社区 feature request 尚未落地 | | [Trae](/scenarios/programming/trae) | ❌ | 自定义模型仅 chat/completions 与 messages 两种端点 | 需要 GPT-5.4+「推理 + 工具调用」的场景,首选 Codex CLI / opencode,Base URL 指向 `https://api.apiyi.com/v1` 即可;只用到 `gpt-5.4`、又想留在 VS Code 系 IDE(含 Trae)里的,可装 Roo Code 插件并选 OpenAI provider。 ## 常见问题 | 现象 | 原因与处理 | | - | - | | `model_not_supported` 报错 | 该模型不支持 responses 端点,换 gpt-5 系列 | | 多轮不记得上文 | 最稳的做法是用 `input` 数组自带完整历史;`previous_response_id` 链式 2026-09-02 实测可用,但 `GET /v1/responses/{id}` 回取仍不可用,依赖前请在自己的分组验一次 | | `output_text` 为空 | 输出全是 `function_call` item(模型在要求调函数),检查 `output` 数组逐项处理 | | 传 `temperature` 报错 | gpt-5 推理模型不支持,删掉改用 `reasoning.effort` | | `Function tools with reasoning_effort are not supported ...`(400) | GPT-5.4+ 在 `/v1/chat/completions` 的官方限制(tools 与显式非 `none` 的 reasoning\_effort 互斥),改用本页 `/v1/responses` 端点即可,迁移步骤见 [端点选型与迁移](/api-capabilities/openai/responses-migration) | ## 相关链接 * 同组页面:[兼容模式调用](/api-capabilities/openai/compatible) · [端点选型与迁移](/api-capabilities/openai/responses-migration) · [缓存计费](/api-capabilities/openai/prompt-caching) · [FC函数调用](/api-capabilities/openai/function-calling) * 获取 / 管理令牌:`https://api.apiyi.com/token` * OpenAI 官方迁移指南:`developers.openai.com/api/docs/guides/migrate-to-responses` # OpenAI Prompt Caching 缓存计费指南 Source: https://docs.apiyi.com/api-capabilities/openai/prompt-caching OpenAI 缓存全自动、零标记、不收写入费:命中部分按输入价 1 折计费。怎么写出会命中的请求、怎么看 cached_tokens。 用 gpt-5 系列跑 Agent、多轮对话、批量文档处理,Prompt Caching 能把命中部分的输入账单打到 **1 折** —— 而且**什么代码都不用改**,缓存是全自动的。 本页基于 OpenAI 官方文档整理(`developers.openai.com/api/docs/guides/prompt-caching`,2026年6月数据),并按 API易 的接入方式给出可直接复制的示例。 ## 一句话理解 只要请求的**开头部分(前缀)和最近一次请求完全相同且不短于 1024 tokens**,服务器就自动跳过重复处理:命中部分按 **0.1×** 计费,延迟最多降 80%。 和 Claude 缓存最大的两个区别: * **不用打标记**:没有 `cache_control`,达到条件自动缓存 * **没有写入费**:Claude 写入要付 1.25× / 2×,OpenAI 写入免费 ## 为什么要用 —— 看账单倍率 以模型原始输入 token 价为 **1×** 计: | 类型 | 价格 | 说明 | | - | - | - | | 普通输入 | **1×** | 没命中的部分,原价 | | 缓存写入 | **0×(免费)** | 自动发生,不收钱 | | **缓存命中** | **0.1×** | 命中部分便宜 90% | **回本点:第 2 次请求就净省。** 没有写入成本要摊,同一前缀只要被复用一次,省下的就是纯收益 —— 这比 Claude(先付 1.25× 写入费、复用 2 次才回本)更无脑。 按 API易 在售价格换算(每 1M tokens): | 模型 | 普通输入 | 缓存命中 | | - | - | - | | `gpt-5.4` | \$2.50 | **\$0.25** | | `gpt-5.4-mini` | \$0.75 | **\$0.075** | | `gpt-5.5` | \$5.00 | **\$0.50** | | `gpt-5.1` / `gpt-5` | \$1.25 | **\$0.125** | ### 适合场景 * 同一份长系统提示词 + 工具定义被反复调用(Agent、客服机器人) * 多轮对话(每加一轮,前面的历史自动命中) * 批量处理同一份文档(一份合同问 50 个问题) * RAG 把稳定的文档块放在 prompt 前部 ### 不适合场景 * 每次请求从第一个字开始就不一样 * 整个 prompt 不足 1024 tokens(到不了起缓阈值) ## 触发命中的三个硬条件 缺一不可。 ### 1. 前缀不短于 1024 tokens 短于 1024 tokens 的请求**永远不会被缓存**(不报错,静默不生效)。超过 1024 之后,命中长度按 **128 token 增量**延伸:实际命中量是 1024、1152、1280……这样的台阶值,所以 `cached_tokens` 通常略小于你的稳定前缀总长,正常现象。 ### 2. 前缀逐字节相同 缓存按**前缀匹配**:从请求第一个字符开始逐字节比对,遇到第一处不同就停止。任何变化 —— 时间戳、用户名、JSON 字段顺序 —— 都会让后面的内容全部按原价计费。 **实践含义:稳定的东西放前面,易变的东西放后面。** ```python theme={null} # ❌ 错:动态内容拼在 system 开头,前缀每次都变,永远命中不了 messages = [ {"role": "system", "content": f"当前时间 {datetime.now()}。你是一个助手。" + 长指令}, {"role": "user", "content": 问题}, ] # ✅ 对:长指令和工具定义在前保持稳定,动态内容放 user 消息里 messages = [ {"role": "system", "content": 长指令}, # 稳定,会命中 {"role": "user", "content": f"当前时间 {datetime.now()}。{问题}"}, # 易变,放最后 ] ``` ### 3. 在保留期内再次请求 * 基础保留:闲置 **5–10 分钟**后逐出,最长不超过 1 小时 * **2026年5月29日起**,gpt-5.1 及之后模型(含 pro 变体)对非 ZDR 组织**默认启用 24 小时扩展保留**(`prompt_cache_retention: "24h"`),价格不变 —— 也就是说当天内的复用基本都能命中 ## 最小可运行示例 同一段长前缀发两次不同问题,第一次自动写入,第二次命中: ```python theme={null} import os from openai import OpenAI client = OpenAI( api_key=os.environ["APIYI_API_KEY"], base_url="https://api.apiyi.com/v1" ) # 必须足够长:不少于 1024 tokens,大约 1500+ 中文字 LONG_SYSTEM = open("long_instructions_zh.txt").read() def ask(question: str, label: str): r = client.chat.completions.create( model="gpt-5.4", messages=[ {"role": "system", "content": LONG_SYSTEM}, {"role": "user", "content": question}, ], ) cached = r.usage.prompt_tokens_details.cached_tokens print(f"[{label}] input={r.usage.prompt_tokens} cached={cached}") ask("请概括要点", "第1次") # 期望 cached=0 ask("请给3个关键词", "第2次") # 期望 cached≈前缀长度 ``` 期望看到的输出: ```text theme={null} [第1次] input=2330 cached=0 [第2次] input=2335 cached=2304 ``` 第 2 次 `cached` 接近系统提示词长度(按 128 取整),这部分只按 1 折计费。 `/v1/responses` 端点同样自动生效,字段为 `usage.input_tokens_details.cached_tokens`。官方内部测试显示 Responses 端点的缓存利用率比 Chat Completions 还要高 40%–80%,多轮 Agent 场景建议优先走 [原生调用](/api-capabilities/openai/native)。 ## 怎么判断命中没命中 —— 看 usage 字段 | 端点 | 命中字段 | | - | - | | `/v1/chat/completions` | `usage.prompt_tokens_details.cached_tokens` | | `/v1/responses` | `usage.input_tokens_details.cached_tokens` | **`cached_tokens > 0` 就在省钱**:这部分按 0.1× 计,剩余的 `prompt_tokens - cached_tokens` 按原价计。 ## 提高命中率的进阶手段 ### prompt\_cache\_key 固定路由 缓存命中要求请求落到同一台缓存机器上。默认按前缀哈希路由已经够用,但当**多个用户共享相似前缀**或并发较高时,显式传 `prompt_cache_key` 可以明显提高命中率: ```python theme={null} r = client.chat.completions.create( model="gpt-5.4", messages=messages, prompt_cache_key="user-12345" # 按用户/会话固定路由 ) ``` 同一个"前缀 + prompt\_cache\_key"组合的请求超过约 **15 次/分钟** 时会溢出分流到其他机器,命中率反而下降。高并发场景应按用户或会话**拆分多个 key**,不要全局共用一个。 ### 稳定前缀工程化 * 工具定义的顺序、JSON 序列化方式保持固定(别让序列化库随机排序字段) * 图片输入也参与前缀比对,复用图片时保持 URL / base64 和 `detail` 参数一致 * 要按场景启用不同工具时,用 `allowed_tools` 限定子集,而不是改动 `tools` 列表本身 —— 前者不破坏缓存前缀 ### 多轮对话天然命中 追加式的 messages 数组天然满足前缀稳定:每一轮的历史就是上一轮的完整前缀,自动命中,无需任何处理。 ## 最常见的踩坑 | 现象 | 原因 | | - | - | | `cached_tokens` 恒为 0 | 总长不足 1024 tokens / 前缀开头有动态内容(时间戳、UUID、随机 ID) | | 时有时无 | 高并发没拆 `prompt_cache_key`,请求被分流 / 闲置超过保留期 | | 命中数比预期少一截 | 128 token 增量截断,属正常 / 动态内容混进了前缀中段 | | 换模型后不命中 | 缓存按模型隔离,`gpt-5.4` 和 `gpt-5.4-mini` 互不共享 | | 调 Claude 模型没有 cached\_tokens | OpenAI 兼容格式调 Claude 拿不到 Claude 缓存,走 [Claude 原生调用](/api-capabilities/claude-prompt-caching) | ## 与 Claude 缓存的差异速查 | | OpenAI(gpt-5 系列) | Claude | | - | - | - | | 触发方式 | **全自动**,零代码 | 手动打 `cache_control` 标记 | | 写入费 | **免费** | 1.25×(5 分钟)/ 2×(1 小时) | | 命中价 | 0.1× | 0.1× | | 最小阈值 | 1024 tokens | 按模型 1024–4096 tokens | | 保留时长 | 5 分钟起,gpt-5.1+ 默认 24h | 5 分钟 / 1 小时(滑动续期) | | 看哪个字段 | `cached_tokens` | `cache_read_input_tokens` | Claude 侧的完整玩法见 [Claude 缓存计费指南](/api-capabilities/claude-prompt-caching)。 ## API易 关于缓存的说明 **API易 的 OpenAI 通道支持缓存命中。** 请求原样转发上游,响应里的 `cached_tokens` 原样回吐,后台账单将命中部分按官方 0.1× 倍率单列"缓存读取"计费项 —— 代码无需为中转层做任何适配。 如何自检: 1. 构造一个不少于 1024 tokens 的稳定前缀,连续发 2 次请求 2. 第 2 次响应应看到 `cached_tokens > 0` 3. 后台调用日志中对应请求的输入费用明显低于第 1 次 ## 要点回顾 不用打标记、没有写入费,达到条件自动缓存,第 2 次复用就是纯省钱。 前缀不少于 1024 tokens 才会起缓存,命中按 128 token 台阶计。 稳定内容在前、易变内容在后;时间戳和随机 ID 别放开头。 `cached_tokens > 0` 才说明真的命中了,这部分按 1 折计费。 ## 相关链接 * 同组页面:[原生调用](/api-capabilities/openai/native) · [兼容模式调用](/api-capabilities/openai/compatible) · [FC函数调用](/api-capabilities/openai/function-calling) * Claude 侧缓存:[Claude 缓存计费指南](/api-capabilities/claude-prompt-caching) * 获取 / 管理令牌:`https://api.apiyi.com/token` * OpenAI 官方文档:`developers.openai.com/api/docs/guides/prompt-caching` # OpenAI 兼容模式:推理模型输出 Source: https://docs.apiyi.com/api-capabilities/openai/reasoning-models /v1/chat/completions 下推理模型的输出特点:思考型(reasoning_content)、思考签名、结构化输出,附各模型实测支持度。 推理模型(reasoning models)在回答前会先「思考」。通过 [兼容模式](/api-capabilities/openai/compatible) 调用时,它们的输出比普通模型多了一些细节。本页讲清三件事:**思考内容怎么拿、多轮怎么传、结构化输出怎么稳**。 本页聚焦 `/v1/chat/completions` 兼容模式。Claude 原生格式的思考块(`/v1/messages` 的 `thinking`)见 [Claude Effort 思考指南](/api-capabilities/claude-effort-thinking);Gemini 原生的 `thinking_level` 与 `thought_signature` 见 [Gemini 原生调用](/api-capabilities/gemini/native)。 ## 推理模型输出总览 兼容模式下,推理模型在「是否输出思考文本」上分三类: | 类型 | 代表模型 | 思考内容 | 取正文方式 | | - | - | - | - | | 不输出思考 | gpt-4.1-mini、gemini-3.1-flash-lite、claude-haiku-4-5 | 无 | 与普通模型一致 | | 输出思考文本 | grok-4.3、qwen3.6-plus、glm-5.1 | `reasoning_content` 字段 | 正文在 `content`,思考在 `reasoning_content` | | 仅计 token | gpt-5.4-mini | 不返回文本,仅 `usage.reasoning_tokens` | 与普通模型一致 | 无论哪一类,**正文永远在 `content`**。只要你只读 `content`,所有推理模型都能像普通模型一样接入;想额外展示思考过程,再去读 `reasoning_content`。 ## 思考型:reasoning\_content 会输出思考文本的模型,把思考链放在与 `content` 平行的 `reasoning_content` 字段。 **非流式**——`message` 同时含两者: ```json theme={null} { "choices": [{ "message": { "role": "assistant", "content": "1+1等于2。", "reasoning_content": "用户在问 1+1 ……(一段思考过程)" }, "finish_reason": "stop" }] } ``` ```python theme={null} msg = resp.choices[0].message print("正文:", msg.content) # 思考过程(仅部分模型有,SDK 里可能在 model_extra 中) print("思考:", getattr(msg, "reasoning_content", None)) ``` **流式**——先推送一连串 `delta.reasoning_content`,思考完才开始推送 `delta.content`。务必把两者**分流渲染**(思考折叠、正文上屏),否则界面会先刷一大段思考: ```python theme={null} for chunk in stream: if not chunk.choices: continue delta = chunk.choices[0].delta reasoning = getattr(delta, "reasoning_content", None) if reasoning: render_thinking(reasoning) # 折叠区/灰字 if delta.content: render_answer(delta.content) # 正文区 ``` **流式里 reasoning 与 content 的「互斥」写法三家不同**,解析时三种都要容忍: * **grok-4.3**:思考阶段只有 `reasoning_content` 键,正文阶段只有 `content` 键(另一个键直接不出现)。 * **qwen3.6-plus**:两个键都在,非当前的为 `null`。 * **glm-5.1**:思考阶段 `content` 为 `""`(空串)+ `reasoning_content` 有值。 统一做法:用「真值判断」取值(`if reasoning:` / `if content:`),自动跳过缺失、`null`、`""` 三种空态。 推理 token 可能远超正文。实测一个「1+1」级问题,grok-4.3 的 `reasoning_tokens` 可达数百,而正文只有几个 token。思考链按输出 token 计费,**对延迟和成本敏感的场景请评估是否需要开启 / 展示思考**。 ## 思考签名与多轮对话 「思考签名」(thought signature)是 **Gemini 原生格式**的概念:原生多模态 / 函数调用里,模型会返回加密的 `thought_signature`,多轮时需原样回传以保持推理连续性(详见 [Gemini 原生调用](/api-capabilities/gemini/native) 与 [Gemini 函数调用](/api-capabilities/gemini/function-calling))。 **在 `/v1/chat/completions` 兼容模式下,推理模型是无状态的:** * 多轮对话只需把上一轮 assistant 的 **`content`** 放进 messages 历史即可; * **无需回传 `reasoning_content`**,响应里也**不出现任何 signature 字段**; * 实测 gemini-3.1-flash-lite、grok-4.3 在仅回传 `content` 的情况下,多轮上下文记忆均正常。 ```python theme={null} messages = [ {"role": "user", "content": "记住数字 42。"}, {"role": "assistant", "content": "好的,我记住了 42。"}, # 只回传 content {"role": "user", "content": "刚才那个数字乘以2是多少?"} ] # grok-4.3 / gemini-3.1-flash-lite 均正确回答 84 ``` 需要跨轮保留 Gemini 的思考签名、或用 Claude 的原生思考块做多轮,请改用对应的**原生格式**端点,而非兼容模式。 ## 结构化输出 通过 `response_format` 让模型只吐 JSON。两种类型: ```python theme={null} # 1) json_schema:严格按 schema 约束字段(OpenAI 标准) response_format = { "type": "json_schema", "json_schema": { "name": "city_info", "strict": True, "schema": { "type": "object", "properties": { "city": {"type": "string"}, "population": {"type": "integer"}, "is_capital": {"type": "boolean"} }, "required": ["city", "population", "is_capital"], "additionalProperties": False } } } # 2) json_object:只保证是合法 JSON,不约束字段 response_format = {"type": "json_object"} ``` ### 各模型实测支持度 `json_schema` 各家支持参差,**这是结构化输出最大的坑**: | 模型 | `json_schema` | `json_object` | content 可否直接 parse | | - | - | - | - | | gpt-4.1-mini | ✅ 严格遵守 | ✅ | ✅ 纯 JSON | | gemini-3.1-flash-lite | ✅ | ✅ | ✅ | | grok-4.3 | ✅(另带思考链) | ✅ | ✅ content 为纯 JSON | | gpt-5.4-mini | ⚠️ JSON 对,但前面带 `…` | — | ❌ 需先剥离 think 块 | | qwen3.6-plus | ⚠️ 报 400:messages 须含 "json" 字样 | ✅ | ✅ | | claude-haiku-4-5 | ❌ 忽略 schema,返回 Markdown | — | ❌ | | glm-5.1 | ❌ 忽略 schema,返回自然语言 | ✅ | json\_object 下 ✅ | ### 跨模型稳定拿 JSON 的建议 不要假设 `json_schema` 在所有模型上都生效。要跨模型稳定,推荐组合拳: 1. 优先用 `json_object`,兼容性比 `json_schema` 好; 2. prompt 里**明确写「只返回 JSON」并出现 "json" 字样**(qwen 强制要求,其它模型也更稳); 3. 解析前做**容错**:剥离 ` ```json ` 代码块围栏、剥离 `…` 前缀,再 `json.loads`,失败则降级处理。 ````python theme={null} import json, re def parse_json_loose(content: str): # 去掉 … 前缀(gpt-5.4-mini) content = re.sub(r".*?", "", content, flags=re.S).strip() # 去掉 ```json 代码块围栏 content = re.sub(r"^```(json)?|```$", "", content, flags=re.M).strip() try: return json.loads(content) except json.JSONDecodeError: return None # 降级:记录原文 / 重试 / 换模型 ```` ## 相关链接 * 同组页面:[响应数据处理](/api-capabilities/openai/response-handling) · [兼容模式调用](/api-capabilities/openai/compatible) · [函数调用](/api-capabilities/openai/function-calling) * 原生格式思考:[Claude Effort 思考指南](/api-capabilities/claude-effort-thinking) · [Gemini 原生调用](/api-capabilities/gemini/native) * 模型与价格:[模型与价格总览](/api-capabilities/model-info) # OpenAI 兼容模式:响应数据处理 Source: https://docs.apiyi.com/api-capabilities/openai/response-handling /v1/chat/completions 流式与非流式响应的统一解析方法:共性优先,附健壮解析参考实现,覆盖各模型不影响接入的少数差异。 调用 [兼容模式](/api-capabilities/openai/compatible) 时,无论你用的是 OpenAI、Claude、Gemini、Grok、Qwen、GLM 还是其它模型,响应都遵循同一套 OpenAI schema。**绝大多数解析逻辑是通用的**——只要按本页的统一写法处理,换模型不用改代码。 本页帮你把「响应数据处理」一次做对:先讲共性,再用一张表列出**少数需要兼容、但不影响接入**的差异点。 请求侧(base\_url、鉴权、换模型)见 [兼容模式调用](/api-capabilities/openai/compatible)。本页只讲**响应侧**:拿到响应后怎么解析。 ## 两种模式,同一端点 同一个 `/v1/chat/completions`,只由 `stream` 参数决定返回形态: | | `stream: false`(默认) | `stream: true` | | - | - | - | | 返回形态 | 单个 JSON 对象 | SSE 数据流(多行 `data:`) | | 顶层类型 | `chat.completion` | `chat.completion.chunk` | | 取正文 | `choices[0].message.content` | 累加各块 `choices[0].delta.content` | | 适用场景 | 后端任务、批处理、需完整结果 | 聊天 UI、需要逐字上屏 | ## 非流式响应 结构稳定,取 `choices[0].message.content` 即可: ```json theme={null} { "id": "chatcmpl-xxx", "object": "chat.completion", "model": "gpt-4.1-mini", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "1+1等于2。" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 31, "completion_tokens": 8, "total_tokens": 39 } } ``` ```python Python theme={null} resp = client.chat.completions.create( model="gpt-4.1-mini", messages=[{"role": "user", "content": "1+1等于几?"}] ) print(resp.choices[0].message.content) print(resp.usage.total_tokens) ``` ```javascript Node.js theme={null} const resp = await openai.chat.completions.create({ model: 'gpt-4.1-mini', messages: [{ role: 'user', content: '1+1等于几?' }] }); console.log(resp.choices[0].message.content); console.log(resp.usage.total_tokens); ``` ```bash cURL theme={null} curl https://api.apiyi.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '{"model":"gpt-4.1-mini","messages":[{"role":"user","content":"1+1等于几?"}]}' ``` 非流式下七家主流模型高度一致,`choices[0].message.content` 可无差别取值。部分模型(如 OpenAI 系)`message` 里还会带 `annotations`、`refusal` 等字段,按需读取,不用则忽略即可。 ## 流式响应(SSE) 流式以 Server-Sent Events 逐块推送,每行形如 `data: {...}`,以 `data: [DONE]` 收尾: ```text theme={null} data: {"choices":[{"delta":{"content":"1"},"index":0}], ...} data: {"choices":[{"delta":{"content":"+1"},"index":0}], ...} data: {"choices":[{"delta":{},"finish_reason":"stop","index":0}], ...} data: [DONE] ``` 用官方 SDK 时迭代即可,核心是**累加 `delta.content`**: ```python Python theme={null} stream = client.chat.completions.create( model="gpt-4.1-mini", messages=[{"role": "user", "content": "写一首短诗"}], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ```javascript Node.js theme={null} const stream = await openai.chat.completions.create({ model: 'gpt-4.1-mini', messages: [{ role: 'user', content: '写一首短诗' }], stream: true }); for await (const chunk of stream) { const delta = chunk.choices?.[0]?.delta?.content; if (delta) process.stdout.write(delta); } ``` ## 接入要点:少数差异,统一处理 不同模型的流式细节略有出入,但**只要遵守下面几条,就能用同一套代码兼容全部模型**。 **结束块的 `choices` 可能是空数组。** 携带 `usage` 的最后一块,部分模型是 `"choices":[]`(如 gpt-4.1-mini、grok、qwen、glm),直接取 `choices[0]` 会越界报错。**解析每块前先判 `choices` 是否非空。** | 差异点 | 表现 | 统一处理方式 | | - | - | - | | 结束块 choices | 可能为 `[]` 空数组,也可能非空 | 取 delta 前先判 `choices` 非空 | | `finish_reason` 中间值 | 多数为 `null`,claude 为 `""`(空串) | 判结束统一用 `finish_reason === "stop"` | | `usage` 出现位置 | 空 choices 块 / 非空 choices 块 / 与 `stop` 同块 | 三处都尝试读取,读到即记录 | | 分块粒度 | 逐 token(gpt 系)或整句(gemini/claude) | 不影响——累加即可,无需关心颗粒 | | 首个角色声明块 | 有的先发一个空 content 块声明 `role` | content 为空时跳过,不要当正文 | | 厂商私有字段 | `obfuscation`、`system_fingerprint`、`first_token_return_time` 等 | 一律忽略,不要硬依赖 | ## 健壮解析参考实现 不依赖 SDK、直接处理原始 SSE 时,按下面的写法可覆盖上述全部差异: ```python Python theme={null} import json, requests def stream_chat(model, messages, api_key): resp = requests.post( "https://api.apiyi.com/v1/chat/completions", headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}, json={"model": model, "messages": messages, "stream": True}, stream=True, timeout=300, ) text, usage = "", None for line in resp.iter_lines(decode_unicode=True): if not line or not line.startswith("data: "): continue data = line[6:] if data == "[DONE]": break chunk = json.loads(data) if chunk.get("usage"): # usage 可能出现在任意块 usage = chunk["usage"] choices = chunk.get("choices") if not choices: # 结束块可能是空数组,先判空 continue delta = choices[0].get("delta", {}) piece = delta.get("content") if piece: # 跳过 role 声明等空 content 块 text += piece print(piece, end="", flush=True) if choices[0].get("finish_reason") == "stop": pass # 仅作结束标记,不要 break(usage 常在其后) return text, usage ``` ```javascript Node.js theme={null} async function streamChat(model, messages, apiKey) { const resp = await fetch("https://api.apiyi.com/v1/chat/completions", { method: "POST", headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" }, body: JSON.stringify({ model, messages, stream: true }), }); const reader = resp.body.getReader(); const decoder = new TextDecoder(); let buffer = "", text = "", usage = null; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop(); // 保留可能不完整的最后一行 for (const line of lines) { if (!line.startsWith("data: ")) continue; const data = line.slice(6).trim(); if (data === "[DONE]") return { text, usage }; const chunk = JSON.parse(data); if (chunk.usage) usage = chunk.usage; // usage 可能出现在任意块 const choices = chunk.choices; if (!choices || choices.length === 0) continue; // 结束块可能为空数组 const piece = choices[0].delta?.content; if (piece) { text += piece; process.stdout.write(piece); } } } return { text, usage }; } ``` 推理模型(grok、qwen、glm 等)流式时会先推送 `delta.reasoning_content`(思考链),再推送 `delta.content`(正文)。上面的解析只取了 `content`,因此思考链被自动跳过。需要展示思考过程时的处理见 [推理模型输出](/api-capabilities/openai/reasoning-models)。 ## usage 与计费 * `usage` 在非流式响应里随结果一起返回;流式则在尾部某一块里返回(位置见上表,建议「读到即覆盖」)。 * 各家字段细分不同:OpenAI 系有 `completion_tokens_details`,Gemini/Claude 额外带 `input_tokens`/`output_tokens`,推理模型带 `reasoning_tokens`。统一以 `prompt_tokens` / `completion_tokens` / `total_tokens` 三个标准字段为准。 **流式 usage 的 `total_tokens` 不要全信。** 实测个别模型(如 gpt-5.4-mini)流式尾块出现 `total ≠ prompt + completion` 的异常帧,同模型非流式则正常。**计费请以账单为准**,不要用流式那一帧的 total 做结算。 ## 相关链接 * 同组页面:[兼容模式调用](/api-capabilities/openai/compatible) · [推理模型输出](/api-capabilities/openai/reasoning-models) · [原生调用](/api-capabilities/openai/native) * 模型与价格:[模型与价格总览](/api-capabilities/model-info) * 获取 / 管理令牌:`https://api.apiyi.com/token` # 两个端点怎么选:GPT-5.4+ 迁移到 Responses Source: https://docs.apiyi.com/api-capabilities/openai/responses-migration GPT-5.4 起的模型在 /v1/chat/completions 上把工具调用和推理档位一起传,可能被直接 400 拒掉。这一页讲怎么确认自己撞上了、两条出路怎么选、代码逐字段怎么改(含带工具调用的完整前后对照)、以及改完怎么验证。 **一句话**:GPT-5.4 起的模型,在 `/v1/chat/completions` 上**同时传 `tools` 和显式的 `reasoning_effort`**(`none` 除外)可能被上游直接拒掉,报 400 `Function tools with reasoning_effort are not supported ...`。 两条出路:**把带工具的请求改走 `/v1/responses`**(推理和工具都保留,推荐),或者**显式设 `reasoning_effort="none"`**(保住端点,但放弃推理)。不带 `tools` 的请求完全不受影响。 ## 先确认你是不是撞上了这条 有三种表现,第二种最容易误判。 ### 表现一:明确的 400 ```text theme={null} Function tools with reasoning_effort are not supported for gpt-5.6-sol in /v1/chat/completions. To use function tools, use /v1/responses or set reasoning_effort to 'none'. ``` 响应里 `param` 是 `reasoning_effort`。这是**上游 OpenAI 的官方限制**,不是 API易 网关的问题——同一段请求直连 OpenAI 官方也是这个结果。 ### 表现二:时灵时不灵 同一个模型可能挂着多条上游链路,**这条限制并不是每条链路都会拦**。我们 2026-09-02 在默认分组实测(同一把 KEY、同一时段,每个组合各发 6 次): | 模型 | `tools` + `reasoning_effort="medium"` | | - | - | | `gpt-5.6-luna` | 6/6 返回 400 | | `gpt-5.6-sol` | 6/6 返回 200,工具正常调用 | | `gpt-5.6-terra` | 6/6 返回 200,工具正常调用 | | `gpt-5.4` | 6/6 返回 200,工具正常调用 | 而同一天早些时候,有客户在 `gpt-5.6-sol` 上实实在在收到了这条 400。 **「我这次没报错」不能当作安全依据。** 同一个模型、同一段代码,换个时间点、换个分组就可能开始 400。要么改走 Responses,要么显式 `reasoning_effort="none"`——这两条在所有链路上都是稳定的。 ### 表现三:没报错,但工具压根没被调用 如果模型该调工具却回了一句闲聊(`finish_reason` 是 `stop`、`tool_calls` 为空),先别急着调提示词:把 `reasoning_effort` 显式设成 `none` 重发一次,工具能正常调用,就说明问题出在这个参数组合上,而不是提示词写得不好。 ## 这条限制的范围 | | 是否受影响 | | - | - | | `gpt-5.6-sol` / `gpt-5.6-terra` / `gpt-5.6-luna` / `gpt-5.5` / `gpt-5.4` 系列 | 受影响(取决于链路,见上) | | `gpt-5.2` / `gpt-5.1` / `gpt-5` 等更早的模型 | 不在官方公告的影响范围内 | | Claude / Gemini / Grok 等非 OpenAI 模型 | 无关,不受影响 | | 请求里没有 `tools` | 不受影响,`reasoning_effort` 随便传 | | 走 `/v1/responses` 端点 | 不受影响,推理 + 工具可以同时用 | 触发条件是**显式传了非 `none` 的档位**。`low` / `medium` / `high` / `xhigh` 四个档位实测都会触发。 **不传 `reasoning_effort` 不会触发。** 在 `gpt-5.6-luna` 这条稳定复现 400 的链路上,四个档位全部 400,而不传该参数时 6/6 正常返回 `tool_calls`。所以最小改动的应急方案其实有两个:显式 `none`,或者干脆把这个参数删掉。 ## 两条出路怎么选 | | 改走 `/v1/responses` | 显式 `reasoning_effort="none"` | | - | - | - | | 保留推理能力 | ✅ 完整保留,档位照传 | ❌ 关掉推理,模型少了规划这一步 | | 改动量 | 请求 / 响应结构都要改,见下文 | 加一个参数,一行 | | 稳定性 | 所有链路一致 | 所有链路一致 | | 适合谁 | Agent、多步工具编排、长期方案 | 线上救火、工具逻辑简单、暂时改不动代码 | 带工具的复杂任务,推理关掉之后模型的表现会明显变差(少了「先想清楚该调哪个工具、按什么顺序调」这一步),所以 `none` 更适合当过渡手段。 ## 不只是为了绕开报错 即使你没撞上这条限制,Responses 本身也是 OpenAI 给新项目的推荐端点。官方给出的差异是:同一个推理模型走 Responses 的 SWE-bench 成绩更高、缓存利用率比 Chat Completions 高一大截、`web_search` / `code_interpreter` 等内置工具只在这里提供。细节和数字见 [原生调用](/api-capabilities/openai/native)。 对账单最直接的是缓存那一条:**多轮 Agent 是缓存命中的最大受益者**,而多轮 Agent 恰恰也是最容易撞上本页这条限制的场景。缓存怎么算、怎么看命中,见 [缓存计费](/api-capabilities/openai/prompt-caching)。 ## 你属于哪一类 | 你的接入方式 | 走哪条路 | | - | - | | **自己写代码**(OpenAI SDK / 裸 HTTP) | 直接改端点,见下一节 | | **用框架**(LangChain 等) | 先查框架有没有 Responses 开关。LangChain 是 `ChatOpenAI(..., use_responses_api=True)`;没有开关的框架只能退到 `reasoning_effort="none"` 或换模型 | | **用客户端 / IDE 插件** | 客户端侧改不了,只能换支持 Responses 的客户端。完整支持清单见 [原生调用的「客户端支持现状」](/api-capabilities/openai/native),Trae / Cline 的具体处理见 [Trae 接入](/scenarios/programming/trae) 与 [Cline 接入](/scenarios/programming/cline) | ## 代码怎么改 完整的字段映射表见 [原生调用](/api-capabilities/openai/native)。这里只讲**工具调用相关**的四处差异,因为这正是本页场景要动的部分: | | Chat Completions | Responses | | - | - | - | | 推理档位 | 顶层 `reasoning_effort="medium"` | 嵌套 `reasoning={"effort": "medium"}` | | tools 定义 | 嵌套:`{"type": "function", "function": {...}}` | 扁平:`{"type": "function", "name": ..., "parameters": ...}` | | 调用返回 | `message.tool_calls[]`,标识是 `id` | `output` 里的 `function_call` item,标识是 `call_id` | | 结果回传 | `{"role": "tool", "tool_call_id": ...}` | `{"type": "function_call_output", "call_id": ...}` | 两套 tools 格式**不能混用**。把 Chat Completions 的嵌套 `function: {...}` 定义发给 `/v1/responses`(或反过来)是 SDK 报「参数无效」最常见的原因。更多细节见 [FC函数调用](/api-capabilities/openai/function-calling)。 同一个「查天气」工具循环,改前改后完整对照: ```python 改前:Chat Completions theme={null} from openai import OpenAI client = OpenAI(api_key="YOUR_APIYI_KEY", base_url="https://api.apiyi.com/v1") tools = [{ "type": "function", "function": { "name": "get_weather", "description": "查询某地天气", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], }, }, }] messages = [{"role": "user", "content": "北京今天天气怎么样?"}] resp = client.chat.completions.create( model="gpt-5.6-luna", messages=messages, tools=tools, reasoning_effort="medium", # ← 与 tools 同传,可能被上游拒绝 ) call = resp.choices[0].message.tool_calls[0] messages.append(resp.choices[0].message) # 助手那一轮原样接回 messages.append({ "role": "tool", "tool_call_id": call.id, "content": '{"temp": 26, "sky": "晴"}', }) final = client.chat.completions.create( model="gpt-5.6-luna", messages=messages, tools=tools, reasoning_effort="medium", ) print(final.choices[0].message.content) ``` ```python 改后:Responses theme={null} from openai import OpenAI client = OpenAI(api_key="YOUR_APIYI_KEY", base_url="https://api.apiyi.com/v1") tools = [{ # 扁平结构,没有 function 这一层 "type": "function", "name": "get_weather", "description": "查询某地天气", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], "additionalProperties": False, }, }] history = [{"role": "user", "content": "北京今天天气怎么样?"}] resp = client.responses.create( model="gpt-5.6-luna", input=history, tools=tools, reasoning={"effort": "medium"}, # ← 嵌套写法,这里不受限制 ) call = next(i for i in resp.output if i.type == "function_call") history += resp.output # 整个 output 原样接回历史 history.append({ "type": "function_call_output", "call_id": call.call_id, # 注意是 call_id,不是 id "output": '{"temp": 26, "sky": "晴"}', }) final = client.responses.create( model="gpt-5.6-luna", input=history, tools=tools, reasoning={"effort": "medium"}, ) print(final.output_text) ``` 两段代码都在 API易 默认分组实测跑通:改前那段稳定复现 400,改后那段正常走完「调用 → 回传 → 最终回答」全循环。 `history += resp.output` 这一步别省。Responses 的 `output` 里除了 `function_call`,还可能有 `reasoning` item —— 把它原样带回下一轮,模型才能接着上一轮的思路继续,这也正是 Responses 在多步工具任务上更强的原因。 ## 迁移时最容易踩的坑 `output` 是一个 **item 数组**,里面可能同时有 `reasoning`、`message`、`function_call` 三类,顺序和数量都不固定。取文本用 `resp.output_text`,取工具调用要遍历筛 `type == "function_call"`,不要写死下标。 `max_tokens`(或 `max_completion_tokens`)改成 `max_output_tokens`;`response_format` 改成 `text.format`;系统提示词可以从 `messages` 里拿出来放到顶层 `instructions`。另外 gpt-5 系列推理模型**不支持 `temperature` / `top_p`**,两个端点都一样,传了会报错,删掉改用 `reasoning.effort` 控制。 `usage.prompt_tokens` → `usage.input_tokens`,`completion_tokens` → `output_tokens`,缓存命中在 `usage.input_tokens_details.cached_tokens`。做用量统计的代码要一起改,否则会静默统计成 0。 最稳的做法是**自己维护 `input` 数组**,把每轮的 `output` 原样接回去——这条在任何分组、任何模型上都成立,也是本页示例的写法。 `previous_response_id` 链式在 2026-09-02 的默认分组实测可用(`gpt-5.6-sol` / `terra` / `luna` / `gpt-5.4` 均能记住上一轮,且 `store` 默认为 `true`;显式传 `store: false` 后再链式会正确报「找不到上一条」)。但 `GET /v1/responses/{id}` 回取历史仍不可用。**上线前请在你自己的分组里验一次**,别把它当作默认保证。相关背景见 [多轮对话指南](/api-capabilities/multi-turn-conversation)。 Chat Completions 流式是一串 `delta` 增量,Responses 是带类型的事件流(`response.output_text.delta`、`response.function_call_arguments.delta` 等)。流式解析逻辑要重写,不能沿用。写法见 [原生调用](/api-capabilities/openai/native)。 ## 迁移后怎么验证 改完别只看 HTTP 200,按这四条过一遍: 打印 `[i.type for i in resp.output]`,应该能看到 `function_call`(推理档位高时前面还会有 `reasoning`)。只有 `message` 说明工具没被调用。 检查 `usage.input_tokens` / `output_tokens` 不为 0,`output_tokens_details.reasoning_tokens` 能反映推理档位的变化。 多轮跑几次,看 `usage.input_tokens_details.cached_tokens` 是否大于 0。这是 Responses 相比兼容模式最直接的账单收益。 同样的 `tools` + `reasoning_effort` 组合,走新端点应该稳定通过。留一条回归用例,之后换模型时能立刻发现问题。 ## 什么时候可以不迁 不必一刀切。以下场景留在兼容模式完全合理: * **根本不用工具调用** —— 这条限制与你无关,`reasoning_effort` 随便传 * **需要用同一套代码调多家模型** —— Claude、Gemini 等只有 `/v1/chat/completions` 这条通路,为 OpenAI 单独分叉未必划算 * **框架 / 客户端锁死了端点** —— 先用 `reasoning_effort="none"` 顶住,等框架跟进 * **用 `gpt-5.2` 及更早的模型** —— 不在影响范围内 兼容模式的完整能力边界见 [兼容模式调用](/api-capabilities/openai/compatible)。 ## 常见问题 模型不再做显式推理,直接输出。单步、工具选择明确的任务影响不大;多步编排、需要「先想清楚调用顺序」的 Agent 任务会明显变差。它适合当过渡,不适合当终态。 可以,而且是常见的渐进式做法:普通对话继续走 `/v1/chat/completions`,只把带 `tools` 的那条链路改成 `/v1/responses`。两个端点用同一把 KEY、同一个 base\_url,价格也完全一致。 不变。同一个模型在两个端点上的输入 / 输出单价一样,计费口径也一样。价格见 [模型与价格总览](/api-capabilities/model-info)。差别只在缓存命中率——Responses 通常更高,实际账单反而更省。 不受影响。这是 OpenAI 对自家 GPT-5.4+ 模型的限制。Claude 走 `/v1/messages` 或兼容模式、Gemini 走原生或兼容模式,工具调用和思考都可以同时开。 `gpt-5.4-pro` / `gpt-5.5-pro` 这类模型在实务上只有 `/v1/responses` 可用(且需要 SVIP 分组)。它们运行时间长,配合 background 模式使用,兼容模式承载不了这个交互形态。见 [原生调用](/api-capabilities/openai/native)。 不会。被官方计划关停的是 **Assistants API**,不是 Chat Completions。两个端点都会长期支持,只是新功能优先落在 Responses。 ## 相关页面 Responses 端点的完整用法:参数、响应结构、内置工具、客户端支持清单 Chat Completions 的用法与能力边界,各语言 SDK 配置 两个端点各自的工具调用完整示例与流式拼装 # OpenAI API 联网搜索使用指南 Source: https://docs.apiyi.com/api-capabilities/openai/web-search Responses API + web_search 工具即可联网,默认分组 KEY 直接可用。gpt-5.5 / gpt-5.4 实测真实联网、返回带来源引用,附计费说明。 本文说明在 API易 上使用 GPT 系列模型实现联网搜索的方法,基于 2026年6月 实测验证。 ## 一句话结论 **API易 完整支持 OpenAI 官方联网搜索**:使用 **Responses API(`/v1/responses`)+ `web_search` 工具**,gpt-5.5 和 gpt-5.4 实测均真实联网、返回带来源引用的最新信息。**默认分组的 KEY 即可使用,无需任何特殊开通。** ``` 端点: POST https://api.apiyi.com/v1/responses 工具: tools: [{"type": "web_search"}] 模型: gpt-5.5 / gpt-5.4(实测验证) ``` ## 真实可用性(实测数据,2026-06-11) | 模型 | 联网结果 | 引用 | 单次问答搜索次数 | 延迟 | | - | - | - | - | - | | gpt-5.4 | ✅ 准确返回当周新闻 | ✅ 结构化 url\_citation | 1 次 | \~11s | | gpt-5.5 | ✅ 准确返回当周新闻(自动界定时间窗、多源交叉验证) | ✅ 结构化 url\_citation | \~8 次 | \~51s | 选型建议:**追求快和省选 gpt-5.4;追求覆盖面和严谨度选 gpt-5.5**(搜索轮次多、检索内容注入大,费用和延迟相应更高,见计费一节)。 ## 快速上手 ### cURL ```bash theme={null} curl https://api.apiyi.com/v1/responses \ -H "content-type: application/json" \ -H "authorization: Bearer 你的APIYI_KEY" \ -d '{ "model": "gpt-5.4", "max_output_tokens": 8192, "tools": [{"type": "web_search"}], "input": "Anthropic 最近一周发布了什么新模型?请搜索并附来源链接" }' ``` ### Python(OpenAI SDK) ```python theme={null} from openai import OpenAI client = OpenAI( api_key="你的APIYI_KEY", # 默认分组即可 base_url="https://api.apiyi.com/v1", ) resp = client.responses.create( model="gpt-5.4", # 或 gpt-5.5 max_output_tokens=8192, # 建议 ≥8k;gpt-5.5 推理 token 消耗较多,过小会 incomplete tools=[{"type": "web_search"}], input="Anthropic 最近一周发布了什么新模型?请搜索并附来源链接", ) # 1) 最终回答文本 print(resp.output_text) # 2) 本次实际搜索次数(计费依据,见下文) search_calls = [item for item in resp.output if item.type == "web_search_call"] print(f"本次搜索 {len(search_calls)} 次") # 3) 来源引用(结构化 url_citation) for item in resp.output: if item.type == "message": for content in item.content: for ann in getattr(content, "annotations", []) or []: print(f"来源: {ann.title} | {ann.url}") ``` ### 响应结构说明 `output` 数组按执行顺序包含: | item type | 含义 | | - | - | | `web_search_call` | 一次实际执行的搜索(**计费按此条目数**) | | `reasoning` | 模型推理过程(gpt-5 系列) | | `message` | 最终回答,其 `content[].annotations` 含 `url_citation`(title + url) | `status` 为 `completed` 表示正常完成;若为 `incomplete` 通常是 `max_output_tokens` 给小了,调大即可。 ## 计费说明(重要) 联网搜索**会收取工具调用费用**,由两部分组成: | 项目 | 价格 | 说明 | | - | - | - | | **工具调用费** | **\$10 / 1000 次**(\$0.01/次) | **工具名:`web_search`**;按响应 `output` 中 `web_search_call` 条目数计——一次提问可能触发多次搜索(gpt-5.4 通常 1 次,gpt-5.5 通常 5–8 次) | | **检索内容 token 费** | 按模型标准 input 价 | 搜索结果会注入模型上下文,按 input token 计费。**这部分往往是大头**:实测 gpt-5.4 单次问答约 9k input token,gpt-5.5 约 48–54k | 实测单次联网问答总开销参考:gpt-5.4 ≈ \$0.01 搜索费 + 9k token;gpt-5.5 ≈ \$0.08 搜索费 + \~50k token。请按业务问答量预估。 ## 注意事项 1. **请走 Responses API,不要用 Chat Completions 的 `web_search_options`**:gpt-5 系列模型不支持该参数(OpenAI 官方行为,会返回 400 `Unknown parameter: 'web_search_options'`)。`web_search_options` 仅适用于 `*-search-preview` 专用模型。 2. **`max_output_tokens` 建议 ≥8192**:gpt-5.5 的推理(reasoning)token 消耗较多,上限过小会返回 `status: "incomplete"`,没有最终回答但 token 照常计费。 3. 旧版工具类型 `web_search_preview` 同样可用,行为一致;新接入建议直接用 `web_search`。 4. 如需控制成本,可在提示词中约束搜索行为(如"最多搜索 2 次"),或选用 gpt-5.4。 ## FAQ **Q:怎么确认这次回答真的联网了?** A:检查响应 `output` 中是否存在 `type="web_search_call"` 的条目,以及 `message` 的 annotations 中是否有 `url_citation`。两者都有即为真实联网;只有正文文字、没有这两个特征的,是模型凭训练数据回答。 **Q:需要换分组或特殊 KEY 吗?** A:不需要。OpenAI 系列模型使用默认分组的 KEY 即可直接调用联网搜索。 **Q:支持哪些模型?** A:gpt-5.5、gpt-5.4 已实测验证。其他 gpt-5 系列模型理论上同样支持 Responses API 的 `web_search` 工具,使用前建议按上面 FAQ 的方法做一次验证。 ## 相关文档 Responses API 端点、参数与接入说明 联网搜索注入的大量 input token 可配合缓存降本 # 令牌管理 API Source: https://docs.apiyi.com/api-capabilities/token-management 用程序创建、查询、禁用和删除 API Key,支持批量发放并按额度、模型、有效期三重限制 ## 接口概述 令牌管理接口让你用程序完成 API Key 的全生命周期管理,不必逐个在控制台点击。 最常见的场景是**批量发放**:给团队成员、给下游客户、给不同项目各发一把 Key, 并分别限制**能花多少钱**、**能用哪些模型**、**用到什么时候**。 `remain_quota` 设定这把 Key 最多能消费多少 `models` 设定白名单,调用名单外的模型直接被拒 `expired_time` 设定到期时间,到点自动失效 只需要建一两把 Key 的话,直接用控制台更快,见 [如何创建 KEY](/faq/token-management)。 本接口面向需要自动化发放、定期轮换、或把 Key 管理接入自有系统的场景。 ## 如何获取个人访问令牌 令牌管理接口用**个人访问令牌**认证(`nap_` 开头),与 API Key 不是一回事。 访问 `api.apiyi.com/account/profile` 个人中心,在「账户选项」里点击 **个人访问令牌** → **创建令牌** 三项权限相互独立,只勾用得到的: | 你要做的事 | 需要勾选 | | - | - | | 列出、查询 API Key | 模型密钥 · 读取 | | 创建、修改、禁用、删除 API Key | 模型密钥 · 写入 | | 获取新建 API Key 的明文(发放给下游) | 模型密钥 · 查看密钥 | 批量发 Key 的脚本三项都要勾;只做用量巡检的脚本勾「读取」即可。 到期时间默认 30 天,建议选久一点。发 Key 的脚本跑在固定 IP 的服务器上时,强烈建议设置 IP 白名单(创建后不能修改,可以先不填调通,再新建一把带白名单的正式令牌) 创建个人访问令牌:名称、到期时间、IP 白名单与权限 **勾了「写入」和「查看密钥」的令牌能创建出可以调用模型的 API Key,请像保管账号密码一样保管它。** 个人访问令牌本身不能调用模型,但它能发出可以调用模型的 Key。因此这类令牌务必**设置 IP 白名单**、 存进密钥管理工具而不是写在代码里,不要提交进代码仓库,并在到期前及时换新。 **旧系统令牌计划于 2026-11-11 (UTC+8) 停用。** 原来在「账号选项 - 系统令牌」生成的 AccessToken 在此之前照常可用,之后会返回 401 `ACCESS_TOKEN_LEGACY_RETIRED`。 平台会视使用情况决定是否延期,建议尽早换成个人访问令牌。换令牌时请同时检查脚本:**新版创建接口返回的 `key` 已打码**,取明文要另调一次 `POST /api/token/{id}/key`,见下文。 详见 [什么是个人访问令牌?](/faq/personal-access-token) ## 端点一览 所有端点的认证方式相同:`Authorization` 请求头填个人访问令牌,`Bearer ` 前缀可加可不加。 | 方法 | 路径 | 用途 | 需要的权限 | | - | - | - | - | | `GET` | `/api/token/?p=0&page_size=100` | 列出本账号所有令牌(`key` 已打码) | 读取 | | `GET` | `/api/token/{id}` | 查询单个令牌 | 读取 | | `POST` | `/api/token/` | **创建令牌**(响应里的 `key` 已打码) | 写入 | | `POST` | `/api/token/{id}/key` | **获取 Key 明文** | 查看密钥 | | `PUT` | `/api/token/` | 更新令牌(需传完整对象) | 写入 | | `PUT` | `/api/token/?status_only=true` | 只切换启用/禁用状态 | 写入 | | `DELETE` | `/api/token/{id}` | 删除令牌 | 写入 | 基础地址为 `https://api.apiyi.com`。 ## 创建令牌 ### 请求示例 ```bash theme={null} export APIYI_SYS_TOKEN='nap_YOUR_PERSONAL_ACCESS_TOKEN' # 个人访问令牌 curl --compressed -s -X POST 'https://api.apiyi.com/api/token/' \ -H "Authorization: $APIYI_SYS_TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "name": "team-alice", "remain_quota": 500000, "unlimited_quota": false, "group": "default", "models": "gpt-5.6,deepseek-chat", "expired_time": -1 }' ``` ### 请求字段 | 字段 | 类型 | 说明 | | - | - | - | | `name` | String | 令牌名称,用于自己识别用途 | | `remain_quota` | Integer | 额度上限(额度数),500,000 = \$1.00 | | `unlimited_quota` | Boolean | 是否不限额度;**缺省为 `false`** | | `group` | String | 令牌绑定的分组标识符,如 `default` | | `models` | String | **模型白名单**,逗号分隔;不传表示不限 | | `expired_time` | Integer | 过期时间戳(Unix 秒),`-1` 表示永不过期 | **`unlimited_quota` 缺省为 `false`,而 `remain_quota` 缺省为 0** —— 两者一起缺省时会建出一把 额度为 0、无法使用的令牌。要么显式给 `remain_quota` 赋值,要么把 `unlimited_quota` 设为 `true`。 **模型白名单请使用 `models` 字段。** 响应结构里还存在 `model_limits`、`model_limits_enabled`、`allow_ips` 三个字段, 传入它们不会报错(接口仍返回 200),但**当前不会生效** —— 回读时这些字段仍为空。 需要限制可用模型请使用 `models`,需要限制来源 IP 请在自己的服务侧实现。 ### 响应示例 ```json theme={null} { "success": true, "message": "", "data": { "id": 119431, "user_id": 80778, "key": "K1RP**********4b75", "status": 1, "name": "team-alice", "group": "default", "models": "gpt-5.6,deepseek-chat", "remain_quota": 500000, "unlimited_quota": false, "used_quota": 0, "expired_time": -1, "created_time": 1785599000 } } ``` **响应里的 `key` 是打码后的值,不能直接使用。** 要拿到明文,用响应里的 `id` 再调一次获取明文接口。 ## 获取 Key 明文 ```bash theme={null} curl --compressed -s -X POST 'https://api.apiyi.com/api/token/119431/key' \ -H "Authorization: $APIYI_SYS_TOKEN" ``` ```json theme={null} { "success": true, "message": "", "data": { "key": "K1RPzapuXLfBU4kDC5D9C0E70b1841AeAa542186B2F54b75" } } ``` * 需要个人访问令牌勾选 **模型密钥 · 查看密钥**,否则返回 403 `ACCESS_TOKEN_SCOPE_DENIED` * 返回的 `key` **不含 `sk-` 前缀**,实际使用时需要自己拼上,即上例中真正的 API Key 是 `sk-K1RPzapu…` * 拿到后立即保存并通过安全渠道分发,不要把响应体留在日志文件里 ## 批量创建 **服务端没有批量创建接口** —— 在请求体里传 `count` 之类的参数不会生效,仍然只创建一把。 批量发放的做法是在客户端循环调用创建接口。 **单个用户最多 1000 个令牌。** 这是账号级别的总量上限,包含已禁用但未删除的令牌。 达到上限后创建接口会失败,需要先删除不再使用的令牌腾出名额。 批量发放前先用 `GET /api/token/?p=0&page_size=100` 翻页统计当前数量, 把「新建 Key → 切流量 → 禁用旧 Key → 确认无调用后删除」中的最后一步真正执行到位, 不要只禁用不删除,否则轮换几轮就会撞到上限。 ```python theme={null} import json import os import requests BASE = "https://api.apiyi.com" HEADERS = {"Authorization": os.environ["APIYI_SYS_TOKEN"], "Content-Type": "application/json"} QUOTA_PER_USD = 500_000 def create_token(name, quota_usd=None, group="default", models=None, days=0): """创建一把令牌。quota_usd 为 None 表示不限额度,days 为 0 表示永不过期。""" import time body = { "name": name, "group": group, "unlimited_quota": quota_usd is None, "remain_quota": 0 if quota_usd is None else int(quota_usd * QUOTA_PER_USD), "expired_time": -1 if not days else int(time.time()) + days * 86400, } if models: body["models"] = models resp = requests.post(f"{BASE}/api/token/", headers=HEADERS, json=body, timeout=30) resp.raise_for_status() data = resp.json()["data"] # 这里的 key 已打码 # 取明文:需要「模型密钥 · 查看密钥」权限 resp = requests.post(f"{BASE}/api/token/{data['id']}/key", headers=HEADERS, timeout=30) resp.raise_for_status() key = resp.json()["data"]["key"] return {"id": data["id"], "name": data["name"], "key": "sk-" + key} if __name__ == "__main__": names = ["team-alice", "team-bob", "team-carol"] created = [ create_token(n, quota_usd=1, group="default", models="gpt-5.6", days=30) for n in names ] for row in created: print(f"{row['name']:16s} id={row['id']} {row['key']}") # keys.json 里是明文 Key,注意妥善保存与分发,用完即删 with open("keys.json", "w") as f: json.dump(created, f, ensure_ascii=False, indent=1) ``` ```javascript theme={null} const BASE = "https://api.apiyi.com"; const HEADERS = { Authorization: process.env.APIYI_SYS_TOKEN, "Content-Type": "application/json", }; const QUOTA_PER_USD = 500_000; async function createToken(name, { quotaUsd = null, group = "default", models = null, days = 0 } = {}) { const body = { name, group, unlimited_quota: quotaUsd === null, remain_quota: quotaUsd === null ? 0 : Math.round(quotaUsd * QUOTA_PER_USD), expired_time: days ? Math.floor(Date.now() / 1000) + days * 86400 : -1, }; if (models) body.models = models; const resp = await fetch(`${BASE}/api/token/`, { method: "POST", headers: HEADERS, body: JSON.stringify(body), }); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); const { data } = await resp.json(); // 这里的 key 已打码 // 取明文:需要「模型密钥 · 查看密钥」权限 const r2 = await fetch(`${BASE}/api/token/${data.id}/key`, { method: "POST", headers: HEADERS }); if (!r2.ok) throw new Error(`HTTP ${r2.status}`); const { data: secret } = await r2.json(); return { id: data.id, name: data.name, key: `sk-${secret.key}` }; } const names = ["team-alice", "team-bob", "team-carol"]; for (const name of names) { const row = await createToken(name, { quotaUsd: 1, models: "gpt-5.6", days: 30 }); console.log(row.name, row.id, row.key); } ``` ```bash theme={null} export APIYI_SYS_TOKEN='nap_YOUR_PERSONAL_ACCESS_TOKEN' # 个人访问令牌 for NAME in team-alice team-bob team-carol; do ID=$(curl --compressed -s -X POST 'https://api.apiyi.com/api/token/' \ -H "Authorization: $APIYI_SYS_TOKEN" \ -H 'Content-Type: application/json' \ -d "{\"name\":\"$NAME\",\"remain_quota\":500000,\"unlimited_quota\":false,\"group\":\"default\",\"expired_time\":-1}" \ | jq -r '.data.id') KEY=$(curl --compressed -s -X POST "https://api.apiyi.com/api/token/$ID/key" \ -H "Authorization: $APIYI_SYS_TOKEN" | jq -r '.data.key') printf '%s\tsk-%s\n' "$NAME" "$KEY" done ``` ## 三重限制怎么用 ### 额度限制 `remain_quota` 是这把令牌能消费的额度上限,换算关系与[余额查询](/api-capabilities/balance-query) 一致: 500,000 额度 = \$1.00 美金 (USD) 例如给下游客户发一把最多消费 \$10 的 Key,就设 `remain_quota: 5000000` 且 `unlimited_quota: false`。用量可以从令牌的 `used_quota` 字段读取。 ### 模型限制 `models` 是逗号分隔的白名单。设置后,用这把 Key 调用名单外的模型会被直接拒绝: ```json theme={null} { "error": { "message": "该令牌无权使用模型:deepseek-chat" } } ``` 返回 HTTP 403,**不产生扣费**。不传 `models` 表示不限制。 ### 有效期限制 `expired_time` 是 Unix 秒时间戳,`-1` 表示永不过期。例如发一把 30 天后失效的 Key: ```python theme={null} import time expired_time = int(time.time()) + 30 * 86400 ``` ## 查询令牌 ```bash theme={null} curl --compressed -s 'https://api.apiyi.com/api/token/?p=0&page_size=100' \ -H "Authorization: $APIYI_SYS_TOKEN" | jq '.data[] | {id, name, status, remain_quota, used_quota, models}' ``` 关键字段: | 字段 | 说明 | | - | - | | `id` | 令牌 ID,更新与删除时用 | | `key` | 打码后的 Key,明文见「获取 Key 明文」 | | `status` | `1` = 启用,`2` = 禁用 | | `remain_quota` / `used_quota` | 剩余 / 已用额度 | | `unlimited_quota` | 是否不限额度 | | `models` | 模型白名单,空表示不限 | | `expired_time` | 过期时间戳,`-1` 表示永不过期 | ## 更新令牌 **更新接口需要传完整对象,不是增量更新(patch)。** 正确做法是:先 `GET` 拿到令牌的完整对象 → 修改需要变更的字段 → 把**整个对象** `PUT` 回去。 只传要改的那几个字段会把其余字段清空。 ```python theme={null} import os import requests BASE = "https://api.apiyi.com" HEADERS = {"Authorization": os.environ["APIYI_SYS_TOKEN"], "Content-Type": "application/json"} # 1. 取回完整对象 token = requests.get(f"{BASE}/api/token/119431", headers=HEADERS, timeout=30).json()["data"] # 2. 只改需要变的字段,其余原样保留 token["remain_quota"] = 2_500_000 # 提到 $5 token["models"] = "gpt-5.6,gemini-3-pro" # 3. 整个对象 PUT 回去 resp = requests.put(f"{BASE}/api/token/", headers=HEADERS, json=token, timeout=30) print(resp.json()["success"]) ``` ## 禁用与删除 ### 禁用(保留记录) 禁用后该 Key 立即失效,再用它调用模型会返回 401,但令牌记录和历史用量仍然保留。 ```python theme={null} token = requests.get(f"{BASE}/api/token/119431", headers=HEADERS, timeout=30).json()["data"] token["status"] = 2 # 1 = 启用,2 = 禁用 requests.put(f"{BASE}/api/token/", headers=HEADERS, params={"status_only": "true"}, json=token, timeout=30) ``` ### 删除(不可恢复) ```bash theme={null} curl --compressed -s -X DELETE 'https://api.apiyi.com/api/token/119431' \ -H "Authorization: $APIYI_SYS_TOKEN" ``` 批量删除同样在客户端循环即可: ```python theme={null} for token_id in [119431, 119432, 119433]: requests.delete(f"{BASE}/api/token/{token_id}", headers=HEADERS, timeout=30) ``` 删除是不可恢复操作。如果只是想临时停用,用上面的禁用方式,历史用量记录会保留下来便于对账。 ## 常见问题 创建接口返回的 `key` 是打码后的值(中间是一串 `*`),不能直接用。用返回的 `id` 调 `POST /api/token/{id}/key` 取明文(需勾「查看密钥」权限),再在前面拼上 `sk-`。 早期脚本若直接用创建响应里的 `key`,换成个人访问令牌时需要补上这一步。 最可能的原因是创建时既没传 `remain_quota`,也没把 `unlimited_quota` 设为 `true` —— 两者的缺省组合会建出一把额度为 0 的令牌。重新创建时显式指定其中之一即可。 这两个字段(以及 `model_limits_enabled`)当前不生效,传入不会报错但也不会落库。 限制可用模型请使用 `models` 字段;限制来源 IP 目前需要在你自己的服务侧实现。 服务端没有批量创建接口,在请求体里传 `count` 之类的参数不会生效。 批量发放请在客户端循环调用创建接口,参考上面「批量创建」一节的示例。 单个用户最多 1000 个令牌,已禁用但未删除的也计入这个总量。达到上限后创建接口会失败, 需要先删除不再使用的令牌。轮换 Key 时记得走到「删除」这一步,只禁用不删除会占着名额。 更新接口需要传完整对象。先 `GET` 拿到完整对象,改完再整个 `PUT` 回去, 不要只传要改的那几个字段。 禁用(`status: 2`)后 Key 立即失效,但令牌记录与历史用量保留,可以随时改回 `1` 恢复。 删除是不可恢复的,记录一并移除。临时停用建议用禁用。 令牌对象的 `used_quota` 字段就是这把 Key 的累计消费(÷ 500,000 = 美元)。 需要按时间段拆分或看每次调用的明细,去控制台日志页按令牌名筛选, 见[如何查看我的调用记录](/faq/call-logs)。 ## 注意事项 **个人访问令牌不是 API Key,两者不能互换** * **API Key**(`sk-` 开头)用于 `/v1/*` 推理端点 * **个人访问令牌**(`nap_` 开头)用于 `/api/*` 管理端点,不能调用模型 报错对照:401 `ACCESS_TOKEN_LEGACY_RETIRED` 是仍在用已停用的旧系统令牌; 401 `This access token may not be used from the current IP address.` 是来源 IP 不在令牌白名单内; 403 `ACCESS_TOKEN_SCOPE_DENIED` 是令牌没勾这个接口需要的权限(见上方端点一览)。 **妥善保管明文 Key** 「获取 Key 明文」接口返回的是可以直接调用模型的 Key。请注意: * 不要把包含 `key` 的原始响应写进日志文件或提交进代码仓库 * 分发给团队成员时使用安全渠道,不要通过聊天群直接发送 * 这段明文不带 `sk-` 前缀,**常见的密钥扫描工具可能扫不出来**,不要依赖自动化检查兜底 **操作建议** * 批量创建时建议在循环里加适度间隔,避免瞬时并发过高 * 给每把 Key 起有意义的 `name`(如 `team-alice`、`prod-webhook`),便于后续在日志里按 `token_name` 归因 * 定期轮换:新建 Key → 切流量 → 禁用旧 Key 观察一段时间 → 确认无调用后再删除 * [如何查看我的调用记录](/faq/call-logs) —— 在控制台查每把 Key 的调用明细与扣费 * [余额查询 API](/api-capabilities/balance-query) —— 查账号剩余额度 * [如何创建 KEY](/faq/token-management) —— 控制台手动创建 * [令牌与分组](/faq/token-and-groups) —— 分组的作用与选择 # API 手册 Source: https://docs.apiyi.com/api-manual API易 接口使用手册。API易 是 OpenAI 兼容的 AI 网关,一套代码即可接入 400+ 主流大模型,本页帮你快速找到模型、在线调试与集成方式。 API易 是一个 **OpenAI 兼容的 AI 网关**:用一套标准接口、一个 API Key,即可调用 400+ 主流大模型。本页是导航入口——帮你快速找到**该用哪个模型**、**在线调试接口**,以及**如何集成**。 ## 平台概览 ### OpenAI 兼容模式 API易 采用 **OpenAI 兼容格式**,跑通一次后,切换模型只需**更换 `model` 字段**,其余代码完全不变: ```python theme={null} from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1" ) # 切换模型 = 只改 model 这一个字段,其它代码不动 response = client.chat.completions.create( model="gpt-5-chat-latest", # 换成任意支持的模型名即可 messages=[{"role": "user", "content": "你好!"}] ) print(response.choices[0].message.content) ``` 具体可用的模型名称、定价与推荐场景,请查看下方「选择模型」中的两个专页,不在本页罗列(避免信息过期)。 ### 功能支持范围 * 对话补全(Chat Completions) * 图像 / 视频生成 * 语音转录(Whisper) * 嵌入向量(Embeddings) * 函数调用(Function Calling) * 流式输出(SSE) * 标准 OpenAI 参数:`temperature`、`top_p`、`max_tokens` 等 * Responses 端点 * 微调接口(Fine-tuning) * Files 文件管理接口 * 组织管理接口 * 计费管理接口 ## 选择模型 不确定用哪个模型?以下两个专页保持更新,含定价、能力对比与推荐场景: GPT、Claude、Gemini、Grok、DeepSeek、通义、Kimi、GLM 等文本与多模态模型的能力、定价与选型建议。 Nano Banana、GPT-image、Seedream、Flux 等图像模型,以及 VEO、Sora、Wan 等视频生成模型的定价与用法。 ## 基础信息 ### API 端点 * **主要端点**:`https://api.apiyi.com/v1` * **备用端点**:`https://b.apiyi.com/v1` ### 认证方式 所有请求需在 Header 中携带 API Key: ```http theme={null} Authorization: Bearer YOUR_API_KEY ``` ### 请求格式 * **Content-Type**:`application/json` * **编码**:UTF-8 * **方法**:大部分接口为 `POST` ## 快速开始 ### 获取 API Key 1. 访问 [API易控制台](https://api.apiyi.com/token) 并登录 2. 在令牌管理页面点击「新增」创建 API Key 3. 复制生成的 Key 用于接口调用 ### 获取多语言代码示例 控制台已内置各语言的可运行代码示例,会随最新 API 版本实时更新,**建议优先使用**: 1. 进入 [令牌管理页面](https://api.apiyi.com/token) 2. 在目标 API Key 所在行,点击「操作」列的 🔧 小扳手图标 3. 选择「请求示例」,即可查看 cURL、Python、Node.js、Java、C#、Go、PHP、Ruby 等语言的完整示例 API易令牌管理界面 - 请求示例 ## 在线调试(Playground) 「API 参考」栏目提供**在线 Playground**:填入 API Key 即可直接发送请求、实时查看响应,无需写代码。 `POST /v1/chat/completions`,对话与多模态主力接口。 `GET /v1/models`,查询当前可用模型。 `POST /v1/embeddings`,文本向量化。 图像、视频生成接口的 Playground 位于各自模型页(见上方「选择模型」中的图片 / 视频模型专页)。 ## 最小调用示例 最常用的对话补全接口,复制即可运行;更多参数与语言请用上方 Playground 或控制台「请求示例」: ```python theme={null} from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apiyi.com/v1" ) response = client.chat.completions.create( model="gpt-5-chat-latest", messages=[ {"role": "system", "content": "你是一个有用的AI助手。"}, {"role": "user", "content": "你好!请介绍一下自己。"} ], temperature=0.7, max_tokens=1000 ) print(response.choices[0].message.content) ``` ```bash theme={null} curl -X POST "https://api.apiyi.com/v1/chat/completions" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5-chat-latest", "messages": [ {"role": "system", "content": "你是一个有用的AI助手。"}, {"role": "user", "content": "你好!请介绍一下自己。"} ], "temperature": 0.7, "max_tokens": 1000 }' ``` ## 流式响应 在请求中设置 `stream: true`,响应将以 Server-Sent Events(SSE)逐块返回,适合打字机式输出: ```python theme={null} stream = client.chat.completions.create( model="gpt-5-chat-latest", messages=[{"role": "user", "content": "讲个短笑话"}], stream=True ) for chunk in stream: content = chunk.choices[0].delta.content or "" print(content, end="", flush=True) ``` SSE 数据以 `data: ` 开头,最后一行为 `data: [DONE]` 表示结束。 ## 错误处理 接口遵循 OpenAI 错误格式: ```json theme={null} { "error": { "message": "Invalid API key provided", "type": "invalid_request_error", "code": "invalid_api_key" } } ``` 常见错误码: | 错误码 | HTTP 状态码 | 说明 | | - | - | - | | invalid\_api\_key | 401 | API 密钥无效 | | insufficient\_quota | 429 | 额度不足 | | model\_not\_found | 404 | 模型不存在 | | invalid\_request\_error | 400 | 请求参数错误 | | rate\_limit\_exceeded | 429 | 请求频率过高 | | server\_error | 500 | 服务器内部错误 | 建议实现指数退避重试:遇到 429 / 500 时间隔翻倍重试,可显著提升稳定性。API Key 请用环境变量存储,不要硬编码进代码。 上表只给出错误码的含义,**具体原因写在响应体的 `error.message` 里**——而这段原文只在接口响应中返回一次,后台日志不会保留。请务必在客户端把它完整打印并落盘: 为什么必须自己打印原始错误、各语言的正确捕获写法、必须留存的 7 个字段,以及可直接复制的报障模板 ## 速率限制 | 限制类型 | 默认值 | 说明 | | - | - | - | | RPM(每分钟请求数) | 3000 | 每个 API 密钥 | | TPM(每分钟令牌数) | 1000000 | 每个 API 密钥 | | 并发请求数 | 100 | 同时处理的请求 | 超出限制会返回 `429`,请合理控制请求频率。 ## 需要帮助? 文本 / 多模态模型推荐与定价。 打开 API 参考 Playground 直接发请求。 * 访问官网:[api.apiyi.com](https://api.apiyi.com) * 技术支持邮箱:`support@apiyi.com` # 接口错误信息留存指南 Source: https://docs.apiyi.com/api-manual/error-reporting API易 的错误只在接口响应体里返回一次,后台日志只记成功计费的调用。本页讲为什么必须自己打印原始错误、Python / Node.js / cURL 的正确捕获写法、ComfyUI 等封装工具怎么拿到原文、必须留存的 7 个字段,以及可直接复制的报障信息模板。 ### 先说结论:错误原文只在响应体里出现一次 API易 的错误信息**只通过接口响应体返回给你**。后台日志是**计费账本**——它记录的是成功产生扣费的调用,报错请求既不扣费、也不会出现在日志里。 所以「后台日志里查不到」并不等于「没发生」,而是意味着**那次错误的唯一记录就在你的客户端**。你没把它打印下来、落到盘上,它就永久消失了——连我们也找不回来。 **一句话结论**:把接口返回的**原始响应体**原样打出来,不要只留你程序包装过的那一句话。`400 Bad Request` 这种字符串对定位问题的贡献接近于零,真正的答案在它下面被丢掉的那个 JSON 里。 ## 一个真实案例:400 Bad Request 说明不了任何问题 一位客户上报的原文只有一行: ```text theme={null} apiyi GPT Image 2 2k: 400 Bad Request from POST https://api.apiyi.com/v1/images/edits ``` 这行字符串是**客户端框架包装后的产物**。它保留了模型名、HTTP 方法、URL 和状态码,唯独把最关键的**响应体丢掉了**。支持侧能给出的最好回答只能是: > 400 一般是内容安全和参数问题,大概率是内容安全。 这是**猜测**,不是结论。因为同一次调用,接口真正返回的响应体可能是下面三种中的任意一种,而它们对应三种完全不同的处理动作: | 响应体里的 `error.message` | 真实原因 | 你该做什么 | | - | - | - | | `Your request was rejected by the safety system` | 上游内容审核拦截 | 改提示词或换参考图。**不要重试**,重试一样会被拦;见[内容安全](/faq/content-safety) | | `Invalid value for 'size': expected one of ...` | 参数枚举值写错 | 代码级 bug,改参数即可,重试毫无意义 | | `invalid_image_file` / `Invalid input image` | 参考图文件本身不合法(例如部分安卓手机拍出的 `.jpg` 实为 MPO 多帧格式) | 用 Pillow 之类重编码一次再传;见[图片 API 必读](/api-capabilities/image-api-best-practices) | **同一个 400,三种完全不同的处理动作。** 丢掉响应体,就是把「三选一」变成「靠猜」——猜错的代价是一轮无效的来回沟通,外加一次本来可以避免的重试。 更要紧的是:**这三种情况里有两种根本不该重试**。分不清是哪一种,就只能盲目重试,白白消耗时间和额度。 ## 后台日志能查到什么,查不到什么 这是最需要先建立的认知:**后台日志是计费账本,不是错误日志。** | 你想查的信息 | 后台[日志页面](https://api.apiyi.com/log) | 接口响应 | | - | - | - | | 本次调用的计费金额 | 有 | 无(`usage` 只给 token 数) | | token 用量 | 有 | 有(`usage` 字段) | | 模型名、调用时间 | 有(仅成功调用) | 需要你自己记 | | `request_id` | 有(仅成功调用) | 响应头 `x-request-id` | | **错误码与错误原文** | **没有** | **唯一来源** | | **上游拒绝的具体理由** | **没有** | **唯一来源** | | 失败的调用本身 | **不出现**(没扣费就不入账本) | — | **反过来用,这是排查断连类问题最有力的判据**:如果日志里**有**这条计费记录,说明请求确实到达了上游并产生了消耗;如果**没有**,那问题多半发生在到达上游之前(网络、鉴权、参数校验)。完整口径见[怎么看懂日志里的计费金额](/faq/log-billing-explained)。 ## 必须留存的 7 个字段 排查一次报错需要的信息就这些。缺任何一项都会让排查退化成猜测: | 字段 | 怎么拿 | 缺了它会怎样 | | - | - | - | | **HTTP 状态码** | `resp.status_code` / `err.status` | 分不清是请求被拒(4xx)、服务端异常(5xx)还是压根没建连(无状态码) | | **响应体全文** | `resp.text` / `await resp.text()` | **最致命的一项**——错误的真正原因全在这里,丢了就只能猜 | | **`x-request-id` 响应头** | `resp.headers.get("x-request-id")` | 客服无法精确定位到那一次调用,只能按时间段模糊查 | | **调用时间(带时区)** | 客户端自己打,格式如 `2026-08-03 15:44 (UTC+8)` | 我们的客户遍布全球,不写时区的时间点无法对齐日志 | | **模型名 + 端点路径** | 你自己的请求参数 | 同一个模型走不同端点(`/v1/images/edits` 与 `/v1/chat/completions`)行为并不相同 | | **关键请求参数** | `size`、`quality`、参考图张数与体积、`max_tokens` 等 | 参数类问题无法复现;图片类问题无法判断是不是素材本身的问题 | | **客户端异常原文 + 已重试次数** | `repr(e)`,以及每次尝试单独记一条 | 客户端重试成功后日志里只剩一条漂亮的 200,你会永远看不到底层到底断了多少次 | **响应体不要截断。** 常规业务日志里截断到 200 字符是合理的,但排障场景下错误详情经常出现在末尾。至少保留前 2000 字符;图片类接口若担心 base64 刷屏,只在 `status_code >= 400` 时全量打印即可——错误响应体本来就不长。 ## 正确的错误捕获写法 核心原则只有一条:**分两层捕获,并且在任何一层都不要丢弃原始信息。** * **传输层异常**:连接被重置、TLS 握手失败、超时、DNS 失败。此时**根本没有 HTTP 响应**,能留的只有异常原文。 * **HTTP 层错误**:服务端返回了 4xx / 5xx。此时**一定有响应体**,必须读出来。 ### Python / requests ```python theme={null} import time import requests BASE_URL = "https://api.apiyi.com/v1" API_KEY = "sk-your-api-key" # 生产环境请从环境变量读取 def call_and_log(path, payload, timeout=300): started = time.strftime("%Y-%m-%d %H:%M:%S %z") # 带时区 try: resp = requests.post( f"{BASE_URL}{path}", headers={"Authorization": f"Bearer {API_KEY}"}, json=payload, timeout=timeout, ) except requests.exceptions.Timeout as exc: # 传输层:超时,没有 HTTP 响应可读 raise RuntimeError(f"[{started}] 请求超时 {timeout}s:{exc!r}") from exc except requests.exceptions.RequestException as exc: # 传输层:连接重置、SSL 错误、DNS 失败……同样没有响应体 raise RuntimeError(f"[{started}] 传输层失败:{exc!r}") from exc if resp.status_code >= 400: # 关键:响应体原样带出,不要在这里改写成自己的措辞 raise RuntimeError( f"[{started}] HTTP {resp.status_code} {path} " f"model={payload.get('model')}\n" f"x-request-id: {resp.headers.get('x-request-id')}\n" f"{resp.text}" ) return resp.json() ``` **不要在读响应体之前就 `raise_for_status()`。** 它抛出的 `HTTPError` 只带一句 `400 Client Error: Bad Request for url: ...`,正文原封不动地留在 `resp.text` 里没人去读——这正是本页开头那个案例的成因之一。要用它,也请先把 `resp.text` 取出来。 ### Python / OpenAI SDK 官方 SDK 已经把三样东西都挂在异常对象上了,只是很多人只 `print` 了一句自己的中文提示: ```python theme={null} from openai import OpenAI, APIStatusError, APIConnectionError client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apiyi.com/v1", max_retries=3, # SDK 内建指数退避,针对 429 / 5xx / 连接错误 timeout=60.0, ) try: resp = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello"}], ) except APIStatusError as e: # 服务端返回了 4xx/5xx:状态码、request-id、响应体三样都在这里 print("HTTP 状态码 :", e.status_code) print("request-id :", e.request_id) print("响应体原文 :", e.response.text) raise except APIConnectionError as e: # 没拿到 HTTP 响应:连接被重置、超时、本地代理故障 print("传输层失败 :", repr(e), "|", repr(e.__cause__)) raise ``` 即便只写一行,也请写 `print(f"API 错误:{e}")` 而不是 `print("调用失败")`——SDK 异常的 `str(e)` 里**已经包含服务端返回的 message**。真正会丢信息的是把异常对象整个扔掉的那种写法。 ### Node.js 用 SDK 时: ```javascript theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apiyi.com/v1', }); try { const resp = await client.images.edit({ /* ... */ }); } catch (err) { if (err instanceof OpenAI.APIError) { // 服务端返回了 4xx/5xx console.error('HTTP 状态码 :', err.status); console.error('request-id :', err.requestID); console.error('响应体原文 :', JSON.stringify(err.error)); } else { // 传输层:ECONNRESET、UND_ERR_* 等,此时没有 HTTP 响应 console.error('传输层失败 :', err.code, err.message, err.cause); } throw err; } ``` 直接用 `fetch` 时,**这一步是最容易出事的地方**: ```javascript theme={null} const resp = await fetch('https://api.apiyi.com/v1/images/edits', { method: 'POST', headers: { Authorization: 'Bearer sk-your-api-key' }, body: form, }); if (!resp.ok) { const raw = await resp.text(); // 必须先读 body,再抛错 throw new Error( `HTTP ${resp.status} ${resp.url}\n` + `x-request-id: ${resp.headers.get('x-request-id')}\n${raw}` ); } ``` 本页开头那个 `400 Bad Request from POST https://api.apiyi.com/v1/images/edits`,字面上就等于 `${resp.status} ${resp.statusText} from ${resp.method} ${resp.url}` ——**响应体从头到尾没有被读取过**。 `fetch` 在 HTTP 层面出错时**不会 reject**,`resp.ok` 为 `false` 而已;如果这时直接抛 `resp.statusText`,body 就随着响应对象一起被丢弃了。**在抛错之前先 `await resp.text()`**,这一行的差别就是能不能定位问题。 ### cURL 复现 请客户复现时,给这条命令最省事——它把状态码、响应头、响应体、耗时一次性全带出来: ```bash theme={null} curl -i -sS -X POST https://api.apiyi.com/v1/images/edits \ -H "Authorization: Bearer sk-your-api-key" \ -F "model=gpt-image-2" \ -F "image=@input.png" \ -F "prompt=把背景换成纯白" \ -w '\n---\nHTTP %{http_code} 耗时 %{time_total}s\n' ``` * `-i` 打印响应头,`x-request-id` 就在里面; * `-sS` 关掉进度条但保留错误输出; * `-w` 在末尾附加状态码与总耗时,方便和超时配置对照。 ## 封装工具与自研中台怎么办 ### 一个正面例子 下面这条报错来自某位客户的 ComfyUI 节点: ```text theme={null} 上游 HTTP 0:OpenSSL SSL_read: Connection was reset, errno 10054 ``` 它比 `400 Bad Request` 难看得多,但**信息是完整的**,几秒钟就能定死方向: | 片段 | 含义 | | - | - | | `HTTP 0` | 根本没拿到 HTTP 响应。`0` 是伪状态码,代表连响应行都没读到,不是 400/500 那种业务错误 | | `SSL_read: Connection was reset` | 在 TLS 读阶段连接被对端或中间设备掐断 | | `errno 10054` | Windows 的 `WSAECONNRESET`,等价于 Linux 的 `ECONNRESET`,表示收到了 TCP RST | 结论直接就出来了:这是**传输层**问题,与内容安全、与参数统统无关,也不会产生计费(请求根本没完成)。排查路径见[图片 API 连接中断排查](/api-capabilities/image-connection-drops)。 **对比一下**:一个是包装得很干净但什么也说明不了的 `400 Bad Request`,一个是又长又丑但直接指向根因的 `errno 10054`。**排障场景下,原始、难看、完整的错误远胜于友好、简洁、被改写过的错误。** ### 常见工具去哪里找原文 | 工具 | 原始错误在哪 | | - | - | | ComfyUI | 节点上的红字通常是截断版;完整堆栈在**启动 ComfyUI 的那个终端窗口**,或安装目录下的 `comfyui.log` | | Dify / Coze / n8n | 工作流运行记录里展开该节点的「运行详情 / 输出」,看原始 HTTP 响应而不是节点的错误摘要 | | LangChain / LlamaIndex | 捕获 `openai.APIStatusError` 而不是笼统的 `Exception`,参见上面的 SDK 写法 | | 各类桌面客户端 | 打开设置里的调试 / 开发者日志开关,或用 `curl` 复现一次 | ### 自研中台的三条原则 中间层可以**追加**上下文(哪个业务、哪个租户、第几次重试),但不能**替换**上游返回的 `error.message`。一旦改写,原文就没有第二个地方可以找回。 参考 [Gemini 图片错误处理](/api-capabilities/gemini-image-error-handling) 里的三分结构:`userMessage`(给终端用户看的友好文案)、`devMessage`(给开发看的判定结论)、`rawResponse`(原始响应体,一字不改)。前两个可以随便润色,第三个必须原样入库。 走到兜底分支时,把 `status`、`x-request-id` 和响应体前 2000 字符一并记下来。一个带着原文的「未分类错误」是可排查的;一句干净的「未知错误」不是。 ## 反面清单:这些写法会让问题无法排查 * `except Exception as e: print("调用失败")` —— 异常对象整个被丢掉,连是哪一层的问题都不知道; * 只记 HTTP 状态码,不记响应体 —— 就是本页开头那个案例; * `raise_for_status()` 之前不读 `resp.text` —— 正文还在内存里,就是没人取; * `fetch` 里 `if (!resp.ok) throw new Error(resp.statusText)` —— body 随响应对象一起被扔了; * 客户端重试成功后只留一条漂亮的 200 —— **把每次尝试单独记一条**,否则你永远看不到底层断了多少次,还容易把自己的重试误读成渠道行为; * 日志只打屏不落盘、或按天覆盖 —— 等客户反馈到你这里时,原始记录往往已经滚没了; * 报障时只发一张手机拍的屏幕照片 —— 请直接复制**文本**,截图里的错误经常正好被裁掉半行。 ## 什么时候找客服 先自己走完上面的留存与判读,如果满足下面**任一条**,就带着材料找客服核查: * 拿到了完整响应体,但 `error.message` 指向上游方向(`upstream_error`、上游 5xx 原文、渠道明确报错); * 同一份请求参数**换个模型或换个时间段就正常**,只有某个特定模型稳定失败; * 报错是 `500` + `write_response_body_failed` 这类下行链路问题,且**稳定复现**(这类不计费,排查方法见[连接中断排查](/api-capabilities/image-connection-drops)); * 你怀疑计费与实际调用对不上——这时 `request_id` 是唯一能精确对账的锚点。 ### 报障信息模板(可直接复制) ```text theme={null} 【问题描述】调用 gpt-image-2 图片编辑接口稳定返回 400 【接口端点】POST https://api.apiyi.com/v1/images/edits 【模型名称】gpt-image-2 【调用时间】2026-08-03 15:44 (UTC+8) 【request-id】从响应头 x-request-id 复制 【HTTP 状态码】400 【响应体原文】 {"error":{"message":"...","type":"...","code":"..."}} 【关键参数】size=2048x2048, quality=high, 参考图 1 张 / 3.2 MB / PNG 【复现情况】连续 5 次调用 5 次失败;换一张参考图后恢复正常 【已排查】Key 有效、余额充足、同一个 Key 调用文本模型正常 ``` 企业微信客服二维码 扫码添加,或点击本卡片联系企业微信客服。 也可通过 Telegram `@apiyi001` 或邮箱 `hi@apiyi.com` 联系我们。 把上面模板里的内容**以文本形式**发过来,比任何描述都高效。有 `request_id` 时我们能直接定位到那一次调用的完整链路,不必再问「大概几点调用的什么模型」。`request_id` 的查询方法见[如何查看我的调用记录](/faq/call-logs)。 ## 相关文档 常见错误码对照表、认证方式与速率限制 `ECONNRESET`、`errno 10054`、SSL EOF 这类传输层报错的完整排查路径 在控制台日志页查每次调用,`request_id` 怎么找、怎么对账 为什么失败调用不进日志,以及「有无计费记录」这条判据怎么用 各类模型的 timeout 分档、已调大仍超时的逐层排查 同步调用、base64 前缀差异、`400 invalid_image_file` 的图片预处理 # AI 开发者套件 Source: https://docs.apiyi.com/developer-kit 把 API易 交给 AI 接入:聊天 Agent 装技能包、终端用命令行、编程 Agent 先读契约与模型注册表再写代码。三条路各有一段可复制的提示词。 账号和 Key 仍需你本人在 [控制台](https://api.apiyi.com/token) 创建。从「拿到 Key」到「代码跑通」这一段,下面三条路都可以交给 AI。 ## 先选路 OpenClaw、Claude Code 这类带技能系统的 Agent。装一次技能包,之后用自然语言调用 API易。 不写代码,在终端里验证 Key、列模型、发消息、出图。`npx apiyi@latest check` 零安装。 Cursor、Claude Code、Codex 写接入代码前,先读契约和模型注册表,不编造端点。 ## 套件里有什么 全部是公开地址,不需要登录,任何 Agent 都能直接抓取: | 文件 | 地址 | 角色 | | - | - | - | | **接入契约** | `https://docs.apiyi.com/skill.md` | 端点表、认证、模型命名规则、常见坑、自检状态、检查单。既是编程 Agent 的规则书,也是 Skills 的正文 | | **模型注册表** | `https://docs.apiyi.com/model-registry.json` | 模型 ID、可用端点、分组、计费方式与标价的机器可读事实来源,随价格总表自动更新 | | **页面索引** | `https://docs.apiyi.com/llms.txt` | 全站目录,让 Agent 自己决定该读哪一页 | | **全文合集** | `https://docs.apiyi.com/llms-full.txt` | 所有页面正文拼接,体积大,按需取用 | | **单页纯文本** | 任意页面地址后加 `.md` | 只关心某一页时,比抓 HTML 省 token | | **MCP 服务** | `https://docs.apiyi.com/mcp` | 把本站接成 MCP server,Agent 可随时检索最新内容 | 本页的纯文本版就是 `https://docs.apiyi.com/developer-kit.md`。 ## 给编程 Agent 的规则 编程 Agent 最常犯的错不是写不出代码,而是**凭记忆编**:编一个不存在的端点、把 `gpt-5.4-mini` 写成 `gpt-5-4-mini`、给 Anthropic SDK 的 base\_url 多加一个 `/v1`。这五条规则把这些坑挡在前面: 1. **不要编造端点、参数名、枚举值或响应结构**。只用 `skill.md` 端点表里列出的路径;参数以对应协议(OpenAI / Anthropic / Gemini)的官方定义为准。 2. **模型 ID 以 `model-registry.json` 为唯一事实来源**。ID 带点号且区分大小写,文档页地址里的连字符是 URL 安全替换,不是模型 ID。 3. **Base URL 按 SDK 选,不按模型选**。OpenAI SDK 用 `https://api.apiyi.com/v1`;Anthropic SDK 用根域名 `https://api.apiyi.com`;Google GenAI SDK 用根域名并把 `api_version` 设为 `v1beta`。 4. **Key 只从环境变量 `APIYI_API_KEY` 读**。不硬编码、不提交进 git、不贴进对话。 5. **读完先解释,再动手**。让 Agent 先说清楚打哪个端点、用哪个模型、超时设多少,你确认后它再改代码。 请先完整阅读 [https://docs.apiyi.com/skill.md](https://docs.apiyi.com/skill.md) 和 [https://docs.apiyi.com/llms.txt。](https://docs.apiyi.com/llms.txt。) 在写任何代码之前,你必须遵守以下规则: * 不要编造 endpoint、参数名、枚举值或响应结构。只使用 skill.md 端点表里列出的路径。 * 模型 ID 以 [https://docs.apiyi.com/model-registry.json](https://docs.apiyi.com/model-registry.json) 为唯一事实来源。 ID 带点号且区分大小写(gpt-5.4-mini,不是 gpt-5-4-mini),不要凭记忆写。 * Base URL 按 SDK 选:OpenAI SDK 用 [https://api.apiyi.com/v1;](https://api.apiyi.com/v1;) Anthropic SDK 用 [https://api.apiyi.com(不加](https://api.apiyi.com(不加) /v1); Google GenAI SDK 用 [https://api.apiyi.com](https://api.apiyi.com) 并把 api\_version 设为 v1beta。 * Key 只从环境变量 APIYI\_API\_KEY 读取,不要硬编码、不要提交进 git。 * 需要某一页的细节时,从 llms.txt 找到页面地址,在末尾加 .md 读纯文本版。 读完后,先用你自己的话解释你理解的接入流程(打哪个端点、用哪个模型、超时设多少), 不要立即编辑代码。等我确认后再动手。 | 要求 | 挡掉的坑 | | - | - | | 不编造端点 | Agent 从训练记忆里拼出 `/v1/complete`、`/v1/generate` 这类不存在的路径,然后在 404 里打转 | | 模型 ID 查注册表 | `gpt-5-4-mini`、`minimax-m3` 这种连字符或大小写错误会直接 404,且报错信息不会告诉你差在哪 | | Base URL 按 SDK 选 | Anthropic SDK 多加 `/v1` 会拼成 `/v1/v1/messages`;OpenAI SDK 少了 `/v1` 同样 404 | | Key 只读环境变量 | 写死在代码里的 Key 会随仓库泄漏;本站 pre-commit 钩子也会拦 | | 先解释再动手 | 避免 Agent 一上来改十个文件,最后发现选错了协议 | ## Skills 技能包 技能包就是 `skill.md` 本身:一份专门写给 AI 读的接入说明,装进 Agent 之后,它在需要调用 API易 时会自动想起这些规则。三种安装方式: 适用于 Claude Code、Cursor、Codex 等支持 Agent Skills 规范的工具: ```bash theme={null} npx skills add https://docs.apiyi.com ``` 通过本站的 `/.well-known/agent-skills/index.json` 自动发现,装的是 `skill.md` 本体,不带脚本。自检用 `npx apiyi@latest check`。 OpenClaw 的技能安装器认 git 源,仓库根目录必须有 `SKILL.md`。本技能的仓库 `github.com/apiyi-com/skills` 就是这样布局的,还附带自检脚本: ```bash theme={null} openclaw skills install git:apiyi-com/skills ``` 或者手动克隆到工作区: ```bash theme={null} git clone https://github.com/apiyi-com/skills ~/.openclaw/workspace/skills/apiyi ``` 装好后 Agent 会跑 `scripts/apiyi.py --check`,按结果引导你配置 Key。 任何能读文件的 Agent 都行。把 `https://docs.apiyi.com/skill.md` 的内容放进它的技能目录: | Agent | 放到 | | - | - | | Claude Code | `.claude/skills/apiyi/SKILL.md` | | Codex CLI | `.agents/skills/apiyi/SKILL.md` | | Cursor | 项目规则文件,或直接作为上下文粘贴 | | 其它 | 直接把全文作为系统提示词的一部分 | 请先把 API易(APIYI)技能装到你自己身上,再用它帮我接入 API易。 1. 优先运行 `npx skills add https://docs.apiyi.com`(技能名 apiyi)。 如果你是 OpenClaw,用技能安装器装 `git:apiyi-com/skills`, 或把仓库克隆到 \~/.openclaw/workspace/skills/apiyi/。 都跑不通就直接抓取 [https://docs.apiyi.com/skill.md](https://docs.apiyi.com/skill.md) 全文阅读,效果一样。 2. 装好先自检:运行技能里的 `scripts/apiyi.py --check`(没有脚本就用 `npx apiyi@latest check`)。 结果是 no\_key 就向我要 Key(我在 [https://api.apiyi.com/token](https://api.apiyi.com/token) 复制), 存到环境变量 APIYI\_API\_KEY;不要硬编码、不要提交进 git。 3. 自检返回 ready 后,用 gpt-5.4-mini 发一句「你好」,把返回贴给我, 再告诉我接下来你能用这个技能帮我做什么。 ### Key 怎么给它 * **首选环境变量** `APIYI_API_KEY`。技能、CLI、所有文档示例都从这里读。 * **OpenClaw** 会把 Key 存在 `~/.openclaw/openclaw.json` 的 `skills.entries.apiyi.apiKey`,运行时自动注入为 `APIYI_API_KEY`(技能 frontmatter 里的 `primaryEnv` 声明的就是这个)。配置文件的写法见 [OpenClaw 配置文件详解](/scenarios/agent/openclaw/config-json)。 * **CLI** 用 `npx apiyi@latest auth set-key` 存到 `~/.config/apiyi/config.json`,文件权限 0600。 ### 自检状态 技能脚本、CLI、手工 curl 三种自检方式返回同一套状态: | 状态 | 含义 | Agent 会怎么做 | | - | - | - | | `ready` | `/v1/models` 返回 200 | 告诉你能看到多少个模型,问你要做什么 | | `no_key` | 哪里都没找到 Key | 引导你去控制台复制 Key,存进环境变量后再检 | | `invalid_key` | 401 或 403 | Key 错了、被禁用或已耗尽,让你重新复制 | | `network_error` | 超时、DNS 失败或 5xx | 重试一次;仍失败则换 `b.apiyi.com` | 普通 `sk-` Key **读不到余额**,所以没有 `no_balance` 这个状态。余额和日志用的是另一套凭证——个人访问令牌(`nap_` 开头),见 [什么是个人访问令牌?](/faq/personal-access-token)。遇到 429 可能是限流也可能是余额用完,Agent 不应猜,应引导你去 [控制台](https://api.apiyi.com/account/profile) 看。 ## CLI 命令行 不写代码也能在终端里跑通第一次调用。Node 18 以上,零安装: ```bash theme={null} npx apiyi@latest check ``` `npx` 是临时运行,不会安装 `apiyi` 命令。下表的命令简写成 `apiyi …`,用 npx 时请写成 `npx apiyi@latest …`(如 `npx apiyi@latest auth set-key`);想直接用 `apiyi`,先 `npm i -g apiyi` 全局安装。详见 [终端提示找不到 apiyi 命令怎么办?](/faq/apiyi-cli-command-not-found) 请帮我安装并跑通 API易 命令行工具:[https://github.com/apiyi-com/cli](https://github.com/apiyi-com/cli) 要求:Node 18 以上;直接 `npx apiyi@latest check` 零安装; 引导我配置 API Key(`npx apiyi@latest auth set-key` 或环境变量 APIYI\_API\_KEY,从 [https://api.apiyi.com/token](https://api.apiyi.com/token) 复制); 最后跑 `npx apiyi@latest models --grep gpt-5` 和 `npx apiyi@latest chat "你好" -m gpt-5.4-mini`,把输出贴给我。 ### 命令一览 | 命令 | 需要 | 做什么 | | - | - | - | | `apiyi check` | Key 可选 | 检查 Key 来源、节点连通、`/v1/models` 是否 200,打印延迟与状态;配了个人访问令牌时顺带显示余额 | | `apiyi models [--grep 关键词]` | Key 可选 | 有 Key 走 `/v1/models` 列你能用的模型;无 Key 读公开注册表 | | `apiyi chat "提示词" [-m 模型] [--stream]` | Key | 发一条 Chat Completions,打印回复与 token 用量。默认模型 `gpt-5.4-mini` | | `apiyi responses "输入" [-m 模型] [--effort low]` | Key | 走 Responses 端点,打印 `output_text` | | `apiyi image "提示词" -m gpt-image-2 [-o 文件名]` | Key | 出图并写到本地文件,超时 360 秒 | | `apiyi balance` | 个人访问令牌 | 查余额(按 500000 配额 = 1 美元换算)。令牌勾「个人资料 · 读取」即可,用 `apiyi auth set-key --access-token` 或环境变量 `APIYI_ACCESS_TOKEN` 配置;旧系统令牌 2026-11-11 (UTC+8) 停用前仍可用 | | `apiyi auth set-key [--access-token]` / `show` / `clear` | 无 | 隐藏输入保存 Key;`show` 打码显示;`clear` 清除 | 全局参数:`--api-key`、`--access-token`、`--node api|vip|b|cf`(选节点)、`--base-url`、`--timeout`、`--json`(机器可读输出)。 ### Key 的查找顺序 `--api-key` 参数 → 环境变量 `APIYI_API_KEY` → `~/.config/apiyi/config.json` → OpenClaw 的 `~/.openclaw/openclaw.json`。装过 OpenClaw 技能的用户不用再配一次。 ### 退出码 脚本和 Agent 靠退出码分支,不用解析文字: | 码 | 含义 | | - | - | | 0 | 成功 | | 2 | `no_key` | | 3 | `invalid_key`(401 / 403) | | 4 | `network_error`(DNS、超时、重试后仍 5xx) | | 5 | 模型不存在(404,通常是 ID 写错) | | 6 | 限流或余额不足(429) | | 7 | 请求参数错误(400,原样打印 `error.message`) | | 8 | 命令行参数错误 | 源码在 `github.com/apiyi-com/cli`,npm 包名 `apiyi`。文档一律写 `npx apiyi@latest`,因为 `npx` 会缓存旧版本。 ## model-registry.json 字段说明 每次刷新价格总表时自动重新生成,与 [模型价格](/models) 页同源。顶层字段: | 字段 | 含义 | | - | - | | `schema_version` | 结构版本,当前为 1。同一版本内只增字段不改名 | | `generated_at` | 生成时间(UTC) | | `base_urls` | 三种 SDK 各自该填的 base\_url,以及 Gemini 的 `api_version` | | `nodes` | 可用节点域名 | | `endpoints` | 端点名到路径与方法的映射:`chat` / `responses` / `messages` / `gemini` / `images` / `embeddings` / `rerank` / `models` | | `groups` | 分组名到显示名与倍率 | | `models[]` | 见下表 | 每个模型条目: | 字段 | 含义 | | - | - | | `id` | 模型 ID,调用时原样使用,区分大小写 | | `vendor_en` | 厂商英文名 | | `category` | `text` / `image` / `video` / `embedding` 等能力类型 | | `endpoints` | 这个模型能走的端点名,对应顶层 `endpoints` 的键 | | `groups` | 哪些令牌分组能调用它 | | `billing.type` | `per_token`(按百万 token)或 `per_call`(按次) | | `billing.input_usd_per_m` / `output_usd_per_m` / `cache_read_usd_per_m` | 按量模型的美元标价 | | `billing.per_call_usd` | 按次模型的单次美元标价 | | `billing.tiered` | 是否有阶梯价(为 true 时详情看模型页) | | `docs_url` | 有详情页时给出地址 | 注册表里的价格是**标价**,不含充值加赠和分组折扣,实际扣费以控制台为准。分组含义见 [分组说明](/faq/groups-explained)。 ## 相关页面 两条路:让 AI 接,或自己动手接。 有,但形态是把文档交给 AI,而不是一个按钮。 开源本地 AI 助手,装上技能包后用自然语言调用 API易。 注册表的人类可读版本,含厂商分组与阶梯价。 # banana pro 改图后画面偏红怎么办? Source: https://docs.apiyi.com/faq/banana-pro-edit-red-cast banana pro 改图后画面整体偏红/偏暖的常见缓解路径:切换 Vertex 通道,或改用 gpt-image-2。 ## 简短回答 Nano Banana Pro(`gemini-3-pro-image`)改图后画面整体偏红/偏暖是改图场景下出现较多的情况。常见可尝试的缓解路径有两种: 1. 把 banana pro 切到 Vertex 通道再试 2. 改用 gpt-image-2 ## 详细说明 ### banana pro 默认走的是哪条通道? API易 上 Nano Banana Pro 默认分组**走的是官转 AI Studio 线路**,Vertex 作为独立算力池在 AIStudio 出问题时顶上(详见 [Google 系模型走 AI Studio 还是 Vertex?](/faq/google-upstream-aistudio-vertex))。 ### 怎么切到 Vertex 通道? Vertex 在 API易 控制台里以**独立分组**形式提供。在创建或编辑令牌时,从「选择分组」里挑到 Vertex 相关的分组即可,调用方式不变,**代码层无需修改**(详见 [Google 系模型走 AI Studio 还是 Vertex?](/faq/google-upstream-aistudio-vertex))。 ### 改用 gpt-image-2 是否能解决? gpt-image-2 在改图场景下整体色彩保真度通常更好,可作为备选方案评估,详见 [GPT-Image-2 图片编辑 API](/api-capabilities/gpt-image-2-all/image-edit)。 ## 排查步骤 打开 `imagen.apiyi.com` 网页端,使用相同的参考图 + 编辑指令再测一次。 * 如果网页端也偏红 → 大概率是模型在该提示词下的表现,可走 Vertex / gpt-image-2 备选 * 如果网页端不偏红 → 排查接入链路(参考 [图片与参考图差异过大排查](/faq/image-result-differs-from-reference)) banana pro 系列**不支持 OpenAI 格式上传参考图**,必须用 base64(参考 [图片与参考图差异过大排查](/faq/image-result-differs-from-reference))。如果参考图是 URL 直接放进 `image_url`,得到的图本身就是模型"猜"的,偏红也可能与参考图没真正传进去有关。 在控制台给令牌选 Vertex 分组,重跑同一指令。记录色偏程度差异。 如果 Vertex 通道仍偏色,作为兜底再试 gpt-image-2。注意出图风格会与 banana pro 有差异,需要重新调提示词。 ## 常见问题 banana pro 偏向"画风化重绘",gpt-image-2 偏向"原图微调"。如果你的诉求是尽量贴近原图色彩,gpt-image-2 通常更合适。 ## 相关文档 * [Google 系模型走 AI Studio 还是 Vertex?](/faq/google-upstream-aistudio-vertex) * [图片与参考图差异过大排查](/faq/image-result-differs-from-reference) * [Nano Banana Pro 图片编辑 API](/api-capabilities/nano-banana-image/image-edit) * [GPT-Image-2 图片编辑 API](/api-capabilities/gpt-image-2-all/image-edit) ## 联系我们 如需确认 Vertex 分组是否在控制台对你的账号可见,或需要协助排查改图色偏,请联系客服。 # Claude 拒答为什么返回空内容? Source: https://docs.apiyi.com/faq/claude-content-safety-refusal Claude 触发原厂安全策略时不报错,返回 HTTP 200、空 content 与 stop_reason: refusal,并附拒答类别。本文给出各调用格式的输出体、计费规则与识别方法。 ## 简短回答 Claude 遇到触发原厂安全策略的请求时,**不会报错**。接口照常返回 HTTP 200,但: * `content` 是**空数组** `[]`,`output_tokens` 为 `0`; * `stop_reason` 是 `refusal`; * `stop_details` 里写明拒答的**类别**(如 `cyber` 网络安全)和一段英文说明。 这是模型本身的行为,不是接口故障。代码里如果直接取 `message.content[0]`,就会抛出 `IndexError`;用 OpenAI 兼容格式调用时,拿到的是空字符串,`finish_reason` 为 `refusal`。 拒答通常在 1\~2 秒内返回。是否计费取决于拒答类别:**`cyber` 等类别在输出前被拒答时不计费**,详见下文「计费规则」。 ## 拒答的输出体 下面是同一条触发网络安全拒答的请求,在四种调用方式下的实测返回(2026-09-29 实测,id 已打码): `POST /v1/messages`,`stream: false`: ```json theme={null} { "id": "msg_xxxxxxxx", "type": "message", "role": "assistant", "model": "claude-sonnet-5", "content": [], "stop_reason": "refusal", "stop_sequence": null, "stop_details": { "type": "refusal", "category": "cyber", "explanation": "This request triggered cyber-related safeguards. To learn about the Cyber Verification Program and apply for access, visit our help center: ..." }, "usage": { "input_tokens": 2863, "output_tokens": 0, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0 } } ``` `POST /v1/messages`,`stream: true`。**没有任何 `content_block_*` 事件**,`message_start` 之后直接是带拒答信息的 `message_delta`: ```text theme={null} event: message_start data: {"type":"message_start","message":{"id":"msg_xxxxxxxx","content":[],"stop_reason":null,"stop_details":null,"usage":{"input_tokens":2863,"output_tokens":0}, ...}} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"refusal","stop_sequence":null,"stop_details":{"type":"refusal","category":"cyber","explanation":"This request triggered cyber-related safeguards. ..."}},"usage":{"input_tokens":2863,"output_tokens":0}} event: message_stop data: {"type":"message_stop"} ``` `POST /v1/chat/completions`。`content` 为空字符串,`finish_reason` 为 `refusal`,**没有拒答类别**: ```json theme={null} { "id": "msg_xxxxxxxx", "object": "chat.completion", "model": "claude-sonnet-5", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "" }, "finish_reason": "refusal" } ], "usage": { "prompt_tokens": 2863, "total_tokens": 2863 } } ``` `POST /v1/chat/completions`,`stream: true`。只有空的 `delta`,某个 chunk 的 `finish_reason` 为 `refusal`: ```text theme={null} data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]} data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"refusal"}]} data: [DONE] ``` | 字段 | 拒答时的表现 | | - | - | | HTTP 状态码 | `200`,不是错误 | | `content` | 原生格式为空数组 `[]`;OpenAI 兼容格式为空字符串 `""` | | `stop_reason` / `finish_reason` | `refusal` | | `stop_details` | 仅原生格式有:`type`、`category`(拒答类别)、`explanation`(英文说明) | | `usage` | 输入 tokens 如实计数,`output_tokens` 为 0(计数不等于计费,见下文) | | 耗时 | 通常 1\~2 秒,远快于正常回答 | 拒答也可能发生在**流式输出的中途**:先输出了一部分正文,最后以 `stop_reason: "refusal"` 结束。这时已输出的部分是不完整的,应当丢弃。 ## 拒答类别 `stop_details.category` 目前有五个取值: | 类别 | 含义 | | - | - | | `cyber` | 可能助长网络安全危害,如恶意软件、漏洞利用开发;正常的安全工作也可能触发 | | `bio` | 可能助长生物危害;正常的生命科学研究也可能触发 | | `frontier_llm` | 可能协助开发与之竞争的 AI 模型(原厂商业条款限制) | | `reasoning_extraction` | 要求模型在回答里复述自己的内部推理过程 | | `general_harms` | 不属于以上四类的其他使用政策领域 | 拒答对应不到具体类别时,`category` 和 `explanation` 都是 `null`,这是正常值。`explanation` 的文字随时可能变化,只适合展示,**不要拿来做字符串匹配**。 ## 常见触发场景 `category: "cyber"`(网络安全)是开发者最常遇到的一类。Claude 对网络安全相关请求有一层实时防护,下面这些任务都可能触发: * 让模型寻找代码漏洞、判断一段代码「是否存在漏洞」、给出漏洞类型(CWE) * 编写或补全漏洞利用代码(exploit)、渗透测试步骤 * 分析、改写恶意代码 **批量评测和数据集蒸馏最容易踩到。** 例如用一整个漏洞数据集逐条让模型判断,往往会有相当一部分样本被拒答。脚本如果默认 `content[0]` 一定存在,就会在拒答那一条上直接崩溃,看起来像「接口时好时坏」。 ## 计费规则 按原厂规则(截至 2026 年 9 月,原厂可能根据误拦率调整): | 拒答发生的时机与类别 | 计费 | | - | - | | 输出前拒答,类别为 `cyber`、`general_harms` 或 `null` | **不计费** | | 输出前拒答,类别为 `bio`、`frontier_llm`、`reasoning_extraction` | 按输入 tokens 计费 | | 流式输出中途拒答(任何类别) | 按输入 tokens 与已输出的 tokens 计费 | 无论是否计费,拒答请求都会计入速率限制。`usage` 里照常显示 token 数,这是计数,不代表一定扣费。 ## 如何识别与处理 原生格式看 `stop_reason == "refusal"`,OpenAI 兼容格式看 `finish_reason == "refusal"`。确认不是拒答之后,再去读 `content`。 拒答是一次成功的调用,不是网络错误。评测类任务建议单独记为「拒答」,连同 `stop_details.category` 一起存档,而不是计入失败重试。 同一内容原样重试,大概率还是同样的拒答,还会占用速率限制;部分类别每次都会产生输入费用。 多轮对话里某一轮被拒答后,要删掉或改写触发拒答的那一轮,或者清空历史,再继续对话。不重置的话,后续请求会持续被拒答。 按 `category` 统计,就能看出是哪类任务在触发。再决定这部分内容是否改用其他模型处理。 ```python theme={null} import os import anthropic client = anthropic.Anthropic( api_key=os.environ["APIYI_API_KEY"], base_url="https://api.apiyi.com", ) def ask(prompt, model="claude-sonnet-5"): message = client.messages.create( model=model, max_tokens=4096, messages=[{"role": "user", "content": prompt}], ) if message.stop_reason == "refusal": details = getattr(message, "stop_details", None) category = getattr(details, "category", None) if details else None return {"refused": True, "category": category, "request_id": message.id} text = "".join(b.text for b in message.content if b.type == "text") return {"refused": False, "text": text} ``` ```python theme={null} import os from openai import OpenAI client = OpenAI( api_key=os.environ["APIYI_API_KEY"], base_url="https://api.apiyi.com/v1", ) def ask(prompt, model="claude-sonnet-5"): resp = client.chat.completions.create( model=model, max_tokens=4096, messages=[{"role": "user", "content": prompt}], ) choice = resp.choices[0] if choice.finish_reason == "refusal" or not choice.message.content: return {"refused": True, "request_id": resp.id} return {"refused": False, "text": choice.message.content} ``` 需要知道拒答**类别**,请用原生 `/v1/messages` 格式调用。OpenAI 兼容格式只保留了 `finish_reason: "refusal"`,没有 `stop_details`。 ## 合规的安全研究怎么办 拒答说明里提到的 **Cyber Verification Program(网络安全验证计划)**,是原厂面向合规安全研究的免费申请计划:通过身份验证后,漏洞利用、攻防工具开发这类「高风险两用」任务可以放宽。但勒索软件开发、大规模数据窃取这类「禁止用途」,任何情况下都会被拦截。 这个计划**由组织管理员以原厂账号申请**。通过第三方平台调用时,原厂说明「并非所有平台都参与」,API易 目前不提供该计划的接入。 所以通过 API易 调用时,对被拒答的样本: * 在评测结果里如实记为「拒答」,连同类别一起统计; * 或改用其他模型处理这部分内容。 API易 不会也无法调整原厂的安全策略。 ## 与 OpenAI 拒答的区别 | | Claude | OpenAI(GPT 系列) | | - | - | - | | HTTP 状态码 | 200 | 200 | | 正文 | 空(`content: []`) | 一句拒答文字,如 `I can't help with that…` | | 结束原因 | `refusal` | `stop`,与正常回答相同 | | 拒答类别 | 有,`stop_details.category` | 无 | | 计费 | 输出前拒答的 `cyber` 等类别不计费 | 按拒答文字正常计费 | | 识别方式 | 判断 `stop_reason` 即可 | 只能看正文特征 | OpenAI 模型的拒答形态详见 [OpenAI 模型拒答长什么样?](/faq/openai-content-safety-refusal)。 ## 常见问题 看类别和时机。输出前被拒答、类别为 `cyber`、`general_harms` 或 `null` 的不计费;`bio`、`frontier_llm`、`reasoning_extraction` 按输入计费;流式中途被拒按输入加已输出部分计费。详见上文「计费规则」。 不能。拒答由原厂模型根据其安全策略决定,API易 无法关闭,也无法调整它的尺度。被拒答的内容建议记为拒答结果,或改用其他模型处理。 防护按每一条请求的内容单独判断。代码片段本身的特征、提示词的问法都会影响结果,所以同一个数据集里通常只有一部分样本被拒。实测同一条被拒的样本连续重发,结果基本稳定一致。 模型没有生成任何内容就结束了,所以输出为 0,只有输入被计数。这也是区分「拒答」和「被 max\_tokens 截断」的方法:后者 `stop_reason` 是 `max_tokens`,且输出 tokens 等于你设置的上限。 ## 相关文档 流式与非流式响应结构、stop\_reason 取值 GPT 系列拒答的形态与识别方法 平台内容安全与合规政策 # Claude 多轮对话报「思考签名无效」怎么办? Source: https://docs.apiyi.com/faq/claude-thinking-signature-invalid Claude Code、Claude Agent 智能体报 Invalid signature in thinking block 且显示 429 时,不是限流,而是会话历史里的思考块签名失效。新开会话即可解决。 ## 简短回答 报错长这样: ```text theme={null} API Error: Request rejected (429) · messages.1.content.0: Invalid `signature` in `thinking` block ``` 报错里虽然带着 429,但**这不是限流**,是**会话历史里某个思考块(thinking block)的签名没有通过原厂校验**。这个历史每次都会原样发出去,所以客户端自动重试多少次都会失败。 **解决办法:新开一个会话。** 智能体、模型、Key 的配置都不用改。 ## 典型症状 用 Claude Code、Claude Agent SDK,或者 Cherry Studio 等客户端的「Claude Agent」智能体模式,同时开着思考时: * 主对话一直出不来,界面显示「请求过于频繁」「9 秒后重试 5/10」之类的提示,重试 10 次后停止; * 控制台日志里**能看到这个 Key 有正常计费的请求**,但都是 token 很少的小请求。那是客户端用 Small 模型发的辅助请求(生成标题、判断意图等),它们不带历史,所以能成功; * 在这个会话里点「继续」、换个说法重发,都是同样的报错;新开会话后立刻恢复正常。 ## 为什么会这样 开启思考后,Claude 每轮回复开头会有一个 `thinking` 块,块里带一个 `signature`(签名)。下一轮请求时,客户端要把上一轮的 `thinking` 块**连同签名一字不改**地放回历史,原厂会校验签名,确认思考内容没有被改动过。 下面几种情况都会让签名校验失败: 客户端保存会话历史时没有存下签名,回传时 `signature` 为空字符串或整个字段缺失。社区里已有多个客户端出现过这类问题,常见于思考和文件读取等工具调用同时开启的场景。 手动编辑过历史消息、客户端对历史做了压缩或裁剪,或者经过的代理对请求体重新排版,都会让思考内容与签名对不上。 同一个会话里先用 A 模型生成了思考块,再切到 B 模型继续,B 模型可能不认 A 留下的签名。 同一个会话中途换了 Key 或令牌分组,后续请求可能由不同的线路处理,前面留下的签名可能无法通过校验。 报错信息里的 `messages.1.content.0` 指的是历史里**第一条 assistant 回复的第一个内容块**,也就是会话开头那一轮的思考块。只要它坏了,这个会话后面的每一轮都会失败。 ## 解决步骤 在客户端里新建一个会话,重新上传文件、重新提问。智能体配置、模型、Key 都不用改。这是最快、最确定的办法。 需要换模型或换 Key 时,新开会话再换,不要在已有思考历史的会话里切换。 不要编辑、删除历史中 assistant 消息的思考内容。需要精简上下文时,用客户端自带的压缩功能,或者直接新开会话。 如果新会话里没过几轮又复现,多半是客户端保存历史时丢了签名。升级到最新版本,并把复现步骤反馈给客户端的开发者。 **自己写代码调用时**:回传历史时,`thinking` 块要么**原样保留**(包括 `signature` 字段),要么**整块去掉**,不要只保留思考文字、丢掉签名。用官方 SDK 时,直接把上一轮响应的 `content` 数组原样放回 `messages` 即可。 ## 常见问题 不需要。这个报错的原因是请求内容无效,跟频率无关,退避重试、换时间段都不会好转。看到 `Invalid signature in thinking block` 就直接新开会话。 不会。签名校验在模型生成之前就失败了,这些请求不产生费用,控制台日志里也不会有记录。日志里看到的计费都来自成功的请求(通常是客户端的辅助小请求)。 在同一个会话里关掉思考不一定有用,历史里已经存在的思考块仍然会被发送。新开会话更可靠。如果任务确实不需要思考,也可以换用不带 `-thinking` 后缀的模型,从新会话开始用。 请联系客服,提供出错的大致时间(注明时区,如 `17:00 (UTC+8)`)、使用的客户端及版本、模型名。我们可以按时间定位这个会话的请求,协助排查。 ## 相关文档 思考模式的开关、流式事件与签名回传 `thinking` 块与 `signature_delta` 的结构 在 Claude Code 中配置 API易 并发与限流说明 # 图片编辑报 Unable to process input image 怎么办? Source: https://docs.apiyi.com/faq/gemini-empty-inlinedata Nano Banana 图片编辑返回 400「Unable to process input image」或「empty inlineData parameter」,原因是请求里 inlineData 的图片数据为空,与图片 URL 是否可访问无关;改用 URL 传图时报 Cannot fetch content 则是谷歌拉图超时。逐项排查 base64 转码步骤并在发送前加一道检查即可。 ## 症状 调用 Nano Banana 系列(`gemini-3.1-flash-image-preview`、`gemini-3-pro-image-preview`、`gemini-nano-banana-2.1` 等)做**带参考图的图片编辑**时,接口返回 HTTP 400,报错是下面两句之一: ```json theme={null} { "error": { "message": "Unable to process input image. Please retry or report in https://developers.generativeai.google/guide/troubleshooting", "code": 400 } } ``` ```text theme={null} Unable to submit request because it has an empty inlineData parameter. Add a value to the parameter and try again. ``` 通常是一段时间内连续报错、换个时间又正常,看起来像是接口不稳定。 ## 原因 **请求里 `inlineData.data` 是空的,原厂没有拿到图片内容。** 两句报错是同一个原因,只是不同节点的措辞不同:一句直接说「inlineData 参数为空」,另一句说「无法处理输入图片」,后者容易让人误以为是图片格式损坏或图片链接打不开。 **和图片 URL、图床是否公网可读无关。** 用 `inlineData` 发送时,图片是以 base64 的形式放在请求体里的,原厂不会去访问任何链接。报这个错说明 base64 字符串本身是空的,问题出在你的程序把图片转成 base64 的那一步。 一个可以自查的旁证:在控制台日志里看这几次请求,失败的请求输入 token 很少(只有提示词的量),而正常带图的请求输入 token 会多出几百到几千。 ## 逐项排查 很多程序是先从对象存储(COS / OSS / S3)把图片下载下来,再转成 base64。如果下载失败、超时,或者异步步骤还没完成就发出了请求,拿到的就是空字符串。先在发送前把 base64 的长度打印出来确认。 JavaScript 的 `JSON.stringify` 会**直接丢掉值为 `undefined` 的字段**。如果 `imageBase64` 是 `undefined`,序列化后的请求里 `inlineData` 只剩 `mimeType`,`data` 字段整个消失,原厂同样会报 inlineData 为空。这种情况在代码里看不出来,必须打印实际发出的请求体才能发现。 `inlineData.data` 只放**纯 base64**。如果把 `data:image/png;base64,iVBORw0...` 整串放进去,要先去掉逗号及之前的前缀。 `mimeType` 写 `image/png`、`image/jpeg` 或 `image/webp`,要与图片的真实格式一致。文件扩展名不可靠,比如有些手机拍的 `.jpg` 实际是多帧格式,最稳妥的做法是转码时统一重新编码成 PNG 或 JPEG。 ## 发送前加一道检查 在组装请求之前加几行校验,问题会在本地直接暴露,不用等接口报错: ```javascript theme={null} const b64 = (imageBase64 || '').replace(/^data:[^;]+;base64,/, ''); if (!b64) { throw new Error('参考图 base64 为空,请检查下载 / 转码步骤'); } const payload = { contents: [{ parts: [ { text: prompt }, { inlineData: { data: b64, mimeType: imageMimeType } } ] }], generationConfig: { responseModalities: ['IMAGE'], imageConfig: { imageSize: '2K' } } }; ``` ```python theme={null} import base64, re def to_inline_data(image_bytes: bytes, mime_type: str) -> dict: if not image_bytes: raise ValueError("参考图内容为空,请检查下载 / 读取步骤") b64 = base64.b64encode(image_bytes).decode() return {"inlineData": {"data": b64, "mimeType": mime_type}} def strip_data_url(s: str) -> str: b64 = re.sub(r"^data:[^;]+;base64,", "", s or "") if not b64: raise ValueError("参考图 base64 为空") return b64 ``` 这类 400 是请求本身的问题,**原样重试不会成功**。修好之后不需要做任何平台侧的配置。 ## 想直接传图片 URL 怎么办 Gemini 原生端点也支持直接传图片链接,写法是 `fileData`,**不是** `inlineData`: ```json theme={null} { "fileData": { "fileUri": "https://your-bucket.example.com/ref.png", "mimeType": "image/png" } } ``` 用 URL 传图时,图片要由谷歌的服务器去下载。如果图床只走国内 CDN(例如腾讯云对象存储的默认配置),谷歌经常拉不到,表现通常是**生成结果不参考这张图**,而不是报错。条件允许的话,**更推荐 base64 方式**,稳定性最高。完整说明见 [Nano Banana 开发指南](/api-capabilities/nano-banana-dev-guide)。 ### URL 传图报 Cannot fetch content from the provided URL 用 `fileData.fileUri` 传图时,还可能遇到另一种 400,这次是会直接报错的: ```json theme={null} { "error": { "message": "Cannot fetch content from the provided URL. Please ensure the URL is valid and accessible. Status: URL_REJECTED-REJECTED_FC_TIMEOUT", "code": 400 } } ``` **这是谷歌的服务器下载你的图片超时了**,和 `inlineData` 为空不是一回事。谷歌拉取 URL 的时间上限约 60 秒,图床响应谷歌服务器慢时就会撞上,所以典型特征是**每次失败都恰好在 60 秒左右返回**。 **图片能在浏览器里秒开,不代表谷歌拉得动。** 我们用海外对象存储(AWS S3 美西)上一张约 500KB 的 PNG 实测:同一个链接在本地 1 秒内下载完成,但交给谷歌拉取时,8 次里有 5 次在约 60 秒时报上面这个错,成功的那几次也要 60–77 秒;**同一张图改成 base64 内联发送,次次成功,约 18 秒出图**。换渠道结果一样,说明问题出在谷歌与该图床之间,与 API易 的渠道无关。 解决办法: 1. **改用 `inlineData` 发送 base64**(推荐):先自己把图片下载下来再转 base64,不再依赖谷歌去拉图,这个错误会彻底消失。记得按上文加一道「base64 不为空」的检查。 2. 必须用 URL 时,尽量降低谷歌拉取的耗时:上传时把对象的 `Content-Type` 设成真实格式(如 `image/png`,而不是 S3 默认的 `binary/octet-stream`),并优先给出 CloudFront 等全球 CDN 的地址。这能缓解,但不能保证不超时。 同一个 URL 第二次请求往往会变快,因为谷歌会缓存拉过的图片。所以「我重试一下就好了」只说明这张图被缓存了,换一张新图还会再超时,不能当作问题已解决。 ## 常见问题 用 `inlineData` 发送时原厂不会访问任何链接,图片是否公网可读不影响结果。请检查程序把图片转成 base64 的环节,确认发出去的 `data` 字段不是空的。 不会。请求在原厂入口就被拒绝了,不会生成图片,也不会产生费用;URL 拉图超时的 400 同样不扣费。具体以控制台调用日志为准。 ## 相关文档 输入图片要求、URL 传图与超时设置 各类报错与 finishReason 的处理方式 图床链接带处理参数导致的格式不符 参考图压缩不影响输出画质 # Gemini 出图返回 blockReason: OTHER 怎么办? Source: https://docs.apiyi.com/faq/gemini-image-input-blocked Gemini 图片模型几秒内返回 promptFeedback.blockReason 且没有 candidates 时,如何定位被拦截的参考图,以及人工与代码两类场景下的参考图预处理建议。 ## 简短回答 如果 Gemini 图片接口返回 HTTP 200,但响应里**没有 `candidates`**,只有 `promptFeedback.blockReason`(最常见的是 `OTHER`),说明请求在**开始生成之前**就被原厂的输入检查拦下了。 * 这类拦截通常几秒内就返回,比正常出图快得多 * `OTHER` 不会告诉你具体原因,`safetyRatings` 往往也是空的 * 它**不一定和提示词写法有关**,很多时候是某一张参考图触发的 * gemini-3-pro-image(Nano Banana Pro)的输入检查比 gemini-3.1-flash-image 更严格,同一个请求可能 Pro 被拦、flash 能出 处理思路是:**先找到是哪一张图,再对参考图做统一的预处理**。 ## 怎么识别 典型响应如下: ```json theme={null} { "promptFeedback": { "blockReason": "OTHER", "safetyRatings": [] }, "usageMetadata": { "promptTokenCount": 1919, "candidatesTokenCount": 0 }, "modelVersion": "gemini-3-pro-image", "responseId": "..." } ``` | 特征 | blockReason 拦截 | NO\_IMAGE | | - | - | - | | 失败信号所在字段 | `promptFeedback.blockReason` | `candidates[0].finishReason` | | `candidates` | 没有 | 有,但 `parts` 为 `null` | | 返回速度 | 几秒内 | 与正常出图相近 | | 常见原因 | 输入内容(多为参考图)被拦截 | 提示词意图不明确 | | 处理方向 | 定位并预处理参考图 | 改提示词,见 [NO\_IMAGE 排查](/faq/gemini-no-image) | ## 一个实测案例 2026-09 (UTC+8) 我们复测了一个服装目录出图请求:1 段提示词 + 6 张参考图(姿势、人物、场景、穿搭、鞋袜拼图、帽子),2:3、2K、`responseModalities: ["IMAGE"]`。 * gemini-3-pro-image 连续 3 次返回 `blockReason: OTHER`;同一请求用 gemini-3.1-flash-image 能正常出图 * 用对半拆分的方法逐步缩小范围,最终定位到**唯一的触发源是那张人物参考图**(一张 AI 生成的人物四宫格设定图,包含正面、背面、侧面和面部特写) * 这张图配任何提示词都会被拦,包括与任务无关的「把背景换成浅灰色」 * 事前最被怀疑的「带水印的真人姿势参考图」和「带商标的帽子图」,单独测试都能通过 最关键的发现是: | 人物参考图的处理方式 | 结果 | | - | - | | 原图 | 拦截 13/13 | | 像素完全相同,只换文件格式(如另存为 PNG) | 拦截 2/2 | | 重新导出为 JPEG(质量 95,肉眼看不出差别) | 通过 6/6 | | 用重新导出的图替换后,发送完整的 6 图请求 | 出图 2/2,人物、姿势、穿搭都正确 | 也就是说,这类检查对某些图片的原始像素非常敏感,重新导出一次就能正常通过。原厂不公开 `OTHER` 的具体判断依据,我们无法进一步归因。 这个案例说明:一次请求放多张参考图、把多个步骤合并成一次生成,本身都没有问题。遇到 `OTHER` 时,不要急着改提示词或拆分流程,先确认是哪一张图。 ## 如何定位是哪一张图 原样重发 2\~3 次。`blockReason` 拦截通常是稳定复现的;如果时好时坏,更可能是 [NO\_IMAGE](/faq/gemini-no-image) 那类问题。 保留全部图片,把提示词换成一句与任务无关的简单指令(例如「把背景换成浅灰色」)。如果仍被拦截,问题在图片上。 把参考图分成两半分别发送,只在仍被拦截的那一半里继续拆分,直到定位到单张图。6 张图最多拆 3 轮。 按下文「处理建议」重新导出该图后,再发送完整请求验证。 定位时出一次图就可以判定这一组「能通过」,不必重复;连续 2 次被拦截即可判定「会被拦截」。这样整个定位过程通常只需要十几次调用。 ## 处理建议 ### 场景一:人工操作(在画布或工具里手动出图) 1. **参考图先重新导出一次再上传**:用任意修图工具(系统自带的预览、Photoshop 等)导出为 JPEG,质量 90\~95,长边不超过 2048px。 2. **大图先缩小**:长边 3000\~4000px 的原图缩到 2048px 以内,不影响出图效果,上传也更快。 3. **几秒就失败时先怀疑参考图**:优先把最近新加入的那张图重新导出后再试;仍不行就按上面的方法逐张排查。 4. **人物参考图优先用全身图**:在上面的案例中,人物四宫格拆开后,单独的正面全身图可以通过。如果某张人物设定图反复被拦截,可以先只用其中的全身图。 5. **临时替代**:某张图确实无法通过 Pro 时,可以先用 gemini-3.1-flash-image 完成这一步。 ### 场景二:代码(工程化自动处理) **1. 所有参考图发送前统一预处理**,不必针对某一张图单独处理:转为 sRGB → 按 EXIF 方向摆正 → 长边限制在 2048px → 重新编码为 JPEG(质量 90\~95)→ 去掉元数据。 这样做首先能显著减小请求体积、加快上传(上面的案例原始请求体约 4.6MB);同时也能减少这类 `OTHER` 拦截。Node.js 示例: ```javascript theme={null} import sharp from "sharp"; async function normalizeReference(buffer) { return sharp(buffer, { failOn: "none" }) .rotate() // 按 EXIF 方向摆正 .toColorspace("srgb") .resize({ width: 2048, height: 2048, fit: "inside", withoutEnlargement: true }) .jpeg({ quality: 92, mozjpeg: true }) .toBuffer(); // 默认不保留元数据 } // parts.push({ inlineData: { mimeType: "image/jpeg", data: (await normalizeReference(buf)).toString("base64") } }); ``` Python 可以用 Pillow 实现同样的流程: ```python theme={null} from io import BytesIO from PIL import Image, ImageOps def normalize_reference(raw: bytes) -> bytes: im = ImageOps.exif_transpose(Image.open(BytesIO(raw))).convert("RGB") im.thumbnail((2048, 2048)) out = BytesIO() im.save(out, "JPEG", quality=92) return out.getvalue() ``` **2. 按失败类型分别处理**: | 返回情况 | 含义 | 建议处理 | | - | - | - | | `promptFeedback.blockReason` 为 `OTHER`,几秒内返回 | 输入被原厂检查拦截,原因未公开 | 换一组编码参数(如质量 88、长边再缩小 1%)自动重发 1 次;仍失败则改用 flash,或提示用户更换参考图 | | `blockReason` 为 `SAFETY` / `PROHIBITED_CONTENT`,或 `finishReason` 为 `IMAGE_SAFETY` 等 | 明确的内容安全拦截 | **不要重试**,直接提示用户修改素材或描述 | | `finishReason` 为 `NO_IMAGE`,输出 token 为 0 | 提示词出图意图不明确 | 在提示词末尾追加「只输出最终图片,不要输出文字」后重发,见 [NO\_IMAGE 排查](/faq/gemini-no-image) | 自动重试只适用于原因不明的 `OTHER`。对于明确的安全类原因,重新编码图片并不能、也不应该用来改变结果,请直接提示用户调整内容。 **3. 记录排查信息**:每次失败都记下 `responseId`,以及每张参考图的哈希值和尺寸。出现问题时能快速找到是哪张图,也方便联系我们时提供。 ## 常见问题 两个模型的输入检查策略不同,Pro 更严格。同一张参考图在 flash 上通过、在 Pro 上被拦截是正常现象,并不代表请求本身有问题。 会有这种情况。上面案例里被拦截的就是一张用自己拍的照片重新生成的人物设定图。是否被拦截与图片来源没有简单对应关系,按本页的方法定位并预处理即可。 在上面的案例中,6 张图本身不是问题:替换掉那一张人物图后,完整的 6 图请求可以正常出图。 请以 API易 控制台的调用日志为准,确认该请求是否产生消费记录。 ## 仍然无法解决?联系我们 请提供以下信息,方便我们协助排查: * 模型名称和令牌分组; * 完整响应(至少包含 `promptFeedback` 和 `responseId`)与 `request ID`; * 问题发生时间(请注明时区); * 已经定位到的参考图(如方便提供)。 请勿发送完整 API Key。提交截图或日志前,请将密钥内容打码。 企业微信客服二维码 扫码添加,或点击本卡片直接联系客服。 **客服邮箱**:[support@apiyi.com](mailto:support@apiyi.com) 邮件标题建议包含「blockReason + 模型名称」。 ## 相关文档 提示词意图不明确导致的未出图与修复方法 内容安全、去水印、知名 IP 和未成年人等常见原因 完整的响应判断顺序与 C 端友好提示方案 通过调用日志确认请求是否成功和是否产生消费 # Gemini 出图返回 IMAGE_OTHER 怎么办? Source: https://docs.apiyi.com/faq/gemini-image-other Gemini 图片模型返回 finishReason: IMAGE_OTHER 时,图已经画完但在输出前被原厂过滤。本文解释它与 NO_IMAGE、blockReason 的区别,并给出实测定位方法与修复建议。 ## 简短回答 如果 Gemini 图片接口返回 HTTP 200,`candidates[0].finishReason` 是 `IMAGE_OTHER`,并且 `finishMessage` 写着 `Unable to show the generated image`,说明**模型已经生成了图片,但这张图在返回之前被原厂的输出检查拦下了**。 * 它是**概率性**的:同一个请求有时成功、有时失败,失败率可能很高 * 它**和负向提示词、参数、网关都无关**,问题出在「画出来的内容」上 * 我们实测到的最典型诱因是:**提示词里点名了真实人物** * 原厂在 `finishMessage` 里注明这类请求不计费 处理思路是:**找出提示词里让画面「像某个具体真人或受保护内容」的那部分,把它换成描述。** ## 怎么识别 典型响应如下: ```json theme={null} { "candidates": [ { "finishReason": "IMAGE_OTHER", "finishMessage": "Unable to show the generated image. The model could not generate the image based on the prompt provided. You will not be charged for this request. Try rephrasing the prompt. ..." } ], "usageMetadata": { "promptTokenCount": 1430, "candidatesTokenCount": 274, "thoughtsTokenCount": 274 }, "modelVersion": "gemini-3-pro-image", "responseId": "..." } ``` 注意 `candidatesTokenCount` 不是 0,而是等于 `thoughtsTokenCount`:模型完成了思考,只是最终的图片没有交付。 三种「没出图」的形态对比: | 特征 | IMAGE\_OTHER | NO\_IMAGE | blockReason: OTHER | | - | - | - | - | | 失败信号所在字段 | `candidates[0].finishReason` | `candidates[0].finishReason` | `promptFeedback.blockReason` | | 发生在哪一步 | 图已生成,**输出时被过滤** | 模型没有去画图 | 生成前,**输入被拦截** | | `finishMessage` | `Unable to show the generated image` | 通常没有 | 没有 `candidates` | | 输出 token | 只有思考 token | 0 或只有文字 | 0 | | 是否稳定复现 | 概率性 | 概率性 | 通常稳定复现 | | 常见原因 | 画面像具体真人等受限内容 | 提示词在要求输出文字 | 某张参考图被拦截 | | 处理方向 | 改提示词里的人物/受限描述 | 见 [NO\_IMAGE 排查](/faq/gemini-no-image) | 见 [blockReason: OTHER 排查](/faq/gemini-image-input-blocked) | 官方 API 文档中,`IMAGE_OTHER` 与 `IMAGE_SAFETY`、`IMAGE_PROHIBITED_CONTENT`、`IMAGE_RECITATION` 同属「图片生成被停止」的一组原因,`IMAGE_OTHER` 表示原因不属于其余几类,原厂不会公开具体判断依据。 ## 一个实测案例 2026-09 (UTC+8) 一位客户反馈:一个纯文生图的球员卡请求,在 gemini-3-pro-image 上**约 63% 的调用不出图**。提示词约 5500 字符,结构如下: * 开头一句点名一位真实的运动员,要求「1:1 复刻其面部特征」 * 姿势、构图、队服配色、画风、白底背景等详细描述 * 两大段负向提示词(NEGATIVE),列了大量品牌名 我们复现到的失败全部是 `IMAGE_OTHER`,约 20 秒返回。随后每组 6 次、逐项删减: | 改动 | 不出图 | | - | - | | 原提示词 | **5/6** | | **只删掉点名真人的那一句**,其余逐字保留 | **0/6** | | 保留人名,只删掉「1:1 复刻」这类措辞 | 4/6 | | 删掉两段负向提示词 | 3/6 | 结论很清楚:**触发源是真人姓名本身**。模型认得这个名字、就会往这个人的样子画,画得越像越容易在输出时被过滤;画得不太像的那几次就能通过,所以表现为「时好时坏」。「1:1 复刻」的措辞和冗长的负向提示词都不是原因。 删掉名字后,原提示词里已有的发色、脸型、眼型等外貌描述足以画出同样风格的球员卡。 二分定位时每组 6 次就够用:原请求失败率 5/6 时,删对了的那一组连续 6 次全部成功,碰巧的概率约为十万分之二。整个定位只用了 24 次调用。 ## 如何定位 原样重发 5\~6 次,确认失败时 `finishReason` 是 `IMAGE_OTHER`,并记下失败率。如果是 `NO_IMAGE` 或 `blockReason`,请看对应的排查页。 优先检查提示词里是否出现了**真实人物的姓名**(明星、运动员、网红、政要等),或要求「和某人一模一样」。删掉后重发一组。 如果不是人名,把提示词按段落对半删减,每组 6 次,只在失败率明显下降的那一半里继续缩小范围。 定位到诱因后,用外貌、服装、风格等**描述**替换掉「指名道姓」,保留你真正需要的画面要求。 ## 处理建议 1. **提示词里不要写真人姓名**:改成具体的外貌描述,例如「金色直发、偏分、杏仁眼、鹅蛋脸」。这是本案例中唯一有效的修复。 2. **负向提示词可以精简,但它不是这个问题的原因**:Gemini 图片模型没有独立的负向提示词参数,大段 NEGATIVE 列表只会被当作普通文字理解,精简后提示词更清晰,但不会降低 `IMAGE_OTHER` 的概率。 3. **不要把重试当成方案**:失败率在 60% 以上时,重试只是在碰运气,还会增加整体耗时。客户端可以在收到 `IMAGE_OTHER` 时重试 1 次兜底,但治本要改提示词。 4. **批量生成场景提前改模板**:如果是按名单批量生成(例如每位球员一张卡),不要把名单里的姓名拼进提示词,姓名只用于你自己的文件命名或后期排版。 5. **需要人物形象一致时用参考图**:如果确实需要画出某个具体的人,请提供本人授权的参考图,并注意 [Nano Banana 出图失败](/faq/nano-banana-image-failure) 里列出的真人、未成年人等限制。 代码里可以这样区分三种形态: ```python theme={null} def classify_no_image(resp: dict) -> str: if resp.get("promptFeedback", {}).get("blockReason"): return "input_blocked" # 见 blockReason: OTHER 排查 cand = (resp.get("candidates") or [{}])[0] reason = cand.get("finishReason") if reason == "IMAGE_OTHER": return "output_filtered" # 改提示词里的人名/受限描述 if reason == "NO_IMAGE": return "no_image_intent" # 见 NO_IMAGE 排查 if reason in ("IMAGE_SAFETY", "IMAGE_PROHIBITED_CONTENT"): return "safety" # 明确的安全拦截,不要重试 return "ok" if any("inlineData" in p for p in (cand.get("content") or {}).get("parts") or []) else "unknown" ``` ## 常见问题 会。在我们的实测中,删掉「1:1 复刻」这类措辞、只保留人名,失败率仍有 4/6。模型认得这个名字,就会往这个人的样子画。 过滤发生在图片生成之后,取决于这一次画出来的结果。每次生成都不一样,所以同一个请求会时而通过、时而被过滤。 不同分组背后的输出检查严格程度可能不同,同一提示词在不同分组上的失败率会有差异。但分组的路由会随时调整,这种差异并不稳定,从提示词上修复才是可靠的做法。 Gemini 图片模型没有单独的负向提示词参数,写在提示词里的 NEGATIVE 列表会被当作普通文字。它对 `IMAGE_OTHER` 没有影响,但过长的列表可能稀释主要描述,建议只保留最关键的几项,并改成正向表述(例如「纯白背景」而不是列一长串「不要体育场、不要草地……」)。 原厂在 `finishMessage` 中注明这类请求不计费。实际是否产生消费,请以 API易 控制台的调用日志为准。 ## 仍然无法解决?联系我们 请提供以下信息,方便我们协助排查: * 模型名称和令牌分组; * 完整响应(至少包含 `finishReason`、`finishMessage` 和 `responseId`)与 `request ID`; * 问题发生时间(请注明时区); * 脱敏后的提示词,以及你统计到的失败率。 请勿发送完整 API Key。提交截图或日志前,请将密钥内容打码。 企业微信客服二维码 扫码添加,或点击本卡片直接联系客服。 **客服邮箱**:[support@apiyi.com](mailto:support@apiyi.com) 邮件标题建议包含「IMAGE\_OTHER + 模型名称」。 ## 相关文档 提示词意图不明确导致的未出图与修复方法 输入图在生成前被拦截时的定位方法与参考图预处理建议 内容安全、去水印、知名 IP 和未成年人等常见原因 完整的响应判断顺序与 C 端友好提示方案 # 为什么 Gemini 图片接口返回 NO_IMAGE? Source: https://docs.apiyi.com/faq/gemini-no-image 解释 Gemini 图片接口返回 NO_IMAGE 的常见原因,并提供提示词优化和故障排查方法。 ## 简短回答 当接口返回 `finishReason: NO_IMAGE` 且 `parts` 为 `null` 时,通常表示模型处理了请求,但没有返回图片内容。 这不一定代表提示词触发了内容安全审核。对于“什么是 GEO”“请介绍一下某个概念”这类更像文本问答的提示词,模型可能无法确认用户是否明确要求生成图片,因此直接返回 `NO_IMAGE`。 建议在提示词开头明确说明要生成什么图片,并补充画面主体、布局、风格和输出要求。 另一类高频原因是**提示词在要求模型写文字**,比如「生成设计方案」「请确保分析内容深入」。在只允许输出图片的调用里,这类请求会以一定概率返回 `NO_IMAGE`,同一个请求时好时坏。见下文「提示词在要求模型输出文字」。 如果响应里没有 `candidates`,而是出现了 `promptFeedback.blockReason`(例如 `OTHER`),那是另一类问题:输入内容在生成前就被原厂拦截了。请看 [Gemini 出图返回 blockReason: OTHER 怎么办?](/faq/gemini-image-input-blocked) ## 为什么会返回 NO\_IMAGE? ### 1. 提示词更像文本问答 例如: ```text theme={null} 什么是 GEO? GEO 就是让企业在大模型 AI 中获得排名…… ``` 这段内容主要是在解释 GEO 的概念,没有明确说明: * 要生成什么类型的图片; * 画面中应该出现哪些元素; * 信息应该如何排版; * 是否只需要图片,不需要文字解释。 即使请求中包含“生成图片”几个字,模型仍可能将整体请求理解为文本说明或知识问答。 ### 2. 图片生成意图不够明确 某些平台工具会自动在用户输入前添加“生成图片:”等提示词。但通过 API 调用时,平台通常只是透明转发请求,不一定会自动补充完整的图像生成意图。 因此,不建议只写: ```text theme={null} 生成图片:什么是 GEO? ``` 而应直接说明图片类型和视觉要求: ```text theme={null} 生成一张中文科技风信息图海报,主题是“什么是 GEO”。 ``` ### 3. 输入内容缺少视觉描述 如果提示词只有概念解释,模型不知道应该把内容转换成什么画面。建议补充以下信息: * 图片类型:信息图、海报、流程图或宣传图; * 画面结构:三栏布局、时间轴或中心辐射结构; * 视觉风格:科技风、商务风、简约风或品牌风; * 文字要求:标题、编号、正文和排版层级; * 输出要求:仅生成图片,不要返回文字解释。 ### 4. 提示词在要求模型输出文字 Gemini 图片模型同时具备看图和写文字的能力。如果提示词里有要求文字产出的句子,模型会以一定概率「先写方案」而不是直接出图: ```text theme={null} ……根据图1的鞋底款式,设计一款马拉松跑鞋…… 针对鞋款生成专业、精准且可操作性强的设计方案。 请确保分析内容深入,判断准确,且设计与配色方案可直接应用于设计开发。 ``` 这类句子适合写给文本模型。写给出图模型时,一旦请求设置了只输出图片(`responseModalities: ["IMAGE"]`),模型想写的方案无法输出,响应就变成 `finishReason: NO_IMAGE`、`parts: null`、输出 token 为 0。 我们对一个真实的鞋款设计请求(3 张参考图 + 上面这类提示词)做过复测(2026-09 (UTC+8),gemini-3.1-flash-image,`responseModalities: ["IMAGE"]`): | 提示词写法 | 返回 NO\_IMAGE 的次数 | | - | - | | 原提示词 | 10/48(约 21%,不同时段在 1/12 \~ 5/12 之间波动) | | 删掉「生成设计方案」「分析内容深入」等句子 | 0/36 | | 原文不动,末尾追加一句「只输出最终图片」 | 0/24 | | 删掉提示词里的品牌名 | 1/12(品牌名不是诱因) | 所以同一个请求会「连续失败几次又成功」,而渠道本身没有任何变化。最简单的修法是在提示词末尾加一句: ```text theme={null} 【重要】本次只需直接输出最终效果图一张,不要输出任何文字方案、分析或提示词。 ``` 参考图里有清晰可读的文字或第三方标识(如鞋底上的品牌字样)时,模型更容易进入「先分析素材」的状态。我们在上述测试中把这类字样模糊处理后,同一请求也是 0/36。 ## responseModalities 不同,表现也不同 同一个「意图不明确」的请求,在不同的 `responseModalities` 设置下表现不一样: | 设置 | 没出图时的表现 | | - | - | | `["IMAGE"]` | `finishReason: NO_IMAGE`,`parts: null`,输出 token 为 0 | | 不传,或 `["TEXT", "IMAGE"]` | `finishReason: STOP`,但 `parts` 里只有一段文字、没有图片 | **排查技巧**:把出问题的请求去掉 `responseModalities` 再发几次,看没出图时模型写的是什么: * 写的是一篇方案、分析,或者一段出图提示词 → 意图不明确,按本页的方法改提示词 * 写的是「无法生成」这类拒绝说明 → 触发了内容审核,参考 [Nano Banana 系列出图失败](/faq/nano-banana-image-failure) ## GEO 提示词示例 可以将原始提示词改写为: ```text theme={null} 生成一张中文科技风信息图海报,主题是“什么是 GEO”。 画面包含一个主标题和三个编号说明模块: 1. 让企业在大模型 AI 的搜索和推荐中获得更高曝光; 2. 让企业成为用户问题的答案; 3. 建立 AI 对企业信息的信任和推荐。 设计要求: - 使用蓝紫色科技风; - 采用清晰的三栏布局; - 突出“排名”“答案”“信任推荐”三个关键词; - 使用简洁、易读的中文排版; - 适合作为企业宣传海报; - 仅生成图片,不要返回文字解释。 ``` “生成图片”本身通常只是一个动作提示,不能完全替代对画面内容的描述。越明确说明图片类型、主体、布局和视觉风格,模型越容易判断这是一个图片生成请求。 ## 如何排查 NO\_IMAGE? `candidates[0].finishReason` 为 `NO_IMAGE`,属于本页讨论的情况。如果没有 `candidates`,而 `promptFeedback.blockReason` 有值,说明输入在生成前就被拦截了,请看 [blockReason: OTHER 排查](/faq/gemini-image-input-blocked)。 检查响应中的 `parts`、`inlineData`、`image` 或等效图片字段。如果 `parts` 为 `null`,通常表示本次响应没有返回图片内容。 确认提示词中包含“生成一张图片”“制作一张海报”或“create an image”等明确指令,不要只提交“什么是……”或“请解释……”这类文本问题。同时检查有没有「生成方案」「深入分析」「给出说明」这类要求文字产出的句子,有的话删掉,或在末尾追加「只输出图片」。 如果已经明确要求生成图片,但仍然返回 `NO_IMAGE`,再检查是否涉及 NSFW、未成年人、知名 IP、去水印、真实人物肖像或其他上游安全策略。 检查调用日志中的完整响应、模型名称、request ID 和消费记录。`usageMetadata` 表示模型处理过请求,但不能单独证明图片已经生成,也不能单独判断是否触发了安全拦截。 ## NO\_IMAGE 和内容安全拦截有什么区别? | 现象 | 可能原因 | 建议处理方式 | | - | - | - | | `NO_IMAGE` 且 `parts` 为 `null`,提示词偏抽象 | 图片生成意图不明确 | 补充图片类型、画面主体和视觉要求 | | 返回安全策略相关错误 | 触发上游内容审核 | 修改或删除可能触发审核的内容 | | `NO_IMAGE` 时有时无,同一请求重试后又成功 | 提示词在要求文字产出,模型以一定概率先写方案 | 删掉索要方案/分析的句子,或末尾追加「只输出最终图片」 | | 没有 `candidates`,`promptFeedback.blockReason` 为 `OTHER` | 输入内容在生成前被原厂拦截 | 见 [blockReason: OTHER 排查](/faq/gemini-image-input-blocked) | | 已明确图片意图仍然无法出图 | 可能是模型、分组、令牌或上游通道问题 | 联系客服,并提供完整错误信息、模型名称、request ID 和调用时间 | `finishReason: NO_IMAGE` 只能说明本次没有返回图片,不能仅凭这个字段断定一定是内容违规。需要结合完整错误消息、提示词内容和调用日志一起判断。 ## 常见问题 不一定。“生成图片”只能表达基本意图,建议同时说明图片类型、主体、构图、风格和输出要求。对于抽象概念,最好明确要求生成信息图、海报或流程图。 从 GEO 的概念本身来看,没有明显的内容安全风险。但 `NO_IMAGE` 并不能完全排除上游策略影响,仍需要结合完整响应和调用日志判断。就当前案例而言,提示词更像知识解释,图片生成意图不够具体是更值得优先排查的方向。 多数情况下渠道没有变化。意图不明确导致的 `NO_IMAGE` 本身是概率性的:模型每次都在「出图」和「先写文字」之间做选择。在我们的实测中,同一请求的失败率约 21%,而且会连续出现。建议客户端在收到 `NO_IMAGE` 时自动重试 1\~2 次,同时从提示词上根治。 `usageMetadata` 只能说明模型处理了输入并产生了推理或文本 token,不代表响应一定包含图片。是否生成图片,应以响应中是否存在图片数据为准。 不能只根据 `NO_IMAGE` 判断是否扣费。请以 API易 控制台的调用日志为准,确认该请求是否产生消费记录。 ## 仍然无法解决?联系我们 如果明确补充了图片生成意图后仍然返回 `NO_IMAGE`,请联系 API易 客服,并提供: * 模型名称和令牌分组; * 完整错误消息和 `request ID`; * 脱敏后的提示词; * 问题发生时间; * 调用日志中的消费记录。 请勿发送完整 API Key。提交截图或日志前,请将密钥内容打码。 企业微信客服二维码 扫码添加,或点击本卡片直接联系客服。 **客服邮箱**:[support@apiyi.com](mailto:support@apiyi.com) 邮件标题建议包含「NO\_IMAGE + 模型名称」。 ## 相关文档 输入图在生成前被拦截时的定位方法与参考图预处理建议 查看内容安全、去水印、知名 IP 和未成年人等常见原因 查看 401、429、503、504、超时和分组问题的通用排查流程 通过调用日志确认请求是否成功和是否产生消费 # 用 Codex 接 GPT-Image 出图,报密钥认证失败怎么办? Source: https://docs.apiyi.com/faq/gpt-image-incorrect-api-key-openai Codex 接 gpt-image-2.5 出图报 Incorrect API key:错来自 OpenAI,请求没到 API易。三条解法:装 Skills、丢提示词、网页出图。 ## 典型报错 ```text theme={null} 秘钥认证失败 Incorrect API key provided: sk-xxxx****************************A6Af. You can find your API key at https://platform.openai.com/account/api-keys. (请求 ID:req_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) ``` **一句话结论**:这条错是 **OpenAI 官方服务器**返回的,不是 API易 返回的。你的代码目前请求的是 `api.openai.com`,API易 的 Key 自然会被 OpenAI 拒绝。**Key 没坏,是请求地址没改。** ## 怎么判断请求没到 API易 两个特征,看到任意一个就能确定: | 特征 | 说明 | | - | - | | 报错里让你去 `platform.openai.com/account/api-keys` 找 Key | 这是 OpenAI 的标准 `invalid_api_key` 文案,API易 的报错不会引导你去 OpenAI 官网 | | 请求 ID 是 `req_` 开头的 32 位串 | OpenAI 的请求 ID 格式。API易 的日志里查不到这条记录,因为请求从未到达 | 这个问题在「让 AI 编程助手写接入代码」时格外常见:Codex、Cursor、Claude Code 等看到 `gpt-image-2.5` 这个模型名,默认会按 OpenAI 官方 SDK 的写法生成代码,`base_url` 用的是 SDK 的默认值 `https://api.openai.com/v1`。你填进去的 Key 是 API易 的,两边对不上。 ## 三条解法,按你的情况选 最省事的做法是让 Agent 先「学会」API易 再写代码,有两个层级的技能包可选: 让你的 Agent 运行下面这条命令安装 API易 技能包;跑不通就让它直接读 `https://docs.apiyi.com/skill.md`: ```bash theme={null} npx skills add https://docs.apiyi.com ``` 这份文件专门写给 AI 看:Base URL、认证方式、模型命名规则、常见坑一应俱全。装完再让它写代码,`base_url` 就会自动指向 `https://api.apiyi.com/v1`。 [GPT-Image-2.5 / 2 系列 Agent 技能](/api-capabilities/gpt-image-2/skills) 页提供一个开箱即用的 Skill:两个文件、一个脚本,覆盖 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` / `gpt-image-2` 等六个模型,用 `--model` 切换,文生图、多图融合、局部重绘都能做。 放进 Codex、OpenClaw、Claude Code 等任意能执行命令行的编码 Agent,对它说一句「帮我出一张……」即可,不需要你自己碰 Base URL。 其它出图 / 视频模型也各有一页「Agent 技能」,都挂在对应模型的文档目录下。文档站左侧导航里找到模型,看有没有名为「Agent 技能」的子页即可。 不想理解 Skill 是什么,也可以直接把我们写好的**接入提示词**复制给 Codex、Claude Code、Cursor 等 AI: 1. 打开 [GPT-Image-2.5 / 2 系列总览](/api-capabilities/gpt-image-2/overview) 2. 找到「让 AI Agent 帮你接入」一节,点提示词右上角的复制按钮 3. 原样粘贴给你的 AI 编程助手 这段提示词里已经写死了 `base_url` 用 `https://api.apiyi.com/v1`、Key 从环境变量 `APIYI_API_KEY` 读,还预先挡掉了超时、base64 渲染、上传压缩、质量参数几个高频坑。AI 会先抓文档页的纯文本版(任意文档页地址后加 `.md`),再按你项目的技术栈写代码。 每个出图 / 视频模型的总览页都有这样一段提示词,不止 GPT-Image。更通用的三条路(聊天 Agent、命令行、编程 Agent)见 [AI 开发者套件](/developer-kit)。 只是想出图、暂时不需要接进自己的程序,可以完全绕开代码: 1. 在 API易 后台「令牌」页复制一个 Key 2. 打开 `imagen.apiyi.com`,填入这个 Key 3. 选择 `gpt-image-2.5-flare`(文生图)或 `gpt-image-2.5-sunburst`(改图)直接出图 网页端用的就是同一个 Key、同一套接口,费用也从同一个账户余额扣。等后续要接进程序,再回到前两条路。 ## 自己改代码:只改一行 如果你已经有一段 Codex 生成的代码,最小改动是给客户端加上 `base_url`,其它一律不动: ```python Python theme={null} from openai import OpenAI client = OpenAI( api_key="sk-your-apiyi-key", base_url="https://api.apiyi.com/v1", # ← 加上这一行 ) result = client.images.generate( model="gpt-image-2.5-flare", prompt="一只戴着宇航员头盔的柴犬,赛博朋克风格", size="1024x1024", quality="medium", ) ``` ```javascript Node.js theme={null} import OpenAI from "openai"; const client = new OpenAI({ apiKey: "sk-your-apiyi-key", baseURL: "https://api.apiyi.com/v1", // ← 加上这一行 }); const result = await client.images.generate({ model: "gpt-image-2.5-flare", prompt: "一只戴着宇航员头盔的柴犬,赛博朋克风格", size: "1024x1024", quality: "medium", }); ``` ```bash 环境变量 theme={null} # 不改代码,用环境变量覆盖 SDK 默认地址 export OPENAI_API_KEY="sk-your-apiyi-key" export OPENAI_BASE_URL="https://api.apiyi.com/v1" ``` 改完用这条命令验证请求确实到了 API易,能返回模型列表就对了: ```bash theme={null} curl https://api.apiyi.com/v1/models \ -H "Authorization: Bearer sk-your-apiyi-key" ``` ## 常见追问 按顺序排查: 1. **多处配置**:Codex 生成的项目常常同时有 `.env`、配置文件、代码初始化三处地方,只改了一处,另一处仍是默认值 2. **环境变量优先**:`OPENAI_BASE_URL` 若在系统里已经设成了别的值,会覆盖代码里没写的那一项;用 `echo $OPENAI_BASE_URL` 看一眼 3. **改完没重启**:进程仍在跑旧配置 4. **拼写**:`apiyi`,不是 `apiyii` 或 `apiyl` 最简单的自证方法:看报错。只要还出现 `platform.openai.com`,请求就还在打 OpenAI。 把报错原文连同这一页一起发给它。每个文档页右上角都有「复制页面」按钮,把页面内容 + 报错一起贴给 AI,它就能对照着定位是哪一处配置没生效。这是最快的排错路径。 那才轮到查 Key 本身:登录后台「令牌」页确认该 Key 状态为「启用」、余额充足、没有限制模型白名单。完整排查见 [为什么提示 API Key 无效?](/faq/invalid-api-key)。 文生图默认 `gpt-image-2.5-flare`,改图 / 局部重绘用 `gpt-image-2.5-sunburst`,两款同价同参数。走量要便宜可以用官逆 `gpt-image-2.5-all`。六个模型的取舍见 [GPT-Image 系列 Agent 技能](/api-capabilities/gpt-image-2/skills) 页的对比表。 ## 相关文档 Base URL 与 Key 一一对应的完整原理与各语言示例。 OpenAI 加 /v1、Claude 填根域名、Gemini 加 /v1beta。 把文档交给 AI 编程助手,让它替你完成对接。 参数、价格、接入提示词与常见报错。 # GPT Image 输出 token 为什么这么高? Source: https://docs.apiyi.com/faq/gpt-image-output-token-calculation 解释 GPT Image 的输入与输出 token 如何区分,以及分辨率、质量、宽高比和生成数量为什么会显著影响图片费用。 ## 简短回答 这是正常现象。GPT Image 的 4K + `high` 本身就是高成本组合,即使只输出一张图片,也可能产生大量 `output_tokens`。 图片输出 token 不是按“最终文件只有一张”或“总像素数简单等比例”计算,而是主要由以下因素共同决定: 1. `quality`:`low`、`medium`、`high` 或 `auto` 2. 输出尺寸与宽高比 3. 生成数量 `n` 4. 模型内部对画布的划分和图像复杂度 `usage.output_tokens` 标注的是**模型生成输出图片所消耗的图像 token**,不是参考图输入。参考图消耗会单独记录在 `usage.input_tokens_details.image_tokens` 中。 ## 为什么只输出一张图也会有大量 token “一张图片”只表示结果数量,不表示生成工作量小。模型需要在内部图像表示空间中生成整张画布,画质越高、分辨率越大,需要计算和输出的图像 token 通常越多。 例如以下两次调用都只返回一张图,但成本可能相差数倍: ```json theme={null} { "size": "1024x1024", "quality": "low", "n": 1 } ``` ```json theme={null} { "size": "3840x2160", "quality": "high", "n": 1 } ``` 第二个请求仍然只生成一张图,但使用了 4K 横版画布和高质量档位,输出 token 明显更高是预期行为。 ## 四个主要影响因素 ### 1. 质量参数 `quality` `quality` 通常是最明显的成本变量: | 参数 | 特点 | 输出 token 趋势 | | - | - | - | | `low` | 快速预览、草稿 | 最低 | | `medium` | 质量与成本平衡 | 中等 | | `high` | 精细纹理、文字和复杂细节 | 最高 | | `auto` | 模型自行选择档位 | 每次可能不同 | 如果使用 `quality: "auto"` 或不传 `quality`,模型可能根据提示词自动选择不同档位。即使尺寸和参考图完全相同,不同请求的 `output_tokens` 也可能相差数倍。需要稳定预算时,请显式指定 `low`、`medium` 或 `high`。 项目中已有真实记录:三次请求的输入都为 1061 token,但输出分别为 1286、5146 和 1287 token。中间一次因自动选择了更高画质,费用约为其他两次的 3.5 倍。 ### 2. 输出尺寸 分辨率越高,通常需要覆盖的内部画布越大,因此输出 token 越多。4K `high` 会比 1K `low` 贵很多。 但不能直接使用以下公式精确预测: ```text theme={null} 输出 token = 宽 × 高 × 固定系数 ``` 像素数量只能用于粗略估算。真正的 `output_tokens` 由模型在生成时返回,应以响应中的 `usage.output_tokens` 为准。 ### 3. 宽高比与内部画布划分 输出 token 还受内部画布如何分块、缩放和覆盖影响。因此,token 与最终像素总数并不总是严格单调对应。 同一质量下,更大的非正方形图片有时可能比更小或更接近正方形的图片消耗更少的输出 token。这并不矛盾,而是因为模型内部使用的是离散画布或分块规则,不是简单按最终像素逐个计费。 比较不同尺寸时,应该同时看 `quality` 和宽高比,不能只看“4K”“2K”标签或像素总量。 ### 4. 生成数量 `n` 通用规则下,生成数量越多,总输出 token 越高;生成 N 张图片相当于承担 N 份输出成本。 不过,当前 `gpt-image-2` 仅支持 `n=1`。需要多张图片时,应发起多次独立请求,每次都单独计算输入和输出 token。其他图片模型是否支持 `n>1`,以对应模型文档为准。 ## 参考图多会影响哪个 token 参考图数量主要增加的是**输入图片 token**,不是输出图片 token: | 字段 | 代表什么 | | - | - | | `usage.input_tokens_details.text_tokens` | 提示词文本输入 | | `usage.input_tokens_details.image_tokens` | 参考图输入 | | `usage.output_tokens_details.image_tokens` | 生成结果图片输出 | | `usage.output_tokens` | 本次所有图片输出 token | `gpt-image-2` 对参考图采用高保真处理。参考图张数会近似线性增加输入 image token,但最终输出图的 `output_tokens` 仍主要由输出质量、尺寸、宽高比和模型内部生成过程决定。 因此,如果日志明确显示高消耗位于“输出图片”,就不能归因于参考图数量。参考图费用应在输入图片 token 中单独看到。 ## 如何计算真实费用 以 `gpt-image-2` 当前计费口径为例: ```text theme={null} 总费用 = 文本输入 token × 文本输入单价 + 参考图输入 token × 图片输入单价 + 输出图片 token × 图片输出单价 ``` 响应示例: ```json theme={null} { "usage": { "input_tokens": 1040, "input_tokens_details": { "text_tokens": 16, "image_tokens": 1024 }, "output_tokens": 5146, "output_tokens_details": { "text_tokens": 0, "image_tokens": 5146 }, "total_tokens": 6186 } } ``` 这表示: * 16 token 来自提示词 * 1024 token 来自参考图 * 5146 token 来自最终生成图片 * 输出只有一张,但该图片采用的质量与画布需要 5146 个输出 image token 2K 和 4K 没有适用于所有尺寸与内容的固定每张 token 数。预算表只能用于预估,最终计费应以每次接口响应或控制台日志中的实际 `usage` 为准。 ## 如何降低 token 消耗 不使用 `auto`,明确传入 `low`、`medium` 或 `high`,避免模型自动升档导致费用波动。 预览或内部审核先用 1K / 2K,最终交付时再生成 4K `high`。 使用业务真正需要的画布,不要为了“看起来更高清”盲目放大尺寸。 多张候选图会线性增加总输出成本。先小批量验证提示词,再进行批量生成。 持久化每次请求的 `size`、`quality`、`output_tokens` 和费用,按实际数据建立自己的成本基线。 ## 相关文档 * [GPT-Image-2 概览与计费说明](/api-capabilities/gpt-image-2/overview) * [GPT-Image-2 文生图 API](/api-capabilities/gpt-image-2/text-to-image) * [GPT-Image-2 图片编辑 API](/api-capabilities/gpt-image-2/image-edit) * [如何查看调用记录?](/faq/call-logs) # 图片生成有异步接口吗?支持任务 ID 查询结果吗? Source: https://docs.apiyi.com/faq/image-async-api API易 图片生成均为同步调用,不提供任务 ID 异步查询接口,本文说明原因和推荐做法 ## 简短回答 目前 API易 **不提供图片生成的异步任务 ID 查询接口**,所有图片模型均为**同步调用**:请求建立长连接 → 等待生成 → 直接返回图片结果。 我们是**原厂透传**模式,且**不记录用户业务数据**,因此无法提供"断线后凭 ID 取回结果"的能力。建议在客户端设置合理 timeout、保持长连接,并在自己的后台记录请求与回执。 **简单理解**:同步调用 + 合理 timeout + 客户端记录任务,等价于自己实现了一个轻量异步队列,本质区别不大。 ## 为什么没有异步任务 ID 查询? 我们对图片接口保持与上游官方完全一致的同步行为,不在中间增加任务队列层,避免引入额外的不一致和延迟 出于用户隐私和数据安全考虑,我们**不记录任何业务内容**(包括 prompt、生成图片),自然也无法事后凭 ID 取回 将 timeout 设到合理范围、保持长连接,绝大多数图片生成请求都能在一次调用内稳定返回 ## 推荐做法 把 HTTP 客户端的 timeout 设到模型生成时间的安全上限(通常 60–300 秒,视模型而定),并启用 keep-alive,避免中间网络层提前断开。 不同模型的耗时差异较大,可联系客服获取**按模型分类的 timeout 建议表**。 由于我方不保存业务数据,请在你自己的服务端为每次请求生成业务 ID,落库存储 prompt、参数、返回结果或错误信息。这样即使前端断线,后端仍持有完整记录。 如果业务必须异步(前端不能长等),可以在你的后端做一层"异步外壳": * 前端 POST 任务 → 后端入队 → 返回业务 ID * 后端 worker 用同步方式调用 API易 → 写回数据库 * 前端凭业务 ID 轮询 / 用 WebSocket 推送 这种模式与"平台原生异步"在体验上几乎等价,且数据全部留在你自己可控范围内。 ## 客户侧异步包装(示例思路) ```python theme={null} # 伪代码:在你自己的后端实现异步外壳 def submit_image_task(prompt): task_id = uuid4() db.save(task_id, status="pending", prompt=prompt) queue.push({"task_id": task_id, "prompt": prompt}) return task_id def worker(job): try: # 同步调用 API易,timeout 设到该模型的安全上限 result = apiyi_client.images.generate( prompt=job["prompt"], timeout=180, ) db.update(job["task_id"], status="done", url=result.url) except TimeoutError: db.update(job["task_id"], status="failed", error="timeout") def query_image_task(task_id): return db.get(task_id) # 前端凭 task_id 轮询自己的后端 ``` **关键点**:业务 ID 是**你自己生成的**,存在**你自己的数据库**里。API易 只负责"同步生成"这一步。 ## 常见问题 多数超时是**客户端 timeout 设置过短**或**中间网络层(如反向代理、网关)提前断开长连接**导致。 排查顺序: 1. 确认 HTTP 客户端的 read timeout 已调大到 60–300 秒 2. 确认 nginx / API 网关 / CDN 等中间层的超时也已调高 3. 启用 keep-alive,避免被中间层强制断连 4. 联系客服获取该模型的推荐 timeout 值 很遗憾不能。我们是原厂透传,不持久化生成结果。如果同步调用因 timeout 中断,**该次结果会丢失**,需要客户端重试。 建议把 timeout 一次性调到该模型的安全上限,避免"接近成功但被自己掐断"的情况。 我们了解部分上游平台速度较慢、异步确实更友好,**未来有可能引入异步能力**,但目前没有时间表,不做承诺。在此之前请按"客户侧实现异步包装"的思路自行处理。 **是的,视频生成接口本身就是异步任务**(由上游官方设计),返回 task\_id,需要客户端轮询任务状态拿到最终视频。这与图片接口的同步模式不同,请按对应模型文档处理。 不同模型生成耗时差异较大(快的几秒、慢的几十秒甚至 3–5 分钟)。我们整理了**按模型分档的 timeout 速查表**,见 [图片 API 调用须知与最佳实践](/api-capabilities/image-api-best-practices);有特殊场景也可联系客服进一步咨询。 ## 相关文档 各模型 timeout 速查表、base64 处理与 URL 输出对照 在同步接口之上自建任务队列的工程实践 了解各图片模型的能力与适用场景 并发上限、限流策略与最佳实践 了解我们的数据记录策略与日志控制 获取 timeout 建议表或进一步咨询 # 上传图片报 image content does not match MIME type 怎么解决? Source: https://docs.apiyi.com/faq/image-mime-type-mismatch-with-query 图片 URL 末尾带 x-oss-process 等 CDN 处理参数时会触发该报错,去掉参数或用原始直链即可 ## 简要回答 上传给 API 的图片 URL 末尾带了 `?x-oss-process=...` 这类云存储处理参数,会触发 `image content does not match MIME type` 报错。 解决办法是去掉 URL 末尾的处理参数、使用原始直链传给 API,或者先把图在自己服务端处理好再上传。 ## 这个报错是什么样? 报错内容如下: ```json theme={null} { "error": { "message": "decode image: image content does not match MIME type 'image/png'", "type": "invalid_request_error", "code": 429 } } ``` 常见的触发 URL 形如: ```text theme={null} https://oss.fzputi.com/tools/aiCraft/...jpg?x-oss-process=image/resize,w_800 ``` 这种 URL 末尾的 `?x-oss-process=...` 是阿里云 OSS 等对象存储的图片处理参数;目前已知带上这类参数后,图片生成类接口会报上述错误。 ## 已知受影响的参数名 以下参数名出现在图片 URL 末尾时,**已被反馈会触发该报错**: * `x-oss-process` 若你使用其他云存储的图片处理参数(例如腾讯云 COS、华为云 OBS 等的等效参数),同样建议按以下步骤处理后再传入 API。 ## 解决步骤 复制图片的原始直链(不带 `?x-oss-process=` 这类参数),重新传入 API。 例如把: ```text theme={null} https://oss.fzputi.com/abc.jpg?x-oss-process=image/resize,w_800 ``` 改成: ```text theme={null} https://oss.fzputi.com/abc.jpg ``` 如果你的业务确实需要 OSS 端做压缩或裁剪,先在服务端把处理后的图片下载到本地,再用本地路径或新的直链(不带参数)传给 API。 ## 注意事项 传进 API 的图片 URL 应尽量保持「直链、零参数」,避免触发该报错。 ## 仍然无法解决时 * 确认你传入的 URL 在浏览器中可以直接打开,且浏览器右键 → 查看图片时显示的真实格式与 URL 后缀(jpg / png / webp)一致 * 如果使用其他云存储的图片处理参数,请联系技术支持附上具体报错 JSON 与请求 ID ## 相关文档 * [图片生成有异步接口吗?支持任务 ID 查询结果吗?](/faq/image-async-api) * [接入模型后生成的图片和参考图相差很大怎么办?](/faq/image-result-differs-from-reference) # 接入模型后生成的图片和参考图相差很大怎么办? Source: https://docs.apiyi.com/faq/image-result-differs-from-reference Gemini-3-Pro-Image 和香蕉系列模型参考图上传需要使用 base64 格式,不支持 OpenAI 格式,常见排查方法见正文。 ## 简短回答 如果使用 Gemini-3-Pro-Image、香蕉 Pro 或香蕉 2 模型生成图片,结果和参考图差异很大,通常是参考图上传方式不匹配导致的: * 这些模型**不支持 OpenAI 格式上传参考图** * 必须使用 **base64 格式**上传参考图 * 这个问题和接入方式有关,与模型本身无关 ## 排查步骤 按以下顺序逐项排查,可以快速定位问题: 打开 `imagen.apiyi.com` 网页端,使用相同的参考图测试一次。 如果网页端生成的图片和参考图一致,说明模型本身工作正常,问题出在你的接入方式;如果网页端也存在差异,再进一步排查模型相关问题。 登录 API易 控制台,确认你的令牌已正确勾选 `gemini-3-pro-image` 或香蕉系列模型所在的分组。 模型未启用时,调用会退回到默认行为,导致结果与预期不一致。 在令牌设置中检查计费模式: * 选择**按量优先**:可使用按量和包月两种额度 * 选择**仅按量**:仅使用按量额度,包月额度不可用 计费模式与模型分组不匹配时,可能导致调用失败或结果异常。 按目标模型的官方接入文档重新核对参数和参考图上传方式。 以香蕉 2 模型为例: `docs.apiyi.com/api-capabilities/nano-banana-2-image/image-edit` 重点关注: * 请求体中参考图字段是 **base64 字符串**,不是 URL * MIME 类型需要与图片实际格式一致 * 多张参考图时,参数结构是否匹配当前模型要求 ## 常见原因总结 | 现象 | 可能原因 | | - | - | | 网页端正常,API 调用差异大 | 接入方式不匹配(最常见) | | 所有调用结果都不一致 | 参考图未上传或上传失败 | | 偶发性差异 | 提示词描述不充分或参考图过多 | | 模型分组可见但调用失败 | 计费模式不匹配 | **最常见的原因**:把图片 URL 直接放到 OpenAI 兼容格式的 `image_url` 字段里。对于不支持 OpenAI 格式的模型,参考图必须先转成 base64 字符串,再放到对应字段中。 ## 相关问题 Nano Banana 模型的常见问题与排查。 图片异步任务接口的使用方式。 在各客户端中接入 API易 的方法。 配置令牌可用的模型分组。 # 怎么生成透明背景的图片(PNG 抠图)? Source: https://docs.apiyi.com/faq/image-transparent-background 透明底图请用官转 gpt-image-2.5-flare / gpt-image-2.5-sunburst,传 background: transparent 直接输出带 alpha 通道的图,png 与 webp 都支持。gpt-image-2 的透明背景已于 2026-09-30 被 OpenAI 官方关停。本页汇总各图像模型的透明背景支持情况、最小示例与常见报错。 ## 简短回答 **用官转 `gpt-image-2.5-flare` 或 `gpt-image-2.5-sunburst`,请求里加两个字段就行**: ```json theme={null} { "model": "gpt-image-2.5-flare", "prompt": "a cute cartoon fox sticker, clean cutout edges, no shadow", "background": "transparent", "output_format": "png" } ``` 返回的图就是带 alpha 通道的透明底 PNG,不需要任何后处理抠图。文生图、图片编辑两条路都支持。 **`gpt-image-2` 已不支持透明背景。** OpenAI 于 2026-09-30 关停了 `gpt-image-2` 的 `background: "transparent"`(此前是 2026-08-21 开放的 preview),并要求改用 `gpt-image-2.5-flare` 或 `gpt-image-2.5-sunburst`,这两款原生支持透明背景。现在给 `gpt-image-2` 传透明会被 OpenAI 官方直接拒绝,返回 400 `Transparent background is not supported for this model.`(不计费)。2026-10-15 本站复测:文生图 14 次、图片编辑 7 次全部被拒。**只需把 `model` 换成 2.5 两款之一,其它参数不用改**,三款同价同参数。 ## 哪些模型能出透明背景 | 模型 | 支持方式 | 稳定性 | | - | - | - | | `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` | **参数控制** —— `background: "transparent"` + `output_format` 为 `png` 或 `webp` | ✅ 稳定,推荐(前提是提示词别描述场景,见下文) | | `gpt-image-2` | 2026-09-30 起 OpenAI 官方关停,传 `background: "transparent"` 返回 400 | ❌ 不再支持,换 2.5 两款 | | `gpt-image-1.5` / `gpt-image-1` | 参数控制,同上 | ✅ 稳定(老模型,新项目建议直接用 `gpt-image-2.5-flare`) | | `gpt-image-2-all` / `gpt-image-2-vip` | **没有 `background` 参数**,只能在提示词里要求透明背景 | ⚠️ 偶现不稳定,同一条提示词可能出白底 | | `seedream-5-0` / `seedream-5-0-pro` | `output_format: "png"` + 提示词里写 `transparent background, alpha channel` | ⚠️ 靠提示词,不保证每次都有 alpha | | Gemini 系出图(Nano Banana 系列) | 只能在提示词里要求 | ⚠️ 同上 | | `seedream-4-5` / `seedream-4-0` | 仅 `jpeg` 输出,没有 alpha 通道 | ❌ 不支持 | **需要稳定拿到透明底就用 `gpt-image-2.5-flare`(文生图)或 `gpt-image-2.5-sunburst`(改图)。** 参数控制和提示词要求是两回事:前者由接口保证,后者是"让模型尽量照做",批量跑的时候差别很明显。 ## 两种调用方式 ### 文生图 `/v1/images/generations` ```bash theme={null} curl https://api.apiyi.com/v1/images/generations \ -H "Authorization: Bearer $APIYI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2.5-flare", "prompt": "a cute cartoon fox sticker, flat vector illustration, single centered subject, clean cutout edges, no background, no shadow", "background": "transparent", "output_format": "png", "quality": "low" }' ``` ### 图片编辑 `/v1/images/edits` 给一张普通照片,让模型去掉背景: ```bash theme={null} curl https://api.apiyi.com/v1/images/edits \ -H "Authorization: Bearer $APIYI_API_KEY" \ -F model=gpt-image-2.5-sunburst \ -F image=@fox.jpg \ -F 'prompt=remove the background completely and keep only the fox, clean cutout edges, no shadow' \ -F background=transparent \ -F output_format=png ``` 蒙版局部重绘(`mask`)与透明背景可以同时用,互不冲突。 ## 为什么 `jpeg` 不行 JPEG 格式本身**没有 alpha 通道**,装不下透明信息。传 `output_format: "jpeg"` 加 `background: "transparent"` 会直接报 400: ```json theme={null} { "error": { "message": "Transparent background is not supported for JPEG output format", "param": "background", "code": "invalid_value" } } ``` 想要透明就选 `png`(无损,体积大)或 `webp`(有损可调,体积小,同样支持 alpha)。`webp` 还能配合 `output_compression` 压体积。 ## 编辑接口是「重绘去背」,不是精确抠像 这一点要提前对齐预期:`/v1/images/edits` 传 `background: transparent` 时,模型是**理解画面之后重新画一遍主体**,而不是像 Photoshop 那样沿着原图轮廓把主体切下来。所以: * 主体的**姿态、风格、细节会有变化**,不是原图像素级保留 * 想尽量贴近原图,用 `quality: "high"`,并在提示词里明确「保持原有构图 / 不要改变主体外观」 * 如果业务要求像素级还原,建议自己用 `rembg`、`PIL`、`sharp` 这类工具做抠图,模型出图更适合"生成可复用素材"这类场景 ## 提示词别描述场景,参数压不过场景描述 `background: "transparent"` 只保证输出带 alpha 通道、并让模型倾向去背;**提示词或参考图一旦在描述一个完整场景,模型会按语义把场景画出来**,透明参数拦不住。这是「透明背景时好时坏」最常见的原因,与画质档位无关。 2026-09-11 用 `gpt-image-2.5-sunburst` 实测 30 次(medium / high 各半,文生图 + 参考图编辑): | 提示词写法 | 真透明 | | - | - | | 只描述主体(苹果、耳机、挥刀的武士),写明 isolated / no background | 16 / 16,medium 与 high 一样稳 | | 写了场景:「秋天枫叶林里挥刀的武士」 | 0 / 3,**整张画满秋林**,medium 和 high 都一样 | | 同一场景改写:几片枫叶飘在武士身边 + `isolated character on a transparent background, no scenery, no ground` | 2 / 2 | | 参考图自带全景,提示词只描述场景、不提去背 | 1 / 2,抛硬币 | | 参考图自带全景,提示词写明 `remove the scenery entirely, keep only the subject` | 8 / 8 | 写法上记三条: * ❌ 别写地面、天空、房间、森林这类**环境词**;要氛围就把元素挂在主体上(「几片飘落的枫叶」而不是「枫叶林」) * ✅ 句尾固定加 `isolated subject on a transparent background, no scenery, no ground, no shadow` * ✅ 参考图带背景时,编辑提示词里明确 `remove the background entirely, keep only the character` 把 `quality` 从 `medium` 提到 `high` **不会**让透明更稳,只会多 4 倍 token(439 → 1,756)。 ## 计费 **透明背景不额外收费。** 相同画质档与尺寸下,`background: "transparent"` 与 `background: "opaque"` 消耗的 image token 完全一致,按所用模型的正常按量计费规则走(2.5 两款与 `gpt-image-2` 同价)。 ## 常见报错 你用的是 `gpt-image-2`。它的透明背景已于 2026-09-30 被 OpenAI 官方关停,这个 400 是 OpenAI 直接返回的,不计费。把 `model` 换成 `gpt-image-2.5-flare`(文生图)或 `gpt-image-2.5-sunburst`(改图)即可,其它参数不用动。 `output_format` 传了 `jpeg`。改成 `png` 或 `webp` 即可。 先确认三件事:一是 `background` 字段真的传上去了(编辑接口是 `multipart/form-data`,字段要用 `-F background=transparent` 而不是塞进 JSON);二是响应顶层的 `background` 回显是不是 `transparent`;三是你用的是不是 `gpt-image-2.5-flare` / `gpt-image-2.5-sunburst` —— `gpt-image-2-all` 和 `gpt-image-2-vip` 没有这个参数,传了会被忽略;`gpt-image-2` 则会直接报 400。 三件都对还是白底或整张背景,几乎一定是**提示词(或参考图)描述了场景**:模型按语义把环境画出来了,参数压不过。见上文「提示词别描述场景」。 用 Python 一行就能验: ```python theme={null} from PIL import Image im = Image.open("out.png") print(im.mode) # RGBA 才有 alpha a = im.convert("RGBA").getchannel("A") print(a.histogram()[0] / (im.width * im.height)) # alpha=0 的像素占比 ``` 模式是 `RGB` 说明没有 alpha 通道;`RGBA` 但 alpha 全是 255,说明有通道但没镂空。 提示词只是"请求模型这么画",模型可能画一个看起来像透明的灰白棋盘格,那仍然是不透明像素。真正的 alpha 通道只有 `background: "transparent"` 参数能保证。反过来也一样:参数保证的是通道,压不过提示词里的场景描述,两边要配合。 ## 相关文档 完整参数、尺寸、画质档与错误码 `/v1/images/generations` 全部字段 `/v1/images/edits` 与多图融合 用 alpha 蒙版标记要改的区域 `gpt-image-2.5-flare` / `sunburst` / `gpt-image-2` / `-all` / `-vip` 怎么选 纯白背景的另一类问题 # Nano Banana 系列出图失败,常见原因有哪些? Source: https://docs.apiyi.com/faq/nano-banana-image-failure Nano Banana Pro/2 出图失败的常见原因分析,包括内容安全、去水印、知名IP、未成年人等触发谷歌安全机制的场景 ## 简要说明 Nano Banana 系列(包括 Nano Banana Pro 和 Nano Banana 2)底层使用谷歌 Gemini 模型。出图失败的主要原因是**触发了谷歌的内容安全机制**,谷歌会在生成阶段直接拦截不合规的请求。API易作为透明代理,忠实转发谷歌的反馈结果。 ## 常见触发原因 包含色情、暴力、血腥、仇恨言论等不适当内容的提示词或参考图,会被谷歌安全机制直接拦截。 要求去除图片上的水印属于违反内容政策的操作,谷歌会拒绝处理此类请求。 涉及迪士尼、漫威、任天堂等知名版权角色的生成请求,会因版权保护被拒绝。 任何涉及未成年人的不当内容生成请求,谷歌执行零容忍政策,严格拦截。 ### Nano Banana 2 新增限制 自 2026 年 2 月 27 日 Nano Banana 2 上线后,谷歌进一步收紧了安全机制,以下场景也会触发拦截: 涉及公众人物(明星、政治人物等)的图像生成或编辑请求会被拒绝。 试图修改图片中的金融信息、订单截图、价格标签等内容会被拦截。 对人物进行换装、换脸等操作涉及隐私和伦理问题,会被安全机制拒绝。 即使没有直接的色情描述,暗示性的、擦边的不当内容也会被识别并拦截。 ## 政策更新时间线 | 时间 | 事件 | 影响 | | - | - | - | | 2026年1月23日 | 谷歌调整风控政策 | 整体安全审核更加严格,部分原本可通过的提示词被拦截 | | 2026年2月27日 | Nano Banana 2 上线 | 新增知名人物、金融信息修改、换装换脸、隐性 NSFW 等拦截规则 | ## 出图失败的典型表现 当出图失败时,API 返回的 HTTP 状态码仍然是 **200**,但响应内容中不包含图片数据,而是返回文字说明。 **为什么状态码是 200?** API易作为透明代理,忠实转发谷歌 API 的原始响应。谷歌在安全拦截时返回的就是 200 状态码 + 文本拒绝说明,而非 HTTP 错误码。 ### 常见的报错文案示例 谷歌 API 返回的拒绝文本通常包含以下内容: * `"我不能完成 xxx 的修改"` * `"我不能为你创建带有色情、不雅或冒犯性内容的图像"` * `"I can't generate images that are sexually explicit."` * `"I'm just a language model and can't help with that."` 注意:谷歌的安全过滤存在一定的随机性。**同一个提示词有时能生成、有时不能**,这与参考图内容、提示词组合方式等因素有关。 ## C 端产品开发者建议 如果你正在基于 Nano Banana 系列 API 开发面向用户的产品,建议做好错误处理逻辑,为用户提供友好的失败提示。 **核心判断指标**: 1. **candidatesTokenCount = 0**:谷歌在内容审核阶段直接拒绝 2. **finishReason 不是 STOP**:生成过程中被安全策略拦截 3. **有文本但无图片**:API 返回了拒绝说明而非图片数据 ## 联系支持 如果你的使用场景是正常合规的,但仍然遇到出图失败问题,欢迎联系我们排查: * 企业微信客服:[点击联系](https://work.weixin.qq.com/kfid/kfc9adfd5810ece25ec) * 邮箱:[hi@apiyi.com](mailto:hi@apiyi.com) # Nano Banana Pro 服装换装时印花变形如何优化? Source: https://docs.apiyi.com/faq/nano-banana-pro-print-distortion 服装换装场景下提示词优化、参考图权重设置、多轮抽卡与跨通道兜底,附正反提示词对比与排查步骤 ## 简短回答 Nano Banana Pro 印花变形不是模型坏了,而是**单次生成本身有随机性 + 提示词越界**两个原因叠加。实用上按以下顺序处理: 1. 先在 [imagen.apiyi.com](https://imagen.apiyi.com) 用同一提示词 + 参考图自测一次,排除客户端问题 2. 改写提示词:去掉"严格锁定""逐像素还原"等绝对化表述,改用具体颜色 / 位置 / 保留项描述 3. 上传清晰、特征明确的参考图,**使用 Base64 编码**(Nano Banana 系列不支持 OpenAI 格式 URL 直传) 4. 启用多轮抽卡:建议业务代码对同一提示词做 1\~3 次自动重试 5. 还不行就换通道或换模型:banana pro 默认走 AI Studio,可切 [Vertex 分组](/faq/google-upstream-aistudio-vertex);或试 [gpt-image-2 系列](/api-capabilities/gpt-image-2-all/image-edit)(改图风格更贴近原图) ## 为什么印花会变形 AI 生图是**单次原子采样**,每次调用都是一次独立抽样,没有"严格还原"这一档开关。高频踩坑的原因有两类: * **提示词表述越界**:用"严格锁定不变""逐像素还原""1:1 还原"这类绝对化措辞,模型反而会理解成"重画一遍",把印花一起重画 * **参考图传错了方式**:Nano Banana 系列**只支持 Base64 编码上传**,把 URL 直接塞进 OpenAI 兼容格式的 `image_url` 字段会让模型"看不见"参考图,印花自然变形 即使你描述得再详细,AI 仍然有约 5%\~15% 的概率在单次采样里"走偏"。这不是模型不行,而是**生成式模型的固有特性**——同提示词多次调用,结果天然不同。 ## 具体排查步骤 打开 [imagen.apiyi.com/#generate](https://imagen.apiyi.com/#generate),用**完全相同的提示词 + 参考图**重新跑一次: * 工具上印花也变形 → 大概率是**提示词本身**的问题,进 Step 2 * 工具上印花保持得很好 → 排查你的接入方式(图片是否真的传进去了、参数是否对),见 [图片与参考图差异过大排查](/faq/image-result-differs-from-reference) 把"严格还原""逐像素"这种命令式措辞,换成**具体属性描述**(见下方"提示词优化示例")。 * Nano Banana 系列**不支持 OpenAI 格式的 URL 上传**,必须用 **Base64** 编码 * 参考图本身要**清晰、特征明确**:模糊或元素过多的参考图会让模型"猜",印花更容易变形 * 单张图 ≤ **7MB**(Gemini 官方限制),建议上传前做无损压缩 * 每个提示词最多 **14 张参考图**;如果只想保留印花,可只传 1 张含印花的局部图,提高权重 在业务代码里对同一提示词实现 **1\~3 次自动重试**。一次失败不代表模型不行,多抽几次命中率显著上升。 * 切到 **Vertex 分组**(在控制台给令牌选 Vertex 分组即可,代码无需改动)——Vertex 渠道审核尺度与 AI Studio 不同,部分"被安全误伤"的换装请求能稳定通过 * 或换 **gpt-image-2 系列**——改图风格更贴近原图,色偏和风格漂移更小,适合"尽量接近原图"的需求 ## 提示词优化示例 下表是服装换装 + 印花保留场景下的常见问题改法对比: | 写法 | ❌ 反面示例 | ✅ 改进写法 | | - | - | - | | 颜色具体化 | "换成黑色" | "改为**哑光纯黑色**,保留原有材质质感" | | 印花描述位置 | "印花必须严格锁定不变" | "胸前印花**位置、颜色、比例、构图**全部保持不变" | | 保留项清单 | "其他不变" | "画面其余所有物体的**颜色、位置、文字标注**全部保持不变" | | 动作拆分 | 一句话堆 5 个动作 | 拆成多步编辑,**每次只改一件事** | **改进后的完整提示词示例**: > 编辑这张图,完成两件事:① 把红色方框内的两只茶壶改为哑光纯黑色,保留原有材质质感和形状;② 删除红色方框线本身。画面其余所有物体的颜色、位置、尺寸标注和文字**全部保持不变**。 **通用原则:一次只改一类东西**。如果你的换装同时涉及"换色 + 换背景 + 加文字",拆成多次编辑,每次的成功率都会显著高于一次堆复杂指令。 ## 关于出图变红 / 整体偏色 如果你发现换装后画面整体偏红或偏暖,那是另一个问题,根因通常是 [banana pro 改图后画面偏红](/faq/banana-pro-edit-red-cast)——和"印花变形"的成因不同,解决路径也不同(切 Vertex 通道或换 gpt-image-2)。 ## 相关文档 * [Nano Banana 系列开发指南](/api-capabilities/nano-banana-dev-guide) — 模型清单、计费、Base64 上传要求 * [图片与参考图差异过大排查](/faq/image-result-differs-from-reference) — 参考图传错格式的排查 * [Google 系模型走 AI Studio 还是 Vertex](/faq/google-upstream-aistudio-vertex) — Vertex 分组切换方法 * [如何生成满意的图片](/api-capabilities/image-generation-success-tips) — 改提示词、重试、换模型、测试定位四大策略 * [Gemini 生图 API 错误处理指南](/api-capabilities/gemini-image-error-handling) — 出图失败/被拦截的排查 * [Nano Banana 系列出图失败常见原因](/faq/nano-banana-image-failure) — 内容安全拦截场景 * [banana pro 改图后画面偏红怎么办](/faq/banana-pro-edit-red-cast) — 整体偏色问题 ## 联系客服 如果按以上步骤仍然无法解决,可在工作台联系客服或邮件 [hi@apiyi.com](mailto:hi@apiyi.com),提供: * 同一提示词在 [imagen.apiyi.com](https://imagen.apiyi.com) 工具上的复现结果(截图或链接) * 当前使用的模型名(`gemini-3-pro-image-preview` / Nano Banana 2 等) * API 调用时间 + 请求 ID(如有) # OpenAI 模型拒答长什么样? Source: https://docs.apiyi.com/faq/openai-content-safety-refusal GPT 系列模型遇到违反原厂使用政策的请求时,返回 200 与一段拒答文字,不报错也不分类。本文给出输出体示例与识别方法。 ## 简短回答 GPT 系列模型遇到违反原厂使用政策的请求时,**不会报错**。接口照常返回 HTTP 200,`finish_reason` 是 `stop`,正文是模型自己写的一句拒答,比如「I can't help with that…」或「抱歉,我不能…」。 响应里**没有错误码,也没有拒答类别或严重程度**,结构与正常回答完全一样,并且按正常输出计费。所以只看状态码和 `finish_reason`,是判断不出「被拒答了」的。 ## 拒答的输出体 下面是 `/v1/chat/completions` 非流式拒答的实测返回(正文已替换为中性示例): ```json theme={null} { "model": "gpt-5.6-terra", "object": "chat.completion", "created": 1789712872, "choices": [ { "index": 0, "message": { "role": "assistant", "content": "I can't help with that request. I can, however, help with a related topic or a rewritten version that stays within the usage policy." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 35, "completion_tokens": 54, "total_tokens": 89 } } ``` | 字段 | 拒答时的表现 | | - | - | | HTTP 状态码 | `200` | | `finish_reason` | `stop`,与正常回答相同 | | `message` | 只有 `role` 和 `content`,**没有**单独的 `refusal` 字段 | | 正文开头 | 常见为 `I can't…`、`Sorry, I can't…`、`抱歉,我不能…` | | 语言 | 跟随提示词:中文提问多用中文拒答 | | 长度 | 多在 50–120 tokens;涉及身心健康的话题会附带求助建议,可能长到 400 tokens 左右 | | 计费 | 按实际输入、输出 tokens 正常计费 | ## 为什么没有分类 OpenAI 的对话接口对违规请求的处理方式,是**让模型自己决定不回答**,而不是由接口返回一个错误。它不会告诉你命中的是哪一类(例如成人向内容、血腥暴力、自我伤害等),也不给严重程度。 这和「报错」是两回事:报错时你拿到的是非 200 状态码和 `error` 对象;拒答时你拿到的是一次**成功的调用**,只是内容不是你想要的。 ## 翻译与结构化输出场景 批量翻译、信息抽取这类任务,通常要求模型按固定格式输出(比如一个 JSON 数组)。一旦某批内容触发拒答,模型返回的是一句普通文字,客户端按 JSON 解析就会失败,常见报错如 `Unrecognized token 'I'`、`Expecting value`。 **这不是接口格式问题**,而是这一批内容本身被拒绝处理。同一批内容原样重试,结果通常也一样。 API易 已对**非流式请求**启用内容安全自动切换:某条官方线路在请求阶段或生成阶段触发内容过滤时,会自动切换到其他官方线路重新处理,无需客户端重试。切换后,大多数请求能拿到正常结果;极个别内容仍可能被模型本身拒答,这时就是本文描述的形态。 ## 如何识别与处理 要求 JSON 就先按 JSON 解析,要求固定条数就核对条数。格式不符,就当作「未拿到结果」处理,不要直接把正文当译文使用。 格式不符时,看正文是否是一句以 `I can't`、`Sorry`、`抱歉` 等开头的短句。是的话基本可以判定为拒答,而不是模型输出格式跑偏。 同一内容原样重试,大概率得到同样的拒答,还会重复计费。 把失败的批次拆成更小的批次重新提交,找出触发拒答的具体条目;其余条目通常可以正常完成。对触发的条目,可以调整表述后再试。 把失败 Case 在你们内部存档(原文、请求时间、请求 ID、返回正文),定期分析拒答集中在哪类内容,再考虑对这部分内容改用其他模型重试。 建议把「失败 Case 存档 → 分析 → 换其他模型重试」做成固定流程。拒答往往集中在少数几类内容上,存档之后很容易看出规律,比每次人工排查省事得多,也能避免对同一内容反复重试、重复计费。 下面是一个最小示例:校验 JSON,不符合就记入本地失败清单。 ```python theme={null} import json import os import time from openai import OpenAI client = OpenAI(api_key=os.environ["APIYI_API_KEY"], base_url="https://api.apiyi.com/v1") REFUSAL_PREFIXES = ("I can't", "I can’t", "Sorry", "I'm sorry", "I’m sorry", "抱歉") def translate_batch(lines): prompt = "把下面的字幕逐句翻译成英文,只输出 JSON 数组:\n" + json.dumps(lines, ensure_ascii=False) resp = client.chat.completions.create( model="gpt-5.6-terra", messages=[{"role": "user", "content": prompt}], ) text = resp.choices[0].message.content or "" try: result = json.loads(text) if isinstance(result, list) and len(result) == len(lines): return result except json.JSONDecodeError: pass # 未拿到结果:记入本地失败清单,供后续分析或改用其他模型重试 with open("failed_cases.jsonl", "a", encoding="utf-8") as f: f.write(json.dumps({ "time": time.strftime("%Y-%m-%d %H:%M:%S %z"), "request_id": resp.id, "is_refusal": text.strip().startswith(REFUSAL_PREFIXES), "input": lines, "output": text, }, ensure_ascii=False) + "\n") return None ``` ## 流式请求的区别 流式请求一旦开始输出,就无法再切换线路,因此**内容安全自动切换只对非流式请求生效**。流式下你可能会看到: * 一句简短的拒答,最后一个事件的 `finish_reason` 为 `content_filter`; * 或者已经输出了一部分正文,最后以 `finish_reason: "content_filter"` 结束。 批量翻译这类不需要逐字展示的任务,建议使用非流式调用。两者的完整对照与实测数据见 [流式和非流式请求,遇到内容过滤有什么不同?](/faq/streaming-content-filter)。 ## 常见问题 会。拒答是一次成功的调用,按实际输入、输出 tokens 正常计费,所以不建议对同一内容反复重试。 不能。拒答由原厂模型根据其使用政策决定,API易 无法关闭,也无法调整它的尺度。 模型的判断本身有一定随机性,不同官方线路的过滤尺度也不完全一致,所以边界附近的内容可能时而通过、时而被拒。明显违反使用政策的内容,基本会被稳定拒答。 OpenAI 的对话接口不返回类别。如果你的业务需要按类别处理,可以在请求前自行对内容做一次分类,或在失败 Case 存档后人工归类。 ## 相关文档 流式与非流式响应的统一解析方法 平台内容安全与合规政策 Claude 拒答的形态与识别方法 # 开着代理调用出图,总在 90 秒左右断开? Source: https://docs.apiyi.com/faq/proxy-idle-timeout-disconnect 本地开发时电脑开着 VPN/代理,出图请求在 90~180 秒成批断开、超时设得再长也没用,而且照样扣费。原因是代理会掐断长时间没有数据的连接,把 api.apiyi.com 设为直连即可解决。 ## 症状 在自己电脑上开发、调试出图程序时,电脑开着 VPN 或代理软件(很多开发者是为了让 AI 编程工具正常联网),出现这些情况: * 出图请求经常报连接中断,例如 `socket hang up`、`ECONNRESET`、`RemoteDisconnected`、`Connection aborted` * 程序里的超时已经设到 600 秒甚至 900 秒,请求却在 **90\~180 秒**左右就断了 * 经常**好几张一起失败**,像是被一刀切掉 * 出图越慢的模型越容易中(如 `gpt-image-2-vip`,单张通常要 60\~120 秒) * 到 [调用日志](/faq/call-logs) 里看,这些请求**大多有成功扣费的记录** ## 简短回答 **这不是并发限制,也不是 API易 的超时,而是你电脑上的代理把「一段时间没有数据」的连接掐断了。** 出图期间,连接上本来就没有任何数据在传,图越慢出来,越容易被代理当成空闲连接回收。把 `api.apiyi.com` 设为直连(不走代理)就能解决,API易 在国内可以直接访问。 ## 为什么会这样 一次非流式的出图请求分三段: 1. **上传**:把提示词和参考图发出去,几秒到几十秒 2. **等待**:服务端出图,通常 60\~200 秒。**这期间连接上没有任何数据** 3. **下载**:图出好后一次性返回 问题出在第 2 段。代理软件和代理节点为了回收资源,会给每条连接设一个「空闲超时」:上传、下载两个方向都没有数据,超过这个时长就把连接断掉。以常见的 Xray / V2Ray 内核为例,对应配置叫 `connIdle`,默认 300 秒,但**节点服务端可以改得更短**,你在本机看不到、也改不了。我们处理过的案例里,这个值约为 90 秒。 所以: * **程序里的 timeout 管不到它**。timeout 是你的程序愿意等多久,代理在中间先断了,程序只能收到「连接中断」 * **为什么一批一起断**:代理通常是定时巡检,每次把所有超过空闲时长的连接一起断开。同时在等图的几个请求会在同一秒失败 * **为什么聊天、写代码不受影响**:流式对话一直有数据在传,连接从来不会空闲;出图是几分钟里一个字节都没有 **断开以后通常照样扣费。** 请求已经到达 API易 并开始出图,你这边断开不会让出图停下来,图照常生成、照常计费,只是结果回不到你的程序。请以 [调用日志](/faq/call-logs) 里的记录为准核对实际扣费,不要只看程序的报错数。 ## 怎么确认是这个问题 把失败请求从发出到报错的耗时打出来。如果集中在某个固定值附近(比如 90 秒、180 秒),且常有几个请求在**同一秒**一起失败,基本就是代理空闲超时。 在 [调用日志](/faq/call-logs) 里找对应时间段:程序报了断开,日志里却有成功扣费的记录,说明 API易 这边已经出图完成,是回程的连接被断了。 按下文把 `api.apiyi.com` 设为直连,同样的参数跑 10\~20 张。断开消失,就可以确认是代理的问题。 ## 解决方法 在代理软件里给 API易 加一条直连规则,其他流量照常走代理,AI 编程工具不受影响。 Clash / Clash Verge / mihomo 的配置文件里,在 `rules` 最前面加: ```yaml theme={null} rules: - DOMAIN-SUFFIX,apiyi.com,DIRECT # ……原有规则 ``` v2rayN 等客户端:在「路由设置」里把 `apiyi.com` 加进直连(direct)列表。 开着 **TUN 模式 / 全局模式**时,代理会接管电脑上所有程序的流量,代码里怎么设置都绕不开,只能用这种规则直连。 如果代理只是「系统代理」或环境变量代理(没开 TUN),也可以让程序自己不走代理: ```bash theme={null} # 运行程序前设置,API易 的域名不走代理 # macOS / Linux export NO_PROXY="api.apiyi.com,.apiyi.com" # Windows PowerShell $env:NO_PROXY="api.apiyi.com,.apiyi.com" ``` * **Python `requests` / `httpx`**:会自动读取系统代理,设 `trust_env=False` 即可关闭,写法见 [脚本报 502 但调用日志里查不到?](/faq/proxy-empty-502) * **Node.js**:原生 `fetch` 默认不读系统代理;如果你用了 `HTTPS_PROXY` 配合 `ProxyAgent`、`global-agent` 之类的库,请把 `api.apiyi.com` 排除在外;`axios` 可以在请求里传 `proxy: false` 只有自建节点才能改服务端配置。把节点的空闲超时调到 600 秒以上(Xray / V2Ray 是 `policy.levels.<等级>.connIdle`)。用的是别人的节点就改不了,还是建议设直连。 ## 部署到服务器后还会遇到吗? **一般不会。** 服务器上通常不装代理,直连 API易,就不存在「代理掐断空闲连接」这一层。这也是生产环境推荐的部署方式:国内云服务器直接访问 `api.apiyi.com` 即可。 不过,服务器和 API易 之间如果还有别的中间层,它们同样有空闲超时,部署时顺手检查一下: | 中间层 | 常见默认值 | 说明 | | - | - | - | | 云厂商 NAT 网关 / 负载均衡 | Azure 默认 4 分钟;AWS NAT 网关 350 秒 | 服务器没有公网 IP、经 NAT 出网时才涉及,多数可在控制台调大 | | 自建 Nginx 反向代理 | `proxy_read_timeout` 默认 60 秒 | 在你的服务和 API易 之间加了一层 Nginx 时,必须调大,见 [如何避免接口超时?](/faq/timeout-configuration) | | 服务器上也装了代理 | 取决于代理配置 | 与本地开发是同一个问题,同样设直连 | 单张出图通常在 60\~200 秒内完成,低于云厂商 NAT 的默认值。只有在你自己加了反向代理,或者服务器也走代理时,才需要特别处理。 ## 常见疑问 对云厂商的 NAT 网关有用:keepalive 探测包能让 NAT 认为连接还活着。对代理软件基本无效:代理按「有没有实际数据」判断空闲,keepalive 探测包不算数据。本地开发遇到的这种断开,还是要靠直连解决。 它们的请求要么很快就返回,要么是流式输出、一直有数据在传,连接从来不会空闲 90 秒以上。只有出图这种「几分钟没有数据、最后一次性返回」的请求才会撞上。 都是本机代理造成的,但现象不同。[空 502 那篇](/faq/proxy-empty-502) 是代理自己生成了一个没有正文的 502;本篇是连接在等图期间被直接掐断,程序收到的是连接中断。两种情况的解决方法相同:让 API易 的请求不走代理。 这类断开发生在你和 API易 之间的代理上,API易 侧出图已经完成并计费。如果金额较大,请把时间段(注明时区,如 16:30\~17:00 (UTC+8))和用户名发给客服,我们会帮你核对调用日志。 目前出图接口是同步返回的,没有任务 ID 查询,详见 [图片生成有异步接口吗?](/faq/image-async-api)。设直连后,长时间等待本身不是问题。 ## 相关文档 本机代理自造的空 502,脚本绕开系统代理即可 国内可直连,不需要代理或 VPN timeout 设置与逐层排查 同步返回的原因与客户侧异步包装 # 流式和非流式请求,遇到内容过滤有什么不同? Source: https://docs.apiyi.com/faq/streaming-content-filter 非流式请求被内容安全过滤时,平台会自动换线路重新生成;流式请求一旦开始输出就无法切换,会以 content_filter 截断。本文给出实测对照、输出形态和按场景的选择建议。 ## 简短回答 **同一段内容,流式和非流式遇到内容过滤时的结果可能完全不同:** 1. **非流式**:某条官方线路触发内容安全过滤时,API易 会**自动换一条官方线路重新生成**。客户端拿到的是完整结果,**只计费一次**。 2. **流式**:内容一开始输出就已经发给客户端了,**中途没法再换线路**。这次请求会以 `finish_reason: "content_filter"` 结束,已经生成的部分**照常计费**。 3. **怎么选**:需要逐字展示给用户的(对话、Agent 回复)用流式;程序生成完才使用的(剧本、分镜、翻译、结构化数据)用非流式。 ## 过滤发生的两个时机 原厂的内容安全过滤会在两个时机介入,流式下两种形态都会出现: | 时机 | 流式下的表现 | 典型耗时 | | - | - | - | | **请求阶段**(审查输入) | 正文只有一句 `I'm sorry, but I cannot assist with that request.`(约 15 tokens),`finish_reason` 为 `content_filter` | 几秒 | | **生成阶段**(审查输出) | 模型已经输出了一部分正文,中途被截断,`finish_reason` 为 `content_filter` | 取决于截断前生成了多少 | 两种情况的 HTTP 状态码都是 `200`,响应以 `data: [DONE]` 正常收尾,**HTTP 错误日志里看不到**,只能从流的最后一个事件判断。 ## 流式与非流式对照 | | 流式 `stream: true` | 非流式 `stream: false` | | - | - | - | | 被过滤时平台怎么处理 | 已经开始输出,无法切换线路 | 自动换一条官方线路重新生成 | | 客户端拿到的结果 | 被截断的半段正文,或一句拒答 | 完整结果,`finish_reason: "stop"` | | 计费 | 被截断的这次按实际输入、输出 tokens 计费;续写或重试再另计 | 只计成功的那一次 | | 客户端要做的处理 | 识别 `content_filter`,清理正文,再决定续写或重试 | 无需额外处理 | | 适合的场景 | 需要逐字展示的内容 | 生成完才使用的内容 | 非流式的自动切换能大幅提高成功率,但不是 100%:如果内容明显违反原厂使用政策,换一条线路后也可能被模型自己拒答,形态见 [OpenAI 模型拒答长什么样?](/faq/openai-content-safety-refusal)。 ## 实测对照 2026-09-25 (UTC+8),用 `gpt-5.6-terra`、相同的参数(`max_tokens=35000`、`temperature=0`、`reasoning_effort=high`),对同一批影视分镜剧本分别发流式和非流式请求: | 剧本题材 | 流式 | 非流式 | | - | - | - | | 武侠打斗,要求把招式、受伤、倒地写具体 | **5/5 在生成阶段被截断**(约 1,700 tokens 处) | **4/4 完整返回** | | 动作片、战争片,含流血和死亡细节 | **10/10 在请求阶段被拦**(固定一句拒答) | **6/6 完整返回** | | 生活题材、悬疑题材 | 8/8 正常结束 | — | 同一段内容原样重试,流式下的结果基本一样。**决定是否触发的主要是内容本身,不是运气。** ## 流式被截断时的输出形态 最后一个带 `choices` 的事件没有正文,只有结束原因: ```text theme={null} data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"gpt-5.6-terra","choices":[{"delta":{},"finish_reason":"content_filter","index":0}],"usage":null} data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"gpt-5.6-terra","choices":[],"usage":{"prompt_tokens":27831,"completion_tokens":6966,"total_tokens":34797}} data: [DONE] ``` **生成阶段被截断时,拼好的正文末尾会直接接上一句英文拒答,中间没有换行**,例如: `……林远把长刀横于胸前,脚尖缓慢挪动,始终让I'm sorry, but I cannot assist with that request.` 如果把这段正文原样带进续写请求,模型会在上下文里看到一句拒答,下一轮更容易继续被拦。**续写前请先去掉这句。** ## 怎么选 * 聊天、客服、Agent 对话回复,用户需要立刻看到输出 * 需要中途打断(用户点「停止」)的场景 * 日常对话触发内容过滤的概率很低,流式的体验优势更重要 * 剧本、分镜、小说章节等创作类长文本 * 批量翻译、信息抽取、结构化 JSON * 结果要先解析、落库,再展示给用户 * **容易碰到敏感情节的内容**(打斗、伤亡、犯罪) 同一个产品完全可以**两种都用**:对话部分走流式,剧本或分镜生成走非流式。只需要把后者请求里的 `stream` 改成 `false`,其他参数不变。 非流式要等整段生成完才返回,长输出的推理模型可能要 30\~100 秒甚至更久,请把这类请求的客户端超时设到 **300 秒以上**,见 [如何避免接口超时?](/faq/timeout-configuration)。前端如果需要进度感,可以先显示「生成中」,完成后一次性展示。 ## 必须用流式时怎么处理 最后一个带 `choices` 的事件里,`finish_reason` 为 `content_filter` 就说明被过滤了。不要只看 HTTP 状态码。 生成阶段被截断时,正文末尾带着一句英文拒答,先把它去掉,再决定怎么用已生成的部分。 对被截断的那一段,改用非流式重新请求,让平台自动切换线路。这比流式续写的成功率高得多。 同一任务非流式也拿不到结果时,把敏感细节写得概括一些再试,不要原样重试。 下面是一个最小示例:流式读取,被过滤时去掉拒答句,再改走非流式重试。 ```python theme={null} import os from openai import OpenAI client = OpenAI(api_key=os.environ["APIYI_API_KEY"], base_url="https://api.apiyi.com/v1") REFUSAL = "I'm sorry, but I cannot assist with that request." def generate(messages, model="gpt-5.6-terra"): stream = client.chat.completions.create( model=model, messages=messages, stream=True, stream_options={"include_usage": True}, ) parts, finish = [], None for chunk in stream: if not chunk.choices: continue choice = chunk.choices[0] if choice.delta and choice.delta.content: parts.append(choice.delta.content) print(choice.delta.content, end="", flush=True) if choice.finish_reason: finish = choice.finish_reason text = "".join(parts) if finish != "content_filter": return text # 被过滤:去掉末尾的拒答句,改走非流式,由平台自动切换线路 partial = text.removesuffix(REFUSAL) print(f"\n[被内容过滤截断,已生成 {len(partial)} 字,改用非流式重试]") resp = client.chat.completions.create(model=model, messages=messages, timeout=600) return resp.choices[0].message.content ``` 示例里直接用非流式把整段重新生成一遍,最省事。如果你的业务必须保留已生成的部分,可以把 `partial` 作为上下文让模型续写,但续写请求同样建议用非流式。 ## 常见问题 扣。原厂已经实际生成了这些内容,按实际输入、输出 tokens 计费。所以容易触发过滤的内容,用非流式更划算:非流式只计成功的那一次。 不能。过滤由原厂执行,API易 无法关闭,也无法调整尺度。平台能做的是在非流式请求被过滤时换一条官方线路重试。 不同官方线路的过滤尺度不完全一致,模型每次生成的措辞也不同,所以边界附近的内容可能时而通过、时而被截断。明显敏感的内容会被稳定拦截。 生成阶段的截断发生在正文已经发出之后,客户端已经收到并展示了前半段,这时再换线路从头生成,内容会对不上。所以流式请求只能由客户端识别 `content_filter` 后自行处理。 ## 相关文档 被模型自己拒答时的输出体与识别方法 两种调用方式的接入、计费与常见误区 非流式长输出的超时设置 平台内容安全与合规政策 # 白底图出现黑点 / 脏块 / 模糊色块怎么办? Source: https://docs.apiyi.com/faq/white-background-image-artifacts Google Aistudio API 出纯白背景图时容易出现脏块、黑点或模糊色块,可改提示词换浅色背景,或切 Vertex 线路解决。 ## 简短回答 这是 Google Aistudio API 出图的已知问题,纯白背景在算力紧张时容易出现黑点、脏块或模糊色块,自 2026 年 4 月起多次出现。有两种解决办法: * **方案一(推荐先试)**:把提示词里的「白色背景」改成别的浅色背景(如浅灰、米色、淡蓝),无需任何配置变更 * **方案二**:联系运营开通 `VertexGemini` 分组,创建令牌时选该分组,走 Google Vertex 平台出图,能根治纯白背景模糊问题 ## 详细说明 Google Aistudio 通道在算力紧张时段(高峰 / 资源调度紧张)对**大面积纯色像素区域**(尤其是白色)压缩处理不够稳定,容易产生三类瑕疵: * **黑点 / 脏块**:图像局部出现深色斑点或不规则脏块 * **模糊色块**:原本纯白的区域出现灰白、浅黄的雾化区域 * **整体偏色**:白色背景偏暖(米黄)或偏冷(灰蓝),与提示词不一致 这个问题**不是模型能力问题**,而是 Aistudio 出图链路在纯色背景上的渲染缺陷。Nano Banana Pro / Nano Banana 2 等 Gemini 图像模型在 Aistudio 通道都会出现。 ## 解决步骤 ### 方案一:提示词规避(首选) 把提示词中的「白色背景 / pure white background / #FFFFFF」改成其他浅色背景描述: * **浅灰**:`浅灰色背景` / `light gray background, #F5F5F5` * **米色**:`米色背景` / `beige background, #F5F0E5` * **淡蓝**:`淡蓝色背景` / `light blue background, #E8F0F8` * **透明**:`透明背景`(若模型支持 alpha 输出) 实测把白色改成浅灰或米色后,黑点、脏块问题几乎完全消失;这不会影响主体物的色彩还原,只改背景色。 ### 方案二:切到 Vertex 线路 如果你的场景必须用纯白背景(例如电商主图、产品白底图),切到 Vertex 通道可以根治此问题。 #### VertexGemini 分组说明 | 项目 | 说明 | | - | - | | **标识** | `VertexGemini` | | **描述** | Vertex Gemini Models: Nano Banana Pro / 2 Series,提供 Vertex 渠道专门的生成图片资源 | | **支持模型** | Nano Banana Pro、Nano Banana 2 | | **优势** | 能解决纯白背景修图时的模糊背景问题 | | **劣势** | 整体并发不高(约 100 RPM),仅开放给特邀客户;图片生成用时比 Aistudio 略慢 | #### 使用方式 联系 API易 客服或运营同事,申请开通 `VertexGemini` 分组权限。该分组目前仅对特邀客户开放。 登录 API易 后台 → 「令牌管理」 → 「创建令牌」 → **选择分组 `VertexGemini`** → 保存密钥。 用该新令牌调用 Nano Banana Pro / Nano Banana 2 模型即可,调用地址不变(仍是 `https://api.apiyi.com/v1`)。 **Vertex 分组并发较低(约 100 RPM)**,不建议用于线上高并发场景。主力出图仍建议使用默认分组(Aistudio),仅在遇到纯白背景问题时切到 Vertex 测试对比。 ## 常见问题 Aistudio 通道的渲染问题主要发生在**大面积纯色像素区域**的压缩与重建环节。把白色改成浅灰 / 米色,相当于让背景变成有细节的浅色填充,规避了「纯色大面积压缩」这个触发条件,问题就消失了。 不一定。Vertex 通道在「纯白 / 纯色背景」场景下渲染更稳,但在其它场景(如复杂构图、人物特写)两者差异不大,且 Vertex 用时更长。**只有当你确认是纯白背景触发问题时才切 Vertex**。 目前仅对特邀客户开放,请联系 API易 客服 / 运营同事申请。普通用户先用方案一(提示词换浅色)通常就够用了。 **不需要**。Base URL 仍是 `https://api.apiyi.com/v1`,只是令牌所属分组变了,API易 后端会自动路由到 Vertex 平台。 ## 相关文档 Nano Banana 系列模型的常见出图问题与排查方法。 Aistudio 与 Vertex 两条 Gemini 出图线路的差异与选择建议。 企业用户如何用 Vertex 分组做兜底出图。 图片生成异步任务接口的使用方式。 # 快速开始 Source: https://docs.apiyi.com/getting-started 两条路接入 API易:把文档丢给你的 AI 编程助手让它代劳,或者按三步自己动手。 注册后账号自带 **\$0.05** 试用额度,不充值也能跑通下面的第一次调用。用 `gpt-5.4-mini` 这类轻量模型足够验证接入是否正常。 ## 两条路,任选一条 复制一段提示词丢给 Codex / Claude Code / Cursor,它自己读文档、写代码、跑通。适合已经在用编程 Agent 的人。 注册 → 建 Key → 发起第一次调用。三步,五分钟。适合想先搞清楚每一步在干嘛的人。 两条路的**第一步是一样的**:账号需要你本人注册、Key 需要你本人创建。AI 能替你做的是从「拿到 Key」到「代码跑通」这一段——选模型、填对 Base URL、写示例、排错。 ## 让 AI 帮你接 ### 把这段话发给你的 Agent 帮我把 API易(APIYI)接入到当前项目。 1. 先获取接入知识:运行 `npx skills add https://docs.apiyi.com` 安装 APIYI 技能包。 如果这条命令跑不通,就直接抓取 [https://docs.apiyi.com/skill.md](https://docs.apiyi.com/skill.md) 全文阅读,效果一样。 2. 需要更细的内容时,从 [https://docs.apiyi.com/llms.txt](https://docs.apiyi.com/llms.txt) 找到对应页面; 任意文档页地址后面加 `.md` 就能拿到纯 Markdown 版,比抓 HTML 省 token。 3. 向我索要 API Key(我在 [https://api.apiyi.com/token](https://api.apiyi.com/token) 复制), 写进环境变量 `APIYI_API_KEY`,**不要硬编码进代码,也不要提交进 git**。 4. 按本项目的技术栈写一段最小可运行示例,模型先用 `gpt-5.4-mini`。 注意:Base URL 要按 SDK 选——OpenAI SDK 用 `https://api.apiyi.com/v1`, Anthropic SDK 用根域名 `https://api.apiyi.com`(不加 /v1), Google GenAI SDK 用根域名并把 api\_version 设为 v1beta。 5. 真跑一次,把返回内容贴给我。跑通后告诉我这次调用花了多少钱, 以及后续要换成哪个模型更合适。 ### 它会做什么 `https://docs.apiyi.com/skill.md` 是一份专门写给 AI 读的接入说明:端点、认证、模型命名规则、常见坑、排查清单。Agent 读完就具备了接入 API易 所需的全部背景。 `llms.txt` 是全站页面索引。Agent 从中挑出相关页面,加 `.md` 后缀拿纯文本版本读。 它会用你项目已有的语言和依赖写示例,而不是照搬文档里的 Python。跑通之前不算完。 ### 可以直接喂给 AI 的五个入口 | 入口 | 地址 | 什么时候用 | | - | - | - | | **技能包** | `https://docs.apiyi.com/skill.md` | 一次性给 Agent 完整接入知识,首选 | | **页面索引** | `https://docs.apiyi.com/llms.txt` | 让 Agent 自己找该读哪一页 | | **单页纯文本** | 任意文档页地址后加 `.md` | 只关心某一页时,比抓 HTML 省 token | | **MCP 服务** | `https://docs.apiyi.com/mcp` | 把本文档站接成 MCP,让 Agent 随时检索最新内容 | | **模型注册表** | `https://docs.apiyi.com/model-registry.json` | 让 Agent 查模型 ID、端点、分组、标价的机器可读源,不再凭记忆编 | Skills 技能包、命令行工具、给编程 Agent 的契约与注册表,三条路的完整说明见 [AI 开发者套件](/developer-kit)。 例如本页的纯文本版就是 `https://docs.apiyi.com/getting-started.md`。全站每一页都支持这个后缀。 ### 在文档页右上角一键送进 AI 每个文档页的**右上角**都有一个「复制页面」按钮,点开右侧箭头还有更多选项: 文档页右上角的复制页面菜单,包含复制页面、以 Markdown 格式查看、在 ChatGPT / Claude / Perplexity / Google AI Studio 中打开 | 选项 | 作用 | | - | - | | **复制页面** | 把当前页以 Markdown 格式复制到剪贴板,直接粘给任意 AI | | **以 Markdown 格式查看** | 在浏览器里打开纯文本版,方便核对或分享链接 | | **在 ChatGPT 中打开** | 带着本页内容跳转到 ChatGPT 提问 | | **在 Claude 中打开** | 同上,跳转 Claude | | **在 Perplexity 中打开** | 同上,跳转 Perplexity | | **在 Google AI Studio 中打开** | 同上,跳转 Google AI Studio | 遇到具体模型的接入问题时,最快的做法是打开那个模型的文档页,点「复制页面」,连同你的报错一起发给 AI。 ## 自己动手接 ### 第一步:注册并拿到 Key 访问 [API易官网](https://api.apiyi.com) 用邮箱注册并验证(推荐用高校、企业邮箱),然后登录控制台。 进入[令牌页面](https://api.apiyi.com/token): 1. 可以直接复制**默认令牌**使用(右侧有复制图标) 2. 也可以点右上角「新增」创建新令牌,起个名字(如 `test-key`)后确认 Key 以 `sk-` 开头。详细说明见 [如何创建 KEY](/faq/token-management)。 自带的 \$0.05 用完后,在控制台「充值」菜单充值即可,支持支付宝、微信。各通道的最低充值额与到账规则见[支付方式说明](/faq/payment-methods),加赠政策见[充值活动](/faq/recharge-promotions)。 ### 第二步:填对接入信息 **Base URL 按 SDK 选,不是按模型选**——这是最常见的接入错误: | 你用的 SDK | Base URL | 说明 | | - | - | - | | OpenAI SDK(及绝大多数客户端) | `https://api.apiyi.com/v1` | SDK 会自己拼 `/chat/completions`,所以必须带 `/v1` | | Anthropic SDK(Claude 原生) | `https://api.apiyi.com` | SDK 会自己拼 `/v1/messages`,**加了 `/v1` 会变成 `/v1/v1/messages` 报 404** | | Google GenAI SDK(Gemini 原生) | `https://api.apiyi.com` | 另需设置 `api_version: "v1beta"` | `base_url` 末尾**不要留斜杠**,否则会拼出双斜杠导致 404。完整说明与节点选择见 [Base URL 怎么填](/faq/base-url-config)。 ### 第三步:发起第一次调用 ```bash theme={null} curl https://api.apiyi.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $APIYI_API_KEY" \ -d '{ "model": "gpt-5.4-mini", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ```python theme={null} import os from openai import OpenAI client = OpenAI( api_key=os.environ["APIYI_API_KEY"], base_url="https://api.apiyi.com/v1" ) response = client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Hello!"} ] ) print(response.choices[0].message.content) ``` ```javascript theme={null} import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: process.env.APIYI_API_KEY, baseURL: 'https://api.apiyi.com/v1' }); const response = await openai.chat.completions.create({ model: 'gpt-5.4-mini', messages: [{ role: 'user', content: 'Hello!' }] }); console.log(response.choices[0].message.content); ``` ```java theme={null} // 使用官方 OpenAI Java 库 OpenAiService service = new OpenAiService( System.getenv("APIYI_API_KEY"), Duration.ofSeconds(60), "https://api.apiyi.com/v1" ); ChatCompletionRequest request = ChatCompletionRequest.builder() .model("gpt-5.4-mini") .messages(List.of( new ChatMessage(ChatMessageRole.USER, "Hello!") )) .build(); ChatCompletionResult result = service.createChatCompletion(request); System.out.println(result.getChoices().get(0).getMessage().getContent()); ``` `gpt-5` 及以上系列有三条参数限制:`temperature` 只能是 1、用 `max_completion_tokens` 替代 `max_tokens`、不要传 `top_p`。 ## 下一步 配置 `ANTHROPIC_BASE_URL`,用 API易 驱动 Claude Code 一份 `config.toml` 同时覆盖桌面端、IDE 插件和 CLI 查看所有支持的 AI 模型与能力速查 完整的接口说明、错误码与调试方法 ## 常见问题 ### 如何切换模型? 只需修改请求中的 `model` 参数: ```json theme={null} { "model": "gpt-5.6-sol", // 使用 GPT-5.6 Sol "model": "claude-opus-5", // 使用 Claude Opus 5 "model": "gemini-3.6-flash" // 使用 Gemini 3.6 Flash } ``` **模型 ID 用点号,不是连字符**。文档页地址里的 `-` 是为了 URL 安全替换的,真实模型 ID 保留点号:页面 `/models/qwen3-7-max` 对应的模型 ID 是 `qwen3.7-max`。写成 `gpt-5-4-mini` 会报 404,正确写法是 `gpt-5.4-mini`。 不确定时用接口列一遍:`GET https://api.apiyi.com/v1/models`。 ### 支持哪些编程语言? API易 兼容 OpenAI API 标准,支持所有 OpenAI SDK 支持的语言: * Python * JavaScript/TypeScript * Java * C#/.NET * Go * Ruby * PHP * 更多... ### 如何查看余额? 登录 [控制台](https://api.apiyi.com/account/profile) 即可查看: * 账户余额 * 使用记录 * 消费统计 也可通过 API 程序化查询: * [余额查询 API](/api-capabilities/balance-query):通过接口获取账户余额、有效期等信息 * [余额告警设置方法](/faq/balance-alerts):余额不足时自动通知,避免服务中断 ### 遇到问题怎么办? 1. 打开出问题的那个文档页,点右上角「复制页面」,连同报错一起发给 AI 2. 查看 [API 文档](/api-manual) 3. 检查 [常见错误](/faq/invalid-api-key) 4. 联系客服:[support@apiyi.com](mailto:support@apiyi.com) 提示:保存好您的 API 密钥,并定期在控制台查看使用日志,每笔请求都有消息历史,合理优化成本。 祝使用愉快\~ # API易 - AI 大模型 API 聚合服务商 Source: https://docs.apiyi.com/index 企业级 AI 大模型 API 中转站:一个令牌调用 400+ 模型,兼容 OpenAI / Claude / Gemini 原生格式,已稳定运营 2 年,月均 15 万访客 **International users?** [Switch to English version](/en) **API易** 是企业级专业稳定的 AI 大模型 API 中转站,兼容统一的 OpenAI API 标准,同时支持 Claude、Gemini 等各大厂商的原生 API 格式,覆盖 400+ 热门AI模型。一个令牌,即可轻松调用OpenAI、Claude、Gemini、DeepSeek、Qwen、Kimi、GLM、Minimax 等所有主流大模型。 ## 🎬 新人必看:40 秒了解 API易