跳到主要内容

重放策略族:有哪些策略、各自取舍

本章讲 legacy/openadapt/strategies/:重放的骨架是一个抽象循环,不同策略只是往里填不同的「下一步怎么产出」。

1. 它要解决的小问题

录好了干净动作序列,怎么在新场景上重放?最笨的办法是照抄坐标(界面没变时能用);进阶是让大模型看着当前屏幕,决定这一步该点哪个元素。OpenAdapt 把这些做成一个策略谱系,共享同一套循环。

2. 思路/直觉:模板方法 + 一个抽象钩子

基类 BaseReplayStrategy(strategies/base.py:17)定义了固定循环 run(),唯一留给子类填的抽象方法是 get_next_action_event():

run() 循环(base.py):
while True:
screenshot = 截当前屏
window_event = 取当前活动窗口
action = get_next_action_event(screenshot, window_event) ◄── 子类在这里各显神通
if StopIteration: break
playback.play_action_event(action) ── 用 pynput 真的去点/打字

怎么读这张图: 循环是死的,get_next_action_event 是活的。策略的全部差异都收敛到这一个方法里。

3. 策略谱系(由简到繁)

策略文件get_next_action_event 怎么产出下一步适用
Naivestrategies/naive.py直接返回录制里的第 i 个动作,连截图都不看,按原时间间隔 sleep 后照抄界面完全没变、纯回放
Vanillastrategies/vanilla.py把录制喂给大模型,让模型逐步产出动作需要模型判断的简单改写
Segmentstrategies/segment.py大模型产出带「元素描述」的动作,再用分割把描述转成当前坐标界面有变、需重定位
Visualstrategies/visual.py先给整段录制打描述、按指令改写,再逐步按描述重定位核心策略,自然语言改写重放
Stateful / VisualBrowserstrategies/stateful.pyvisual_browser.py前者带跨步状态,后者用浏览器 DOM 而非像素定位特定场景

4. 真实实现

4.1 基类循环

BaseReplayStrategy.run(strategies/base.py:56)每轮:截图 → 取活动窗口 WindowEvent.get_active_window_event(base.py:73)→ 调子类的 get_next_action_event(base.py:79)→ 校验时间单调 → playback.play_action_event(base.py:105)。还有个可选开关 CHECK_ACTION_COMPLETE(base.py:13),会调用大模型判断「上一步动作是否已生效」(prompt_is_action_complete,base.py:140)再继续——一种朴素的动作完成检测。

4.2 Naive:最朴素的照抄

NaiveReplayStrategy.get_next_action_event(strategies/naive.py:51)只是按索引取 processed_action_events 的下一个,按 timestampsleep(naive.py:85-90),原样返回。不看屏幕、不调模型。

4.3 Visual:先改写整段,再逐步重定位

这是核心策略,分两阶段:

构造时(改写) —— VisualReplayStrategy.__init__(strategies/visual.py:163)先给每个鼠标动作打上元素描述 add_active_segment_descriptions(visual.py:92),再用自然语言指令整段改写 apply_replay_instructions(visual.py:114)。后者把动作序列 + 指令塞进 apply_replay_instructions.j2 提示,让模型返回改写后的动作。

运行时(重定位) —— get_next_action_event(visual.py:187)对每个鼠标动作,调 get_window_segmentation 分割当前窗口,用录制时记的 active_segment_description 在当前分割结果里按描述找回目标区域,取质心换算成屏幕坐标(visual.py:229-245)。这套「按描述重定位」是第 4 章的主题。

5. 关键细节/坑

  • 策略是可插拔的。 入口 replay.py:replay(legacy/openadapt/replay.py:31)靠 utils.get_strategy_class_by_name(replay.py:73)按类名动态挑策略,命令行 python -m openadapt.replay VisualReplayStrategy ... 直接指定。
  • Segment 复用 Visual 的零件。 SegmentReplayStrategy 直接 from openadapt.strategies.visual import ...(strategies/segment.py:10-14)复用分割/改写函数,自己只换「怎么产出下一步」(改成每步现问模型)。
  • 老版调试残留。 segment.py 里散落 import ipdb; ipdb.set_trace()(segment.py:169:171)这类交互调试代码——提醒你这是研究阶段的单体老版,不是打磨过的生产路径。

6. 代码地图

主题文件路径符号
抽象循环legacy/openadapt/strategies/base.pyBaseReplayStrategyrunprompt_is_action_complete
照抄回放legacy/openadapt/strategies/naive.pyNaiveReplayStrategy
视觉改写+重定位legacy/openadapt/strategies/visual.pyVisualReplayStrategyapply_replay_instructionsadd_active_segment_descriptions
逐步分割重放legacy/openadapt/strategies/segment.pySegmentReplayStrategygenerate_action_event
策略分发入口legacy/openadapt/replay.pyreplay
真正落键落鼠标legacy/openadapt/playback.pyplay_action_event