关于 generate-images-with-firebase-ai
generate-images-with-firebase-ai 是使用 Dart 和 Firebase AI Logic 构建图像生成功能的技能,说明请求配置、文本与图像混合响应、尺寸处理、提示词和测试。
主要功能
- 01配置图像生成请求
- 02解析交错的文本与图像部分
- 03诊断空响应或受阻响应
- 04准备输入照片并调整输出尺寸
- 05将生成图像与文本数据结合
适用场景
- 01在 Dart 应用中加入 Gemini 图像生成
- 02从响应中提取图像字节和文本
- 03排查账单、App Check 或模态配置问题
- 04测试响应解析并改进提示词
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ evanca/ flutter-ai-rules --skill generate-images-with-firebase-ai安装后在 Claude Code 中输入/generate-images-with-firebase-ai确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/models.mdprompting.mdsetup.mdtesting.md详细介绍
使用 Firebase AI Logic 生成图像
Gemini 图像模型会在一次调用中返回交错的文本和图像部分。响应是一个需要遍历的序列,而不是一段可直接读取的字符串。
请求返回空结果通常是配置问题,而不是代码错误,因此第 1 节介绍了导致此问题的三个设置。
1. 阻止首次调用的三个因素
首次调用返回错误、空响应或 403,几乎总是由以下因素之一导致,而不是 Dart 的问题。在调试代码之前,先排除这些因素。
结算。 图像生成没有免费层级。对于 Spark 方案的项目,图像模型的 generate_content_free_tier_requests 限额为 limit: 0,因此首次请求就会因配额不足而失败,即使此前一次请求都没发过。文本模型在 Spark 方案下可以正常使用,因此“我的其他 Gemini 调用能用”并不能证明什么。升级到 Blaze,然后核实当前限额,不要只相信此处的说明:
gcloud services quota list --service=generativelanguage.googleapis.com --consumer=projects/YOUR_PROJECT_ID
App Check。 如果项目启用了 App Check,Firebase AI Logic 就会强制执行 App Check。否则,任何人都可以从已发布的客户端中提取配置并访问该端点,而配置本来就是公开的。任何可从你无法控制的设备访问的内容都需要 App Check。调试版本使用调试提供程序进行验证,发布版本则使用真实提供程序。Web 上有个特殊陷阱,会让你耗费半天时间,详见 references/setup.md。
responseModalities。 如果不设置它,模型就没有返回图像的权限,因此你得到的会是描述它本会绘制什么的文本。像下面的调用一样,同时设置这两种模态。
2. 可用的最简调用
final model = FirebaseAI.googleAI().generativeModel(
model: 'gemini-3.1-flash-image',
generationConfig: GenerationConfig(
responseModalities: [
ResponseModalities.text,
ResponseModalities.image,
],
imageConfig: ImageConfig(
aspectRatio: ImageAspectRatio.landscape16x9,
imageSize: ImageSize.size2K,
),
),
);
final response = await model.generateContent([
Content.multi([
TextPart(prompt),
InlineDataPart('image/jpeg', selfieBytes), // omit for text-to-image
]),
]);
FirebaseAI.googleAI() 使用 Gemini Developer API。优先选择它:它不需要单独配置 GCP 资源,而且免费层级支持文本。FirebaseAI.vertexAI() 无论使用哪种模型都需要 Blaze,但可以获得 GCP 侧的控制,因此项目已经使用 Vertex 时再选它,而不是默认使用它。
不要向 googleAI() 传入 appCheck: 或 auth:。在当前的 firebase_ai 中,这两个参数已弃用;实例会自行从 FirebaseApp 获取它们。
模型 ID、宽高比和尺寸枚举,以及何时 Imagen 比 Gemini 更合适,请参阅 references/models.md。
3. 读取响应
便捷访问器 .text 无法获取完整序列,因此要直接遍历各个部分。根据部分类型进行模式匹配,这样即使 SDK 添加了新的部分类型,switch 也能保持正确:
Uint8List? image;
final buffer = StringBuffer();
for (final part in candidate.content.parts) {
switch (part) {
case InlineDataPart(:final bytes):
image ??= bytes; // first image wins
case TextPart(:final text):
buffer.write(text);
default:
break;
}
}
将响应解析逻辑放在单独的纯函数中,并让它接收一个 Candidate。Candidate、Content、TextPart 和 InlineDataPart 都可以公开构造,因此无需 Firebase 或测试替身,也能使用真实的 SDK 类型测试该函数。这是此技术栈中唯一一个单元测试真正能够触及的接缝。
未返回图像时
空结果会表现为一条空白错误,看起来像客户端问题,导致人们去调试系统中错误的一侧。响应中包含原因,所以应将其报告出来。按以下顺序检查:
| 检查项 | 位置 | 含义 |
|---|---|---|
response.promptFeedback?.blockReason | 候选项之前 | 输入被拒绝。也要读取 blockReasonMessage。 |
response.candidates 为空 | 不适用 | 完全没有生成内容。 |
candidate.finishReason | 候选项上 | 模型已停止:原因可能是安全限制、复述检测或令牌数限制。finishMessage 会提供更多详情。 |
| 没有图像,但有文本 | 遍历各部分后 | 模型用文字作答,而不是绘制图像。通常是提示词的问题。 |
| 所有内容均为空,也没有原因 | 不适用 | 如实说明,并让用户重试。这种情况偶尔会发生。 |
即使某些提示词通常能稳定返回文本和图像,也偶尔会只返回图像,完全没有文本。如果你要求图像附带文本,就应将文本缺失视为正常情况并降级处理,而不是抛出错误。
4. 发送用户照片
发送前先缩小图片。InlineDataPart.toJson() 会在主 isolate 上同步对字节进行 base64 编码,因此在构建请求时,一张全尺寸手机照片会让 UI 卡顿数秒。Gemini 无论如何都会缩小大图片,因此你付出的细节最终会被丢弃。应在选取时缩放,而不是事后处理:
final file = await picker.pickImage(
source: ImageSource.gallery,
maxWidth: 1280,
maxHeight: 1280,
imageQuality: 85,
);
应根据主体来设置尺寸上限,而不是照搬习惯值。长边为 1280px 足以清晰呈现人脸或单个人物;如果需要保留细腻纹理、让原图中的文字清晰可读,或者需要模型通读宽幅场景,则要使用更大的尺寸。mimeType 应与选取器实际返回的格式一致。
5. 调整结果尺寸
如果你的宽高比属于支持的枚举值,请使用 ImageConfig,因为它是真正的约束。在提示词文本中要求某个宽高比,只是建议,模型经常会忽略。如果需要枚举中没有的宽高比,例如 2:1,请测量返回的图片并据此布局:
final descriptor = await ui.ImageDescriptor.encoded(
await ui.ImmutableBuffer.fromUint8List(bytes),
);
final size = ui.Size(descriptor.width.toDouble(), descriptor.height.toDouble());
descriptor.dispose();
对测量操作进行封装,使其失败时返回 null,而不是抛出异常。无法测量的图片仍然值得展示。
6. 一次调用同时获取文本和图片
你经常会需要有关模型刚刚绘制的图片的机器可读数据,例如说明文字或模型为其编造的名称。一旦这些数据变成像素,你的应用就无法读取,因此应在同一次调用中要求以文本形式返回,并在提示词中协调这两部分:“绘制在图片中的标题必须与 JSON 逐字一致。”
稳妥地解析文本。要求模型返回带围栏的 ```json 块时,它在同一个会话中可能返回带围栏的 JSON、裸 JSON、带说明文字的 JSON,也可能什么都不返回。先尝试解析围栏内容,再回退到第一个完整配对的 {...},并在失败时返回 null,而不是抛出异常。
7. 编写提示词
这里的失败并非代码问题,单元测试也无法触及。反复出现的问题都有具体的解决方法,值得你在编写第一个提示词前了解:占位词被原样绘制进作品、源照片中的服装在换装后仍然保留、同一文本在不同位置拼写不一致,以及不同面板没有共用背景。请阅读 references/prompting.md。
8. 确定测试内容
边界就在你的代码边界处。单元测试只能保护你对响应的处理,无法覆盖其后的内容,因此测试套件全绿并不能说明应用能生成优质图片。references/testing.md 介绍了各个测试层及其覆盖不到的内容。
外部文档
这里的内容都是摘要,可能会逐渐过时。遇到重要细节时,请以原始资料为准。
- Firebase AI Logic 文档,包括快速入门和使用 Gemini 生成图片
firebase_ai,Dart SDK。要确认 API 问题,查看其源代码最快:~/.pub-cache/hosted/pub.dev/firebase_ai-*/lib/src/image_picker,提供第 4 节中的pickImage调用。它是可替换的第三方选择。- Patrol,由 LeanCode 开发的第三方 E2E 框架,
references/testing.md中有相关介绍
参考文件
references/setup.md:Firebase 控制台路径、提供方选择、账单,以及 Web App Check 调试令牌陷阱references/models.md:模型 ID、宽高比和尺寸枚举、Gemini 与 Imagenreferences/prompting.md:图片提示词中实际会出现的问题,以及解决这些问题的措辞references/testing.md:单元测试、golden、e2e 和 eval 层,以及各层覆盖不到的内容