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, Document
Pipeline, decorator, doc class.
from haystack.dataclasses import ChatMessage, ByteStream, StreamingChunk
Wire-level types.
from haystack.components.converters import TextFileToDocument, PyPDFToDocument, HTMLToDocument
Source → Document.
from haystack.components.preprocessors import DocumentCleaner, DocumentSplitter
Cleaning & chunking.
from haystack.components.embedders import SentenceTransformersDocumentEmbedder, SentenceTransformersTextEmbedder
Local embedders (doc & query).
from haystack.components.embedders import OpenAIDocumentEmbedder, OpenAITextEmbedder
OpenAI variants.
from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever, InMemoryBM25Retriever
Default retrievers.
from haystack.components.builders import PromptBuilder, ChatPromptBuilder, AnswerBuilder
Prompt + answer assembly.
from haystack.components.generators import OpenAIGenerator, HuggingFaceLocalGenerator
Plain-text generators.
from haystack.components.generators.chat import OpenAIChatGenerator, AnthropicChatGenerator
Chat generators.
from haystack.components.writers import DocumentWriter
Persist docs to a store.
from haystack.components.agents import Agent
Tool-using agent.
from haystack.tools import Tool
Wrap a function as a tool.
from haystack.document_stores.in_memory import InMemoryDocumentStore
Default store.
from haystack_integrations.document_stores.chroma import ChromaDocumentStore
External store integration.
the unit of workComponents
@component
Class 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.
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.
Wrap LLM output + source docs into Answer objects.
generation_kwargs={"temperature": 0.2}
Per-call provider params.
streaming_callback=fn
Callback 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])
SentenceTransformersTextEmbedder
Single-string embedder. For queries.
SentenceTransformersDocumentEmbedder
List-of-Document embedder. For ingestion.
PromptBuilder template variables
Use Jinja: {{ documents }}, {% for d in docs %}.
AnswerBuilder()
Pair replies with their source documents into Answer.
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.documents → prompt.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.
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.