基于AI Agent与LangChain实现自然语言测试用例自动化执行

发布时间:2026/6/26 22:24:14
基于AI Agent与LangChain实现自然语言测试用例自动化执行 1. 项目概述当手工测试遇见AI智能体作为一名在软件测试领域摸爬滚打了十多年的老兵我经历过从纯手工“点点点”到自动化脚本再到如今DevOps流水线中各种测试工具的洗礼。测试工程师的日常总绕不开一个永恒的痛点那些数量庞大、逻辑复杂、变更频繁的手工测试用例。它们像是测试资产库里的“不动产”价值很高但“流动性”极差——执行起来耗时费力维护成本高昂且严重依赖测试人员的经验和状态。最近随着AI Agent智能体技术的爆火尤其是看到各种“十大智能体排名”、“AI Agent开发框架”的讨论一个想法在我脑子里越来越清晰能不能让AI智能体来“读懂”并“执行”这些手工测试用例实现真正的智能化测试执行这个想法并非空穴来风。我们团队维护着上千条手工测试用例覆盖了核心业务流程、边缘场景和兼容性测试。每次回归哪怕只执行其中一部分也需要投入大量人力而且难免会有疏漏或执行标准不一的问题。传统的自动化测试框架如Selenium、Appium虽然能解决一部分问题但它们对测试用例有严苛的要求——需要写成结构化的脚本。而我们的手工用例大多是自然语言描述的Word或Excel文档比如“在登录页面输入错误的密码点击登录按钮验证提示信息为‘密码错误’”。让AI来理解并执行这样的描述听起来就像给测试工作装上了“大脑”和“双手”。于是我决定启动一个探索性项目基于现有的AI Agent开发框架构建一个能够理解自然语言手工测试用例并驱动浏览器或应用程序进行自动化执行的智能体系统。这不仅仅是“用AI写自动化脚本”而是让AI直接成为测试执行者动态解析、决策并操作。接下来我将详细拆解整个项目的设计思路、核心技术选型、实操步骤以及踩过的那些坑希望能给同样被手工测试所困的同仁们一些切实可行的参考。2. 核心思路与框架选型为什么是AI Agent框架在决定动手之前我花了大量时间研究市面上的技术方案。最直接的想法可能是用大语言模型LLM的API写个程序去解析测试用例然后调用Selenium。但这很快会遇到问题大模型输出不稳定它告诉你“应该点击登录按钮”但你怎么把这个指令转换成Selenium WebDriver能执行的find_element(By.ID, “loginBtn”).click()你需要一个能持续理解上下文、进行多轮决策、并具备工具调用能力的“智能体”。2.1 为什么传统自动化框架不够用传统的UI自动化框架如Selenium、Cypress、Playwright本质上是“脚本回放器”。它们需要精确的、预先编写好的指令序列和元素定位器。而手工测试用例是高度抽象和描述性的存在大量模糊空间。例如“在商品列表页找到最新上架的商品并加入购物车”。这个“找到”依赖于对页面布局的理解和实时判断这是传统脚本难以应对的。我们需要一个能进行视觉理解、语义分析和实时规划的“智能”层这正是AI Agent所擅长的。2.2 AI Agent开发框架的核心价值AI Agent开发框架如LangChain、LlamaIndex、Dify、Coze等平台提供的智能体构建能力提供了一套标准化的“骨架”帮助我们快速组装一个具备以下能力的智能体记忆与上下文管理能够记住之前执行过的步骤、页面的状态以及测试用例的全局目标。工具调用Tool Calling这是最关键的能力。智能体可以调用我们为其“装备”的工具比如“打开浏览器”、“输入文本”、“点击元素”、“验证文本”。规划与决策将复杂的测试用例目标拆解成一系列可执行的动作步骤。自我反思与纠错当操作失败如元素未找到时能够分析原因并尝试替代方案。我的选型思路是不重复造轮子基于成熟框架进行领域适配。经过对比我选择了LangChain作为核心Agent框架并结合Playwright作为浏览器自动化工具。理由如下LangChain生态成熟社区活跃对工具调用、记忆管理和与多种大模型集成的支持非常好。它的“ReAct”推理行动范式非常适合测试执行这种需要多步决策的场景。Playwright相比SeleniumPlaywright的API更现代自动等待机制更健壮对现代Web应用单页应用支持更好且自带强大的元素定位和录制功能能为AI提供更丰富的页面上下文信息。大模型选择考虑到成本、响应速度和API稳定性我选择了OpenAI的GPT-4 Turbo作为核心的“大脑”。它在理解复杂指令和进行步骤拆解方面表现非常出色。对于内部或对成本敏感的场景也可以考虑使用开源的Llama 3 70B或DeepSeek等模型通过Ollama本地部署。注意框架选型没有绝对的对错关键看团队技术栈和需求。如果你团队更熟悉PythonLangChain是首选如果追求低代码快速搭建Dify、Coze这类平台也能通过工作流的方式组装智能体但它们对深度定制和集成到现有CI/CD流程的支持可能不如代码级框架灵活。3. 系统架构设计与核心组件拆解整个系统的设计目标是输入一段自然语言描述的手工测试用例输出测试执行结果成功/失败及详细的执行日志。为了实现这个目标我设计了如下分层架构3.1 架构总览整个系统可以划分为四个核心层用例理解与规划层由大模型驱动的AI Agent核心。负责解析自然语言用例生成初始执行计划。工具执行层封装了一系列可被AI Agent调用的原子操作工具如浏览器控制、元素操作、断言验证等。上下文感知层实时获取并处理应用程序的当前状态如页面URL、DOM结构、可视元素文本将其转化为AI能理解的上下文信息。控制与协调层协调整个执行流程处理异常管理执行状态并生成最终报告。[自然语言测试用例] | v [AI Agent (LLM 规划器)] --- [记忆存储器] | (调用工具) v [工具集(Toolkit)] | (驱动) v [浏览器(Playwright)] --- [被测应用] | v [结果与日志收集] --- [测试报告]3.2 核心组件详解3.2.1 AI Agent与规划器Planner这是系统的大脑。我使用LangChain的AgentExecutor和create_react_agent来构建智能体。但关键的改进在于自定义规划器Planner。单纯的ReAct Agent在面对长流程测试时容易迷失或做出不合理规划。因此我设计了一个两阶段规划策略第一阶段高层任务分解。让大模型先将整个测试用例分解为几个关键阶段。例如对于“用户登录后搜索商品并下单”这个用例分解为1. 用户登录2. 搜索目标商品3. 进入商品详情页并加入购物车4. 完成结算流程。第二阶段单步动作生成。在执行每个阶段时Agent再根据当前页面上下文规划出具体的下一个动作如click(‘登录按钮’),type(‘搜索框’ ‘手机’)。这样做的优点是避免了Agent一开始就陷入细节提高了规划的成功率和执行效率。规划器的提示词Prompt需要精心设计明确告诉模型它的角色一个QA测试工程师、可用的工具以及输出的格式要求。3.2.2 工具集Toolkit的设计工具是AI Agent的“手和脚”。我基于Playwright封装了以下核心工具函数并通过LangChain的tool装饰器暴露给Agentnavigate_to(url: str): 导航到指定URL。click(description: str): 根据描述点击元素。描述可以是文本内容如“登录按钮”、CSS选择器或XPath。内部实现会尝试多种定位策略。type_text(description: str, text: str): 在描述的元素中输入文本。get_page_text(): 获取当前页面的主要文本内容作为上下文提供给Agent。extract_element_info(description: str): 获取特定元素的详细信息如标签、属性、位置用于辅助定位。assert_text_present(text: str): 断言页面上存在某段文本。take_screenshot(): 对当前页面截图用于失败分析和报告。工具设计的核心挑战在于元素定位。手工用例中的描述如“点击提交按钮”是模糊的。我的策略是“描述优先选择器兜底”首先工具函数会尝试通过page.get_by_text()或page.get_by_role()等Playwright的语义化定位器来查找元素。如果失败则利用大模型将当前页面DOM摘要和元素描述结合起来让模型推荐一个更精确的CSS选择器或XPath然后进行重试。在工具函数内部加入重试和等待逻辑以应对页面加载或元素渲染延迟。3.2.3 上下文构建与记忆管理AI Agent需要知道“我在哪”、“我做了什么”。上下文信息主要包括页面文本摘要通过get_page_text()获取但全量DOM文本可能太长。我实现了一个摘要函数使用轻量级NLP方法如提取标题、按钮文本、输入框标签等关键信息生成页面概要。执行历史LangChain的ConversationBufferMemory可以存储Agent与用户的对话历史在这里是动作序列和结果。我将其扩展为存储完整的测试步骤日志。自定义状态维护一个全局字典记录当前登录用户、购物车商品数等业务状态这些信息可以作为后续步骤的判断依据。4. 实操搭建从零构建你的测试AI智能体理论讲完我们来点实际的。下面是我搭建核心系统的关键步骤和代码片段。假设你已经有一个Python环境3.8并安装了基本依赖。4.1 环境准备与依赖安装首先创建项目并安装核心库# 创建项目目录 mkdir ai-test-agent cd ai-test-agent python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate # 安装核心依赖 pip install langchain langchain-openai playwright # LangChain核心、OpenAI集成、浏览器自动化 pip install python-dotenv # 用于管理环境变量如API Key pip install pydantic # 数据验证 # 安装Playwright浏览器 playwright install chromium4.2 构建核心工具类我们首先封装一个基于Playwright的浏览器操作类这是所有工具的基础。# browser_controller.py import asyncio from playwright.async_api import async_playwright, Page from typing import Optional class BrowserController: def __init__(self): self.playwright None self.browser None self.page: Optional[Page] None self.context None async def start(self, headless: bool False): 启动浏览器实例 self.playwright await async_playwright().start() self.browser await self.playwright.chromium.launch(headlessheadless) self.context await self.browser.new_context(viewport{width: 1920, height: 1080}) self.page await self.context.new_page() print(浏览器启动成功) async def navigate(self, url: str): 导航到指定URL if not self.page: raise RuntimeError(浏览器未启动) await self.page.goto(url) # 等待页面基本加载完成 await self.page.wait_for_load_state(networkidle) print(f已导航至: {url}) async def get_page_text_summary(self) - str: 获取页面关键文本摘要简化版 if not self.page: return # 获取所有可见文本 all_text await self.page.evaluate(() { const elements document.querySelectorAll(body *:not(script):not(style)); let texts []; elements.forEach(el { if (el.offsetWidth 0 el.offsetHeight 0) { const text el.innerText?.trim(); if (text text.length 1) { texts.push(text); } } }); return Array.from(new Set(texts)).join( | ); }) # 简单截断防止上下文过长 return all_text[:2000] if len(all_text) 2000 else all_text async def close(self): 关闭浏览器 if self.browser: await self.browser.close() if self.playwright: await self.playwright.stop() print(浏览器已关闭) # 注意为了与LangChain的同步接口兼容实际使用时可能需要同步包装或使用LangChain的异步支持。4.3 实现LangChain工具接下来我们将浏览器操作封装成LangChain能识别的工具。这里以click和assert_text_present为例。# test_agent_tools.py import os from langchain.tools import tool from langchain_core.messages import HumanMessage from browser_controller import BrowserController from typing import Type from pydantic import BaseModel, Field import asyncio # 声明一个全局的浏览器控制器实例生产环境建议用更好的方式管理 _browser_controller BrowserController() # 定义工具的输入Schema这能帮助大模型更好地理解如何调用工具 class ClickInput(BaseModel): element_description: str Field(description对要点击的元素的描述如‘登录按钮’、‘提交表单’) class AssertTextInput(BaseModel): expected_text: str Field(description期望在页面上出现的文本内容) tool(args_schemaClickInput) def click_tool(element_description: str) - str: 根据描述点击页面上的一个元素。 描述可以是按钮文本、链接文字或其他可识别特征。 # 由于Playwright是异步的我们需要在同步工具函数中运行异步代码 async def _async_click(): page _browser_controller.page if not page: return 错误页面未就绪 # 策略1优先使用Playwright的文本定位器 try: await page.get_by_text(element_description, exactFalse).first.click(timeout5000) return f成功点击描述为‘{element_description}’的元素 except Exception as e1: print(f文本定位失败: {e1}) # 策略2尝试通过角色定位如button try: await page.get_by_role(button, nameelement_description).click(timeout5000) return f成功通过角色点击‘{element_description}’ except Exception as e2: # 策略3获取页面上下文让模型辅助定位简化示例 page_text await _browser_controller.get_page_text_summary() # 这里可以调用一个LLM来根据描述和页面文本生成选择器为简化直接返回失败 return f无法根据描述‘{element_description}’找到可点击元素。当前页面关键词{page_text[:500]}... loop asyncio.get_event_loop() return loop.run_until_complete(_async_click()) tool(args_schemaAssertTextInput) def assert_text_present_tool(expected_text: str) - str: 验证指定的文本是否出现在当前页面上。 async def _async_assert(): page_text await _browser_controller.get_page_text_summary() if expected_text in page_text: return f验证成功页面上存在文本‘{expected_text}’ else: return f验证失败未在页面上找到文本‘{expected_text}’。当前页面内容摘要{page_text[:1000]} loop asyncio.get_event_loop() return loop.run_until_complete(_async_assert()) # 同样地可以封装 navigate_to, type_text 等工具4.4 组装AI Agent并定义执行流程现在我们将工具、模型和记忆组装起来创建智能体。# test_agent_executor.py import os from langchain import hub from langchain.agents import create_react_agent, AgentExecutor from langchain_openai import ChatOpenAI from langchain.memory import ConversationBufferMemory from test_agent_tools import click_tool, assert_text_present_tool # 导入所有工具 from browser_controller import BrowserController import asyncio from dotenv import load_dotenv load_dotenv() # 加载环境变量OPENAI_API_KEY应放在.env文件中 class TestCaseAIAgent: def __init__(self): # 1. 初始化大模型 self.llm ChatOpenAI( modelgpt-4-turbo-preview, temperature0.1, # 低温度保证输出稳定 api_keyos.getenv(OPENAI_API_KEY) ) # 2. 准备工具列表 self.tools [click_tool, assert_text_present_tool] # 添加所有定义好的工具 # 3. 从LangChain Hub拉取一个优化的ReAct提示词也可以自定义 self.prompt hub.pull(hwchase17/react-chat) # 修改提示词明确测试工程师的角色和任务 self.prompt.messages[0].prompt.template 你是一个专业的QA测试工程师负责执行手工测试用例。你的任务是根据给定的测试用例步骤操作浏览器来完成测试。 你可以使用以下工具 {tools} 请严格按照测试用例的描述来执行操作。在行动前先思考当前页面状态和下一步最适合的操作。 如果你认为测试步骤已经完成或遇到无法解决的问题请用中文回复“测试执行结束”或说明原因。 之前的对话历史 {chat_history} 测试用例{input} 开始执行 {agent_scratchpad} # 4. 创建记忆保存对话历史即执行步骤 self.memory ConversationBufferMemory(memory_keychat_history, return_messagesTrue) # 5. 创建智能体 self.agent create_react_agent(llmself.llm, toolsself.tools, promptself.prompt) # 6. 创建执行器 self.agent_executor AgentExecutor( agentself.agent, toolsself.tools, memoryself.memory, verboseTrue, # 打印详细执行过程便于调试 handle_parsing_errorsTrue, # 处理解析错误 max_iterations15 # 防止无限循环 ) # 7. 浏览器控制器 self.browser BrowserController() async def execute_test_case(self, test_case_description: str, start_url: str None): 执行一个测试用例 print(f\n 开始执行测试用例 ) print(f用例描述: {test_case_description}) # 启动浏览器并导航到起始页 await self.browser.start(headlessFalse) # 调试时设为False看浏览器操作 if start_url: await self.browser.navigate(start_url) try: # 将测试用例描述和初始页面信息作为输入 initial_context f测试用例{test_case_description} if start_url: initial_context f\n起始页面{start_url}。请从当前页面开始执行。 # 执行Agent result await self.agent_executor.ainvoke({input: initial_context}) print(f\n 执行结果 ) print(result[output]) except Exception as e: print(f执行过程中发生错误: {e}) finally: # 执行完毕可以关闭浏览器或保持打开用于检查 # await self.browser.close() print(执行流程结束。) # 主程序 async def main(): agent_system TestCaseAIAgent() # 示例测试用例 test_case 1. 在登录页面输入用户名‘testuser’。 2. 输入错误密码‘wrongpass’。 3. 点击登录按钮。 4. 验证页面是否出现‘密码错误’的提示信息。 start_url https://example.com/login # 替换为你的测试登录页地址 await agent_system.execute_test_case(test_case, start_url) if __name__ __main__: asyncio.run(main())5. 核心挑战与优化策略实录在实际搭建和运行过程中我遇到了不少预料之中和预料之外的挑战。下面分享几个最典型的“坑”以及我的解决方案。5.1 挑战一元素定位的模糊性与鲁棒性问题手工用例描述“点击登录按钮”但页面上可能有多个按钮包含“登录”文本或者元素是图标按钮没有文本。Agent调用click_tool(“登录按钮”)后工具函数可能点击了错误的元素或直接失败。解决方案我采用了“多策略融合定位”和“上下文增强定位”。多策略融合在click_tool内部按优先级尝试page.get_by_role(“button”, name“登录”)(ARIA角色)page.get_by_text(“登录”)(模糊文本匹配)page.locator(“button:has-text(‘登录’)”)(CSS 文本)利用Playwright的locator(“input[type‘submit’]”)等基于属性的定位。上下文增强当基础定位失败时工具函数会调用一个“元素定位辅助模型”一个轻量级的LLM调用例如GPT-3.5-turbo将当前页面DOM摘要和元素描述发送给它请求它返回一个更精确的CSS选择器或XPath。这显著提高了复杂场景下的定位成功率。视觉辅助进阶对于纯图标或难以用代码描述的元素可以集成视觉AI模型如基于SIFT的特征匹配或轻量级CNN通过截图对比来定位元素。但这会大幅增加系统复杂度目前我仅在实验阶段尝试。5.2 挑战二测试步骤的规划与逻辑判断问题测试用例中常包含条件判断例如“如果库存大于0则显示‘立即购买’按钮否则显示‘到货通知’”。单纯的顺序动作规划无法处理这种逻辑。解决方案增强Agent的规划能力和工具集。引入“观察”工具增加一个observe_page_state(question: str)工具。Agent可以主动提问例如“当前商品库存数量是多少”或“‘立即购买’按钮是否可见”。该工具会从页面中提取相关信息通过DOM解析或OCR识别数字并返回给Agent供其决策。分阶段规划与检查点在高层任务分解后在每个阶段结束时强制Agent调用一个checkpoint(阶段目标)工具。该工具会验证阶段目标是否达成例如“是否成功跳转到商品详情页”如果未达成则触发重试或异常处理流程。使用支持复杂规划的高级框架探索使用更高级的Agent框架如LangChain的Plan-and-Execute模式或者微软的AutoGen它们对多智能体协作和复杂规划有更好的支持。5.3 挑战三执行效率与稳定性问题每一步操作都依赖大模型的推理和网络调用导致执行速度慢且API调用有成本和稳定性风险。优化策略缓存与记忆复用对于重复的测试步骤如登录将成功的动作序列工具调用链缓存下来。下次遇到类似场景可以直接复用缓存的“操作剧本”无需重新规划。本地轻量级模型对于元素定位辅助、页面状态判断等对推理能力要求不高的任务使用本地部署的小模型如经过微调的BERT分类模型或规则引擎减少对昂贵大模型的依赖。超时与重试机制在每个工具调用外层包裹健壮的重试和超时逻辑。对于网络波动或页面加载慢导致的失败自动重试数次。异步并行执行对于独立的测试用例集可以启动多个浏览器实例由多个Agent并行执行充分利用资源。6. 效果评估与未来展望经过一段时间的开发和调优这个原型系统已经能够处理我们约30%的冒烟测试级别的手工用例成功率完全自动执行通过在85%左右。对于剩下的用例失败原因主要在于1) 页面元素过于动态或复杂2) 用例描述存在严重歧义3) 涉及非UI操作如文件上传、短信验证码。带来的价值是显而易见的释放人力将测试工程师从重复、枯燥的手工执行中解放出来更专注于测试设计、探索性测试和复杂场景挖掘。提升覆盖率与一致性AI可以不知疲倦地执行用例确保每次回归的步骤完全一致避免了人为疏忽。加速反馈可以集成到CI/CD流水线中在代码提交后自动触发核心场景的验证。当然这远非终点。我认为这个方向还有巨大的演进空间从“执行者”到“设计者”未来的测试AI智能体或许不仅能执行用例还能基于需求文档或用户故事自动生成测试用例和场景实现测试左移。多模态能力融合结合视觉CV和语音ASR/TTS模型使其能处理图像验证码、语音提示等更真实的交互场景。自愈与自适应当应用UI发生变更时智能体能够通过对比历史截图和DOM结构自动学习新的元素定位方式更新“操作剧本”实现一定程度的自我维护。这个项目让我深刻体会到AI Agent不是要取代测试工程师而是成为一个强大的“数字同事”。它处理确定性的、重复的劳作而人类则专注于创造性的、战略性的、需要深度判断的工作。构建这样一个系统的过程本身也是对测试理论和自动化框架的重新思考。如果你也对此感兴趣不妨从一个小而具体的测试场景开始尝试比如“登录功能的异常流测试”一步步搭建你的第一个测试AI智能体。