A modular, extensible grading system for agent trajectories.
This system evaluates agent performance by running a pipeline of Helpers, Verifiers, and Scoring Methods. It is designed to be composable, allowing you to easily add new types of evaluations without modifying the core runner logic.
The grading pipeline consists of three main stages:
- Helpers: Pre-computation steps that extract common data (e.g., diffing files, parsing logs) to be shared across multiple verifiers.
- Verifiers (Evals): Individual checks that run against the trajectory and helper data. These can be LLM-based judges, static analysis tools, or domain-specific validators. Verifiers can depend on other verifiers.
- Scoring: A final aggregation step that takes all verifier results and computes a final score for the run.
graph TD
A[Inputs: Snapshots, Trajectory] --> B[Helpers]
B --> C{Verifiers}
C -->|Dependency| C
C --> D[Scoring Method]
D --> E[Final Grade]
Helpers are designed to "compute once, use many times." They run before any verifiers.
- Purpose: Efficient data extraction (e.g., don't re-download and diff the S3 bucket for every single verifier).
- Registry: Defined in
runner/helpers/registry.py. - Implementation: A simple async function that returns
Any.
- Add a new ID to
HelperIdsinrunner/helpers/models.py. - Implement the logic in a new file under
runner/helpers/. - Register it in
runner/helpers/registry.py.
# runner/helpers/my_helper/main.py
async def my_helper(
initial_snapshot: io.BytesIO,
final_snapshot: io.BytesIO,
trajectory: AgentTrajectoryOutput
) -> dict:
# logic here
return {"result": "data"}Verifiers are the core units of grading. Each verifier is an instance of an Eval Definition.
- Eval Defn: The "class" of evaluation (e.g.,
OUTPUT_LLM,SQL_VALIDATOR). Defined in code. - Verifier: An instance of that class configured for a specific task.
- Registry: Defined in
runner/evals/registry.py.
- Add a new ID to
EvalIdsinrunner/evals/models.py. - Create the implementation in
runner/evals/. It receives anEvalImplInputobject. - Register it in
runner/evals/registry.py, specifying helper dependencies and config fields.
# runner/evals/registry.py
EVAL_REGISTRY = {
EvalIds.MY_EVAL: EvalDefn(
eval_id=EvalIds.MY_EVAL,
eval_impl=my_eval_impl,
helper_dependencies=[HelperIds.SNAPSHOT_DIFF],
eval_config_fields=[],
verifier_config_fields=[
TaskFieldSchema(field_id="threshold", field_type=TaskFieldType.NUMBER, label="Threshold")
],
verifier_output_fields=[],
)
}The scoring method takes the list of all VerifierResult objects and reduces them to a single ScoringMethodResult.
- Purpose: Flexible grading policies (e.g., weighted sum, pass/fail thresholds).
- Registry: Defined in
runner/scoring_methods/registry.py.
- Add a new ID to
ScoringMethodIdsinrunner/scoring_methods/models.py. - Implement the reduction logic.
- Register it in
runner/scoring_methods/registry.py.
-
Set up environment variables:
cp .env.example .env # Edit .env with your LLM API key -
Install dependencies:
uv sync
Run the grading system locally using the CLI:
uv run python -m runner.main \
--grading-run-id "run_123" \
--trajectory-id "traj_456" \
--initial-snapshot "./original.zip" \
--final-snapshot "./final.zip" \
--trajectory "./trajectory.json" \
--grading-settings "./settings.json" \
--verifiers "./verifiers.json" \
--eval-configs "./eval_configs.json" \
--scoring-config "./scoring_config.json" \
--output "./results.json"The grading runner requires several configuration files. Here's how to create them:
1. grading_settings.json - LLM judge configuration:
{
"llm_judge_model": "anthropic/claude-3-5-sonnet-20241022",
"llm_judge_extra_args": null
}2. verifiers.json - Grading criteria:
[
{
"verifier_id": "ver_001",
"verifier_version": 1,
"world_id": null,
"task_id": "my_task",
"eval_config_id": "ec_output_llm",
"verifier_values": {
"criteria": "The agent successfully completed the requested task",
"is_primary_objective": true
},
"verifier_index": 0,
"verifier_dependencies": null
}
]3. eval_configs.json - Eval definitions:
[
{
"eval_config_id": "ec_output_llm",
"eval_config_name": "Output LLM Verifier",
"eval_defn_id": "output_llm",
"eval_config_values": {}
}
]Available eval IDs:
output_llm- LLM-based output evaluationoutput_llm_lite- Lightweight output evaluation
4. scoring_config.json - Score calculation:
{
"scoring_config_id": "sc_default",
"scoring_config_name": "Default Scoring",
"scoring_defn_id": "task_score_unweighted_and_universal_penalty",
"scoring_config_values": {
"task_primary_objective_scaling_factor": 2.0,
"task_non_primary_objective_scaling_factor": 1.0,
"task_negative_scaling_factor": 2.0,
"universal_penalty_cap": 0.2,
"final_score_ceiling": 1.0,
"final_score_floor": 0.0
}
}Important: The grading system expects
.zipfiles for snapshots. If you have.tar.gzfiles from the environment, convert them first:
import tarfile
import zipfile
def tar_gz_to_zip(tar_gz_path: str, zip_path: str):
with tarfile.open(tar_gz_path, "r:gz") as tar:
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
for member in tar.getmembers():
if member.isfile():
f = tar.extractfile(member)
if f is not None:
zf.writestr(member.name, f.read())
tar_gz_to_zip("snapshot.tar.gz", "snapshot.zip")The context object passed to every verifier implementation.
initial_snapshot_bytes/final_snapshot_bytes: Raw zip files (asio.BytesIO)trajectory: Full conversation history and metadatagrading_settings: Global settings (e.g., LLM judge model)verifier: Configuration for this check instanceeval_config: Configuration for the type of evaldependencies: Results from other verifiers this one depends onhelper_results: Output of all pre-computed helpers
The output of a single verifier execution.
verifier_id: ID of the verifier that produced this resultverifier_version: Version for point-in-time accuracyscore: Float score (typically 0.0 to 1.0)verifier_result_values: Flexible dict for metadata (reasoning, errors, etc.)status:okorerrormessage: Optional context message
Global settings for the grading run.
llm_judge_model: Model for LLM-based verifiersllm_judge_extra_args: Additional LLM arguments
The final aggregated output.
final_score: Single float scorescoring_method_result_values: Breakdown of calculation