Agent Evals Explained
← /learn · 05

Comparing two agents

Two agents on the same tasks: the paired difference and McNemar's test against the unpaired comparison, the correlation that pairing exploits, and how seeds and repeated runs add or remove variance.

Loading the animation…

Concept

Most evals end in a comparison: is the new agent better than the old one? The cheapest way to get a sharper answer is not more tasks but a better design: run both agents on the same tasks and compare them task by task.

The reason is that much of the noise in a score is which tasks you drew (chapter 1). When both agents face the same tasks, that noise is shared: an easy draw flatters both, a hard one hurts both, and in the difference it cancels. In the animation, agent A (skill 0.45) and agent B (skill 0.1) run on the same 300 tasks, one run each. A's per-task scores and B's correlate at 0.39. After all 300 tasks, A leads by 7.3 points (the true gap on these tasks is 7.2 points). The paired 95% interval is 1.2% to 13.5% and excludes zero; the unpaired one, the interval you would report if the two had been run on separate task sets, is -0.6% to 15.2% and does not. Same data, a standard error 0.78 times the unpaired one, a different conclusion.

McNemar's test sees the same thing from the tiles. A task both agents pass, or both fail, says nothing about which is better; only the discordant tasks do, where exactly one passes. With one run each, A alone passed 56 tasks and B alone 34. If the agents were equally good, each discordant task would be a coin flip, and a split this uneven or more comes up with probability 0.0263.

Seeds and temperature add a second source of noise: the same agent on the same task does not always do the same thing. The bars under the chart split the variance of the difference into the part from which tasks were drawn and the part from the runs. Unpaired, the tasks account for 32% of that variance; paired, for 0.4%. Pairing removes almost all of the first part; more runs per task shrink the second. Switch to five runs per task and the paired standard error falls from 3.1 points to 2.5 points. Each run costs as much as a new task, so whether to buy runs or tasks depends on which part dominates.

Maths

Write ata_t and btb_t for the agents' scores on task tt (a pass, or a mean over runs) and dt=at−btd_t = a_t - b_t. The difference in mean score is Δ^=dˉ\hat\Delta = \bar d, with standard error sd/Ts_d/\sqrt{T}. Expanding sd2=sA2+sB2−2 cov⁡^(A,B)s_d^2 = s_A^2 + s_B^2 - 2\,\widehat{\operatorname{cov}}(A, B) shows what pairing buys: the covariance, which the unpaired standard error (sA2+sB2)/T\sqrt{(s_A^2 + s_B^2)/T} ignores. The engine checks both against scipy's paired and Welch tt statistics.

For pass/fail outcomes the paired table has four cells; under "no difference" each of the n10+n01n_{10} + n_{01} discordant tasks goes either way with probability 12\tfrac12, so the exact McNemar pp-value is a two-sided binomial test, 2 P(X≤min⁡(n10,n01))2\,P(X \le \min(n_{10}, n_{01})) with X∼Binomial(n10+n01,12)X \sim \text{Binomial}(n_{10}+n_{01}, \tfrac12), and the continuity-corrected χ2\chi^2 above approximates it. Planning a paired study needs the discordance ψ=p10+p01\psi = p_{10} + p_{01} and the difference δ=p10−p01\delta = p_{10} - p_{01} (Connor 1987):

n=(z1−α/2ψ+z1−βψ−δ2)2δ2.n = \frac{\big(z_{1-\alpha/2}\sqrt{\psi} + z_{1-\beta}\sqrt{\psi - \delta^2}\big)^2}{\delta^2}.

With mm runs per task and within-task correlation ρ\rho, one task's mean has variance μ(1−μ) 1+(m−1)ρm\mu(1-\mu)\,\frac{1 + (m-1)\rho}{m}: more runs help only as far as ρ\rho allows, and not at all when ρ=1\rho = 1.

Code

The paired and unpaired standard errors, cut from the vendored engine (stats.ts):

const seP = Math.sqrt(vd / n);
const seU = Math.sqrt(va / n + vb / n);
const cov = (va + vb - vd) / 2.0;