mcpField
G
generate-images-with-firebase-ai

generate-images-with-firebase-ai

github.com/evanca创意与媒体663100 次安装3 周前更新
代码仓库

关于 generate-images-with-firebase-ai

generate-images-with-firebase-ai 是使用 Dart 和 Firebase AI Logic 构建图像生成功能的技能,说明请求配置、文本与图像混合响应、尺寸处理、提示词和测试。

主要功能

  1. 01配置图像生成请求
  2. 02解析交错的文本与图像部分
  3. 03诊断空响应或受阻响应
  4. 04准备输入照片并调整输出尺寸
  5. 05将生成图像与文本数据结合

适用场景

  1. 01在 Dart 应用中加入 Gemini 图像生成
  2. 02从响应中提取图像字节和文本
  3. 03排查账单、App Check 或模态配置问题
  4. 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.md
prompting.md
setup.md
testing.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 介绍了各个测试层及其覆盖不到的内容。

外部文档

这里的内容都是摘要,可能会逐渐过时。遇到重要细节时,请以原始资料为准。

参考文件

  • references/setup.md:Firebase 控制台路径、提供方选择、账单,以及 Web App Check 调试令牌陷阱
  • references/models.md:模型 ID、宽高比和尺寸枚举、Gemini 与 Imagen
  • references/prompting.md:图片提示词中实际会出现的问题,以及解决这些问题的措辞
  • references/testing.md:单元测试、golden、e2e 和 eval 层,以及各层覆盖不到的内容

评论

支持 Markdown