Fine-Grained Reward Estimation#
This page explains the core method behind LLM-as-a-Verifier.
The implementation lives in llm_verifier/fine_grained_reward.py.
Motivation: generation is not the bottleneck — verification is#
Most agents already “know” how to solve their tasks. On Terminal-Bench, repeatedly sampling 100 trajectories per task can outperform frontier models and nearly solve the entire benchmark — but the agent doesn’t know which trajectory is correct, particularly on long-horizon tasks. Standard LLM-as-a-Judge scoring fails to provide sufficiently fine-grained feedback: when comparing complex solutions, judges often assign the same discrete score, resulting in a tie. Coarse scoring leads to 27% ties on Terminal-Bench V2.
The scoring prompt#
Given a task prompt \(x\), a language model \(p_\theta\), a criterion \(c\), and two candidate trajectories \(\tau_i\) and \(\tau_j\), we construct scoring prompts and obtain the conditional distributions \(p_{\theta}(v \mid x,c,\tau_i)\) and \(p_{\theta}(v \mid x,c,\tau_j)\) by extracting the logprobs from the <score_A> and <score_B> tags:
You are an expert [domain] reviewer. You will see a task description and two trajectories.
Evaluation Criteria: [domain specific criteria]
Task: {task prompt}
Trajectory A: {A} Trajectory B: {B}
Carefully analyze each trajectory, then provide your final scores:
<score_A> INTEGER_1_TO_20 </score_A>
<score_B> INTEGER_1_TO_20 </score_B>
Rating Rules: Rate correctness on a 1–20 scale based on evaluation criteria
(1 = incorrect, 10 = borderline, 20 = correct)
Note
A letter-based scale (A–T) is used internally instead of digits so that each score level is a single token, enabling logprob extraction at any granularity.
The reward#
Rather than reducing each distribution into a single discrete score (as in LLM-as-a-Judge), LLM-as-a-Verifier approximates the reward of a trajectory \(\tau\) on task \(x\) as:
where:
\(C\) = number of evaluation criteria
\(K\) = number of repeated verifications
\(G\) = number of score tokens (granularity level; the default
GRANULARITYis 20)\(p_{\theta}(v_g \mid x, c, \tau)\) = probability assigned by model \(\theta\) to score token \(v_g\)
\(\phi(v_g)\) = maps each scoring token to a scalar value
\(V_{\text{score}} = \{v_1, \ldots, v_G\}\) = ordered set of discrete score tokens
This probabilistic formulation substantially reduces tie rates when comparing complex solutions: the continuous reward captures the verifier’s belief, including its uncertainty, instead of collapsing it to one integer.
From rewards to preferences#
For ranking, the continuous rewards convert to a pairwise preference:
These preferences are aggregated by the Probabilistic Pivot Tournament when selecting the best of N candidates.
Score caching#
Every scored (criterion, task, A, B, repeat) tuple is cacheable.
Pass cache="path/to/cache.json" to select (the bundled benchmarks each define their own cache under cache/): re-running with the same cache re-scores only comparisons not seen before, so interrupted runs resume cheaply and repeated experiments are free.
Failed calls scored as ties (on_error="tie") are never persisted to the cache.