
Design Image Studio
- 27 installs
- 97 repo stars
- Updated April 20, 2026
- kangarooking/design-image-studio
Transform vague visual requirements into high-quality professional designs by applying design thinking before image generation.
About
AI agent applies structured design reasoning to image generation, producing purpose-driven designs instead of generic outputs. Multi-stage architecture ensures quality at each step.
- Design-first approach prioritizes composition and purpose over generic generation
- Four-layer architecture: identification, reasoning, brief, execution
Design Image Studio by the numbers
- 27 all-time installs (skills.sh)
- Ranked #983 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/kangarooking/design-image-studio --skill design-image-studioAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 27 |
|---|---|
| repo stars | ★ 97 |
| Last updated | April 20, 2026 |
| Repository | kangarooking/design-image-studio ↗ |
What it does
Transform vague visual requirements into high-quality professional designs by applying design thinking before image generation.
Files
Design Image Studio
Generate design-quality images directly. This skill preserves the full Claude design-system prompt as the upstream design brain, then compiles that design logic into a shorter image-model prompt.
Primary Source of Truth
Do not treat references/design-principles.md as the whole design system. It is only an index.
The primary design source is:
references/claude-design-sys-prompt-full.txt
Always read that file first for substantive design work. Then use:
references/claude-design-map.mdreferences/design-compiler.md- the task-specific reference file for the current request
This skill should preserve as much of the original design-system prompt as possible at the reasoning layer, while stripping away HTML/tool-specific noise before handing a prompt to the image model.
When to Use
Use this skill when the user wants any of the following:
- Poster generation
- Product hero images, ad visuals, or e-commerce scenes
- PPT cover art, chapter art, or slide illustrations
- Infographic-style visuals
- Teaching/demo diagrams or explanatory scenes
- Visual concept exploration with stronger art direction than a generic image prompt
Do not use this skill for pixel-accurate UI recreation, editable charts, or layouts that require precise text rendering. For those, generate HTML/SVG/PPT assets instead.
Default Workflow
1. Classify the request into one of: poster, product, ppt, infographic, teaching, or auto 2. Read the full design system prompt:
references/claude-design-sys-prompt-full.txt
3. Read the compiler references:
references/claude-design-map.mdreferences/design-compiler.mdreferences/model-routing.md
4. Read the matching task file, such as references/poster.md 5. If the user wants refinement or the first result is weak, also read:
references/anti-slop-and-failure-patterns.md
6. Compile the full design system into a design_reasoning layer:
- purpose
- audience
- channel
- context/brand strategy
- visual system
- hierarchy strategy
- safe-zone or text-zone logic
- anti-filler rules
- anti-slop rules
- task-specific design constraints
7. Condense the reasoning into a compiled_brief 8. Translate the compiled brief into the shortest useful image prompt 9. Run scripts/design_image.py to generate directly, unless the user explicitly asks for prompt-only output 10. Iterate by changing one major variable at a time: direction, hierarchy, palette, lighting, realism, or density
What Must Be Preserved From the Full Prompt
These must survive the compilation process:
- Start from purpose, audience, and channel
- Create a coherent visual system up front
- Treat hierarchy and whitespace as design decisions
- Avoid filler content and decorative noise
- Avoid AI-slop tropes
- Respect brand/context when available
- If no context exists, still commit to a strong direction instead of averaging styles
- Prefer multiple directions for ambiguous work, usually
conservative,balanced, andbold
Primary Command
Use the wrapper script first. It is the opinionated entry point for this skill.
python3 scripts/design_image.py \
--task poster \
--brief "为 AI 训练营生成一张高冲击力招生海报,强调增长、实战和速度" \
--direction balanced \
--aspect 3:4 \
--quality final \
--output training-poster.pngPrompt-Only Mode
If the user only wants prompts, do:
python3 scripts/design_image.py \
--task product \
--brief "高端陶瓷咖啡杯广告图,适合电商首图" \
--prompt-onlyThe wrapper prints:
design_reasoningcompiled_brief- final
prompt
Use those intermediate layers to judge whether the full design-system prompt has actually been preserved.
Task References
references/poster.md— posters, key visuals, coversreferences/product-image.md— product ads, hero shots, e-commerce visualsreferences/ppt-visual.md— slide cover art, chapter visuals, concept illustrationsreferences/infographic.md— infographic-like visuals and structured information compositionsreferences/teaching-demo.md— educational and explanatory diagrams/scenesreferences/claude-design-map.md— which sections of the full design prompt matter for image generationreferences/design-compiler.md— how to compile the full prompt into design reasoning, a compiled brief, and a final image prompt
Execution Notes
- Prefer
Seedream 5.0 liteas the default final model - Use lower-cost draft settings before premium reruns when the direction is still unclear
- Use image-to-image or multi-image fusion when the user provides source materials
- For infographic or teaching visuals, avoid asking the model to render dense, tiny text accurately; prefer text placeholders or low-text compositions
- The wrapper script is not the design brain; it is the compiler between the full design system and the image model
- Do not collapse the full design prompt into a few style adjectives unless the user explicitly wants a minimal prompt
Files
scripts/design_image.py— design compiler and prompt builderscripts/generate.py— bundled Volcengine generation enginereferences/claude-design-sys-prompt-full.txt— full upstream design-system promptreferences/claude-design-map.md— section map for image usereferences/design-compiler.md— compilation workflowreferences/models.md— model and resolution referencereferences/troubleshooting.md— common error handling
__pycache__/
*.pyc
.DS_Store
MIT License
Copyright (c) 2026 kangarooking
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Design Image Studio
把一个模糊的视觉需求,直接变成可落地生成的高质量设计图。
design-image-studio 是一个面向创意与设计场景的 Agent Skill。它不是简单地把一句话塞给文生图模型,而是先读取并保留完整的 Claude 设计系统提示词,再把需求整理成设计推理和结构化 brief,最后把这些设计判断压缩成更强的生图 Prompt,并直接调用 Volcengine Seedream 生成图片。
它适合做的不是泛泛的“AI 生图”,而是更接近设计工作流的任务:海报、商品图、PPT 配图、信息图风格视觉、教学演示图。
为什么做这件事
现在很多 AI 生图的问题,不是模型不够强,而是输入太弱。
用户给模型的往往只有一句模糊需求,比如“帮我做一张海报”“来一张高级感商品图”。这种输入对生成模型来说信息远远不够,所以结果就很容易滑向几个常见问题:
- 画面有细节,但没有主次
- 风格很满,但没有设计目的
- 看起来像 AI 图,却不像真正可用的商业视觉
- 可以“好看”,但不适合投放、演示、教学、招商这些真实场景
design-image-studio 的目标,就是把“设计判断”补回到生图链路里:先明确用途、受众、构图、气质、留白、安全区和禁忌项,再去调模型。
核心设计
这个 skill 现在不是简单的 prompt 模板器,而是一个“设计编译器”:
1. 保留完整的 Claude-Design-Sys-Prompt 作为上游设计脑 2. 先生成 design_reasoning 3. 再压缩成 compiled_brief 4. 最后才生成给图片模型吃的短 Prompt
也就是说,真正做设计判断的是完整的设计系统提示词,而不是几条零散的风格关键词。
它解决了什么问题
- 普通生图 Prompt 太泛,结果不稳定
- 海报和 PPT 配图经常没有留白,后续没法放字
- 商品图容易有廉价感,材质、灯光、构图不成立
- 信息图和教学图常常又乱又密,缺乏结构
- 用户想要的是“像设计师一样思考后的图”,不是“模型随便画出来的图”
它是怎么工作的
design-image-studio 把生图拆成四层:
1. 任务识别层 先判断这是海报、商品图、PPT 配图、信息图还是教学演示图,不同场景使用不同的默认构图和约束。
2. 设计推理层 基于完整的 Claude 设计系统提示词,整理出设计推理:目标、受众、使用场景、视觉系统、主次层级、留白策略、anti-filler 规则和 anti-slop 规则。
3. 编译简报层 把设计推理压缩成结构化 compiled_brief,只保留对生图真正有用的部分。
4. 生成执行层 基于 compiled_brief 组装最终 Prompt,选择合适的 Volcengine Seedream 模型、分辨率和比例,并直接生成图片。
整个流程由两个脚本组成:
scripts/design_image.py:设计编译层scripts/generate.py:Volcengine Seedream 生成执行层
支持的场景
- 海报生成
- 商品图生成
- PPT 配图生成
- 信息图风格视觉生成
- 教学演示图生成
Demo
下面这张图是用本仓库生成的示例海报,需求是:
为 AI 训练营生成一张高冲击力招生海报,强调速度、增长、实战

Case Gallery
下面这些案例图都来自同一套 design-image-studio 工作流,重点不是“单张图好不好看”,而是这个 skill 能否稳定覆盖不同设计任务。
海报方向稿
同一份 brief 先出多个方向,适合做活动海报、课程海报、社群传播图的第一轮探索。

商品图素材组
同一商品一次性生成多场景电商图,适合主图、办公场景、通勤场景、细节图和社交媒体展示图。

PPT 配图方向稿
适合企业内部分享、培训课件和汇报材料。重点不是做海报感,而是帮助观众更快理解观点。

信息图结构图
适合流程说明、培训材料和结构化表达。重点是把信息重新组织成更容易被看懂的层级。

教学演示图
适合知识讲解、内训和 AI 工作流说明。下面这张图展示的是 RAG(AI知识库)工作流程 的教学型表达。

最简单的使用方式
直接对你的 Agent 说:
帮我安装这个skill:https://github.com/kangarooking/design-image-studio安装完成后,再直接说你的需求即可,例如:
用 design-image-studio 帮我生成一张 AI 训练营招生海报,强调速度、增长、实战用 design-image-studio 帮我生成一张高端陶瓷咖啡杯商品图,适合电商首图使用前提
这个 skill 依赖火山引擎 ARK 的 API Key。
- 你需要先申请并配置
ARK_API_KEY - Key 获取入口见:火山引擎 ARK API Key 页面
- 调用会产生费用,本质上是在调用火山引擎的图像生成模型能力
本仓库当前主要支持并实际调用的是字节的豆包生图模型:
doubao-seedream-4-5-251128doubao-seedream-5-0-260128doubao-seedream-5-0-lite-260128
其中常用关系可以简单理解为:
Seedream 4.5:更偏高质量成品Seedream 5.0:更强的高阶生成版本,可显式指定Seedream 5.0 lite:默认的高性价比版本
也就是说,这个 skill 虽然对外表现成一个“设计编译 + 生图”工作流,但底层生成模型其实就是豆包生图对应的 Seedream 系列。
本地运行(进阶)
1. 安装依赖
pip install "volcengine-python-sdk[ark]"2. 设置 API Key
export ARK_API_KEY="your_volcengine_ark_api_key"如果没有 Key,可以从 火山引擎 ARK API Key 页面 获取。
注意:
- 调用会消耗额度并产生费用
- 不同模型单次生成成本不同
Seedream 4.5 / 5.0 / 5.0 lite可以通过参数显式指定
3. 直接生成一张海报
python3 scripts/design_image.py \
--task poster \
--brief "为 AI 训练营生成一张高冲击力招生海报,强调速度、增长、实战" \
--direction balanced \
--aspect 3:4 \
--quality final \
--output ai-training-camp-poster.png4. 只输出 Prompt,不直接生成
python3 scripts/design_image.py \
--task product \
--brief "高端陶瓷咖啡杯电商首图,温暖晨光,突出釉面质感" \
--prompt-only脚本会输出三层中间结果:
design_reasoningcompiled_briefprompt
示例命令
海报
python3 scripts/design_image.py \
--task poster \
--brief "AI 训练营招生海报,强调速度、增长、实战,面向想要快速上手 AI 的职场人" \
--aspect 3:4 \
--quality final \
--output poster.png显式指定模型
python3 scripts/design_image.py \
--task poster \
--brief "职场 AI 提效训练营宣传海报,专业、高级、活力感,面向职场人和管理者" \
--direction balanced \
--model-override doubao-seedream-5-0-260128 \
--aspect 3:4 \
--output poster-seedream5.png商品图
python3 scripts/design_image.py \
--task product \
--brief "高端陶瓷咖啡杯电商首图,温暖晨光,突出釉面质感" \
--quality final \
--output cup.pngPPT 配图
python3 scripts/design_image.py \
--task ppt \
--brief "AI 工作流主题演讲封面图,留出标题区,整体偏未来感但不过度花哨" \
--aspect 16:9 \
--quality final \
--output ppt-cover.png信息图风格视觉
python3 scripts/design_image.py \
--task infographic \
--brief "展示从数据采集到分析决策的完整流程,要求结构清楚、模块分明、低文本密度" \
--aspect 4:3 \
--quality final \
--output infographic.png教学演示图
python3 scripts/design_image.py \
--task teaching \
--brief "解释 RAG 工作流程的教学图,分步骤展示,适合培训课件" \
--aspect 16:9 \
--quality final \
--output teaching-demo.png仓库结构
design-image-studio/
├── README.md
├── LICENSE
├── SKILL.md
├── assets/
│ └── demo-poster.png
├── references/
│ ├── claude-design-sys-prompt-full.txt
│ ├── claude-design-map.md
│ ├── design-compiler.md
│ ├── design-principles.md
│ ├── prompt-framework.md
│ ├── model-routing.md
│ ├── poster.md
│ ├── product-image.md
│ ├── ppt-visual.md
│ ├── infographic.md
│ ├── teaching-demo.md
│ ├── anti-slop-and-failure-patterns.md
│ ├── models.md
│ └── troubleshooting.md
└── scripts/
├── design_image.py
└── generate.py设计方法来源
这个 skill 的上层设计判断主要来自两部分:
- 完整保留并使用 Claude 设计系统提示词,而不是只保留摘要
- 对 Volcengine Seedream 能力的封装:保留其模型路由、成本控制、图生图、多图融合和错误处理能力
因此它本质上是“完整设计系统 + 生图执行”的组合,而不是单纯的 API 包装器。
已知边界
- 信息图和教学图更适合生成“视觉底图”或“结构图感”,不适合直接要求模型输出大量精确小字
- 如果任务目标是可编辑图表、精确排版、像素级 UI,还应该走 HTML / SVG / PPT 这类可编辑产物
- 这套工作流更擅长先出高质量视觉方向,再做后续设计加工
关于作者
袋鼠帝 kangarooking — AI 博主,独立开发者。AI Top 公众号「袋鼠帝 AI 客栈」主理人
火山引擎领航 KOL,百度千帆开发者大使,GLM 布道师,Trae 昆明第一任 Fellow
| 平台 | 链接 |
|---|---|
| 𝕏 Twitter(袋鼠帝) | https://x.com/aikangarooking |
| 小红书(袋鼠帝) | https://xhslink.com/m/5YejKvIDBbL |
| 抖音(袋鼠帝) | https://v.douyin.com/hYpsjphuuKc |
| 公众号 | 袋鼠帝 AI 客栈 |
| 视频号 | AI 袋鼠帝 |
License
MIT. See LICENSE.
Anti-Slop and Failure Patterns
Use this file when the first result feels generic, messy, or off-brief.
Ban These Defaults
- Random gradient fog with no composition logic
- Floating geometric debris
- Meaningless HUD overlays
- Excess lens flares
- Crowded detail everywhere
- Mixed visual styles in one image
Recovery Moves
- Re-state the hero focal point in one sentence
- Remove one full layer of background complexity
- Force a text-safe or content-safe empty zone
- Narrow the palette to 2 or 3 dominant colors
- Replace vague style words with art-direction words such as editorial, commercial, cinematic, tactile, diagrammatic
If the Result Is Too Generic
- Add a stronger compositional instruction
- Add one specific texture or material cue
- Add a clearer mood target
- Reference the real usage context, such as keynote cover, recruitment poster, luxury product ad, teaching slide
If the Result Is Too Busy
- Reduce prop count
- Reduce background contrast
- Increase scale of the hero subject
- Ask for cleaner negative space
Claude Design Map
This file explains how design-image-studio should use the full Claude design-system prompt for image generation.
Primary Rule
Do not replace the full prompt with this file. This file is only a routing map.
Primary source:
claude-design-sys-prompt-full.txt
Preserve These Sections Aggressively
These sections contain the design judgment that should survive into image-generation reasoning:
## Your workflow## Output creation guidelines### How to do design work## Content Guidelines**Do not add filler content.****Ask before adding material.****Create a system up front:****Use appropriate scales:****Avoid AI slop tropes:**
Use These Ideas at the Reasoning Layer
When compiling for image generation, carry forward these ideas explicitly:
1. Start from purpose, audience, and output context 2. Build a coherent visual system before detailing the image 3. Prefer hierarchy, rhythm, and negative space over decorative clutter 4. Every element should earn its place 5. If context or brand exists, use it 6. If context does not exist, still choose a strong direction 7. Explore multiple directions when the ask is ambiguous 8. Surprise the user with strong visual design, but keep the result usable
Task Mapping
Poster
Most relevant source ideas:
- one dominant visual idea
- typography-safe or CTA-safe space
- high contrast and emotional direction
- campaign rather than generic web-art energy
Product
Most relevant source ideas:
- visual system and brand consistency
- material and lighting discipline
- avoid decorative clutter
- hero-object clarity
PPT
Most relevant source ideas:
- slide-safe composition
- readability from distance
- broad shapes over small details
- room for titles and supporting copy
Infographic
Most relevant source ideas:
- structure over spectacle
- no data slop
- no filler labels
- grouped modules and clear hierarchy
Teaching
Most relevant source ideas:
- clarity over cinematic excess
- visible sequence and logic
- limited background distraction
- strong explanatory hierarchy
Down-Weight or Ignore for Image Prompt Compilation
Keep these in the repository, but do not push them directly into the image prompt:
- React/Babel details
- HTML file management
done, verifier, file tools, GitHub tooling- tweaks protocol
- deck speaker notes plumbing
- cross-project file path rules
These are implementation details for artifact creation, not useful content for a text-to-image model.
Design Compiler
design-image-studio should behave like a compiler:
1. Read the full design system 2. Select the relevant design rules 3. Produce design reasoning 4. Condense that reasoning into a compiled brief 5. Translate the compiled brief into a short image-model prompt
Do not skip directly from user brief to image prompt unless the user explicitly asks for a very short prompt.
Stage 1: Read the Full Prompt
Always begin with:
claude-design-sys-prompt-full.txt
Then use:
claude-design-map.md- task-specific references
anti-slop-and-failure-patterns.mdif refinement is needed
Stage 2: Design Reasoning
Produce a design_reasoning object with fields like:
taskcommunication_goalaudiencechannelbrand_strategyvisual_systemhierarchy_strategysafe_zone_strategydetail_densitylighting_strategypalette_strategyanti_filler_rulesanti_slop_rulesdirectionsource_sections
This is where the full Claude design prompt should do most of its work.
Stage 3: Compiled Brief
Compress the reasoning into a shorter compiled_brief:
- what the image is for
- what the viewer should notice first
- what kind of layout or safe zone must remain open
- what the visual system should feel like
- what must not appear
The compiled brief should still read like design thinking, not like raw model syntax.
Stage 4: Final Image Prompt
Translate the compiled brief into a short prompt with:
- one clear hero idea
- scene or environment
- composition and framing
- lighting
- color system
- material or rendering style
- negative constraints
The final prompt should be much shorter than the design reasoning.
Direction Modes
When the ask is ambiguous, explore with one of these modes:
conservative— restrained, cleaner, safer, more corporatebalanced— premium and energetic without overreachingbold— larger scale, stronger contrast, more experimental, but still disciplined
Success Condition
The full Claude design-system prompt should still be visible in the intermediate reasoning layer, even if it is compressed before the actual generation call.
Design Principles
This file is an index, not the full source of truth.
The full upstream design system lives in:
claude-design-sys-prompt-full.txt
Use that file first. Then use this file as a quick reminder of the principles that must survive the compilation into image prompts.
Core Stance
- Think like a design director, not a generic prompt writer
- Start from purpose, audience, and channel
- Create a visual system before chasing detail
- Preserve hierarchy, rhythm, and whitespace
Must-Carry Rules
- Do not add filler content
- Avoid AI slop tropes
- If context exists, use it
- If context does not exist, still choose a coherent direction
- Prefer one strong hero idea over many equal-weight elements
- Treat text-safe or layout-safe zones as first-class design constraints
Next Files to Read
claude-design-map.mddesign-compiler.md- the task-specific reference file
Infographic Generation
Use Cases
- Process overview visual
- Structured summary graphic
- Data-story style illustration
- Relationship or system overview
Important Limitation
Image models are weak at dense, precise text and exact charts. Use them to generate infographic-like visual structures, not final editable information graphics.
What Matters Most
- Grouping and hierarchy
- Flow and structure
- Iconic or symbolic clarity
- Low text density
Default Composition Moves
- Modular blocks
- Directional flow cues
- Numbered stages or grouped panels
- Large labels only, if any
Common Failures
- Tiny unreadable labels
- Fake data clutter
- Too many decorative icons
- Dense chart-like visuals that cannot survive close inspection
Model Routing
Use these defaults when calling the bundled Volcengine generator.
Default Model Choice
- Draft exploration:
doubao-seedream-4-0-250828 - Default final output:
doubao-seedream-5-0-lite-260128 - Premium rerun when realism/material is critical:
doubao-seedream-4-5-251128
Resolution Strategy
- Posters and product visuals: 3K when final quality matters
- PPT visuals: 2K is usually enough unless the image will be full-bleed on large screens
- Infographics and teaching visuals: 2K by default; increase only if the composition is detail-heavy
Aspect Recommendations
- Poster:
3:4,2:3,9:16, sometimes1:1 - Product:
1:1,4:5,16:9 - PPT:
16:9 - Infographic:
4:3,3:4,16:9 - Teaching:
16:9,4:3
Execution Rules
- If the user supplies reference images, pass them through
--image - If the design still feels unresolved, run a draft first
- Prefer changing one major variable per rerun
- Use
--dry-runif the user is cost-sensitive or asking for many variants
Model Comparison & Pricing Guide
Available Models
| Attribute | Seedream 5.0 | Seedream 5.0 lite | Seedream 4.5 | Seedream 4.0 |
|---|---|---|---|---|
| Model ID | doubao-seedream-5-0-260128 | doubao-seedream-5-0-lite-260128 | doubao-seedream-4-5-251128 | doubao-seedream-4-0-250828 |
| Tier | High | Mid | High | Low |
| Max Resolution | 3K | 3K | 4K | 4K |
| Output Formats | PNG, JPEG | PNG, JPEG | JPEG | JPEG |
| Web Search | Yes | Yes | No | No |
| Prompt Optimization | Standard | Standard | Standard | Standard + Fast |
| Rate Limit (IPM) | 500 | 500 | 500 | 500 |
Resolution Presets
Seedream 5.0 lite — Recommended Pixel Values
| Aspect Ratio | 2K | 3K |
|---|---|---|
| 1:1 | 2048×2048 | 3072×3072 |
| 3:4 | 1728×2304 | 2592×3456 |
| 4:3 | 2304×1728 | 3456×2592 |
| 16:9 | 2848×1600 | 4096×2304 |
| 9:16 | 1600×2848 | 2304×4096 |
| 3:2 | 2496×1664 | 3744×2496 |
| 2:3 | 1664×2496 | 2496×3744 |
| 21:9 | 3136×1344 | 4704×2016 |
Pixel Range Per Model
| Model | Min Pixels | Max Pixels |
|---|---|---|
| Seedream 5.0 lite | 3,686,400 (2560×1440) | 10,404,496 (3072×3072×1.1) |
| Seedream 4.5 | 3,686,400 (2560×1440) | 16,777,216 (4096×4096) |
| Seedream 4.0 | 921,600 (1280×720) | 16,777,216 (4096×4096) |
Cost Estimation (Approximate, CNY per Image)
Note: Actual pricing may vary. These are estimated costs for budgeting purposes.
Check the Volcengine console for exact pricing.
| Resolution | Seedream 5.0 | Seedream 5.0 lite | Seedream 4.5 | Seedream 4.0 |
|---|---|---|---|---|
| 1K | ¥0.04 | ¥0.02 | ¥0.04 | ¥0.02 |
| 2K | ¥0.06 | ¥0.04 | ¥0.06 | ¥0.03 |
| 3K | ¥0.10 | ¥0.06 | ¥0.08 | ¥0.05 |
| 4K | ¥0.14 | ¥0.08 | ¥0.12 | ¥0.08 |
Cost Optimization Strategies
1. Draft → Final Workflow
Use Seedream 4.0 at 1K for rapid prototyping (¥0.02/image), then generate the final version with 5.0 lite or 4.5 at higher resolution. This costs ~¥0.06 for two rounds instead of ¥0.10+ for multiple high-res attempts.
2. Batch Budget Planning
For N sequential images at resolution R with model M:
Total cost = N × cost_per_image(M, R)Example: 4-frame storyboard with 5.0 lite at 2K = 4 × ¥0.04 = ¥0.16
3. Model Selection Decision Tree
Need web context? → 5.0 lite (only model with web search)
↓ No
Need PNG output? → 5.0 or 5.0 lite
↓ No (JPEG ok)
Need max 4K resolution? → 4.5 or 4.0
↓ No
Lowest cost? → 4.0
↓ No
Best value? → 5.0 lite (recommended default)4. Fallback Chain
When cost is critical, configure a fallback chain:
Primary: 5.0 lite (¥0.04 @ 2K)
↓ on failure
Fallback: 4.0 (¥0.03 @ 2K)Input Image Constraints
| Constraint | Limit |
|---|---|
| Formats | JPEG, PNG, WebP, BMP, TIFF, GIF |
| Input method | Public URL or Base64 data URI |
| Aspect ratio range | 1:16 to 16:1 |
| Min dimension | 14px per side |
| Max file size | 10 MB per image |
| Max total pixels | 6000×6000 = 36M pixels |
| Max reference images | 14 per request |
| Total (input + output) | ≤ 15 images |
Rate Limits
| Limit | Value |
|---|---|
| IPM (images/minute) | 500 (all models) |
| Data retention | 24 hours (save promptly) |
Poster Generation
Use Cases
- Event posters
- Course recruitment posters
- Campaign key visuals
- Cover images
What Matters Most
- One dominant visual idea
- Clear focal hierarchy
- A deliberate title-safe zone
- Strong contrast and emotional direction
Default Composition Moves
- Large hero subject with clean upper or side negative space
- Strong foreground/background separation
- Controlled background texture rather than many small objects
Good Prompt Ingredients
- audience and channel
- emotional target
- headline-safe region
- composition shape
- palette
- surface texture or atmosphere
Common Failures
- Busy image with no focal point
- No space for typography
- Generic tech gradients and random floating elements
- Too many icons or meaningless visual metaphors
PPT Visual Generation
Use Cases
- Deck cover art
- Chapter separator image
- Concept illustration
- Executive summary visual
What Matters Most
- Readability from distance
- Simple, memorable metaphor
- Slide-safe composition with room for title and supporting text
- Not too detailed for presentation contexts
Default Composition Moves
- Mid-complexity visual with one clear metaphor
- Broad shapes and visible contrast
- Large negative space for text overlay
Common Failures
- Too much detail for slide viewing
- Full-bleed clutter leaving no title space
- Trying to render too much literal text in-image
- Looking like an ad poster instead of a presentation visual
Product Image Generation
Use Cases
- E-commerce hero image
- Advertising key visual
- Lifestyle product scene
- Product detail close-up
What Matters Most
- Product consistency
- Material realism
- Commercial lighting
- Controlled background that supports the product
Default Composition Moves
- Hero object dominant in frame
- Lighting designed to reveal edges, finish, and shape
- Background and props limited to those that reinforce positioning
Good Prompt Ingredients
- product type
- product finish and material
- commercial mood
- camera angle
- lighting plan
- environment style
- brand positioning
Common Failures
- Product proportions drifting
- Unrealistic reflections
- Oversized props stealing focus
- Luxury claims with cheap-looking lighting
Prompt Framework
Use this structure when assembling the final image prompt.
Base Structure
1. Task and use case 2. Subject and focal point 3. Scene or environment 4. Composition and framing 5. Lighting 6. Material or rendering style 7. Color system 8. Mood and energy 9. Layout-safe zones or text-safe zones 10. Quality constraints 11. Avoid-list
Prompt Template
Create a [task type] for [usage context and audience].
Primary subject: [subject].
Visual goal: [what the image should make the audience feel or understand].
Composition: [framing, focal hierarchy, safe zones, balance, depth].
Scene/background: [environment or abstract background treatment].
Lighting: [lighting plan].
Material/style: [photorealistic / editorial / 3D / flat vector-like / mixed-media / cinematic].
Color palette: [palette].
Mood: [tone].
Important constraints: [must-have constraints].
Avoid: [failure modes and unwanted tropes].Constraint Patterns
- Text-safe: leave a clean negative-space zone in the upper third for headline placement
- Slide-safe: keep the center clear enough for overlaid presentation title and subtitle
- Product-safe: ensure the product shape, finish, logo area, and proportions remain consistent
- Diagram-safe: emphasize grouped structure and directional flow; minimize decorative background noise
Prompt Length
- Aim for 8 to 12 strong clauses
- Long enough to direct composition
- Short enough to avoid conflicting instructions
Teaching and Demo Visuals
Use Cases
- Explaining a process
- Showing before/after
- Training material diagrams
- Visual teaching aids
What Matters Most
- Logical clarity
- Sequence visibility
- Cause-and-effect readability
- Simple, teachable visual language
Default Composition Moves
- Step-by-step panel structure
- Strong arrows or directional logic
- Limited background distraction
- Big shapes, clear labels, obvious progression
Common Failures
- Overly cinematic visuals with weak instructional value
- Too many simultaneous steps
- Tiny labels
- Decorative scenes replacing explanation
Troubleshooting Guide
Error Classification
Authentication Errors (401/403)
Symptom: Unauthorized or Forbidden
Causes & Fixes:
ARK_API_KEYnot set → Export the environment variable:
export ARK_API_KEY="your-api-key-here"- Invalid/expired key → Generate a new key at:
https://console.volcengine.com/ark/region:ark+cn-beijing/apikey
- Wrong region → Ensure you're using
ark.cn-beijing.volces.com
Retry: No. Fix the key and try again.
---
Rate Limiting (429)
Symptom: Too Many Requests or Rate limit exceeded
Causes & Fixes:
- IPM limit (500 images/minute) exceeded → Wait 60 seconds
- RPM limit exceeded → Check your account's RPM quota in the console
Retry: Yes, automatic with exponential backoff. The script handles this.
---
Invalid Request (400)
Symptom: Bad Request or Invalid parameter
Common causes & fixes:
| Cause | Fix |
|---|---|
| Unsupported resolution for model | Check references/models.md for valid resolution/model combos |
| Invalid image URL | Ensure URLs are publicly accessible |
| Image too large | Reduce to < 10MB per image |
| Too many images | Max 14 reference images, total input+output ≤ 15 |
| Invalid size format | Use preset (2K) or exact pixels (2048x2048) |
| Mixed size methods | Don't mix preset and pixel formats |
| Aspect ratio out of range | Keep between 1:16 and 16:1 |
| Prompt too long | Keep under ~300 Chinese chars / ~600 English words |
Retry: No. Fix the parameter and try again.
---
Content Policy Violation
Symptom: Error mentioning "content", "safety", or "policy"
Causes: Prompt or reference image triggered safety filters.
Fix: 1. Review the prompt for sensitive content (violence, explicit material, etc.) 2. If using reference images, ensure they don't contain policy-violating content 3. Rephrase the prompt to avoid flagged terms 4. Try a more abstract or metaphorical description instead
Retry: No. Revise the prompt.
---
Server Errors (5xx)
Symptom: Internal Server Error, Service Unavailable, Gateway Timeout
Causes: Temporary Volcengine platform issues.
Retry: Yes, automatic with exponential backoff. If persistent:
- Check Volcengine status page
- Try a different model as fallback
- Wait a few minutes and retry
---
Network/Timeout Errors
Symptom: ConnectionError, Timeout, Connection reset
Causes: Network issues between client and API server.
Retry: Yes, automatic with exponential backoff.
Workarounds:
- Use
b64_jsonresponse format to avoid download timeouts - Reduce image size to speed up generation
- Check network connectivity and proxy settings
---
SDK Installation Issues
Symptom: ModuleNotFoundError: No module named 'volcenginesdkarkruntime'
Fix:
pip install 'volcengine-python-sdk[ark]'For version issues:
pip install --upgrade 'volcengine-python-sdk[ark]'---
Output Issues
| Problem | Possible Cause | Fix |
|---|---|---|
| Blurry/low quality | Resolution too low | Use 2K or higher |
| Wrong aspect ratio | Size mismatch | Specify exact pixels (e.g., 2048x2048) |
| Unwanted watermark | --watermark flag | Remove the flag (default is no watermark) |
| JPEG artifacts | JPEG format | Use --output-format png (5.0/5.0 lite only) |
| URL expired | 24-hour retention | Download images promptly |
| Color shift | JPEG compression | Use PNG format |
| Content not matching prompt | Prompt too vague | Add specific details, style, composition |
---
Retry Strategy Reference
Attempt 1: Immediate
Attempt 2: ~1s + jitter (default)
Attempt 3: ~2s + jitter (default)
Attempt 4: ~4s + jitter (if max_retries > 3)
↓ on final failure
Fallback model (if configured)
↓ retry cycle restarts on fallback
↓ on exhaustion
Report error to uservolcengine-python-sdk[ark]
#!/usr/bin/env python3
"""
Design-aware wrapper for Volcengine image generation.
This script acts as a compiler:
1. User brief -> design reasoning
2. Design reasoning -> compiled brief
3. Compiled brief -> final image prompt
4. Final image prompt -> Volcengine generation
"""
from __future__ import annotations
import argparse
import json
import shlex
import subprocess
import sys
from pathlib import Path
TASK_KEYWORDS = {
"poster": ["海报", "poster", "封面", "主视觉", "campaign", "kv"],
"product": ["商品", "产品", "电商", "product", "hero shot", "广告图", "首图"],
"ppt": ["ppt", "幻灯片", "演示", "deck", "slide", "配图", "章节页"],
"infographic": ["信息图", "infographic", "流程图", "结构图", "总结图", "overview"],
"teaching": ["教学", "演示图", "讲解", "培训", "课件", "步骤图", "demo"],
}
DEFAULT_ASPECT = {
"poster": "3:4",
"product": "1:1",
"ppt": "16:9",
"infographic": "4:3",
"teaching": "16:9",
}
QUALITY_MODEL = {
"draft": "doubao-seedream-4-0-250828",
"final": "doubao-seedream-5-0-lite-260128",
"premium": "doubao-seedream-4-5-251128",
}
ASPECT_SIZES = {
"1:1": {"2K": "2048x2048", "3K": "3072x3072"},
"3:4": {"2K": "1728x2304", "3K": "2592x3456"},
"4:3": {"2K": "2304x1728", "3K": "3456x2592"},
"16:9": {"2K": "2848x1600", "3K": "4096x2304"},
"9:16": {"2K": "1600x2848", "3K": "2304x4096"},
"3:2": {"2K": "2496x1664", "3K": "3744x2496"},
"2:3": {"2K": "1664x2496", "3K": "2496x3744"},
"4:5": {"2K": "1840x2304", "3K": "2760x3456"},
}
CORE_SOURCE_SECTIONS = [
"## Your workflow",
"## Output creation guidelines",
"### How to do design work",
"## Content Guidelines",
"**Do not add filler content.**",
"**Create a system up front:**",
"**Avoid AI slop tropes:**",
]
TASK_SOURCE_SECTIONS = {
"poster": [
"### How to do design work",
"## Content Guidelines",
"**Do not add filler content.**",
],
"product": [
"## Output creation guidelines",
"## Content Guidelines",
"**Avoid AI slop tropes:**",
],
"ppt": [
"## Content Guidelines",
"**Create a system up front:**",
"**Use appropriate scales:**",
],
"infographic": [
"## Content Guidelines",
"**Do not add filler content.**",
"**Avoid AI slop tropes:**",
],
"teaching": [
"## Content Guidelines",
"**Do not add filler content.**",
"**Use appropriate scales:**",
],
}
DIRECTION_PROFILES = {
"conservative": {
"style_bias": "restrained, cleaner, corporate, polished, lower-risk",
"energy_bias": "controlled, composed, authoritative",
"composition_bias": "cleaner geometry, more negative space, lower background complexity",
"palette_bias": "restricted palette with restrained accents",
"detail_bias": "cleaner surfaces, less decorative detail",
},
"balanced": {
"style_bias": "premium editorial, contemporary, polished, commercially strong",
"energy_bias": "energetic but disciplined, confident, professional",
"composition_bias": "clear hierarchy, dynamic but stable layout, deliberate focal contrast",
"palette_bias": "premium neutrals with one strong accent family",
"detail_bias": "hero-detail emphasis with a restrained background",
},
"bold": {
"style_bias": "larger scale, more dramatic, more surprising, high-contrast",
"energy_bias": "ambitious, high-energy, assertive, vivid",
"composition_bias": "bolder crop, stronger scale contrast, more motion cues, still disciplined",
"palette_bias": "higher-contrast palette with strong accent energy",
"detail_bias": "high-impact hero detail, expressive texture, controlled spectacle",
},
}
GLOBAL_DIRECTIVES = [
"Start from purpose, audience, and channel rather than surface-level adjectives.",
"Create a coherent visual system before detailing the image.",
"Use one clear hero idea and preserve obvious hierarchy.",
"Treat negative space and safe zones as design decisions, not leftover space.",
"Avoid filler content, decorative noise, and meaningless visual data.",
"Respect existing brand or context when available; if not, still commit to a clear direction.",
"Avoid AI-slop tropes such as random HUD overlays, generic fog, empty gradients, and scattered floating debris.",
]
TASK_PROFILES = {
"poster": {
"communication_goal": "attract and persuade quickly in a campaign or recruitment context",
"hero_strategy": "one dominant hero visual or symbolic concept, never a collage of equal-weight objects",
"safe_zone": "reserve a clean, obvious text-safe zone in the upper third or one side for headline and CTA copy",
"lighting": "crisp premium lighting with controlled contrast and energetic highlights",
"palette": "restricted premium palette with disciplined neutrals and one energetic accent family",
"detail_density": "hero-rich detail with restrained background complexity",
"base_style": "editorial campaign key visual",
"task_constraints": [
"make the result feel campaign-ready rather than generic AI art",
"prioritize focal hierarchy and typography-safe composition",
],
"task_avoid": [
"poster text rendered directly into the image unless explicitly requested",
"random marketing icons and decorative interface fragments",
],
},
"product": {
"communication_goal": "make the product feel desirable, premium, and commercially credible",
"hero_strategy": "the product is the undisputed focal point with clear silhouette and edge readability",
"safe_zone": "keep surrounding space supportive and uncluttered so the product remains dominant",
"lighting": "commercial lighting that reveals material, finish, and shape without cheap reflections",
"palette": "palette chosen to support product positioning rather than compete with the product",
"detail_density": "high fidelity on the hero object, restrained props and background",
"base_style": "high-end commercial product advertising",
"task_constraints": [
"preserve product proportions and perceived material integrity",
"use props only when they reinforce product positioning",
],
"task_avoid": [
"oversized props stealing attention",
"luxury claims paired with cheap lighting or noisy backgrounds",
],
},
"ppt": {
"communication_goal": "support presentation storytelling with a clear, memorable visual metaphor",
"hero_strategy": "a single strong metaphor or scene readable at presentation distance",
"safe_zone": "reserve a large clean area for slide title and subtitle overlay",
"lighting": "clean, legible lighting that supports shape readability over moodiness",
"palette": "presentation-friendly palette with strong contrast and limited visual noise",
"detail_density": "mid-detail image readable at a glance, not overloaded with tiny elements",
"base_style": "presentation cover art",
"task_constraints": [
"favor readability from distance over excess detail",
"keep enough room for future title placement",
],
"task_avoid": [
"ad-poster density",
"tiny embedded text or fragile detail that disappears on slides",
],
},
"infographic": {
"communication_goal": "communicate structure, grouping, and flow rather than literal dense data",
"hero_strategy": "clear modular hierarchy with one dominant organizing principle",
"safe_zone": "leave room for headings or labels without relying on the model to render tiny text",
"lighting": "flat-to-controlled lighting that supports structure and clarity",
"palette": "structured palette with clear grouping and low noise",
"detail_density": "low-to-mid detail, with emphasis on grouping and directional logic",
"base_style": "structured infographic-like visual system",
"task_constraints": [
"prioritize visual structure over fake data richness",
"use symbolic clarity and modular composition",
],
"task_avoid": [
"tiny chart labels",
"data slop, fake dashboards, and dense unreadable micro-details",
],
},
"teaching": {
"communication_goal": "explain a process, comparison, or sequence with maximum clarity",
"hero_strategy": "show the logic of the teaching point first, then add supporting visuals",
"safe_zone": "keep panel or label areas simple and legible for later annotation",
"lighting": "clear explanatory lighting, not overly cinematic",
"palette": "clarity-first palette with simple grouping and controlled contrast",
"detail_density": "mid-to-low detail with emphasis on readable sequence and large forms",
"base_style": "instructional visual storytelling",
"task_constraints": [
"make sequence and cause-effect legible at first glance",
"use big forms and obvious directional logic",
],
"task_avoid": [
"cinematic clutter that weakens explanation",
"too many simultaneous steps in one frame",
],
},
}
def detect_task(brief: str) -> str:
text = brief.lower()
scores = {}
for task, keywords in TASK_KEYWORDS.items():
scores[task] = sum(1 for keyword in keywords if keyword.lower() in text)
best_task = max(scores, key=scores.get)
return best_task if scores[best_task] > 0 else "poster"
def normalize_task(task: str, brief: str) -> str:
return detect_task(brief) if task == "auto" else task
def choose_model(quality: str) -> str:
return QUALITY_MODEL[quality]
def choose_size(task: str, aspect: str, quality: str) -> str:
tier = "2K" if quality == "draft" or task in {"ppt", "infographic", "teaching"} else "3K"
return ASPECT_SIZES.get(aspect, ASPECT_SIZES[DEFAULT_ASPECT[task]])[tier]
def join_phrases(items: list[str]) -> str:
return "; ".join(item for item in items if item)
def unique_preserving_order(items: list[str]) -> list[str]:
seen = set()
ordered = []
for item in items:
if item not in seen:
seen.add(item)
ordered.append(item)
return ordered
def make_design_reasoning(args: argparse.Namespace, task: str) -> dict:
profile = TASK_PROFILES[task]
direction = DIRECTION_PROFILES[args.direction]
brand_strategy = (
f"use the provided brand/context: {args.brand}"
if args.brand
else "no explicit brand provided; commit to one coherent visual system instead of averaging styles"
)
reference_strategy = (
"use provided reference images to preserve consistency and context"
if args.image
else "no reference images provided; rely on the compiled visual system and brief"
)
source_sections = unique_preserving_order(CORE_SOURCE_SECTIONS + TASK_SOURCE_SECTIONS[task])
visual_system = [
f"base mode: {profile['base_style']}",
f"direction bias: {direction['style_bias']}",
f"energy bias: {direction['energy_bias']}",
f"composition bias: {direction['composition_bias']}",
f"palette bias: {direction['palette_bias']}",
]
hierarchy_strategy = [
"one clear hero idea",
profile["hero_strategy"],
"secondary elements must support the hero rather than compete with it",
"background should create rhythm, not narrative confusion",
]
anti_filler_rules = [
"every element must earn its place",
"do not add objects, labels, icons, or stats that do not strengthen the core message",
"if the frame feels empty, solve with scale, crop, rhythm, or texture rather than random extra elements",
]
anti_slop_rules = [
"avoid generic AI clutter",
"avoid random floating UI fragments or HUD overlays",
"avoid generic gradient fog with no composition logic",
"avoid cheap neon cyberpunk treatment unless explicitly requested",
"avoid noisy micro-detail that weakens the hierarchy",
]
if args.avoid:
anti_slop_rules.append(args.avoid)
return {
"task": task,
"direction": args.direction,
"communication_goal": args.goal or profile["communication_goal"],
"audience": args.audience or "broad professional audience",
"channel": args.usage or task,
"brief": args.brief.strip(),
"brand_strategy": brand_strategy,
"reference_strategy": reference_strategy,
"visual_system": visual_system,
"hierarchy_strategy": hierarchy_strategy,
"safe_zone_strategy": args.safe_zone or profile["safe_zone"],
"lighting_strategy": args.lighting or profile["lighting"],
"palette_strategy": args.palette or profile["palette"],
"detail_density": direction["detail_bias"] + "; " + profile["detail_density"],
"style_direction": args.style or join_phrases(visual_system),
"mood_direction": args.mood or direction["energy_bias"],
"composition_logic": args.composition or direction["composition_bias"],
"anti_filler_rules": anti_filler_rules,
"anti_slop_rules": anti_slop_rules,
"task_constraints": profile["task_constraints"] + ([args.constraints] if args.constraints else []),
"task_avoid": profile["task_avoid"],
"global_directives": GLOBAL_DIRECTIVES,
"source_sections": source_sections,
"primary_source_file": "references/claude-design-sys-prompt-full.txt",
}
def compile_design_brief(reasoning: dict, aspect: str) -> dict:
return {
"task": reasoning["task"],
"direction": reasoning["direction"],
"brief": reasoning["brief"],
"communication_goal": reasoning["communication_goal"],
"audience": reasoning["audience"],
"channel": reasoning["channel"],
"brand_strategy": reasoning["brand_strategy"],
"reference_strategy": reasoning["reference_strategy"],
"visual_system": join_phrases(reasoning["visual_system"]),
"hierarchy": join_phrases(reasoning["hierarchy_strategy"]),
"composition": reasoning["composition_logic"],
"safe_zone": reasoning["safe_zone_strategy"],
"lighting": reasoning["lighting_strategy"],
"palette": reasoning["palette_strategy"],
"detail_density": reasoning["detail_density"],
"style_direction": reasoning["style_direction"],
"mood": reasoning["mood_direction"],
"constraints": join_phrases(reasoning["task_constraints"]),
"avoid": join_phrases(reasoning["anti_slop_rules"] + reasoning["task_avoid"]),
"aspect": aspect,
"source_sections": reasoning["source_sections"],
}
def build_prompt(brief: dict) -> str:
parts = [
f"Create a {brief['task']} image for {brief['channel']} aimed at {brief['audience']}.",
f"Treat this as a design-led visual solving this brief: {brief['brief']}.",
f"Communication goal: {brief['communication_goal']}.",
"Translate the brief into one strong hero concept rather than many equal-weight elements.",
f"Brand and context strategy: {brief['brand_strategy']}.",
f"Visual system: {brief['visual_system']}.",
f"Hierarchy: {brief['hierarchy']}.",
f"Composition: {brief['composition']}.",
f"Safe zone: {brief['safe_zone']}.",
f"Lighting: {brief['lighting']}.",
f"Color strategy: {brief['palette']}.",
f"Detail density: {brief['detail_density']}.",
f"Style direction: {brief['style_direction']}.",
f"Mood: {brief['mood']}.",
f"Aspect ratio: {brief['aspect']}.",
f"Important constraints: {brief['constraints']}.",
f"Avoid: {brief['avoid']}.",
"Emphasize strong hierarchy, intentional whitespace, disciplined background complexity, and polished professional finish.",
]
return " ".join(parts)
def run_generation(
args: argparse.Namespace,
prompt: str,
task: str,
model: str,
size: str,
) -> int:
skill_root = Path(__file__).resolve().parent
generate_script = skill_root / "generate.py"
command = [
sys.executable,
str(generate_script),
"--prompt",
prompt,
"--model",
model,
"--size",
size,
"--response-format",
args.response_format,
"--output-format",
args.output_format,
]
if args.output:
command.extend(["--output", args.output])
if args.output_dir:
command.extend(["--output-dir", args.output_dir])
if args.budget_limit is not None:
command.extend(["--budget-limit", str(args.budget_limit)])
if args.dry_run:
command.append("--dry-run")
if args.web_search:
command.append("--web-search")
if args.fast_mode:
command.append("--fast-mode")
if args.watermark:
command.append("--watermark")
if args.fallback_model:
command.extend(["--fallback-model", args.fallback_model])
if args.image:
command.extend(["--image", *args.image])
if task in {"teaching", "infographic"} and args.sequential:
command.append("--sequential")
command.extend(["--max-images", str(args.max_images)])
print("\n[command]")
print(" ".join(shlex.quote(part) for part in command))
sys.stdout.flush()
result = subprocess.run(command, check=False)
return result.returncode
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Compile a design-system-driven brief into an image prompt and generate the image."
)
parser.add_argument("--task", default="auto", choices=["auto", "poster", "product", "ppt", "infographic", "teaching"])
parser.add_argument("--brief", required=True, help="User brief or request")
parser.add_argument("--audience", default=None, help="Target audience")
parser.add_argument("--usage", default=None, help="Where the image will be used")
parser.add_argument("--brand", default=None, help="Brand tone or reference context")
parser.add_argument("--style", default=None, help="Preferred visual style override")
parser.add_argument("--mood", default=None, help="Preferred mood override")
parser.add_argument("--goal", default=None, help="Specific communication goal override")
parser.add_argument("--composition", default=None, help="Composition override")
parser.add_argument("--constraints", default=None, help="Must-have constraints")
parser.add_argument("--avoid", default=None, help="Things to avoid")
parser.add_argument("--aspect", default=None, help="Aspect ratio such as 1:1, 3:4, 16:9")
parser.add_argument("--direction", default="balanced", choices=["conservative", "balanced", "bold"])
parser.add_argument("--safe-zone", default=None, help="Safe-zone strategy override")
parser.add_argument("--lighting", default=None, help="Lighting strategy override")
parser.add_argument("--palette", default=None, help="Palette strategy override")
parser.add_argument("--quality", default="final", choices=["draft", "final", "premium"])
parser.add_argument("--model-override", default=None, help="Explicit model id override such as doubao-seedream-5-0-260128")
parser.add_argument("--image", nargs="+", default=None, help="Reference image path(s) or URL(s)")
parser.add_argument("--output", "-o", default=None, help="Output filename")
parser.add_argument("--output-dir", default=None, help="Output directory")
parser.add_argument("--output-format", default="png", choices=["png", "jpeg"])
parser.add_argument("--response-format", default="b64_json", choices=["url", "b64_json"])
parser.add_argument("--prompt-only", action="store_true", help="Only print design reasoning, compiled brief, and the final prompt")
parser.add_argument("--budget-limit", type=float, default=None, help="Optional spend guard in CNY")
parser.add_argument("--fallback-model", default=None, help="Fallback model id")
parser.add_argument("--dry-run", action="store_true", help="Estimate cost without generating")
parser.add_argument("--web-search", action="store_true", help="Enable web search if the selected model supports it")
parser.add_argument("--fast-mode", action="store_true", help="Enable fast mode for compatible models")
parser.add_argument("--watermark", action="store_true", help="Enable output watermark")
parser.add_argument("--sequential", action="store_true", help="Generate sequential images for explanatory series")
parser.add_argument("--max-images", type=int, default=4, help="Max images for sequential generation")
return parser.parse_args()
def main() -> int:
args = parse_args()
task = normalize_task(args.task, args.brief)
args.aspect = args.aspect or DEFAULT_ASPECT[task]
model = args.model_override or choose_model(args.quality)
size = choose_size(task, args.aspect, args.quality)
design_reasoning = make_design_reasoning(args, task)
compiled_brief = compile_design_brief(design_reasoning, args.aspect)
prompt = build_prompt(compiled_brief)
print("[design_reasoning]")
print(json.dumps(design_reasoning, ensure_ascii=False, indent=2))
print("\n[compiled_brief]")
print(json.dumps(compiled_brief, ensure_ascii=False, indent=2))
print("\n[prompt]")
print(prompt)
print(f"\n[settings]\nmodel={model}\nsize={size}\naspect={args.aspect}\ndirection={args.direction}")
sys.stdout.flush()
if args.prompt_only:
return 0
return run_generation(args, prompt, task, model, size)
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""
Volcengine Seedream Image Generation Script
============================================
Features:
- Text-to-image, image-to-image, multi-image fusion, sequential generation
- Automatic retry with exponential backoff and jitter
- Model fallback on repeated failures
- Cost estimation, budget limiting, and usage logging
- Streaming support for sequential images
Usage:
python generate.py --prompt "A sunset over mountains" --model auto --size 2K
python generate.py --prompt "Edit background" --image photo.png --size 2K
python generate.py --prompt "Storyboard" --sequential --max-images 4
"""
import argparse
import base64
import json
import os
import random
import sys
import time
from datetime import datetime
from pathlib import Path
from typing import Optional
# ── Configuration ─────────────────────────────────────────────
MODELS = {
"doubao-seedream-5-0-260128": {
"name": "Seedream 5.0",
"tier": "high",
"resolutions": ["2K", "3K"],
"formats": ["png", "jpeg"],
"cost_per_image": {"1K": 0.04, "2K": 0.06, "3K": 0.10, "4K": 0.14},
},
"doubao-seedream-5-0-lite-260128": {
"name": "Seedream 5.0 lite",
"tier": "mid",
"resolutions": ["2K", "3K"],
"formats": ["png", "jpeg"],
"cost_per_image": {"1K": 0.02, "2K": 0.04, "3K": 0.06, "4K": 0.08},
},
"doubao-seedream-4-5-251128": {
"name": "Seedream 4.5",
"tier": "high",
"resolutions": ["2K", "4K"],
"formats": ["jpeg"],
"cost_per_image": {"1K": 0.04, "2K": 0.06, "3K": 0.08, "4K": 0.12},
},
"doubao-seedream-4-0-250828": {
"name": "Seedream 4.0",
"tier": "low",
"resolutions": ["1K", "2K", "4K"],
"formats": ["jpeg"],
"cost_per_image": {"1K": 0.02, "2K": 0.03, "3K": 0.05, "4K": 0.08},
},
}
# Resolution presets → approximate pixel counts
RESOLUTION_MAP = {
"1K": 921600,
"2K": 3686400,
"3K": 9437184,
"4K": 16777216,
}
COST_LOG_DEFAULT = os.path.expanduser("~/.volcengine-image-costs.json")
# ── Cost Management ───────────────────────────────────────────
def resolve_resolution(size_arg: str, model_id: str) -> str:
"""Resolve size argument to a resolution tier for cost estimation."""
if size_arg in RESOLUTION_MAP:
return size_arg
# Parse pixel dimensions like "2048x2048"
if "x" in size_arg.lower():
parts = size_arg.lower().split("x")
try:
pixels = int(parts[0]) * int(parts[1])
return min(
RESOLUTION_MAP,
key=lambda tier: abs(RESOLUTION_MAP[tier] - pixels),
)
except (ValueError, IndexError):
pass
return "2K" # default
def estimate_cost(model_id: str, size: str, num_images: int = 1) -> float:
"""Estimate cost in CNY for a generation request."""
model_info = MODELS.get(model_id, {})
cost_table = model_info.get("cost_per_image", {})
resolution = resolve_resolution(size, model_id)
per_image = cost_table.get(resolution, 0.04) # default estimate
return per_image * num_images
def check_budget(cost: float, budget_limit: Optional[float], cost_log_path: str) -> bool:
"""Check if this generation would exceed the budget limit."""
if budget_limit is None:
return True
cumulative = 0.0
if os.path.exists(cost_log_path):
try:
with open(cost_log_path, "r") as f:
log = json.load(f)
cumulative = sum(e.get("cost", 0) for e in log.get("entries", []))
except (json.JSONDecodeError, IOError):
pass
if cumulative + cost > budget_limit:
print(f"⚠ Budget limit: cumulative ¥{cumulative:.4f} + estimated ¥{cost:.4f} "
f"> limit ¥{budget_limit:.4f}", file=sys.stderr)
return False
return True
def log_cost(model_id: str, size: str, prompt_len: int, cost: float,
success: bool, error: str, cost_log_path: str):
"""Append a cost entry to the JSON log."""
log = {"entries": []}
if os.path.exists(cost_log_path):
try:
with open(cost_log_path, "r") as f:
log = json.load(f)
except (json.JSONDecodeError, IOError):
log = {"entries": []}
log["entries"].append({
"timestamp": datetime.now().isoformat(),
"model": model_id,
"resolution": size,
"prompt_length": prompt_len,
"estimated_cost_cny": round(cost, 4),
"success": success,
"error": error,
})
os.makedirs(os.path.dirname(cost_log_path) or ".", exist_ok=True)
with open(cost_log_path, "w") as f:
json.dump(log, f, indent=2, ensure_ascii=False)
# ── Retry Logic ───────────────────────────────────────────────
class RetryExhausted(Exception):
"""All retry attempts failed."""
pass
class BudgetExceeded(Exception):
"""Generation would exceed budget."""
pass
def classify_error(status_code: int, error_msg: str) -> str:
"""Classify an API error to determine retry strategy.
Returns one of:
- "retry" → transient, safe to retry
- "auth" → authentication error, must stop
- "bad_request" → invalid params, must fix
- "content_policy" → safety filter, must revise prompt
- "unknown" → unclassified
"""
if status_code in (401, 403):
return "auth"
if status_code == 400:
if "content" in error_msg.lower() or "safety" in error_msg.lower():
return "content_policy"
return "bad_request"
if status_code == 429:
return "retry"
if status_code >= 500:
return "retry"
if "timeout" in error_msg.lower() or "connection" in error_msg.lower():
return "retry"
return "unknown"
def retry_with_backoff(func, max_retries=3, initial_delay=1.0,
multiplier=2.0, on_fallback=None):
"""Execute func with exponential backoff + jitter on retriable errors.
Args:
func: Callable that performs the API call. Should raise on failure.
max_retries: Maximum retry attempts.
initial_delay: First retry delay in seconds.
multiplier: Backoff multiplier between retries.
on_fallback: Optional callable(model_id) to switch to a fallback model.
Returns:
The result of func().
Raises:
RetryExhausted: All retries failed.
"""
last_error = None
for attempt in range(max_retries + 1):
try:
return func()
except Exception as e:
last_error = e
# Extract status code from various SDK exception formats
status_code = getattr(e, "status_code", None) or 0
error_msg = str(e)
error_type = classify_error(status_code, error_msg)
# Non-retriable errors: fail immediately
if error_type == "auth":
print(f"✘ Authentication error: {error_msg}", file=sys.stderr)
raise
if error_type == "bad_request":
print(f"✘ Bad request: {error_msg}", file=sys.stderr)
raise
if error_type == "content_policy":
print(f"✘ Content policy violation: {error_msg}", file=sys.stderr)
print(" Suggestion: Revise your prompt to avoid sensitive content.",
file=sys.stderr)
raise
# Retriable: attempt retry or fallback
if attempt < max_retries:
delay = initial_delay * (multiplier ** attempt)
jitter = random.uniform(0, delay * 0.3)
total_delay = delay + jitter
print(f"⟳ Attempt {attempt + 1}/{max_retries} failed "
f"({error_type}): {error_msg[:100]}", file=sys.stderr)
print(f" Retrying in {total_delay:.1f}s...", file=sys.stderr)
# On last retry attempt, try fallback model if provided
if attempt == max_retries - 1 and on_fallback:
fallback_msg = on_fallback()
if fallback_msg:
print(f" ↓ Falling back to: {fallback_msg}", file=sys.stderr)
time.sleep(total_delay)
else:
print(f"✘ All {max_retries} retries exhausted.", file=sys.stderr)
raise RetryExhausted(f"Failed after {max_retries} retries. Last error: {last_error}")
# ── Image Helpers ─────────────────────────────────────────────
def image_to_data_uri(image_path: str) -> str:
"""Convert a local image file to a data URI for the API."""
ext = Path(image_path).suffix.lower().lstrip(".")
if ext == "jpg":
ext = "jpeg"
with open(image_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode("utf-8")
return f"data:image/{ext};base64,{b64}"
def download_image(url: str, output_path: str):
"""Download an image from URL to local file."""
import urllib.request
urllib.request.urlretrieve(url, output_path)
# ── Main Generation Logic ────────────────────────────────────
def select_model(model_arg: str) -> str:
"""Resolve 'auto' or shorthand to a full model ID."""
if model_arg == "auto":
return "doubao-seedream-5-0-lite-260128" # best value default
# Allow short names
aliases = {
"5.0": "doubao-seedream-5-0-260128",
"5.0-lite": "doubao-seedream-5-0-lite-260128",
"4.5": "doubao-seedream-4-5-251128",
"4.0": "doubao-seedream-4-0-250828",
}
return aliases.get(model_arg, model_arg)
def run_generation(args):
"""Execute the image generation with all features."""
# ── Resolve model ──
model_id = select_model(args.model)
fallback_model = select_model(args.fallback_model) if args.fallback_model else None
if model_id not in MODELS:
print(f"✘ Unknown model: {model_id}", file=sys.stderr)
print(f" Available: {', '.join(MODELS.keys())}", file=sys.stderr)
sys.exit(1)
# ── Resolve images ──
images = None
if args.image:
images = []
for img_path in args.image:
if img_path.startswith(("http://", "https://")):
images.append(img_path)
elif os.path.isfile(img_path):
images.append(image_to_data_uri(img_path))
else:
print(f"✘ Image not found: {img_path}", file=sys.stderr)
sys.exit(1)
# ── Estimate cost ──
num_images = args.max_images if args.sequential else 1
estimated_cost = estimate_cost(model_id, args.size, num_images)
print(f"Model: {MODELS[model_id]['name']} ({model_id})")
print(f"Resolution: {args.size}")
print(f"Images: {num_images}")
print(f"Estimated cost: ¥{estimated_cost:.4f}")
if args.dry_run:
print("\n[Dry run] No API call made.")
return
# ── Budget check ──
if not check_budget(estimated_cost, args.budget_limit, args.cost_log):
print("✘ Aborted: would exceed budget limit.", file=sys.stderr)
sys.exit(1)
# ── Prepare API client ──
try:
from volcenginesdkarkruntime import Ark
except ImportError:
print("✘ volcengine-python-sdk not installed.", file=sys.stderr)
print(" Run: pip install 'volcengine-python-sdk[ark]'", file=sys.stderr)
sys.exit(1)
api_key = os.environ.get("ARK_API_KEY")
if not api_key:
print("✘ ARK_API_KEY environment variable not set.", file=sys.stderr)
print(" Get your key at: https://console.volcengine.com/ark/region:ark+cn-beijing/apikey",
file=sys.stderr)
sys.exit(1)
client = Ark(
base_url="https://ark.cn-beijing.volces.com/api/v3",
api_key=api_key,
)
# ── Build request kwargs ──
gen_kwargs = {
"model": model_id,
"prompt": args.prompt,
"size": args.size,
"response_format": args.response_format,
"watermark": args.watermark,
}
# Seedream 4.5 rejects the `output_format` parameter entirely.
if model_id != "doubao-seedream-4-5-251128":
gen_kwargs["output_format"] = args.output_format
if images:
gen_kwargs["image"] = images if len(images) > 1 else images[0]
if args.sequential:
from volcenginesdkarkruntime.types.images.images import (
SequentialImageGenerationOptions,
)
gen_kwargs["sequential_image_generation"] = "auto"
gen_kwargs["sequential_image_generation_options"] = (
SequentialImageGenerationOptions(max_images=args.max_images)
)
if args.web_search:
gen_kwargs["tools"] = [{"type": "web_search"}]
if args.fast_mode:
gen_kwargs["optimize_prompt_options"] = {"mode": "fast"}
# ── Execute with retry ──
current_model = model_id
def do_call():
gen_kwargs["model"] = current_model
if current_model == "doubao-seedream-4-5-251128":
gen_kwargs.pop("output_format", None)
else:
gen_kwargs["output_format"] = args.output_format
return client.images.generate(**gen_kwargs)
def try_fallback():
nonlocal current_model
if fallback_model and fallback_model != current_model:
current_model = fallback_model
return MODELS[fallback_model]["name"]
return None
try:
result = retry_with_backoff(
do_call,
max_retries=args.max_retries,
initial_delay=args.retry_delay,
multiplier=args.retry_multiplier,
on_fallback=try_fallback if fallback_model else None,
)
except RetryExhausted as e:
log_cost(model_id, args.size, len(args.prompt), estimated_cost,
False, str(e), args.cost_log)
print(f"\n✘ Generation failed: {e}", file=sys.stderr)
sys.exit(1)
except Exception as e:
log_cost(model_id, args.size, len(args.prompt), estimated_cost,
False, str(e), args.cost_log)
print(f"\n✘ Generation failed: {e}", file=sys.stderr)
sys.exit(1)
# ── Process results ──
output_dir = args.output_dir or "."
os.makedirs(output_dir, exist_ok=True)
saved_files = []
for i, img_data in enumerate(result.data):
if args.response_format == "url" and hasattr(img_data, "url") and img_data.url:
if args.output:
filename = args.output if num_images == 1 else f"{Path(args.output).stem}_{i+1}{Path(args.output).suffix}"
else:
ext = "png" if args.output_format == "png" else "jpg"
filename = f"generated_{int(time.time())}_{i+1}.{ext}"
filepath = os.path.join(output_dir, filename)
download_image(img_data.url, filepath)
saved_files.append(filepath)
print(f" ✓ Saved: {filepath}")
elif args.response_format == "b64_json" and hasattr(img_data, "b64_json"):
if args.output:
filename = args.output if num_images == 1 else f"{Path(args.output).stem}_{i+1}{Path(args.output).suffix}"
else:
ext = "png" if args.output_format == "png" else "jpg"
filename = f"generated_{int(time.time())}_{i+1}.{ext}"
filepath = os.path.join(output_dir, filename)
with open(filepath, "wb") as f:
f.write(base64.b64decode(img_data.b64_json))
saved_files.append(filepath)
print(f" ✓ Saved: {filepath}")
# ── Log cost ──
actual_cost = estimated_cost # Use estimate (API doesn't return exact cost)
log_cost(current_model, args.size, len(args.prompt), actual_cost,
True, "", args.cost_log)
total = sum(e.get("estimated_cost_cny", 0) for e in
json.load(open(args.cost_log)).get("entries", [])) \
if os.path.exists(args.cost_log) else actual_cost
print(f"\n Cost this request: ~¥{actual_cost:.4f}")
print(f" Files saved: {len(saved_files)}")
return saved_files
# ── CLI ───────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(
description="Generate images via Volcengine Seedream API",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
# ── Required ──
parser.add_argument("--prompt", required=True, help="Text prompt for image generation")
# ── Model & Output ──
parser.add_argument("--model", default="auto",
help="Model: auto, 5.0, 5.0-lite, 4.5, 4.0, or full model ID")
parser.add_argument("--fallback-model", default=None,
help="Fallback model if primary fails after retries")
parser.add_argument("--size", default="2K",
help="Resolution: 1K/2K/3K/4K or WxH (e.g. 2048x2048)")
parser.add_argument("--output", "-o", default=None, help="Output filename")
parser.add_argument("--output-dir", default=None, help="Output directory")
parser.add_argument("--output-format", default="png",
choices=["png", "jpeg"], help="Output image format")
parser.add_argument("--response-format", default="url",
choices=["url", "b64_json"], help="API response format")
# ── Input Images ──
parser.add_argument("--image", nargs="+", default=None,
help="Input image path(s) or URL(s) for image-to-image")
# ── Sequential Generation ──
parser.add_argument("--sequential", action="store_true",
help="Enable sequential image generation")
parser.add_argument("--max-images", type=int, default=4,
help="Max images for sequential generation (default: 4)")
# ── Advanced Features ──
parser.add_argument("--web-search", action="store_true",
help="Enable web search (5.0 lite only)")
parser.add_argument("--fast-mode", action="store_true",
help="Use fast prompt optimization (4.0 only)")
parser.add_argument("--watermark", action="store_true",
help="Add AI-generated watermark")
# ── Retry & Error Handling ──
parser.add_argument("--max-retries", type=int, default=3,
help="Max retry attempts (default: 3)")
parser.add_argument("--retry-delay", type=float, default=1.0,
help="Initial retry delay in seconds (default: 1)")
parser.add_argument("--retry-multiplier", type=float, default=2.0,
help="Backoff multiplier (default: 2)")
# ── Cost Management ──
parser.add_argument("--budget-limit", type=float, default=None,
help="Max cumulative spend in CNY")
parser.add_argument("--cost-log", default=COST_LOG_DEFAULT,
help=f"Cost log JSON path (default: {COST_LOG_DEFAULT})")
parser.add_argument("--dry-run", action="store_true",
help="Estimate cost without calling API")
args = parser.parse_args()
run_generation(args)
if __name__ == "__main__":
main()