EasyRun UI Agent · 部署与使用文档

EasyRun UI Agent · 部署与使用文档

基于 AI Agent 的 UI 自动化测试平台 · LLM 走 DeepSeek 官方 API · 其余组件开源本地部署

1. 快速开始(新机器一键初始化)

sh scripts/bootstrap.sh          # 自动:venv / 依赖 / 浏览器内核 / Allure / .env / 环境检查

bootstrap 会按平台自动处理:macOS 12 及更早自动锁定 playwright 1.50(新版 chromium 不支持旧系统)、Allure CLI+JRE 下载到项目内 tools/(不碰系统)、生成 .env 模板。Windows 10/11 用:powershell -ExecutionPolicy Bypass -File scripts\bootstrap.ps1

  1. 编辑 .env 填入 DEEPSEEK_API_KEY=sk-xxx
  2. source .venv/bin/activate && easyrun serve
  3. 打开控制台 http://127.0.0.1:8001/app/
  4. 另开终端跑演示验证:python scripts/demo.py

2. 部署指南

2.1 操作系统支持矩阵

系统支持程度初始化方式说明
Ubuntu / Debian(Linux x64/arm64)✅ 完整支持(推荐生产环境)sh scripts/bootstrap.shDocker 形态即 Linux;多机集群推荐 Ubuntu 执行节点
macOS(x64 / Apple Silicon)✅ 完整支持sh scripts/bootstrap.shmacOS 12 及更早自动锁定 playwright 1.50
Windows 10/11(x64)✅ 支持powershell -ExecutionPolicy Bypass -File scripts\bootstrap.ps1核心代码全平台;Windows 初始化脚本已提供

三个平台可同时混合部署:不同系统的执行节点接入同一 Redis + PostgreSQL 集群。

2.2 数据库配置(重点)

方案一:SQLite(默认,零配置)——适合单机开发,什么都不用配:

# 留空即可:自动使用 data/easyrun.db
# EASYRUN_DATABASE_URL=

方案二:Docker 启动 PostgreSQL(生产推荐):

docker run -d --name easyrun-pg --restart unless-stopped \
  -e POSTGRES_USER=easyrun -e POSTGRES_PASSWORD=EasyRun@2026 -e POSTGRES_DB=easyrun \
  -p 5432:5432 -v easyrun_pg:/var/lib/postgresql/data postgres:16-alpine

方案三:已有 PostgreSQL 实例——先建库建用户:

# psql 执行(或数据库管理工具):
CREATE USER easyrun WITH PASSWORD 'EasyRun@2026';
CREATE DATABASE easyrun OWNER easyrun;

连接串格式:postgresql+asyncpg://用户名:密码@主机:端口/库名(需先 pip install -e ".[postgres]" 安装驱动):

# .env 示例
EASYRUN_DATABASE_URL=postgresql+asyncpg://easyrun:EasyRun@2026@192.168.1.10:5432/easyrun
密码含特殊字符时需要 URL 编码,否则连接串解析错误。常见转义:@%40:%3A/%2F#%23%%25。上例中 EasyRun@2026 实际是 EasyRun%402026——推荐密码避免 @ : / 以省去转义。

验证连接:

# 容器内检查
docker exec -it easyrun-pg psql -U easyrun -d easyrun -c "SELECT 1;"
# 平台侧:启动后看日志无报错,或访问 /api/health 正常返回

2.3 Redis 配置

单机开发留空即可(进程内队列);多机/多进程部署必配

# Docker 启动(带密码)
docker run -d --name easyrun-redis --restart unless-stopped \
  -p 6379:6379 redis:7-alpine --requirepass EasyRunRedis

# .env 示例
EASYRUN_REDIS_URL=redis://:EasyRunRedis@192.168.1.10:6379/0

Redis 连接串格式:redis://[:密码]@主机:端口/库号。所有节点必须配置同一个 Redis 地址。需先 pip install -e ".[redis]"

验证:redis-cli -h 192.168.1.10 -a EasyRunRedis ping → PONG。

2.4 LLM(DeepSeek)配置

# .env 示例
DEEPSEEK_API_KEY=sk-你的密钥
# 可选:自定义端点(默认 https://api.deepseek.com)
# EASYRUN_DEEPSEEK_BASE_URL=https://api.deepseek.com
# 可选:换本地开源模型(OpenAI 兼容端点,如 Ollama/vLLM)
# EASYRUN_DEEPSEEK_BASE_URL=http://127.0.0.1:11434/v1
# EASYRUN_DEEPSEEK_CHAT_MODEL=qwen3:32b
# EASYRUN_DEEPSEEK_REASONER_MODEL=qwen3:32b

密钥只在服务端使用,页面与报告不会回显。

2.5 执行参数配置示例

# 生产集群推荐配置
EASYRUN_WORKERS=4                # 并发 Agent 数(扩容单元 = 浏览器实例;控制节点设 0)
EASYRUN_BROWSER_HEADLESS=true
EASYRUN_MAX_ATTEMPTS=1           # 失败不自动重试
EASYRUN_TASK_TIMEOUT_SECONDS=600
EASYRUN_REPLAY_STEP_DELAY_MS=3000
EASYRUN_DATA_DIR=/srv/easyrun-data   # 多机共享时指向 NFS 挂载点

2.6 形态 A:单机开发(零外部依赖)

SQLite + 内存队列 + 内置 Worker。只配 DEEPSEEK_API_KEY 即可启动。

2.7 形态 B:单机 Docker(推荐生产起步)

export DEEPSEEK_API_KEY=sk-xxx
docker compose up -d --build

# 验证
docker compose ps
curl http://127.0.0.1:8001/api/health

四服务:api(控制面)+ worker(执行面,内置 chromium)+ redis + postgres(compose 内已自动配好连接串,无需手改)。镜像构建时已内置 Allure CLI + JRE,报告生成开箱即用;运行时数据(截图 / Allure 结果与 HTML / PostgreSQL 数据)绑定挂载到项目根目录 ./data/

docker compose logs -f worker         # 看执行日志
docker compose up -d --build worker   # 代码更新后重建
docker compose down                   # 停止(数据保留在 ./data/,不随 down 删除)

2.7.1 同机多 Worker(控制器 ×1 + 执行节点 ×N)

形态 B 默认只起 1 个 worker 容器(4 个并发 Agent)。机器资源充足时可用 --scale 把执行面扩到 N 个容器,控制面仍是 1 个 api 容器。

docker compose build
docker compose up -d --scale worker=3    # api×1 + worker×3 + redis + postgres
docker compose logs -f worker            # 提交多用例计划后应看到多个容器同时执行

配置要点(compose 已用 YAML 锚点给 api 和 worker 配好同一份连接,无需手改):

配置取值为什么
EASYRUN_REDIS_URLredis://redis:6379/0所有节点连同一个队列,任务才分发得出去
EASYRUN_DATABASE_URLcompose 内 postgres 连接串所有节点读写同一个元数据库(任务/事件/报告)
EASYRUN_WORKERSapi=0 / worker=4控制节点不跑浏览器;执行节点按资源设并发数
EASYRUN_DATA_DIR/srv/data(绑定挂载项目根目录 ./data共享工件目录:控制台可预览任意 worker 节点的截图 / Allure
EASYRUN_ALLURE_BIN不设(自动探测)探测顺序:显式配置 → PATH → 项目根 tools/bin/allure;Docker 镜像已内置(容器内 /srv/tools/bin/allure),无需配置
EASYRUN_BROWSER_HEADLESStrue容器内无显示器,必须无头

在线扩缩容:docker compose up -d --scale worker=5(扩容)/ --scale worker=1(缩容,多余容器自动停止)。--scale 只能用于 worker:api 发布 8001 端口,多副本会冲突。

容量:1 个并发 Agent ≈ 1 个 chromium ≈ 400-600MB 内存。8G 内存建议 --scale worker=2,16G 建议 4,32G 建议 8。改每容器并发数(默认 4):加 docker-compose.override.ymlEASYRUN_WORKERS(compose 自动合并,不改主文件)。

注意:/api/healthworkers 显示的是 api 容器自身的 Worker 数(0),不代表执行能力,以 docker compose logs worker 为准。

2.8 形态 C:多机集群(水平扩容)

节点运行内容关键配置
控制节点 ×1easyrun serveEASYRUN_WORKERS=0
执行节点 ×Neasyrun workerEASYRUN_WORKERS=4
基础设施Redis + PostgreSQL见 2.2 / 2.3 的连接串示例
共享工件截图 / Allure把项目根目录 data/(或 EASYRUN_DATA_DIR)挂到 NFS

控制节点 .env 完整示例:

DEEPSEEK_API_KEY=sk-xxx
EASYRUN_DATABASE_URL=postgresql+asyncpg://easyrun:EasyRun@2026@192.168.1.10:5432/easyrun
EASYRUN_REDIS_URL=redis://:EasyRunRedis@192.168.1.10:6379/0
EASYRUN_WORKERS=0
EASYRUN_DATA_DIR=/mnt/easyrun-share

执行节点 .env 完整示例:

DEEPSEEK_API_KEY=sk-xxx
EASYRUN_DATABASE_URL=postgresql+asyncpg://easyrun:EasyRun@2026@192.168.1.10:5432/easyrun
EASYRUN_REDIS_URL=redis://:EasyRunRedis@192.168.1.10:6379/0
EASYRUN_WORKERS=4
EASYRUN_DATA_DIR=/mnt/easyrun-share

部署顺序:① 基础设施(2.2/2.3)→ ② 控制节点 bootstrap + .env + easyrun serve → ③ 每台执行机 bootstrap + .env + easyrun worker → ④ NFS 挂载共享数据目录(完整配置见 2.8.1 数据路径配置详解)。

扩容 = 新机器执行第 ③ 步;节点下线直接停进程,崩溃任务由调度器超时回收。

注意:多机部署不是只在控制节点上执行命令——每台执行机都要独立完成部署(拷贝代码 → bootstrap → 配 .env → 启动)。控制节点无法远程下发部署;执行节点连上共享 Redis 即自动接入,无需控制节点审批(无中心注册表)。执行机需要本机浏览器内核(bootstrap 自动下载),这是必须逐台部署的根本原因。

执行节点也可用 Docker:docker compose up -d --no-deps --build worker,并用 override 把 EASYRUN_REDIS_URL / EASYRUN_DATABASE_URL 指向中心基础设施 IP。--no-deps 必须加,否则 compose 会连带把依赖的 redis/postgres 也在这台机器上各起一套。Docker 执行节点镜像已内置 Allure CLI + JRE,工件落在该机项目根目录 ./data/artifacts/(compose 绑定挂载 ./data:/srv/data)。

2.8.1 数据路径配置(共享工件)详解

谁写谁读:控制节点(api)生成 Allure 原始结果与 HTML(data/artifacts/allure/allure-html/),并从共享目录读全部节点的截图打包附件;执行节点(worker)把步骤截图写入 data/artifacts/sessions/、视觉基线写入 data/artifacts/baselines/,基本只写不读。所以控制节点必须能读到各执行节点的 data/artifacts/

默认路径(不改配置即如此):裸机节点 = <项目根目录>/dataEASYRUN_DATA_DIR 默认值);Docker 节点 = 宿主机 <项目根目录>/data ↔ 容器 /srv/data(compose 绑定挂载 ./data:/srv/data)。

方案 A(推荐):共享路径 = 控制节点项目根的 data/(控制节点当 NFS 服务器,所有节点数据都在各自项目 data/ 下,配置零改动):

# 控制节点(Linux):
sudo apt install -y nfs-kernel-server
echo '<项目根目录>/data 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)' | sudo tee -a /etc/exports
sudo exportfs -ra && sudo systemctl enable --now nfs-server
# 控制节点(macOS):
# sudo nfsd enable
# echo '<项目根目录>/data -network 192.168.1.0 -mask 255.255.255.0 -maproot=root:wheel' | sudo tee -a /etc/exports
# sudo nfsd update

# 每台执行节点:
sudo apt install -y nfs-common
sudo mkdir -p <项目根目录>/data
sudo mount -t nfs <控制节点IP>:<控制节点项目根目录>/data <项目根目录>/data
echo '<控制节点IP>:<控制节点项目根目录>/data <项目根目录>/data nfs defaults,nofail 0 0' | sudo tee -a /etc/fstab

挂好后执行节点零配置:裸机默认路径即该挂载点;Docker 的 ./data:/srv/data 自动指向它。先挂载再启动容器(或挂载后 docker compose restart)——容器运行后再挂 NFS,容器看不到新内容。验证:执行节点 touch data/nfs-check 控制节点应能看到;控制台提交多用例计划后应能预览全部截图;生成 Allure 后各节点 data/artifacts/allure-html/<run_id>/ 应一致。

方案 B:独立共享存储:每台机器(含控制节点)把共享挂到任意位置(如 /mnt/easyrun-share);裸机 .envEASYRUN_DATA_DIR=/mnt/easyrun-share;Docker 用 docker-compose.override.yml 把 volume 改为 /mnt/easyrun-share:/srv/data。控制节点必须也挂同一共享——生成 Allure 报告的是控制节点。

关键坑:① exports 必须加 no_root_squash(Docker 容器以 root 写,否则 Permission denied);② 裸机节点写失败通常是各机 uid 不一致——统一 uid 或加 all_squash,anonuid=<uid>;③ fstab 加 nofail 防开机阻塞;④ 不共享时能跑,但跨节点截图无法预览、Allure 附件缺失——挂好共享后重新点「生成 Allure 报告」即可完整重打包;⑤ data/ 整树导出含 data/postgres/,postgres 只在基础设施机本机访问,勿在多机同时启动指向同一目录的 postgres。

2.9 配置排查速查

  • 启动报数据库连接失败:确认 PostgreSQL 端口可达(nc -zv 主机 5432)、密码转义正确、驱动已装(pip install -e ".[postgres]"
  • Worker 连不上队列:确认 Redis 密码/地址、驱动已装(.[redis])、多节点配置的是同一 Redis
  • 报告看不到其他节点截图EASYRUN_DATA_DIR 未指向共享存储
  • 配置优先级:命令行环境变量 > .env 文件 > 代码默认值

3. 平台功能使用

用例管理

  • 步骤写「做什么」,不写「怎么点」——Agent 负责翻译成具体操作;每条步骤一行自然语言
  • 每个用例可设默认访问网址(运行时仍可覆盖);断言至少一条,覆盖业务结果
  • 不会写断言?用「AI 生成断言」:输入「页面出现订单编号;订单金额大于 100」自动转换
  • 完成条件(可选):页面出现某文本即停止操作(如「中性新闻 (」),防多余动作
  • 一条用例一个业务场景,建议 ≤10 步;多场景用「计划」批量跑

执行与目标地址优先级

运行时填写的网址  >  用例默认网址  >  平台默认目标地址(配置页设置)

报告

  • 时间轴:每步 LLM 决策理由 / 动作结果 / 截图 / 断言 / 完成条件命中
  • 失败时自动 AI 归因(五类)+ 缺陷草稿;「重跑失败用例」只重跑失败的
  • Allure 报告:报告页点「生成 Allure 报告」按需生成(带进度显示),生成后按钮变为「查看 Allure 报告」,点击在新窗口打开 /allure-html/<run_id>/;已生成过的执行直接显示查看按钮,无需重复生成
  • 执行中可随时「取消执行」(排队任务直接跳过,执行中任务在下一步停止;多机/多容器部署同样生效);执行记录支持多选/全选批量删除(不可恢复,删除前有确认)

固化与导出(0 token 回归)

  • 探索模式通过后自动记录动作 → 用例行出现「固化」按钮 → 点击后变回放模式(不耗 LLM)
  • 「导出代码」生成独立 Playwright 脚本(语义定位器 + 断言),脱离平台可跑、可进 CI
  • 回放动作间默认间隔 3 秒(等待动态渲染),断言前有沉降等待——适配平滑滚动类页面

4. 断言与完成条件参考

9 类断言(确定性校验,由代码执行):

类型用途target / expected 示例
text_contains页面正文包含文本订单编号 / —
url_containsURL 包含片段checkout / —
element_existsCSS 选择器存在.cart-button / —
element_count选择器数量等于.product / 3
element_text存在文本元素欢迎回来 / —
text_in_view屏幕可见元素含文本(全 DOM,含普通 div/span 标签)中性新闻 ( / —
text_near_top文本元素出现在窗口上方区域中性新闻 ( / 0.4(阈值可选)
value_compare标签后数值比较(支持同元素与相邻兄弟元素)订单金额 / >= 100
visual截图与基线一致(首次自动建基线)checkout / —

完成条件:每步动作前检查,全部满足立即停止操作进入断言。字段 min_steps 可设"执行满 N 个动作后条件才生效"——用于目标状态与初始状态相同的场景(如利好新闻标签页面加载即可见,但用例要求先点日期再点按钮)。

步骤后断言:断言可绑定步骤序号(断言行的「步骤序号」输入框,1-99)。探索模式下 Agent 每完成一个步骤调用 case_step_done(step=N) 标记,平台在该步骤动作后立即执行绑定断言(0 token 确定性校验;失败即止:有自愈配置先自愈,否则用例失败)。Agent 忘标记直接结束时,收尾兜底按步骤序补跑所有未标记步骤的绑定断言,再接无绑定断言。固化回放同样在标记点执行(标记随固化动作保存,deterministic 用例的步骤列表中可见 case_step_done JSON 行,属正常现象);导出代码时绑定断言就地生成在对应动作之后。

步骤序号怎么对应:填的序号 = 用例「步骤」列表的行号(从 1 开始,每行一步)。规则:

  • 一个步骤可绑多条断言;一条断言只绑一个序号。
  • 序号是步骤行号,不是动作数——一个步骤可能对应多个动作(找元素 + 点击 + 等待),动作由 Agent 自主决策。
  • 提示词中步骤带编号列出,绑定断言的步骤行会追加「完成本步骤后调用 case_step_done(step=N) 触发绑定断言」提示。
  • Agent 漏标记、或序号超出步骤数:断言不会丢——收尾兜底按序号顺序补跑(执行时机退化为「结束时」,报告仍显示「步骤 N」标签)。
  • 不填序号 = 全部步骤完成后统一执行;增删步骤行后序号不会自动跟随,需手动核对。

示例(断言「页面出现订单编号」填步骤序号 4):

步骤绑定断言执行时机
1. 打开商城首页url_contains /home(序号 1)步骤 1 动作完成后立即校验
2. 搜索机械键盘element_count 搜索结果 = 12(序号 2)步骤 2 动作完成后立即校验
3. 点击第一个搜索结果(无)
4. 加入购物车并结算text_contains 订单编号(序号 4)步骤 4 动作完成后立即校验
value_compare 订单金额 > 100(不填)全部步骤完成后统一校验

5. 执行策略

场景行为
动作执行失败立即终止,不重试不换方式
同一动作被重复请求跳过并提示执行下一步(超过 2 次跳过才终止)
用例失败不自动重试(max_attempts=1
断言失败不自愈重试,直接判失败
等不到资源锁直接失败,不回队列
连续失败 3 次隔离(quarantine,新执行不受影响)
需要重试报告页「重跑失败用例」手动触发
观察类工具豁免browser_wait / browser_get_text / 截图不参与"重复动作"判定,等待和读取可多次调用。

6. 配置项参考(环境变量,前缀 EASYRUN_)

变量默认说明
EASYRUN_DATA_DIR./data运行时数据(数据库/截图/Allure/浏览器内核)
EASYRUN_DATABASE_URLdata/easyrun.db生产用 postgresql+asyncpg://user:pass@host/db
EASYRUN_REDIS_URL空(内存队列)多机必填 redis://host:6379/0
EASYRUN_WORKERS4并发 Agent 数;纯 API 节点设 0
EASYRUN_MAX_ATTEMPTS1失败自动重试次数(1 = 只执行一次);⚙ 可在控制台「配置」页运行时修改(1-10)
EASYRUN_MAX_NOOP_REPEATS1同一动作允许执行次数
EASYRUN_MAX_SKIPPED_REPEATS2重复请求最多跳过次数
EASYRUN_MAX_STEPS_PER_CASE30单用例动作步数上限;⚙ 可运行时修改(3-100,页面键名 max_steps
EASYRUN_HEAL_ATTEMPTS0断言失败自愈轮数(0 = 不自愈);⚙ 可运行时修改(0-5)
EASYRUN_REPLAY_STEP_DELAY_MS3000固化回放动作间延迟
EASYRUN_TASK_TIMEOUT_SECONDS600单任务执行上限
EASYRUN_BROWSER_HEADLESStrue浏览器无头模式
EASYRUN_ALLURE_BIN自动探测allure CLI 路径(PATH → tools/bin/allure)
DEEPSEEK_API_KEYDeepSeek API Key
EASYRUN_DEEPSEEK_BASE_URLapi.deepseek.comOpenAI 兼容端点,可换本地 vLLM/Ollama

⚙ = 可在控制台「配置」页运行时修改(存于共享数据库,多机一致,保存即生效;输入框置空保存 = 清除覆盖、回落环境变量默认)。「失败归因」开关也在配置页(关闭后失败任务不再自动调 deepseek-reasoner,省 token)。

7. REST API 摘要

GET    /api/cases                    用例列表(含整数编号 case_no)
POST   /api/cases                    新建用例          PUT /api/cases/{id}       更新
DELETE /api/cases/{id}               删除用例          POST /api/cases/{id}/run   单用例执行
POST   /api/cases/{id}/cure          启用固化回放      POST /api/cases/{id}/export-code  导出 Playwright 代码
POST   /api/cases/assertions/parse   自然语言 → 断言
GET    /api/plans                    计划列表          POST /api/plans/{id}/run   计划执行
POST   /api/runs                     提交执行          GET  /api/runs?page=&page_size=  分页列表
GET    /api/runs/{id}/report         聚合报告(含归因)  GET  /api/runs/{id}/events    事件流
POST   /api/runs/{id}/cancel         取消执行          POST /api/runs/{id}/rerun-failed 重跑失败用例
DELETE /api/runs/{id}                删除执行          POST /api/runs/batch-delete     批量删除
POST   /api/runs/{id}/allure         Allure 导出+HTML   GET  /api/trends            趋势
GET    /api/locators                 元素库            GET/PUT /api/settings        平台配置

交互式 API 文档:/docs(Swagger UI)

8. 常见问题

  • 用例步骤 6 条,为什么执行了 20+ 个动作? 步骤是自然语言目标,动作是 LLM 的每步决策(导航/点击/等待/确认…),一条步骤可能对应多个动作。上限由 max_steps_per_case 控制。
  • 为什么有的用例没有「固化」按钮? 固化动作在探索模式通过后自动记录(≥1 个动作)。从未通过、或已固化的用例没有该按钮(已固化的保留「导出代码」)。
  • 完成条件配置了但不生效? 检查页面真实文本(如全角/半角括号差异:「中性新闻(」vs「中性新闻 (」);若目标状态与初始状态相同,配合 min_steps 使用。
  • 点击新闻链接页面没反应?browser_click_link 按名称点击 class=sub-links 内的链接;站点平滑滚动需等待动画完成(平台已内置渲染轮询与沉降等待)。
  • 回放(固化)失败但探索成功? 页面结构变了 → 回到探索模式重跑一次重新固化;或检查 EASYRUN_REPLAY_STEP_DELAY_MS 是否足够。
  • 时间显示差 8 小时? 平台统一存 UTC、展示层转本地时区。若仍有偏差,确认服务器与浏览器时区设置。
  • macOS 12 装不了浏览器? 新版 chromium 不支持旧系统,bootstrap 会自动锁定 playwright 1.50。
  • 删除执行记录能恢复吗? 不能。删除会清除任务/事件/截图/Allure 全部关联内容,操作前有确认提示。

9. 成本与省钱

  • 探索模式:每步决策约 0.5~1.5k tokens(页面快照占大头);失败即终止 + 跳过重复 + 完成条件三重护栏防空转
  • 固化回放 / 导出代码 = 0 token——高频回归用例务必固化
  • 失败归因用 deepseek-reasoner(更贵),仅失败时触发一次
  • 总览页可查看 Token 消耗趋势;单用例成本 ≈ 决策数 × 单步 tokens