Skip to main content
Kapso can run a caller-owned evaluator once for every candidate finalized by an evolution iteration. This is useful for integration tests, benchmark harnesses, validation suites, performance measurements, and observational holdout reporting. External metrics are deliberately separate from Kapso’s internal node.score. They do not affect idea generation, stopping, parent selection, or the final branch selected by default.

Basic usage

solution.metadata["external_metrics"] contains the metrics attached to the internally selected best candidate. All candidate metrics are retained in .kapso/experiment_history.json and, for resumable runs, in .kapso/run_state.json.

Callback context

The callback receives an IterationEvaluationContext:
  • iteration is the one-based campaign iteration, including resumed calls.
  • workspace_dir is a temporary detached Git worktree at exactly git_ref.
  • git_ref is the finalized candidate branch.
  • parent_ref is the branch from which the candidate was created.
  • node is a snapshot. Mutating it cannot change Kapso’s live search state.
The worktree exists only during the callback. Read outputs and return metrics before the callback exits; do not retain its path. Changes made inside it are discarded and the root experiment workspace’s current checkout is unchanged.

Result contract

The callback must return IterationEvaluationResult:
Metric names must be non-empty strings. Values must be finite numbers; booleans, strings, NaN, and infinity are rejected. primary_metric, when supplied, must name one of the returned metrics. Metadata must be JSON-compatible and have string keys. primary_metric describes the report. It does not make that metric a search or selection signal.

Lifecycle and persistence

Generic search normally finalizes one candidate per iteration. Tree search can finalize several; the evaluator runs exactly once for each new candidate. A resumed run evaluates only candidates created after the resume, not candidates already stored in its checkpoint. Make callbacks idempotent. If the process exits after the callback finishes but before the atomic checkpoint replacement, recovery may repeat that candidate’s measurement because the completed result was never made durable. Metrics are attached before experiment history and checkpoint persistence, so durable representations agree. Internal score, feedback, and stop state have already been produced by the strategy and are never replaced by the callback. Kapso does not include external metrics or evaluator errors in the agent-facing experiment-history formatter. They remain available to the caller and in durable run records without becoming feedback for later ideas.

Failure policy

The default record policy keeps the experiment loop running:
The candidate receives empty external metrics and an external_evaluation_error containing the exception type and message. The error is persisted in experiment history and the checkpoint. Use raise when every external measurement is mandatory:
Kapso raises IterationEvaluationError and stops before writing that candidate to experiment history or the run checkpoint. The candidate Git branch remains available for inspection.

Holdout guidance

A true holdout metric should remain observational. Returning it from this hook is safe with the default selection behavior because Kapso continues to rank by internal node.score. Do not copy holdout values into the agent prompt, feedback, internal score, or a custom parent-selection policy. When you want a metric to guide optimization, use a validation or development split and label it accordingly. Reserve the holdout split for final reporting after model and configuration choices are complete. The callback is trusted application code, not a security sandbox. Kapso isolates its normal filesystem view and node object to prevent accidental mutation; the callback process still has the operating-system permissions of the caller.

Resume compatibility

When an evaluator is configured, Kapso includes its module-qualified callable name and failure policy in the run configuration fingerprint. Resume therefore requires the same evaluator entry point and policy. Closures with the same callable name but different captured values cannot be distinguished; keep such parameters in stable application configuration and do not change them during a campaign.