教程
构建 AI 图像提示词增强器:面向 ideogram-v4 和 flux-2 的 LLM 提示词扩写
简短的提示词只能生成泛泛的图像。“coffee shop logo” 几乎没给模型任何可用的信息——没有要渲染的名称,没有风格,没有构图,没有配色。现代图像流水线用提示词扩写解决这个问题:在提示词进入图像 API 之前,先用一次成本低廉的 LLM 处理,把五个词的提示词变成一段丰富的、符合模型特性的描述。有些平台现在已把扩写器内置到特定模型中,这本身就说明这一预处理步骤有多普遍。
本教程将自行构建这一步,做成一个小型 Python 流水线:由 LLM 丰富用户的简短提示词,再通过 hiapi 的统一任务 API 把结果发给 ideogram-v4 或 flux-2。你可以按模型控制扩写规则——这很重要,因为让 Ideogram 提示词出彩的要素(用引号包裹的精确文字)与让 FLUX.2 提示词出彩的要素(场景、光照、镜头)并不相同。
前置条件:一个 hiapi API key(在控制台创建)、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——分别是排版和提示词遵循方面最强的两个模型——没有内置扩写器。它们的输入 schema 只接受提示词字符串和生成参数,别无其他。如果要在这些模型上获得更丰富的提示词,你需要自带扩写步骤。好处是:你自己的扩写器能感知模型、感知品牌,而且可调试。你能确切看到发送的是哪个字符串。
扩写器:每个模型家族一个系统提示词
整个技巧就是一个字符串进、字符串出的函数。下面是与服务商无关的 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
注意输入 schema——每个模型都不同
任务 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'),并且没有负面提示词——这也是扩写器应始终用正面表述的另一个原因。
生产环境加固
高并发量下优先用回调而非轮询。创建任务时加上 callback 块,hiapi 会在终态时 POST 通知你,而无需你轮询:
payload = {
"model": "flux-2/text-to-image",
"input": input_obj,
"callback": {"url": "https://your-domain.com/hiapi/callback", "when": "final"},
}
轮询更简单,适合本地脚本、cron 任务和低并发量;回调则避免在长时间生成上浪费请求,并且每个任务恰好通知一次。常见的生产模式是以回调为主通道,以慢速对账轮询为备份。
让重试幂等。创建任务时发送 Idempotency-Key 头(最多 255 字节,保留 24 小时)。用相同 key 重试会返回原始 taskId(响应头带 Idempotent-Replay: true),而不会创建——并计费——一个重复任务。用不同的请求体复用 key 会返回 422 IDEMPOTENCY_KEY_MISMATCH;重试与进行中的原始请求发生竞态则返回 409 及 Retry-After。从你自己的任务 ID 派生 key:
headers = {**HEADERS, "Idempotency-Key": f"expand-pipeline-{job_id}"}
显式处理认证失败。无效或权限不足的 key 会以 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 completion(通常可忽略不计)加上图像生成——ideogram-v4 按 rendering_speed 档位按图计费,flux-2 按分辨率档位按图计费。两者的当前费率见定价页。
接下来做什么
- 模型详情和参数:ideogram-v4 和 flux-2 模型页
- 平台已内置扩写器的地方,可跳过 DIY 扩写器: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 token 处截断)。密度胜过长度:每一个新增短语都应约束图像。
flux-2 支持负面提示词吗? 不支持。表述你想要什么,而不是你不想要什么——并把这条规则写进扩写器的系统提示词,使它永远不会输出 “no text, no watermark” 这类会被模型当作内容读取的措辞。
如果 LLM 改变了用户的意图怎么办? 在系统提示词中加以约束(“keep the user's intent exactly”),保持适中的 temperature,并记录前后对照。对于高风险流程,在生成前向用户展示扩写后的提示词,或提供一个“原样使用我的提示词”开关以绕过扩写器。
为什么我的请求在任何生成发生之前就以 400 失败?
Schema 校验。每个模型严格校验 input——缺少必填字段(如 ideogram-v4 上的 rendering_speed)或出现未知字段都会快速失败,返回 error_code: "INVALID_REQUEST" 以及指明违规字段的消息。这些失败不收费;修正字段后重新提交即可。