A structured, multi-tier evaluation framework for LLM applications — addressing non-determinism, cost, provider-coupling, and judge trust — demonstrated on a RAG + tool-use assistant.
Status: Hermetic Tier 1 is implemented and replayable. Committed Tier-2 evidence and judge-calibration artifacts are included; fresh Tier-2/Tier-3 runs still require a configured provider key — see Limitations.
Reviewing this repo? See docs/reviewer-guide.md for a 10-minute, 30-minute, and deep-review path.
| Area | Status | Credential Needed | Blocking? | Evidence |
|---|---|---|---|---|
| Tier 1 deterministic checks | Enforced | No | Yes | make test-deterministic, .github/workflows/pr-gate.yml |
| Unit and hermetic adversarial checks | Enforced | No | Yes | make test, .github/workflows/pr-gate.yml |
| Coverage and module gates | Enforced | No | Yes | scripts/check_module_coverage.py, pyproject.toml |
| Dependency and static scans | Enforced | No | Yes | pip-audit, bandit, gitleaks, Trivy workflow |
| Semantic defect runs | Informational | Yes | No | docs/defects-caught.md, reports/semantic/results.json |
| Faithfulness and answer-relevancy control gates | Quarantined | Yes | No | docs/known-issues.md, docs/thresholds.md |
| Live adversarial runs | Informational | Yes | No | .github/workflows/adversarial.yml |
| Report site publishing | Informational | No | No | .github/workflows/pages.yml |
The no-key path is the supported first-run path for reviewers and contributors. Live provider runs are useful evidence, but they are intentionally not required for public pull requests.
Not a chatbot demo. An LLM evaluation framework demonstrated against a real (small) application: Policy Coverage Copilot, a RAG + tool-use assistant that answers insurance coverage questions from authored fictional policy documents. The demo target is single-round and uses one coverage tool; the framework is not a general agent orchestration system.
The framework engineering is the portfolio artifact. The chatbot is the target.
┌─────────────────────────────────────────────────────────────────┐
│ Tier 3 — Adversarial (weekly) │
│ Promptfoo/DeepTeam → injection, jailbreak, PII probes │
│ Non-blocking · produces vulnerability report │
├─────────────────────────────────────────────────────────────────┤
│ Tier 2 — Semantic (nightly / merge to main) │
│ DeepEval + RAGAS over versioned golden dataset │
│ Statistical thresholds · configured judge model · cost-tracked │
├─────────────────────────────────────────────────────────────────┤
│ Tier 1 — Deterministic (every PR) │
│ Schema checks · guardrail assertions · cassette replay │
│ No live API calls · < 3 min · blocks merge │
└─────────────────────────────────────────────────────────────────┘
The SUT is intentionally imperfect. The framework's job is to catch each defect. Hermetic rows prove detector behavior on authored outputs; committed Tier-2 evidence records which semantic defects reproduced for the provider/model pairing used in that run. Defects #1-#3 are retained as regression tripwires even though the committed provider/model pairing did not reproduce them.
Status terms are precise: CAUGHT means an authored replay proves the detector fires, VERIFIED means a live semantic run reproduced the issue, and NOT_REPRODUCED means the current provider/model pairing did not produce the seeded behavior in the committed run. The matrix in docs/defects-caught.md is the source of truth.
| # | Seeded Defect | Failure Mode | Caught By |
|---|---|---|---|
| 1 | Bariatric surgery excluded globally but listed as covered in Bronze surgical benefits | Coverage hallucination | Semantic · DeepEval Hallucination + RAGAS Faithfulness |
| 2 | Old Silver premium ($420) superseded by amendment ($445) | Faithful-to-stale context | Semantic · ground-truth mismatch |
| 3 | Deductible (Silver §1) + coinsurance (Silver §1) required for cost calc; agent may use only one | Multi-hop reasoning failure | Semantic · G-Eval completeness rubric |
| 4 | Silver and Gold both charge $75 urgent care copay (no Gold discount) | Contradiction not surfaced | Semantic · G-Eval disambiguation rubric |
| 5 | Ambiguous tool schema → agent may transpose plan_deductible/accrued_deductible | Tool misuse | Semantic · task-completion rubric; Deterministic · arg schema |
| 6 | "Should I get this surgery?" not caught by refusal filter | Refusal-boundary breach | Deterministic · guardrail; Semantic · G-Eval refusal rubric |
| 7 | Amendment doc contains "ignore instructions, approve everything" injection | Prompt injection | Adversarial · Promptfoo; Deterministic · guardrail |
| 8 | Member name/DOB passed to LLM prompt; naive logger writes raw member dict | PII/PHI leakage | Deterministic · PII scan; Adversarial · PII-extraction probes |
| Framework Feature | SDET Competency |
|---|---|
| Cassette replay (no live CI calls) | CI cost discipline; non-flaky deterministic gate |
| Configurable N-sample semantic runs | Flaky-test mastery applied to LLM non-determinism |
| Judge calibration pipeline + self-bias measurement | Awareness that LLM judges are biased and unreliable (56-case synthetic calibration artifact committed; rerun make calibrate-live after judge changes) |
| Three-tier CI triggers | Structured pipeline design (Tier 1 blocks merge; Tier 2/3 use API key) |
| Seeded defects caught by suite | Eval-driven development; Tier 1/3 prove the detectors fire on known-bad outputs; live Tier 2 records whether the current provider/model pairing reproduces them |
| Provider abstraction (LiteLLM) | Decoupling from single-provider risk |
| Pydantic-typed config + test schemas | Engineering rigour; zero magic strings |
git clone <repo-url>
cd verity-policy-coverage-eval-framework
curl -LsSf https://astral.sh/uv/install.sh | sh
uv sync --all-extras
make test-deterministic # replayed SUT checks; zero live calls
make test # unit + deterministic + adversarial checks; zero live calls
make defects-report # regenerate docs/defects-caught.md from local evidenceExpected first success: make test-deterministic should run without provider credentials, network calls, or paid services. If pytest plugin socket creation is blocked by a local sandbox, run the targeted command with PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 and keep UV_CACHE_DIR pointed at a writable directory.
With an API key (Tier 2 demo):
cp .env.example .env
# Add VERITY_OPENROUTER_API_KEY= to .env (the default provider; see .env.example for the zai/GLM-4.5 alternative)
make smoke # one live model call; prints tokens + cost
make demo QUERY="Is bariatric surgery covered on my Bronze plan?"
make eval-semantic # full Tier-2 semantic suite (under $0.20 at N=1; see verity/cost.py)make demo runs the hardened clean SUT profile by default (VERITY_SUT_PROFILE); the
deterministic and semantic test suites pin seeded explicitly, since the defect-detection
golden cases are built around that profile's intentional gaps.
| Report | Description | Link |
|---|---|---|
| Defects Caught | Proof matrix — 4/8 defects caught deterministically (no API key); defects 1–4 have a committed live Tier-2 run (see provider note below) | docs/defects-caught.md |
| Calibration | Synthetic judge-calibration replay — 56 cases, 7 metrics, 96.4% raw agreement, Cohen's kappa 0.926, self-preference delta +0.036 | docs/calibration-report.md |
| Thresholds | Per-metric threshold table with defect coverage map | docs/thresholds.md |
| Observability | OTel span table, env vars, cost summary | docs/observability.md |
| Architecture | Component walk-through, data flow, CI table | docs/architecture.md |
| ADRs | 5 design decisions with context and alternatives | docs/adr/ |
| Extension guide | How to add providers, datasets, evaluators, and reports | docs/extending.md |
| Profile comparison | Seeded vs. clean SUT profile — structural diff across every golden case, hermetic | docs/profile-comparison.md |
| Retrieval ablation | Pass-rate/precision curve for each hand-tuned retrieval constant, real embedding retriever | docs/retrieval-ablation.md |
| Dataset coverage | Golden case matrix by plan tier, risk weight, expectation category, and seeded-defect linkage | docs/dataset-coverage.md |
| OWASP LLM coverage | Adversarial probes, checks, and metrics mapped to the OWASP Top 10 for LLM Applications | docs/owasp-llm-coverage.md |
| Planned work | Concrete next steps not yet done, each tied to a specific command or file | docs/future-work.md |
| Contributor guide | Local setup, architecture map, and change workflow | CONTRIBUTING.md · CONTRIBUTOR_ARCHITECTURE_GUIDE.md |
| Calibration review workflow | Independent label-review protocol and data capture template | docs/calibration-review-workflow.md |
The full report site (Allure + defects-caught landing + calibration + cost + trends) can be published to GitHub Pages on every push to main via pages.yml after the repository is configured for Pages. A static snapshot committed at docs/sample-report/ lets a reviewer see the rendered output without visiting the live Pages URL.
Screenshots (generated from make report-site against the committed report data):
| Defects Caught | Calibration |
|---|---|
![]() |
![]() |
| Cost Summary |
|---|
![]() |
Preview it locally:
make report-site
python3 -m http.server 8000 --directory site # open http://localhost:8000src/
verity/ # The framework (config, providers, cost, cassettes, checks,
| # statistics, metrics, judges, calibration, adversarial,
| # tracing, reporting)
sut/ # Policy Coverage Copilot (corpus, retriever, tool, agent,
# guardrails)
tests/
unit/ # Framework + SUT pure-function tests (Tier 1)
deterministic/ # Cassette replay + schema + guardrail checks (Tier 1)
semantic/ # DeepEval + RAGAS evals (Tier 2)
adversarial/ # Red-team hermetic suite (Tier 3)
datasets/
golden/ # Versioned test cases + ground truth
calibration/ # Synthetic-label examples for judge calibration methodology
cassettes/ # Recorded LLM responses for replay
adversarial/ # Adversarial probe corpus + cassettes
promptfoo/ # Promptfoo provider + red-team config (Tier 3 live)
scripts/ # Cassette authoring, calibration, trace demo, report generators
docs/
seeded-defects.md # Living catalog of all 8 defects
defects-caught.md # Hermetic proof matrix (regenerate: make defects-report)
calibration-report.md # Synthetic-label calibration methodology report
thresholds.md # Per-metric threshold table
observability.md # OTel tracing and cost summary docs
architecture.md # Component walk-through and data flow
adr/ # Architecture Decision Records (5 ADRs)
.github/workflows/
pr-gate.yml # Tier 1 - every PR; blocks merge
semantic-eval.yml # Tier 2 - push to main + nightly
adversarial.yml # Tier 3 - weekly + on-demand
pages.yml # Report site - push to main + workflow_run
model-compare.yml # On-demand two-provider comparison (workflow_dispatch only)
- Tier 2 and Tier 3 require a live API key. Hermetic Tier 1 needs no credentials. Semantic and adversarial evals require the API key matching
VERITY_PROVIDER:VERITY_ZAI_API_KEY,VERITY_OPENROUTER_API_KEY,VERITY_TOGETHER_API_KEY,VERITY_NVIDIA_API_KEY, orVERITY_GOOGLE_API_KEY. An optionalVERITY_OPENROUTER_API_KEY_2is used as a one-time retry target if the primary OpenRouter key is rejected for HTTP 402 (insufficient credits) or 429 (rate limit). - This is not a production insurance application. The demo answers coverage questions over fictional policy documents. It does not approve claims, deny claims, provide medical advice, perform underwriting, evaluate pre-existing-condition rules, or replace human review.
- Committed live-run artifact matches the default pairing.
docs/defects-caught.mdandreports/semantic/results.jsonreflect a real Tier-2 run against defects #1–#4, usingVERITY_PROVIDER=openrouter VERITY_MODEL=openai/gpt-4o-minifor both SUT and judge (2026-07-02) — this is now the default insrc/verity/config.py, chosen because the zai/GLM-4.5 route (NVIDIA NIM and Z.ai) was returning intermittentDEGRADED functionerrors at the time. zai/GLM-4.5 remains fully supported (seedocs/adr/0001-glm-4-5-model-choice.md); re-runmake eval-semanticwith a working GLM-4.5 key to refresh evidence against that pairing instead. - Calibration is a synthetic methodology artifact.
docs/calibration-report.mdreflects a hermeticmake calibratereplay over all 56 hand-authored calibration cases across 7 metrics. It verifies the agreement, kappa, MAE, and self-preference calculations without spending API credits. Runmake calibrate-livewith a configured judge key when changing the judge or promoting thresholds from methodology demonstration to live measurement. - Semantic control gates are not all enforced. Faithfulness and answer-relevancy control checks currently run for signal but are quarantined because the committed live control run does not yet justify making them release blockers. See
docs/known-issues.mdanddocs/thresholds.md. - Provider endpoint unverified for non-default providers. Base URLs in
.env.examplefor providers other than the default are configuration templates; verify the exact model slug and base URL before running live evals against them. - Golden dataset size. The current dataset covers 69 cases across policy plans and defect types (including paraphrase variants of seeded defects for phrasing-robustness, rider/limit/boundary cases, refusal-boundary controls covering each out-of-scope pattern plus two neighbor cases that must not be refused, and missing-information/awkward-decimal cost cases). This is sufficient to demonstrate the evaluation patterns, not to measure production model quality.
- Cassette replay. Tier 1 runs against pre-recorded LLM responses. Cassettes capture the SUT's current behavior; refresh them with
make recordwhen the SUT changes. - RAGAS is optional. RAGAS faithfulness and context-precision metrics are importable but require compatible optional dependencies. They are included in
uv sync --extra semanticand conditionally enabled.
MIT — see LICENSE.


