feat(tools): add TraceIndex for progressive trace disclosure in judge-based evaluators - #343
feat(tools): add TraceIndex for progressive trace disclosure in judge-based evaluators#343pdebjyot wants to merge 4 commits into
Conversation
… agents
Large agent trajectories overflow a judge model's context window when inlined
into the evaluation prompt, forcing Experiment error-isolation to record the
case as score:0 / test_pass:False — a false failure for a correct agent.
TraceIndex builds an in-memory index over a Session and exposes:
- overview(): one compact line per span (index, type, tool, sizes, preview)
that always fits the judge context, substituted for the full trajectory.
- three discovery tools the judge calls on demand: list_spans / get_span /
search_spans (mirrors MLflow's ListSpans/GetSpan/SearchTraceRegex).
get_span pages oversized spans via max_read_chars + offset so no single tool
return can itself overflow the judge. Backend-agnostic: consumes any Session
produced by a provider/mapper.
… TraceIndex Covers overview() formatting, list_spans/get_span/search_spans behavior, offset paging on oversized spans, and end-to-end use through OutputEvaluator with tools=index.tools.
Compares OutputEvaluator judging grounded vs fabricated claims two ways — full trajectory inlined vs overview + discovery tools — asserting the index-equipped judge separates grounded from fabricated where inline overflows. Skips without live Bedrock credentials.
|
/strands review |
|
@strandly-the-agent Review the changes and provide consolidate feedbacks. Also, I can see that the tool contains certain existing logic in strands-evals. What's your suggestion here? |
|
TL;DR — request changes. The list/get/search shape is the right primitive (MLflow-style progressive disclosure), the diff is tight and additive, and the paranoid read is clean. But four verified defects make the judge score silently wrong — the failure mode this PR exists to eliminate — and #342's actual bug (overflow reported as Verified: head 🔴 Blockers
On the overlap with existing strands-evals logic (your question)Six touchpoints; one is worth fixing now because it's a bug, two deserve a design call, three only look similar — leave them alone. No case for a big shared-helper refactor.
🟡 Should-fix (6)
QuestionsBlocking
Non-blocking Appendix — non-blocking (11)
Also checked, clean: no network/env/subprocess/telemetry anywhere in the diff — I'm an AI reviewer — treat this as prepared input for a human decision, not a gate. Happy to re-review on update. |
feat(tools): TraceIndex — progressive trace disclosure for judge-based evaluators
Branch:
feat/trace-indexBuilds on: #324 (custom
tools=onOutputEvaluator/TrajectoryEvaluator)Addresses: #342
Problem
When the trajectory handed to a judge-based evaluator is larger than the judge model's context window, the judge call raises
ContextWindowOverflowException.Experiment._run_evaluatorcatches it under error isolation and records the case asscore: 0, test_pass: False— indistinguishable from a genuine quality failure. A correct agent response gets a false-negative failing score purely because its trace was too big for the judge to read.This isn't a theoretical edge. We observe it on real production agent traces, where routine multi-step sessions serialize past a 200K-token judge window and the largest run into the millions of tokens — so under a default Sonnet-class judge, correct agents are being silently scored as failures today. The problem is stack-agnostic: we reproduced identical overflow behavior across strands-evals, DeepEval, and a Langfuse-style managed judge on a shared Bedrock judge model (three distinct error signatures, same trace-size cliff).
The measured evidence below is from a synthetic, deterministic, offline benchmark — no real data — so it's independently reproducible.
What this PR adds
TraceIndex— an in-memory index over aSessionthat lets a judge read a large trace without inlining it. It is an established pattern: MLflow's Agent-as-a-Judge trace scorers hand the judgeListSpans/GetSpan/SearchTraceRegex, and Zhuge et al.'s Agent-as-a-Judge (arXiv:2410.10934) usesretrieve/read/locatemodules to pull only the relevant segments.overview()— one compact line per span (index, type, tool name, sizes, preview). Always fits the judge context; substituted for the full trajectory.list_spans/get_span/search_spans— discovery tools the judge calls on demand (mirrors the MLflow triad).get_spanpages oversized spans viamax_read_chars(default 8000) +offset, so no single tool return can itself overflow the judge.Sessiona provider/mapper produces.Two disclosure strategies compose from these pieces: index (substitute
overview()into the prompt — portable, works with any judge) and explore (index + discovery tools via #324'stools=— Strands-native, best accuracy).Evidence
Cross-framework matrix (1344 cells). 200-trace labeled corpus × 3 frameworks (strands-evals, DeepEval, Langfuse-style) × 4 metrics (groundedness, accuracy, trajectory, tool_use) × inline/index/explore, all bound to one shared Bedrock judge. Each metric scored as a binary classifier against planted ground truth (overflow ⇒ wrong prediction):
indexandexplorehold accuracy on traces that fit and recover it on traces that overflow (inline is unusable in the overflow bucket; index/explore score correctly).exploretools beat a bare index when the decisive fact is buried in a large tool result: onwrong_tooltraces the deciding refund amount sits inside a large search result thatoverview()elides, so the index judge false-fails groundedness (0.83);get_span/search_spanslet the judge retrieve it → groundedness 1.00.Ground-truth A/B (grounded / fabricated claims, evidence buried mid-trace): the index-equipped judge separates grounded from fabricated where the inline judge overflows — captured as the integ test
test_judge_reliability_inline_vs_explore.Known limitation (called out honestly)
TrajectoryEvaluatorinlinesactual_trajectoryunconditionally (case_prompt_template.py:51), so the trajectory metric still overflows on large traces even with the index — the substitution only reaches evaluators that route through the caller-controlledactual_output(the output-family metrics, fixed today). Making the trajectory metric disclosure-aware needs a template change and is proposed as a follow-up; this PR does not change that path.Separately, this PR adds the capability but does not change
_run_evaluator's error handling — distinguishing harness overflow from a qualityscore: 0is tracked in #342 as an independent change.Testing
OutputEvaluatorwithtools=index.tools) — all pass.ruff check/ruff formatclean.Checklist