用途
该技能用于通过 Playwright MCP 驱动 FiftyOne App,适用于插件/算子验证、演示或录屏,以及针对 FiftyOne App 的端到端 UI 自动化。FiftyOne App 是一个由 Python 会话通过 WebSocket 支撑的 React/MUI 单页应用,资料指出大多数坑点都来自这一生命周期。
主要能力与关键规则
资料强调的第一条规则:在调用 ctx.ops.reload_dataset() 的算子之后,绝不要执行 browser_navigate 或 location.reload()。症状是 FiftyOne 服务器静默退出(curl localhost:5151 返回 HTTP 000,ps 中无 PID),磁盘上的数据集完好,崩溃发生在会话层。原因是导航在远程会话 reload 中途关闭了活动 WebSocket,导致 session.wait() 退出。资料给出的修复按成本排序:
- 方案 A(最佳):通过触发文件 IPC 调用 session.refresh()。session.refresh() 与 dataset.reload() 都不会关闭 WebSocket,但需要活动句柄,因此要把 session.wait() 替换为监视循环。随附的 scripts/launch_app.py 正是这样实现的:克隆源数据集、以 remote=True 启动,然后轮询触发文件,一旦出现就执行 reload() + refresh()。示例启动命令:
nohup python scripts/launch_app.py
--source quickstart --clone verify_clone --port 5151 \
/tmp/fo_app.log 2>&1 &
循环核心如下:
while True: if os.path.exists(TRIGGER): os.remove(TRIGGER) clone.reload() # refresh this process's view of MongoDB session.refresh() # push refresh event over existing WebSocket time.sleep(0.5)
自动化侧可执行 touch /tmp/fo_refresh.trigger(脚本默认 --trigger 路径),刷新约 0.5 秒内生效,无需 UI 往返。资料还提到更健壮的 IPC 变体:Unix socket/命名管道、Jupyter 内核,或在后台 shell 中保持会话的 python -i。
- 方案 B:通过反引号面板使用内置的 “Reload the dataset” 算子,无需改动启动器。资料给出步骤:按 ` 打开面板,用 React 受控输入模式搜索 “reload the dataset”,再 browser_click 结果。该方式较慢(约 2 秒),但无需 Python 侧管线。资料提醒:“Reload samples from the dataset” 是另一个更弱的算子,它不会刷新侧边栏标签索引。
资料还说明为何不用兄弟进程的 dataset.reload():python -c 只刷新该进程自己的副本,启动器的会话与浏览器 WebSocket 不受影响,仍需回到方案 A 调用 session.refresh()。
使用方式
启动器模式方面,资料建议:始终使用 remote=True,以避免每次导航都多开一个操作系统浏览器标签页,并由 Playwright 单独连接 http://localhost:5151;使用带预删除保护的非持久克隆(随附启动器已实现),这类克隆在非正常崩溃时不会自动删除,会残留在 MongoDB 中直到下次运行的守卫将其移除;以分离方式运行:nohup python scripts/launch_app.py --source <dataset> --clone <clone-name> --port 5151 > /tmp/fo_app.log 2>&1 &(nohup 并不能防止规则 1 的崩溃,只是隔离 shell 信号噪声);阶段之间做健康检查:curl -s -o /dev/null -w "%{http_code}" localhost:5151 加 ps -p $PID 以及早发现静默崩溃。若没有窗口自行打开,健康检查通过后应告知用户 App URL,使其可在自己的浏览器中作为被动查看者打开(自动化运行时只看不点)。Playwright MCP 浏览器本身是否可见,取决于 MCP 服务器启动时的设置(@playwright/mcp 除非以 --headless 启动,否则为有头模式),该技能在两种模式下均可工作,不要在会话中途更改。
MUI/React 注意事项:React 受控输入不能通过 input.value = "foo" 更新状态,需使用原生原型 setter 并派发 input 事件,适用于算子表单中的所有文本/数字/文本框字段:
const setter = Object.getOwnPropertyDescriptor( window.HTMLInputElement.prototype, 'value' ).set; setter.call(inputEl, newValue); inputEl.dispatchEvent(new Event('input', { bubbles: true }));
MUI 下拉框是 combobox 而非 <select>,browser_select_option 会失败并提示 “Element is not a <select>”,选项仅在列表框打开时才存在于 DOM。做法是:从新快照取 ref 并 browser_click 打开 combobox,等待约 1 秒让列表框挂载,再用 browser_evaluate 查找 [role="option"] 并点击目标项。打开弹出层要用真实 browser_click,综合事件不一定能被 MUI 状态机接受。对 SVG 图标、MUI chip 关闭按钮、部分选项行,需派发真实 MouseEvent。MUI 对话框滚动时外层容器不滚动,需向上寻找可滚动祖先并设置 scrollTop。
元素定位方面,优先使用 data-cy 属性,FiftyOne 为其 Cypress 测试提供了大量稳定的 data-cy 钩子,例如 sidebar-field-container-tags、sidebar-field-arrow-enabled-tags、checkbox-tags、categorical-filter-tags、selector-sidebar-search-tags、flashlight-section、looker。文本内容匹配时应过滤到叶子元素。
侧边栏标签过滤的 UX 较隐晦:要通过 caret(sidebar-field-arrow-enabled-tags)展开 sample tags,而不是点 checkbox(那会切换整组);点击过滤器输入框(selector-sidebar-search-tags)打开自动补全;点击目标标签行使其成为 chip 并过滤网格,再点 chip 清除。标签行是动态自动补全,不是预渲染的复选框。
算子变更后的陈旧 UI 状态:Execute 添加样本/标签后,网格计数滞后、侧边栏标签索引陈旧,需用 reload_dataset 内置算子修复(规则 1),reload_samples 不够,它不刷新标签索引。
模态查看器导航:在卡片上点击 “Click to expand” 的 ref(真实 browser_click,而非在画布上综合点击,后者往往打不开模态),ArrowRight/ArrowLeft 循环切换,Escape 关闭,URL 会带 ?id=<sample_id>。
节奏控制:任何对话框关闭后等待 1–2 秒(状态同步防抖);在 dynamic=True 表单输入后约 300–500 毫秒再检查 Warnings/Notices;录屏级节奏时 ArrowRight 之间间隔 1–2 秒。
录屏与清理
录屏建议:在易崩溃边界拆分长演示,分别录制 Execute 前与 Done 后的片段,在编辑器中拼接;各次拍摄使用相同算子参数以便拼接;在启动器中预配置确定性状态(克隆、保存视图、默认值),使镜头时间用于创作性操作;在 browser_navigate 前执行 browser_resize(width=2560, height=1440),因为默认 1440×900 在 Retina 上较粗糙。
清理(每次尝试之间运行):终止陈旧启动器(pgrep -f launch_app.py | xargs -r kill; sleep 1);删除非持久克隆;仅删除本次运行产生的近期孤立输出文件,绝不要只用宽泛的名称通配;清理 Playwright 快照临时文件。资料特别提醒清理安全:裸 find -name "$OUTPUT_GLOB" 会匹配无关会话的文件,始终用 -mmin 或会话专用前缀过滤。
限制与注意事项
资料给出的检查清单包括:启动器使用 remote=True + 触发文件监视(或在可接受规则 1 时用 session.wait());克隆为非持久、确定性命名、带预删除守卫;确定性保存视图/标签由启动器创建而非演示时创建;首次导航前视口至少 2560×1440;算子 Execute 后绝不 browser_navigate/location.reload();Execute 后通过 touch <trigger-file> 或反引号面板的 reload_dataset 刷新;优先 data-cy 选择器,叶子文本匹配为后备;MUI 下拉框真实点击打开、对 [role="option"] 用 JS 点击选择;React 输入用原型 setter + input 事件;对话框滚动走到可滚动祖先并设置 scrollTop;清理脚本按 -mmin/前缀限定范围,绝不用裸名称通配;阶段之间做健康检查(curl + ps)。资料未提供该技能的收费、价格、平台或效果信息。