DS DevShelfHub Projects · AI tools
Cheatsheets / Haystack
Cheatsheet · AI frameworks

Haystack: Pipelines, Components and RAG Reference Guide

By DevShelfHub

Components, pipelines, document stores, retrievers, generators, agents — the deepset framework as a one-page reference.

79 items 6 min Pipelines Components Stores

Start hereQuick start · 6 you’ll reach for daily

Build pipelinep = Pipeline()
Add componentp.add_component("llm", OpenAIGenerator())
Wire itp.connect("a.out", "b.in")
Runp.run({"a": {…}})
Savep.dumps() / p.dump(yaml_path)
Servehayhooks pipeline deploy …

Target versions · paceVersions

Targets: haystack-ai ≥ 2.10 python ≥ 3.9 hayhooks ≥ 0.6

Haystack 2.x is a clean rewrite of the 1.x “farm-haystack” package — install haystack-ai, not farm-haystack. Components, pipelines, and connections replace the old node / store / model trio. Integrations live in haystack_integrations.* sub-packages. Names current as of May 2026.

install · env · serveSetup

bash
# Core + the integrations you'll need
pip install haystack-ai                          # the v2.x package (do NOT install old "farm-haystack")
pip install chroma-haystack                      # vector store
pip install -q "sentence-transformers"           # local embedder

# Provider-specific packages
pip install openai-haystack
pip install anthropic-haystack
pip install cohere-haystack

# env
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-...
export HAYSTACK_TELEMETRY_ENABLED=false          # opt out of usage telemetry

# Run a saved pipeline as a service (Hayhooks)
pip install hayhooks
hayhooks pipeline deploy ./pipeline.yaml

where things liveCommon imports

Core lives in haystack (the haystack-ai distribution). Provider + store integrations sit under haystack_integrations.*.

from haystack import Pipeline, component, DocumentPipeline, decorator, doc class.
from haystack.dataclasses import ChatMessage, ByteStream, StreamingChunkWire-level types.
from haystack.components.converters import TextFileToDocument, PyPDFToDocument, HTMLToDocumentSource → Document.
from haystack.components.preprocessors import DocumentCleaner, DocumentSplitterCleaning & chunking.
from haystack.components.embedders import SentenceTransformersDocumentEmbedder, SentenceTransformersTextEmbedderLocal embedders (doc & query).
from haystack.components.embedders import OpenAIDocumentEmbedder, OpenAITextEmbedderOpenAI variants.
from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever, InMemoryBM25RetrieverDefault retrievers.
from haystack.components.builders import PromptBuilder, ChatPromptBuilder, AnswerBuilderPrompt + answer assembly.
from haystack.components.generators import OpenAIGenerator, HuggingFaceLocalGeneratorPlain-text generators.
from haystack.components.generators.chat import OpenAIChatGenerator, AnthropicChatGeneratorChat generators.
from haystack.components.writers import DocumentWriterPersist docs to a store.
from haystack.components.agents import AgentTool-using agent.
from haystack.tools import ToolWrap a function as a tool.
from haystack.document_stores.in_memory import InMemoryDocumentStoreDefault store.
from haystack_integrations.document_stores.chroma import ChromaDocumentStoreExternal store integration.

the unit of workComponents

@componentClass decorator. Makes a component pipeline-compatible.
@component.output_types(answer=str)Declare output names + types. Required.
def run(self, x: str) -> dict:The single entry point. Returns a dict matching output_types.
def to_dict(self) / from_dict(cls, d)Serialisation hooks for YAML save / load.
def warm_up(self):One-time setup (model load). Called by pipeline.run.
async def run_async(self, …) -> dict:Async variant. Used by AsyncPipeline.
A pipeline is a DAG of components. Inputs flow via socket names — a.documents connects to b.documents. Sockets match by name + type when you call pipeline.connect.

wire components togetherPipelines

p = Pipeline()New empty pipeline.
p.add_component("name", ComponentInstance())Register a component under a name.
p.connect("a.out", "b.in")Wire one output socket to one input socket.
p.connect("a", "b")Shorthand when sockets are unambiguous.
p.run({"a": {"arg": val}})Run. Inputs are addressed by component name + socket.
p.run_async({…})Async pipeline execution (with AsyncPipeline).
p.draw("graph.png")Render the DAG to an image (requires graphviz).
p.dumps() / p.dump(path)Serialise to YAML.
Pipeline.loads(yaml_str) / Pipeline.load(path)Reload.
p.show_inputs() / p.show_outputs()List unconnected sockets. Handy when wiring up.
p.warm_up()Pre-load all components. run calls it automatically.
One indexing pipeline, one query pipeline. Don’t put writers and generators in the same graph. Keep ingestion offline and the query path lean.

where documents liveDocument stores

InMemoryDocumentStore()Process-local. Default for dev + tests.
ChromaDocumentStore(persist_path="./chroma")Local persistent. chroma-haystack.
QdrantDocumentStore(url=…, index=…)Self-host / cloud.
PineconeDocumentStore(index=…)Managed.
PgvectorDocumentStore(connection_string=…, table_name=…)Postgres + pgvector.
WeaviateDocumentStore(client=…)Weaviate.
store.write_documents(docs, policy=DuplicatePolicy.OVERWRITE)Idempotent ingest.
store.filter_documents(filters={"src": "wiki"})Metadata filter.
store.delete_documents(ids=[…])Remove by id.

find the right docsRetrievers

InMemoryEmbeddingRetriever(document_store=…, top_k=4)Dense retrieval against in-memory store.
InMemoryBM25Retriever(document_store=…, top_k=10)Sparse / lexical.
ChromaEmbeddingRetriever(document_store=…, top_k=4)Dense over Chroma.
QdrantHybridRetriever(document_store=…, top_k=10)Dense + sparse via Qdrant’s hybrid mode.
FilterRetriever(document_store=…)Metadata-only retrieval. No embedding step.
SentenceTransformersDiversityRanker()Re-rank for diversity (MMR-style).
SentenceTransformersSimilarityRanker(model=…)Cross-encoder rerank.
CohereRanker(top_k=3)Provider rerank.
MetaFieldRanker(meta_field="date")Sort by metadata.

Hybrid pattern

Run a BM25 retriever and an embedding retriever in parallel, then merge with DocumentJoiner(join_mode="reciprocal_rank_fusion") and rerank with a cross-encoder. Strong default for FAQ-shaped corpora.

call the modelGenerators

OpenAIGenerator(model="gpt-4o-mini")Plain text completion. Input: prompt → output: replies.
OpenAIChatGenerator(model=…)Preferred Chat-format. Use with ChatMessage.
AnthropicChatGenerator(model="claude-sonnet-4-6")Anthropic chat.
HuggingFaceLocalGenerator(model="HuggingFaceH4/zephyr-7b-beta")Local HF model.
HuggingFaceAPIGenerator(api_type="text_generation_inference")Remote TGI / HF Inference API.
PromptBuilder(template="Q: ")Jinja-style template. Inputs become render vars.
ChatPromptBuilder(template=[ChatMessage.from_system(…)])Chat-format prompt.
AnswerBuilder()Wrap LLM output + source docs into Answer objects.
generation_kwargs={"temperature": 0.2}Per-call provider params.
streaming_callback=fnCallback per StreamingChunk.

ETL into a storeIndexing pipeline

Compose converter → cleaner → splitter → embedder → writer. The output of one socket flows to the next as Document lists.

python
from pathlib import Path
from haystack import Pipeline
from haystack.components.converters import TextFileToDocument, PyPDFToDocument
from haystack.components.preprocessors import DocumentCleaner, DocumentSplitter
from haystack.components.writers import DocumentWriter
from haystack.components.embedders import SentenceTransformersDocumentEmbedder
from haystack_integrations.document_stores.chroma import ChromaDocumentStore

store = ChromaDocumentStore(persist_path="./chroma")

idx = Pipeline()
idx.add_component("txt",    TextFileToDocument())
idx.add_component("clean",  DocumentCleaner())
idx.add_component("split",  DocumentSplitter(split_by="word", split_length=180, split_overlap=30))
idx.add_component("embed",  SentenceTransformersDocumentEmbedder(model="BAAI/bge-small-en-v1.5"))
idx.add_component("write",  DocumentWriter(document_store=store))

idx.connect("txt.documents",   "clean.documents")
idx.connect("clean.documents", "split.documents")
idx.connect("split.documents", "embed.documents")
idx.connect("embed.documents", "write.documents")

idx.run({"txt": {"sources": list(Path("./data").glob("*.txt"))}})

retrieve + generateRAG pipeline

python
from haystack import Pipeline
from haystack.components.embedders import SentenceTransformersTextEmbedder
from haystack.components.builders import PromptBuilder
from haystack.components.generators import OpenAIGenerator
from haystack_integrations.components.retrievers.chroma import ChromaEmbeddingRetriever

TEMPLATE = """
Answer using only the context. If unsure, say so.

Context:
{% for d in documents %}- {{ d.content }}
{% endfor %}

Question: {{ question }}
Answer:""".strip()

rag = Pipeline()
rag.add_component("embed",   SentenceTransformersTextEmbedder(model="BAAI/bge-small-en-v1.5"))
rag.add_component("retrieve", ChromaEmbeddingRetriever(document_store=store, top_k=4))
rag.add_component("prompt",  PromptBuilder(template=TEMPLATE))
rag.add_component("llm",     OpenAIGenerator(model="gpt-4o-mini"))

rag.connect("embed.embedding",      "retrieve.query_embedding")
rag.connect("retrieve.documents",   "prompt.documents")
rag.connect("prompt.prompt",        "llm.prompt")

out = rag.run({
    "embed":  {"text": "What is the WFH policy?"},
    "prompt": {"question": "What is the WFH policy?"},
})
print(out["llm"]["replies"][0])
SentenceTransformersTextEmbedderSingle-string embedder. For queries.
SentenceTransformersDocumentEmbedderList-of-Document embedder. For ingestion.
PromptBuilder template variablesUse Jinja: {{ documents }}, {% for d in docs %}.
AnswerBuilder()Pair replies with their source documents into Answer.

tools + reasoning loopAgents

Tool.from_function(function=fn, name=…, description=…)Wrap a Python function. Docstring + types become schema.
ComponentTool(component=…)Expose a whole pipeline / component as a tool.
Agent(chat_generator=…, tools=[…])Tool-using agent. Wraps any chat generator.
Agent(…, system_prompt=…)Pin behaviour.
Agent(…, max_agent_steps=8)Cap reasoning loops.
Agent(…, exit_conditions=["text"])Stop on first plain-text reply (vs. waiting for a tool call).
agent.run(messages=[ChatMessage…])Sync entry.
agent.run_async(…)Async entry.
result["messages"]Full message trace. Last one is the final reply.
python
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.tools import Tool
from haystack.dataclasses import ChatMessage

def get_weather(city: str) -> str:
    """Return current weather for a city."""
    return f"{city}: 22 C, clear."

weather = Tool.from_function(
    function=get_weather,
    name="get_weather",
    description="Look up current weather for a city.",
)

agent = Agent(
    chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
    tools=[weather],
    system_prompt="You are a precise assistant. Use tools when helpful.",
    exit_conditions=["text"],            # stop on first plain-text reply
    max_agent_steps=8,
)

result = agent.run(messages=[ChatMessage.from_user("What's the weather in Berlin?")])
print(result["messages"][-1].text)

index then askEnd-to-end · Minimal RAG

Two pipelines (index + query) over an in-memory store, with sentence-transformer embeddings and an OpenAI generator. Drop in a real store + your docs and you have a working RAG.

python
from pathlib import Path
from haystack import Pipeline
from haystack.components.converters import TextFileToDocument
from haystack.components.preprocessors import DocumentSplitter
from haystack.components.embedders import (
    SentenceTransformersDocumentEmbedder, SentenceTransformersTextEmbedder,
)
from haystack.components.writers import DocumentWriter
from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever
from haystack.components.builders import PromptBuilder
from haystack.components.generators import OpenAIGenerator
from haystack.document_stores.in_memory import InMemoryDocumentStore

store = InMemoryDocumentStore()

# Index
idx = Pipeline()
idx.add_component("read",  TextFileToDocument())
idx.add_component("split", DocumentSplitter(split_by="word", split_length=180))
idx.add_component("embed", SentenceTransformersDocumentEmbedder(model="BAAI/bge-small-en-v1.5"))
idx.add_component("write", DocumentWriter(document_store=store))
idx.connect("read.documents", "split.documents")
idx.connect("split.documents", "embed.documents")
idx.connect("embed.documents", "write.documents")
idx.run({"read": {"sources": list(Path("data").glob("*.txt"))}})

# Query
rag = Pipeline()
rag.add_component("q_emb",   SentenceTransformersTextEmbedder(model="BAAI/bge-small-en-v1.5"))
rag.add_component("retrieve", InMemoryEmbeddingRetriever(document_store=store, top_k=4))
rag.add_component("prompt",  PromptBuilder(template="Q: {{q}}\nDocs: {{docs}}\nA:"))
rag.add_component("llm",     OpenAIGenerator(model="gpt-4o-mini"))
rag.connect("q_emb.embedding", "retrieve.query_embedding")
rag.connect("retrieve.documents", "prompt.docs")
rag.connect("prompt.prompt", "llm.prompt")

print(rag.run({"q_emb": {"text": "Summarise the handbook."},
               "prompt": {"q": "Summarise the handbook."}})["llm"]["replies"][0])

Best practiceGood to know

Use the Document embedder at ingest, Text embedder at query. They differ. Document variants embed a list of Document; text variants take a plain string. Mixing them up is the most common runtime TypeError.
Serialise pipelines to YAML for prod. p.dump("pipeline.yaml") + hayhooks pipeline deploy gives you a deployable HTTP service without a custom server.
Hybrid retrieval is one wiring change. Add a BM25 retriever in parallel with the embedding retriever, then a DocumentJoiner with join_mode="reciprocal_rank_fusion". Quick recall win.

Common trapsWatch out for

Don’t install farm-haystack. That’s Haystack 1.x — archived. The 2.x API documented here lives in haystack-ai. Mixing the two in one env breaks imports in ways that look like missing components.
Socket names must match exactly. retrieve.documentsprompt.documents works only if PromptBuilder.documents is a declared input variable in the template. Typos surface only at connect time.
PromptBuilder is Jinja with strict undefined. Referencing a variable you forgot to pass raises at runtime. Use p.show_inputs() to see what the builder is waiting for.

Go deeperSee also

Haystack FAQ

What is Haystack used for?

Haystack is an open-source Python framework by deepset for building production-grade search and LLM pipelines. It provides a pipeline API to connect components — document stores, retrievers, generators, and agents — without writing infrastructure glue code.

What is the difference between Haystack 1.x and 2.x?

Haystack 2.x is a clean rewrite. Install haystack-ai, not the old farm-haystack. The new API replaces nodes and models with composable Components and Pipelines; connections are explicit wires between component outputs and inputs.

What document stores does Haystack support?

Haystack ships integrations for Elasticsearch, OpenSearch, Weaviate, Qdrant, Pinecone, Chroma, pgvector, Milvus, and an in-memory store. Install the matching haystack_integrations sub-package for whichever backend you choose.

Is Haystack free to use?

Yes. Haystack is open source under the Apache 2.0 license. The core library and all official integrations are free. deepset offers a paid cloud platform (deepset Cloud) for teams who want managed deployments, but the framework itself has no cost.

How do Haystack pipelines work?

A pipeline is a directed graph of components. You add each component with p.add_component(name, instance), then wire outputs to inputs with p.connect('a.out', 'b.in'). Calling p.run(inputs) executes the graph and returns the outputs of all terminal components.