Skip to content

refactor(evals): move the Anthropic judge from forced tool use to structured outputs - #850

Open
atomchung wants to merge 1 commit into
mainfrom
claude/judge-structured-outputs
Open

atomchung wants to merge 1 commit into
mainfrom
claude/judge-structured-outputs

Conversation

@atomchung

Copy link
Copy Markdown
Owner

Provenance

Recovered from uncommitted work that had been sitting in .claude/worktrees/qa-multi-turn-trace-counting-ed1876 since 2026-09-02. It was never committed, never pushed, and never had a PR. git log --all -S"_output_schema" returns nothing — none of this is on main in any form.

What changes

The Anthropic judge backend stops forcing a tool call and asks for a schema-constrained reply instead.

Before After
_tool(axes) returning a forced-tool spec with strict: true _output_schema(axes) returning the same JSON schema
"tool_choice": dict(STRUCTURED_TOOL_CHOICE) output_config.format = {"type": "json_schema", "schema": ...}
read the tool_use block read the text block, then the existing fail-closed _parse_verdicts
_structured_tool transport label _structured_output
SCORE_TOOL in tests/agent/judge_narrative.py SCORE_SCHEMA

The shape guarantee is unchanged — the same strict schema, the same additionalProperties: false, the same required axis list. A malformed verdict stays impossible rather than merely unlikely.

Why

A call that exists only to get JSON back is what structured outputs are for, and Claude Fable 5.1 rejects forced tool use outright.

This is not a live bug fix. DEFAULT_MODELS["anthropic"] is claude-opus-5, which still accepts forced tool use, so nothing on main is broken today. This is the prerequisite for pointing TR_JUDGE_MODEL at Fable 5.1.

New coverage

  • test_structured_output_judge_reads_the_text_block_and_fails_closed — five degenerate replies (no text block, empty text, non-JSON, wrong shape, missing axis) must each raise rather than score.
  • The harness tests now assert tools and tool_choice are absent from the sent request, so a silent regression to forced tool use fails.

Verification

python3 tests/run_all.py → PASS: all 60 suites passed (group: all).

The remaining half of that recovered worktree — instruction-surface prompt-audit cleanup and a compact freeform witness — is a separate concern and does not touch these files. It follows in its own PR.

🤖 Generated with Claude Code

…uctured outputs

A call that exists only to get JSON back is what structured outputs are for,
and Claude Fable 5.1 rejects forced tool use outright. The shape guarantee is
unchanged: `output_config.format` carries the same strict JSON schema the
forced tool carried, so a malformed verdict stays impossible rather than
merely unlikely.

`_tool()` becomes `_output_schema()`, `STRUCTURED_TOOL_CHOICE` is gone, the
reply is read from the text block and still passes through the existing
fail-closed `_parse_verdicts`. `judge_trade_answers.py`'s transport label
follows (`_structured_tool` -> `_structured_output`), as do
`tests/agent/judge_narrative.py`'s `SCORE_TOOL` -> `SCORE_SCHEMA` and both
README descriptions.

This is not a live bug fix. `DEFAULT_MODELS["anthropic"]` is `claude-opus-5`,
which still accepts forced tool use; this is the prerequisite for pointing
`TR_JUDGE_MODEL` at Fable 5.1.

New coverage: `test_structured_output_judge_reads_the_text_block_and_fails_closed`
asserts five degenerate replies all raise rather than score, and the harness
tests now assert `tools`/`tool_choice` are absent from the sent request.

60/60 offline suites pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant