Skip to content

Getting started

EvalHawk is in development. The offline CLI is usable locally; the demonstration below makes no model calls and needs no API credentials.

Prerequisites

  • uv: it installs the right Python (3.12 for development) automatically, so your system Python version doesn't matter.
  • Git.

Set up

git clone https://github.com/evalhawk/evalhawk.git
cd evalhawk
uv sync --all-groups          # creates .venv with the package and all dev tools
uv run pre-commit install     # run the checks automatically on every commit

Everyday commands

Command What it does
uv run pytest Run the tests (add -m "not slow" to skip the long simulations)
uv run ruff check . Lint
uv run ruff format . Format code, including Python blocks in Markdown
uv run pyright Type-check (strict on core/ and stats/)
uv run lint-imports Check the architecture's layer rules
uv run zensical serve Preview these docs at http://localhost:8000
uv run evalhawk --version Run the CLI

Windows

If uv isn't found in PowerShell, add it to the path for the session: env:Path="env:USERPROFILE\.local\bin;$env:Path"

Next

Read the engineering standards before your first pull request.

Offline support-bot demo

uv run evalhawk demo init demo --seed 0
uv run evalhawk import demo/v1.jsonl --target-version v1 --db demo/state.db
uv run evalhawk import demo/v2.jsonl --target-version v2 --db demo/state.db
uv run evalhawk report --run v1 --db demo/state.db --seed 0
uv run evalhawk report --run v2 --db demo/state.db --seed 0
uv run evalhawk report --run v2 --tag billing --db demo/state.db --seed 0
uv run evalhawk compare --baseline v1 --candidate v2 --db demo/state.db --seed 0
uv run evalhawk compare --baseline v1 --candidate v2 --tag billing --db demo/state.db
uv run evalhawk audit --run v1 --db demo/state.db

Keep the database and virtual environment outside cloud-synced folders on Windows. If Windows refuses the generated evalhawk executable launcher, use uv run python -m evalhawk in place of uv run evalhawk in the commands above. The default demo contains 1,000 independent support cases, each with three dependent paraphrases. Labels and outcomes are simulated, not actual blind human reviews. The synthetic judge is lenient; v2 improves shipping/account but regresses on billing. After viewing the estimates, inspect demo/answer_key.json for the true test rates. Reports and comparisons never read that file. No confidence interval is guaranteed to contain truth for a particular seed.

flowchart LR
    A[Recorded outputs and annotations] --> B[Validate and import bounded batches]
    B --> C[(SQLite: locked train/dev/test)]
    C --> D[Dev: latest reviewer votes, majority]
    C --> E[Test: judge predictions, audited read]
    D --> F[Judge report card and calibration]
    F --> G[Naive / Rogan-Gladen / PPI with intervals]
    E --> G
    C --> H[Pair versions by example and repeat]
    H --> I[Judge-proxy difference with cluster interval]

Reading the report

  • Train remains unused, reserved for tuning. Dev calibrates the judge. Test provides predictions and every outcome read is logged.
  • Rate reports choose the smallest example ID, repeat 0, per locked group before inspecting outcomes. They estimate this fixed representative population, not all paraphrases. Cases must be independent and resolved labels representative.
  • Per reviewer, latest timestamp wins; equal-time conflicts abstain. UNKNOWN votes abstain; majority PASS/FAIL wins; ties are unresolved. Reports show exclusions.
  • Rogan-Gladen assumes stable sensitivity/specificity. PPI assumes labelled and unlabelled representatives come from the same population. Unknown judgments and unresolved labels can introduce selection bias; rates are conditional.
  • Comparisons estimate judge-proxy changes, not bias-corrected human quality. Dependent rows use cluster intervals; IID McNemar and sample planning are omitted. Tag intervals are not simultaneous/multiplicity-adjusted.

To exercise independent-pair sample planning, generate another demo with --paraphrases 1, import both files into its own database, and compare with --target-effect 0.05. Planning reports required total and additional pairs for a five-percentage-point effect at 80% power, subject to IID and discordance assumptions. It is not a promise that collecting those pairs will prove improvement.

Import your recorded data

JSONL rows contain input, output, judge (PASS/FAIL/UNKNOWN), optional reference, group_id, tags, repeat_index, and labels:

{"input":"Refund request","output":"Approved","judge":"PASS","labels":[{"annotator":"reviewer-a","outcome":"FAIL","created_at":"2026-01-01T00:00:00Z"}]}

CSV uses text input/output/judge, pipe-separated tags, and JSON cells for labels, contexts, metadata, usage and repeat_index. Supply a stable created_at for label history. Non-unit inclusion probabilities are rejected by report adjudication until weighted inference is implemented. Human labels must be collected without showing the judge's answer or version identity; file import cannot verify blindness.

Import writes a sibling .run.json manifest by default. Repeat the same command to resume; select --run-manifest NEW_PATH for a genuinely new execution. A version name is not an execution identity: if several executions share it, supply their exact run ID to report/compare/audit. Criterion, judge and split configuration must match when resuming or comparing. Imports may leave completed writes if a later row fails; processed rows are not a count of newly inserted unique records.

Remaining work includes a public human-labelled dataset, DeepEval-format mapping, live targets/judges, weighted inference and full-population clustered calibration/PPI. See Proposed ADRs 0011 and 0012.