proofrag
Turn "did my change make the RAG better or worse?" into one reproducible command.
You (the agent) wire the user's app to the kit; the kit does dataset generation,
judging, and reporting.
When to use
- User changed a prompt, model, chunker, embedder, or retriever and wants to know if quality moved.
- User has docs/a knowledge base but no evaluation set.
- User wants a hallucination/groundedness number, or a CI gate on answer quality.
Install the engine
This skill drives the proofrag CLI. Make sure it's on PATH (install once), or run
it ad-hoc with uvx:
uv tool install "proofrag[anthropic]" # or: pipx install "proofrag[anthropic]"
# no install needed: uvx "proofrag[anthropic]" demo
Use [openai] instead of [anthropic] for an OpenAI-compatible/local backend.
Credentials: ANTHROPIC_API_KEY (default, cheap Haiku judge) or OPENAI_API_KEY
(OPENAI_BASE_URL for local/Ollama). No key? proofrag demo renders a sample scorecard.
If both keys exist, auto-detection chooses Anthropic; use PROOFRAG_PROVIDER=openai
to override. Proofrag does not auto-load .env, so source it first.
The loop
Inspect and generate from the user's corpus.
proofrag corpus ./docs
Use --include, --exclude, and --no-gitignore when the docs tree is noisy.
PDF loading needs the proofrag[pdf] extra; HTML is supported by default.
Then generate:
proofrag generate --corpus ./docs --out goldenset.jsonl --n 20
Produces JSONL: {id, question, gold_answer, gold_contexts[], difficulty, sources[]}
with tiers single_doc / multi_doc / unanswerable, plus context_metadata
for each gold context. Commit this file — it is versioned.
Validate the golden set before committing it.
proofrag validate --goldenset goldenset.jsonl --corpus ./docs --out validation.json
This checks the JSONL contract, duplicate ids/questions, answerable cases without
gold contexts, unanswerable cases that still cite context, source coverage, and a
stable fingerprint. It exits non-zero on hard errors; add --strict to fail on
warnings too.
Manually search the full corpus for every unanswerable candidate, and verify
both distinct sources are necessary for every multi_doc case. Schema validation
cannot prove either semantic property.
Run the user's RAG over every question to produce predictions.
Prefer proofrag run when the app exposes a local HTTP endpoint or Python callable:
proofrag run --goldenset goldenset.jsonl \
--endpoint http://localhost:8000/ask \
--out predictions.jsonl
proofrag run --goldenset goldenset.jsonl \
--callable myapp.rag:answer \
--out predictions.jsonl
HTTP mode POSTs {"id": "...", "question": "..."}. Callable mode calls
answer(question) by default; add --call-style record to pass the full golden
record. The adapter may return an answer string, (answer, contexts), or:
{"id": "q000", "answer": "<system answer>", "retrieved_contexts": ["<chunk>", "..."]}
retrieved_contexts are the chunks their retriever returned (used for retrieval
metrics). If neither adapter fits, write a small driver script that emits the same
JSONL shape. If you can't find their entrypoint, ask the user where their "ask a
question" function lives.
Judge.
proofrag evaluate --goldenset goldenset.jsonl --predictions predictions.jsonl --out results.json
Scores groundedness, correctness, completeness, citation_quality (LLM-as-judge,
pinned + fingerprinted) and rank-aware retrieval metrics — Recall@k, Precision@k,
NDCG@k, MRR (--k sets the cutoff; Jaccard by default, --exact for original
chunks, or --semantic for embeddings). Evaluation fails if prediction IDs do not
exactly cover the golden set or any judge call fails.
To score generation with DeepEval instead, add --backend deepeval (needs the
proofrag[deepeval] extra; metrics become faithfulness / answer_relevancy / correctness).
To score with Ragas instead, add --backend ragas (needs the proofrag[ragas]
extra; metrics become faithfulness / factual_correctness, plus answer_relevancy
when OpenAI-compatible embeddings are configured). Retrieval metrics and everything
downstream stay the same. DeepEval metric reasons, when available, are preserved
in the scorecard's weakest-case notes.
Report.
proofrag report --results results.json --out scorecard.html
proofrag summary --results results.json # optional markdown for CI/logs
Self-contained HTML — open it, attach it to a PR, screenshot it. Surfaces overall
score, per-metric bars, and the weakest cases with the judge's rationale. The
markdown summary gives CI systems a compact score table without opening the HTML.
CI gate
Absolute floor:
proofrag evaluate --goldenset goldenset.jsonl --predictions predictions.jsonl \
--out results.json --fail-under 0.7 # exits 1 if overall generation score < 0.7
Regression vs a committed baseline (a known-good results.json):
proofrag diff --baseline baseline.json --candidate results.json --tolerance 0.02
To wire this into GitHub Actions, use the bundled composite action
uses: unshDee/proofrag@v0.8.0 (see the repo README / examples/ci/). Tell the user to
commit a baseline results.json from a good run, then diff every PR against it. The
action writes a GitHub Actions job summary and uploads the scorecard/results artifact
by default, including when a gate fails.
Diff rejects mismatched datasets, backends, cutoffs, matchers, and metric schemas.
A/B comparison (blind)
To compare two variants (vector vs GraphRAG, two prompts, two models), run each over
the same golden set to produce two prediction files, then:
proofrag compare --goldenset goldenset.jsonl \
--a vector_preds.jsonl --a-name vector \
--b graphrag_preds.jsonl --b-name graphrag \
--out comparison.json --html comparison.html
The same pinned judge picks the better answer per question, blind — answers are
shown in randomized order so it never knows which variant is which. Output: win
counts + per-variant retrieval metrics + an HTML report. Render later with
proofrag report --results comparison.json (it auto-detects the comparison format).
Credibility rules (state these to the user)
- Judge model is pinned; mixing judges makes scores non-comparable.
- LLM-as-judge has variance — treat single-point differences cautiously; the
retrieval metrics are deterministic and separate retriever from generator faults.
- A low score on
unanswerable cases means the system hallucinates instead of refusing.
citation_quality means attribution to retrieved context, not literal citation syntax
or URL verification.
Reference
- Engine + source: https://github.com/unshDee/proofrag (
src/proofrag/).
- Runnable end-to-end example:
examples/docs-rag/ in that repo (corpus + naive RAG driver).
proofrag --help lists all commands and flags.
1---2name: proofrag3description: Evaluate a RAG or LLM app. Use when the user wants to test, score, benchmark, or catch regressions in a retrieval/RAG/LLM system, generate an evaluation/golden dataset from their docs, measure hallucination/groundedness/correctness, or gate CI on answer quality. Generates a golden set from the user's own corpus, runs LLM-as-judge plus retrieval metrics, and produces a shareable HTML scorecard.4---56# proofrag78Turn "did my change make the RAG better or worse?" into one reproducible command.9You (the agent) wire the user's app to the kit; the kit does dataset generation,10judging, and reporting.1112## When to use13- User changed a prompt, model, chunker, embedder, or retriever and wants to know if quality moved.14- User has docs/a knowledge base but no evaluation set.15- User wants a hallucination/groundedness number, or a CI gate on answer quality.1617## Install the engine18This skill drives the `proofrag` CLI. Make sure it's on PATH (install once), or run19it ad-hoc with `uvx`:20```bash21uv tool install "proofrag[anthropic]" # or: pipx install "proofrag[anthropic]"22# no install needed: uvx "proofrag[anthropic]" demo23```24Use `[openai]` instead of `[anthropic]` for an OpenAI-compatible/local backend.25Credentials: `ANTHROPIC_API_KEY` (default, cheap Haiku judge) or `OPENAI_API_KEY`26(`OPENAI_BASE_URL` for local/Ollama). No key? `proofrag demo` renders a sample scorecard.27If both keys exist, auto-detection chooses Anthropic; use `PROOFRAG_PROVIDER=openai`28to override. Proofrag does not auto-load `.env`, so source it first.2930## The loop311. **Inspect and generate from the user's corpus.**32 ```bash33 proofrag corpus ./docs34 ```35 Use `--include`, `--exclude`, and `--no-gitignore` when the docs tree is noisy.36 PDF loading needs the `proofrag[pdf]` extra; HTML is supported by default.3738 Then generate:39 ```bash40 proofrag generate --corpus ./docs --out goldenset.jsonl --n 2041 ```42 Produces JSONL: `{id, question, gold_answer, gold_contexts[], difficulty, sources[]}`43 with tiers `single_doc` / `multi_doc` / `unanswerable`, plus `context_metadata`44 for each gold context. Commit this file — it is versioned.45462. **Validate the golden set before committing it.**47 ```bash48 proofrag validate --goldenset goldenset.jsonl --corpus ./docs --out validation.json49 ```50 This checks the JSONL contract, duplicate ids/questions, answerable cases without51 gold contexts, unanswerable cases that still cite context, source coverage, and a52 stable fingerprint. It exits non-zero on hard errors; add `--strict` to fail on53 warnings too.5455 Manually search the full corpus for every `unanswerable` candidate, and verify56 both distinct sources are necessary for every `multi_doc` case. Schema validation57 cannot prove either semantic property.58593. **Run the user's RAG over every question to produce predictions.**60 Prefer `proofrag run` when the app exposes a local HTTP endpoint or Python callable:61 ```bash62 proofrag run --goldenset goldenset.jsonl \63 --endpoint http://localhost:8000/ask \64 --out predictions.jsonl6566 proofrag run --goldenset goldenset.jsonl \67 --callable myapp.rag:answer \68 --out predictions.jsonl69 ```70 HTTP mode POSTs `{"id": "...", "question": "..."}`. Callable mode calls71 `answer(question)` by default; add `--call-style record` to pass the full golden72 record. The adapter may return an answer string, `(answer, contexts)`, or:73 ```json74 {"id": "q000", "answer": "<system answer>", "retrieved_contexts": ["<chunk>", "..."]}75 ```76 `retrieved_contexts` are the chunks their retriever returned (used for retrieval77 metrics). If neither adapter fits, write a small driver script that emits the same78 JSONL shape. If you can't find their entrypoint, ask the user where their "ask a79 question" function lives.80814. **Judge.**82 ```bash83 proofrag evaluate --goldenset goldenset.jsonl --predictions predictions.jsonl --out results.json84 ```85 Scores groundedness, correctness, completeness, citation_quality (LLM-as-judge,86 pinned + fingerprinted) and rank-aware retrieval metrics — Recall@k, Precision@k,87 NDCG@k, MRR (`--k` sets the cutoff; Jaccard by default, `--exact` for original88 chunks, or `--semantic` for embeddings). Evaluation fails if prediction IDs do not89 exactly cover the golden set or any judge call fails.90 To score generation with DeepEval instead, add `--backend deepeval` (needs the91 `proofrag[deepeval]` extra; metrics become faithfulness / answer_relevancy / correctness).92 To score with Ragas instead, add `--backend ragas` (needs the `proofrag[ragas]`93 extra; metrics become faithfulness / factual_correctness, plus answer_relevancy94 when OpenAI-compatible embeddings are configured). Retrieval metrics and everything95 downstream stay the same. DeepEval metric reasons, when available, are preserved96 in the scorecard's weakest-case notes.97985. **Report.**99 ```bash100 proofrag report --results results.json --out scorecard.html101 proofrag summary --results results.json # optional markdown for CI/logs102 ```103 Self-contained HTML — open it, attach it to a PR, screenshot it. Surfaces overall104 score, per-metric bars, and the weakest cases with the judge's rationale. The105 markdown summary gives CI systems a compact score table without opening the HTML.106107## CI gate108Absolute floor:109```bash110proofrag evaluate --goldenset goldenset.jsonl --predictions predictions.jsonl \111 --out results.json --fail-under 0.7 # exits 1 if overall generation score < 0.7112```113Regression vs a committed baseline (a known-good results.json):114```bash115proofrag diff --baseline baseline.json --candidate results.json --tolerance 0.02116```117To wire this into GitHub Actions, use the bundled composite action118`uses: unshDee/proofrag@v0.8.0` (see the repo README / `examples/ci/`). Tell the user to119commit a baseline results.json from a good run, then diff every PR against it. The120action writes a GitHub Actions job summary and uploads the scorecard/results artifact121by default, including when a gate fails.122Diff rejects mismatched datasets, backends, cutoffs, matchers, and metric schemas.123124## A/B comparison (blind)125To compare two variants (vector vs GraphRAG, two prompts, two models), run each over126the **same** golden set to produce two prediction files, then:127```bash128proofrag compare --goldenset goldenset.jsonl \129 --a vector_preds.jsonl --a-name vector \130 --b graphrag_preds.jsonl --b-name graphrag \131 --out comparison.json --html comparison.html132```133The same pinned judge picks the better answer per question, **blind** — answers are134shown in randomized order so it never knows which variant is which. Output: win135counts + per-variant retrieval metrics + an HTML report. Render later with136`proofrag report --results comparison.json` (it auto-detects the comparison format).137138## Credibility rules (state these to the user)139- Judge model is pinned; mixing judges makes scores non-comparable.140- LLM-as-judge has variance — treat single-point differences cautiously; the141 retrieval metrics are deterministic and separate retriever from generator faults.142- A low score on `unanswerable` cases means the system hallucinates instead of refusing.143- `citation_quality` means attribution to retrieved context, not literal citation syntax144 or URL verification.145146## Reference147- Engine + source: https://github.com/unshDee/proofrag (`src/proofrag/`).148- Runnable end-to-end example: `examples/docs-rag/` in that repo (corpus + naive RAG driver).149- `proofrag --help` lists all commands and flags.