fast-browser-use 本地部署与实测报告
部署日期:2026-09-23 · 目标机:Windows(i5-13400F / 68.5GB RAM / RTX 3070 Ti 8GB) 上游项目:APUS-AI-Lab/fast-browser-use(MIT,Jev 开源复现)
一、结论先行
| 问题 | 结论 |
|---|---|
| 装好了吗 | ✅ 完整装好:CLI + 依赖 + Chromium 1243 + 18GB 权重 |
| 在这台机器上快吗 | ❌ 不快。实测 单步决策 46~47 秒(官方 M2 Pro 基准为 5.7 秒) |
| 能加速本项目测试吗 | ❌ 不能。比现有探针更慢(现有探针主要慢在作品首屏,不在决策) |
| 根因 | 8GB 显存装不下 9B(源码要求单设备、且明确拒绝量化)⇒ 只能 CPU fp32 |
一句话:工具本身没问题,是这台机器的显卡"吨位不够"。要发挥它的威力,需要 ≥24GB 显存的 GPU(官方文档标注 9B BF16 需 24GB VRAM、峰值 20~22GB)。
二、已安装组件清单
| 组件 | 位置 / 版本 | 备注 |
|---|---|---|
fbu CLI | ~/.local/bin/fbu(uv tool) | v0.1.0 |
| Python 运行时 | 3.12.13(uv 管理) | 上游要求 ≥3.12 |
| PyTorch | 2.14.0+cpu | 见下方"踩坑1" |
| transformers | 5.17.0 | 含 Qwen3_5ForCausalLM |
| playwright | 1.63.0 | 期望 chromium v1243 |
| Chromium | %LOCALAPPDATA%\ms-playwright\chromium-1243\chrome-win64\chrome.exe | 手动装配,见"踩坑2" |
| Headless Shell | ...\chromium_headless_shell-1243\ | 同上 |
| 模型权重 | D:\tools\models\Qwen3.5-9B\(18.00 GB / 4 分片) | ModelScope 下载,见"踩坑3" |
| 环境脚本 | D:\tools\fbu\env.sh | 一键加载环境变量 |
| CUDA 版 torch 备用 | D:\tools\wheels\torch-2.14.0+cu130-cp312-cp312-win_amd64.whl(1.9GB) | 未安装,备用 |
三、实测性能数据
3.1 本机 CPU(fp32)
| 指标 | 实测值 |
|---|---|
| 模型加载 | 32.1 s |
| 单步决策(prompt 1777 tokens) | 46.4 / 46.0 / 47.4 s(三次中位 46.4 s) |
| 内存占用 | 约 36GB(fp32 权重),加载后系统剩余约 13GB |
| CPU 实测内存带宽 | 57.3 GB/s |
| CPU 实测 fp32 算力 | 0.528 TFLOPS |
3.2 与官方基准对照
| 环境 | 单步决策 | 备注 |
|---|---|---|
| Apple M2 Pro + MLX 4-bit(官方) | 5,663 ms | 带宽 200GB/s,权重仅 5.95GB |
| 本机 CPU + fp32 | 46,400 ms | 慢 8.2 倍 |
差距来源有二:
- 精度:fp32 权重 36GB vs 4-bit 的 5.95GB,每 token 要搬的数据差 6 倍;
- 缺失加速内核:日志明确警告
causal_conv1d未安装 → 回退参考实现,"much slower"flash-linear-attention未安装 → 回退参考实现,"much slower"- Qwen3.5 是 linear-attention 架构,这两个内核正是它的性能命脉。
装上这两个内核可能带来数倍提升,但它们在 Windows/CPU 上安装困难(通常需 CUDA 源码编译),投入产出比低。
四、踩坑记录(可复现)
踩坑 1:PyPI 镜像给的是 CPU-only torch
清华源同步自 PyPI 的 Windows wheel 默认 不含 CUDA(安装后显示 2.14.0+cpu,torch.version.cuda 为 None)。 考虑到 8GB 显存本也跑不动 9B fp32,这个结果恰好避免了 OOM 崩溃,可视为歪打正着。
踩坑 2:fbu install-browser 会静默挂死(最耗时的一坑)
症状:命令跑 12 分钟无任何输出、无报错。
根因:Playwright 1.63 需要 chromium v1243,而机器上只有项目探针用的 v1223。Playwright 找不到就尝试从 cdn.playwright.dev 下载,国内直连仅 ~388 KB/s 且常常卡住。
定位方法:python -m playwright install chromium --dry-run 可打印真实的期望版本与下载地址,比盲猜 URL 快得多。
解决:Chromium 1243 本质就是 Chrome for Testing 153.0.8010.12,改用 npmmirror 镜像手动装配:
# 镜像速度 33 MB/s,比官方快 85 倍
BASE=https://cdn.npmmirror.com/binaries/chrome-for-testing/153.0.8010.12/win64
curl -sL -o chrome-win64.zip "$BASE/chrome-win64.zip"
curl -sL -o chrome-headless-shell-win64.zip "$BASE/chrome-headless-shell-win64.zip"
解压到对应目录,并在目录下创建两个空标记文件 INSTALLATION_COMPLETE 与 DEPENDENCIES_VALIDATED(Playwright 靠它们判断"已安装"):
%LOCALAPPDATA%\ms-playwright\chromium-1243\chrome-win64\chrome.exe
%LOCALAPPDATA%\ms-playwright\chromium_headless_shell-1243\chrome-headless-shell-win64\chrome-headless-shell.exe
踩坑 3:HuggingFace 直连不可用,ModelScope 快 8 倍
| 源 | 实测速度 |
|---|---|
| huggingface.co | 超时(20s 无响应) |
| hf-mirror.com | 1.26 MB/s(18GB 需约 4 小时) |
| modelscope.cn | 9.78 MB/s(18GB 约 30 分钟)✅ |
fbu download 走 huggingface_hub,可设 HF_ENDPOINT=https://hf-mirror.com 改道,但太慢; 最终采用 ModelScope 直连 + 4 分片并行 curl,实测约 10 分钟下完。
权重完整性验收(与官方逐一一对账):
| 分片 | 实际 | 期望 |
|---|---|---|
...-00001-of-00004.safetensors | 4.91 GB | 4.91 GB |
...-00002-of-00004.safetensors | 4.97 GB | 4.97 GB |
...-00003-of-00004.safetensors | 5.00 GB | 5.00 GB |
...-00004-of-00004.safetensors | 3.10 GB | 3.10 GB |
config.json 亦命中官方校验条件:model_type=qwen3_5、hidden_size=4096、num_hidden_layers=32、intermediate_size=12288。
踩坑 4:源码层面的三条硬约束(决定了本机走不通 GPU)
审读 fast_browser_use/torch_backend.py 发现:
| 行号 | 约束 | 后果 |
|---|---|---|
| L73 | device_map={"": str(device)} — 强制单设备,不许分层卸载 | 8GB 显存装不下 18GB 权重 |
| L65-66 | 明确拒绝任何量化权重(raise ValueError) | 4-bit 方案被源码焊死 |
| L63-64 | 只认 Qwen3.5-9B / 35B-A3B | 换小模型也不行 |
⇒ 官方推荐的 CPU 路径是本机唯一可行路径,也是慢的唯一原因。
踩坑 5(环境相关):本沙箱内 Python asyncio 完全不可用
症状:sync_playwright() 永远挂死,连 asyncio.new_event_loop() 都卡住。
排查过程:
| 测试 | 结果 |
|---|---|
subprocess.run(同步子进程) | ✅ 0.22s 正常 |
asyncio.new_event_loop() | ❌ 挂死 |
WindowsSelectorEventLoopPolicy()(不依赖 IOCP) | ❌ 同样挂死 |
| 系统 Python 3.11 / uv Python 3.12 | ❌ 都挂死 |
| Node.js | ✅ 正常(项目探针全靠它) |
结论:这是本机 Python 执行环境的底层限制,与 fbu 本身无关。Node 侧(项目现有探针体系)不受影响。 若在真实交互式终端中 asyncio 正常,则 fbu 可直接使用;建议以真实终端下的 fbu --help / 一次 fbu run 为最终判据。
五、探针提速验证(2026-09-23 完成,三条假设两条被推翻)
5.1 结论速览
| 原假设 | 验证结果 |
|---|---|
| 「首屏 54 秒」是软渲染导致的 | ❌ 不成立。首屏装配时间与渲染后端几乎无关 |
| 「换 d3d11 能大幅提速」 | ⚠️ 有收益但非数量级:真实探针 9.2s → 8.1s(省 12%) |
| 「54 秒」这个数字现在还成立吗 | ❌ 已过时。那是 2500 万面时代的数据 |
5.2 首屏耗时三组对照(probe_boot_time.cjs,各 3 轮)
新增 PROBE_RENDER 环境变量开关(默认行为不变),并让探针自报实际生效的渲染器:
| 渲染后端 | 可玩(ms) 中位 | 稳帧(ms) 区间 | 3 轮总耗时 | 实际渲染器 |
|---|---|---|---|---|
| 默认(不加参数) | 1813 | 2998~4445 | 12 s | NVIDIA RTX 3070 Ti |
d3d11(显式) | 1859 | 3060~4115 | 11 s | NVIDIA RTX 3070 Ti |
swiftshader | 2099 | 6401~7516 | 23 s | SwiftShader(软渲染) |
两条独立结论:
- 首屏世界装配与渲染后端无关:1813 / 1859 / 2099 ms,差距仅 ~15%。
⇒ 记忆里「2500 万面软渲染首屏 54.5s」的说法已失效: 当前场景实测 947,336 面(94.7 万面),比当时的 2500 万面瘦身 96%, 首屏降到 1.8 秒。首屏问题早已被资源瘦身解决,与渲染后端无关。
- 但软渲染确实拖慢探针:3 轮 12s → 23s(+92%)。
铁证在「稳帧」列 —— 代码里只 sleep(1200),实测却花了 6401~7516 ms, 多出 4300~5000ms ⇒ 软渲染把事件循环堵死了(渲染任务饿死 JS 定时器)。
5.3 真实探针验证(probe_world_dims.cjs,各 1 轮)
| 渲染后端 | 总耗时 |
|---|---|
swiftshader(原历史行为) | 9215 ms |
d3d11 | 8053 ms |
省 1162 ms(12.6%),输出语义一致(仅个别浮点尾数差异,与渲染无关)。
为什么只有 12% 而不是 92%:该探针内含 waitForTimeout(3000) 等纯等待, 期间不渲染,把差距稀释了。⇒ 收益大小取决于探针的「渲染密集度」: 轻量的(只等首屏)接近翻倍,含大量硬等待的只有十几个百分点。
5.4 🔴 渲染不是主因 —— 真正的大头在这里
探针里写死的 waitForTimeout:274 处,合计 555.9 秒(9.3 分钟),平均 2029 ms
单个最大:30000 ms / 16000 ms / 9000 ms …
重灾区:e2e.cjs 一家 59 处
9.3 分钟的纯硬等待,远超"换渲染后端"能省下的量级(全部 62 个写死 swiftshader 的探针 按每个省 1~4s 计,总共约 1~4 分钟)。
⇒ 真正的提速要靠 1 把固定等待改成条件轮询(判据满足即返回,既快又更稳) + 2 并行化(10 核 / 68GB 内存,余量充足) + 3 复用浏览器进程与登录态,而不是换渲染后端。
5.5 已落地的改动(默认行为均不变)
| 文件 | 改动 | 默认行为 |
|---|---|---|
web/tools/probe_boot_time.cjs | 加 PROBE_RENDER 开关 + 自报实际渲染器 | 不变(不设变量=与历史完全一致) |
web/tools/probe_world_dims.cjs | 加 PROBE_RENDER 开关 | 不变(默认仍 swiftshader) |
# 现状(= 历史行为)
node web/tools/probe_boot_time.cjs http://127.0.0.1:8080/ 3
# 显式走硬件 GPU
PROBE_RENDER=d3d11 node web/tools/probe_world_dims.cjs
📌 自报渲染器这一项值得保留:它防止"以为在测硬件、实则在测软件"这类静默错误 —— 本次实测就发现「不加参数」反而拿到硬件 GPU,与"写死 swiftshader"的直觉相反。
六、使用说明(真实终端下)
# 1. 加载环境
source /d/tools/fbu/env.sh # Git Bash
# 或
call D:\tools\fbu\env.bat # CMD
# 2. 冒烟测试(本地靶子,不依赖外网)
fbu run "file:///D:/tools/fbu-test/index.html" \
--goal "Type Pumpkin into the Article name field and press Search" \
--expect-text "Found 1 result for: Pumpkin" \
--trace D:/tools/fbu-test/trace.json
# 3. 打开本地靶子服务(可选)
cd /d/tools/fbu-test && python -m http.server 8899 --bind 127.0.0.1
环境变量(D:\tools\fbu\env.sh 已内置):
| 变量 | 值 | 说明 |
|---|---|---|
FBU_BACKEND | torch | 非 Apple Silicon 只能选 torch |
FBU_DEVICE | cpu | 8GB 显存跑不了,必须显式指定 cpu |
FBU_DTYPE | float32 | CPU 上只支持 float32/bfloat16 |
FBU_MODEL | D:/tools/models/Qwen3.5-9B | 本地权重,配合 HF_HUB_OFFLINE=1 完全离线 |
七、给"加速测试"的务实建议(已按实测数据重排)
| 优先级 | 动作 | 实测依据 | 预期收益 |
|---|---|---|---|
| 🥇 最高 | 把 274 处硬等待改成条件轮询 | 合计 555.9 秒(9.3 分钟) 纯等待;e2e.cjs 一家 59 处 | 数量级(判据早已满足时可提前返回) |
| 🥈 高 | 探针并行化 | 10 核 / 68GB 内存,探针间无依赖 | 理论 5~8 倍 |
| 🥉 中 | 批量探针复用浏览器进程 + 登录态 | 每个 .cjs 各自冷启动 Chromium + 各自注册账号 | 每个省 1~2s |
| 4 | 渲染后端换 d3d11(62 个探针写死 swiftshader) | 真实探针 9.2s → 8.1s | 每探针省 1~4s |
| 5 | fbu 本身(本机不可行) | 单步决策 46s | 需换 ≥24GB 显存 GPU |
💡 第 1 条是唯一可能带来数量级提升的:把waitForTimeout(3000)换成 「轮询某判据直到成立、超时 3000ms」—— 判据早早满足就能提前返回, 既提速又提高稳定性(硬等待等不够会假红,等太久纯浪费)。 ⚠️ 注意项目既定纪律:等待预算一律用_auth.cjs的ticksMs()折算, 别写死字面量 —— 这个油门老大已经调过两次,写死的地方每次都要满目录搜。
7.1 一处顺带发现(值得单独记)
本机实测「不加任何渲染参数」反而拿到硬件 GPU(RTX 3070 Ti), 而多数探针写死了 --use-angle=swiftshader —— 也就是说: 大家以为在测"真实渲染路径",其实一直在测软渲染后的降级路径。
这属于「不报错、看着正常、但结果与预期不符」的静默坑, 所以本次给探针加了自报渲染器的能力(跑完直接打印实际生效的 renderer)。
7.2 关于「54 秒」那条记忆
记忆 MEMORY-verify.md 与 _auth.cjs 里都写着「首屏实测 54503ms」, 并据此把超时预算设成 90s。本次实测该数字已失效(现为 1.8s,因资源瘦身 96%)。
⚠️ 但不要因此把超时改小 —— 那句 90s 的注释本身写得很有道理: "超时给 90 秒不是随手写的…系统负载一高就超时,于是探针头顶打出『作品可玩:否』, 后面却 42 条全过,因为那只是探针自己等不及了。假警报比没警报更费人。" ⇒ 保守的超时是故意的余量,不是过时数据。只更新数字认知,别动超时值。
八、复现用文件
| 文件 | Purpose |
|---|---|
D:\tools\fbu-test\bench_model.py | 模型加载 + 单步决策延迟实测(纯同步,绕开 asyncio) |
D:\tools\fbu-test\bench_render.cjs | 渲染后端对比实验(swiftshader / d3d11 / headed) |
D:\tools\fbu-test\index.html 等 | 本地测试靶子(表单 + 结果页 + 关于页) |
D:\tools\fbu\env.sh | 环境变量加载脚本 |
D:\tools\fbu-src\ | 上游源码(浅克隆,供审读) |