feat(evals): correlate evaluation traces with the result that caused them - #465
Merged
Merged
Conversation
…them `Runner` already accepted an `around_evaluation:` wrapper and already owned a `run_id` in its `metadata:`, and a telemetry backend already offers a per-block agent scope that can report the trace it opened. Nothing joined them, so every host that wanted a report row to link to the conversation behind it wrote the same correlation plumbing itself. `ActiveAgent::Evals::Correlation` is that join. A run mints a `run_id`, each evaluation a `result_id`, and both ride every trace opened inside them as `eval.`-prefixed attributes. The trace ids travel the other way onto `result.replay.metadata`: `trace_id` for the replay, `judge_trace_ids` for the judge calls that graded it. The verdict, being a judge call outside any evaluation, lands on the run metadata a Report carries rather than on whichever result was evaluated last. The tracer is injected, so the module takes on no telemetry dependency and `require "active_agent/evals"` still loads on its own — covered by a test that drives a correlated run out of process. Hand the object to `Runner.new(around_evaluation:)` directly; a plain lambda there is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TonsOfFun
force-pushed
the
feat/evals-telemetry-correlation
branch
from
September 18, 2026 02:46
d90cd4d to
482169a
Compare
TonsOfFun
marked this pull request as ready for review
September 18, 2026 16:46
# Conflicts: # CHANGELOG.md
TonsOfFun
added a commit
that referenced
this pull request
Sep 18, 2026
chore(release): activeagent and actionagent 1.6.3 Brings main up to the 1.6.3 release line: context-meter token attribution (#458), host-adapter metering, provider validation and on_removed (#460), evaluation trace correlation (#465), the judge call kind (#468, closes #462), Agent#generations (#469, closes #464), and the host-adapter docs (#466).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ActiveAgent::Evalsshipped two halves of this feature and never connected them.Runneraccepts anaround_evaluation:wrapper and owns arun_idin itsmetadata:. A telemetry backend offers a per-block agent scope that can report the trace it opened. But nothing in the gem joined the two, so a host wanting a report row to link back to the conversation behind it had to write the correlation itself — minting result ids, keeping an execution-local context stack, slicing correlation keys into trace attributes, and routing replay trace ids apart from judge trace ids. The dashboard end already assumed this existed:docs/framework/evaluations.mdsays persisted results "preserve host run/result IDs and response/judge trace IDs", with no producer for them upstream.This adds that producer.
Changes
ActiveAgent::Evals::Correlation(lib/active_agent/evals/correlation.rb). Configured with the trace names for the agent and its judge plus a tracer callable:with_run(metadata) { |metadata| ... }mintsrun_idunless the caller supplied one and yields the metadata hash to pass toRunner.new(metadata:).around_evaluation:callable — it implementscall(scenario, spec, &block), mints aresult_id, and merges the correlation ontoresult.replay.metadata.replay/judgewrap a block in a trace. A replay's trace id lands onreplay.metadata["trace_id"]; a judge call appends to["judge_trace_ids"]. A judge call outside any evaluation is the verdict and appends to the run metadata — the same hash the Report carries — rather than to whichever result was evaluated last.eval.-prefixed attributes.trace_keys:chooses which keys; anything else in the run metadata stays on the report but off the traces.require "active_agent/evals"is documented as loading standalone, andtest/evals/standalone_load_test.rbnow drives a full correlated run out of process to hold that. Without a tracer the correlation still mints ids and merges metadata, with the blocks untraced.around_evaluation:lambda is untouched;Runnerdid not change at all.docs/framework/evaluations.md, plus a CHANGELOG entry.One design point worth flagging for review:
with_runmutates the caller's hash in place rather than copying it. A copy looks tidier but silently breaks the verdict — the run is reopened aroundReport#verdictafterRunner#callreturns, and a copy means the verdict's trace id lands on a hash nobody holds.test_a_reopened_run_accumulates_onto_the_metadata_a_report_already_carriespins it.UAT Steps
mise x ruby@3.4.9 -- env BUNDLE_GEMFILE=$PWD/gemfiles/rails8.gemfile bundle installmise x ruby@3.4.9 -- env BUNDLE_GEMFILE=$PWD/gemfiles/rails8.gemfile CI=true RAILS_ENV=test bin/test test/evals/correlation_test.rb test/evals/standalone_load_test.rb— 13 runs, 0 failures.... bin/test— 2033 runs, 0 failures. The 34 errors are pre-existing onmain(live-API tests inRubyLLMProviderTest/RubyLLMIntegrationTesthitting a provider billing limit);mainreports the same 34 at 2020 runs.mise x ruby@3.4.9 -- bin/lint— 554 files, 0 offenses.... bundle exec ruby -Ilib test/evals/support/standalone_correlation_script.rbprintsok.Screenshots
N/A — no visual surface.
Closes
Unreported
Special Handling
🤖 Generated with Claude Code