Haystack 3.0.0 | Haystack
Get Enterprise Support
Get Started
🚀 Day 1/5: Haystack 3.0 is here. Follow Launch Week for 5 days of new drops
Haystack 3.0.0
Haystack 3.0.0
Check on Github
⭐️ Highlights
Haystack 3.0 is a major release for building production-grade agents with full control and flexibility . It ships a wave of new capabilities: a more capable Agent with hooks and first-class skills, built-in run introspection, first-class async for serving, a leaner core, and safer pipeline loading.
A few small, intentional breaking changes come with it but our<br>Migration Guide and Upgrading to Haystack 3.0 make it easy to upgrade.
⚠️ marks a breaking change. See Upgrade Notes below for migration details.
🤖 A more capable, adaptable Agent
The Agent gained a general-purpose hooks system and now owns tool execution end to end (the standalone ToolInvoker has been removed ). Hooks at before_run, before_llm, before_tool, after_tool, on_exit, and after_run let you shape behavior, enforce guardrails, and add human-in-the-loop checkpoints without touching the agent’s internals:
from haystack.components.agents import Agent<br>from haystack.components.generators.chat import OpenAIChatGenerator<br>from haystack.hooks import hook
@hook<br>def audit_tool_calls(state):<br>pending = state.data["messages"][-1].tool_calls<br>print(f"about to run: {[tc.tool_name for tc in pending]}")
agent = Agent(<br>chat_generator=OpenAIChatGenerator(),<br>tools=[...],<br>hooks={"before_tool": [audit_tool_calls]},
Built on this foundation:
Skills as first-class citizens — SkillToolset discovers skills through progressive disclosure , so the model sees only names and one-line descriptions until it loads one, keeping context lean.
Dynamic tool selection at runtime — pass tools=... to run / run_async so one reusable Agent serves different teams, tenants, and tasks.
Native async tools — @tool routes async def callables to a Tool’s new async_function.
Human-in-the-Loop is now a before_tool hook (ConfirmationHook). ⚠️
Tool result offloading (ToolResultOffloadHook) writes large results to a store and leaves a compact pointer in the conversation.
🔎 Built-in introspection and observability
Agents now expose step_count, token_usage, and tool_call_counts as built-in state. React to them to compact context when the window fills, cap runaway tool loops, or route to a cheaper approach past a budget threshold. Tracing now emits dedicated step-level spans (haystack.agent.step with nested .llm / .tool children) tagged with the tools actually used, so you can see exactly what your agent did. ⚠️
⚡ A core built for serving: first-class async
Pipeline and AsyncPipeline are now one class — no more switching between them. A single Pipeline exposes run, run_async, run_async_generator, and stream; serialized pipelines, SuperComponents, and PipelineTools load as Pipeline instances. ⚠️
from haystack import Pipeline
pipeline = Pipeline()<br>result = pipeline.run(data) # synchronous<br>result = await pipeline.run_async(data) # asynchronous
Concurrent tool calls and token-by-token streaming come standard: Pipeline.stream() yields StreamingChunks as they’re produced and exposes the final output on handle.result — the lower latency and streaming UX serving apps need. Components and Pipeline also get a symmetric lifecycle — warm_up to acquire, close to release — so long-running services don’t leak connections, GPU memory, or file handles. External resources (and API keys) are now created at warm_up, not in __init__. ⚠️
🧱 A leaner, faster-moving framework ⚠️
Legacy Generators are gone, haystack-experimental is no longer a core dependency, and 30 components now live in independently released packages in<br>haystack-core-integrations: Sentence Transformers, local & API Hugging Face, Whisper, spaCy/langdetect, Tika and Azure OCR, SerperDev/SearchApi, OpenAPI connectors, and the Datadog/OpenTelemetry tracers. As a result, there are fewer transitive dependencies to audit and we can ship independent of the core release cycle. Migration is an install plus an import change:
# pip install sentence-transformers-haystack<br># Before: from haystack.components.embedders import SentenceTransformersTextEmbedder<br>from haystack_integrations.components.embedders.sentence_transformers import SentenceTransformersTextEmbedder
The full old → new import table is in<br>MIGRATION.md.
💬 Chat Generators everywhere (legacy Generators removed) ⚠️
OpenAIGenerator, AzureOpenAIGenerator, HuggingFaceAPIGenerator, and HuggingFaceLocalGenerator have been removed in favor of their Chat Generator counterparts. To ease the switch, all Chat Generators now accept a plain str for messages:
# Before<br>from haystack.components.generators import OpenAIGenerator<br>text = OpenAIGenerator().run("What is NLP?")["replies"][0] # str
# After<br>from haystack.components.generators.chat import OpenAIChatGenerator<br>reply = OpenAIChatGenerator().run("What is NLP?")["replies"][0] # ChatMessage<br>text = reply.text # str;...