@tangle-network/agent-eval 0.125.0 → 0.126.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +62 -35
  2. package/README.md +270 -189
  3. package/dist/analyst/index.d.ts +15 -145
  4. package/dist/analyst/index.js +33 -47
  5. package/dist/analyst/index.js.map +1 -1
  6. package/dist/benchmarks/index.d.ts +45 -162
  7. package/dist/benchmarks/index.js +8 -9
  8. package/dist/campaign/index.d.ts +3674 -5393
  9. package/dist/campaign/index.js +21 -95
  10. package/dist/{chunk-R226UZOI.js → chunk-474LBSOX.js} +2 -2
  11. package/dist/{chunk-HM6V7F3M.js → chunk-FO7HEH76.js} +3 -3
  12. package/dist/chunk-IILEIWGW.js +635 -0
  13. package/dist/chunk-IILEIWGW.js.map +1 -0
  14. package/dist/{chunk-EQUK3RFS.js → chunk-J5SQWP6Y.js} +8 -5
  15. package/dist/chunk-J5SQWP6Y.js.map +1 -0
  16. package/dist/{chunk-W5B3ZGP3.js → chunk-KE2VWPZX.js} +8 -6
  17. package/dist/{chunk-W5B3ZGP3.js.map → chunk-KE2VWPZX.js.map} +1 -1
  18. package/dist/{chunk-DT7OXY3C.js → chunk-LUNF2SEL.js} +538 -851
  19. package/dist/chunk-LUNF2SEL.js.map +1 -0
  20. package/dist/chunk-NGUYT5CI.js +4637 -0
  21. package/dist/chunk-NGUYT5CI.js.map +1 -0
  22. package/dist/{chunk-QFQZ3U3X.js → chunk-OCFJACJU.js} +2 -2
  23. package/dist/{chunk-GID26AN4.js → chunk-P22LJ3Y2.js} +4 -6
  24. package/dist/{chunk-GID26AN4.js.map → chunk-P22LJ3Y2.js.map} +1 -1
  25. package/dist/{chunk-SJT4OBVL.js → chunk-SDPM6554.js} +3 -3
  26. package/dist/{chunk-D5JZ7UDZ.js → chunk-UCLVDLCH.js} +136 -50
  27. package/dist/chunk-UCLVDLCH.js.map +1 -0
  28. package/dist/chunk-VMUENW6F.js +7274 -0
  29. package/dist/chunk-VMUENW6F.js.map +1 -0
  30. package/dist/{chunk-JKDNAOF5.js → chunk-W4L6C2XT.js} +2 -2
  31. package/dist/chunk-WGXIEX7P.js +116 -0
  32. package/dist/chunk-WGXIEX7P.js.map +1 -0
  33. package/dist/{chunk-GRCDRKII.js → chunk-WS3NZZQQ.js} +58 -20
  34. package/dist/chunk-WS3NZZQQ.js.map +1 -0
  35. package/dist/cli.js +3 -3
  36. package/dist/contract/index.d.ts +3220 -3094
  37. package/dist/contract/index.js +173 -42
  38. package/dist/contract/index.js.map +1 -1
  39. package/dist/control.js +2 -3
  40. package/dist/fuzz.d.ts +14 -1
  41. package/dist/fuzz.js +1 -1
  42. package/dist/hosted/index.d.ts +8 -1
  43. package/dist/index.d.ts +71 -687
  44. package/dist/index.js +178 -497
  45. package/dist/index.js.map +1 -1
  46. package/dist/openapi.json +1 -1
  47. package/dist/rl.d.ts +5 -100
  48. package/dist/rl.js +4 -5
  49. package/dist/rl.js.map +1 -1
  50. package/dist/{run-campaign-I3JXKVAK.js → run-campaign-LVFKZCEU.js} +3 -3
  51. package/dist/traces.js +2 -3
  52. package/dist/wire/index.d.ts +14 -1
  53. package/dist/wire/index.js +3 -3
  54. package/docs/campaign-proposers.md +363 -168
  55. package/docs/design/loop-taxonomy.md +142 -190
  56. package/docs/design.md +1 -1
  57. package/docs/distributed-driver.md +8 -11
  58. package/docs/feature-guide.md +20 -19
  59. package/docs/knowledge-readiness.md +2 -5
  60. package/docs/multi-shot-optimization.md +35 -27
  61. package/docs/rollout.md +5 -5
  62. package/package.json +4 -4
  63. package/dist/chunk-A62YMFWA.js +0 -9269
  64. package/dist/chunk-A62YMFWA.js.map +0 -1
  65. package/dist/chunk-A6GT67HT.js +0 -550
  66. package/dist/chunk-A6GT67HT.js.map +0 -1
  67. package/dist/chunk-D5JZ7UDZ.js.map +0 -1
  68. package/dist/chunk-DT7OXY3C.js.map +0 -1
  69. package/dist/chunk-EQUK3RFS.js.map +0 -1
  70. package/dist/chunk-GC4ATIKK.js +0 -317
  71. package/dist/chunk-GC4ATIKK.js.map +0 -1
  72. package/dist/chunk-GRCDRKII.js.map +0 -1
  73. package/dist/chunk-LOW3U7JZ.js +0 -328
  74. package/dist/chunk-LOW3U7JZ.js.map +0 -1
  75. package/dist/chunk-PMITBABE.js +0 -3841
  76. package/dist/chunk-PMITBABE.js.map +0 -1
  77. /package/dist/{chunk-R226UZOI.js.map → chunk-474LBSOX.js.map} +0 -0
  78. /package/dist/{chunk-HM6V7F3M.js.map → chunk-FO7HEH76.js.map} +0 -0
  79. /package/dist/{chunk-QFQZ3U3X.js.map → chunk-OCFJACJU.js.map} +0 -0
  80. /package/dist/{chunk-SJT4OBVL.js.map → chunk-SDPM6554.js.map} +0 -0
  81. /package/dist/{chunk-JKDNAOF5.js.map → chunk-W4L6C2XT.js.map} +0 -0
  82. /package/dist/{run-campaign-I3JXKVAK.js.map → run-campaign-LVFKZCEU.js.map} +0 -0
@@ -1,212 +1,164 @@
1
- # Loop taxonomy: execution driver, worker, measurement, and surface proposer
1
+ # Evaluation And Improvement Model
2
2
 
3
- This is the canonical vocabulary for the Tangle agent stack. It exists because
4
- the same word ("loop", "shot", "worker") was being used at three different
5
- layers, and the layers were getting conflated. Every role below has exactly
6
- one meaning. Use these words and nothing else.
3
+ This document defines the package's internal terms and ownership boundaries.
4
+ Public examples should use concrete words such as prompt, skill, case, and agent whenever possible.
7
5
 
8
- Cross-links: [`concepts.md`](../concepts.md) (eval mental model),
9
- [`campaign-proposers.md`](../campaign-proposers.md) (proposer catalog), and
10
- [`multi-shot-optimization.md`](../multi-shot-optimization.md) (GEPA).
6
+ ## The Evaluation Unit
11
7
 
12
- ## Core Roles
8
+ One evaluation cell contains:
13
9
 
14
- | Role | Definition | Lives at |
15
- |---|---|---|
16
- | **Execution driver** | The thing that decides or routes the next turn/action inside a sandbox or worker conversation. | Inner layer only |
17
- | **Surface proposer** | The thing that proposes the next prompt/config/code surface for the improvement loop to measure. | Outer layer only |
18
- | **Worker** | An agent harness instance (Claude Code, Codex, OpenCode, …) running inside a sandbox. Does the actual work; responds in chat. | Inner layer only |
19
- | **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 |
20
- | **Measurement** | Runs the worker over a set of scenarios and judges the outputs into a scorecard with confidence intervals. This is `runCampaign`. | Outer layer |
10
+ ```text
11
+ candidate surface
12
+ + scenario
13
+ + repetition
14
+ + dispatch
15
+ + judges
16
+ = artifact, scores, trace, usage, cost, and status
17
+ ```
21
18
 
22
- Two facts that trip people up:
19
+ The dispatch runs the system under test.
20
+ A judge converts one artifact into dimension scores and a composite score.
21
+ A campaign repeats this process across cases and candidates.
23
22
 
24
- 1. **A sandbox is not a worker.** One sandbox can hold ten workers: a driver
25
- can coordinate CC + Codex + OpenCode siblings sharing one workspace, or a
26
- fleet spread across machines. `runLoop`'s placement encodes exactly this:
27
- `{ sibling, sandboxId }` = co-located workers; `{ fleet, fleetId,
28
- machineId, sandboxId }` = workers across machines.
23
+ ## The Value Being Changed
29
24
 
30
- 2. **"Driver" is reserved for execution.** The outer loop uses a
31
- **surface proposer**: it proposes the next prompt / tool config / code
32
- surface for the measurement loop.
25
+ `MutableSurface` is the API type for the value under optimization.
26
+ It can be:
33
27
 
34
- ## The nesting
28
+ - a prompt string,
29
+ - a serialized configuration,
30
+ - a code surface owned by a runtime,
31
+ - a named component map for multi-part GEPA optimization.
35
32
 
36
- There are two loops. The outer one improves the thing the inner one runs.
33
+ A campaign measures surfaces but does not decide how a product stores or activates them.
37
34
 
38
- ```
39
- runImprovementLoop OUTER loop: improve the agent over time
40
-
41
- ├─ PROPOSER = SurfaceProposer proposes a candidate SURFACE
42
- │ (evolutionary mutator | (the worker's system prompt / tools / config)
43
- │ reflective analyst) : NOT a conversation turn
44
-
45
- └─ for each candidate surface:
46
-
47
- runCampaign a MEASUREMENT: scores ONE surface
48
-
49
- └─ for each scenario × rep:
50
-
51
- dispatch(scenario) THE SEAM: topology-opaque, returns an artifact
52
-
53
- └─ runLoop / runMultishot INNER loop: one conversation
54
- ├─ DRIVER = persona / user / planner chats with ↓
55
- └─ WORKERS = 1..N agent harnesses in 1..M sandboxes
56
-
57
- → transcript / artifact
58
- judge(artifact) → score
59
- → scorecard + CIs
60
- gate(winner vs baseline) → PR
61
- ```
35
+ ## Two Candidate Paths
36
+
37
+ ### Complete Optimization Method
38
+
39
+ `OptimizationMethod` owns search, candidate history, selection, and stopping.
40
+ It receives train and selection cases and returns one selected surface.
41
+
42
+ Use this path for:
43
+
44
+ - `gepaOptimizationMethod()`, which calls official GEPA engines and composition functions,
45
+ - `skillOptOptimizationMethod()`, which calls Microsoft's official `ReflACTTrainer`,
46
+ - another optimizer that already owns its search behavior.
47
+
48
+ Complete methods run through `compareOptimizationMethods()`.
49
+ The comparison function scores selected surfaces on final cases after all optimization finishes.
62
50
 
63
- ### `dispatch` is the topology-opaque seam
51
+ ### Caller-Owned Candidate Generator
64
52
 
65
- `dispatch(scenario) → artifact` is the boundary between the measurement layer
66
- and the execution layer. The measurement does **not** know or care how the
67
- artifact was produced. Behind the seam can be:
53
+ `SurfaceProposer` suggests candidates inside Agent Eval's `runImprovementLoop()`.
54
+ It receives the current surface, prior campaign history, findings, generation number, requested population size, and cancellation signal.
68
55
 
69
- - one LLM call,
70
- - one worker (CC) in one sandbox,
71
- - a conversation driver coordinating 10 workers (CC + Codex + OpenCode)
72
- sharing a workspace in one sandbox,
73
- - a fleet across machines.
56
+ Use this path when:
74
57
 
75
- All of it is invisible to `runCampaign`. This is why the substrate has no
76
- opinion about execution topology: the topology lives inside `dispatch`.
58
+ - product rules generate candidates,
59
+ - agent-runtime delegates candidate creation to a worker,
60
+ - a human-authored list defines possible edits.
77
61
 
78
- ### Corrected statements (things that were said backwards)
62
+ Do not reproduce an upstream optimizer behind this interface.
63
+ Use its complete method adapter so the upstream package retains control of its own search state.
79
64
 
80
- - The worker is the agent in the sandbox. The driver talks to it. ✓
81
- - `runCampaign` is a **measurement**, not a worker. It *runs the worker* (via
82
- `dispatch`); the worker does not "run the eval".
83
- - The outer improvement loop has **no single worker**: its proposer proposes a
84
- *surface*, and each surface is scored by a *measurement* that drives the
85
- inner workers.
65
+ ## Three Data Partitions
86
66
 
87
- ## The dataset flywheel: why every loop run matters
67
+ | Partition | May author candidates | May select candidates | May rank final methods |
68
+ |---|---:|---:|---:|
69
+ | Train | yes | yes | no |
70
+ | Selection | yes | yes | no |
71
+ | Final test | no | no | yes |
88
72
 
89
- **Every loop run, regardless of why it ran, feeds the same dataset.** This is
90
- the through-line that ties measurement and improvement together.
73
+ An `OptimizationMethodInput` has no final-test field.
74
+ This structural omission prevents an optimizer from receiving final cases through the normal API.
75
+ Official methods receive serialized train and selection cases, so both partitions are optimizer-visible.
91
76
 
92
- When `runCampaign` runs with a `labeledStore`, each cell captures
93
- `(scenario, artifact, judgeScore, source)` into the `LabeledScenarioStore`.
94
- The `source` discriminates *why* the run happened: but the captured tuple is
95
- identical in shape:
77
+ Change authors must still avoid indirect leaks through files, environment variables, cached artifacts, or custom scenario descriptions.
96
78
 
97
- | `captureSource` | The run that produced it |
79
+ ## Main APIs
80
+
81
+ | API | Responsibility |
98
82
  |---|---|
99
- | `'eval-run'` | a plain evaluation campaign |
100
- | `'production-trace'` | a real user conversation in production |
101
- | `'red-team'` | an adversarial probe |
102
- | `'synthetic'` | a generated scenario |
103
- | `'manual'` | a human-curated example |
104
-
105
- That captured corpus **is the GEPA training set.** A basic eval run, a
106
- production conversation, and an autoresearch loop all deposit the same
107
- `(input, output, reward)` tuples. The optimization proposer later samples from
108
- that corpus to evolve the surface. So:
109
-
110
- > Running *any* loop: even one whose purpose is not optimization: builds the
111
- > dataset that optimization needs. The flywheel turns whether or not you are
112
- > currently optimizing.
113
-
114
- This is enforced, not aspirational: `runImprovementLoop` **refuses**
115
- `tracing: 'off'` whenever a proposer is wired, precisely because a loop that
116
- doesn't feed the dataset is a loop that breaks the flywheel.
117
-
118
- Temporal-split discipline (train vs holdout, `capturedBefore`) and
119
- default-off-for-training of `production-trace` are enforced at the
120
- `LabeledScenarioStore.sample()` boundary so the flywheel cannot contaminate
121
- the holdout it is judged against. See `src/campaign/labeled-store/`.
122
-
123
- ## One improvement loop, pluggable proposers
124
-
125
- The improvement loop is **proposer-agnostic**. `runOptimization` (the loop body)
126
- and `runImprovementLoop` (the gated-promotion shell) call
127
- `proposer.propose(...)` → measure → `proposer.decide(...)`. They do not know
128
- which strategy proposed the candidate. The API interface is `SurfaceProposer`:
129
-
130
- ```ts
131
- interface SurfaceProposer<TFindings = unknown> {
132
- kind: string
133
- propose(args: {
134
- currentSurface: MutableSurface
135
- history: GenerationRecord[] // what's been tried + scored
136
- findings: TFindings[] // external signal (e.g. analyst output)
137
- populationSize: number
138
- generation: number
139
- signal: AbortSignal
140
- }): Promise<MutableSurface[]>
141
- decide?(args: { history: GenerationRecord[] }): { stop: boolean; reason?: string }
142
- }
143
- ```
83
+ | `runCampaign()` | Execute and score a fixed set of candidate cells. |
84
+ | `runImprovementLoop()` | Search with a caller-owned `SurfaceProposer` and apply a release rule. |
85
+ | `compareOptimizationMethods()` | Run complete methods and compare selected surfaces on shared final cases. |
86
+ | `gepaOptimizationMethod()` | Adapt official GEPA recipes to Agent Eval execution and scoring. |
87
+ | `skillOptOptimizationMethod()` | Adapt official SkillOpt training to Agent Eval execution and scoring. |
88
+
89
+ ## Runtime Ownership
90
+
91
+ Agent Eval owns measurement:
92
+
93
+ - scenarios,
94
+ - dispatch contracts,
95
+ - judges,
96
+ - run records,
97
+ - cost receipts,
98
+ - statistics,
99
+ - method comparison.
100
+
101
+ Agent Runtime owns execution policy:
102
+
103
+ - agent sessions,
104
+ - worker creation,
105
+ - steering,
106
+ - code edits,
107
+ - process placement,
108
+ - activation in a running product.
109
+
110
+ Agent Knowledge owns knowledge state:
111
+
112
+ - sources,
113
+ - retrieval,
114
+ - memory adapters,
115
+ - knowledge writes,
116
+ - freshness and provenance.
117
+
118
+ Runtime and knowledge packages can expose their values as candidate surfaces and use Agent Eval to measure them.
119
+ Agent Eval must not import either consumer package.
120
+
121
+ ## Resume And Parallel Work
122
+
123
+ Campaign storage keeps cell-level results and cost receipts.
124
+ Official optimizer adapters add their own compatible-run identity and process lock.
125
+
126
+ A compatible official run includes:
127
+
128
+ - upstream package revision,
129
+ - optimizer recipe or trainer settings,
130
+ - starting surface,
131
+ - train and selection descriptions,
132
+ - evaluation ID for execution and scoring behavior,
133
+ - seed,
134
+ - work limits.
135
+
136
+ SkillOpt and a direct GEPA engine can restore official state.
137
+ Composed GEPA recipes restart and report `resumed: false`.
138
+ Method-level concurrency and candidate-level concurrency are separate controls.
139
+ Each method receives its own run directory and optimization spend account.
140
+
141
+ ## Cost Accounting
142
+
143
+ Agent and judge calls must report receipts through `DispatchContext.cost`.
144
+ Unknown spend remains unknown.
145
+
146
+ Standard GEPA and SkillOpt model calls pass through Agent Eval's local proxy.
147
+ The proxy enforces limits and records provider usage at caller-supplied rates.
148
+ Other GEPA engines can report their own spend, but that amount remains incomplete because Agent Eval did not observe those model calls.
149
+ Missing usage remains unknown instead of being treated as zero.
150
+
151
+ ## Promotion
152
+
153
+ Optimization returns a candidate.
154
+ Product activation remains a separate caller decision.
155
+
156
+ The caller should require:
157
+
158
+ - a calibrated score,
159
+ - improvement on cases not used to author candidates,
160
+ - acceptable regressions by dimension,
161
+ - complete enough cost data for the decision,
162
+ - an inspectable exact change.
144
163
 
145
- | Implementation | Strategy | How it proposes | Where it lives |
146
- |---|---|---|---|
147
- | `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) |
148
- | 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 |
149
- | Runtime agentic proposer | Agentic | Full generator: runs a coding harness in the worktree (≤ `maxImprovementShots`) to edit in place. | **agent-runtime** |
150
-
151
- This resolves the prior duplication where `runImprovementLoop` (evolutionary,
152
- agent-eval) and `runAnalystLoop` (reflective, agent-runtime) were two parallel
153
- loops doing "propose change → measure → gate → PR". There is **one loop** and
154
- one proposer contract. The reflective and agentic paths are two settings of the
155
- same cost dial, not separate outer loops. The dependency direction permits this
156
- cleanly: agent-eval is the leaf and owns the proposer contract; agent-runtime
157
- imports agent-eval and implements it.
158
-
159
- ## What "the surface" is: improvement tiers
160
-
161
- `MutableSurface` is the thing the proposer changes. It has tiers, least → most
162
- invasive. `MutableSurface = string | CodeSurface` spans all of them: `string`
163
- for tiers 1–2, and a finalized `CodeSurface` for tier 4. A code surface's
164
- worktree path is only its locator; exact base/candidate commits, final tree,
165
- and binary-patch digest are its portable identity. Call `verifyCodeSurface`
166
- before executing the checkout so a moved ref or post-finalization mutation
167
- fails before measurement. Verification hashes raw files and executable modes
168
- without Git filters and rejects external symlinks or submodules whose bytes are
169
- not represented by the candidate tree.
170
-
171
- | Tier | Surface | Generator that changes it | Blast radius |
172
- |---|---|---|---|
173
- | 1 | System prompt / prompt-signature addendum | `evolutionaryProposer` (GEPA), `reflectiveGenerator` | prompt only |
174
- | 2 | Tool config / tool signatures | `reflectiveGenerator` | which tools, their schemas |
175
- | 3 | Knowledge (wiki / knowledge graph) | agent-knowledge's knowledge adapter | what the agent *knows* |
176
- | 4 | Code / scaffolding | `agenticGenerator` (coding harness reads codebase + report) → worktree / PR | the implementation itself |
177
-
178
- The cost/capability distinction:
179
-
180
- - **`reflectiveGenerator`** updates the *signatures*: prompt + tool surface
181
- (tiers 1–2). Cheap (drafts patches, no sandbox), reversible, measured
182
- directly against the dataset.
183
- - **`agenticGenerator`** updates the *code* (tier 4). A coding harness reads
184
- the repository + the report, edits in a worktree, iterates up to
185
- `maxImprovementShots`: measured by re-running the inner loop against the
186
- changed code.
187
-
188
- Both are implementations of the one proposer contract (propose → measure → gate
189
- → PR). They differ only in *what* they edit and *how invasive* it is: and both
190
- consume the **same dataset** the flywheel builds.
191
-
192
- ## Vocabulary quick reference
193
-
194
- - **shot**: one conversational turn (driver says X, worker responds Y). Used
195
- in `runMultishot`. Never used to mean a whole eval run.
196
- - **runMultishot**: many shots in one conversation; persona-driver ↔ one
197
- router-agent. agent-eval.
198
- - **runLoop**: driver ↔ workers in sandboxes; topology-agnostic execution.
199
- agent-runtime.
200
- - **runCampaign**: a measurement: a surface scored over N scenarios × M reps.
201
- agent-eval. (A "campaign" = a coordinated batch of measurements.)
202
- - **runOptimization**: the improvement loop body: proposer suggests surfaces, each is measured, and only a candidate that beats the global incumbent is promoted. agent-eval.
203
- - **runImprovementLoop**: `runOptimization` + holdout re-score + release gate
204
- + optional PR. agent-eval.
205
- - **runAnalystLoop**: reflective autoresearch: findings + knowledge updates +
206
- improvement proposals. agent-runtime.
207
- - **SurfaceProposer**: the contract a surface proposer implements.
208
- `evolutionaryProposer` (agent-eval) is one; agent-runtime can provide
209
- reflective or agentic implementations.
210
- - **CandidateGenerator**: the byte-producing seam inside a runtime proposer;
211
- `reflectiveGenerator` (cheap, no sandbox) and `agenticGenerator` (coding
212
- harness in the worktree) are the two cost settings. agent-runtime.
164
+ The package records the decision inputs but does not deploy a prompt, skill, model, code change, memory, or knowledge base.
package/docs/design.md CHANGED
@@ -14,7 +14,7 @@ agent-runtime Runs agents (chat turns, one-shot tasks, multi-attempt loops),
14
14
  run as a trace, and exposes improve(), which composes agent-eval's improvement
15
15
  loop. Produces the RunRecords + traces agent-eval scores. Depends on agent-eval.
16
16
 
17
- agent-eval selfImprove, analyzeRuns, runCampaign + surface proposers (GEPA proposer, …),
17
+ agent-eval selfImprove, analyzeRuns, runCampaign, official optimizer adapters,
18
18
  (this repo) the gates (heldOutGate, defaultProductionGate, paretoSignificanceGate), the
19
19
  InsightReport, the RL bridge, the wire protocol. Depends on neither consumer.
20
20
 
@@ -1,10 +1,10 @@
1
1
  # Distributed campaign execution: coordinator-on-A, workers-on-B
2
2
 
3
- The coordinator process (running `runCampaign` / `runImprovementLoop` /
4
- `gepaProposer`) and the worker (running your actual agent) **do not have to live in the
5
- same process, machine, region, or cloud.** `Dispatch` is just a
6
- function: scenario in, artifact out. Whatever returns the artifact is
7
- the worker: local, remote, sandboxed, or fanned out across a fleet.
3
+ The coordinator process runs `runCampaign()`, `runImprovementLoop()`, or `compareOptimizationMethods()`.
4
+ The worker runs your actual agent.
5
+ They do not have to live in the same process, machine, region, or cloud.
6
+ `Dispatch` is a function from scenario to artifact.
7
+ The implementation can be local, remote, sandboxed, or spread across a worker fleet.
8
8
 
9
9
  ## Why you'd want this
10
10
 
@@ -161,12 +161,9 @@ and using `cellPlacement` to fan across many of them.
161
161
  stay on the worker. We need to forward them in the response body so
162
162
  `defaultProductionGate`'s `budgetUsd` ceiling reflects total spend, not
163
163
  coordinator-side spend. Tracked as a 0.45.x follow-up.
164
- - **Per-cell artifact streaming**: when the worker writes intermediate
165
- artifacts via `ctx.artifacts.write`, those land on the worker's
166
- storage. For multi-worker campaigns you'll want a shared object store
167
- (S3/GCS) reachable from both sides; today consumers wire that as a
168
- `CampaignStorage` impl. A reference S3-backed storage is on the
169
- roadmap.
164
+ - **Per-cell artifact streaming**: when the worker writes intermediate artifacts through `ctx.artifacts.write`, those files remain in worker storage.
165
+ Multi-worker campaigns need a shared `CampaignStorage` implementation reachable from both sides.
166
+ This package does not include an S3 or GCS implementation.
170
167
  - **gRPC / NATS / Temporal transports**: the wire is HTTP today by
171
168
  default because everything speaks HTTP. Other transports can ship as
172
169
  additional adapters; the `Dispatch` interface itself is
@@ -5,9 +5,8 @@ then shows when to use each one.
5
5
 
6
6
  ## Overview
7
7
 
8
- LLM agents can write code, drafts, research, plans, and actions. The hard part
9
- is knowing whether they actually did a good job, whether they should keep
10
- trying, and whether a change made them better or worse.
8
+ LLM agents can write code, drafts, research, plans, and actions.
9
+ Evaluation measures whether the result met its requirements, whether another attempt is warranted, and whether a change improved outcomes.
11
10
 
12
11
  `agent-eval` gives you reusable tools for that:
13
12
 
@@ -18,8 +17,7 @@ trying, and whether a change made them better or worse.
18
17
  - **Feedback trajectories** turn normal user approvals/rejections into training
19
18
  and eval data.
20
19
  - **Datasets and holdouts** keep examples organized so you do not overfit.
21
- - **Optimizers and mutation loops** try prompt/signature/code variants and keep
22
- the ones that really improve.
20
+ - **Optimizers and mutation loops** try prompt, signature, or code variants and retain measured improvements.
23
21
  - **Traces and telemetry** show what happened, step by step.
24
22
 
25
23
  ## Which Primitive Should I Use?
@@ -27,15 +25,15 @@ trying, and whether a change made them better or worse.
27
25
  | Problem | Use | Why |
28
26
  | --- | --- | --- |
29
27
  | “Did this single answer/draft pass?” | Judge or rubric | Fast quality signal for one artifact. |
30
- | “Does generated code actually work?” | `BuilderSession`, `MultiLayerVerifier`, sandbox harness | Build/test/runtime gates catch failures judges miss. |
28
+ | “Does generated code work?” | `BuilderSession`, `MultiLayerVerifier` | Build, test, and runtime checks catch failures judges miss. |
31
29
  | “Should the agent keep trying?” | `runAgentControlLoop` | Budgeted `observe -> validate -> decide -> act` runtime. |
32
30
  | “The agent should propose, verify, review, and revise.” | `runProposeReviewAsControlLoop` | Reusable preset over the generic control loop. |
33
31
  | “Human feedback should become reusable eval data.” | `FeedbackTrajectory` | Captures approvals, rejections, edits, choices, metrics, and policy blocks. |
34
32
  | “I want Vercel-style eval folders agents can add quickly.” | `loadEvalFixtureScenarios`, `planEvalFixtureRun`, `runCampaign` | Folder-per-eval DX with campaign scoring and cache-safe dry runs. |
35
33
  | “Can this action run, or does it need approval?” | `evaluateActionPolicy` | Generic preflight for side effects, budgets, and required evidence. |
36
34
  | “I need train/dev/test/holdout examples.” | `Dataset` plus feedback trajectory conversion | Stable splits and contamination control. |
37
- | “Which prompt or signature wins?” | `runImprovementLoop`, steering optimizers | Runs variants on scenarios and compares scores. |
38
- | “Improve a multi-turn agent over real task traces.” | `runImprovementLoop` | GEPA-style trajectory optimization with ASI and held-out promotion. |
35
+ | “Which optimization procedure wins?” | `compareOptimizationMethods` | Runs complete methods on shared train and selection cases, then compares them on separate final cases. |
36
+ | “Improve a multi-turn agent with candidates from my runtime.” | `runImprovementLoop` | Evaluates caller-generated candidates and applies a separate release rule. |
39
37
  | “Improve prompts, then code if prompts plateau.” | `runPromptEvolution`, composite mutator, code mutator | Bounded evolution with telemetry and lineage. |
40
38
  | “Find why a regression happened.” | bisector, traces, run records | Narrows changes and preserves evidence. |
41
39
  | “Expose evals to another language.” | Wire protocol and Python client | HTTP/RPC boundary for non-TypeScript apps. |
@@ -102,21 +100,24 @@ Result:
102
100
  generated code -> build/test/runtime gates -> score -> ship or revise
103
101
  ```
104
102
 
105
- ### Prompt/Signature Optimizer
103
+ ### Prompt And Skill Optimization
106
104
 
107
- Use when you want Ax/GEPA-style improvement.
105
+ Use `gepaOptimizationMethod()` for official GEPA recipes.
106
+ Use `skillOptOptimizationMethod()` for Microsoft's official SkillOpt trainer.
107
+ For DSPy programs, use official DSPy optimizers with Python's `DspyJudgeMetric`.
108
+ Run complete methods through `compareOptimizationMethods()`.
109
+ Use `runImprovementLoop()` only when your application or runtime owns candidate generation through `SurfaceProposer`.
108
110
 
109
- 1. For variable-length agent tasks, use `runImprovementLoop`.
110
- 2. Build search/dev/test/holdout splits from the real product loop.
111
- 3. Score full trajectories, not just final text.
112
- 4. Emit actionable side information for failures the mutator can fix.
113
- 5. Promote only `promotedVariant`, never a rejected `searchBestVariant`.
114
- 6. Keep run records with prompt hash, model, config, cost, and commit.
111
+ 1. Build train, selection, and final splits from the real product path.
112
+ 2. Score complete trajectories rather than final text alone.
113
+ 3. Give optimizers actionable evidence from train and selection failures.
114
+ 4. Keep final cases out of candidate generation and method selection.
115
+ 5. Record prompt hash, model, configuration, cost status, and source revision.
115
116
 
116
117
  Result:
117
118
 
118
119
  ```text
119
- candidate variant -> repeated evals -> statistical comparison -> promotion gate
120
+ candidate variant -> repeated evaluations -> statistical comparison -> release decision
120
121
  ```
121
122
 
122
123
  Do not optimize a toy harness if users run a different product loop. Build
@@ -158,9 +159,9 @@ Store as `FeedbackTrajectory`, then derive:
158
159
  | Action policy | `evaluateActionPolicy` | Approval/budget preflight | Blocks or labels actions before `act()`. |
159
160
  | Datasets | `Dataset`, holdout tools, canaries | Train/dev/test/holdout corpora | Keeps optimization honest. |
160
161
  | Eval fixtures | `discoverEvalFixtures`, `loadEvalFixtureScenarios`, `planEvalFixtureRun` | Coding-agent eval folders | Vercel-style input shape; still executes through `runCampaign`. |
161
- | Optimization | `runImprovementLoop`, steering optimizers | Prompt/signature comparison | Use held-out gates before promotion. |
162
+ | Optimization | `compareOptimizationMethods`, official optimizer adapters, `runImprovementLoop` | Prompt and skill comparison | Reserve separate final cases for method ranking. |
162
163
  | Evolution | prompt/code mutators, sandbox pool, telemetry | Autoresearch and mutation loops | Use budgets and lineage; do not run unbounded. |
163
- | Telemetry | `TraceStore`, OTLP, file sinks | Audit and replay | Treat traces as evidence, not just logs. |
164
+ | Telemetry | `TraceStore`, OTLP, file sinks | Evidence and replay | Store traces as inspectable run evidence. |
164
165
  | Reporting | summaries, pareto, cost tracker | Decision support | Useful for PRs, launch gates, research notes. |
165
166
 
166
167
  ## Guardrails
@@ -77,8 +77,5 @@ Knowledge-related failures use the normal failure taxonomy:
77
77
  - `contradictory_evidence`
78
78
  - `ambiguous_user_intent`
79
79
 
80
- For optimization, scorers should use responsible surfaces such as
81
- `knowledge-requirements`, `data-acquisition`, `retrieval-policy`, and
82
- `user-question-policy` in actionable side information. That lets GEPA-style
83
- loops improve data acquisition and retrieval policy instead of blaming every
84
- failure on the prompt.
80
+ For optimization, scorers should identify responsible surfaces such as `knowledge-requirements`, `data-acquisition`, `retrieval-policy`, and `user-question-policy`.
81
+ That evidence lets an official optimization method or caller-owned `SurfaceProposer` improve acquisition and retrieval policy instead of blaming every failure on the prompt.
@@ -1,32 +1,45 @@
1
1
  # Improve One Surface
2
2
 
3
- `runImprovementLoop` generates candidate prompts or configs, runs them on training scenarios, and checks the selected candidate on separate holdout scenarios.
3
+ `runImprovementLoop()` evaluates candidates from a `SurfaceProposer`, selects one on training data, and compares it with the baseline on separate holdout cases.
4
4
 
5
- Use it when the question is: "Can this method improve my current surface?"
6
- Use [`compareOptimizationMethods`](./campaign-proposers.md#compare-complete-methods) when the question is: "Which complete optimization method performs best?"
5
+ Use it when your application or runtime owns candidate generation.
6
+ Use [`compareOptimizationMethods()`](./campaign-proposers.md) when GEPA, SkillOpt, or another external system owns the complete search procedure.
7
7
 
8
8
  ## Inputs
9
9
 
10
10
  | Input | Meaning |
11
11
  |---|---|
12
- | `baselineSurface` | Current prompt or config. |
13
- | `scenarios` | Training scenarios used to generate and score candidates. |
14
- | `holdoutScenarios` | Separate scenarios used by the release rule after search. |
15
- | `dispatchWithSurface` | Runs one scenario with one candidate surface. |
12
+ | `baselineSurface` | Current prompt or serialized configuration. |
13
+ | `scenarios` | Training cases used to generate and score candidates. |
14
+ | `holdoutScenarios` | Separate cases used for the release decision after search. |
15
+ | `dispatchWithSurface` | Runs one case with one candidate surface. |
16
16
  | `judges` | Scores the returned artifact. |
17
- | `proposer` | Generates candidate surfaces. |
18
- | `gate` | Applies the caller's release rule to baseline and winner results. |
19
- | `runDir` | Stores run artifacts, traces, and resumable state. |
17
+ | `proposer` | Caller-owned candidate generator. |
18
+ | `gate` | Caller-owned release rule for the baseline and selected candidate. |
19
+ | `runDir` | Directory for run artifacts, traces, and resumable state. |
20
20
 
21
21
  ## Example
22
22
 
23
23
  ```ts
24
24
  import {
25
25
  defaultProductionGate,
26
- gepaProposer,
27
26
  runImprovementLoop,
27
+ type SurfaceProposer,
28
28
  } from '@tangle-network/agent-eval/campaign'
29
29
 
30
+ const proposer: SurfaceProposer = {
31
+ kind: 'product-rules',
32
+ async propose({ currentSurface, populationSize }) {
33
+ return [
34
+ {
35
+ surface: `${String(currentSurface)}\nReturn JSON only.`,
36
+ label: 'json-only',
37
+ rationale: 'Training failures contained prose around the JSON object.',
38
+ },
39
+ ].slice(0, populationSize)
40
+ },
41
+ }
42
+
30
43
  const result = await runImprovementLoop({
31
44
  baselineSurface: currentSystemPrompt,
32
45
  scenarios: trainScenarios,
@@ -34,13 +47,9 @@ const result = await runImprovementLoop({
34
47
  dispatchWithSurface: async (surface, scenario, ctx) =>
35
48
  runYourAgent({ prompt: String(surface), scenario, signal: ctx.signal }),
36
49
  judges: [qualityJudge],
37
- proposer: gepaProposer({
38
- llm: { apiKey, baseUrl },
39
- model,
40
- target: 'the complete system prompt',
41
- }),
42
- populationSize: 4,
43
- maxGenerations: 4,
50
+ proposer,
51
+ populationSize: 1,
52
+ maxGenerations: 1,
44
53
  gate: defaultProductionGate({
45
54
  holdoutScenarios,
46
55
  deltaThreshold: 0,
@@ -54,14 +63,13 @@ if (result.gateResult.decision === 'ship') {
54
63
  }
55
64
  ```
56
65
 
57
- ## Behavior
66
+ ## Rules
58
67
 
59
- - Training and holdout scenario IDs must be disjoint.
60
- - Candidate generation cannot read holdout judge scores through `SurfaceProposer`.
61
- - The selected candidate is measured against the baseline on holdout scenarios.
62
- - A selected surface identical to the baseline is held instead of treating model variance as lift.
63
- - `result.cost` includes worker, candidate-generation, and judge calls recorded through the shared cost ledger.
64
- - `result.promotedDiff` describes the exact selected surface change.
68
+ - Training and holdout case IDs must be disjoint.
69
+ - Candidate generation cannot read holdout scores through `SurfaceProposer`.
70
+ - An unchanged selected surface does not receive credit for model variance.
71
+ - `result.cost` includes calls recorded through the shared cost ledger.
72
+ - `result.promotedDiff` identifies the exact selected change.
65
73
 
66
- The release decision is only as useful as the scenarios and judges supplied by the caller.
67
- Calibrate the judge on known strong and weak outputs before using it for promotion.
74
+ Calibrate the judge on known strong and weak artifacts before using its decision in production.
75
+ The runnable offline example is [`examples/multi-shot-optimization`](../examples/multi-shot-optimization/).