@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
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,8 @@ All notable changes to `@tangle-network/agent-eval` and its sibling `agent-eval-
|
|
|
8
8
|
|
|
9
9
|
### Added
|
|
10
10
|
|
|
11
|
+
- `callLlmJson()` accepts `jsonPayloadMode: 'exact'` when callers must reject fenced, prose-wrapped, or multi-root responses instead of extracting a JSON value.
|
|
12
|
+
- `llmPolicyEditProposer({ redactCurrentSurfaceForModel })` can remove credentials and unrelated fields from the current surface sent to the model while applying validated edits to the complete original surface.
|
|
11
13
|
- `CostLedger.listPending()` exposes immutable pending paid calls and distinguishes calls that are active, late after cancellation, or interrupted by a prior process so durable workflows can reconcile exact reservations before resuming.
|
|
12
14
|
- `traceAnalystProposer()` accepts an opt-in `resolvePriorFindings` callback that forwards canonical prior findings into the existing analyst registry.
|
|
13
15
|
|
|
@@ -20,8 +22,35 @@ All notable changes to `@tangle-network/agent-eval` and its sibling `agent-eval-
|
|
|
20
22
|
|
|
21
23
|
### Fixed
|
|
22
24
|
|
|
25
|
+
- `llmPolicyEditProposer()` asks the model only for semantic edit choices, deterministically binds caller-owned policy fields, and requires one exact whole-response JSON value before admission.
|
|
26
|
+
- `compareOptimizationMethods()` owns three non-empty, pairwise-disjoint scenario sets.
|
|
27
|
+
Methods receive independent copies of train and selection data, every method finishes before final test scoring starts, and final test data is absent from `OptimizationMethodInput`.
|
|
28
|
+
Built-in GEPA, SkillOpt, and FAPO methods read the shared baseline, runner, judges, directories, and execution settings from the comparison call instead of duplicating them in each method config.
|
|
29
|
+
Final test scoring uses one shared spend limit.
|
|
30
|
+
Reused cost ledgers isolate comparison receipts from unrelated calls, and final test scenarios are copied for each measured surface.
|
|
31
|
+
Results report optimization, test, and total cost with accounting completeness and reasons for unknown charges.
|
|
32
|
+
Per-method results include the paired scenario values used to compute lift.
|
|
33
|
+
Cost orders a tied-lift group only when every method in that group has complete accounting.
|
|
34
|
+
Bootstrap intervals adjust across all method contrasts, and the default resample count increases when needed to represent the adjusted tails.
|
|
35
|
+
`optimizationConcurrency` can run independent methods in parallel.
|
|
36
|
+
- `runSkillOpt()` validates every numeric control before scoring, enforces `patchesPerEpoch` for custom proposers, and no longer mutates the caller's `runDir`.
|
|
37
|
+
- Include the complete required response shape in `llmPolicyEditProposer` model instructions, and add `LlmClientOptions.jsonSchemaTransport: 'json-object'` for providers that do not implement native JSON Schema enforcement.
|
|
38
|
+
- `compositeProposer` restores each member's original labels when replaying history, so stateful members do not repeat candidates whose labels were decorated for provenance.
|
|
39
|
+
- `InsightReport.interRater.kappa` now reports quadratic weighted kappa instead of Pearson correlation.
|
|
40
|
+
Read `interRater.pearson` for the previous correlation measure; `icc` and `spearman` are now reported separately.
|
|
23
41
|
- Compute contextual-bandit doubly robust estimates with separate logged-action and target-policy value terms, expose how many rows use DR versus IPS or the deprecated scalar path, and carry both values through belief-state records.
|
|
24
42
|
|
|
43
|
+
### Breaking
|
|
44
|
+
|
|
45
|
+
- `compareProposers()` is replaced by `compareOptimizationMethods()`.
|
|
46
|
+
Rename `proposers` to `methods`, `ProposerEntry` to `OptimizationMethod`, and the built-in `*Entry` factories to `*Method`.
|
|
47
|
+
Pass `trainScenarios`, `selectionScenarios`, and `testScenarios`; the ambiguous `holdoutScenarios` option is rejected.
|
|
48
|
+
`OptimizerEntryConfig` is replaced by `BuiltinOptimizationMethodConfig`, which contains method-specific settings only.
|
|
49
|
+
Move shared method execution settings to `optimizationRunOptions` on the comparison call.
|
|
50
|
+
Read `optimizationCost`, `testCost`, and `totalCost`; each includes `totalCostUsd`, `accountingComplete`, and `incompleteReasons`.
|
|
51
|
+
- `runSkillOpt({ holdoutScenarios })` fails closed because those rows are adaptively reused.
|
|
52
|
+
Pass `selectionScenarios`; selection result fields now use `Selection` instead of `Holdout`, and `lift` is now `selectionLift`.
|
|
53
|
+
|
|
25
54
|
## [0.122.2] — 2026-07-17 — premeasured optimization continuation
|
|
26
55
|
|
|
27
56
|
### Added
|
package/README.md
CHANGED
|
@@ -1,202 +1,255 @@
|
|
|
1
1
|
# `@tangle-network/agent-eval`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
`agent-eval` turns agent outputs, traces, judge scores, and production feedback into a decision packet: did this change help, what failed, what should ship, and what needs more data?
|
|
3
|
+
A TypeScript library that measures whether your AI agent got better or worse, using the runs it already produces.
|
|
6
4
|
|
|
7
5
|
[](https://www.npmjs.com/package/@tangle-network/agent-eval)
|
|
8
6
|
[](https://pypi.org/project/agent-eval-rpc/)
|
|
9
7
|
[](https://github.com/tangle-network/agent-eval/actions/workflows/ci.yml)
|
|
10
8
|
[](./LICENSE)
|
|
11
9
|
|
|
10
|
+
You give it agent runs: outputs, traces, scores, and production feedback.
|
|
11
|
+
It gives you numbers you can act on: how much the new prompt changed outcomes, how uncertain that estimate is, what failed and why, and whether the change meets your release rule.
|
|
12
|
+
|
|
12
13
|
Use it when you need to:
|
|
13
14
|
|
|
14
|
-
- compare a candidate
|
|
15
|
-
- turn production traces or human feedback into eval results,
|
|
16
|
-
- run
|
|
17
|
-
- explain failures by cluster, cost, judge disagreement
|
|
15
|
+
- compare a candidate prompt/model/config against a baseline, with confidence intervals instead of vibes,
|
|
16
|
+
- turn production traces or human feedback you already collect into eval results,
|
|
17
|
+
- run an automated improve-and-verify loop over a prompt, held to a promotion rule you choose,
|
|
18
|
+
- explain failures by cluster, cost, and judge disagreement.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
The deterministic evaluator runs in your process and makes no network calls.
|
|
21
|
+
Features that use a model send their inputs to the model client you pass.
|
|
22
|
+
Trace exporters and hosted ingestion are also opt-in.
|
|
23
|
+
Python can drive the same engine over HTTP via [`agent-eval-rpc`](./clients/python/README.md).
|
|
20
24
|
|
|
21
25
|
---
|
|
22
26
|
|
|
23
27
|
## Install
|
|
24
28
|
|
|
25
29
|
```sh
|
|
26
|
-
pnpm add @tangle-network/agent-eval
|
|
30
|
+
pnpm add @tangle-network/agent-eval # or npm / yarn
|
|
27
31
|
```
|
|
28
32
|
|
|
29
|
-
Python clients can use the RPC package:
|
|
30
|
-
|
|
31
33
|
```sh
|
|
32
|
-
pip install agent-eval-rpc
|
|
34
|
+
pip install agent-eval-rpc # optional Python client
|
|
33
35
|
```
|
|
34
36
|
|
|
35
37
|
---
|
|
36
38
|
|
|
37
|
-
##
|
|
38
|
-
|
|
39
|
-
### 1. Analyze runs you already have
|
|
39
|
+
## Quickstart
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Copy this into `quickstart.ts` and run `npx tsx quickstart.ts`.
|
|
42
|
+
It is fully offline; the "agent" and "judge" are plain functions you replace with your own.
|
|
42
43
|
|
|
43
44
|
```ts
|
|
44
|
-
import {
|
|
45
|
+
import { defineAgentEval } from '@tangle-network/agent-eval/contract'
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
interface SupportScenario {
|
|
48
|
+
id: string
|
|
49
|
+
kind: 'support'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function main() {
|
|
53
|
+
const scenarios: SupportScenario[] = [
|
|
54
|
+
{ id: 'refund', kind: 'support' },
|
|
55
|
+
{ id: 'shipping', kind: 'support' },
|
|
56
|
+
{ id: 'cancel', kind: 'support' },
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
const evalKit = defineAgentEval<SupportScenario, string>({
|
|
60
|
+
scenarios,
|
|
61
|
+
// Your agent takes the prompt under test and one scenario, then returns its output.
|
|
62
|
+
agent: async (prompt, scenario) =>
|
|
63
|
+
String(prompt).includes('ticket') ? `Re ${scenario.id}: on it.` : 'On it.',
|
|
64
|
+
// Your judge scores one output from 0 to 1. Swap in an LLM judge for real work.
|
|
65
|
+
judge: {
|
|
66
|
+
name: 'cites-ticket',
|
|
67
|
+
dimensions: [{ key: 'ticket_id', description: 'The answer includes the ticket id' }],
|
|
68
|
+
score: ({ artifact, scenario }) => {
|
|
69
|
+
const ticketId = artifact.includes(scenario.id) ? 1 : 0
|
|
70
|
+
return { dimensions: { ticket_id: ticketId }, composite: ticketId, notes: '' }
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
baselineSurface: 'Answer the customer politely.',
|
|
74
|
+
expectUsage: 'off',
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
console.log('baseline: ', (await evalKit.evaluate()).aggregates.byJudge)
|
|
78
|
+
const candidate = await evalKit.evaluate({ surface: 'Answer politely, cite the ticket id.' })
|
|
79
|
+
console.log('candidate:', candidate.aggregates.byJudge)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
main().catch((error: unknown) => {
|
|
83
|
+
console.error(error)
|
|
84
|
+
process.exitCode = 1
|
|
49
85
|
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Output:
|
|
50
89
|
|
|
51
|
-
console.log(report.recommendations)
|
|
52
|
-
console.log(report.lift)
|
|
53
|
-
console.log(report.failureClusters)
|
|
54
90
|
```
|
|
91
|
+
baseline: { 'cites-ticket': { mean: 0, stdev: 0, ci95: [ 0, 0 ], n: 3 } }
|
|
92
|
+
candidate: { 'cites-ticket': { mean: 1, stdev: 0, ci95: [ 1, 1 ], n: 3 } }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Each `evaluate()` call runs every scenario through the agent, scores each output with the judge, and returns per-judge score distributions.
|
|
96
|
+
The "surface" is the thing you are changing: here a system-prompt string, and in general any prompt or config value.
|
|
97
|
+
From the same definition, `evalKit.improve()` proposes candidate prompts, measures each one, and checks the winner against a held-back scenario set before recommending it.
|
|
98
|
+
The default candidate generator calls a model, so pass `llm: { baseUrl, apiKey, model }` to `.improve()` or provide your own `proposer`.
|
|
55
99
|
|
|
56
|
-
|
|
100
|
+
Already have run data and no runnable agent? Skip the loop and call [`analyzeRuns()`](./docs/concepts.md#the-top-level-functions) on your existing records instead.
|
|
57
101
|
|
|
58
|
-
###
|
|
102
|
+
### Use A Model Judge
|
|
59
103
|
|
|
60
|
-
|
|
104
|
+
`llmJudge()` converts one model call into the same `JudgeConfig` used above:
|
|
61
105
|
|
|
62
106
|
```ts
|
|
63
|
-
import {
|
|
107
|
+
import { createChatClient, llmJudge } from '@tangle-network/agent-eval/contract'
|
|
64
108
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
agent: async (surface, scenario, ctx) =>
|
|
68
|
-
myAgent.run({ scenario, systemPrompt: String(surface), signal: ctx.signal }),
|
|
69
|
-
judge: myJudge,
|
|
70
|
-
baselineSurface: currentPrompt,
|
|
71
|
-
})
|
|
109
|
+
const apiKey = process.env.OPENAI_API_KEY
|
|
110
|
+
if (!apiKey) throw new Error('OPENAI_API_KEY is required')
|
|
72
111
|
|
|
73
|
-
const
|
|
74
|
-
|
|
112
|
+
const chat = createChatClient({
|
|
113
|
+
transport: 'direct-provider',
|
|
114
|
+
baseUrl: process.env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1',
|
|
115
|
+
apiKey,
|
|
116
|
+
defaultModel: 'gpt-4.1-mini',
|
|
117
|
+
})
|
|
75
118
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
119
|
+
const judge = llmJudge<string, SupportScenario>(
|
|
120
|
+
'support-quality',
|
|
121
|
+
'Score whether the response resolves the request using only supported facts.',
|
|
122
|
+
{
|
|
123
|
+
chat,
|
|
124
|
+
dimensions: [
|
|
125
|
+
{ key: 'correct', description: 'The answer is factually correct' },
|
|
126
|
+
{ key: 'complete', description: 'The answer addresses the whole request' },
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
)
|
|
80
130
|
```
|
|
81
131
|
|
|
82
|
-
`
|
|
132
|
+
Pass `judge` to `defineAgentEval()` in place of the offline judge.
|
|
133
|
+
The model receives the scenario, artifact, scoring prompt, and dimension descriptions.
|
|
83
134
|
|
|
84
|
-
###
|
|
135
|
+
### Compare Optimization Methods
|
|
85
136
|
|
|
86
|
-
|
|
87
|
-
|
|
137
|
+
Use `compareOptimizationMethods()` when you need to compare complete search procedures rather than individual prompts.
|
|
138
|
+
The function gives every method the same baseline, runner, judges, train data, and selection data, then ranks their selected surfaces on separate final test data.
|
|
88
139
|
|
|
89
|
-
|
|
90
|
-
|
|
140
|
+
```ts
|
|
141
|
+
import {
|
|
142
|
+
type BuiltinOptimizationMethodConfig,
|
|
143
|
+
compareOptimizationMethods,
|
|
144
|
+
gepaParetoMethod,
|
|
145
|
+
gepaReflectionMethod,
|
|
146
|
+
skillOptMethod,
|
|
147
|
+
} from '@tangle-network/agent-eval/campaign'
|
|
148
|
+
|
|
149
|
+
const methodConfig: BuiltinOptimizationMethodConfig<MyScenario, MyArtifact> = {
|
|
150
|
+
llm,
|
|
151
|
+
model,
|
|
152
|
+
target: 'the complete prompt being improved',
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const result = await compareOptimizationMethods<MyScenario, MyArtifact>({
|
|
156
|
+
methods: [
|
|
157
|
+
gepaReflectionMethod(methodConfig),
|
|
158
|
+
gepaParetoMethod(methodConfig),
|
|
159
|
+
skillOptMethod(methodConfig),
|
|
160
|
+
],
|
|
161
|
+
baselineSurface,
|
|
162
|
+
trainScenarios,
|
|
163
|
+
selectionScenarios,
|
|
164
|
+
testScenarios,
|
|
165
|
+
dispatchWithSurface,
|
|
166
|
+
judges,
|
|
167
|
+
runDir,
|
|
91
168
|
})
|
|
92
|
-
|
|
93
|
-
const traceRuns = fromOtelSpans({ spans: yourOtelSpans })
|
|
94
|
-
|
|
95
|
-
await analyzeRuns({ runs: [...runs, ...traceRuns], raterScores })
|
|
96
169
|
```
|
|
97
170
|
|
|
98
|
-
|
|
171
|
+
Read `result.scores` for lift and intervals.
|
|
172
|
+
Read `result.totalCost` for dollars plus whether every charge was known.
|
|
173
|
+
Ranks follow estimated lift; use the intervals and pairwise results to determine whether the observed difference excludes zero.
|
|
174
|
+
See the [method-comparison guide](./docs/campaign-proposers.md) and [runnable example](./examples/compare-optimization-methods/).
|
|
99
175
|
|
|
100
|
-
|
|
176
|
+
---
|
|
101
177
|
|
|
102
|
-
|
|
103
|
-
- **Scenario**: one task or case the agent attempts.
|
|
104
|
-
- **Judge**: a scoring function, rule-based or model-based.
|
|
105
|
-
- **Surface**: the thing being changed, usually a prompt string or config object.
|
|
106
|
-
- **InsightReport**: the decision packet returned by `analyzeRuns()` and embedded in `selfImprove()`.
|
|
107
|
-
- **Gate**: the policy that decides `ship`, `hold`, or `need_more_work`.
|
|
178
|
+
## Core APIs
|
|
108
179
|
|
|
109
|
-
|
|
180
|
+
Start from `/contract` for the common path.
|
|
181
|
+
Use `/campaign` when you need direct control over runs, candidate generation, or method comparison.
|
|
110
182
|
|
|
111
|
-
|
|
|
112
|
-
|
|
113
|
-
| **
|
|
114
|
-
| **
|
|
115
|
-
| **
|
|
183
|
+
| Primitive | What it does |
|
|
184
|
+
|---|---|
|
|
185
|
+
| **Evaluation** (`runEval`, `runCampaign`) | Run agent × scenarios × repetitions, score every run, and record the result. |
|
|
186
|
+
| **Scoring** (`JudgeConfig`, `llmJudge`, calibration) | Score one output on weighted dimensions with code or a model, then compare model scores against human ratings. |
|
|
187
|
+
| **Release rules** (`heldOutGate`, `paretoSignificanceGate`, `composeGate`, …) | Decide whether a candidate ships, such as requiring an improvement on scenarios that candidate generation never saw. |
|
|
188
|
+
| **Candidate generation** (`gepaProposer`, `evolutionaryProposer`, …) | Generate candidate prompts or configs from prior failures. |
|
|
189
|
+
| **Method comparison** (`compareOptimizationMethods`) | Run complete optimization methods on shared train and selection data, then rank them on separate final test data. |
|
|
190
|
+
| **Run analysis** (`analyzeRuns`, `diffRuns`) | Turn any set of `RunRecord`s into a report: score distributions, baseline-vs-candidate lift with confidence intervals, failure clusters, cost breakdown, recommendations. |
|
|
191
|
+
| **Intake adapters** (`fromFeedbackTable`, `fromOtelSpans`) | Convert data you already have, such as human ratings tables and OpenTelemetry spans, into `RunRecord`s. |
|
|
192
|
+
| **Cost tracking** | Attribute every model call's tokens and dollars to the run, phase, and judge that spent them, including interrupted calls. |
|
|
193
|
+
| **Human feedback storage** | Persist runs with approved, rejected, or edited labels so review activity becomes training and eval data. |
|
|
194
|
+
| **Statistics** (`pairedBootstrap`, `benjaminiHochberg`, sequential tests) | The release-decision math, usable standalone. |
|
|
195
|
+
| **Trace tools** (`/traces`, `/analyst`) | Store and replay structured run traces; cluster failures with an LLM analyst panel. |
|
|
196
|
+
| **HTTP and RPC** (`/wire`) | Expose judging and ingestion to non-TypeScript stacks, including the Python client. |
|
|
197
|
+
|
|
198
|
+
Our own experiments with these primitives live in [`examples/`](./examples/README.md); they are demonstrations, not part of the API.
|
|
199
|
+
|
|
200
|
+
| Runnable example | Shows |
|
|
201
|
+
|---|---|
|
|
202
|
+
| [`examples/selfimprove-quickstart/`](./examples/selfimprove-quickstart/) | The closed improve-and-verify loop, fully offline |
|
|
203
|
+
| [`examples/customer-feedback-loop/`](./examples/customer-feedback-loop/) | Multi-rater human feedback (CSV/Sheets/Obsidian) → per-rater judges → report |
|
|
204
|
+
| [`examples/customer-otel-traces/`](./examples/customer-otel-traces/) | Production OpenTelemetry traces → report, no closed loop required |
|
|
205
|
+
| [`examples/compare-optimization-methods/`](./examples/compare-optimization-methods/) | Compare complete optimization methods with separate train, selection, and test data |
|
|
116
206
|
|
|
117
|
-
Each
|
|
207
|
+
Each is a single `index.ts` you run with `pnpm tsx`.
|
|
118
208
|
|
|
119
209
|
---
|
|
120
210
|
|
|
121
|
-
##
|
|
211
|
+
## Entry points
|
|
212
|
+
|
|
213
|
+
Import from `@tangle-network/agent-eval/<subpath>`. Every row below is verified importable from the published package.
|
|
122
214
|
|
|
123
215
|
| Subpath | What it gives you |
|
|
124
216
|
|---|---|
|
|
125
|
-
|
|
|
126
|
-
|
|
|
127
|
-
|
|
|
128
|
-
|
|
|
129
|
-
|
|
|
130
|
-
|
|
|
131
|
-
|
|
|
132
|
-
|
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
|
|
|
136
|
-
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
| `…/benchmarks` | `BenchmarkAdapter` contract, `runBenchmarkAdapter`, `calibrateBenchmarkMetric`, standard retrieval parsers + ranked retrieval metrics, `deterministicSplit`, and the bundled `routing` reference benchmark |
|
|
140
|
-
|
|
141
|
-
**Specialized surfaces** (subpath-only): `…/prm` (process-reward grading + best-of-N), `…/meta-eval` (judge calibration + the deployment-outcome store), `…/belief-state` (decision-point extraction + selective-policy reports), `…/pipelines` (trace-diagnostic views: budget breach, failure cluster, stuck loop, …), `…/governance` (EU AI Act / NIST AI RMF / SOC2 reports), `…/knowledge` (knowledge-readiness gating before a run), `…/builder-eval` (code-generator three-layer eval), `…/storyboard` (trace → watchable replay), `…/authenticity` (anti-Goodhart "real or convincing BS" scorer over produced files), `…/workflow` (workflow-trace eval + partner export), `…/telemetry` (Workers-safe telemetry client), `…/testing` (test-only reset helpers).
|
|
142
|
-
|
|
143
|
-
The root export remains broad for compatibility; new code should prefer the focused subpaths above — `/contract` first.
|
|
217
|
+
| `/contract` | **Start here.** Stable APIs for defining an eval, running it, improving a prompt, judging outputs, analyzing existing runs, and storing results. |
|
|
218
|
+
| `/campaign` | Lower-level control over candidate generation, release rules, storage, and comparisons. |
|
|
219
|
+
| `/reporting` | Statistical comparisons and report renderers. |
|
|
220
|
+
| `/analyst` | Model-based failure clustering and stored findings. |
|
|
221
|
+
| `/traces` | Trace stores, emitters, deterministic replay, trace analysis. |
|
|
222
|
+
| `/rl` | Export eval artifacts as training signal: rewards, preferences, trainer-format datasets. |
|
|
223
|
+
| `/benchmarks` | Benchmark adapter contract + retrieval metrics + a bundled reference benchmark. |
|
|
224
|
+
| `/wire` | The HTTP/RPC server and Zod schemas (what the Python client speaks). |
|
|
225
|
+
| `/hosted` | Client for shipping eval-run events to a remote orchestrator (see below). |
|
|
226
|
+
| `/control` | A generic observe → validate → decide → act agent loop with eval-backed stopping rules. |
|
|
227
|
+
| `/matrix`, `/multishot` | N-axis configuration sweeps; multi-turn persona × turn-count runners. |
|
|
228
|
+
| `/meta-eval`, `/belief-state`, `/builder-eval`, `/pipelines`, `/storyboard`, `/authenticity`, `/fuzz`, `/trace-attributes` | Specialized surfaces: judge calibration, decision-point extraction, code-generator grading, trace diagnostics, run replay rendering, anti-gaming output checks, input fuzzing, trace attribute vocabulary. |
|
|
229
|
+
|
|
230
|
+
The root export (`@tangle-network/agent-eval`) remains broad for compatibility; prefer the subpaths for new code.
|
|
144
231
|
|
|
145
232
|
---
|
|
146
233
|
|
|
147
|
-
##
|
|
148
|
-
|
|
149
|
-
agent-eval is the bottom of the layering: consumers depend on it, it depends on none of them.
|
|
150
|
-
|
|
151
|
-
```
|
|
152
|
-
agent-runtime Runs agents (chat turns, one-shot tasks, multi-attempt loops), captures every
|
|
153
|
-
run as a trace, and calls optimizePrompt / runImprovementLoop. Produces the
|
|
154
|
-
RunRecords + traces agent-eval scores. Depends on agent-eval.
|
|
234
|
+
## Documentation
|
|
155
235
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
loops run on; agent-eval scores what comes back.
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
The rule: **agent-eval has zero upward dependencies on a consumer.** A concept that makes sense *without* a running agent loop — a verdict, a run record, a scenario, a judge score — is substrate and lives here. Runtime execution details (a validation context with an abort signal, a concrete sandbox session) live in agent-runtime or sandbox. Agent profile shape is the shared `@tangle-network/agent-interface` contract.
|
|
236
|
+
- [`docs/concepts.md`](./docs/concepts.md): the mental model for runs, judges, verifiers, traces, and the top-level functions (5-minute read)
|
|
237
|
+
- [`docs/customer-journeys.md`](./docs/customer-journeys.md): three complete adoption paths with code
|
|
238
|
+
- [`docs/insight-report.md`](./docs/insight-report.md): annotated walkthrough of every section of the `analyzeRuns()` report
|
|
239
|
+
- [`docs/campaign-proposers.md`](./docs/campaign-proposers.md): candidate generation and fair comparison of complete optimization methods
|
|
240
|
+
- [`docs/adapters-observability.md`](./docs/adapters-observability.md): composing with LangSmith, Langfuse, Phoenix, and OpenLLMetry
|
|
241
|
+
- [`docs/wire-protocol.md`](./docs/wire-protocol.md): the HTTP/RPC contract for other languages
|
|
242
|
+
- [`docs/design.md`](./docs/design.md): how this package relates to the rest of the Tangle agent stack, and the dependency rules that keep it reusable
|
|
243
|
+
- [`CHANGELOG.md`](./CHANGELOG.md): every release, with additive and breaking changes identified
|
|
168
244
|
|
|
169
245
|
---
|
|
170
246
|
|
|
171
|
-
##
|
|
247
|
+
## Optional hosted tier
|
|
172
248
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
- [`docs/insight-report.md`](./docs/insight-report.md) — annotated walkthrough of every section of the decision packet
|
|
176
|
-
- [`docs/customer-journeys.md`](./docs/customer-journeys.md) — three end-to-end journeys with code + expected output
|
|
177
|
-
- [`docs/adapters-observability.md`](./docs/adapters-observability.md) — composing agent-eval with LangSmith, Langfuse, Phoenix, OpenLLMetry, TraceAI
|
|
178
|
-
- [`docs/wire-protocol.md`](./docs/wire-protocol.md) — the HTTP/RPC contract Python (and any future language) speaks
|
|
179
|
-
- [`docs/hosted-ingest-spec.md`](./docs/hosted-ingest-spec.md) — the hosted-tier wire format, frozen at `2026-05-26.v1`
|
|
180
|
-
- [`docs/design/loop-taxonomy.md`](./docs/design/loop-taxonomy.md) — plain-language vocabulary for execution drivers, workers, measurements, and proposers
|
|
181
|
-
|
|
182
|
-
The `.claude/skills/agent-eval/SKILL.md` skill ships embedded directives so LLM agents writing integration code don't reintroduce historical bug classes.
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
## Hosted tier
|
|
187
|
-
|
|
188
|
-
Wire your loop to a hosted orchestrator (ours, or your own implementation of the spec) with one config:
|
|
249
|
+
The library is complete without it.
|
|
250
|
+
If you want a dashboard over many loops, point any run at our remote orchestrator or your own implementation of the [open ingest spec](./docs/hosted-ingest-spec.md):
|
|
189
251
|
|
|
190
252
|
```ts
|
|
191
|
-
import { defineAgentEval } from '@tangle-network/agent-eval/contract'
|
|
192
|
-
|
|
193
|
-
const evalKit = defineAgentEval({
|
|
194
|
-
scenarios,
|
|
195
|
-
agent,
|
|
196
|
-
judge,
|
|
197
|
-
baselineSurface,
|
|
198
|
-
})
|
|
199
|
-
|
|
200
253
|
await evalKit.improve({
|
|
201
254
|
hostedTenant: {
|
|
202
255
|
endpoint: 'https://intelligence.tangle.tools',
|
|
@@ -206,42 +259,23 @@ await evalKit.improve({
|
|
|
206
259
|
})
|
|
207
260
|
```
|
|
208
261
|
|
|
209
|
-
The
|
|
262
|
+
The loop still runs in your process.
|
|
263
|
+
Hosted ingest sends run identifiers and paths, scenario IDs, candidate surfaces, scores, errors, costs, summaries, and trace attributes.
|
|
264
|
+
Review the [wire format](./docs/hosted-ingest-spec.md) before enabling it for sensitive inputs.
|
|
265
|
+
A reference receiver you can self-host is at [`examples/hosted-ingest-server/`](./examples/hosted-ingest-server/).
|
|
210
266
|
|
|
211
267
|
---
|
|
212
268
|
|
|
213
269
|
## Development
|
|
214
270
|
|
|
215
|
-
Run an example:
|
|
216
|
-
|
|
217
|
-
```sh
|
|
218
|
-
pnpm tsx examples/selfimprove-quickstart/index.ts
|
|
219
|
-
pnpm tsx examples/customer-feedback-loop/index.ts
|
|
220
|
-
pnpm tsx examples/customer-otel-traces/index.ts
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Run the test suite:
|
|
224
|
-
|
|
225
271
|
```sh
|
|
226
272
|
pnpm install
|
|
227
273
|
pnpm build
|
|
228
|
-
pnpm test
|
|
274
|
+
pnpm test # vitest, ~3300 tests
|
|
275
|
+
pnpm typecheck
|
|
229
276
|
```
|
|
230
277
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
## Public API
|
|
234
|
-
|
|
235
|
-
The `/contract` surface is the **stability contract**: its barrel freezes the API — a `0.x` minor only *adds*; nothing there changes shape or disappears. Start there for app code.
|
|
236
|
-
|
|
237
|
-
| Surface | Meaning |
|
|
238
|
-
|---|---|
|
|
239
|
-
| `/contract` | Frozen app-facing API. Prefer this first. |
|
|
240
|
-
| Named subpaths | Public capability areas such as `/campaign`, `/rl`, `/prm`, `/meta-eval`, `/belief-state`, `/wire`, `/reporting`, `/traces`, and `/analyst`. |
|
|
241
|
-
| `/testing` | Test-only helpers. Do not import from production code. |
|
|
242
|
-
| Unexported source paths | Not public API. Open an issue if you need one promoted. |
|
|
243
|
-
|
|
244
|
-
[`CHANGELOG.md`](./CHANGELOG.md) tracks every release with what's new / additive / breaking.
|
|
278
|
+
Run any example: `pnpm tsx examples/selfimprove-quickstart/index.ts`
|
|
245
279
|
|
|
246
280
|
---
|
|
247
281
|
|
package/dist/analyst/index.d.ts
CHANGED
|
@@ -224,9 +224,10 @@ declare class ValidationError extends AgentEvalError {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
type AgentProfileCellSchemaVersion = 'agent-profile-cell/v1';
|
|
227
|
-
type
|
|
227
|
+
type AgentProfileJsonObject = {
|
|
228
228
|
[key: string]: AgentProfileJson;
|
|
229
229
|
};
|
|
230
|
+
type AgentProfileJson = string | number | boolean | null | AgentProfileJson[] | AgentProfileJsonObject;
|
|
230
231
|
type AgentProfileDimensionValue = string | number | boolean | null;
|
|
231
232
|
interface AgentProfileSource {
|
|
232
233
|
/** Runtime/profile contract being fingerprinted, e.g. `agent-interface-profile`. */
|
|
@@ -843,9 +844,16 @@ interface CostReceiptInput extends CostUsage {
|
|
|
843
844
|
costUnknown?: boolean;
|
|
844
845
|
usageUnknown?: boolean;
|
|
845
846
|
}
|
|
847
|
+
/** Per-million token rates for a model or endpoint not covered by package pricing. */
|
|
848
|
+
interface CustomTokenPricing {
|
|
849
|
+
inputUsdPerMillion: number;
|
|
850
|
+
outputUsdPerMillion: number;
|
|
851
|
+
}
|
|
846
852
|
type MaximumCharge = {
|
|
847
853
|
externallyEnforcedMaximumUsd: number;
|
|
848
854
|
} | ({
|
|
855
|
+
customTokenPricing: CustomTokenPricing;
|
|
856
|
+
} & Pick<CostUsage, 'inputTokens' | 'outputTokens'>) | ({
|
|
849
857
|
model: string;
|
|
850
858
|
} & CostUsage);
|
|
851
859
|
interface RunPaidCallInput<T> {
|
|
@@ -857,7 +865,7 @@ interface RunPaidCallInput<T> {
|
|
|
857
865
|
model?: string;
|
|
858
866
|
tags?: Record<string, string>;
|
|
859
867
|
signal?: AbortSignal;
|
|
860
|
-
/** Provider-enforced dollar maximum, or maximum
|
|
868
|
+
/** Provider-enforced dollar maximum, or maximum token usage with known pricing. Required when capped. */
|
|
861
869
|
maximumCharge?: MaximumCharge;
|
|
862
870
|
/** `callId` can be forwarded as the provider's idempotency key. */
|
|
863
871
|
execute(signal: AbortSignal, callId: string): Promise<T>;
|
|
@@ -1041,8 +1049,8 @@ interface LlmCallResult {
|
|
|
1041
1049
|
content: string;
|
|
1042
1050
|
usage: LlmUsage;
|
|
1043
1051
|
/**
|
|
1044
|
-
* Cost in USD.
|
|
1045
|
-
* `null` when neither
|
|
1052
|
+
* Cost in USD. Uses the provider's reported cost when present, otherwise
|
|
1053
|
+
* caller-supplied token pricing. `null` when neither is available.
|
|
1046
1054
|
*/
|
|
1047
1055
|
costUsd: number | null;
|
|
1048
1056
|
/** Model name actually used (echoed from response). */
|
|
@@ -1100,6 +1108,21 @@ interface LlmClientOptions {
|
|
|
1100
1108
|
deadlineMs?: number;
|
|
1101
1109
|
/** Total provider attempts. Legacy option name; default 3 (1 initial + 2 retries). */
|
|
1102
1110
|
maxRetries?: number;
|
|
1111
|
+
/** Token rates used when the provider omits cost or package pricing does not cover the model. */
|
|
1112
|
+
customTokenPricing?: CustomTokenPricing;
|
|
1113
|
+
/**
|
|
1114
|
+
* Transport for requests that declare `jsonSchema`. `native` sends
|
|
1115
|
+
* `response_format: json_schema`; `json-object` sends the broadly supported
|
|
1116
|
+
* JSON mode and relies on the caller to include the schema in model-visible
|
|
1117
|
+
* instructions. Default: `native`.
|
|
1118
|
+
*/
|
|
1119
|
+
jsonSchemaTransport?: 'native' | 'json-object';
|
|
1120
|
+
/**
|
|
1121
|
+
* JSON payload parsing policy. `extract` accepts fenced or prose-prefixed JSON.
|
|
1122
|
+
* `exact` requires the complete response content to be one JSON value.
|
|
1123
|
+
* Default: `extract`.
|
|
1124
|
+
*/
|
|
1125
|
+
jsonPayloadMode?: 'extract' | 'exact';
|
|
1103
1126
|
/** Fetch implementation — defaults to global `fetch`. Override for custom transport (e.g. tests). */
|
|
1104
1127
|
fetch?: typeof fetch;
|
|
1105
1128
|
/**
|
package/dist/analyst/index.js
CHANGED
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
diffFindings,
|
|
10
10
|
emitSkillUsageFindings,
|
|
11
11
|
runSemanticConceptJudge
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-QNVBALEX.js";
|
|
13
13
|
import {
|
|
14
14
|
behavioralAnalyst,
|
|
15
15
|
buildDefaultAnalystRegistry,
|
|
16
16
|
createChatClient,
|
|
17
17
|
deriveEfficiencyFindings
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-WP3GEXKI.js";
|
|
19
19
|
import "../chunk-HHWE3POT.js";
|
|
20
20
|
import {
|
|
21
21
|
ANALYST_SEVERITIES,
|
|
@@ -71,15 +71,15 @@ import {
|
|
|
71
71
|
validatePolicyEdit,
|
|
72
72
|
validatePolicyEditCandidateRecord,
|
|
73
73
|
validateUsageSettlementTimeout
|
|
74
|
-
} from "../chunk-
|
|
75
|
-
import "../chunk-
|
|
74
|
+
} from "../chunk-MHPEGJHC.js";
|
|
75
|
+
import "../chunk-MK3Q6GCL.js";
|
|
76
76
|
import {
|
|
77
77
|
CostLedger
|
|
78
|
-
} from "../chunk-
|
|
78
|
+
} from "../chunk-GRCDRKII.js";
|
|
79
79
|
import "../chunk-VI2UW6B6.js";
|
|
80
80
|
import "../chunk-IR3KBHOY.js";
|
|
81
81
|
import "../chunk-PC4UYEBM.js";
|
|
82
|
-
import "../chunk-
|
|
82
|
+
import "../chunk-GC4ATIKK.js";
|
|
83
83
|
import "../chunk-VSMTAMNK.js";
|
|
84
84
|
import "../chunk-ONWEPEDO.js";
|
|
85
85
|
import "../chunk-K4DBDHLK.js";
|
|
@@ -11,10 +11,11 @@ function escapeRe(s) {
|
|
|
11
11
|
function declaredNames(content) {
|
|
12
12
|
const names = /* @__PURE__ */ new Set();
|
|
13
13
|
const re = /\b(?:contract|library|interface|abstract\s+contract|class|enum|struct|module|package)\s+([A-Za-z_]\w*)/g;
|
|
14
|
-
let m;
|
|
15
|
-
while (m
|
|
14
|
+
let m = re.exec(content);
|
|
15
|
+
while (m) {
|
|
16
16
|
const name = m[1];
|
|
17
17
|
if (name && name.length >= 4) names.add(name);
|
|
18
|
+
m = re.exec(content);
|
|
18
19
|
}
|
|
19
20
|
return [...names];
|
|
20
21
|
}
|