Evaluación de modelos de imagen
Introducción
Los modelos de imagen se utilizan cada vez más en flujos de trabajo de productos reales: maquetas de diseño, activos de marketing, pruebas virtuales y ediciones de alta precisión de materiales de marca existentes. Para confiar en estos sistemas en producción, necesitas más que "¿se ve bien?". Necesitas una evaluación repetible y específica para el flujo de trabajo que mida si los resultados satisfacen los requisitos, fallan de forma segura y mejoran de manera predecible con el tiempo.
Las evaluaciones de visión (o vision evals) son más difíciles que las evaluaciones de texto porque la "respuesta" es una imagen que mezcla:
- Restricciones estrictas: texto exacto, recuentos, atributos, localidad ("cambiar solo esta región").
- Calidad perceptual: nitidez, coherencia, realismo, coincidencia estética.
- Modos de fallo ocultos: distorsiones sutiles, ediciones no intencionadas o pequeños errores de texto que parecen correctos a primera vista pero que incumplen los requisitos del producto, especialmente en la edición.
Una buena evaluación de visión no califica "una imagen bonita". Califica si el modelo es confiable para un flujo de trabajo específico. Muchas imágenes que se ven visualmente fuertes aún fallan porque el texto es incorrecto, el estilo no coincide con la marca o las ediciones se desbordan del área prevista. Las evaluaciones de imagen miden la calidad, la controlabilidad y la usabilidad para prompts reales, no solo el atractivo visual.
Qué cubre esta guía
Este recetario se centra en la construcción de un sistema práctico de evaluación de imágenes para cuatro categorías principales:
1) Evaluaciones de generación de imágenes
- Seguimiento de instrucciones (restricciones satisfechas)
- Renderizado de texto (precisión, legibilidad, ubicación)
- Control de estilo (coincidencia estética, consistencia de marca/personaje)
- Alineación de preferencias (etiquetas de rúbrica + comparaciones por pares)
2) Evaluaciones de edición de imágenes
- Corrección de la transformación (el cambio solicitado se realiza exactamente)
- Localidad (las ediciones ocurren solo donde se pretende)
- Preservación (las regiones no solicitadas permanecen sin cambios)
- Control espacial (ediciones aplicadas a la instancia/región correcta)
3) Alineación de la retroalimentación humana
- Etiquetas basadas en rúbricas y preferencias por pares para capturar la calidad subjetiva y la "vibra"
- Técnicas de calibración para mantener la coherencia de los juicios humanos a lo largo del tiempo
4) Estrategia para construir evaluaciones
- Comienza con puertas de corrección no negociables
- Agrega métricas de calidad graduadas una vez que los fallos estén controlados
- Etiqueta los modos de fallo para impulsar una iteración dirigida
Construyendo un arnés de evaluación de visión
Un arnés de evaluación de visión es un sistema pequeño y repetible que convierte "¿funcionó esta imagen?" en resultados estructurados y comparables entre prompts, modelos y configuraciones.
A un alto nivel, las evaluaciones de visión siguen el mismo ciclo que cualquier sistema de evaluación de LLM:
Entradas → Modelo → Salidas → Calificadores → Puntuaciones → Retroalimentación → Mejora
Para que esto sea reutilizable en el resto del recetario (generación + edición), construye el arnés alrededor de tres complementos:
- Casos de prueba: qué ejecutar (prompt + criterios + imágenes/máscara de entrada opcionales)
- Runners: cómo llamar a un modelo y guardar imágenes de salida
- Graders: cómo calificar una salida (rúbricas, LLM-como-juez, etiquetas humanas más tarde)
Configuración
Ejecuta esto una vez. Esto:
- crea el cliente API
- crea
images/en la carpeta.
import os
from openai import OpenAI
client = OpenAI()
os.makedirs("../../images", exist_ok=True)
A continuación se muestra un paquete mínimo vision_harness/ que puedes colocar en tu repositorio.
Para un arnés completo y ejecutable que puedes extender (editar, reproducir y agregar tus propios casos), consulta examples/evals/imagegen_evals.
vision_harness/types.py
Mantén los tipos principales genéricos para que puedas reutilizarlos para ambos, la generación y la edición de imágenes.
from __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Literal, Optional
TaskType = Literal["image_generation", "image_editing"]
ScoreValue = bool | int | float | str
@dataclass(frozen=True)
class ImageInputs:
"""Editing inputs: one or more reference images + optional mask."""
image_paths: list[Path]
mask_path: Optional[Path] = None
@dataclass(frozen=True)
class TestCase:
"""A single evaluable example."""
id: str
task_type: TaskType
prompt: str
criteria: str
image_inputs: Optional[ImageInputs] = None
@dataclass(frozen=True)
class ModelRun:
"""One model configuration to evaluate (useful for sweeps)."""
label: str
task_type: TaskType
params: dict[str, Any] # e.g. {"model": "...", "quality": "...", ...}
@dataclass(frozen=True)
class Artifact:
"""A saved artifact from a run (usually an image)."""
kind: Literal["image"]
path: Path
mime: str = "image/png"
@dataclass
class ModelResponse:
"""Normalized output from any runner."""
artifacts: list[Artifact] = field(default_factory=list)
raw: dict[str, Any] = field(default_factory=dict) # optional debug payload
@dataclass(frozen=True)
class Score:
key: str
value: ScoreValue
reason: str = ""
tags: Optional[list[str]] = None
vision_harness/io.py
Lo usarás en los calificadores (LLM-como-juez) y a veces en las llamadas al modelo.
from __future__ import annotations
import base64
from pathlib import Path
_MIME_BY_SUFFIX = {
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".webp": "image/webp",
}
def image_to_data_url(path: Path) -> str:
mime = _MIME_BY_SUFFIX.get(path.suffix.lower(), "image/png")
b64 = base64.b64encode(path.read_bytes()).decode("utf-8")
return f"data:{mime};base64,{b64}"
vision_harness/storage.py
from __future__ import annotations
import time
from dataclasses import dataclass
from pathlib import Path
@dataclass
class OutputStore:
"""
Simple artifact store that writes directly to the root folder.
Note: This implementation writes directly to `root` (no per-test subfolders),
which keeps site-relative image paths stable for cookbook rendering.
"""
root: Path
def run_dir(self, test_id: str, model_label: str) -> Path:
# Ignore test/model subfolders; write everything to the root.
self.root.mkdir(parents=True, exist_ok=True)
return self.root
def new_basename(self, prefix: str) -> str:
created_ms = int(time.time() * 1000)
return f"{prefix}_{created_ms}"
def save_png(self, run_dir: Path, basename: str, idx: int, png_bytes: bytes) -> Path:
out = run_dir / f"{basename}_{idx}.png"
out.write_bytes(png_bytes)
return out
vision_harness/sweeps.py
from __future__ import annotations
from itertools import product
from typing import Any
def grid_sweep(
*,
base_label: str,
task_type: TaskType,
fixed: dict[str, Any],
grid: dict[str, list[Any]],
) -> list[ModelRun]:
keys = list(grid.keys())
runs: list[ModelRun] = []
for values in product(*[grid[k] for k in keys]):
params = dict(fixed)
label_parts = [base_label]
for k, v in zip(keys, values):
params[k] = v
label_parts.append(f"{k}={v}")
runs.append(ModelRun(label=",".join(label_parts), task_type=task_type, params=params))
return runs
vision_harness/runners.py
Dos runners: uno para generación, otro para edición. Ambos devuelven un ModelResponse normalizado que contiene las imágenes de salida guardadas.
from __future__ import annotations
import base64
from contextlib import ExitStack
from typing import Optional
from openai import OpenAI
def _mime_for_path(path) -> str:
suffix = str(path).lower()
if suffix.endswith(".png"):
return "image/png"
if suffix.endswith(".jpg") or suffix.endswith(".jpeg"):
return "image/jpeg"
if suffix.endswith(".webp"):
return "image/webp"
return "application/octet-stream"
def _extract_b64_items(images_response) -> list[str]:
b64_items: list[str] = []
for item in getattr(images_response, "data", []) or []:
b64 = getattr(item, "b64_json", None)
if b64:
b64_items.append(b64)
return b64_items
class ImageGenerationRunner:
"""Text-to-image runner."""
def __init__(self, client: Optional[OpenAI] = None):
self.client = client or OpenAI()
def run(self, case: TestCase, run_cfg: ModelRun, store: OutputStore) -> ModelResponse:
assert case.task_type == "image_generation"
assert run_cfg.task_type == "image_generation"
params = dict(run_cfg.params)
model = params.pop("model")
n = int(params.pop("n", 1))
run_dir = store.run_dir(case.id, run_cfg.label)
basename = store.new_basename(f"gen_{case.id}_{run_cfg.label}")
images_response = self.client.images.generate(
model=model,
prompt=case.prompt,
n=n,
**params,
)
artifacts: list[Artifact] = []
for idx, b64_json in enumerate(_extract_b64_items(images_response)):
png_bytes = base64.b64decode(b64_json)
out_path = store.save_png(run_dir, basename, idx, png_bytes)
artifacts.append(Artifact(kind="image", path=out_path))
return ModelResponse(artifacts=artifacts, raw={"model": model, "params": run_cfg.params})
class ImageEditRunner:
"""Image editing runner (reference image(s) + optional mask)."""
def __init__(self, client: Optional[OpenAI] = None):
self.client = client or OpenAI()
def run(self, case: TestCase, run_cfg: ModelRun, store: OutputStore) -> ModelResponse:
assert case.task_type == "image_editing"
assert run_cfg.task_type == "image_editing"
assert case.image_inputs is not None and case.image_inputs.image_paths
params = dict(run_cfg.params)
model = params.pop("model")
n = int(params.pop("n", 1))
run_dir = store.run_dir(case.id, run_cfg.label)
basename = store.new_basename(f"edit_{case.id}_{run_cfg.label}")
with ExitStack() as stack:
image_files = []
for p in case.image_inputs.image_paths:
f = stack.enter_context(p.open("rb"))
image_files.append((p.name, f, _mime_for_path(p)))
mask_file = None
if case.image_inputs.mask_path:
mf = stack.enter_context(case.image_inputs.mask_path.open("rb"))
mask_file = (case.image_inputs.mask_path.name, mf, _mime_for_path(case.image_inputs.mask_path))
edit_kwargs = dict(
model=model,
prompt=case.prompt,
image=image_files,
n=n,
**params,
)
if mask_file is not None:
edit_kwargs["mask"] = mask_file
images_response = self.client.images.edit(**edit_kwargs)
artifacts: list[Artifact] = []
for idx, b64_json in enumerate(_extract_b64_items(images_response)):
png_bytes = base64.b64decode(b64_json)
out_path = store.save_png(run_dir, basename, idx, png_bytes)
artifacts.append(Artifact(kind="image", path=out_path))
return ModelResponse(artifacts=artifacts, raw={"model": model, "params": run_cfg.params})
vision_harness/graders.py
Una interfaz de calificador limpia + un calificador LLM-como-juez reutilizable que se puede usar tanto para la generación como para la edición cambiando cómo construyes las entradas del juez.
from __future__ import annotations
import json
from dataclasses import dataclass, field
from pathlib import Path
from typing import Callable, Optional, Protocol
from openai import OpenAI
class Grader(Protocol):
key: str
def grade(self, response: ModelResponse, case: TestCase) -> Score | list[Score]: ...
def pick_first_image(response: ModelResponse) -> Optional[Path]:
for artifact in response.artifacts:
if artifact.kind == "image":
return artifact.path
return None
def build_generation_judge_content(case: TestCase, output_image: Path) -> list[dict]:
return [
{
"type": "input_text",
"text": f"Prompt:\n{case.prompt}\n\nCriteria:\n{case.criteria}",
},
{
"type": "input_image",
"image_url": image_to_data_url(output_image),
},
]
def build_editing_judge_content(case: TestCase, output_image: Path) -> list[dict]:
assert case.image_inputs is not None and case.image_inputs.image_paths
content: list[dict] = [
{
"type": "input_text",
"text": f"Edit instruction:\n{case.prompt}\n\nCriteria:\n{case.criteria}",
}
]
for image_path in case.image_inputs.image_paths:
content.append(
{
"type": "input_image",
"image_url": image_to_data_url(image_path),
}
)
if case.image_inputs.mask_path:
content.append(
{
"type": "input_image",
"image_url": image_to_data_url(case.image_inputs.mask_path),
}
)
content.append(
{
"type": "input_image",
"image_url": image_to_data_url(output_image),
}
)
return content
def default_schema() -> dict:
return {
"type": "object",
"properties": {
"pass": {"type": "boolean"},
"reason": {"type": "string"},
},
"required": ["pass", "reason"],
"additionalProperties": False,
}
@dataclass
class LLMajRubricGrader:
"""
Reusable vision LLM-as-judge grader.
- Provide a system prompt + a content_builder for generation/editing.
- Optionally provide a custom JSON schema and parser.
"""
key: str
system_prompt: str
content_builder: Callable[[TestCase, Path], list[dict]]
judge_model: str = "gpt-5.2"
client: Optional[OpenAI] = None
json_schema_name: str = "vision_eval_result"
json_schema: dict = field(default_factory=default_schema)
result_parser: Optional[Callable[[dict, str], Score | list[Score]]] = None
def _parse_result(self, data: dict) -> Score | list[Score]:
if self.result_parser:
return self.result_parser(data, self.key)
return Score(
key=self.key,
value=bool(data.get("pass", False)),
reason=(data.get("reason") or "").strip(),
tags=data.get("tags") or None,
)
def grade(self, response: ModelResponse, case: TestCase) -> Score | list[Score]:
output_image = pick_first_image(response)
if not output_image:
return Score(key=self.key, value=False, reason="No output image artifact found")
client = self.client or OpenAI()
content = self.content_builder(case, output_image)
completion = client.responses.create(
model=self.judge_model,
input=[
{"role": "system", "content": self.system_prompt},
{"role": "user", "content": content},
],
text={
"format": {
"type": "json_schema",
"name": self.json_schema_name,
"schema": self.json_schema,
"strict": True,
}
},
)
data = json.loads(completion.output_text)
return self._parse_result(data)
vision_harness/evaluate.py
Un bucle de evaluación simple que devuelve datos de Python planos (sin clase "eval row"). Las secciones posteriores pueden escribir sus propias utilidades de informes/CSV encima.
from __future__ import annotations
from typing import Any
def _as_score_list(score_or_scores: Score | list[Score]) -> list[Score]:
if isinstance(score_or_scores, list):
return score_or_scores
return [score_or_scores]
def evaluate(
*,
cases: list[TestCase],
model_runs: list[ModelRun],
graders: list[Grader],
output_store: OutputStore,
) -> list[dict[str, Any]]:
gen_runner = ImageGenerationRunner()
edit_runner = ImageEditRunner()
results: list[dict[str, Any]] = []
for case in cases:
for run_cfg in model_runs:
if run_cfg.task_type != case.task_type:
continue
if case.task_type == "image_generation":
response = gen_runner.run(case, run_cfg, output_store)
elif case.task_type == "image_editing":
response = edit_runner.run(case, run_cfg, output_store)
else:
raise ValueError(f"Unknown task_type: {case.task_type}")
score_map: dict[str, Any] = {}
reason_map: dict[str, str] = {}
for grader in graders:
scored = grader.grade(response, case)
for score in _as_score_list(scored):
score_map[score.key] = score.value
reason_map[score.key] = score.reason
results.append(
{
"test_id": case.id,
"model_label": run_cfg.label,
"task_type": case.task_type,
"artifact_paths": [str(a.path) for a in response.artifacts],
"scores": score_map,
"reasons": reason_map,
"run_params": run_cfg.params,
}
)
return results
Ayudas para la representación de tablas de resultados
import os
from html import escape
from IPython.display import HTML, display
def summarize_scores(scores: dict[str, object]) -> str:
return os.linesep.join(f"{k}: {scores[k]}" for k in sorted(scores.keys()))
def summarize_reasons(reasons: dict[str, str]) -> str:
verdict_reason = (reasons.get("verdict") or "").strip()
if verdict_reason:
return verdict_reason
return os.linesep.join(f"{k}: {reasons[k]}" for k in sorted(reasons.keys()) if reasons[k])
def _pre(text: str) -> str:
return (
"<pre style='white-space:pre-wrap; word-break:break-word; margin:0'>"
f"{escape(text)}"
"</pre>"
)
def render_result_table(
*,
case: TestCase,
result: dict[str, object],
title: str,
) -> None:
sep = os.linesep
prompt_text = f"{case.prompt}{sep}{sep}Criteria:{sep}{case.criteria}"
scores = result["scores"]
reasons = result["reasons"]
prompt_html = _pre(prompt_text)
scores_html = _pre(summarize_scores(scores))
reasons_html = _pre(summarize_reasons(reasons))
table_html = (
"<table style='width:100%; table-layout:fixed; border-collapse:collapse;'>"
"<colgroup>"
"<col style='width:33%'>"
"<col style='width:33%'>"
"<col style='width:33%'>"
"</colgroup>"
"<thead><tr>"
"<th style='text-align:left; padding:8px; border-bottom:1px solid #ddd'>Input Prompt</th>"
"<th style='text-align:left; padding:8px; border-bottom:1px solid #ddd'>Scores</th>"
"<th style='text-align:left; padding:8px; border-bottom:1px solid #ddd'>Reasoning</th>"
"</tr></thead>"
"<tbody><tr>"
f"<td style='text-align:left; padding:8px; vertical-align:top'>{prompt_html}</td>"
f"<td style='text-align:left; padding:8px; vertical-align:top'>{scores_html}</td>"
f"<td style='text-align:left; padding:8px; vertical-align:top'>{reasons_html}</td>"
"</tr></tbody></table>"
)
display(HTML(f"<div style='font-weight:600; margin:6px 0'>{escape(title)}</div>"))
display(HTML(table_html))
Las secciones posteriores del recetario solo necesitarán proporcionar:
- Una lista de objetos
TestCasepara el caso de uso (maquetas de UI, gráficos de marketing, pruebas virtuales, ediciones de logotipos, etc.) - Un conjunto de
ModelRuns (ejecución única o un barrido congrid_sweep) - Un conjunto de calificadores, típicamente:
- Calificadores de control (aprobado/reprobado): no negociables, por ejemplo: seguimiento de instrucciones, corrección de texto, localidad/preservación
- Métricas calificadas (0–5): dimensiones de calidad, por ejemplo: realismo/usabilidad, diseño/jerarquía, gravedad de artefactos
- Opcional: etiquetas de rúbrica humanas y preferencias por pares (almacenadas fuera del arnés)
Esto mantiene el resto del recetario centrado en qué medir por caso de uso, sin reescribir la fontanería del arnés cada vez.
Evaluaciones de generación de imágenes
Ideas de casos de uso: maquetas de UI, gráficos/pósteres de marketing
Objetivo: evaluar la calidad, controlabilidad y utilidad de texto a imagen para prompts reales
Cubre:
- seguimiento de instrucciones (restricciones satisfechas),
- Renderizado de texto (el texto generado es preciso, legible y se coloca correctamente),
- Estilo (estética solicitada + calidad visual, puede incluir consistencia de marca, personaje y producto)
- Alineación de la retroalimentación humana (etiquetas basadas en rúbricas + preferencias por pares).
Los modelos de generación de imágenes se utilizan para crear artefactos que influyen en el trabajo real: maquetas de UI, diseños de productos, gráficos de marketing y presentaciones internas. En estos contextos, las imágenes no se juzgan puramente por su atractivo visual. Se juzgan por si comunican la intención, siguen las restricciones y son utilizables por las partes interesadas posteriores.
Maquetas de UI
Las maquetas de UI generadas por modelos de imagen se utilizan cada vez más para la exploración temprana de productos, la ideación de diseños y las revisiones internas. En estos flujos de trabajo, las maquetas no son solo inspiración visual. Son herramientas de comunicación que ayudan a diseñadores, ingenieros y partes interesadas a razonar sobre el diseño, la jerarquía, la intención de interacción y el alcance de las características antes de que se construya algo. Como resultado, el éxito se define menos por el gusto estético y más por si el resultado puede funcionar plausiblemente como un artefacto de producto.
Ejemplo:
Esta sección muestra cómo evaluar una maqueta de UI generada utilizando un ejemplo a nivel de pantalla. El objetivo es evaluar si la UI generada consiste en un diseño coherente, utiliza componentes reconocibles y representa una experiencia de producto plausible.
Por qué las evaluaciones de maquetas de UI son diferentes
Las maquetas de UI combinan varias dimensiones de evaluación difíciles:
- Fidelidad de los componentes (control)
La imagen generada debe representar claramente el tipo y estado de pantalla solicitados. Los botones deben parecer clicables, las entradas deben parecer editables y la navegación debe parecer navegación. - Realización del diseño (calificado)
Una buena maqueta de UI comunica instantáneamente lo que el usuario puede hacer y lo que más importa. El diseño debe hacer obvias las acciones primarias, claramente subordinadas las acciones secundarias y la información agrupada de una manera que refleje el flujo de interacción real. - Renderizado de texto en la imagen (control)
El texto de la UI codifica la funcionalidad. Las etiquetas, los encabezados y las llamadas a la acción deben ser legibles y renderizarse correctamente.
Tarea de ejemplo: generación de pantalla de pago móvil
- Escenario: una aplicación de comercio electrónico para consumidores necesita una pantalla de pago móvil para revisar un pedido y completar el pago.
- Guía del prompt: sé explícito sobre el tipo de pantalla, la plataforma, los elementos de UI requeridos, el texto exacto de los botones/enlaces, las restricciones de jerarquía y los extras no permitidos.
- Implementación: el prompt completo y los criterios se encuentran en la celda de código de configuración del arnés a continuación (
ui_promptyui_criteria).
Qué evaluar (métricas prácticas):
1) Seguimiento de instrucciones — Aprobado / Reprobado (control)
La mayoría de las evaluaciones de maquetas de UI comienzan con la fidelidad de las instrucciones. A un nivel básico, la pregunta es simple: ¿el modelo generó la pantalla que se le pidió? Si el prompt especifica una pantalla de pago móvil, la salida debe parecer una pantalla de pago móvil, no una página de aterrizaje genérica. Las secciones, estados o restricciones requeridos deben estar presentes, y la estructura general debe coincidir con la intención de la solicitud. Esta dimensión suele ser la más ponderada, porque si el modelo no cumple con la solicitud principal, el resto de la salida no importa. Una vez que una maqueta de UI satisface la instrucción básica, la evaluación se centra en si la pantalla es utilizable y coherente como artefacto de producto.
APROBADO si
- El tipo de pantalla y el contexto de la plataforma correctos están presentes.
- Todas las secciones, estados o restricciones requeridos están incluidos.
REPROBADO si
- Falta algún componente requerido o la salida altera el propósito de la UI.
2) Diseño y jerarquía — 0–5
Mide si la pantalla se comunica claramente a primera vista.
Qué buscar:
- Jerarquía visual clara entre acciones primarias y secundarias
- Alineación y espaciado consistentes
- Agrupación lógica de elementos relacionados
3) Renderizado y legibilidad del texto — Aprobado / Reprobado (control)
Las etiquetas, los encabezados y las llamadas a la acción deben ser legibles e inequívocos.
APROBADO si
- El texto es legible, está correctamente escrito y etiquetado de forma sensata.
- Los tamaños de fuente reflejan la jerarquía (encabezados vs. etiquetas vs. texto de ayuda).
REPROBADO si
- Cualquier texto crítico es ilegible, cortado, mal escrito o distorsionado.
4) Realismo y usabilidad de la UI — 0–5
Mide si la maqueta se asemeja a una interfaz de producto plausible.
Qué buscar:
- Las entradas parecen editables / Los botones parecen clicables
- La navegación parece navegación
- Los elementos interactivos son visualmente distintos del contenido estático
5) Retroalimentación humana (rápida y de alto impacto)
La "claridad de usabilidad" de la UI es donde los humanos añaden más valor. Mantenla ligera y centrada en la intención de interacción.
- Etiquetas de rúbrica (rápidas):
- ¿Usarías esta maqueta para iterar en una pantalla de producto real? (S/N)
- Si N, ¿por qué? (sección faltante, botón de acción poco claro, texto ilegible, diseño confuso, etc.)
- Claridad general de usabilidad: 1–5
- Etiquetas de fallos comunes (para depuración + iteración):
wrong_screen_typemissing_required_sectionextra_ui_elementsprimary_cta_not_cleartext_unreadable_or_garbledaffordances_unclearlayout_confusing
Reglas de veredicto (cómo convertir métricas en un único aprobado/reprobado)
- El seguimiento de instrucciones debe ser APROBADO
- El diseño y la jerarquía ≥ 3
- El renderizado de texto debe ser APROBADO
- El realismo de la UI ≥ 3
Si alguna regla falla → REPROBADO general.
Conjunto de TestCase de ejemplo (pequeño pero de alta señal)
Comienza con algunos casos que cubran variantes comunes de maquetas de UI y casos extremos.
- Pantalla de pago móvil (el prompt "maqueta de UI de pago" anterior)
- Diseño mínimo (encabezado + total del pedido + CTA principal solamente) - prueba el diseño y la jerarquía con elementos escasos.
- Jerarquía densa (resumen del pedido + método de pago + fila de promoción + impuestos + dos acciones secundarias) - prueba la jerarquía bajo carga de información.
- Texto exacto del CTA (el texto del botón principal debe ser exactamente "Realizar pedido") - prueba la fidelidad del renderizado de texto en la imagen.
- Presencia de acción secundaria (incluir tanto "Editar carrito" como "Cambiar método de pago") - prueba que las acciones secundarias permanezcan visualmente subordinadas.
- Restricción de ubicación (requerir el total del pedido directamente encima del CTA principal)
- Fidelidad de la plataforma (versión móvil vs. escritorio) - prueba el encuadre de la pantalla y las señales de la plataforma.
Prompt de rúbrica LLM-como-juez
A continuación se muestra un prompt de juez alineado con tu LLMajRubricGrader existente. Devuelve puntuaciones métricas estructuradas + un veredicto general.
Puedes usar esto con tu calificador existente cambiando el esquema JSON para incluir los campos a continuación (o crear calificadores separados por métrica si lo prefieres).
Prompt del sistema (juez de maqueta de UI)
PROMPT = """<core_mission>
Evaluate whether a generated UI mockup image represents a usable mobile checkout screen by checking screen type fidelity, layout/hierarchy, in-image text rendering, and UI affordance clarity.
</core_mission>
<role>
You are an expert evaluator of UI mockups used by designers and engineers. You care about structural correctness, readable UI text, clear hierarchy, and realistic rendering of UI elements.
</role>
<scope_constraints>
- Judge only against the provided instructions.
- Be strict about required UI elements and exact button/link text.
- Do NOT infer intent beyond what is explicitly stated.
- Do NOT reward creativity that violates constraints.
- Missing or extra required components are serious errors.
- If the UI intent or function is unclear, score conservatively.
</scope_constraints>
<metrics>
1) instruction_following: PASS/FAIL
2) layout_hierarchy: 0–5
3) in_image_text_rendering: PASS/FAIL
4) ui_affordance_rendering: 0–5
</metrics>
Evaluate EACH metric independently using the definitions below.
--------------------------------
1) Instruction Following (PASS / FAIL)
--------------------------------
PASS if:
- All required components are present.
- No unrequested components or features are added.
- The screen matches the requested type and product context.
FAIL if:
- Any required component is missing.
- Any unrequested component materially alters the UI.
- The screen does not match the requested type.
--------------------------------
2) Layout and Hierarchy (0–5)
--------------------------------
5: Layout is clear, coherent, and immediately usable.
Hierarchy, grouping, spacing, and alignment are strong.
3: Generally understandable, but one notable hierarchy or layout issue
that would require iteration.
0-2: Layout problems materially hinder usability or comprehension.
--------------------------------
3) In-Image Text Rendering (PASS / FAIL)
--------------------------------
PASS if:
- Text is readable, correctly spelled, and sensibly labeled.
- Font sizes reflect hierarchy (headings vs labels vs helper text).
FAIL if:
- Any critical text is unreadable, cut off, misspelled, or distorted.
--------------------------------
4) UI Affordance Rendering (0–5)
--------------------------------
5: Clearly resembles a real product interface that designers could use.
3: Marginally plausible; intent is visible but execution is weak.
0-2: Poor realism; interface would be difficult to use in practice.
<verdict_rules>
- Instruction Following must PASS.
- In-Image Text Rendering must PASS.
- Layout and Hierarchy score must be ≥ 3.
- UI Affordance Rendering score must be ≥ 3.
If ANY rule fails, the overall verdict is FAIL.
Do not average scores to determine the verdict.
</verdict_rules>
<output_constraints>
Return JSON only.
No extra text.
</output_constraints>
"""
Salida JSON recomendada
{
"verdict": "PASS",
"instruction_following": True,
"layout_hierarchy": 3,
"in_image_text_rendering": True,
"ui_affordance_rendering": 4,
"reason": "..."
}
{'verdict': 'PASS',
'instruction_following': True,
'layout_hierarchy': 3,
'in_image_text_rendering': True,
'ui_affordance_rendering': 4,
'reason': '...'}
Ejemplo de maqueta de UI: Configuración del arnés
Define un caso de prueba de maqueta de UI, una ejecución del modelo y un almacén de salida bajo images/.
# Capture the UI judge prompt before PROMPT is overwritten later.
ui_judge_prompt = PROMPT
ui_prompt = """Generate a high-fidelity mobile checkout screen for an ecommerce app.
Orientation: portrait.
Screen type: checkout / order review.
Use the REQUIRED TEXT:
- Checkout
- Place Order
- Edit Cart
Constraints:
- Order total appears directly above the primary CTA.
- Primary CTA is the most visually prominent element.
- Do not include popups, ads, marketing copy, or extra screens.
- Do not include placeholder or lorem ipsum text.
"""
ui_criteria = """The image clearly depicts a mobile checkout screen.
All required sections are present and visually distinct.
UI elements look clickable/editable and follow common conventions.
Primary vs secondary actions are unambiguous.
No extra UI states, decorative noise, or placeholder text."""
ui_case = TestCase(
id="ui_checkout_mockup",
task_type="image_generation",
prompt=ui_prompt,
criteria=ui_criteria,
)
ui_run = ModelRun(
label="gpt-image-1.5-ui",
task_type="image_generation",
params={
"model": "gpt-image-1.5",
"n": 1,
"size": "1024x1024",
},
)
ui_store = OutputStore(root=Path("../../images"))
Ejemplo de maqueta de UI: Ejecutar y calificar
Ejecuta el arnés y califica la maqueta de UI usando la rúbrica del juez de UI.
ui_schema = {
"type": "object",
"properties": {
"verdict": {"type": "string"},
"instruction_following": {"type": "boolean"},
"layout_hierarchy": {"type": "number"},
"in_image_text_rendering": {"type": "boolean"},
"ui_affordance_rendering": {"type": "number"},
"reason": {"type": "string"},
},
"required": [
"verdict",
"instruction_following",
"layout_hierarchy",
"in_image_text_rendering",
"ui_affordance_rendering",
"reason",
],
"additionalProperties": False,
}
def parse_ui_result(data: dict, base_key: str) -> list[Score]:
return [
Score(key="instruction_following", value=bool(data["instruction_following"]), reason=""),
Score(key="layout_hierarchy", value=float(data["layout_hierarchy"]), reason=""),
Score(key="in_image_text_rendering", value=bool(data["in_image_text_rendering"]), reason=""),
Score(key="ui_affordance_rendering", value=float(data["ui_affordance_rendering"]), reason=""),
Score(key="verdict", value=str(data["verdict"]), reason=(data.get("reason") or "").strip()),
]
ui_grader = LLMajRubricGrader(
key="ui_eval",
system_prompt=ui_judge_prompt,
content_builder=build_generation_judge_content,
judge_model="gpt-5.2",
json_schema_name="ui_mockup_eval",
json_schema=ui_schema,
result_parser=parse_ui_result,
)
ui_results = evaluate(
cases=[ui_case],
model_runs=[ui_run],
graders=[ui_grader],
output_store=ui_store,
)
ui_result = ui_results[0]
ui_result
{'test_id': 'ui_checkout_mockup',
'model_label': 'gpt-image-1.5-ui',
'task_type': 'image_generation',
'artifact_paths': ['../../images/gen_ui_checkout_mockup_gpt-image-1.5-ui_1769656414221_0.png'],
'scores': {'instruction_following': True,
'layout_hierarchy': 5.0,
'in_image_text_rendering': True,
'ui_affordance_rendering': 5.0,
'verdict': 'PASS'},
'reasons': {'instruction_following': '',
'layout_hierarchy': '',
'in_image_text_rendering': '',
'ui_affordance_rendering': '',
'verdict': 'All required text is present ("Checkout", "Edit Cart", "Place Order"). Screen clearly matches a mobile checkout/order review with distinct sections (Shipping Information, Payment Method, Items, Order Summary). Order Total appears directly above the primary CTA, and the "Place Order" button is the most visually prominent element. No popups, ads, marketing copy, or lorem/placeholder text. Text is crisp and readable with appropriate hierarchy. UI elements (back arrow, Edit Cart link, Change links, primary CTA) have clear, realistic affordances.'},
'run_params': {'model': 'gpt-image-1.5', 'n': 1, 'size': '1024x1024'}}
Resultados de la maqueta de UI
Muestra el prompt, la imagen generada y las puntuaciones en una sola tabla de pandas.
render_result_table(case=ui_case, result=ui_result, title="UI Mockup: Prompt vs. Scores")
<IPython.core.display.HTML object>
<IPython.core.display.HTML object>
| Prompt de entrada | Puntuaciones | Razonamiento |
|---|---|---|
Generate a high-fidelity mobile checkout screen for an ecommerce app. Orientation: portrait. Screen type: checkout / order review. Use the REQUIRED TEXT: - Checkout - Place Order - Edit Cart Constraints: - Order total appears directly above the primary CTA. - Primary CTA is the most visually prominent element. - Do not include popups, ads, marketing copy, or extra screens. - Do not include placeholder or lorem ipsum text. Criteria: The image clearly depicts a mobile checkout screen. All required sections are present and visually distinct. UI elements look clickable/editable and follow common conventions. Primary vs secondary actions are unambiguous. No extra UI states, decorative noise, or placeholder text. | in_image_text_rendering: True instruction_following: True layout_hierarchy: 5.0 ui_affordance_rendering: 5.0 verdict: PASS | All required text is present ("Checkout", "Edit Cart", "Place Order"). Screen clearly matches a mobile checkout/order review with distinct sections (Shipping Information, Payment Method, Items, Order Summary). Order Total appears directly above the primary CTA, and the "Place Order" button is the most visually prominent element. No popups, ads, marketing copy, or lorem/placeholder text. Text is crisp and readable with appropriate hierarchy. UI elements (back arrow, Edit Cart link, Change links, primary CTA) have clear, realistic affordances. |
Generación de gráficos de marketing
Los gráficos de marketing son un flujo de trabajo de "texto a imagen de alto riesgo": el resultado está destinado a ser enviado (o al menos revisado como si pudiera ser enviado). Un folleto puede verse "bonito" pero aún así fallar si el texto es incorrecto, la oferta no está clara o el diseño oculta el mensaje clave.
Esta sección muestra cómo evaluar la generación de folletos con un ejemplo de cafetería. El objetivo es hacer que la evaluación sea relevante para el flujo de trabajo: ¿puede un comercializador o diseñador usar este resultado con ediciones mínimas?
Ejemplo:
Por qué las evaluaciones de gráficos de marketing son diferentes
Las imágenes de marketing combinan tres tipos de problemas difíciles:
- Requisitos de texto exactos (control)
- Diseño + jerarquía (calificado)
- Estilo + consistencia de marca (calificado + preferencias humanas opcionales)
Tarea de ejemplo: generación de folletos de cafetería
- Escenario: una cafetería local necesita un folleto promocional para una bebida por tiempo limitado y una oferta entre semana.
- Guía del prompt: sé explícito sobre el entregable, el texto requerido, la jerarquía y los extras no permitidos.
- Implementación: el prompt completo y los criterios se encuentran en la celda de código de configuración del arnés a continuación (
coffee_generation_promptycoffee_criteria).
Qué evaluar (métricas prácticas):
Usa una mezcla de controles (restricciones estrictas) y señales calificadas (calidad).
1) Seguimiento de instrucciones — Aprobado / Reprobado (control)
Los activos de marketing fallan rápidamente si no coinciden con la especificación.
APROBADO si
- Tipo de entregable correcto: claramente un diseño de folleto/póster (no una pantalla de UI, no una foto aleatoria).
- Existen los componentes requeridos (encabezado/subencabezado/oferta/CTA/pie de página + imagen principal).
REPROBADO si
- Falta algún componente requerido o la salida no es reconociblemente un folleto.
2) Precisión del renderizado de texto — Aprobado / Reprobado (control)
Este suele ser el modo de fallo de producción número 1.
APROBADO si
- Todas las cadenas de texto requeridas están presentes y son exactamente correctas (ortografía, puntuación, mayúsculas, símbolos).
- El texto es legible (no manchado, cortado, deformado o superpuesto).
REPROBADO si
- Cualquier texto requerido es incorrecto/faltante/ilegible, o aparece texto adicional.
Consejo: si tu flujo de trabajo requiere "texto exacto", trátalo como un control estricto. No lo promedies.
3) Diseño y jerarquía — 0–5
Mide si el folleto se comunica claramente a primera vista.
Qué buscar:
- Prioridad clara: el encabezado domina, el subencabezado apoya, la oferta destaca, el pie de página es secundario.
- La alineación y el espaciado se sienten intencionales (sin grupos abarrotados, sin elementos flotantes aleatorios).
- El orden de lectura es inequívoco.
4) Estilo y ajuste de marca — 0–5
Mide si coincide con la "vibra" solicitada y evita apariencias que no sean de marca.
Qué buscar:
- Cálido, acogedor, minimalista; lenguaje visual de café de especialidad.
- Paleta y tipografía consistentes (no múltiples estilos conflictivos).
- Evita la ilustración caricaturesca si el prompt solicitó fotorrealismo.
5) Calidad visual y gravedad de los artefactos — 0–5
Un folleto puede ser "correcto" pero aún así inutilizable si está visualmente roto.
Qué buscar:
- Sin objetos distorsionados, manos/tazas rotas, texturas de espuma derretidas, artefactos extraños alrededor del texto.
- La textura de fondo es sutil (no compite con el texto).
- La imagen principal se ve apetitosa y coherente.
6) Retroalimentación humana (rápida y de alto impacto)
La "claridad de usabilidad" de la UI es donde los humanos añaden más valor. Mantenla ligera y centrada en la intención de interacción.
- Etiquetas de rúbrica (rápidas):
- ¿Usarías esta maqueta para iterar en una pantalla de producto real? (S/N)
- Si N, ¿por qué? (sección faltante, botón de acción poco claro, texto ilegible, diseño confuso, etc.)
- Claridad general de usabilidad: 1–5
- Etiquetas de fallos comunes (para depuración + iteración):
wrong_screen_typemissing_required_sectionextra_ui_elementsprimary_cta_not_cleartext_unreadable_or_garbledaffordances_unclearlayout_confusing
Reglas de veredicto (cómo convertir métricas en un único aprobado/reprobado)
Un conjunto de reglas simple y estricto funciona bien:
- El seguimiento de instrucciones debe ser APROBADO
- El renderizado de texto debe ser APROBADO
- El diseño y la jerarquía ≥ 3
- El estilo y el ajuste de marca ≥ 3
- La calidad visual ≥ 3
Si alguna regla falla → REPROBADO general.
(No promedies las puntuaciones para anular la corrección del texto).
Conjunto de TestCase de ejemplo (pequeño pero de alta señal)
Comienza con ~8–12 casos para cubrir variantes comunes de folletos y casos extremos.
- Campaña estacional (el prompt "Semana del Latte de Invierno" anterior)
- Texto mínimo (solo 2 líneas + logo + horario) — prueba diseños escasos
- Información densa (destacados del menú + oferta + dos CTA) — prueba la jerarquía bajo carga
- Sin texto extra estricto — prueba el texto de relleno alucinado
- Puntuación exacta ("20% DE DESCUENTO • Lun–Jue") — prueba la fidelidad de los símbolos
- Dos ofertas ("Martes 2x1" + "Happy Hour 2–4") — prueba el diseño de múltiples insignias
- Restricción de combinación de colores ("usar solo crema + marrón oscuro + naranja apagado")
- Variante de accesibilidad (alto contraste, texto grande, fondo simple)
Prompt de rúbrica LLM-como-juez
A continuación se muestra un prompt de juez alineado con tu LLMajRubricGrader existente. Devuelve puntuaciones métricas estructuradas + un veredicto general.
Puedes usar esto con tu calificador existente cambiando el esquema JSON para incluir los campos a continuación (o crear calificadores separados por métrica si lo prefieres).
PROMPT = """<core_mission>
Evaluate whether a generated marketing flyer is usable for a real coffee shop
promotion by checking instruction adherence, exact text correctness, layout clarity,
style fit, and artifact severity.
</core_mission>
<role>
You are an expert evaluator of marketing design deliverables.
You care about correctness, readability, hierarchy, and brand-fit.
You do NOT reward creativity that violates constraints.
</role>
<scope_constraints>
- Judge ONLY against the provided prompt and criteria.
- Be strict about required copy: spelling, punctuation, casing, and symbols must match exactly.
- Extra or missing text is a serious error.
- If unsure, score conservatively (lower score).
</scope_constraints>
<metrics>
1) instruction_following: PASS/FAIL
2) text_rendering: PASS/FAIL
3) layout_hierarchy: 0-5
4) style_brand_fit: 0-5
5) visual_quality: 0-5
Use these anchors:
Layout/Hierarchy 5 = instantly readable; clear order; strong spacing/alignment.
3 = understandable but needs iteration (one clear issue).
0-2 = confusing or hard to parse.
Style/Brand Fit 5 = clearly matches requested vibe; consistent; not off-style.
3 = generally matches but with noticeable mismatch.
0-2 = wrong style (e.g. cartoonish when photo-real requested).
Visual Quality 5 = clean; no distracting artifacts; hero image coherent.
3 = minor artifacts but still usable.
0-2 = obvious artifacts or distortions that break usability.
</metrics>
<verdict_rules>
Overall verdict is FAIL if:
- instruction_following is FAIL, OR
- text_rendering is FAIL, OR
- any of layout_hierarchy/style_brand_fit/visual_quality is < 3.
Otherwise PASS.
</verdict_rules>
<output_constraints>
Return JSON only.
No extra text.
</output_constraints>
"""
Salida JSON recomendada
{
"verdict": "PASS",
"instruction_following": True,
"text_rendering": True,
"layout_hierarchy": 4,
"style_brand_fit": 4,
"visual_quality": 3,
"reason": "..."
}
{'verdict': 'PASS',
'instruction_following': True,
'text_rendering': True,
'layout_hierarchy': 4,
'style_brand_fit': 4,
'visual_quality': 3,
'reason': '...'}
Ejemplo de café: Configuración del arnés
Define un único caso de prueba de folleto de marketing, una ejecución del modelo y un almacén de salida.
# Run the harness for a single coffee flyer generation case.
coffee_generation_prompt = """Create a print-ready vertical A4 flyer for a coffee shop called Sunrise Coffee.
Use a warm, cozy, minimal specialty coffee aesthetic (not cartoonish).
Required text (must be exact):
- WINTER LATTE WEEK
- Try our Cinnamon Oat Latte
- 20% OFF - Mon-Thu
- Order Ahead
- 123 Market St - 7am-6pm
Do not include any other words, prices, URLs, or QR codes.
"""
coffee_criteria = """All required text appears exactly as written and is legible.
Layout reads clearly: shop name -> headline -> subheadline -> offer -> CTA -> footer.
Style matches warm, cozy, specialty coffee and is not cartoonish.
No extra text, watermarks, or irrelevant UI-like elements."""
coffee_case = TestCase(
id="coffee_flyer_generation",
task_type="image_generation",
prompt=coffee_generation_prompt,
criteria=coffee_criteria,
)
coffee_run = ModelRun(
label="gpt-image-1.5",
task_type="image_generation",
params={
"model": "gpt-image-1.5",
"n": 1,
"size": "1024x1024",
},
)
# Save artifacts under the repo images/ folder so they render on the site.
coffee_store = OutputStore(root=Path("../../images"))
Ejemplo de café: Ejecutar y calificar
Ejecuta el arnés y califica la salida con la rúbrica del juez de marketing.
# Use the prebuilt graders + evaluate helpers with a structured marketing schema.
from openai import OpenAI
marketing_schema = {
"type": "object",
"properties": {
"verdict": {"type": "string"},
"instruction_following": {"type": "boolean"},
"text_rendering": {"type": "boolean"},
"layout_hierarchy": {"type": "number"},
"style_brand_fit": {"type": "number"},
"visual_quality": {"type": "number"},
"reason": {"type": "string"},
},
"required": [
"verdict",
"instruction_following",
"text_rendering",
"layout_hierarchy",
"style_brand_fit",
"visual_quality",
"reason",
],
"additionalProperties": False,
}
def parse_marketing_result(data: dict, base_key: str) -> list[Score]:
return [
Score(key="instruction_following", value=bool(data["instruction_following"]), reason=""),
Score(key="text_rendering", value=bool(data["text_rendering"]), reason=""),
Score(key="layout_hierarchy", value=float(data["layout_hierarchy"]), reason=""),
Score(key="style_brand_fit", value=float(data["style_brand_fit"]), reason=""),
Score(key="visual_quality", value=float(data["visual_quality"]), reason=""),
Score(key="verdict", value=str(data["verdict"]), reason=(data.get("reason") or "").strip()),
]
marketing_grader = LLMajRubricGrader(
key="marketing_eval",
system_prompt=PROMPT,
content_builder=build_generation_judge_content,
judge_model="gpt-5.2",
json_schema_name="marketing_flyer_eval",
json_schema=marketing_schema,
result_parser=parse_marketing_result,
)
coffee_results = evaluate(
cases=[coffee_case],
model_runs=[coffee_run],
graders=[marketing_grader],
output_store=coffee_store,
)
coffee_result = coffee_results[0]
# Simple alternative: exact text-rendering check via OCR-style extraction.
REQUIRED_TEXT = {
"WINTER LATTE WEEK",
"Try our Cinnamon Oat Latte",
"20% OFF • Mon–Thu",
"Order Ahead",
"123 Market St • 7am–6pm",
}
def extract_text_from_flyer(image_path: str | Path, model: str = "gpt-5.2") -> list[str]:
judge_client = client if "client" in globals() else OpenAI()
image_url = image_to_data_url(Path(image_path))
instructions = (
"List every piece of text visible in this flyer image. "
"Return one line per text item and preserve capitalization, punctuation, and spacing exactly."
)
response = judge_client.responses.create(
model=model,
instructions=instructions,
input=[
{
"role": "user",
"content": [
{"type": "input_image", "image_url": image_url},
],
}
],
)
return [line.strip() for line in (response.output_text or "").splitlines() if line.strip()]
coffee_image_path = Path(coffee_result["artifact_paths"][0])
extracted_lines = extract_text_from_flyer(coffee_image_path)
missing = sorted(REQUIRED_TEXT - set(extracted_lines))
extra = sorted(set(extracted_lines) - REQUIRED_TEXT)
text_rendering_pass = not missing and not extra
parts = []
if missing:
parts.append("missing: " + ", ".join(missing))
if extra:
parts.append("extra: " + ", ".join(extra))
text_rendering_reason = "; ".join(parts) if parts else "Exact text match."
# Keep both: the judge's text_rendering and the OCR exact-match result.
text_rendering_judge_score = coffee_result["scores"].get("text_rendering")
text_rendering_judge_reason = coffee_result["reasons"].get("text_rendering", "")
coffee_result["scores"]["text_rendering_judge"] = text_rendering_judge_score
coffee_result["reasons"]["text_rendering_judge"] = text_rendering_judge_reason
coffee_result["scores"]["text_rendering_ocr"] = text_rendering_pass
coffee_result["reasons"]["text_rendering_ocr"] = text_rendering_reason
coffee_result
{'test_id': 'coffee_flyer_generation',
'model_label': 'gpt-image-1.5',
'task_type': 'image_generation',
'artifact_paths': ['../../images/gen_coffee_flyer_generation_gpt-image-1.5_1769657762450_0.png'],
'scores': {'instruction_following': True,
'text_rendering': True,
'layout_hierarchy': 5.0,
'style_brand_fit': 5.0,
'visual_quality': 5.0,
'verdict': 'PASS',
'text_rendering_judge': True,
'text_rendering_ocr': False},
'reasons': {'instruction_following': '',
'text_rendering': '',
'layout_hierarchy': '',
'style_brand_fit': '',
'visual_quality': '',
'verdict': 'All required lines appear and match exactly (including casing, hyphen/en-dash, and punctuation), with no extra words, prices, URLs, or QR codes. Text is crisp and legible. Hierarchy follows the requested order: shop name at top, then headline, subheadline, offer, CTA, and footer address/hours. Visual style is warm, cozy, minimal specialty coffee with a realistic latte photo (not cartoonish). No visible watermarks, UI elements, or distracting artifacts; overall print-flyer quality is clean.',
'text_rendering_judge': '',
'text_rendering_ocr': 'missing: 123 Market St • 7am–6pm, 20% OFF • Mon–Thu; extra: 123 Market St - 7am-6pm, 20% OFF - Mon-Thu, SUNRISE COFFEE'},
'run_params': {'model': 'gpt-image-1.5', 'n': 1, 'size': '1024x1024'}}
Resultados de la generación de folletos
Renderiza la imagen generada, el prompt y las puntuaciones lado a lado.
render_result_table(case=coffee_case, result=coffee_result, title="Coffee Flyer: Prompt vs. Scores")
<IPython.core.display.HTML object>
<IPython.core.display.HTML object>
| Prompt de entrada | Puntuaciones | Razonamiento |
|---|---|---|
Create a print-ready vertical A4 flyer for a coffee shop called Sunrise Coffee. Use a warm, cozy, minimal specialty coffee aesthetic (not cartoonish). Required text (must be exact): - WINTER LATTE WEEK - Try our Cinnamon Oat Latte - 20% OFF - Mon-Thu - Order Ahead - 123 Market St - 7am-6pm Do not include any other words, prices, URLs, or QR codes. Criteria: All required text appears exactly as written and is legible. Layout reads clearly: shop name -> headline -> subheadline -> offer -> CTA -> footer. Style matches warm, cozy, specialty coffee and is not cartoonish. No extra text, watermarks, or irrelevant UI-like elements. | instruction_following: True layout_hierarchy: 5.0 style_brand_fit: 5.0 text_rendering: True text_rendering_judge: True text_rendering_ocr: False verdict: PASS visual_quality: 5.0 | All required lines appear and match exactly (including casing, hyphen/en-dash, and punctuation), with no extra words, prices, URLs, or QR codes. Text is crisp and legible. Hierarchy follows the requested order: shop name at top, then headline, subheadline, offer, CTA, and footer address/hours. Visual style is warm, cozy, minimal specialty coffee with a realistic latte photo (not cartoonish). No visible watermarks, UI elements, or distracting artifacts; overall print-flyer quality is clean. |




