用 agent-browser 验证 AIRI 显示模型导入:Live2D / VRM / MMD 跨端自动化测试实战
2026/9/10 19:26:01 网站建设 项目流程

用 agent-browser 验证 AIRI 显示模型导入:Live2D / VRM / MMD 跨端自动化测试实战

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

导读

AIRI(stage-tamagotchi Electron 桌面端、stage-web 网页端、stage-pocket 移动端)都支持用户通过模型选择器导入自定义显示模型(Live2D ZIP、VRM、MMD ZIP/PMX/PMD)。本指南讲解如何使用agent-browser(CDP 驱动的浏览器自动化 CLI)配合$use-agent-browser-with-input-file技能,完整跑通「绕过 onboarding → 打开模型设置 → 上传本地模型文件 → 通过 UI 选择导入的模型 → 验证舞台渲染」的端到端自动化测试流程。读完本文,你将掌握三种运行时(Electron / Web / 移动端 Web 布局)的启动与定位方法、各格式的导入契约与选择器、五条渲染后置条件,以及上传、持久化、渲染三个独立检查点的判定原则。

背景:为什么用 agent-browser 测 AIRI 的模型导入

AIRI 的显示模型导入链路涉及**原生文件对话框、VueUseuseFileDialog动态创建的隐藏 file input、IndexedDB 持久化、多渲染器(Live2D / VRM / MMD)**四层复杂逻辑,手工测试难以覆盖格式差异与竞态条件。agent-browser是专为 AI Agent 设计的浏览器自动化 CLI:通过 Chrome/Chromium CDP 连接,以无障碍树快照(accessibility-tree snapshot)和紧凑的@eN元素引用驱动交互,不依赖 Playwright/Puppeteer,并内置会话、认证、状态持久化与视频录制能力。它在仓库中的入口 stub 位于 .agents/skills/agent-browser/SKILL.md,安装方式为npm i -g agent-browser && agent-browser install

仓库的 AGENTS.md 明确约定了技能组合:涉及 HTML input 上传的 Web/Electron 流程先调用$use-agent-browser-with-input-file;涉及 AIRI 显示模型导入与渲染验证的测试则调用$use-agent-browser-for-airi,后者在文件上传机制上复用前者,并额外封装 AIRI 专属的路由、状态准备、格式行为和渲染器验证。

工具链初始化约定

执行浏览器命令前必须先拉取与当前安装版本严格匹配的技能内容,避免过时指令:

agent-browser skills get core --full

目标为 Electron 时额外执行:

agent-browser skills get electron --full

同时调用$agent-browser$use-agent-browser-with-input-file(以及 Electron 场景下的$agent-browser-electron)三个技能,分工如下:agent-browser 负责浏览器自动化原语;input-file 技能负责文件输入发现、临时插桩、上传命令与通用上传后验证;Airi 技能负责 AIRI 路由、状态与格式行为。其 Agent 注册元数据见 .agents/skills/use-agent-browser-for-airi/agents/openai.yaml。

运行时选择:三种被测表面

导入测试可以覆盖三个运行时,先从主文档选择入口进入对应的运行时参考指南:

  • Electron stage-tamagotchi:读 .agents/skills/use-agent-browser-for-airi/references/electron.md,验证桌面端真正的多窗口目标切换与 CDP 连接。
  • stage-web:读 .agents/skills/use-agent-browser-for-airi/references/web.md,验证纯浏览器端的导入与渲染。
  • stage-pocket 与移动端覆盖:读 .agents/skills/use-agent-browser-for-airi/references/mobile.md,验证共享 Vue UI 在紧凑布局下的表现与原生端边界。

三个运行时共享同一份模型导入契约(见下一节),差异只在于启动方式、目标定位与收尾清理。

模型导入契约:格式、菜单项与选择器

AIRI 当前通过 VueUseuseFileDialog创建每种格式的文件输入,并保持其与 DOM 分离(detached)——这在 model-selector.vue 中可以得到源码印证:live2dDialogvrmDialogmmdDialog分别以useFileDialog({ accept: '.zip' })useFileDialog({ accept: '.vrm' })useFileDialog({ accept: '.zip,.pmx,.pmd' })创建。由于输入节点不挂在文档树上,agent-browser 无法用 CSS 选择器直接选中,因此三种格式统一采用detached-input 方法(详见 .agents/skills/use-agent-browser-with-input-file/references/detached-input.md),且必须在点击格式菜单项之前安装桥接。

格式菜单项输入选择器格式专属后置结果
Live2DLive2Dinput[data-agent-browser-upload][accept=".zip"]检查校验报告,仅当报告允许导入时点击Confirm
VRMVRMinput[data-agent-browser-upload][accept=".vrm"]等待导入卡片出现;不期望出现 Live2D 校验确认。
MMDMMDinput[data-agent-browser-upload][accept=".zip,.pmx,.pmd"]将归档/模型存储与纹理、物理、动态导入、渲染分别验证。

源码中 model-selector.vue 显示 MMD 的格式判定逻辑:.pmd归为DisplayModelFormat.PMD.pmx.zip归为PMXZip,其余扩展名直接返回;渲染器在加载时通过魔数(magic bytes)区分 zip 与裸模型。Live2D 导入前会先执行validateLive2DZip(model-selector.vue),status === 'VALID'且无错误时自动确认,否则弹出Live2DReportModal校验报告等待人工/自动化确认。

测试夹具的来源与合规

  • Live2D / VRM:解析$use-vishot-for-airi技能文档(.agents/skills/use-vishot-for-airi/SKILL.md)中记录的本地夹具路径。
  • MMD:使用包含模型及其纹理目录的已授权归档(ZIP 内需同时含模型与纹理目录)。注意不要在测试或源码注释中编码贡献者的私有夹具名。

detached-input 桥接原理

核心问题:应用点击控件时通过input.click()触发文件对话框,而该 input 从未append到文档。agent-browser 无法对游离节点使用选择器,因此需要在浏览器测试会话内临时暴露它。在点击格式菜单项之前,先注入桥接:

agent-browser eval 'window.__agentBrowserOriginalFileInputClick ??= HTMLInputElement.prototype.click; HTMLInputElement.prototype.click = function () { if (this.type === "file") { this.dataset.agentBrowserUpload = "true"; document.body.append(this); return; } return window.__agentBrowserOriginalFileInputClick.call(this); }; true'

这段代码的行为要点:

  • 保留普通(非 file 类型)点击的原生行为,只拦截type === "file"的输入;
  • 阻止原生选择器弹出,给应用创建的精确 input 打上data-agent-browser-upload标记;
  • 将同一个节点附加到文档,保留其既有change监听器(VueUseuseFileDialogonChange回调仍能触发)。

随后检查被捕获的输入并上传(注意:必须使用绝对路径):

agent-browser eval 'Array.from(document.querySelectorAll("input[data-agent-browser-upload]"), input => ({ accept: input.accept, multiple: input.multiple, connected: input.isConnected }))' agent-browser upload 'input[data-agent-browser-upload][accept=".zip"]' '/absolute/path/to/archive.zip'

若桥接捕获了多个输入,务必用acceptmultiple或其他应用自有属性收窄选择器,不要向所有标记输入上传。在应用的change处理器消费完文件后,恢复原型并只移除本桥接创建的节点:

agent-browser eval 'if (window.__agentBrowserOriginalFileInputClick) { HTMLInputElement.prototype.click = window.__agentBrowserOriginalFileInputClick; delete window.__agentBrowserOriginalFileInputClick; } document.querySelectorAll("input[data-agent-browser-upload]").forEach(input => input.remove()); true'

当页面会继续保持打开时,这一步(恢复桥接)是导入流程第 7 步的强制要求。桥接属于可丢弃测试会话的运行时插桩,不是应用代码,绝不能提交进仓库。

准备 AIRI 状态:绕过 onboarding

为了保证测试的可复现性,使用全新的浏览器会话或专用 Electron user-data 目录,并在打开模型设置路由之前标记 onboarding 已完成:

agent-browser eval 'localStorage.setItem("onboarding/completed", "true"); localStorage.setItem("onboarding/skipped", "false"); location.reload(); true'

这两个 key 的实际消费方可以在 display-model-from-file.ts 中印证:Vishot 场景的markOnboardingCompleted同样写入onboarding/completed=trueonboarding/skipped=false。随后打开/settings/models,点击Select model,再次快照。

导入模型:七步操作序列

  1. 点击第一个Options for Display Models按钮(源码中该aria-label出现在 model-selector.vue 的导入下拉触发器上,点击后展开Live2D / VRM / Spine / MMD / Tachie菜单项)。
  2. 重新快照,点击精确的 Live2D / VRM / MMD 菜单项。
  3. 按 detached-input 方法,用上表匹配的选择器上传文件。
  4. 重新快照。若 Live2D 校验弹出报告,检查报告内容,仅当报告允许导入时点击Confirm
  5. 等待精确的 basename出现在导入模型卡片上,且该卡片拥有自己的Pick按钮——detached 输入控件上显示的文件名不足为凭(它只证明赋值成功)。
  6. 点击该导入卡片的Pick按钮。
  7. 页面保持打开时,在 change 处理器完成后恢复 detached-input 桥接。

每一步交互后都应重新快照,因为元素引用(@eN)在 DOM 变化后会失效。Electron 场景下 Live2D 校验报告可能打开模态框,若可见的Confirm按钮被可访问性点击路径中的遮挡对话框盖住,先重新快照并解析遮挡对话框;仅在诊断兜底时才使用 DOM.click()直接点击精确的可见 Confirm 按钮,并记录「普通指针自动化未能到达它」这一事实。

验证结果:五条必查后置条件

导入并点击Pick后,全部满足以下条件才算通过:

  1. localStorage.getItem("settings/stage/model")display-model-开头——即选中了刚导入的自定义模型而非预设。
  2. 最终舞台展示的是导入的模型,而不是预设模型、空白画布、加载状态或导入对话框。
  3. 截图在视觉上与上传的格式和夹具匹配。
  4. agent-browser errors中无模型加载失败。
  5. agent-browser console中无相关的 Live2D、VRM、MMD、ZIP、纹理、物理或动态导入失败。

两个重要警示:

  • 不要从 AIRI 头部卡片/档案下拉框推断当前显示模型——它标识的是活动角色卡片,可能保留不同的标签。
  • 上传、持久化、渲染是三个独立检查点:可见的导入卡片只能证明「存储成功」,不能证明渲染器能消费该文件。正如 verify-upload.md 强调的:把「导入卡片 + 空白舞台」上报为「存储通过、渲染失败」,而不是一次成功的上传测试。

localStorage 与持久化契约的源码印证

第 1 条条件的含义在 stage-model.ts 中一目了然:模型选择以useLocalStorageManualReset<string>('settings/stage/model', 'preset-live2d-1', ...)持久化,默认值为预设preset-live2d-1。而自定义模型的 key 生成于 display-models.ts 的addDisplayModelid: \display-model-${nanoid()}`,随后通过localforage.setItem写入 IndexedDB(数据库localforage、storekeyvaluepairs)。加载时 [loadDisplayModelsFromIndexedDB](https://link.gitcode.com/i/593997ca449ec07fb5bc5589fd8ab89b) 遍历所有display-model-前缀的 key 并入列表——这就是后置条件 1 判断前缀的依据。同时源码注释明确要求保持await localforage.setItem不丢事件,否则updateStageModel可能在 IndexedDB 写入完成前读取新模型 id 并回退到默认模型([display-models.ts](https://link.gitcode.com/i/aea660d0a89447b83660c5eeea1cf63a)、[stage-model.ts](https://link.gitcode.com/i/43dce2db4eede78c2316156f8639cc4e))。updateStageModel还会根据模型格式解析渲染器:Live2D ZIP →live2d、VRM →vrm、PMX/PMD →mmd`(stage-model.ts)。

Electron 运行时:stage-tamagotchi 实操

以已知 CDP 端口启动

优先使用专用 user-data 目录,避免导入的夹具与 onboarding 状态污染贡献者日常 profile。开发模式:

APP_REMOTE_DEBUG=true \ APP_REMOTE_DEBUG_PORT=9250 \ APP_REMOTE_DEBUG_NO_OPEN=true \ pnpm dev:tamagotchi

根目录 package.json 中dev:tamagotchi定义为pnpm -rF @proj-airi/stage-tamagotchi run dev。环境变量的底层实现在 apps/stage-tamagotchi/src/main/app/debugger.ts:APP_REMOTE_DEBUG=true时在 app ready 之前通过app.commandLine.appendSwitch('remote-debugging-port', ...)开启 Electron 的 CDP 端点,端口默认 9222,并校验端口为 0–65535 的整数;APP_REMOTE_DEBUG_NO_OPEN=true阻止自动打开系统浏览器(见该文件的openDebugger)。

若使用构建产物,则直接以 Electron 可执行文件启动apps/stage-tamagotchi/out/main/index.js,并显式传入新建临时目录下的--user-data-dir

选择渲染目标

Electron 会暴露多个 target,必须先枚举原始目标再连接:

curl -sS http://127.0.0.1:9250/json/list agent-browser --session airi-electron --cdp 9250 tab

识别主/#/target,用其稳定的tNid 切换,校验 URL,设置 onboarding 存储,并关闭已存在的/onboardingtarget。展开主控件并打开设置:

agent-browser --session airi-electron --cdp 9250 snapshot -i # 将 @eN 替换为当前快照中 Expand 的引用 agent-browser --session airi-electron --cdp 9250 click @eN agent-browser --session airi-electron --cdp 9250 find role button click --name 'Open settings'

再次枚举 target,切换到新的/settingstarget 并路由到模型设置:

agent-browser --session airi-electron --cdp 9250 eval 'location.hash = "/settings/models"; true' agent-browser --session airi-electron --cdp 9250 snapshot -i

每条命令都必须带上--session airi-electron --cdp 9250,然后遵循 SKILL.md 的共享导入契约。点击Pick后切回主 target,等待其 canvas 出现并截图,检查 errors 与 console 输出;需要在格式间做隔离时,用全新的临时 profile 重跑。

stage-web 运行时实操

在已知 origin 启动 stage-web,每种格式使用一个全新的 agent-browser 会话:

pnpm -F @proj-airi/stage-web dev --host 127.0.0.1 --port 5173 agent-browser --session airi-web-live2d open http://127.0.0.1:5173/settings/models

设置 onboarding 存储、reload、等待Select model,遵循共享的 detached-input 导入契约。点击Pick后:

agent-browser eval 'localStorage.getItem("settings/stage/model")' agent-browser open http://127.0.0.1:5173/ agent-browser wait 'canvas' agent-browser screenshot agent-browser errors agent-browser console

两个细节:其一,当应用暴露渲染器就绪信号时应显式等待——canvas 存在于 DOM 不等于渲染成功,它可能因加载器失败而保持空白;其二,MMD 要专门检查 console 中的纹理、归档、物理与 Vite 动态导入失败。收集完证据后关闭每个会话,全新会话可以避免从贡献者浏览器 profile 中误删无关的 IndexedDB 模型。

stage-pocket 与移动端覆盖实操

浏览器驱动的移动布局

运行 stage-pocket 的 Web 表面(专用 HTTPS 端口):

pnpm -F @proj-airi/stage-pocket dev:web --host 127.0.0.1 --port 5174 agent-browser --session airi-pocket-web --ignore-https-errors open https://127.0.0.1:5174/settings/models

dev:web对应 apps/stage-pocket/package.json 的"dev:web": "vite"。操作设置页时,若控件存在于 DOM 但视觉上位于紧凑布局之外,可临时切到桌面宽度视口:

agent-browser set viewport 768 1024

遵循共享导入契约。点击Pick后,切换到目标手机视口再打开舞台并采集证据:

agent-browser set viewport 390 844 agent-browser open https://127.0.0.1:5174/ agent-browser wait 'canvas' agent-browser screenshot

覆盖边界:这一步只验证 stage-pocket 的共享 Vue UI 与移动端响应式渲染器,不验证原生文件选择器、Android WebView、WKWebView、文件系统权限或 Capacitor 桥。这类能力必须走原生端测试。

Android 模拟器(原生覆盖)

仅当adb devices列出了已启动的模拟器、且存在可检查 WebView 或驱动原生选择器的自动化桥时才运行原生 Android 测试。agent-browser 需要可达的 Chromium CDP 端点——不要因为应用能启动就声称已覆盖。使用根目录 package.json 的pnpm dev:pocket:android(对应 apps/stage-pocket/package.json 的cap-vite -- android)运行项目运行时;通过原生文档选择器上传每种格式,返回 AIRI 选中导入卡片,验证最终渲染器与 Logcat。当adb或兼容的 WebView 调试端点不可用时,如实记录为「not runnable」。

iOS Simulator

使用根目录 package.json 的pnpm dev:pocket:ios(对应 apps/stage-pocket/package.json 的cap-vite -- ios)配合已启动的 Simulator,做手动或 XCUITest/Appium 覆盖。WKWebView 暴露的是 Safari Web Inspector 而非 Chromium CDP,agent-browser 无法直接自动化。不要用 agent-browser 的移动视口结果替代原生 iOS 覆盖;浏览器驱动的 stage-pocket 结果与原生 Simulator 结果必须作为独立的两行分别报告。

原生文件选择器的通用边界

依据 native-file-chooser.md:agent-browser 的upload命令只能把文件赋给 HTML input,不能驱动任意操作系统选择器窗口。因此先判断选择器归谁所有——Electron 场景下若应用最终在渲染器创建 HTML input 就回到 attached/detached 方法;真正的 Electron 主进程或 OS 对话框走平台自动化机制后再回 agent-browser 做渲染验证;Android/iOS 原生选择器走模拟器自动化并单独报告。若选择器无法被安全控制,把原生选择检查点记录为 not runnable,不得用键入路径、合成文件名或视口模拟冒充已覆盖。

上传验证的四层检查点

无论哪种运行时,都要验证「最深的可观察结果」,而不是在agent-browser upload成功退出时停止:

  1. 赋值(Assignment):目标输入收到了预期的文件。
  2. 处理(Processing):应用产生了预期的文件名、预览、校验结果、进度状态或导入记录。
  3. 持久化(Persistence):应用在 store / IndexedDB / 后续路由中保留了新选择(AIRI 中是settings/stage/model指向display-model-*,且模型实体写入 localforage)。
  4. 消费(Consumption):目标查看器/编辑器/渲染器实际使用了上传的内容(AIRI 中是舞台 canvas 渲染出该模型)。

赋值后重新快照,等待应用专属的后置条件;最终检查点执行agent-browser errorsagent-browser console。采集能区分「上传文件」与「默认或缓存数据」的证据,报告第一个失败的检查点与相关错误,而不要把整个流程折叠成一个笼统的通过/失败。

关闭与会话收尾

测试结束后:

  1. 关闭所有 agent-browser 会话;
  2. 停止为测试启动的进程;
  3. 只删除专用的临时 Electron user-data 目录(不要动贡献者正常 profile);
  4. 按「每个格式 × 每个运行时」分别上报passed / failed / not runnable,附上第一个失败检查点与相关 console 错误。

与 Vishot 场景的衔接

当需要确定性视觉证据时,可把 agent-browser 验证稳定的流程编码为 Playwright 驱动的 Vishot 场景。仓库提供了现成的可复用场景 display-model-from-file.ts,通过环境变量注入模型路径与格式:

pnpm build:tamagotchi AIRI_DISPLAY_MODEL_FORMAT=live2d \ AIRI_DISPLAY_MODEL_PATH='/absolute/path/to/model.zip' \ pnpm exec vishot capture \ ./packages/scenarios-stage-tamagotchi-electron/src/scenarios/display-model-from-file.ts \ --target electron \ --app-entrypoint ./apps/stage-tamagotchi/out/main/index.js \ --cwd . \ --settle-ms 2500 \ --output-dir ./.vishot/display-model/live2d

AIRI_DISPLAY_MODEL_FORMAT仅接受live2dvrmmmd三值,AIRI_DISPLAY_MODEL_PATH必须为绝对路径(display-model-from-file.ts)。该场景的契约与本指南完全同构:标记 onboarding 完成并关闭已打开的 onboarding 渲染器 → 通过窗口助手打开 Settings → Models → 在选择 Live2D/VRM/MMD 前等待 Playwright 文件选择器 → 通过新的 IndexedDB 模型 key 关联导入 → 选中导入卡片并等待主窗口选择同步 → 捕获主舞台并在结束后移除导入夹具(其cleanup会恢复 localStorage 并删除 IndexedDB 中该display-model-*记录,见 display-model-from-file.ts)。当截图仍显示预设模型、导入对话框、空白舞台或加载状态时,该捕获视为失败,必须逐一检查生成的每张图片。

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询