vlm.md
← 所有食谱 · 多图推理 · 入门

前后图片对比分析

让 agent 对比两张图片(UI 截图、产品图、文档标注前后)并输出结构化的变更清单。

2026/4/30 · vlm.md · 推荐模型: GPT-4oGemini 1.5 ProClaude 3.5 Sonnet

场景

你的 agent 需要比较同一对象的两个版本,并生成一份变更报告:

  • UI 截图:前端部署前后的页面截图,找出布局、文字、颜色的变化
  • 产品图:修图前后的商品照片,识别裁剪、调色、水印等改动
  • 文档标注:PDF 审阅前后,找出新增批注、删除段落、修改内容

核心需求:不是像素级差异,而是语义层面的”什么发生了变化”。

推荐模型

模型适用场景
GPT-4o均衡首选,对 UI 变更描述准确,JSON 输出稳定
Gemini 1.5 Pro对图片细节的感知力强,适合产品图对比
Claude 3.5 Sonnet结构化输出质量高,变更分类更精准

建议使用 GPT-4o 或 Claude 3.5 Sonnet,二者在结构化 diff 输出上最为可靠。

Prompt 模板

你将收到两张图片:第一张是「变更前」,第二张是「变更后」。
请分析两张图片的语义差异,并严格按以下 JSON 格式输出,不要有任何多余文字。

{
  "summary": "一句话总结变更",
  "changes": [
    {
      "type": "added" | "removed" | "modified",
      "element": "发生变化的元素名称或位置描述",
      "before": "变更前的状态(如不适用则为 null)",
      "after": "变更后的状态(如不适用则为 null)",
      "severity": "critical" | "warning" | "info"
    }
  ]
}

注意:
- 忽略抗锯齿、字体渲染等像素级细微差异
- 只报告用户可感知的语义变化
- 如果两张图片完全相同,changes 返回空数组

代码示例

import base64
import json
from pathlib import Path
from openai import OpenAI

client = OpenAI()

SYSTEM_PROMPT = "你是一个图片差异分析专家,只输出 JSON,不输出任何其他内容。"

DIFF_PROMPT = """你将收到两张图片:第一张是「变更前」,第二张是「变更后」。
请分析两张图片的语义差异,并严格按以下 JSON 格式输出,不要有任何多余文字。

{
  "summary": "一句话总结变更",
  "changes": [
    {
      "type": "added | removed | modified",
      "element": "发生变化的元素名称或位置描述",
      "before": "变更前的状态(如不适用则为 null)",
      "after": "变更后的状态(如不适用则为 null)",
      "severity": "critical | warning | info"
    }
  ]
}

注意:
- 忽略抗锯齿、字体渲染等像素级细微差异
- 只报告用户可感知的语义变化
- 如果两张图片完全相同,changes 返回空数组"""


def encode_image(path: str) -> tuple[str, str]:
    """返回 (base64_data, mime_type)"""
    suffix = Path(path).suffix.lower().lstrip(".")
    mime = {"jpg": "image/jpeg", "jpeg": "image/jpeg", "png": "image/png", "webp": "image/webp"}.get(suffix, "image/jpeg")
    data = base64.b64encode(Path(path).read_bytes()).decode()
    return data, mime


def compare_images(before_path: str, after_path: str) -> dict:
    before_data, before_mime = encode_image(before_path)
    after_data, after_mime = encode_image(after_path)

    response = client.chat.completions.create(
        model="gpt-4o",
        response_format={"type": "json_object"},
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {
                "role": "user",
                "content": [
                    # 明确标注顺序,避免模型混淆 before/after
                    {"type": "text", "text": "【变更前图片】"},
                    {"type": "image_url", "image_url": {"url": f"data:{before_mime};base64,{before_data}"}},
                    {"type": "text", "text": "【变更后图片】"},
                    {"type": "image_url", "image_url": {"url": f"data:{after_mime};base64,{after_data}"}},
                    {"type": "text", "text": DIFF_PROMPT},
                ],
            },
        ],
        max_tokens=1024,
    )

    return json.loads(response.choices[0].message.content)


if __name__ == "__main__":
    result = compare_images("screenshot_before.png", "screenshot_after.png")
    print(json.dumps(result, ensure_ascii=False, indent=2))

运行:

pip install openai
python compare_images.py

预期输出:

{
  "summary": "导航栏新增了「帮助」按钮,主标题文字由蓝色改为深灰色",
  "changes": [
    {
      "type": "added",
      "element": "导航栏 - 帮助按钮",
      "before": null,
      "after": "右上角新增「帮助」文字链接",
      "severity": "info"
    },
    {
      "type": "modified",
      "element": "主标题文字颜色",
      "before": "#1a73e8(蓝色)",
      "after": "#333333(深灰色)",
      "severity": "warning"
    }
  ]
}

踩坑记录

坑 1:问”有什么不同?“太模糊,输出五花八门

直接问模型”这两张图有什么不同”,它可能描述光线差异、图片压缩噪点、字体渲染差别……几乎没有可用信息。解决方案:提供结构化输出 schema,明确 type(added/removed/modified)和 severity 字段,强制模型给出分类清晰的结果。

坑 2:图片顺序不明确导致 before/after 颠倒

多图 API 调用中,模型有时会搞不清哪张是”前”哪张是”后”。务必在每张图片前加文字标注,如上面代码中的 【变更前图片】 / 【变更后图片】,而不是依赖图片在消息列表中的位置顺序。

坑 3:抗锯齿和字体微调导致”误报”

同一页面在不同分辨率或操作系统下截图,字体渲染会有亚像素级差异,VLM 有时会将其报告为”文字样式变化”。在 prompt 中明确要求”忽略抗锯齿、字体渲染等像素级细微差异,只报告用户可感知的语义变化”可以显著减少误报。