@tangle-network/agent-eval 0.123.1 → 0.123.2
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 +5 -0
- package/README.md +151 -161
- package/dist/analyst/index.d.ts +9 -1
- package/dist/analyst/index.js +5 -5
- package/dist/authenticity/index.js +3 -2
- package/dist/authenticity/index.js.map +1 -1
- package/dist/benchmarks/index.d.ts +2 -1
- package/dist/benchmarks/index.js +6 -6
- package/dist/campaign/index.d.ts +29 -33
- package/dist/campaign/index.js +6 -6
- package/dist/{chunk-A5S77LSE.js → chunk-4SOQ4ND2.js} +2 -2
- package/dist/{chunk-VJ7T5WIO.js → chunk-5YMKIFYP.js} +3 -3
- package/dist/{chunk-U5CHZ5M3.js → chunk-DNVPOYUS.js} +4 -4
- package/dist/{chunk-6WX7CBAR.js → chunk-E3HAD4A3.js} +19 -8
- package/dist/chunk-E3HAD4A3.js.map +1 -0
- package/dist/{chunk-LBAHQOBI.js → chunk-EBDOTTZJ.js} +37 -11
- package/dist/chunk-EBDOTTZJ.js.map +1 -0
- package/dist/{chunk-XJYR7XFV.js → chunk-GC4ATIKK.js} +1 -1
- package/dist/chunk-GC4ATIKK.js.map +1 -0
- package/dist/{chunk-HZJF4IUO.js → chunk-HQY7LBV2.js} +3 -3
- package/dist/{chunk-NJC7U437.js → chunk-J7S4YM27.js} +6 -5
- package/dist/chunk-J7S4YM27.js.map +1 -0
- package/dist/{chunk-S3UZOQ5Y.js → chunk-LOW3U7JZ.js} +2 -2
- package/dist/{chunk-OYZAPX5G.js → chunk-R226UZOI.js} +2 -2
- package/dist/{chunk-GS3FJGUF.js → chunk-RQP5UTK5.js} +120 -14
- package/dist/chunk-RQP5UTK5.js.map +1 -0
- package/dist/{chunk-G2GPNLSX.js → chunk-WMJR67FX.js} +3 -3
- package/dist/{chunk-FC5NDO3E.js → chunk-WXQTVEKM.js} +3 -3
- package/dist/cli.js +100 -10
- package/dist/cli.js.map +1 -1
- package/dist/contract/index.d.ts +97 -5
- package/dist/contract/index.js +9 -7
- package/dist/contract/index.js.map +1 -1
- package/dist/control.js +3 -3
- package/dist/fuzz.js +3 -2
- package/dist/fuzz.js.map +1 -1
- package/dist/hosted/index.d.ts +8 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +13 -13
- package/dist/index.js.map +1 -1
- package/dist/openapi.json +1 -1
- package/dist/rl.d.ts +9 -1
- package/dist/rl.js +4 -4
- package/dist/storyboard/index.js +1 -1
- package/dist/storyboard/index.js.map +1 -1
- package/dist/traces.js +3 -3
- package/dist/wire/index.d.ts +61 -4
- package/dist/wire/index.js +2 -2
- package/docs/adapters-observability.md +6 -6
- package/docs/building-doctrine.md +5 -5
- package/docs/concepts.md +29 -29
- package/docs/customer-journeys.md +80 -155
- package/docs/design/loop-taxonomy.md +26 -27
- package/docs/design.md +70 -0
- package/docs/distributed-driver.md +14 -14
- package/docs/eval-surface-map.md +11 -11
- package/docs/hosted-ingest-spec.md +4 -4
- package/docs/improvement-glossary.md +38 -38
- package/docs/insight-report.md +32 -27
- package/docs/multi-shot-optimization.md +8 -8
- package/docs/research-report-methodology.md +9 -9
- package/docs/self-improvement-map.md +13 -13
- package/docs/trace-analysis.md +2 -2
- package/docs/wire-protocol.md +16 -16
- package/package.json +2 -1
- package/dist/chunk-6WX7CBAR.js.map +0 -1
- package/dist/chunk-GS3FJGUF.js.map +0 -1
- package/dist/chunk-LBAHQOBI.js.map +0 -1
- package/dist/chunk-NJC7U437.js.map +0 -1
- package/dist/chunk-XJYR7XFV.js.map +0 -1
- package/docs/auto-research-loop-end-to-end.md +0 -186
- /package/dist/{chunk-A5S77LSE.js.map → chunk-4SOQ4ND2.js.map} +0 -0
- /package/dist/{chunk-VJ7T5WIO.js.map → chunk-5YMKIFYP.js.map} +0 -0
- /package/dist/{chunk-U5CHZ5M3.js.map → chunk-DNVPOYUS.js.map} +0 -0
- /package/dist/{chunk-HZJF4IUO.js.map → chunk-HQY7LBV2.js.map} +0 -0
- /package/dist/{chunk-S3UZOQ5Y.js.map → chunk-LOW3U7JZ.js.map} +0 -0
- /package/dist/{chunk-OYZAPX5G.js.map → chunk-R226UZOI.js.map} +0 -0
- /package/dist/{chunk-G2GPNLSX.js.map → chunk-WMJR67FX.js.map} +0 -0
- /package/dist/{chunk-FC5NDO3E.js.map → chunk-WXQTVEKM.js.map} +0 -0
package/docs/insight-report.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# `InsightReport
|
|
1
|
+
# `InsightReport`: the report
|
|
2
2
|
|
|
3
3
|
The single shape every analysis call returns. `selfImprove()` embeds it in `SelfImproveResult.insight`; `analyzeRuns()` returns it directly. The hosted-tier wire format carries it on `EvalRunEvent.insightReport?`.
|
|
4
4
|
|
|
5
5
|
Use `summarizeExecution({ runs })` when observed traces have no task-quality labels.
|
|
6
6
|
It returns only `execution` and `costProvenance`, so callers do not need to fabricate a quality score to report runtime facts.
|
|
7
7
|
|
|
8
|
-
Every section is **opt-in based on what your data supports
|
|
8
|
+
Every section is **opt-in based on what your data supports**: the function never invents signal. If your runs don't carry judge scores, `judges` is empty. If there's no baseline/candidate split, `lift` is undefined. The shape is consistent; population is honest.
|
|
9
9
|
|
|
10
10
|
This page walks every section with a real (synthetic) example and explains how to act on it.
|
|
11
11
|
|
|
@@ -27,13 +27,13 @@ interface InsightReport {
|
|
|
27
27
|
contamination?: ContaminationInsight // when canaryScenarios supplied
|
|
28
28
|
outcomeCorrelation?: OutcomeCorrelationInsight // when outcomeSignal supplied
|
|
29
29
|
release: ReleaseSummary // always
|
|
30
|
-
recommendations: Recommendation[] // always
|
|
30
|
+
recommendations: Recommendation[] // always: read this FIRST
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
|
-
## `execution
|
|
36
|
+
## `execution`: runtime facts, separate from quality
|
|
37
37
|
|
|
38
38
|
Always present.
|
|
39
39
|
It reports duration, optional queue time, direct input, output, reasoning, cache-read, and cache-write tokens, model-call coverage, model cohorts, explicit failures, and separately reported orchestration aggregates.
|
|
@@ -73,7 +73,7 @@ Cost remains in `costQuality`, where observed, estimated, and uncaptured USD sta
|
|
|
73
73
|
|
|
74
74
|
---
|
|
75
75
|
|
|
76
|
-
## `n` + `composite` + `perDimension
|
|
76
|
+
## `n` + `composite` + `perDimension`: distributional summary
|
|
77
77
|
|
|
78
78
|
Always present. The basic "where are my numbers" view.
|
|
79
79
|
|
|
@@ -99,13 +99,13 @@ Always present. The basic "where are my numbers" view.
|
|
|
99
99
|
|
|
100
100
|
**Read first:** the `composite.mean`. If it's < 0.5, your agent has a ceiling problem, not a tuning problem.
|
|
101
101
|
|
|
102
|
-
**Read next:** `perDimension`. If `clarity` is high but `concision` is low, your prompts get the right ideas in too many words
|
|
102
|
+
**Read next:** `perDimension`. If `clarity` is high but `concision` is low, your prompts get the right ideas in too many words: different fix than "wrong ideas."
|
|
103
103
|
|
|
104
104
|
**Use the histogram for:** finding bimodal failure modes. A bin with `count > 0` near zero and another > 0 near 1 means your agent has two distinct behaviors, not one noisy one.
|
|
105
105
|
|
|
106
106
|
---
|
|
107
107
|
|
|
108
|
-
## `costQuality
|
|
108
|
+
## `costQuality`: cost-vs-quality Pareto
|
|
109
109
|
|
|
110
110
|
Always present. `cost.histogram` is the per-run cost distribution; `pareto` is the substrate's `ParetoFigureSpec`.
|
|
111
111
|
|
|
@@ -131,11 +131,11 @@ Always present. `cost.histogram` is the per-run cost distribution; `pareto` is t
|
|
|
131
131
|
|
|
132
132
|
**Use this when:** comparing prompts, models, or candidate surfaces. The Pareto frontier is your menu of "best you can do at each cost level."
|
|
133
133
|
|
|
134
|
-
**Render with:** any chart library
|
|
134
|
+
**Render with:** any chart library: `points` is plain JSON. Hosted-tier dashboards render this as a scatter with the frontier highlighted.
|
|
135
135
|
|
|
136
136
|
---
|
|
137
137
|
|
|
138
|
-
## `judges
|
|
138
|
+
## `judges`: per-judge mean
|
|
139
139
|
|
|
140
140
|
Populated when run records carry `outcome.judgeScores`.
|
|
141
141
|
|
|
@@ -154,20 +154,23 @@ The substrate's full judge-calibration suite (positional bias, self-preference,
|
|
|
154
154
|
|
|
155
155
|
---
|
|
156
156
|
|
|
157
|
-
## `interRater
|
|
157
|
+
## `interRater`: multi-rater agreement and disagreement review
|
|
158
158
|
|
|
159
|
-
Populated when `analyzeRuns({ raterScores })` is supplied
|
|
159
|
+
Populated when `analyzeRuns({ raterScores })` is supplied: typically via `fromFeedbackTable()`.
|
|
160
160
|
|
|
161
161
|
```jsonc
|
|
162
162
|
{
|
|
163
163
|
"interRater": {
|
|
164
164
|
"raters": 3,
|
|
165
165
|
"jointlyRated": 30,
|
|
166
|
-
"kappa": 0.
|
|
166
|
+
"kappa": 0.40,
|
|
167
|
+
"icc": 0.42,
|
|
168
|
+
"pearson": 0.43,
|
|
169
|
+
"spearman": 0.41,
|
|
167
170
|
"perPair": {
|
|
168
|
-
"alice::bob": 0.
|
|
169
|
-
"alice::carol": 0.
|
|
170
|
-
"bob::carol": 0.
|
|
171
|
+
"alice::bob": 0.53,
|
|
172
|
+
"alice::carol": 0.47,
|
|
173
|
+
"bob::carol": 0.19
|
|
171
174
|
},
|
|
172
175
|
"disagreementCases": [
|
|
173
176
|
{ "runId": "claim-7", "range": 1.00,
|
|
@@ -180,13 +183,15 @@ Populated when `analyzeRuns({ raterScores })` is supplied — typically via `fro
|
|
|
180
183
|
}
|
|
181
184
|
```
|
|
182
185
|
|
|
183
|
-
**Read first:**
|
|
186
|
+
**Read first:** `kappa` and `icc`, which measure absolute agreement.
|
|
187
|
+
Pearson and Spearman measure correlation and can remain high when raters use different score levels.
|
|
188
|
+
When absolute agreement is low, review the largest disagreement cases before automating the rubric.
|
|
184
189
|
|
|
185
190
|
**Use this when:** building per-rater LLM judges. Each rater's individual scores are the gold signal you calibrate against. Once a calibrated LLM matches the human ≥85%, you can auto-grade and escalate only the disagreement cases.
|
|
186
191
|
|
|
187
192
|
---
|
|
188
193
|
|
|
189
|
-
## `lift
|
|
194
|
+
## `lift`: paired-bootstrap statistical lift
|
|
190
195
|
|
|
191
196
|
Populated when baseline + candidate candidates are present (auto-detected from two distinct `candidateId`s, or explicit via `baselineCandidateId` + `candidateCandidateId`).
|
|
192
197
|
|
|
@@ -211,13 +216,13 @@ Populated when baseline + candidate candidates are present (auto-detected from t
|
|
|
211
216
|
- `ci95[0] ≤ threshold < ci95[1]` → **INCONCLUSIVE.** Expand the corpus or wait for more data.
|
|
212
217
|
- `ci95[1] ≤ threshold` → **HOLD.** No evidence the candidate is better.
|
|
213
218
|
|
|
214
|
-
The `recommendations` array surfaces exactly this decision (`kind: 'ship' | 'hold' | 'expand-corpus'`)
|
|
219
|
+
The `recommendations` array surfaces exactly this decision (`kind: 'ship' | 'hold' | 'expand-corpus'`): that's what consumers should read.
|
|
215
220
|
|
|
216
221
|
**Why bootstrap, not t-test alone:** paired bootstrap is distribution-free. Your judge scores are bounded in [0,1] and almost never normal; the bootstrap CI is the honest one.
|
|
217
222
|
|
|
218
223
|
---
|
|
219
224
|
|
|
220
|
-
## `failureClusters
|
|
225
|
+
## `failureClusters`: grouped failure modes
|
|
221
226
|
|
|
222
227
|
Populated when an `AnalystRegistry` is passed via `analyzeRuns({ analyst })`. The substrate runs each failed run through the registered analysts and groups findings by `analyst_id` / `area`.
|
|
223
228
|
|
|
@@ -245,7 +250,7 @@ Populated when an `AnalystRegistry` is passed via `analyzeRuns({ analyst })`. Th
|
|
|
245
250
|
|
|
246
251
|
---
|
|
247
252
|
|
|
248
|
-
## `contamination
|
|
253
|
+
## `contamination`: canary check
|
|
249
254
|
|
|
250
255
|
Populated when canary scenarios are passed via `analyzeRuns({ canaryScenarios })`. Each canary carries a sentinel string the agent should never emit; the report counts leaks.
|
|
251
256
|
|
|
@@ -277,7 +282,7 @@ When `leaks > 0`:
|
|
|
277
282
|
|
|
278
283
|
---
|
|
279
284
|
|
|
280
|
-
## `outcomeCorrelation
|
|
285
|
+
## `outcomeCorrelation`: closing the loop on real outcomes
|
|
281
286
|
|
|
282
287
|
Populated when `outcomeSignal: { metric, valueByRunId }` is supplied.
|
|
283
288
|
|
|
@@ -307,9 +312,9 @@ This is the layer that says **"does my judge's taste actually predict the metric
|
|
|
307
312
|
|
|
308
313
|
---
|
|
309
314
|
|
|
310
|
-
## `release
|
|
315
|
+
## `release`: pass/warn/fail axes
|
|
311
316
|
|
|
312
|
-
Always present. Roll-up across three axes
|
|
317
|
+
Always present. Roll-up across three axes: quality lift, contamination, composite distribution.
|
|
313
318
|
|
|
314
319
|
```jsonc
|
|
315
320
|
{
|
|
@@ -334,7 +339,7 @@ Overall `status` is `fail` if any axis fails; `warn` if any warn; `pass` otherwi
|
|
|
334
339
|
|
|
335
340
|
---
|
|
336
341
|
|
|
337
|
-
## `recommendations
|
|
342
|
+
## `recommendations`: the actionable layer
|
|
338
343
|
|
|
339
344
|
Always present. Read this first.
|
|
340
345
|
|
|
@@ -342,7 +347,7 @@ Always present. Read this first.
|
|
|
342
347
|
{
|
|
343
348
|
"recommendations": [
|
|
344
349
|
{ "priority": "critical", "kind": "ship",
|
|
345
|
-
"title": "Ship
|
|
350
|
+
"title": "Ship: lift 0.070 (95% CI 0.040..0.100)",
|
|
346
351
|
"detail": "Holdout lift exceeds threshold 0.02 with 95% bootstrap confidence (n=40, p=0.0008, d=0.41).",
|
|
347
352
|
"evidencePath": "lift" },
|
|
348
353
|
{ "priority": "high", "kind": "investigate",
|
|
@@ -357,7 +362,7 @@ Always present. Read this first.
|
|
|
357
362
|
|---|---|
|
|
358
363
|
| `ship` | lift CI lower bound > threshold |
|
|
359
364
|
| `hold` | lift CI upper bound ≤ threshold |
|
|
360
|
-
| `expand-corpus` | lift CI straddles threshold
|
|
365
|
+
| `expand-corpus` | lift CI straddles threshold: more data needed |
|
|
361
366
|
| `fix` | canary contamination detected |
|
|
362
367
|
| `recalibrate` | inter-rater κ < 0.5, OR outcome correlation < 0.3 |
|
|
363
368
|
| `investigate` | top failure cluster > some-share |
|
|
@@ -378,4 +383,4 @@ Always present. Read this first.
|
|
|
378
383
|
| `contamination` | `canaryScenarios` passed in |
|
|
379
384
|
| `outcomeCorrelation` | `outcomeSignal` passed in |
|
|
380
385
|
|
|
381
|
-
All sections beyond the always-present ones are `T | undefined`, never empty objects. If a section is missing, your inputs didn't support it
|
|
386
|
+
All sections beyond the always-present ones are `T | undefined`, never empty objects. If a section is missing, your inputs didn't support it: the report is honest about that.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
> live API; see also [feature-guide.md](./feature-guide.md) and [concepts.md](./concepts.md).
|
|
7
7
|
|
|
8
8
|
`runImprovementLoop` is the public entry for GEPA-style optimization over a whole
|
|
9
|
-
task trajectory
|
|
9
|
+
task trajectory: the thing you improve is not a single model call but an agent
|
|
10
10
|
system prompt, tool descriptions, a routing policy, or any scaffolding that affects
|
|
11
11
|
the entire run. It is the OUTER loop: it improves the SURFACE the inner workers run.
|
|
12
12
|
|
|
@@ -15,13 +15,13 @@ the entire run. It is the OUTER loop: it improves the SURFACE the inner workers
|
|
|
15
15
|
You own a few seams; the loop owns the release-critical glue (paired seeds, the
|
|
16
16
|
held-out re-score, the promotion gate, provenance):
|
|
17
17
|
|
|
18
|
-
- **`baselineSurface
|
|
19
|
-
- **`dispatchWithSurface(surface, scenario, ctx)
|
|
18
|
+
- **`baselineSurface`**: the current surface (a prompt string, or a `CodeSurface`).
|
|
19
|
+
- **`dispatchWithSurface(surface, scenario, ctx)`**: run one task to completion
|
|
20
20
|
under a candidate surface; return the artifact the judges score.
|
|
21
|
-
- **`judges
|
|
22
|
-
- **`proposer
|
|
21
|
+
- **`judges`**: score the artifact (`{ composite, dimensions }`).
|
|
22
|
+
- **`proposer`**: proposes candidate surfaces each generation: `gepaProposer`
|
|
23
23
|
(reflective + Pareto frontier) or `evolutionaryProposer` (mutator).
|
|
24
|
-
- **`gate
|
|
24
|
+
- **`gate`**: `defaultProductionGate` (held-out significance + red-team +
|
|
25
25
|
reward-hacking + canary). Ships ONLY on a CI-lower-bound held-out lift.
|
|
26
26
|
|
|
27
27
|
## Minimal example
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
const result = await runImprovementLoop({
|
|
37
37
|
baselineSurface: currentSystemPrompt,
|
|
38
38
|
scenarios: trainScenarios, // optimizer-visible
|
|
39
|
-
holdoutScenarios, // DISJOINT
|
|
39
|
+
holdoutScenarios, // DISJOINT: only the gate sees these
|
|
40
40
|
dispatchWithSurface: async (surface, scenario) =>
|
|
41
41
|
runYourAgentToCompletion({ scenario, prompt: String(surface) }),
|
|
42
42
|
judges: [myJudge],
|
|
@@ -64,7 +64,7 @@ if (result.gateResult.decision === 'ship') {
|
|
|
64
64
|
paired-bootstrap CI lower bound clears `deltaThreshold`; a few-instance swing at
|
|
65
65
|
thin `n` is held (`few_runs`), not promoted.
|
|
66
66
|
- **No-op never ships.** If no candidate beats the baseline, the winner IS the
|
|
67
|
-
baseline (empty diff) and the loop forces `hold
|
|
67
|
+
baseline (empty diff) and the loop forces `hold`: it does not score
|
|
68
68
|
baseline-vs-itself and read model noise as lift.
|
|
69
69
|
- **Provenance falls out.** `result.promotedDiff` + `emitLoopProvenance` give the
|
|
70
70
|
auditable candidate→gate→promote chain (rationale, content hashes, a held-out lift
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# researchReport
|
|
1
|
+
# researchReport: methodology
|
|
2
2
|
|
|
3
3
|
This document is the methodological brief for `researchReport` (exported from
|
|
4
4
|
`@tangle-network/agent-eval` and `@tangle-network/agent-eval/reporting`). It
|
|
@@ -11,14 +11,14 @@ corresponding function or option so the doc and the code don't drift.
|
|
|
11
11
|
|
|
12
12
|
## Inputs
|
|
13
13
|
|
|
14
|
-
- `runs: RunRecord[]
|
|
14
|
+
- `runs: RunRecord[]`: every record carries `runId`, `candidateId`, `seed`,
|
|
15
15
|
`experimentId`, `splitTag`, and an `outcome` with the configured score.
|
|
16
|
-
- `comparator: string
|
|
16
|
+
- `comparator: string`: the candidate id treated as the null reference. Must
|
|
17
17
|
be selected before data inspection; `preregistrationHash` should pin this.
|
|
18
|
-
- `split: 'search' | 'holdout'
|
|
18
|
+
- `split: 'search' | 'holdout'`: defaults to `holdout`. Decisions on `search`
|
|
19
19
|
are descriptive only; promotion calls require the holdout.
|
|
20
|
-
- `rope: { low, high }
|
|
21
|
-
in score units. Must come from the domain owner
|
|
20
|
+
- `rope: { low, high }`: Region of Practical Equivalence on the paired delta,
|
|
21
|
+
in score units. Must come from the domain owner: there is no
|
|
22
22
|
statistically-defensible default.
|
|
23
23
|
- `minPairs` (soft floor, default 20) and `RESEARCH_REPORT_HARD_PAIR_FLOOR`
|
|
24
24
|
(hard floor, 6). Below the soft floor, the verdict is `needs_more_data` and
|
|
@@ -35,7 +35,7 @@ with non-finite scores are dropped before pairing.
|
|
|
35
35
|
|
|
36
36
|
## Decision rule
|
|
37
37
|
|
|
38
|
-
In order
|
|
38
|
+
In order: first match wins:
|
|
39
39
|
|
|
40
40
|
1. `comparator` itself → `hold` (baseline).
|
|
41
41
|
2. No comparator → `hold` if on the cost/quality Pareto frontier, else
|
|
@@ -46,7 +46,7 @@ In order — first match wins:
|
|
|
46
46
|
"below hard floor" reason. Bootstrap CIs degenerate at this size.
|
|
47
47
|
5. ROPE configured AND paired-delta CI ⊂ ROPE → `equivalent`.
|
|
48
48
|
6. Paired-delta CI upper bound < 0 → `reject` (CI excludes a non-negative
|
|
49
|
-
effect). Note: this uses **paired delta only
|
|
49
|
+
effect). Note: this uses **paired delta only**: not the marginal mean.
|
|
50
50
|
7. Paired N < `minPairs` (soft floor) → `needs_more_data` with the MDE at
|
|
51
51
|
current N attached so the verdict is actionable.
|
|
52
52
|
8. BH-adjusted q ≤ `fdr` AND CI lower bound > 0 → `promote`. The BH q-value
|
|
@@ -71,7 +71,7 @@ The Pr(Δ>0) and Pr(Δ∈ROPE) summaries use the bootstrap-prior duality of
|
|
|
71
71
|
[Rubin 1981]: under a non-informative Dirichlet prior, the bootstrap
|
|
72
72
|
distribution of a sample statistic is its posterior. We expose these as
|
|
73
73
|
posterior summaries on the **mean** delta and the bootstrap CI on the
|
|
74
|
-
**median** delta
|
|
74
|
+
**median** delta: the median is more robust to the heavy-tailed score
|
|
75
75
|
distributions seen in agent benchmarks; the mean lets us read off the
|
|
76
76
|
Bayesian-style probability of superiority in a single number.
|
|
77
77
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# The self-improvement map
|
|
2
2
|
|
|
3
3
|
One loop. Four roles. A proposer catalog of pluggable strategies. A bench rig that
|
|
4
|
-
proves the loop produces real lift. Nothing here is duplicated
|
|
4
|
+
proves the loop produces real lift. Nothing here is duplicated: it is one engine
|
|
5
5
|
pointed at different surfaces. This map exists because the surface count makes it
|
|
6
6
|
*look* like many competing systems when it is one.
|
|
7
7
|
|
|
@@ -26,11 +26,11 @@ product imports the same function. Each generation it does four things:
|
|
|
26
26
|
repeat N generations
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
## The four roles
|
|
29
|
+
## The four roles: keep them separate and the confusion clears
|
|
30
30
|
|
|
31
31
|
| Role | What it is | Plain meaning |
|
|
32
32
|
|---|---|---|
|
|
33
|
-
| **Surface** | a *string
|
|
33
|
+
| **Surface** | a *string*: an agent directive, a `SKILL.md`, a playbook, a memory, a judge rubric | **what** gets improved |
|
|
34
34
|
| **Proposer** | a `SurfaceProposer` (the catalog below) | **how** candidate surfaces are proposed |
|
|
35
35
|
| **Gate** | held-out split + significance (`paretoSignificanceGate` / `heldOutGate` / `defaultProductionGate`) | **did it actually get better**, vs noise |
|
|
36
36
|
| **Judge** | scores a run | **how good** any version is |
|
|
@@ -44,7 +44,7 @@ surface; **bench-only proposers** exist solely to be raced inside
|
|
|
44
44
|
|
|
45
45
|
| Proposer factory | Surface | Strategy | Role | Notes |
|
|
46
46
|
|---|---|---|---|---|
|
|
47
|
-
| `gepaProposer` | prompt | reflective full-surface rewrite + Pareto frontier | **production default** | consumes trace-analysis findings
|
|
47
|
+
| `gepaProposer` | prompt | reflective full-surface rewrite + Pareto frontier | **production default** | consumes trace-analysis findings: see below |
|
|
48
48
|
| `fapoProposer` | prompt/config/code | reviewed escalation policy over prompt → parameter → structural proposers | production, benchmark | encodes FAPO's scope + reviewer + prompt-first escalation rules; structural generator is injected |
|
|
49
49
|
| `parameterSweepProposer` | config | JSON config patch/sweep | production, benchmark | middle FAPO level for parameter/config edits such as `retrieval.k`, `temperature`, `max_tokens` |
|
|
50
50
|
| `skillOptProposer` | skill-doc | anchored add/delete/replace patch | production | preserves earlier rules; edit budget = "textual learning rate" |
|
|
@@ -58,29 +58,29 @@ Default choice: start with `gepaProposer` for prompt surfaces, add
|
|
|
58
58
|
`parameterSweepProposer` when config knobs are the likely failure mode, and wrap
|
|
59
59
|
them with `fapoProposer` when evidence should decide when to escalate.
|
|
60
60
|
|
|
61
|
-
## Trace analysis
|
|
61
|
+
## Trace analysis: what it is and the three places it is used
|
|
62
62
|
|
|
63
63
|
"Trace analysis" is the **evidence layer**: it turns raw OTLP traces into "here is
|
|
64
64
|
exactly *why* the agent failed" (failure clusters → findings). The engine is
|
|
65
65
|
`analyzeRuns()` + the analyst registry (`src/contract/analyze-runs.ts`). It is used in
|
|
66
|
-
three places
|
|
66
|
+
three places: this is the answer to "if GEPA does its own thing, what is trace
|
|
67
67
|
analysis *for*?":
|
|
68
68
|
|
|
69
|
-
1. **Ships to customers
|
|
70
|
-
2. **Feeds the proposer
|
|
69
|
+
1. **Ships to customers**: `analyzeRuns()` → `InsightReport`, the Intelligence product.
|
|
70
|
+
2. **Feeds the proposer**: `gepaProposer` calls `renderAnalystEvidence(ctx.findings,
|
|
71
71
|
ctx.report)` (`src/campaign/proposers/gepa.ts`). GEPA's rewrites are grounded
|
|
72
72
|
in the diagnosis instead of guessing blind. Trace analysis
|
|
73
73
|
**is** on the GEPA side.
|
|
74
|
-
3. **Races HALO
|
|
74
|
+
3. **Races HALO**: wrapped as `traceAnalystProposer` so our analysis competes
|
|
75
75
|
head-to-head with the external SOTA inside `compareProposers`.
|
|
76
76
|
|
|
77
77
|
## Where HALO fits (and why it feels "removed")
|
|
78
78
|
|
|
79
79
|
`haloProposer` is alive (`src/campaign/proposers/halo.ts`, exported from the campaign
|
|
80
80
|
barrel) but it is **never in the product loop**. It shells out to an *external* engine
|
|
81
|
-
(`halo-engine`)
|
|
81
|
+
(`halo-engine`): so the analysis genuinely lives outside this repo; we only wrap it.
|
|
82
82
|
|
|
83
|
-
Its only job is the **bake-off**. HALO's real opponent is **not** `gepaProposer
|
|
83
|
+
Its only job is the **bake-off**. HALO's real opponent is **not** `gepaProposer`: it is
|
|
84
84
|
`traceAnalystProposer`. `compareProposers` holds the apply step identical (same
|
|
85
85
|
`APPLY_SYSTEM`, same `traces.jsonl`, same held-out scoring) so the only variable is
|
|
86
86
|
**analysis quality: HALO vs ours.** A measuring stick, like a benchmark baseline.
|
|
@@ -90,7 +90,7 @@ Its only job is the **bake-off**. HALO's real opponent is **not** `gepaProposer`
|
|
|
90
90
|
`agent-runtime/bench/src/gepa-refine.ts` runs **this same loop** against a *public
|
|
91
91
|
benchmark* (AppWorld, CAD, …) instead of product data. Why a separate rig:
|
|
92
92
|
|
|
93
|
-
- On product traces, "+4 lift" can be model noise or a judge flattering itself
|
|
93
|
+
- On product traces, "+4 lift" can be model noise or a judge flattering itself: no
|
|
94
94
|
ground truth.
|
|
95
95
|
- On a benchmark the score is **objective and ungameable** (AppWorld runs the agent's
|
|
96
96
|
code against its own unit tests). If a GEPA-optimized directive beats a deliberately
|
|
@@ -110,6 +110,6 @@ The code is well-factored; the confusion is narrative:
|
|
|
110
110
|
- **The real gap is the missing proof, not the design.** The loop kept being proved on
|
|
111
111
|
benchmarks too easy to show value: when a capable model ceilings an extraction task,
|
|
112
112
|
**0 findings fire** and the whole trace-analysis→optimizer apparatus is inert. It
|
|
113
|
-
earns its keep only on **hard agentic tasks
|
|
113
|
+
earns its keep only on **hard agentic tasks**: which is why the AppWorld REPL run
|
|
114
114
|
(multi-turn, real tool execution, unbounded turns) is the one that can finally
|
|
115
115
|
separate the evidence-grounded optimizer from baseline.
|
package/docs/trace-analysis.md
CHANGED
|
@@ -51,7 +51,7 @@ The analyst runs one Ax executor loop and accepts only an explicit structured `f
|
|
|
51
51
|
## Deterministic failure coverage (no LLM)
|
|
52
52
|
|
|
53
53
|
Before (or alongside) the LLM analyst, `OtlpFileTraceStore.getOverview()` returns a
|
|
54
|
-
`DatasetOverview` whose `error_clusters` are computed deterministically
|
|
54
|
+
`DatasetOverview` whose `error_clusters` are computed deterministically: error
|
|
55
55
|
spans are grouped by a normalized failure signature (uuids / hex ids / numbers /
|
|
56
56
|
absolute paths / durations collapsed), each cluster carrying its prevalence,
|
|
57
57
|
exemplar `trace_id`/`span_id`, and a verbatim sample. This is a zero-LLM,
|
|
@@ -60,7 +60,7 @@ reproducible failure checklist the analyst then explains and closes:
|
|
|
60
60
|
```ts
|
|
61
61
|
const overview = await store.getOverview()
|
|
62
62
|
for (const c of overview.error_clusters) {
|
|
63
|
-
console.log(`${c.trace_count}× ${c.signature}
|
|
63
|
+
console.log(`${c.trace_count}× ${c.signature}: e.g. trace ${c.exemplar_trace_ids[0]}`)
|
|
64
64
|
}
|
|
65
65
|
```
|
|
66
66
|
|
package/docs/wire-protocol.md
CHANGED
|
@@ -29,9 +29,9 @@ Both transports talk to identical handlers. If you need a sustained connection (
|
|
|
29
29
|
|
|
30
30
|
## Methods
|
|
31
31
|
|
|
32
|
-
The current surface is the smallest useful slice. Adding a method is mechanical
|
|
32
|
+
The current surface is the smallest useful slice. Adding a method is mechanical: see [§Adding a method](#adding-a-method).
|
|
33
33
|
|
|
34
|
-
### `judge
|
|
34
|
+
### `judge`: score content against a rubric
|
|
35
35
|
|
|
36
36
|
```http
|
|
37
37
|
POST /v1/judge
|
|
@@ -63,7 +63,7 @@ Pass either `rubricName` (built-in) or `rubric` (inline definition). Not both. T
|
|
|
63
63
|
|
|
64
64
|
`rubricVersion` is the stable hash of the rubric used. Scores are only comparable across runs when this matches.
|
|
65
65
|
|
|
66
|
-
### `listRubrics
|
|
66
|
+
### `listRubrics`: discover what's registered
|
|
67
67
|
|
|
68
68
|
```http
|
|
69
69
|
GET /v1/rubrics
|
|
@@ -87,7 +87,7 @@ GET /v1/rubrics
|
|
|
87
87
|
}
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
### `version
|
|
90
|
+
### `version`: server + wire-protocol versions
|
|
91
91
|
|
|
92
92
|
```http
|
|
93
93
|
GET /v1/version
|
|
@@ -102,13 +102,13 @@ GET /v1/version
|
|
|
102
102
|
}
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
`version` matches the package version. `wireVersion` bumps independently
|
|
105
|
+
`version` matches the package version. `wireVersion` bumps independently: only on breaking request/response schema changes. Package versions can differ across releases as long as `wireVersion` matches.
|
|
106
106
|
|
|
107
|
-
### `GET /healthz
|
|
107
|
+
### `GET /healthz`: liveness
|
|
108
108
|
|
|
109
109
|
For probing whether a server is up. Returns `{ "status": "ok", "uptimeSec": <number> }`.
|
|
110
110
|
|
|
111
|
-
### `GET /openapi.json
|
|
111
|
+
### `GET /openapi.json`: full spec
|
|
112
112
|
|
|
113
113
|
Auto-generated from the Zod schemas. This is what code generators consume to produce typed clients in other languages.
|
|
114
114
|
|
|
@@ -168,11 +168,11 @@ echo '{}' | agent-eval rpc listRubrics
|
|
|
168
168
|
# judge (one-shot)
|
|
169
169
|
echo '{"rubricName":"anti-slop","content":"…"}' | agent-eval rpc judge
|
|
170
170
|
|
|
171
|
-
# JSONL batch
|
|
171
|
+
# JSONL batch: one request per line
|
|
172
172
|
cat requests.jsonl | agent-eval rpc-batch judge > results.jsonl
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
-
Each invocation is one process
|
|
175
|
+
Each invocation is one process: Node startup adds ~500 ms. For more than a few calls, stand up a server.
|
|
176
176
|
|
|
177
177
|
## Clients
|
|
178
178
|
|
|
@@ -182,13 +182,13 @@ Each invocation is one process — Node startup adds ~500 ms. For more than a fe
|
|
|
182
182
|
|
|
183
183
|
## Adding a method
|
|
184
184
|
|
|
185
|
-
1. **Schema
|
|
186
|
-
2. **Handler
|
|
187
|
-
3. **Server route
|
|
188
|
-
4. **RPC case
|
|
189
|
-
5. **OpenAPI route
|
|
190
|
-
6. **Test
|
|
191
|
-
7. **Python client
|
|
185
|
+
1. **Schema**: define `XRequestSchema` and `XResponseSchema` in `src/wire/schemas.ts`. Every field gets a `.describe()` so docs flow through to OpenAPI.
|
|
186
|
+
2. **Handler**: pure function in `src/wire/handlers.ts`. Throws `WireError` for caller-fixable issues.
|
|
187
|
+
3. **Server route**: `app.post('/v1/x', …)` in `src/wire/server.ts`.
|
|
188
|
+
4. **RPC case**: add `case 'x':` in `dispatchRpc` in `src/wire/rpc.ts`.
|
|
189
|
+
5. **OpenAPI route**: register in `src/wire/openapi.ts` so it shows up in the spec.
|
|
190
|
+
6. **Test**: add to `tests/wire/`. At minimum: schema validation, happy-path, error-path.
|
|
191
|
+
7. **Python client**: add a method on `Client` in `clients/python/src/agent_eval_rpc/client.py`, plus pydantic models in `models.py` mirroring the new schemas.
|
|
192
192
|
|
|
193
193
|
The pattern is mechanical. When the surface grows past ~10 methods, swap the hand-written Python models for `datamodel-code-generator -i openapi.json -o models.py`.
|
|
194
194
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-eval",
|
|
3
|
-
"version": "0.123.
|
|
3
|
+
"version": "0.123.2",
|
|
4
4
|
"description": "Evaluate and improve AI agents from runs, traces, judges, and feedback. Compare candidates, cluster failures, measure lift, and gate releases.",
|
|
5
5
|
"homepage": "https://github.com/tangle-network/agent-eval#readme",
|
|
6
6
|
"repository": {
|
|
@@ -142,6 +142,7 @@
|
|
|
142
142
|
"test": "vitest run",
|
|
143
143
|
"test:watch": "vitest",
|
|
144
144
|
"typecheck": "tsc --noEmit",
|
|
145
|
+
"typecheck:examples": "tsc -p tsconfig.examples.json",
|
|
145
146
|
"lint": "biome check src",
|
|
146
147
|
"format": "biome format --write src",
|
|
147
148
|
"openapi": "node dist/cli.js openapi --out dist/openapi.json",
|