RAG Studio
Purple8 RAG Studio is a configuration, tuning, and evaluation surface for the built-in RAG pipeline. Every parameter — chunking strategy, embedding model, retrieval behaviour, generation settings, and quality metrics — is configurable per tenant via REST API and Python SDK.
Since v0.27.2, RAG Studio includes a 39-model LLM registry across 9 providers, a 21-model embedding registry, 8 chunking strategies (including Late Chunking and Syntactic), and a 3-model reranker registry — all discoverable via GET /rag/models and swappable at runtime with PUT /rag/config (no restart required). v0.31.0 added Multi-Index Routing (RouterAgent), RRF Fusion, and Late Chunking. v0.32.0 added Agentic Chunking Intelligence (ChunkingAgent) with per-doc-type embedding model selection and per-label query re-embedding.
No other graph database ships a built-in RAG pipeline, let alone the tooling to tune and evaluate it.
Headless engine — build any UI you want
Purple8 Hyper Graph is headless by design. pip install purple8-hyper-graph gives you the full engine. The RAG pipeline is consumed via REST APIs — wire POST /rag/query into your own chatbot, MCP agent, backend service, CLI, or any interface that speaks HTTP. The optional web dashboard (port 3000) is a low-code convenience layer that calls the same endpoints.
Install evaluation metrics
pip install "purple8-hyper-graph[eval]" # adds DeepEval (faithfulness, relevancy, hallucination, context recall)Architecture
┌─────────────────────────────────────────────────────────────┐
│ RAG Studio │
│ │
│ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐ │
│ │ Chunking │ │ Retrieval │ │Generation│ │ Evaluation│ │
│ │ Strategy │ │ Tuning │ │ Params │ │ Metrics │ │
│ └────┬─────┘ └─────┬─────┘ └────┬─────┘ └─────┬─────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ POST /rag/query │ │
│ │ Embed → Retrieve → (Rerank) → Assemble → Generate │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ POST /rag/evaluate │ │
│ │ Faithfulness · Relevancy · Recall · Hallucination │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘Configuration
Every tenant has an independent RAG configuration stored as rag_config.json in the tenant data directory. Read and write it via the REST API.
Read current config
curl /rag/config \
-H "Authorization: Bearer $TOKEN"Update config
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chunking_strategy": "semantic",
"retrieval_k": 10,
"embedding_model": "text-embedding-3-large",
"min_similarity": 0.75,
"hybrid_weight": 0.8,
"temperature": 0.1,
"max_tokens": 1024,
"system_prompt": "You are a compliance expert. Answer using ONLY the provided context. Cite section numbers."
}'Only the fields you include are updated — everything else is preserved.
Chunking Strategies
Control how documents are split before embedding. Set via chunking_strategy in the RAG config. Each strategy serves a different document type and query style — pick the one that matches your data.
How to choose
Not sure which to pick? Start with Recursive Character (recursive) — it handles most document types well and is the industry default in LangChain / LlamaIndex. Switch to a specialised strategy only when evaluation scores plateau.
| Strategy | Key | How it works | Best for | Tradeoff |
|---|---|---|---|---|
| Fixed-Size | fixed | Split every chunk_size tokens with chunk_overlap overlap | General purpose, predictable chunk count | May split mid-sentence — increase overlap to mitigate |
| Recursive Character | recursive | Split by paragraphs → sentences → words, recursively falling back until chunks fit target size | Semi-structured text (Markdown, HTML, code) — most popular strategy | Slightly variable chunk sizes |
| Semantic | semantic | Embed each sentence, split at embedding-similarity drop-off points (topic boundaries) | Long-form prose, articles, research papers — any doc with topic shifts | Requires per-sentence embedding at ingest — slower and costlier |
| Sentence Window | sentence_window | Each chunk is a single sentence; retrieval returns the sentence + N surrounding sentences | Precise Q&A — legal clauses, medical records, compliance | Many small chunks = larger index; best combined with a reranker |
| Parent–Child | parent_child | Large parent chunks (e.g. 2048 tokens) + small child chunks (e.g. 256 tokens). Retrieve by child, return parent as context | Technical manuals, legal contracts, specifications | ~2× index size; more complex configuration |
| Markdown / Header | markdown_header | Split at Markdown headers (# / ## / ###), preserving header hierarchy as metadata on each chunk | Documentation, READMEs, wikis, knowledge bases | Only works for structured markup; falls back to recursive for headerless sections |
| Late Chunking | late | Embed the entire document first with a long-context model (voyage-4-large, 32 K ctx), then pool token embeddings into context-aware chunk vectors | Legal contracts, research papers, financial reports — any doc where cross-sentence context matters | Requires a long-context model; slower and costlier at ingest; dramatically better retrieval quality |
| Syntactic | syntactic | Split at syntactic boundaries (function/class/block definitions) using AST-aware parsing | Source code, notebooks, structured programs | Requires language detection; not suitable for prose |
When to use each — decision tree
Is your content source code?
└─ Yes → syntactic
Is your content Markdown / HTML with headings?
└─ Yes → markdown_header
└─ No
├─ Long doc (legal / research / financial) needing cross-sentence context?
│ └─ Yes → late (voyage-4-large, 32 K context)
├─ Do you need surgical single-sentence Q&A?
│ └─ Yes → sentence_window (+ reranker)
├─ Are documents very long with clear topic shifts?
│ └─ Yes → semantic
├─ Do answers require both local detail AND broader context?
│ └─ Yes → parent_child
└─ Default → recursive (best all-rounder)
or → fixed (simplest, fastest)Let ChunkingAgent decide automatically (v0.32.0)
Enable chunking_agent_enabled: true in PUT /rag/config and Purple8 will automatically classify each document at ingest time and select the right chunking strategy + embedding model combination. See ChunkingAgent below.
Example: Switch to semantic chunking
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{"chunking_strategy": "semantic"}'Example: Late Chunking for legal / research documents (v0.31.0)
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{
"chunking_strategy": "late",
"late_chunking_model": "voyage-4-large",
"late_chunking_max_tokens": 28000
}'| Late Chunking parameter | Default | Description |
|---|---|---|
late_chunking_model | voyage-4-large | Long-context embedding model used for whole-document embedding before pooling |
late_chunking_max_tokens | 32000 | Document token limit before late chunking falls back to recursive splitting |
Supported models for late chunking:
| Model | Context | Provider |
|---|---|---|
voyage-4-large | 32 K tokens | Voyage AI (recommended) |
jina-embeddings-v3 | 8 K tokens | Jina AI |
text-embedding-3-large | 8 K tokens | OpenAI (fallback) |
Example: Syntactic chunking for source code (v0.32.0)
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{"chunking_strategy": "syntactic"}'Syntactic chunking uses AST-aware splitting to keep function and class definitions intact — critical for code retrieval quality. Works with Python, JavaScript, TypeScript, Go, Rust, and more.
ChunkingAgent — Automatic Strategy Selection (v0.32.0)
ChunkingAgent classifies each document at ingest time and automatically selects the optimal (chunking_strategy, embedding_model) combination for that document type. Enable it with a single config flag:
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{"chunking_agent_enabled": true}'When enabled, every POST /ingest/commit call runs classification and stores the result as node metadata:
{
"doc_type": "legal_contract",
"chunking_strategy": "late",
"embedding_model": "voyage-4-large",
"chunking_confidence": 0.95,
"classification_source": "heuristic"
}Default doc-type → strategy mapping:
| Doc Type | Chunking Strategy | Embedding Model |
|---|---|---|
legal_contract | late | voyage-4-large |
research_paper | late | voyage-4-large |
document | semantic | voyage-4-large |
code | syntactic | voyage-code-3 |
financial_report | semantic | voyage-finance-2 |
clinical_note | sentence_window | voyage-3 |
transcript | sentence_window | voyage-3 |
markdown | markdown_header | text-embedding-3-small |
* (catch-all) | fixed | text-embedding-3-small |
DocIntel fast-path: If you're using Purple8 DocIntel to extract documents, pass the doc_type, chunking_hint, and suggested_embedding_model fields from job_to_preview() into /ingest/commit. The ChunkingAgent will use DocIntel's classification directly — no heuristic re-classification:
curl -X POST /ingest/commit \
-H "Authorization: Bearer $TOKEN" \
-d '{
"source_name": "contract_q1_2026.pdf",
"doc_type": "legal_contract",
"chunking_hint": "late",
"suggested_embedding_model": "voyage-4-large",
"entities": [...]
}'The registry is fully customisable — save a chunking_registry.json and load it via the Python SDK:
from purple8_graph.chunking_agent import ChunkingStrategyRegistry
registry = ChunkingStrategyRegistry.from_file("chunking_registry.json")
# or build programmatically:
from purple8_graph.chunking_agent import ChunkingRule
registry.rules["my_doc_type"] = ChunkingRule(
chunking_strategy="semantic",
embedding_model="voyage-4-large",
chunk_size=1024,
chunk_overlap=128,
)
registry.save("chunking_registry.json")| Parameter | Default | Range | Description |
|---|---|---|---|
chunk_size | 512 | 64–4096 | Target tokens per chunk. Larger = more context per retrieval, less precision. Smaller = more precise, less context. 256–1024 is the sweet spot. |
chunk_overlap | 64 | 0–512 | Overlapping tokens between consecutive chunks (absolute value). |
chunk_overlap_percent | — | 0–50 | Overlap as a percentage of chunk size. Overrides chunk_overlap when set. Recommended: 10–15%. |
How overlap works
Overlap creates a "stitching" region between adjacent chunks so the LLM doesn't miss information at boundaries.
Chunk 1: [tokens 0–512]
Chunk 2: [tokens 448–960] ← 64 tokens (12.5%) shared
Chunk 3: [tokens 896–1408]- Too little overlap (< 5%) → context lost at split boundaries, lower faithfulness scores.
- Too much overlap (> 30%) → redundant embeddings waste index space and retrieval budget.
- Sweet spot: 10–15% — enough stitching without waste.
Use the chunk_overlap_percent field to set overlap as a percentage — the backend auto-computes the absolute value. This is recommended for frontend / low-code users who shouldn't need to know chunk sizes in tokens.
Example: 15% overlap on 1024-token chunks
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{"chunk_size": 1024, "chunk_overlap_percent": 15}'
# → backend computes chunk_overlap = 153 tokensRetrieval Tuning
Fine-tune how the RAG pipeline retrieves context from the graph.
| Parameter | Default | Range | Description |
|---|---|---|---|
retrieval_k | 5 | 1–100 | Number of graph nodes retrieved per query |
embedding_model | text-embedding-3-small | Any supported model | Embedding model for query encoding. Swap without reindexing. |
min_similarity | 0.7 | 0.0–1.0 | Cosine similarity floor. Raise for precision, lower for recall. |
hybrid_weight | 0.7 | 0.0–1.0 | Balance between BM25 (0.0) and vector (1.0) in hybrid retrieval |
mmr_diversity | 0.0 | 0.0–1.0 | Maximal Marginal Relevance — higher values reduce redundancy in retrieved context |
reranker | null | Provider name | Optional reranker (e.g. "cohere") applied after initial retrieval |
fusion_strategy | weighted | weighted | rrf | Fusion method for hybrid retrieval. rrf = Reciprocal Rank Fusion (rank-position–based, no weight tuning needed). (v0.31.0) |
rrf_k | 60 | 1–200 | RRF constant k in score = 1/(k + rank). 60 is the empirically optimal value (Cormack et al. 2009). (v0.31.0) |
routing_enabled | false | bool | Enable RouterAgent — per-label strategy dispatch via rag_routing.json. (v0.31.0) |
Embedding Models
Purple8 Hyper Graph supports any OpenAI-compatible embedding model. The following are pre-configured and returned by GET /rag/models:
Listing available models
curl /rag/models -H "Authorization: Bearer $TOKEN" | jq '.embedding_models[] | {id, provider, dimensions}'| Model | Provider | Dimensions | Max Tokens | Best for |
|---|---|---|---|---|
text-embedding-3-small | OpenAI | 1,536 | 8,191 | Default — fast, cheap, strong multilingual |
text-embedding-3-large | OpenAI | 3,072 | 8,191 | Highest-quality OpenAI. Supports dimension reduction (256–3072). |
embed-v4.0 | Cohere | 1,536 | 128,000 | Latest Cohere. Text + image + PDF. 128k context. Flexible dims. |
embed-english-v3.0 | Cohere | 1,024 | 512 | English-only. Good quality/speed balance. |
embed-multilingual-v3.0 | Cohere | 1,024 | 512 | 100+ languages. |
voyage-4-large | Voyage AI | 1,024 | 32,000 | Best general-purpose retrieval quality. Flexible dims (256–2048). |
voyage-4 | Voyage AI | 1,024 | 32,000 | Balanced quality/cost. |
voyage-code-3 | Voyage AI | 1,024 | 32,000 | Optimized for code retrieval. |
voyage-finance-2 | Voyage AI | 1,024 | 32,000 | Optimized for financial documents. |
voyage-law-2 | Voyage AI | 1,024 | 16,000 | Optimized for legal documents. |
gemini-embedding-001 | 768 | 2,048 | Google Gemini text embedding. | |
text-embedding-004 | 768 | 2,048 | Google PaLM. Good for GCP stacks. | |
amazon.titan-embed-text-v2:0 | AWS Bedrock | 1,024 | 8,192 | AWS-native stacks. |
BAAI/bge-large-en-v1.5 | Self-hosted | 1,024 | 512 | Top open-source English (MIT). vLLM / TEI / Ollama. |
BAAI/bge-m3 | Self-hosted | 1,024 | 8,192 | Open-source 100+ language multi-granularity. |
nomic-ai/nomic-embed-text-v1.5 | Self-hosted | 768 | 8,192 | Apache 2.0. Matryoshka dims (64–768). Budget self-hosted. |
mixedbread-ai/mxbai-embed-large-v1 | Self-hosted | 1,024 | 512 | Top MTEB benchmark. Apache 2.0. Binary quantization. |
Custom models: set embedding_model to any model ID your provider accepts. If you're using a self-hosted model behind an OpenAI-compatible API (vLLM, TEI, Ollama), just point OPENAI_BASE_URL to your server.
Reranker Models
| Model | Description |
|---|---|
cohere | Cohere Rerank v4 Pro — state-of-the-art multilingual, 32k context, supports semi-structured JSON |
cohere-fast | Cohere Rerank v4 Fast — low-latency variant for high-throughput pipelines |
cross-encoder | Cross-Encoder (self-hosted) — run locally (e.g. ms-marco-MiniLM). No API cost, full privacy. |
Example: High-precision legal retrieval
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{
"retrieval_k": 15,
"min_similarity": 0.85,
"hybrid_weight": 0.5,
"mmr_diversity": 0.3,
"reranker": "cohere"
}'Example: Broad exploratory search
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{
"retrieval_k": 25,
"min_similarity": 0.5,
"hybrid_weight": 0.6,
"mmr_diversity": 0.0
}'RRF Fusion — fusion_strategy: "rrf" (v0.31.0)
Reciprocal Rank Fusion replaces weighted-sum as the hybrid fusion strategy. Because it works on rank positions rather than raw scores, it handles mismatched score distributions (BM25 vs cosine similarity vs graph-hop count) without manual weight tuning.
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{"fusion_strategy": "rrf", "rrf_k": 60}'RRF score: score(d) = Σ 1 / (k + rank_r(d)) across all retrievers. k=60 is the empirically optimal constant (Cormack et al. 2009). Set k lower to amplify top-rank differences; set it higher to reduce their impact.
Multi-Index Router — RouterAgent (v0.31.0)
RouterAgent dispatches each query to the optimal retrieval strategy based on the matched node label and query confidence. Configure per-label rules in rag_routing.json and enable with one flag:
# Enable routing
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{"routing_enabled": true}'
# Configure per-label routing rules
curl -X PUT /rag/routing \
-H "Authorization: Bearer $TOKEN" \
-d '{
"Document": [{"strategy": "hybrid", "min_confidence": 0.0}],
"Person": [{"strategy": "graph", "min_confidence": 0.8, "expand_hops": 2},
{"strategy": "vector", "min_confidence": 0.0}],
"Code": [{"strategy": "bm25", "min_confidence": 0.0, "embedding_model": "voyage-code-3"}],
"*": [{"strategy": "hybrid", "min_confidence": 0.0}]
}'The embedding_model field on a routing rule (v0.32.0) triggers per-label query re-embedding — when a query matches the Code label, the router re-embeds the query with voyage-code-3 before searching the code vector sub-space. This ensures the query vector lives in the same domain-specific space as the indexed code chunks.
Each response includes a "retrieval" metadata block:
{
"answer": "...",
"retrieval": {
"strategy": "bm25",
"label_matched": "Code",
"embedding_model_used": "voyage-code-3",
"latency_ms": 1.4,
"fusion": null
}
}Read and write routing rules:
# Read current routing config
curl /rag/routing -H "Authorization: Bearer $TOKEN"
# Replace entire routing config
curl -X PUT /rag/routing \
-H "Authorization: Bearer $TOKEN" \
-d @rag_routing.jsonGeneration Parameters
Control how the LLM generates answers from the retrieved context.
| Parameter | Default | Range | Description |
|---|---|---|---|
provider | openai | See providers table | LLM provider — determines which API endpoint is called |
model | gpt-5.4-mini | Any supported model | Language model ID for answer generation |
temperature | 0.0 | 0.0–2.0 | Randomness. 0.0 = deterministic, higher = more creative. |
max_tokens | 512 | 1–4096 | Maximum tokens in the generated answer |
top_p | 1.0 | 0.0–1.0 | Nucleus sampling. Lower values = more focused. |
system_prompt | (see below) | Any string | System instruction sent to the LLM before the context + question |
Supported LLM Providers
| Provider key | Service | Auth required |
|---|---|---|
openai | OpenAI API | api_key |
azure_openai | Azure OpenAI Service | azure_endpoint + api_key + azure_deployment |
anthropic | Anthropic API | api_key |
google | Google AI (Gemini API) | api_key |
vertex | Google Vertex AI | google_project + google_location |
bedrock | AWS Bedrock | aws_region + optional aws_profile |
mistral | Mistral AI (La Plateforme) | api_key |
cohere | Cohere API | api_key |
self-hosted | Any OpenAI-compatible endpoint | base_url + optional api_key |
Language Models
All models are pre-configured in the backend registry and returned by GET /rag/models. Use the model id as the model config value.
💡 Tip: Run
curl /rag/models -H "Authorization: Bearer $TOKEN" | jq '.llm_models[] | {id, provider, context_window}'to discover all available models.
| Model | Provider | Context Window | Max Output | Best for |
|---|---|---|---|---|
gpt-5.4 | OpenAI | 1M | 128k | Flagship — best intelligence for agentic, coding, complex reasoning |
gpt-5.4-mini | OpenAI | 400k | 128k | Fast, cost-efficient — ideal default for RAG pipelines |
gpt-5.4-nano | OpenAI | 200k | 64k | Lowest latency and cost — high-volume extraction |
o3 | OpenAI | 200k | 100k | Advanced reasoning with thinking tokens — math, science |
o4-mini | OpenAI | 200k | 100k | Fast reasoning — STEM tasks |
claude-sonnet-4-20250514 | Anthropic | 200k | 64k | Best speed/intelligence balance — strong tool use and coding |
claude-opus-4-20250514 | Anthropic | 200k | 32k | Most intelligent Claude — sustained autonomous performance |
claude-3.5-haiku-20241022 | Anthropic | 200k | 8k | Fastest Claude 3.5 — classification, extraction, high-throughput |
claude-haiku-4-20250514 | Anthropic | 200k | 8k | Ultra-fast Claude 4 — real-time classification, routing, high-volume extraction |
gemini-3.1-pro-preview | 1M | 65k | Most advanced Gemini — deep reasoning, agentic coding | |
gemini-3-flash | 1M | 65k | Frontier-class performance at fraction of the cost | |
gemini-2.5-flash | 1M | 65k | Best price-performance — low-latency reasoning | |
gemini-2.5-pro | 1M | 65k | Deep reasoning + coding with 1M token context | |
gemini-2.5-flash-lite | 1M | 65k | Fastest and most budget-friendly Gemini | |
mistral-large-latest | Mistral | 128k | 32k | State-of-the-art open-weight multimodal |
mistral-medium-latest | Mistral | 128k | 32k | Frontier-class multimodal — strong tool use |
mistral-small-latest | Mistral | 128k | 32k | Hybrid instruct/reasoning/coding — open-weight |
magistral-medium-latest | Mistral | 128k | 32k | Multimodal reasoning model |
magistral-small-latest | Mistral | 128k | 32k | Fast, cost-efficient reasoning with multilingual support |
codestral-latest | Mistral | 256k | 32k | Cutting-edge code generation — 256k context |
command-a-03-2025 | Cohere | 256k | 8k | Most performant Cohere — RAG, agents, multilingual |
command-a-reasoning-08-2025 | Cohere | 256k | 32k | First Cohere reasoning model — 23 languages |
command-r7b-12-2024 | Cohere | 128k | 4k | Small, fast — excels at RAG and tool use |
amazon.nova-pro-v1:0 | AWS Bedrock | 300k | 5k | Multimodal — strong for AWS-native stacks |
amazon.nova-lite-v1:0 | AWS Bedrock | 300k | 5k | Fast, budget-friendly multimodal |
amazon.nova-micro-v1:0 | AWS Bedrock | 128k | 5k | Text-only, lowest latency and cost |
llama-4-maverick-17b-128e | Self-hosted | 1M | 32k | Llama 4 Maverick — 17B active (128 experts MoE), 1M context, multimodal |
llama-4-scout-17b-16e | Self-hosted | 512k | 32k | Llama 4 Scout — 17B active (16 experts MoE), 512k context, efficient |
llama-3.3-70b | Self-hosted | 128k | 32k | Open-weight via vLLM / Ollama — strong general-purpose |
deepseek-r1 | Self-hosted | 128k | 32k | Open reasoning model — math, code, logic |
Custom models: set provider and model to any combination your deployment supports. For self-hosted models, point base_url to your OpenAI-compatible server (vLLM, Ollama, TGI, etc.).
Choosing a model for RAG
┌─────────────────────┐
│ What's your priority?│
└──────┬──────────────┘
┌───────────┼───────────┐
▼ ▼ ▼
Quality Speed Cost
│ │ │
▼ ▼ ▼
gpt-5.4 / opus gpt-5.4-nano gpt-5.4-mini
gemini-2.5-pro haiku gemini-2.5-flash
gemini-3.1-pro nova-micro command-a💡 RAG best practice: Start with
gpt-5.4-miniorgemini-2.5-flash(fast, cheap, strong). Upgrade togpt-5.4orclaude-sonnet-4only if answer quality on your domain needs it. For privacy-first deployments, usellama-3.3-70bself-hosted via vLLM.
Default system prompt
You are a knowledge assistant. Answer the question using ONLY the graph context provided.
Cite entity names when relevant. If the context doesn't contain the answer, say so.Example: Custom system prompt for compliance
curl -X PUT /rag/config \
-H "Authorization: Bearer $TOKEN" \
-d '{
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"temperature": 0.0,
"max_tokens": 1024,
"system_prompt": "You are a regulatory compliance assistant for a financial institution. Answer using ONLY the graph context. Cite regulation section numbers. If the context is insufficient, explicitly state what is missing."
}'Evaluation
RAG Studio includes built-in evaluation using DeepEval metrics. Run a test set through your pipeline and get quality scores — no external tooling required.
Install
pip install "purple8-hyper-graph[eval]"Available metrics
| Metric | What it measures | Score range |
|---|---|---|
| Faithfulness | Is the answer supported by the retrieved context? (no hallucinated facts) | 0.0–1.0 |
| Answer Relevancy | Does the answer address the question? | 0.0–1.0 |
| Context Recall | Did the retriever find the right context nodes? | 0.0–1.0 |
| Hallucination | Does the answer contain claims not in the context? (lower is better) | 0.0–1.0 |
Run an evaluation
curl -X POST /rag/evaluate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"test_cases": [
{
"question": "Who authored the most papers on neural scaling laws?",
"expected_answer": "Dr. Emily Chen authored 12 papers on neural scaling laws."
},
{
"question": "What is the current compliance status of Project Alpha?",
"expected_answer": "Project Alpha has 2 outstanding audit findings."
},
{
"question": "Which building has the highest seismic risk?",
"expected_answer": "Building C-7 has the highest seismic risk score of 0.89."
}
],
"metrics": ["faithfulness", "answer_relevancy", "context_recall", "hallucination"]
}'Response
{
"test_cases": 3,
"results": [
{
"question": "Who authored the most papers on neural scaling laws?",
"answer": "Based on the graph, Dr. Emily Chen authored 12 papers...",
"context_nodes": ["n_42", "n_87", "n_103"],
"scores": {
"faithfulness": 0.95,
"answer_relevancy": 0.92,
"context_recall": 0.88,
"hallucination": 0.05
}
}
],
"aggregates": {
"faithfulness": 0.91,
"answer_relevancy": 0.89,
"context_recall": 0.85,
"hallucination": 0.08
},
"config": {
"retrieval_k": 10,
"embedding_model": "text-embedding-3-large",
"min_similarity": 0.75,
"chunking_strategy": "semantic",
"temperature": 0.0
}
}Prometheus metrics
When evaluation_enabled: true is set in the RAG config, evaluation scores are automatically exported as Prometheus gauges:
| Metric | Labels | Description |
|---|---|---|
purple8_rag_faithfulness | tenant_id | Rolling average faithfulness score |
purple8_rag_answer_relevancy | tenant_id | Rolling average answer relevancy |
purple8_rag_context_recall | tenant_id | Rolling average context recall |
purple8_rag_hallucination_rate | tenant_id | Rolling average hallucination rate |
These metrics are scrapeable at GET /metrics alongside all existing Purple8 Prometheus metrics. See Observability for Grafana dashboard setup.
A/B Testing Workflow
RAG Studio configs are per-tenant — use this to A/B test different configurations:
1. Baseline
# Save baseline config
curl -X PUT /rag/config -H "Authorization: Bearer $TENANT_A_TOKEN" \
-d '{"retrieval_k": 5, "embedding_model": "text-embedding-3-small", "chunking_strategy": "fixed"}'
# Run evaluation
curl -X POST /rag/evaluate -H "Authorization: Bearer $TENANT_A_TOKEN" \
-d '{"test_cases": [...], "metrics": ["faithfulness", "answer_relevancy", "context_recall"]}'
# → aggregates: {faithfulness: 0.82, answer_relevancy: 0.78, context_recall: 0.71}2. Candidate
# Save candidate config
curl -X PUT /rag/config -H "Authorization: Bearer $TENANT_B_TOKEN" \
-d '{"retrieval_k": 15, "embedding_model": "text-embedding-3-large", "chunking_strategy": "semantic", "reranker": "cohere"}'
# Run same evaluation
curl -X POST /rag/evaluate -H "Authorization: Bearer $TENANT_B_TOKEN" \
-d '{"test_cases": [...], "metrics": ["faithfulness", "answer_relevancy", "context_recall"]}'
# → aggregates: {faithfulness: 0.91, answer_relevancy: 0.89, context_recall: 0.85}3. Compare
| Metric | Baseline (A) | Candidate (B) | Delta |
|---|---|---|---|
| Faithfulness | 0.82 | 0.91 | +11% |
| Answer Relevancy | 0.78 | 0.89 | +14% |
| Context Recall | 0.71 | 0.85 | +20% |
Promote the winner by copying the config to your production tenant.
Python SDK
All RAG Studio features are available programmatically:
from purple8_graph.genai import AIGraphBuilder, OpenAIProvider
provider = OpenAIProvider(api_key="sk-...")
builder = AIGraphBuilder(engine=engine, provider=provider)
# Natural language query — uses the full RAG pipeline
result = await builder.query("Who are the key stakeholders for Project Alpha?")
# The /rag/config, /rag/query, and /rag/evaluate endpoints are also
# available via the REST API client:
from httpx import AsyncClient
async with AsyncClient(base_url="http://localhost:8000") as client:
# Update config
await client.put("/rag/config", json={
"chunking_strategy": "semantic",
"retrieval_k": 15,
"temperature": 0.1,
}, headers={"Authorization": f"Bearer {token}"})
# Query
resp = await client.post("/rag/query", json={
"question": "What is the compliance status of Building C-7?",
}, headers={"Authorization": f"Bearer {token}"})
# Evaluate
resp = await client.post("/rag/evaluate", json={
"test_cases": [
{"question": "...", "expected_answer": "..."},
],
"metrics": ["faithfulness", "answer_relevancy"],
}, headers={"Authorization": f"Bearer {token}"})Consuming the RAG Pipeline
Purple8 Hyper Graph is headless — the generation endpoint POST /rag/query is designed to be consumed from any interface. Here are the four common patterns:
1. MCP (AI Agents)
The RAG pipeline is exposed as an MCP tool (rag_query), so any MCP-compatible AI agent (Claude Desktop, Cursor, VS Code Copilot) can query your knowledge graph with natural language — zero custom code:
pip install "purple8-hyper-graph[mcp]"
purple8-hyper-graph mcp serveTool: rag_query
Input: {"question": "What compliance violations were flagged last quarter?"}
→ Returns grounded answer + source nodesThe MCP tool inherits the tenant's RAG Studio configuration — all tuning applies automatically.
2. Chatbot / Custom UI
Wire POST /rag/query into any conversational interface — Streamlit, Gradio, React chat widget, Slack bot, Teams integration, or a custom app:
# Streamlit chatbot (5 lines)
import streamlit as st, httpx
question = st.chat_input("Ask your knowledge graph…")
if question:
r = httpx.post("http://localhost:8000/rag/query",
json={"question": question},
headers={"Authorization": f"Bearer {TOKEN}"})
st.chat_message("assistant").write(r.json()["answer"])// React / Next.js — fetch from any frontend
const res = await fetch('/api/rag/query', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ question }),
});
const { answer, context_nodes } = await res.json();3. Backend Service
Another microservice calls the REST API — no SDK required, just HTTP:
async def answer_customer_query(question: str) -> str:
async with httpx.AsyncClient() as client:
r = await client.post("http://purple8-hyper-graph-api:8000/rag/query",
json={"question": question},
headers={"Authorization": f"Bearer {SERVICE_TOKEN}"})
return r.json()["answer"]4. CLI / Scripts / CI
curl -s -X POST http://localhost:8000/rag/query \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"question": "Who are the key stakeholders?"}' | jq -r .answerThe optional web UI
The bundled web UI (purple8-hyper-graph-ui, port 3000) is a fifth option — a point-and-click dashboard for teams who prefer a low-code experience. It calls the same POST /rag/query endpoint under the hood. Developers who have built their own interface don't need it.
Complete Configuration Reference
{
"retrieval_k": 5,
"embedding_model": "text-embedding-3-small",
"min_similarity": 0.7,
"reranker": null,
"hybrid_weight": 0.7,
"mmr_diversity": 0.0,
"fusion_strategy": "weighted",
"rrf_k": 60,
"routing_enabled": false,
"chunking_strategy": "fixed",
"chunk_size": 512,
"chunk_overlap": 64,
"chunk_overlap_percent": null,
"late_chunking_model": "voyage-4-large",
"late_chunking_max_tokens": 32000,
"chunking_agent_enabled": false,
"provider": "openai",
"model": "gpt-5.4-mini",
"temperature": 0.0,
"max_tokens": 512,
"top_p": 1.0,
"system_prompt": "You are a knowledge assistant. Answer the question using ONLY the graph context provided. Cite entity names when relevant. If the context doesn't contain the answer, say so.",
"evaluation_enabled": false
}All fields are optional in PUT /rag/config — only the fields you include are updated.
Supported values
| Field | Type | Values |
|---|---|---|
provider | string | openai, azure_openai, anthropic, google, vertex, bedrock, mistral, cohere, self-hosted |
model | string | Any model from GET /rag/models .llm_models or a custom ID |
chunking_strategy | string | fixed, recursive, semantic, sentence_window, parent_child, markdown_header, late, syntactic |
embedding_model | string | Any model from GET /rag/models .embedding_models or a custom ID |
reranker | string | null | null, cohere, cohere-fast, cross-encoder |
chunk_overlap_percent | float | null | null (use absolute chunk_overlap) or 0–50 (auto-computes chunk_overlap) |
fusion_strategy | string | weighted (default), rrf (v0.31.0) |
rrf_k | int | 1–200, default 60 (v0.31.0) |
routing_enabled | bool | false (default), true — enables RouterAgent (v0.31.0) |
late_chunking_model | string | Any long-context embedding model ID, default voyage-4-large (v0.31.0) |
late_chunking_max_tokens | int | Token limit before fallback to recursive, default 32000 (v0.31.0) |
chunking_agent_enabled | bool | false (default), true — enables ChunkingAgent at ingest (v0.32.0) |
Model & strategy discovery
# List all supported embedding models, LLM models, chunking strategies, and rerankers
curl /rag/models -H "Authorization: Bearer $TOKEN"Response:
{
"embedding_models": [
{"id": "text-embedding-3-small", "provider": "openai", "dimensions": 1536, "max_tokens": 8191, "description": "..."},
...
],
"llm_models": [
{"id": "gpt-5.4-mini", "provider": "openai", "context_window": 400000, "max_output": 128000, "description": "..."},
{"id": "claude-sonnet-4-20250514", "provider": "anthropic", "context_window": 200000, "max_output": 64000, "description": "..."},
...
],
"chunking_strategies": [
{"id": "recursive", "label": "Recursive Character", "description": "...", "when": "...", "why": "...", "tradeoff": "..."},
...
],
"reranker_models": [
{"id": "cohere", "label": "Cohere Rerank v4 Pro", "description": "..."},
...
]
}