HiAPI
Блог

Руководство

Создание усилителя промптов для ИИ-изображений: расширение промптов через LLM для ideogram-v4 и flux-2

Jul 9, 2026
Создание усилителя промптов для ИИ-изображений: расширение промптов через LLM для ideogram-v4 и flux-2

Короткие промпты порождают лишь обобщённые изображения. «coffee shop logo» почти не даёт модели полезной информации — нет названия для отрисовки, нет стиля, нет композиции, нет цветовой палитры. Современные конвейеры генерации изображений решают эту проблему с помощью расширения промптов: до того как промпт попадёт в API изображений, дешёвый проход через LLM превращает промпт из пяти слов в насыщенное описание, учитывающее особенности модели. Некоторые платформы уже встраивают расширитель в конкретные модели — это само по себе говорит о том, насколько распространён этот этап предобработки.

В этом руководстве мы построим этот этап самостоятельно в виде небольшого конвейера на Python: LLM обогащает короткий промпт пользователя, а затем результат отправляется в ideogram-v4 или flux-2 через единый API задач hiapi. Правила расширения можно настраивать под каждую модель — это важно, потому что то, что делает промпт для Ideogram отличным (точный текст в кавычках), не совпадает с тем, что делает промпт для FLUX.2 отличным (сцена, освещение, камера).

Требования: ключ hiapi API (создаётся в консоли), Python 3.10+ и requests (pip install requests), а также совместимая с chat-completions конечная точка LLM для шага расширения (достаточно небольшой/дешёвой модели — расширитель пишет всего около 100 слов за раз).

Зачем расширять промпты?

Две модели на hiapi имеют встроенный переключатель расширения: qwen-image-2.0 имеет prompt_extend, а flux-1.1-pro — prompt_upsampling. При включении платформа обогащает промпт на стороне сервера.

Но у ideogram-v4 и flux-2/text-to-image — двух сильнейших моделей в области типографики и следования промптам соответственно — нет встроенного расширителя. Их входная схема принимает только строку промпта и параметры генерации, и ничего больше. Если вы хотите получать более насыщенные промпты на этих моделях, вам нужно иметь собственный шаг расширения. Преимущество в том, что ваш собственный расширитель учитывает модель и бренд, а также поддаётся отладке. Вы точно видите, какая строка отправляется.

Расширитель: отдельный системный промпт для каждого семейства моделей

Вся хитрость сводится к функции, принимающей строку и возвращающей строку. Ниже — вызов LLM, не зависящий от поставщика: направьте LLM_BASE_URL / LLM_API_KEY / LLM_MODEL на любую уже работающую у вас конечную точку, совместимую с chat-completions:

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

Поскольку конвейеру нужен только str -> str, вы можете заменить его на любой расширитель — более крупную LLM, дообученную модель или даже детерминированный шаблон для фиксированной брендовой кампании.

До и после

Вход: coffee shop logo

После расширения для ideogram-v4:

Минималистичный круглый значок-логотип для кофейни под названием "Driftwood Coffee", надпись "DRIFTWOOD COFFEE" чистыми заглавными буквами с засечками окружает простую линейную чашку кофе с тремя линиями пара. Тёплый кремовый фон, текст и линии тёмно-эспрессо-коричневого цвета, плоский векторный стиль, высокий контраст, центрированная композиция, достаточно свободного пространства по краям.

После расширения для flux-2:

Интерьер уютной кофейни в золотой час, тёплый солнечный свет льётся через широкое переднее окно на стойку из орехового дерева, бариста создаёт латте-арт в керамической чашке, позади на полках стоят янтарные стеклянные банки и мешки с кофейными зёрнами из мешковины, мягко поднимается пар, малая глубина резкости, фотографическая текстура 35mm, тёплая и приятная цветовая палитра, чёткий фокус на чашке.

Один и тот же замысел из пяти символов, два совершенно разных — и каждый точно попадающий в цель — промпта. Версия для Ideogram выделяет кавычками точный текст для отрисовки (её сильная сторона); версия для FLUX.2 тратит усилия на сцену и свет (его сильная сторона).

Полный конвейер: расширение, создание задачи, опрос, загрузка

Все модели генерации hiapi вызываются через одну асинхронную конечную точку: POST /v1/tasks создаёт задачу, GET /v1/tasks/<id> опрашивает задачу, готовые артефакты появляются в 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"))

Запустите его:

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

Обратите внимание на входную схему — у каждой модели она разная

API задач строго проверяет input в соответствии с моделью: неизвестные поля отклоняются, а не игнорируются. Если вы отправите в ideogram-v4 поле, которое она не знает:

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

В этом конвейере стоит обратить внимание на три различия:

  • rendering_speed обязателен для ideogram-v4 (TURBO/BALANCED/QUALITY — цена зависит от уровня). У flux-2 такого поля нет.
  • Наборы соотношений сторон не совпадают. ideogram-v4 предлагает пять предустановок примерно на 1MP (1:1, 16:9, 4:3, 9:16, 3:4); flux-2 предлагает более широкий набор, а также custom с явными width/height (256–2048, кратные 16). Если ваше приложение позволяет пользователю выбирать соотношение сторон, сопоставляйте его с моделью перед отправкой задачи.
  • flux-2 задаёт размер в мегапиксельных уровнях (resolution: от '0.5 MP' до '4 MP') и не имеет негативного промпта — ещё одна причина, по которой расширитель должен всегда использовать положительные формулировки.

Укрепление для production

При высокой нагрузке предпочитайте колбэки вместо опроса. Добавьте блок callback при создании задачи, и hiapi отправит вам POST-уведомление при достижении конечного состояния, избавив вас от необходимости опрашивать:

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

Опрос проще и подходит для локальных скриптов, cron-задач и низкой нагрузки; колбэки же избавляют от лишних запросов при длительной генерации и уведомляют ровно один раз на задачу. Распространённый production-паттерн — использовать колбэк как основной канал, а медленный сверочный опрос — как резервный.

Сделайте повторные попытки идемпотентными. Отправляйте заголовок Idempotency-Key при создании задачи (до 255 байт, хранится 24 часа). Повторная попытка с тем же ключом вернёт исходный taskId (в заголовке ответа будет Idempotent-Replay: true), а не создаст — и не выставит счёт за — дублирующую задачу. Использование ключа с другим телом запроса вернёт 422 IDEMPOTENCY_KEY_MISMATCH; повторная попытка, конкурирующая с выполняющимся исходным запросом, вернёт 409 и Retry-After. Выводите ключ из собственного идентификатора задачи:

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

Явно обрабатывайте сбои аутентификации. Недействительный ключ или ключ с недостаточными правами приводит к HTTP 401 с "code": "permission_denied" в теле ошибки — перехватывайте это отдельно от ошибок генерации, так как повторные попытки не помогут:

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

Сразу сохраняйте результаты. URL-адреса результатов задачи имеют срок действия. Как только задача выполнится успешно, скачайте байты и сохраните их самостоятельно.

Держите расширитель в узде. Ограничьте расширение примерно 120 словами, передавайте temperature ≤ 0.8 и логируйте короткий промпт и расширенный промпт вместе с taskId. Когда результат генерации выглядит неправильно, первый вопрос всегда — «что модель на самом деле получила?» — а с собственным расширителем вы можете на него ответить.

Помните о суммировании затрат. Теперь вы платите за два вызова на каждое изображение: небольшое завершение LLM (обычно пренебрежимо малое) плюс генерация изображения — ideogram-v4 тарифицируется за изображение по уровню rendering_speed, flux-2 — за изображение по уровню разрешения. Актуальные тарифы обоих см. на странице цен.

Что делать дальше

  • Подробности о моделях и параметрах: страницы моделей ideogram-v4 и flux-2
  • Где платформа уже имеет встроенные расширители, можно обойтись без самодельного расширителя: qwen-image-2.0 (prompt_extend) и flux-1.1-pro (prompt_upsampling)
  • Справочник по API задач и другие руководства по интеграции: документация
  • Сравнение цен за изображение по моделям: цены

Часто задаваемые вопросы

Можно ли отправлять один и тот же расширенный промпт обеим моделям? Да, но за счёт качества. Ideogram предпочитает буквальный текст в кавычках и язык макета; FLUX.2 предпочитает детали сцены, освещения и камеры. Хранить отдельный системный промпт расширителя для каждого семейства моделей стоит всего несколько строк кода, но окупается при каждой генерации.

Есть ли на hiapi модель, которая расширит промпт за меня? Да — qwen-image-2.0 принимает prompt_extend, а flux-1.1-pro принимает prompt_upsampling. У ideogram-v4 и flux-2/text-to-image нет встроенного расширителя, и именно для этого случая подходит данное руководство.

Какой длины должен быть расширенный промпт? Для этого конвейера 60–120 слов — оптимальный диапазон. За его пределами сигнал размывается, а некоторые недорогие модели жёстко обрезают длинные промпты (например, flux-schnell обрезает примерно на 256 токенах). Плотность важнее длины: каждая добавленная фраза должна ограничивать изображение.

Поддерживает ли flux-2 негативные промпты? Нет. Формулируйте, что вы хотите, а не чего вы не хотите — и запишите это правило в системный промпт расширителя, чтобы он никогда не выводил формулировки вроде «no text, no watermark», которые модель прочитает как содержимое.

Что если LLM изменит замысел пользователя? Ограничьте это в системном промпте («keep the user's intent exactly»), поддерживайте умеренную temperature и логируйте сравнение до/после. Для критичных сценариев показывайте расширенный промпт пользователю перед генерацией или предусмотрите переключатель «использовать мой промпт как есть» для обхода расширителя.

Почему мой запрос завершается с ошибкой 400 ещё до начала какой-либо генерации? Проверка схемы. Каждая модель строго проверяет input — отсутствие обязательного поля (например, rendering_speed у ideogram-v4) или наличие неизвестного поля приводит к быстрому отказу с error_code: "INVALID_REQUEST" и сообщением, указывающим нарушающее поле. За такие ошибки плата не взимается; исправьте поле и отправьте снова.

Похожие статьи

Все статьи →

Начните работать с постоянными артефактами ИИ

Получите API-ключ и вызывайте модели изображений, видео и аудио через один API на основе tasks.

Читать документацию API