Skip to main content
This guide explains how to extend AgentCompass without crossing runtime boundaries.

Add a Benchmark

A benchmark owns the dataset contract, task preparation, evaluation logic, and aggregate metrics. It should not own model-provider calls, agent loops, sandbox lifecycle, or provider-specific image selection. Add a benchmark when you are introducing a new task family or a new scoring protocol. If the change only adapts an existing benchmark to a new sandbox image or workspace layout, add a recipe instead.

Benchmark File

Create a module under src/agentcompass/benchmarks/. Most benchmarks define a RuntimeBenchmarkConfig subclass, an optional BenchmarkPlan, and a BaseBenchmark subclass.

Benchmark Responsibilities

Use TaskSpec.metadata for source records, image names, docker metadata, expected files, and benchmark-specific IDs. Use PreparedTask.input and PreparedTask.output for the actual harness contract.

Register And Verify

Export the benchmark in src/agentcompass/benchmarks/__init__.py so registry import side effects can discover it:
Then verify the component list and run a single task:

Add a Harness

A harness owns the agent or model execution loop. It consumes PreparedTask, RunRequest.model, and an EnvironmentSession, then returns a normalized RunResult. It should not load datasets, score benchmark correctness, or hard-code provider-specific sandbox images. Add a harness when you are integrating a new agent framework, coding assistant, GUI agent, direct model-call path, or tool-use strategy.

Harness File

Create a module under src/agentcompass/harnesses/. A typical harness defines a user-facing config, a runtime plan, and a BaseHarness subclass.

Harness Responsibilities

Use prepared.input.workspace, prepared.input.files, prepared.input.media, prepared.input.tools, and prepared.input.messages instead of reaching back into benchmark internals. A harness should be reusable across benchmarks that emit the same prepared-task contract.

Register And Verify

Export the harness in src/agentcompass/harnesses/__init__.py:
Then check discovery and a smoke run:

Add an Environment

An environment provider owns execution primitives: commands, file transfer, text IO, directory sync, endpoint discovery, and teardown. It should not know benchmark scoring rules or agent behavior. Add an environment when AgentCompass needs to run the same benchmark/harness matrix on a new local process, container runtime, cloud sandbox, or remote execution provider.

Environment File

Create a module under src/agentcompass/environments/. Each provider usually has a session class, config dataclass, and BaseEnvironment implementation.

Environment Responsibilities

Provider defaults belong in config/defaults.yaml when they are useful for normal users. Benchmark-specific image, snapshot, workspace, or resource mapping belongs in recipes.

Register And Verify

Export the environment from src/agentcompass/environments/__init__.py. Guard optional SDK imports if the dependency is not always installed:
Then compile the provider and run a minimal command-oriented benchmark or harness smoke test:

Add a Recipe

A recipe is a compatibility layer between one benchmark family and one environment provider. It rewrites the per-task ExecutionPlan before the sandbox starts, usually to select images, workspaces, resource requests, snapshots, environment variables, or evaluation layout. Add a recipe when task metadata already contains provider-relevant information and users should not have to pass it manually on the CLI.

Recipe File

Create a module under src/agentcompass/recipes/<benchmark_family>/. Keep one provider recipe per file when the provider behavior is meaningfully different.

Recipe Responsibilities

Recipes are applied automatically when execution.enabled_recipes is empty. Users can restrict recipe selection with --enabled-recipes.

Register And Verify

Export the recipe from the package initializer:
Run a one-sample task and confirm the logs contain Recipe matched with the new recipe id:

Add an Analyzer

Analyzers run after a task produces a RunResult. They inspect trajectories, metrics, errors, latency, model output, or tool calls, then attach an AnalysisResult under analysis_result.<AnalyzerId> in each details JSON. Aggregate fields are rendered into analysis_summary.md and analysis_summary.json. Add an analyzer when the logic is post-execution diagnosis or statistics. Do not put analyzer logic in a benchmark or harness if it can be rerun later with agentcompass analysis.

Analyzer File

Create a new file under src/agentcompass/analyzers/basic/, or create a dedicated sub-package for a larger analyzer family. Each analyzer must subclass BaseAnalyzer, register with @ANALYZERS.register(), and implement analysis().

Analyzer Attributes

AnalysisResult.is_badcase should be True for detected badcases, False for explicitly clean samples, and None for statistics-only analyzers that should not affect badcase ratios.

Aggregate Fields

Declare every cross-task summary field in distribution_fields: Only keys returned inside AnalysisResult.details can be aggregated.

Register And Verify

Export the analyzer from package initializers so registry import side effects can see it:
Then verify it appears in the component list:
Run it during evaluation:
Or rerun it on existing results:

Developer Notes

  • Keep analyzer code read-only with respect to task execution. It should inspect persisted results, not mutate benchmark behavior.
  • Use datasets for benchmark-specific analyzers instead of branching on unrelated task metadata.
  • Use base_analyzer and priority when a benchmark-specific analyzer should override a generic analyzer.
  • Return a descriptive error instead of raising when a sample cannot be analyzed because optional trajectory fields are missing.