Tutorial
Construir un ampliador de prompts de IA para imágenes: expansión de prompts con LLM para ideogram-v4 y flux-2
Los prompts breves solo generan imágenes genéricas. "coffee shop logo" apenas le da al modelo información útil: ningún nombre que renderizar, ningún estilo, ninguna composición, ninguna paleta de colores. Los pipelines de imágenes modernos resuelven esto con la expansión de prompts: antes de que el prompt llegue a la API de imágenes, se usa una pasada de LLM de bajo coste para convertir un prompt de cinco palabras en una descripción rica y adaptada a las características del modelo. Algunas plataformas ya integran el ampliador dentro de modelos concretos, lo que por sí solo demuestra lo común que es este paso de preprocesamiento.
Este tutorial construye ese paso por tu cuenta como un pequeño pipeline en Python: un LLM enriquece el prompt breve del usuario y, después, el resultado se envía a ideogram-v4 o flux-2 a través de la API de tareas unificada de hiapi. Puedes controlar las reglas de expansión por modelo, lo cual es importante porque los elementos que hacen brillar un prompt de Ideogram (texto exacto entre comillas) no son los mismos que hacen brillar un prompt de FLUX.2 (escena, iluminación, cámara).
Requisitos previos: una API key de hiapi (créala en la consola), Python 3.10+ y requests (pip install requests), además de un endpoint de LLM compatible con chat-completions que puedas usar para el paso de expansión (un modelo pequeño/barato es suficiente: el ampliador solo escribe unas 100 palabras cada vez).
¿Por qué expandir los prompts?
En hiapi hay dos modelos con su propio interruptor de expansión: qwen-image-2.0 tiene prompt_extend, y flux-1.1-pro tiene prompt_upsampling. Al activarlos, la plataforma enriquece el prompt en el servidor.
Pero ideogram-v4 y flux-2/text-to-image —los dos modelos más fuertes en tipografía y en seguimiento de prompts, respectivamente— no tienen ampliador integrado. Su esquema de entrada solo acepta la cadena del prompt y los parámetros de generación, nada más. Para obtener prompts más ricos en estos modelos, necesitas aportar tú mismo el paso de expansión. La ventaja: tu propio ampliador conoce el modelo, conoce la marca y es depurable. Puedes ver exactamente qué cadena se envía.
El ampliador: un system prompt por familia de modelos
Toda la técnica es una función que recibe una cadena y devuelve una cadena. A continuación, la llamada al LLM independiente del proveedor: apunta LLM_BASE_URL / LLM_API_KEY / LLM_MODEL al endpoint compatible con chat-completions que ya tengas en marcha:
import os
import requests
LLM_BASE_URL = os.environ["LLM_BASE_URL"] # e.g. https://your-llm-endpoint/v1
LLM_API_KEY = os.environ["LLM_API_KEY"]
LLM_MODEL = os.environ["LLM_MODEL"]
EXPANDER_PROMPTS = {
# ideogram-v4 is a typography specialist: exact strings to render go in
# double quotes, and layout/style language matters more than camera language.
"ideogram-v4": (
"You expand short image prompts for a typography-focused image model. "
"Rules: keep the user's intent exactly; if the image should contain text, "
"put the exact words in double quotes; describe layout (centered, badge, "
"poster), lettering style, palette, and background; flat/vector terms are "
"good; no camera or lens language; output 60-100 words, one paragraph, "
"no preamble."
),
# flux-2 has strong prompt adherence for scenes: subject, setting, lighting,
# mood, camera. It has no negative prompt, so state what you want, not what
# you don't.
"flux-2/text-to-image": (
"You expand short image prompts for a high-fidelity text-to-image model. "
"Rules: keep the user's intent exactly; add subject detail, setting, "
"lighting, mood, composition and camera hints; state desired qualities "
"positively (the model has no negative prompt); output 80-120 words, one "
"paragraph, no preamble."
),
}
def expand_prompt(short_prompt: str, target_model: str) -> str:
"""LLM pass: short user prompt -> enriched, model-appropriate prompt."""
r = requests.post(
f"{LLM_BASE_URL}/chat/completions",
headers={"Authorization": f"Bearer {LLM_API_KEY}"},
json={
"model": LLM_MODEL,
"messages": [
{"role": "system", "content": EXPANDER_PROMPTS[target_model]},
{"role": "user", "content": short_prompt},
],
"temperature": 0.7,
},
timeout=60,
)
r.raise_for_status()
return r.json()["choices"][0]["message"]["content"].strip()
Dado que el pipeline solo necesita str -> str, puedes sustituirlo por cualquier ampliador: un LLM más grande, un modelo ajustado, o incluso una plantilla determinista para una campaña de marca fija.
Antes y después
Entrada: coffee shop logo
Tras expandir para ideogram-v4:
Un logotipo de emblema circular minimalista para una cafetería de especialidad llamada "Driftwood Coffee", con el texto "DRIFTWOOD COFFEE" en una tipografía serif de mayúsculas limpias rodeando una sencilla taza de café de línea, con tres líneas de vapor. Fondo cálido color crema, texto y líneas en marrón espresso oscuro, estilo vectorial plano, alto contraste, composición centrada con amplio espacio negativo alrededor.
Tras expandir para flux-2:
Interior de una acogedora cafetería de especialidad durante la hora dorada, con luz solar cálida entrando por un amplio ventanal sobre una barra de nogal, un barista creando latte art en una taza de cerámica, estantes detrás llenos de tarros de cristal ámbar y sacos de café de arpillera, suave vapor elevándose, profundidad de campo reducida, estética fotográfica de 35mm, tonos cálidos y acogedores, enfoque nítido en la taza.
La misma intención de cinco caracteres, dos prompts completamente distintos —y cada uno preciso en su propio terreno—. La versión de Ideogram marca entre comillas el texto exacto que debe renderizarse (su especialidad); la versión de FLUX.2 dedica el esfuerzo a la escena y la luz (su especialidad).
Pipeline completo: expandir, crear tarea, sondear, descargar
Todos los modelos de generación de hiapi se invocan a través de un único endpoint asíncrono: POST /v1/tasks crea la tarea, GET /v1/tasks/<id> la sondea, y el artefacto completado aparece en data.output[0].url.
import json
import os
import time
import requests
HIAPI_KEY = os.environ["HIAPI_API_KEY"]
TASKS = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {HIAPI_KEY}", "Content-Type": "application/json"}
def create_task(model: str, input_obj: dict) -> str:
r = requests.post(TASKS, headers=HEADERS,
json={"model": model, "input": input_obj}, timeout=60)
data = r.json()
task_id = (data.get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"create task failed: {json.dumps(data)[:300]}")
return task_id
def wait_task(task_id: str, timeout_s: int = 300, poll_s: int = 5) -> dict:
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{TASKS}/{task_id}",
headers={"Authorization": f"Bearer {HIAPI_KEY}"}, timeout=30)
task = r.json().get("data") or {}
if task.get("status") == "success":
return task
if task.get("status") == "fail":
err = task.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
time.sleep(poll_s)
raise TimeoutError(f"task {task_id} still running after {timeout_s}s")
def generate(short_prompt: str, target_model: str) -> str:
expanded = expand_prompt(short_prompt, target_model)
print(f"expanded prompt -> {expanded}\n")
if target_model == "ideogram-v4":
input_obj = {
"prompt": expanded,
"rendering_speed": "BALANCED", # required: TURBO | BALANCED | QUALITY
"aspect_ratio": "1:1", # 1:1, 16:9, 4:3, 9:16, 3:4
}
else: # flux-2/text-to-image
input_obj = {
"prompt": expanded,
"aspect_ratio": "3:2", # 1:1, 16:9, 3:2, 2:3, 4:5, 5:4, 9:16, 3:4, 4:3, custom
"resolution": "1 MP", # '0.5 MP' | '1 MP' | '2 MP' | '4 MP'
}
task_id = create_task(target_model, input_obj)
task = wait_task(task_id)
url = task["output"][0]["url"]
# Output URLs expire — download immediately, don't hot-link them.
img = requests.get(url, timeout=120).content
fname = f"{target_model.replace('/', '-')}.png"
with open(fname, "wb") as f:
f.write(img)
return fname
if __name__ == "__main__":
print(generate("coffee shop logo", "ideogram-v4"))
print(generate("coffee shop logo", "flux-2/text-to-image"))
Ejecútalo:
export HIAPI_API_KEY="sk-..."
export LLM_BASE_URL="https://your-llm-endpoint/v1"
export LLM_API_KEY="..."
export LLM_MODEL="your-small-model"
python pipeline.py
Atención al esquema de entrada: cada modelo es distinto
La API de tareas valida input estrictamente según el modelo; los campos desconocidos se rechazan en lugar de ignorarse. Si envías a ideogram-v4 un campo que no reconoce:
{
"code": 400,
"error_code": "INVALID_REQUEST",
"message": "invalid input: <root>: additional properties 'size' not allowed"
}
Hay tres diferencias en este pipeline que merecen atención:
rendering_speedes obligatorio en ideogram-v4 (TURBO/BALANCED/QUALITY: cada nivel tiene un precio distinto). flux-2 no tiene este campo.- El menú de relaciones de aspecto no coincide. ideogram-v4 ofrece cinco presets de aproximadamente 1 MP (
1:1,16:9,4:3,9:16,3:4); flux-2 ofrece un conjunto más amplio, además decustomywidth/heightexplícitos (256–2048, múltiplos de 16). Si tu aplicación permite al usuario elegir la relación de aspecto, mapéala según el modelo antes de despachar. - flux-2 dimensiona por niveles de megapíxeles (
resolution: de'0.5 MP'a'4 MP') y no tiene prompt negativo: otra razón por la que el ampliador debe formular siempre en positivo.
Refuerzo para producción
Con gran volumen, prefiere callbacks en lugar de sondeo. Al crear la tarea, añade el bloque callback y hiapi te enviará una notificación POST al alcanzar el estado final, sin necesidad de sondear:
payload = {
"model": "flux-2/text-to-image",
"input": input_obj,
"callback": {"url": "https://your-domain.com/hiapi/callback", "when": "final"},
}
El sondeo es más simple y adecuado para scripts locales, tareas cron y volúmenes bajos; los callbacks evitan desperdiciar peticiones en generaciones largas y notifican exactamente una vez por tarea. El patrón de producción habitual es usar el callback como canal principal y un sondeo de reconciliación lento como respaldo.
Haz los reintentos idempotentes. Al crear la tarea, envía la cabecera Idempotency-Key (máximo 255 bytes, se conserva 24 horas). Un reintento con la misma key devuelve el taskId original (con la cabecera de respuesta Idempotent-Replay: true) en lugar de crear —y facturar— una tarea duplicada. Reutilizar la key con un cuerpo de petición distinto devuelve 422 IDEMPOTENCY_KEY_MISMATCH; si un reintento compite con la petición original en curso, devuelve 409 y Retry-After. Deriva la key de tus propios IDs de tarea:
headers = {**HEADERS, "Idempotency-Key": f"expand-pipeline-{job_id}"}
Gestiona explícitamente los fallos de autenticación. Una key no válida o sin permisos suficientes falla con HTTP 401 y un cuerpo de error con "code": "permission_denied": captúralo por separado de los errores de generación, porque reintentar no sirve de nada:
if r.status_code == 401:
raise SystemExit("hiapi key invalid or lacks access to this model")
Persiste las salidas de inmediato. Las URL de salida de las tareas tienen caducidad. En cuanto la tarea tenga éxito, descarga los bytes y guárdalos por tu cuenta.
Pon riendas al ampliador. Limita la expansión a unas 120 palabras como máximo, pasa temperature ≤ 0.8 y registra en el log el prompt breve y el prompt expandido junto con el taskId. Cuando una generación parezca incorrecta, la primera pregunta siempre es «¿qué recibió realmente el modelo?». Con tu propio ampliador, puedes responderla.
Ojo con la acumulación de costes. Ahora pagas dos llamadas por imagen: un completion de LLM pequeño (normalmente insignificante) más la generación de imagen. ideogram-v4 factura por imagen según el nivel de rendering_speed, y flux-2 factura por imagen según el nivel de resolución. Consulta las tarifas vigentes de ambos en la página de precios.
Qué hacer a continuación
- Detalles y parámetros de los modelos: páginas de ideogram-v4 y flux-2
- Dónde la plataforma ya incluye un ampliador y puedes saltarte el tuyo propio: qwen-image-2.0 (
prompt_extend) y flux-1.1-pro (prompt_upsampling) - Referencia de la API de tareas y más guías de integración: documentación
- Compara el precio por imagen de cada modelo: precios
Preguntas frecuentes
¿Puedo enviar el mismo prompt expandido a los dos modelos? Sí, pero sacrificas calidad. Ideogram prefiere texto literal entre comillas y lenguaje de maquetación; FLUX.2 prefiere escena, iluminación y detalles de cámara. Mantener un system prompt de ampliador por familia de modelos cuesta solo unas pocas líneas de código y rinde en cada generación.
¿Hay algún modelo en hiapi que expanda el prompt por mí?
Sí: qwen-image-2.0 acepta prompt_extend, y flux-1.1-pro acepta prompt_upsampling. Para ideogram-v4 y flux-2/text-to-image no hay ampliador integrado, que es justo el caso de uso de este tutorial.
¿Qué longitud debe tener el prompt expandido? Para este pipeline, el punto óptimo son 60–120 palabras. Pasarse diluye la señal, y algunos modelos de bajo coste truncan de forma agresiva los prompts largos (por ejemplo, flux-schnell trunca en torno a 256 tokens). La densidad vence a la longitud: cada frase añadida debe restringir la imagen.
¿flux-2 admite prompt negativo? No. Formula lo que quieres, no lo que no quieres, y escribe esta regla en el system prompt del ampliador para que nunca emita expresiones como "no text, no watermark" que el modelo leería como contenido.
¿Y si el LLM cambia la intención del usuario? Restríngelo en el system prompt ("keep the user's intent exactly"), mantén una temperature moderada y registra el antes y el después. Para flujos de alto riesgo, muestra al usuario el prompt expandido antes de generar, u ofrece un interruptor de «usar mi prompt tal cual» para saltarse el ampliador.
¿Por qué mi petición falla con 400 antes de que ocurra ninguna generación?
Por la validación del esquema. Cada modelo valida estrictamente input: un campo obligatorio ausente (como rendering_speed en ideogram-v4) o un campo desconocido falla de inmediato, devolviendo error_code: "INVALID_REQUEST" y un mensaje que señala el campo infractor. Estos fallos no se cobran; corrige el campo y vuelve a enviarla.
Artículos relacionados
Ver todos los artículos →-
Tutorial
Cómo llamar a hailuo-2.3-fast/image-to-video a través de la API de HiAPI: práctica con curl y Python
La primera solicitud ejecutable del nivel rápido y de bajo coste de imagen a vídeo: del ID del modelo a la descarga del MP4
-
Tutorial
Cómo llamar a hailuo-2.3/text-to-video mediante la API de HiAPI: curl, Python y ejemplos de solicitud ejecutables
-
Tutorial
Uso de Kandinsky 6.0 Pro texto a video en HiAPI: tutorial con curl y Python
Demostración práctica centrada en la solicitud: construir la llamada de prompt a clip de video, enviar la solicitud, sondear el estado de finalización y descargar el resultado