How to Orchestrate Multi-Agent Workflows
In this guide, we will learn how to dynamically create and invoke multiple sub-agents within a single chat agent to orchestrate complex multi-agent workflows. This enables you to modularize roles, perspectives, or specialized behaviors, letting your main agent manage a collaborative or debate-style conversation between several sub-agents.
Prerequisites
Before you begin, make sure you have completed the Getting Started with the Agents SDK tutorial and the How to Create Agents that Use Tools guide.
Simple Example
Let's scaffold the code for the main agent and the simple sub-agent. Run the following commands in your project directory:
rag_agents new "chat_agent"
rag_agents new "sub_agent"
Open sub_agent.py
and paste the following code:
from typing import AsyncGenerator, List, Optional
from zav.agents_sdk import ChatAgentClassRegistry, ChatMessage, StreamableChatAgent
from zav.agents_sdk.adapters import ZAVChatCompletionClient
@ChatAgentClassRegistry.register()
class SubAgent(StreamableChatAgent):
agent_name = "sub_agent"
def __init__(
self, client: ZAVChatCompletionClient, system_prompt: Optional[str] = None
):
self.client = client
self.system_prompt = system_prompt
async def execute_streaming(
self, conversation: List[ChatMessage]
) -> AsyncGenerator[ChatMessage, None]:
response = await self.client.complete(
bot_setup_description=self.system_prompt,
messages=conversation,
stream=True,
)
async for chat_client_response in response:
if chat_client_response.error is not None:
raise chat_client_response.error
if chat_client_response.chat_completion is None:
raise Exception("No response from chat completion client")
yield ChatMessage.from_orm(chat_client_response.chat_completion)
Next, open chat_agent.py
and paste the following code:
from typing import AsyncGenerator, Dict, List
from zav.agents_sdk import (
AgentCreator,
ChatAgentClassRegistry,
ChatMessage,
ChatMessageSender,
StreamableChatAgent,
)
from zav.agents_sdk.adapters import ZAVChatCompletionClient
@ChatAgentClassRegistry.register()
class ChatAgent(StreamableChatAgent):
agent_name = "chat_agent"
def __init__(
self,
client: ZAVChatCompletionClient,
agent_creator: AgentCreator,
):
self.client = client
self.agent_creator = agent_creator
self.tools_registry.add(executable=self.__create_agent, name="create_agent")
self.tools_registry.add(executable=self.__call_agent, name="listen_to_agent")
self.__sub_agent_prompts: Dict[str, str] = {}
async def __create_agent(self, name: str, instructions: str):
"""
Creates a new agent with the specified name and behavioral instructions.
Args:
name (str):
The unique name you will use to refer to this agent.
It must be a short, descriptive identifier (e.g., 'CEO', 'Employee', 'HR').
instructions (str):
A detailed description of how this agent should behave in the debate.
Specify the agent's role, perspective, and any characteristics that should shape its responses
(e.g., 'You are a CEO. Argue for efficiency and profitability in all statements.').
"""
self.__sub_agent_prompts[name] = instructions
return f"Agent '{name}' created successfully."
async def __call_agent(self, name: str, input: str):
"""
Invites the specified agent to contribute to the conversation, using the provided summary as context.
Args:
name (str):
The name of the agent who should speak next.
This must match the name given when the agent was created.
input (str):
A concise summary of the current state of the conversation, including key arguments or points raised so far.
This will help the agent tailor their response and avoid repetition.
"""
if name not in self.__sub_agent_prompts:
return f"Agent '{name}' not found. Please create it first using the create_agent tool."
sub_agent = await self.agent_creator.create(
agent_identifier="sub_agent",
system_prompt=self.__sub_agent_prompts[name],
)
response = await sub_agent.execute(
[ChatMessage(content=input, sender=ChatMessageSender.USER)]
)
if not response:
return f"Agent '{name}' did not respond."
output = response.content
self.debug({"name": name, "input": input, "output": output})
return output
async def execute_streaming(
self, conversation: List[ChatMessage]
) -> AsyncGenerator[ChatMessage, None]:
response = await self.client.complete(
bot_setup_description="""You are a debate moderator for a multi-agent discussion.
Your task is to explore a given topic by orchestrating a debate between several agents, each representing a different role or perspective.
You have access to the following tools:
1. **create_agent**
* `name`: the unique name you will use to refer to the agent.
* `instructions`: a detailed description of how the agent should behave and the perspective it should represent in the debate. Clearly specify the role, point of view, and any relevant characteristics the agent should adopt. For example: "You are a CEO of a large corporation. Argue from a business perspective, prioritizing efficiency and profitability."
2. **listen_to_agent**
* `name`: the name of the agent you want to speak next.
* `input`: a concise summary of the current conversation, so the agent knows what has already been said and can respond or add new arguments.
**Your workflow:**
1. Identify at least three distinct perspectives or roles relevant to the debate topic.
2. For each role, use `create_agent` to create a subagent. Carefully craft the `instructions` so each agent has a clear, unique perspective.
3. Use `listen_to_agent` to let each subagent present their argument in turn, providing the current state of the conversation as `input`.
4. Continue the debate with additional rounds:
* After the first round, review the conversation.
* If agents have further clarifications, disagreements, or new points to add, use `listen_to_agent` again to allow rebuttals or follow-ups. Always provide an updated summary as `input`.
* **Repeat this process for multiple rounds** as long as new ideas or responses emerge.
5. **End the debate only when**:
* No agent has significant new arguments or rebuttals to add, OR
* All agents indicate they have nothing further to contribute, OR
* The conversation reaches a clear consensus or natural stopping point.
6. After the debate concludes, summarize the key arguments and points raised.
**Remember:**
* Each agent should only respond from their assigned perspective.
* The debate should be fair and balanced, allowing each perspective to be fully explored.
* You may ask an agent if they wish to add anything further before concluding.
**Begin by identifying the roles for this debate and proceed to create agents as described.**
""",
messages=conversation,
stream=True,
tools=self.tools_registry,
execute_tools=True,
log_fn=self.debug,
)
async for chat_client_response in response:
if chat_client_response.error is not None:
raise chat_client_response.error
if chat_client_response.chat_completion is None:
raise Exception("No response from chat completion client")
yield ChatMessage.from_orm(chat_client_response.chat_completion)
Using AgentCreator in Your Agent
Your agent can accept an AgentCreator
dependency to dynamically spawn and configure sub-agents at runtime. When you want to invoke a sub-agent, call the create
method:
sub_agent = await self.agent_creator.create(
agent_identifier="...",
system_prompt="...",
tools=[...],
bot_params={...},
conversation_context=ctx,
)
Parameter | Description |
---|---|
agent_identifier | Identifier matching an entry in your agent_setups.json . |
system_prompt | (optional) Override the sub-agent's system instructions. |
tools | (optional) List of Tool objects to register on the new agent. |
bot_params | (optional) Keyword arguments passed to the sub-agent's constructor. |
conversation_context | (optional) Initial ConversationContext for the sub-agent. |
The AgentCreator
utility will apply your agent_setups.json
configuration, wire up dependencies, and return a ready-to-use ChatAgent
instance.
Learn More
For deeper dives into the components used in this guide, see:
- Agent Setup Configuration: Learn how to define and merge settings in
agent_setups.json
in the How to Configure Agents guide. - Tool & ToolsRegistry: Understand how to register and define tools in the How to Create Agents that Use Tools guide and refer to the Tool API reference.
- ConversationContext: See how to pass static or scoped context via the
conversation_context
payload in the How to Configure Agents guide.