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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
491db4f275052071de83b2e558932440096c9aca3ed320c0617d7a087d02aea8
|
|
| MD5 |
74819519ea482720a5f26570ff90cd32
|
|
| BLAKE2b-256 |
4726246f3b9ee7179c21cd171920b1808357968c35fcab19c5a2a49d90c636cb
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2808555c0ed134f65a68ad23aa0d1497cf37628534f67b4ba2072be36b88e865
|
|
| MD5 |
85ca9e26f9ac38c698e4752237242114
|
|
| BLAKE2b-256 |
0680144731a2207c078b7676c8d906d949b4f899a1acbe74e1ecd0137bdc28f2
|