feat(evals): tell a judge block which call it is serving - #468
Merged
Merged
Conversation
`Judge` calls its host-supplied block with only `instructions:` and `prompt:`,
but it serves three different calls — scoring an answer, recommending a fix for
a failed scenario, and writing the run's verdict. A host that wants to trace
them apart, budget them apart, or grade with a cheaper model than it writes the
verdict with has no way to tell which one it is in.
The only signal was the instructions string, so hosts matched against the gem's
own constants:
when ActiveAgent::Evals::Judge::RECOMMEND_INSTRUCTIONS then 'recommend'
That couples a host to prose the gem is free to reword, and rewording it does
not break anything loudly — every such host falls through to its `else` branch
and mislabels traces, which is worse than an error.
A block that accepts `kind:` now receives `:score`, `:recommend` or `:verdict`.
The block signature is public API, so the keyword reaches only a block that
names it or collects `**`. A block taking exactly `instructions:` and `prompt:`
would raise ArgumentError on a third keyword — and `ask` rescues StandardError,
so that would not even surface as an error: it would count as a judge failure
and quietly degrade the run to rule scoring. The existing judge suite, whose
`fake_judge` helper takes two keywords, covers that path.
Closes #462
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J9cRpVTtxA8QZBTmnYSC7V
# 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.
Judgecalls its host-supplied block with onlyinstructions:andprompt:, but it serves three different calls — scoring an answer, recommending a fix for a failed scenario, and writing the run's verdict. A host that wants to trace them apart, budget or rate-limit them, or grade with a cheaper model than it writes the verdict with has no way to tell which one it is in.The only signal was the instructions string, so hosts matched against the gem's own constants:
That couples a host to prose the gem is free to reword — and rewording it does not break anything loudly. Every such host falls through to its
elsebranch and mislabels traces, which is worse than an error.Changes
kind:now receives:score,:recommendor:verdict. The fouraskcall sites already knew which they were; threading a symbol through was mechanical.**, decided byProc#parametersand memoized per judge.Why the arity check is not optional
The block signature is public API, and a block taking exactly
instructions:andprompt:raisesArgumentErroron a third keyword. That would not even surface as an error:askrescuesStandardError, so the raise would be swallowed bywarn_failureand counted as a judge failure — silently degrading every run to rule scoring. Passing it unconditionally is a breaking change wearing a disguise, so it waits for a major if it is ever wanted.The existing judge suite is itself the backward-compatibility proof: its
fake_judgehelper builds a two-keyword block, and all of it still passes untouched.UAT Steps
BUNDLE_GEMFILE=gemfiles/rails8.gemfile bin/test test/evals/judge_call_kind_test.rb test/evals/judge_test.rb— 10 runs, 0 failures.... bin/test test/evals/*_test.rb— 133 runs, 0 failures.bin/lint— 552 files, no offenses.lib/active_agent/evals/judge.rband re-run (1) — 4 of the 5 new tests fail. The fifth is the legacy two-keyword case, which is expected to pass either way.Screenshots
N/A — no visual surface.
Closes
Closes #462
Special Handling
🤖 Generated with Claude Code
https://claude.ai/code/session_01J9cRpVTtxA8QZBTmnYSC7V
Generated by Claude Code