> ## Documentation Index
> Fetch the complete documentation index at: https://docs.teio.me/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用 background=true 异步生图（OpenAI Responses API）

本文档面向接入方，说明如何通过 OpenAI Responses API 的 `background=true` 模式调用生图模型， 异步生成图片。内容包含完整示例代码，并重点演示**如何传入多张参考图片**以及**如何传入多份提示词文档**。

***

## 1. 概述

生图通常需要 30 秒到 5 分钟。如果用同步方式调用，客户端必须一直保持 HTTP 连接等待，网络抖动或网关 超时都可能导致请求失败。

`background=true` 模式把"等待完成"拆成三步：

```text theme={null}
1) 提交任务  → 立即拿到 resp_xxx（status=queued），连接随即关闭
2) 轮询状态  → status 在 queued / in_progress 之间流转
3) 取回结果  → status=completed，output 中带回图片
```

适用场景：

* 高分辨率图片生成（耗时长）
* 客户端网络不稳定，需要避免长连接超时
* 批量提交、稍后统一取结果

> 兼容性：本服务完全兼容 OpenAI Responses API 的 background 语义。直接使用官方 OpenAI SDK， 只需把 `base_url` 指向本服务即可，`responses.create / retrieve / cancel` 均可正常使用。

***

## 2. 前置条件

<Tip>
  创建密钥时务必选择**OpenAI-IMG**分组
</Tip>

| 项        | 值                                         |
| -------- | ----------------------------------------- |
| Base URL | `https://your-sub2api-domain.com/v1`      |
| API Key  | `sk-your-api-key`                         |
| 生图端点     | `POST /v1/responses`                      |
| 轮询端点     | `GET /v1/responses/{response_id}`         |
| 取消端点     | `POST /v1/responses/{response_id}/cancel` |

安装官方 SDK：

```bash theme={null}
pip install openai
```

初始化客户端：

```python theme={null}
from openai import OpenAI

client = OpenAI(
    base_url="https://your-sub2api-domain.com/v1",
    api_key="sk-your-api-key",
)
```

***

## 3. 请求结构说明

生图在 Responses API 中是一个 **工具调用（tool）**，不是独立的模型端点。标准结构是：

* 顶层 `model`：文本模型（负责理解 prompt 并决定调用生图工具）
* `tools[]`：包含一个 `image_generation` 工具，工具里的 `model` 才是图片模型
* `tool_choice`：强制走生图工具
* `input`：用户输入（文字 + 参考图片）

```json theme={null}
{
  "model": "gpt-5.5",
  "background": true,
  "tools": [
    {
      "type": "image_generation",
      "model": "gpt-image-2",
      "quality": "high",
      "size": "2560x1440",
      "output_format": "png"
    }
  ],
  "tool_choice": { "type": "image_generation" },
  "input": [
    {
      "role": "user",
      "content": [
        { "type": "input_text", "text": "Generate a beautiful sunset landscape" }
      ]
    }
  ]
}
```

### 关于简写形式（实测不可用，请勿使用）

有一种"偷懒"写法：把图片模型直接放在顶层 `model`、并在顶层写 `size` / `output_format`，不写 `tools`：

```json theme={null}
{
  "model": "gpt-image-2",
  "background": true,
  "size": "2560x1440",
  "output_format": "png",
  "input": "Generate a beautiful sunset landscape"
}
```

**这种写法经实测在本服务上不可用，请不要使用：**

* 官方 OpenAI SDK 直接拒绝——`responses.create(size=...)` 会报 `unexpected keyword argument 'size'`，根本发不出去。
* 即便用裸 HTTP 绕过 SDK 发出去，服务端虽然接受并返回 `queued`，但任务随后会变成 `failed`，上游报错 `400: Unsupported parameter: size`（顶层 `size` 未被并入生图工具， 被原样转发给上游）。

> 结论：**始终使用第 3 节开头的标准结构**——顶层 `model` 填文本模型（如 `gpt-5.5`）， `size` / `quality` / `output_format` 等参数放在 `tools[]` 里的 `image_generation` 工具内。 这是唯一经端到端验证可用的写法。

***

## 4. 快速开始（三步）

### 第 1 步：提交任务

```python theme={null}
response = client.responses.create(
    model="gpt-5.5",
    background=True,
    tools=[{
        "type": "image_generation",
        "model": "gpt-image-2",
        "quality": "high",
        "size": "2560x1440",
        "output_format": "png",
    }],
    tool_choice={"type": "image_generation"},
    input=[{
        "role": "user",
        "content": [
            {"type": "input_text", "text": "Generate a beautiful sunset landscape"},
        ],
    }],
)

print(response.id)      # resp_xxxxxxxx
print(response.status)  # queued
```

提交后**立即**返回（通常 \< 100ms）：

```json theme={null}
{
  "id": "resp_xxxxxxxx",
  "object": "response",
  "status": "queued",
  "created_at": 1748600000,
  "model": "gpt-5.5",
  "output": []
}
```

### 第 2 步：轮询状态

```python theme={null}
import time

while True:
    result = client.responses.retrieve(response.id)
    print(result.status)

    if result.status == "completed":
        break
    if result.status in ("failed", "cancelled"):
        print("生成未成功:", getattr(result, "error", result.status))
        break

    time.sleep(10)  # 生图建议每 10-15 秒轮询一次
```

轮询过程中可能看到：

```json theme={null}
{ "id": "resp_xxxxxxxx", "object": "response", "status": "in_progress" }
```

### 第 3 步：取回图片

完成后 `output` 中会包含一个或多个 `image_generation_call`，图片数据在 `result` 字段（base64）：

```python theme={null}
import base64

for item in result.output:
    if item.type == "image_generation_call":
        with open("output.png", "wb") as f:
            f.write(base64.b64decode(item.result))
        print("已保存 output.png")
```

***

## 5. 使用多个参考图片

参考图片通过 `input` 里 `content` 数组中的多个 `input_image` 部分传入。 每张参考图是一个 `input_image`，`image_url` 填 **base64 data URL**，配 `detail` 控制参考精细度。 按你传入的顺序排列，数量不限，与文字提示放在**同一个 user 消息**里。

> 下面的写法对应项目内实际使用的参考脚本 base64 data URL，`detail="high"`，单条 user 消息，`background=True`。

```python theme={null}
import base64
import mimetypes
from pathlib import Path

def to_data_url(path: str) -> str:
    """把本地图片编码成 base64 data URL（与参考脚本一致）。"""
    mime, _ = mimetypes.guess_type(path)
    mime = mime or "image/png"
    data = Path(path).read_bytes()
    return f"data:{mime};base64,{base64.b64encode(data).decode('ascii')}"

# 多张本地参考图
content = [
    {"type": "input_text",  "text": "参考这几张图的构图与配色，生成一张电影感分镜图"},
    {"type": "input_image", "image_url": to_data_url("ref_pose.jpg"),  "detail": "high"},
    {"type": "input_image", "image_url": to_data_url("ref_style.png"), "detail": "high"},
    {"type": "input_image", "image_url": to_data_url("ref_bg.jpg"),    "detail": "high"},
]

response = client.responses.create(
    model="gpt-5.5",
    background=True,
    tools=[{
        "type": "image_generation",
        "model": "gpt-image-2",
        "quality": "high",
        "size": "2560x1440",
        "output_format": "png",
    }],
    tool_choice={"type": "image_generation"},
    input=[{"role": "user", "content": content}],
)
print("已提交:", response.id)
```

要点：

* `input_image` 的数量不限，按数组顺序传给模型。
* `image_url` 可以用 **base64 data URL**（`data:image/...;base64,...`），也可以用 **https 公网链接**——两种都经端到端实测可用（见下）。
* 参考图片与文字提示放在**同一个 user 消息**的 `content` 数组里即可。

> **实测结论（已端到端验证）：**
>
> * **base64 data URL 多图**：通过。3 张本地图编码后同条消息传入，成功生成图片。
> * **https 公网链接**：通过。用一个公开可访问的 https 图片 URL 作参考图，约 54 秒成功生成图片。 注意链接必须是上游可公开访问的稳定 URL（会被原样转发给上游抓取）。
> * `detail` 字段实测用的是 `high`；其余取值（`low` / `auto`）未单独验证。

***

## 6. 使用多个提示词文档

当你有多份提示词（例如：角色设定文档、场景描述文档、风格指南文档），有两种传法， 两种都会被原样转发给模型。

### 方式 A：同一条消息里放多个 `input_text`（推荐）

把每份文档作为一个独立的 `input_text` 部分，按逻辑顺序排列，最后再放参考图片：

```python theme={null}
character_doc = Path("character.md").read_text(encoding="utf-8")
scene_doc     = Path("scene.md").read_text(encoding="utf-8")
style_doc     = Path("style_guide.md").read_text(encoding="utf-8")

content = [
    {"type": "input_text", "text": f"【角色设定】\n{character_doc}"},
    {"type": "input_text", "text": f"【场景描述】\n{scene_doc}"},
    {"type": "input_text", "text": f"【风格指南】\n{style_doc}"},
    {"type": "input_text", "text": "请综合以上三份文档，生成一张符合设定的主视觉图。"},
    {"type": "input_image", "image_url": to_data_url("mood_ref.jpg"), "detail": "high"},
]

response = client.responses.create(
    model="gpt-5.5",
    background=True,
    tools=[{
        "type": "image_generation",
        "model": "gpt-image-2",
        "quality": "high",
        "size": "3072x1728",
        "output_format": "png",
    }],
    tool_choice={"type": "image_generation"},
    input=[{"role": "user", "content": content}],
)
```

### 方式 B：多条 user 消息

如果你想让文档在对话结构上彼此独立，也可以用多条消息：

```python theme={null}
input_messages = [
    {"role": "user", "content": [{"type": "input_text", "text": f"【角色设定】\n{character_doc}"}]},
    {"role": "user", "content": [{"type": "input_text", "text": f"【场景描述】\n{scene_doc}"}]},
    {"role": "user", "content": [{"type": "input_text", "text": f"【风格指南】\n{style_doc}"}]},
    {"role": "user", "content": [
        {"type": "input_text", "text": "请综合以上文档生成主视觉图。"},
        {"type": "input_image", "image_url": to_data_url("mood_ref.jpg"), "detail": "high"},
    ]},
]

response = client.responses.create(
    model="gpt-5.5",
    background=True,
    tools=[{"type": "image_generation", "model": "gpt-image-2", "size": "3072x1728"}],
    tool_choice={"type": "image_generation"},
    input=input_messages,
)
```

要点：

* 只要 `input` 是**结构化数组**（如上两种写法），服务端会原样转发，不做改写。
* 给每份文档加一个清晰的小标题（如 `【角色设定】`）有助于模型区分文档边界。
* 文档与参考图片可以自由组合：在任意一条消息的 `content` 里同时放 `input_text` 和 `input_image` 即可。

***

## 7. 完整示例：多参考图 + 多文档 + 轮询保存

```python theme={null}
#!/usr/bin/env python3
import base64
import time
import mimetypes
from pathlib import Path
from openai import OpenAI

client = OpenAI(
    base_url="https://your-sub2api-domain.com/v1",
    api_key="sk-your-api-key",
)

def to_data_url(path: str) -> str:
    mime, _ = mimetypes.guess_type(path)
    mime = mime or "image/png"
    return f"data:{mime};base64,{base64.b64encode(Path(path).read_bytes()).decode('ascii')}"

# 多份提示词文档
character_doc = Path("character.md").read_text(encoding="utf-8")
style_doc     = Path("style_guide.md").read_text(encoding="utf-8")

# 一条 user 消息：多文档 + 指令 + 多参考图
content = [
    {"type": "input_text",  "text": f"【角色设定】\n{character_doc}"},
    {"type": "input_text",  "text": f"【风格指南】\n{style_doc}"},
    {"type": "input_text",  "text": "综合以上文档与参考图，生成一张高分辨率电影分镜图。"},
    {"type": "input_image", "image_url": to_data_url("ref1.jpg"), "detail": "high"},
    {"type": "input_image", "image_url": to_data_url("ref2.jpg"), "detail": "high"},
]

# 1) 提交 background 任务
response = client.responses.create(
    model="gpt-5.5",
    background=True,
    tools=[{
        "type": "image_generation",
        "model": "gpt-image-2",
        "quality": "high",
        "size": "3072x1728",
        "output_format": "png",
    }],
    tool_choice={"type": "image_generation"},
    input=[{"role": "user", "content": content}],
)
print("任务已提交:", response.id)

# 2) 轮询（带超时保护）
started = time.time()
TIMEOUT = 600  # 10 分钟
while True:
    elapsed = time.time() - started
    if elapsed > TIMEOUT:
        print("超时，放弃等待")
        break

    time.sleep(10)
    result = client.responses.retrieve(response.id)
    print(f"[{elapsed:.0f}s] {result.status}")

    if result.status == "completed":
        # 3) 保存所有图片
        idx = 0
        for item in result.output:
            if item.type == "image_generation_call":
                out = f"storyboard_{idx}.png"
                Path(out).write_bytes(base64.b64decode(item.result))
                print("已保存", out)
                idx += 1
        print(f"完成，用时 {elapsed:.0f}s")
        break

    if result.status in ("failed", "cancelled"):
        print("生成未成功:", getattr(result, "error", result.status))
        break
```

***

## 8. 使用 curl 测试

```bash theme={null}
# 1) 提交任务（注意：input 用结构化数组传多图/多文档）
curl -X POST "https://your-sub2api-domain.com/v1/responses" \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "background": true,
    "tools": [{"type": "image_generation", "model": "gpt-image-2", "size": "2560x1440", "output_format": "png"}],
    "tool_choice": {"type": "image_generation"},
    "input": [{
      "role": "user",
      "content": [
        {"type": "input_text",  "text": "文档一：角色设定..."},
        {"type": "input_text",  "text": "文档二：风格指南..."},
        {"type": "input_text",  "text": "综合以上文档生成主视觉图"},
        {"type": "input_image", "image_url": "data:image/jpeg;base64,/9j/4AAQ...", "detail": "high"},
        {"type": "input_image", "image_url": "data:image/png;base64,iVBORw0KGgo...", "detail": "high"}
      ]
    }]
  }'
# → {"id":"resp_xxxx","object":"response","status":"queued","output":[]}

# 2) 轮询
curl "https://your-sub2api-domain.com/v1/responses/resp_xxxx" \
  -H "Authorization: Bearer sk-your-api-key"
# → {"status":"in_progress"}            生成中
# → {"status":"completed","output":[...]}  完成，output 中含 image_generation_call.result(base64)

# 3) 取消（可选）
curl -X POST "https://your-sub2api-domain.com/v1/responses/resp_xxxx/cancel" \
  -H "Authorization: Bearer sk-your-api-key"
# → {"id":"resp_xxxx","object":"response","status":"cancelled"}
```

***

## 9. 取消任务

```python theme={null}
cancelled = client.responses.cancel(response.id)
print(cancelled.status)  # cancelled
```

取消成功后该任务的结果映射会被立即清除，后续再 `retrieve` 将返回 404。

***

## 10. 状态流转

```text theme={null}
queued → in_progress → completed
                     → failed
                     → cancelled（用户主动取消）
```

| 状态            | 含义                  |
| ------------- | ------------------- |
| `queued`      | 任务已接收，等待开始          |
| `in_progress` | 正在生成中               |
| `completed`   | 生成完成，`output` 中包含结果 |
| `failed`      | 生成失败，`error` 字段含原因  |
| `cancelled`   | 用户主动取消              |

***

## 11. 支持的图片尺寸

| 尺寸        | 像素数  | 状态  |
| --------- | ---- | --- |
| 1024x1024 | 1.0M | 支持  |
| 1920x1088 | 2.1M | 支持  |
| 2048x2048 | 4.2M | 支持  |
| 2560x1440 | 3.7M | 支持  |
| 3072x1728 | 5.3M | 支持  |
| 3840x2160 | 8.3M | 不支持 |

* **标准结构下，出图尺寸与请求 `size` 精确一致**（3072x1728、3840x2160 落地像素均吻合）。 注意：第 3 节那种"简写写法"会让 `size` 被上游忽略，尺寸不可控。
* **大尺寸的失败多为可重试的偶发错误**，不一定是"尺寸不支持"。实测中见过两类： 传输层 `INTERNAL_ERROR`、上游 `server_error: You can retry`。两者都建议重试后再判断。

耗时参考（1024 档约 15-30 秒；3072x1728 约 70-80 秒；3840x2160 约 80 秒）。

***

## 12. 注意事项

1. **结果有效期 15 分钟**
   * 生成完成后，结果在服务端保留 15 分钟，过期后再 `retrieve` 返回 404。
   * 建议尽快取走结果，不要拿到 `completed` 后长时间不读取。
2. **轮询频率**
   * 图片生成：每 10-15 秒轮询一次。
   * 不要高频轮询（\< 1 秒），会增加服务器负载。
3. **取走即清除**
   * 一旦 `retrieve` 返回了 `completed` 的完整结果，该结果映射会被删除； 请在同一次轮询里就把图片保存下来，不要依赖二次拉取。
4. **单实例限制**
   * 取消（cancel）只对当前处理该任务的服务实例生效。
   * 若服务重启，进行中的任务会丢失（映射 15 分钟后过期返回 404）。
5. **多图 / 多文档务必用结构化 `input` 数组**
   * 传 `input_image`、多个 `input_text` 时，`input` 必须是数组结构（见第 5、6 节）。
   * 仅当 `input` 是单条纯文本字符串时才适合用简写。
6. **错误处理**
   * 提交（POST）返回非 `queued` 状态，说明请求参数有问题。
   * 轮询返回 `failed` 时，查看 `error.message`：常见原因有上游临时错误（可重试）、 不支持的尺寸、内容审核拒绝（`moderation_blocked`）。
7. **response\_id 格式**
   * 形如 `resp_` + 一串十六进制字符，请原样保存用于后续轮询 / 取消。
