HiAPI
Blogue

Tutorial

Criando um ampliador de prompts de imagem com IA: expansão de prompts por LLM para ideogram-v4 e flux-2

Jul 9, 2026
Criando um ampliador de prompts de imagem com IA: expansão de prompts por LLM para ideogram-v4 e flux-2

Prompts curtos só geram imagens genéricas. "coffee shop logo" quase não dá ao modelo nenhuma informação útil — nenhum nome para renderizar, nenhum estilo, nenhuma composição, nenhuma paleta de cores. Os pipelines de imagem modernos resolvem isso com expansão de prompts: uma passada barata por um LLM antes de o prompt entrar na API de imagem, transformando um prompt de cinco palavras em uma descrição rica e adequada às características do modelo. Algumas plataformas já embutem o ampliador em modelos específicos, o que por si só mostra como essa etapa de pré-processamento é comum.

Este tutorial vai construir essa etapa do zero, como um pequeno pipeline em Python: um LLM enriquece o prompt curto do usuário e, em seguida, o resultado é enviado ao ideogram-v4 ou ao flux-2 pela API unificada de tarefas do hiapi. Você pode controlar as regras de expansão por modelo — o que é importante, porque os elementos que fazem um prompt do Ideogram se destacar (texto exato entre aspas) não são os mesmos que fazem um prompt do FLUX.2 se destacar (cena, iluminação, câmera).

Pré-requisitos: uma chave de API do hiapi (criada no console), Python 3.10+ e requests (pip install requests), além de um endpoint de LLM compatível com chat-completions para a etapa de expansão (um modelo pequeno/barato é suficiente — o ampliador escreve apenas cerca de 100 palavras por vez).

Por que expandir prompts?

Dois modelos no hiapi já vêm com um interruptor de expansão: qwen-image-2.0 tem prompt_extend e flux-1.1-pro tem prompt_upsampling. Quando ativados, a plataforma enriquece o prompt no lado do servidor.

Mas ideogram-v4 e flux-2/text-to-image — os dois modelos mais fortes em tipografia e em aderência ao prompt, respectivamente — não têm ampliador embutido. O schema de entrada deles aceita apenas a string do prompt e parâmetros de geração, nada além disso. Se você quer prompts mais ricos nesses modelos, precisa trazer sua própria etapa de expansão. A vantagem: seu próprio ampliador conhece o modelo, conhece a marca e é depurável. Você vê exatamente qual string está sendo enviada.

O ampliador: um system prompt por família de modelo

Todo o truque é uma função que recebe uma string e devolve uma string. A seguir está a chamada ao LLM independente de provedor — aponte LLM_BASE_URL / LLM_API_KEY / LLM_MODEL para qualquer endpoint compatível com chat-completions que você já tenha em execução:

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()

Como o pipeline só precisa de str -> str, você pode trocá-lo por qualquer ampliador — um LLM maior, um modelo ajustado fino ou até um template determinístico preparado para uma campanha de marca fixa.

Antes e depois

Entrada: coffee shop logo

Após expansão para ideogram-v4:

Logotipo minimalista em formato de emblema circular para uma cafeteria artesanal chamada "Driftwood Coffee", com a inscrição "DRIFTWOOD COFFEE" em letras maiúsculas de tipografia serifada limpa circundando uma xícara de café desenhada em linha simples, com três linhas de vapor acima dela. Fundo em tom creme quente, texto e traços em marrom café expresso escuro, estilo vetorial plano, alto contraste, composição centralizada, com bastante espaço em branco ao redor.

Após expansão para flux-2:

Interior aconchegante de uma cafeteria artesanal durante a hora dourada, com luz solar quente atravessando a ampla janela da frente e batendo no balcão de nogueira, um barista criando latte art em uma xícara de cerâmica, prateleiras atrás cobertas de potes de vidro âmbar e sacos de estopa com grãos de café, vapor suave subindo, profundidade de campo rasa, textura fotográfica de 35mm, tons quentes e acolhedores, foco nítido na xícara.

A mesma intenção de cinco caracteres, dois prompts completamente diferentes — e cada um acertando em cheio no que seu modelo faz melhor. A versão para o Ideogram destaca entre aspas o texto exato a ser renderizado (sua especialidade); a versão para o FLUX.2 investe em cena e iluminação (sua especialidade).

Pipeline completo: expandir, criar tarefa, consultar, baixar

Todos os modelos de geração do hiapi são chamados por um único endpoint assíncrono: POST /v1/tasks cria a tarefa, GET /v1/tasks/<id> consulta a tarefa, e o artefato finalizado aparece em 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"))

Execute-o:

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

Atenção ao schema de entrada — cada modelo é diferente

A API de tarefas valida o input estritamente por modelo; campos desconhecidos são rejeitados, não ignorados. Se você enviar ao ideogram-v4 um campo que ele não reconhece:

{
  "code": 400,
  "error_code": "INVALID_REQUEST",
  "message": "invalid input: <root>: additional properties 'size' not allowed"
}

Há três diferenças notáveis neste pipeline:

  • rendering_speed é obrigatório no ideogram-v4 (TURBO/BALANCED/QUALITY — cada nível tem preço diferente). O flux-2 não tem esse campo.
  • Os menus de proporção de aspecto são inconsistentes. O ideogram-v4 oferece cinco predefinições de cerca de 1MP (1:1, 16:9, 4:3, 9:16, 3:4); o flux-2 oferece um conjunto mais amplo, além de custom e width/height explícitos (256–2048, múltiplos de 16). Se a sua aplicação permite que o usuário escolha a proporção de aspecto, faça o mapeamento por modelo antes de despachar.
  • O flux-2 dimensiona por faixas de megapixels (resolution: de '0.5 MP' a '4 MP') e não tem prompt negativo — mais um motivo para o ampliador sempre usar formulações positivas.

Reforçando para produção

Em volumes altos, prefira callbacks a polling. Adicione um bloco callback ao criar a tarefa; o hiapi fará um POST de notificação quando ela chegar ao estado terminal, em vez de você precisar consultar:

payload = {
    "model": "flux-2/text-to-image",
    "input": input_obj,
    "callback": {"url": "https://your-domain.com/hiapi/callback", "when": "final"},
}

O polling é mais simples e ideal para scripts locais, tarefas cron e baixo volume; o callback evita desperdiçar requisições em gerações longas e notifica exatamente uma vez por tarefa. O padrão de produção comum é usar o callback como canal principal e um polling lento de reconciliação como backup.

Torne as retentativas idempotentes. Envie o cabeçalho Idempotency-Key ao criar a tarefa (até 255 bytes, mantido por 24 horas). Repetir com a mesma key retorna o taskId original (com o cabeçalho de resposta Idempotent-Replay: true), em vez de criar — e cobrar — uma tarefa duplicada. Reutilizar a key com um corpo de requisição diferente retorna 422 IDEMPOTENCY_KEY_MISMATCH; repetir em corrida com a requisição original em andamento retorna 409 com Retry-After. Derive a key do seu próprio ID de tarefa:

headers = {**HEADERS, "Idempotency-Key": f"expand-pipeline-{job_id}"}

Trate falhas de autenticação explicitamente. Keys inválidas ou sem permissão falham com HTTP 401 e trazem "code": "permission_denied" no corpo do erro — capture isso separadamente dos erros de geração, porque repetir não adianta:

if r.status_code == 401:
    raise SystemExit("hiapi key invalid or lacks access to this model")

Persista a saída imediatamente. As URLs de saída das tarefas têm validade. Baixe os bytes assim que a tarefa for concluída e armazene por conta própria.

Mantenha o ampliador sob controle. Limite a expansão a cerca de 120 palavras, use temperature ≤ 0.8 e registre no log o prompt curto e o prompt expandido junto com o taskId. Quando o resultado parecer estranho, a primeira pergunta é sempre "o que o modelo realmente recebeu?" — com seu próprio ampliador, você consegue responder.

Fique de olho no acúmulo de custos. Agora você paga por duas chamadas por imagem: uma completion de um LLM pequeno (normalmente desprezível) mais a geração de imagem — o ideogram-v4 cobra por imagem conforme a faixa de rendering_speed, e o flux-2 cobra por imagem conforme a faixa de resolução. Consulte as tarifas atuais de ambos na página de preços.

Próximos passos

  • Detalhes e parâmetros dos modelos: páginas de ideogram-v4 e flux-2
  • Onde a plataforma já traz ampliador embutido, pule o ampliador DIY: qwen-image-2.0 (prompt_extend) e flux-1.1-pro (prompt_upsampling)
  • Referência da API de tarefas e mais guias de integração: documentação
  • Compare o preço por imagem de cada modelo: preços

Perguntas frequentes

Posso enviar o mesmo prompt expandido para os dois modelos? Pode, mas você abre mão de qualidade. O Ideogram prefere texto literal entre aspas e linguagem de layout; o FLUX.2 prefere detalhes de cena, iluminação e câmera. Manter um system prompt de ampliador para cada família de modelo custa apenas algumas linhas de código, mas compensa em cada geração.

Algum modelo do hiapi expande o prompt por mim? Sim — qwen-image-2.0 aceita prompt_extend e flux-1.1-pro aceita prompt_upsampling. Para ideogram-v4 e flux-2/text-to-image não há ampliador embutido, que é exatamente o cenário em que este tutorial se aplica.

Qual deve ser o tamanho do prompt expandido? Para este pipeline, 60–120 palavras é o ponto ideal. Acima disso, o sinal se dilui, e alguns modelos de baixo custo truncam prompts longos de forma agressiva (por exemplo, o flux-schnell trunca por volta de 256 tokens). Densidade vale mais que comprimento: cada frase adicionada deve restringir a imagem.

O flux-2 aceita prompt negativo? Não. Expresse o que você quer, não o que você não quer — e inclua essa regra no system prompt do ampliador, para que ele nunca produza formulações como "no text, no watermark", que o modelo leria como conteúdo.

E se o LLM alterar a intenção do usuário? Restrinja isso no system prompt ("keep the user's intent exactly"), mantenha uma temperature moderada e registre a comparação antes/depois. Para fluxos críticos, mostre o prompt expandido ao usuário antes da geração, ou ofereça um interruptor de "usar meu prompt como está" para contornar o ampliador.

Por que minha requisição falha com 400 antes de qualquer geração? Validação de schema. Cada modelo valida o input estritamente — campos obrigatórios ausentes (como rendering_speed no ideogram-v4) ou campos desconhecidos falham rapidamente, retornando error_code: "INVALID_REQUEST" e uma mensagem que indica o campo infrator. Essas falhas não são cobradas; corrija os campos e reenvie.

Artigos relacionados

Ver todos os artigos →

Comece a criar com artefactos de IA persistentes

Obtenha a sua chave de API e chame modelos de imagem, vídeo e áudio através de uma única API baseada em tasks.

Ler a documentação da API