跳到主要内容

数据截至 (上游 commit 2689884a6257)

精华、边界与横向对比

这一章讲什么: 前面六章是「它怎么做的」。这一章是「你该带走什么」「它在哪会崩」「它和别人比选了什么不同的路」。


1. 巧妙之处(可以直接抄走的七条)

1.1 用接口设计约束模型,而不是用 prompt

ActionEvent.to_prompt_dict(legacy/openadapt/models.py:517-520):只要这个动作有了元素描述,就把 mouse_x/y/dx/dy 从给模型看的字典里删掉。

模型看不到坐标,就不可能照抄坐标,只能在描述层面推理。这比在系统提示词里写「请不要输出坐标」可靠一个数量级——能删的字段就别靠嘴说。

1.2 把异常喂回下一次 prompt

三处都用了同一个模式:

场景位置
改写流程失败strategies/visual.py:114(@utils.retry_with_exceptions())
描述数量对不上掩膜数量strategies/visual.py:516-530
描述在新屏幕上匹配不到strategies/visual.py:232-234

exceptions 列表传进模板渲染,模型下一轮能看到自己上次错在哪。重试不只是重来,是带着错误信息重来。

1.3 录制期就把系统参数快照下来

Recording.double_click_interval_seconds / double_click_distance_pixels(models.py:56-57)。

「两次点击算不算双击」必须按录制那台机器的设置判定。归并逻辑优先读录制上的值,读不到才回退当前机器并打 warning(events.py:391-399)。

推广一句:任何依赖环境的判定阈值,都应该和数据一起存下来。

1.4 归并不删数据,只建父子树

make_parent_event(events.py:151)+ ActionEvent.parent_id 自引用外键。

收益在三个地方兑现:回放时对键盘事件递归播子事件(playback.py:97-100)、脱敏时只需处理顶层(scrub.py:189-192)、调试时能一路回溯到原始事件。

1.5 空间信息一起塞进 DTW 向量

browser.py:517-548:对齐浏览器 DOM 事件和操作系统事件时,不只用时间戳,而是构造 [时间, x, y, dx, dy] 五维向量做多维 DTW。

时钟会偏,坐标不会。 加一个正交维度,对齐的鲁棒性显著上升。

1.6 判定逻辑做成纯函数,就能穷举测试

check_release_health.py 把「采集状态」(collect_state,要联网)和「根据状态判定」(evaluate,:265,纯函数)彻底分开。于是 300 多行的 self_test() 可以完全离线跑遍所有状态组合,--dump-state 还能存下线上真实状态复现。

1.7 用 AST 检查那些永远不会被执行的 import

tests/test_import_integrity.py:懒加载的 import 藏在函数体里,普通测试跑不到。解法是不运行、直接解析 AST(抽象语法树,源码被解析成的树状结构),跨包比对符号是否存在,连关键字参数都比对(test_no_phantom_kwargs)。

任何用了大量惰性导入的项目都该抄这一条。


2. 边界与局限(诚实清单)

2.1 关于这个仓库本身

  • 产品核心不在这里。 编译器、确定性回放、效果验证、失败即停、受管修复,全部在 openadapt-flow。本仓库的 CLAUDE.mdREADME.md 都明确要求「不要在这个仓库实现第二个引擎」。凡是 README 里关于 VERIFIED/halt 的描述,在本克隆里只能看到声明,看不到实现
  • docs/architecture.md 已经过时。 它画的还是「meta-package + capture/ml/evals/viewer」的图,完全没有 openadapt-flow。仓库自己的 CLAUDE.mddocs/ 标注为「非规范的历史仓库文档」。

2.2 关于 legacy 代码

问题位置后果
交互式调试器留在生产路径strategies/base.py:112playback.py:110events.py:936adapters/prompt.py:37无人值守时挂死
时间戳乱序只记 log 不处理record.py:196-198下游所有归并逻辑的前提被破坏
描述匹配靠精确字符串相等strategies/visual.py:229-231模型措辞一变就找不到
重试循环无上限strategies/visual.py:223可能不退出
分割结果只在内存strategies/visual.py:59进程退出全丢,# TODO: store to db
相似片段无法区分strategies/visual.py:426表格类界面直接 raise ValueError
SoM(Set-of-Mark)适配器不可用adapters/som.py:87-90服务端压缩破坏了「颜色即掩膜」的假设
归并只跑一轮events.py:14(MAX_PROCESS_ITERS = 1)「反复直到收敛」的框架实际未生效
屏幕采集无节流record.py:707-710# TODO: throttle 仍在
脱敏默认关闭、仅英文、视频未实现config.py:183presidio.py:59privacy/base.py:90默认录制不脱敏
模块 import 有副作用record.py:68(截屏)、presidio.py:34-39(下模型)无头/离线环境炸
Python 版本已冻结docs/LEGACY_FREEZE.mdlegacy 只支持 3.10–3.11,不再更新

2.3 关于方法本身

OpenAdapt 的路线有一个结构性前提:必须先有人演示一遍。 它不解决「没演示过的新任务」。README 也把这条边界写在门口——用得上 API 的时候就用 API,只在界面绕不过去时才用它。


3. 横向对比:同货架的 computer-use 项目

分歧点只有一个:模型在执行路径上吗

模型介入程度 低 ├────[1]────┼────[2]────┤ 高

[1] OpenAdapt(今天)
演示 → 编译成可重复执行的程序
健康路径零模型调用
确定、可审计,代价是必须先有人演示一遍
[2] OpenAdapt(legacy)
演示 + 一句自然语言指令 → 模型改写整条动作序列
每步一次模型调用
灵活,但慢且不确定
[3] 通用 GUI agent
自然语言任务 → 模型每步决策
每步一到多次模型调用
最灵活、最慢、最不确定

同一个项目的两个时期,正好站在这条轴的两端。 这是 OpenAdapt 最有教学价值的地方——它自己走完了从「全交给模型」到「模型只用在编译期」的路。

和兄弟项目的取舍对照

项目任务从哪来定位元素靠什么执行期要模型吗
OpenAdapt(今天)人的一次演示编译期留下的结构/a11y/视觉/OCR 多路证据健康路径不要
OpenAdapt(legacy)演示 + 一句自然语言修改分割片段的自然语言描述每步都要
agent-s自然语言任务规划 + 经验记忆每步都要
ui-tars-desktop自然语言任务端到端多模态模型直接出坐标每步都要
midscene自然语言描述视觉理解 + 缓存过的定位结果首次要,命中缓存可省
microsoft-ufo自然语言任务Windows UI Automation 控件树每步都要
self-operating-computer自然语言任务截图 + 模型直出坐标每步都要

三条可以拿去比较任何 GUI agent 的判据

  1. 锚点是什么? 坐标(最脆)→ 视觉特征 → 无障碍/DOM 结构 → 多路证据交叉(最稳)。
  2. 谁承担不确定性? 每步问模型 = 每步都可能错;编译成程序 = 错误集中在编译期,可以人工审。
  3. 怎么知道自己成功了? 大多数项目止步于「动作发出去了」。OpenAdapt 今天的主张是用独立通道确认业务结果——这一条是它和整排兄弟项目最大的差异,虽然实现不在本克隆里。

一句话总结这个项目在货架上的位置

别的 GUI agent 在回答「模型能不能自己操作电脑」;OpenAdapt 今天在回答「怎么让一件已经被演示过的事,可重复、可验证地办成」。前者是能力问题,后者是可靠性问题。

延伸阅读:货架总览见 ../index.md


4. 全局代码地图

4.1 本仓库(启动器 + 治理)

主题文件路径符号名
CLI 根与转发openadapt/cli.pymain_FlowPassthroughGroup_run_flow
教程与预检openadapt/cli.pyquickstartdeploydoctor
懒加载导出openadapt/__init__.py__getattr__
版本单一来源openadapt/version.py__version__
幻觉导入检查tests/test_import_integrity.pytest_no_phantom_importstest_no_phantom_kwargstest_external_packages_installed_in_ci
开源边界守卫scripts/check_source_boundary.pySourcePolicyscan
平台清单scripts/generate_platform_manifest.pygenerateUNSIGNED_SIGNATURE
发布健康scripts/check_release_health.pyevaluatebump_levelself_test
产物校验scripts/verify_release_artifacts.pyverify_release_artifacts

4.2 legacy(录制 / 回放 / 脱敏)

主题文件路径符号名
录制编排legacy/openadapt/record.pyrecordprocess_eventstrigger_action_event
跨平台窗口legacy/openadapt/window/__init__.pywindow/_macos.pyget_active_window_dataget_active_window_state
事件归并legacy/openadapt/events.pymerge_eventsmerge_consecutive_action_eventsmerge_consecutive_mouse_click_events
DOM/OS 对齐legacy/openadapt/browser.pyalign_eventsfit_linear_transformation
数据模型legacy/openadapt/models.pyRecordingActionEventScreenshotWindowEvent
回放骨架legacy/openadapt/strategies/base.pyBaseReplayStrategy
描述锚点策略legacy/openadapt/strategies/visual.pyVisualReplayStrategyget_window_segmentation
全模型策略legacy/openadapt/strategies/vanilla.pyVanillaReplayStrategy
浏览器回放策略legacy/openadapt/strategies/visual_browser.pyVisualBrowserReplayStrategy
掩膜处理legacy/openadapt/vision.pyget_masks_from_segmented_imagerefine_masks
输入注入legacy/openadapt/playback.pyplay_action_event
脱敏legacy/openadapt/privacy/base.pyscrub.pyTextScrubbingMixin.scrub_dictscrub

4.3 不在本克隆里的东西(去哪找)

想看什么去哪
编译器、确定性回放、效果验证、halt、受管修复OpenAdaptAI/openadapt-flow
新版本的本地录制实现openadapt-capture
模型训练 / 基准评测openadapt-mlopenadapt-evals
桌面应用OpenAdaptAI/openadapt-desktop