Skip to content

Commit 56b855c

Browse files
committed
Add harness-engineering-bench (benchmarks at top level)
Bring the benchmark harness with each benchmark as a top-level sibling of gaia/ (ale-bench, officeqa, swe-atlas-qna, tau3), flattening the former candidates/ grouping.
1 parent 3fdcf90 commit 56b855c

71 files changed

Lines changed: 19060 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
36+
### Candidates
37+
38+
| Benchmark | Editable target | Dataset | Split |
39+
| --- | --- | --- | --- |
40+
| [SWE-Atlas-QnA baseline](candidates/swe-atlas-qna/baseline/) | Codebase investigation agent | Harbor `scale-ai/swe-atlas-qna` | 20% / 40% / 40% |
41+
| [tau3 baseline](candidates/tau3/baseline/) | MCP customer-service agent | Harbor `sierra-research/tau3-bench` | 20% / 40% / 40% |
42+
| [OfficeQA baseline](candidates/officeqa/baseline/) | Grounded document-QA agent | Treasury Bulletin corpus | 20% / 40% / 40% |
43+
| [ALE-Bench (ahc011)](candidates/ale-bench/) | C++ solver program (component, not an agent) | ALE-Bench AHC problem | public / private seeds |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
environment/vero/
2+
runs/
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Main service: the optimizer workbench a coding agent works in.
2+
# Holds the editable target (solution.cpp) plus the VeRO CLI so the agent can
3+
# call `vero harbor eval` / `vero harbor status` against the trusted sidecar.
4+
FROM ghcr.io/astral-sh/uv:python3.12-bookworm
5+
6+
RUN apt-get update \
7+
&& apt-get install -y --no-install-recommends git ca-certificates \
8+
&& rm -rf /var/lib/apt/lists/*
9+
10+
# VeRO is not on public PyPI; vendor it into the build context first
11+
# (`cp -r <repo>/vero environment/vero`) then install it.
12+
COPY vero /opt/vero
13+
RUN uv pip install --system "/opt/vero[harbor]"
14+
15+
COPY agent-seed /opt/agent-seed
16+
COPY main/seed.sh /opt/seed.sh
17+
RUN chmod +x /opt/seed.sh && useradd -m -u 1001 agent
18+
19+
WORKDIR /work/agent
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <bits/stdc++.h>
2+
int main(){
3+
std::ios::sync_with_stdio(false);
4+
std::string line;
5+
while (std::getline(std::cin, line)) {}
6+
std::cout << "\n";
7+
return 0;
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <bits/stdc++.h>
2+
int main(){
3+
std::ios::sync_with_stdio(false);
4+
std::string line;
5+
while (std::getline(std::cin, line)) {}
6+
std::cout << "\n";
7+
return 0;
8+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Harbor merges this after its generated main-service configuration.
2+
services:
3+
main:
4+
command: ["/opt/seed.sh"]
5+
environment:
6+
VERO_EVAL_URL: "http://eval-sidecar:8000"
7+
OPENAI_API_KEY: "${OPENAI_API_KEY:?OPENAI_API_KEY must be set}"
8+
OPENAI_BASE_URL: "${OPENAI_BASE_URL:?OPENAI_BASE_URL must be set}"
9+
volumes:
10+
- agent_repo:/work/agent
11+
- token_state:/state/token:ro
12+
depends_on:
13+
eval-sidecar:
14+
condition: service_healthy
15+
16+
eval-sidecar:
17+
build:
18+
context: .
19+
dockerfile: sidecar/Dockerfile
20+
# Runs its own Docker daemon (DinD) so ALE-Bench's sibling judge/tool
21+
# containers share this container's filesystem — required for their bind
22+
# mounts to resolve. Needs privileged + host cgroup ns (nested cgroup v2
23+
# otherwise fails: "cannot enter cgroupv2 ... it is in threaded mode").
24+
privileged: true
25+
cgroup: host
26+
command:
27+
- "vero"
28+
- "harbor"
29+
- "serve"
30+
- "--factory"
31+
- "ale_factory:build"
32+
- "--config"
33+
- "/opt/serve.json"
34+
- "--admin-token"
35+
- "/state/token/admin.token"
36+
environment:
37+
# ALE-Bench talks to the in-container dockerd started by the entrypoint.
38+
DOCKER_HOST: "unix:///var/run/docker.sock"
39+
ALE_BENCH_DATA: "/state/ale-data"
40+
volumes:
41+
- agent_repo:/work/agent:ro
42+
- admin_state:/state/admin
43+
- token_state:/state/token
44+
- ale_data:/state/ale-data
45+
# DinD storage (avoids overlay-on-overlay issues for the inner daemon).
46+
- docker_storage:/var/lib/docker
47+
healthcheck:
48+
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
49+
interval: 5s
50+
timeout: 10s
51+
# The entrypoint starts dockerd + pulls the ~4GB judge image before serving.
52+
retries: 40
53+
start_period: 180s
54+
55+
volumes:
56+
agent_repo:
57+
admin_state:
58+
token_state:
59+
ale_data:
60+
docker_storage:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/sh
2+
set -eu
3+
if [ ! -d /work/agent/.git ]; then
4+
cp -a /opt/agent-seed/. /work/agent/
5+
cd /work/agent
6+
git init -q
7+
git add -A
8+
git -c user.email=seed@vero.test -c user.name=seed commit -qm "baseline"
9+
fi
10+
find /work/agent -exec chown agent:agent {} +
11+
git config --system --add safe.directory /work/agent
12+
exec sleep infinity
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Trusted evaluation sidecar. It owns scoring, budget, disclosure, and final
2+
# selection. The inner loop is a plain CommandBackend (harness/score.py), which
3+
# calls ALE-Bench's deterministic judge. ALE-Bench spawns the C++ judge + its
4+
# Rust-tool builder as sibling Docker containers with bind mounts, so this
5+
# container runs its OWN Docker daemon (DinD) — the sibling containers must share
6+
# this container's filesystem for their mounts to resolve. Requires privileged.
7+
FROM ghcr.io/astral-sh/uv:python3.12-bookworm
8+
9+
RUN apt-get update \
10+
&& apt-get install -y --no-install-recommends \
11+
git ca-certificates curl \
12+
docker.io iptables \
13+
libcairo2 libffi8 \
14+
&& rm -rf /var/lib/apt/lists/*
15+
16+
# VeRO (not on public PyPI): vendor into the build context, then install.
17+
COPY vero /opt/vero
18+
RUN uv pip install --system "/opt/vero[harbor]"
19+
20+
# ALE-Bench toolkit (the judge orchestration + scoring).
21+
RUN uv pip install --system "git+https://github.com/SakanaAI/ALE-Bench.git"
22+
23+
COPY agent-baseline /opt/agent-baseline
24+
COPY sidecar/ale_factory.py /opt/ale_factory.py
25+
COPY sidecar/harness /opt/harness
26+
COPY sidecar/serve.json /opt/serve.json
27+
COPY sidecar/dind-entrypoint.sh /opt/dind-entrypoint.sh
28+
RUN chmod +x /opt/dind-entrypoint.sh
29+
30+
RUN cd /opt/agent-baseline \
31+
&& git init -q \
32+
&& git add -A \
33+
&& git -c user.email=baseline@vero.test -c user.name=baseline commit -qm "baseline" \
34+
&& git config --system --add safe.directory /opt/agent-baseline \
35+
&& git config --system --add safe.directory /work/agent \
36+
&& git config --system --add safe.directory /work/agent/.git
37+
38+
ENV PYTHONPATH=/opt
39+
WORKDIR /opt
40+
# Start the in-container Docker daemon + bootstrap the judge image, then exec the
41+
# serve command passed by docker-compose.
42+
ENTRYPOINT ["/opt/dind-entrypoint.sh"]
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
"""Custom Harbor sidecar factory for ALE-Bench (program optimization).
2+
3+
vero drives the OUTER loop: a coding agent edits /work/agent/solution.cpp to
4+
maximize the ALE-Bench score for a fixed AHC problem. Each candidate is scored by
5+
a plain CommandBackend (harness/score.py -> ALE-Bench's deterministic judge),
6+
NOT a nested `harbor run`. Disclosure maps onto ALE-Bench's public/private split:
7+
development+validation are scored on public seeds, test on private seeds.
8+
"""
9+
10+
from __future__ import annotations
11+
12+
from pathlib import Path
13+
14+
from vero.candidate_repository import GitCandidateRepository
15+
from vero.evaluation import (
16+
BackendRegistry,
17+
BudgetLedger,
18+
CaseRange,
19+
EvaluationBudget,
20+
EvaluationDatabase,
21+
EvaluationSet,
22+
Evaluator,
23+
MetricSelector,
24+
ObjectiveSpec,
25+
)
26+
from vero.evaluation.command import CommandBackend, CommandBackendConfig
27+
from vero.evaluation.engine import EvaluationEngine
28+
from vero.harbor.serve import SidecarComponents
29+
from vero.harbor.sidecar import EvaluationSidecar, SidecarEvaluationPolicy
30+
from vero.harbor.transport import GitCandidateTransport
31+
from vero.harbor.verifier import CanonicalVerifier, VerificationSelection, VerificationTarget
32+
from vero.sandbox import LocalSandbox
33+
from vero.workspace import GitWorkspace
34+
35+
SET = "ale-bench"
36+
# ahc011 is a maximization problem; ALE-Bench zeroes invalid/CE/TLE submissions.
37+
OBJ = ObjectiveSpec(
38+
selector=MetricSelector(metric="score"),
39+
direction="maximize",
40+
failure_value=0.0,
41+
)
42+
43+
44+
def _backend(harness_root: str) -> CommandBackend:
45+
return CommandBackend(
46+
CommandBackendConfig(
47+
harness_root=harness_root,
48+
command=[
49+
"python", "{harness}/score.py",
50+
"--workspace", "{workspace}",
51+
"--request", "{request}",
52+
"--report", "{report}",
53+
"--artifacts", "{artifacts}",
54+
],
55+
# The judge runs the submission in Docker; the scorer needs daemon access.
56+
passthrough_environment=["PATH", "HOME", "DOCKER_HOST", "HF_TOKEN"],
57+
)
58+
)
59+
60+
61+
async def build(config: dict) -> SidecarComponents:
62+
repo_path = config["repo_path"]
63+
agent_repo_path = config["agent_repo_path"]
64+
session_dir = Path(config["session_dir"])
65+
harness_root = config["harness_root"]
66+
session_dir.mkdir(parents=True, exist_ok=True)
67+
68+
sandbox = await LocalSandbox.create(root=Path(repo_path).parent)
69+
workspace = await GitWorkspace.from_path(sandbox, repo_path)
70+
candidate_repository = await GitCandidateRepository.create(
71+
session_dir / "candidates", workspace=workspace
72+
)
73+
database = EvaluationDatabase.load_reconciled(
74+
database_path=session_dir / "database.json",
75+
evaluations_dir=session_dir / "evaluations",
76+
database_id="ale",
77+
)
78+
ledger = BudgetLedger(
79+
[
80+
EvaluationBudget(
81+
backend_id="cmd",
82+
evaluation_set_key=f"cmd:{SET}:development",
83+
total_runs=40,
84+
),
85+
EvaluationBudget(
86+
backend_id="cmd",
87+
evaluation_set_key=f"cmd:{SET}:validation",
88+
total_runs=40,
89+
),
90+
],
91+
path=session_dir / "budgets.json",
92+
)
93+
ledger.save()
94+
95+
engine = EvaluationEngine(
96+
evaluator=Evaluator(
97+
candidate_repository=candidate_repository,
98+
sandbox=workspace.sandbox,
99+
session_dir=session_dir,
100+
session_id="ale",
101+
),
102+
backends=BackendRegistry({"cmd": _backend(harness_root)}),
103+
database=database,
104+
database_path=session_dir / "database.json",
105+
budget_ledger=ledger,
106+
)
107+
transport = GitCandidateTransport(
108+
workspace=workspace,
109+
candidate_repository=candidate_repository,
110+
agent_repo_path=agent_repo_path,
111+
)
112+
baseline = await transport.trusted_candidate("HEAD")
113+
114+
selection = VerificationSelection(
115+
mode="auto_best",
116+
backend_id="cmd",
117+
evaluation_set=EvaluationSet(
118+
name=SET, partition="validation", selection=CaseRange(start=0, stop=1)
119+
),
120+
objective=OBJ,
121+
baseline_candidate=baseline,
122+
)
123+
sidecar = EvaluationSidecar(
124+
engine=engine,
125+
candidate_transport=transport,
126+
access_policies=[
127+
SidecarEvaluationPolicy(
128+
backend_id="cmd", evaluation_set_name=SET, partition="development",
129+
disclosure="full", expose_case_resources=True, objective=OBJ,
130+
),
131+
SidecarEvaluationPolicy(
132+
backend_id="cmd", evaluation_set_name=SET, partition="validation",
133+
disclosure="aggregate", min_aggregate_cases=1, objective=OBJ,
134+
),
135+
],
136+
admin_volume=session_dir / "admin",
137+
)
138+
await sidecar.initialize_context()
139+
140+
verifier = CanonicalVerifier(
141+
engine=engine,
142+
selection=selection,
143+
targets=[
144+
VerificationTarget(
145+
reward_key="score", backend_id="cmd",
146+
evaluation_set=EvaluationSet(name=SET, partition="test"),
147+
objective=OBJ, max_attempts=1,
148+
)
149+
],
150+
admin_volume=session_dir / "admin",
151+
score_baseline=True,
152+
)
153+
return SidecarComponents(sidecar=sidecar, verifier=verifier)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/sh
2+
# Start an in-container Docker daemon (DinD) so ALE-Bench's judge + Rust-tool
3+
# containers are SIBLINGS on a daemon whose filesystem IS this container's — the
4+
# only way their bind mounts resolve. (A host/VM socket mount fails: the sibling
5+
# containers resolve mount sources against the daemon host, not this container.)
6+
# Requires the container to run privileged. Then serve (the passed command).
7+
set -eu
8+
9+
dockerd >/var/log/dockerd.log 2>&1 &
10+
for i in $(seq 1 60); do
11+
docker info >/dev/null 2>&1 && break
12+
sleep 1
13+
done
14+
if ! docker info >/dev/null 2>&1; then
15+
echo "dockerd failed to start" >&2
16+
cat /var/log/dockerd.log >&2 || true
17+
exit 1
18+
fi
19+
20+
# Bootstrap the ALE-Bench C++ judge image. ALE-Bench expects the tag
21+
# ale-bench:cpp20-202301; upstream builds it as `FROM yimjk/ale-bench:cpp20-202301`
22+
# plus `chown UID:GID /workdir` — a no-op as root — so a pull + retag suffices.
23+
docker pull yimjk/ale-bench:cpp20-202301
24+
docker tag yimjk/ale-bench:cpp20-202301 ale-bench:cpp20-202301
25+
# rust:1.79.0-buster (tool build) and httpd (vis) are pulled on demand by ale_bench.
26+
27+
exec "$@"

0 commit comments

Comments
 (0)