Руководство
Создание усилителя промптов для ИИ-изображений: расширение промптов через 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" и сообщением, указывающим нарушающее поле. За такие ошибки плата не взимается; исправьте поле и отправьте снова.
Похожие статьи
Все статьи →-
Руководство
Как вызвать hailuo-2.3-fast/image-to-video через HiAPI API: практика с curl и Python
Первый рабочий запрос к быстрому и недорогому уровню генерации видео по изображению — от ID модели до загрузки MP4
-
Руководство
Как вызывать hailuo-2.3/text-to-video через API HiAPI: curl, Python и рабочие примеры запросов
-
Руководство
Использование Kandinsky 6.0 Pro для преобразования текста в видео на HiAPI: руководство по curl и Python
Практическая демонстрация с упором на запросы: построение вызова от промпта до видеоклипа, отправка запроса, опрос статуса завершения и скачивание результата