From 680b19b5765e32acbefdd241970e3fc1fb46fd40 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Fri, 8 May 2026 14:50:04 +0200 Subject: [PATCH 01/12] Make separate multi agent docs --- .../docs/concepts/multi-agent-systems.mdx | 203 ++++++++++++++++++ .../pipeline-components/agents-1/agent.mdx | 85 +------- docs-website/sidebars.js | 1 + 3 files changed, 206 insertions(+), 83 deletions(-) create mode 100644 docs-website/docs/concepts/multi-agent-systems.mdx diff --git a/docs-website/docs/concepts/multi-agent-systems.mdx b/docs-website/docs/concepts/multi-agent-systems.mdx new file mode 100644 index 0000000000..b9f216692e --- /dev/null +++ b/docs-website/docs/concepts/multi-agent-systems.mdx @@ -0,0 +1,203 @@ +--- +title: "Multi-Agent Systems" +id: multi-agent-systems +slug: "/multi-agent-systems" +description: "Learn how to build multi-agent systems in Haystack by spawning agents as tools. Use the @tool decorator or ComponentTool to connect specialist agents to a coordinator." +--- + +# Multi-Agent Systems + +Multi-agent systems let you compose multiple `Agent` instances into larger architectures where a **coordinator** agent delegates to **specialist** agents. +Each specialist focuses on a specific task with its own tools and system prompt — the coordinator plans and routes work without needing to know how each task gets done. + +Spawning agents as tools is useful when: + +- A task is too broad for a single agent to handle reliably, +- You want to isolate different capabilities into focused, reusable agents, +- You need to keep the coordinator's context lean for better decisions and lower token usage. + +In Haystack, you spawn a specialist agent as a tool using either the `@tool` decorator (recommended) or `ComponentTool`. + +## Converting an Agent to a Tool + +### `@tool` Decorator (Recommended) + +Wrapping an agent inside a `@tool` function gives you full control over what the coordinator LLM sees: + +- **Simplified parameters**: define explicit `Annotated` arguments instead of exposing `agent.run()`'s full interface +- **Formatted output**: extract and return only what the coordinator needs, rather than the full result dict +- **Error handling**: catch exceptions and return a clean message so the coordinator can recover + +This approach works better with smaller LLMs because the tool has a clean, minimal signature. +The coordinator only needs to provide a query string — all the `ChatMessage` construction and result unpacking is hidden inside the function. + +```python +from typing import Annotated +from haystack.components.agents import Agent +from haystack.components.generators.chat import OpenAIChatGenerator +from haystack.components.generators.utils import print_streaming_chunk +from haystack.dataclasses import ChatMessage +from haystack.tools import ComponentTool, tool +from haystack.components.websearch import SerperDevWebSearch +from haystack.utils import Secret + + +research_agent = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[ + ComponentTool( + component=SerperDevWebSearch( + api_key=Secret.from_env_var("SERPERDEV_API_KEY"), + top_k=3, + ), + name="web_search", + description="Search the web for current information on any topic", + ), + ], + system_prompt="You are a research specialist. Search the web to find information.", +) + + +@tool +def research(query: Annotated[str, "The research question to investigate"]) -> str: + """Research a topic and return a summary of findings.""" + try: + result = research_agent.run(messages=[ChatMessage.from_user(query)]) + return result["last_message"].text + except Exception as e: + return f"Research failed: {e}" + + +coordinator = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[research], + system_prompt="You are a coordinator. Delegate research tasks to the research tool.", + streaming_callback=print_streaming_chunk, +) + +result = coordinator.run( + messages=[ + ChatMessage.from_user("What are the latest developments in Haystack AI?"), + ], +) +print(result["last_message"].text) +``` + +### `ComponentTool` + +`ComponentTool` wraps an agent directly without a wrapper function. +This is the right choice when configuring pipelines from YAML or through a visual pipeline builder, since the full component graph serialises without any custom Python code. + +Use `outputs_to_string={"source": "last_message"}` to surface only the specialist's final reply to the coordinator rather than the full result dict. + +```python +from haystack.tools import ComponentTool + +research_tool = ComponentTool( + component=research_agent, + name="research_specialist", + description="A specialist that researches topics on the web", + outputs_to_string={"source": "last_message"}, +) + +coordinator = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[research_tool], + system_prompt="You are a coordinator. Delegate research tasks to the research specialist.", + streaming_callback=print_streaming_chunk, +) +``` + +## Coordinator / Specialist Pattern + +The coordinator/specialist pattern splits responsibilities cleanly: the coordinator handles planning and delegation, while each specialist owns a focused toolset and a targeted system prompt. + +This is also a form of **context engineering** — deliberately controlling what each agent sees. +A specialist accumulates its own tool call trace as it works, but the coordinator only needs the final answer. +By returning just `result["last_message"].text` (with `@tool`) or using `outputs_to_string` (with `ComponentTool`), you surface only the specialist's final reply. +This keeps the coordinator's context lean and focused, which leads to better decisions and lower token usage. + +Here is a complete example with a coordinator that delegates research to a specialist: + +```python +from typing import Annotated +from haystack.components.agents import Agent +from haystack.components.fetchers.link_content import LinkContentFetcher +from haystack.components.generators.chat import OpenAIChatGenerator +from haystack.components.generators.utils import print_streaming_chunk +from haystack.components.websearch import SerperDevWebSearch +from haystack.dataclasses import ChatMessage +from haystack.tools import ComponentTool, tool +from haystack.utils import Secret + + +search_tool = ComponentTool( + component=SerperDevWebSearch( + api_key=Secret.from_env_var("SERPERDEV_API_KEY"), + top_k=3, + ), + name="web_search", + description="Search the web for current information on any topic", +) + + +@tool +def fetch_page(url: Annotated[str, "The URL of the web page to fetch"]) -> str: + """Fetch the full content of a web page given its URL.""" + try: + streams = LinkContentFetcher().run(urls=[url])["streams"] + return ( + streams[0].data.decode("utf-8", errors="replace") + if streams + else "No content found." + ) + except Exception as e: + return f"Failed to fetch page: {e}" + + +research_agent = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[search_tool, fetch_page], + system_prompt=( + "You are a research specialist. Search the web to find relevant pages, " + "then fetch their full content for detailed information." + ), +) + + +@tool +def research(query: Annotated[str, "The research question to investigate"]) -> str: + """Research a topic and return a summary of findings.""" + try: + result = research_agent.run(messages=[ChatMessage.from_user(query)]) + return result["last_message"].text + except Exception as e: + return f"Research failed: {e}" + + +coordinator = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[research], + system_prompt="You are a coordinator. Delegate research tasks to the research tool.", + streaming_callback=print_streaming_chunk, +) + +result = coordinator.run( + messages=[ + ChatMessage.from_user("What are the latest developments in Haystack AI?"), + ], +) +print(result["last_message"].text) +``` + +## Additional References + +📖 Related docs: + +- [Agent](../pipeline-components/agents-1/agent.mdx) +- [State](../pipeline-components/agents-1/state.mdx) +- [ComponentTool](../tools/componenttool.mdx) + +📚 Tutorials: + +- [Creating a Multi-Agent System](https://haystack.deepset.ai/tutorials/45_creating_a_multi_agent_system) diff --git a/docs-website/docs/pipeline-components/agents-1/agent.mdx b/docs-website/docs/pipeline-components/agents-1/agent.mdx index 18f2681c53..a721e2097e 100644 --- a/docs-website/docs/pipeline-components/agents-1/agent.mdx +++ b/docs-website/docs/pipeline-components/agents-1/agent.mdx @@ -336,90 +336,9 @@ Write a custom callback only if you need a specific transport (for example, SSE/ ## Multi-Agent Systems -You can wrap an `Agent` using [`ComponentTool`](../../tools/componenttool.mdx) to build multi-agent systems where specialized agents act as tools for a coordinator agent. +You can wrap an `Agent` as a tool to build multi-agent systems where specialist agents handle focused subtasks and a coordinator agent plans and delegates. -This pattern is useful when a task is too broad or complex for a single agent to handle well. -Instead of giving one agent a large toolset and hoping it makes good decisions, you can decompose the problem: -a **coordinator** agent handles planning and delegation, while **specialist** agents each own a focused set of tools and a targeted system prompt. - -This is also a form of **context engineering** — deliberately controlling what each agent sees. -A specialist accumulates its own tool call trace as it works, but the coordinator only needs the final answer. -By using `outputs_to_string={"source": "last_message"}` when wrapping a specialist as a `ComponentTool`, you surface only its final reply to the coordinator rather than forwarding the full tool call trace. -This keeps the coordinator's context lean and focused, which leads to better decisions and lower token usage as the conversation grows. - -```python -from typing import Annotated -from haystack.components.agents import Agent -from haystack.components.fetchers.link_content import LinkContentFetcher -from haystack.components.generators.chat import OpenAIChatGenerator -from haystack.components.generators.utils import print_streaming_chunk -from haystack.components.websearch import SerperDevWebSearch -from haystack.dataclasses import ChatMessage -from haystack.tools import ComponentTool, tool -from haystack.utils import Secret - -# Create the specialist agent with web search and page fetching tools - -# Option 1: ComponentTool — wrap a component directly (good for straightforward cases) -search_tool = ComponentTool( - component=SerperDevWebSearch( - api_key=Secret.from_env_var("SERPERDEV_API_KEY"), - top_k=3, - ), - name="web_search", - description="Search the web for current information on any topic", -) - - -# Option 2: @tool decorator — wrap a component inside a function for a simpler -# signature, built-in error handling, and custom result formatting -@tool -def fetch_page(url: Annotated[str, "The URL of the web page to fetch"]) -> str: - """Fetch the full content of a web page given its URL.""" - try: - streams = LinkContentFetcher().run(urls=[url])["streams"] - return ( - streams[0].data.decode("utf-8", errors="replace") - if streams - else "No content found." - ) - except Exception as e: - return f"Failed to fetch page: {e}" - - -research_agent = Agent( - chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), - tools=[search_tool, fetch_page], - system_prompt=( - "You are a research specialist. Search the web to find relevant pages, " - "then fetch their full content for detailed information." - ), -) - -# Wrap the specialist agent as a tool for the coordinator -research_tool = ComponentTool( - component=research_agent, - name="research_specialist", - description="A specialist that researches topics on the web", - outputs_to_string={"source": "last_message"}, # surface only the final reply -) - -# Create the coordinator agent with streaming -coordinator_agent = Agent( - chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), - tools=[research_tool], - system_prompt="You are a coordinator. Delegate research tasks to the research specialist.", - streaming_callback=print_streaming_chunk, -) - -result = coordinator_agent.run( - messages=[ - ChatMessage.from_user("What are the latest developments in Haystack AI?"), - ], -) - -print(result["last_message"].text) -``` +See [Multi-Agent Systems](../../concepts/multi-agent-systems.mdx) for a full guide, including the recommended `@tool` decorator approach and the `ComponentTool` alternative for YAML-based pipelines. ## Additional References diff --git a/docs-website/sidebars.js b/docs-website/sidebars.js index a88eb8c67e..f440580cdd 100644 --- a/docs-website/sidebars.js +++ b/docs-website/sidebars.js @@ -28,6 +28,7 @@ export default { items: [ 'concepts/concepts-overview', 'concepts/agents', + 'concepts/multi-agent-systems', { type: 'category', label: 'Components', From 2b69aa1f2adddb2b08ab6e544d83fba8c1d29c88 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Fri, 8 May 2026 15:25:34 +0200 Subject: [PATCH 02/12] Doc edits --- .../docs/concepts/multi-agent-systems.mdx | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/docs-website/docs/concepts/multi-agent-systems.mdx b/docs-website/docs/concepts/multi-agent-systems.mdx index b9f216692e..f80a412ef1 100644 --- a/docs-website/docs/concepts/multi-agent-systems.mdx +++ b/docs-website/docs/concepts/multi-agent-systems.mdx @@ -113,11 +113,14 @@ coordinator = Agent( The coordinator/specialist pattern splits responsibilities cleanly: the coordinator handles planning and delegation, while each specialist owns a focused toolset and a targeted system prompt. This is also a form of **context engineering** — deliberately controlling what each agent sees. -A specialist accumulates its own tool call trace as it works, but the coordinator only needs the final answer. -By returning just `result["last_message"].text` (with `@tool`) or using `outputs_to_string` (with `ComponentTool`), you surface only the specialist's final reply. -This keeps the coordinator's context lean and focused, which leads to better decisions and lower token usage. +A specialist accumulates its own tool call trace, but the coordinator only needs the final answer. +Returning just `result["last_message"].text` (with `@tool`) or using `outputs_to_string` (with `ComponentTool`) surfaces only the specialist's final reply, keeping the coordinator's context lean. -Here is a complete example with a coordinator that delegates research to a specialist: +When covering multiple topics, the coordinator can call the same specialist tool several times in a single response. +All tool calls from one LLM response are executed concurrently using a thread pool. +Control the level of parallelism with `max_workers` in `tool_invoker_kwargs` (default: `4`). + +The example below asks the coordinator about two topics — it calls `research` twice and both specialists run in parallel: ```python from typing import Annotated @@ -178,13 +181,19 @@ def research(query: Annotated[str, "The research question to investigate"]) -> s coordinator = Agent( chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), tools=[research], - system_prompt="You are a coordinator. Delegate research tasks to the research tool.", + system_prompt=( + "You are a coordinator. Delegate research tasks to the research tool. " + "For questions covering multiple topics, research each one independently." + ), streaming_callback=print_streaming_chunk, + tool_invoker_kwargs={"max_workers": 4}, # run up to 4 specialist calls in parallel ) result = coordinator.run( messages=[ - ChatMessage.from_user("What are the latest developments in Haystack AI?"), + ChatMessage.from_user( + "What are the latest developments in large language models and retrieval-augmented generation?", + ), ], ) print(result["last_message"].text) From b250ed3d9e2eadaa6ffdc69e4de3507150017383 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Fri, 8 May 2026 15:56:17 +0200 Subject: [PATCH 03/12] Update docs --- .../docs/concepts/multi-agent-systems.mdx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/docs-website/docs/concepts/multi-agent-systems.mdx b/docs-website/docs/concepts/multi-agent-systems.mdx index f80a412ef1..125e3031f0 100644 --- a/docs-website/docs/concepts/multi-agent-systems.mdx +++ b/docs-website/docs/concepts/multi-agent-systems.mdx @@ -120,11 +120,19 @@ When covering multiple topics, the coordinator can call the same specialist tool All tool calls from one LLM response are executed concurrently using a thread pool. Control the level of parallelism with `max_workers` in `tool_invoker_kwargs` (default: `4`). -The example below asks the coordinator about two topics — it calls `research` twice and both specialists run in parallel: +The example below asks the coordinator about two topics — it calls `research` twice and both specialists run in parallel. + +`HTMLToDocument` uses [Trafilatura](https://trafilatura.readthedocs.io) to extract clean text from HTML pages. +Install it before running: + +```shell +pip install trafilatura +``` ```python from typing import Annotated from haystack.components.agents import Agent +from haystack.components.converters import HTMLToDocument from haystack.components.fetchers.link_content import LinkContentFetcher from haystack.components.generators.chat import OpenAIChatGenerator from haystack.components.generators.utils import print_streaming_chunk @@ -146,14 +154,13 @@ search_tool = ComponentTool( @tool def fetch_page(url: Annotated[str, "The URL of the web page to fetch"]) -> str: - """Fetch the full content of a web page given its URL.""" + """Fetch the content of a web page given its URL.""" try: streams = LinkContentFetcher().run(urls=[url])["streams"] - return ( - streams[0].data.decode("utf-8", errors="replace") - if streams - else "No content found." - ) + if not streams: + return "No content found." + documents = HTMLToDocument().run(sources=streams)["documents"] + return documents[0].content if documents else "No content extracted." except Exception as e: return f"Failed to fetch page: {e}" From 5d67a3f46376e14e04bc2b2940b7e9ee1c3819f4 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Mon, 11 May 2026 08:52:31 +0200 Subject: [PATCH 04/12] Move to follow established structure --- .../concepts/{ => agents}/multi-agent-systems.mdx | 6 +++--- docs-website/sidebars.js | 13 +++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) rename docs-website/docs/concepts/{ => agents}/multi-agent-systems.mdx (98%) diff --git a/docs-website/docs/concepts/multi-agent-systems.mdx b/docs-website/docs/concepts/agents/multi-agent-systems.mdx similarity index 98% rename from docs-website/docs/concepts/multi-agent-systems.mdx rename to docs-website/docs/concepts/agents/multi-agent-systems.mdx index 125e3031f0..09a5e8d268 100644 --- a/docs-website/docs/concepts/multi-agent-systems.mdx +++ b/docs-website/docs/concepts/agents/multi-agent-systems.mdx @@ -210,9 +210,9 @@ print(result["last_message"].text) 📖 Related docs: -- [Agent](../pipeline-components/agents-1/agent.mdx) -- [State](../pipeline-components/agents-1/state.mdx) -- [ComponentTool](../tools/componenttool.mdx) +- [Agent](../../pipeline-components/agents-1/agent.mdx) +- [State](../../pipeline-components/agents-1/state.mdx) +- [ComponentTool](../../tools/componenttool.mdx) 📚 Tutorials: diff --git a/docs-website/sidebars.js b/docs-website/sidebars.js index f440580cdd..4c85e94737 100644 --- a/docs-website/sidebars.js +++ b/docs-website/sidebars.js @@ -27,8 +27,17 @@ export default { label: 'Haystack Concepts', items: [ 'concepts/concepts-overview', - 'concepts/agents', - 'concepts/multi-agent-systems', + { + type: 'category', + label: 'Agents', + link: { + type: 'doc', + id: 'concepts/agents' + }, + items: [ + 'concepts/agents/multi-agent-systems', + ], + }, { type: 'category', label: 'Components', From 4bdb897dae944e52fc7d7715484333b980700315 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Mon, 11 May 2026 08:55:00 +0200 Subject: [PATCH 05/12] update link --- docs-website/docs/pipeline-components/agents-1/agent.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-website/docs/pipeline-components/agents-1/agent.mdx b/docs-website/docs/pipeline-components/agents-1/agent.mdx index a721e2097e..fcf70fea9a 100644 --- a/docs-website/docs/pipeline-components/agents-1/agent.mdx +++ b/docs-website/docs/pipeline-components/agents-1/agent.mdx @@ -338,7 +338,7 @@ Write a custom callback only if you need a specific transport (for example, SSE/ You can wrap an `Agent` as a tool to build multi-agent systems where specialist agents handle focused subtasks and a coordinator agent plans and delegates. -See [Multi-Agent Systems](../../concepts/multi-agent-systems.mdx) for a full guide, including the recommended `@tool` decorator approach and the `ComponentTool` alternative for YAML-based pipelines. +See [Multi-Agent Systems](../../concepts/agents/multi-agent-systems.mdx) for a full guide, including the recommended `@tool` decorator approach and the `ComponentTool` alternative for YAML-based pipelines. ## Additional References From 3bd0fd2ee3c47f3d00bda057442bf5d20b5dc35e Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Mon, 11 May 2026 09:07:59 +0200 Subject: [PATCH 06/12] update messaging to be declaritive config vs interface control --- docs-website/docs/concepts/agents/multi-agent-systems.mdx | 2 +- docs-website/docs/pipeline-components/agents-1/agent.mdx | 2 +- docs-website/docs/tools/componenttool.mdx | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs-website/docs/concepts/agents/multi-agent-systems.mdx b/docs-website/docs/concepts/agents/multi-agent-systems.mdx index 09a5e8d268..59838e2bfe 100644 --- a/docs-website/docs/concepts/agents/multi-agent-systems.mdx +++ b/docs-website/docs/concepts/agents/multi-agent-systems.mdx @@ -86,7 +86,7 @@ print(result["last_message"].text) ### `ComponentTool` `ComponentTool` wraps an agent directly without a wrapper function. -This is the right choice when configuring pipelines from YAML or through a visual pipeline builder, since the full component graph serialises without any custom Python code. +Choose it when you want **declarative configuration**: the full specialist setup — model, tools, system prompt — lives in one serializable object alongside the coordinator. Use `outputs_to_string={"source": "last_message"}` to surface only the specialist's final reply to the coordinator rather than the full result dict. diff --git a/docs-website/docs/pipeline-components/agents-1/agent.mdx b/docs-website/docs/pipeline-components/agents-1/agent.mdx index fcf70fea9a..149c415984 100644 --- a/docs-website/docs/pipeline-components/agents-1/agent.mdx +++ b/docs-website/docs/pipeline-components/agents-1/agent.mdx @@ -338,7 +338,7 @@ Write a custom callback only if you need a specific transport (for example, SSE/ You can wrap an `Agent` as a tool to build multi-agent systems where specialist agents handle focused subtasks and a coordinator agent plans and delegates. -See [Multi-Agent Systems](../../concepts/agents/multi-agent-systems.mdx) for a full guide, including the recommended `@tool` decorator approach and the `ComponentTool` alternative for YAML-based pipelines. +See [Multi-Agent Systems](../../concepts/agents/multi-agent-systems.mdx) for a full guide, including the recommended `@tool` decorator approach for full interface control and `ComponentTool` for declarative configuration. ## Additional References diff --git a/docs-website/docs/tools/componenttool.mdx b/docs-website/docs/tools/componenttool.mdx index ca153c6493..fb72975f92 100644 --- a/docs-website/docs/tools/componenttool.mdx +++ b/docs-website/docs/tools/componenttool.mdx @@ -120,6 +120,10 @@ print(result) ## Additional References +📖 Related docs: + +- [Multi-Agent Systems](../concepts/agents/multi-agent-systems.mdx) + 📚 Tutorials: - [Build a Tool-Calling Agent](https://haystack.deepset.ai/tutorials/43_building_a_tool_calling_agent) From ec7eb156534eeba7cdda84c4e956d54aef02f9c8 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Mon, 11 May 2026 09:22:58 +0200 Subject: [PATCH 07/12] fix link and improve code examples --- docs-website/docs/concepts/agents.mdx | 2 +- .../concepts/agents/multi-agent-systems.mdx | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs-website/docs/concepts/agents.mdx b/docs-website/docs/concepts/agents.mdx index 0ecd16b3c7..3423686865 100644 --- a/docs-website/docs/concepts/agents.mdx +++ b/docs-website/docs/concepts/agents.mdx @@ -43,7 +43,7 @@ Key capabilities include: - **State management**: Share typed data between tools, accumulate results across iterations, and surface them in the result dict using `state_schema`. See [State](../pipeline-components/agents-1/state.mdx). - **Streaming**: Stream token-by-token output with a `streaming_callback`. - **Human-in-the-loop**: Intercept tool calls for human review before execution. See [Human in the Loop](../pipeline-components/agents-1/human-in-the-loop.mdx). -- **Multi-agent systems**: Wrap an `Agent` as a `ComponentTool` to build coordinator/specialist architectures. See [Multi-Agent Systems](../pipeline-components/agents-1/agent.mdx#multi-agent-systems). +- **Multi-agent systems**: Wrap an `Agent` as a `ComponentTool` to build coordinator/specialist architectures. See [Multi-Agent Systems](./agents/multi-agent-systems.mdx). Check out the [Agent](../pipeline-components/agents-1/agent.mdx) documentation, or the [example](#tool-calling-agent) below to get started. diff --git a/docs-website/docs/concepts/agents/multi-agent-systems.mdx b/docs-website/docs/concepts/agents/multi-agent-systems.mdx index 59838e2bfe..09bf924efd 100644 --- a/docs-website/docs/concepts/agents/multi-agent-systems.mdx +++ b/docs-website/docs/concepts/agents/multi-agent-systems.mdx @@ -8,7 +8,7 @@ description: "Learn how to build multi-agent systems in Haystack by spawning age # Multi-Agent Systems Multi-agent systems let you compose multiple `Agent` instances into larger architectures where a **coordinator** agent delegates to **specialist** agents. -Each specialist focuses on a specific task with its own tools and system prompt — the coordinator plans and routes work without needing to know how each task gets done. +Each specialist focuses on a specific task with its own tools and system prompt - the coordinator plans and routes work without needing to know how each task gets done. Spawning agents as tools is useful when: @@ -29,7 +29,7 @@ Wrapping an agent inside a `@tool` function gives you full control over what the - **Error handling**: catch exceptions and return a clean message so the coordinator can recover This approach works better with smaller LLMs because the tool has a clean, minimal signature. -The coordinator only needs to provide a query string — all the `ChatMessage` construction and result unpacking is hidden inside the function. +The coordinator only needs to provide a query string - all the `ChatMessage` construction and result unpacking is hidden inside the function. ```python from typing import Annotated @@ -80,13 +80,12 @@ result = coordinator.run( ChatMessage.from_user("What are the latest developments in Haystack AI?"), ], ) -print(result["last_message"].text) ``` ### `ComponentTool` `ComponentTool` wraps an agent directly without a wrapper function. -Choose it when you want **declarative configuration**: the full specialist setup — model, tools, system prompt — lives in one serializable object alongside the coordinator. +Choose it when you want **declarative configuration**: the full specialist setup (model, tools, system prompt) lives in one serializable object alongside the coordinator. Use `outputs_to_string={"source": "last_message"}` to surface only the specialist's final reply to the coordinator rather than the full result dict. @@ -110,9 +109,9 @@ coordinator = Agent( ## Coordinator / Specialist Pattern -The coordinator/specialist pattern splits responsibilities cleanly: the coordinator handles planning and delegation, while each specialist owns a focused toolset and a targeted system prompt. +The coordinator/specialist pattern cleanly splits responsibilities: the coordinator handles planning and delegation, while each specialist owns a focused toolset and a targeted system prompt. -This is also a form of **context engineering** — deliberately controlling what each agent sees. +This is also a form of **context engineering**: deliberately controlling what each agent sees. A specialist accumulates its own tool call trace, but the coordinator only needs the final answer. Returning just `result["last_message"].text` (with `@tool`) or using `outputs_to_string` (with `ComponentTool`) surfaces only the specialist's final reply, keeping the coordinator's context lean. @@ -120,7 +119,7 @@ When covering multiple topics, the coordinator can call the same specialist tool All tool calls from one LLM response are executed concurrently using a thread pool. Control the level of parallelism with `max_workers` in `tool_invoker_kwargs` (default: `4`). -The example below asks the coordinator about two topics — it calls `research` twice and both specialists run in parallel. +The example below asks the coordinator about two topics: it calls `research` twice and both specialists run in parallel. `HTMLToDocument` uses [Trafilatura](https://trafilatura.readthedocs.io) to extract clean text from HTML pages. Install it before running: @@ -170,7 +169,8 @@ research_agent = Agent( tools=[search_tool, fetch_page], system_prompt=( "You are a research specialist. Search the web to find relevant pages, " - "then fetch their full content for detailed information." + "then fetch their full content for detailed information. " + "Return a concise summary of your findings in 3-5 sentences." ), ) @@ -190,7 +190,8 @@ coordinator = Agent( tools=[research], system_prompt=( "You are a coordinator. Delegate research tasks to the research tool. " - "For questions covering multiple topics, research each one independently." + "For questions covering multiple topics, research each one independently. " + "Keep your final answer concise." ), streaming_callback=print_streaming_chunk, tool_invoker_kwargs={"max_workers": 4}, # run up to 4 specialist calls in parallel @@ -203,7 +204,6 @@ result = coordinator.run( ), ], ) -print(result["last_message"].text) ``` ## Additional References From effb4407f644202e6ea4102f7cb0a86fc0dc91a6 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Mon, 11 May 2026 09:29:28 +0200 Subject: [PATCH 08/12] copy over changes --- .../version-2.28/concepts/agents.mdx | 2 +- .../concepts/agents/multi-agent-systems.mdx | 219 ++++++++++++++++++ .../pipeline-components/agents-1/agent.mdx | 85 +------ .../version-2.28/tools/componenttool.mdx | 4 + .../version-2.28-sidebars.json | 12 +- 5 files changed, 237 insertions(+), 85 deletions(-) create mode 100644 docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx diff --git a/docs-website/versioned_docs/version-2.28/concepts/agents.mdx b/docs-website/versioned_docs/version-2.28/concepts/agents.mdx index 0ecd16b3c7..3423686865 100644 --- a/docs-website/versioned_docs/version-2.28/concepts/agents.mdx +++ b/docs-website/versioned_docs/version-2.28/concepts/agents.mdx @@ -43,7 +43,7 @@ Key capabilities include: - **State management**: Share typed data between tools, accumulate results across iterations, and surface them in the result dict using `state_schema`. See [State](../pipeline-components/agents-1/state.mdx). - **Streaming**: Stream token-by-token output with a `streaming_callback`. - **Human-in-the-loop**: Intercept tool calls for human review before execution. See [Human in the Loop](../pipeline-components/agents-1/human-in-the-loop.mdx). -- **Multi-agent systems**: Wrap an `Agent` as a `ComponentTool` to build coordinator/specialist architectures. See [Multi-Agent Systems](../pipeline-components/agents-1/agent.mdx#multi-agent-systems). +- **Multi-agent systems**: Wrap an `Agent` as a `ComponentTool` to build coordinator/specialist architectures. See [Multi-Agent Systems](./agents/multi-agent-systems.mdx). Check out the [Agent](../pipeline-components/agents-1/agent.mdx) documentation, or the [example](#tool-calling-agent) below to get started. diff --git a/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx b/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx new file mode 100644 index 0000000000..09bf924efd --- /dev/null +++ b/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx @@ -0,0 +1,219 @@ +--- +title: "Multi-Agent Systems" +id: multi-agent-systems +slug: "/multi-agent-systems" +description: "Learn how to build multi-agent systems in Haystack by spawning agents as tools. Use the @tool decorator or ComponentTool to connect specialist agents to a coordinator." +--- + +# Multi-Agent Systems + +Multi-agent systems let you compose multiple `Agent` instances into larger architectures where a **coordinator** agent delegates to **specialist** agents. +Each specialist focuses on a specific task with its own tools and system prompt - the coordinator plans and routes work without needing to know how each task gets done. + +Spawning agents as tools is useful when: + +- A task is too broad for a single agent to handle reliably, +- You want to isolate different capabilities into focused, reusable agents, +- You need to keep the coordinator's context lean for better decisions and lower token usage. + +In Haystack, you spawn a specialist agent as a tool using either the `@tool` decorator (recommended) or `ComponentTool`. + +## Converting an Agent to a Tool + +### `@tool` Decorator (Recommended) + +Wrapping an agent inside a `@tool` function gives you full control over what the coordinator LLM sees: + +- **Simplified parameters**: define explicit `Annotated` arguments instead of exposing `agent.run()`'s full interface +- **Formatted output**: extract and return only what the coordinator needs, rather than the full result dict +- **Error handling**: catch exceptions and return a clean message so the coordinator can recover + +This approach works better with smaller LLMs because the tool has a clean, minimal signature. +The coordinator only needs to provide a query string - all the `ChatMessage` construction and result unpacking is hidden inside the function. + +```python +from typing import Annotated +from haystack.components.agents import Agent +from haystack.components.generators.chat import OpenAIChatGenerator +from haystack.components.generators.utils import print_streaming_chunk +from haystack.dataclasses import ChatMessage +from haystack.tools import ComponentTool, tool +from haystack.components.websearch import SerperDevWebSearch +from haystack.utils import Secret + + +research_agent = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[ + ComponentTool( + component=SerperDevWebSearch( + api_key=Secret.from_env_var("SERPERDEV_API_KEY"), + top_k=3, + ), + name="web_search", + description="Search the web for current information on any topic", + ), + ], + system_prompt="You are a research specialist. Search the web to find information.", +) + + +@tool +def research(query: Annotated[str, "The research question to investigate"]) -> str: + """Research a topic and return a summary of findings.""" + try: + result = research_agent.run(messages=[ChatMessage.from_user(query)]) + return result["last_message"].text + except Exception as e: + return f"Research failed: {e}" + + +coordinator = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[research], + system_prompt="You are a coordinator. Delegate research tasks to the research tool.", + streaming_callback=print_streaming_chunk, +) + +result = coordinator.run( + messages=[ + ChatMessage.from_user("What are the latest developments in Haystack AI?"), + ], +) +``` + +### `ComponentTool` + +`ComponentTool` wraps an agent directly without a wrapper function. +Choose it when you want **declarative configuration**: the full specialist setup (model, tools, system prompt) lives in one serializable object alongside the coordinator. + +Use `outputs_to_string={"source": "last_message"}` to surface only the specialist's final reply to the coordinator rather than the full result dict. + +```python +from haystack.tools import ComponentTool + +research_tool = ComponentTool( + component=research_agent, + name="research_specialist", + description="A specialist that researches topics on the web", + outputs_to_string={"source": "last_message"}, +) + +coordinator = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[research_tool], + system_prompt="You are a coordinator. Delegate research tasks to the research specialist.", + streaming_callback=print_streaming_chunk, +) +``` + +## Coordinator / Specialist Pattern + +The coordinator/specialist pattern cleanly splits responsibilities: the coordinator handles planning and delegation, while each specialist owns a focused toolset and a targeted system prompt. + +This is also a form of **context engineering**: deliberately controlling what each agent sees. +A specialist accumulates its own tool call trace, but the coordinator only needs the final answer. +Returning just `result["last_message"].text` (with `@tool`) or using `outputs_to_string` (with `ComponentTool`) surfaces only the specialist's final reply, keeping the coordinator's context lean. + +When covering multiple topics, the coordinator can call the same specialist tool several times in a single response. +All tool calls from one LLM response are executed concurrently using a thread pool. +Control the level of parallelism with `max_workers` in `tool_invoker_kwargs` (default: `4`). + +The example below asks the coordinator about two topics: it calls `research` twice and both specialists run in parallel. + +`HTMLToDocument` uses [Trafilatura](https://trafilatura.readthedocs.io) to extract clean text from HTML pages. +Install it before running: + +```shell +pip install trafilatura +``` + +```python +from typing import Annotated +from haystack.components.agents import Agent +from haystack.components.converters import HTMLToDocument +from haystack.components.fetchers.link_content import LinkContentFetcher +from haystack.components.generators.chat import OpenAIChatGenerator +from haystack.components.generators.utils import print_streaming_chunk +from haystack.components.websearch import SerperDevWebSearch +from haystack.dataclasses import ChatMessage +from haystack.tools import ComponentTool, tool +from haystack.utils import Secret + + +search_tool = ComponentTool( + component=SerperDevWebSearch( + api_key=Secret.from_env_var("SERPERDEV_API_KEY"), + top_k=3, + ), + name="web_search", + description="Search the web for current information on any topic", +) + + +@tool +def fetch_page(url: Annotated[str, "The URL of the web page to fetch"]) -> str: + """Fetch the content of a web page given its URL.""" + try: + streams = LinkContentFetcher().run(urls=[url])["streams"] + if not streams: + return "No content found." + documents = HTMLToDocument().run(sources=streams)["documents"] + return documents[0].content if documents else "No content extracted." + except Exception as e: + return f"Failed to fetch page: {e}" + + +research_agent = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[search_tool, fetch_page], + system_prompt=( + "You are a research specialist. Search the web to find relevant pages, " + "then fetch their full content for detailed information. " + "Return a concise summary of your findings in 3-5 sentences." + ), +) + + +@tool +def research(query: Annotated[str, "The research question to investigate"]) -> str: + """Research a topic and return a summary of findings.""" + try: + result = research_agent.run(messages=[ChatMessage.from_user(query)]) + return result["last_message"].text + except Exception as e: + return f"Research failed: {e}" + + +coordinator = Agent( + chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), + tools=[research], + system_prompt=( + "You are a coordinator. Delegate research tasks to the research tool. " + "For questions covering multiple topics, research each one independently. " + "Keep your final answer concise." + ), + streaming_callback=print_streaming_chunk, + tool_invoker_kwargs={"max_workers": 4}, # run up to 4 specialist calls in parallel +) + +result = coordinator.run( + messages=[ + ChatMessage.from_user( + "What are the latest developments in large language models and retrieval-augmented generation?", + ), + ], +) +``` + +## Additional References + +📖 Related docs: + +- [Agent](../../pipeline-components/agents-1/agent.mdx) +- [State](../../pipeline-components/agents-1/state.mdx) +- [ComponentTool](../../tools/componenttool.mdx) + +📚 Tutorials: + +- [Creating a Multi-Agent System](https://haystack.deepset.ai/tutorials/45_creating_a_multi_agent_system) diff --git a/docs-website/versioned_docs/version-2.28/pipeline-components/agents-1/agent.mdx b/docs-website/versioned_docs/version-2.28/pipeline-components/agents-1/agent.mdx index 18f2681c53..149c415984 100644 --- a/docs-website/versioned_docs/version-2.28/pipeline-components/agents-1/agent.mdx +++ b/docs-website/versioned_docs/version-2.28/pipeline-components/agents-1/agent.mdx @@ -336,90 +336,9 @@ Write a custom callback only if you need a specific transport (for example, SSE/ ## Multi-Agent Systems -You can wrap an `Agent` using [`ComponentTool`](../../tools/componenttool.mdx) to build multi-agent systems where specialized agents act as tools for a coordinator agent. +You can wrap an `Agent` as a tool to build multi-agent systems where specialist agents handle focused subtasks and a coordinator agent plans and delegates. -This pattern is useful when a task is too broad or complex for a single agent to handle well. -Instead of giving one agent a large toolset and hoping it makes good decisions, you can decompose the problem: -a **coordinator** agent handles planning and delegation, while **specialist** agents each own a focused set of tools and a targeted system prompt. - -This is also a form of **context engineering** — deliberately controlling what each agent sees. -A specialist accumulates its own tool call trace as it works, but the coordinator only needs the final answer. -By using `outputs_to_string={"source": "last_message"}` when wrapping a specialist as a `ComponentTool`, you surface only its final reply to the coordinator rather than forwarding the full tool call trace. -This keeps the coordinator's context lean and focused, which leads to better decisions and lower token usage as the conversation grows. - -```python -from typing import Annotated -from haystack.components.agents import Agent -from haystack.components.fetchers.link_content import LinkContentFetcher -from haystack.components.generators.chat import OpenAIChatGenerator -from haystack.components.generators.utils import print_streaming_chunk -from haystack.components.websearch import SerperDevWebSearch -from haystack.dataclasses import ChatMessage -from haystack.tools import ComponentTool, tool -from haystack.utils import Secret - -# Create the specialist agent with web search and page fetching tools - -# Option 1: ComponentTool — wrap a component directly (good for straightforward cases) -search_tool = ComponentTool( - component=SerperDevWebSearch( - api_key=Secret.from_env_var("SERPERDEV_API_KEY"), - top_k=3, - ), - name="web_search", - description="Search the web for current information on any topic", -) - - -# Option 2: @tool decorator — wrap a component inside a function for a simpler -# signature, built-in error handling, and custom result formatting -@tool -def fetch_page(url: Annotated[str, "The URL of the web page to fetch"]) -> str: - """Fetch the full content of a web page given its URL.""" - try: - streams = LinkContentFetcher().run(urls=[url])["streams"] - return ( - streams[0].data.decode("utf-8", errors="replace") - if streams - else "No content found." - ) - except Exception as e: - return f"Failed to fetch page: {e}" - - -research_agent = Agent( - chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), - tools=[search_tool, fetch_page], - system_prompt=( - "You are a research specialist. Search the web to find relevant pages, " - "then fetch their full content for detailed information." - ), -) - -# Wrap the specialist agent as a tool for the coordinator -research_tool = ComponentTool( - component=research_agent, - name="research_specialist", - description="A specialist that researches topics on the web", - outputs_to_string={"source": "last_message"}, # surface only the final reply -) - -# Create the coordinator agent with streaming -coordinator_agent = Agent( - chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"), - tools=[research_tool], - system_prompt="You are a coordinator. Delegate research tasks to the research specialist.", - streaming_callback=print_streaming_chunk, -) - -result = coordinator_agent.run( - messages=[ - ChatMessage.from_user("What are the latest developments in Haystack AI?"), - ], -) - -print(result["last_message"].text) -``` +See [Multi-Agent Systems](../../concepts/agents/multi-agent-systems.mdx) for a full guide, including the recommended `@tool` decorator approach for full interface control and `ComponentTool` for declarative configuration. ## Additional References diff --git a/docs-website/versioned_docs/version-2.28/tools/componenttool.mdx b/docs-website/versioned_docs/version-2.28/tools/componenttool.mdx index ca153c6493..fb72975f92 100644 --- a/docs-website/versioned_docs/version-2.28/tools/componenttool.mdx +++ b/docs-website/versioned_docs/version-2.28/tools/componenttool.mdx @@ -120,6 +120,10 @@ print(result) ## Additional References +📖 Related docs: + +- [Multi-Agent Systems](../concepts/agents/multi-agent-systems.mdx) + 📚 Tutorials: - [Build a Tool-Calling Agent](https://haystack.deepset.ai/tutorials/43_building_a_tool_calling_agent) diff --git a/docs-website/versioned_sidebars/version-2.28-sidebars.json b/docs-website/versioned_sidebars/version-2.28-sidebars.json index ec978d73fd..a77106eef3 100644 --- a/docs-website/versioned_sidebars/version-2.28-sidebars.json +++ b/docs-website/versioned_sidebars/version-2.28-sidebars.json @@ -23,7 +23,17 @@ "label": "Haystack Concepts", "items": [ "concepts/concepts-overview", - "concepts/agents", + { + "type": "category", + "label": "Agents", + "link": { + "type": "doc", + "id": "concepts/agents" + }, + "items": [ + "concepts/agents/multi-agent-systems" + ] + }, { "type": "category", "label": "Components", From 138728a8c4e590a1b954a093ce612c2410270974 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee <10526848+sjrl@users.noreply.github.com> Date: Mon, 11 May 2026 10:00:01 +0200 Subject: [PATCH 09/12] Update docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx Co-authored-by: Julian Risch --- .../version-2.28/concepts/agents/multi-agent-systems.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx b/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx index 09bf924efd..fbaec5aae7 100644 --- a/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx +++ b/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx @@ -105,6 +105,12 @@ coordinator = Agent( system_prompt="You are a coordinator. Delegate research tasks to the research specialist.", streaming_callback=print_streaming_chunk, ) + +result = coordinator.run( + messages=[ + ChatMessage.from_user("What are the latest developments in Haystack AI?"), + ], +) ``` ## Coordinator / Specialist Pattern From e52c47625dc28a87f92bcfc12088c86017d3a8d2 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee <10526848+sjrl@users.noreply.github.com> Date: Mon, 11 May 2026 10:00:31 +0200 Subject: [PATCH 10/12] Update docs-website/docs/concepts/agents/multi-agent-systems.mdx Co-authored-by: Julian Risch --- docs-website/docs/concepts/agents/multi-agent-systems.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs-website/docs/concepts/agents/multi-agent-systems.mdx b/docs-website/docs/concepts/agents/multi-agent-systems.mdx index 09bf924efd..fbaec5aae7 100644 --- a/docs-website/docs/concepts/agents/multi-agent-systems.mdx +++ b/docs-website/docs/concepts/agents/multi-agent-systems.mdx @@ -105,6 +105,12 @@ coordinator = Agent( system_prompt="You are a coordinator. Delegate research tasks to the research specialist.", streaming_callback=print_streaming_chunk, ) + +result = coordinator.run( + messages=[ + ChatMessage.from_user("What are the latest developments in Haystack AI?"), + ], +) ``` ## Coordinator / Specialist Pattern From 0d9a36fbf14fe678bca75b64b097ea77a61c55c2 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Mon, 11 May 2026 10:24:14 +0200 Subject: [PATCH 11/12] add yaml example --- .../concepts/agents/multi-agent-systems.mdx | 112 ++++++++++++++++++ .../concepts/agents/multi-agent-systems.mdx | 112 ++++++++++++++++++ 2 files changed, 224 insertions(+) diff --git a/docs-website/docs/concepts/agents/multi-agent-systems.mdx b/docs-website/docs/concepts/agents/multi-agent-systems.mdx index fbaec5aae7..dc5a707863 100644 --- a/docs-website/docs/concepts/agents/multi-agent-systems.mdx +++ b/docs-website/docs/concepts/agents/multi-agent-systems.mdx @@ -113,6 +113,118 @@ result = coordinator.run( ) ``` +The full specialist configuration is captured inline when serialized. Wrap the coordinator in a `Pipeline` and call `pipeline.dumps()` to get the YAML, which can be loaded back with `Pipeline.loads()` — no Python code required. + +
+View YAML + +```yaml +components: + coordinator: + init_parameters: + chat_generator: + init_parameters: + api_base_url: null + api_key: + env_vars: + - OPENAI_API_KEY + strict: true + type: env_var + generation_kwargs: {} + http_client_kwargs: null + max_retries: null + model: gpt-5.4-nano + organization: null + streaming_callback: null + timeout: null + tools: null + tools_strict: false + type: haystack.components.generators.chat.openai.OpenAIChatGenerator + confirmation_strategies: null + exit_conditions: + - text + max_agent_steps: 100 + raise_on_tool_invocation_failure: false + required_variables: null + state_schema: {} + streaming_callback: null + system_prompt: You are a coordinator. Delegate research tasks to the research + specialist. Keep your final answer concise. + tool_invoker_kwargs: null + tools: + - data: + component: + init_parameters: + chat_generator: + init_parameters: + api_base_url: null + api_key: + env_vars: + - OPENAI_API_KEY + strict: true + type: env_var + generation_kwargs: {} + http_client_kwargs: null + max_retries: null + model: gpt-5.4-nano + organization: null + streaming_callback: null + timeout: null + tools: null + tools_strict: false + type: haystack.components.generators.chat.openai.OpenAIChatGenerator + confirmation_strategies: null + exit_conditions: + - text + max_agent_steps: 100 + raise_on_tool_invocation_failure: false + required_variables: null + state_schema: {} + streaming_callback: null + system_prompt: You are a research specialist. Search the web to find + information. Return a concise summary of your findings in 3-5 sentences. + tool_invoker_kwargs: null + tools: + - data: + component: + init_parameters: + allowed_domains: null + api_key: + env_vars: + - SERPERDEV_API_KEY + strict: true + type: env_var + exclude_subdomains: false + search_params: {} + top_k: 3 + type: haystack.components.websearch.serper_dev.SerperDevWebSearch + description: Search the web for current information on any topic + inputs_from_state: null + name: web_search + outputs_to_state: null + outputs_to_string: null + parameters: null + type: haystack.tools.component_tool.ComponentTool + user_prompt: null + type: haystack.components.agents.agent.Agent + description: A specialist that researches topics on the web + inputs_from_state: null + name: research_specialist + outputs_to_state: null + outputs_to_string: + source: last_message + parameters: null + type: haystack.tools.component_tool.ComponentTool + user_prompt: null + type: haystack.components.agents.agent.Agent +connection_type_validation: true +connections: [] +max_runs_per_component: 100 +metadata: {} +``` + +
+ ## Coordinator / Specialist Pattern The coordinator/specialist pattern cleanly splits responsibilities: the coordinator handles planning and delegation, while each specialist owns a focused toolset and a targeted system prompt. diff --git a/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx b/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx index fbaec5aae7..dc5a707863 100644 --- a/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx +++ b/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx @@ -113,6 +113,118 @@ result = coordinator.run( ) ``` +The full specialist configuration is captured inline when serialized. Wrap the coordinator in a `Pipeline` and call `pipeline.dumps()` to get the YAML, which can be loaded back with `Pipeline.loads()` — no Python code required. + +
+View YAML + +```yaml +components: + coordinator: + init_parameters: + chat_generator: + init_parameters: + api_base_url: null + api_key: + env_vars: + - OPENAI_API_KEY + strict: true + type: env_var + generation_kwargs: {} + http_client_kwargs: null + max_retries: null + model: gpt-5.4-nano + organization: null + streaming_callback: null + timeout: null + tools: null + tools_strict: false + type: haystack.components.generators.chat.openai.OpenAIChatGenerator + confirmation_strategies: null + exit_conditions: + - text + max_agent_steps: 100 + raise_on_tool_invocation_failure: false + required_variables: null + state_schema: {} + streaming_callback: null + system_prompt: You are a coordinator. Delegate research tasks to the research + specialist. Keep your final answer concise. + tool_invoker_kwargs: null + tools: + - data: + component: + init_parameters: + chat_generator: + init_parameters: + api_base_url: null + api_key: + env_vars: + - OPENAI_API_KEY + strict: true + type: env_var + generation_kwargs: {} + http_client_kwargs: null + max_retries: null + model: gpt-5.4-nano + organization: null + streaming_callback: null + timeout: null + tools: null + tools_strict: false + type: haystack.components.generators.chat.openai.OpenAIChatGenerator + confirmation_strategies: null + exit_conditions: + - text + max_agent_steps: 100 + raise_on_tool_invocation_failure: false + required_variables: null + state_schema: {} + streaming_callback: null + system_prompt: You are a research specialist. Search the web to find + information. Return a concise summary of your findings in 3-5 sentences. + tool_invoker_kwargs: null + tools: + - data: + component: + init_parameters: + allowed_domains: null + api_key: + env_vars: + - SERPERDEV_API_KEY + strict: true + type: env_var + exclude_subdomains: false + search_params: {} + top_k: 3 + type: haystack.components.websearch.serper_dev.SerperDevWebSearch + description: Search the web for current information on any topic + inputs_from_state: null + name: web_search + outputs_to_state: null + outputs_to_string: null + parameters: null + type: haystack.tools.component_tool.ComponentTool + user_prompt: null + type: haystack.components.agents.agent.Agent + description: A specialist that researches topics on the web + inputs_from_state: null + name: research_specialist + outputs_to_state: null + outputs_to_string: + source: last_message + parameters: null + type: haystack.tools.component_tool.ComponentTool + user_prompt: null + type: haystack.components.agents.agent.Agent +connection_type_validation: true +connections: [] +max_runs_per_component: 100 +metadata: {} +``` + +
+ ## Coordinator / Specialist Pattern The coordinator/specialist pattern cleanly splits responsibilities: the coordinator handles planning and delegation, while each specialist owns a focused toolset and a targeted system prompt. From cfc8f6842093f67b0868b2c5eb413a3020dd7d83 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Mon, 11 May 2026 10:25:17 +0200 Subject: [PATCH 12/12] fix description --- docs-website/docs/concepts/agents/multi-agent-systems.mdx | 3 ++- .../version-2.28/concepts/agents/multi-agent-systems.mdx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs-website/docs/concepts/agents/multi-agent-systems.mdx b/docs-website/docs/concepts/agents/multi-agent-systems.mdx index dc5a707863..0f9aaba083 100644 --- a/docs-website/docs/concepts/agents/multi-agent-systems.mdx +++ b/docs-website/docs/concepts/agents/multi-agent-systems.mdx @@ -113,7 +113,8 @@ result = coordinator.run( ) ``` -The full specialist configuration is captured inline when serialized. Wrap the coordinator in a `Pipeline` and call `pipeline.dumps()` to get the YAML, which can be loaded back with `Pipeline.loads()` — no Python code required. +The full specialist configuration is captured inline when serialized. +Wrap the coordinator in a `Pipeline` and call `pipeline.dumps()` to get the YAML, which can be loaded back with `Pipeline.loads()`.
View YAML diff --git a/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx b/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx index dc5a707863..0f9aaba083 100644 --- a/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx +++ b/docs-website/versioned_docs/version-2.28/concepts/agents/multi-agent-systems.mdx @@ -113,7 +113,8 @@ result = coordinator.run( ) ``` -The full specialist configuration is captured inline when serialized. Wrap the coordinator in a `Pipeline` and call `pipeline.dumps()` to get the YAML, which can be loaded back with `Pipeline.loads()` — no Python code required. +The full specialist configuration is captured inline when serialized. +Wrap the coordinator in a `Pipeline` and call `pipeline.dumps()` to get the YAML, which can be loaded back with `Pipeline.loads()`.
View YAML