agentic-rag-toolkit
Drop-in agent primitives for pipelines you already have -- not another framework.
agentic-rag-toolkit is a small Python library of six reusable agent building blocks distilled from production agentic-RAG work: a clinical/behavioral-health nudge engine and an LLM-powered lead-generation pipeline. Each block does one job -- classify, select a retriever, retrieve, quality-check, self-validate, or personalize tone -- and any of them can be dropped into an existing LangChain/LlamaIndex/raw-Python pipeline without adopting a whole new framework. Works with any LLM backend (Ollama, OpenAI/GPT-4o) and any vector store (Chroma, Pinecone, or your own) through one shared interface.
Why this exists
| Existing tool | What it actually does | Why it's not the same |
|---|---|---|
raki, rag-evaluation |
Score RAG quality after a session is over (post-hoc analysis) | Never runs inside the live pipeline -- can't stop a bad answer before the user sees it |
create-agentic-rag |
A project scaffold/boilerplate generator | One-time template, not an importable library you keep using |
flashrag-dev, mini-rag |
Full end-to-end batteries-included RAG research pipelines | You adopt their entire chunk-embed-retrieve-generate flow -- can't cherry-pick one piece |
| LangGraph / LlamaIndex ADW | Quality gates and self-correction exist as internal concepts | Only usable if you buy into their full state-machine/orchestration model |
pydantic-ai-rag |
RAG layer bolted onto one specific agent framework | Locked to pydantic-ai, not portable |
Three things to lead with:
- Runtime gating, not post-hoc scoring.
QualityGateAgentblocks a bad answer before the user sees it. - Composable, not all-or-nothing. Import just
QualityGateAgentinto an existing LangChain chain -- no rewrite required. - Provider-agnostic by contract. One
LLMClientinterface, swap Ollama <-> OpenAI with one line. OneRetrieverinterface, swap Chroma <-> Pinecone with one line.
Install
pip install agentic-rag-toolkit # core only
pip install "agentic-rag-toolkit[ollama]" # + local Ollama support
pip install "agentic-rag-toolkit[openai]" # + OpenAI/GPT-4o support
pip install "agentic-rag-toolkit[chromadb]" # + Chroma example support
Quickstart
from agentic_rag_toolkit import MetricClassifierAgent, QualityGateAgent
class MyLLM:
def complete(self, prompt: str, **kwargs) -> str:
... # call your model here, return raw text
llm = MyLLM()
classifier = MetricClassifierAgent(llm=llm, categories=["urgent", "routine"])
result = classifier.run("heart rate spiked to 180")
print(result.label, result.confidence)
gate = QualityGateAgent(llm=llm, min_evidence_count=1)
gate_result = gate.run(evidence=["some retrieved doc"], query="what happened?")
if not gate_result.passed:
print("blocked:", gate_result.reason)
See examples/basic_pipeline.py for a full runnable demo (no API keys needed) chaining all six agents, and examples/ollama_chroma_pipeline.py for a production-shaped version with Ollama + Chroma.
API Reference
All LLM-facing agents ask for structured JSON output (validated with Pydantic, with automatic retry-with-error-feedback on a bad response) instead of parsing ad-hoc text formats like "label|0.92" -- see complete_json below. Every agent exposes a single .run(...) entrypoint.
LLMClient (Protocol) -- agentic_rag_toolkit.core.llm_client
Any object with a matching .complete() method satisfies this automatically (structural typing via @runtime_checkable -- no subclassing required).
class LLMClient(Protocol):
def complete(self, prompt: str, **kwargs) -> str: ...
Two ready-made implementations ship with the toolkit:
| Class | Constructor args | Requires |
|---|---|---|
OllamaClient |
model: str = "llama3" |
pip install ollama + Ollama daemon running locally |
OpenAIClient |
model: str = "gpt-4o" |
pip install openai + OPENAI_API_KEY env var set |
from agentic_rag_toolkit import OllamaClient, OpenAIClient
llm = OllamaClient(model="llama3.1") # or:
llm = OpenAIClient(model="gpt-4o")
llm.complete("Say hello in one word.") # -> "Hello"
To use your own backend (Anthropic, a local vLLM server, etc.), just implement .complete(prompt: str, **kwargs) -> str on any class -- every agent below accepts it.
complete_json() -- agentic_rag_toolkit.core.structured
The shared helper every LLM-facing agent uses internally. Public and importable if you want the same schema-validated-JSON-with-retry behavior in your own custom agents.
def complete_json(
llm: LLMClient,
prompt: str,
schema: type[BaseModel],
max_retries: int = 2,
) -> BaseModel
| Argument | Type | Description |
|---|---|---|
llm |
LLMClient |
Any object with .complete() |
prompt |
str |
Your instruction text (the JSON schema is appended automatically) |
schema |
type[BaseModel] |
A Pydantic model class describing the expected response shape |
max_retries |
int, default 2 |
Extra attempts if the response fails to parse/validate. Total attempts = max_retries + 1 |
Returns: a validated instance of schema.
Raises: ValueError if every attempt fails (message includes the last raw response and validation error).
from pydantic import BaseModel
from agentic_rag_toolkit import complete_json
class Sentiment(BaseModel):
positive: bool
reason: str
result = complete_json(llm, "Is this review positive? 'Loved it!'", Sentiment)
print(result.positive, result.reason)
MetricClassifierAgent -- agentic_rag_toolkit.classifiers.metric_classifier
Labels incoming text into one of a fixed set of categories -- the first decision point in a pipeline.
Constructor
MetricClassifierAgent(llm: LLMClient, categories: list[str], max_retries: int = 2)
| Argument | Type | Default | Notes |
|---|---|---|---|
llm |
LLMClient |
required | |
categories |
list[str] |
required | Must be non-empty -- raises ValueError otherwise |
max_retries |
int |
2 |
Passed through to complete_json |
Method
.run(input_text: str) -> ClassificationResult
ClassificationResult is {label: str, confidence: float} (confidence constrained to 0.0-1.0).
Raises ValueError if input_text is empty/blank, if the LLM returns a label outside categories, or if the response can't be parsed after retries.
from agentic_rag_toolkit import MetricClassifierAgent
classifier = MetricClassifierAgent(llm=llm, categories=["urgent", "routine"])
result = classifier.run("heart rate spiked to 180")
# result.label == "urgent", result.confidence == 0.91
RetrieverSelectorAgent + Retriever -- agentic_rag_toolkit.retrieval.retriever_selector
Routes a classified category to the correct retriever. Pure lookup -- does not call an LLM, so it takes no llm argument (unlike every other agent here).
Retriever is a Protocol: any object with .retrieve(query: str, top_k: int = 5) -> list[str] qualifies -- wrap your Chroma collection, Pinecone index, or anything else this way.
Constructor
RetrieverSelectorAgent(retriever_map: dict[str, Retriever])
retriever_map must be non-empty -- raises ValueError otherwise.
Method
.run(category: str) -> Retriever
Raises KeyError (with the list of known categories in the message) if category isn't in retriever_map.
from agentic_rag_toolkit import RetrieverSelectorAgent
selector = RetrieverSelectorAgent(retriever_map={
"urgent": urgent_chroma_wrapper,
"routine": routine_chroma_wrapper,
})
retriever = selector.run("urgent")
docs = retriever.retrieve("blood sugar spike guidance")
SubQuestionRetrieverAgent -- agentic_rag_toolkit.retrieval.sub_question_retriever
Breaks one complex question into smaller sub-questions and retrieves evidence for each in parallel, rather than one broad fuzzy search.
Constructor
SubQuestionRetrieverAgent(
llm: LLMClient,
retriever: Retriever,
max_sub_questions: int = 4,
max_retries: int = 2,
)
| Argument | Type | Default | Notes |
|---|---|---|---|
llm |
LLMClient |
required | Used only to decompose the question |
retriever |
Retriever |
required | Any object with .retrieve() |
max_sub_questions |
int |
4 |
Caps how many sub-questions are generated (and therefore worker threads spawned) |
max_retries |
int |
2 |
Retries for the decomposition call |
Method
.run(question: str) -> dict[str, list[str]]
Returns a dict mapping each generated sub-question to its retrieved evidence list. Raises ValueError if question is empty. If decomposition fails validation even after retries, it silently falls back to treating the original question as the only sub-question -- decomposition is a nice-to-have, not a hard requirement.
from agentic_rag_toolkit import SubQuestionRetrieverAgent
agent = SubQuestionRetrieverAgent(llm=llm, retriever=my_retriever, max_sub_questions=3)
results = agent.run("What should happen when a patient's blood sugar spikes?")
# {"What is a safe blood sugar range?": [...], "What should someone do right now?": [...]}
QualityGateAgent + GateResult -- agentic_rag_toolkit.quality.quality_gate
The core differentiator of this toolkit. Checks retrieved evidence is sufficient before generation happens, so the pipeline can stop instead of letting the LLM hallucinate.
Constructor
QualityGateAgent(llm: LLMClient, min_evidence_count: int = 1, max_retries: int = 2)
| Argument | Type | Default | Notes |
|---|---|---|---|
llm |
LLMClient |
required | |
min_evidence_count |
int |
1 |
If fewer evidence items are passed in, the gate fails immediately -- no LLM call needed |
max_retries |
int |
2 |
Method
.run(evidence: list[str], query: str) -> GateResult
GateResult is {passed: bool, reason: str}. Raises ValueError if the LLM's sufficiency judgment can't be parsed after retries.
from agentic_rag_toolkit import QualityGateAgent
gate = QualityGateAgent(llm=llm, min_evidence_count=2)
result = gate.run(evidence=["doc A", "doc B"], query="what is a safe blood sugar range?")
if not result.passed:
print("blocked:", result.reason)
SelfValidatorAgent + ValidationResult -- agentic_rag_toolkit.quality.self_validator
Final compliance/safety check on a generated answer -- runs after your own generation step, against rules you define.
Constructor
SelfValidatorAgent(llm: LLMClient, rules: list[str], max_retries: int = 2)
rules must be non-empty -- raises ValueError otherwise.
Method
.run(generated_answer: str) -> ValidationResult
ValidationResult is {valid: bool, violated_rules: list[str]}. Raises ValueError if generated_answer is empty.
from agentic_rag_toolkit import SelfValidatorAgent
validator = SelfValidatorAgent(llm=llm, rules=[
"never state a specific medication dosage",
"always suggest consulting a doctor if severe",
])
result = validator.run("Take 500mg twice a day.")
# result.valid == False, "never state a specific medication dosage" in result.violated_rules
TonePersonalizerAgent -- agentic_rag_toolkit.personalization.tone_personalizer
Rewrites an already-approved answer into a target voice, changing delivery only -- never facts. Meant to run last, after QualityGateAgent and SelfValidatorAgent have both passed, so a nicer tone can never mask a bad or non-compliant answer. Returns plain text, not structured JSON -- there's no schema for "does this sound gentle enough."
Constructor
TonePersonalizerAgent(llm: LLMClient, tone: str)
tone must be non-empty -- raises ValueError otherwise.
Method
.run(approved_answer: str) -> str
Raises ValueError if approved_answer is empty.
from agentic_rag_toolkit import TonePersonalizerAgent
personalizer = TonePersonalizerAgent(llm=llm, tone="gentle and encouraging, like a supportive friend")
message = personalizer.run("Your blood sugar reading is high.")
Full end-to-end wiring of all six agents: examples/basic_pipeline.py (no API keys needed) and examples/ollama_chroma_pipeline.py (production-shaped, Ollama + Chroma).
Development
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest --cov=agentic_rag_toolkit tests/ -v
Release process for maintainers lives in PUBLISHING.md, not here -- this README is for people using the package, not publishing it.
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 agentic_rag_toolkit-0.1.0.tar.gz.
File metadata
- Download URL: agentic_rag_toolkit-0.1.0.tar.gz
- Upload date:
- Size: 16.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d87edd8ba4991d36feea1a7d3e9da7390b1308421430cead4698889c5817c28
|
|
| MD5 |
5d1c87c54d55dbfba0d3713895b79b56
|
|
| BLAKE2b-256 |
c3c6fcfb1e5a174e2cb95568939f70c87be163eedb7e1c9339e2717e4d5d4a44
|
Provenance
The following attestation bundles were made for agentic_rag_toolkit-0.1.0.tar.gz:
Publisher:
publish.yml on Mohanapriya-sk/agentic-rag-toolkit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentic_rag_toolkit-0.1.0.tar.gz -
Subject digest:
1d87edd8ba4991d36feea1a7d3e9da7390b1308421430cead4698889c5817c28 - Sigstore transparency entry: 2225538448
- Sigstore integration time:
-
Permalink:
Mohanapriya-sk/agentic-rag-toolkit@27d68f38398f854fa49fffabdd89893046961340 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Mohanapriya-sk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@27d68f38398f854fa49fffabdd89893046961340 -
Trigger Event:
push
-
Statement type:
File details
Details for the file agentic_rag_toolkit-0.1.0-py3-none-any.whl.
File metadata
- Download URL: agentic_rag_toolkit-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d71a9c6d8aa2b73c9d61ec2acaeb095c6fc089b10a1934ff7722896a9913071
|
|
| MD5 |
d5ee6ba9da94db3871c90fe2ac4e7e65
|
|
| BLAKE2b-256 |
64e8bc9ff99e25c417e42a87d9fbfd34b104853f05282bfd6e6ace0f7ab3dbf6
|
Provenance
The following attestation bundles were made for agentic_rag_toolkit-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on Mohanapriya-sk/agentic-rag-toolkit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentic_rag_toolkit-0.1.0-py3-none-any.whl -
Subject digest:
3d71a9c6d8aa2b73c9d61ec2acaeb095c6fc089b10a1934ff7722896a9913071 - Sigstore transparency entry: 2225538672
- Sigstore integration time:
-
Permalink:
Mohanapriya-sk/agentic-rag-toolkit@27d68f38398f854fa49fffabdd89893046961340 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Mohanapriya-sk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@27d68f38398f854fa49fffabdd89893046961340 -
Trigger Event:
push
-
Statement type: