abeval 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- abeval-0.1.0/.github/FUNDING.yml +1 -0
- abeval-0.1.0/.github/dependabot.yml +10 -0
- abeval-0.1.0/.github/workflows/test.yml +29 -0
- abeval-0.1.0/CITATION.cff +18 -0
- abeval-0.1.0/LICENSE +21 -0
- abeval-0.1.0/Makefile +33 -0
- abeval-0.1.0/PKG-INFO +201 -0
- abeval-0.1.0/README.md +178 -0
- abeval-0.1.0/abeval/__init__.py +34 -0
- abeval-0.1.0/abeval/__main__.py +191 -0
- abeval-0.1.0/abeval/_dist.py +109 -0
- abeval-0.1.0/abeval/ci.py +124 -0
- abeval-0.1.0/abeval/compare.py +131 -0
- abeval-0.1.0/abeval/data.py +54 -0
- abeval-0.1.0/abeval/power.py +127 -0
- abeval-0.1.0/abeval/reliability.py +85 -0
- abeval-0.1.0/docs/.nojekyll +0 -0
- abeval-0.1.0/docs/index.html +251 -0
- abeval-0.1.0/examples/judge_repeats.jsonl +120 -0
- abeval-0.1.0/examples/make_fixtures.py +50 -0
- abeval-0.1.0/examples/run_a.jsonl +200 -0
- abeval-0.1.0/examples/run_b.jsonl +200 -0
- abeval-0.1.0/pyproject.toml +44 -0
- abeval-0.1.0/tests/test_ci.py +57 -0
- abeval-0.1.0/tests/test_compare.py +62 -0
- abeval-0.1.0/tests/test_data_cli.py +80 -0
- abeval-0.1.0/tests/test_dist.py +31 -0
- abeval-0.1.0/tests/test_power.py +50 -0
- abeval-0.1.0/tests/test_reliability.py +53 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
github: mohammadi-hadi
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.14"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- name: Install
|
|
20
|
+
run: pip install -e ".[dev]"
|
|
21
|
+
- name: Lint
|
|
22
|
+
run: ruff check .
|
|
23
|
+
- name: Test
|
|
24
|
+
run: pytest -q
|
|
25
|
+
- name: CLI smoke test
|
|
26
|
+
run: |
|
|
27
|
+
abeval --version
|
|
28
|
+
abeval compare examples/run_a.jsonl examples/run_b.jsonl
|
|
29
|
+
abeval power --baseline 0.75 --delta 0.03 --json
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If abeval is useful in your work, please cite this repository."
|
|
3
|
+
title: "abeval: A/B-test statistics for LLM evals"
|
|
4
|
+
type: software
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Mohammadi
|
|
7
|
+
given-names: Hadi
|
|
8
|
+
version: 0.1.0
|
|
9
|
+
date-released: 2026-08-05
|
|
10
|
+
license: MIT
|
|
11
|
+
repository-code: "https://github.com/mohammadi-hadi/abeval"
|
|
12
|
+
url: "https://mohammadi.cv/abeval/"
|
|
13
|
+
keywords:
|
|
14
|
+
- llm
|
|
15
|
+
- evaluation
|
|
16
|
+
- statistics
|
|
17
|
+
- ab-testing
|
|
18
|
+
- significance
|
abeval-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hadi Mohammadi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
abeval-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.PHONY: install lint test demo serve build
|
|
2
|
+
|
|
3
|
+
install:
|
|
4
|
+
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
5
|
+
|
|
6
|
+
lint:
|
|
7
|
+
.venv/bin/ruff check .
|
|
8
|
+
|
|
9
|
+
test:
|
|
10
|
+
.venv/bin/python -m pytest -q
|
|
11
|
+
|
|
12
|
+
demo:
|
|
13
|
+
.venv/bin/python -m abeval compare examples/run_a.jsonl examples/run_b.jsonl
|
|
14
|
+
.venv/bin/python -m abeval power --baseline 0.75 --delta 0.03
|
|
15
|
+
|
|
16
|
+
serve:
|
|
17
|
+
.venv/bin/python -m http.server 8765 -d docs
|
|
18
|
+
|
|
19
|
+
# Build sdist + wheel from a clean `git archive` export in a temp directory
|
|
20
|
+
# OUTSIDE the working tree (hatchling walks upward and would otherwise pick up
|
|
21
|
+
# local ignore files), so only committed files can reach a distribution.
|
|
22
|
+
# The artifacts are audited afterwards.
|
|
23
|
+
build:
|
|
24
|
+
rm -rf dist
|
|
25
|
+
@export_dir=$$(mktemp -d) && \
|
|
26
|
+
git archive HEAD | tar -x -C "$$export_dir" && \
|
|
27
|
+
.venv/bin/python -m build --outdir dist "$$export_dir" && \
|
|
28
|
+
rm -rf "$$export_dir"
|
|
29
|
+
@if tar -tzf dist/*.tar.gz | grep -qiE 'claude|gitignore'; then \
|
|
30
|
+
echo "TAINTED SDIST"; exit 1; fi
|
|
31
|
+
@if unzip -l dist/*.whl | grep -qiE 'claude|gitignore'; then \
|
|
32
|
+
echo "TAINTED WHEEL"; exit 1; fi
|
|
33
|
+
@echo "artifacts clean:" && ls dist
|
abeval-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: abeval
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A/B-test statistics for LLM evals: error bars, paired comparisons, and sample-size planning
|
|
5
|
+
Project-URL: Homepage, https://github.com/mohammadi-hadi/abeval
|
|
6
|
+
Project-URL: Calculator, https://mohammadi.cv/abeval/
|
|
7
|
+
Author: Hadi Mohammadi
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: ab-testing,evaluation,llm,sample-size,significance,statistics
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: build; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# abeval
|
|
25
|
+
|
|
26
|
+
A/B-test statistics for LLM evals.
|
|
27
|
+
|
|
28
|
+
Eval scores are sample estimates, but they're routinely reported as exact
|
|
29
|
+
numbers: "our prompt scores 76.5%, the old one 63.5%, ship it." On 200 items
|
|
30
|
+
much smaller gaps than that are pure noise. abeval treats an eval like an A/B
|
|
31
|
+
test — confidence intervals for a single run, paired significance tests for
|
|
32
|
+
comparing two runs on the same items, and sample-size planning *before* you
|
|
33
|
+
spend on inference. Standard library only, no dependencies.
|
|
34
|
+
|
|
35
|
+
**Sample-size calculator:** https://mohammadi.cv/abeval/
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
pip install abeval
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or from source: `git clone https://github.com/mohammadi-hadi/abeval && cd abeval && make install`.
|
|
44
|
+
|
|
45
|
+
## Is my new prompt actually better?
|
|
46
|
+
|
|
47
|
+
Point `abeval compare` at two JSONL result files that share item ids:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
$ abeval compare examples/run_a.jsonl examples/run_b.jsonl
|
|
51
|
+
A: 63.5% B: 76.5% (n=200 paired items)
|
|
52
|
+
B - A: +13.0% [4.5%, 21.5%] (95% CI)
|
|
53
|
+
p (sign-flip permutation): 0.0041 p (paired t): 0.0034
|
|
54
|
+
discordant items: B-only wins 53, A-only wins 27 p (exact McNemar): 0.0049
|
|
55
|
+
verdict: significant at the chosen level
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The comparison is **paired**: both runs are scored on the same items, so
|
|
59
|
+
item difficulty cancels out and you detect much smaller differences than two
|
|
60
|
+
independent runs would allow. Items present in only one run are dropped (and
|
|
61
|
+
reported).
|
|
62
|
+
|
|
63
|
+
Every command takes `--json` for machine-readable output, and `--seed` makes
|
|
64
|
+
the resampling reproducible.
|
|
65
|
+
|
|
66
|
+
## Error bars for one run
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
$ abeval ci examples/run_a.jsonl
|
|
70
|
+
score: 63.5% [56.6%, 69.9%] (n=200, 95% CI, wilson)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Binary metrics get a Wilson score interval, continuous metrics a t interval
|
|
74
|
+
(or `--bootstrap`). If your items aren't independent — 5 questions generated
|
|
75
|
+
from each source document, say — pass `--cluster-key document` and the
|
|
76
|
+
interval widens to account for within-cluster correlation.
|
|
77
|
+
|
|
78
|
+
## How many items do I need?
|
|
79
|
+
|
|
80
|
+
Run this *before* the eval, not after:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
$ abeval power --baseline 0.75 --delta 0.03
|
|
84
|
+
to detect 3.0% difference from a 75.0% baseline (corr=0.5):
|
|
85
|
+
n = 1568 paired items (80% power, 95% confidence)
|
|
86
|
+
with 100 items you can detect >= 11.9%
|
|
87
|
+
with 200 items you can detect >= 8.4%
|
|
88
|
+
with 500 items you can detect >= 5.3%
|
|
89
|
+
with 1000 items you can detect >= 3.8%
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The lesson generalizes: a 200-item eval cannot see a 3-point improvement.
|
|
93
|
+
`--corr` is the item-level correlation between the two runs' outcomes (0.5 is
|
|
94
|
+
a reasonable default for two variants of the same system; higher correlation
|
|
95
|
+
means fewer items needed). There's an interactive version at
|
|
96
|
+
https://mohammadi.cv/abeval/.
|
|
97
|
+
|
|
98
|
+
## How noisy is my judge?
|
|
99
|
+
|
|
100
|
+
If an LLM judge scores the same item differently on repeat calls, that noise
|
|
101
|
+
eats your statistical power. Feed repeated judgments to `reliability`:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
$ abeval reliability examples/judge_repeats.jsonl
|
|
105
|
+
40 items, 120 judgments (3.0 per item)
|
|
106
|
+
ICC (signal share of variance): 0.764
|
|
107
|
+
between-item sd: 1.708 judge noise sd: 0.9487
|
|
108
|
+
exact agreement across repeats: 15.0%
|
|
109
|
+
repeats per item to push judge noise under 10%: 3
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
ICC is the share of score variance that is real item signal rather than judge
|
|
113
|
+
noise, from a one-way random-effects decomposition. The last line answers the
|
|
114
|
+
practical question: how many repeat judgments to average per item.
|
|
115
|
+
|
|
116
|
+
## Data format
|
|
117
|
+
|
|
118
|
+
JSONL, one item per line, any extra fields ignored:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{"id": "item-001", "score": 1}
|
|
122
|
+
{"id": "item-002", "score": 0, "category": "reasoning"}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`--metric` and `--id-key` rename the fields; booleans are accepted as 0/1.
|
|
126
|
+
The files in `examples/` are generated by `examples/make_fixtures.py`
|
|
127
|
+
(seeded, so they're reproducible).
|
|
128
|
+
|
|
129
|
+
## Python API
|
|
130
|
+
|
|
131
|
+
Everything the CLI does is a plain function:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from abeval import paired_compare, proportion_ci, sample_size, sd_diff_from_rates
|
|
135
|
+
|
|
136
|
+
proportion_ci(127, 200) # Wilson interval, level=0.95
|
|
137
|
+
result = paired_compare(scores_a, scores_b, seed=0)
|
|
138
|
+
result.diff, result.ci_lo, result.ci_hi, result.p_permutation
|
|
139
|
+
|
|
140
|
+
sd = sd_diff_from_rates(0.75, 0.78, corr=0.5)
|
|
141
|
+
sample_size(0.03, sd) # -> 1568 paired items
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## What's inside
|
|
145
|
+
|
|
146
|
+
| Question | Method |
|
|
147
|
+
|---|---|
|
|
148
|
+
| CI for a pass rate | Wilson score interval |
|
|
149
|
+
| CI for a mean score | Student t, percentile bootstrap, or cluster-robust t |
|
|
150
|
+
| Is B better than A? | Sign-flip permutation test on paired differences (primary), paired t, exact McNemar on discordant pairs (binary) |
|
|
151
|
+
| How many items? | Normal-approximation power for the paired design, with a Monte-Carlo check (`power_simulated`) |
|
|
152
|
+
| Judge noise | One-way random-effects ICC with unbalanced-design correction |
|
|
153
|
+
|
|
154
|
+
The permutation p-value is the primary test: it's exact under item
|
|
155
|
+
exchangeability and makes no normality assumption. The paired t and McNemar
|
|
156
|
+
values are printed alongside because reviewers ask for them.
|
|
157
|
+
|
|
158
|
+
The approach follows Miller,
|
|
159
|
+
["Adding Error Bars to Evals"](https://arxiv.org/abs/2411.00640) (2024) —
|
|
160
|
+
report standard errors, use paired designs, plan sample sizes — packaged as a
|
|
161
|
+
tool you can point at result files.
|
|
162
|
+
|
|
163
|
+
## Honest limitations
|
|
164
|
+
|
|
165
|
+
- The power formulas use the normal approximation; for very small samples or
|
|
166
|
+
rates near 0/1 trust `power_simulated` over the closed form.
|
|
167
|
+
- Cluster-robust intervals are approximate with fewer than ~20 clusters.
|
|
168
|
+
- Multiple-comparison correction is on you: if you compare ten prompts and
|
|
169
|
+
ship the best p-value, it's inflated (a `sweep` command with Bonferroni
|
|
170
|
+
correction is on the roadmap).
|
|
171
|
+
- This is not a stats library. If you need regressions or GLMs, use
|
|
172
|
+
statsmodels; abeval covers the eval loop with zero dependencies.
|
|
173
|
+
|
|
174
|
+
## Sponsoring
|
|
175
|
+
|
|
176
|
+
abeval is MIT-licensed and dependency-free, and it stays that way. Sponsoring
|
|
177
|
+
funds the roadmap below and the maintenance time to keep the statistics
|
|
178
|
+
trustworthy. Sponsors are credited in release notes and vote on what lands
|
|
179
|
+
next: [GitHub Sponsors](https://github.com/sponsors/mohammadi-hadi).
|
|
180
|
+
|
|
181
|
+
## Roadmap
|
|
182
|
+
|
|
183
|
+
- `sweep`: compare N runs against a baseline with multiple-comparison
|
|
184
|
+
correction.
|
|
185
|
+
- Confidence intervals for pairwise win rates (Bradley-Terry) for
|
|
186
|
+
head-to-head judge outputs.
|
|
187
|
+
- Variance-reduction guidance: how much power you buy by averaging k judge
|
|
188
|
+
repeats, given your measured ICC.
|
|
189
|
+
|
|
190
|
+
## Related projects
|
|
191
|
+
|
|
192
|
+
- [judgewatch](https://github.com/mohammadi-hadi/judgewatch) — monthly bias
|
|
193
|
+
audits of LLM judges; abeval tells you whether this month's change is real.
|
|
194
|
+
- [judgekit](https://github.com/mohammadi-hadi/judgekit) — audit your own
|
|
195
|
+
judge pipelines.
|
|
196
|
+
- [trajectory-judge](https://github.com/mohammadi-hadi/trajectory-judge) —
|
|
197
|
+
evaluating agent trajectories with LLM judges.
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT — see [LICENSE](LICENSE).
|
abeval-0.1.0/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# abeval
|
|
2
|
+
|
|
3
|
+
A/B-test statistics for LLM evals.
|
|
4
|
+
|
|
5
|
+
Eval scores are sample estimates, but they're routinely reported as exact
|
|
6
|
+
numbers: "our prompt scores 76.5%, the old one 63.5%, ship it." On 200 items
|
|
7
|
+
much smaller gaps than that are pure noise. abeval treats an eval like an A/B
|
|
8
|
+
test — confidence intervals for a single run, paired significance tests for
|
|
9
|
+
comparing two runs on the same items, and sample-size planning *before* you
|
|
10
|
+
spend on inference. Standard library only, no dependencies.
|
|
11
|
+
|
|
12
|
+
**Sample-size calculator:** https://mohammadi.cv/abeval/
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
pip install abeval
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or from source: `git clone https://github.com/mohammadi-hadi/abeval && cd abeval && make install`.
|
|
21
|
+
|
|
22
|
+
## Is my new prompt actually better?
|
|
23
|
+
|
|
24
|
+
Point `abeval compare` at two JSONL result files that share item ids:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
$ abeval compare examples/run_a.jsonl examples/run_b.jsonl
|
|
28
|
+
A: 63.5% B: 76.5% (n=200 paired items)
|
|
29
|
+
B - A: +13.0% [4.5%, 21.5%] (95% CI)
|
|
30
|
+
p (sign-flip permutation): 0.0041 p (paired t): 0.0034
|
|
31
|
+
discordant items: B-only wins 53, A-only wins 27 p (exact McNemar): 0.0049
|
|
32
|
+
verdict: significant at the chosen level
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The comparison is **paired**: both runs are scored on the same items, so
|
|
36
|
+
item difficulty cancels out and you detect much smaller differences than two
|
|
37
|
+
independent runs would allow. Items present in only one run are dropped (and
|
|
38
|
+
reported).
|
|
39
|
+
|
|
40
|
+
Every command takes `--json` for machine-readable output, and `--seed` makes
|
|
41
|
+
the resampling reproducible.
|
|
42
|
+
|
|
43
|
+
## Error bars for one run
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
$ abeval ci examples/run_a.jsonl
|
|
47
|
+
score: 63.5% [56.6%, 69.9%] (n=200, 95% CI, wilson)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Binary metrics get a Wilson score interval, continuous metrics a t interval
|
|
51
|
+
(or `--bootstrap`). If your items aren't independent — 5 questions generated
|
|
52
|
+
from each source document, say — pass `--cluster-key document` and the
|
|
53
|
+
interval widens to account for within-cluster correlation.
|
|
54
|
+
|
|
55
|
+
## How many items do I need?
|
|
56
|
+
|
|
57
|
+
Run this *before* the eval, not after:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
$ abeval power --baseline 0.75 --delta 0.03
|
|
61
|
+
to detect 3.0% difference from a 75.0% baseline (corr=0.5):
|
|
62
|
+
n = 1568 paired items (80% power, 95% confidence)
|
|
63
|
+
with 100 items you can detect >= 11.9%
|
|
64
|
+
with 200 items you can detect >= 8.4%
|
|
65
|
+
with 500 items you can detect >= 5.3%
|
|
66
|
+
with 1000 items you can detect >= 3.8%
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The lesson generalizes: a 200-item eval cannot see a 3-point improvement.
|
|
70
|
+
`--corr` is the item-level correlation between the two runs' outcomes (0.5 is
|
|
71
|
+
a reasonable default for two variants of the same system; higher correlation
|
|
72
|
+
means fewer items needed). There's an interactive version at
|
|
73
|
+
https://mohammadi.cv/abeval/.
|
|
74
|
+
|
|
75
|
+
## How noisy is my judge?
|
|
76
|
+
|
|
77
|
+
If an LLM judge scores the same item differently on repeat calls, that noise
|
|
78
|
+
eats your statistical power. Feed repeated judgments to `reliability`:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
$ abeval reliability examples/judge_repeats.jsonl
|
|
82
|
+
40 items, 120 judgments (3.0 per item)
|
|
83
|
+
ICC (signal share of variance): 0.764
|
|
84
|
+
between-item sd: 1.708 judge noise sd: 0.9487
|
|
85
|
+
exact agreement across repeats: 15.0%
|
|
86
|
+
repeats per item to push judge noise under 10%: 3
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
ICC is the share of score variance that is real item signal rather than judge
|
|
90
|
+
noise, from a one-way random-effects decomposition. The last line answers the
|
|
91
|
+
practical question: how many repeat judgments to average per item.
|
|
92
|
+
|
|
93
|
+
## Data format
|
|
94
|
+
|
|
95
|
+
JSONL, one item per line, any extra fields ignored:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{"id": "item-001", "score": 1}
|
|
99
|
+
{"id": "item-002", "score": 0, "category": "reasoning"}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`--metric` and `--id-key` rename the fields; booleans are accepted as 0/1.
|
|
103
|
+
The files in `examples/` are generated by `examples/make_fixtures.py`
|
|
104
|
+
(seeded, so they're reproducible).
|
|
105
|
+
|
|
106
|
+
## Python API
|
|
107
|
+
|
|
108
|
+
Everything the CLI does is a plain function:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from abeval import paired_compare, proportion_ci, sample_size, sd_diff_from_rates
|
|
112
|
+
|
|
113
|
+
proportion_ci(127, 200) # Wilson interval, level=0.95
|
|
114
|
+
result = paired_compare(scores_a, scores_b, seed=0)
|
|
115
|
+
result.diff, result.ci_lo, result.ci_hi, result.p_permutation
|
|
116
|
+
|
|
117
|
+
sd = sd_diff_from_rates(0.75, 0.78, corr=0.5)
|
|
118
|
+
sample_size(0.03, sd) # -> 1568 paired items
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## What's inside
|
|
122
|
+
|
|
123
|
+
| Question | Method |
|
|
124
|
+
|---|---|
|
|
125
|
+
| CI for a pass rate | Wilson score interval |
|
|
126
|
+
| CI for a mean score | Student t, percentile bootstrap, or cluster-robust t |
|
|
127
|
+
| Is B better than A? | Sign-flip permutation test on paired differences (primary), paired t, exact McNemar on discordant pairs (binary) |
|
|
128
|
+
| How many items? | Normal-approximation power for the paired design, with a Monte-Carlo check (`power_simulated`) |
|
|
129
|
+
| Judge noise | One-way random-effects ICC with unbalanced-design correction |
|
|
130
|
+
|
|
131
|
+
The permutation p-value is the primary test: it's exact under item
|
|
132
|
+
exchangeability and makes no normality assumption. The paired t and McNemar
|
|
133
|
+
values are printed alongside because reviewers ask for them.
|
|
134
|
+
|
|
135
|
+
The approach follows Miller,
|
|
136
|
+
["Adding Error Bars to Evals"](https://arxiv.org/abs/2411.00640) (2024) —
|
|
137
|
+
report standard errors, use paired designs, plan sample sizes — packaged as a
|
|
138
|
+
tool you can point at result files.
|
|
139
|
+
|
|
140
|
+
## Honest limitations
|
|
141
|
+
|
|
142
|
+
- The power formulas use the normal approximation; for very small samples or
|
|
143
|
+
rates near 0/1 trust `power_simulated` over the closed form.
|
|
144
|
+
- Cluster-robust intervals are approximate with fewer than ~20 clusters.
|
|
145
|
+
- Multiple-comparison correction is on you: if you compare ten prompts and
|
|
146
|
+
ship the best p-value, it's inflated (a `sweep` command with Bonferroni
|
|
147
|
+
correction is on the roadmap).
|
|
148
|
+
- This is not a stats library. If you need regressions or GLMs, use
|
|
149
|
+
statsmodels; abeval covers the eval loop with zero dependencies.
|
|
150
|
+
|
|
151
|
+
## Sponsoring
|
|
152
|
+
|
|
153
|
+
abeval is MIT-licensed and dependency-free, and it stays that way. Sponsoring
|
|
154
|
+
funds the roadmap below and the maintenance time to keep the statistics
|
|
155
|
+
trustworthy. Sponsors are credited in release notes and vote on what lands
|
|
156
|
+
next: [GitHub Sponsors](https://github.com/sponsors/mohammadi-hadi).
|
|
157
|
+
|
|
158
|
+
## Roadmap
|
|
159
|
+
|
|
160
|
+
- `sweep`: compare N runs against a baseline with multiple-comparison
|
|
161
|
+
correction.
|
|
162
|
+
- Confidence intervals for pairwise win rates (Bradley-Terry) for
|
|
163
|
+
head-to-head judge outputs.
|
|
164
|
+
- Variance-reduction guidance: how much power you buy by averaging k judge
|
|
165
|
+
repeats, given your measured ICC.
|
|
166
|
+
|
|
167
|
+
## Related projects
|
|
168
|
+
|
|
169
|
+
- [judgewatch](https://github.com/mohammadi-hadi/judgewatch) — monthly bias
|
|
170
|
+
audits of LLM judges; abeval tells you whether this month's change is real.
|
|
171
|
+
- [judgekit](https://github.com/mohammadi-hadi/judgekit) — audit your own
|
|
172
|
+
judge pipelines.
|
|
173
|
+
- [trajectory-judge](https://github.com/mohammadi-hadi/trajectory-judge) —
|
|
174
|
+
evaluating agent trajectories with LLM judges.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""abeval: A/B-test statistics for LLM evals.
|
|
2
|
+
|
|
3
|
+
Error bars for a single run, paired significance tests for comparing two
|
|
4
|
+
runs, sample-size planning before you spend on inference, and judge-noise
|
|
5
|
+
reliability. Standard library only.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .ci import Interval, clustered_mean_ci, mean_ci, proportion_ci
|
|
9
|
+
from .compare import Comparison, mcnemar_exact, paired_compare
|
|
10
|
+
from .data import extract, pair, read_jsonl
|
|
11
|
+
from .power import mde, power_simulated, sample_size, sd_diff_from_rates
|
|
12
|
+
from .reliability import Reliability, judge_reliability
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"Comparison",
|
|
18
|
+
"Interval",
|
|
19
|
+
"Reliability",
|
|
20
|
+
"__version__",
|
|
21
|
+
"clustered_mean_ci",
|
|
22
|
+
"extract",
|
|
23
|
+
"judge_reliability",
|
|
24
|
+
"mcnemar_exact",
|
|
25
|
+
"mde",
|
|
26
|
+
"mean_ci",
|
|
27
|
+
"pair",
|
|
28
|
+
"paired_compare",
|
|
29
|
+
"power_simulated",
|
|
30
|
+
"proportion_ci",
|
|
31
|
+
"read_jsonl",
|
|
32
|
+
"sample_size",
|
|
33
|
+
"sd_diff_from_rates",
|
|
34
|
+
]
|