@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
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
# Improvement glossary + proposer chooser + composition
|
|
2
|
-
|
|
3
|
-
> **In plain terms:** this is the dictionary for the *improvement* half of the stack — the words that show up when you optimize an agent (proposer, surface, candidate, generation, holdout, lift, gate…) rather than when you *run* one (driver, worker, iteration — those live in [`agent-runtime/docs/glossary.md`](../../agent-runtime/docs/glossary.md)).
|
|
4
|
-
> Read this once and you can read any improvement result, pick a proposer, and wire two of them together.
|
|
5
|
-
|
|
6
|
-
**Who this is for.**
|
|
7
|
-
A **novice** (never seen the repo) should be able to read a `CampaignResult`, a `ProposeContext`, and a proposer chooser table without opening the source.
|
|
8
|
-
An **expert** who knows DSPy/GEPA should be able to map their existing mental model onto our names in about a minute — every core term below carries a *"if you know DSPy/GEPA"* line.
|
|
9
|
-
If code and this file disagree, the code wins — fix this file the same turn (the anti-staleness law).
|
|
10
|
-
|
|
11
|
-
Neighbors, so this page does not duplicate them: [`concepts.md`](./concepts.md) (eval mental model), [`self-improvement-map.md`](./self-improvement-map.md) (one loop / four roles / proposer catalog), [`campaign-proposers.md`](./campaign-proposers.md) (proposer ELI5), [`eval-surface-map.md`](./eval-surface-map.md) (which `run*` primitive), [`design/loop-taxonomy.md`](./design/loop-taxonomy.md) (execution vs proposer layering).
|
|
12
|
-
|
|
13
|
-
## The 60-second mental model
|
|
14
|
-
|
|
15
|
-
One loop improves one **surface**.
|
|
16
|
-
Every generation: a **proposer** reads what failed and emits **candidate** surfaces; a **campaign** measures each candidate by running the agent over **scenarios × reps** and **judging** every run; the loop ranks candidates and re-scores the best on a **holdout**; a **gate** decides whether that beat the **baseline** for real; if yes, it is **promoted**.
|
|
17
|
-
|
|
18
|
-
```text
|
|
19
|
-
baseline surface
|
|
20
|
-
└─► PROPOSER: read findings ─► N candidate surfaces (one GENERATION)
|
|
21
|
-
└─► CAMPAIGN: run agent on scenarios × reps ─► JUDGE each ─► composite
|
|
22
|
-
└─► rank candidates ─► re-score best on HOLDOUT
|
|
23
|
-
└─► GATE: did it beat baseline (significance)? ship / hold
|
|
24
|
-
repeat for maxGenerations
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Glossary — one plain sentence each
|
|
28
|
-
|
|
29
|
-
Grounded to `agent-eval/src/campaign/types.ts` unless noted.
|
|
30
|
-
|
|
31
|
-
| Term | Plain sentence | If you know DSPy/GEPA |
|
|
32
|
-
|---|---|---|
|
|
33
|
-
| **surface** | The one thing being changed this run — a prompt string, a JSON config string, or a finalized code candidate (`MutableSurface = string \| CodeSurface`, `types.ts`). | The optimized artifact: a signature/predictor's instruction text, or the module config. |
|
|
34
|
-
| **proposer** | The strategy that, given the current surface + what failed, proposes the next batch of candidate surfaces to measure — it does **not** run the agent or score anything (`SurfaceProposer.propose`, `types.ts:286`). | The optimizer / teleprompter (MIPRO, BootstrapFewShot, GEPA's reflective proposer). |
|
|
35
|
-
| **candidate** | One proposed surface plus its human `label` and `rationale`, ready to be measured (`ProposedCandidate`, `types.ts:166`). | One trial instruction/program the optimizer wants to evaluate. |
|
|
36
|
-
| **generation** | One round of *propose → measure → rank → promote*; the loop runs up to `maxGenerations` of them (`GenerationRecord`, `types.ts:549`). | One GEPA iteration / optimization step. |
|
|
37
|
-
| **populationSize** | BREADTH — how many candidate surfaces the proposer returns *this* generation (`ProposeContext.populationSize`, `types.ts:237`). Paired with `maxGenerations` (DEPTH) as the search budget. | Beam width / number of minibatch candidates per step. |
|
|
38
|
-
| **population budget** | Informal name for the pair `{ populationSize, maxGenerations }` — the total candidates the search may evaluate (breadth × depth). | Optimizer trial budget. |
|
|
39
|
-
| **campaign** | One measurement: run a dispatch over scenarios × seeds × reps, judge each output, aggregate → `CampaignResult` (`runCampaign`; a "campaign" = a coordinated batch of measurements). | One evaluation pass of a candidate over a valset. |
|
|
40
|
-
| **cell** | The atomic measurement unit — exactly one `(scenario, rep)` execution producing one artifact + its judge scores + its cost (`CampaignCellResult`, `types.ts:509`; `DispatchContext.cellId/rep`). | One (example, seed) evaluation datapoint. |
|
|
41
|
-
| **scenario** | One input case with a stable `id` + `kind` (consumers attach their payload: persona, task, requirement) (`Scenario`, `types.ts:23`). | One dataset example / `dspy.Example`. |
|
|
42
|
-
| **rep** | The repetition index — the same scenario run more than once so noise/variance is measurable rather than mistaken for signal (`ctx.rep`). | Repeated sampling of the same example (temperature/seed variance). |
|
|
43
|
-
| **judge** | A scorer: given an artifact, return dimensions + a single `composite` + free-form `notes`; it throws on failure rather than silently scoring 0 (`JudgeConfig` → `JudgeScore`, `types.ts:94/116`). | The metric function, but pluggable (LLM-judge, deterministic checks, or an ensemble). |
|
|
44
|
-
| **composite** | The single 0..1 number that combines all judge dimensions — the number you rank and gate on (`JudgeScore.composite`, `types.ts:116`). | The scalar metric value. |
|
|
45
|
-
| **findings** | The failure analysis handed to the proposer so it edits from evidence, not guesses — worst cells + judge reasons, or a trace-analyst's clusters (`ProposeContext.findings`). | GEPA's reflective feedback / the "textual gradient". |
|
|
46
|
-
| **baseline** | The starting surface (the current prompt/config) every candidate is measured against (`baselineSurface`). | The unoptimized program you compare lift against. |
|
|
47
|
-
| **holdout** | A separate scenario split the winner is re-scored on and that the proposer is *never* allowed to see — a compile-time firewall (`ProposeContext.judgeScores: never`, `types.ts:265`) keeps held-out verdicts out of proposal so the optimizer can't game the acceptance axis. | The held-out valset/testset — but here it is *write-only* to the optimizer. |
|
|
48
|
-
| **lift** | Winner minus baseline `composite` on the holdout — the actual improvement, reported with a bootstrap confidence interval (`ImproveResult.lift`, `ProposerScore.lift`). | Δ metric between optimized and baseline program. |
|
|
49
|
-
| **MDE** | Minimum Detectable Effect — the smallest lift your budget could statistically distinguish from noise, computed up front from baseline cells; a structurally-hopeless budget warns before you spend (surfaced as `result.power` / "power preflight", `agent-optimization-map.md`). Not spelled "MDE" in code yet — this is the standard name for it. | Power analysis on the eval set; the reason a tiny valset can't certify a small gain. |
|
|
50
|
-
| **Pareto frontier** | The set of surfaces that are non-dominated across the per-scenario score vectors — a candidate worse on the mean but uniquely best on one hard scenario survives, so its lesson is not discarded (`ParetoParent`, `types.ts:198`; GEPA, arXiv:2507.19457). | Exactly GEPA's Pareto candidate pool — same paper, same idea. |
|
|
51
|
-
| **gate** | The promotion decision: does the winner beat baseline on the holdout with significance, returning one of five verdicts `ship / hold / need_more_work / model_ceiling / arch_ceiling` (`Gate`, `GateDecision`, `types.ts:315/340`). | The accept/reject rule on the held-out valset, plus a significance test and ceiling diagnosis. |
|
|
52
|
-
| **promotion** | What happens on a `ship` verdict — the winning surface is written back into the profile field it came from (`GenerationRecord.promoted`; `applyWinnerToProfile`, `improve.ts`). | Committing the optimized program as the new default. |
|
|
53
|
-
|
|
54
|
-
## Which proposer — the chooser
|
|
55
|
-
|
|
56
|
-
Every optimizer is a factory `xProposer(opts): SurfaceProposer`, all exported from `@tangle-network/agent-eval/campaign`.
|
|
57
|
-
Start at the top row and only move down when the row's *"reach for it when"* matches your failure mode.
|
|
58
|
-
|
|
59
|
-
| Proposer factory | Reach for it when | Surface it edits | Wired to the paved path? |
|
|
60
|
-
|---|---|---|---|
|
|
61
|
-
| `gepaProposer` | You want the strong default: reflective full-surface prompt rewrites, grounded in findings, keeping a Pareto frontier of complementary winners. | prompt string | **Yes — `improve({ surface: 'prompt' })` default.** Proven live. |
|
|
62
|
-
| `skillOptProposer` | You are editing a structured `SKILL.md`/runbook and want small anchored add/delete/replace patches that preserve earlier rules. | skill/prompt string | **Yes — `improve({ surface: 'skills' })` default.** Not yet proven live. |
|
|
63
|
-
| `parameterSweepProposer` | The likely fix is a config knob, not words — `retrieval.k`, `temperature`, `max_tokens`. You give it candidate patches; it applies them to a JSON surface. | JSON config string | Yes, but you supply the candidate list. |
|
|
64
|
-
| `fapoProposer` | You want *evidence to decide when to escalate*: try prompt edits first, move to parameters, then to structural code — one scoped change per cycle, only escalating when the cheaper level is exhausted. | whatever its level proposers return | Exported; you wire the level proposers. |
|
|
65
|
-
| `compositeProposer` | You want several proposers to share one candidate-generation budget in the same round. It allocates the population by declared weights, preserves member provenance, deduplicates surfaces, and isolates a member failure unless every member fails. | whatever its member proposers return | Exported; you wire the member proposers. |
|
|
66
|
-
| `aceProposer` | You are accumulating hard-won lessons into a playbook and must **never** summarize an old lesson away (append-only, provenance-tagged). | playbook string | Exported. |
|
|
67
|
-
| `memoryCurationProposer` | Same as ACE but you want a compact, deduped, re-ranked memory instead of append-only growth. | memory string | Exported. |
|
|
68
|
-
| `evolutionaryProposer` | You want blind population search (mutate → measure → select) with no reflection over findings — a cheap control or a baseline to beat. | any string | Exported. |
|
|
69
|
-
| `traceAnalystProposer` | Bench-only: race our trace-analysis evidence engine head-to-head inside `compareProposers`. | prompt string | Bench-only. |
|
|
70
|
-
| `haloProposer` | Bench-only: race the external `halo-engine` analysis against ours. | prompt string | Bench-only, external. |
|
|
71
|
-
|
|
72
|
-
Default path: `gepaProposer` for prompts; add `parameterSweepProposer` when a config knob is the suspect; wrap levels in `fapoProposer` when the loop should decide *when* to escalate, or use `compositeProposer` when multiple proposer families must split one fixed population budget.
|
|
73
|
-
|
|
74
|
-
## Composing proposers — four distinct shapes
|
|
75
|
-
|
|
76
|
-
Choose the shape that matches the experiment:
|
|
77
|
-
|
|
78
|
-
1. **Portfolio** — `compositeProposer` splits one generation's population across member proposers by fixed weights and returns one provenance-labelled pool.
|
|
79
|
-
2. **Escalate** — `fapoProposer` wraps prompt + parameter + structural levels into one proposer and spends on the cheapest level until evidence says to escalate.
|
|
80
|
-
3. **Race** — `compareProposers` gives proposers separate loops, then re-scores their winners on one holdout and returns per-proposer lift intervals plus pairwise results.
|
|
81
|
-
4. **Plug in** — hand any proposer to `runImprovementLoop({ proposer })`, or use `improve({ surface, generator })` in `@tangle-network/agent-runtime`.
|
|
82
|
-
|
|
83
|
-
### 2 + 4 — compose by escalation, then run the improvement loop
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
import {
|
|
87
|
-
fapoProposer,
|
|
88
|
-
gepaProposer,
|
|
89
|
-
parameterSweepProposer,
|
|
90
|
-
defaultProductionGate,
|
|
91
|
-
runImprovementLoop,
|
|
92
|
-
} from '@tangle-network/agent-eval/campaign'
|
|
93
|
-
|
|
94
|
-
const llm = { baseUrl: process.env.TANGLE_BASE_URL, apiKey: process.env.TANGLE_API_KEY }
|
|
95
|
-
const model = 'deepseek-v4-flash'
|
|
96
|
-
|
|
97
|
-
// Compose by escalation: prompt edits first (GEPA), then a config knob only
|
|
98
|
-
// when prompt-level search plateaus. This is distinct from a peer portfolio.
|
|
99
|
-
const proposer = fapoProposer({
|
|
100
|
-
scope: { allowedLevels: ['prompt', 'parameter'] }, // no structural/code tier here
|
|
101
|
-
promptProposer: gepaProposer({ llm, model, target: 'agent system prompt' }),
|
|
102
|
-
parameterProposer: parameterSweepProposer({
|
|
103
|
-
candidates: [
|
|
104
|
-
{
|
|
105
|
-
label: 'raise-retrieval-k',
|
|
106
|
-
rationale: 'retrieval-miss findings suggest the search budget is too low',
|
|
107
|
-
changes: [{ path: 'retrieval.k', value: 10 }],
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
}),
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
// dispatchWithSurface scores ONE surface on ONE scenario — this is the topology-
|
|
114
|
-
// opaque seam (one LLM call, one worker, or a whole fleet — the loop can't tell).
|
|
115
|
-
const dispatchWithSurface = async (surface, scenario, ctx) =>
|
|
116
|
-
runYourAgent({ surface, scenario, signal: ctx.signal }) // returns the artifact to judge
|
|
117
|
-
|
|
118
|
-
const result = await runImprovementLoop({
|
|
119
|
-
scenarios: trainScenarios, // proposer trains on these
|
|
120
|
-
holdoutScenarios, // winner is re-scored here; proposer never sees them
|
|
121
|
-
baselineSurface: currentPromptString,
|
|
122
|
-
dispatchWithSurface,
|
|
123
|
-
judges, // JudgeConfig[]
|
|
124
|
-
proposer,
|
|
125
|
-
gate: defaultProductionGate({ holdoutScenarios, deltaThreshold: 0 }),
|
|
126
|
-
populationSize: 2, // BREADTH per generation
|
|
127
|
-
maxGenerations: 6, // DEPTH
|
|
128
|
-
autoOnPromote: 'none', // 'pr' to open a PR on ship
|
|
129
|
-
runDir: '/tmp/improve-run', // a REAL path makes the run durable
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
result.winnerSurface // the promoted surface
|
|
133
|
-
result.gateDecision // 'ship' | 'hold' | 'need_more_work' | 'model_ceiling' | 'arch_ceiling'
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Same thing, one line, when you have an `AgentProfile` (facade in `@tangle-network/agent-runtime`):
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
import { improve } from '@tangle-network/agent-runtime'
|
|
140
|
-
|
|
141
|
-
// surface 'prompt'/'skills' pick their proposer automatically; pass `generator`
|
|
142
|
-
// to plug the composed FAPO proposer above into the gated loop instead.
|
|
143
|
-
const out = await improve(profile, findings, {
|
|
144
|
-
surface: 'prompt',
|
|
145
|
-
generator: proposer, // any SurfaceProposer, incl. the composed one
|
|
146
|
-
scenarios, judge, agent, runDir: '/tmp/improve-run',
|
|
147
|
-
})
|
|
148
|
-
if (out.shipped) deploy(out.profile) // out.lift is the held-out winner − baseline
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### 3 — race proposers head-to-head for a lift CI
|
|
152
|
-
|
|
153
|
-
```ts
|
|
154
|
-
import {
|
|
155
|
-
compareProposers,
|
|
156
|
-
gepaParetoEntry,
|
|
157
|
-
fapoEscalationEntry,
|
|
158
|
-
} from '@tangle-network/agent-eval/campaign'
|
|
159
|
-
|
|
160
|
-
const config = {
|
|
161
|
-
baselineSurface: currentPromptString,
|
|
162
|
-
trainScenarios,
|
|
163
|
-
holdoutScenarios,
|
|
164
|
-
dispatchWithSurface,
|
|
165
|
-
judges,
|
|
166
|
-
llm, model,
|
|
167
|
-
target: 'agent system prompt',
|
|
168
|
-
runDir: '/tmp/compare-run',
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const comparison = await compareProposers({
|
|
172
|
-
proposers: [
|
|
173
|
-
gepaParetoEntry(config), // GEPA + Pareto frontier
|
|
174
|
-
fapoEscalationEntry({ // FAPO escalation policy
|
|
175
|
-
...config,
|
|
176
|
-
parameterCandidates: [
|
|
177
|
-
{ label: 'raise-retrieval-k', rationale: 'retrieval misses', changes: [{ path: 'retrieval.k', value: 10 }] },
|
|
178
|
-
],
|
|
179
|
-
}),
|
|
180
|
-
],
|
|
181
|
-
baselineSurface: currentPromptString,
|
|
182
|
-
holdoutScenarios,
|
|
183
|
-
dispatchWithSurface,
|
|
184
|
-
judges,
|
|
185
|
-
runDir: '/tmp/compare-run',
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
comparison.best // highest-lift proposer
|
|
189
|
-
comparison.scores // per-proposer { lift, liftCi:{low,high}, cost } — low>0 ⇒ real gain
|
|
190
|
-
comparison.pairwise // best vs each other, paired-bootstrap: 'a' | 'b' | 'tie'
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Every entrant is re-scored on the **same** holdout with the **same** judges, so the comparison never trusts how a proposer measured itself — the only variable is proposal quality.
|
|
194
|
-
|
|
195
|
-
## Common traps (they cost the most)
|
|
196
|
-
|
|
197
|
-
- Do not put eval logic inside a proposer — scoring lives in `dispatch` + `judges`, proposing lives in the proposer.
|
|
198
|
-
- Do not let a proposer read held-out judge scores — `ProposeContext` makes that a compile error on purpose; a proposer that games the acceptance axis is an oracle, not an optimizer.
|
|
199
|
-
- Do not read `lift` without `result.power`/MDE — a "+4" on a valset too small to detect +4 is noise wearing a number.
|
|
200
|
-
- Do not confuse `compositeProposer` with `fapoProposer`: the former allocates one fixed population across peers, while the latter escalates through ordered levels from cheaper to more structural changes.
|
|
201
|
-
|
|
202
|
-
### neutralizationGate — the placebo / content-causality control
|
|
203
|
-
|
|
204
|
-
Standard gates prove a candidate *beat baseline*. `neutralizationGate` proves the candidate's **content** caused the lift, not the extra prompt bytes: it blanks the candidate's added content to byte-length-matched filler, holds everything else fixed, and requires the lift to vanish. A fully-neutralized candidate that still scores is decorative and is rejected. Exports: `neutralizationGate` (`src/campaign/gates/neutralization-gate.ts`), `neutralizeText` (`src/campaign/neutralize.ts`). Since 0.107.0. Pair it with `heldOutGate` in any loop that promotes authored artifacts (prompts, tool docs, knowledge) so a lift that is really just added prompt size cannot be shipped.
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# The self-improvement map
|
|
2
|
-
|
|
3
|
-
One loop. Four roles. A proposer catalog of pluggable strategies. A bench rig that
|
|
4
|
-
proves the loop produces real lift. Nothing here is duplicated — it is one engine
|
|
5
|
-
pointed at different surfaces. This map exists because the surface count makes it
|
|
6
|
-
*look* like many competing systems when it is one.
|
|
7
|
-
|
|
8
|
-
Neighbors: [`concepts.md`](./concepts.md) (the mental model), [`trace-analysis.md`](./trace-analysis.md)
|
|
9
|
-
(the evidence engine), [`distributed-driver.md`](./distributed-driver.md) (running the loop across cells).
|
|
10
|
-
|
|
11
|
-
## The one loop
|
|
12
|
-
|
|
13
|
-
`runImprovementLoop()` (wrapped by `selfImprove()` for the one-call surface). Every
|
|
14
|
-
product imports the same function. Each generation it does four things:
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
run AGENT on SCENARIOS ──► JUDGE scores each run
|
|
18
|
-
│
|
|
19
|
-
PROPOSER reads the failures and
|
|
20
|
-
proposes better SURFACE versions
|
|
21
|
-
│
|
|
22
|
-
GATE: did a candidate beat the parent on a
|
|
23
|
-
HELD-OUT split, for real (significance test)?
|
|
24
|
-
│ yes → promote │ no → discard
|
|
25
|
-
│
|
|
26
|
-
repeat N generations
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## The four roles — keep them separate and the confusion clears
|
|
30
|
-
|
|
31
|
-
| Role | What it is | Plain meaning |
|
|
32
|
-
|---|---|---|
|
|
33
|
-
| **Surface** | a *string* — an agent directive, a `SKILL.md`, a playbook, a memory, a judge rubric | **what** gets improved |
|
|
34
|
-
| **Proposer** | a `SurfaceProposer` (the catalog below) | **how** candidate surfaces are proposed |
|
|
35
|
-
| **Gate** | held-out split + significance (`paretoSignificanceGate` / `heldOutGate` / `defaultProductionGate`) | **did it actually get better**, vs noise |
|
|
36
|
-
| **Judge** | scores a run | **how good** any version is |
|
|
37
|
-
|
|
38
|
-
## The proposer catalog (one loop, multiple strategies)
|
|
39
|
-
|
|
40
|
-
The package intentionally exposes named proposer factories instead of a hidden
|
|
41
|
-
auto-selector. The split that matters: **production proposers** mutate a live
|
|
42
|
-
surface; **bench-only proposers** exist solely to be raced inside
|
|
43
|
-
`compareProposers`.
|
|
44
|
-
|
|
45
|
-
| Proposer factory | Surface | Strategy | Role | Notes |
|
|
46
|
-
|---|---|---|---|---|
|
|
47
|
-
| `gepaProposer` | prompt | reflective full-surface rewrite + Pareto frontier | **production default** | consumes trace-analysis findings — see below |
|
|
48
|
-
| `fapoProposer` | prompt/config/code | reviewed escalation policy over prompt → parameter → structural proposers | production, benchmark | encodes FAPO's scope + reviewer + prompt-first escalation rules; structural generator is injected |
|
|
49
|
-
| `parameterSweepProposer` | config | JSON config patch/sweep | production, benchmark | middle FAPO level for parameter/config edits such as `retrieval.k`, `temperature`, `max_tokens` |
|
|
50
|
-
| `skillOptProposer` | skill-doc | anchored add/delete/replace patch | production | preserves earlier rules; edit budget = "textual learning rate" |
|
|
51
|
-
| `aceProposer` | playbook | append-only, provenance-tagged | production | accumulate hard-won lessons, never summarize away |
|
|
52
|
-
| `memoryCurationProposer` | memory | dedup + rank + graft | production | compact alternative to `ace` |
|
|
53
|
-
| `evolutionaryProposer` | any | population mutate → measure → select | production | blind search; no reflection over findings |
|
|
54
|
-
| `traceAnalystProposer` | prompt | analysis → one LLM edit | **bench-only** | our evidence engine, wrapped as a proposer |
|
|
55
|
-
| `haloProposer` | prompt | analysis → one LLM edit | **bench-only**, external | wraps `pip install halo-engine` (Inference.net) |
|
|
56
|
-
|
|
57
|
-
Default choice: start with `gepaProposer` for prompt surfaces, add
|
|
58
|
-
`parameterSweepProposer` when config knobs are the likely failure mode, and wrap
|
|
59
|
-
them with `fapoProposer` when evidence should decide when to escalate.
|
|
60
|
-
|
|
61
|
-
## Trace analysis — what it is and the three places it is used
|
|
62
|
-
|
|
63
|
-
"Trace analysis" is the **evidence layer**: it turns raw OTLP traces into "here is
|
|
64
|
-
exactly *why* the agent failed" (failure clusters → findings). The engine is
|
|
65
|
-
`analyzeRuns()` + the analyst registry (`src/contract/analyze-runs.ts`). It is used in
|
|
66
|
-
three places — this is the answer to "if GEPA does its own thing, what is trace
|
|
67
|
-
analysis *for*?":
|
|
68
|
-
|
|
69
|
-
1. **Ships to customers** — `analyzeRuns()` → `InsightReport`, the Intelligence product.
|
|
70
|
-
2. **Feeds the proposer** — `gepaProposer` calls `renderAnalystEvidence(ctx.findings,
|
|
71
|
-
ctx.report)` (`src/campaign/proposers/gepa.ts`). GEPA's rewrites are grounded
|
|
72
|
-
in the diagnosis instead of guessing blind. Trace analysis
|
|
73
|
-
**is** on the GEPA side.
|
|
74
|
-
3. **Races HALO** — wrapped as `traceAnalystProposer` so our analysis competes
|
|
75
|
-
head-to-head with the external SOTA inside `compareProposers`.
|
|
76
|
-
|
|
77
|
-
## Where HALO fits (and why it feels "removed")
|
|
78
|
-
|
|
79
|
-
`haloProposer` is alive (`src/campaign/proposers/halo.ts`, exported from the campaign
|
|
80
|
-
barrel) but it is **never in the product loop**. It shells out to an *external* engine
|
|
81
|
-
(`halo-engine`) — so the analysis genuinely lives outside this repo; we only wrap it.
|
|
82
|
-
|
|
83
|
-
Its only job is the **bake-off**. HALO's real opponent is **not** `gepaProposer` — it is
|
|
84
|
-
`traceAnalystProposer`. `compareProposers` holds the apply step identical (same
|
|
85
|
-
`APPLY_SYSTEM`, same `traces.jsonl`, same held-out scoring) so the only variable is
|
|
86
|
-
**analysis quality: HALO vs ours.** A measuring stick, like a benchmark baseline.
|
|
87
|
-
|
|
88
|
-
## `gepa-refine.ts` is the loop on a test bench, not a second loop
|
|
89
|
-
|
|
90
|
-
`agent-runtime/bench/src/gepa-refine.ts` runs **this same loop** against a *public
|
|
91
|
-
benchmark* (AppWorld, CAD, …) instead of product data. Why a separate rig:
|
|
92
|
-
|
|
93
|
-
- On product traces, "+4 lift" can be model noise or a judge flattering itself — no
|
|
94
|
-
ground truth.
|
|
95
|
-
- On a benchmark the score is **objective and ungameable** (AppWorld runs the agent's
|
|
96
|
-
code against its own unit tests). If a GEPA-optimized directive beats a deliberately
|
|
97
|
-
weak baseline **on held-out tasks it never trained on**, with a CI excluding zero,
|
|
98
|
-
that is a *certified* proof the loop produces real lift.
|
|
99
|
-
|
|
100
|
-
Products run the loop to get **better**. `gepa-refine` runs the identical loop to
|
|
101
|
-
**prove the loop works at all**.
|
|
102
|
-
|
|
103
|
-
## Where the "mess" feeling actually comes from
|
|
104
|
-
|
|
105
|
-
The code is well-factored; the confusion is narrative:
|
|
106
|
-
|
|
107
|
-
- **Surface sprawl reads as chaos.** Several proposers with overlapping shapes *look* like
|
|
108
|
-
competing loops. They are exported factories for one loop; this map makes that
|
|
109
|
-
explicit.
|
|
110
|
-
- **The real gap is the missing proof, not the design.** The loop kept being proved on
|
|
111
|
-
benchmarks too easy to show value: when a capable model ceilings an extraction task,
|
|
112
|
-
**0 findings fire** and the whole trace-analysis→optimizer apparatus is inert. It
|
|
113
|
-
earns its keep only on **hard agentic tasks** — which is why the AppWorld REPL run
|
|
114
|
-
(multi-turn, real tool execution, unbounded turns) is the one that can finally
|
|
115
|
-
separate the evidence-grounded optimizer from baseline.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|