ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Omi Desktop Experience Harness:macOS 桌面端无光标体验测试框架实战指南

Omi Desktop Experience Harness:macOS 桌面端无光标体验测试框架实战指南 Omi Desktop Experience HarnessmacOS 桌面端无光标体验测试框架实战指南【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend导读Omi 桌面端macOS在迭代过程中需要持续验证界面语义导航、状态断言、动画时序、AX 无障碍契约、空闲功耗等体验指标又不能像传统 UI 测试那样依赖坐标点击与光标移动。desktop/macos/scripts/omi-harness正是为此设计的本地体验测试框架它通过桌面自动化桥接Desktop Automation Bridge驱动命名 App 的语义操作采集行为、延迟、日志、追踪与截图并以可复现的 YAML 流程文件组织断言。读完本文你将掌握该框架的三级通道bridge / visual / ui、全部十种 typed step 的写法与底层实现、Chat-first 本地 fixture 流程的编排方式以及如何用summarize/compare/cleanup管理本地运行产物。一、框架定位为体验检查而生的无光标自动化Omi 桌面体验 Harness 的运行主体是 Python 脚本 desktop/macos/scripts/omi-harness它面向本地自动化桥接发起请求。所谓本地自动化桥接指的是 App 内暴露的 HTTP 自动化服务默认端口47777可通过OMI_AUTOMATION_PORT环境变量覆盖见 omi-harness 源码 L28Harness 通过它完成语义导航与状态读取。框架的核心理念是保持无光标cursor-free所有导航与动作都走桥接的语义接口/navigate、/action不移动系统光标截图由 App 侧自身导出/visual/export不调用screencapture无障碍AX断言通过agent-swift的语义press动作完成同样不依赖坐标。Harness 对每个运行会记录cursor_safety字段coordinate_clicks: 0与external_screenshot_tool: False并在 summary.md 生成逻辑 L1211 中回显确保任何一次运行都不会偷偷引入坐标点击或外部截图工具。二、三级通道Lanes从纯语义到最终可访问性Harness 通过--lane参数选择运行通道源码 L1462默认是bridgeLane能力范围典型用途bridge语义导航 / 动作 / 状态断言默认通道纯无光标语义检查保持 cursor-freevisualbridge全部能力 App 侧 PNG 导出需要截图证据的视觉回归不使用screencapture、不移动光标ui在visual基础上追加agent-swiftAX 检查最终可访问性检查仅用于真实用户可见的界面元素ui通道要节约使用它面向的是菜单、Sheet、权限弹窗、引导onboarding与系统集成这类真实用户可感知的控件。2.1 每次运行的 UI 呈现管理无论哪个通道每次运行都会查询set_automation_ui_presentation动作把 bridge / visual 工作停泊在quiet模式App 不抢占桌面焦点运行结束后恢复运行前的呈现模式——即使某一步失败也会在finally块中执行恢复源码 restore_automation_ui_presentation L227AX 快照与语义press全程保持quiet且无光标。唯一的例外是ax.activate步骤显式请求action: click时此时会在右下角短暂切到interactive模式完成点击然后立刻回到quiet源码 run_agent_swift_in_interactive_mode L245-L256。若 App 是旧版本、不支持该动作Harness 会记录一条 setup warning 并沿用旧行为继续运行源码 prepare_automation_ui_presentation L195-L225。三、快速上手启动本地栈并运行一个流程3.1 准备本地开发环境在仓库根目录启动本地开发栈并启动一个命名桌面 App 指向 localhost 服务make dev-up make desktop-run-local DESKTOP_APP_NAMEomi-harness-testmake dev-up调用 scripts/dev-harness/dev-up.shdesktop-run-local则通过 scripts/dev-harness/desktop-run-local.sh 以命名 App 名与指定用户启动本地实例见 Makefile L104-L118。3.2 在另一个终端运行体验 Harness进入desktop/macos目录后v1 流程是旧版兼容格式需要显式 opt-incd desktop/macos python3 scripts/omi-harness run e2e/flows/harness-smoke.yaml \ --allow-legacy-flow-version --lane visual python3 scripts/omi-harness summarize latest python3 scripts/omi-harness latest python3 scripts/omi-harness compare before-run after-run --markdown python3 scripts/omi-harness cleanup --keep 10harness-smoke.yamldesktop/macos/e2e/flows/harness-smoke.yaml是官方冒烟流程依次把自动化 UI 置为quiet、导航到 Dashboard 与 Memories、执行refresh_all_data、断言/action追踪为 200、断言日志无DesktopAutomationBridge: failed或unsupported_route并导出两张 PNG。3.3 进程 CPU/RSS 采样若流程包含power.sample步骤需要传入唯一的命令行匹配串来定位命名 App 进程python3 scripts/omi-harness run e2e/flows/ask-omi-chat-power-benchmark.yaml \ --allow-legacy-flow-version \ --process-match /Applications/omi-harness-test.app/Contents/MacOS/Omi Computer也可以不传参数、改用环境变量OMI_HARNESS_PROCESS_MATCH源码 L1467-L1471。匹配串必须唯一resolve_sample_pid会通过pgrep -fl解析进程若匹配到多个会直接报错同时会过滤 Harness 自身及其祖先 shell避免脚本命令行中恰好包含匹配串而误命中源码 L474-L545。3.4ui通道的 AX 断言ui通道的 AX 步骤要求传入命名非生产 bundle idpython3 scripts/omi-harness run e2e/flows/harness-smoke.yaml \ --allow-legacy-flow-version --lane ui --bundle-id com.omi.omi-harness-test源码 L624-L644 强制校验bundle id 必须以com.omi.omi-开头NAMED_NON_PRODUCTION_BUNDLE_PREFIXHarness 明确拒绝生产版与未命名 bundle id。四、运行产物与数据安全边界每次运行写入desktop/macos/.harness/runs/该目录已被 gitignore。产物包括summary.md—— 人类可读的 PASS/FAIL 汇总与 Visual Evidence 清单metrics.json—— 结构化指标schema version、环境、cursor_safety、逐步骤耗时、日志错误计数initial-state.json/final-state.json/traces.json—— 前后状态与最近追踪steps/—— 每步的状态、动作响应、追踪、日志、PNG 截图与功耗采样文件logs.txt—— 从运行起点截取的 App 日志尾部。安全警示原文档明确强调产物中可能包含真实用户数据——日志、状态、截图、AX 树、记忆与聊天内容。未经人工审查不得将某个 run 目录附加或发布到任何渠道。cleanup命令会校验删除路径必须位于 Harness 运行存储之内防止误删源码 L1443-L1457。五、Typed Steps十种步骤类型详解流程文件是 YAMLsteps数组中的每一步通过顶层键声明类型源码 execute_step 分发逻辑见 L895-L1082步骤类型职责底层调用bridge.navigate语义导航到目标界面POST /navigatebridge.action触发桥接语义动作POST /actionvisual.exportApp 侧导出当前窗口 PNGPOST /visual/exportvisual.action_sequence并发投递动作并连续抓帧动画/action/visual/export并发state.expect断言当前状态快照GET /statetrace.expect断言最近 HTTP 追踪GET /traces/recentlog.expect断言日志包含/排除文本本地日志尾部ax.activate通过稳定 AX 标识符激活控件agent-swiftax.expect断言 AX 树可见性 / 焦点顺序 / VoiceOver 标签agent-swift snapshotpower.sample采样进程 CPU/RSS 并断言上限ps5.1 语义动作与状态等待bridge / state / trace / logbridge.action支持expect子键对动作响应做断言如result.detail.mode: quiet支持min/max/exists/contains四类数值与存在性操作符expectation_mismatch L346-L391对非字典期望值则保持严格类型相等。log.expect的contains/absent支持字符串或字符串列表assert_log L669-L684。trace.expect的latest: true是只断言刚执行的路径的选择器——按trace.pathtrace.method选出最新一条再套用其余谓词避免状态等待产生的/state记账请求干扰结果assert_trace L687-L718。5.2 wait步骤间的稳定等待wait:在桥接步骤之后用于等待状态或追踪稳定后再执行下一步。以harness-smoke.yaml中的导航为例- name: Navigate to Dashboard bridge.navigate: target: dashboard activateApp: false wait: state.chatFirstRoute: chat需要注意不要 wait 在state.selectedTabIndex上——chat-first 快照始终把它报告为nil该等待永远不会成立。stability_window_seconds可以让某个状态谓词在下一步派发前持续成立它计入已有的timeout_seconds上限且一旦谓词不再匹配就会重置计时wait_for_state L408-L429。若流程确实需要固定停顿可在bridge.navigatepayload、/conversation/openpayload 或动作参数中传settleMs:。另外桥接导航只有在目标界面挂载完成之后才会返回chat-first shell 还要求精确的 route-visible 确认因此数据、追踪等导航后的就绪检查都应交给wait:。5.3 保持断言可测量原文档反复强调一个边界Harness 不评判截图质量。自动化断言只应覆盖它能直接测量的客观事实——状态、追踪、日志、时序与 AX 文本。summary.md中列出的 PNG 需要由人来打开人工评判布局、打磨度、裁切、空状态与视觉回归。5.4 AX 无障碍契约ui 通道ax.activate通过稳定无障碍标识符激活 SwiftUI/AppKit 控件默认使用语义 AXpress动作绝不使用坐标。仅当控件不暴露press动作时才保留action: click兜底该兜底会在右下角短暂显示 App见源码 activate_ax L844-L859。ax.expect支持三种断言identifiers_visible—— 交互式 AX 树中必须可见的稳定标识符列表focus_order—— 按稳定标识符断言键盘焦点顺序实现为子序列匹配见 assert_ax L805-L823voiceover_labels—— 以稳定标识符为键、断言精确的 VoiceOver 标签。官方示例原文档完整示例- name: Verify chat-first sidebar accessibility contract ax.expect: identifiers_visible: - chat-first-sidebar-chat - chat-first-sidebar-goals focus_order: - chat-first-sidebar-chat - chat-first-sidebar-goals voiceover_labels: chat-first-sidebar-chat: Chat chat-first-sidebar-goals: Goals - name: Open Goals by stable accessibility identifier ax.activate: identifier: chat-first-sidebar-goals wait: state.chatFirstRoute: goals使用规范AX 断言只用于静态、用户可见的控件期望标签绝不能包含用户内容源码 L830-L840 还刻意把标签不匹配的失败只暴露为稳定标识符避免把可能的用户数据打印到终端稳定标识符须匹配[A-Za-z0-9][A-Za-z0-9_.-]*L32在新增 AX 步骤前先查看./scripts/omi-ctl actions的动作描述含surfaces、safety、sideEffects、examples字段当preferSemantic为 true 时应优先选用匹配的语义bridge.action——尤其对只读探针、本地捕获与确定性视觉夹具。5.5 visual.action_sequence捕获快速动画动画类验证用visual.action_sequence它并发投递桥接动作并抓帧避免在普通动作响应返回之前动画就已结束、错过关键中间帧。官方示例- name: Capture Ask Omi Open Animation visual.action_sequence: action: open_ask_omi params: wait: false target: main frames: 8 interval_ms: 16源码 L951-L1017 使用ThreadPoolExecutor单线程执行动作请求主线程按interval_ms间隔循环导出frame-00.png至frame-07.png最后把动作响应与逐帧清单写入sequence.json清单capture_mode: concurrent。5.6 power.sample轻量空闲功耗采样power.sample在桥接步骤把目标 UI 打开后使用ps采样进程在运行摘要中记录平均/峰值 CPU 与 RSS并支持断言上限。官方示例- name: Sample Open Chat Idle Power power.sample: warmup_ms: 500 duration_ms: 8000 interval_ms: 250 max_avg_cpu_percent: 5参数源码 sample_process_power L569-L617参数默认值说明warmup_ms0开始采样前的预热等待duration_ms5000下限 250采样总时长interval_ms250下限 100采样间隔max_avg_cpu_percent无平均 CPU 上限断言max_peak_cpu_percent无峰值 CPU 上限断言max_avg_rss_mb无平均 RSSMB上限断言ask-omi-chat-power-benchmark.yamldesktop/macos/e2e/flows/ask-omi-chat-power-benchmark.yaml是现成的功耗基准流程打开 Ask Omi 聊天、采样 8 秒空闲功耗、再断言聊天仍保持打开。原文档的定位说明这只是廉价可重复的近似手段不能替代 Instruments 或powermetrics但足够让 Agent 在优化空闲 UI 功耗时反复运行。六、Chat-first 本地 fixture 流程chat-first-cohesive.yaml、chat-first-question-deferral.yaml、chat-first-cold-start.yaml、chat-first-capability-isolation.yaml是手动流程依赖服务端持有的本地 fixture——桌面桥接绝不自行伪造eligibility、目标、任务、抓取、提示、问题选项或 rollout 状态。6.1 完整启动序列PROVIDER_MODEoffline make dev-up make seed-memory-scenario SCENARIOhappy_path make chat-first-e2e-fixture CHAT_FIRST_E2E_ACTIONprepare CHAT_FIRST_E2E_CASEenabled make desktop-run-local DESKTOP_APP_NAMEomi-chat-first-e2e DESKTOP_USERomi-local-emulator-chat-first-enabled-v1seed-memory-scenario调用 scripts/dev-harness/seed-memory-scenario.py 播种指定记忆场景chat-first-e2e-fixture调用 scripts/dev-harness/chat-first-e2e-fixture.sh 准备服务端 fixtureMakefile L117-L118。使用CHAT_FIRST_E2E_CASEquestion可跑 question-deferral 流程它从 fixture 拥有的完整rich cold-start receipt之后开始因此真正由服务端物化的 question 始终是 Chat 尾部可操作的。安全边界fixture 助手通过 Firebase Auth 模拟器凭据认证并用模拟器分配的 UID 对照 Harness manifest 校验它的读回与时钟推进响应只包含有界的状态与计数——绝不允许在这些流程里添加桌面能力覆盖或打印 fixture 内容。6.2 capability-isolation 的两启动矩阵chat-first-capability-isolation.yamldesktop/macos/e2e/flows/chat-first-capability-isolation.yaml是双启动矩阵对disabled_control与unreachable_control两个 case各自用独立的omi-*bundle 和自动化端口准备并启动后再运行同一个单 case 流程。流程头部写明了三组命令对例如make chat-first-e2e-fixture CHAT_FIRST_E2E_ACTIONprepare CHAT_FIRST_E2E_CASEdisabled_control OMI_AUTOMATION_PORT47852 make desktop-run-local DESKTOP_APP_NAMEomi-chat-first-e2e-disabled-control DESKTOP_USERomi-local-emulator-chat-first-disabled-v1 python3 desktop/macos/scripts/omi-harness run desktop/macos/e2e/flows/chat-first-capability-isolation.yaml --lane bridge --port 47852 --bundle-id com.omi.omi-chat-first-e2e-disabled-control关键限制原文档明确强调一次 Harness 运行只能占用一个自动化端口、对应一个命名 App不能在流程中途切换 bundle。因此不要把一次串行运行当作三个账户能力的证明。每个运行都断言真正的legacyshell 仍能打开 Chat。同时no-rich-block、no-materialization、no-proactive-work 等无泄漏契约由独立的领域测试负责验证该流程的 header 注释给出了职责归属见 chat-first-capability-isolation.yaml L22-L30desktop/macos/agent/tests/chat-first-capability-projection.test.ts证明动态工具缺失且tools/list字节与 legacy 相等backend/tests/unit/test_chat_first_blocks.py证明 disabled 时 block 校验不做实体工作backend/tests/unit/test_chat_first_proactive_router.py与test_chat_first_proactive_engine.py证明 disabled 时物化与主动工作不做任何 feature-store / provider / metric 工作。因此不要为了重复这些断言而再添加桥接 fixture 或客户端能力覆盖。七、命令参考与回归对比omi-harness的子命令源码 main L1480-L1518子命令说明run flow运行一个 typed flow打印 run 目录退出码 0全通过baseline flowrun的别名用于 before/after 对比repeat flow --count N顺序重复运行并汇总耗时分布best / median / worst 每步中位数表compare before after [--markdown]对比两个 run 目录的总耗时、日志错误数、逐步骤 delta / 加速比summarize [run]打印 run 的 summary.md默认 latestlatest打印最新 run 目录路径cleanup --keep N清理旧 run 产物仅限 Harness 存储目录内compare --markdown会输出两段 Markdown 表格指标表total_ms、delta、speedup、日志错误数变化与逐步骤对比表before / after / delta / change / speedup / ok。配合baseline子命令可以在代码改动前后分别建立基线运行用compare量化响应性与功耗是否回归。八、最佳实践小结先语义后 AX能用bridge.action覆盖的尤其preferSemantic为 true 的只读探针、本地捕获、确定性视觉夹具不要轻易落到ui通道的 AX 步骤上。用wait:承接导航后的就绪需要固定停顿才用settleMs:需要持续成立的谓词用stability_window_seconds。不要 waitstate.selectedTabIndexchat-first 快照恒为 nil。保持断言可测量状态、追踪、日志、时序、AX 文本是 Harness 能直接证明的事实截图质量交给人工判断。妥善处理产物隐私.harness/runs/可能含真实用户数据发布前必须审查cleanup只清理 Harness 自己的存储。fixture 流程保持服务端持有桥接不伪造 fixture 内容capability 断言的契约由对应领域测试拥有。相关延伸阅读CORE_E2E.md 与 SKILL.md 记录了桌面端更完整的端到端测试编排思路desktop/macos/e2e/flows/ 目录下 90 余个 YAML 流程覆盖了从首页、记忆、任务到功耗基准的绝大多数桌面体验场景可直接作为编写新流程的参考模板。【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表