AxProverBase

ID axproverbase · Company Axiomatic AI

AxProverBase [6] is a self-contained LangGraph Lean agent with its own proposer → builder → reviewer → memory loop. This prover uses the AgentProver with the AxProverBaseHarness.

Authentication

Billing is directly against an API provider. By default the harness reads the provider’s key from the host environment:

export ANTHROPIC_API_KEY=...

Check if the key is in your environment with:

open-atp auth-status axproverbase

Alternatively, pass the key to the harness explicitly:

from open_atp.harness import AxProverBaseHarness

AxProverBaseHarness(provider_api_key="sk-...")

The provider is inferred from the model prefix, and the harness forwards the key into the sandbox under its canonical env var (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY). See Tracking cost and usage for details.

Using the prover

Standard prover via Python API

The simplest way to run the prover is through standard_prover() which uses a standard configuration pointing at the claude-opus-4-8 model. Either set ANTHROPIC_API_KEY in the host environment or pass it explicitly to the harness. Here, we prove the MUL_REORDER example theorem:

from pathlib import Path

from open_atp.backends.docker import DockerBackend
from open_atp.config import standard_prover
from open_atp.examples import EXAMPLE, example_task

task = example_task(EXAMPLE.MUL_REORDER)
prover = standard_prover("axproverbase", backend=DockerBackend())
result = prover.prove(task, output_dir=Path("demo"))

Standard prover via CLI

The standard prover can also be run from the CLI:

open-atp prove path/to/task.lean output_dir axproverbase

Customizing the prover

To override knobs like model, effort, and max_iterations, construct the class directly.

from pathlib import Path

from open_atp.backends.docker import DockerBackend
from open_atp.examples import EXAMPLE, example_task
from open_atp.harness import AxProverBaseHarness
from open_atp.images import DEFAULT_IMAGE
from open_atp.provers import AgentProver

task = example_task(EXAMPLE.MUL_REORDER)
prover = AgentProver(
    harness=AxProverBaseHarness(
        model="claude-opus-4-8",
        effort="high",
        max_iterations=None,  # None keeps ax-prover's own default of 50; set an int to cap
    ),
    backend=DockerBackend(image=DEFAULT_IMAGE),
)
result = prover.prove(task, output_dir=Path("demo"))

See the API Reference for all AxProverBaseHarness configuration options.

Harness details

AxProverBase has its own Lean tooling. It doesn’t use any of the skills or the Lean LSP MCP server [8] used by the other agent harnesses. See the script below for the full ax-prover CLI invocation.

src/open_atp/harness/assets/scripts/axprover_agent.sh
#!/usr/bin/env bash
set -euo pipefail

# $PROMPT is exported by the AgentProver before this script runs but is unused:
# ax-prover ships its own prompts. The backend has already cd'd into the workdir
# and symlinked .lake to the warm Mathlib cache.
#
# The Harness contract is project-wide, while ax-prover needs a target, so we
# self-discover every .lean carrying a `sorry` (skipping the warm .lake cache)
# and prove each in turn.
#
# Notes:
#   * --config is a TOP-LEVEL flag and MUST precede the `prove` subcommand. The
#     CLI's --config is argparse action="append" with default ["default.yaml"], so
#     passing axprover.yaml *appends* to (does not replace) the bundled default.yaml:
#     the effective merge is [default.yaml, axprover.yaml]. AxProverBaseHarness relies on
#     this (it overrides only deltas) and works around the deep-merge it implies --
#     see AxProverBaseHarness._render_config.
#   * --skip-build reuses the warm Mathlib .lake instead of re-running lake build
#     (the same assumption the other harnesses make).
#   * --overwrite re-proves even files ax-prover thinks are already done.
#   * `|| true` keeps one unprovable file from aborting the rest; the final
#     Verifier pass is the source of truth either way.
#   * -o writes ax_output.<target>.json: per-target {success, error, summary,
#     input_tokens, output_tokens, ...}. AxProverBaseHarness.parse_result sums the token fields
#     across these files for cost (the pinned fork commit adds the usage fields).
#   * ax-prover logs are human-readable (not the JSONL the parsers consume), so we
#     tee each run's stdout+stderr to ax_prover.<target>.log. The file lands in the
#     workdir and is pulled back with it (Modal) / lives on the bind mount (Docker),
#     so the logs survive even when the harness discards the stream. PYTHONUNBUFFERED
#     defeats CPython's block-buffering of a piped stdout so the tee is line-fresh.
#
# https://github.com/henryrobbins/ax-prover-base

export PYTHONUNBUFFERED=1

while IFS= read -r f; do
  [ -z "$f" ] && continue
  # Strip the leading ./ that `grep -rl . ` prepends: ax-prover derives a module
  # path via file_path.replace("/", ".") and a leading ./ corrupts it (-> ..Module),
  # which then resolves to a bogus file and hides every sorry ("No unproven found").
  f="${f#./}"
  safe=$(printf '%s' "$f" | tr '/.' '__')
  # `|| true` is on the pipeline: with `set -o pipefail` a failing ax-prover still
  # lets the loop continue, and tee captures the full log either way.
  ax-prover --config axprover.yaml prove "$f" \
    --folder . \
    --skip-build \
    --overwrite \
    -o "ax_output.${safe}.json" 2>&1 | tee "ax_prover.${safe}.log" || true
done < <(grep -rl --include='*.lean' '\bsorry\b' . | grep -v '/\.lake/' || true)

Tracking cost and usage

The ax-prover CLI does not report cost by default. We manually log input / output tokens to ax_output.<target>.json files, which are summed and combined with pricing estimates in COST_PER_MTOK to populate cost_usd in ProofResult. Those files carry no cache breakdown, so the whole input is priced at the uncached rate and cost_usd is an upper bound. You can also monitor consumption from your provider’s usage dashboard. For example, Anthropic’s dashboard is at Anthropic Usage.