Raglan
A lightweight, highly configurable RAG retrieval engine. Framework-free, protocol-driven.
Why Raglan?
Standard RAG has one step: vector search → top-K results. Real-world retrieval needs more:
| Problem | Standard RAG | Raglan |
|---|---|---|
| Single query misses aspects | "return policy" finds policy text, misses refund flow | Auto-generates 3 query variants, searches in parallel |
| Dense-only retrieval | Poor short-keyword / jargon matching | BM25 sparse + vector dense, RRF hybrid fusion |
| Small chunks lack context | "refund takes 7 days" without surrounding doc | Child-chunk match → expanded to full parent context |
| Vector similarity != semantic match | "how to return" vs "I don't want this" look close | Cross-Encoder pair-wise reranking filters false positives |
| One failure kills the pipeline | Embedding service timeout → empty results | Each stage degrades independently, rest continues |
Six-Stage Pipeline
User query
│
▼
┌─────────────────────────────────┐
│ Stage 1: QueryExpander │ ← LLM generates entity extraction + 3 variants
└──────────────┬──────────────────┘
│ original + variants = parallel search
▼
┌─────────────────────────────────┐
│ Stage 2: Retrievers (parallel) │
│ · Dense: pgvector / Qdrant / ChromaDB / ...
│ · Sparse: BM25 full-text │
└──────────────┬──────────────────┘
│ multi-source results
▼
┌─────────────────────────────────┐
│ Stage 3: Fusion │ ← RRF / Weighted / RoundRobin
└──────────────┬──────────────────┘
│ fused candidates
▼
┌─────────────────────────────────┐
│ Stage 4: Reranker (optional) │ ← Cross-Encoder / Cohere Rerank
│ Filters scores below threshold │
└──────────────┬──────────────────┘
│ top-N candidates
▼
┌─────────────────────────────────┐
│ Stage 5: ContextBuilder │ ← Parent expansion / window / passthrough
│ Greedy fill up to max_tokens │
└──────────────┬──────────────────┘
│
▼
Final Top-N results
Installation
pip install raglan-retrieval
With optional providers:
pip install raglan-retrieval[openai] # OpenAI embedder + expander
pip install raglan-retrieval[pgvector] # Postgres + pgvector retriever
pip install raglan-retrieval[huggingface] # HF embedder + Cross-Encoder reranker
pip install raglan-retrieval[qdrant] # Qdrant retriever
pip install raglan-retrieval[chromadb] # ChromaDB retriever
pip install raglan-retrieval[cohere] # Cohere reranker
pip install raglan-retrieval[dashscope] # Alibaba DashScope embedder
pip install raglan-retrieval[litellm] # LiteLLM multi-provider expander
pip install raglan-retrieval[all] # Everything
5-Minute Quickstart
from raglan import Raglan
from raglan.retrievers import BM25Retriever
import asyncio
async def main():
# Step 1: Set up a retriever with your data
bm25 = BM25Retriever()
async def chunks():
yield [
("doc1", "Return policy: items can be returned within 30 days.", None),
("doc2", "Refund process: refunds are issued to the original payment method.", None),
("doc3", "Shipping: orders ship within 2 business days.", None),
]
await bm25.index(chunks())
# Step 2: Build the pipeline (all stages optional beyond retrievers)
rag = Raglan([bm25])
# Step 3: Search
results, trace = await rag.search("how to return my order")
for r in results:
print(f"[{r.score:.3f}] {r.content}")
print(f"Pipeline took {trace.total_ms:.1f}ms")
asyncio.run(main())
Incremental configuration
Prefer building up a pipeline piece by piece? Start with an empty
instance and add stages incrementally — no build() needed:
from raglan import Raglan
from raglan.retrievers import BM25Retriever
rag = Raglan() # empty, configurable
rag.add_retriever(bm25) # add a retriever
rag.set_embedder("openai:text-embedding-3-small") # string shorthand
rag.set_expander("openai:gpt-4o-mini") # vendor:model form
rag.set_fusion("rrf")
# every setter returns self — chainable:
# Raglan().add_retriever(bm25).set_fusion("rrf")
results, trace = await rag.search("my query")
Components accept three forms: a live object (BM25Retriever()), a
"vendor:model" string ("openai:text-embedding-3-small", "rrf"), or a
{"type": ..., "params": ...} dict — the same format used by from_dict().
Configuration templates
For team-shared or file-based configuration, grab a template and fill it in:
cfg = Raglan.config() # → {"retrievers": [], "fusion": "rrf", "expander": None, ...}
cfg["retrievers"].append(
{"type": "pgvector", "params": {"connection_string": "...", "table": "kb.chunks"}}
)
cfg["expander"] = "openai:gpt-4o-mini"
rag = Raglan.from_config(cfg)
The template is JSON/YAML-serialisable, so configs can live in a file:
# raglan.yaml
retrievers:
- type: pgvector
params: {connection_string: "postgresql://...", table: kb.chunks}
- type: bm25
fusion: rrf
expander: "openai:gpt-4o-mini"
Adding vector search and reranking
from raglan.context_builders import ParentExpander
from raglan.embedders import OpenAIEmbedder
from raglan.expanders import OpenAIExpander
from raglan.rerankers import CrossEncoderReranker
from raglan.retrievers import ConfigurablePgvectorRetriever
rag = (
Raglan.builder()
.with_expander(OpenAIExpander(model="gpt-4o-mini"))
.with_embedder(OpenAIEmbedder(model="text-embedding-3-small"))
.with_retrievers(
[
ConfigurablePgvectorRetriever(
connection_string="postgresql://...",
table="kb.chunks",
embedding_column="embedding",
parent_id_column="parent_id",
),
BM25Retriever(),
]
)
.with_reranker(
CrossEncoderReranker(
model_name="ms-marco-TinyBERT-L2-v2",
min_score=0.5,
)
)
.with_context_builder(
ParentExpander(
loader=my_parent_loader,
max_tokens=6000,
)
)
.with_fallback_mode("degrade")
.build()
)
results, trace = await rag.search("damaged order return policy")
Design Philosophy
-
Framework-Free — No dependency on LangChain, LlamaIndex, or any specific vector database or LLM provider. Protocols define the interfaces; you provide the implementations.
-
Graceful Degradation — Every stage is independent. Query expansion fails? Use the original query. Cross-Encoder not installed? Skip reranking. BM25 unavailable? Pure vector search still works.
-
Fully Configurable — Every stage's parameters, weights, models, and thresholds are configurable via the Builder or
from_dict(). -
Production-Ready — Extracted from production systems processing thousands of support emails daily. Handles extreme text, multilingual, and high-concurrency scenarios.
Comparison with Existing RAG Frameworks
| Tool | Positioning | vs Raglan |
|---|---|---|
| LangChain RAG | Full-stack LLM framework's RAG module | LC binds to LangChain ecosystem; Raglan is zero-dependency, direct use |
| LlamaIndex | Data→LLM full pipeline | LI has many concepts (Node, Index, QueryEngine); Raglan has one: Search |
| RAGatouille | ColBERT-specific RAG | RAGatouille focuses on ColBERT; Raglan is general retrieval + reranking |
| Cohere Rerank | Commercial API reranking | Cohere charges, sends data to cloud; Raglan runs locally |
| rerankers (answer.ai) | Unified reranking API | rerankers only does reranking; Raglan covers retrieval→fusion→reranking |
Project Structure
raglan/
├── README.md
├── docs/
│ ├── architecture.md # Architecture design
│ ├── configuration.md # Full configuration reference
│ ├── pipeline.md # Six-stage deep dive
│ └── examples.md # Multi-scenario examples
├── raglan/
│ ├── __init__.py
│ ├── raglan.py # Raglan facade + Builder
│ ├── pipeline.py # Pipeline engine + stage dispatch
│ ├── protocols.py # User-implementable abstract interfaces
│ ├── types.py # ScoredChunk, SearchResult, Filter, etc.
│ ├── exceptions.py # Exception hierarchy
│ ├── observability.py # Metrics collector
│ ├── expanders/ # Query expansion (OpenAI, LiteLLM, Identity)
│ ├── embedders/ # Embedding (OpenAI, HuggingFace, DashScope)
│ ├── retrievers/ # Search backends (BM25, pgvector, Qdrant, ChromaDB)
│ ├── fusion/ # Result fusion (RRF, Weighted, RoundRobin)
│ ├── rerankers/ # Reranking (CrossEncoder, Cohere)
│ ├── context_builders/ # Context assembly (Parent, Window, Passthrough)
│ ├── middleware/ # Timeout, Retry, CircuitBreaker, Logging
│ └── resilience/ # RateLimiter, RetryBudget, HealthChecker
└── tests/
License
MIT
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 raglan_retrieval-0.2.1.tar.gz.
File metadata
- Download URL: raglan_retrieval-0.2.1.tar.gz
- Upload date:
- Size: 740.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f2da8cf6a7da1eb911f11306d0cc2e705f1fbac6ec8aa75b862e73f7f869f99
|
|
| MD5 |
eefc246deced073f1be028f84792ba8a
|
|
| BLAKE2b-256 |
89a728c568bdf4ee5d470a9e26256d60ca7ce3bfab0314cf5bdd959fe5c1098d
|
File details
Details for the file raglan_retrieval-0.2.1-py3-none-any.whl.
File metadata
- Download URL: raglan_retrieval-0.2.1-py3-none-any.whl
- Upload date:
- Size: 77.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94374ec6fc22c3f954287dc57ca4ec0ce7401e1fe0abacf1fbafe5fece3368fc
|
|
| MD5 |
3c0d488fb6f7bb52ed2bd310f1e41497
|
|
| BLAKE2b-256 |
7188eedf10c9a7f2d51732e480a46fee681b72821c67cd7a5e3556277430eb45
|