Honest RAG evaluation harness: judge metrics with confidence intervals, cost and latency first-class, offline-first.
Project description
GNOMON
Evaluates a RAG pipeline and reports quality with the confidence interval included, plus cost and latency, running offline with Ollama. Statistical honesty is an invariant, not decoration: no judge metric is reported as a bare number.
The name is the gnomon — the rod of a sundial that casts the shadow you read — and it doubles as a backronym for what the project does: Gated Numerical Offline Metrics Over N-cases.
What it does
Reads a versioned dataset of evaluation cases, runs each case against a target RAG (via adapter), scores the responses with an LLM judge, and reports faithfulness and context precision — each with a confidence interval — alongside token cost and latency in milliseconds. The same eval runs as a regression test in CI and fails the build when a metric drops below a configurable threshold.
The example evaluates a MockTarget (canonical responses) scored by a local Ollama judge, with no external dependency or paid API key. Switching to a real RAG that speaks the OpenAI-compat protocol means changing the [target] block in config/example.toml — not the code.
Run the evaluation (offline, one command)
Prerequisite: Docker. The example evaluates the MockTarget scored by the real Ollama judge — no external RAG service required.
# 1. Start Ollama and pull the judge model (first run takes a few minutes)
docker compose up -d ollama
docker compose exec ollama ollama pull llama3
# 2. Run the example evaluation (versioned config + dataset)
docker compose run --rm harness
The process exits with code 0 if the gate passes, 1 if any metric falls below the threshold in config/docker.toml (RF-09) — that is what makes it usable as a regression gate in CI.
Example output
Evaluation report
=================
Quality (judge metrics):
faithfulness: mean=0.933 [0.800, 1.000] (95% CI, N=3)
context_precision: mean=1.000 [1.000, 1.000] (95% CI, N=3)
Cost & latency:
total tokens: 411
mean latency: 512.0 ms
Per case:
rpg-001: 137 tokens, 512.0 ms
rpg-002: 137 tokens, 512.0 ms
rpg-003: 137 tokens, 512.0 ms
N is the number of cases over which the interval is calculated (the dataset is the sample; see ADR-008). The interval is a bootstrap percentile, bounded to [0,1] by construction. To tighten the CI, add more cases to the dataset — not more judge runs.
Run on the host (local Ollama, no Docker for the harness)
pip install -e ".[dev]"
# with Ollama running at localhost:11434 and llama3 pulled:
gnomon --config config/example.toml
Pre-commit setup
pip install pre-commit
pre-commit install
Run pre-commit install once per clone.
Deterministic check (no Ollama, no Docker)
pip install -e ".[dev]"
python -m pytest -q # 77 tests: unit, reproducibility, and gate smoke
Reproducibility (RF-11 / RNF-01)
Same seed + same config + same machine produce the same numbers. With a deterministic judge (temperature 0) and a seeded bootstrap, the result is identical across runs. Verified by test:
python -m pytest tests/reproducibility -q
Architecture
The evaluation core depends on interfaces, never on concrete implementations. Targets and judge depend on the core. Details and diagram in docs/ARCHITECTURE.md.
Config -> Runner -> [Target adapter] -> target RAG (OpenAI-compat or Mock)
-> [Judge] --------> faithfulness + context precision
-> [Metrics] ------> mean over cases + CI (bootstrap)
-> [Reporting] ----> machine + human report (same source)
-> [Gate] ---------> pass/fail in CI (compares ci_low)
Configuration
All configuration lives in versioned TOML files (RNF-07):
config/example.toml— run on the host with Ollama atlocalhost:11434config/docker.toml— run viadocker compose run --rm harness(judge points to theollamaservice on the compose network)
The parameters (seed, judge runs, model, gate thresholds, target) are documented inside the files themselves. Invalid configuration — including a gate threshold outside [0,1] or a missing seed in reproducible mode — fails-closed at load time, before any model call.
How to run the tests
pip install -e ".[dev]"
python -m pytest -q # full suite: 77 tests
python -m pytest tests/unit -q # unit tests only
python -m pytest tests/reproducibility -q # reproducibility only
python -m pytest tests/gate -q # gate smoke only
ruff check src tests && ruff format --check src tests
Design decisions
The decisions governing the project are in docs/adr/:
- ADR-001 — adapter-based target, so the harness can serve any RAG.
- ADR-002 — judge nondeterminism, with a confidence interval instead of a single number.
- ADR-003 — offline-first execution with Ollama, so the example runs at zero cost.
- ADR-004 — cost and latency as first-class metrics.
- ADR-005 — retrieved contexts in an OpenAI-compat extension field (fail-closed).
- ADR-006 — gate compares against the lower bound of the CI (
ci_low), not the mean. - ADR-007 — Ollama judge determinism via
seed+run, one call per case. - ADR-008 — per-case aggregation + bootstrap CI (statistical honesty of
n).
Full requirements in docs/REQUIREMENTS.md. Product overview in docs/PRODUCT_OVERVIEW.md. The project development loop is in docs/DEVELOPMENT_LOOP.md.
What it does not do (yet)
Answer relevance, context recall, temporal dashboard, multi-target comparison, and history persistence are left for v2. The architecture accommodates these additions without a rewrite. The detailed backlog, with dependencies and proposed sequencing, is in docs/ROADMAP.md.
Contributing
Commits follow Conventional Commits. The CI gate runs pytest and ruff check — PRs must pass both.
License
Apache-2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gnomon_eval-0.1.0.tar.gz.
File metadata
- Download URL: gnomon_eval-0.1.0.tar.gz
- Upload date:
- Size: 44.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
409277a54ebb84cc8a98eab9a32fe006c5f087c4b262bbee5d8ec858624d17b7
|
|
| MD5 |
d2c82a20ad97a504bbd59e9d8938e0a5
|
|
| BLAKE2b-256 |
9b0f754561cab5f57588eb5a4debc691b14c1c2d786fc71a37876c7a8b25a617
|
File details
Details for the file gnomon_eval-0.1.0-py3-none-any.whl.
File metadata
- Download URL: gnomon_eval-0.1.0-py3-none-any.whl
- Upload date:
- Size: 68.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83f6bce46a3e547401d0df7f696e5f554ad262ca5f13c4fbc3cb90e83d784fea
|
|
| MD5 |
4872ffe900f9b1eec0aa9867bed78373
|
|
| BLAKE2b-256 |
33914ef5faf94ca50355e910eb0d7c3c19dee52cc6b89c6282429e1289030068
|