news 2026/10/3 11:55:57

告别手写测试用例:用 LangGraph + MCP 多智能体自动生成 Selenium 测试套件,把模型端点改到 TaoToken

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
告别手写测试用例:用 LangGraph + MCP 多智能体自动生成 Selenium 测试套件,把模型端点改到 TaoToken

1. 从手写用例到多智能体协作:Selenium 测试套件自动生成到底解决什么问题

如果你写过 Selenium 脚本,大概率经历过这样的循环:需求评审刚结束,页面还没稳定,测试用例先堆了一堆;等 UI 一改版,几十个findElement集体失效,定位符改到怀疑人生。Selenium 依然是 Web 自动化测试的主力工具,但“需求分析 → 用例设计 → 脚本编写 → 调试优化”这条链路,在敏捷节奏下越来越吃力。

我试过把大模型直接拉进来写测试代码,结果也不理想:单次对话生成的脚本往往只覆盖正常路径,边界场景靠运气,元素定位策略单一,跑两次就挂。问题不在于模型能力,而在于“一个模型一次性干完所有事”这种模式本身就不适合测试生成这种多阶段、强依赖上下文的工程任务。

于是思路转向多智能体协作:把测试生成拆成需求解析、场景扩展、元素定位、脚本生成、质量校验几个专业角色,用 LangGraph 编排它们的状态流转,用 MCP 把浏览器操作、DOM 分析、代码检查这些工具能力标准化接入,再通过 TaoToken 统一承接模型调用通道。这样每个智能体只专注一件事,输出质量可控,流程可追踪。

这套方案适合谁?一是被 UI 回归测试维护成本压得喘不过气的测试工程师;二是想用 AI 提升研发效能、但不想把生产库直连给模型的团队;三是对 LangGraph、MCP 这些新架构感兴趣,想找一个真实落地场景练手的开发者。接下来我会把可复制的节点配置、MCP 服务声明、端点环境变量都摊开讲,最后跑一次从需求描述到本地执行验证的完整流程。

2. TaoToken 前置准备:统一 Key 与 API 通道,让多智能体调用模型不再散乱

多智能体系统最容易被低估的成本,是模型调用的管理。四个智能体如果各自配置不同的模型端点、不同的 Key,调试时你根本分不清是哪个节点出了问题。更麻烦的是,LangGraph 的节点函数里如果硬编码了某个厂商的 SDK,换模型就得改代码。

TaoToken 在这里的角色是统一通道:官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 提供控制台和文档,API 入口是 https://taotoken.net/api(不加 UTM)。你只需要在控制台创建一个 Key,然后在环境变量里配置 Base URL 和 Model ID,所有智能体节点共用这一套配置。这样 LangGraph 的每个节点函数都通过同一个 OpenAI 兼容接口调用模型,换模型只改环境变量,不动业务代码。

具体操作路径:进入控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 创建 API Key,然后在 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 复制你的 Key。模型对话调试可以用 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 先验证通道是否通。如果你后续要做长期编码或 Agent 任务,Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 有更详细的配额说明。

这里有个关键点:MCP 负责工具能力接入,TaoToken 负责模型调用通道,两者职责不要混。MCP 服务器里不要写模型 Key,模型调用统一走 LangGraph 节点里的 TaoToken 配置。这样你的架构是干净的:智能体逻辑在 LangGraph,工具能力在 MCP,模型通道在 TaoToken。

环境变量建议这样组织,放在项目根目录的.env文件里:

# TaoToken 统一模型通道 TAOTOKEN_BASE_URL=https://taotoken.net/api TAOTOKEN_API_KEY=sk-your-key-here TAOTOKEN_MODEL_ID=claude-3-5-sonnet-20241022 # MCP 服务配置 MCP_BROWSER_SERVER_URL=http://localhost:3001 MCP_DOM_ANALYZER_URL=http://localhost:3002 # 目标测试站点 TARGET_BASE_URL=https://your-app.example.com

注意TAOTOKEN_BASE_URL后面不要加/v1,OpenAI 兼容客户端会自动拼接。如果你用的是 LangChain 的ChatOpenAI,配置方式如下:

import os from langchain_openai import ChatOpenAI llm = ChatOpenAI( model=os.getenv("TAOTOKEN_MODEL_ID"), base_url=os.getenv("TAOTOKEN_BASE_URL"), api_key=os.getenv("TAOTOKEN_API_KEY"), temperature=0.2, )

temperature设低一点,测试代码生成需要稳定性,不需要创意。这个llm实例会被注入到 LangGraph 的每个节点函数里,四个智能体共用同一个通道,但可以通过不同的 system prompt 区分角色。

3. 可复制配置:LangGraph 节点、MCP 服务声明与 settings 片段

这一节是整篇的核心,我会把 LangGraph 的图结构、节点函数、MCP 服务声明、以及 Claude Code 的 settings 片段都写出来,你可以直接复制到项目里改。

先看 LangGraph 的状态定义和节点编排。测试生成流程的状态需要携带:原始需求、扩展后的场景列表、页面元素地图、生成的脚本、校验结果。

from typing import TypedDict, List, Dict, Any from langgraph.graph import StateGraph, END class TestGenState(TypedDict): requirement: str scenarios: List[Dict[str, Any]] element_map: Dict[str, Any] generated_scripts: List[str] validation_report: Dict[str, Any] retry_count: int def build_test_gen_graph(llm, mcp_client): graph = StateGraph(TestGenState) graph.add_node("analyze_requirement", make_analyze_node(llm)) graph.add_node("expand_scenarios", make_expand_node(llm)) graph.add_node("analyze_dom", make_dom_node(mcp_client)) graph.add_node("generate_scripts", make_generate_node(llm)) graph.add_node("validate_scripts", make_validate_node(llm, mcp_client)) graph.set_entry_point("analyze_requirement") graph.add_edge("analyze_requirement", "expand_scenarios") graph.add_edge("expand_scenarios", "analyze_dom") graph.add_edge("analyze_dom", "generate_scripts") graph.add_edge("generate_scripts", "validate_scripts") graph.add_conditional_edges( "validate_scripts", should_retry, {"retry": "generate_scripts", "done": END} ) return graph.compile() def should_retry(state: TestGenState) -> str: if state["validation_report"].get("passed"): return "done" if state["retry_count"] >= 2: return "done" return "retry"

每个节点函数的核心逻辑是:构造 prompt → 调用 TaoToken 通道 → 解析输出 → 更新状态。以需求分析节点为例:

def make_analyze_node(llm): def analyze_node(state: TestGenState) -> TestGenState: prompt = f"""你是一个测试需求分析专家。请解析以下需求,提取: 1. 被测功能模块 2. 核心验证点(不超过5个) 3. 前置条件 4. 预期结果 需求:{state['requirement']} 以 JSON 格式输出,字段为 module, verify_points, preconditions, expected_results。""" response = llm.invoke(prompt) import json parsed = json.loads(response.content) return {"scenarios": [parsed], "retry_count": 0} return analyze_node

MCP 服务声明部分,你需要一个mcp_config.json来告诉 LangGraph 的 MCP 客户端去哪里找工具服务:

{ "mcpServers": { "browser": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-puppeteer"], "env": { "PUPPETEER_LAUNCH_OPTIONS": "{\"headless\": true}" } }, "dom-analyzer": { "command": "python", "args": ["-m", "mcp_dom_analyzer.server"], "env": { "TARGET_BASE_URL": "${TARGET_BASE_URL}" } } } }

如果你用的是 Claude Code 来辅助开发这套系统,settings.json里需要配置 TaoToken 的接入信息。Claude Code 的配置文件路径通常在~/.claude/settings.json或项目级.claude/settings.json:

{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-your-key-here", "ANTHROPIC_MODEL": "claude-3-5-sonnet-20241022" }, "permissions": { "allow": ["Bash(python:*)", "Bash(npx:*)", "Read", "Write"] } }

注意 Claude Code 用的是ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY这两个变量名,不要和 OpenAI 兼容的变量混用。如果你同时用 Cline 或 Codex,它们的配置方式不同:Cline 在 VS Code 设置里填 Base URL 和 Key,Codex 的auth.json里写api_base和api_key。三件套永远是 Base URL + Key + Model ID,缺一不可。

MCP 客户端在 LangGraph 节点里的调用方式:

from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def call_mcp_tool(server_params, tool_name, arguments): async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() result = await session.call_tool(tool_name, arguments) return result

DOM 分析节点会调用 MCP 的浏览器工具打开目标页面,抓取 DOM 结构,然后让模型生成元素定位策略。这里的关键是:MCP 负责“拿到页面”,模型负责“决定怎么定位”,两者通过标准协议解耦。

4. 验证请求与成功结果:从需求描述到可运行测试套件的完整执行

配置写完后,跑一次完整流程。输入需求:“为电商平台的用户登录功能生成自动化测试,覆盖正常登录、密码错误、用户名不存在三种场景。”

执行入口脚本:

import asyncio from dotenv import load_dotenv load_dotenv() async def main(): llm = ChatOpenAI( model=os.getenv("TAOTOKEN_MODEL_ID"), base_url=os.getenv("TAOTOKEN_BASE_URL"), api_key=os.getenv("TAOTOKEN_API_KEY"), temperature=0.2, ) mcp_client = await init_mcp_client("mcp_config.json") graph = build_test_gen_graph(llm, mcp_client) initial_state = { "requirement": "为电商平台的用户登录功能生成自动化测试,覆盖正常登录、密码错误、用户名不存在三种场景。", "scenarios": [], "element_map": {}, "generated_scripts": [], "validation_report": {}, "retry_count": 0, } result = await graph.ainvoke(initial_state) print("生成脚本数量:", len(result["generated_scripts"])) print("校验通过:", result["validation_report"].get("passed")) asyncio.run(main())

执行后,LangGraph 会依次触发五个节点。需求分析节点输出结构化场景,场景扩展节点补充边界条件,DOM 分析节点通过 MCP 打开登录页抓取元素,脚本生成节点产出 Python 测试代码,校验节点做静态检查和一次试运行。

生成的测试套件大致长这样:

import pytest from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class TestUserLogin: @pytest.fixture(autouse=True) def setup(self): self.driver = webdriver.Chrome() self.driver.get(f"{os.getenv('TARGET_BASE_URL')}/login") yield self.driver.quit() def test_valid_login(self): self.driver.find_element(By.CSS_SELECTOR, "[data-testid='username']").send_keys("standard_user") self.driver.find_element(By.CSS_SELECTOR, "[data-testid='password']").send_keys("secret_sauce") self.driver.find_element(By.CSS_SELECTOR, "[data-testid='login-button']").click() WebDriverWait(self.driver, 10).until( EC.url_contains("/inventory.html") ) assert "/inventory.html" in self.driver.current_url def test_invalid_password(self): self.driver.find_element(By.CSS_SELECTOR, "[data-testid='username']").send_keys("standard_user") self.driver.find_element(By.CSS_SELECTOR, "[data-testid='password']").send_keys("wrong_password") self.driver.find_element(By.CSS_SELECTOR, "[data-testid='login-button']").click() error = WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='error-message']")) ) assert "do not match" in error.text def test_nonexistent_user(self): self.driver.find_element(By.CSS_SELECTOR, "[data-testid='username']").send_keys("ghost_user") self.driver.find_element(By.CSS_SELECTOR, "[data-testid='password']").send_keys("any_password") self.driver.find_element(By.CSS_SELECTOR, "[data-testid='login-button']").click() error = WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='error-message']")) ) assert "do not match" in error.text

本地执行pytest test_user_login.py -v,三个用例全部通过。校验节点会记录执行耗时和通过率,如果某个用例失败,should_retry会触发脚本生成节点重新生成,最多重试两次。

实测下来,从输入需求到拿到可运行套件,整个流程大约 40 秒,其中模型调用占大头。生成质量比单次对话高很多,因为场景扩展节点强制覆盖了反例,DOM 分析节点提供了真实的元素定位依据,校验节点又做了一轮过滤。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth 报错对照

多智能体 + MCP + 统一通道这套组合,出错的地方比较集中,我按真实报错整理一下排查路径。

401 Unauthorized:最常见的是 Key 没生效或 Base URL 写错。检查.env里TAOTOKEN_API_KEY是否以sk-开头,TAOTOKEN_BASE_URL是否为https://taotoken.net/api(不要加/v1)。如果你在 Claude Code 里遇到 401,检查settings.json里用的是ANTHROPIC_API_KEY而不是OPENAI_API_KEY。另外,Key 复制时前后有空格也会导致 401,用echo $TAOTOKEN_API_KEY | tr -d ' '清理一下。

local proxy failed:这个报错通常出现在 MCP 客户端启动浏览器服务时。原因是 MCP 服务器尝试连接本地代理端口但失败了。检查mcp_config.json里PUPPETEER_LAUNCH_OPTIONS是否配置了headless: true,以及本地是否有其他进程占用了 3001/3002 端口。如果你在容器里跑,确保 MCP 服务器的command路径在容器内可执行。

reading choices 报错:这个通常发生在模型返回的 JSON 解析阶段。LangGraph 节点里用json.loads(response.content)时,如果模型输出带了 markdown 代码块标记(```json),解析就会失败。解决办法是在解析前做一次清洗:

import re def clean_json_response(content: str) -> str: content = re.sub(r"^```json\s*", "", content.strip()) content = re.sub(r"\s*```$", "", content) return content

另外,temperature设太高也会导致模型输出不稳定,测试生成场景建议 0.1–0.3。

OAuth 相关报错:如果你在 Claude Code 里看到 OAuth token 过期或认证失败,说明它没有走 API Key 通道,而是尝试用 OAuth 登录。检查settings.json里是否同时存在ANTHROPIC_API_KEY和 OAuth 配置,两者会冲突。删掉 OAuth 相关字段,只保留 API Key 配置。如果你用的是 Codex,检查auth.json里api_base是否指向https://taotoken.net/api,api_key是否填写正确。

MCP 工具调用超时:DOM 分析节点调用浏览器工具时,如果目标页面加载慢,MCP 默认超时可能不够。在mcp_config.json的env里加MCP_TIMEOUT=30000,或者在节点函数里包一层asyncio.wait_for。

生成脚本元素定位失败:这不是报错,但很常见。原因是 DOM 分析节点抓取页面时,目标元素还没渲染出来。解决办法是在 MCP 浏览器工具里配置waitUntil: networkidle0,确保页面完全加载后再抓取 DOM。

6. 语义一致 CTA:把模型端点改到 TaoToken,让多智能体测试生成稳定跑起来

整套流程跑通后,你会发现最值得固化下来的不是某个节点的 prompt,而是模型调用通道的稳定性。LangGraph 的图结构可以改,MCP 工具可以换,但 TaoToken 作为统一 Key/API 通道,让四个智能体共用一套配置,调试时只需要看一个地方。

如果你要复现这套方案,建议按这个顺序操作:先去控制台创建 Key,把.env里的TAOTOKEN_BASE_URL、TAOTOKEN_API_KEY、TAOTOKEN_MODEL_ID填好;然后用模型对话页面发一条测试请求,确认通道通;接着把mcp_config.json和 LangGraph 节点代码复制到项目里;最后跑一次完整流程,观察每个节点的输出。

接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 有更详细的参数说明。如果你后续要做长期编码或 Agent 任务,Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 可以看配额和模型选择。Claude Code 用户直接参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=ClaudeCodeAnthropic&utm_campaign=rewrite 的配置片段,把ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY填对就行。

最后说一个实用技巧:把每次生成的测试套件和校验报告存到本地 SQLite 里,积累几十次之后,你可以分析哪些场景类型容易生成失败,反过来优化场景扩展节点的 prompt。这比单纯调模型参数有效得多。测试生成不是一次性的代码输出,而是一个可以持续迭代的工程流程。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 11:55:51

AI编程-使用Trae接入TaoToken实现一个热搜榜单页面

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华