3tears-evals
Evals for LLM features, from a classifier to a tool-using agent: it runs each version over the same cases and says what moved, with the uncertainty that says whether the move is real. Python 3.14+.
pip install 3tears-evals
Rung 0 is one TOML file and no Python. examples/1-triage/triage.toml triages
30 support emails, each with the triage a person gave it, three times each:
# Rung 0, no Python: python -m threetears.evals triage.toml
cases = "emails.jsonl"
grader = ["category", "urgent", "needs_human"]
recording = "recordings/triage-toml.jsonl"
k = 3
[runner]
model = "deepseek/deepseek-v4-flash"
prompt = "Triage this support email:\n\n{email}"
[runner.output]
category = ["billing", "bug", "account", "sales", "spam"]
urgent = "bool"
needs_human = "bool"
python -m threetears.evals examples/1-triage/triage.toml replays the recorded model, offline and free, and its
first line says how it went:
triage: 16/30 cases passed; 14 misses: gdpr-delete, nonprofit, phish-suspend, phish-invoice, crash-at-checkout, and 9 more
Below it come each field's pass rate, each category label's precision and recall, the top misses with why each
missed, and where its report page is. Then climb the ladder, one idea per rung:
| Rung | Example | Climb it when |
|---|---|---|
| 1 | 1-triage/triage.py |
Point it at your own function: 15 lines of Python. |
| 2 | 2-replies/ |
When no code can grade the answer, let a model judge it, and check the judge against a person. |
| 3 | 3-invoices/ |
Changing a prompt? Compare it against the current one, and get a report you can hand to a reviewer. |
| 4 | 4-booking/ |
Is your agent doing the right thing to the world, not only saying it? Then serve your evals to the team. |
Where the science lives:
- Measuring soundly: which design answers your question, how many cases, and the traps.
- Judges and calibration: whether to trust a judge, and how far.
- Reading reports: every verdict, interval and disclosure a report prints.
Wiring it into your app (your store, your launcher, your world): Adopting the engine.
Install
pip install 3tears-evals is the base install. The FastMCP transport, for serving evals to an agent, is the [fastmcp] extra:
pip install "3tears-evals[fastmcp]"
To install a pre-release or an unreleased checkout, install from the repository at a tag instead:
pip install "3tears-evals @ git+https://github.com/pacepace/3tears.git@<tag>#subdirectory=packages/evals"
In a checkout of this repo, uv sync at the root does it, and the commands above run from packages/evals with
uv run in front. threetears.evals.__version__ says which version is installed. The completion client (Model) is
on the base install: it speaks each provider's HTTP API over httpx, with no SDK and no other 3tears package. The
examples run on cheap models through OpenRouter (OPENROUTER_API_KEY); Model(id, provider="anthropic")
(ANTHROPIC_API_KEY) and provider="openai" (OPENAI_API_KEY) are options, and base_url= takes any
OpenAI-compatible endpoint. After a pip install, fetch the examples from the repository first
(how).
The public API is still changing. From 0.70.0 only its host contract is promised: a name on it moves with a deprecation cycle, and any other declared name may change in any release, recorded in
MIGRATIONS.toml. About 520 names are declared, mapped by rung; the rest a root exports are provisional (stability tiers).
What is different
- A verdict, not a mean. Every version is run over the same frozen cases and tested case by case against a control, and evidence that cannot tell two versions apart says "not separated", never "no difference" (why).
- Guardrails apart from capability. What a version must never do is decided on its own, so a gain cannot pay for a breach (why).
- A judge is measured, not trusted. Every judged reading carries the evidence tier its judge's agreement with people and with itself reached (how).
- An agent is graded on the world it leaves, a world your app declares, with checks proven to beat doing nothing and a conformance kit your app runs against its own world (what is new).
Docs
These links are relative to the package, so on GitHub each opens the page at the ref you are reading; on PyPI, where this README is the package's description, they do not resolve, so read the docs in the repository at your tag.
Climb
- Building an inventory: what to test before a line of code, ending in rung 0; then which cases a classifier needs, and how many.
- The ladder: rungs 1 to 4, through the four examples.
- Concepts: the six concepts, and the glossary of every other word.
Go deeper
- Judges and calibration: rubrics, labels, and whether to trust the judge.
- Reading reports: what each part of a comparison and a report means.
- Measuring soundly: which design answers a question, how many cases, and the traps campaigns have hit.
- Evaluating a tool-using agent: a world, seeds, goal checks, the conformance kit, and why the world contract is shaped as it is.
- Cost and budgets: what a launch will cost, and what stops it.
- Serving: the command line, keeping and reading runs, the actions an agent calls, and MCP.
- Adopting the engine: your own host, store, kind and launcher.
Background and reference
- Design: the principles the engine keeps, why each concept is shaped as it is, and the prior art.
- Open problems: known gaps.
- The surface: the declared names on one page, by rung.
- Reference: every declared name, setting, report field, goal-check builtin and action, generated from the code.
For experts
| Capability | API | Doc | Example |
|---|---|---|---|
| World contract | World(State, tools=[...]), each case's world key, expression graders; on a host, WorldRegistry |
Evaluating a tool-using agent | 4-booking/ |
| World conformance kit | check_world_conformance (threetears.evals.kernel.host) |
Step 5 | |
| Guardrails, decided apart from capability | grader(fn, guard=...), Judge(guard={dim: ...}), Result.guardrails(); on a host, MetricDescriptor(guardrail=True), RubricDim(axis="boundary") |
Reading the guardrails | 3-invoices/ |
| Equivalence, the only verdict that says "good enough" | grader("label", margin=0.05), grader(fn, margin=...), grader(rating, range=(1, 5)); on a host, MetricDescriptor.materiality_threshold and value_range |
Reading a comparison | 3-invoices/ |
| Typed verdicts and a CI gate | Result.verdicts(), Result.gate(), python -m threetears.evals gate --fail-on regressed,breached |
gate |
3-invoices/ |
| Grids of factors | arms named as k=v pairs ("prompt=v2, effort=low"), Result.against |
Start from the question | 3-invoices/ |
| Campaigns and declared designs | create_campaign, update_campaign, add_runs_to_campaign, declared_design, exploratory, control_from_run_id |
Declare the design | |
| Cost against quality | Answer, max_cost_usd=, the frontier |
Cost and budgets, the frontier | |
| Judge calibration and evidence tiers | Judge(labels=...), rate_result, judge_agreement, JudgeEvidenceTier |
Judges and calibration | 2-replies/ |
| Analysis written by a model over frozen evidence | generate_analysis |
Having a model write the analysis | llm_analysis.py |
| Cassettes | World(tools=[...], recording=...); on a host, cassette_mode="capture" / "replay" |
Step 4, on a host | |
| MCP | threetears.evals.actions, mount_fastmcp (extra fastmcp) |
Serving | 4-booking/serve.py |
| Keeping runs | Eval(...).run(store=path) (by default .evals/evals.sqlite); any DocumentStore, proven with STORE_CONFORMANCE_CASES |
Keep your runs, the store | |
| A complete host | EvalHost, HostProfile, CandidateKind |
Adopting the engine | courierhost, then toyhost |
Import only from the public roots the reference lists: a module below a root is internal and may move. A public name that leaves a root is recorded with its new home in each release's notes (Upgrading: names that move).
Metadata
Release files for 3tears-evals 0.70.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| 3tears_evals-0.70.1.tar.gz | 4.3 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 3tears_evals-0.70.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 6.7 MB
Release files / 3tears_evals-0.70.1.tar.gz
| Download URL | 3tears_evals-0.70.1.tar.gz |
|---|---|
| Size | 4.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
471817e85cb2db40943d5e2b64784d487d13a09630b1b26cf1b5cc7c4b4ba137
|
|
BLAKE2b-256 checksum How to use checksums |
49f589f17502a88c5530214439b0ba60374eb1b4e398164eb4fd698f0c81fa49
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logRelease files / 3tears_evals-0.70.1-py3-none-any.whl
| Download URL | 3tears_evals-0.70.1-py3-none-any.whl |
|---|---|
| Size | 2.4 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a601c740d2a6ca5bdf5b41f615c8bb62393b1f80dd69405338aafd8b1bbbd539
|
|
BLAKE2b-256 checksum How to use checksums |
3cf8f4e0ea0dd781a163563d958733108e3c6b10091314eb5d4d70ae8ba0e482
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency log