@tangle-network/agent-eval 0.137.0 → 0.139.0
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 +78 -0
- package/README.md +34 -0
- package/dist/analyst/index.d.ts +485 -104
- package/dist/analyst/index.d.ts.map +1 -1
- package/dist/analyst/index.js +10 -607
- package/dist/analyst/index.js.map +1 -1
- package/dist/{benchmark-YDrpumqB.js → benchmark-CYtcIF2V.js} +299 -159
- package/dist/benchmark-CYtcIF2V.js.map +1 -0
- package/dist/{benchmark-CHX4orG7.d.ts → benchmark-DDVdWcwA.d.ts} +67 -15
- package/dist/benchmark-DDVdWcwA.d.ts.map +1 -0
- package/dist/benchmark-command-BKfjOBJ5.js +4537 -0
- package/dist/benchmark-command-BKfjOBJ5.js.map +1 -0
- package/dist/benchmarks/index.d.ts +1 -1
- package/dist/benchmarks/index.js +1 -1
- package/dist/{benchmarks-DCLkQOmc.js → benchmarks-zxhy1QV3.js} +4 -3
- package/dist/{benchmarks-DCLkQOmc.js.map → benchmarks-zxhy1QV3.js.map} +1 -1
- package/dist/campaign/index.d.ts +5 -5
- package/dist/campaign/index.js +4 -3
- package/dist/{campaign-lgObcHFC.js → campaign-DrS6_hLd.js} +19 -11
- package/dist/campaign-DrS6_hLd.js.map +1 -0
- package/dist/canonical-D011XM8r.js +86 -0
- package/dist/canonical-D011XM8r.js.map +1 -0
- package/dist/cli.js +10 -3
- package/dist/cli.js.map +1 -1
- package/dist/{client-C8L6h6Wf.d.ts → client-BohnDFBq.d.ts} +4 -4
- package/dist/{client-C8L6h6Wf.d.ts.map → client-BohnDFBq.d.ts.map} +1 -1
- package/dist/{completion-verifier-DSyRNVzU.d.ts → completion-verifier-IPoP4fQO.d.ts} +178 -4
- package/dist/completion-verifier-IPoP4fQO.d.ts.map +1 -0
- package/dist/contract/index.d.ts +10 -10
- package/dist/contract/index.js +9 -8
- package/dist/contract/index.js.map +1 -1
- package/dist/control.d.ts +2 -2
- package/dist/{cost-ledger-D-5_-dhi.js → cost-ledger-CZ9diLxY.js} +90 -45
- package/dist/cost-ledger-CZ9diLxY.js.map +1 -0
- package/dist/{cost-ledger-D2o6JOrL.d.ts → cost-ledger-DKgyIWRj.d.ts} +6 -2
- package/dist/cost-ledger-DKgyIWRj.d.ts.map +1 -0
- package/dist/default-registry-B8vf7Rmf.d.ts +118 -0
- package/dist/default-registry-B8vf7Rmf.d.ts.map +1 -0
- package/dist/default-registry-BgJJItGr.js +2364 -0
- package/dist/default-registry-BgJJItGr.js.map +1 -0
- package/dist/dspy-rlm-engine-DTkVyDX-.js +344 -0
- package/dist/dspy-rlm-engine-DTkVyDX-.js.map +1 -0
- package/dist/{eval-campaign-CHqfLnff.js → eval-campaign-BmptJj50.js} +2 -2
- package/dist/{eval-campaign-CHqfLnff.js.map → eval-campaign-BmptJj50.js.map} +1 -1
- package/dist/exact-types-MaaFcllV.d.ts +234 -0
- package/dist/exact-types-MaaFcllV.d.ts.map +1 -0
- package/dist/external-optimizer-contracts-BrxY2Sli.d.ts +32 -0
- package/dist/external-optimizer-contracts-BrxY2Sli.d.ts.map +1 -0
- package/dist/{extract-usage-p-56bh8q.js → extract-usage-DZs601Va.js} +2 -2
- package/dist/{extract-usage-p-56bh8q.js.map → extract-usage-DZs601Va.js.map} +1 -1
- package/dist/{feedback-trajectory-N_F0PwHz.d.ts → feedback-trajectory-BJUWOkJM.d.ts} +3 -2
- package/dist/feedback-trajectory-BJUWOkJM.d.ts.map +1 -0
- package/dist/fuzz.d.ts +1 -1
- package/dist/fuzz.js +1 -1
- package/dist/{hf-dataset-DBJXXoY1.js → hf-dataset-XggBupCr.js} +2 -2
- package/dist/{hf-dataset-DBJXXoY1.js.map → hf-dataset-XggBupCr.js.map} +1 -1
- package/dist/hosted/index.d.ts +3 -3
- package/dist/{index-C-Pr4OWg.d.ts → index-BTm_P9aC.d.ts} +12 -11
- package/dist/index-BTm_P9aC.d.ts.map +1 -0
- package/dist/{index-U3RHOShi.d.ts → index-CWOPCJiw.d.ts} +2 -2
- package/dist/{index-U3RHOShi.d.ts.map → index-CWOPCJiw.d.ts.map} +1 -1
- package/dist/{index-DRNl6g_N.d.ts → index-CtR1xh4V.d.ts} +3 -3
- package/dist/{index-DRNl6g_N.d.ts.map → index-CtR1xh4V.d.ts.map} +1 -1
- package/dist/index-DEb46kc6.d.ts.map +1 -1
- package/dist/{index-BnP1QJUv.d.ts → index-_66rVpwN.d.ts} +5 -5
- package/dist/{index-BnP1QJUv.d.ts.map → index-_66rVpwN.d.ts.map} +1 -1
- package/dist/index.d.ts +35 -55
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -514
- package/dist/index.js.map +1 -1
- package/dist/{insight-report-B9ooYH_g.d.ts → insight-report-Bu5Wi9tG.d.ts} +4 -4
- package/dist/{insight-report-B9ooYH_g.d.ts.map → insight-report-Bu5Wi9tG.d.ts.map} +1 -1
- package/dist/{integrity-CKxosZ5Z.d.ts → integrity-COTh3DTH.d.ts} +2 -2
- package/dist/{integrity-CKxosZ5Z.d.ts.map → integrity-COTh3DTH.d.ts.map} +1 -1
- package/dist/kind-factory-CFxA0JQX.js +2133 -0
- package/dist/kind-factory-CFxA0JQX.js.map +1 -0
- package/dist/ledger-core/index.js +2 -1
- package/dist/{ledger-core-t6sItivm.js → ledger-core-Dxz0Rkwa.js} +210 -99
- package/dist/ledger-core-Dxz0Rkwa.js.map +1 -0
- package/dist/{llm-client-DKB25jV8.js → llm-client-bkztEfIx.js} +5 -5
- package/dist/llm-client-bkztEfIx.js.map +1 -0
- package/dist/meta-eval/index.d.ts +2 -2
- package/dist/multishot/index.d.ts +2 -2
- package/dist/openapi.json +1 -1
- package/dist/{proposal-findings-DCawte-y.js → proposal-findings-2GIUo1et.js} +2 -68
- package/dist/proposal-findings-2GIUo1et.js.map +1 -0
- package/dist/{release-report-CofgVNZt.d.ts → release-report-fZarvIm-.d.ts} +3 -3
- package/dist/{release-report-CofgVNZt.d.ts.map → release-report-fZarvIm-.d.ts.map} +1 -1
- package/dist/{replay-K8FaC0CB.d.ts → replay-DjG4IG60.d.ts} +34 -143
- package/dist/replay-DjG4IG60.d.ts.map +1 -0
- package/dist/{replay-Bju0T8Ls.js → replay-SA4OB7O7.js} +48 -136
- package/dist/replay-SA4OB7O7.js.map +1 -0
- package/dist/reporting.d.ts +4 -4
- package/dist/{researcher-Da0Wj-bt.d.ts → researcher-BxhtGfKa.d.ts} +5 -5
- package/dist/{researcher-Da0Wj-bt.d.ts.map → researcher-BxhtGfKa.d.ts.map} +1 -1
- package/dist/{reward-hacking-CQ3hTCO3.d.ts → reward-hacking-CqSLiV51.d.ts} +2 -2
- package/dist/{reward-hacking-CQ3hTCO3.d.ts.map → reward-hacking-CqSLiV51.d.ts.map} +1 -1
- package/dist/rl.d.ts +5 -5
- package/dist/rl.js +1 -1
- package/dist/rollout/index.d.ts +1 -1
- package/dist/rollout/index.js +2 -2
- package/dist/{rollout-DQFl0UXA.js → rollout-8nj3mYvx.js} +2 -2
- package/dist/{rollout-DQFl0UXA.js.map → rollout-8nj3mYvx.js.map} +1 -1
- package/dist/{rubric-predictive-validity-C4sztLR3.d.ts → rubric-predictive-validity-DQBQj6uV.d.ts} +2 -2
- package/dist/{rubric-predictive-validity-C4sztLR3.d.ts.map → rubric-predictive-validity-DQBQj6uV.d.ts.map} +1 -1
- package/dist/{run-evidence-BDIircdA.d.ts → run-evidence-C4RcRQT5.d.ts} +3 -3
- package/dist/{run-evidence-BDIircdA.d.ts.map → run-evidence-C4RcRQT5.d.ts.map} +1 -1
- package/dist/{run-record-BPCa2rQ8.d.ts → run-record-CztDMXVF.d.ts} +2 -2
- package/dist/{run-record-BPCa2rQ8.d.ts.map → run-record-CztDMXVF.d.ts.map} +1 -1
- package/dist/{semantic-concept-judge-Bz64IckK.js → semantic-concept-judge-BuIJ9IfB.js} +49 -6
- package/dist/semantic-concept-judge-BuIJ9IfB.js.map +1 -0
- package/dist/{server-KjXZZUDX.js → server-DaCpLfi0.js} +3 -3
- package/dist/{server-KjXZZUDX.js.map → server-DaCpLfi0.js.map} +1 -1
- package/dist/single-run-lock-BTTtPZ9N.js +989 -0
- package/dist/single-run-lock-BTTtPZ9N.js.map +1 -0
- package/dist/{skill-usage-CFDLLlhF.d.ts → skill-usage-B-BFS8M2.d.ts} +65 -40
- package/dist/skill-usage-B-BFS8M2.d.ts.map +1 -0
- package/dist/{skillopt-optimization-method-f4o9sUT4.js → skillopt-optimization-method-BbGnCC53.js} +20 -979
- package/dist/skillopt-optimization-method-BbGnCC53.js.map +1 -0
- package/dist/{skillopt-optimization-method-BpbnlvAZ.d.ts → skillopt-optimization-method-_s0Tub7Y.d.ts} +11 -39
- package/dist/skillopt-optimization-method-_s0Tub7Y.d.ts.map +1 -0
- package/dist/{statistics-_7P642CN.d.ts → statistics-B5d0Zd-z.d.ts} +2 -2
- package/dist/{statistics-_7P642CN.d.ts.map → statistics-B5d0Zd-z.d.ts.map} +1 -1
- package/dist/store-otlp-DX4fGIcf.js +757 -0
- package/dist/store-otlp-DX4fGIcf.js.map +1 -0
- package/dist/{summary-report-DHipz9Kx.d.ts → summary-report-Cg7BifAM.d.ts} +3 -3
- package/dist/{summary-report-DHipz9Kx.d.ts.map → summary-report-Cg7BifAM.d.ts.map} +1 -1
- package/dist/tool-groups-CdYq22lX.d.ts +258 -0
- package/dist/tool-groups-CdYq22lX.d.ts.map +1 -0
- package/dist/traces.d.ts +7 -6
- package/dist/traces.js +5 -4
- package/dist/{types-CTGbIm57.d.ts → types-BBFNHxSK.d.ts} +5 -5
- package/dist/{types-CTGbIm57.d.ts.map → types-BBFNHxSK.d.ts.map} +1 -1
- package/dist/{types-CKswbJGO.d.ts → types-DoEYskCd.d.ts} +5 -5
- package/dist/{types-CKswbJGO.d.ts.map → types-DoEYskCd.d.ts.map} +1 -1
- package/dist/{types-CTvKfr5F.d.ts → types-uPrS6mD-.d.ts} +2 -2
- package/dist/{types-CTvKfr5F.d.ts.map → types-uPrS6mD-.d.ts.map} +1 -1
- package/dist/usage-receipt-CgxMEBZq.js +134 -0
- package/dist/usage-receipt-CgxMEBZq.js.map +1 -0
- package/dist/wire/index.d.ts +3 -3
- package/dist/wire/index.js +1 -1
- package/docs/trace-analysis.md +191 -385
- package/package.json +5 -4
- package/dist/analyze-runs-PVtnfjvA.d.ts +0 -72
- package/dist/analyze-runs-PVtnfjvA.d.ts.map +0 -1
- package/dist/benchmark-CHX4orG7.d.ts.map +0 -1
- package/dist/benchmark-YDrpumqB.js.map +0 -1
- package/dist/campaign-lgObcHFC.js.map +0 -1
- package/dist/completion-verifier-DSyRNVzU.d.ts.map +0 -1
- package/dist/concurrency-MUjT7VjM.js +0 -109
- package/dist/concurrency-MUjT7VjM.js.map +0 -1
- package/dist/cost-ledger-D-5_-dhi.js.map +0 -1
- package/dist/cost-ledger-D2o6JOrL.d.ts.map +0 -1
- package/dist/default-registry-CLXbRt0f.js +0 -2594
- package/dist/default-registry-CLXbRt0f.js.map +0 -1
- package/dist/default-registry-Dc5D_Loc.d.ts +0 -202
- package/dist/default-registry-Dc5D_Loc.d.ts.map +0 -1
- package/dist/feedback-trajectory-N_F0PwHz.d.ts.map +0 -1
- package/dist/index-C-Pr4OWg.d.ts.map +0 -1
- package/dist/ledger-core-t6sItivm.js.map +0 -1
- package/dist/llm-client-DKB25jV8.js.map +0 -1
- package/dist/proposal-findings-DCawte-y.js.map +0 -1
- package/dist/registry-BdM7SuTr.d.ts +0 -124
- package/dist/registry-BdM7SuTr.d.ts.map +0 -1
- package/dist/replay-Bju0T8Ls.js.map +0 -1
- package/dist/replay-K8FaC0CB.d.ts.map +0 -1
- package/dist/semantic-concept-judge-Bz64IckK.js.map +0 -1
- package/dist/skill-usage-CFDLLlhF.d.ts.map +0 -1
- package/dist/skillopt-optimization-method-BpbnlvAZ.d.ts.map +0 -1
- package/dist/skillopt-optimization-method-f4o9sUT4.js.map +0 -1
- package/dist/tools-DZk2Jn64.js +0 -1876
- package/dist/tools-DZk2Jn64.js.map +0 -1
package/docs/trace-analysis.md
CHANGED
|
@@ -1,438 +1,244 @@
|
|
|
1
1
|
# Trace Analysis
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A trace analyst is a recursive research program that answers a question by choosing which trace data to inspect.
|
|
4
|
+
It is not a single prompt over a prebuilt trace dump.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
2. Which exact steps support a suspected problem?
|
|
7
|
-
3. Does an analyst find labeled problems reliably enough to use?
|
|
6
|
+
Agent Eval separates five concerns:
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
| Part | Responsibility |
|
|
9
|
+
|---|---|
|
|
10
|
+
| Analyst definition | The question, investigation policy, allowed tools, and limits |
|
|
11
|
+
| Analysis engine | Runs the recursive investigation |
|
|
12
|
+
| Trace store | Provides bounded reads over OTLP traces |
|
|
13
|
+
| Finding | Records one structured claim with exact evidence |
|
|
14
|
+
| Analysis result | Returns the prose answer, findings, investigation steps, model calls, tool calls, and runtime |
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
The built-in model-backed analysts use the official DSPy `RLM`.
|
|
17
|
+
DSPy runs the research loop and a sandboxed Python interpreter.
|
|
18
|
+
Agent Eval owns trace access, credentials, cancellation, cost accounting, and output validation.
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
`callLlmJson()` and `createPublicBenchmarkDirectRunner()` are direct-call baselines.
|
|
21
|
+
They are not trace analysts.
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
buildDefaultAnalystRegistry,
|
|
20
|
-
} from '@tangle-network/agent-eval/analyst'
|
|
21
|
-
import { OtlpFileTraceStore } from '@tangle-network/agent-eval/traces'
|
|
22
|
-
|
|
23
|
-
const traceStore = new OtlpFileTraceStore({ path: 'traces.otlp.jsonl' })
|
|
24
|
-
const analysts = buildDefaultAnalystRegistry()
|
|
23
|
+
## Install
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
Install the TypeScript package and the matching Python package with DSPy:
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
```sh
|
|
28
|
+
pnpm add @tangle-network/agent-eval
|
|
29
|
+
python -m venv .venv
|
|
30
|
+
.venv/bin/pip install "agent-eval-rpc[dspy]"
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
An analyst failure is recorded separately from an agent failure.
|
|
33
|
+
The Python extra pins the tested stable DSPy and Deno versions.
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
Custom stores provide `hasTrace` and batched `hasSpans` alongside the seven reads, and accept a `TraceAnalysisStoreContext` so cancellation reaches storage and scans.
|
|
38
|
-
The binding validates every custom-store result.
|
|
39
|
-
Missing fields, undeclared fields, inconsistent counts, and false continuation flags throw `TraceAnalysisStoreContractError` with code `backend_integrity`.
|
|
40
|
-
The analyst runs one Ax executor loop and accepts only an explicit structured `final(task, { report, findings })` result; max-turn fallback text fails loud.
|
|
41
|
-
|
|
42
|
-
### Bind the same reads into another agent environment
|
|
43
|
-
|
|
44
|
-
`buildTraceAnalysisToolDescriptors()` is the canonical definition of the analyst's seven bounded read operations and does not expose Ax types.
|
|
45
|
-
Each descriptor carries the stable `traces` namespace, function name, description, JSON input schema in `parameters`, and a handler already bound to the supplied `TraceAnalysisStore`.
|
|
46
|
-
`buildTraceAnalystTools()` adapts those descriptors into Ax functions; it does not define a second tool surface.
|
|
47
|
-
The bound handlers wrap custom stores with `createBoundedTraceAnalysisStore()`, so page limits, byte ceilings, not-found errors, and cancellation do not depend on the transport or adapter.
|
|
35
|
+
## Answer One Question
|
|
48
36
|
|
|
49
37
|
```ts
|
|
50
38
|
import {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} from '@tangle-network/agent-eval/traces'
|
|
54
|
-
|
|
55
|
-
declare const store: TraceAnalysisStore
|
|
56
|
-
declare function qualifyToolName(namespace: string, name: string): string
|
|
57
|
-
|
|
58
|
-
const tools = buildTraceAnalysisToolDescriptors({ store }).map(
|
|
59
|
-
({ namespace, name, description, parameters, handler }) => ({
|
|
60
|
-
name: qualifyToolName(namespace, name),
|
|
61
|
-
description,
|
|
62
|
-
inputSchema: parameters,
|
|
63
|
-
handler,
|
|
64
|
-
}),
|
|
65
|
-
)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Map these fields into the host's existing tool transport.
|
|
69
|
-
The host owns namespace encoding; use its existing convention instead of inventing one here.
|
|
70
|
-
Do not copy the schemas or reimplement the handlers in an MCP, Runtime, or provider adapter.
|
|
71
|
-
|
|
72
|
-
`queryTraces.limit`, `viewSpans.span_ids`, and search `max_matches` caps are present in the JSON Schemas and enforced before store calls.
|
|
73
|
-
Invalid arguments throw `TraceAnalysisValidationError` with code `validation`; responses that cannot fit their byte ceiling throw `TraceAnalysisLimitError` with code `limit_exceeded`.
|
|
74
|
-
Search patterns use RE2 syntax, which rejects backreferences and lookaround instead of allowing exponential-time expressions.
|
|
75
|
-
Search results return `hits` and an exact `has_more` flag; they do not invent a total after a capped scan.
|
|
76
|
-
`viewSpans` partitions every requested id across `spans`, `missing_span_ids`, and `omitted_span_ids`; `has_more` is true when omitted ids must be retried.
|
|
77
|
-
Attribute and match text shortening includes a deterministic marker.
|
|
78
|
-
Trace pages set `has_more`, and the overview returns every error cluster or fails explicitly when the configured response limit is too small.
|
|
79
|
-
|
|
80
|
-
### Analyze captured tool spans in memory
|
|
81
|
-
|
|
82
|
-
Use `toolSpansToTraceAnalysisStore()` when a live worker already returns canonical `ToolSpan[]` records.
|
|
83
|
-
The function snapshots the records immediately, groups them by `runId`, and exposes the same bounded reads and searches as the file-backed store.
|
|
84
|
-
|
|
85
|
-
```ts
|
|
39
|
+
createDspyRlmTraceEngine,
|
|
40
|
+
} from '@tangle-network/agent-eval/analyst'
|
|
86
41
|
import {
|
|
87
42
|
analyzeTraces,
|
|
88
|
-
toolSpansToTraceAnalysisStore,
|
|
89
|
-
type ToolSpan,
|
|
90
|
-
ToolTraceMissingError,
|
|
91
43
|
} from '@tangle-network/agent-eval/traces'
|
|
92
44
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
## Deterministic failure coverage (no LLM)
|
|
105
|
-
|
|
106
|
-
Before (or alongside) the LLM analyst, `OtlpFileTraceStore.getOverview()` returns a
|
|
107
|
-
`DatasetOverview` whose `error_clusters` are computed deterministically: error
|
|
108
|
-
spans are grouped by a normalized failure signature (uuids / hex ids / numbers /
|
|
109
|
-
absolute paths / durations collapsed), each cluster carrying its prevalence,
|
|
110
|
-
exemplar `trace_id`/`span_id`, and a verbatim sample. This is a zero-LLM,
|
|
111
|
-
reproducible failure checklist the analyst then explains and closes:
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
const overview = await store.getOverview()
|
|
115
|
-
for (const c of overview.error_clusters) {
|
|
116
|
-
console.log(`${c.trace_count}× ${c.signature}: e.g. trace ${c.exemplar_trace_ids[0]}`)
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Recursive control integrity (no LLM)
|
|
121
|
-
|
|
122
|
-
`CONTROL_INTEGRITY_ANALYST` checks the existing `SupervisorRunSources` or `SupervisorRunTree` directly.
|
|
123
|
-
It does not define another run format.
|
|
124
|
-
Register it as a custom-input analyst and pass the existing value under its stable id:
|
|
125
|
-
|
|
126
|
-
```ts
|
|
127
|
-
import {
|
|
128
|
-
AnalystRegistry,
|
|
129
|
-
CONTROL_INTEGRITY_ANALYST,
|
|
130
|
-
} from '@tangle-network/agent-eval/analyst'
|
|
131
|
-
import {
|
|
132
|
-
readLoopsSupervisorRun,
|
|
133
|
-
} from '@tangle-network/agent-eval/supervisor-run'
|
|
134
|
-
|
|
135
|
-
const sources = await readLoopsSupervisorRun(runDir)
|
|
136
|
-
const registry = new AnalystRegistry()
|
|
137
|
-
registry.register(CONTROL_INTEGRITY_ANALYST)
|
|
138
|
-
|
|
139
|
-
const result = await registry.run('run-123', {
|
|
140
|
-
custom: { 'control-integrity': sources },
|
|
45
|
+
const engine = createDspyRlmTraceEngine({
|
|
46
|
+
baseUrl: process.env.LLM_BASE_URL!,
|
|
47
|
+
apiKey: process.env.LLM_API_KEY!,
|
|
48
|
+
model: process.env.LLM_MODEL!,
|
|
49
|
+
pricing: {
|
|
50
|
+
inputUsdPerMillion: 3,
|
|
51
|
+
outputUsdPerMillion: 15,
|
|
52
|
+
},
|
|
53
|
+
maxCostUsd: 0.50,
|
|
54
|
+
runner: { command: '.venv/bin/python' },
|
|
141
55
|
})
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Pass `SupervisorRunSources` when it is available.
|
|
145
|
-
A `SupervisorRunTree` does not retain raw journal multiplicity or worker request and acknowledgement rows, so tree input explicitly reports those checks as unavailable.
|
|
146
56
|
|
|
147
|
-
|
|
57
|
+
const result = await analyzeTraces(
|
|
58
|
+
{ question: 'What first caused this run to fail?' },
|
|
59
|
+
{
|
|
60
|
+
source: 'run.otlp.jsonl',
|
|
61
|
+
engine,
|
|
62
|
+
toolGroup: 'singleTrace',
|
|
63
|
+
limits: {
|
|
64
|
+
maxIterations: 8,
|
|
65
|
+
maxLlmCalls: 4,
|
|
66
|
+
maxToolCalls: 32,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
)
|
|
148
70
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
| Is the causal order possible? | `outcome.metrics.spawned_at`, `started_at`, `settled_at`, `completed_at`, `finished_at` when present | A child before its parent, a child after its parent closed, or a close before a start is a violation; absent timestamps produce no timing claim. |
|
|
154
|
-
| Did a queued steer reach the worker? | `SupervisorRunSources.workers[].inbox` and `.events` | Requests and acknowledgements are joined by request id, not compared as totals. Missing, malformed, duplicate, or uncorrelated rows make the affected count unavailable. |
|
|
155
|
-
| Can behavior be attributed to an exact profile? | `policy.agent_profile_cell_id` | An absent id is reported as unavailable. |
|
|
156
|
-
| Can action authorship or reasoning be inspected? | `messages[]` | Empty gap rows are reported as unavailable. |
|
|
71
|
+
console.log(result.answer)
|
|
72
|
+
console.log(result.findings)
|
|
73
|
+
console.log(result.trajectory)
|
|
74
|
+
```
|
|
157
75
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Those claims require upstream action-decision events carrying `action_id`, `actor_rollout_id`, `target_rollout_id`, `action_kind`, `authority_snapshot_id`, requested and granted resource/depth values, the authorization result, and any `finding_id` or evidence references that caused the action.
|
|
161
|
-
Resume integrity additionally requires an explicit prior-session id and resumed-session id rather than a prose summary.
|
|
76
|
+
Omit `pricing` only when Agent Eval already recognizes the exact model or model family.
|
|
77
|
+
Unknown pricing fails before the first model call.
|
|
162
78
|
|
|
163
|
-
|
|
164
|
-
|
|
79
|
+
The provider key remains in the Node process.
|
|
80
|
+
The Python process receives an authenticated loopback model endpoint with an ephemeral credential.
|
|
81
|
+
Each trace read also crosses an authenticated loopback callback and counts against `maxToolCalls`.
|
|
165
82
|
|
|
166
|
-
##
|
|
83
|
+
## Define A Reusable Analyst
|
|
167
84
|
|
|
168
|
-
|
|
169
|
-
The
|
|
85
|
+
An analyst definition contains no model, credentials, or execution state.
|
|
86
|
+
The same definition can run with DSPy or another engine that implements `TraceAnalysisEngine`.
|
|
170
87
|
|
|
171
88
|
```ts
|
|
172
89
|
import {
|
|
173
|
-
AnalystRegistry,
|
|
174
90
|
defineTraceAnalyst,
|
|
175
|
-
|
|
91
|
+
runTraceAnalyst,
|
|
176
92
|
} from '@tangle-network/agent-eval/analyst'
|
|
177
|
-
|
|
178
|
-
const analysts = new AnalystRegistry()
|
|
179
|
-
|
|
180
|
-
analysts.register(defineTraceAnalyst({
|
|
181
|
-
id: 'repeated-tool-errors',
|
|
182
|
-
description: 'Reports the largest repeated tool error cluster.',
|
|
183
|
-
cost: { kind: 'deterministic' },
|
|
184
|
-
async analyze(store) {
|
|
185
|
-
const overview = await store.getOverview({ has_errors: true })
|
|
186
|
-
const cluster = overview.error_clusters[0]
|
|
187
|
-
if (!cluster) return []
|
|
188
|
-
|
|
189
|
-
return [makeFinding({
|
|
190
|
-
analyst_id: 'repeated-tool-errors',
|
|
191
|
-
area: 'tool-use',
|
|
192
|
-
subject: cluster.signature,
|
|
193
|
-
claim: `${cluster.span_count} failed spans share one error`,
|
|
194
|
-
severity: 'high',
|
|
195
|
-
confidence: 1,
|
|
196
|
-
evidence_refs: [{
|
|
197
|
-
kind: 'span',
|
|
198
|
-
uri: `trace://${encodeURIComponent(cluster.exemplar_trace_ids[0])}/span/${encodeURIComponent(cluster.exemplar_span_ids[0])}`,
|
|
199
|
-
excerpt: cluster.status_message_sample,
|
|
200
|
-
}],
|
|
201
|
-
recommended_action: 'Fix the highest-frequency tool error before changing prompts.',
|
|
202
|
-
validation_plan: 'Run fresh cases and require this error signature to disappear.',
|
|
203
|
-
})]
|
|
204
|
-
},
|
|
205
|
-
}))
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Use code for exact facts such as exit codes, missing fields, and repeated calls.
|
|
209
|
-
Use model-assisted analysts for semantic questions such as whether a response ignored user intent.
|
|
210
|
-
|
|
211
|
-
## Measure An Analyst
|
|
212
|
-
|
|
213
|
-
Do not judge an analyst by persuasive prose.
|
|
214
|
-
Label the issue identity and exact evidence locations, then run the same cases through every implementation.
|
|
215
|
-
|
|
216
|
-
```ts
|
|
217
93
|
import {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
renderAnalystBenchmarkMarkdown,
|
|
221
|
-
runAnalystBenchmark,
|
|
222
|
-
traceStoreEvidenceResolver,
|
|
223
|
-
} from '@tangle-network/agent-eval/analyst'
|
|
94
|
+
OtlpFileTraceStore,
|
|
95
|
+
} from '@tangle-network/agent-eval/traces'
|
|
224
96
|
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
resolveEvidence: traceStoreEvidenceResolver((input) => input.traceStore),
|
|
243
|
-
benchmark: {
|
|
244
|
-
id: 'failure-localization',
|
|
245
|
-
dataset: {
|
|
246
|
-
id: 'my-team/trace-failures',
|
|
247
|
-
revision: 'git-sha-or-content-digest',
|
|
248
|
-
split: 'test',
|
|
249
|
-
},
|
|
97
|
+
const repeatedFailure = defineTraceAnalyst({
|
|
98
|
+
id: 'repeated-tool-failure',
|
|
99
|
+
description: 'Finds the repeated tool failure with the largest impact.',
|
|
100
|
+
area: 'tool-use',
|
|
101
|
+
version: '1.0.0',
|
|
102
|
+
question: 'Which repeated tool failure should we fix first?',
|
|
103
|
+
instructions: [
|
|
104
|
+
'Compare frequency and downstream impact.',
|
|
105
|
+
'Cite the failing span and at least one affected downstream span.',
|
|
106
|
+
'Return no finding when the traces do not support a repeated failure.',
|
|
107
|
+
].join('\n'),
|
|
108
|
+
toolGroup: 'all',
|
|
109
|
+
minimumEvidenceCitations: 2,
|
|
110
|
+
limits: {
|
|
111
|
+
maxIterations: 10,
|
|
112
|
+
maxLlmCalls: 6,
|
|
113
|
+
maxToolCalls: 40,
|
|
250
114
|
},
|
|
251
115
|
})
|
|
252
116
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
The result reports:
|
|
257
|
-
|
|
258
|
-
- issue recall and finding precision,
|
|
259
|
-
- first bad step accuracy,
|
|
260
|
-
- citation coverage, agreement with labeled locations, and actual location resolution,
|
|
261
|
-
- false positives on clean cases,
|
|
262
|
-
- repeat agreement,
|
|
263
|
-
- failed runs,
|
|
264
|
-
- latency, calls, every reported token counter, and known or missing cost,
|
|
265
|
-
- dataset revision, case tags, case metadata, and runner metadata.
|
|
266
|
-
|
|
267
|
-
Use `compareAnalystRunners()` for paired differences between two implementations.
|
|
268
|
-
Repetitions are averaged within each case before comparison.
|
|
269
|
-
Treat its interval as inferential only with at least 20 independent cases.
|
|
270
|
-
|
|
271
|
-
## Load Public Trace Labels
|
|
272
|
-
|
|
273
|
-
Use the published label adapters with `@tangle-network/traces` or your own trajectory loader.
|
|
274
|
-
Agent Eval does not download datasets or own trace capture.
|
|
275
|
-
Load public data at an immutable commit and record that commit in `benchmark.dataset.revision`.
|
|
276
|
-
|
|
277
|
-
```ts
|
|
278
|
-
import {
|
|
279
|
-
agentRxBenchmarkCase,
|
|
280
|
-
agentRxPredictionsToFindings,
|
|
281
|
-
codeTraceBenchCase,
|
|
282
|
-
codeTracerPredictionsToFindings,
|
|
283
|
-
} from '@tangle-network/agent-eval/analyst'
|
|
284
|
-
import { otlpTextToTraceAnalysisStore } from '@tangle-network/agent-eval/traces'
|
|
285
|
-
import { chatTrajectoryToSpans, serializeSpans } from '@tangle-network/traces'
|
|
286
|
-
|
|
287
|
-
const codeSpans = chatTrajectoryToSpans(codeTraceTrajectory, {
|
|
288
|
-
traceId: codeTraceRow.traj_id,
|
|
289
|
-
})
|
|
290
|
-
const codeCase = codeTraceBenchCase(codeTraceRow, {
|
|
291
|
-
traceStore: otlpTextToTraceAnalysisStore(serializeSpans(codeSpans)),
|
|
292
|
-
})
|
|
117
|
+
const store = new OtlpFileTraceStore({ path: 'runs.otlp.jsonl' })
|
|
118
|
+
await store.ensureIndexed()
|
|
293
119
|
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
120
|
+
const result = await runTraceAnalyst({
|
|
121
|
+
definition: repeatedFailure,
|
|
122
|
+
engine,
|
|
123
|
+
store,
|
|
124
|
+
context: { runId: 'release-42' },
|
|
297
125
|
})
|
|
298
|
-
const rootCauseCase = agentRxBenchmarkCase(agentRxRow, {
|
|
299
|
-
traceStore: otlpTextToTraceAnalysisStore(serializeSpans(agentRxSpans)),
|
|
300
|
-
}, {
|
|
301
|
-
stepCount: agentRxMessages.length,
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
const codeTracerFindings = codeTracerPredictionsToFindings(
|
|
305
|
-
codeTraceRow.traj_id,
|
|
306
|
-
codetracerLabels,
|
|
307
|
-
{ stepCount: codeTraceRow.step_count },
|
|
308
|
-
)
|
|
309
|
-
const agentRxFindings = agentRxPredictionsToFindings(
|
|
310
|
-
agentRxRow.trajectory_id,
|
|
311
|
-
agentRxJudgeOutput,
|
|
312
|
-
{ stepCount: agentRxMessages.length },
|
|
313
|
-
)
|
|
314
126
|
```
|
|
315
127
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
Every cited step is checked against `step_count`.
|
|
320
|
-
|
|
321
|
-
`agentRxBenchmarkCase()` accepts the public [AgentRx](https://huggingface.co/datasets/microsoft/AgentRx) label format.
|
|
322
|
-
AgentRx category quality and root-step accuracy are scored independently.
|
|
323
|
-
`traceAnalystQualityJudge` averages them when a root-step label exists.
|
|
324
|
-
Pass `target: 'all-failures'` only when the analyst is designed to identify every annotated failure.
|
|
128
|
+
Keep product policy in the definition.
|
|
129
|
+
Keep model transport, recursion mechanics, secrets, and accounting in the engine.
|
|
130
|
+
Keep exact trace facts in deterministic tools or checks.
|
|
325
131
|
|
|
326
|
-
|
|
327
|
-
`@tangle-network/traces` uses the same IDs when converting chat trajectories.
|
|
328
|
-
Pass `stepUri` when your trace store uses another URI scheme.
|
|
329
|
-
`codeTracerPredictionsToFindings()` and `agentRxPredictionsToFindings()` translate the maintained upstream engines' native outputs into the same evidence and category shape.
|
|
330
|
-
AgentRx `Report.to_dict()` judge votes reduce to the upstream majority failure type and Python-rounded mean step, and direct `failures` arrays use the same reduction.
|
|
331
|
-
`failure_case: 0` produces no finding, which scores as a missed root cause on AgentRx's failed trajectories.
|
|
132
|
+
## Run The Built-In Set
|
|
332
133
|
|
|
333
|
-
|
|
134
|
+
The default registry always includes deterministic behavior checks.
|
|
135
|
+
It adds the four recursive analysts only when an engine is supplied:
|
|
334
136
|
|
|
335
|
-
|
|
137
|
+
- `failure-mode`
|
|
138
|
+
- `knowledge-gap`
|
|
139
|
+
- `knowledge-poisoning`
|
|
140
|
+
- `improvement`
|
|
336
141
|
|
|
337
142
|
```ts
|
|
338
|
-
import { createEvaluator } from '@arizeai/phoenix-evals'
|
|
339
|
-
import { ExactMatch } from 'autoevals'
|
|
340
143
|
import {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
} from '@tangle-network/agent-eval/campaign'
|
|
344
|
-
|
|
345
|
-
const phoenix = createEvaluator(
|
|
346
|
-
({ output, expected }) => output === expected ? 1 : 0,
|
|
347
|
-
{ name: 'exact-match', kind: 'CODE', telemetry: { isEnabled: false } },
|
|
348
|
-
)
|
|
144
|
+
buildDefaultAnalystRegistry,
|
|
145
|
+
} from '@tangle-network/agent-eval/analyst'
|
|
349
146
|
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
})
|
|
147
|
+
const registry = buildDefaultAnalystRegistry({ engine })
|
|
148
|
+
const run = await registry.run('release-42', { traceStore: store })
|
|
353
149
|
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
mapInput: ({ artifact, scenario }) => ({ output: artifact, expected: scenario.expected }),
|
|
358
|
-
})
|
|
150
|
+
for (const finding of run.findings) {
|
|
151
|
+
console.log(finding.analyst_id, finding.claim, finding.evidence_refs)
|
|
152
|
+
}
|
|
359
153
|
```
|
|
360
154
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
155
|
+
`run.per_analyst` records each analyst's status, latency, calls, tokens, and cost.
|
|
156
|
+
One analyst failure does not become an agent failure.
|
|
157
|
+
|
|
158
|
+
Pass `definitions` to replace the four built-ins.
|
|
159
|
+
Omit `engine` for deterministic-only analysis.
|
|
160
|
+
|
|
161
|
+
## Trace Tools
|
|
162
|
+
|
|
163
|
+
The engine receives only the tool group declared by the analyst:
|
|
164
|
+
|
|
165
|
+
| Group | Use |
|
|
166
|
+
|---|---|
|
|
167
|
+
| `singleTrace` | Inspect one known trajectory |
|
|
168
|
+
| `discoveryAndSearch` | Find relevant traces and spans across a dataset |
|
|
169
|
+
| `all` | Use every bounded trace operation |
|
|
170
|
+
|
|
171
|
+
The canonical operations are:
|
|
172
|
+
|
|
173
|
+
- `getDatasetOverview`
|
|
174
|
+
- `queryTraces`
|
|
175
|
+
- `countTraces`
|
|
176
|
+
- `viewTrace`
|
|
177
|
+
- `viewSpans`
|
|
178
|
+
- `searchTrace`
|
|
179
|
+
- `searchSpan`
|
|
180
|
+
|
|
181
|
+
Use `buildTraceAnalysisToolDescriptors({ store })` to bind the same operations into another runtime.
|
|
182
|
+
Each descriptor includes its name, namespace, description, JSON input schema, and bounded handler.
|
|
183
|
+
Do not copy the schemas or reimplement the handlers in another adapter.
|
|
184
|
+
|
|
185
|
+
Custom stores implement `TraceAnalysisStore`.
|
|
186
|
+
`OtlpFileTraceStore`, `otlpTextToTraceAnalysisStore()`, and `toolSpansToTraceAnalysisStore()` provide common adapters.
|
|
187
|
+
Store results are checked for missing fields, undeclared fields, inconsistent counts, invalid continuation flags, oversized responses, and unsafe search patterns.
|
|
188
|
+
|
|
189
|
+
## Result Contract
|
|
190
|
+
|
|
191
|
+
Every recursive run returns:
|
|
192
|
+
|
|
193
|
+
| Field | Meaning |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `answer` | Direct answer to the analyst question |
|
|
196
|
+
| `findings` | Valid cited claims accepted by the analyst policy |
|
|
197
|
+
| `trajectory` | DSPy RLM investigation steps |
|
|
198
|
+
| `modelCalls` | Successful provider completions used by the engine |
|
|
199
|
+
| `toolCalls` | Trace reads admitted for execution, including a read that later fails |
|
|
200
|
+
| `runtime` | Engine, package, sandbox identity, provider attempts, and successful completions |
|
|
201
|
+
|
|
202
|
+
Each finding requires exact `trace://` or `finding://` evidence.
|
|
203
|
+
Trace citations must resolve to an existing span.
|
|
204
|
+
When a citation includes an excerpt, the exact text must occur in that span or referenced finding.
|
|
205
|
+
Unknown, transformed, or fabricated identifiers are rejected.
|
|
206
|
+
An empty findings array means no submitted claim passed the evidence rules.
|
|
207
|
+
It does not prove the run was correct.
|
|
208
|
+
|
|
209
|
+
## Measure Analyst Quality
|
|
210
|
+
|
|
211
|
+
Measure the analyst on labeled traces before using its findings for automated changes.
|
|
212
|
+
At minimum, report issue recall, finding precision, exact evidence accuracy, trusted-negative false positives, repeat agreement, failures, calls, tokens, cost, and latency.
|
|
213
|
+
|
|
214
|
+
`runAnalystBenchmark()` compares any `AnalystBenchmarkRunner` implementations.
|
|
215
|
+
`agent-eval analyst-benchmark` runs the public AgentRx or CodeTraceBench adapters with:
|
|
216
|
+
|
|
217
|
+
1. an empty-finding baseline,
|
|
218
|
+
2. the actual DSPy RLM trace analyst.
|
|
219
|
+
|
|
220
|
+
```sh
|
|
221
|
+
agent-eval analyst-benchmark \
|
|
222
|
+
--dataset codetracebench \
|
|
223
|
+
--labels .artifacts/manifest.jsonl \
|
|
224
|
+
--trace-dir .artifacts/traces \
|
|
225
|
+
--artifact-dir .artifacts/results \
|
|
226
|
+
--out .artifacts/analyst-run \
|
|
227
|
+
--revision aa213b84ffb6690fc37ca15766d6ca174ec36d4d \
|
|
228
|
+
--split verified \
|
|
229
|
+
--base-url http://127.0.0.1:3355/v1 \
|
|
230
|
+
--api-key-env CLI_BRIDGE_BEARER \
|
|
231
|
+
--model claude-code/sonnet \
|
|
232
|
+
--python .venv/bin/python \
|
|
233
|
+
--limit 20 \
|
|
234
|
+
--seed 7 \
|
|
235
|
+
--concurrency 1 \
|
|
236
|
+
--max-cost-usd 5
|
|
416
237
|
```
|
|
417
238
|
|
|
418
|
-
|
|
419
|
-
It can
|
|
420
|
-
|
|
421
|
-
Its score is F1 over confirmed findings and independently identified misses.
|
|
422
|
-
Generic labels and run-level outcomes do not satisfy these requirements.
|
|
423
|
-
|
|
424
|
-
## Required Trace Data
|
|
425
|
-
|
|
426
|
-
Useful analysis needs:
|
|
427
|
-
|
|
428
|
-
- stable run, trace, and span IDs,
|
|
429
|
-
- parent-child links and ordered timestamps,
|
|
430
|
-
- model, prompt, and configuration identity,
|
|
431
|
-
- complete tool names, arguments, results, and error codes,
|
|
432
|
-
- token, cost, and latency data when available,
|
|
433
|
-
- retrieval source IDs and scores when retrieval is involved,
|
|
434
|
-
- final environment outcomes such as tests, task completion, or policy blocks.
|
|
435
|
-
|
|
436
|
-
Do not include secrets, raw OAuth tokens, or unredacted personal data.
|
|
239
|
+
The command writes every observation before producing `result.json` and `report.md`.
|
|
240
|
+
It can resume without repeating completed cases.
|
|
241
|
+
Dataset revisions, selected case IDs, input hashes, trace hashes, result artifacts, usage, errors, and comparison settings remain in the output.
|
|
437
242
|
|
|
438
|
-
Use
|
|
243
|
+
Use fresh development cases while changing questions or instructions.
|
|
244
|
+
Report final quality only on an untouched holdout.
|