图片生成 Provider
Spark Work 图片生成 Provider:图片 API 源与 imageApiType 字段、9 个内置图片预设、spark_image MCP 工具与 spark_media 统一栈的双链路判定、产物输出目录与 7 类渠道的参数归一规则。
图片生成有两条链路:旧的 spark_image(单 Provider、单工具、只生图)与统一的 spark_media(多 Provider 路由、覆盖生图 / 编辑 / 语音 / 视频)。二者的取舍规则 是确定的:只要能解析出统一媒体配置,就只注入 spark_media;spark_image 是兜底,不是主力。这篇讲清楚两条链路各管什么、字段怎么落、参数怎么被 归一。
1. 配置步骤与真实选项
- 打开 设置 → Provider,新建或编辑一条 Provider。
- 模型类型选生图模型(存储字面量
image)。 图片专属字段只有modelType=image时才会写入。 - 选图片 API 源。表单里是下面 9 个选项,括号内是真实枚举值、默认端点与默认调用方式:
界面标签 imageProvider 默认端点 默认 imageApiType OpenAI Images openaihttps://api.openai.com/v1 syncAPIMart apimarthttps://api.apimart.ai/v1 asyncOpenRouter openrouterhttps://openrouter.ai/api/v1 syncGemini / Imagen geminihttps://generativelanguage.googleapis.com/v1beta sync火山方舟 Seedream / Seedance seeddancehttps://ark.cn-beijing.volces.com/api/v3 sync阿里百炼 bailianhttps://dashscope.aliyuncs.com/api/v1/services/aigc async智谱 GLM Image zhipuhttps://open.bigmodel.cn/api/paas/v4 syncxAI Imagine xaihttps://api.x.ai/v1 sync自定义兼容接口 custom(空,自己填) sync - 填 Model ID 与 API Key,保存。
注意枚举拼写:是 seeddance 而不是 seedance。保存时 Spark 会做兼容归一,seedance / volcengine 也会被映射成 volcengine-ark, 但表单与预设写的是 seeddance。
内置的生图预设共 9 条:openai-images(OpenAI,gpt-image-2)、apimart-images(APIMart,gpt-image-2)、xai-imagine-image(xAI, grok-imagine-image-quality)、bailian-images(阿里云百炼,wan2.7-image-pro)、volcengine-seedream-image(火山方舟,doubao-seedream-5-0-pro-260628)、google-gemini-images(Google Gemini,gemini-3.1-flash-image)、midjourney-gateway(Midjourney 网关,需改自建网关地址)、minimax-image(MiniMax 图片,image-01)、tencent-tokenhub-image(腾讯云,hy-image-v3.0)。
2. 运行时链路:spark_image 与 spark_media 的分界
每一轮对话开始时,Spark 按固定顺序解析:
1. resolveMediaGenerationContext()
聚合所有 enabled 且凭据可用的图片 / 语音 / 视频 Provider
→ 有结果:注入 spark_media(14 个工具),不再注入 spark_image
2. 只有当上一步返回 null 时,才调用 resolveImageGenerationContext()
找第一条 modelType=image 且凭据可用的 Provider
→ 注入 spark_image,只暴露 mcp__spark_image__generate_image也就是说:一个 Provider 同时能生图、语音、视频时,图片请求也走 spark_media。 只有「配了图片 Provider 但统一媒体栈解析不出任何可用配置」这种旧环境,才会落到 spark_image。
两条链路的差异:
| spark_media | spark_image | |
|---|---|---|
| 工具 | 14 个(生图 / 编辑 / 语音 / 视频 / 文件 / 任务) | 1 个(generate_image) |
| Provider 范围 | 所有 enabled 的 image / voice / video Provider,可混用 | 第一条可用的 image Provider |
| 模型选择 | 靠 model(selectionKey / manifest id / 模型 id)路由 | 固定用该 Provider 的默认模型 |
| 产物目录 | .spark-artifacts/media/{images,audio,videos,text} | .spark-artifacts/images |
| 能力 | 11 个统一能力 | 仅文生图 |
图片编辑 / 变体 / 语音 / 视频不在 spark_image 的能力范围里,请看 多媒体 Provider。
3. spark_image 工具
注入时通过环境变量把配置传进本地 stdio 子进程:
SPARK_IMAGE_API_KEY Provider 的 API Key(只在子进程里) SPARK_IMAGE_MODEL 默认模型 id SPARK_IMAGE_PROVIDER imageProvider 值,缺省 openai SPARK_IMAGE_API_TYPE sync | async | auto,缺省 sync SPARK_IMAGE_BASE_URL 仅当 Provider 配了 apiEndpoint 时传 SPARK_IMAGE_OUTPUT_DIR 固定为 <workspace>/.spark-artifacts/images
工具 mcp__spark_image__generate_image 的参数:
| 参数 | 类型 | 说明 |
|---|---|---|
prompt | string(必填) | 详细提示词 |
size | string | 像素尺寸或宽高比,如 1024x1024、1:1、16:9,也接受语义值 portrait / landscape / square |
n | integer 1~4 | 生成数量,默认 1,超出会被夹到 4 |
filename | string | 输出文件名,不要带路径 |
extraJson | object | 渠道私有参数 |
执行细节:
- 默认尺寸
1024x1024;同步请求超时 10 分钟;异步轮询间隔 5 秒,轮询GET {baseUrl}/tasks/{taskId}。 - 结果解析会遍历整个响应体,抓
url/image_url/imageUrl、data:image/*;base64,以及长度大于 64 的b64_json/base64, 按内容去重后下载落盘。 - 默认文件名形如
img_<时间戳>_<随机串>;一次生成多张时会在基础名后追加_001这类三位序号,再拼扩展名。 - 返回结构:
{ success, provider, mode, files, urls },其中provider形如apimart/gpt-image-2。只有配置了 URL 前缀时urls才会是外链,否则与files相同(都是本地路径)——应用当前不注入该 前缀变量,所以默认拿到本地路径。 - Agent 侧 system prompt 要求:成功后必须调用
mcp__spark_files__present_files把本地 图片文件交给应用渲染预览;返回纯 URL 不算完成;失败时不要自动重试,要报错并建议 调整模型 / 提示词 / 尺寸 / Provider 配置。
4. 各渠道参数归一
size 会按渠道被改写成该渠道真正接受的字段,这是最容易踩坑的地方:
| imageProvider | 提交路径 | size / 比例处理 |
|---|---|---|
openai | /images/generations | 比例映射成像素尺寸:1:1→1024x1024、3:2/16:9→1536x1024、2:3/3:4/9:16→1024x1536 |
xai | /images/generations | 删除 size(xAI 会报 Argument not supported: size),比例改写成 aspect_ratio |
apimart | /images/generations,异步 | 比例或尺寸原样传递 |
bailian | 同步 /multimodal-generation/generation;异步 /image-generation/generation(带 X-DashScope-Async: enable) | 用星号尺寸(1:1→1280*1280、3:4→1104*1472、4:3→1472*1104、9:16→960*1696、16:9→1696*960);body 改写成 input.messages + parameters |
openrouter | /chat/completions | 比例写进 image_config.aspect_ratio;body 是 chat 格式并带 modalities(默认 ["image","text"]) |
gemini / seeddance / zhipu | /images/generations | 把 aspect_ratio 与 aspectRatio 同时写进 extraJson |
custom | /images/generations | 比例或尺寸原样传递,其余靠 extraJson |
5. Provider 选择规则
spark_image 自动挑选 Provider 的条件,四条缺一不可:
enabled = 1;config_json.modelType === "image";- 有
keystore_ref,且能真的从密钥库里解出非空 API Key; - 默认模型(
defaultModel,回退model)非空。
命中条件时取「第一条」。查询语句是 SELECT * FROM provider_profiles LIMIT 1000,没有 ORDER BY, 实际就是数据库自然顺序(通常是创建顺序)。所以想固定用哪条,最简单的做法是只保留一条可用的 图片 Provider。
选择过程不检查健康状态:不会去 ping 渠道。只要字段齐全就会注入,真实失败会在第一次 调用时暴露。
6. 与统一媒体栈的字段同步
保存 modelType=image 的 Provider 时,Spark 会同步三个统一字段:
| imageProvider | 写回的 mediaProvider |
|---|---|
openai / openai-compatible | openai-compatible |
apimart / xai / bailian / custom | 原值 |
seeddance / seedance / volcengine | volcengine-ark |
gemini / google | google-generative-ai |
其它(如 zhipu、openrouter) | custom 兜底 |
mediaApiType 取 imageApiType 的归一值;mediaCapabilities 保证至少包含 image.generate。运行时解析统一媒体配置时,遇到未知 mediaProvider 也会兜底成 openai-compatible。
7. 常见问题排查
| 现象 | 排查 |
|---|---|
| 会话里没有图片生成工具 | 先确认是否已有可用的统一媒体 Provider(有的话只会出现 spark_media,这是预期行为)。若两套都没有,逐条对照第 5 节的四个条件。 |
| 「测试连接」失败,但图片能生成 | 正常。测试连接发的是文本 ping(OpenAI 兼容 /chat/completions),纯生图渠道通常没有 chat 端点。 |
xAI 报 Argument not supported: size | 说明 size 被透传了。走 spark_image 时会自动改写为 aspect_ratio;如果你自己拼 HTTP 请求,请只传 aspect_ratio + resolution。 |
| 异步渠道报「没有图片也没有 task id」 | 渠道返回体结构与预期不符。代码按 task_id / taskId / job_id / jobId / request_id / requestId / id 顺序找任务号;都没有就只能报错。 |
| 图片生成成功但界面没有预览 | Agent 需要在成功后调用 mcp__spark_files__present_files 把本地文件交给应用。 |
| 想要图片编辑 / 变体 / 多图合成 | spark_image 只做文生图,配置统一媒体 Provider 后用 spark_media 的 generate_image / edit_image。 |
速查表
- 图片 API 源
- openai / apimart / openrouter / gemini / seeddance / bailian / zhipu / xai / custom(注意是 seeddance)
- 调用方式
- sync / async / auto(缺省 sync)
- 内建生图预设
- 9 条:openai-images、apimart-images、xai-imagine-image、bailian-images、volcengine-seedream-image、google-gemini-images、midjourney-gateway、minimax-image、tencent-tokenhub-image
- spark_image 工具
- mcp__spark_image__generate_image(参数:prompt、size、n 1-4、filename、extraJson)
- spark_image 产物目录
- <workspace>/.spark-artifacts/images
- 超时
- 同步 10 分钟;异步轮询间隔 5 秒
- imageProvider → mediaProvider
- openai→openai-compatible · seeddance/seedance/volcengine→volcengine-ark · gemini/google→google-generative-ai · 其它→custom
常见问题
展开 5 条常见问题
- 图片生成到底走 spark_image 还是 spark_media?
只要能解析出可用的统一媒体配置,就只注入 spark_media(图片也走它);spark_image 仅在统一媒体栈解析为空时作为兜底注入,避免两个工具争抢模型选择。
- 图片模型和文本模型有什么区别?
modelType=image 时会多写 imageProvider 与 imageApiType 两个字段,并把它们同步到 mediaProvider / mediaApiType / mediaCapabilities(至少含 image.generate)。非图片模型会清掉图片专属字段。
- 生成的图存在哪里?
spark_image 是 <workspace>/.spark-artifacts/images;spark_media 是 <workspace>/.spark-artifacts/media/images。落盘目录会被写进仓库本地忽略。
- 可以同时挂多个图片 Provider 吗?
可以。spark_media 会把所有可用 Provider 聚合起来,用 model 参数路由;但走旧的 spark_image 时只会取第一条满足条件的 Provider(数据库自然顺序,通常是创建顺序),且不检查健康状态。
- 为什么「测试连接」通不过?
测试连接发的是文本协议 ping(/chat/completions 或 Anthropic messages),不校验图片端点。纯生图渠道没有 chat 端点会失败,这不代表图片功能不可用。