跳转到主要内容
整合常见问题、性能优化、错误处理的最佳实践,接入前建议通读

任务提交与轮询

提交接口都是异步任务:提交后返回 task_id,再周期性查询 GET /v1/midjourney/{task_id} 拿状态,直到 SUCCESS / FAILURE
import time, httpx

def wait_task(task_id, timeout=300):
    deadline = time.time() + timeout
    while time.time() < deadline:
        resp = httpx.get(f"{HOST}/v1/midjourney/{task_id}",
                         headers={"Authorization": f"Bearer {API_KEY}"}).json()
        if resp["status"] in ("SUCCESS", "FAILURE"):
            return resp
        if resp["status"] == "MODAL":
            raise RuntimeError(f"task {task_id} 需要调 /modal 补参完成")
        time.sleep(3)
    raise TimeoutError(task_id)
  • 轮询节奏:建议 3–5s 一次,更高频无意义且浪费配额。
  • 不要在 web 请求里同步阻塞等任务完成 —— 提交后立即返回 task_id,让前端异步轮询。

Prompt 设计

好的 prompt:
a serene mountain lake at sunrise, photorealistic, soft golden light,
mist rising from water, snow-capped peaks in distance --ar 16:9 --v 8.1 --s 100
  • 主体在前:先主体,再描述场景,最后修饰词。
  • 结构化参数显式:用 --ar / --v / --s(或对应 body 字段)比依赖默认值更可控。
  • 避免歧义词photorealisticrealistic 更明确。
避免: 过于抽象(“make it good”)、主体散乱(多个并列对象不分主次)、给词加引号(会被当字面值)。 Niji 动漫:niji: true + version: "7",平台归一化为 --niji 7,计费走 midjourney@imagine-niji7

垫图最佳实践

来源推荐做法注意
用户上传先存自己的 OSS / CDN,提交时传该 URL不要直接传 base64(浪费带宽)
公开 URL直接传注意 SSRF(须公网可达)与 12 MiB 限制
第三方 / 其他产物先转存到自己的 OSS第三方 URL 可能过期
  • 压缩到 < 5 MiB:平台上限 12 MiB,但小图传输 / 处理都更快。
  • 格式 PNG / JPG / WebP 均可,推荐高质量 JPG。
  • 分辨率 1024–2048 px 已足够,更高浪费。
  • 垫图权重 iw(0–3,默认 1):>1 更贴原图,<1 更自由。

错误处理与重试策略

code含义重试策略
1 / 200成功
4 VALIDATION_ERROR参数错❌ 不要重试,修正参数
3 NOT_FOUND无可用实例 / task_id 不存在实例不可用可稍后重试;task_id 不存在不要重试
9 FAILURE服务拒绝 / 内部错误⏳ 可重试,指数退避(1s, 4s, 16s)
21 MODAL非终态✅ 继续调 /modal
24 BANNED_PROMPT敏感词❌ 不要重试,改 prompt;已自动退款
429限流⏳ 指数退避 + jitter
5xx / 网络错服务端 / 网络⏳ 指数退避,网络错可立即重试 1 次
import time, random, httpx

def submit_with_retry(payload, max_attempts=5):
    for attempt in range(max_attempts):
        try:
            r = httpx.post(f"{HOST}/v1/midjourney/generations/imagine",
                           json=payload,
                           headers={"Authorization": f"Bearer {API_KEY}"},
                           timeout=30)
            data = r.json()
            if r.status_code == 200 and data["code"] in (1, 200):
                return data
            if data["code"] in (4, 24):
                raise ValueError(data["description"])      # 不可重试
            if data["code"] == 3 and "task" in data["description"]:
                raise ValueError(data["description"])      # task_id 不存在
            # 其余(9 / 429 / 5xx)可重试
        except httpx.RequestError:
            pass
        time.sleep((4 ** attempt) + random.uniform(0, 1))  # 1s / 4s / 16s ...
    raise RuntimeError(f"达到最大重试次数 {max_attempts}")

二次操作流程

# imagine → 轮询 → upscale
imagine_id = submit({"prompt": "a cat"})["data"][0]["task_id"]
result = wait_task(imagine_id)           # grid_image_url + 4 张 image_urls + buttons
upscale_id = submit_to("/upscale", {"task_id": imagine_id, "index": 2})["data"][0]["task_id"]
final = wait_task(upscale_id)            # upscale 本地合成,1–2s
single_image = final["image_urls"][0]
局部重绘(inpaint → modal 两步):
imagine_id = submit({"prompt": "a portrait"})["data"][0]["task_id"]; wait_task(imagine_id)
upscale_id = submit_to("/upscale", {"task_id": imagine_id, "index": 1})["data"][0]["task_id"]; wait_task(upscale_id)

inpaint_id = submit_to("/inpaint", {"task_id": upscale_id})["data"][0]["task_id"]  # status=modal
# 前端画 mask(白=重绘区),上传到自己的 OSS 拿 mask_url
final = submit_to("/modal", {
    "task_id": inpaint_id,
    "prompt": "replace the eyes with cybernetic blue eyes",
    "mask_url": "https://your-oss.com/mask.png"
})
wait_task(final["data"][0]["task_id"])
⚠️ inpaint 进 MODAL 后 30 分钟内必须调 /modal,否则后台自动 CANCEL + 退款。

video 计费控制

  • 单段:batch_size: 1 → 扣 1 × midjourney@video
  • 批量 4 段:batch_size: 4 → 扣 4 × midjourney@video
  • 高清单段:video_type: "vid_1.1_i2v_720" + batch_size: 1 → 扣 1 × midjourney@video-720p
建议:出片只要 1 段就用 batch_size=1,批量比稿才用 4,不要默认开 4(成本翻 N 倍)。

并发与吞吐

import asyncio
sem = asyncio.Semaphore(10)  # 客户端最多 10 个并发提交

async def submit_one(prompt):
    async with sem:
        return await submit({"prompt": prompt})
  • 平台对每分钟提交数有上限,超出返回 429,需退避重试。
  • 实际生成并发由系统容量决定,超出会排队;任务长时间停在 SUBMITTED 通常是排队中。
  • 轮询务必带 sleep,不要无 sleep 死循环。

监控建议

指标参考阈值含义
任务 SUCCESS 率(近 1h)> 95%偏低说明服务 / 网络异常
平均完成耗时< 90s偏高说明排队
MODAL 停留任务数接近 0偏多说明客户端没调 /modal
code=24 比例< 5%偏高说明 prompt 频繁触发敏感词

排错清单

现象排查方向
任务长时间 SUBMITTED系统排队中,稍后再查
任务长时间 NOT_START平台稍后会自动超时退款,无需手动处理
任务 MODAL 超 30 分钟客户端没调 /modal,已被自动 CANCEL + 退款
prompt 字段为空describe 任务的文字结果在 description 字段
image_urls 少一张内容审核拦了部分图,看 fail_reason
计费超预期quota 字段;video 记得 × batch_size