Skip to content

Commit 64c71c6

Browse files
authored
Merge pull request #46 from scaleapi/pr3-harness-bench
2 parents 9582cd5 + 091c92e commit 64c71c6

115 files changed

Lines changed: 34521 additions & 29 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,3 @@ wheels/
1515
secrets.env
1616
*.secrets.env
1717
!*.example
18-
19-
# Local benchmark scoping notes (paper planning; not tracked)
20-
harness-engineering-bench/benchmark-scoping.md

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "browsecomp-plus-upstream"]
2+
path = harness-engineering-bench/browsecomp-plus/upstream
3+
url = https://github.com/texttron/BrowseComp-Plus.git

harness-engineering-bench/CONFIGURATION.md

Lines changed: 437 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Harness engineering benchmarks
2+
3+
`harness-engineering-bench` contains end-to-end benchmarks for automatically
4+
improving the harness of an agent or the code of a component used to build
5+
agents. Each leaf directory pairs one editable target program with one immutable
6+
Harbor dataset and compiles them into an outer Harbor optimization task.
7+
8+
The benchmark definitions intentionally keep three boundaries visible:
9+
10+
- `target/` is the program the optimization agent may edit.
11+
- `partitions/` pins the cases and the development/validation/test split.
12+
- `build.yaml` is trusted configuration: model, evaluator, access policy,
13+
budgets, and final scoring.
14+
15+
In each benchmark, the complete development tasks and attachments are mounted
16+
read-only for the optimization agent. Development evaluations expose per-case
17+
results and complete Harbor trial records, including exact failures and
18+
target-agent logs. Validation remains aggregate-only, and
19+
test is reachable only by the trusted final verifier.
20+
21+
The paper-era benchmark stack remains available on the `paper/v1` branch and
22+
the `paper-v1` tag. New benchmarks should use this Harbor-native layout.
23+
24+
## Benchmarks
25+
26+
Promoted benchmarks live at the top level. Task sets still under review live in
27+
`candidates/`; we work through the list in the paper's `benchmark-scoping.md` and
28+
promote a task set to the top level once it is ready.
29+
30+
### Promoted
31+
32+
| Benchmark | Editable target | Dataset | Split |
33+
| --- | --- | --- | --- |
34+
| [GAIA baseline](gaia/baseline/) | Tool-using Responses API agent | Harbor `gaia/gaia` | 20% / 40% / 40% |
35+
| [OfficeQA baseline](officeqa/baseline/) | Grounded document-QA agent | Treasury Bulletin corpus | 20% / 40% / 40% |
36+
| [SWE-Atlas-QnA baseline](swe-atlas-qna/baseline/) | Codebase investigation agent | Harbor `scale-ai/swe-atlas-qna` | 20% / 40% / 40% |
37+
| [tau3 baseline](tau3/baseline/) | MCP customer-service agent | Harbor `sierra-research/tau3-bench` | 20% / 40% / 40% |
38+
| [BrowseComp-Plus baseline](browsecomp-plus/baseline/) | Fixed-corpus deep-research agent | Pinned local Harbor tasks | 20% / 40% / 40% |
39+
40+
**`swe-bench-pro/` is at the top level but is not promoted, and its numbers are
41+
not comparable to the five above.** It predates the normalization pass those five
42+
went through and still differs on most of it: case budgets are 1x the partition
43+
size rather than 4x, the held-out target has no `n_attempts: 3` / `mean`
44+
override so it is scored once, there is no pinned `baseline_reward` (and
45+
`score_baseline: true` adds a second full held-out pass), the agent clock runs at
46+
0.6x the declared case timeout, gateway request and token caps are 20-30x tighter
47+
than the sizing convention, there is no `agent_env` block, and telemetry goes to
48+
its own W&B project. Treat it as a work in progress: launching it will produce a
49+
number, but not a measurement of the same quantity. `CONFIGURATION.md` documents
50+
the conventions it is missing.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Generated from the obfuscated upstream dataset. These files contain decrypted
2+
# benchmark questions and answers and must not be committed.
3+
tasks/
4+
baseline/target/.venv/
5+
**/__pycache__/
6+
**/*.pyc
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# BrowseComp-Plus
2+
3+
This benchmark turns all 830 queries from
4+
[BrowseComp-Plus](https://github.com/texttron/BrowseComp-Plus) into local Harbor
5+
tasks. It evaluates a deep-research agent against the benchmark's fixed corpus
6+
and canonical BM25 index rather than the live web.
7+
8+
## Pinned sources
9+
10+
- Upstream repository submodule: `046949032b0328319cc9a02663a759ec601d9402`
11+
- Query dataset `Tevatron/browsecomp-plus`:
12+
`144cff8e35b5eaef7e526346aa60774a9deb941f`
13+
- BM25 index `Tevatron/browsecomp-plus-indexes`:
14+
`b3f37f70c33829eb09d04784a54277a31871fd63`
15+
16+
The submodule is the Git pointer requested for the integration. The generator
17+
refuses to run if it is checked out at another commit. Hugging Face revisions
18+
are full immutable commit ids as well.
19+
20+
## Generate Harbor tasks
21+
22+
Initialize the submodule and run the builder from this directory:
23+
24+
```bash
25+
git submodule update --init harness-engineering-bench/browsecomp-plus/upstream
26+
27+
cd harness-engineering-bench/browsecomp-plus
28+
uv run --no-project --python 3.12 --with datasets==4.0.0 -- \
29+
python scripts/build_tasks.py
30+
```
31+
32+
The builder downloads and decrypts the pinned query dataset using the pinned
33+
upstream implementation, then writes one complete Harbor task per query under
34+
the ignored `tasks/` directory. It also regenerates the committed deterministic
35+
166/332/332 development, validation, and test split. Use `--force` to replace
36+
an existing generated tree or `--check` to verify it byte-for-byte.
37+
38+
The first Harbor image build downloads the pinned BM25 index (about 2.2 GB).
39+
Every task has an identical environment, so subsequent tasks reuse that image.
40+
41+
## Scoring and trust boundary
42+
43+
Answers use BrowseComp-Plus's required Explanation / Exact Answer / Confidence
44+
format. The verifier follows the official upstream OpenAI evaluator and its
45+
default `gpt-4.1` judge. The upstream primary leaderboard instead runs the same
46+
judge prompt with Qwen3-32B; this Harbor integration deliberately uses the
47+
repository's supported API evaluator so tasks do not require a local GPU judge.
48+
49+
The judge runs inside the task environment and therefore receives the real
50+
upstream credential. As with the SWE-Atlas-QnA rubric judge and tau3 task-owned
51+
LLM services, this disables uid isolation and assumes a non-adversarial
52+
optimizer. The editable baseline itself exposes no live-web or shell tool and
53+
uses only the fixed local index.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
compiled/
2+
target/.venv/
3+
target/.pytest_cache/
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# BrowseComp-Plus baseline
2+
3+
This editable target is a Responses API deep-research agent with three tools:
4+
search the pinned BM25 index, open a document, and submit a formatted response.
5+
The optimization agent may change its prompts, control flow, tool use, or
6+
dependencies, but not the dataset, index, split, evaluated model, or verifier.
7+
8+
Build the generated tasks first as described in the parent
9+
[`README.md`](../README.md), then compile from the repository root:
10+
11+
```bash
12+
cd vero
13+
VERO_SKIP_SECRET_CHECK=1 uv run vero harbor build \
14+
--config ../harness-engineering-bench/browsecomp-plus/baseline/build.yaml \
15+
--output ../harness-engineering-bench/browsecomp-plus/baseline/compiled
16+
```
17+
18+
For a real run, copy `secrets.env.example` to the ignored `secrets.env`, fill it
19+
in, and use `vero harbor run` in the same way as the other harness-engineering
20+
benchmarks.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: vero/optimize-browsecomp-plus-baseline
2+
description: >-
3+
Improve a deep-research agent on BrowseComp-Plus using its fixed corpus,
4+
canonical BM25 index, and semantic answer evaluator.
5+
agent_repo: target
6+
task_source: ../tasks
7+
task_manifest: ../partitions/manifest.json
8+
agent_import_path: browsecomp_plus_agent.agent:BrowseCompPlusAgent
9+
harbor_requirement: harbor[modal]==0.20.0
10+
11+
partition_files:
12+
development: ../partitions/development.json
13+
validation: ../partitions/validation.json
14+
test: ../partitions/test.json
15+
16+
# Four full passes over each agent-visible partition.
17+
agent_access:
18+
- partition: development
19+
disclosure: full
20+
expose_case_resources: true
21+
total_runs: 100
22+
total_cases: 132
23+
- partition: validation
24+
disclosure: aggregate
25+
expose_case_resources: false
26+
min_aggregate_cases: 5
27+
total_runs: 100
28+
total_cases: 264
29+
30+
selection_partition: validation
31+
targets:
32+
- partition: test
33+
reward_key: reward
34+
baseline_reward: 0.4619 # re-pinned 0.424 / 0.470 / 0.492 (sd 0.0283); was 0.4495. See runs/BASELINES.md
35+
failure_value: 0.0
36+
max_attempts: 1
37+
# The held-out eval is noisy: score the selected candidate 3x per case and
38+
# average, so the final reward is comparable to the pinned baseline, which was
39+
# itself pooled over 3 rounds. Without this the candidate carries ~sqrt(3) more
40+
# standard error than the floor it is judged against.
41+
# Per-target override - search/validation keep the global n_attempts (1).
42+
n_attempts: 3
43+
aggregate_attempts: mean
44+
45+
evaluation_set_name: browsecomp-plus
46+
objective:
47+
selector:
48+
metric: score
49+
direction: maximize
50+
reward_mode: submit
51+
baseline_floor: false
52+
score_baseline: false
53+
rescore_top_k: 3
54+
rescore_attempts: 1
55+
56+
model: fireworks_ai/deepseek-v4-flash
57+
environment_name: ${inner_env:-modal}
58+
extra_harbor_args: ["--ek", "app_name=harness-engineering-bench", "--ek", "sandbox_idle_timeout_secs=3600"]
59+
harbor_python_version: "3.12"
60+
n_attempts: 1
61+
max_retries: 1
62+
infrastructure_max_attempts: 3
63+
infrastructure_retry_delay_seconds: 5
64+
aggregate_attempts: best
65+
feedback_transcripts: true
66+
feedback_max_bytes: 16000
67+
expose_attempt_detail: false
68+
# Unreachable: worst case is ceil(198/24) x 3600 = 32400s, every
69+
# finalize trial (66 held-out x n_attempts=3) hitting its own cap. Assumes
70+
# max_concurrency=24; recompute if that drops.
71+
timeout_seconds: 39600
72+
# Exactly the dataset's declared [agent] timeout_sec, so vero's derived
73+
# --agent-timeout-multiplier is 1.0 and the target agent gets precisely the
74+
# clock the benchmark intends. Harbor times agent setup, environment build and
75+
# verification on separate clocks with separate multipliers, so none of them
76+
# eat into this budget and no buffer is warranted.
77+
case_timeout_seconds: 3600
78+
task_agent_timeout_seconds: 3600
79+
max_concurrency: 24 # 8 -> 24; see officeqa for the measured headroom argument
80+
error_rate_threshold: 0.1
81+
# Unreachable: worst-case finalize (32400) + worst-case rescore_top_k=3
82+
# validation rescore (32400). A verifier timeout loses the score outright.
83+
verifier_timeout_seconds: 75600
84+
secrets:
85+
- MODAL_TOKEN_ID
86+
- MODAL_TOKEN_SECRET
87+
- WANDB_API_KEY
88+
- WANDB_BASE_URL
89+
90+
wandb:
91+
project: harness-engineering-bench # one project for the whole suite
92+
group: browsecomp-plus # keeps the benchmark distinguishable in the shared project
93+
name: ${wandb_run:-browsecomp-plus} # per-launch label, e.g. --param wandb_run=browsecomp-plus__claude-sonnet-5
94+
tags: [browsecomp-plus]
95+
log_traces: true
96+
97+
inference_gateway:
98+
upstream_api_key_env: OPENAI_API_KEY
99+
upstream_base_url_env: OPENAI_BASE_URL
100+
producer:
101+
allowed_models: ["${optimizer_model:-openai/gpt-5.4}"]
102+
max_concurrency: 8
103+
# See officeqa/baseline/build.yaml for the sizing rationale: the case budget is
104+
# the spend control, so a token cap only needs to stop a runaway.
105+
evaluation:
106+
allowed_models: [fireworks_ai/deepseek-v4-flash]
107+
max_requests: 200000
108+
max_tokens: 2000000000 # 396 agent case-runs (132 dev + 264 validation)
109+
max_concurrency: 64
110+
# Reserved so a search-phase overspend can never starve held-out scoring.
111+
finalization:
112+
allowed_models: [fireworks_ai/deepseek-v4-flash]
113+
max_requests: 200000
114+
max_tokens: 2000000000 # 66 test cases x3 attempts + rescore headroom
115+
max_concurrency: 64
116+
117+
instruct_multifidelity: true
118+
instruct_exhaust_budget: true
119+
120+
# The official upstream OpenAI evaluator runs inside the task container. It
121+
# uses the raw upstream credential while the editable target agent continues to
122+
# receive only the evaluation-scoped gateway credential.
123+
task_services_use_upstream: true
124+
harness_user: null
125+
# Optimizer-agent env (forwarded to the harbor claude-code agent as --ae KEY=VALUE).
126+
# Claude Code's Bash tool caps a single call at BASH_MAX_TIMEOUT_MS (default
127+
# 600000=10min), well under one inner eval, which pushed the officeqa optimizer
128+
# into --detach + background-poll + end-turn -- and a headless --print run is
129+
# never re-woken, so the search died there. Raise the cap so a whole eval fits in
130+
# one blocking call. The background-task vars are defence in depth only: they gate
131+
# *automatic* backgrounding and do NOT remove the Bash tool's run_in_background
132+
# parameter, which the model can still choose. The instruction forbids that.
133+
agent_env:
134+
# Above this benchmark's widest single eval: a full validation pass is
135+
# ceil(66/24) x 3600 = 10800s worst case.
136+
BASH_MAX_TIMEOUT_MS: "14400000"
137+
BASH_DEFAULT_TIMEOUT_MS: "14400000" # same as max: an un-timed eval must still block
138+
ENABLE_BACKGROUND_TASKS: "0"
139+
FORCE_AUTO_BACKGROUND_TASKS: "0"
140+
# Harnesses installed with `uv tool install` (mini-swe-agent, swe-agent)
141+
# default to symlinking their entry point into /usr/local/bin, which the
142+
# unprivileged optimizer user cannot write: "Failed to install executable
143+
# ... Permission denied". npm/nvm-based harnesses (claude-code, opencode)
144+
# are unaffected, so this only bites when the harness changes.
145+
UV_TOOL_BIN_DIR: "/home/agent/.local/bin"
146+
147+
task_environment:
148+
BROWSECOMP_JUDGE_MODEL: gpt-4.1
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Optimizer and evaluated-agent inference. OPENAI_BASE_URL may point to an
2+
# OpenAI-compatible upstream; use https://api.openai.com/v1 for OpenAI.
3+
OPENAI_API_KEY=sk-your-upstream-inference-key
4+
OPENAI_BASE_URL=https://api.openai.com/v1
5+
6+
MODAL_TOKEN_ID=your-modal-token-id
7+
MODAL_TOKEN_SECRET=your-modal-token-secret
8+
9+
WANDB_API_KEY=your-wandb-api-key
10+
WANDB_BASE_URL=https://api.wandb.ai

0 commit comments

Comments
 (0)