Skip to main content

ragsanity

A sanity check for RAG (Retrieval-Augmented Generation) pipelines. One function call, no setup.

pip install ragsanity

from ragsanity import evaluate

result = evaluate( question="What is the refund policy?", contexts=["You can return items within 30 days of purchase for a full refund."], answer="The refund policy allows returns within 30 days of purchase.", )

print(result.summary())

ragsanity result

Question: What is the refund policy? Faithfulness: 0.62 Relevancy: 1.00 Completeness: 1.00 Overall: 0.88

That's the whole setup. No API key, no config file, no model download, no signup. Two lines to install and import, one function call to get a score.

What it catches

ragsanity scores three failure modes independently, because they need different fixes.

  • Faithfulness. Is the answer grounded in the retrieved context, or did the model add things that aren't there?
  • Relevancy. Does the answer actually address the question, or is it correct but off topic?
  • Completeness. For multi-part questions, did the answer cover every part, or did it quietly skip half of it?

from ragsanity import evaluate

result = evaluate( question="What is the refund window and who pays for return shipping?", contexts=["Refunds are given within 30 days. The customer pays return shipping."], answer="The refund window is 30 days.", ) print(result.summary())

ragsanity result

Question: What is the refund window and who pays for return shipping? Faithfulness: 0.50 Relevancy: 0.40 Completeness: 0.50 Overall: 0.47

result.completeness_details["parts"] tells you exactly which part got skipped. In this example it's "who pays for return shipping?", instead of just a low number with no explanation.

Here's faithfulness and relevancy catching a fully hallucinated, off-topic answer, back to back.

from ragsanity import evaluate

context = ["Our return policy allows customers to return items within 30 " "days of purchase for a full refund. Items must be unused and " "in original packaging."]

A grounded, on-topic answer

good = evaluate( question="What is the refund policy?", contexts=context, answer="The refund policy allows returns within 30 days of purchase.", ) print(good.faithfulness, good.relevancy) # 0.875 1.0

A hallucinated, off-topic answer

bad = evaluate( question="What is the refund policy?", contexts=context, answer="Our headquarters are located in San Francisco.", ) print(bad.faithfulness, bad.relevancy) # 0.0 0.0

No fixture, no mock, no API key required to reproduce that. Copy it into a .py file and run it right now.

Why zero dependencies matters here

Most RAG-eval tooling needs an LLM API key for judge-based scoring, or pulls in ML libraries (embedding models, BERT-based scorers) for semantic comparison. Both are the right call when you need that level of nuance, but they mean a real setup cost before you get your first number: API keys, .env files, multi-GB model downloads, dependency conflicts in an existing project.

ragsanity is deliberately the opposite tradeoff. It uses lexical overlap, checking how much of an answer's meaningful vocabulary is grounded in the context, and how much of a question's vocabulary the answer addresses. Pure Python, standard library only. That makes it:

  • Instant to install. No extras, no optional dependency tree.
  • Instant to run. No network calls, no model to warm up, no cost.
  • Safe to drop into CI. Nothing to leak, nothing to rate-limit.
  • Honest about its limits. See below.

The tradeoff is real and worth naming: this is a proxy, not a semantic judge. A well-paraphrased answer that shares little vocabulary with the source can score lower than it "should." Use ragsanity as a fast first pass, the thing you run on every commit or every response before reaching for something heavier, not as your only evaluation layer.

API

evaluate(question, contexts, answer, *, metrics=None)

  • question: str, the user's question.
  • contexts: list[str], the chunks your retriever returned.
  • answer: str, the LLM's generated answer.
  • metrics: optional list[str], subset of ["faithfulness", "relevancy", "completeness"] to run. Defaults to all three.

Returns an EvalResult.

EvalResult

Attribute Type Description
faithfulness float | None 0-1 groundedness score
relevancy float | None 0-1 question-alignment score
completeness float | None 0-1 multi-part question coverage score
overall float | None average of computed scores (property)
faithfulness_details dict per-sentence breakdown, for debugging
relevancy_details dict matched/unmatched keywords, for debugging
completeness_details dict per-part coverage breakdown, for debugging
.summary() str pretty-printed report
.to_dict() dict JSON-serializable version of the result

CLI

No Python needed for one-off checks.

ragsanity run examples.json ragsanity run examples.json --json # machine-readable output

examples.json can be a single object or a list of objects:

{ "question": "What is the refund policy?", "contexts": ["You can return items within 30 days of purchase."], "answer": "The refund policy allows returns within 30 days." }

Contributing

Issues and PRs welcome.

pip install -e ".[dev]" pytest

License

MIT

Metadata

Release files for ragsanity 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ragsanity 0.3.0
File Size Uploaded Provenance Actions
ragsanity-0.3.0.tar.gz 14.6 kB Without provenance Details for ragsanity-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ragsanity 0.3.0
File Interpreter ABI Platform Actions
ragsanity-0.3.0-py3-none-any.whl Python 3 none any Details for ragsanity-0.3.0-py3-none-any.whl

Total release size: 28.0 kB

Release files / ragsanity-0.3.0.tar.gz

Download URL ragsanity-0.3.0.tar.gz
Size 14.6 kB
Tags Source
SHA-256 checksum
How to use checksums
42341d827d54b94bc95096933c71f5a0b8b98fa201b3c387e237c07e333cf1a1
BLAKE2b-256 checksum
How to use checksums
805cb9db64c97e488f3e5989c694da0db148015ea7a8521718f0b6f0c45cfe1b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / ragsanity-0.3.0-py3-none-any.whl

Download URL ragsanity-0.3.0-py3-none-any.whl
Size 13.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
13160eee3e38a44c58bebf49cb140ca556c42945400cc5749e804aa7f0bdce05
BLAKE2b-256 checksum
How to use checksums
ba66f1f37da50b5aa2c7bf339b336fc332806a3fe783cd02c1754db1a78ac7e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for ragsanity 0.3.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page