基于Phi-3-mini与OpenClaw的AI驱动自动化测试实践

基于Phi-3-mini与OpenClaw的AI驱动自动化测试实践

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

最近在搞自动化测试的朋友,估计都听过一个词:AI Agent。简单来说,就是让AI像人一样,去操作软件、执行任务。我之前也折腾过不少方案,要么是写死规则的脚本,维护起来头大;要么是调用一些视觉识别API,成本高不说,稳定性还堪忧。直到我开始尝试用微软开源的Phi-3-mini-128k-instruct这个“小钢炮”模型,结合OpenClaw这个新兴的自动化框架,来批量执行Python脚本,整个流程的效率和智能化程度直接上了一个台阶。

这个组合的核心思路很清晰:用轻量但足够聪明的AI模型(Phi-3-mini)来理解任务意图和动态环境,用OpenClaw作为“手和脚”去精准执行操作,最终实现Python脚本的自动化、批量化运行与测试。它解决的痛点非常明确:传统的UI自动化测试脚本(比如基于Selenium、Playwright写的)在面对频繁变化的页面元素、复杂的业务流程时,极其脆弱,一个按钮的ID变了或者页面加载慢了一秒,整个脚本就可能挂掉。而引入AI模型后,我们可以用自然语言描述任务(比如“登录系统,找到最新的订单并导出报告”),让AI去理解并生成或调整执行步骤,再由OpenClaw去可靠地执行这些原子操作,这样脚本的健壮性和适应性会强很多。

这套方案特别适合几类场景:一是测试左移,开发在提交代码后,可以快速用自然语言描述测试用例,让AI自动生成并执行测试脚本,快速反馈;二是复杂的端到端(E2E)业务流程测试,比如一个电商的下单-支付-退款全流程,用传统脚本写起来很冗长,而用AI驱动可以更灵活地处理流程中的分支和异常;三是需要处理非结构化界面或老旧系统的自动化任务,这些系统可能没有良好的可访问性支持,传统定位方式失效,AI的图像理解和推理能力就能派上用场。

接下来,我会带你从零开始,拆解如何搭建这个环境,并分享我在实际项目中趟过的一些坑和总结出的最佳实践。你会发现,虽然听起来高大上,但一步步来,门槛并没有想象中那么高。

2. 核心工具选型与部署踩坑实录

工欲善其事,必先利其器。在这个方案里,三个核心组件是:Phi-3-mini-128k-instruct模型、OpenClaw框架,以及你的Python脚本资产。每一环的选型和部署都藏着细节,搞错了后面就会步步维艰。

2.1 为什么是Phi-3-mini-128k-instruct?

市面上开源模型那么多,为啥偏偏选这个3.8B参数的“小个子”?这是经过一番对比和实测后的决定。首先,它足够轻量。相比动辄7B、13B的模型,Phi-3-mini对硬件要求友好得多,在我的RTX 3060 12GB显卡上就能流畅跑起FP16精度,甚至用CPU推理虽然慢点,也不是不能接受。这对于需要长期运行、可能部署在测试机上的自动化任务来说,成本可控。

其次,也是更重要的,它的指令跟随(Instruction Following)能力在同尺寸模型中非常出色。微软在它身上做了大量的指令微调,这使得它能很好地理解“请点击那个蓝色的提交按钮”、“在表格第二行找到金额大于100的条目”这类操作指令。我们不需要它进行天马行空的创作,就需要它精准地理解任务并输出结构化的操作步骤,这一点Phi-3-mini做得很好。

最后,128k的上下文长度是巨大优势。自动化测试的一个场景是,需要让AI记住之前操作的历史和当前页面的状态(比如一堆HTML元素或截图信息),上下文短了根本记不住。128k的超长上下文,足以我们把一个复杂任务的多次交互历史、页面DOM树片段都喂给它,让它做出更连贯的决策。

注意:模型下载建议直接从Hugging Face的官方仓库(microsoft/Phi-3-mini-128k-instruct)获取。网上有些第三方转换的GGUF格式版本,虽然方便CPU运行,但可能会丢失一些原始模型的细微调优特性,对于指令跟随的准确性可能有影响。我建议优先使用原版Transformers格式。

2.2 OpenClaw:不只是另一个自动化框架

OpenClaw在国内开发者社区的热度最近在快速上升,但资料还相对零散。它本质上是一个AI驱动的自动化代理框架,和纯粹的录制回放工具(如Selenium IDE)或库(如Playwright)有本质区别。它的核心思想是提供一个标准化的“环境”给AI模型,AI通过这个环境去感知(获取屏幕截图、DOM、控件树)和行动(点击、输入、滚动)。

我选择OpenClaw而不是自己从头造轮子,主要看中两点:一是抽象层次好。它把与操作系统、浏览器交互的脏活累活都封装好了,提供了统一的Action接口(如click,type,scroll),我们只需要关心给AI什么输入,以及解析AI的输出成这些Action。二是正在快速迭代,社区活跃。虽然它现在可能还有些小bug,但你能看到它在积极融合MCP(Model Context Protocol)等新协议,生态在扩大,这意味着未来的可扩展性更强。

部署OpenClaw,官方推荐用Docker,这确实是最省心的方式。但如果你像我一样,需要在本地开发调试,或者宿主机环境特殊,手动部署就不可避免。这里有个大坑:依赖冲突。OpenClaw的Python环境依赖比较新,比如它可能要求playwright>=1.40,而你的老项目可能还锁在1.38。最稳妥的办法是使用虚拟环境(venv或conda)为OpenClaw单独创建一个纯净的环境。

# 创建并激活虚拟环境 python -m venv openclaw_env source openclaw_env/bin/activate # Linux/Mac # openclaw_env\Scripts\activate # Windows # 克隆仓库并安装核心依赖 git clone https://github.com/openclaw-ai/openclaw.git cd openclaw pip install -e . # 可编辑模式安装,方便修改源码 playwright install # 安装浏览器驱动

部署完成后,一定要跑一下它的示例脚本,验证最基本的环境感知(比如截图)和动作执行(比如打开浏览器点击)是否正常。很多时候问题出在系统权限(如Linux下对/dev/shm的访问)或缺失的系统库(如libgtk-3)上。

2.3 环境联调:让AI和OpenClaw握手

单独部署好模型和框架只是第一步,关键是要让它们能协同工作。这里的架构通常是:你的主控Python脚本作为大脑,负责调度。它加载Phi-3模型,接收任务描述,调用OpenClaw的API获取当前环境状态(截图+可访问性树),将“任务描述+环境状态”组合成Prompt送给模型,解析模型返回的下一步操作指令,再通过OpenClaw的API执行操作。如此循环,直到任务完成。

我设计了一个最简单的连接示例:

# 文件名:ai_driver.py import torch from transformers import AutoModelForCausalLM, AutoTokenizer from openclaw import OpenClawClient # 假设OpenClaw提供了Python SDK class AITestDriver: def __init__(self, model_path, openclaw_server_url="http://localhost:8000"): # 加载Phi-3模型和分词器 self.tokenizer = AutoTokenizer.from_pretrained(model_path) self.model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.float16, device_map="auto" # 自动分配GPU/CPU ) # 连接OpenClaw服务 self.claw = OpenClawClient(openclaw_server_url) def think_and_act(self, task_description): """核心循环:感知-思考-行动""" # 1. 感知:通过OpenClaw获取当前环境状态 # 通常包括截图(base64)和可访问性树(XML/JSON) state = self.claw.get_state() screenshot_info = state.get('screenshot') dom_tree = state.get('dom', '') # 2. 思考:构建Prompt,让模型决定下一步做什么 prompt = self._build_prompt(task_description, screenshot_info, dom_tree) inputs = self.tokenizer(prompt, return_tensors="pt").to(self.model.device) with torch.no_grad(): outputs = self.model.generate(**inputs, max_new_tokens=200) response = self.tokenizer.decode(outputs[0], skip_special_tokens=True) # 3. 解析:从模型回复中提取结构化操作指令 action, params = self._parse_model_response(response) # 4. 行动:通过OpenClaw执行操作 if action and action != 'stop': result = self.claw.execute_action(action, **params) return result, action else: return None, 'stop' def _build_prompt(self, task, screenshot_info, dom): # 这里是Prompt工程的关键,直接决定模型表现 # 一个简单的示例: system_msg = "你是一个自动化测试助手。根据当前屏幕信息和DOM树,决定下一步操作来完成用户任务。只输出JSON格式:{\"action\": \"click\"|\"type\"|\"scroll\"|\"stop\", \"params\": {...}}" user_msg = f"任务:{task}\n当前DOM摘要:{dom[:2000]}...\n请决定下一步操作。" return f"{system_msg}\n\n{user_msg}" def _parse_model_response(self, response): # 尝试从回复中解析JSON,这里需要健壮的解析逻辑 import json try: # 假设模型回复是纯JSON,或者包含在```json ```块中 if '```json' in response: json_str = response.split('```json')[1].split('```')[0].strip() else: json_str = response.strip() data = json.loads(json_str) return data.get('action', 'stop'), data.get('params', {}) except json.JSONDecodeError: # 解析失败时的降级策略,例如尝试关键词匹配 if '点击' in response or 'click' in response.lower(): # 这里可以加入更复杂的逻辑来提取坐标或选择器 return 'click', {'selector': 'button:has-text("提交")'} # 示例 return 'stop', {}

这个示例虽然简单,但勾勒出了核心循环。在实际项目中,_build_prompt_parse_model_response这两个函数是需要你花80%精力去打磨的,它们直接决定了整个系统的智能程度和可靠性。

3. 批量执行Python脚本的工程化实践

有了能单步思考和执行的基础框架,下一步就是如何规模化,批量地运行我们已有的或新生成的Python测试脚本。这里的“批量”不是简单用os.system循环调用,而是要融入调度、监控、报告和错误处理。

3.1 脚本管理与参数化驱动

首先,你的Python测试脚本需要被“改造”以适应AI驱动的环境。传统的脚本可能直接硬编码了定位器和操作。在新的范式下,脚本更应该被看作一个由高级任务描述构成的清单,或者是一个生成此类清单的模板

我推荐两种模式:

  1. 描述文件驱动:为每个测试场景创建一个YAML或JSON文件,描述测试步骤。主控程序读取这些文件,将每一步的描述喂给AI驱动。

    # test_login.yaml name: "用户登录测试" steps: - description: "在浏览器中打开登录页 https://example.com/login" action: "navigate" params: url: "https://example.com/login" - description: "在用户名输入框中输入测试账号 'test_user'" action: "type" # 这里可以不写死选择器,让AI根据描述去找 # params: # selector: "#username" - description: "在密码输入框中输入密码 'password123'" action: "type" - description: "点击‘登录’按钮" action: "click"

    然后,你的主控程序遍历这些steps,将description和当前状态发给AI,由AI决定具体操作参数。这样,即使页面元素变了,只要描述语义不变,AI就有可能找到新的正确元素。

  2. 脚本模板+变量替换:对于更复杂的逻辑(如循环、条件判断),可以维护Python脚本模板,使用Jinja2等模板引擎,将需要AI决策的点作为变量。运行前,由AI或规则引擎填充这些变量。

    # template_test_order.py.j2 def test_order(): open_url("{{ login_url }}") login("{{ username }}", "{{ password }}") # AI决策点:如何找到商品并加入购物车? product_to_select = decide_product_from_list() # 这个函数内部调用AI add_to_cart(product_to_select) proceed_to_checkout()

3.2 任务队列与并行执行控制

当你有成百上千个测试用例或脚本需要运行时,串行执行是不可接受的。我们需要引入任务队列。我个人偏好使用RQ(Redis Queue)或Celery,因为它们轻量且与Python集成好。架构是这样的:一个任务分发器(Dispatcher)负责读取所有待执行的脚本/描述文件,将其封装成任务,推送到Redis队列中。多个工作进程(Worker)从队列中取出任务执行,每个Worker都包含一个完整的AI驱动环境(即加载了Phi-3模型和OpenClaw客户端)。

这里的关键是环境隔离。每个Worker必须有自己的独立环境,包括浏览器实例、模型实例(如果GPU内存够,可以共享加载,但要注意并发锁)和临时目录。否则,并行任务会相互干扰,比如一个任务关闭了浏览器导致另一个任务失败。

# worker.py 示例 import redis from rq import Worker, Queue, Connection from ai_driver import AITestDriver # 上一节定义的类 listen = ['default'] redis_conn = redis.from_url('redis://localhost:6379') def run_test_task(task_description_file): """每个Worker执行单个任务""" driver = AITestDriver(model_path='./phi3-model') driver.claw.launch_browser(headless=False) # 启动浏览器 with open(task_description_file, 'r') as f: steps = yaml.safe_load(f)['steps'] for step in steps: result, action = driver.think_and_act(step['description']) if action == 'stop' and result is None: print(f"任务提前终止于步骤:{step['description']}") break # 可以在这里加入检查点,验证操作结果是否符合预期 # assert "登录成功" in driver.claw.get_page_text() driver.claw.close_browser() return {"status": "success", "task_file": task_description_file} if __name__ == '__main__': with Connection(redis_conn): worker = Worker(list(map(Queue, listen))) worker.work()

使用supervisorsystemd来管理这些Worker进程的启动和守护,确保它们挂了能自动重启。

3.3 结果收集、报告与错误自愈

批量执行最怕的就是黑盒,跑完了不知道哪些成功哪些失败,为什么失败。一个健壮的体系必须包含完善的结果收集。

  1. 结构化日志:不要只用print。每个Worker在执行时,应该将关键事件(开始任务、AI决策、执行动作、遇到错误)以结构化的格式(如JSON)写入日志文件或直接发送到中央日志服务(如ELK栈或Loki)。日志里必须包含任务ID、时间戳、截图(在错误时)、AI的原始回复和解析后的操作。
  2. 测试报告生成:任务完成后,Worker应生成一个标准格式(如JUnit XML、Allure JSON)的报告片段。由一个汇总服务收集所有片段,生成统一的HTML测试报告,清晰展示通过率、失败用例、错误截图和日志。
  3. 错误自愈与重试机制:这是AI驱动自动化的优势所在。当某个步骤失败(比如AI点击了错误元素),不应该立即标记整个用例失败。可以设计一个重试策略:将失败时的屏幕状态和错误信息,再次作为输入喂给AI,Prompt改为“上一步操作失败了,错误是‘元素未找到’,请重新评估当前界面并尝试另一种方式完成任务”。AI可能会尝试点击另一个相似按钮,或者先滚动再查找。我通常会给每个步骤2-3次重试机会。
def execute_step_with_retry(driver, step_description, max_retries=3): for attempt in range(max_retries): result, action = driver.think_and_act(step_description) if action == 'stop' and result is None: if attempt < max_retries - 1: # 重试前,可以让AI先总结一下失败原因 diagnosis_prompt = f"上一步尝试完成‘{step_description}’时似乎失败了,当前状态依旧。请分析可能的原因并给出新的方案。" # 这里可以调用一个专门的“诊断”模型或模式 continue else: raise StepFailedError(f"步骤‘{step_description}’重试{max_retries}次后仍失败。") # 执行成功,验证结果 if validate_step_result(result): return result else: # 结果验证失败,也触发重试 continue raise StepFailedError(f"步骤‘{step_description}’结果验证失败。")

4. 提示工程与模型调优实战心得

整个系统的智能核心在于你如何与Phi-3模型对话,也就是Prompt Engineering。经过大量实测,我总结出了几个非常有效的模式。

4.1 结构化Prompt模板设计

给AI的指令必须清晰、结构化,并且要把它当成一个“有点死板但很听话的新手操作员”。我的Prompt通常分为四部分:

  1. 角色与能力定义:明确告诉模型它现在是什么角色,具备什么能力。
  2. 当前环境状态:以文本形式描述屏幕上的关键信息(从DOM树中提取),并附上截图的存在性提示(注意,我们通常不直接把Base64图片传给文本模型,而是告诉它“有一张当前界面的截图可供参考”,具体截图可以通过多模态模型处理或作为上下文元数据)。
  3. 任务目标与历史:清晰说明这一步要干什么,以及之前几步做了什么(提供简短历史)。
  4. 输出格式约束:强制要求模型以指定格式(如JSON)回复,只包含允许的动作类型和参数。

一个高级别的Prompt示例:

你是一个专业的Web自动化测试助手。你可以通过OpenClaw框架执行点击(click)、输入(type)、滚动(scroll)、等待(wait)、导航(navigate)等操作。 当前浏览器页面是一个电商网站的商品列表页。页面顶部有一个搜索框,下方有多个商品卡片,每个卡片包含商品图片、名称、价格和一个“加入购物车”按钮。屏幕中央有一个横幅广告。 刚刚完成的操作是:打开了浏览器并导航到了这个页面。 当前任务目标是:找到第一个价格低于50元的商品,并点击它的“加入购物车”按钮。 请根据以上描述,决定下一步最合适的操作。你只能输出一个JSON对象,格式如下: { "reasoning": "简要说明你为什么选择这个操作(1-2句话)", "action": "click | type | scroll | wait | navigate | stop", "params": { // 根据action不同,参数不同 // click: {"selector": "CSS选择器或文本内容", "x": 可选坐标, "y": 可选坐标} // type: {"selector": "输入框选择器", "text": "要输入的文本"} // scroll: {"direction": "up|down|left|right", "amount": 像素数或"page"} // wait: {"time": 秒数, "for": "等待的元素选择器(可选)"} // navigate: {"url": "目标网址"} // stop: {} } }

这种结构化的Prompt能极大提高模型输出动作的准确性和可解析性。reasoning字段对于调试和后续分析AI的决策过程非常有用。

4.2 上下文管理与历史压缩

Phi-3-mini有128k上下文,但也不是无限的。一个漫长的测试流程,每一步的交互都追加到上下文里,很快就会耗尽。我们必须管理上下文。

我的策略是滑动窗口摘要。不是保留所有原始历史,而是维护一个“摘要”。每完成5-10步,就让模型(或者用一个更小的、专门做摘要的模型)对之前的历史进行总结,生成一段简短的摘要文本(例如:“用户已登录,浏览了手机分类,将一款价值2999元的手机加入了购物车,目前处于购物车页面。”)。然后将这个摘要作为新的“历史”放入后续步骤的Prompt中,替换掉冗长的原始记录。这样可以始终保持上下文在可控长度内,同时不丢失关键的任务状态信息。

4.3 针对领域任务的微调(可选但有效)

如果你有大量的、特定领域的自动化操作日志(比如操作公司内部CRM系统的日志),那么用这些数据对Phi-3-mini进行轻量级的微调(LoRA),效果会有显著提升。微调的目标是让模型更熟悉你业务中的特有名词、UI组件命名和操作模式。

例如,你可以收集一批成功的操作序列:{“屏幕状态描述”: “CRM联系人列表页”, “指令”: “找到名叫张三的客户并点击进入详情”, “动作”: {“action”: “click”, “params”: {“selector”: “tr:has-text(‘张三’)”}}}。用几百条这样的数据做LoRA微调,模型在你特定系统上的表现会稳定得多。这相当于为它注入了“领域知识”。

5. 避坑指南与性能优化

在实际项目中趟过的坑,比任何文档都宝贵。这里分享几个让我熬夜最多的点。

5.1 稳定性陷阱与超时控制

AI模型生成的内容具有不确定性,可能偶尔会“胡言乱语”,输出一个无法解析的指令,或者陷入死循环(比如反复滚动页面)。因此,必须在主控循环中加入严格的超时和熔断机制

  • 单步超时:给model.generateclaw.execute_action都设置超时。如果模型思考超过10秒,或者执行动作超过30秒没反应,就中断当前步骤,触发重试或失败处理。
  • 循环检测:记录最近N步的操作序列。如果检测到完全相同的操作(如scroll down)连续出现超过3次,很可能陷入了循环,应中断任务并报警。
  • 异常回复处理:在_parse_model_response函数里,要对模型回复做健壮性检查。除了JSON解析,还要检查action是否在允许列表中,params是否包含必需的字段。对于无法处理的回复,可以回退到一个预定义的“安全操作”,比如{"action": "wait", "params": {"time": 2}},让系统暂停一下,然后重新获取状态并思考。

5.2 选择器生成与元素定位的可靠性

模型在回复中需要给出操作的具体参数,比如点击的CSS选择器。让模型直接生成精确的CSS选择器(如#main > div.content > button:nth-child(3))是非常容易出错的。更好的做法是:

  1. 让OpenClaw提供候选元素列表:在get_state()时,不仅返回DOM树,还让OpenClaw返回一个当前页面所有可交互元素的列表,每个元素附带其可读的文本、角色、位置以及一个稳定的后端标识符(如xpathaccessibility id)。
  2. 让AI做选择题:在Prompt中,将这个元素列表(只包含关键属性,如文本、角色)提供给模型。模型的输出动作参数里,不再是一个原始的CSS选择器字符串,而是这个列表中的索引或ID。然后由主控程序将这个ID映射回真正的定位器,再交给OpenClaw执行。这大大降低了模型输出的复杂度,提高了可靠性。

5.3 性能瓶颈分析与优化

当批量执行几十个任务时,性能问题会凸显。主要瓶颈在两方面:

  1. 模型推理速度:Phi-3-mini在GPU上推理一次(生成200个token)大约需要1-3秒。如果每个测试步骤都要调用,累积起来时间很长。优化方法:

    • 缓存:对于相同的“状态+指令”输入,输出很可能相同。可以建立一个简单的哈希缓存,避免重复调用模型。
    • 步骤聚合:对于一些简单的、连续的操作(比如在表单中连续输入多个字段),可以在一个Prompt里让模型规划多个步骤(输出一个动作列表),然后批量执行,减少模型调用次数。
    • 使用量化模型:将模型转换为INT8或GPTQ量化格式,能显著提升推理速度,对精度损失很小,非常适合自动化这种任务。
  2. 浏览器与OpenClaw开销:每个Worker一个浏览器实例很耗资源。

    • 使用无头模式:在不需要观察的测试环境中,务必使用headless=True模式,能节省大量内存和GPU资源。
    • 浏览器实例复用:在一个Worker内,一个浏览器实例可以顺序执行多个测试任务(注意清理Cookies和LocalStorage),而不是每个任务都开一个新的。
    • 调整OpenClaw的轮询间隔:OpenClaw获取屏幕状态可能有默认的轮询频率。如果不是对实时性要求极高,可以适当调低这个频率,减少系统开销。

5.4 常见错误与快速排查表

错误现象可能原因排查步骤
模型输出乱码或无关内容Prompt格式不对,或系统提示词被淹没检查发送给模型的完整Prompt,确保系统指令在开头且清晰。尝试在Prompt开头加上“### 系统指令 ###”等强分隔符。
OpenClaw执行动作失败,报“元素未找到”AI生成的选择器不对,或页面尚未加载完成1. 检查AI回复中的选择器是否合理。2. 在执行动作前增加一个wait动作,等待元素出现。3. 让OpenClaw返回更丰富的元素列表供AI选择。
任务陷入无限循环AI决策逻辑陷入局部最优,或状态感知失效1. 检查上下文历史,看是否状态描述重复。2. 实现循环检测机制,强制跳出。3. 在Prompt中强调“避免重复操作”。
批量任务中部分Worker卡死资源竞争(如GPU内存溢出)或浏览器崩溃1. 监控GPU内存使用。2. 为每个Worker设置任务超时和看门狗。3. 检查浏览器进程是否残留,实现进程清理脚本。
测试结果不稳定,时好时坏页面加载时间波动,或AI决策的随机性1. 在关键断言前增加显式等待。2. 对于AI决策,可以引入“投票机制”,同一状态让模型思考多次,取多数票的动作执行。3. 适当降低模型生成时的temperature参数,减少随机性。

最后,我想说的是,将Phi-3-mini和OpenClaw结合用于自动化测试,目前还是一个前沿的、需要大量调试和适配的领域。它不会完全替代你精心编写的、高稳定性的单元测试或API测试。但它为处理那些变化频繁、逻辑复杂、传统自动化维护成本极高的UI场景,打开了一扇新的大门。我的体会是,初期投入在Prompt工程和系统稳定性上的时间会比较多,但一旦流程跑通,对于应对频繁的UI变更和探索性测试,它能带来的长期收益是显著的。先从一个小而具体的场景开始尝试,比如自动登录或者填写一个复杂表单,积累经验后再逐步扩大范围,这样更容易获得正反馈并坚持下去。