第八章:回放策略
欢迎回来,OpenAdapt探索者~
在第七章:隐私保护(擦除)中,我们学习了OpenAdapt如何保护录制内容中的敏感信息。在此之前,我们了解了录制数据的存储方式(第六章:数据库管理),以及原始事件如何被精炼为有意义的操作(第五章:事件处理与合并)。
现在让我们探讨最激动人心的部分:让OpenAdapt实际执行我们录制的任务!
假设我们已录制了填写在线表单的过程。当需要OpenAdapt重复此任务时,它是如何知道具体执行方式的?它会像鹦鹉学舌般逐像素复现鼠标点击和按键操作吗?还是能像智能助手般理解任务目标,在界面稍有变化时自动适应?
若采用像素级精确回放,当遇到以下情况时将面临挑战:
- 浏览器窗口位置偏移
 - 按钮坐标发生数像素变动
 - 列表项顺序调整
 
这时"像素完美"的回放就可能失败!这正是OpenAdapt需要回放策略的根本原因。
什么是回放策略?
回放策略可视为OpenAdapt执行自动化任务的"战术手册",每种策略代表不同的工作流回放方法。它们指导系统如何解析和执行已录制的操作序列。
为何需要多种策略?
就像优秀教练需要针对不同场景制定战术手册,OpenAdapt的多样化策略源于自动化任务的差异性:
- 可靠性:确保在界面微调时仍能稳定执行
 - 适应性:像智能助手般理解上下文,执行类似"点击'提交'按钮"的抽象指令
 - 高效性:快速精准完成目标操作
 
以网站登录自动化为例:
- 简单策略直接点击固定坐标的"登录"按钮(位置不变时有效)
 - 智能策略通过视觉识别定位按钮(适应界面布局变化)
 
回放策略实践指南
使用回放策略的流程非常直观。在终端执行如下命令:
            
            
              bash
              
              
            
          
          python -m openadapt.replay <策略名称> --timestamp=<录制时间戳>
        参数说明:
<策略名称>:指定战术手册类型(如NaiveReplayStrategy/VanillaReplayStrategy/VisualReplayStrategy)--timestamp:指定目标录制时间戳(通过python -m openadapt.list查询)
应用示例:
使用基础策略回放最新录制:
            
            
              bash
              
              
            
          
          python -m openadapt.replay NaiveReplayStrategy
        使用智能策略执行指定录制(20230101_123456)并附加自然语言指令:
            
            
              bash
              
              
            
          
          python -m openadapt.replay VisualReplayStrategy --timestamp=20230101_123456 --instructions="先点击'登录'按钮,再在用户名字段输入'myuser'"
        部分AI驱动策略支持通过--instructions参数用自然语言调整回放行为。
策略执行机制
执行openadapt.replay命令时,系统会按以下流程展开工作:

核心入口文件:openadapt/replay.py
该模块负责初始化回放流程:
            
            
              python
              
              
            
          
          # 简化版openadapt/replay.py
import fire
from openadapt import utils
from openadapt.db import crud
def replay(
    strategy_name: str,
    timestamp: str | None = None,
    recording = None,
    **kwargs: dict,
) -> bool:
    session = crud.get_new_session(read_only=True)
    # 获取指定录制数据
    recording = crud.get_recording(session, timestamp) if timestamp else crud.get_latest_recording(session)
    
    # 动态加载策略类
    strategy_class = utils.get_strategy_class_by_name()[strategy_name]
    
    # 实例化并执行策略
    strategy = strategy_class(recording, **kwargs)
    strategy.run()
    
    return True
if __name__ == "__main__":
    fire.Fire(replay)
        策略注册中心:openadapt/strategies/init.py
该文件集中管理所有可用策略:
            
            
              python
              
              
            
          
          # 策略目录文件
from .base import BaseReplayStrategy
from .naive import NaiveReplayStrategy
from .vanilla import VanillaReplayStrategy
from .visual import VisualReplayStrategy
from .visual_browser import VisualBrowserReplayStrategy
        策略基类:openadapt/strategies/base.py
定义所有策略的通用框架:
            
            
              python
              
              
            
          
          # 策略基类实现
from abc import ABC, abstractmethod
from pynput import keyboard, mouse
from openadapt import models, playback
class BaseReplayStrategy(ABC):
    def __init__(self, recording: models.Recording, **kwargs) -> None:
        self.recording = recording
        self.keyboard_controller = keyboard.Controller()
        self.mouse_controller = mouse.Controller()
    @abstractmethod
    def get_next_action_event(self, screenshot: models.Screenshot, window_event: models.WindowEvent) -> models.ActionEvent:
        pass
    def run(self) -> None:
        while True:
            current_screenshot = models.Screenshot.take_screenshot()
            current_window_event = models.WindowEvent.get_active_window_event()
            try:
                action_event = self.get_next_action_event(current_screenshot, current_window_event)
                playback.play_action_event(action_event, self.mouse_controller, self.keyboard_controller)
            except StopIteration:
                break
        具体策略实现
基础策略:NaiveReplayStrategy
该策略机械复现录制动作:
            
            
              python
              
              
            
          
          # 简单回放策略实现
import time
from openadapt import models, strategies
class NaiveReplayStrategy(strategies.base.BaseReplayStrategy):
    def __init__(self, recording: models.Recording, **kwargs) -> None:
        super().__init__(recording)
        self.action_event_idx = -1
        self.processed_events = recording.processed_action_events
        self.prev_timestamp = None
    def get_next_action_event(self, screenshot: models.Screenshot, window_event: models.WindowEvent) -> models.ActionEvent | None:
        self.action_event_idx += 1
        if self.action_event_idx >= len(self.processed_events):
            raise StopIteration()
        
        action_event = self.processed_events[self.action_event_idx]
        # 保持原始时间间隔
        if self.prev_timestamp:
            time.sleep(action_event.timestamp - self.prev_timestamp)
        self.prev_timestamp = action_event.timestamp
        
        return action_event
        智能策略范例
VanillaReplayStrategy(AI基础版)
            
            
              python
              
              
            
          
          # AI驱动策略实现
from openadapt import adapters, models, strategies
class VanillaReplayStrategy(strategies.base.BaseReplayStrategy):
    def __init__(self, recording: models.Recording, instructions: str = "") -> None:
        super().__init__(recording)
        self.instructions = instructions
        self.action_history = []
        # 使用AI解析录制内容
        self.recording_description = self.describe_recording(recording)
    def get_next_action_event(self, current_screenshot: models.Screenshot, current_window_event: models.WindowEvent) -> models.ActionEvent | None:
        # 调用AI模型决策下一步
        action_event = self.generate_action_event(
            current_screenshot,
            current_window_event,
            self.recording.processed_action_events,
            self.action_history,
            self.instructions
        )
        self.action_history.append(action_event)
        return action_event
        VisualReplayStrategy(视觉增强版)
            
            
              python
              
              
            
          
          # 视觉策略实现
from openadapt import adapters, models, strategies, vision
class VisualReplayStrategy(strategies.base.BaseReplayStrategy):
    def __init__(self, recording: models.Recording, instructions: str) -> None:
        super().__init__(recording)
        self.modified_actions = self.apply_replay_instructions(
            recording.processed_action_events,
            instructions
        )
        self.recording_action_idx = 0
    def get_next_action_event(self, active_screenshot: models.Screenshot, active_window: models.WindowEvent) -> models.ActionEvent:
        # 视觉分割定位元素
        modified_reference_action = self.modified_actions[self.recording_action_idx]
        if modified_reference_action.name in common.MOUSE_EVENTS:
            active_window_segmentation = self.get_window_segmentation(modified_reference_action)
            # 计算目标元素坐标
            target_centroid = active_window_segmentation.centroids[target_segment_idx]
            modified_reference_action.mouse_x, modified_reference_action.mouse_y = self.calculate_coordinates(target_centroid)
        return modified_reference_action
        策略对比矩阵
| 策略名称 | 工作原理 | 适应性 | AI依赖 | 最佳场景 | 
|---|---|---|---|---|
| NaiveReplayStrategy | 精确复现录制动作 | 无 | 否 | 稳定环境的基础测试 | 
| VanillaReplayStrategy | AI解析上下文决策动作 | 中 | 是 | 需要语境理解的常规任务 | 
| VisualReplayStrategy | 视觉分割识别界面元素 | 高 | 是 | 界面频繁变化的复杂应用 | 
| VisualBrowserReplayStrategy | 结合DOM解析与视觉分析 | 极高 | 是 | 动态网页元素的精准自动化 | 
本章总结
我们深入探讨了OpenAdapt的回放策略 体系,从基础复现到智能决策的多层次实现。理解不同策略在get_next_action_event方法中的差异化实现,能帮助我们根据具体场景选择最优自动化方案。接下来我们将揭秘驱动这些智能策略的AI模型工作机制。
第九章:AI模型驱动程序
在第八章:回放策略中,我们学习了OpenAdapt如何使用不同的"剧本"来自动化任务,从简单重复到智能的AI驱动适配。我们提到了像VanillaReplayStrategy和VisualReplayStrategy这样的策略如何利用人工智能来"看"屏幕并"决定"下一步操作。
但OpenAdapt实际上是如何与GPT-4、Google Gemini或Anthropic Claude等强大AI模型"对话"的?它们都使用相同的"语言"吗?不完全!每个AI模型都有自己独特的请求接收和响应返回方式。
想象我们有一个高智商团队,每个成员说不同语言(比如英语、西班牙语、法语)。如果要给他们分配任务,就需要为每个成员配备翻译对吗?
这正是AI模型驱动程序在OpenAdapt中解决的问题。
什么是AI模型驱动程序?
AI模型驱动程序 可以看作**"翻译器"或"连接器"**,使OpenAdapt能与各种强大AI模型无缝通信。OpenAdapt核心逻辑无需学习每个AI模型的独特"语言"(API),只需通过这些驱动程序即可。
为什么需要这个?
当OpenAdapt的智能回放策略需要询问AI模型(比如"屏幕上有什么?"或"下一步该点击哪里?")时,会发送请求。但不同AI模型需要不同格式的请求,并以不同方式响应。
没有AI模型驱动程序,OpenAdapt的代码将变得非常混乱和复杂,充满针对每个AI的特定指令。如果出现新AI模型或现有模型改变"语言",就需要重写核心代码。
AI模型驱动程序通过标准化方式解决这个问题:
- 发送提示:将请求(可包含文本甚至截图图像!)打包成特定AI模型理解的格式
 - 接收响应:将AI模型的响应翻译回OpenAdapt易用的格式
 
目标是使OpenAdapt的AI集成:
- 模块化:轻松切换不同AI模型
 - 灵活性:添加新AI模型无需更改核心逻辑
 - 可靠性:确保跨AI模型的稳定通信
 
以核心示例说明:OpenAdapt使用AI模型分析当前屏幕截图并确定VisualReplayStrategy中的下一步操作。AI模型驱动程序正是实现与AI模型对话的关键组件。
如何使用AI模型驱动程序(简易方式)
作为用户,我们通常不直接与AI模型驱动程序交互。它们默默在后台工作,为更高级的回放策略提供动力。
我们主要通过配置设置与AI模型驱动程序交互,特别是为要使用的AI服务提供必要的API密钥。
可以通过OpenAdapt的Web仪表盘完成:
首先启动仪表盘:
            
            
              bash
              
              
            
          
          python -m openadapt.app.dashboard.run
        浏览器打开仪表盘后,导航到"设置"部分。在"API密钥"类别下,会看到各AI服务的字段:
OPENAI_API_KEY:用于GPT-4o、GPT-4 Vision等模型GOOGLE_API_KEY:用于Google Gemini模型ANTHROPIC_API_KEY:用于Anthropic Claude模型REPLICATE_API_TOKEN:用于Replicate托管的模型
只需将对应服务的API密钥粘贴到相应字段。
如果提供多个API密钥会怎样?
OpenAdapt很智能!当AI驱动的回放策略需要与AI模型通信时,会按预设顺序尝试使用驱动程序。如果首个(如OpenAI)失败或未配置,将自动尝试下一个(如Google、Anthropic)。这提供了内置回退机制,使自动化更健壮。
AI模型驱动程序:通信枢纽
(AI在自动化领域的运用)
当回放策略需要AI协助时(如分析截图),AI模型驱动程序的工作流程:

该流程显示回放策略不直接与OpenAI或Google对话,而是通过提示适配器使用正确的AI模型驱动程序处理具体AI通信。
openadapt/adapters/prompt.py:主协调器
协调与不同AI模型通信的核心组件位于openadapt/adapters/prompt.py。该文件包含prompt函数,作为OpenAdapt各模块(如回放策略)向AI模型提问的通用入口。
            
            
              python
              
              
            
          
          # 摘自openadapt/adapters/prompt.py(简化版)
from typing import Type
from PIL import Image
from openadapt.custom_logger import logger
# 导入特定AI模型驱动程序
from openadapt.drivers import anthropic, google, openai
# 定义驱动程序尝试顺序
DRIVER_ORDER: list[Type] = [openai, google, anthropic]
def prompt(
    text: str,
    images: list[Image.Image] | None = None,
    system_prompt: str | None = None,
) -> str:
    """按优先级顺序尝试从各服务获取提示完成"""
    text = text.strip()
    for driver in DRIVER_ORDER: # 按偏好顺序遍历驱动程序
        try:
            logger.info(f"尝试驱动程序: {driver.__name__}")
            # 调用特定AI模型驱动程序的'prompt'方法
            return driver.prompt(text, images=images, system_prompt=system_prompt)
        except Exception as e:
            logger.error(f"驱动程序 {driver.__name__} 失败,错误: {e}")
            continue # 尝试下一个驱动程序
    raise Exception("所有驱动程序均未能提供响应")
        这个prompt函数是关键。它遍历DRIVER_ORDER列表,尝试从每个AI模型驱动程序获取响应。
这就是设置多个API密钥能提供回退的原因!每个driver(如openai、google)都有自己的prompt函数,知晓如何与其特定AI通信。
具体翻译器:openadapt/drivers/*.py
openadapt/drivers/文件夹中的每个文件(如anthropic.py、google.py、openai.py)都是专用的AI模型驱动程序。它们是真正的"翻译器",处理准备特定AI模型请求、通过互联网发送请求和解析响应的具体细节。
以openadapt/drivers/openai.py的简化示例,说明如何向OpenAI模型(如GPT-4o)发送文本和图像:
            
            
              python
              
              
            
          
          # 摘自openadapt/drivers/openai.py(简化版)
from PIL import Image
import requests # 用于通过互联网发送请求
from openadapt import cache, utils
from openadapt.config import config # 获取API密钥
MODEL_NAME = "gpt-4o" # 该驱动程序对话的特定AI模型
@cache.cache() # OpenAdapt缓存AI响应以加速
def create_payload(
    prompt: str,
    system_prompt: str | None = None,
    images: list[Image.Image] | None = None,
    model: str = MODEL_NAME,
) -> dict:
    """按OpenAI专用格式准备数据"""
    messages = [
        {"role": "user", "content": [{"type": "text", "text": prompt}]}
    ]
    # 添加图像(转换为base64格式)
    images = images or []
    for image in images:
        base64_image = utils.image2utf8(image) # 转换为网页友好格式
        messages[0]["content"].append(
            {"type": "image_url", "image_url": {"url": base64_image}}
        )
    if system_prompt: # 添加AI初始指令
        messages = [{"role": "system", "content": [{"type": "text", "text": system_prompt}]}] + messages
    return {"model": model, "messages": messages}
@cache.cache()
def get_response(
    payload: dict,
    api_key: str = config.OPENAI_API_KEY, # 从OpenAdapt配置获取API密钥
) -> dict:
    """向OpenAI发送格式化请求并返回原始响应"""
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}",
    }
    response = requests.post(
        "https://api.openai.com/v1/chat/completions", # OpenAI专用地址
        headers=headers,
        json=payload, # 发送准备的数据
    )
    result = response.json()
    if "error" in result:
        raise Exception(result["error"]["message"])
    return result
def prompt(
    prompt_text: str, # 重命名以避免冲突
    system_prompt: str | None = None,
    images: list[Image.Image] | None = None,
    max_tokens: int | None = None,
) -> str:
    """供其他OpenAdapt模块调用的公共方法"""
    payload = create_payload(
        prompt_text,
        system_prompt,
        images,
        max_tokens=max_tokens,
    )
    result = get_response(payload)
    # 从AI响应中提取实际文本内容
    return result["choices"][0]["message"]["content"]
        这个OpenAI驱动程序的简化代码展示了关键步骤:
create_payload:将prompt文本和images(转换为base64格式)按OpenAI API要求的JSON结构整理get_response:使用requests库将payload发送到OpenAI专用服务器地址,使用OPENAI_API_KEY认证prompt:openadapt/adapters/prompt.py调用的主公共方法,协调创建载荷和获取响应步骤,从复杂JSON输出提取实际文本响应
anthropic.py和google.py有类似文件,各自包含针对相应AI模型API定制的create_payload、get_response和prompt函数。
AI模型驱动程序概览
以下是OpenAdapt常用AI模型驱动程序摘要:
| 驱动程序文件 | AI模型 | 用途 | 关键配置 | 
|---|---|---|---|
openadapt/drivers/openai.py | 
GPT-4 Vision、GPT-4o | 连接OpenAdapt与OpenAI多模态模型 | OPENAI_API_KEY | 
openadapt/drivers/google.py | 
Gemini Pro Vision、Gemini 1.5 Pro | 连接OpenAdapt与Google Gemini模型 | GOOGLE_API_KEY | 
openadapt/drivers/anthropic.py | 
Claude 3.5 Sonnet | 连接OpenAdapt与Anthropic Claude模型 | ANTHROPIC_API_KEY | 
openadapt/adapters/replicate.py | 
Replicate.com托管的模型(如Llama) | 连接Replicate平台模型 | REPLICATE_API_TOKEN | 
注:openadapt/adapters/replicate.py在此列为"驱动程序",因为它同样处理外部AI通信,尽管结构与直接驱动程序略有不同。
结论
我们现在已了解AI模型驱动程序 在OpenAdapt中的关键作用!这些驱动程序作为不可或缺的"翻译器",使OpenAdapt的先进回放策略能与各类强大AI模型通信。
通过理解如何配置API密钥,以及OpenAdapt如何利用prompt.py协调器和具体驱动程序文件实现与外部AI的信息收发,我们已完整掌握OpenAdapt实现智能自适应自动化的机制。
本章完成了对OpenAdapt核心概念的深入探讨。现在我们已经全面理解OpenAdapt如何记录操作、组织数据、理解GUI界面、管理设置、处理事件、存储信息、保护隐私,以及通过各类AI驱动的策略智能回放任务。
END ★,° :.☆( ̄▽ ̄):.°★ 。