Skip to main content

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:

  1. Runtime gating, not post-hoc scoring. QualityGateAgent blocks a bad answer before the user sees it.
  2. Composable, not all-or-nothing. Import just QualityGateAgent into an existing LangChain chain -- no rewrite required.
  3. Provider-agnostic by contract. One LLMClient interface, swap Ollama <-> OpenAI with one line. One Retriever interface, 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.

Metadata

Release files for agentic-rag-toolkit 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 agentic-rag-toolkit 0.1.0
File Size Uploaded
agentic_rag_toolkit-0.1.0.tar.gz 16.1 kB Details

Built distribution (wheel)

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

Total release size: 32.9 kB

Release files / agentic_rag_toolkit-0.1.0.tar.gz

Download URL agentic_rag_toolkit-0.1.0.tar.gz
Size 16.1 kB
Tags Source
SHA-256 checksum
How to use checksums
1d87edd8ba4991d36feea1a7d3e9da7390b1308421430cead4698889c5817c28
BLAKE2b-256 checksum
How to use checksums
c3c6fcfb1e5a174e2cb95568939f70c87be163eedb7e1c9339e2717e4d5d4a44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

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

Download URL agentic_rag_toolkit-0.1.0-py3-none-any.whl
Size 16.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3d71a9c6d8aa2b73c9d61ec2acaeb095c6fc089b10a1934ff7722896a9913071
BLAKE2b-256 checksum
How to use checksums
64e8bc9ff99e25c417e42a87d9fbfd34b104853f05282bfd6e6ace0f7ab3dbf6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

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