RAG Pipeline Guide¶
Give your AI agents access to real knowledge. Helix provides a complete Retrieval-Augmented Generation (RAG) pipeline as declarative YAML steps — from document ingestion through chunking, embedding, storage, retrieval, postprocessing, and synthesis. Each stage is a separate step that can be composed, customized, and extended independently.
Table of Contents¶
- Pipeline Overview
- Minimal RAG Pipeline
- Stage 1: Ingestion
- Reader Types
- Plain Text
- PDF Documents
- HTML Content
- CSV / TSV Files
- Markdown
- XML
- Directory Reader
- Stage 2: Chunking
- Strategy Overview
- Fixed Size (Default)
- Token-Based
- Sentence Splitter
- Recursive Character Splitter
- Semantic Chunker
- Code Splitter
- Markdown Splitter
- HTML Splitter
- Sliding Window
- Hierarchical Chunker
- Stage 3: Embedding
- Supported Providers
- Basic Usage
- Custom Endpoint
- Local Embeddings
- Stage 4: Vector Storage
- Store Overview
- In-Memory Store
- PostgreSQL (pgvector)
- Redis Vector Search
- ChromaDB
- OpenSearch
- Milvus
- Pinecone
- Qdrant
- Elasticsearch
- Weaviate
- MemX
- Stage 5: Retrieval
- Strategy Overview
- Simple (Default)
- BM25 Keyword Search
- Fusion (Vector + BM25)
- Hybrid Retrieval
- Maximal Marginal Relevance (MMR)
- Ensemble Retrieval
- Auto-Merging Retrieval
- Router Retrieval
- MemX Dense Search
- MemX Text Search (BM25)
- MemX Hybrid Search
- Stage 6: Postprocessing
- Postprocessor Types
- Similarity Threshold
- Metadata Filter
- Multi-Condition Metadata Filter
- PII Masking
- Cohere Reranker
- Jina Reranker
- Long Context Reorder
- Chaining Postprocessors
- Stage 7: Response Synthesis
- Mode Overview
- Compact (Default)
- Refine
- Tree Summarize
- Accumulate
- Simple Summarize (Single-Shot)
- Model Configuration
- Advanced Stages
- Metadata Extraction
- Evaluation
- Caching
- Knowledge Graph RAG
- Complete Examples
- Production RAG API
- Code Documentation Search
- Multi-Source Knowledge Base
- Quality Evaluation Pipeline
- Graph-Enhanced RAG
- Strategy Selection Guide
- Choosing a Chunking Strategy
- Choosing a Retrieval Strategy
- Choosing a Synthesis Mode
- Choosing a Vector Store
- OCR & Document Intelligence
- Supported Backends
- OCR with Docling
- OCR with MinerU
- OCR with Marker
- OCR with Tesseract
- OCR with Surya
- OCR with Unstructured.io
- OCR with Azure Document Intelligence
- OCR with Vision LLM
- Table Extraction
- OCR Configuration Reference
- Large File OCR (Streaming)
- Query Transformation
- HyDE (Hypothetical Document Embeddings)
- MultiQuery
- StepBack Prompting
- SubQuestion Decomposition
- Choosing a Query Transformation Strategy
Pipeline Overview¶
A full RAG pipeline flows through these stages:
Additional stages for advanced use cases:
Top-Level pipelines: Primitive¶
RAG flows can also be defined as top-level pipelines: primitives, which provide a simplified syntax focused on data processing:
# Top-level pipelines: primitive (recommended for standalone RAG/ETL)
pipelines:
- id: doc-search
source: file://./docs
steps:
- rag-chunk:
strategy: semantic
threshold: 0.5
- rag-embed:
provider: openai
model: text-embedding-3-small
- rag-store:
store: pgvector
connection: "postgres://user:pass@db:5432/rag"
- rag-retrieve:
strategy: hybrid
top_k: 10
- rag-synthesize:
mode: compact
model:
provider: openai
model: gpt-4o
output: text
Use pipelines: when the flow is a self-contained data processing pipeline. Use workflows: with RAG steps when the RAG pipeline is part of a larger integration (e.g., triggered by Kafka, with results routed to another system).
RAG as a tools: Primitive¶
For RAG endpoints that agents or MCP clients call, use tools: — this adds mandatory input/output schemas and input validation:
tools:
- id: kb-search
description: "Search the knowledge base"
input:
query: { type: string, description: "Search query", required: true }
top_k: { type: integer, description: "Max results", default: 5 }
output:
answer: { type: string, description: "Synthesized answer" }
sources: { type: array, description: "Source documents" }
steps:
- rag-retrieve:
strategy: hybrid
top_k: 5
- rag-postprocess:
steps:
- type: cohere-rerank
params:
top_n: "3"
- rag-synthesize:
mode: compact
model:
provider: openai
model: gpt-4o-mini
Agents reference this tool by ID: tools: [{ name: kb-search, type: tool, tool_id: kb-search }]
Minimal RAG Pipeline¶
Using the traditional workflows: syntax:
workflows:
- id: simple-rag
trigger:
uri: http-server://0.0.0.0:8080/api/ask
steps:
- rag-ingest:
parser: text
- rag-chunk:
strategy: fixed
chunk_size: 512
overlap: 50
- rag-embed:
provider: openai
model: text-embedding-3-small
- rag-store:
store: memory
collection: docs
- rag-retrieve:
strategy: simple
top_k: 5
- rag-synthesize:
mode: compact
model:
provider: openai
model: gpt-4o
Stage 1: Ingestion¶
The rag-ingest step parses raw data into documents. Helix includes 10 readers covering text, structured data, markup, and binary formats.
Reader Types¶
| Reader | Extensions | Description |
|---|---|---|
text |
.txt | Plain text passthrough |
json |
.json | JSON document parsing |
pdf |
PDF text extraction (pure Go, no external deps) | |
html |
.html, .htm | HTML tag stripping with structure preservation |
csv |
.csv, .tsv | Tabular data with header support |
markdown |
.md | Markdown with frontmatter and heading-based splitting |
xml |
.xml | XML element extraction |
docx |
.docx | Microsoft Word document parsing |
starlang |
.star | Starlang script-based parsing |
directory |
(folder) | Auto-detect reader by file extension |
Plain Text¶
PDF Documents¶
Extracts text from PDF binary streams. Metadata includes pdf_version, stream_count, image_count, and title when available.
HTML Content¶
Strips <script>, <style>, <nav>, and <footer> tags. Converts headings to markdown-like formatting. Decodes HTML entities. Extracts <title> into metadata.
CSV / TSV Files¶
| Parameter | Default | Description |
|---|---|---|
delimiter |
, |
Field delimiter character |
has_header |
true |
First row contains column names |
rows_per_chunk |
0 |
Rows per document (0 = all in one) |
With has_header: true, each row is formatted as column: value pairs. With rows_per_chunk: 100, large CSVs are split into multiple documents.
Markdown¶
| Parameter | Default | Description |
|---|---|---|
split_by_heading |
false |
Create separate documents per heading section |
heading_level |
1 |
Heading level to split at (1-6) |
strip_formatting |
false |
Remove markdown formatting from text |
When split_by_heading: true, each section becomes a separate document with the heading as metadata. YAML frontmatter (between --- delimiters) is extracted into document metadata.
XML¶
| Parameter | Default | Description |
|---|---|---|
row_tag |
(none) | Repeating element tag; each match becomes a document |
text_tags |
(all) | Extract only these tags' text content |
Directory Reader¶
| Parameter | Default | Description |
|---|---|---|
recursive |
false |
Walk subdirectories |
extensions |
(all supported) | Filter by file extensions |
The directory reader auto-detects the file format by extension and dispatches to the appropriate reader (text, JSON, PDF, HTML, CSV, Markdown, XML, DOCX).
Stage 2: Chunking¶
The rag-chunk step splits documents into smaller chunks for embedding. Helix provides 10 chunking strategies ranging from simple fixed-size to embedding-aware semantic splitting.
Strategy Overview¶
| Strategy | Best For | Key Feature |
|---|---|---|
fixed |
General purpose | Consistent chunk sizes |
token |
Token-aware splitting | Respects token boundaries |
sentence |
Natural language | Preserves sentence boundaries |
recursive |
Mixed content | Hierarchical separator fallback |
semantic |
High-quality RAG | Embedding-based breakpoints |
code |
Source code | Language-aware function/class boundaries |
markdown |
Documentation | Heading hierarchy awareness |
html |
Web content | Block element boundaries |
sliding_window |
Overlapping coverage | Fixed stride with overlap |
hierarchical |
Auto-merging retrieval | Multi-level parent-child chunks |
Fixed Size (Default)¶
Splits text at character boundaries with configurable overlap.
Token-Based¶
Splits respecting token (word) boundaries rather than raw characters.
Sentence Splitter¶
Detects sentence boundaries (., !, ? followed by whitespace) and groups sentences until the chunk size is reached. Respects paragraph boundaries (\n\n) as natural break points.
| Parameter | Default | Description |
|---|---|---|
chunk_size |
1000 |
Target chunk size in characters |
overlap |
0 |
Overlap between chunks (max: chunk_size/4) |
Recursive Character Splitter¶
- rag-chunk:
strategy: recursive
chunk_size: 1000
overlap: 100
separators:
- "\n\n"
- "\n"
- ". "
- " "
- ""
Tries splitting by each separator in order. If a chunk is still too large after splitting by the first separator, it recursively splits with the next separator. This preserves the most meaningful boundaries possible.
| Parameter | Default | Description |
|---|---|---|
chunk_size |
1000 |
Target chunk size |
overlap |
0 |
Overlap (max: chunk_size/4) |
separators |
["\n\n", "\n", ". ", " ", ""] |
Separator hierarchy (tried in order) |
Semantic Chunker¶
Uses an embedding model to compute cosine similarity between adjacent sentences. Creates chunk breaks where similarity drops below the threshold. Produces chunks with natural semantic boundaries.
| Parameter | Default | Description |
|---|---|---|
threshold |
0.5 |
Similarity threshold (0.0-1.0); lower = larger chunks |
chunk_size |
2048 |
Maximum chunk size |
Requires an embedder to be configured (uses the workflow's rag-embed provider). Falls back to Jaccard similarity if embedding fails.
Code Splitter¶
Splits source code at language-specific declaration boundaries:
| Language | Split Points |
|---|---|
go |
func, type ... struct, type ... interface |
python |
def, class, async def |
javascript |
function, class, const ... = (, export |
java |
public, private, protected, class, interface |
rust |
fn, pub fn, struct, impl, enum, trait |
generic |
Blank lines + indentation changes |
Falls back to recursive character splitting if individual code blocks exceed chunk_size.
Markdown Splitter¶
Splits at heading boundaries (# through ######), respecting heading hierarchy. Small subsections under the same parent heading stay together.
| Parameter | Default | Description |
|---|---|---|
chunk_size |
1000 |
Maximum chunk size |
overlap |
0 |
Overlap (max: chunk_size/4) |
retain_headers |
false |
Prepend parent headings to each chunk for context |
When retain_headers: true, each chunk includes its parent heading chain:
Becomes a chunk with content: # User Guide\n## Authentication\n### JWT\nThis section covers JWT setup...
HTML Splitter¶
Splits at block-level HTML tag boundaries using regex-based tag matching (no external dependencies).
| Parameter | Default | Description |
|---|---|---|
chunk_size |
1000 |
Maximum chunk size |
tags |
div, section, article, p, h1-h6, li, tr, blockquote, pre |
Tags to split on |
Sliding Window¶
Produces overlapping fixed-size windows. Every position in the text is covered by at least one chunk.
| Parameter | Default | Description |
|---|---|---|
window_size |
500 |
Window size |
stride |
window_size/2 |
Step size between windows |
unit |
chars |
Unit of measurement: chars or words |
Hierarchical Chunker¶
Creates chunks at multiple granularity levels with parent-child relationships. Used together with the auto_merging retrieval strategy -- if enough child chunks match a query, the parent chunk is returned instead for better context.
| Parameter | Default | Description |
|---|---|---|
levels |
(required) | Chunk sizes per level, descending (e.g., [2048, 512, 128]) |
overlap |
0 |
Overlap between chunks at each level |
Each chunk's metadata includes chunk_level, parent_id, and children for tree traversal.
Stage 3: Embedding¶
The rag-embed step converts text chunks into vector embeddings.
Supported Providers¶
| Provider | Default Model | Dimensions | Batch Size |
|---|---|---|---|
openai |
text-embedding-3-small | 1536 | 2048 |
ollama |
nomic-embed-text | 768 | 1 |
mistral |
mistral-embed | 1024 | 512 |
vertex / gemini |
text-embedding-004 | 768 | 100 |
bedrock |
amazon.titan-embed-text-v2:0 | 1024 | 1 |
jina |
jina-embeddings-v3 | 1024 | 500 |
huggingface |
BAAI/bge-large-en-v1.5 | 1024 | 64 |
nomic |
nomic-embed-text-v1.5 | 768 | 400 |
cohere |
embed-v4.0 | 1024 | 96 |
voyage |
voyage-3 | 1024 | 128 |
Basic Usage¶
Custom Endpoint¶
- rag-embed:
provider: openai
model: text-embedding-3-large
api_key: "${env:OPENAI_API_KEY}"
base_url: "https://my-proxy.example.com/v1"
Local Embeddings¶
Stage 4: Vector Storage¶
The rag-store step persists embeddings in a vector database. Helix supports 10 vector store backends.
Store Overview¶
| Store | Type | Best For |
|---|---|---|
memory |
In-memory | Testing, small datasets (<100K vectors) |
pgvector |
PostgreSQL extension | Teams already on PostgreSQL |
redis |
Redis module | Low-latency, caching-oriented |
chromadb |
Dedicated vector DB | Simple setup, local development |
opensearch |
Search engine | AWS ecosystem, full-text + vector |
milvus |
Dedicated vector DB | Large-scale production |
pinecone |
Managed SaaS | Zero-ops, serverless |
qdrant |
Dedicated vector DB | Advanced filtering, Rust performance |
elasticsearch |
Search engine | Existing Elastic infrastructure |
weaviate |
Dedicated vector DB | Multi-modal, GraphQL API |
memx |
Unified search+SQL | Dual-mode (ES API + PostgreSQL), hybrid search, high throughput |
In-Memory Store¶
Pure Go brute-force cosine similarity. No external dependencies. Thread-safe. Ideal for testing and prototyping.
PostgreSQL (pgvector)¶
- rag-store:
store: pgvector
connection: "postgres://${DB_USER}:${DB_PASS}@localhost:5432/mydb"
collection: documents
dimensions: 1536
| Parameter | Default | Description |
|---|---|---|
connection |
(required) | PostgreSQL connection string |
collection |
vector_store |
Table name |
dimensions |
1536 |
Vector dimensions |
Auto-creates the table and enables the vector extension. Uses <=> (cosine distance) for search. Batch upserts with ON CONFLICT handling.
Redis Vector Search¶
- rag-store:
store: redis
connection: "redis://localhost:6379"
collection: doc-vectors
dimensions: 1536
| Parameter | Default | Description |
|---|---|---|
connection |
(required) | Redis connection string |
collection |
vector_idx |
Index name |
dimensions |
1536 |
Vector dimensions |
Requires the RediSearch module. Supports FLAT (exact) and HNSW (approximate) algorithms. Vectors stored as float32 little-endian bytes.
ChromaDB¶
| Parameter | Default | Description |
|---|---|---|
connection |
http://localhost:8000 |
ChromaDB server URL |
collection |
(required) | Collection name |
Pure HTTP REST client. Auto-creates the collection. Distance scores converted to similarity scores.
OpenSearch¶
- rag-store:
store: opensearch
connection: "https://opensearch.example.com:9200"
collection: documents
dimensions: 1536
username: admin
password: "${env:OPENSEARCH_PASSWORD}"
| Parameter | Default | Description |
|---|---|---|
connection |
http://localhost:9200 |
OpenSearch URL |
collection |
(required) | Index name |
dimensions |
1536 |
Vector dimensions |
username |
(none) | Basic auth username |
password |
(none) | Basic auth password |
Uses the k-NN plugin with HNSW algorithm. NDJSON bulk operations for efficient upsert/delete.
Milvus¶
Pinecone¶
- rag-store:
store: pinecone
connection: "https://my-index-abc123.svc.pinecone.io"
collection: my-namespace
Requires api_key set via environment or component config.
Qdrant¶
Elasticsearch¶
- rag-store:
store: elasticsearch
connection: "http://localhost:9200"
collection: documents
dimensions: 1536
Weaviate¶
MemX¶
MemX provides a unified search backend that combines Elasticsearch-speed full-text search with PostgreSQL-grade SQL joins behind one service. Two connection modes are available:
http://host:port-- ES-compatible HTTP API (fastest, recommended for search workloads)postgres://user:pass@host:port/db-- PostgreSQL wire protocol (for SQL joins and transactions)
Auto-Discovery: When connecting to an existing memx table, column names are auto-detected from table indexes -- no manual column configuration needed:
- HNSW/IVF/DiskANN index on a column --> auto-detected as the embedding column for vector search
- FULLTEXT index on a column --> auto-detected as the content column for BM25 text search
- Column with
_idsuffix or EMBEDDING type --> auto-detected as ID or embedding column
This means id_column, embedding_column, and content_column configuration is optional. Helix reads the table schema and indexes at startup and resolves them automatically. You can still provide explicit column overrides as escape hatches when auto-discovery does not match your schema.
database: field: Both rag-store and rag-retrieve support the database: field to target a specific memx database:
# Minimal (auto-discovery) -- recommended
- rag-store:
store: memx
connection: "http://localhost:8080"
database: my_database
collection: products
# With explicit column overrides (optional escape hatch)
- rag-store:
store: memx
connection: "http://localhost:8080"
database: my_database
collection: products
id_column: product_id
embedding_column: product_vec
content_column: description
dimensions: 1536
# PostgreSQL mode -- for SQL joins
- rag-store:
store: memx
connection: "postgres://user:pass@localhost:5432/mydb"
collection: documents
dimensions: 1536
Raw Results: memx retrieve returns raw []map[string]any containing all columns from the table (embedding column excluded, _score field included). There is no forced ID/Content/Metadata mapping -- the agent receives the raw JSON with all columns exactly as stored. Use Starlang transforms downstream if you need to reshape the data.
Performance: MemX achieves 1549 embeddings/sec ingest throughput and sub-300us vector search latency on commodity hardware.
Stage 5: Retrieval¶
The rag-retrieve step searches the vector store and returns relevant chunks. Helix provides 9 retrieval strategies.
Strategy Overview¶
| Strategy | Description | Best For |
|---|---|---|
simple |
Direct vector similarity search | Default, most use cases |
bm25 |
Keyword-based BM25 scoring | Exact keyword matching |
fusion |
Reciprocal Rank Fusion of vector + BM25 | Balanced relevance |
hybrid |
Weighted combination of two retrievers | Fine-tuned relevance |
mmr |
Maximal Marginal Relevance | Diversity in results |
ensemble |
Multiple retrievers with RRF | Combining specialized retrievers |
auto_merging |
Merges child chunks into parents | Hierarchical chunking |
router |
Routes queries to the best retriever | Multi-domain systems |
parent |
Returns parent chunks for matching children | Context preservation |
memx |
MemX dense vector search | MemX backend, fast similarity |
memx_text |
MemX BM25 keyword search | MemX backend, exact keyword matching |
memx_hybrid |
MemX fusion (vector + BM25) | MemX backend, balanced relevance |
Simple (Default)¶
Direct cosine similarity search against the vector store.
BM25 Keyword Search¶
Term frequency-inverse document frequency scoring. Best for queries with specific keywords or terminology.
Fusion (Vector + BM25)¶
Runs both vector and BM25 retrievers, combines results using Reciprocal Rank Fusion (RRF). Balances semantic understanding with keyword precision.
Hybrid Retrieval¶
| Parameter | Default | Description |
|---|---|---|
top_k |
5 |
Number of results to return |
vector_weight |
0.7 |
Weight for vector similarity scores |
keyword_weight |
0.3 |
Weight for keyword match scores |
Runs vector and keyword retrievers in parallel, normalizes scores to [0,1], combines with configurable weights, and deduplicates by chunk ID.
Maximal Marginal Relevance (MMR)¶
| Parameter | Default | Description |
|---|---|---|
top_k |
5 |
Final number of results |
lambda |
0.5 |
Diversity factor (0.0 = max diversity, 1.0 = max relevance) |
fetch_k |
top_k * 3 |
Initial candidate pool size |
Iteratively selects results that balance relevance to the query with diversity from already-selected results. Prevents returning near-duplicate chunks.
Ensemble Retrieval¶
Runs multiple retrievers in parallel and combines results using weighted Reciprocal Rank Fusion:
where K defaults to 60 (the RRF constant). Deduplicates and returns top-k by combined score.
Auto-Merging Retrieval¶
| Parameter | Default | Description |
|---|---|---|
top_k |
5 |
Number of results |
merge_threshold |
0.5 |
Fraction of children needed to trigger merge |
Designed for use with the hierarchical chunking strategy. Groups retrieved child chunks by their parent_id metadata. If the fraction of a parent's children that appear in results exceeds the threshold, replaces them with the full parent chunk for better context.
Example: Hierarchical + Auto-Merging Pipeline
workflows:
- id: hierarchical-rag
trigger:
uri: http-server://0.0.0.0:8080/api/ask
steps:
- rag-ingest:
parser: text
- rag-chunk:
strategy: hierarchical
levels: [2048, 512, 128]
overlap: 50
- rag-embed:
provider: openai
model: text-embedding-3-small
- rag-store:
store: memory
collection: docs
- rag-retrieve:
strategy: auto_merging
top_k: 10
merge_threshold: 0.4
- rag-synthesize:
mode: compact
model:
provider: openai
model: gpt-4o
Router Retrieval¶
Routes queries to the best-suited retriever based on query analysis. The KeywordRouteSelector matches query text against keyword patterns to select the retriever.
MemX Dense Search¶
# Minimal (auto-discovery detects columns from table indexes)
- rag-retrieve:
strategy: memx
store: memx
connection: http://localhost:8080
database: products_db
collection: products
top_k: 10
# With optional column overrides
- rag-retrieve:
strategy: memx
store: memx
connection: http://localhost:8080
database: products_db
collection: products
id_column: product_id
embedding_column: product_vec
content_column: description
top_k: 10
Dense vector similarity search using the MemX backend. Auto-discovery reads table indexes to detect the embedding column (HNSW/IVF/DiskANN) and content column (FULLTEXT) automatically. Results are returned as raw []map[string]any with all table columns (embedding excluded, _score included).
MemX Text Search (BM25)¶
- rag-retrieve:
strategy: memx_text
store: memx
connection: http://localhost:8080
database: products_db
collection: products
top_k: 20
BM25 keyword-based search using the MemX full-text index. No embeddings needed for queries -- pure keyword matching. The content column is auto-detected from FULLTEXT indexes.
MemX Hybrid Search¶
- rag-retrieve:
strategy: memx_hybrid
store: memx
connection: http://localhost:8080
database: products_db
collection: products
top_k: 10
vector_weight: 0.7
keyword_weight: 0.3
Reciprocal rank fusion of MemX dense vector search and BM25 text search. The vector_weight and keyword_weight parameters control the balance between semantic and keyword relevance.
MemX End-to-End Pipeline Example¶
A complete RAG pipeline using MemX for storage and retrieval with auto-discovery (no column config needed):
workflows:
# Ingest documents into MemX
- id: ingest
trigger:
uri: http-server://0.0.0.0:8080/api/ingest
steps:
- rag-ingest:
reader: directory
path: ./docs
recursive: true
- rag-chunk:
strategy: semantic
threshold: 0.5
chunk_size: 512
- rag-embed:
provider: openai
model: text-embedding-3-small
- rag-store:
store: memx
connection: "http://localhost:8080"
database: docs_db
collection: knowledge
# Query with hybrid search
- id: ask
trigger:
uri: http-server://0.0.0.0:8080/api/ask
steps:
- rag-retrieve:
strategy: memx_hybrid
store: memx
connection: "http://localhost:8080"
database: docs_db
collection: knowledge
top_k: 10
vector_weight: 0.7
keyword_weight: 0.3
- rag-postprocess:
steps:
- type: similarity-filter
params:
threshold: "0.6"
- rag-synthesize:
mode: compact
model:
provider: openai
model: gpt-4o
MemX Agent + Pipeline (E2E Verified)¶
A verified end-to-end pipeline where a ReAct agent uses a memx-backed pipeline as a tool:
pipelines:
- id: product-search
steps:
- rag-retrieve:
strategy: memx
store: memx
connection: http://localhost:8080
database: products_db
collection: products
top_k: 3
agents:
- id: product-agent
type: react
model: { provider: openai, model: gpt-4o, api_key: ${OPENAI_API_KEY} }
system_prompt: Use the search tool to find products.
tools:
- name: search-products
type: tool
tool_id: product-search
description: Search the product catalog
parameters:
type: object
properties:
query: { type: string, description: What to search for }
trigger: agui://0.0.0.0:4500/products
The agent receives raw JSON results with all table columns (embedding excluded, _score included). No Chunk wrapping -- the data is passed through as []map[string]any.
Stage 6: Postprocessing¶
The rag-postprocess step refines retrieval results before synthesis. Multiple postprocessors can be chained.
Postprocessor Types¶
| Type | Description |
|---|---|
similarity-filter |
Remove chunks below a score threshold |
top-k |
Keep only top K results |
keyword-filter |
Filter by keyword presence |
metadata-filter |
Filter by metadata key-value conditions |
multi-metadata-filter |
Structured multi-condition metadata filter |
dedup |
Remove duplicate content |
pii-mask |
Mask PII (email, phone, SSN, etc.) |
recency |
Boost recent documents |
length-filter |
Filter by content length |
diversity |
Limit results per source document |
normalize |
Normalize scores to [0,1] |
keyword-boost |
Boost scores for keyword matches |
cohere-rerank |
Rerank via Cohere API |
jina-rerank |
Rerank via Jina API |
long-context-reorder |
Reorder for LLM attention optimization |
Similarity Threshold¶
Removes chunks with similarity scores below the threshold.
Metadata Filter¶
Operators: eq, ne, contains, exists.
Multi-Condition Metadata Filter¶
- rag-postprocess:
steps:
- type: multi-metadata-filter
mode: all
filters:
- key: source
operator: eq
value: "docs"
- key: version
operator: gt
value: "2.0"
- key: language
operator: contains
value: "en"
- key: reviewed
operator: exists
value: ""
| Parameter | Default | Description |
|---|---|---|
mode |
all |
all = all conditions must match; any = at least one |
filters |
(required) | List of conditions |
Each condition supports: eq, neq, gt, lt, contains, exists.
PII Masking¶
Masks: email addresses, phone numbers, SSNs, credit card numbers, IP addresses.
Cohere Reranker¶
- rag-postprocess:
steps:
- type: cohere-rerank
params:
api_key: "${env:COHERE_API_KEY}"
model: rerank-v3.5
top_n: "5"
| Parameter | Default | Description |
|---|---|---|
api_key |
(required) | Cohere API key |
model |
rerank-v3.5 |
Reranking model |
top_n |
(all) | Maximum results to return |
Calls the Cohere /v2/rerank API to reorder results by neural relevance scoring. Significantly improves result quality compared to vector similarity alone.
Jina Reranker¶
- rag-postprocess:
steps:
- type: jina-rerank
params:
api_key: "${env:JINA_API_KEY}"
model: jina-reranker-v2-base-multilingual
top_n: "5"
| Parameter | Default | Description |
|---|---|---|
api_key |
(required) | Jina API key |
model |
jina-reranker-v2-base-multilingual |
Reranking model |
top_n |
(all) | Maximum results to return |
Long Context Reorder¶
Reorders chunks based on the "Lost in the Middle" research paper. Places the most relevant results at the beginning and end of the context, with less relevant results in the middle. This matches LLM attention patterns -- models pay more attention to the start and end of their context window.
Given ranked results [1, 2, 3, 4, 5, 6], reorders to: [1, 3, 5, 6, 4, 2].
Chaining Postprocessors¶
Multiple postprocessors execute in sequence:
- rag-postprocess:
steps:
- type: similarity-filter
params:
min_score: "0.5"
- type: cohere-rerank
params:
api_key: "${env:COHERE_API_KEY}"
top_n: "10"
- type: diversity-filter
params:
max_per_document: "2"
- type: long-context-reorder
This chain: filters low-quality results, reranks with Cohere, limits duplicates per source, then optimizes ordering for LLM attention.
Stage 7: Response Synthesis¶
The rag-synthesize step generates the final answer from retrieved context. Helix provides 5 synthesis modes optimized for different context sizes and quality requirements.
Mode Overview¶
| Mode | LLM Calls | Best For |
|---|---|---|
compact |
1 (usually) | Default; context fits in one call |
refine |
N (per chunk) | Large context; iterative improvement |
tree-summarize |
log(N) | Very large context; recursive summarization |
accumulate |
N + 1 | Independent per-chunk answers; comprehensive coverage |
simple-summarize |
1 | Simple single-shot generation |
Compact (Default)¶
| Parameter | Default | Description |
|---|---|---|
max_tokens |
4000 |
Maximum context tokens (estimated as chars/4) |
Concatenates as many chunks as possible into a single LLM context, separated by \n\n---\n\n. If chunks exceed the token limit, overflows to refine mode for remaining chunks. Minimizes LLM calls while maximizing context.
Refine¶
| Parameter | Default | Description |
|---|---|---|
max_chunks |
0 (all) |
Maximum chunks to process |
- Generates an initial answer from the first chunk
- For each subsequent chunk, asks the LLM to refine the answer given new context
- Each refinement can correct, expand, or improve the answer
Best for large context sets where quality matters more than speed.
Tree Summarize¶
- rag-synthesize:
mode: tree-summarize
model:
provider: anthropic
model: claude-sonnet-4-20250514
chunks_per_level: 3
| Parameter | Default | Description |
|---|---|---|
chunks_per_level |
3 |
Chunks combined per summarization step |
- Groups chunks into batches of
chunks_per_level - Summarizes each batch with the LLM
- If multiple summaries remain, recursively summarizes them
- Continues until a single final answer
Scales logarithmically with context size. Ideal for very large document collections.
Accumulate¶
| Parameter | Default | Description |
|---|---|---|
parallel |
false |
Generate per-chunk answers concurrently |
- Generates an independent answer from each chunk (can run in parallel)
- Combines all per-chunk answers into a final response using a combine prompt
Best when each chunk may contain a distinct, valid answer. Parallel mode improves throughput.
Simple Summarize (Single-Shot)¶
Simple single-shot generation from the assembled context. No advanced chunking or iteration.
Model Configuration¶
All synthesis modes accept a model block:
model:
provider: groq # LLM provider
model: llama-3.3-70b-versatile # Model name
api_key: "${env:GROQ_API_KEY}" # API key (optional if set globally)
base_url: "https://..." # Custom endpoint (optional)
temperature: 0.7 # Sampling temperature (optional)
max_tokens: 2048 # Max response tokens (optional)
See the LLM Providers page for all 16 supported providers.
Advanced Stages¶
Metadata Extraction¶
The rag-extract step extracts metadata from documents using rule-based or LLM-powered methods.
- rag-extract:
extractors:
- title
- summary
- keywords
- keywords-llm
- questions
- entities
- language
model:
provider: openai
model: gpt-4o-mini
| Extractor | Method | Description |
|---|---|---|
title |
LLM | Extracts or generates a concise title |
summary |
LLM | Generates a summary of the document |
keywords |
Rule-based | Extracts significant terms by frequency |
keywords-llm |
LLM | LLM-generated keywords |
questions |
LLM | Generates questions the document answers |
entities |
LLM | Named entity extraction |
language |
Rule-based | Detects document language |
LLM-powered extractors (summary, keywords-llm, questions, entities) require a model block.
Evaluation¶
The rag-eval step measures RAG pipeline quality.
- rag-eval:
metrics:
- faithfulness
- answer_relevancy
- context_relevancy
- hit_rate
- mrr
- ndcg
model:
provider: openai
model: gpt-4o
| Metric | Type | Description |
|---|---|---|
faithfulness |
LLM-judged | Is the answer supported by the context? |
answer_relevancy |
LLM-judged | Does the answer address the query? |
context_relevancy |
LLM-judged | Is the retrieved context relevant? |
correctness |
LLM-judged | Is the answer factually correct? |
hit_rate |
Statistical | Fraction of queries with a relevant result in top-k |
mrr |
Statistical | Mean Reciprocal Rank of first relevant result |
ndcg |
Statistical | Normalized Discounted Cumulative Gain |
map |
Statistical | Mean Average Precision |
LLM-judged metrics require a model block.
Caching¶
The rag-cache step caches embeddings and retrieval results.
| Parameter | Default | Description |
|---|---|---|
enabled |
true |
Enable caching |
max_size |
1000 |
Maximum cache entries |
transform |
(none) | Cache key transformation |
Knowledge Graph RAG¶
Build and query knowledge graphs from documents.
Build:
Extracts entities and relationships from text using an LLM, building a queryable knowledge graph.
Query:
| Parameter | Default | Description |
|---|---|---|
hops |
2 |
Graph traversal depth |
top_k |
10 |
Maximum results |
Traverses the knowledge graph from query entities, following relationships up to hops edges deep.
Complete Examples¶
Production RAG API¶
A complete production pipeline with Jina embeddings, pgvector storage, hybrid retrieval, Cohere reranking, and Groq synthesis:
workflows:
- id: production-rag
trigger:
uri: http-server://0.0.0.0:8080/api/ask
auth:
jwt:
secret: "${env:JWT_SECRET}"
steps:
- rag-ingest:
reader: directory
path: ./knowledge-base
recursive: true
extensions: [".md", ".txt", ".pdf"]
- rag-chunk:
strategy: recursive
chunk_size: 512
overlap: 50
separators: ["\n\n", "\n", ". ", " "]
- rag-extract:
extractors: [title, keywords]
- rag-embed:
provider: jina
model: jina-embeddings-v3
api_key: "${env:JINA_API_KEY}"
- rag-store:
store: pgvector
connection: "postgres://${DB_USER}:${DB_PASS}@db:5432/rag"
collection: knowledge
dimensions: 1024
- rag-retrieve:
strategy: hybrid
top_k: 20
vector_weight: 0.7
keyword_weight: 0.3
- rag-postprocess:
steps:
- type: similarity-filter
params:
min_score: "0.4"
- type: cohere-rerank
params:
api_key: "${env:COHERE_API_KEY}"
model: rerank-v3.5
top_n: "8"
- type: long-context-reorder
- rag-cache:
enabled: true
max_size: 50000
- rag-synthesize:
mode: compact
model:
provider: groq
model: llama-3.3-70b-versatile
api_key: "${env:GROQ_API_KEY}"
max_tokens: 6000
- log:
message: "RAG query completed"
Code Documentation Search¶
A pipeline optimized for searching source code and technical documentation:
workflows:
- id: code-search
trigger:
uri: http-server://0.0.0.0:8080/api/code-search
steps:
- rag-ingest:
reader: directory
path: ./src
recursive: true
extensions: [".go", ".py", ".js", ".md"]
- rag-chunk:
strategy: code
language: go
chunk_size: 1500
overlap: 100
- rag-extract:
extractors: [title, keywords]
- rag-embed:
provider: openai
model: text-embedding-3-small
- rag-store:
store: qdrant
connection: "http://localhost:6333"
collection: code-docs
- rag-retrieve:
strategy: mmr
top_k: 10
lambda: 0.6
fetch_k: 30
- rag-postprocess:
steps:
- type: diversity-filter
params:
max_per_document: "3"
- rag-synthesize:
mode: refine
model:
provider: anthropic
model: claude-sonnet-4-20250514
max_chunks: 8
Multi-Source Knowledge Base¶
A pipeline that ingests from multiple formats with different chunking per format, metadata filtering, and graph-enhanced retrieval:
workflows:
# Ingest markdown documentation
- id: ingest-docs
trigger:
uri: timer://ingest?period=1h
steps:
- rag-ingest:
reader: directory
path: ./docs
recursive: true
extensions: [".md"]
- rag-chunk:
strategy: markdown
chunk_size: 800
retain_headers: true
- rag-embed:
provider: mistral
model: mistral-embed
- rag-store:
store: chromadb
connection: "http://localhost:8000"
collection: knowledge
# Ingest CSV data
- id: ingest-data
trigger:
uri: timer://ingest-csv?period=1h
steps:
- rag-ingest:
reader: csv
delimiter: ","
has_header: true
rows_per_chunk: 50
- rag-chunk:
strategy: fixed
chunk_size: 1000
- rag-embed:
provider: mistral
model: mistral-embed
- rag-store:
store: chromadb
connection: "http://localhost:8000"
collection: knowledge
# Query endpoint
- id: query
trigger:
uri: http-server://0.0.0.0:8080/api/query
steps:
- rag-retrieve:
strategy: fusion
top_k: 15
- rag-postprocess:
steps:
- type: multi-metadata-filter
mode: any
filters:
- key: source
operator: contains
value: "docs"
- key: source
operator: contains
value: "data"
- type: jina-rerank
params:
api_key: "${env:JINA_API_KEY}"
top_n: "8"
- rag-synthesize:
mode: tree-summarize
model:
provider: groq
model: llama-3.3-70b-versatile
chunks_per_level: 3
Quality Evaluation Pipeline¶
Evaluate your RAG pipeline quality with multiple metrics:
workflows:
- id: rag-with-eval
trigger:
uri: http-server://0.0.0.0:8080/api/ask
steps:
- rag-ingest:
parser: text
- rag-chunk:
strategy: semantic
threshold: 0.5
chunk_size: 1024
- rag-embed:
provider: openai
model: text-embedding-3-small
- rag-store:
store: memory
collection: eval-docs
- rag-retrieve:
strategy: simple
top_k: 10
- rag-synthesize:
mode: compact
model:
provider: openai
model: gpt-4o
- rag-eval:
metrics:
- faithfulness
- answer_relevancy
- context_relevancy
model:
provider: openai
model: gpt-4o
- log:
message: "Eval scores: ${header.HelixRAGEvalScores}"
Graph-Enhanced RAG¶
Combine vector retrieval with knowledge graph traversal:
workflows:
- id: graph-rag
trigger:
uri: http-server://0.0.0.0:8080/api/graph-ask
steps:
- rag-ingest:
parser: text
- rag-chunk:
strategy: sentence
chunk_size: 500
- rag-graph-build:
store: in-memory
model:
provider: openai
model: gpt-4o-mini
- rag-embed:
provider: openai
model: text-embedding-3-small
- rag-store:
store: memory
collection: graph-docs
- rag-retrieve:
strategy: simple
top_k: 10
- rag-graph-query:
hops: 2
top_k: 5
- rag-synthesize:
mode: refine
model:
provider: openai
model: gpt-4o
Strategy Selection Guide¶
Choosing a Chunking Strategy¶
| Scenario | Recommended Strategy |
|---|---|
| General text documents | recursive or sentence |
| High-quality RAG (slower) | semantic |
| Source code repositories | code with language specified |
| Technical documentation (Markdown) | markdown with retain_headers: true |
| Web-scraped HTML content | html |
| Need overlapping coverage | sliding_window |
| Auto-merging retrieval | hierarchical with auto_merging retriever |
| Simple, predictable chunks | fixed |
Choosing a Retrieval Strategy¶
| Scenario | Recommended Strategy |
|---|---|
| Simple similarity search | simple |
| Keyword-heavy queries | bm25 |
| Balanced keyword + semantic | fusion or hybrid |
| Diverse results (avoid duplicates) | mmr with lambda: 0.5 |
| Multiple specialized retrievers | ensemble |
| Hierarchical chunks | auto_merging |
| Multi-domain knowledge base | router |
Choosing a Synthesis Mode¶
| Scenario | Recommended Mode |
|---|---|
| Context fits in one LLM call | compact (default) |
| Large context, quality matters | refine |
| Very large context | tree-summarize |
| Each chunk has independent answers | accumulate |
| Simple, fast response | simple-summarize |
Choosing a Vector Store¶
| Scenario | Recommended Store |
|---|---|
| Testing / prototyping | memory |
| Already using PostgreSQL | pgvector |
| Need low latency + caching | redis |
| Simple local development | chromadb |
| AWS ecosystem | opensearch |
| Large-scale production | milvus or pinecone |
| Advanced filtering needs | qdrant |
| Existing Elastic infrastructure | elasticsearch |
OCR & Document Intelligence¶
Helix integrates with 8 OCR backends to extract text from scanned PDFs, images, and complex document layouts. Each backend runs as an external service — Helix communicates via HTTP REST APIs (no cgo required).
Supported Backends¶
| Backend | Service | Best For |
|---|---|---|
docling |
IBM Docling | High-quality PDF/doc conversion with tables |
mineru |
MinerU | Academic papers, complex layouts |
marker |
Marker | Fast, high-fidelity PDF to markdown |
tesseract |
Tesseract OCR | Traditional OCR, scanned images |
surya |
Surya | Multilingual OCR, handwriting |
unstructured |
Unstructured.io | Document parsing with element classification |
azure-doc-intelligence |
Azure Document Intelligence | Enterprise, tables, forms, invoices |
vision-llm |
Any vision LLM (GPT-4o, Claude, Gemini) | Flexible, no external OCR service needed |
OCR with Docling¶
workflows:
- id: ocr-ingest
trigger:
uri: http-server://0.0.0.0:8080/api/ingest
steps:
- rag-ingest:
parser: ocr
ocr:
backend: docling
endpoint: "http://localhost:5000"
output_format: markdown
- rag-chunk:
strategy: markdown
- rag-embed:
provider: openai
model: text-embedding-3-small
- rag-store:
store: pgvector
collection: scanned_docs
OCR with MinerU¶
OCR with Marker¶
OCR with Tesseract¶
OCR with Surya¶
OCR with Unstructured.io¶
- rag-ingest:
parser: ocr
ocr:
backend: unstructured
endpoint: "https://api.unstructured.io"
api_key: "${env:UNSTRUCTURED_API_KEY}"
OCR with Azure Document Intelligence¶
- rag-ingest:
parser: ocr
ocr:
backend: azure-doc-intelligence
endpoint: "https://my-resource.cognitiveservices.azure.com"
api_key: "${env:AZURE_DOC_KEY}"
model: prebuilt-layout
Azure supports async processing — Helix automatically polls the Operation-Location header until the analysis is complete.
OCR with Vision LLM¶
No external OCR service needed — uses any vision-capable LLM to extract text from images:
- rag-ingest:
parser: ocr
ocr:
backend: vision-llm
model:
provider: openai
model: gpt-4o
output_format: markdown
Table Extraction¶
The TableExtractor can isolate markdown tables from OCR output for structured data processing:
workflows:
- id: table-extract
trigger:
uri: http-server://0.0.0.0:8080/api/extract-tables
steps:
- rag-ingest:
parser: ocr
ocr:
backend: azure-doc-intelligence
endpoint: "${env:AZURE_ENDPOINT}"
api_key: "${env:AZURE_DOC_KEY}"
extract_tables: true
OCR Configuration Reference¶
| Parameter | Description | Default |
|---|---|---|
backend |
OCR backend to use | (required) |
endpoint |
Backend service URL | (required for non-vision) |
api_key |
API key for the service | (optional) |
model |
Model name (Azure: prebuilt-layout, prebuilt-invoice, etc.) |
backend-specific |
language |
Language hint for OCR | (optional) |
output_format |
markdown or text |
markdown |
timeout |
HTTP timeout | 120s |
Large File OCR (Streaming)¶
For large scanned PDFs (100MB+), use the streaming OCR reader which avoids loading the entire file in memory:
workflows:
- id: large-pdf-ocr
trigger:
uri: http-server://0.0.0.0:8080/api/ingest-large
steps:
- rag-ingest:
parser: streaming-ocr
ocr:
backend: docling
endpoint: "http://localhost:5000"
streaming:
max_memory_bytes: 52428800 # 50MB — files larger spill to temp disk
page_split: true # Split PDFs into pages, OCR each independently
concurrency: 4 # Parallel page OCR workers
max_file_size: 1073741824 # 1GB hard limit
- rag-chunk:
strategy: markdown
- rag-embed:
provider: openai
model: text-embedding-3-small
- rag-store:
store: pgvector
collection: large_docs
How it works:
- Small files (<
max_memory_bytes): Buffered in memory, sent to OCR as-is - Large files: Spilled to a temp file on disk, then streamed to the OCR backend via
io.Pipemultipart upload (constant memory) - Page splitting (PDFs only): Large PDFs are split at
/Type /Pageboundaries, each page OCR-ed independently with bounded concurrency, results concatenated with<!-- Page N -->markers - Unknown-size streams: Reads up to threshold, spills to temp file if exceeded
- Progress tracking: Optional callback for monitoring long-running OCR jobs
Streaming OCR Configuration:
| Parameter | Description | Default |
|---|---|---|
max_memory_bytes |
Memory threshold before spilling to temp file | 50MB |
page_split |
Split PDFs into pages for parallel OCR | false |
concurrency |
Number of parallel page workers | 4 |
max_file_size |
Maximum allowed file size (0 = unlimited) | 0 |
Partial failure handling: When page_split is enabled, individual page failures don't abort the entire document. Successfully OCR-ed pages are combined, and the failed_pages metadata field reports how many pages failed.
Query Transformation¶
Query transformation improves retrieval quality by rewriting or expanding the user's query before embedding and searching. Helix supports 4 strategies.
HyDE (Hypothetical Document Embeddings)¶
Generates a hypothetical answer to the query, then embeds that answer for retrieval. Works best when the expected answer has different vocabulary than the query.
workflows:
- id: hyde-rag
trigger:
uri: http-server://0.0.0.0:8080/api/ask
steps:
- rag-retrieve:
strategy: hyde
top_k: 10
hyde:
model:
provider: openai
model: gpt-4o
embedding:
provider: openai
model: text-embedding-3-small
- rag-synthesize:
mode: compact
model:
provider: openai
model: gpt-4o
MultiQuery¶
Generates N alternative phrasings of the query, retrieves for each, and merges results using Reciprocal Rank Fusion.
- rag-retrieve:
strategy: multi_query
top_k: 10
multi_query:
model:
provider: openai
model: gpt-4o
num_queries: 3
embedding:
provider: openai
model: text-embedding-3-small
StepBack Prompting¶
Generates a more abstract, broader query alongside the original. Retrieves for both and merges results. Good for specific or narrow queries.
- rag-retrieve:
strategy: step_back
top_k: 10
step_back:
model:
provider: openai
model: gpt-4o
embedding:
provider: openai
model: text-embedding-3-small
SubQuestion Decomposition¶
Breaks complex queries into simpler sub-questions, retrieves for each independently, and merges all results.
- rag-retrieve:
strategy: sub_question
top_k: 10
sub_question:
model:
provider: openai
model: gpt-4o
embedding:
provider: openai
model: text-embedding-3-small
Choosing a Query Transformation Strategy¶
| Scenario | Recommended Strategy |
|---|---|
| Query uses different words than documents | hyde |
| Query is ambiguous or has multiple interpretations | multi_query |
| Query is too specific or narrow | step_back |
| Query is complex with multiple aspects | sub_question |
| Simple, direct queries | No transformation needed (use simple retrieval) |