Browser Harness 开发说明

本文记录了 Browser Harness v1 中对后端、前端和 QA 贡献者真正重要的契约。

该功能冻结后的产品决策见 specs/6_browser_harness_v1.md

范围

Browser Harness v1 明确保持窄范围:

  • 仅文本
  • 仅 primary
  • 仅 prompt-only 的签名 pack
  • 浏览器捕获结果作为普通 run 导入

实现目标是在不把答案或 checker 逻辑泄露给第三方页面的前提下,实现可复现的浏览器捕获。

核心数据契约

Provider

Provider 现在区分 API-backed 与 browser-only 两类目标:

{
  "surface": "api",
  "browser_target": null
}
{
  "surface": "browser",
  "browser_target": {
    "preset": "chatgpt",
    "origin": "https://chatgpt.com",
    "display_name": "ChatGPT Web"
  }
}

规则:

  • 只有 surface="api" 的 providers 会出现在 provider 管理、smoke tests、普通 run launcher 与 schedules 中
  • 只有 Browser Harness 导入流程会创建 surface="browser" 的 providers
  • browser-provider 的 upsert key 为 preset + origin

Run config

Run config 现在支持显式 judge provider:

{
  "variation": {
    "enabled": false,
    "per_item": 0,
    "strategies": []
  },
  "judge": {
    "provider_id": "prov_judge",
    "model": "gpt-4o-mini",
    "prompt": "Return 0 or 1.",
    "source": "browser_harness"
  }
}

Worker 行为:

  • 优先使用 config.judge.provider_id
  • 对旧 run 回退到 run 自身 provider
  • 写入 judge failure 细节,但不破坏已导入的 browser run

REST 端点

POST /browser-harness/packs

为选定的数据集范围构建仅包含 prompt 的签名 pack。

响应包含:

  • 数据集标识与 version hash
  • prompt_text 的过滤后 items
  • section 元数据
  • scoring eligibility
  • dataset_token

响应不包含:

  • answers
  • checker logic
  • embedded assets
  • 完整数据集包

POST /browser-harness/imports

从以下两种输入导入 Browser Harness capture:

  • .eval752.zip
  • JSON fallback payload

导入职责:

  • 校验 dataset_token
  • 校验数据集/version 是否与当前工作区一致
  • 创建或复用 browser-only provider
  • 创建一个 triggered_by = browser_harness 的已完成 run
  • 持久化 run items、浏览器元数据和日志
  • 入队 runs.score

响应会返回:

  • run_id
  • provider_id
  • provider_name
  • 数据集是否复用
  • 是否已入队评分

ZIP 打包格式

运行时 ZIP 导出为 store-only archives,包含:

  • manifest.json
  • browser_harness.json
  • results.jsonl

manifest.json

{
  "format": "eval752.browser_harness.v1",
  "exported_at": "2026-03-12T18:10:00Z",
  "item_count": 12,
  "error_count": 1
}

browser_harness.json

{
  "dataset_token": "signed-token",
  "target": {
    "preset": "gemini",
    "origin": "https://gemini.google.com",
    "display_name": "Gemini Web"
  },
  "judge": {
    "provider_id": "prov_judge",
    "model": "gpt-4o-mini",
    "prompt": "Return 0 or 1."
  },
  "session": {
    "started_at": "2026-03-12T18:09:00Z",
    "finished_at": "2026-03-12T18:10:00Z"
  },
  "label": "Gemini Web Browser Harness import"
}

results.jsonl

每个被捕获条目对应一条 JSON 对象:

{
  "dataset_item_id": "item-1",
  "prompt_text": "What is the capital of France?",
  "response": "Paris",
  "error": null,
  "timing": {
    "started_at": "2026-03-12T18:09:02Z",
    "finished_at": "2026-03-12T18:09:05Z",
    "latency_ms": 3120
  },
  "model_label": "Gemini 2.5 Flash"
}

前端运行时

Browser Harness 页面会生成:

  • 一段可直接运行的 raw self-contained script
  • 一个包装该脚本的 bookmarklet

运行时会:

  • 校验 window.location.origin
  • 显示轻量 overlay
  • 在每个条目前点击 new chat
  • 填充 composer 并发送 prompt
  • 结合 pacing 与 selector 规则等待响应稳定
  • 记录 timing、model label 与错误
  • 下载 ZIP;如果 ZIP 创建失败则回退到 JSON

最近一次 capture payload 还会写入 window.__EVAL752_LAST_BROWSER_HARNESS_RESULT__,方便调试。

Fixture 策略

仓库内置了三个可确定复现的本地 fixture:

  • chatgpt.html
  • gemini.html
  • custom.html

每个 fixture 都必须支持:

  • new chat
  • send
  • busy / done signaling
  • assistant turn rendering
  • model label visibility

这些 fixtures 是以下场景的签收基准:

  • Playwright E2E
  • playwright-interactive 手工 QA
  • 在触碰真实厂商页面前先做 selector 调试

QA 期望

任何 Browser Harness 变更都应覆盖:

  • 后端 API 测试:pack / import / judge-provider selection
  • 前端测试:preflight blocking 与 selector validation
  • Playwright E2E:ChatGPT、Gemini、Custom 与 viewport fit
  • 基于 fixture pages 的手工浏览器 QA

规范的手工清单见 docs/testing/browser-harness-signoff.md