@ssheleg/agent-stack 0.17.1 → 0.18.1
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.
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
+
## v0.18.1 — the board row a sibling's gate refused
|
|
2
|
+
|
|
3
|
+
v0.18.0's B-124 row landed in the **wrong table** — the board carries two, an
|
|
4
|
+
eight-column ledger and a three-column *"Open, and why"* — and it was appended to the
|
|
5
|
+
file's end, which is the second one. It also carried an unescaped `|` inside a grep
|
|
6
|
+
pattern, so even in the right table its columns would have shifted and `Status` would
|
|
7
|
+
have read as whatever landed in its place.
|
|
8
|
+
|
|
9
|
+
**This repository's own gate passed it.** `npm test` here was green on the broken row;
|
|
10
|
+
the refusal came from `sshlg-skills`' validator reading this submodule's board during a
|
|
11
|
+
re-pin — *"row B-124 has 12 cells against the 3 its own header declares"*. A member's
|
|
12
|
+
board is checked by the umbrella, not by the member, and that asymmetry is why a
|
|
13
|
+
documentation defect reached a tag.
|
|
14
|
+
|
|
15
|
+
The neighbouring rows already showed the convention: `AG-06` and `AG-06b` carry escaped
|
|
16
|
+
`\|` inside code spans and count correctly. The fix follows them.
|
|
17
|
+
|
|
18
|
+
## v0.18.0 — how many runs before a difference is real, and the trajectory rule between its two measured edges
|
|
19
|
+
|
|
20
|
+
`agent-evals` shipped 315 lines of doctrine about **what** to assert with **no
|
|
21
|
+
`references/` directory at all**, and said nothing anywhere about how many times to run
|
|
22
|
+
anything. A skill whose whole job is answering *did it get better* had no way to say
|
|
23
|
+
whether a number was a result or noise.
|
|
24
|
+
|
|
25
|
+
**`references/statistics.md`** is that layer, and every figure in it was recomputed rather
|
|
26
|
+
than quoted:
|
|
27
|
+
|
|
28
|
+
- `SE(p) = √(p(1−p)/n)` — at n=100, p=0.70 the 95% band is **±8.98 pp**, so a 73%-vs-70%
|
|
29
|
+
comparison on a hundred cases is a number inside its own noise. Error falls as `1/√n`,
|
|
30
|
+
which makes the remedy *more tasks*, not more argument.
|
|
31
|
+
- **`pass@k` and `pass^k` differ by 91 points on the same agent** — at p=0.6, k=5 they are
|
|
32
|
+
99.0% and 7.8%. The first is a capability ceiling a human picks from; the second is what
|
|
33
|
+
a payment or a permission change needs. An operation with side effects may not "retry
|
|
34
|
+
until it works", so `pass@k` is not available to it as a metric at all.
|
|
35
|
+
- **Trials are not independent, and the published data proves it.** τ-bench's airline
|
|
36
|
+
Pass^k for claude-3-5-sonnet runs 0.460 → 0.326 → 0.263 → 0.225, where independence from
|
|
37
|
+
Pass^1 would predict 0.460 → 0.212 → 0.097 → 0.045. Successes cluster by task, not by
|
|
38
|
+
trial. So `pass^k` cannot be computed from `pass^1`, and Anthropic's `0.75³ ≈ 42%` is the
|
|
39
|
+
right shape for an argument and the wrong number for a gate. **This one is ours** — it
|
|
40
|
+
came out of recomputing the table rather than restating it.
|
|
41
|
+
- Pairing on the same tasks and the same 3–5 seeds with McNemar or a paired bootstrap,
|
|
42
|
+
because the task-difficulty variance the previous point measures is exactly what pairing
|
|
43
|
+
removes.
|
|
44
|
+
- **The harness is a variable**: 6 pp between the most- and least-resourced setups on
|
|
45
|
+
Terminal-Bench 2.0 (p<0.01), moving within noise from 1× to 3× (p=0.40) and lifting ~4 pp
|
|
46
|
+
from 3× to uncapped — because generous headroom lets the agent attempt strategies a tight
|
|
47
|
+
cap forbids. Two caps measure two agents. The remedy is a floor *and* a ceiling.
|
|
48
|
+
- A ladder for what a given piece of evidence authorises next, ending on the rule people
|
|
49
|
+
skip: **4/4 on a slice is not 100% system-wide.**
|
|
50
|
+
|
|
51
|
+
**§5's trajectory rule moved between two measurements rather than being deleted.** It read
|
|
52
|
+
*"Judge the trajectory, not just the answer. Right tools, right order, right arguments."*
|
|
53
|
+
Anthropic calls exact tool-order assertions *"too rigid … agents regularly find valid
|
|
54
|
+
approaches that eval designers didn't anticipate"*, with a worked case of an agent that
|
|
55
|
+
solved a τ²-bench booking task through a policy loophole and failed the eval while serving
|
|
56
|
+
the user better. But the opposite edge is measured too: a grader blind to the trajectory
|
|
57
|
+
misses **44% of safety violations and 13% of robustness failures**. So the rule now reads
|
|
58
|
+
*read the trajectory; do not match it* — assert what was produced and what changed, and use
|
|
59
|
+
the trajectory as a **set and a forbidden list** for the claims an outcome cannot carry.
|
|
60
|
+
§2's axis table lost its `→` sequence example for the same reason.
|
|
61
|
+
|
|
62
|
+
The single-step example keeps its *"must call `find_meeting_times` first"*, with a sentence
|
|
63
|
+
saying why: at that granularity the fixture **is** one decision, so ordering is the subject
|
|
64
|
+
rather than a proxy for it. Across a trajectory it stops being one.
|
|
65
|
+
|
|
1
66
|
# Changelog
|
|
2
67
|
|
|
3
68
|
## v0.17.1 — the evals run for the first time, and the tails go to zero
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "agent-stack",
|
|
4
4
|
"displayName": "Agent Stack",
|
|
5
5
|
"description": "Four skills: agent-orchestrator — tool-calling loops, pipelines with checkpoints, provider routing with fallback, memory architecture, plus the wallet side of reselling LLM access; agent-evals — run/trace/thread evals, LLM judges, and fixtures grown from production; agent-interop — MCP servers and clients, A2A agent cards, the MCP Registry, and gateways; agent-harness — system prompts, tool shaping, workflow-vs-agent, and auditing an agent system.",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.18.1",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "ssheleg",
|
|
9
9
|
"url": "https://x.com/sshlg93"
|
|
@@ -63,6 +63,10 @@ point — tool name, argument shape.
|
|
|
63
63
|
> "Schedule a meeting with Harrison tomorrow morning", with `find_meeting_times`,
|
|
64
64
|
> `schedule_meeting` and `send_email` available, must call `find_meeting_times` first.
|
|
65
65
|
|
|
66
|
+
This is the one granularity where *first* is a legitimate assertion: the fixture is a
|
|
67
|
+
single decision, so the ordering claim is the subject rather than a proxy for it. Across a
|
|
68
|
+
whole trajectory it stops being one — see §5.
|
|
69
|
+
|
|
66
70
|
Cheap, deterministic, CI-blocking. **Precondition: a stable agent architecture.** These
|
|
67
71
|
break on a graph refactor, and a suite that fails on every refactor gets deleted.
|
|
68
72
|
|
|
@@ -72,7 +76,7 @@ Assert on three axes at once, with three different mechanisms:
|
|
|
72
76
|
|
|
73
77
|
| Axis | Assert | With |
|
|
74
78
|
|---|---|---|
|
|
75
|
-
| Trajectory |
|
|
79
|
+
| Trajectory | what the run **must not** do, and what it must have touched — never the order | set/subset matchers, forbidden-call lists |
|
|
76
80
|
| Final response | quality, tone, policy compliance | rubric or judge |
|
|
77
81
|
| **State change** | the memory row exists, the file was written, the artifact is there | direct inspection of the side effect |
|
|
78
82
|
|
|
@@ -147,8 +151,20 @@ That is what makes a judge reproducible and a disagreement resolvable.
|
|
|
147
151
|
assertions, tool-call correctness — all deterministic, all faster and cheaper than a model
|
|
148
152
|
call. Send to a judge only what cannot be decided by code.
|
|
149
153
|
|
|
150
|
-
**
|
|
151
|
-
|
|
154
|
+
**Read the trajectory; do not match it.** An agent that reaches a correct answer through
|
|
155
|
+
three wrong tool calls is a latent outage — and asserting the *sequence* to catch that is
|
|
156
|
+
measurably the wrong instrument. Anthropic names the instinct and rejects its strict form:
|
|
157
|
+
exact tool-order assertions are *"too rigid and results in overly brittle tests, as agents
|
|
158
|
+
regularly find valid approaches that eval designers didn't anticipate"*, and the worked
|
|
159
|
+
case is an agent that solved a τ²-bench booking task through a policy loophole, failing
|
|
160
|
+
the eval as written while serving the user better. Grade **what was produced and what
|
|
161
|
+
changed**, and let the path vary.
|
|
162
|
+
|
|
163
|
+
The opposite edge is measured too, so this is not "grade the final answer": a grader blind
|
|
164
|
+
to the trajectory misses **44% of safety violations and 13% of robustness failures**,
|
|
165
|
+
because a policy breach on the way to a correct result leaves no trace in the outcome. Use
|
|
166
|
+
the trajectory for the claims the outcome cannot carry — a forbidden call, a missing
|
|
167
|
+
confirmation, a secret read — as a **set and a forbidden list**, never as an order.
|
|
152
168
|
|
|
153
169
|
**Calibrate the judge before trusting it.** Collect human labels on the same traces,
|
|
154
170
|
measure agreement, iterate the judge prompt until agreement is high — *then* let it score
|
|
@@ -304,6 +320,16 @@ None of the above runs without these, and they are the part people skip:
|
|
|
304
320
|
- [ ] Every production failure minimised into a permanent fixture
|
|
305
321
|
- [ ] Annotation queue with filters, and the two reviewer roles kept separate
|
|
306
322
|
- [ ] Simulated users trained on real transcripts, with adversarial personas
|
|
323
|
+
- [ ] Every comparison that changes a decision states `n`, `k`, the reducer and the noise
|
|
324
|
+
band — `references/statistics.md`; a three-point gap on a hundred cases is noise
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## References
|
|
329
|
+
|
|
330
|
+
| Load | When |
|
|
331
|
+
|---|---|
|
|
332
|
+
| [`references/statistics.md`](references/statistics.md) | a number is about to change a decision — how many runs before a difference is real, `pass@k` vs `pass^k` and why trials are not independent, paired comparison, the harness as a variable, and what a given piece of evidence authorises next |
|
|
307
333
|
|
|
308
334
|
---
|
|
309
335
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Statistics for agent evals — how many runs before a difference is real
|
|
2
|
+
|
|
3
|
+
**Load this when a number is about to change a decision:** picking between two models or
|
|
4
|
+
two harness configurations, setting a release threshold, deciding whether a regression is
|
|
5
|
+
real, or writing the sentence "X is better than Y" in a document somebody will act on.
|
|
6
|
+
|
|
7
|
+
Everything here is arithmetic over binary outcomes. It needs no library and no
|
|
8
|
+
statistician, and it is the layer most eval advice skips — including this skill's own
|
|
9
|
+
first eight sections, which say what to assert and never say how many times.
|
|
10
|
+
|
|
11
|
+
## Contents
|
|
12
|
+
|
|
13
|
+
- [The band around a pass rate](#the-band-around-a-pass-rate)
|
|
14
|
+
- [pass@k and pass^k are different questions](#passk-and-passk-are-different-questions)
|
|
15
|
+
- [Trials are not independent, and the published data says so](#trials-are-not-independent-and-the-published-data-says-so)
|
|
16
|
+
- [Pairing: same tasks, same seeds, per-task deltas](#pairing-same-tasks-same-seeds-per-task-deltas)
|
|
17
|
+
- [The harness is a variable, so pin it](#the-harness-is-a-variable-so-pin-it)
|
|
18
|
+
- [What a number authorises](#what-a-number-authorises)
|
|
19
|
+
- [The report contract](#the-report-contract)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## The band around a pass rate
|
|
24
|
+
|
|
25
|
+
A pass rate over `n` cases is an estimate, and its standard error is
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
SE(p) = sqrt( p * (1 - p) / n )
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The 95% band is roughly `±1.96 · SE`. Computed, not quoted:
|
|
32
|
+
|
|
33
|
+
| n | p | 95% band |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| 100 | 0.70 | **±8.98 pp** |
|
|
36
|
+
| 400 | 0.70 | ±4.49 pp |
|
|
37
|
+
| 1000 | 0.70 | ±2.84 pp |
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import math
|
|
41
|
+
def band(p, n): return 1.96 * math.sqrt(p * (1 - p) / n) * 100 # percentage points
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**So "the new one gets 73% where the old one got 70%, on a hundred cases" is not a
|
|
45
|
+
result.** It is a number inside its own noise. The error shrinks as `1/√n`, which is the
|
|
46
|
+
whole practical consequence: **the fix for a 2–3 pp expected gain is more tasks, not more
|
|
47
|
+
argument.** Quadrupling the set halves the band.
|
|
48
|
+
|
|
49
|
+
A corollary worth stating because leaderboards invite the opposite: **differences below
|
|
50
|
+
about 3 pp deserve scepticism until both configurations are documented and matched.**
|
|
51
|
+
|
|
52
|
+
> The formula assumes independent cases. A benchmark whose tasks share a fixture, an
|
|
53
|
+
> environment or a generator violates that, and the true band is wider than this. Wider,
|
|
54
|
+
> never narrower — so the table is a floor on your uncertainty, not a ceiling.
|
|
55
|
+
|
|
56
|
+
## pass@k and pass^k are different questions
|
|
57
|
+
|
|
58
|
+
Two metrics, one letter apart, measuring opposite things.
|
|
59
|
+
|
|
60
|
+
| Metric | Formula | Asks | Who it is for |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| `pass@k` | `1 − (1 − p)^k` | did **at least one** of k attempts succeed | a **capability ceiling** — a human picks the best of k |
|
|
63
|
+
| `pass^k` | `p^k` (if independent) | did **every** one of k succeed | a **reliability floor** — nobody is picking |
|
|
64
|
+
|
|
65
|
+
At `p = 0.6`, `k = 5`:
|
|
66
|
+
|
|
67
|
+
- `pass@5 = 1 − 0.4⁵ = 0.98976` → **99.0%**
|
|
68
|
+
- `pass^5 = 0.6⁵ = 0.07776` → **7.8%**
|
|
69
|
+
|
|
70
|
+
**A 91-point gap between two numbers describing the same agent.** The first makes a demo;
|
|
71
|
+
the second is what a payment, a refund or a permission change actually needs.
|
|
72
|
+
|
|
73
|
+
Anthropic states the same arithmetic for the everyday case: at a 75% per-trial rate, three
|
|
74
|
+
trials all passing is `0.75³ ≈ 42%` — verified, `0.421875`.
|
|
75
|
+
|
|
76
|
+
**The rule that follows: an operation with side effects may not "retry until it works."**
|
|
77
|
+
If a failed attempt leaves a charge, a message or a mutated row behind, `pass@k` is not
|
|
78
|
+
available to you as a metric — you cannot pick the best of five refunds. Sample in a
|
|
79
|
+
sandbox or a rollback-capable environment, and count **every** failure.
|
|
80
|
+
|
|
81
|
+
**A report that gives k without saying which k it means is unreadable.** *k independent
|
|
82
|
+
samples of one task* and *k consecutive tasks on one live pipeline* are different claims.
|
|
83
|
+
|
|
84
|
+
## Trials are not independent, and the published data says so
|
|
85
|
+
|
|
86
|
+
`pass^k = p^k` assumes each trial is a fresh coin flip. Real benchmarks do not behave that
|
|
87
|
+
way, and the τ-bench leaderboard is the cleanest demonstration — claude-3-5-sonnet on the
|
|
88
|
+
airline domain, published Pass^k beside what independence would predict from Pass^1:
|
|
89
|
+
|
|
90
|
+
| k | published Pass^k | `0.460^k` if independent |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| 1 | 0.460 | 0.460 |
|
|
93
|
+
| 2 | **0.326** | 0.212 |
|
|
94
|
+
| 3 | **0.263** | 0.097 |
|
|
95
|
+
| 4 | **0.225** | 0.045 |
|
|
96
|
+
|
|
97
|
+
The observed curve falls far slower than independence predicts. The reason is not
|
|
98
|
+
mysterious: **some tasks are reliably easy and some reliably hard**, so successes cluster
|
|
99
|
+
by task rather than scattering by trial. Positive correlation across trials of the same
|
|
100
|
+
task.
|
|
101
|
+
|
|
102
|
+
Two consequences, and they cut in opposite directions:
|
|
103
|
+
|
|
104
|
+
- **You cannot compute `pass^k` from `pass^1`.** Exponentiating a headline rate gives a
|
|
105
|
+
number far below the truth. Measure `pass^k` directly, at the k you care about.
|
|
106
|
+
- **Anthropic's `0.75³ ≈ 42%` is a worst case, not a forecast.** It is the right shape for
|
|
107
|
+
an argument — *consistency is a much harder bar* — and the wrong number to put in a
|
|
108
|
+
release gate.
|
|
109
|
+
|
|
110
|
+
The other half of independence is the harness, not the task: Anthropic requires each trial
|
|
111
|
+
start from a clean environment, because *"unnecessary shared state between runs (leftover
|
|
112
|
+
files, cached data, resource exhaustion) can cause correlated failures."* Correlated
|
|
113
|
+
failures break the arithmetic above — so isolation is not hygiene, it is what makes the
|
|
114
|
+
metric mean anything.
|
|
115
|
+
|
|
116
|
+
## Pairing: same tasks, same seeds, per-task deltas
|
|
117
|
+
|
|
118
|
+
**Never subtract two independent averages.** Run both configurations over the *same* task
|
|
119
|
+
list with the *same* fixed seeds, record a per-task win/loss/tie, and test the deltas.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
for task in tasks: # identical list
|
|
123
|
+
for seed in seeds: # identical seeds, 3-5 of them
|
|
124
|
+
a = run(config_A, task, seed)
|
|
125
|
+
b = run(config_B, task, seed)
|
|
126
|
+
delta[task, seed] = a - b
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
- **3–5 seeds per configuration**, reporting mean and spread. A single run screens a
|
|
130
|
+
direction; it does not establish one.
|
|
131
|
+
- Test the paired deltas with **McNemar** (binary outcomes) or a **paired bootstrap**.
|
|
132
|
+
Pairing removes the task-difficulty variance that dominates the unpaired comparison —
|
|
133
|
+
which is exactly the correlation the previous section measured.
|
|
134
|
+
- Testing several hypotheses at once needs a **multiple-comparisons correction**, or an
|
|
135
|
+
independent re-run of whichever ones came out positive. Five hypotheses at p<0.05 gives
|
|
136
|
+
you roughly a one-in-four chance of a false positive somewhere.
|
|
137
|
+
|
|
138
|
+
**Ship on three conditions, not one:** the difference exceeds the noise band, it survives
|
|
139
|
+
the paired analysis, and it reproduces on a rerun.
|
|
140
|
+
|
|
141
|
+
## The harness is a variable, so pin it
|
|
142
|
+
|
|
143
|
+
The container spec is part of the measurement. On Terminal-Bench 2.0 the gap between the
|
|
144
|
+
most- and least-resourced setups was **6 percentage points (p < 0.01)** — larger than most
|
|
145
|
+
model differences anyone argues about.
|
|
146
|
+
|
|
147
|
+
The shape of the effect matters more than the number:
|
|
148
|
+
|
|
149
|
+
- Between **1× and 3×** the task's specified resources, scores move **within noise
|
|
150
|
+
(p = 0.40)**.
|
|
151
|
+
- From 3× to uncapped, infrastructure errors drop a further **1.6 pp** and success jumps
|
|
152
|
+
nearly **4 pp** — because the extra headroom lets the agent attempt strategies that only
|
|
153
|
+
work with it: pulling large dependencies, spawning expensive subprocesses, running
|
|
154
|
+
memory-hungry suites.
|
|
155
|
+
- Infrastructure errors alone: **5.8% of tasks at 1×**, cut to **2.1% at a 3× ceiling
|
|
156
|
+
(p < 0.001)**.
|
|
157
|
+
|
|
158
|
+
So a tight cap and a generous cap **measure different agent strategies**, not the same
|
|
159
|
+
agent more or less precisely. The remedy is a **floor and a ceiling**, calibrated so that
|
|
160
|
+
scores at both fall within noise of each other — not a single pinned value.
|
|
161
|
+
|
|
162
|
+
The effect is task-distribution dependent and does not transfer: SWE-bench moves only
|
|
163
|
+
**1.54 pp from 1× to 5×**. Measure it for your own suite rather than importing a multiplier.
|
|
164
|
+
|
|
165
|
+
## What a number authorises
|
|
166
|
+
|
|
167
|
+
Evidence licenses the next action its scope supports, and nothing further. A worked
|
|
168
|
+
three-round loop on a deliberately narrow slice — four tasks, one run each, model, seed,
|
|
169
|
+
step limit and environment fixed, arm order alternated, **one variable changed per round**:
|
|
170
|
+
|
|
171
|
+
| Round | The only change | Success | Tokens vs control | What it authorised |
|
|
172
|
+
|---|---|---|---|---|
|
|
173
|
+
| H1 | added navigation and final-check instructions | 25% → 25% | 0.47× | the prompt is not the bottleneck — stop tuning it |
|
|
174
|
+
| H5 | accessibility feed → UIAutomator tree | 25% → **100%** | **2.498×** | right mechanism, too expensive — try to cheapen it |
|
|
175
|
+
| H5C | prune invisible/textless/non-actionable nodes | 100% → 100% | **0.506×** | qualifies for a full rerun |
|
|
176
|
+
|
|
177
|
+
Two rules fall out, and the second is the one people skip:
|
|
178
|
+
|
|
179
|
+
- **Observation before prompt.** More detailed instructions cannot restore information the
|
|
180
|
+
agent never received. When a score will not move, ask what the agent could see before
|
|
181
|
+
asking how it was asked.
|
|
182
|
+
- **4/4 on a slice is not 100% system-wide, and must not be reported as one.** With four
|
|
183
|
+
tasks per arm these numbers can decide whether a larger rerun is worth paying for. They
|
|
184
|
+
cannot estimate success across the benchmark. The ladder's output is *the next
|
|
185
|
+
experiment*, not a result.
|
|
186
|
+
|
|
187
|
+
## The report contract
|
|
188
|
+
|
|
189
|
+
A comparison that will change a decision states all of these, or it is a claim rather than
|
|
190
|
+
a measurement:
|
|
191
|
+
|
|
192
|
+
- [ ] `n` — how many tasks, and `k` — how many runs each
|
|
193
|
+
- [ ] which reducer: `pass@k`, `pass^k`, `mean`, and **which k means what**
|
|
194
|
+
- [ ] the noise band for that `n`, computed
|
|
195
|
+
- [ ] paired or unpaired; if paired, the seed set
|
|
196
|
+
- [ ] the harness configuration: resource floor and ceiling, isolation between trials
|
|
197
|
+
- [ ] whether the difference reproduced on a rerun
|
|
198
|
+
- [ ] what the scope of the evidence authorises next — not what it suggests
|
|
199
|
+
|
|
200
|
+
**A green suite with none of these is a number, not a verdict.** That distinction is the
|
|
201
|
+
reason this file exists.
|