MM×CODE 多模态(看图)× Coding 评测案例集 v0.1
CASE C02

视觉回归闭环

看差异图后能否收敛——这是评分机制,不是区分度题目

modeiterative_render_loopscorerrender_diff结构3 轮迭代自动化5/5趣味2/5Live Run2026-08-22 已实跑 ↓
01

输入与证据锚点

输入 INPUT

一张现实中截来的图表(新闻 / 他人 PPT),非合成

证据锚点 EVIDENCE ANCHOR
「chart-to-code 已被 ChartMimic / Plot2Code / ChartX 反复刷过,作为题目区分度低」
02

双轴设计

轴一 · 能力 AXIS Ⅰ
三轮后相似度
测「能不能收敛到与原图逐像素接近」
AXIS Ⅱ 轴二 · 节制
单调改进率
测「会不会越改越偏——相似度必须一轮不低于一轮」
两轴独立计分、独立报告,合起来画 Pareto 前沿——只报轴一的横评没有信息量。
03

Prompt

同一份 PROMPT 贯穿 3 轮(rounds: 3):首轮只有原图;第二轮起每次附上一版渲染与差异热力图。

ROUND 1/3首轮 · 从零复刻只看原图复刻这张图表。输出可直接运行的 Python(matplotlib)代码,目标是渲染结果与…
复刻这张图表。输出可直接运行的 Python(matplotlib)代码,目标是渲染结果与原图逐像素接近,
而不是"做一张类似的图"。
ROUND 2–3/3第二、三轮 · 差异驱动修正加送上一版渲染+差异热力图第二轮起,你会收到你上一版的渲染结果、以及它与原图的差异热力图。
第二轮起,你会收到你上一版的渲染结果、以及它与原图的差异热力图。
请只修改差异热力图指出的部分,并在每轮开头用一句话说明你这轮改了什么、为什么。
SOURCE完整 PROMPT 原文两段合并 · 一键复制与 source/cases.yaml 逐字一致,含段间空行。
复刻这张图表。输出可直接运行的 Python(matplotlib)代码,目标是渲染结果与原图逐像素接近,
而不是"做一张类似的图"。

第二轮起,你会收到你上一版的渲染结果、以及它与原图的差异热力图。
请只修改差异热力图指出的部分,并在每轮开头用一句话说明你这轮改了什么、为什么。
执行闭环 EXECUTION LOOP · rounds: 3 · scorer: render_diff
原图 R1 · 生成代码 渲染 v1 R2 · 差异修正 渲染 v2 R3 · 差异修正 渲染 v3 render_diff 差异热力图 差异热力图 S₁ · 20分 S₂ S₃ · 40分
每个「渲染 vᵢ」都与原图比对产出差异热力图,喂给下一轮代码修改。S₁ / S₃ 对应 rubric 的「首轮相似度」(20)与「三轮后相似度」(40);S₂ ≥ S₁ 且 S₃ ≥ S₂ 是「单调改进」(30)的判据,回退一次 −15;三轮代码全部无异常运行另得「可执行率」10 分。本图为机制示意,非真实数据。
04

Rubric 评分表

维度满分由谁打判据
首轮相似度20脚本1 - 归一化像素距离(结构相似度 SSIM 与色彩直方图距离各半)。
三轮后相似度40脚本同上,取第三轮。
单调改进30脚本每轮相似度不下降记满;出现回退每次 -15。越改越偏是主要失败模式。
可执行率10脚本三轮代码全部无异常运行记满。

脚本= deterministic,脚本自动计算,不进 judge;judge= 主观维度,必须先看 deterministic 结果再打分。本案例总分归一化到 0-100。

本案例 4 个维度全部为 deterministic——这正是它的考点:评分机制本身即题目,收敛过程完全由脚本客观度量,无需人工或 judge 介入。

05

跑批命令

python run_batch.py --models anthropic/claude-opus-4.5 openai/gpt-5.2 google/gemini-3-pro moonshotai/kimi-k2.5 --only C02
python score.py --judge anthropic/claude-opus-4.5 --out report.csv

数据准备:按 data/C02/manifest.jsonl 的格式放置原图与真值。run_batch 会自动执行 3 轮渲染—比对—修正闭环;因 4 个维度全部 deterministic,score 阶段的 judge 实际不参与本案例打分。

06

实测结果 · Live Run

真实数据 · 2026-08-22 · 3 模型
真实性声明 AUTHENTICITY

以下是 2026 年 8 月 22 日三个模型经 OpenRouter chat/completions 接口(temperature=0)各自真实跑完本案例的结果:stealth/ox-alpha、z-ai/glm-5v-turbo、moonshotai/kimi-k3。三个模型面对的是同一张输入图片、同一份首轮 Prompt(三份 JSON 直接比对逐字相同);回复是各模型原话(未润色、未改写),分数是评分脚本对真实渲染像素计算出的数值。样本规模为演示级(每模型 1 个样本 × 3 轮迭代,远小于原始题库设计)。数据出处:results/C02.json · results/z-ai__glm-5v-turbo/C02.json · results/moonshotai__kimi-k3/C02.json · data/C02/manifest.jsonl。

modelsox-alpha · glm-5v-turbo · kimi-k3 date2026-08-22 samples3 模型 × 1(item_id: main) rounds3 scorerrender_diff · SSIM+色彩直方图 @300×200 温度temperature=0
真实输入图片:季度营收柱状图 target.png
data/C02/images/target.png · PNG · 640×420 · RGB —— 本案例唯一的真实输入图片,第一轮原样发给三个模型;ox-alpha 与 glm-5v-turbo 的第二、三轮按其提示语继续随附「原图+上一版渲染+差异热力图」,kimi-k3 第 2、3 轮收到的则是另一条恢复提示(原文见下方实录)。
关于这次运行
  • manifest 的 vars 为空({}),三个模型的首轮 Prompt 与 source/cases.yaml 模板逐字一致(三份 JSON 直接比对);第 2、3 轮实际发送的提示语与设计模板措辞不同:ox-alpha 与 glm-5v-turbo 收到的是「这是你上一版的渲染结果与差异热力图……」,而 kimi-k3 因首轮无可执行代码,harness 对其改发统一的恢复提示「你上一轮的代码没有成功渲染出图……」——均以本页展示的实际文本为准。
  • 真值(manifest truth):chart_type = bar · categories = Q1–Q4 · values = [32, 58, 41, 73]。三个模型都正确读出了这组数值与 Q1–Q4 分类;配色各自近似还原(ox-alpha 用 Excel 默认系,glm-5v-turbo 用近 Excel 系,kimi-k3 用 seaborn deep 系)。
  • 三个模型的三轮闭环全部真实执行:回复→抽取 Python 代码→matplotlib 真实出图→与原图逐像素算 SSIM+色彩直方图距离,无任何启发式近似。
  • stealth/ox-alpha:前两轮稳定收敛(0.746 → 0.781),第三轮未收到任何模型输出而归零,双轴同时清零。
  • z-ai/glm-5v-turbo:首轮相似度 0.7554 为三模型最高,但第 2、3 轮代码虽完整给出却执行失败(unknown_error),后两轮相似度记 0,同样双轴清零。
  • moonshotai/kimi-k3:首轮空响应记 0 后,在第 3 轮以 0.802 真实收敛——三模型中唯一的非零「三轮后相似度」,单调改进 1.0。
三轮实测实录 ROUND-BY-ROUND · Prompt 与回复均为逐字原文(各模型取自各自的 results/*/C02.json)
附图说明:三个模型第 1 轮的图像输入都是本页上方那张原图(同一文件原样发送,不重复贴图);差异热力图为每次成功渲染后的即时产物,runs_tmp/ 目前仅留存两张并已在 stealth/ox-alpha 实录中贴出——其余轮次要么没有成功渲染、要么产物未留存,本页如实留空。
stealth/ox-alpha强制深度推理 stealth 模型results/C02.json
ROUND 1/3首轮 · 从零复刻执行成功 · 相似度 0.7456输入:仅原图
实际发送 PROMPT(逐字 · 取自 results/C02.json prompt_text)
复刻这张图表。输出可直接运行的 Python(matplotlib)代码,目标是渲染结果与原图逐像素接近,
而不是"做一张类似的图"。

第二轮起,你会收到你上一版的渲染结果、以及它与原图的差异热力图。
请只修改差异热力图指出的部分,并在每轮开头用一句话说明你这轮改了什么、为什么。
本轮实际附带的图像输入
真实输入原图 target.png
原图 target.png
640×420 · data/C02/images/
注:harness 只把「差异热力图」落盘留存于 runs_tmp/,上一版渲染图未持久化,故此处无法展示当轮的渲染画面;热力图为 300×200 灰度差映射(绿=一致,红=差异)。
模型回复原文(stealth/ox-alpha · 未删改)
第一轮:先按原图的布局、配色和标注方式完整复刻一版,作为后续比对的基准。

```python
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib as mpl

# 中文字体支持
mpl.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei',
                                   'PingFang SC', 'Noto Sans CJK SC']
mpl.rcParams['axes.unicode_minus'] = False

fig = plt.figure(figsize=(6.4, 4.8), dpi=100)
# 手动定位坐标区,使绘图区边框位置与原图对齐
ax = fig.add_axes([0.086, 0.292, 0.82, 0.625])

quarters = ['Q1', 'Q2', 'Q3', 'Q4']
values   = [32, 58, 41, 73]
colors   = ['#4472C4', '#ED7D31', '#70AD47', '#C0504D']  # 蓝 / 橙 / 绿 / 红

bars = ax.bar(range(4), values, width=0.57, color=colors)

# 数值标签:位于柱顶上方、与柱子左边缘对齐(原图特征)
for rect, v in zip(bars, values):
    ax.text(rect.get_x(), v + 2.5, str(v),
            ha='left', va='bottom', fontsize=11)

ax.set_xlim(-0.62, 3.62)
ax.set_ylim(0, 80)
ax.set_xticks(range(4))
ax.set_xticklabels(quarters, fontsize=12)
ax.set_yticks([])                      # 原图无 y 轴刻度
ax.tick_params(axis='both', length=0)  # 无刻度线

# 标题在左上角、绘图区外
ax.set_title('季度营收(万元)', loc='left', fontsize=14, pad=12)

# 四周黑色边框
for s in ax.spines.values():
    s.set_visible(True)
    s.set_linewidth(1.0)
    s.set_color('black')

fig.savefig('chart.png', dpi=100)
plt.show()
```

要点说明:
- **布局**:用 `add_axes` 精确控制绘图区位置,使黑色边框与原图四边对齐;
- **配色**:蓝 `#4472C4`、橙 `#ED7D31`、绿 `#70AD47`、红 `#C0504D`;
- **数值标签**:原图中数字与柱子左边缘对齐(而非居中),故用 `ha='left'` 放在柱左上角;
- **无 y 轴**:隐藏 y 刻度与刻度线,仅保留 x 轴的 Q1–Q4 文字。

请发来渲染结果与差异热力图,我会在下一轮针对性修正。
ROUND 2/3差异修正执行成功 · 相似度 0.7814输入:原图+渲染v1+热力图①
实际发送 PROMPT(逐字 · 取自 results/C02.json prompt_text)
这是你上一版的渲染结果与差异热力图(红色越深差异越大)。只修改差异热力图指出的部分,先用一句话说明这轮改了什么、为什么。
本轮实际附带的图像输入
原图(供参照)
原图(供参照)
第 1 轮渲染与原图的差异热力图
差异热力图①
runs_tmp/c02_diff_r1.png
注:harness 只把「差异热力图」落盘留存于 runs_tmp/,上一版渲染图未持久化,故此处无法展示当轮的渲染画面;热力图为 300×200 灰度差映射(绿=一致,红=差异)。
模型回复原文(stealth/ox-alpha · 未删改)
本轮修改:依据差异热力图重新标定了整体几何参数——绘图区边框精确到 [0.075, 0.2917, 0.8313, 0.6292]、xlim 改为 (-0.496, 3.596)、柱宽改为 0.5(原图柱宽约为间距的一半),并微调了标题缩进、数值标签间距与各文字字号,消除此前的整体右移和柱子偏宽问题。

```python
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib as mpl

# 中文字体支持
mpl.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei',
                                   'PingFang SC', 'Noto Sans CJK SC']
mpl.rcParams['axes.unicode_minus'] = False

fig = plt.figure(figsize=(6.4, 4.8), dpi=100)
# 边框位置按原图实测:左 48px、右 580px、上 38px、下 340px
ax = fig.add_axes([0.075, 0.29167, 0.83125, 0.62917])

quarters = ['Q1', 'Q2', 'Q3', 'Q4']
values   = [32, 58, 41, 73]
colors   = ['#4472C4', '#ED7D31', '#70AD47', '#C0504D']  # 蓝 / 橙 / 绿 / 红

# 原图柱宽约 65px、中心间距约 130px → 宽度 = 0.5 个数据单位
bars = ax.bar(range(4), values, width=0.5, color=colors)

# 数值标签:与柱左边缘对齐,紧贴柱顶(间隙约 3px ≈ 0.8 数据单位)
for rect, v in zip(bars, values):
    ax.text(rect.get_x(), v + 0.8, str(v),
            ha='left', va='bottom', fontsize=13)

# xlim 由柱心像素位置反解:Q1 中心在 112.5px,间距 130px/单位
ax.set_xlim(-0.4962, 3.5961)
ax.set_ylim(0, 80)
ax.set_xticks(range(4))
ax.set_xticklabels(quarters, fontsize=13)
ax.set_yticks([])                       # 原图无 y 轴刻度
ax.tick_params(axis='x', length=0, pad=5.5)  # 标签顶部距轴约 8px

# 标题:左上角、相对边框左缘缩进约 7px、底部距框顶约 8px
ax.set_title('季度营收(万元)', loc='left', fontsize=15,
             pad=6, x=0.013)

# 四周黑色细边框
for s in ax.spines.values():
    s.set_visible(True)
    s.set_linewidth(1.0)
    s.set_color('black')

fig.savefig('chart.png', dpi=100)
plt.show()
```

主要改动点:
- **绘图区矩形**:由 `[0.086, 0.292, 0.82, 0.625]` 修正为实测值,边框四边与原图对齐;
- **水平刻度**:xlim 收窄为 4.0923 个单位宽(原图柱心间距 130px ÷ 边框宽 532px),消除柱子逐个右移的累积偏差;
- **柱宽**:0.57 → 0.5,与原图 65px 柱宽一致;
- **文字**:数值标签间隙从 2.5 收紧到 0.8(原贴柱顶),x 刻度标签加 `pad=5.5` 控制与轴的距离,标题加 `x=0.013` 缩进并下调 `pad`。

请发来新一轮渲染结果与差异热力图,我继续针对性修正。
ROUND 3/3差异修正无响应 · no_code_block · 记 0输入已发出 → 未收到任何回复
实际发送 PROMPT(逐字 · 取自 results/C02.json prompt_text)
这是你上一版的渲染结果与差异热力图(红色越深差异越大)。只修改差异热力图指出的部分,先用一句话说明这轮改了什么、为什么。
本轮实际附带的图像输入
原图(供参照)
原图(供参照)
第 2 轮渲染与原图的差异热力图
差异热力图②
runs_tmp/c02_diff_r2.png
注:harness 只把「差异热力图」落盘留存于 runs_tmp/,上一版渲染图未持久化,故此处无法展示当轮的渲染画面;热力图为 300×200 灰度差映射(绿=一致,红=差异)。
模型回复原文
该次请求未收到任何模型输出

第 3 轮请求已真实发出(附带原图、第 2 版渲染与新差异热力图),但未收到任何文本回复:response_text = null。harness 从中提取不到 Python 代码块,记录 exec_error = "no_code_block"、exec_ok = false,本轮相似度按 0 计。调用层在指数退避重试 5 次仍拿不到有效响应时会把 text 记为 null;本次结果文件未保存更底层的错误信息,因此无法进一步区分是接口故障还是模型返回了空内容——此处如实存疑,不作推断。

z-ai/glm-5v-turboGLM-5V · 推理行为未特别配置results/z-ai__glm-5v-turbo/C02.json
ROUND 1/3首轮 · 从零复刻执行成功 · 相似度 0.7554输入:仅原图
实际发送 PROMPT(逐字 · 取自 results/z-ai__glm-5v-turbo/C02.json prompt_text)
复刻这张图表。输出可直接运行的 Python(matplotlib)代码,目标是渲染结果与原图逐像素接近,
而不是"做一张类似的图"。

第二轮起,你会收到你上一版的渲染结果、以及它与原图的差异热力图。
请只修改差异热力图指出的部分,并在每轮开头用一句话说明你这轮改了什么、为什么。
模型回复原文(z-ai/glm-5v-turbo · 未删改)
这轮我主要修正了**颜色值**(使用更接近原图的RGB)、**柱子宽度**、**数值标签位置**(与柱顶距离)、**边框粗细**以及**标题位置**,以匹配原图的视觉比例。

```python
import matplotlib.pyplot as plt
import numpy as np

# 设置中文字体(根据系统调整,这里使用通用配置)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# 数据
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
values = [32, 58, 41, 73]
# 精确匹配原图颜色
colors = ['#4472C4', '#ED7D31', '#70AD47', '#C5504B']

# 创建图形,尺寸比例接近原图
fig, ax = plt.subplots(figsize=(7, 4.2))
fig.patch.set_facecolor('white')

# 绘制柱状图
bar_width = 0.55
x_pos = np.arange(len(quarters))
bars = ax.bar(x_pos, values, width=bar_width, color=colors, 
              edgecolor='black', linewidth=0.6, zorder=3)

# 添加数值标签(位置微调至柱子上方合适距离)
for i, (bar, value) in enumerate(zip(bars, values)):
    height = bar.get_height()
    ax.text(bar.get_x() + bar.get_width()/2., height + 1.2,
            f'{value}',
            ha='center', va='bottom', fontsize=11, color='black', 
            fontweight='normal')

# 设置X轴
ax.set_xticks(x_pos)
ax.set_xticklabels(quarters, fontsize=11)
ax.tick_params(axis='x', length=0)  # 移除X轴刻度线

# 设置Y轴(隐藏刻度线和标签,只保留轴线作为边界)
ax.set_yticks([])
ax.set_ylim(0, 82)

# 设置边框(原图有完整的黑色矩形边框)
for spine in ax.spines.values():
    spine.set_visible(True)
    spine.set_linewidth(1.2)
    spine.set_color('black')

# 标题设置(左对齐,与原图一致)
ax.set_title('季度营收(万元)', fontsize=14, loc='left', pad=12, 
             fontweight='normal', color='black')

# 移除网格
ax.grid(False)

# 调整布局
plt.tight_layout()

# 保存图片
plt.savefig('quarterly_revenue.png', dpi=150, bbox_inches='tight', 
            facecolor='white', edgecolor='none')
plt.show()
```
ROUND 2/3差异修正代码已提取 · 执行失败 unknown_error · 记 0输入:原图+渲染v1+热力图①(据提示语)
实际发送 PROMPT(逐字 · 取自 results/z-ai__glm-5v-turbo/C02.json prompt_text)
这是你上一版的渲染结果与差异热力图(红色越深差异越大)。只修改差异热力图指出的部分,先用一句话说明这轮改了什么、为什么。
模型回复原文(z-ai/glm-5v-turbo · 未删改)
这轮我修正了**背景色确保为纯白**、**精确校准了四根柱子的RGB颜色值**以匹配原图色调、**调整了图形比例和边距**使布局与原图一致,并**强化了边框线条**。

```python
import matplotlib.pyplot as plt
import numpy as np

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 11

# 数据
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
values = [32, 58, 41, 73]
# 精确匹配原图的RGB颜色
colors = ['#4472C4', '#ED7D31', '#70AD47', '#C5504B']

# 创建图形 - 调整尺寸比例更接近原图
fig, ax = plt.subplots(figsize=(6.5, 4))
fig.patch.set_facecolor('white')  # 确保背景纯白
ax.set_facecolor('white')

# 绘制柱状图
bar_width = 0.5
x_pos = np.arange(len(quarters))
bars = ax.bar(x_pos, values, width=bar_width, color=colors, 
              edgecolor='black', linewidth=0.8, zorder=3)

# 添加数值标签
for bar, value in zip(bars, values):
    height = bar.get_height()
    ax.text(bar.get_x() + bar.get_width()/2., height + 1.0,
            f'{value}',
            ha='center', va='bottom', fontsize=11, color='black')

# 设置坐标轴
ax.set_xticks(x_pos)
ax.set_xticklabels(quarters, fontsize=11)
ax.set_yticks([])  # 隐藏Y轴刻度
ax.set_ylim(0, 80)

# 设置边框 - 确保四边都有黑色边框
for spine in ax.spines.values():
    spine.set_visible(True)
    spine.set_linewidth(1.2)
    spine.set_color('black')

# 标题 - 左对齐
ax.set_title('季度营收(万元)', fontsize=13, loc='left', pad=10, 
             fontweight='normal', color='black')

# 移除网格线
ax.grid(False)

# 调整布局,减少空白
plt.subplots_adjust(left=0.08, right=0.98, top=0.92, bottom=0.12)

# 保存图片 - 使用高DPI并确保白色背景
plt.savefig('quarterly_revenue_v2.png', dpi=150, bbox_inches='tight', 
            facecolor='white', edgecolor='none')
plt.show()
```
该轮模型有完整回复且代码块提取成功(json 的 code 字段非空),但脚本执行报 exec_error = "unknown_error"、无渲染产物可比对,相似度按 0 记;结果文件未保留更底层的报错信息,无法进一步定位失败原因。
ROUND 3/3差异修正代码已提取 · 执行失败 unknown_error · 记 0输入:原图+渲染v2+热力图②(据提示语)
实际发送 PROMPT(逐字 · 取自 results/z-ai__glm-5v-turbo/C02.json prompt_text)
这是你上一版的渲染结果与差异热力图(红色越深差异越大)。只修改差异热力图指出的部分,先用一句话说明这轮改了什么、为什么。
模型回复原文(z-ai/glm-5v-turbo · 未删改)
这轮我**重新校准了所有颜色的精确RGB值**、**调整了画布比例和边距**以匹配原图的宽高比、**修正了数值标签的垂直偏移量**,并**确保保存时使用sRGB色彩空间**以避免颜色偏差。

```python
import matplotlib.pyplot as plt
import numpy as np

# 字体设置
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.family'] = 'sans-serif'

# 数据
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
values = [32, 58, 41, 73]
# 精确匹配原图的颜色(基于标准Excel配色)
colors = ['#4472C4', '#ED7D31', '#70AD47', '#C5504B']

# 创建图形 - 调整为更接近原图的比例
fig, ax = plt.subplots(figsize=(7, 4.5))
fig.patch.set_facecolor('#FFFFFF')  # 纯白背景
ax.set_facecolor('#FFFFFF')

# 绘制柱状图
bar_width = 0.55
x_pos = np.arange(len(quarters))
bars = ax.bar(x_pos, values, width=bar_width, color=colors, 
              edgecolor='#000000', linewidth=0.8, zorder=3)

# 添加数值标签 - 调整位置使其紧贴柱顶
for bar, value in zip(bars, values):
    height = bar.get_height()
    ax.text(bar.get_x() + bar.get_width()/2., height + 0.8,
            str(value),
            ha='center', va='bottom', fontsize=11, color='#000000',
            fontweight='normal')

# X轴设置
ax.set_xticks(x_pos)
ax.set_xticklabels(quarters, fontsize=11, color='#000000')
ax.tick_params(axis='x', length=0)  # 无刻度线

# Y轴设置 - 完全隐藏但保留空间
ax.set_yticks([])
ax.set_ylim(0, 82)

# 边框设置 - 四边黑色边框
for spine in ['top', 'bottom', 'left', 'right']:
    ax.spines[spine].set_visible(True)
    ax.spines[spine].set_linewidth(1.0)
    ax.spines[spine].set_color('#000000')

# 标题设置 - 左对齐,适当间距
ax.set_title('季度营收(万元)', fontsize=13, loc='left', pad=12,
             fontweight='normal', color='#000000')

# 移除网格
ax.grid(False)

# 精确控制布局边距
plt.subplots_adjust(left=0.08, right=0.96, top=0.90, bottom=0.15)

# 保存 - 使用高质量设置确保颜色准确
plt.savefig('quarterly_revenue_v3.png', dpi=150, bbox_inches='tight',
            facecolor='#FFFFFF', edgecolor='none', 
            pil_kwargs={'quality': 95})
plt.show()
```
同第 2 轮:回复与代码块完整,执行再次报 unknown_error,相似度按 0 记。三轮闭环到此结束:只有第 1 轮留下了可比对的渲染。
moonshotai/kimi-k3Kimi K3 · 推理行为未特别配置results/moonshotai__kimi-k3/C02.json
ROUND 1/3首轮 · 从零复刻无响应 · no_code_block · 记 0输入:仅原图
实际发送 PROMPT(逐字 · 取自 results/moonshotai__kimi-k3/C02.json prompt_text)
复刻这张图表。输出可直接运行的 Python(matplotlib)代码,目标是渲染结果与原图逐像素接近,
而不是"做一张类似的图"。

第二轮起,你会收到你上一版的渲染结果、以及它与原图的差异热力图。
请只修改差异热力图指出的部分,并在每轮开头用一句话说明你这轮改了什么、为什么。
模型回复原文
该次请求未收到任何模型输出

第 1 轮请求已真实发出(附带原图),但未收到任何文本回复:response_text = null。harness 提取不到 Python 代码块,记录 exec_error = "no_code_block"、exec_ok = false,本轮相似度按 0 计——与 stealth/ox-alpha 第 3 轮属同一失败模式。同样地,结果文件未保存更底层的错误信息,无法区分是接口故障还是模型返回了空内容——如实存疑,不作推断。由于第 1 轮没有可执行代码,下一轮起 harness 不再发送「差异热力图修正」模板,而改发统一的恢复提示(见第 2、3 轮)。

ROUND 2/3恢复重试代码已提取 · 执行失败 unknown_error · 记 0harness 恢复提示 · 第 2 次尝试
实际发送 PROMPT(harness 恢复提示 · 逐字取自 results/moonshotai__kimi-k3/C02.json prompt_text)
你上一轮的代码没有成功渲染出图(可能没给出代码块,或者代码执行失败)。请重新输出一版完整、可独立运行的代码。
模型回复原文(moonshotai/kimi-k3 · 未删改)
本轮说明:上一版代码未能成功渲染,因此重新输出一版完整、可独立运行的代码,并按原图精确还原画布尺寸(640×420)、坐标轴位置、柱宽、配色(seaborn deep 色系)、数值标签位置及左侧标题。

```python
import matplotlib.pyplot as plt
from matplotlib import font_manager

# 标题需要中文字体;数字与刻度标签保持 matplotlib 默认的 DejaVu Sans
_cjk_candidates = ['SimHei', 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB',
                   'Noto Sans CJK SC', 'WenQuanYi Zen Hei', 'Arial Unicode MS']
_available = {f.name for f in font_manager.fontManager.ttflist}
_cjk = next((n for n in _cjk_candidates if n in _available), None)
title_font = font_manager.FontProperties(family=_cjk, size=13) if _cjk else None

# 数据
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
values = [32, 58, 41, 73]
colors = ['#4C72B0', '#DD8452', '#55A868', '#C44E52']

# 画布 640x420,坐标区位置按原图像素换算
fig, ax = plt.subplots(figsize=(6.4, 4.2), dpi=100)
fig.subplots_adjust(left=0.094, right=0.906, top=0.905, bottom=0.190)

# 柱状图(分类轴默认 xlim=(-0.5, 3.5),柱宽 0.5)
bars = ax.bar(quarters, values, width=0.5, color=colors)

# 柱顶数值标签
for rect, v in zip(bars, values):
    ax.text(rect.get_x() + rect.get_width() / 2, v + 2, str(v),
            ha='center', va='bottom', fontsize=10, color='black')

# y 轴:0~80,无刻度无标签;x 轴只保留文字标签、无 tick 小竖线
ax.set_ylim(0, 80)
ax.set_yticks([])
ax.tick_params(axis='x', length=0, labelsize=10)

# 标题:靠左、位于坐标区上方
if title_font is not None:
    ax.set_title('季度营收(万元)', loc='left', fontproperties=title_font, pad=8)
else:
    ax.set_title('季度营收(万元)', loc='left', fontsize=13, pad=8)

# 四周黑色边框
for spine in ax.spines.values():
    spine.set_visible(True)
    spine.set_edgecolor('black')
    spine.set_linewidth(1.5)

fig.savefig('quarterly_revenue.png', dpi=100)
plt.show()
```
该轮模型有完整回复且代码块提取成功,但脚本执行仍报 exec_error = "unknown_error",相似度按 0 记;底层报错信息未留存,无法进一步定位失败原因。
ROUND 3/3恢复重试执行成功 · 相似度 0.802 —— 全场最高单轮harness 恢复提示 · 第 3 轮真实出图
实际发送 PROMPT(harness 恢复提示 · 逐字取自 results/moonshotai__kimi-k3/C02.json prompt_text)
你上一轮的代码没有成功渲染出图(可能没给出代码块,或者代码执行失败)。请重新输出一版完整、可独立运行的代码。
模型回复原文(moonshotai/kimi-k3 · 未删改)
本轮说明:上一版可能因后端或字体探测逻辑导致执行失败,本轮显式使用 Agg 后端、简化字体处理并加异常保护,保证代码在任何环境都能独立运行并保存出图;图表参数(640×420 画布、坐标区位置、柱宽 0.5、配色、标签位置)保持不变。

```python
import matplotlib
matplotlib.use('Agg')  # 无显示环境下也能正常渲染保存
import matplotlib.pyplot as plt
from matplotlib import font_manager

# ---------- 中文字体(仅标题用;找不到则退回默认字体) ----------
title_font = None
try:
    candidates = ['SimHei', 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB',
                  'Noto Sans CJK SC', 'WenQuanYi Zen Hei', 'Arial Unicode MS']
    available = {f.name for f in font_manager.fontManager.ttflist}
    for name in candidates:
        if name in available:
            title_font = font_manager.FontProperties(family=name, size=13)
            break
except Exception:
    title_font = None

# ---------- 数据 ----------
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
values = [32, 58, 41, 73]
colors = ['#4C72B0', '#DD8452', '#55A868', '#C44E52']  # seaborn deep 前四色

# ---------- 画布与坐标区(按原图 640x420 像素换算) ----------
fig, ax = plt.subplots(figsize=(6.4, 4.2), dpi=100)
fig.subplots_adjust(left=0.094, right=0.906, top=0.905, bottom=0.190)

# ---------- 柱状图 ----------
bars = ax.bar(quarters, values, width=0.5, color=colors)

# ---------- 柱顶数值标签 ----------
for rect, v in zip(bars, values):
    ax.text(rect.get_x() + rect.get_width() / 2, v + 2, str(v),
            ha='center', va='bottom', fontsize=10, color='black')

# ---------- 坐标轴样式 ----------
ax.set_ylim(0, 80)
ax.set_yticks([])                      # y 轴无刻度无标签
ax.tick_params(axis='x', length=0, labelsize=10)  # x 轴只留文字、无 tick 线

# ---------- 标题(靠左,位于坐标区上方) ----------
if title_font is not None:
    ax.set_title('季度营收(万元)', loc='left', fontproperties=title_font, pad=8)
else:
    ax.set_title('季度营收(万元)', loc='left', fontsize=13, pad=8)

# ---------- 四周黑色边框 ----------
for spine in ax.spines.values():
    spine.set_visible(True)
    spine.set_edgecolor('black')
    spine.set_linewidth(1.5)

# ---------- 保存 ----------
fig.savefig('quarterly_revenue.png', dpi=100)
print('saved: quarterly_revenue.png')
```
本轮代码真实执行出图,与原图比对得 0.802——三模型全部轮次中最高的单轮相似度;该轮差异热力图未在 runs_tmp 以独立文件留存,本页无法展示。
分数 SCORE · stealth/ox-alpha 明细(脚本对真实渲染像素计算,数值逐字取自 results/C02.json)
S₁0.7456→ S₂0.7814 ↑→ S₃无输出 · 记 0⇒ 双轴轴一 0.0 / 轴二 0.0
维度满分由谁打实测值判定依据(本次实际发生的事)
首轮相似度20脚本0.746第 1 轮代码真实执行出图,与原图按 SSIM+色彩直方图各半综合比对(原始记录 similarity = 0.7456)。
三轮后相似度40脚本0.000第 3 轮无任何输出→无代码可执行→无渲染可比,脚本按 0 记;前两轮的收敛成果因此被机制清零。
单调改进30脚本0.000regression_between_rounds相似度序列 0.7456 → 0.7814 → 0.000 在末轮回退,判负并触发 flags 标记。
可执行率10脚本0.667(3 轮中 2 轮)第 1、2 轮代码均无异常运行并出图;第 3 轮无代码可执行。

双轴读数(取自 results/C02.json 的 run.axis_1 / run.axis_2):轴一 · 能力(三轮后相似度)= 0.0 · 轴二 · 节制(单调改进率)= 0.0。results/C02.json 顶层的 score 字段为 null——脚本未输出加权百分制总分,本页因此不自行折算总分,只如实呈现上表各维度的原始值。

本案例 4 个维度全部为真实执行的测量(代码真实运行、像素真实比对),不涉及其它案例那类「沙箱启发式匹配」的近似判定;results/C02.json 的 limitations 仅注明规模:

  • 样本规模为演示级缩小版(1),非原始题库规模
  • 三模型分数对比 SCORE × 3 MODELS · 各维数值逐字取自三份 results/*/C02.json 的 score 字段
    模型图例 MODELSGLM-X-Preview-F (stealth/ox-alpha)GLM-5V (glm-5v-turbo)Kimi K3
    首轮相似度满分 20轴 0 – 1
    0.746
    0.755
    0
    kimi-k3 首轮空响应(no_code_block)相似度按 0 计;glm-5v-turbo 的 0.755 为三者最高,ox-alpha 以 0.746 紧随其后。
    三轮后相似度满分 40轴 0 – 1
    0
    0
    0.802
    只有 kimi-k3 在第 3 轮真实出图收敛到 0.802;ox-alpha 第 3 轮空响应、glm-5v-turbo 第 3 轮代码执行失败,均被机制清零。
    单调改进满分 30轴 0 – 1
    0regression_between_rounds
    0regression_between_rounds
    1
    相似度序列:ox 0.7456→0.7814→0、glm 0.7554→0→0,均在末轮回退并触发标记;kimi 0→0→0.802 从未下降,判满且无任何 flags。
    可执行率满分 10轴 0 – 1
    0.667
    0.333
    0.333
    ox-alpha 3 轮中 2 轮跑通;glm-5v-turbo 仅第 1 轮;kimi-k3 仅第 3 轮。

    双轴读数(各 JSON 的 run.axis_1 / run.axis_2):轴一 · 能力(三轮后相似度)= 0.0 / 0.0 / 0.802 · 轴二 · 节制(单调改进率)= 0.0 / 0.0 / 1.000——本案例唯一双轴非零的是 moonshotai/kimi-k3。三份结果文件顶层 score 字段均为 null(脚本未输出加权百分制总分),本页不自行折算总分。

    三个模型在本案例都产出了完整的四维自动分数(无 no_auto_score / no_score_found 一类的缺失状态);上表所有数字均可在对应 JSON 的 items[0].score 中逐字找到。

    方法论局限(沿用总览对比页的说明):三个模型的调用方式相同——同一套 run_batch.py 风格的 OpenRouter chat/completions 调用、同一批图片、temperature=0;某轮没有可执行代码时收到的也是同一条 harness 恢复提示。但 stealth/ox-alpha 是强制深度推理的 stealth 模型,glm-5v-turbo 与 kimi-k3 未特别配置推理行为——输出风格与推理深度可能天然不同。这是解读对比时的已知局限,不是哪一方「作弊」。

    结果解读 READING

    ① 前两轮是真实的「看图修正」:S₁ 0.7456 → S₂ 0.7814,模型从差异热力图反解出了具体偏差并逐一修正——边框像素位置(左48 / 右580 / 上38 / 下340)、柱宽(65px ≈ 0.5 数据单位)、标签间隙与字号——而不是泛泛重写一版。这正是本题想考的「能否读懂差异图并收敛」。

    ② 翻车发生在第 3 轮,且不是「越改越偏」式的视觉回退:前两轮相似度一直在上升,第 3 轮是彻底没收到输出。按评分机制,这一次空响应就把「三轮后相似度」清零、判负「单调改进」,双轴同时归零。

    ③ 这恰好撞上本题的设计论点——「评分机制本身即题目」:闭环对单轮故障零容忍,前两轮的全部收敛成果被最后一次空响应抹平。这是机制的真实行为,也是它脆弱的一面;横向对比其它模型时,这一维度同时度量了能力与稳定性。

    ④ 如实存疑处:结果文件只记录了 response_text = null 与 no_code_block,未保留底层错误信息,无法区分是接口故障还是模型返回了空内容。页面不作推断,仅呈现记录。

    三模型解读 CROSS-MODEL READING

    ① 迭代方向完全相反:首轮 ox-alpha 0.746 与 GLM-5V 0.755 并列领先、Kimi K3 因空响应垫底记 0;三轮之后唯一收敛到非零相似度的恰是开局最差的 Kimi K3(0.802),另两家双双归零并都触发 regression_between_rounds 标记——与总览对比页对本案例「迭代方向完全相反」的判定一致。

    ② 三家的失败方式各不相同,且都不是「越改越偏」式的视觉回退:ox-alpha 死于第 3 轮彻底空响应;GLM-5V 三轮都有完整回复和可提取的代码块,但第 2、3 轮代码执行失败(unknown_error),渲染不出可比像素;Kimi K3 第 1 轮同样空响应,却在 harness 恢复提示给的第 2、3 次机会里于末轮交付了全场最高的单轮相似度。「评分机制本身即题目」在这组对照里再次显形——一次单轮故障就足以抹平此前的全部收敛成果。

    ③ 输出风格差异肉眼可见:ox-alpha 每轮长篇反解几何参数并逐项说明改动;GLM-5V 从第一轮起就用「这轮我主要修正了……」的口吻作答(首轮其实并无上一版可言);Kimi K3 把精力放在环境适配上(字体探测、显式 Agg 后端)。这与「另两个模型未特别配置推理深度」的方法论局限相符——横向只比较行为记录,不下能力高低的结论。

    ④ 规模提醒:每模型 n=1 × 3 轮,演示级样本;以上是真实发生的行为对照,不是统计意义上的排名。