Skip to main content

pramana-rag

A grounding-first RAG library: threshold-gated retrieval, concern-based chunking, a locked grounding prompt, and built-in faithfulness evaluation as the default path — not pieces you have to assemble yourself.

pip install pramana-rag[openai,chroma,eval]

What "Pramana" means

Pramana (प्रमाण) is a Sanskrit term from the Nyaya school of classical Indian epistemology — a valid source of knowledge. Not belief. Not assumption. Not the most plausible guess. A source that has been retrieved, verified, and bounded.

Applied to a RAG system: the retrieval step is the system's Pramana — its valid source, the boundary it is not permitted to answer beyond. This library's architecture (embed → chunk by concern → store → retrieve above a calibrated threshold → generate only from what was retrieved → measure faithfulness) is the Pramana Framework taught across RAG Essentials Book 1 (zUdyog / ShopBot), credited here as its origin. See examples/shopbot/ for the book's own example ported onto this library.

Honest positioning

Threshold-gated "retrieve or refuse" is not a new idea — it has real prior art (e.g. ConfRAG, Microsoft's Confidence-Aware RAG). NVIDIA's NeMo Guardrails already ships a production fact-checking rail for RAG groundedness. RAGAS is already integrated into LangChain, LlamaIndex, Langfuse, and Braintrust. This library does not claim to be first at any of that.

What it does differently is packaging. Today, getting this full discipline — refuse when evidence is weak, chunk by concern instead of character count, lock the prompt to retrieved context only, measure faithfulness — means assembling 2-3 separate, heavier systems: an orchestration framework, a guardrails middleware with its own DSL, and a separate eval tool. pramana-rag is a single lightweight library where that discipline is the default call path, not an optional rail you wire in later.

Core abstractions

Concern Type Default implementation
Chunking Chunker ConcernChunker — one chunk per caller-defined concern
Embedding Embedder OpenAIEmbedder (text-embedding-3-small)
Vector storage VectorStore InMemoryStore (zero deps) / ChromaStore
Retrieval Retriever threshold-gated: returns evidence or None, never "closest anyway"
Threshold selection calibrate_threshold() finds the score that separates labeled should-retrieve / should-not-retrieve queries
Generation LLM OpenAIChat (temperature 0 by default)
Grounding GroundingPrompt + GroundedAnswerer locks the model to retrieved context, refuses explicitly otherwise
Evaluation evaluate() RAGAS Faithfulness / Context Precision (+ Recall / Relevancy with ground truth)

Every abstraction is domain-agnostic: concern is a string you define ("care", "clause", "dosage", "eligibility", ...), not a fixed enum. src/pramana/ contains no e-commerce vocabulary anywhere — that only appears in examples/.

Quickstart

from pramana import (
    ConcernChunker, ConcernSpec, InMemoryStore, OpenAIEmbedder,
    Retriever, calibrate_threshold, GroundingPrompt, GroundedAnswerer, OpenAIChat,
)

# 1. Define the concerns your domain actually gets asked about.
concerns = [
    ConcernSpec(name="definition", build_text=lambda d: d.get("definition")),
    ConcernSpec(name="eligibility", build_text=lambda d: d.get("eligibility")),
]
chunker = ConcernChunker(concerns)

# 2. Chunk your documents, embed them, store them.
documents = [{"id": "policy-1", "definition": "...", "eligibility": "..."}]
embedder = OpenAIEmbedder()
store = InMemoryStore()  # or ChromaStore() for persistence
for doc in documents:
    chunks = chunker.chunk(doc)
    store.add(chunks, embedder.embed_documents([c.text for c in chunks]))

# 3. Build a retriever and calibrate its threshold against labeled examples.
retriever = Retriever(embedder=embedder, store=store)
retriever.threshold = calibrate_threshold(retriever, [
    ("who is eligible for this policy?", True),
    ("what's the weather today?", False),
])

# 4. Wrap it in a locked grounding prompt.
answerer = GroundedAnswerer(
    retriever=retriever,
    llm=OpenAIChat(),
    prompt=GroundingPrompt(persona="You are a policy assistant."),
)

print(answerer.answer("who is eligible for this policy?"))

Architecture

document ──▶ ConcernChunker ──▶ Chunk(s) ──▶ Embedder ──▶ VectorStore
                                                                │
query ──▶ Embedder ──▶ Retriever (threshold-gated) ────────────┘
                             │
                   evidence ▼ or None
                    ┌────────┴────────┐
                    ▼                 ▼
          GroundingPrompt      refusal_message
          + LLM.complete
                    │
              grounded answer

Evaluation

from pramana import evaluate

result = evaluate(answerer, test_cases)  # list[TestCase]
print(result.faithfulness, result.context_precision)

Requires pip install pramana-rag[eval].

Development

pip install -e ".[dev]"
pytest -q

The core test suite runs fully offline (fake embedder/LLM, in-memory store) — no API key required.

License

MIT — see LICENSE.

Release files for pramana-rag 0.1.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 pramana-rag 0.1.0
File Size Uploaded
pramana_rag-0.1.0.tar.gz 17.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pramana-rag 0.1.0
File Interpreter ABI Platform
pramana_rag-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 33.7 kB

Release files / pramana_rag-0.1.0.tar.gz

Download URL pramana_rag-0.1.0.tar.gz
Size 17.6 kB
Tags Source
SHA-256 checksum
How to use checksums
491db4f275052071de83b2e558932440096c9aca3ed320c0617d7a087d02aea8
BLAKE2b-256 checksum
How to use checksums
4726246f3b9ee7179c21cd171920b1808357968c35fcab19c5a2a49d90c636cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.5

Release files / pramana_rag-0.1.0-py3-none-any.whl

Download URL pramana_rag-0.1.0-py3-none-any.whl
Size 16.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2808555c0ed134f65a68ad23aa0d1497cf37628534f67b4ba2072be36b88e865
BLAKE2b-256 checksum
How to use checksums
0680144731a2207c078b7676c8d906d949b4f899a1acbe74e1ecd0137bdc28f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.5

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

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