Tutoriel
Créer un amplificateur de prompts pour IA générative d'images : l'expansion de prompt par LLM pour ideogram-v4 et flux-2
Les prompts courts ne produisent que des images génériques. « coffee shop logo » ne donne presque rien d'exploitable au modèle — pas de nom à rendre, pas de style, pas de composition, pas de palette de couleurs. Les pipelines d'images modernes résolvent ce problème avec l'expansion de prompt : avant que le prompt n'atteigne l'API d'image, avant cela, un passage peu coûteux par un LLM transforme un prompt de cinq mots en une description riche et adaptée au modèle. Certaines plateformes intègrent désormais des amplificateurs directement dans des modèles spécifiques, ce qui montre bien à quel point cette étape de prétraitement est répandue.
Ce tutoriel construit lui-même cette étape, sous la forme d'un petit pipeline Python : un LLM enrichit le prompt court de l'utilisateur, puis le résultat est envoyé à ideogram-v4 ou à flux-2 via l'API de tâches unifiée de hiapi. Vous pouvez contrôler les règles d'expansion par modèle — c'est important, car ce qui fait briller un prompt Ideogram (le texte exact entouré de guillemets) diffère de ce qui fait briller un prompt FLUX.2 (la scène, l'éclairage, la caméra).
Prérequis : une clé API hiapi (à créer dans la console), Python 3.10+ et requests (pip install requests), ainsi qu'un endpoint LLM compatible chat-completions utilisable pour l'étape d'expansion (un petit modèle ou un modèle bon marché suffit — l'amplificateur n'écrit qu'environ 100 mots à chaque fois).
Pourquoi amplifier les prompts ?
Deux modèles sur hiapi intègrent un commutateur d'amplification : qwen-image-2.0 dispose de prompt_extend, et flux-1.1-pro de prompt_upsampling. Une fois activés, la plateforme enrichit le prompt côté serveur.
Mais ideogram-v4 et flux-2/text-to-image — les deux modèles les plus forts respectivement en typographie et en respect du prompt — n'ont pas d'amplificateur intégré. Leur schéma d'entrée n'accepte qu'une chaîne de prompt et des paramètres de génération, rien d'autre. Si vous voulez des prompts plus riches sur ces modèles, il vous faut apporter votre propre étape d'expansion. L'avantage : votre amplificateur connaît le modèle, connaît la marque, et se débogue. Vous voyez exactement quelle chaîne est envoyée.
L'amplificateur : un prompt système par famille de modèles
Toute l'astuce tient dans une fonction chaîne-entrée, chaîne-sortie. Voici l'appel LLM indépendant du fournisseur — pointez LLM_BASE_URL / LLM_API_KEY / LLM_MODEL vers n'importe quel endpoint compatible chat-completions que vous faites déjà tourner :
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()
Comme le pipeline n'attend qu'un str -> str, vous pouvez remplacer l'amplificateur par n'importe quel autre — un LLM plus grand, un modèle affiné, ou même un template déterministe pour une campagne de marque fixe.
Avant / après
Entrée : coffee shop logo
Après expansion pour ideogram-v4 :
Un logo minimaliste en badge circulaire pour un café de spécialité nommé « Driftwood Coffee », les mots « DRIFTWOOD COFFEE » disposés en une police serif majuscule propre autour d'une tasse à café simple en trait fin, avec trois lignes de vapeur. Fond crème chaleureux, texte et traits en brun espresso profond, style vectoriel plat, fort contraste, composition centrée, généreuses marges blanches tout autour.
Après expansion pour flux-2 :
Intérieur chaleureux d'un café de spécialité à l'heure dorée, la lumière du soleil chaude traverse une grande baie vitrée et se pose sur un comptoir en noyer, un barista réalise un latte art dans une tasse en céramique, derrière lui des étagères garnies de bocaux en verre ambré et de sacs de grains de café en toile de jute, une vapeur douce s'élève, faible profondeur de champ, rendu photographique 35 mm, palette chaude et accueillante, mise au point nette sur la tasse.
La même intention de cinq caractères, deux prompts totalement différents — et chacun parfaitement dans sa cible. La version Ideogram met entre guillemets le texte exact à rendre (sa spécialité) ; la version FLUX.2 consacre son texte à la scène et à la lumière (sa spécialité).
Le pipeline complet : expansion, création de tâche, interrogation, téléchargement
Tous les modèles de génération hiapi s'appellent via un seul endpoint asynchrone : POST /v1/tasks crée la tâche, GET /v1/tasks/<id> l'interroge, et le résultat terminé apparaît dans 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"))
Pour l'exécuter :
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
Attention au schéma d'entrée — chaque modèle est différent
L'API de tâches valide strictement l'input selon le modèle : les champs inconnus sont rejetés, pas ignorés. Si vous envoyez à ideogram-v4 un champ qu'il ne reconnaît pas :
{
"code": 400,
"error_code": "INVALID_REQUEST",
"message": "invalid input: <root>: additional properties 'size' not allowed"
}
Trois différences méritent l'attention dans ce pipeline :
rendering_speedest obligatoire sur ideogram-v4 (TURBO/BALANCED/QUALITY— les tarifs diffèrent selon le niveau). flux-2 n'a pas ce champ.- Les menus de ratio d'aspect ne concordent pas. ideogram-v4 propose cinq préréglages d'environ 1 MP (
1:1,16:9,4:3,9:16,3:4) ; flux-2 propose un ensemble plus large, pluscustomavecwidth/heightexplicites (256–2048, multiples de 16). Si votre application laisse l'utilisateur choisir le ratio d'aspect, effectuez le mappage par modèle avant l'envoi. - flux-2 dimensionne par paliers de mégapixels (
resolution: de'0.5 MP'à'4 MP'), et n'a pas de prompt négatif — une raison de plus pour que l'amplificateur s'exprime toujours en positif.
Durcissement pour la production
À fort volume, préférez les callbacks à l'interrogation. Ajoutez un bloc callback à la création de la tâche : hiapi vous notifiera par POST à l'état terminal, sans que vous ayez à interroger :
payload = {
"model": "flux-2/text-to-image",
"input": input_obj,
"callback": {"url": "https://your-domain.com/hiapi/callback", "when": "final"},
}
L'interrogation est plus simple, adaptée aux scripts locaux, aux tâches cron et aux faibles volumes ; le callback évite de gaspiller des requêtes sur des générations longues et notifie exactement une fois par tâche. Le schéma de production courant consiste à utiliser le callback comme canal principal et une interrogation lente de réconciliation comme filet de sécurité.
Rendez les retries idempotents. Envoyez l'en-tête Idempotency-Key à la création de la tâche (255 octets max, conservée 24 heures). Un retry avec la même clé renvoie le taskId d'origine (en-tête de réponse Idempotent-Replay: true) au lieu de créer — et facturer — une tâche en double. Réutiliser la clé avec un corps de requête différent renvoie 422 IDEMPOTENCY_KEY_MISMATCH ; un retry qui entre en course avec la requête originale en cours renvoie 409 avec Retry-After. Dérivez la clé de votre propre ID de tâche :
headers = {**HEADERS, "Idempotency-Key": f"expand-pipeline-{job_id}"}
Gérez explicitement les échecs d'authentification. Une clé invalide ou insuffisamment autorisée échoue en HTTP 401, avec "code": "permission_denied" dans le corps d'erreur — capturez-le séparément des erreurs de génération, car un retry ne servirait à rien :
if r.status_code == 401:
raise SystemExit("hiapi key invalid or lacks access to this model")
Persistez immédiatement les sorties. Les URL de sortie des tâches ont une durée de validité. Téléchargez les octets et stockez-les vous-même dès que la tâche réussit.
Tenez l'amplificateur en laisse. Limitez l'expansion à environ 120 mots, passez temperature ≤ 0.8, et journalisez le prompt court et le prompt étendu avec le taskId. Quand un résultat de génération paraît anormal, la première question est toujours « qu'est-ce que le modèle a réellement reçu ? » — avec votre propre amplificateur, vous pouvez y répondre.
Surveillez l'empilement des coûts. Désormais, chaque image vous coûte deux appels : un petit completion LLM (généralement négligeable) plus la génération d'image — ideogram-v4 facture par image selon le niveau de rendering_speed, flux-2 par image selon le palier de résolution. Les tarifs actuels des deux figurent sur la page de tarification.
Et ensuite ?
- Détails et paramètres des modèles : pages des modèles ideogram-v4 et flux-2
- Là où la plateforme intègre déjà un amplificateur, vous pouvez sauter l'amplificateur maison : qwen-image-2.0 (
prompt_extend) et flux-1.1-pro (prompt_upsampling) - Référence de l'API de tâches et autres guides d'intégration : documentation
- Comparer le tarif par image des modèles : tarification
Questions fréquentes
Peut-on envoyer le même prompt étendu aux deux modèles ? Oui, mais au détriment de la qualité. Ideogram privilégie le texte littéral entre guillemets et le langage de mise en page ; FLUX.2 privilégie la scène, l'éclairage et les détails de caméra. Conserver un prompt système d'amplificateur par famille de modèles coûte quelques lignes de code, mais se rentabilise à chaque génération.
hiapi a-t-il un modèle qui peut étendre les prompts à ma place ?
Oui — qwen-image-2.0 accepte prompt_extend, flux-1.1-pro accepte prompt_upsampling. Pour ideogram-v4 et flux-2/text-to-image, il n'y a pas d'amplificateur intégré, ce qui est exactement le cas d'usage de ce tutoriel.
Quelle longueur doit faire un prompt étendu ? Pour ce pipeline, 60 à 120 mots constituent le point idéal. Au-delà, le signal se dilue, et certains modèles bon marché tronquent durement les longs prompts (par exemple flux-schnell tronque vers 256 tokens). La densité prime sur la longueur : chaque phrase ajoutée doit contraindre l'image.
flux-2 prend-il en charge les prompts négatifs ? Non. Formulez ce que vous voulez, pas ce que vous ne voulez pas — et inscrivez cette règle dans le prompt système de l'amplificateur, afin qu'il n'émette jamais de formulations telles que « no text, no watermark » que le modèle lirait comme du contenu.
Et si le LLM modifie l'intention de l'utilisateur ? Encadrez-le dans le prompt système (« keep the user's intent exactly »), gardez une temperature modérée et journalisez le avant/après. Pour les flux à fort enjeu, montrez le prompt étendu à l'utilisateur avant la génération, ou proposez un commutateur « utiliser mon prompt tel quel » pour contourner l'amplificateur.
Pourquoi ma requête échoue-t-elle en 400 avant toute génération ?
Validation de schéma. Chaque modèle valide strictement l'input — un champ obligatoire manquant (comme rendering_speed sur ideogram-v4) ou un champ inconnu échoue immédiatement, en renvoyant error_code: "INVALID_REQUEST" et un message indiquant le champ fautif. Ces échecs ne sont pas facturés ; corrigez le champ et soumettez à nouveau.
Articles similaires
Voir tous les articles →-
Tutoriel
Comment appeler hailuo-2.3-fast/image-to-video via l'API HiAPI : curl et Python en pratique
La première requête exploitable du palier image-vers-vidéo rapide et économique — de l'ID du modèle au téléchargement du MP4
-
Tutoriel
Comment appeler hailuo-2.3/text-to-video via l'API HiAPI : curl, Python et exemples de requêtes exécutables
-
Tutoriel
Utiliser Kandinsky 6.0 Pro texte-vers-vidéo sur HiAPI : tutoriel curl et Python
Démonstration pratique axée sur la requête : construire l'appel du prompt au clip vidéo, soumettre la requête, interroger l'état d'achèvement et télécharger le résultat