@tangle-network/agent-eval 0.123.1 → 0.123.3
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 +29 -0
- package/README.md +191 -157
- package/dist/analyst/index.d.ts +27 -4
- package/dist/analyst/index.js +6 -6
- package/dist/authenticity/index.js +3 -2
- package/dist/authenticity/index.js.map +1 -1
- package/dist/benchmarks/index.d.ts +12 -4
- package/dist/benchmarks/index.js +9 -9
- package/dist/campaign/index.d.ts +270 -254
- package/dist/campaign/index.js +22 -18
- package/dist/{chunk-GS3FJGUF.js → chunk-22VO7T2I.js} +805 -366
- package/dist/chunk-22VO7T2I.js.map +1 -0
- package/dist/{chunk-LBAHQOBI.js → chunk-4KO6D2BO.js} +38 -12
- package/dist/chunk-4KO6D2BO.js.map +1 -0
- package/dist/{chunk-3FCG7FBV.js → chunk-D5JZ7UDZ.js} +2 -2
- package/dist/{chunk-XJYR7XFV.js → chunk-GC4ATIKK.js} +1 -1
- package/dist/chunk-GC4ATIKK.js.map +1 -0
- package/dist/{chunk-BGVTIE2C.js → chunk-GRCDRKII.js} +13 -2
- package/dist/chunk-GRCDRKII.js.map +1 -0
- package/dist/{chunk-FHFTYX2Q.js → chunk-JKDNAOF5.js} +2 -2
- package/dist/{chunk-S3UZOQ5Y.js → chunk-LOW3U7JZ.js} +2 -2
- package/dist/{chunk-FC5NDO3E.js → chunk-MHPEGJHC.js} +4 -4
- package/dist/{chunk-NJC7U437.js → chunk-MK3Q6GCL.js} +33 -18
- package/dist/chunk-MK3Q6GCL.js.map +1 -0
- package/dist/{chunk-U5CHZ5M3.js → chunk-N7ZS6PEF.js} +4 -4
- package/dist/{chunk-A5S77LSE.js → chunk-QFQZ3U3X.js} +2 -2
- package/dist/chunk-QFQZ3U3X.js.map +1 -0
- package/dist/{chunk-G2GPNLSX.js → chunk-QNVBALEX.js} +4 -4
- package/dist/{chunk-OYZAPX5G.js → chunk-R226UZOI.js} +2 -2
- package/dist/{chunk-6WX7CBAR.js → chunk-SJT4OBVL.js} +20 -9
- package/dist/chunk-SJT4OBVL.js.map +1 -0
- package/dist/{chunk-HZJF4IUO.js → chunk-SUN7QLPB.js} +6 -6
- package/dist/{chunk-HZJF4IUO.js.map → chunk-SUN7QLPB.js.map} +1 -1
- package/dist/{chunk-VJ7T5WIO.js → chunk-WP3GEXKI.js} +3 -3
- package/dist/cli.js +101 -11
- package/dist/cli.js.map +1 -1
- package/dist/contract/index.d.ts +115 -8
- package/dist/contract/index.js +11 -9
- package/dist/contract/index.js.map +1 -1
- package/dist/control.js +3 -3
- package/dist/fuzz.d.ts +8 -1
- package/dist/fuzz.js +7 -6
- package/dist/fuzz.js.map +1 -1
- package/dist/hosted/index.d.ts +8 -2
- package/dist/index.d.ts +36 -10
- package/dist/index.js +18 -16
- package/dist/index.js.map +1 -1
- package/dist/multishot/index.d.ts +2 -2
- package/dist/openapi.json +1 -1
- package/dist/rl.d.ts +24 -3
- package/dist/rl.js +6 -4
- package/dist/rl.js.map +1 -1
- package/dist/{run-campaign-ZKR5MQMQ.js → run-campaign-I3JXKVAK.js} +3 -3
- package/dist/storyboard/index.js +1 -1
- package/dist/storyboard/index.js.map +1 -1
- package/dist/traces.d.ts +2 -2
- package/dist/traces.js +3 -3
- package/dist/wire/index.d.ts +77 -5
- package/dist/wire/index.js +3 -3
- package/docs/adapters-observability.md +6 -6
- package/docs/building-doctrine.md +5 -5
- package/docs/campaign-proposers.md +142 -131
- package/docs/concepts.md +49 -53
- package/docs/customer-journeys.md +80 -155
- package/docs/design/loop-taxonomy.md +27 -29
- package/docs/design.md +69 -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/insight-report.md +32 -27
- package/docs/multi-shot-optimization.md +41 -49
- package/docs/research-report-methodology.md +9 -9
- 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-A5S77LSE.js.map +0 -1
- package/dist/chunk-BGVTIE2C.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/docs/improvement-glossary.md +0 -204
- package/docs/self-improvement-map.md +0 -115
- /package/dist/{chunk-3FCG7FBV.js.map → chunk-D5JZ7UDZ.js.map} +0 -0
- /package/dist/{chunk-FHFTYX2Q.js.map → chunk-JKDNAOF5.js.map} +0 -0
- /package/dist/{chunk-S3UZOQ5Y.js.map → chunk-LOW3U7JZ.js.map} +0 -0
- /package/dist/{chunk-FC5NDO3E.js.map → chunk-MHPEGJHC.js.map} +0 -0
- /package/dist/{chunk-U5CHZ5M3.js.map → chunk-N7ZS6PEF.js.map} +0 -0
- /package/dist/{chunk-G2GPNLSX.js.map → chunk-QNVBALEX.js.map} +0 -0
- /package/dist/{chunk-OYZAPX5G.js.map → chunk-R226UZOI.js.map} +0 -0
- /package/dist/{chunk-VJ7T5WIO.js.map → chunk-WP3GEXKI.js.map} +0 -0
- /package/dist/{run-campaign-ZKR5MQMQ.js.map → run-campaign-I3JXKVAK.js.map} +0 -0
|
@@ -4,7 +4,7 @@ How every fleet agent that consumes `agent-eval` is built. Each rule is mechanic
|
|
|
4
4
|
|
|
5
5
|
## 1. Defaults must be provably reachable
|
|
6
6
|
|
|
7
|
-
Every hard-coded model id or endpoint default is verifiable against the live router. Membership in `{baseUrl}/models` is the free check; an optional 1-token probe per model confirms the router will actually serve it. A default the router cannot serve is a config bug caught before the run, not a runtime surprise that silently degrades into a stub. Backend ids are namespaced by binding: cli-bridge ids (`claude-code/*`, `kimi-code/*`, `opencode/*`) never appear as defaults in code reachable from production
|
|
7
|
+
Every hard-coded model id or endpoint default is verifiable against the live router. Membership in `{baseUrl}/models` is the free check; an optional 1-token probe per model confirms the router will actually serve it. A default the router cannot serve is a config bug caught before the run, not a runtime surprise that silently degrades into a stub. Backend ids are namespaced by binding: cli-bridge ids (`claude-code/*`, `kimi-code/*`, `opencode/*`) never appear as defaults in code reachable from production: bridge use is an explicit env opt-in, never an implicit fallback.
|
|
8
8
|
|
|
9
9
|
Enforced by: `preflightModels` (membership + optional probe) and `assertModelsServed` (gate that names every unreachable id with status + detail).
|
|
10
10
|
|
|
@@ -12,13 +12,13 @@ Enforced by: `preflightModels` (membership + optional probe) and `assertModelsSe
|
|
|
12
12
|
|
|
13
13
|
When a request fails, one direct call against the live endpoint bisects platform-versus-client before any code-level debugging begins. A 401 from the router on a `model_not_found` is the platform telling you the default is dead; a connection refused is the platform being unreachable. Establish which side is at fault with a probe first, then debug only the side that is actually broken.
|
|
14
14
|
|
|
15
|
-
Enforced by: `preflightModels({ probe: true })
|
|
15
|
+
Enforced by: `preflightModels({ probe: true })`: the probe is the platform-side bisection, carrying the router's own `error.message` back to the caller.
|
|
16
16
|
|
|
17
17
|
## 3. Agent-produced findings are hypotheses
|
|
18
18
|
|
|
19
19
|
Enumeration of candidate problems may fan out to agents, but agent output is not evidence. Truth comes from probes against ground truth, not from an agent's assertion. Every classification carries quoted evidence, and nothing unverified is merged or reported as fact. A confident-sounding agent claim with no probe behind it is a hypothesis awaiting falsification.
|
|
20
20
|
|
|
21
|
-
Enforced by: `assertRealBackend` over the resulting `RunRecord[]
|
|
21
|
+
Enforced by: `assertRealBackend` over the resulting `RunRecord[]`: an agent that claims success while the backend was never called reads as a stub, not a pass.
|
|
22
22
|
|
|
23
23
|
## 4. Experiment integrity checklist
|
|
24
24
|
|
|
@@ -30,7 +30,7 @@ Any lift or benchmark claim satisfies all of the following before it is reported
|
|
|
30
30
|
- The same scorer and the same items on both sides of any comparison.
|
|
31
31
|
- A leakage check from builder inputs into the evaluation set.
|
|
32
32
|
- Cross-family judge panels, with inter-rater reliability reported and gated.
|
|
33
|
-
- Missing evidence is never scored as zero
|
|
33
|
+
- Missing evidence is never scored as zero: fail loud over fabricate.
|
|
34
34
|
- No optional stopping: the stopping rule is fixed before the run.
|
|
35
35
|
|
|
36
36
|
Enforced by: `pairedBootstrap` (CI), `assertCrossFamily` (panel diversity), `interRaterReliability` (agreement), and `assertRealBackend` (no stub run masquerading as a result).
|
|
@@ -39,4 +39,4 @@ Enforced by: `pairedBootstrap` (CI), `assertCrossFamily` (panel diversity), `int
|
|
|
39
39
|
|
|
40
40
|
A drifted default is the symptom of a missing convention. The fix ships the convention and its guard alongside the one-line correction, so the same drift cannot recur silently. Patching the single dead id without adding the preflight gate leaves the class open; the next default rots the same way.
|
|
41
41
|
|
|
42
|
-
Enforced by: `assertModelsServed` wired into the campaign preflight
|
|
42
|
+
Enforced by: `assertModelsServed` wired into the campaign preflight: the guard that turns "this one model was dead" into "no campaign spends tokens against an unreachable default."
|
|
@@ -1,170 +1,181 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Candidate Generation and Method Comparison
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
`SurfaceProposer` generates candidate prompts or configs.
|
|
4
|
+
It does not run an agent, score output, or choose a winner.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
serialized config string, or a code/worktree surface.
|
|
6
|
+
`OptimizationMethod` runs a complete search procedure on train and selection data.
|
|
7
|
+
It returns one selected surface plus its optimization cost.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
now reserved for execution, sandbox, and router agents that actually drive
|
|
12
|
-
workers.
|
|
9
|
+
`compareOptimizationMethods` runs multiple methods, waits for every method to finish, then scores their selected surfaces on the same final test data.
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
```text
|
|
17
|
-
current surface
|
|
18
|
-
-> proposer suggests candidate surfaces
|
|
19
|
-
-> runCampaign runs each candidate on scenarios
|
|
20
|
-
-> judges score the artifacts
|
|
21
|
-
-> runOptimization picks the best candidate
|
|
22
|
-
-> runImprovementLoop re-scores on holdout and gates release
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Proposer Input
|
|
26
|
-
|
|
27
|
-
Every `SurfaceProposer.propose(ctx)` receives:
|
|
28
|
-
|
|
29
|
-
| Field | Plain meaning |
|
|
11
|
+
| API | Responsibility |
|
|
30
12
|
|---|---|
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `signal` | Abort signal for cancellation. |
|
|
37
|
-
| `report` | Optional larger analysis report. |
|
|
38
|
-
| `dataset` | Optional labeled scenario store. |
|
|
39
|
-
| `paretoParents` | Optional non-dominated surfaces from prior generations. |
|
|
13
|
+
| `SurfaceProposer` | Suggest the next candidate surface. |
|
|
14
|
+
| `runOptimization` | Run and score candidates on training scenarios. |
|
|
15
|
+
| `runImprovementLoop` | Optimize one surface and apply a release rule on separate scenarios. |
|
|
16
|
+
| `OptimizationMethod` | Adapt one complete optimization procedure for comparison. |
|
|
17
|
+
| `compareOptimizationMethods` | Compare selected surfaces on shared final test data. |
|
|
40
18
|
|
|
41
|
-
##
|
|
19
|
+
## Candidate Generators
|
|
42
20
|
|
|
43
|
-
|
|
21
|
+
Every `SurfaceProposer.propose(ctx)` receives the current surface, prior candidate scores, findings, requested candidate count, generation number, and cancellation signal.
|
|
22
|
+
It may also receive a larger analysis report, a labeled scenario store, or Pareto parents when the caller provides them.
|
|
23
|
+
|
|
24
|
+
A proposer may return a bare surface or a labeled candidate:
|
|
44
25
|
|
|
45
26
|
```ts
|
|
46
27
|
{
|
|
47
|
-
surface: 'the
|
|
48
|
-
label: '
|
|
49
|
-
rationale: '
|
|
28
|
+
surface: 'the complete new prompt or config',
|
|
29
|
+
label: 'require-citations',
|
|
30
|
+
rationale: 'three training failures omitted source references',
|
|
50
31
|
}
|
|
51
32
|
```
|
|
52
33
|
|
|
53
|
-
|
|
54
|
-
auditable, so new proposers should return `ProposedCandidate`.
|
|
55
|
-
|
|
56
|
-
## Which Proposer To Use
|
|
34
|
+
Use a labeled `ProposedCandidate` when you need the result to retain why the candidate was generated.
|
|
57
35
|
|
|
58
|
-
|
|
|
36
|
+
| Factory | Use it for | Surface |
|
|
59
37
|
|---|---|---|
|
|
60
|
-
| `gepaProposer` |
|
|
61
|
-
| `skillOptProposer` |
|
|
62
|
-
| `aceProposer` |
|
|
63
|
-
| `memoryCurationProposer` |
|
|
64
|
-
| `parameterSweepProposer` |
|
|
65
|
-
| `fapoProposer` |
|
|
66
|
-
|
|
67
|
-
## FAPO Proposer
|
|
38
|
+
| `gepaProposer` | Rewrite a prompt from prior scores and findings. | string |
|
|
39
|
+
| `skillOptProposer` | Apply bounded edits to a structured skill or runbook. | string |
|
|
40
|
+
| `aceProposer` | Append distinct lessons from findings. | string |
|
|
41
|
+
| `memoryCurationProposer` | Deduplicate and compact lessons from findings. | string |
|
|
42
|
+
| `parameterSweepProposer` | Apply declared changes to a JSON config. | JSON string |
|
|
43
|
+
| `fapoProposer` | Try prompt, parameter, and optional structural changes under one escalation policy. | caller-defined |
|
|
68
44
|
|
|
69
|
-
|
|
70
|
-
policy:
|
|
45
|
+
## Compare Complete Methods
|
|
71
46
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
3. propose one scoped change,
|
|
75
|
-
4. review the change for scope/leakage/compatibility,
|
|
76
|
-
5. measure it,
|
|
77
|
-
6. keep moving or escalate only when the cheaper level is exhausted.
|
|
78
|
-
|
|
79
|
-
The simplest useful setup is prompt plus JSON config. Structural/code edits are
|
|
80
|
-
optional and should be injected by the app or runtime layer.
|
|
47
|
+
The following call owns the shared baseline, runner, judges, directories, and three data sets.
|
|
48
|
+
Method configuration contains only settings that differ by method.
|
|
81
49
|
|
|
82
50
|
```ts
|
|
83
51
|
import {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
52
|
+
type BuiltinOptimizationMethodConfig,
|
|
53
|
+
compareOptimizationMethods,
|
|
54
|
+
gepaParetoMethod,
|
|
55
|
+
gepaReflectionMethod,
|
|
56
|
+
skillOptMethod,
|
|
88
57
|
} from '@tangle-network/agent-eval/campaign'
|
|
89
58
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
changes: [{ path: 'retrieval.k', value: 10 }],
|
|
99
|
-
},
|
|
100
|
-
],
|
|
101
|
-
}),
|
|
102
|
-
})
|
|
59
|
+
const methodConfig: BuiltinOptimizationMethodConfig<MyScenario, MyArtifact> = {
|
|
60
|
+
llm,
|
|
61
|
+
model,
|
|
62
|
+
target: 'the complete prompt or config being improved',
|
|
63
|
+
populationSize: 2,
|
|
64
|
+
maxGenerations: 3,
|
|
65
|
+
maxEpochs: 6,
|
|
66
|
+
}
|
|
103
67
|
|
|
104
|
-
await
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
68
|
+
const comparison = await compareOptimizationMethods<MyScenario, MyArtifact>({
|
|
69
|
+
methods: [
|
|
70
|
+
gepaReflectionMethod(methodConfig),
|
|
71
|
+
gepaParetoMethod(methodConfig),
|
|
72
|
+
skillOptMethod(methodConfig),
|
|
73
|
+
],
|
|
74
|
+
baselineSurface,
|
|
75
|
+
trainScenarios,
|
|
76
|
+
selectionScenarios,
|
|
77
|
+
testScenarios,
|
|
108
78
|
dispatchWithSurface,
|
|
109
79
|
judges,
|
|
110
|
-
proposer,
|
|
111
|
-
gate,
|
|
112
|
-
autoOnPromote: 'none',
|
|
113
80
|
runDir,
|
|
114
|
-
|
|
115
|
-
|
|
81
|
+
optimizationRunOptions: {
|
|
82
|
+
costCeiling: 5,
|
|
83
|
+
dispatchTimeoutMs: 60_000,
|
|
84
|
+
maxConcurrency: 4,
|
|
85
|
+
},
|
|
86
|
+
optimizationConcurrency: 2,
|
|
87
|
+
costCeiling: 2,
|
|
88
|
+
maxConcurrency: 4,
|
|
89
|
+
confidence: 0.95,
|
|
116
90
|
})
|
|
117
91
|
```
|
|
118
92
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
93
|
+
The runnable version is [`examples/compare-optimization-methods`](../examples/compare-optimization-methods/).
|
|
94
|
+
|
|
95
|
+
## Data Use
|
|
96
|
+
|
|
97
|
+
| Set | Who can read it | Purpose |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| Train | Optimization methods and candidate generators | Generate and fit candidates. |
|
|
100
|
+
| Selection | Optimization methods | Accept candidates, stop early, and select one surface per method. |
|
|
101
|
+
| Test | `compareOptimizationMethods` only | Estimate final lift and rank methods. |
|
|
102
|
+
|
|
103
|
+
All three sets must be non-empty and pairwise disjoint by scenario ID.
|
|
104
|
+
Test must contain at least two scenarios.
|
|
105
|
+
Two is only an API minimum; use enough scenarios to detect the effect size that matters for your product.
|
|
106
|
+
|
|
107
|
+
Each method receives independent copies of train and selection scenarios.
|
|
108
|
+
The final test set is absent from `OptimizationMethodInput`.
|
|
109
|
+
Every method finishes before the first test call starts.
|
|
110
|
+
When `optimizationConcurrency` is greater than one, the shared runner and judges must support concurrent calls.
|
|
111
|
+
|
|
112
|
+
## Execution And Cost
|
|
113
|
+
|
|
114
|
+
`optimizationConcurrency` controls how many methods run at once.
|
|
115
|
+
`optimizationRunOptions.maxConcurrency` controls scenario calls inside each method.
|
|
116
|
+
Top-level `maxConcurrency` controls scenario calls during final test scoring.
|
|
122
117
|
|
|
123
|
-
|
|
118
|
+
`optimizationRunOptions.costCeiling` is a separate limit for each method.
|
|
119
|
+
Top-level `costCeiling` is one shared limit across baseline and selected-surface scoring on final test.
|
|
120
|
+
|
|
121
|
+
The result reports three cost objects:
|
|
124
122
|
|
|
125
123
|
```ts
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
} from '@tangle-network/agent-eval/campaign'
|
|
124
|
+
comparison.optimizationCost
|
|
125
|
+
comparison.testCost
|
|
126
|
+
comparison.totalCost
|
|
127
|
+
```
|
|
131
128
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
runDir,
|
|
151
|
-
})
|
|
129
|
+
Each object contains `totalCostUsd`, `accountingComplete`, and `incompleteReasons`.
|
|
130
|
+
An unknown provider charge therefore cannot appear as a trustworthy zero-dollar total.
|
|
131
|
+
Cost breaks a lift tie only when every method in that tied group reports complete accounting.
|
|
132
|
+
|
|
133
|
+
## Read The Result
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
for (const method of comparison.scores) {
|
|
137
|
+
console.log({
|
|
138
|
+
rank: method.rank,
|
|
139
|
+
name: method.name,
|
|
140
|
+
lift: method.lift,
|
|
141
|
+
interval: method.liftCi,
|
|
142
|
+
scenarios: method.scenarioScores,
|
|
143
|
+
optimizationCostUsd: method.optimizationCost.totalCostUsd,
|
|
144
|
+
costComplete: method.optimizationCost.accountingComplete,
|
|
145
|
+
})
|
|
146
|
+
}
|
|
152
147
|
```
|
|
153
148
|
|
|
154
|
-
|
|
149
|
+
`rank` orders methods by estimated lift, then by cost only when every method with that lift has complete cost accounting.
|
|
150
|
+
It does not mean the higher-ranked method is conclusively better.
|
|
151
|
+
Read `liftCi` and `comparison.pairwise[].favored` before making that claim.
|
|
152
|
+
`scenarioScores` contains the paired values used to compute each method's result.
|
|
153
|
+
|
|
154
|
+
Repetitions are averaged within each test scenario before scenarios are resampled.
|
|
155
|
+
The intervals assume scenarios are the independent sampling units.
|
|
156
|
+
|
|
157
|
+
`confidence: 0.95` applies to the complete family of method-vs-baseline and possible method-vs-method contrasts.
|
|
158
|
+
The implementation adjusts each interval for that family and raises the default resample count when more methods require finer interval tails.
|
|
159
|
+
An explicit resample count that is too small is rejected before optimization starts.
|
|
160
|
+
|
|
161
|
+
The final test data is spent when this function ranks methods.
|
|
162
|
+
If you choose a method from this result and later claim its deployed effect, confirm that claim on new data that was not used for this ranking.
|
|
163
|
+
|
|
164
|
+
## FAPO
|
|
165
|
+
|
|
166
|
+
`fapoProposer` can move from prompt edits to declared parameter edits and then to an injected structural proposer.
|
|
167
|
+
Every level must accept and return the same surface representation.
|
|
155
168
|
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
a type-level firewall.
|
|
159
|
-
- Do not call FAPO a prompt-only optimizer. Its main value is evidence-based
|
|
160
|
-
escalation beyond prompt edits.
|
|
161
|
-
- Do not put Claude Code or sandbox-specific code in `agent-eval`. Structural
|
|
162
|
-
code generation should be supplied as an injected `SurfaceProposer` from the
|
|
163
|
-
runtime/app layer.
|
|
169
|
+
`agent-eval` does not generate repository code itself.
|
|
170
|
+
Pass a code-capable `structuralProposer` from your runtime or application when structural edits are part of the comparison.
|
|
164
171
|
|
|
165
|
-
|
|
172
|
+
Use `fapoEscalationMethod(config)` to compare the complete FAPO procedure with other methods.
|
|
166
173
|
|
|
167
|
-
|
|
174
|
+
## Common Errors
|
|
168
175
|
|
|
169
|
-
|
|
170
|
-
|
|
176
|
+
- Do not pass a raw `SurfaceProposer` to `compareOptimizationMethods`.
|
|
177
|
+
- Do not let a custom `OptimizationMethod` load final test rows from another source.
|
|
178
|
+
- Do not compare methods with different runners, judges, or final test scenarios.
|
|
179
|
+
- Do not read `method.optimizationCost` as total comparison cost.
|
|
180
|
+
- Do not report a dollar total as complete when `accountingComplete` is false.
|
|
181
|
+
- Do not reuse the final test set for repeated method selection and continue calling it untouched.
|
package/docs/concepts.md
CHANGED
|
@@ -1,50 +1,47 @@
|
|
|
1
1
|
# Concepts
|
|
2
2
|
|
|
3
|
-
`agent-eval`
|
|
4
|
-
replayed, be optimized, or be promoted.
|
|
3
|
+
`agent-eval` records agent runs, scores their outputs, compares variants, and applies caller-defined release rules.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
connected, or the answer lacks required sources. The package gives products a
|
|
9
|
-
shared way to record runs, check outcomes, classify failures, compare variants,
|
|
10
|
-
and make release decisions.
|
|
5
|
+
A model can say a task is complete while the build fails, a browser flow is broken, an integration is disconnected, or required sources are missing.
|
|
6
|
+
This package lets code, model judges, and human feedback check those outcomes through the same run format.
|
|
11
7
|
|
|
12
8
|
## The top-level functions
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
Start with `/contract` and `defineAgentEval()` for a new integration.
|
|
11
|
+
Use the lower-level functions when you need direct control over execution, storage, or statistics.
|
|
16
12
|
|
|
17
13
|
| Function | When to call it | What you give it | What you get back |
|
|
18
14
|
|---|---|---|---|
|
|
19
|
-
| **`defineAgentEval()`** | You have scenarios, an agent, a judge, and a baseline surface, and you want one object you can score or improve. | scenarios, agent, judge, baseline surface | `{ evaluate(), improve() }` where `evaluate()` returns a campaign result and `improve()` returns a
|
|
20
|
-
| **`selfImprove()`** | You
|
|
15
|
+
| **`defineAgentEval()`** | You have scenarios, an agent, a judge, and a baseline surface, and you want one object you can score or improve. | scenarios, agent, judge, baseline surface | `{ evaluate(), improve() }` where `evaluate()` returns a campaign result and `improve()` returns a report |
|
|
16
|
+
| **`selfImprove()`** | You want candidate generation, scoring, and a release decision in one call. | scenarios, agent, judge, baseline surface | report, ship/hold decision, winner surface |
|
|
21
17
|
| **`loadEvalFixtureScenarios()`** | You want agents to add evals as folders with `PROMPT.md`, checks, and starter files. | `evals/<name>/PROMPT.md + EVAL.ts + package.json` | `Scenario[]` that runs through `runCampaign`; pair with `planEvalFixtureRun()` before spending tokens |
|
|
22
|
-
| **`analyzeRuns()`** | You have
|
|
23
|
-
| **Intake adapters** (`fromFeedbackTable`, `fromOtelSpans`) | Your data isn't already in `RunRecord` shape
|
|
18
|
+
| **`analyzeRuns()`** | You have existing runs and do not need to invoke an agent. | `RunRecord[]` and options | `InsightReport` |
|
|
19
|
+
| **Intake adapters** (`fromFeedbackTable`, `fromOtelSpans`) | Your data isn't already in `RunRecord` shape: it's in Obsidian, Sheets, an OTel collector, etc. | source-specific input | `RunRecord[]` ready to pipe into `analyzeRuns()` |
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
entry points. See [`customer-journeys.md`](./customer-journeys.md) for the
|
|
27
|
-
runnable walkthroughs.
|
|
21
|
+
See [`customer-journeys.md`](./customer-journeys.md) for runnable paths from existing logs, human ratings, and a callable agent.
|
|
28
22
|
|
|
29
|
-
|
|
23
|
+
`analyzeRuns()` and the high-level contract return the same `InsightReport` shape.
|
|
24
|
+
It contains score distributions, paired lift intervals, judge agreement, cost, failure clusters, contamination checks, outcome correlation, and recommendations.
|
|
25
|
+
[`insight-report.md`](./insight-report.md) defines every field.
|
|
30
26
|
|
|
31
|
-
##
|
|
27
|
+
## Package Boundary
|
|
32
28
|
|
|
33
|
-
`agent-
|
|
29
|
+
`agent-runtime` and `agent-knowledge` may import `agent-eval`.
|
|
30
|
+
`agent-eval` must not import either package.
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
Run records, scenarios, judge scores, statistics, and release decisions belong here because they work without an agent runtime.
|
|
33
|
+
Agent sessions, worker coordination, sandbox execution, and runtime-specific profiles belong in `agent-runtime`.
|
|
36
34
|
|
|
37
35
|
## Main Objects
|
|
38
36
|
|
|
39
37
|
| Thing | What it is | One-line example |
|
|
40
38
|
|---|---|---|
|
|
41
39
|
| **Judge** | A function that scores one piece of output. | "Did this scaffold implement async fetching?" |
|
|
42
|
-
| **Rubric** | The recipe a judge uses
|
|
40
|
+
| **Rubric** | The recipe a judge uses: what to score on, with what weights. | "Score on buyer_quality (0.5), voice (0.3), signal (0.2)." |
|
|
43
41
|
| **Verifier** | A pipeline of judges run in order, with dependencies. | "install → typecheck → build → semantic" |
|
|
44
42
|
| **Feedback trajectory** | A multi-shot record of attempts, approvals, rejections, edits, metrics, and policy outcomes. | "draft → user rejects → revised draft → approved → measured" |
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
traces, datasets, control runtime, optimizers, statistics, and reports.
|
|
44
|
+
Traces, datasets, optimization, statistics, and reports build on these objects.
|
|
48
45
|
|
|
49
46
|
When the thing being evaluated is an agent that should keep working, use
|
|
50
47
|
[`runAgentControlLoop`](./control-runtime.md). It turns validators into a
|
|
@@ -52,30 +49,28 @@ runtime loop: observe typed state, validate it, decide the next action, act,
|
|
|
52
49
|
and repeat until the task passes, blocks, times out, spends too much, or stops
|
|
53
50
|
making progress.
|
|
54
51
|
|
|
55
|
-
When normal agent usage should become reusable training
|
|
52
|
+
When normal agent usage should become reusable training or eval data, use
|
|
56
53
|
[`FeedbackTrajectory`](./feedback-trajectories.md). It captures approvals,
|
|
57
54
|
rejections, edits, option choices, metrics, and policy blocks as portable data
|
|
58
55
|
that can seed memory, replay scenarios, and optimization.
|
|
59
56
|
|
|
60
|
-
##
|
|
57
|
+
## Terms
|
|
61
58
|
|
|
62
59
|
| Term | Plain English |
|
|
63
60
|
|---|---|
|
|
64
61
|
| **Artifact** | The thing being judged. Often a workdir of files, sometimes a string of text. |
|
|
65
|
-
| **Snapshot** | A frozen view of an artifact (every file path → content).
|
|
62
|
+
| **Snapshot** | A frozen view of an artifact (every file path → content). This is the input the judge reads. |
|
|
66
63
|
| **Harness** | A description of *how to run* the artifact: setup command, test command, working dir, timeout. |
|
|
67
|
-
| **Sandbox driver** |
|
|
64
|
+
| **Sandbox driver** | Executes commands inside the harness, using a local subprocess or remote container. |
|
|
68
65
|
| **Layer** | One stage of a verifier pipeline (install, typecheck, build, semantic, …). |
|
|
69
|
-
| **Finding** | A specific issue a judge found
|
|
66
|
+
| **Finding** | A specific issue a judge found: file, line, severity, message. |
|
|
70
67
|
| **Trace store** | The append-only log of every span/event during a run. Replay = read this back. |
|
|
71
68
|
| **Composite score** | A 0..1 number combining all dimensions. The single number you gate on. |
|
|
72
69
|
| **Rubric version** | A stable hash of the rubric. Scores from different rubric versions are not comparable. |
|
|
73
|
-
| **Muffled gate** | A check that should fail loud but silently passes (e.g. `command || true`). The most expensive bug class in this codebase. |
|
|
74
70
|
|
|
75
71
|
## The feedback trajectory loop
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
workflow, not a separate labeling UI:
|
|
73
|
+
Normal review activity can provide labels without a separate labeling interface:
|
|
79
74
|
|
|
80
75
|
```text
|
|
81
76
|
agent proposes -> user approves/rejects/edits/selects -> agent revises -> outcome is measured
|
|
@@ -103,30 +98,31 @@ L1 app-build Does the artifact build / typecheck / test?
|
|
|
103
98
|
│
|
|
104
99
|
▼
|
|
105
100
|
L2 app-runtime Does the artifact actually run end-to-end?
|
|
106
|
-
(Dynamic signal
|
|
101
|
+
(Dynamic signal: only worth checking if L1 passed.)
|
|
107
102
|
```
|
|
108
103
|
|
|
109
104
|
`BuilderSession` orchestrates this. It opens at `startChat`, runs the build at `ship`, runs the runtime check at `runAppScenario`. Each layer emits a trace span. Composite score aggregates them with `scoreProject`.
|
|
110
105
|
|
|
111
106
|
Why three? Because each catches a different failure mode:
|
|
112
|
-
- L0 misses
|
|
113
|
-
- L1 misses
|
|
114
|
-
- L2 misses
|
|
107
|
+
- L0 misses: agent crashed mid-generation, you have a half-written file.
|
|
108
|
+
- L1 misses: files exist but typecheck fails. LLM judges can't reliably catch this.
|
|
109
|
+
- L2 misses: code compiles but does the wrong thing at runtime.
|
|
115
110
|
|
|
116
111
|
If you only check one layer, you ship the bugs that the other two layers would have caught.
|
|
117
112
|
|
|
118
113
|
## How rubrics work
|
|
119
114
|
|
|
120
115
|
A rubric describes:
|
|
121
|
-
1. **Dimensions
|
|
122
|
-
2. **Weights
|
|
123
|
-
3. **Failure modes
|
|
124
|
-
4. **Wins
|
|
125
|
-
5. **System prompt
|
|
116
|
+
1. **Dimensions**: the axes you score on (e.g. `buyer_quality`, `voice`, `signal`).
|
|
117
|
+
2. **Weights**: how to combine dimensions into a composite (`0.5 * buyer_quality + 0.3 * voice + 0.2 * signal`).
|
|
118
|
+
3. **Failure modes**: named patterns the judge looks for ("ai-cadence", "vague-claim").
|
|
119
|
+
4. **Wins**: named positive patterns ("specific-component", "earned-detail").
|
|
120
|
+
5. **System prompt**: what to tell the judging LLM about the persona and the task.
|
|
126
121
|
|
|
127
|
-
Built-in rubrics ship in `src/wire/rubrics.ts
|
|
122
|
+
Built-in rubrics ship in `src/wire/rubrics.ts`, including `anti-slop` for technical-buyer voice.
|
|
123
|
+
You can also pass the same rubric shape inline at the call site.
|
|
128
124
|
|
|
129
|
-
A rubric is plain data. The hash of that data is the `rubricVersion`. Two scores are only comparable if they used the same `rubricVersion
|
|
125
|
+
A rubric is plain data. The hash of that data is the `rubricVersion`. Two scores are only comparable if they used the same `rubricVersion`: change the rubric and you start a new comparison series.
|
|
130
126
|
|
|
131
127
|
## How verifiers work
|
|
132
128
|
|
|
@@ -141,14 +137,14 @@ const verifier = new MultiLayerVerifier([
|
|
|
141
137
|
])
|
|
142
138
|
|
|
143
139
|
const report = await verifier.run({ env: { runner, workdir, ... } })
|
|
144
|
-
report.allPass // boolean
|
|
145
|
-
report.blendedScore // 0..1
|
|
140
|
+
report.allPass // boolean: every layer passed
|
|
141
|
+
report.blendedScore // 0..1: weighted aggregate
|
|
146
142
|
report.layers // per-layer status, findings, duration
|
|
147
143
|
```
|
|
148
144
|
|
|
149
145
|
Two rules that will save you bugs:
|
|
150
146
|
|
|
151
|
-
1. **Run both gates.** Build gates catch code that doesn't compile; structural assertions catch missing files. Run both unconditionally
|
|
147
|
+
1. **Run both gates.** Build gates catch code that doesn't compile; structural assertions catch missing files. Run both unconditionally: they catch orthogonal failures.
|
|
152
148
|
|
|
153
149
|
2. **Pair LLM judges with build outcomes.** An LLM judge will rate non-compiling code as "looks right" (0.8). Always short-circuit on `buildOutcome.passed === false` before any LLM judging.
|
|
154
150
|
|
|
@@ -159,7 +155,7 @@ Two questions to answer before trusting any LLM judge:
|
|
|
159
155
|
1. **Does it agree with humans?** `calibrateJudge(golden, candidate)` reports Pearson, MAE, integer-rounded κ, and worst-N miscalibrations vs a human golden set.
|
|
160
156
|
2. **Does it agree with itself / other judges?** `continuousAgreement(scores)` and `calibrateJudgeContinuous(golden, candidate)` report κ_w + ICC(2,1) + Pearson + Spearman with bootstrap 95% CIs on the raw [0,1] scores.
|
|
161
157
|
|
|
162
|
-
Why two κ flavours: the original `calibrateJudge` rounds scores to ints before computing κ. For fine-grained judges that loses information
|
|
158
|
+
Why two κ flavours: the original `calibrateJudge` rounds scores to ints before computing κ. For fine-grained judges that loses information: 0.78 vs 0.81 both round to "1" and look perfectly agreed. Use `calibrateJudgeContinuous` (or `continuousAgreement` for N≥2 raters) when scores are continuous. ICC(2,1) catches systematic bias that Pearson misses: if judge B scores 2× judge A, Pearson stays ≈ 1 while ICC drops: that's the signal.
|
|
163
159
|
|
|
164
160
|
Bias probes (`positionalBias`, `verbosityBias`, `selfPreference`) cover the orthogonal failure modes: position-dependent scoring, length-correlated scoring, and judge-prefers-its-own-family.
|
|
165
161
|
|
|
@@ -178,7 +174,7 @@ builder-session [span]
|
|
|
178
174
|
└── scenario.run [span]
|
|
179
175
|
```
|
|
180
176
|
|
|
181
|
-
Spans are append-only and have stable ids
|
|
177
|
+
Spans are append-only and have stable ids: replay is reading the same store back. OTLP export ships them out for distributed tracing.
|
|
182
178
|
|
|
183
179
|
You usually should not build this tree by hand. Product runtimes,
|
|
184
180
|
`runAgentControlLoop`, harnesses, and verifiers should emit it while they run.
|
|
@@ -187,12 +183,12 @@ release decision.
|
|
|
187
183
|
|
|
188
184
|
## Where to go next
|
|
189
185
|
|
|
190
|
-
- **
|
|
191
|
-
- **
|
|
192
|
-
- **Need the
|
|
193
|
-
- **
|
|
194
|
-
- **
|
|
195
|
-
- **
|
|
186
|
+
- **Choosing a candidate-generation method?** Read [campaign-proposers.md](./campaign-proposers.md) for the available methods, their inputs, and runnable composition examples.
|
|
187
|
+
- **Choosing a `run*` function or grading produced state?** Read [eval-surface-map.md](./eval-surface-map.md) for a use-case table and complete grading composition.
|
|
188
|
+
- **Need the feature map?** Read [feature-guide.md](./feature-guide.md) for integration patterns and operational limits.
|
|
189
|
+
- **Scoring a string from another language?** Read [wire-protocol.md](./wire-protocol.md) for the HTTP/RPC interface.
|
|
190
|
+
- **Building a driver and worker loop?** Read [control-runtime.md](./control-runtime.md) for coding, browser, computer-use, and research patterns.
|
|
191
|
+
- **Turning review feedback into reusable data?** Read [feedback-trajectories.md](./feedback-trajectories.md) for dataset, optimization, and preference-memory examples.
|
|
196
192
|
- **Building a code-generator eval?** → Start with `BuilderSession`, `SandboxHarness`, and `MultiLayerVerifier`.
|
|
197
193
|
- **Multi-layer verifier?** → Use [control-runtime.md](./control-runtime.md) and `MultiLayerVerifier` for ordered gates with dependencies.
|
|
198
194
|
- **Adding a new judge or rubric?** → `src/wire/rubrics.ts` for the cross-language path; `src/anti-slop.ts` and `src/judges.ts` for the in-process path.
|