【翻车复盘1】我以为 ChatGPT Image 2 出 Bug 了:这次翻车把我打醒了

0 阅读5分钟

这是第一篇“翻车复盘篇”:不吹模型,只讲真实踩坑和修复过程。

上一篇看结果,这一篇看过程。

【评测系列4】测试视角:我通宵测了 ChatGPT Image 2:100%通过背后,藏着1个危险信号​ 我通宵测了 C - 掘金

【评测系列3】测试角度:我把ChatGPT Images 2 当测试对象“暴力实测”了一遍,结果有点猛今天我没做“主观测 - 掘金

先给结论(先别急着骂模型)

我最初判断“是不是模型有 bug”并不离谱,但不完整。

这次翻车真正的根因是三类问题叠加:

  1. 任务约束不足(长提示词但缺少可执行结构)

  2. 生成路径不合适(直接文生图,无法强约束素材来源)

  3. 接口环境波动干扰(网络超时、连接中断、DNS 抖动)

最终我们通过“提示词重构 + 调用方式切换 + 调用容错”把结果修正到可交付状态。

一句话总结:

不是单点 bug,而是“模型约束 + 工程实现 + 网络链路”共同决定成败。


一、起点:我只是想做一张“第二波视觉复盘图”

背景很简单:

第二波 Vision 测试已经跑完,而且结果很好(4/4 PASS)。

我想把这套结果做成一张复盘海报放进公众号文章里。

我原本以为这事很直接:

gpt-image-2 一条长提示词,让它一次性生成完整信息图

这是最终的效果,但是最开始没有生成这张图

WAVE2_EDIT_COMPOSE_RICH.jpg


二、第一次翻车:图“看着对”,细节全错位

首版生成后,表面看没问题:

  • 有标题
  • 有模块
  • 有科技风排版

但细看就不对了:

  • 局部文案和我们的真实测试内容不一致

  • 素材图并不总是来自我们指定的测试图片

  • 个别数字和细节出现偏差

    ​编辑

  • 标红的图片跟我输入的图片不一致;

我当时第一反应:

“是不是 ChatGPT Image 2 有 bug?”


三、排查:把“感觉”变成“证据”

我没停在情绪判断,而是按工程方式拆问题。

1)先分层:模型问题 vs 链路问题

我把异常分成两类:

  • 模型侧:文字精确性、语义一致性、布局稳定性
  • 调用侧:接口超时、连接中断、参数兼容差异

实际日志里,链路波动确实很明显(超时、连接中断、DNS 失败都出现过)。

2)先改提示词

我做了几轮提示词优化:

  • 从自由叙述改成结构化约束
  • 固定模块
  • 固定数字
  • 固定问答
  • 增加“生成前自检”

结果:有改善,但仍不能保证“只用我们的真实素材图”。

3)关键转折:换成 images/edits

这里是整个复盘最关键一步:

从“纯文本生成”切到“上传素材图编辑组装”。

关键代码(核心调用):

def build_edits_url(base_url: str) -> str:
    cleaned = base_url.rstrip("/")
    if cleaned.endswith("/v1"):
        return f"{cleaned}/images/edits"
    return f"{cleaned}/v1/images/edits"
image_paths = [
    input_dir / "cat_only.jpg",
    input_dir / "room_layout.png",
    input_dir / "blur_error.png",
    input_dir / "product_1.jpg",
    input_dir / "product_2.jpg",
    input_dir / "product_3.jpg",
]
prompt = (
    "严格使用我上传的6张输入图进行组装,不要重新生成新场景素材。"
    "输出一张16:9中文复盘海报,标题“gpt-image-2 视觉理解测试复盘(第二波)”。"
)
data = {
    "model": "gpt-image-2",
    "prompt": prompt,
    "size": "2048x1152",
    "n": "1",
}
headers = {"Authorization": f"Bearer {api_key}"}
url = build_edits_url(base_url)
files = []
for p in image_paths:
    mime"image/png" if p.suffix.lower() == ".png" else "image/jpeg"
    files.append(("image[]", (p.name, p.read_bytes(), mime)))
resp = requests.post(
    url,
    headers=headers,
    data=data,
    files=files,
    timeout=300,
)

这段代码的核心意义是:

我们不再让模型“自由想象场景”,而是把真实测试图作为输入资产上传,让模型只做“组装与排版”。

核心约束明确写进提示词:

  • 必须使用 vision_inputs 的指定图片
  • 不允许重新生成新场景素材
  • 保留固定数据和结论文本

这一步之后,素材一致性才真正稳定下来。


四、第二次翻车:不是模型,而是工程细节

切到 edits 后并不是一次成功,仍踩了坑:

  • 上传写入超时(write timeout)
  • 接口参数不兼容(format 在该接口不支持)
  • 请求时间过短导致误判失败

处理方式:

  • 压缩输入图(保留内容,降低上传负担)
  • 去掉不兼容参数
  • 放宽超时并重跑
  • 持续记录状态码、耗时、错误信息

最后拿到 200 成功返回,目标图落盘。


五、我从“怀疑 bug”到“复盘闭环”的关键变化

变化 1:从“追求一次完美生成”改成“可控流程”

  • 先定义约束
  • 再选调用路径
  • 最后做容错与回归

变化 2:从“看图感觉”改成“CSV证据”

  • 每次调用有状态码
  • 每次失败有错误信息
  • 每次输出有文件路径

变化 3:从“模型评价”升级为“系统评价”

模型能力只是其中一环,链路、参数、提示词同样决定最终质量。


六、这次翻车的真正价值

这次最有价值的不是“我又做出一张图”,而是我拿到了一套可复现的方法:

  • 出现异常时,如何快速分层定位
  • 什么情况下该改提示词,什么情况下该换接口
  • 如何在不稳定链路下保证结果可追踪、可恢复

如果你也在做 AI 落地,这件事特别重要:

真正的生产力,不是一次偶然成功,而是可复现成功。


文末互动

如果你也遇到过“明明看着像 bug,但又说不清”的场景,欢迎评论区交流。

我下一篇可以继续拆:

  • 提示词稳定性评分表怎么做
  • 什么时候必须从 generations 切到 edits
  • 如何把这套复盘流程沉淀成团队模板

也欢迎您点赞,转发,您的支持是我坚持下去的动力,感谢。