Open Source RAG Tools & Frameworks
To deploy a production Retrieval-Augmented Generation (RAG) system, developers must select tools across three major layers: AI Orchestration Frameworks, Vector Databases, and Embedding/Reranking Models. This page evaluates the trade-offs of these options and defines a recommended production architecture stack.
๐ 1. AI Orchestration Frameworks
AI orchestration frameworks handle document loading, chunking pipelines, vector store connections, and prompt assembly configurations.
| Framework | Primary Control Flow | Custom Pipeline Definition | Agent Integration | Production Suitability |
|---|---|---|---|---|
| LlamaIndex | Data-Centric: Focuses on indexes, query engines, and graph-structured document storage. | Workflows: Event-driven pipeline states with type checking. | LlamaAgents: Dedicated service-oriented multi-agent orchestration. | High: Outstanding for structured layout parsing and search optimization. |
| LangChain | Chain-Centric: Focuses on connecting components and prompt sequencing. | LCEL (LangChain Expression Language): Declarative pipe-based chain execution. | LangGraph: Stateful multi-agent graph orchestration. | High: Extremely flexible ecosystem; ideal for complex cyclic workflows. |
| Haystack | Component-Centric: Modular nodes connected in a Directed Acyclic Graph (DAG). | Pipelines: Expressive node-based inputs/outputs validation. | Custom Agents: Component loops that integrate directly into DAG pipelines. | High: Excellent performance and modularity for standard search pipelines. |
๐๏ธ 2. Vector Databases
Vector databases index document embeddings and execute rapid similarity queries (Cosine, Euclidean, or Inner Product distances).
| Database Type | Key Candidates | Indexing and Search Latency | Filtering Capabilities | Deployment & Ops Complexity |
|---|---|---|---|---|
| Relational Extension | pgvector (PostgreSQL) | Low (using HNSW/IVFFlat indexes). | Extremely High: Fuses vector matching with standard relational SQL query structures. | Low: Reuses existing PostgreSQL database instances and access rules. |
| Native Vector DB | Qdrant, Weaviate | Extremely Low: Optimized custom engines for vector lookups. | High: Custom metadata payload indexes and filtering interfaces. | Medium: Requires hosting and monitoring separate dedicated services. |
| Managed SaaS | Pinecone | Low: Serverless engine offloads operational scaling. | Moderate-High: Payload metadata filters; bound to hosted index keys. | Minimal: API-driven SaaS setup; zero cluster management. |
๐ฌ 3. Embeddings & Reranking Models
Choosing embedding and reranking models requires balancing dimensionality, cost, and retrieval precision.
Embedding Models
- OpenAI
text-embedding-3-large: Matryoshka-trained model. Supports dim-slicing (e.g. 1536 down to 512 dimensions) while preserving high recall. - Cohere Embed v3: Trained specifically to match search intent; features native binary/int8 compression to reduce vector database RAM usage.
- Local HuggingFace Models (
BGE,MiniLM): Highly performant options for air-gapped or zero-egress enterprise deployments.
Reranker Models
- Cohere Rerank v3: Industry-standard cross-encoder API for scoring query-passage relevance.
- BGE-Reranker-Large: State-of-the-art open-source cross-encoder model for self-hosted reranking servers.
๐ ๏ธ 4. Hybrid Search & Reranking Implementation
Below is a self-contained Python implementation of a Hybrid Search Pipeline. It combines BM25 keyword matching with dense vector cosine similarity, merges results using Reciprocal Rank Fusion (RRF), and executes final semantic reranking.
import numpy as np
from typing import List, Dict, Tuple
class SimpleBM25:
"""Lightweight BM25 keyword search retriever."""
def __init__(self, corpus: List[str]):
self.corpus = corpus
self.doc_len = [len(doc.split()) for doc in corpus]
self.avg_doc_len = sum(self.doc_len) / (len(corpus) + 1e-5)
self.doc_freqs = []
self.idf = {}
# Calculate term frequencies and document frequencies
df = {}
for doc in self.corpus:
freqs = {}
for word in doc.lower().split():
freqs[word] = freqs.get(word, 0) + 1
self.doc_freqs.append(freqs)
for word in set(freqs.keys()):
df[word] = df.get(word, 0) + 1
# Calculate inverse document frequency (IDF)
for word, count in df.items():
self.idf[word] = np.log((len(corpus) - count + 0.5) / (count + 0.5) + 1.0)
def retrieve(self, query: str, k1: float = 1.5, b: float = 0.75) -> List[Tuple[float, int]]:
query_words = query.lower().split()
scores = []
for idx, freqs in enumerate(self.doc_freqs):
score = 0.0
doc_len = self.doc_len[idx]
for word in query_words:
if word in freqs:
tf = freqs[word]
num = self.idf.get(word, 0.0) * tf * (k1 + 1.0)
den = tf + k1 * (1.0 - b + b * (doc_len / self.avg_doc_len))
score += num / den
scores.append((score, idx))
return sorted(scores, key=lambda x: x[0], reverse=True)
# Mocked vector models to enable self-contained compilation
class MockEmbeddingModel:
def encode(self, texts: List[str]) -> np.ndarray:
rng = np.random.default_rng(seed=42)
vectors = rng.random((len(texts), 384))
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
return vectors / norms
class MockCrossEncoder:
def predict(self, pairs: List[Tuple[str, str]]) -> np.ndarray:
scores = []
for query, doc in pairs:
overlap = len(set(query.lower().split()) & set(doc.lower().split()))
score = min(1.0, overlap / (len(query.split()) + 1e-5) + 0.1)
scores.append(score)
return np.array(scores)
class HybridRerankPipeline:
def __init__(self, corpus: List[str]):
self.corpus = corpus
self.bm25 = SimpleBM25(corpus)
self.embedder = MockEmbeddingModel()
self.reranker = MockCrossEncoder()
self.doc_embeddings = self.embedder.encode(corpus)
def dense_search(self, query: str) -> List[Tuple[float, int]]:
query_vector = self.embedder.encode([query])[0]
similarities = np.dot(self.doc_embeddings, query_vector)
scores = [(float(sim), idx) for idx, sim in enumerate(similarities)]
return sorted(scores, key=lambda x: x[0], reverse=True)
def reciprocal_rank_fusion(
self,
sparse_res: List[Tuple[float, int]],
dense_res: List[Tuple[float, int]],
k: int = 60
) -> List[Tuple[float, int]]:
rrf_scores = {}
for rank, (_, idx) in enumerate(sparse_res):
rrf_scores[idx] = rrf_scores.get(idx, 0.0) + (1.0 / (k + rank + 1))
for rank, (_, idx) in enumerate(dense_res):
rrf_scores[idx] = rrf_scores.get(idx, 0.0) + (1.0 / (k + rank + 1))
sorted_rrf = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
return [(score, idx) for idx, score in sorted_rrf]
def query(self, query_text: str, top_k: int = 5, top_n: int = 2) -> List[Tuple[str, float]]:
# 1. Execute dual searches
sparse_res = self.bm25.retrieve(query_text)[:top_k]
dense_res = self.dense_search(query_text)[:top_k]
# 2. Fuse candidate ranks via RRF
fused_candidates = self.reciprocal_rank_fusion(sparse_res, dense_res, k=60)[:top_k]
# 3. Assemble pairs for Cross-Encoder re-scoring
candidates = [self.corpus[idx] for _, idx in fused_candidates]
pairs = [(query_text, doc) for doc in candidates]
rerank_scores = self.reranker.predict(pairs)
# 4. Sort and select top N items
final_results = [(candidates[i], float(rerank_scores[i])) for i in range(len(candidates))]
return sorted(final_results, key=lambda x: x[1], reverse=True)[:top_n]๐ 5. Recommended Production Stack (2026)
For enterprise-grade applications, the following default architectural stack balances developer velocity, search accuracy, and runtime cost.
| Layer | Recommended Choice | Rationale |
|---|---|---|
| Parsing | LlamaParse | Retains structural headers, document hierarchy, and multi-column tables via layout-aware models. |
| Embeddings | OpenAI text-embedding-3-large | Matryoshka-trained; sliced down to 512 dimensions for minimal vector DB memory footprint with near-zero loss in recall. |
| Vector Database | pgvector on PostgreSQL | Simplifies access control and avoids service fragmentation by hosting vector indices next to relational transactional records. |
| Retrieval Strategy | Hybrid Search (Vector + BM25) + RRF | Fuses keyword precision (for entity matching/product IDs) with dense vector semantic matching. |
| Reranker | Cohere Rerank v3 | Delivers high Mean Reciprocal Rank (MRR) optimization; cuts LLM costs by pruning low-score chunks. |
| Evaluation | RAGAS | Provides robust offline pipeline evaluation metrics (Faithfulness, Recall, Precision). |
| Observability | OpenTelemetry + Arize Phoenix | Tracks multi-stage trace spans conforming to standard GenAI OTel semantic conventions. |
๐ Related Sections
- Anatomy of RAG Systems โ Deep component layers, Matryoshka learning, and pgvector schemas.
- RAG Design Patterns โ CRAG, Self-RAG, Adaptive routing, and Parent-Child retrieval.
- Agentic Document Workflows (ADW) โ Production layout parsing pipelines, unstructured files processing, and worker queues.