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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pramana_rag-0.1.0.tar.gz (17.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pramana_rag-0.1.0-py3-none-any.whl (16.0 kB view details)

Uploaded Python 3

File details

Details for the file pramana_rag-0.1.0.tar.gz.

File metadata

  • Download URL: pramana_rag-0.1.0.tar.gz
  • Upload date:
  • Size: 17.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.5

File hashes

Hashes for pramana_rag-0.1.0.tar.gz
Algorithm Hash digest
SHA256 491db4f275052071de83b2e558932440096c9aca3ed320c0617d7a087d02aea8
MD5 74819519ea482720a5f26570ff90cd32
BLAKE2b-256 4726246f3b9ee7179c21cd171920b1808357968c35fcab19c5a2a49d90c636cb

See more details on using hashes here.

File details

Details for the file pramana_rag-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pramana_rag-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.5

File hashes

Hashes for pramana_rag-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2808555c0ed134f65a68ad23aa0d1497cf37628534f67b4ba2072be36b88e865
MD5 85ca9e26f9ac38c698e4752237242114
BLAKE2b-256 0680144731a2207c078b7676c8d906d949b4f899a1acbe74e1ecd0137bdc28f2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page