Cheat Sheet intermediate · 8 min read

Haystack Cheat Sheet — RAG & NLP Pipelines — Haystack Refere

version 2.x

Production RAG & NLP pipelines, end-to-end

OPENAI_API_KEY
install pip install haystack-ai
core imports
python
from haystack import Pipeline, Document
from haystack.components.builders import PromptBuilder
from haystack.components.generators import OpenAIGenerator
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
Mental model

Component-based pipelines connecting LLMs, retrievers, and document stores.

Like a Blender 3D node graph editor: each node is a component (retriever, LLM, filter), edges are data flow, and the whole graph is your pipeline. Wire them together visually in code, hit play (pipeline.run()), get results.

Core Patterns

01 Basic RAG Pipeline
Retrieve docs, pass to LLM, generate answer
python
from haystack import Pipeline, Document
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack.components.builders import PromptBuilder
from haystack.components.generators import OpenAIGenerator
import os

# Create document store
doc_store = InMemoryDocumentStore()
docs = [
    Document(content="Haystack is a framework for NLP pipelines."),
    Document(content="Pipelines are DAGs of components.")
]
doc_store.write_documents(docs)

# Build pipeline
pipeline = Pipeline()
retriever = InMemoryBM25Retriever(document_store=doc_store)
prompt_builder = PromptBuilder(
    template="""Answer based on these docs: {% for doc in documents %}
{{ doc.content }}{% endfor %}
Question: {{ query }}"""
)
generator = OpenAIGenerator(
    api_key=os.environ["OPENAI_API_KEY"],
    model="gpt-4o"
)

# Connect components
pipeline.add_component("retriever", retriever)
pipeline.add_component("prompt_builder", prompt_builder)
pipeline.add_component("generator", generator)

pipeline.connect("retriever.documents", "prompt_builder.documents")
pipeline.connect("prompt_builder.prompt", "generator.prompt")

# Run
result = pipeline.run({
    "retriever": {"query": "What is Haystack?"},
    "prompt_builder": {"query": "What is Haystack?"}
})
print(result["generator"]["replies"][0])
output Haystack is a framework for building NLP pipelines with reusable components.
Connections use exact output key names from previous components. Typos silently fail at runtime. Always match component output keys to input keys.
02 Custom Component
Wrap custom logic into a reusable pipeline component
python
from haystack import component
import os
from openai import OpenAI

@component
class CustomSummarizer:
    def __init__(self, max_length: int = 100):
        self.max_length = max_length
        self.client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
    
    @component.output_types(summary=str)
    def run(self, text: str):
        """Summarize text using OpenAI."""
        response = self.client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "user", "content": f"Summarize in {self.max_length} chars: {text}"}
            ]
        )
        return {"summary": response.choices[0].message.content}

# Use in pipeline
pipeline = Pipeline()
summarizer = CustomSummarizer(max_length=50)
pipeline.add_component("summarizer", summarizer)

result = pipeline.run({"summarizer": {"text": "Long document text here..."}})
print(result["summarizer"]["summary"])
@component.output_types() decorator is required. Missing it causes attribute errors at runtime. Output dict keys must match the decorator exactly.
03 Multi-Turn Q&A with Memory
Build conversational Q&A that remembers context
python
from haystack import Pipeline, Document
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack.components.builders import PromptBuilder
from haystack.components.generators import OpenAIGenerator
from haystack.components.joiners import DocumentJoiner
import os

# Setup
doc_store = InMemoryDocumentStore()
doc_store.write_documents([
    Document(content="Python is a programming language."),
    Document(content="RAG stands for Retrieval-Augmented Generation.")
])

pipeline = Pipeline()
retriever = InMemoryBM25Retriever(document_store=doc_store)
prompt = PromptBuilder(
    template="""Chat history: {{ chat_history }}
Docs: {% for doc in documents %}{{ doc.content }}{% endfor %}
User: {{ query }}
Assistant:"""
)
generator = OpenAIGenerator(api_key=os.environ["OPENAI_API_KEY"], model="gpt-4o")

pipeline.add_component("retriever", retriever)
pipeline.add_component("prompt", prompt)
pipeline.add_component("generator", generator)

pipeline.connect("retriever.documents", "prompt.documents")
pipeline.connect("prompt.prompt", "generator.prompt")

# Run conversation
chat_history = ""
queries = ["What is Python?", "Is RAG related to Python?"]

for query in queries:
    result = pipeline.run({
        "retriever": {"query": query},
        "prompt": {"query": query, "chat_history": chat_history}
    })
    response = result["generator"]["replies"][0]
    chat_history += f"User: {query}\nAssistant: {response}\n"
    print(f"Q: {query}\nA: {response}\n")
Chat history is NOT persisted by Haystack. You must manually build and pass it between pipeline.run() calls. Without this, each turn forgets context.
04 Document Processing & Chunking
Process raw docs, split into chunks, embed and store
python
from haystack import Pipeline, Document
from haystack.components.document_splitters import DocumentSplitter, SentenceSplitter
from haystack.components.writers import DocumentWriter
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.embedders import OpenAIDocumentEmbedder
import os

# Create document store
doc_store = InMemoryDocumentStore()

# Build pipeline
pipeline = Pipeline()

# Load or create documents
docs = [
    Document(content="This is a long document. " * 100)  # Simulate long text
]

# Add components
splitter = SentenceSplitter(sentences_per_page=5)
embedder = OpenAIDocumentEmbedder(
    api_key=os.environ["OPENAI_API_KEY"],
    model="text-embedding-3-small"
)
writer = DocumentWriter(document_store=doc_store)

pipeline.add_component("splitter", splitter)
pipeline.add_component("embedder", embedder)
pipeline.add_component("writer", writer)

pipeline.connect("splitter.documents", "embedder.documents")
pipeline.connect("embedder.documents", "writer.documents")

# Run
pipeline.run({"splitter": {"documents": docs}})
print(f"Stored {doc_store.document_count()} chunks")
DocumentSplitter returns a list of Documents. SentenceSplitter requires pre-splitting by sentence. Always test chunk sizes against your retriever's context window.

Component API Reference

Method / Property Description Returns
Pipeline.add_component(name, component) Add a component to the pipeline. name is a string ID, component is a @component instance or built-in. None
Pipeline.connect(from_output, to_input) Connect component outputs to inputs. Format: 'component_name.output_key' → 'component_name.input_key'. None
Pipeline.run(data_dict) Execute pipeline. data_dict has structure {component_name: {input_key: value}}. Returns dict with same structure. dict
@component decorator Mark a class as reusable. Decorate run() with @component.output_types(key1=type, key2=type). Required for custom components. Component class
DocumentStore.write_documents(docs) Persist Document objects. InMemoryDocumentStore keeps in RAM. PineconeDocumentStore, WeaviateDocumentStore use vector DBs. None
InMemoryBM25Retriever.run(query, top_k) Lexical search in document store. Returns {documents: [Document, ...]}. dict

Common Retriever Parameters

Retriever Configuration

ParameterTypeDefaultNotes
top_k int 10 Number of documents to retrieve. Increase for broad recall, decrease for speed.
filters dict None Filter documents by metadata. Example: {'field': {'$eq': 'value'}}.
scale_score bool True (DPR), False (BM25) Normalize scores to 0-1. BM25 scores are unbounded; DPR is already normalized.
model str gpt-4o Use current model names: gpt-4o, gpt-4o-mini, gpt-4.1, claude-opus-4, claude-3-5-sonnet-20241022.
api_key str os.environ['OPENAI_API_KEY'] Never hardcode. Use environment variables for all API keys.
temperature float 0.7 0.0 = deterministic, 1.0 = random. Use 0.0 for Q&A, 0.7 for creative tasks.
max_tokens int None Max output length. None = model default (~4k for gpt-4o). Set for cost control.

Common Errors & Fixes

01 PipelineValidationError: 'missing_input' not found in component outputs

Cause: Connection typo or component output key doesn't match input key. Pipeline validation catches this before run().

Fix:
python
Check exact output key from previous component. Use pipeline.to_dict() to debug connections:

print(pipeline.to_dict())
# Check 'connections' section for typos
# Verify component output_types() decorator matches connection keys
02 AttributeError: 'Component' object has no attribute 'run'

Cause: Custom component missing @component decorator or run() method not defined.

Fix:
python
Ensure decorator is applied:

@component
class MyComponent:
    @component.output_types(result=str)
    def run(self, text: str):
        return {"result": text.upper()}

# Not valid: run() without @component.output_types()
# Not valid: class without @component
03 DocumentNotFoundError: No documents with ID 'xyz' in document store

Cause: Retrieving or updating docs that don't exist. InMemoryDocumentStore requires write_documents() before retrieval.

Fix:
python
Always write documents before querying:

doc_store = InMemoryDocumentStore()
docs = [Document(content="text")]
doc_store.write_documents(docs)  # Must do this first

retriever = InMemoryBM25Retriever(document_store=doc_store)
result = retriever.run(query="search term")
04 OpenAIError: Invalid model name 'gpt-3.5-turbo'

Cause: Using deprecated or incorrect model names. Haystack passes model names directly to OpenAI SDK.

Fix:
python
Use current model names only:

# Correct
generator = OpenAIGenerator(model="gpt-4o", api_key=os.environ["OPENAI_API_KEY"])
generator = OpenAIGenerator(model="gpt-4o-mini", api_key=os.environ["OPENAI_API_KEY"])

# Wrong
generator = OpenAIGenerator(model="gpt-3.5-turbo")  # Deprecated
generator = OpenAIGenerator(model="gpt-4-turbo")   # Deprecated
05 KeyError: 'component_name' when accessing pipeline.run() result

Cause: Component not in pipeline or result key doesn't match component name used in add_component().

Fix:
python
Use exact component name from add_component():

pipeline.add_component("my_generator", generator)
result = pipeline.run({"my_generator": {"prompt": "..."}})  # Exact name

# Returns result["my_generator"]["replies"] not result["generator"][...]

Production Gotchas

InMemoryDocumentStore loses all data on restart

InMemoryDocumentStore is for development/testing only. For production, use PineconeDocumentStore, WeaviateDocumentStore, or QdrantDocumentStore. These persist across restarts and scale to millions of documents. If using in-memory for demos, expect data loss on every pipeline restart.

Embeddings are not automatically created for retrieval

InMemoryBM25Retriever uses BM25 lexical search (no embeddings needed). For semantic search, you must add an OpenAIDocumentEmbedder in your document processing pipeline AND use a vector-aware retriever (HybridRetriever, DensePassageRetriever). Forgetting embeddings means retrieval falls back to keyword matching with poor results on semantic queries.

Pipeline.run() returns nested dict structure: not flat

Results are {component_name: {output_key: value}}. Many devs expect flat output. Always nest access: result["generator"]["replies"][0], not result["replies"][0]. Debug with json.dumps(result, indent=2) to see structure.

Custom components with @component must have matching input/output keys

If you define @component.output_types(summary=str), you MUST return {"summary": value}. If you return {"text": value} instead, pipeline.run() will silently fail or throw a KeyError. Always double-check dict keys match decorator exactly.

PromptBuilder templates require exact variable names

If template has {{ documents }}, input dict must have {"prompt_builder": {"documents": [...]}}. If you pass {"docs": [...]} instead, PromptBuilder throws a template rendering error. Always match template {{ variable }} names to pipeline input keys exactly.

Document de-duplication is not automatic

If you write the same document multiple times, InMemoryDocumentStore will store duplicates. No built-in dedup. Use document IDs or external dedup logic before write_documents(). For large document ingestion, batch writes and validate uniqueness first.

Document Store Comparison

StorePersistenceSearch TypeScalingSetup Time
InMemoryDocumentStoreRAM only (lost on restart)BM25 lexicalUp to ~100k docsInstant
PineconeDocumentStoreCloud persistentDense vector + sparse hybridMillions of docs, serverless5 min (API key)
WeaviateDocumentStoreSelf-hosted or cloudDense vector, hybrid, keywordMillions of docs10 min (container)
QdrantDocumentStoreSelf-hosted or cloudDense vectorBillions of docs10 min (container)
Verified 2026-04 · v2.x · gpt-4o, gpt-4o-mini, gpt-4.1, claude-opus-4, claude-3-5-sonnet-20241022, text-embedding-3-small
Verify ↗

Community Notes

No notes yetBe the first to share a version-specific fix or tip.