Files
gemini-skill/references/gemini-flow.md

6.8 KiB
Raw Permalink Blame History

Gemini Flow

1) 登录校验

最小校验项:

  • 页面存在可输入提问的输入框
  • 右上角有用户头像或账户入口

若未登录:提示用户先在 Daemon 托管的浏览器中手动登录 Google 账号Daemon 未运行时会自动后台拉起)。

2) 模型策略

优先级:

  1. Gemini 3.1 Pro
  2. 当前页面可见的次优 Pro/Advanced 模型

若切换失败,保留默认并告知用户。

3) 按钮状态检测

.send-button-container 内的按钮通过 aria-label 区分三种状态:

  • 空闲idlearia-label 为麦克风相关,按钮 disabled输入框为空。
  • 可发送readyaria-label 为"发送"/"Send",输入框有内容。
  • 生成中loadingaria-label 为"停止"/"Stop"Gemini 正在输出。

使用方式:

  • GeminiOps.getStatus() → 返回 {status: 'idle'|'ready'|'loading', label, disabled}
  • GeminiOps.pollStatus() → 返回 {status, label, pageVisible, ts}毫秒级返回,供调用端分段轮询

CDP 保活轮询(重要)

禁止在页面内做长 Promise 等待(旧版 waitForComplete 已移除)。

正确做法:调用端每 8~10 秒 evaluate 一次 GeminiOps.pollStatus(),自行累计耗时并判断超时。 这确保 CDP WebSocket 通道持续有消息流量,避免被网关判定空闲而断开连接。

4) 生图结果获取

⚠️ 严禁使用浏览器截图screenshot获取生成图片。

  • 默认流程:通过 src URL 右键另存为Save Image As保存到本地再发送给用户。
  • 高清流程:仅当用户明确要求高清/原图时,才调用 downloadLatestImage() 点击原图下载按钮。

Gemini 一次只生成一张图片,流程上只关心最新生成的那张,历史图片不做处理。

调用 GeminiOps.getLatestImage() 获取最新一张生成图片。

DOM 结构

<div class="image-container ...">
  <button class="image-button ...">
    <img class="image loaded" src="https://lh3.googleusercontent.com/..." alt="AI 生成">
  </button>
  <div class="button-icon-wrapper">
    <mat-icon fonticon="download" data-mat-icon-name="download" class="button-icon ..."></mat-icon>
  </div>
</div>

图片定位

  • 选择器:img.image.loaded
  • image class = Gemini 的图片元素
  • loaded class = 图片已渲染完成(未加载完的不会有此 class
  • 两个 class 同时存在才算有效图片
  • DOM 中可能存在多张历史图片,取最后一个即为最新生成

下载按钮定位

  • img 向上找到最近的 .image-container 祖先容器
  • 在容器内查找 mat-icon[fonticon="download"](即下载原图按钮)
  • getLatestImage() 返回 hasDownloadBtn 字段标识是否有下载按钮

API

所有操作函数的返回值都包含 debug 字段,记录该次调用每一步的日志(含时间戳、步骤名、成功/失败、上下文详情),方便排查问题和改进策略。

  • GeminiOps.getLatestImage() → 获取最新一张图片信息
{
  "ok": true,
  "src": "https://lh3.googleusercontent.com/...",
  "alt": "AI 生成",
  "width": 1024,
  "height": 1024,
  "hasDownloadBtn": true,
  "debug": [
    {"ts": 1710000000000, "fn": "getLatestImage", "step": "start", "ok": true},
    {"ts": 1710000000001, "fn": "getLatestImage", "step": "query_imgs", "ok": true, "detail": {"totalFound": 1}},
    {"ts": 1710000000002, "fn": "getLatestImage", "step": "picked_latest", "ok": true, "detail": {"index": 0, "src": "https://lh3.google..."}},
    {"ts": 1710000000003, "fn": "getLatestImage", "step": "find_container", "ok": true},
    {"ts": 1710000000004, "fn": "getLatestImage", "step": "find_download_btn", "ok": true}
  ]
}
  • GeminiOps.downloadLatestImage() → 点击最新图片的下载原图按钮(仅用户要求高清时)
{"ok": true, "src": "https://lh3.googleusercontent.com/...", "debug": [...]}
  • GeminiOps.extractImageBase64()默认图片获取方式,从 DOM 直接提取 Base64
{
  "ok": true,
  "dataUrl": "data:image/png;base64,iVBORw0KGgo...",
  "width": 1024,
  "height": 1024,
  "method": "canvas",
  "debug": [...]
}

提取策略(自动选择,无需调用端干预):

  1. Canvas 提取(优先):将已渲染的 <img> 绘制到虚拟 Canvas同步导出 toDataURL('image/png')。零网络请求,毫秒级完成。method 返回 "canvas"
  2. Fetch fallback:若 Canvas 因跨域 tainted 而报错,自动回退到页面内 fetch(img.src)blobFileReader.readAsDataURL()method 返回 "fetch"

⚠️ 该函数返回 Promise。CDP 调用时必须设置 awaitPromise: true

// CDP Runtime.evaluate 示例
{ expression: "GeminiOps.extractImageBase64()", awaitPromise: true, returnByValue: true }

调用端拿到 dataUrl 后,去掉 data:image/png;base64, 前缀,解码为二进制存为 .png 文件即可。

  • GeminiOps.probe() / click() / fillPrompt() / pollStatus() → 同样携带 debug 字段

  • GeminiOps.getDebugLog() → 获取完整累积日志(不清空),用于事后排查

{"log": [...], "count": 15}

debug 日志格式

每条日志条目:

字段 类型 说明
ts number 毫秒级时间戳
fn string 函数名,如 clickgetLatestImage
step string 步骤名,如 startfind_containerclicked
ok boolean 该步骤是否成功
detail object? 可选,上下文信息(匹配的选择器、找到的元素数量等)

调用端应将 debug 数组回传给用户,便于分析定位失败原因和优化选择器策略。


### 图片交付流程(重要)

**默认流程Base64 提取):**
1. 调用 `GeminiOps.getLatestImage()` 确认图片已渲染完成(`ok: true`
2. 调用 `GeminiOps.extractImageBase64()` 提取图片数据(需 `awaitPromise: true`
3. 去掉 `dataUrl` 的 `data:image/png;base64,` 前缀,解码为二进制,保存为 `.png` 文件
4. 将本地图片文件发送给用户

**高清流程(仅用户要求时):**
1. 调用 `GeminiOps.getLatestImage()` 确认图片已渲染完成
2. 调用 `GeminiOps.downloadLatestImage()` 点击原图下载按钮
3. 将下载到本地的高清原图文件发送给用户

> **严禁**在任何环节使用浏览器截图screenshot代替保存图片。

### 回退

- `ok === false` → 页面可能还在渲染,等几秒再调一次
- 连续两次失败 → 做 snapshot 排查页面状态snapshot 仅用于排查,不用于交付图片)

## 5) 用户提示文案(建议)

- 开始生图:
  - `已收到,正在用 Gemini 给你绘图中 🎨`
- 生成中超时:
  - `还在渲染中,我继续盯着,马上回你。`
- 完成:
  - `画好了,给你发图啦~`