1. 为什么 Midscene 跑一次要等这么久Midscene 是字节 Web Infra 团队开源的 AI 驱动 UI 自动化 SDK它能用自然语言直接操作 Web、Android、iOS 页面适合做端到端回归、跨端巡检和可视化用例生成。但很多人第一次把它接进 CI 就会发现一条十几步的用例跑完要一两分钟其中绝大部分时间不是在点按钮而是在等模型返回。我按官方回放报告里的耗时视图拆过几次单次aiAction的链路大致是截图采集 50–200ms、上下文组装 10–50ms、模型推理 1–10s、结果解析 10–50ms、真实操作执行 50–500ms。模型推理这一段通常占总耗时的 70%–90%所以“压降运行耗时”本质上就是两件事减少每次请求携带的上下文体积以及减少模型被调用的次数。这篇就围绕这两个变量展开模型上下文大小截图尺寸、DOM 辅助数据、历史对话和 Prompt 缩减把规划型指令拆成原子操作。我会给出可直接复制的config.toml/settings.json配置骨架以及通过 TaoToken 统一 Key 接入的示例最后用调整前后的耗时对比动作来验证效果。目标很明确不牺牲任务成功率的前提下把单次运行时间压下来。2. 前置准备用 TaoToken 统一模型通道Midscene 的模型调用走 OpenAI 兼容协议所以只要有一个兼容的base_url和api_key就能跑。我习惯把模型通道统一到 TaoToken好处是 Key 只维护一份切换模型时不用改一堆环境变量回放报告里的 Token 消耗也方便横向对比。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个地址不加 UTM 参数。先去控制台建一个 Key路径是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。如果你只是想先验证某个视觉模型在 Midscene 里的定位效果可以直接在模型对话页试 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 不用先写代码。Midscene v1.0 之后环境变量名从OPENAI_API_KEY改成了MODEL_API_KEY这点很容易踩坑。下面是最小可用的环境变量骨架# .env —— Midscene 模型通道配置 MODEL_API_KEYsk-你的TaoTokenKey OPENAI_BASE_URLhttps://taotoken.net/api MIDSCENE_MODEL_NAMEqwen3-vl-plus MIDSCENE_CACHE1注意OPENAI_BASE_URL末尾不要带/v1Midscene 内部会自己拼接路径带了会出现 404 或路径重复。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各模型对应的MIDSCENE_MODEL_NAME取值。选模型时优先挑原生支持视觉定位的 VL 模型比如 Qwen3-VL 系列它们直接从截图返回坐标比走 DOM 标记的通用模型省一大截上下文。3. 可复制配置骨架config.toml 与 settings.jsonMidscene 的配置分两层一层是 Agent 初始化时的运行时参数截图缩放、缓存策略、模型分工另一层是项目级的config.toml/settings.json用来固化这些参数避免每次改代码。下面这份骨架是我在多个项目里收敛出来的直接改数值就能用。3.1 config.toml 骨架# config.toml —— Midscene 性能相关配置骨架 [midscene] # 截图缩放因子0.5 是性能与精度的平衡点 screenshot_shrink_factor 0.5 # 缓存策略开发用 read-write生产用 read-only cache_strategy read-write cache_id midscene-perf-cache # 单步超时复杂任务给足 timeout 240000 [midscene.model] # 规划意图用规划能力强的模型 planning_model qwen3-vl-plus # 交互定位用视觉定位准、成本低的模型 interaction_model qwen3-vl-plus # 洞察提取用语义理解好的模型 insight_model qwen3-vl-plus [midscene.prompt] # 关闭 DOM 辅助纯视觉路线 dom_included false # 裁剪历史对话避免上下文膨胀 history_window 3 # 默认不开启深度思考复杂控件单独开 deep_think false3.2 settings.json 骨架{ midscene: { screenshotShrinkFactor: 0.5, cache: { strategy: read-write, id: midscene-perf-cache }, timeout: 240000, modelConfig: { planningModel: qwen3-vl-plus, interactionModel: qwen3-vl-plus, insightModel: qwen3-vl-plus }, prompt: { domIncluded: false, historyWindow: 3, deepThink: false } } }3.3 参数对照表参数作用性能影响建议值screenshotShrinkFactor截图缩放比例0.5 时 Token 降约 60%0.5小字体场景 0.75cache.strategy缓存读写策略命中时耗时降 40%–50%生产 read-onlydomIncluded是否附带 DOM 数据关闭后 Token 降约 80%falsehistoryWindow保留的历史对话轮数越小上下文越短3deepThink深度思考模式开启增加 60%–80% 耗时默认 falsetimeout单步超时过小会误判失败240000注意screenshotShrinkFactor缩小后坐标会自动等比映射回原始尺寸不影响定位精度但需要识别极小字体的场景别低于 0.75。4. Prompt 缩减把规划型指令拆成原子操作配置只是骨架真正决定模型调用次数的是 Prompt 的写法。Midscene 有两类 API一类是aiAction()/ai()这种规划执行型AI 要拆解任务、规划步骤、定位、断言一次调用可能触发多轮模型推理另一类是aiTap()/aiInput()/aiKeyboardPress()这种即时操作型AI 只负责定位元素操作由 Playwright 直接执行单次模型调用就够。4.1 慢写法与快写法对比// 慢AI 需要规划整个流程多次模型调用 await agent.ai(在搜索框中输入 Headphones按下回车键); // 快拆成原子操作每次只做定位 await agent.aiInput(Headphones, 搜索框); await agent.aiKeyboardPress(Enter);实测下来把一条aiAction拆成 2–3 个即时操作单步耗时能降 40%–60%因为省掉了规划推理那一轮。代价是代码行数变多但换来的是稳定性和速度。4.2 Prompt 精简四原则第一加修饰词消除歧义。不要写“点击提交”写“表单底部的蓝色提交按钮”模型一次就能定位不用反复试。第二能传 XPath 就传 XPath。await aiTap(用户名输入框, { xpath: //input[idusername] })模型直接按 XPath 找省掉视觉推理。第三用结构化 API 替代复杂指令。aiBoolean、aiString、aiNumber比模糊的断言更省 Token也更准。第四裁剪历史上下文。多步骤操作里把historyWindow调到 3避免对话历史无限膨胀。4.3 上下文冻结静态页面连续操作静态页面或连续操作场景每次操作都重新截图是浪费。用freezePageContext()把当前 UI 快照缓存下来后续操作直接复用// 冻结页面上下文后续多次操作复用同一截图 await agent.freezePageContext(); await agent.aiInput(张三, 姓名输入框); await agent.aiInput(13800138000, 手机号输入框); await agent.aiTap(下一步按钮); await agent.unfreezePageContext();在连续 5 次操作的场景里冻结上下文能省掉 4 次截图采集和上传静态页面上节省 30%–50% 时间。5. 验证请求与耗时对比配置改完不能凭感觉得用数据验证。Midscene 的回放报告里有 Token 消耗视图这是最直接的观测点。5.1 验证脚本import { PlaywrightAgent } from midscene/web/playwright; const agent new PlaywrightAgent(page, { screenshotShrinkFactor: 0.5, cache: { strategy: read-write, id: perf-test }, modelConfig: { planningModel: qwen3-vl-plus, interactionModel: qwen3-vl-plus, }, }); const start Date.now(); await agent.aiInput(Headphones, 搜索框); await agent.aiKeyboardPress(Enter); await agent.aiTap(第一个商品卡片); const cost Date.now() - start; console.log(本次运行耗时: ${cost}ms);5.2 调整前后对比指标默认配置优化后变化单步平均耗时3.2s1.4s-56%单次运行 Token4184980-77%截图尺寸1920×1080960×540-75%模型调用次数53-40%任务成功率92%94%2%成功率没降反升原因是原子操作比规划型指令更稳定模型不用“猜”整个流程。这也是为什么我建议优先做 Prompt 缩减而不是一味调模型参数。5.3 缓存预热生产环境用read-only模式前先用write-only预热一份黄金缓存const agent new PlaywrightAgent(page, { cache: { strategy: write-only, id: golden-cache }, }); await agent.aiTap(登录按钮); await agent.flushCache(); // 确认无误后写入之后 CI 里切回read-only命中缓存时耗时能再降 40%–50%。6. 本篇常见错排查报错一MODEL_API_KEY is not set。v1.0 之后变量名改了检查.env里是不是还写着OPENAI_API_KEY。两个都写上最稳。报错二请求 404 或路径重复。OPENAI_BASE_URL末尾带了/v1去掉即可。TaoToken 的基址就是https://taotoken.net/api。报错三截图缩放后点不准。检查screenshotShrinkFactor是不是低于 0.5小字体场景建议 0.75 以上。另外确认模型是原生视觉定位的 VL 模型通用模型在缩放后定位精度会掉。报错四缓存命中率低。缓存是基于 Prompt 字符串精确匹配的Prompt 里带了时间戳、随机数或动态 ID 就会一直不命中。把动态部分抽出来保持 Prompt 稳定。报错五deepThink开了反而更慢。深度思考会调用两次模型增加 60%–80% 耗时只在复杂控件定位时单独开别全局打开。报错六超时误判。复杂任务把timeout调到 240000ms 以上默认值在慢模型上容易触发超时。7. 下一步把优化固化进团队规范性能优化不是一次性动作。建议按这个路径推进先用默认配置跑通核心场景再通过回放报告识别耗时瓶颈然后按“缓存 → 截图缩放 → 原子操作 → 模型切换”的优先级逐一尝试最后把验证有效的参数写进团队的自动化测试规范。如果你还在选模型或调 Prompt 阶段可以先去模型对话页快速试 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 确认视觉定位效果后再落到代码里。长期跑编码和 Agent 任务的话Coding Plan 页 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 有更细的额度说明。Key 和接入细节分别看 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 和 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。最后留一个我踩过的坑别一上来就把所有优化全开先开缓存和截图缩放这两个零成本的跑一轮对比数据再决定要不要拆 Prompt。一次只改一个变量耗时曲线才看得清。