# 微调 laya 做浏览器 agent 决策头(替代 TypeSafe Jev) 流水线(全部本地、无付费 API): | 步骤 | 脚本 | 说明 | |---|---|---| | 1 抓页面 | `collect_pages.py` | headless Chromium + browser-harness,90 个真实页面的元素表 + 正文 | | 2 反向生成目标 | `gen_goals.py` | 随机选一个元素当 gold,本地 Qwen3-8B-AWQ(sglang)写出"要用它的用户目标",1121 条 | | 3 真实 DONE 样本 | `make_done_cases.py` | 在浏览器里真的执行点击,落地页 + 历史 = DONE,250 条 | | 4 构建样本 | `build_items.py` | 复刻 jev-ultrafast 的 systemone 请求格式(`common_ft.py`),head_max_len 512;页面按 index%5 留出 | | 5 训练 | `train.py` | laya 官方 RLCD 配方(噪声 logit 策略梯度 + soft CE)单卡版,4 轮约 10 分钟 | | 6 评测 | `eval.py` | 留出页面上的操作准确率 / 目标 top-1 | ```fish bash finetune/run_v2.sh # 4-6 步 bash finetune/run_all.sh # 含零样本基线 ``` ## 加入魔搭 Mind2Web 后(v3/v4) `osunlp/Mind2Web`(魔搭,6.7 GB,12-25 MB/s 不走代理)→ `convert_mind2web.py`:按 backend_node_id 从 cleaned_html 还原元素文本/角色, 正例 + 44 个负例打乱成 jev 格式元素表,历史用 action_reprs,7296 条;按网站哈希留出 20%。v4 = 自采 + 真实 DONE + 全部 Mind2Web, DONE / TYPE_TEXT / SELECT 操作样本 3 倍过采样,16020 条训练样本,3 轮 64 分钟。`bash finetune/run_v4.sh`。 评测 1642 条(自采留出页 232 + Mind2Web 未见网站 1410): | 模型 | 操作准确率 | 目标 top-1 | live DONE | m2w CLICK 目标 | m2w TYPE_TEXT 操作 | m2w SELECT 操作 | |---|---|---|---|---|---|---| | typed-decisions 零样本 | 0.26 | 0.13 | 0.39 | 0.03 | 0.00 | 0.00 | | v3(无过采样) | 0.790 | 0.496 | 0.58 | 0.40 | 0.04 | 0.20 | | **v4(过采样)** | **0.795** | **0.524** | **0.82** | **0.43** | **0.80** | **0.55** | 真实浏览器 6 任务:零样本 0/6 → v2 2/6 → **v4 3/6**(python.org Downloads 2.0 s、Travel 分类 0.3 s、Wikipedia 随机文章), Wikipedia 搜索任务首次正确选 TYPE_TEXT 并由本地 Qwen 填入 "Python programming language",但之后重复填同一字段而没有提交—— 训练数据里缺"字段已填好 → 下一步提交"的样本。GitHub Issues 点成 Releases、HN new 提前 DONE。 ## 最终对比 v2(2026-09-21,修正检查脚本后:下拉按 current_value 文本判定、导航后等 1.5 s 再判) | 模型 | 真实任务 16×3 | 留出 top-1 | 每步 | |---|---|---|---| | v10(421M) | 50% | 0.656 | 41–50 ms | | **v10s(322M)** | **62%** | 0.631 | 17–23 ms | | v11s(322M + 682 条滚动/搜索/下拉脚本轨迹) | 56% | 0.623 | 19 ms | 10 个任务 3/3 稳过、6 个 3/3 稳挂;v10s/v11s 差异小于站点随机波动。老师对比:27B 思考预算 300 最好(0.861/0.603,4.7 s/步), 仍不超过 laya v11s(0.890/0.623,0.021 s);8B/27B 都不适合做 DAgger 老师或 System-2 兜底。 ## 最终对比(2026-09-21,16 个真实任务 × 3 次,apps/browser_suite.py) | 模型 | 底座 | 一步延迟 | 留出目标 top-1 | 真实任务通过率 | +门控 τ=0.7(Qwen3-8B 兜底) | |---|---|---|---|---|---| | v10 | ModernBERT-large 421M | 41–50 ms | 0.656 | **58%**(28/48) | 42%,升级率 78% | | v10s | mmBERT-base 322M,格式 v3 | **17–23 ms** | 0.631 | 50%(24/48) | 52%,升级率 85% | 结果高度双峰:9 个任务 3/3 稳定通过(导航、分类、勾选、HN 各页、DuckDuckGo 搜索),7 个任务 0/3 稳定失败 (Wikipedia 两个搜索任务、GitHub Issues、下拉选择、翻页需滚动、arXiv 搜索、Google Flights)。 门控把低置信步骤交给 Qwen3-8B 反而更差:在这些页面上微调后的 laya 比 8B 通用模型判断得更准,System 2 需要更强的模型或专门提示。 失败任务的共性是"输入后提交 / 选建议"和"需要先滚动",训练数据里几乎没有滚动样本。 v10 数据:5244 条自采目标(421 页)+ 700 真实 DONE + step-2 + Mind2Web 全量 + DAgger,格式 v2,head 768,4 轮 2.2 小时。 ## 输入格式 v2(v9 起) `LAYA_FMT=v2 LAYA_HEAD=768`:元素表不再塞进 state(1024 token 里会被截掉),只在选项里保留完整标签 + 角色 + 已填值, state 只留标题 / URL / 历史 / 1500 字正文,head_max_len 512→768。v9 只用 Mind2Web + 该格式,真实任务从 6/16 到 **10/16**, Mind2Web 点击目标 0.44→0.51。checkpoint 的 `rl_agent_config.json` 记录 `laya_fmt` / `head_max_len_train`,服务端自动跟随。 注意:jev 的 `snapshot.js` 有意隐藏密码框(`!['password','file','hidden'].includes(e.type)`),密码登录任务在该框架里不可能完成,任务集已移除。 ## 真实任务集(apps/browser_suite.py,16 个任务,自动验收) | 版本 | 通过 | 主要失败模式 | |---|---|---| | v4 | 3/14 | 过早 DONE(6 个),填完不提交(2 个),点错(3 个) | | v5(DONE 过采样 2x + 字段带已填值 + 温度 1.40) | 4/14 | 登录/搜索仍在填一个字段后 DONE | v5 的根因:真实 DONE 样本历史恰好都是 1 步、自采点击样本历史都是 0 步,模型学到"有历史 ⇒ DONE"。 v6 加 `gen_step2.py`:在 250 个落地页上保留历史再生成 659 条新目标作为负样本。 ## 结果(v1/v2,仅自采数据) 留出 18 个未见页面、232 条: | 模型 | 操作准确率 | 目标 top-1 | DONE 判断 | 每条延迟 | |---|---|---|---|---| | typed-decisions 零样本 | 0.543 | 0.103(随机) | 0.39 | 254 ms | | multilingual 零样本 | 0.127 | 0.124 | 0.00 | 249 ms | | **微调 v2(1886 样本)** | **0.875** | **0.665** | 0.66 | 55 ms | 真实浏览器 6 个任务(jev-ultrafast 循环 + 本地 laya 服务):python.org Downloads 2.4 s 完成、books.toscrape Travel 分类 0.3 s 完成;HN "new"、GitHub Issues、Wikipedia 两个任务失败(点错元素或提前 DONE)。零样本时 6 个全失败。 已知问题:置信度全是 1.00(未做温度校准);数据只有 90 个页面,泛化有限。下一步是把页面扩到 500+、 每轮把线上失败样本加回训练集、拟合温度。v1 的教训:模板化的 DONE 样本会让模型学到"看到 stop when 就答 DONE", DONE 样本必须来自真实执行后的落地页。