DEV Community

Cover image for 500 portadas distintas de un solo modelo de $0.04: arte de portada con IA, consistente con la marca, en un generador económico

500 portadas distintas de un solo modelo de $0.04: arte de portada con IA, consistente con la marca, en un generador económico

La lección de cabecera: en un modelo económico, la calidad no la consigues escribiendo un mejor prompt único, la consigues decidiendo en qué es bueno el modelo, haciendo el resto tú mismo, y diseñando la variedad como una característica del producto en lugar de esperar que la semilla la entregue.

Arquitectura

Admin (SPA) ──POST /blog/generate-image──▶ FastAPI (Fargate, us-east-1)
                                              │
                              boto3 bedrock-runtime (us-west-2)
                                              │
                                   Stable Image Core ──▶ PNG en base64 (2016×1152)
                                              │
                       Pillow: composición opcional del lockup de marca
                                              │
                          redimensionar → WebP (1200×630) → S3 → CloudFront
Enter fullscreen mode Exit fullscreen mode

Una sola petición sincrónica. Sin cola, sin GPU, sin hospedar el modelo.
El endpoint es un orquestador delgado: construye un prompt, llama a
Bedrock, opcionalmente superpone el logo, y le entrega los bytes a la
pipeline de imágenes que ya existía (que quita los metadatos, redimensiona, y convierte a WebP).

La decisión arquitectónica más importante de todas es lo que no está aquí: sin modelo con ajuste fino, sin LoRA, sin grupo de autoescalado de GPU. La consistencia de marca se compra por completo con un envoltorio de prompt y un post-proceso. Eso mantiene el costo marginal de una portada en unos centavos y el costo operativo en cero, que es justo el punto para arte editorial de bajo volumen.

Fase 0, el bug del paso directo

La primera versión hizo lo obvio: tomar el texto y mandarlo al
modelo.

# NO LO HAGAS, el punto de partida fuera de marca
body = {"prompt": admin_text, "mode": "text-to-image"}
Enter fullscreen mode Exit fullscreen mode

El agente interno de autoría, mientras tanto, envolvía cada prompt en unestilo de la casa fijo. Así que las portadas hechas por el agente eran fieles a la marca y las tecleadas a mano eran un terreno sin reglas misma plataforma, dos identidades visuales. La solución es un envoltorio.

Fase 1, un estilo de la casa es un envoltorio de prompt más un prompt negativo

def _styled_cover_prompt(raw: str) -> str:
    return (
        "Wide editorial illustration for a community blog post. "
        f"Theme: {raw.strip()}. "
        "Modern flat-vector art style with subtle gradients, a warm "
        "purple and teal palette, optimistic mood. Abstract metaphor, "
        "no people in close-up, no readable text anywhere. "
        "Clean 16:9 hero composition."
    )

NEGATIVE = "text, words, watermark, logo, signature, low quality, blurry, frame, border"
Enter fullscreen mode Exit fullscreen mode

El prompt negativo importa tanto como el positivo. text, words está
haciendo trabajo de verdad: es la forma más barata de impedir que el
modelo garabatee letras falsas por todo el arte. Nos vamos a apoyar fuerte en eso en un momento.

Este es el estilo de la casa "plano". Luego quisimos un segundo pictórico, dorado y oscuro, para contenido gamificado / de torneo. Ahí fue donde el modelo económico empezó a mostrar sus limites.

Fase 2, nunca dejes que el modelo escriba texto; superpón el logo tú mismo

Todo modelo de difusión de esta gama renderiza el texto como
pseudo glifos garabateados. El arte de referencia que perseguíamos tenía un wordmark; el nuestro salió como MYBRAND → MYBARND. Dos opciones: pelear con el modelo, o dejar de pedirle que haga tipografía.

Dejamos de pedírselo. El prompt positivo dice no readable text anywhere, el prompt negativo prohíbe text, logo, y el lockup de marca real y transparente, wordmark incluido, se superpone encima con Pillow después de generar.

El pegado ingenuo falla: un logo dorado sobre una pintura cargada de
dorado se desvanece. La solución es un respaldo oscuro difuminado detrás
del lockup, luego el lockup, abajo a la derecha:

def overlay_brand(image_bytes: bytes) -> bytes:
    base = Image.open(io.BytesIO(image_bytes)).convert("RGBA")
    logo = Image.open(LOGO_PATH).convert("RGBA")
    target_w = int(base.width * 0.22)
    logo = logo.resize((target_w, int(logo.height * target_w / logo.width)))
    x = base.width - logo.width - int(base.width * 0.035)
    y = base.height - logo.height - int(base.height * 0.035)
    base.alpha_composite(_soft_dark_ellipse(base.size, logo, x, y))  # respaldo de contraste
    base.alpha_composite(logo, (x, y))
    return _to_png(base.convert("RGB"))
Enter fullscreen mode Exit fullscreen mode

Wordmark nítido, legible sobre cualquier arte, cero tipografía generada. Este es el principio general para modelos económicos: cualquier cosa que tenga que ser exacta al píxel, texto, logos, layout, lo haces en código determinista, no en el prompt.

Fase 3, Core fusiona sujetos; diseña alrededor de eso

Pedimos "un grifo de frente a un dragón". Core regresó una sola criatura con partes de grifo y de dragón fusionadas. Este es un modo de falla conocido de los modelos chicos: dos sujetos independientes en un mismo cuadro se mezclan.

Dos mitigaciones, las dos baratas:

Prompt negativo: fused creature, merged animals, two-headed, hybrid,
extra limbs, extra heads
.

Componer alrededor de arquetipos conocidos. Un consejo alrededor de una mesa redonda, un grupo de cuatro aventureros, figuras frente a un portal esas son composiciones que el modelo ha visto miles de veces y monta correctamente, multitud y todo. Un duelo libre de dos bestias distintas no lo es. Convertimos las cosas que queríamos retratar (comunidad, colaboración) en arquetipos que el modelo de verdad puede renderizar.

La regla general: no pelees contra los ajustes composicionales del modelo elige sujetos o temas que se adecuen a ellos.

El callejón sin salida que vale la pena documentar: fusión cálido + frío

Intentamos reproducir una referencia que fusionaba una taberna medieval acogedora con interfaces holográficas cian, cálido y frío, fantasía y tecnología, en un mismo cuadro. Catorce generaciones a través de cuatro estrategias de prompt después, el veredicto fue concluyente:

La tecnología enterrada en una descripción cálida → Core soltaba la
tecnología por completo (taberna pura).

La tecnología al frente → Core inundaba el cuadro de pantallas cian (sala de servidores pura).

Equilibrado, con cuentas explícitas ("dos o tres hologramas") → revertía a uno o al otro, dependiendo de la semilla.

Core no puede sostener dos estéticas fuertes y opuestas en tensión. Un modelo más fuerte (la referencia salió de uno) sí puede; Core no. Anotamos el hallazgo y seguimos adelante en lugar de quemar más generaciones contra un techo del modelo. El movimiento de ingeniería honesto en una gama económica es saber cuándo pegaste contra el muro y dejar de pagar por darle de topes, la alternativa (componer los hologramas nosotros mismos de manera procedural) era real pero no valía la pena para el valor.

Fases 4–6, diseñar la variedad, porque la semilla no lo va a hacer

Este era el problema de producto de verdad. Con el envoltorio "épico"
pesado puesto, prompts distintos producían imágenes casi idénticas. Dos
temas opuestos, "trabajo remoto asíncrono entre zonas horarias" y
"depurar una caída de producción a las 3am", los dos renderizaban al
mismo guerrero dorado con una espada en llamas. El andamio de estilo
(elementos de héroe en dorado bruñido, ambiente heroico ceremonial,
heráldica de gremio) era tan dominante que el modelo se aferraba a él e ignoraba el tema. La semilla aleatoria no ayudaba: el prompt restringe la salida mucho más de lo que la semilla la perturba.

Tres palancas, cada una medida contra la anterior:

4a. Encabeza con el tema; degrada el estilo a un tratamiento. Pon el sujeto primero, aplica el look como un acabado en lugar de como palabras clave que dictan el sujeto. De repente "trabajo remoto" renderizaba a un desarrollador frente a un mapamundi brillante y "depurar a las 3am" renderizaba a un ingeniero en un cuarto de control oscuro. Fidelidad al tema: arreglada. Pero cada portada seguía siendo dorado cálido sobre fondo oscuro.

4b. Rota la paleta. La paleta fija única era el mayor motor de
mismidad. Un conjunto rotatorio, teal/brasa/azul-acero/violeta/esmeralda/carmesí,
cada uno conservando el dorado como hilo conductor, rompió el monocromo.
El color y el ambiente ahora variaban por generación.

4c. Rota el escenario. Los temas de nuestro blog son casi sinónimos
(carreras, empleos, mentoría, comunidad), así que aun con la rotación de
paleta, "avanza en tu carrera" y "sube de nivel tus habilidades" todavía
se montaban igual. La solución que funcionó: meter el sujeto en un
escenario distinto y fiel a la marca cada vez, biblioteca, fragua, pico
de montaña, observatorio, mercado. Tres temas sinónimos de carrera luego
renderizaron como una biblioteca, una fragua, y un observatorio.

def _epic_cover_prompt(raw, motif="champion", composition=None, palette=None, setting=None):
    scene = MOTIFS.get(motif, "")
    if scene:                                  # un motivo ES el sujeto
        subject = f"{scene}, evoking {raw.strip()}"
    else:                                      # si no: el tema encabeza, metido en un escenario
        subject = f"{raw.strip()}, {setting or random.choice(SETTINGS)}"
    comp = composition or random.choice(COMPOSITIONS)
    pal  = palette or random.choice(PALETTES)
    return f"{subject}. {comp}. Rendered as painterly key-art, ... a rich {pal} palette ..."
Enter fullscreen mode Exit fullscreen mode

La variedad es combinatoria y del lado del servidor: 10 escenarios × 7
paletas × 8 composiciones ≈ 560 encuadres distintos, elegidos al azar por
llamada, antes de que la semilla siquiera entre. La variedad dejó de ser
algo que esperábamos que el modelo proveyera y se volvió un espacio de
parámetros que poseemos.

Límite honesto: para exactamente el mismo tema, Core todavía tiende a
montar el sujeto igual, los modificadores de composición son débiles
contra una escena fuertemente implicada. La variedad de verdad viene de
temas distintos + paleta + escenario + semilla, no de volver a tirar un
mismo prompt.

Fase 6, un lente de campaña. Como la plataforma es gamificada (roles,
niveles, misiones), un encuadre de campaña tipo RPG se mapea sobre cada
tema: aprender = misiones, roles = clases, crecimiento = subir de nivel,
comunidad = el grupo. Es un tercer estilo junto a los otros (no
reemplazamos los que funcionan), reutilizando la misma maquinaria de
rotación con escenas de RPG, un tablero de misiones, un grupo de clases,
un árbol de habilidades. Mismo motor, narrativa más rica, más variedad
gratis.

La economía

A bajo volumen editorial, el modelo de API por imagen gana de manera
decisiva. Las cifras son precio de lista al momento de escribir (us-west-2)
y se van a mover, trátalas como proporciones, no como cotizaciones.

Opción Costo unitario Costo fijo Operación
Bedrock Stable Image Core ~$0.04 / imagen $0 ninguna
Bedrock Stable Image Ultra ~$0.14 / imagen $0 ninguna
SDXL autoalojado (g5.xlarge) ~$0 marginal ~$1.00 / hora-GPU siempre encendido o dolor de arranque en frío AMI, escalado, parchado
Google Imagen (Vertex) ~$0.04 / imagen $0 ninguna, pero una segunda nube
Midjourney n/a (suscripción) $10–60 / mes sin API sancionada

Un blog que genera un puñado de portadas al día cuesta centavos al mes en
Core. Para llegar al punto de equilibrio contra una sola g5.xlarge siempre
encendida (~$720/mes) tendrías que generar ~18,000 portadas al mes.
Nosotros generamos quizás 100. La cuenta del autoalojado solo se voltea a
una escala que nunca vamos a alcanzar para arte editorial, e incluso
entonces te apuntaste a AMIs, fijado de drivers, y un autoescalador. El
modelo más barato que pasa la barra de calidad, llamado por imagen, es la
respuesta correcta aquí, y reconocer eso temprano nos ahorró un flujo de
trabajo completo de operación de GPU.

La elección de gama dentro de Bedrock también importa: Ultra es ~3.5× el
precio de Core por una mejora de calidad que un logo post-superpuesto y la
rotación de paleta volvieron innecesaria para las miniaturas. Gastamos el
dinero donde aterriza el ojo humano (el sujeto y el color), no en un número
de gama.

La trampa de región

Los modelos de imágenes de Bedrock no están en cada región, y el modelo
que quieres acota la región que llamas. Nuestro backend corre en
us-east-1; Stable Image Core se aprovisionó en us-west-2. Así que el
cliente está fijado a otra región a propósito:

client = boto3.client("bedrock-runtime", region_name="us-west-2")  # NO la región de la app
Enter fullscreen mode Exit fullscreen mode

Tres consecuencias que vale la pena saber antes de armar la arquitectura
alrededor de esto:

La disponibilidad es por modelo, por región. "Bedrock está en mi región"
no es "este modelo está en mi región". Revisa el modelo, no el servicio.

Las llamadas a otra región agregan latencia y cruzan una frontera de
datos. Para arte de portada de dispara-y-olvida eso está bien; para
cualquier cosa sensible a la latencia o a la residencia no lo está, y
puede que necesites aprovisionar el modelo en la región de tu app o elegir
otro.

El acceso al modelo es una concesión explícita. Habilitar un modelo de
Bedrock es un paso de consola por cuenta/región, y el IAM tiene que
permitir bedrock:InvokeModel sobre ese ARN del modelo. Un ARN con
comodín sobre el modelo base nos ahorró re-editar la política en cada
cambio de modelo.

Lo que un modelo económico no puede hacer (medido, no adivinado)

Tipografía. Nunca. Superpón el texto y los logos tú mismo.

Dos estéticas opuestas en un cuadro (cálido/frío, fantasía/tecnología). Se
colapsa a una. Elige un carril o superpón.

Escenas libres de varios sujetos. Dos sujetos distintos se fusionan. Usa
arquetipos que el modelo ya monta (mesas, grupos, portales).

Variedad composicional por tema bajo demanda. Una escena fuertemente
implicada se monta igual sin importar las pistas de cámara. Diseña la
variedad a través de paleta/escenario/semilla en lugar de esperarla de un
solo prompt.

Ninguno de estos fue un motivo de descarte. Cada uno se volvió una
restricción de diseño que empujó el trabajo fuera del modelo y hacia
código que controlamos, que es justo donde quieres que vivan las partes
deterministas de una marca de todos modos.

Lo que te diría

Decide el trabajo del modelo, luego recupera el resto. El texto, los
logos, el layout, y la variedad exacta de marca son deterministas; hazlos
en código. Deja que el modelo pinte.

Un estilo de la casa es un envoltorio de prompt + un prompt negativo, no
un ajuste fino. Para bajo volumen, ese es todo el presupuesto de
consistencia de marca que necesitas.

La variedad es un espacio de parámetros que posees, no una semilla a la
que le cruzas los dedos. Rota los ejes que no definen la marca (paleta,
escenario, cámara); fija el que sí (aquí, el dorado).

Pon precio a la API por imagen contra una GPU con honestidad. Por debajo
de decenas de miles de imágenes al mes, la API gana en costo y en
operación, no construyas la flota.

Revisa modelo-por-región antes de armar la arquitectura. El modelo acota
la región, y la concesión es explícita.

Conoce el techo y detente ahí. Catorce generaciones nos dijeron que Core
no puede fusionar cálido y frío. Anotar eso y seguir adelante fue más
barato que un prompt ingenioso que nunca iba a llegar.

Si te llevas una sola cosa: en un modelo de imágenes económico, la calidad
y la variedad no son cosas que pides en un prompt, son cosas que diseñas
alrededor de los límites conocidos del modelo. El prompt es la parte más
chica.

Top comments (0)