Skip to main content

ek

ek (Evaluation Kit) — a framework for building Knowledge Evaluation systems, evaluating the outputs of information-extraction systems. OCR is treated as the noisiest special case of a general problem, so the core is source-agnostic and the OCR pieces are optional.

import ek

ek.score("hello wrld", "hello world")  # -> Score(value=0.0909..., metric='cer')
ek.score("hello wrld", "hello world", metric="wer").value  # 0.5
ek.evaluate(
    [("ct", "cat"), ("dg", "dog")], metric="cer"
).aggregate  # 0.333... (global CER)

What it does

Evaluating an extraction splits along two axes — is there a gold answer (reference-based) or not (reference-free), and are we scoring one item or a whole corpus. ek gives you both halves through two facades over one shared typed schema:

  • score() / evaluate()reference-based: compare against gold, one item or a corpus, the metric chosen by output type (string → CER/WER, record → field-F1), aggregated correctly (global error-rate accumulation, micro-F1; never a naive mean) with optional per-slice cuts.
  • estimate_quality()reference-free: gather signals → calibrate → validate → decide accept/flag/block, with no gold answer.

Everything swappable is a strategy injected with a smart default, so the simple call works out of the box and every layer stays replaceable.

Evaluate an OCR engine

The first concrete instance: measure OCR accuracy over a gold corpus. ek consumes ocracy's normalized OcrResult, so it can benchmark any of its ~16 engines — or any image -> OcrResult callable of your own.

import ek.ocr

gold = {
    "inv-1": {
        "image": "scan.png",
        "reference_text": "INVOICE 2024",
        "slice": "invoices",
    }
}
report = ek.ocr.evaluate_ocr(
    "ocrmac",
    gold,
    metric="cer",
    normalize=["lower", "collapse_whitespace"],
    persist=True,
)
report.aggregate  # corpus CER
report.per_slice  # CER per document slice
report.detail["per_item"]  # prediction, reference, score, confidence per document

Gold corpora, results, and runs persist to local dol stores under ~/.local/share/ek/.

Evaluate an AI agent — in cost per successful task

The second instance. Agent evaluation is the same 2×2, but the evaluated object is an episode (tool calls + observations ending in a final state) and the unit that matters is cost per successfully completed task, not cost per token — because tokens spent on a failed episode are pure waste.

from ek.agents import TaskSpec, run_suite, per_million

tasks = [
    TaskSpec("t1", input="2+2", gold="4", slice="easy"),
    TaskSpec("t2", input="17*23", gold="391", slice="hard"),
]

report = run_suite(my_agent, tasks, k=8, price=per_million(3.0, 15.0))

report.pass_hat_k  # reliability: succeeds on ALL 8 trials (the production number)
report.pass_at_k  # capability: succeeds on ANY of 8 trials
report.success_ci  # a Wilson interval — a point estimate is not a result
report.cost[
    "cost_per_success"
]  # Cost-of-Pass: dollars per *successful* task (inf if none)
report.per_slice  # ...cut by difficulty

Two numbers, not one: an agent that "usually works" is not shippable, and pass^k is what exposes it (a 90%-reliable agent is only ~43% reliable across 8 tries). Agent scores are stochastic, so the regression gate compares intervals, not points, and refuses to compare runs whose user-simulator or suite version changed:

from ek.agents import agent_regression_gate, save_agent_baseline

save_agent_baseline(report, "v1")
assert agent_regression_gate(
    new_report, "v1"
)  # fails only on a *real* regression, not noise

Also included: BFCL-style tool-call correctness (cost-weighted by the tool grammar, so a wrong argument to a destructive tool costs more), an order-sensitive trajectory distance, and an LLM-as-judge signal — with judge_validation(), because an unvalidated judge is a liability, not a metric. None of this needs an extra: ek.agents is pure-python, and the bridges duck-type, so it scores an Inspect/DeepEval run without importing either.

Install

pip install ek            # lean, permissive core (dol, config2py, jiwer, rapidfuzz)
pip install "ek[ocr]"     # + the ocracy OCR fleet (install engines via ocracy extras)
pip install "ek[agents]"  # + external agent harnesses to *run* (inspect-ai, deepeval, ragas)
pip install "ek[all]"     # + the permissive capability tiers (metrics, calibration, ...)

Heavier or copyleft/non-commercial libraries are never installed by default, and a CI license gate enforces it; see the extras in pyproject.toml.

CLI

ek cer "hello wrld" "hello world"     # character error rate
ek wer "hello wrld" "hello world"     # word error rate
ek pass-k 10 9 --k 8                  # pass@k (capability) vs pass^k (reliability)
ek cost-per-success 12.50 5           # dollars per successfully completed task
ek where                              # the local data folder
ek check tesseract                    # what an OCR engine needs to run

For contributors

The architecture, conventions, and the research behind the design are documented for agents and humans in AGENTS.md, the dev skills under skills/, and the research reports under misc/docs/.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ek-0.1.10.tar.gz (463.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ek-0.1.10-py3-none-any.whl (150.5 kB view details)

Uploaded Python 3

File details

Details for the file ek-0.1.10.tar.gz.

File metadata

  • Download URL: ek-0.1.10.tar.gz
  • Upload date:
  • Size: 463.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ek-0.1.10.tar.gz
Algorithm Hash digest
SHA256 90f46dd85f02a14ebb9170f18973f1ef824b45371eff557881550a8ef9c0a8ab
MD5 f1a18e72e5746caa9b521d2945b177fc
BLAKE2b-256 028531e324f9a940cd96535505d0462b65d4236df0ee2f216de5f4302aadf00b

See more details on using hashes here.

File details

Details for the file ek-0.1.10-py3-none-any.whl.

File metadata

  • Download URL: ek-0.1.10-py3-none-any.whl
  • Upload date:
  • Size: 150.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ek-0.1.10-py3-none-any.whl
Algorithm Hash digest
SHA256 73c6561924b0a11c3bcd3e543a2d5b1f2337c733522c537105b4cbc401d2b86f
MD5 2b8091b002d82b390c8d1d2de3e0c911
BLAKE2b-256 f9869cec7f672b27a05cbd94e2631a423c99fd4eefb8e074d9d460fa1aad5d5e

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.10 This release

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.0.6

2 files

0.0.5

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page