@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
|
@@ -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."
|
package/docs/concepts.md
CHANGED
|
@@ -16,17 +16,17 @@ can; drop to the raw functions when you need lower-level control.
|
|
|
16
16
|
|
|
17
17
|
| Function | When to call it | What you give it | What you get back |
|
|
18
18
|
|---|---|---|---|
|
|
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 have a closed loop
|
|
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 report |
|
|
20
|
+
| **`selfImprove()`** | You have a closed loop: scenarios, judge, agent in hand, and you want the substrate to propose better candidates + gate them. | scenarios, agent, judge, baseline surface | `SelfImproveResult.insight: InsightReport` + ship/hold verdict + winner surface |
|
|
21
21
|
| **`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
22
|
| **`analyzeRuns()`** | You have observed runs (production traces, an approve/reject corpus, a CSV gold set) and want the same rigor packet without invoking an agent. | `RunRecord[]` + optional flags | `InsightReport` |
|
|
23
|
-
| **Intake adapters** (`fromFeedbackTable`, `fromOtelSpans`) | Your data isn't already in `RunRecord` shape
|
|
23
|
+
| **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
24
|
|
|
25
|
-
The customer maturity stages
|
|
25
|
+
The customer maturity stages: logs only → ratings → closed loop: map to these
|
|
26
26
|
entry points. See [`customer-journeys.md`](./customer-journeys.md) for the
|
|
27
27
|
runnable walkthroughs.
|
|
28
28
|
|
|
29
|
-
The shape of the answer
|
|
29
|
+
The shape of the answer: `InsightReport`: is identical across all three paths. Distributional summary, paired-bootstrap lift CI, judge stats, inter-rater agreement, cost-quality Pareto, failure clusters, contamination check, outcome correlation, release axes, and a ranked recommendations array. Walked through section-by-section in [`insight-report.md`](./insight-report.md).
|
|
30
30
|
|
|
31
31
|
## The layering rule
|
|
32
32
|
|
|
@@ -39,7 +39,7 @@ The test: *does this concept make sense WITHOUT a running agent loop?* If yes, i
|
|
|
39
39
|
| Thing | What it is | One-line example |
|
|
40
40
|
|---|---|---|
|
|
41
41
|
| **Judge** | A function that scores one piece of output. | "Did this scaffold implement async fetching?" |
|
|
42
|
-
| **Rubric** | The recipe a judge uses
|
|
42
|
+
| **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
43
|
| **Verifier** | A pipeline of judges run in order, with dependencies. | "install → typecheck → build → semantic" |
|
|
44
44
|
| **Feedback trajectory** | A multi-shot record of attempts, approvals, rejections, edits, metrics, and policy outcomes. | "draft → user rejects → revised draft → approved → measured" |
|
|
45
45
|
|
|
@@ -66,7 +66,7 @@ that can seed memory, replay scenarios, and optimization.
|
|
|
66
66
|
| **Harness** | A description of *how to run* the artifact: setup command, test command, working dir, timeout. |
|
|
67
67
|
| **Sandbox driver** | The thing that actually executes commands inside the harness. Local subprocess, or remote container. |
|
|
68
68
|
| **Layer** | One stage of a verifier pipeline (install, typecheck, build, semantic, …). |
|
|
69
|
-
| **Finding** | A specific issue a judge found
|
|
69
|
+
| **Finding** | A specific issue a judge found: file, line, severity, message. |
|
|
70
70
|
| **Trace store** | The append-only log of every span/event during a run. Replay = read this back. |
|
|
71
71
|
| **Composite score** | A 0..1 number combining all dimensions. The single number you gate on. |
|
|
72
72
|
| **Rubric version** | A stable hash of the rubric. Scores from different rubric versions are not comparable. |
|
|
@@ -103,30 +103,30 @@ L1 app-build Does the artifact build / typecheck / test?
|
|
|
103
103
|
│
|
|
104
104
|
▼
|
|
105
105
|
L2 app-runtime Does the artifact actually run end-to-end?
|
|
106
|
-
(Dynamic signal
|
|
106
|
+
(Dynamic signal: only worth checking if L1 passed.)
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
`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
110
|
|
|
111
111
|
Why three? Because each catches a different failure mode:
|
|
112
|
-
- L0 misses
|
|
113
|
-
- L1 misses
|
|
114
|
-
- L2 misses
|
|
112
|
+
- L0 misses: agent crashed mid-generation, you have a half-written file.
|
|
113
|
+
- L1 misses: files exist but typecheck fails. LLM judges can't reliably catch this.
|
|
114
|
+
- L2 misses: code compiles but does the wrong thing at runtime.
|
|
115
115
|
|
|
116
116
|
If you only check one layer, you ship the bugs that the other two layers would have caught.
|
|
117
117
|
|
|
118
118
|
## How rubrics work
|
|
119
119
|
|
|
120
120
|
A rubric describes:
|
|
121
|
-
1. **Dimensions
|
|
122
|
-
2. **Weights
|
|
123
|
-
3. **Failure modes
|
|
124
|
-
4. **Wins
|
|
125
|
-
5. **System prompt
|
|
121
|
+
1. **Dimensions**: the axes you score on (e.g. `buyer_quality`, `voice`, `signal`).
|
|
122
|
+
2. **Weights**: how to combine dimensions into a composite (`0.5 * buyer_quality + 0.3 * voice + 0.2 * signal`).
|
|
123
|
+
3. **Failure modes**: named patterns the judge looks for ("ai-cadence", "vague-claim").
|
|
124
|
+
4. **Wins**: named positive patterns ("specific-component", "earned-detail").
|
|
125
|
+
5. **System prompt**: what to tell the judging LLM about the persona and the task.
|
|
126
126
|
|
|
127
|
-
Built-in rubrics ship in `src/wire/rubrics.ts` (e.g. `anti-slop` for technical-buyer voice). You can also pass a rubric inline
|
|
127
|
+
Built-in rubrics ship in `src/wire/rubrics.ts` (e.g. `anti-slop` for technical-buyer voice). You can also pass a rubric inline: the same shape, just defined at the call site.
|
|
128
128
|
|
|
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
|
|
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`: change the rubric and you start a new comparison series.
|
|
130
130
|
|
|
131
131
|
## How verifiers work
|
|
132
132
|
|
|
@@ -141,14 +141,14 @@ const verifier = new MultiLayerVerifier([
|
|
|
141
141
|
])
|
|
142
142
|
|
|
143
143
|
const report = await verifier.run({ env: { runner, workdir, ... } })
|
|
144
|
-
report.allPass // boolean
|
|
145
|
-
report.blendedScore // 0..1
|
|
144
|
+
report.allPass // boolean: every layer passed
|
|
145
|
+
report.blendedScore // 0..1: weighted aggregate
|
|
146
146
|
report.layers // per-layer status, findings, duration
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
Two rules that will save you bugs:
|
|
150
150
|
|
|
151
|
-
1. **Run both gates.** Build gates catch code that doesn't compile; structural assertions catch missing files. Run both unconditionally
|
|
151
|
+
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
152
|
|
|
153
153
|
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
154
|
|
|
@@ -159,7 +159,7 @@ Two questions to answer before trusting any LLM judge:
|
|
|
159
159
|
1. **Does it agree with humans?** `calibrateJudge(golden, candidate)` reports Pearson, MAE, integer-rounded κ, and worst-N miscalibrations vs a human golden set.
|
|
160
160
|
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
161
|
|
|
162
|
-
Why two κ flavours: the original `calibrateJudge` rounds scores to ints before computing κ. For fine-grained judges that loses information
|
|
162
|
+
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
163
|
|
|
164
164
|
Bias probes (`positionalBias`, `verbosityBias`, `selfPreference`) cover the orthogonal failure modes: position-dependent scoring, length-correlated scoring, and judge-prefers-its-own-family.
|
|
165
165
|
|
|
@@ -178,7 +178,7 @@ builder-session [span]
|
|
|
178
178
|
└── scenario.run [span]
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
Spans are append-only and have stable ids
|
|
181
|
+
Spans are append-only and have stable ids: replay is reading the same store back. OTLP export ships them out for distributed tracing.
|
|
182
182
|
|
|
183
183
|
You usually should not build this tree by hand. Product runtimes,
|
|
184
184
|
`runAgentControlLoop`, harnesses, and verifiers should emit it while they run.
|
|
@@ -187,12 +187,12 @@ release decision.
|
|
|
187
187
|
|
|
188
188
|
## Where to go next
|
|
189
189
|
|
|
190
|
-
- **Confused by "GEPA / HALO / trace analysis / proposers everywhere"?** → [self-improvement-map.md](./self-improvement-map.md)
|
|
191
|
-
- **Which `run*` primitive do I use, and how do I grade produced state?** → [eval-surface-map.md](./eval-surface-map.md)
|
|
192
|
-
- **Need the layman feature map?** → [feature-guide.md](./feature-guide.md)
|
|
193
|
-
- **Just want to score a string against a rubric?** → [wire-protocol.md](./wire-protocol.md)
|
|
194
|
-
- **Need a reusable driver/worker/evaluator loop?** → [control-runtime.md](./control-runtime.md)
|
|
195
|
-
- **Want review feedback to become eval/optimization data?** → [feedback-trajectories.md](./feedback-trajectories.md)
|
|
190
|
+
- **Confused by "GEPA / HALO / trace analysis / proposers everywhere"?** → [self-improvement-map.md](./self-improvement-map.md): one loop, four roles, the proposer catalog (production vs bench-only), and why `gepa-refine` is the same loop on a test bench.
|
|
191
|
+
- **Which `run*` primitive do I use, and how do I grade produced state?** → [eval-surface-map.md](./eval-surface-map.md): the campaign/matrix/optimization/gate primitives as a pick-by-"use-when" table, plus the produced-state grading composition (verifyCompletion-as-judge: there is no persona-dispatch wrapper) and the in-band body contract.
|
|
192
|
+
- **Need the layman feature map?** → [feature-guide.md](./feature-guide.md): what each primitive does, when to use it, integration patterns, and guardrails.
|
|
193
|
+
- **Just want to score a string against a rubric?** → [wire-protocol.md](./wire-protocol.md): HTTP/RPC interface, pluggable from any language.
|
|
194
|
+
- **Need a reusable driver/worker/evaluator loop?** → [control-runtime.md](./control-runtime.md): generic runtime plus coding, browser, computer-use, and research integration patterns.
|
|
195
|
+
- **Want review feedback to become eval/optimization data?** → [feedback-trajectories.md](./feedback-trajectories.md): turn feedback into datasets, optimizer rows, and preference memory.
|
|
196
196
|
- **Building a code-generator eval?** → Start with `BuilderSession`, `SandboxHarness`, and `MultiLayerVerifier`.
|
|
197
197
|
- **Multi-layer verifier?** → Use [control-runtime.md](./control-runtime.md) and `MultiLayerVerifier` for ordered gates with dependencies.
|
|
198
198
|
- **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.
|
|
@@ -1,210 +1,135 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Adoption Paths
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Choose the path that matches the data and code you already have.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
| Starting point | API | Result |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| Completed OpenTelemetry spans | `fromOtelSpans()` and `analyzeRuns()` | Failure, score, token, and cost summaries |
|
|
8
|
+
| Human ratings | `fromFeedbackTable()` and `analyzeRuns()` | Reviewer agreement, largest disagreements, and score distributions |
|
|
9
|
+
| A runnable agent, scenarios, and a judge | `defineAgentEval()` | Repeatable evaluation and optional prompt improvement |
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
3. **Scenarios, judge, agent — full closed loop** → [Closed-loop journey](#3-closed-loop-journey-selfimprove-quickstart)
|
|
11
|
+
All three paths return plain objects and run in your process.
|
|
12
|
+
They call a remote service only when you pass a model client, exporter, or hosted endpoint.
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
## 1. Analyze Existing Traces
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## 1. Production traces journey — `customer-otel-traces`
|
|
16
|
-
|
|
17
|
-
**The customer:** an agentic GTM-as-a-service company. Multiple agent steps in prod (social media posting, image generation, translation). OTel observability piped to their collector. Doesn't run formal evals. CTO hand-rolled their tracing.
|
|
18
|
-
|
|
19
|
-
**The frustration:** "Which step is unreliable? What's our cost-quality profile? Where do we fix next?" They have the data; they don't have the answer.
|
|
20
|
-
|
|
21
|
-
**What they need from agent-eval:** day-1 analysis of their existing logs. No scenarios, no judges, no closed loop. Just turn the trace stream into a decision packet.
|
|
22
|
-
|
|
23
|
-
### The code
|
|
16
|
+
Use this path when the agent already emits OpenTelemetry spans and you do not want to run it again.
|
|
24
17
|
|
|
25
18
|
```ts
|
|
26
19
|
import { analyzeRuns, fromOtelSpans } from '@tangle-network/agent-eval/contract'
|
|
27
20
|
|
|
28
|
-
const runs = fromOtelSpans({ spans:
|
|
21
|
+
const runs = fromOtelSpans({ spans: yourOtelSpans })
|
|
29
22
|
const report = await analyzeRuns({ runs })
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// report.recommendations → top-3 actions
|
|
24
|
+
console.log(report.composite)
|
|
25
|
+
console.log(report.costQuality)
|
|
26
|
+
console.log(report.recommendations)
|
|
35
27
|
```
|
|
36
28
|
|
|
37
|
-
|
|
29
|
+
`fromOtelSpans()` groups spans by run ID and reads recorded scores, failures, model IDs, token counts, and costs.
|
|
30
|
+
It does not infer values that are missing from the spans.
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
Runs analyzed: 40
|
|
41
|
-
Composite mean: 0.721 (p50: 0.717, p95: 0.925, stddev: 0.210)
|
|
42
|
-
Cost mean: $0.103 (p95: $0.131)
|
|
43
|
-
|
|
44
|
-
── Failures ──
|
|
45
|
-
6 runs with status=ERROR or failureMode set:
|
|
46
|
-
tool.search (3x)
|
|
47
|
-
agent.turn (3x)
|
|
32
|
+
Add model-based failure clustering only when you need it:
|
|
48
33
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
34
|
+
```ts
|
|
35
|
+
const report = await analyzeRuns({
|
|
36
|
+
runs,
|
|
37
|
+
analyst,
|
|
38
|
+
})
|
|
52
39
|
|
|
53
|
-
|
|
54
|
-
[medium] expand-corpus — Mean composite 0.721 has room
|
|
40
|
+
console.log(report.failureClusters)
|
|
55
41
|
```
|
|
56
42
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
1. Wire an `AnalystRegistry` to cluster the 6 failures by root cause via LLM analysis.
|
|
60
|
-
2. Add `outcomeSignal` once they have downstream conversion / engagement / post-engagement data, and the report fits a reward model showing whether their score predicts the customer outcome.
|
|
61
|
-
3. Once they identify a step worth optimizing (translation, say), graduate to journey #3 — wrap that step as an `agent(surface, scenario)` and call `defineAgentEval()`.
|
|
62
|
-
|
|
63
|
-
**Runnable:** [`examples/customer-otel-traces/`](../examples/customer-otel-traces/)
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## 2. Feedback corpus journey — `customer-feedback-loop`
|
|
43
|
+
Runnable example: [`examples/customer-otel-traces`](../examples/customer-otel-traces/)
|
|
68
44
|
|
|
69
|
-
|
|
45
|
+
## 2. Analyze Human Ratings
|
|
70
46
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
**What they need from agent-eval:** turn the approve/reject corpus into actionable signal:
|
|
74
|
-
- Where do reviewers disagree? (triage list)
|
|
75
|
-
- Can we synthesize each reviewer's taste into an LLM judge? (auto-grade)
|
|
76
|
-
- Does the taste actually predict downstream engagement? (close the loop)
|
|
77
|
-
|
|
78
|
-
### The code
|
|
47
|
+
Use this path when multiple people score the same outputs in a database, spreadsheet, or review tool.
|
|
79
48
|
|
|
80
49
|
```ts
|
|
81
50
|
import { analyzeRuns, fromFeedbackTable } from '@tangle-network/agent-eval/contract'
|
|
82
51
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
52
|
+
const ratings = [
|
|
53
|
+
{ runId: 'answer-1', rater: 'alice', rating: true },
|
|
54
|
+
{ runId: 'answer-1', rater: 'bob', rating: false },
|
|
55
|
+
{ runId: 'answer-2', rater: 'alice', rating: true },
|
|
56
|
+
{ runId: 'answer-2', rater: 'bob', rating: true },
|
|
57
|
+
]
|
|
86
58
|
|
|
87
|
-
// 2. Pipe through the adapter:
|
|
88
59
|
const { runs, raterScores } = fromFeedbackTable({ ratings })
|
|
60
|
+
const report = await analyzeRuns({ runs, raterScores })
|
|
89
61
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
raterScores,
|
|
94
|
-
// Optional: close the loop with engagement data once you have it.
|
|
95
|
-
outcomeSignal: { metric: 'engagement_rate', valueByRunId: enrichedFromProd },
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
// report.interRater.disagreementCases → top 20 claims worth a meeting
|
|
99
|
-
// report.outcomeCorrelation → does team taste predict engagement?
|
|
100
|
-
// report.recommendations → action list
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### What the report shows
|
|
104
|
-
|
|
62
|
+
console.log(report.interRater?.kappa)
|
|
63
|
+
console.log(report.interRater?.icc)
|
|
64
|
+
console.log(report.interRater?.disagreementCases)
|
|
105
65
|
```
|
|
106
|
-
Runs analyzed: 30
|
|
107
|
-
Composite mean: 0.756 (approve rate ~76%)
|
|
108
|
-
|
|
109
|
-
── Inter-rater agreement ──
|
|
110
|
-
Raters: 3 (alice, bob, carol)
|
|
111
|
-
Jointly rated runs: 30
|
|
112
|
-
Pairwise pearson κ:
|
|
113
|
-
alice::bob 0.53
|
|
114
|
-
alice::carol 0.55
|
|
115
|
-
bob::carol 0.21
|
|
116
|
-
Mean κ: 0.43
|
|
117
|
-
|
|
118
|
-
── Top 5 disagreement cases ──
|
|
119
|
-
claim-1 range=1.00 ratings: alice=0, bob=0, carol=1
|
|
120
|
-
claim-7 range=1.00 ratings: alice=0, bob=1, carol=0
|
|
121
|
-
...
|
|
122
|
-
|
|
123
|
-
── Recommendations ──
|
|
124
|
-
[high] recalibrate — Inter-rater agreement κ=0.43 is below 0.5
|
|
125
|
-
Raters disagree on what 'good' looks like. Refine the rubric or triage the disagreement cases.
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### Next steps for this customer
|
|
129
|
-
|
|
130
|
-
1. **Triage meeting on the disagreement cases.** Mean κ=0.43 means the rubric is ambiguous; clarify it on the cases that split.
|
|
131
|
-
2. **Calibrate one LLM judge per reviewer.** Each reviewer's history is the gold signal — substrate primitive `calibrateJudge` against `raterScores` filtered to that reviewer.
|
|
132
|
-
3. **Add engagement as `outcomeSignal`** once the content downstream is instrumented. The `outcomeCorrelation` section tells the team whether their taste predicts the founder's token-max goal — and if not, the linear reward model says how to retarget.
|
|
133
|
-
4. **Graduate to journey #3** — wrap the research-generation Claude-P call as an `agent(surface, scenario)`, use the calibrated judges, run `evalKit.improve()` nightly. Open a PR against the GitHub Action when the holdout approval rate beats baseline.
|
|
134
|
-
|
|
135
|
-
**Runnable:** [`examples/customer-feedback-loop/`](../examples/customer-feedback-loop/)
|
|
136
66
|
|
|
137
|
-
|
|
67
|
+
Weighted kappa and ICC measure absolute agreement.
|
|
68
|
+
Pearson and Spearman measure correlation and are reported separately because reviewers can correlate while using different score levels.
|
|
138
69
|
|
|
139
|
-
|
|
70
|
+
Review the largest disagreements before using those labels to calibrate a model judge.
|
|
71
|
+
Test the judge on human ratings that were not used during calibration.
|
|
140
72
|
|
|
141
|
-
|
|
73
|
+
Runnable example: [`examples/customer-feedback-loop`](../examples/customer-feedback-loop/)
|
|
142
74
|
|
|
143
|
-
|
|
75
|
+
## 3. Evaluate Or Improve A Runnable Agent
|
|
144
76
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
### The code
|
|
77
|
+
Use this path when you can call the agent for a scenario and score the returned artifact.
|
|
148
78
|
|
|
149
79
|
```ts
|
|
150
80
|
import { defineAgentEval } from '@tangle-network/agent-eval/contract'
|
|
151
81
|
|
|
152
82
|
const evalKit = defineAgentEval({
|
|
153
83
|
scenarios,
|
|
154
|
-
agent: async (
|
|
155
|
-
await myAgent.run({ systemPrompt: (surface as { systemPrompt: string }).systemPrompt, scenario }),
|
|
84
|
+
agent: async (prompt, scenario) => yourAgent.run({ prompt: String(prompt), scenario }),
|
|
156
85
|
judge: {
|
|
157
|
-
name: '
|
|
158
|
-
dimensions: [
|
|
159
|
-
|
|
86
|
+
name: 'task-quality',
|
|
87
|
+
dimensions: [
|
|
88
|
+
{ key: 'correct', description: 'The answer is correct' },
|
|
89
|
+
{ key: 'complete', description: 'The answer covers the whole request' },
|
|
90
|
+
],
|
|
91
|
+
score: ({ artifact, scenario }) => scoreArtifact(artifact, scenario),
|
|
160
92
|
},
|
|
161
|
-
baselineSurface:
|
|
162
|
-
budget: { generations: 3, populationSize: 2 },
|
|
93
|
+
baselineSurface: currentPrompt,
|
|
163
94
|
})
|
|
164
95
|
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
result.gateDecision // 'ship' | 'hold' | ...
|
|
168
|
-
result.insight // full decision packet
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### What the report shows
|
|
172
|
-
|
|
96
|
+
const baseline = await evalKit.evaluate()
|
|
97
|
+
const candidate = await evalKit.evaluate({ surface: proposedPrompt })
|
|
173
98
|
```
|
|
174
|
-
═══ selfImprove() decision packet ═══
|
|
175
99
|
|
|
176
|
-
|
|
177
|
-
|
|
100
|
+
Call `.evaluate()` when you already have a candidate to compare.
|
|
101
|
+
Call `.improve()` when you want the library to generate and test candidates:
|
|
178
102
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
103
|
+
```ts
|
|
104
|
+
const result = await evalKit.improve({
|
|
105
|
+
llm: {
|
|
106
|
+
baseUrl: process.env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1',
|
|
107
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
108
|
+
model: 'gpt-4.1-mini',
|
|
109
|
+
},
|
|
110
|
+
})
|
|
186
111
|
|
|
187
|
-
|
|
188
|
-
|
|
112
|
+
console.log(result.winner.surface)
|
|
113
|
+
console.log(result.lift)
|
|
114
|
+
console.log(result.gateDecision)
|
|
189
115
|
```
|
|
190
116
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
2. **Wire `hostedTenant`** to ship the decision packet to a dashboard (the hosted Intelligence orchestrator, or your own implementation of the wire spec).
|
|
195
|
-
3. **Add `canaryScenarios`** to guard against the holdout leaking into the candidate prompt.
|
|
196
|
-
4. **Add `outcomeSignal`** in `analyzeRuns()` for any post-deploy reruns to verify the predicted lift actually shows up in real outcomes.
|
|
117
|
+
The default candidate generator calls the configured model.
|
|
118
|
+
The agent and judge may make their own calls depending on your implementation.
|
|
119
|
+
Set generation, population, concurrency, and dollar limits through `budget`.
|
|
197
120
|
|
|
198
|
-
|
|
121
|
+
For production use, provide enough scenarios to keep candidate generation and final comparison disjoint.
|
|
122
|
+
Use an explicit held-back scenario set when the split must remain stable across runs.
|
|
199
123
|
|
|
200
|
-
|
|
124
|
+
Runnable example: [`examples/selfimprove-quickstart`](../examples/selfimprove-quickstart/)
|
|
201
125
|
|
|
202
|
-
##
|
|
126
|
+
## Moving Between Paths
|
|
203
127
|
|
|
204
|
-
|
|
128
|
+
The paths compose without changing data formats:
|
|
205
129
|
|
|
206
|
-
1.
|
|
207
|
-
2.
|
|
208
|
-
3.
|
|
130
|
+
1. Convert traces or ratings into `RunRecord[]` and use `analyzeRuns()` to find recurring failures.
|
|
131
|
+
2. Turn those failures into representative scenarios and deterministic checks where possible.
|
|
132
|
+
3. Use `defineAgentEval()` to compare changes against the same scenarios.
|
|
133
|
+
4. Use `.improve()` only after the evaluation reliably separates known-good from known-bad behavior.
|
|
209
134
|
|
|
210
|
-
|
|
135
|
+
See [`concepts.md`](./concepts.md) for data types and [`examples/README.md`](../examples/README.md) for the full runnable index.
|
|
@@ -8,8 +8,7 @@ one meaning. Use these words and nothing else.
|
|
|
8
8
|
Cross-links: [`concepts.md`](../concepts.md) (eval mental model),
|
|
9
9
|
[`campaign-proposers.md`](../campaign-proposers.md) (proposer catalog),
|
|
10
10
|
[`multi-shot-optimization.md`](../multi-shot-optimization.md) (GEPA), and
|
|
11
|
-
[`
|
|
12
|
-
(analyst / autoresearch).
|
|
11
|
+
[`self-improvement-map.md`](../self-improvement-map.md) (supported improvement paths).
|
|
13
12
|
|
|
14
13
|
## Core Roles
|
|
15
14
|
|
|
@@ -18,12 +17,12 @@ Cross-links: [`concepts.md`](../concepts.md) (eval mental model),
|
|
|
18
17
|
| **Execution driver** | The thing that decides or routes the next turn/action inside a sandbox or worker conversation. | Inner layer only |
|
|
19
18
|
| **Surface proposer** | The thing that proposes the next prompt/config/code surface for the improvement loop to measure. | Outer layer only |
|
|
20
19
|
| **Worker** | An agent harness instance (Claude Code, Codex, OpenCode, …) running inside a sandbox. Does the actual work; responds in chat. | Inner layer only |
|
|
21
|
-
| **Sandbox** | A multi-harness VM. Hosts **1..N workers**, which can share a workspace. Not an agent
|
|
20
|
+
| **Sandbox** | A multi-harness VM. Hosts **1..N workers**, which can share a workspace. Not an agent: the substrate an agent runs in. | Inner layer only |
|
|
22
21
|
| **Measurement** | Runs the worker over a set of scenarios and judges the outputs into a scorecard with confidence intervals. This is `runCampaign`. | Outer layer |
|
|
23
22
|
|
|
24
23
|
Two facts that trip people up:
|
|
25
24
|
|
|
26
|
-
1. **A sandbox is not a worker.** One sandbox can hold ten workers
|
|
25
|
+
1. **A sandbox is not a worker.** One sandbox can hold ten workers: a driver
|
|
27
26
|
can coordinate CC + Codex + OpenCode siblings sharing one workspace, or a
|
|
28
27
|
fleet spread across machines. `runLoop`'s placement encodes exactly this:
|
|
29
28
|
`{ sibling, sandboxId }` = co-located workers; `{ fleet, fleetId,
|
|
@@ -38,21 +37,21 @@ Two facts that trip people up:
|
|
|
38
37
|
There are two loops. The outer one improves the thing the inner one runs.
|
|
39
38
|
|
|
40
39
|
```
|
|
41
|
-
runImprovementLoop OUTER loop
|
|
40
|
+
runImprovementLoop OUTER loop: improve the agent over time
|
|
42
41
|
│
|
|
43
42
|
├─ PROPOSER = SurfaceProposer proposes a candidate SURFACE
|
|
44
43
|
│ (evolutionary mutator | (the worker's system prompt / tools / config)
|
|
45
|
-
│ reflective analyst)
|
|
44
|
+
│ reflective analyst) : NOT a conversation turn
|
|
46
45
|
│
|
|
47
46
|
└─ for each candidate surface:
|
|
48
47
|
│
|
|
49
|
-
runCampaign a MEASUREMENT
|
|
48
|
+
runCampaign a MEASUREMENT: scores ONE surface
|
|
50
49
|
│
|
|
51
50
|
└─ for each scenario × rep:
|
|
52
51
|
│
|
|
53
|
-
dispatch(scenario) THE SEAM
|
|
52
|
+
dispatch(scenario) THE SEAM: topology-opaque, returns an artifact
|
|
54
53
|
│
|
|
55
|
-
└─ runLoop / runMultishot INNER loop
|
|
54
|
+
└─ runLoop / runMultishot INNER loop: one conversation
|
|
56
55
|
├─ DRIVER = persona / user / planner chats with ↓
|
|
57
56
|
└─ WORKERS = 1..N agent harnesses in 1..M sandboxes
|
|
58
57
|
│
|
|
@@ -82,18 +81,18 @@ opinion about execution topology: the topology lives inside `dispatch`.
|
|
|
82
81
|
- The worker is the agent in the sandbox. The driver talks to it. ✓
|
|
83
82
|
- `runCampaign` is a **measurement**, not a worker. It *runs the worker* (via
|
|
84
83
|
`dispatch`); the worker does not "run the eval".
|
|
85
|
-
- The outer improvement loop has **no single worker
|
|
84
|
+
- The outer improvement loop has **no single worker**: its proposer proposes a
|
|
86
85
|
*surface*, and each surface is scored by a *measurement* that drives the
|
|
87
86
|
inner workers.
|
|
88
87
|
|
|
89
|
-
## The dataset flywheel
|
|
88
|
+
## The dataset flywheel: why every loop run matters
|
|
90
89
|
|
|
91
90
|
**Every loop run, regardless of why it ran, feeds the same dataset.** This is
|
|
92
91
|
the through-line that ties measurement and improvement together.
|
|
93
92
|
|
|
94
93
|
When `runCampaign` runs with a `labeledStore`, each cell captures
|
|
95
94
|
`(scenario, artifact, judgeScore, source)` into the `LabeledScenarioStore`.
|
|
96
|
-
The `source` discriminates *why* the run happened
|
|
95
|
+
The `source` discriminates *why* the run happened: but the captured tuple is
|
|
97
96
|
identical in shape:
|
|
98
97
|
|
|
99
98
|
| `captureSource` | The run that produced it |
|
|
@@ -109,7 +108,7 @@ production conversation, and an autoresearch loop all deposit the same
|
|
|
109
108
|
`(input, output, reward)` tuples. The optimization proposer later samples from
|
|
110
109
|
that corpus to evolve the surface. So:
|
|
111
110
|
|
|
112
|
-
> Running *any* loop
|
|
111
|
+
> Running *any* loop: even one whose purpose is not optimization: builds the
|
|
113
112
|
> dataset that optimization needs. The flywheel turns whether or not you are
|
|
114
113
|
> currently optimizing.
|
|
115
114
|
|
|
@@ -147,7 +146,7 @@ interface SurfaceProposer<TFindings = unknown> {
|
|
|
147
146
|
| Implementation | Strategy | How it proposes | Where it lives |
|
|
148
147
|
|---|---|---|---|
|
|
149
148
|
| `evolutionaryProposer` | Evolutionary (GEPA / AxGEPA) | Standalone `SurfaceProposer`. Mutates the current best surface into N candidates, blind to history beyond the current best. Optimizes against the dataset's rewards. | **agent-eval** (pure: dataset → surface, no sandbox) |
|
|
150
|
-
| Runtime reflective proposer | Reflective | Cheap generator: drafts patches from the report and applies them into a worktree (shots=1, no sandbox). | **agent-runtime
|
|
149
|
+
| Runtime reflective proposer | Reflective | Cheap generator: drafts patches from the report and applies them into a worktree (shots=1, no sandbox). | **agent-runtime**: implements agent-eval's proposer contract |
|
|
151
150
|
| Runtime agentic proposer | Agentic | Full generator: runs a coding harness in the worktree (≤ `maxImprovementShots`) to edit in place. | **agent-runtime** |
|
|
152
151
|
|
|
153
152
|
This resolves the prior duplication where `runImprovementLoop` (evolutionary,
|
|
@@ -158,7 +157,7 @@ same cost dial, not separate outer loops. The dependency direction permits this
|
|
|
158
157
|
cleanly: agent-eval is the leaf and owns the proposer contract; agent-runtime
|
|
159
158
|
imports agent-eval and implements it.
|
|
160
159
|
|
|
161
|
-
## What "the surface" is
|
|
160
|
+
## What "the surface" is: improvement tiers
|
|
162
161
|
|
|
163
162
|
`MutableSurface` is the thing the proposer changes. It has tiers, least → most
|
|
164
163
|
invasive. `MutableSurface = string | CodeSurface` spans all of them: `string`
|
|
@@ -179,36 +178,36 @@ not represented by the candidate tree.
|
|
|
179
178
|
|
|
180
179
|
The cost/capability distinction:
|
|
181
180
|
|
|
182
|
-
- **`reflectiveGenerator`** updates the *signatures
|
|
181
|
+
- **`reflectiveGenerator`** updates the *signatures*: prompt + tool surface
|
|
183
182
|
(tiers 1–2). Cheap (drafts patches, no sandbox), reversible, measured
|
|
184
183
|
directly against the dataset.
|
|
185
184
|
- **`agenticGenerator`** updates the *code* (tier 4). A coding harness reads
|
|
186
185
|
the repository + the report, edits in a worktree, iterates up to
|
|
187
|
-
`maxImprovementShots
|
|
186
|
+
`maxImprovementShots`: measured by re-running the inner loop against the
|
|
188
187
|
changed code.
|
|
189
188
|
|
|
190
189
|
Both are implementations of the one proposer contract (propose → measure → gate
|
|
191
|
-
→ PR). They differ only in *what* they edit and *how invasive* it is
|
|
190
|
+
→ PR). They differ only in *what* they edit and *how invasive* it is: and both
|
|
192
191
|
consume the **same dataset** the flywheel builds.
|
|
193
192
|
|
|
194
193
|
## Vocabulary quick reference
|
|
195
194
|
|
|
196
|
-
- **shot
|
|
195
|
+
- **shot**: one conversational turn (driver says X, worker responds Y). Used
|
|
197
196
|
in `runMultishot`. Never used to mean a whole eval run.
|
|
198
|
-
- **runMultishot
|
|
197
|
+
- **runMultishot**: many shots in one conversation; persona-driver ↔ one
|
|
199
198
|
router-agent. agent-eval.
|
|
200
|
-
- **runLoop
|
|
199
|
+
- **runLoop**: driver ↔ workers in sandboxes; topology-agnostic execution.
|
|
201
200
|
agent-runtime.
|
|
202
|
-
- **runCampaign
|
|
201
|
+
- **runCampaign**: a measurement: a surface scored over N scenarios × M reps.
|
|
203
202
|
agent-eval. (A "campaign" = a coordinated batch of measurements.)
|
|
204
|
-
- **runOptimization
|
|
205
|
-
- **runImprovementLoop
|
|
203
|
+
- **runOptimization**: the improvement loop body: proposer suggests surfaces, each is measured, and only a candidate that beats the global incumbent is promoted. agent-eval.
|
|
204
|
+
- **runImprovementLoop**: `runOptimization` + holdout re-score + release gate
|
|
206
205
|
+ optional PR. agent-eval.
|
|
207
|
-
- **runAnalystLoop
|
|
206
|
+
- **runAnalystLoop**: reflective autoresearch: findings + knowledge updates +
|
|
208
207
|
improvement proposals. agent-runtime.
|
|
209
|
-
- **SurfaceProposer
|
|
208
|
+
- **SurfaceProposer**: the contract a surface proposer implements.
|
|
210
209
|
`evolutionaryProposer` (agent-eval) is one; agent-runtime can provide
|
|
211
210
|
reflective or agentic implementations.
|
|
212
|
-
- **CandidateGenerator
|
|
211
|
+
- **CandidateGenerator**: the byte-producing seam inside a runtime proposer;
|
|
213
212
|
`reflectiveGenerator` (cheap, no sandbox) and `agenticGenerator` (coding
|
|
214
213
|
harness in the worktree) are the two cost settings. agent-runtime.
|