1. 项目概述:当大模型遇上UI自动化测试

最近在搞一个挺有意思的玩意儿,把Qwen3.5-9B这个开源大模型和OpenClaw这个自动化测试框架给“撮合”到了一起,目标是让AI能看懂界面、操作界面,还能自己判断测试结果对不对。听起来是不是有点科幻?其实这背后是自动化测试领域一个很实在的痛点:传统的UI自动化脚本太“脆”了,界面元素一变,脚本就“瞎”了,维护成本高得吓人。我们能不能让测试脚本像人一样,有“眼睛”和“大脑”,能适应变化呢?这个项目就是一次探索。

简单来说,这个项目就是让Qwen3.5-9B扮演一个“智能测试员”。它通过OpenClaw获取当前屏幕或应用窗口的截图和可操作元素信息(比如按钮、输入框的位置和属性),然后像人一样“思考”:“我现在要登录,应该点哪里?用户名该填什么?”接着,它生成操作指令(如点击坐标、输入文本),再由OpenClaw去执行。最后,它还能根据执行后的界面变化,判断测试是否通过。这不仅仅是“录制-回放”的升级,而是向“感知-决策-执行-验证”的闭环智能测试迈进了一步。

如果你正在被繁重的UI回归测试所困扰,或者对AI如何落地到具体的工程实践(尤其是测试领域)感兴趣,那这篇内容应该能给你带来一些直接的参考和启发。我会从为什么选这两个工具开始,拆解整个架构的设计思路,然后一步步带你走通环境搭建、核心功能实现、以及最关键的——如何让大模型“理解”UI并做出可靠操作的全过程。当然,中间踩过的坑和总结的经验,也会毫无保留地分享给你。

2. 核心架构与工具选型背后的逻辑

为什么是OpenClaw + Qwen3.5-9B?这个组合不是拍脑袋定的,而是基于能力互补和实际落地成本综合考量后的结果。我们先来拆解一下这两个核心组件各自扮演的角色,以及它们是如何协同工作的。

2.1 OpenClaw:为什么选它作为“手和眼睛”

市面上UI自动化框架很多,Selenium、Playwright、Appium都是赫赫有名的。OpenClaw可能名气没那么大,但它有几个特质特别适合这个AI驱动的场景:

  1. 跨平台与统一API :OpenClaw设计之初就强调对Windows、macOS、Linux桌面应用以及Web、移动端(通过集成)的统一操作能力。它提供了一套相对一致的API来获取屏幕信息、查找控件、执行点击/输入等操作。这意味着我们为大模型编写的“操作指令生成逻辑”可以更通用,不需要为Web、桌面App写两套截然不同的提示词(Prompt)。
  2. 丰富的上下文信息获取 :除了截图,OpenClaw能通过底层访问(如UI Automation、Accessibility APIs)获取控件的层次结构、类型、名称、状态等丰富属性。这些结构化信息对大模型理解界面至关重要。纯截图是像素,结合控件树就是语义。例如,模型不仅能“看到”一个蓝色方块,还能知道它是一个“已禁用的提交按钮”。
  3. 灵活的执行与控制 :它支持基于坐标、基于控件属性的多种操作方式,并且执行稳定。在我们的架构里,OpenClaw负责忠实执行Qwen模型生成的指令,并将执行结果(成功/失败、新的界面状态)反馈回去。

注意 :OpenClaw的安装和配置需要根据你的操作系统来。比如在Windows上,你可能需要额外安装.NET框架或VC++运行库。如果遇到“无法识别openclaw命令”的错误,99%是因为系统PATH环境变量没有包含OpenClaw的安装目录。务必将其安装目录(例如 C:\Program Files\OpenClaw )添加到系统的PATH中。

2.2 Qwen3.5-9B:为什么是它而不是GPT-4

选择Qwen3.5-9B,核心是平衡能力、成本与控制权。

  1. 强大的多模态与推理能力 :Qwen3.5-9B虽然参数量比GPT-4小,但在9B这个级别上,它的视觉理解(VL)和指令跟随(Instruct)能力非常突出。它能够较好地理解截图中的UI元素布局、文字内容,并结合文本指令进行推理。这对于“根据当前界面决定下一步操作”至关重要。
  2. 可本地部署与数据隐私 :这是最关键的一点。测试代码、测试数据、甚至是被测系统的界面,都可能涉及敏感信息。使用GPT-4等云端API存在数据出境和安全合规风险。将Qwen3.5-9B部署在本地或内网服务器,数据不出域,完全可控。
  3. 成本与效率 :对于自动化测试这种可能需要高频调用的场景,使用云端API按token计费,长期成本不可控。本地部署后,一次投入硬件资源,后续调用边际成本几乎为零。虽然9B模型对GPU显存有要求(大约需要20GB以上才能流畅运行),但相比动辄上百B的大模型,部署门槛和推理速度都有优势。
  4. 指令微调(SFT)潜力 :Qwen3.5-9B是开源模型,我们可以针对“UI自动化测试”这个垂直领域,收集高质量的(截图, 操作指令, 验证点)数据对,对模型进行进一步的指令微调,让它更擅长理解测试场景,减少误操作。这是闭源API模型无法做到的深度定制。

架构流程图(文字描述) : 整个系统运行在一个循环中:

  1. 启动 :测试脚本定义初始状态(如打开浏览器到登录页)。
  2. 感知 :OpenClaw捕获当前活动窗口的截图和控件树信息,拼接成多模态提示。
  3. 决策 :将提示发送给本地部署的Qwen3.5-9B服务。模型分析后,输出结构化操作指令(JSON格式),例如 {"action": "click", "target": "id=loginButton"} {"action": "verify", "expect": "Welcome page appears"}
  4. 执行 :OpenClaw解析并执行该指令。
  5. 验证/迭代 :如果是验证指令,模型会对比执行前后的状态;如果是操作指令,执行后回到步骤2,开始下一个循环,直到测试用例结束。

3. 环境搭建与核心依赖部署实操

理论讲完了,我们动手把环境搭起来。这一部分会非常详细,因为基础不牢,地动山摇。我会以Windows系统为例,但思路是通用的。

3.1 OpenClaw的安装与基础配置

首先解决OpenClaw。建议直接从GitHub仓库下载最新版本的Release安装包。

  1. 下载与安装

    • 访问OpenClaw的GitHub Releases页面,下载对应你操作系统的安装包(如 .msi 对于Windows)。
    • 运行安装程序,选择安装路径。记住这个路径,比如 D:\Tools\OpenClaw
  2. 配置系统环境变量

    • 这是解决“无法识别openclaw命令”的关键。右键点击“此电脑”->“属性”->“高级系统设置”->“环境变量”。
    • 在“系统变量”中找到并选中 Path ,点击“编辑”。
    • 点击“新建”,将OpenClaw的安装目录路径(如 D:\Tools\OpenClaw )添加进去。
    • 验证 :打开一个新的命令行终端(CMD或PowerShell),输入 openclaw --version 。如果正确显示版本号,说明配置成功。
  3. 初步测试OpenClaw

    • 我们可以写一个简单的Python脚本来测试OpenClaw的Python SDK是否工作正常。首先确保安装了Python(3.8以上)。
    • 安装OpenClaw的Python包: pip install openclaw-sdk (具体包名请以官方文档为准,有时可能是 openclaw )。
    • 编写测试脚本 test_openclaw.py
      import openclaw
      import time
      
      # 初始化驱动,这里以Windows桌面为例
      driver = openclaw.DesktopDriver()
      
      # 获取当前屏幕信息(例如,截图)
      screenshot = driver.capture_screen()
      screenshot.save('test_screenshot.png')
      print("截图已保存")
      
      # 尝试查找计算器应用并打开(示例)
      # driver.execute('start calc.exe')
      # time.sleep(2)
      # 更多操作...
      
    • 运行这个脚本,如果能在当前目录下生成 test_screenshot.png ,说明OpenClaw的基础功能正常。

3.2 Qwen3.5-9B的本地部署与API服务启动

部署大模型是本项目的难点。我们需要一个能够加载Qwen3.5-9B-Instruct(带视觉能力的版本)并提供类似OpenAI格式API的服务。这里推荐使用 vLLM Ollama

方案一:使用vLLM(推荐,性能好) vLLM是一个高性能的推理和服务引擎,对Qwen系列支持很好。

  1. 硬件准备 :确保有一张显存 >= 20GB 的NVIDIA GPU(如RTX 3090/4090, A10等)。
  2. 环境安装
    # 创建并激活Python虚拟环境
    python -m venv venv_qwen
    .\venv_qwen\Scripts\activate  # Windows
    # source venv_qwen/bin/activate # Linux/macOS
    
    # 安装vLLM,注意版本兼容性
    pip install vllm
    # 如果需要视觉能力,确保安装相应的视觉依赖,Qwen-VL通常需要额外的transformers和torchvision
    pip install transformers torch torchvision
    
  3. 启动API服务
    • 对于纯文本的Qwen3.5-9B-Instruct,启动命令相对简单。但对于多模态版本(如Qwen2.5-VL-9B-Instruct),启动方式可能不同,请务必查阅模型卡(Model Card)中的官方示例。
    • 假设我们使用纯文本版本(通过文本描述UI),启动服务:
      vllm serve Qwen/Qwen2.5-9B-Instruct --api-key token-abc123 --port 8000
      
      这会在本地的8000端口启动一个兼容OpenAI API的服务。 --api-key 参数是为了简单鉴权。
  4. 测试API服务
    • 使用curl或Python脚本测试:
      import requests
      import json
      
      url = "http://localhost:8000/v1/chat/completions"
      headers = {"Authorization": "Bearer token-abc123", "Content-Type": "application/json"}
      data = {
          "model": "Qwen/Qwen2.5-9B-Instruct",
          "messages": [{"role": "user", "content": "你好,请介绍一下你自己。"}],
          "max_tokens": 100
      }
      response = requests.post(url, headers=headers, data=json.dumps(data))
      print(response.json())
      
    • 如果能收到正常的回复,说明模型服务启动成功。

方案二:使用Ollama(更简单,但可能版本滞后) Ollama简化了模型的下载和运行,但对最新模型的支持有时会慢一点。

  1. 安装Ollama :从官网下载安装。
  2. 拉取并运行模型
    # 拉取模型(如果Ollama提供了Qwen3.5-9B的版本)
    ollama pull qwen2.5:9b
    # 运行模型并开启API
    ollama run qwen2.5:9b
    # Ollama默认在11434端口提供API,格式也与OpenAI稍有不同,需要适配。
    

    实操心得 :在项目初期快速验证想法时,Ollama非常方便。但在生产环境追求稳定性和最新模型能力时,vLLM是更专业的选择。务必根据你拉取的模型名称调整代码中的API端点。

3.3 项目工程结构搭建

环境就绪后,我们来组织代码。一个清晰的结构有助于后续开发和维护。

openclaw_qwen_autotest/
├── config/
│   ├── __init__.py
│   └── settings.py        # 存放模型API地址、密钥、OpenClaw配置等
├── core/
│   ├── __init__.py
│   ├── openclaw_client.py # 封装OpenClaw操作
│   ├── llm_client.py      # 封装与大模型API的交互
│   └── orchestrator.py    # 核心协调器,串联感知-决策-执行循环
├── prompts/               # 存放给大模型的各种提示词模板
│   └── ui_operation.md
├── test_cases/           # 存放具体的测试用例定义
│   └── login_test.json
├── utils/
│   ├── image_processor.py # 图像处理工具(如截图编码为base64)
│   └── logger.py
├── main.py               # 主程序入口
└── requirements.txt

requirements.txt 中,需要包含:

openclaw-sdk
requests
pillow
openai>=1.0.0  # 使用OpenAI兼容的客户端库
python-dotenv

4. 核心实现:如何让大模型“看懂”并“操作”UI

这是整个项目最核心、也最具挑战性的部分。我们不能直接把截图丢给模型说“去登录”,需要精心设计“沟通”的方式。

4.1 多模态提示词(Prompt)工程实战

模型的表现,八成取决于提示词。我们的提示词需要完成以下几件事:

  1. 定义角色和任务 :告诉模型它现在是一个自动化测试助手。
  2. 提供上下文 :当前测试的目标是什么(如“测试登录功能”)。
  3. 结构化输入 :以清晰的方式提供截图和/或控件树信息。
  4. 规定输出格式 :要求模型必须以严格的JSON格式回复,便于程序解析。

下面是一个经过多次调试后相对稳定的提示词模板(存储在 prompts/ui_operation.md ):

你是一个专业的UI自动化测试助手。你的任务是根据提供的当前界面截图和可操作元素信息,决定下一步要执行的操作,以完成指定的测试步骤。

## 测试步骤目标
{test_step_description}

## 当前界面信息
{current_ui_context}

## 可操作元素列表(部分)
{element_list}

## 输出要求
请你思考并输出一个JSON对象,且只输出这个JSON对象,不要有任何其他解释。
JSON结构必须如下:
{
  "reasoning": "简要说明你为什么选择这个操作,基于界面上的什么信息。",
  "action": "操作类型。必须是以下之一:click, input, verify, scroll, wait, finish",
  "target": "操作目标。对于click,可以是元素ID、XPath或大致描述;对于input,是要输入的文本;对于verify,是验证的预期结果描述。",
  "value": "可选。当action为input时,此字段为要输入的文本值。",
  "confidence": "你对这个操作决策的置信度,0-1之间的小数。"
}

现在,请基于以上信息进行分析并输出JSON。

关键点解析

  • {test_step_description} : 替换为具体步骤,如“在登录页面,输入用户名‘admin’和密码‘123456’,然后点击登录按钮”。
  • {current_ui_context} : 这里可以放入截图的Base64编码,或者对截图的文字描述。为了节省token和加快推理,我们 更推荐先使用OpenClaw提取界面上的主要文字和控件类型,生成一段结构化文本描述 。例如:“当前界面为登录页。顶部有标题‘用户登录’。中间有两个输入框,第一个标签为‘用户名’,第二个标签为‘密码’。下方有一个蓝色按钮,文字为‘登录’。右下角有链接‘忘记密码?’。” 这比直接传图片base64更高效,且对纯文本模型(Qwen3.5-9B-Instruct)也适用。
  • {element_list} : 从OpenClaw控件树中提取的关键可交互元素列表,用简明的格式提供,如: - 元素1: 类型=文本框, id=username, 名称=‘用户名输入框’, 位置=(200,300) - 元素2: 类型=密码框, id=password, 名称=‘密码输入框’ - 元素3: 类型=按钮, id=loginBtn, 名称=‘登录按钮’, 文本=‘登录’

避坑技巧 :直接让模型从截图像素中定位点击坐标(grounding)非常困难且不准。最佳实践是 让模型做“语义选择” ,即输出它想操作的元素描述(如“登录按钮”),然后由OpenClaw根据这个描述去控件树中查找最匹配的元素(通过id、name、text等属性),再执行精确操作。这解耦了感知和定位,成功率大大提升。

4.2 协调器(Orchestrator)的核心循环逻辑

协调器是系统的大脑,它负责管理整个测试流程的状态机。下面是 core/orchestrator.py 的核心逻辑伪代码:

class TestOrchestrator:
    def __init__(self, openclaw_client, llm_client, test_case):
        self.driver = openclaw_client
        self.llm = llm_client
        self.test_case = test_case # 加载的测试用例数据
        self.current_step_index = 0
        self.context_history = [] # 记录历史步骤,可供模型参考

    def run_test_case(self):
        while self.current_step_index < len(self.test_case['steps']):
            step = self.test_case['steps'][self.current_step_index]
            print(f"执行步骤 {self.current_step_index + 1}: {step['description']}")

            # 1. 感知:获取当前UI状态
            ui_description = self.driver.get_ui_description() # 获取文本化描述
            element_list = self.driver.get_interactive_elements() # 获取可操作元素列表

            # 2. 决策:调用大模型
            prompt = self._build_prompt(step, ui_description, element_list)
            llm_response = self.llm.get_action(prompt)

            # 解析模型返回的JSON
            action_command = json.loads(llm_response)

            # 3. 执行与验证
            if action_command['action'] == 'finish':
                print(f"模型指示测试步骤完成。")
                break

            success = self._execute_action(action_command)
            if not success:
                print(f"步骤 {self.current_step_index + 1} 执行失败。")
                # 可以加入重试或失败处理逻辑
                break

            # 如果是验证操作,需要特别处理
            if action_command['action'] == 'verify':
                verification_passed = self._perform_verification(action_command)
                if not verification_passed:
                    print(f"验证失败: {action_command['target']}")
                    break
                else:
                    print("验证通过。")

            # 记录上下文,可选地提供给下一步
            self.context_history.append({
                'step': step['description'],
                'action': action_command,
                'ui_snapshot': ui_description[:100] # 存个摘要
            })

            self.current_step_index += 1
            time.sleep(1) # 操作间等待,避免界面未响应

        print("测试用例执行结束。")

    def _execute_action(self, command):
        # 根据command中的action和target,调用OpenClaw客户端执行具体操作
        if command['action'] == 'click':
            # 模型返回的target可能是“登录按钮”,需要映射到OpenClaw可识别的定位器
            locator = self._parse_target_to_locator(command['target'])
            return self.driver.click(locator)
        elif command['action'] == 'input':
            locator = self._parse_target_to_locator(command['target'])
            return self.driver.input_text(locator, command['value'])
        # ... 处理其他action类型
        return False

    def _parse_target_to_locator(self, target_description):
        # 这是一个关键函数,将模型的自然语言描述转换为OpenClaw的定位器。
        # 例如,将“登录按钮”转换为通过文本属性查找:driver.find_element(text='登录')
        # 更复杂的可以实现一个简单的语义匹配算法。
        # 这里简化处理:假设target_description就是元素的id或文本。
        if target_description.startswith('id='):
            return {'id': target_description[3:]}
        else:
            # 默认按文本查找
            return {'text': target_description}

4.3 大模型客户端封装与响应处理

与LLM API的交互需要健壮的错误处理和结果解析。 core/llm_client.py 需要做这些事:

import openai # 使用兼容OpenAI的客户端
import json
import re

class QwenClient:
    def __init__(self, base_url, api_key, model_name):
        self.client = openai.OpenAI(base_url=base_url, api_key=api_key)
        self.model = model_name

    def get_action(self, prompt_text):
        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=[{"role": "user", "content": prompt_text}],
                temperature=0.1, # 低温度,让输出更确定
                max_tokens=500,
            )
            raw_content = response.choices[0].message.content

            # 关键:从返回文本中提取JSON。模型有时会在JSON外加引号或markdown代码块。
            json_str = self._extract_json(raw_content)

            # 验证JSON结构
            action_data = json.loads(json_str)
            required_keys = {"reasoning", "action", "target", "confidence"}
            if not all(k in action_data for k in required_keys):
                raise ValueError(f"返回JSON缺少必要字段: {action_data}")
            if action_data['action'] not in ['click', 'input', 'verify', 'scroll', 'wait', 'finish']:
                raise ValueError(f"非法action类型: {action_data['action']}")

            return json.dumps(action_data, ensure_ascii=False)

        except json.JSONDecodeError as e:
            print(f"解析模型返回的JSON失败: {e}\n原始返回: {raw_content}")
            # 返回一个安全的默认操作,比如等待
            return json.dumps({"action": "wait", "target": "解析错误,等待重试", "confidence": 0.0})
        except Exception as e:
            print(f"调用模型API失败: {e}")
            raise

    def _extract_json(self, text):
        # 尝试匹配 ```json ... ``` 模式
        match = re.search(r'```json\n(.*?)\n```', text, re.DOTALL)
        if match:
            return match.group(1).strip()
        # 尝试匹配 {...} 模式
        match = re.search(r'(\{.*?\})', text, re.DOTALL)
        if match:
            return match.group(1).strip()
        # 如果都没有,返回原文本(假设它就是纯JSON)
        return text.strip()

注意事项 :大模型的输出具有不确定性。即使要求它“只输出JSON”,它有时也会加上解释性文字或markdown代码块。因此,在解析响应时, 必须 编写健壮的提取逻辑(如上面的 _extract_json 方法),而不是直接 json.loads(response) 。同时,要对返回的JSON结构进行验证,防止缺少字段或字段值异常导致程序崩溃。

5. 完整测试用例执行与结果验证流程

让我们用一个具体的例子,串联起整个系统。假设我们要测试一个Web登录功能。

5.1 定义测试用例

test_cases/login_test.json 中定义:

{
  "name": "用户登录功能测试",
  "start_url": "https://example.com/login",
  "steps": [
    {
      "id": 1,
      "description": "在用户名输入框中输入‘test_user’",
      "type": "input"
    },
    {
      "id": 2,
      "description": "在密码输入框中输入‘secure_password’",
      "type": "input"
    },
    {
      "id": 3,
      "description": "点击‘登录’按钮",
      "type": "click"
    },
    {
      "id": 4,
      "description": "验证登录成功后,页面跳转至仪表盘,且右上角显示用户名‘test_user’",
      "type": "verify"
    }
  ]
}

5.2 执行过程拆解

  1. 初始化 main.py 读取测试用例,初始化OpenClaw驱动(打开浏览器并导航到 start_url ),初始化Qwen客户端。
  2. 步骤1 - 输入用户名
    • 协调器调用 driver.get_ui_description() ,得到:“当前页面标题为‘用户登录’。包含两个输入框,标签分别为‘用户名’和‘密码’,一个按钮文字为‘登录’。”
    • 协调器构建Prompt,将步骤1的描述、UI描述和元素列表传给Qwen模型。
    • 模型返回: {"reasoning": "界面有一个标签为‘用户名’的输入框,与测试目标匹配。", "action": "input", "target": "用户名输入框", "value": "test_user", "confidence": 0.95}
    • 协调器解析 target ,将其转换为定位器(例如按标签文本查找),并调用 driver.input_text(locator, "test_user")
    • OpenClaw执行输入操作。
  3. 步骤2 - 输入密码 :过程类似,模型需要能区分“用户名”和“密码”输入框。
  4. 步骤3 - 点击登录 :模型识别“登录”按钮并生成点击指令。
  5. 步骤4 - 验证结果
    • 这是关键。点击登录后,协调器再次获取新的UI描述。
    • 构建验证Prompt,可能包含前后界面的对比描述,要求模型判断验证点是否满足。
    • 模型返回: {"reasoning": "页面标题变为‘仪表盘’,且右上角有文本‘欢迎,test_user’。与预期相符。", "action": "verify", "target": "页面跳转至仪表盘且显示用户名", "confidence": 0.98}
    • 协调器根据置信度(例如>0.9)判断验证通过,记录成功。

5.3 验证策略的深化

简单的文本匹配验证(如“页面包含‘登录成功’”)很脆弱。更可靠的验证策略包括:

  • 多元素综合判断 :让模型同时验证多个点,如标题、特定关键元素的存在、URL的变化等。
  • 对比验证 :在Prompt中同时提供“预期状态描述”和“当前状态描述”,让模型判断两者是否一致。
  • 自定义验证函数 :对于非常确定的验证点,可以不依赖模型,而是在测试用例中定义Python函数,由协调器执行。例如,验证跳转后的特定URL。模型更适合处理模糊的、语义层面的验证。

6. 常见问题、调试技巧与性能优化

在实际跑起来的过程中,你肯定会遇到各种问题。这里把我踩过的坑和解决方案总结一下。

6.1 模型“犯傻”与指令不遵循

问题 :模型不按要求的JSON格式输出,或者操作指令完全错误(比如该点登录却点了注册)。

排查与解决

  1. 检查Prompt :首先确认Prompt是否清晰无歧义。角色定义、任务说明、输出格式要求必须明确。可以在Prompt开头用“你必须...”等强指令词。
  2. 降低Temperature :将API调用的 temperature 参数设为0.1或更低,减少随机性。
  3. 加入示例(Few-Shot) :在Prompt中提供一两个完整的输入输出示例,能极大提高模型输出格式的准确性。
  4. 后处理与重试 :像前面 llm_client.py 那样,实现健壮的JSON提取和验证。对于明显错误的操作(如置信度过低),可以设计重试逻辑:将错误操作和当前界面信息再次发给模型,要求其纠正。
  5. 模型能力边界 :认识到9B模型的局限性。对于非常复杂、元素密集的界面,它的“理解”能力可能会下降。此时,可以尝试让OpenClaw提供更精细的控件信息(如只聚焦于当前操作相关的区域),或者对截图进行预处理(如裁剪、灰度化)再辅以文字描述,减少信息噪音。

6.2 OpenClaw元素定位失败

问题 :模型返回了“登录按钮”,但OpenClaw在控件树里找不到匹配的元素。

排查与解决

  1. 丰富元素列表信息 :确保传给模型的 element_list 包含尽可能多的属性,如 id , name , text , class , role 等。模型可能会利用其中任何一个属性来指代目标。
  2. 改进定位器解析 _parse_target_to_locator 函数不能只依赖一种查找方式。实现一个优先级查找策略:
    • 首先尝试精确匹配( id= name= )。
    • 其次尝试部分文本匹配( text contains ‘登录’ )。
    • 最后可以尝试基于元素类型的模糊匹配( type=button AND index=0 )。
  3. 引入视觉辅助定位(高级) :如果控件树信息不可靠,可以回退到基于坐标的点击。这需要模型输出grounding信息(在图片中框出目标),对多模态模型要求较高,且精度需仔细评估。

6.3 执行速度慢与稳定性

问题 :一次“感知-决策-执行”循环耗时好几秒,无法用于大规模测试。

优化方向

  1. 模型推理加速
    • 使用vLLM的 连续批处理 (Continuous Batching)功能,同时处理多个测试步骤的请求(如果并行执行多个测试实例)。
    • 考虑模型 量化 。将Qwen3.5-9B用GPTQ或AWQ量化到4bit或8bit,可以大幅减少显存占用并提升推理速度,精度损失在可接受范围内。
    • 调整生成参数:适当减少 max_tokens ,因为我们的回答不需要很长。
  2. 缓存与优化Prompt
    • 对于不变的界面或操作,可以缓存模型的响应,避免重复计算。
    • 持续优化Prompt,使其更简短有效,减少不必要的token消耗。
  3. 异步操作 :将UI信息获取、模型调用、执行操作设计为异步流程,可以掩盖部分I/O等待时间。

6.4 测试用例设计的艺术

问题 :测试用例写得不好,导致模型困惑或测试覆盖不全。

设计原则

  1. 原子化 :一个步骤只做一件事。“输入用户名并登录”应该拆成“输入用户名”、“输入密码”、“点击登录”三个步骤。
  2. 描述具体,避免歧义 :使用界面上的确切文字。用“点击‘记住我’复选框”,而不是“勾选记住我选项”。
  3. 提供上下文 :在测试用例的预置条件或步骤描述中,说明前置状态。例如,“假设已处于商品详情页,现在将商品加入购物车”。
  4. 预期结果明确 :验证步骤的“预期”要具体、可观测。“登录成功”不明确,“页面跳转到URL包含‘/dashboard’且出现元素‘用户菜单’”更明确。

7. 项目总结与未来展望

这个项目走下来,最大的感受是, AI不是来完全取代传统自动化测试的,而是来增强和解决其痛点的 。OpenClaw+Qwen3.5-9B的组合,让我们看到了实现“自适应”UI自动化测试的曙光。它特别适合用于:

  • 快速生成冒烟测试脚本 :对着新页面描述测试意图,让AI自动探索并生成操作序列。
  • 维护期脚本自适应 :当UI发生非破坏性改动(如按钮文字从“Submit”改为“提交”),传统的基于绝对定位的脚本会失败,而AI驱动的脚本有可能通过语义理解自动适应。
  • 探索性测试辅助 :让AI在测试过程中随机探索,可能会发现一些测试人员未预料到的路径或状态。

当然,目前它还远非完美。稳定性、执行速度、对复杂交互(如拖拽、手势)的支持,都是需要继续攻克的难题。但作为一次将前沿AI模型与具体工程实践结合的尝试,它已经展示了足够的潜力和价值。

我个人在实际操作中的体会是, Prompt工程和错误处理机制的健壮性,是项目成败的关键 。花在调试和优化Prompt上的时间,可能比写代码的时间还多。另外,建立一个包含各种典型UI场景(表单、列表、弹窗、导航等)的“测试集”,用于评估和迭代你的AI测试助手,是非常有必要的。

最后再分享一个小技巧:在项目初期,可以先用GPT-4等更强的云端模型来生成“标准答案”(即给定界面和步骤,输出正确的操作指令),用这些高质量数据来微调本地的Qwen模型,能显著提升它在特定任务上的表现。这相当于让“大老师”教“小学生”,是一种高效的领域适配方法。这条路走通了,你的AI测试助手就会越来越聪明,越来越可靠。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐