How to Build an Orchestrator-Workers Agents Pattern#

Prerequisites

This guide assumes you have already defined either a Flow or an Agent using PyAgentSpec and exported its Agent Spec configuration.

This guide demonstrates how to:

  1. Define the configuration for an LLM model

  2. Define a tool to retrieve additional information about a user

  3. Define an orchestrator agent

  4. Define a set of worker agents

  5. Wrap all the components in a flow

  6. Export the Agent Spec configuration of the flow

This how-to guide demonstrates how to build an Oracle-themed IT assistant using an orchestrator-workers pattern. The orchestrator attempts to understand the user’s problem and, based on its topic—network, device, or account—redirects the conversation to an agent which has expertise in the topic.

The assistant is implemented as a Flow. This Flow takes the username of the user as input and starts by retrieving additional information about the user via a ServerTool call. Then the orchestrator starts the conversation with the user, identifies the topic of the problem, and redirects the conversation to the appropriate expert agent. Once the problem is solved by the expert, the conversation goes back to the orchestrator to deal with further issues, if needed.

Basic implementation#

1. Define the LLM model#

The decision mechanism of Agents is powered by a Large Language Model. Defining the agent with Agent Spec requires to pass the configuration for the LLM. There are several options such as using OCI GenAI Service or self hosting a model with vLLM.

Start by defining the LLM configuration to be shared across all agents. This example uses a vLLM instance running Llama3.3 70B.

from pyagentspec.llms import OciGenAiConfig
from pyagentspec.llms.ociclientconfig import OciClientConfigWithApiKey

client_config = OciClientConfigWithApiKey(
    name="Oci Client Config",
    service_endpoint="https://url-to-service-endpoint.com",
    auth_profile="DEFAULT",
    auth_file_location="~/.oci/config"
)

llm_config = OciGenAiConfig(
    name="Oci GenAI Config",
    model_id="provider.model-id",
    compartment_id="compartment-id",
    client_config=client_config,
)

2. Define a tool#

Define a tool responsible for retrieving additional information about the user. This is a simple ServerTool that accepts a username as input and returns a single string containing user-related information.

from pyagentspec.property import StringProperty
from pyagentspec.tools import ServerTool

username_property = StringProperty(title="username")
user_information_property = StringProperty(title="user_information")

tool = ServerTool(
    name="Gather user information tool",
    description="Tool that gathers user information based on its username",
    inputs=[username_property],
    outputs=[user_information_property],
)

3. Define an orchestrator agent#

The first agent to define is the orchestrator. As anticipated in the introduction, the goal of this agent is to understand what is the topic of the user’s problem, so that it can redirect the conversation to the appropriate expert agent.

In addition to identifying the problem topic, the orchestrator is also responsible for producing a summary of the conversation. According to the Agent Spec specification, the conversations with sub-agents are isolated from one another. Therefore, relevant parts of the conversation with the orchestrator are passed along to expert agents. This ensures that any user-provided details remain available throughout the interaction.

from pyagentspec.agent import Agent
from pyagentspec.property import StringProperty

conversation_summary_property = StringProperty(
    title="conversation_summary", default="(No conversation)"
)
user_information_property = StringProperty(title="user_information")
topic_property = StringProperty(title="topic")

orchestrator_agent = Agent(
    name="Orchestrator Agent",
    description="Orchestrator agent that routes to different experts",
    inputs=[conversation_summary_property, user_information_property],
    llm_config=llm_config,
    outputs=[conversation_summary_property, topic_property],
    system_prompt="""You are an orchestrator assistant, and you are part of a larger
assistant called Oracle IT Assistant. You just have to understand the topic
of the problem, do not try to solve it.

CONTEXT
-------

The information about the user you are talking to is the following (in json
format):
```
{{user_information}}
```

The user might have already had a past conversation with the agent, here''s
the summary:
```
{{conversation_summary}}
```

TASK
----

Your task is to understand what is the CURRENT problem of the user, and based
on that, output the topic of the problem.
Do not resolve the specific problem. Someone else will take care of the solution.
You just have to understand and output the topic of the problem. Don''t ask
for confirmation to the user, when you know the topic, go ahead.
If the user had a problem in the past conversation summary, ask if the user
has other problems to solve.
Do not get the topic from the past conversation summary.

The available problem topics are:
- network
- device
- account
- unknown

If the user is done and wants to exit the conversation, you can output "unknown"
as topic. In that case, thank the user and say goodbye.

Another output you should provide is a summary of the past conversation you
had with the user. It should be a short summary, 2 sentences at most.

Once you understood the topic, submit it as the output, submit the conversation
summary, and exit.
Remember, do NOT try to solve the problem yourself.

If there''s no past conversation summary, do not talk about it in your messages.

Try to personalize your messages when you talk to the user.
Be always kind and nice.""",
    tools=[],
)

4. Define a set of worker agents#

Next define a set of expert agents—one for each topic: network, device, and account. Each agent shares the same basic prompt structure, but provides specific information regarding the topic (e.g., through RAG tools). This would improve the quality of the overall assistant.

from pyagentspec.agent import Agent
from pyagentspec.property import StringProperty

conversation_summary_property = StringProperty(title="conversation_summary")

account_agent = Agent(
    name="Account Agent",
    description="Expert in Oracle Account setup",
    llm_config=llm_config,
    system_prompt="""You are an expert assistant, part of the Oracle IT support assistant.
Your domain of expertise is Oracle account setup.

Please assist the user in solving his problem.


Here''s a summary of the ongoing conversation:

{{conversation_summary}}


When the user thinks that the problem is solved, or if you do not know how to
solve the problem, just submit and exit. Do not mention to the user that you
are submitting, just thank the user.


You should provide as an output a summary of the past conversation you had with
the user. It should be a short summary, 2 sentences at most. Do NOT submit the
conversation summary until the user says that the problem is solved.""",
    inputs=[conversation_summary_property],
    outputs=[conversation_summary_property],
)

network_agent = Agent(
    name="Network Agent",
    description="Expert in Oracle networking setup",
    llm_config=llm_config,
    system_prompt="""You are an expert assistant, part of the Oracle IT support assistant.
Your domain of expertise is computer networks.

Please assist the user in solving his problem.


Here''s a summary of the ongoing conversation:

{{conversation_summary}}


When the user thinks that the problem is solved, or if you do not know how to
solve the problem, just submit and exit. Do not mention to the user that you
are submitting, just thank the user.


You should provide as an output a summary of the past conversation you had with
the user. It should be a short summary, 2 sentences at most. Do NOT submit the
conversation summary until the user says that the problem is solved.""",
    inputs=[conversation_summary_property],
    outputs=[conversation_summary_property],
)

device_agent = Agent(
    name="Device Agent",
    description="Expert in Oracle devices setup",
    llm_config=llm_config,
    system_prompt="""You are an expert assistant, part of the Oracle IT support assistant.
Your domain of expertise is electronic devices.

Please assist the user in solving his problem.


Here''s a summary of the ongoing conversation:

{{conversation_summary}}


When the user thinks that the problem is solved, or if you do not know how to
solve the problem, just submit and exit. Do not mention to the user that you
are submitting, just thank the user.


You should provide as an output a summary of the past conversation you had with
the user. It should be a short summary, 2 sentences at most. Do NOT submit the
conversation summary until the user says that the problem is solved.""",
    inputs=[conversation_summary_property],
    outputs=[conversation_summary_property],
)

5. Wrap all the components in a flow#

With all internal components defined, the next step is to construct the flow. This involves building the corresponding nodes and connecting them to define both the execution order and the data dependencies.

Nodes#

Define the nodes that compose the flow. One node is required for each previously defined component:

  • A tool node for gathering user information

  • An agent node for the orchestrator

  • One agent node per expert worker (three in total)

Additionally, include a StartNode and an EndNode to mark the entry and exit points of the flow. A BranchingNode is also required to route the conversation to the appropriate expert agent based on the topic identified by the orchestrator.

from pyagentspec.flows.nodes import (
    AgentNode,
    BranchingNode,
    EndNode,
    StartNode,
    ToolNode,
)

orchestrator_agent_node = AgentNode(
    name="Orchestrator Agent Execution",
    agent=orchestrator_agent,
)

account_agent_node = AgentNode(
    name="Account Agent Execution",
    agent=account_agent,
)

network_agent_node = AgentNode(
    name="Network Agent Execution",
    agent=network_agent,
)

device_agent_node = AgentNode(
    name="Device Agent Execution",
    agent=device_agent,
)

tool_node = ToolNode(
    name="Get user info tool Execution",
    tool=tool,
)

branching_node = BranchingNode(
    name="Branching",
    inputs=[topic_property],
    mapping={
        "account": "account",
        "device": "device",
        "network": "network",
    },
)

start_node = StartNode(name="Start", inputs=[username_property])
end_node = EndNode(name="End")

Control flow edges#

Now define the control flow edges to specify the execution order of the nodes in the flow.

from pyagentspec.flows.edges import ControlFlowEdge

control_flow_edges = [
    ControlFlowEdge(name="start_to_tool", from_node=start_node, to_node=tool_node),
    ControlFlowEdge(
        name="tool_to_orchestrator", from_node=tool_node, to_node=orchestrator_agent_node
    ),
    ControlFlowEdge(
        name="orchestrator_to_branching", from_node=orchestrator_agent_node, to_node=branching_node
    ),
    ControlFlowEdge(
        name="branching_to_network",
        from_node=branching_node,
        to_node=network_agent_node,
        from_branch="network",
    ),
    ControlFlowEdge(
        name="branching_to_device",
        from_node=branching_node,
        to_node=device_agent_node,
        from_branch="device",
    ),
    ControlFlowEdge(
        name="branching_to_account",
        from_node=branching_node,
        to_node=account_agent_node,
        from_branch="account",
    ),
    ControlFlowEdge(
        name="branching_to_end", from_node=branching_node, to_node=end_node, from_branch="default"
    ),
    ControlFlowEdge(
        name="network_to_orchestrator",
        from_node=network_agent_node,
        to_node=orchestrator_agent_node,
    ),
    ControlFlowEdge(
        name="device_to_orchestrator", from_node=device_agent_node, to_node=orchestrator_agent_node
    ),
    ControlFlowEdge(
        name="account_to_orchestrator",
        from_node=account_agent_node,
        to_node=orchestrator_agent_node,
    ),
]

Data flow edges#

Next the data connections between inputs and outputs.

from pyagentspec.flows.edges import DataFlowEdge

data_flow_edges = [
    DataFlowEdge(
        name="start_to_tool_username",
        source_node=start_node,
        source_output="username",
        destination_node=tool_node,
        destination_input="username",
    ),
    DataFlowEdge(
        name="tool_to_orchestrator_user_information",
        source_node=tool_node,
        source_output="user_information",
        destination_node=orchestrator_agent_node,
        destination_input="user_information",
    ),
    DataFlowEdge(
        name="tool_to_orchestrator_topic",
        source_node=orchestrator_agent_node,
        source_output="topic",
        destination_node=branching_node,
        destination_input="topic",
    ),
    DataFlowEdge(
        name="orchestrator_to_device_conversation_summary",
        source_node=orchestrator_agent_node,
        source_output="conversation_summary",
        destination_node=device_agent_node,
        destination_input="conversation_summary",
    ),
    DataFlowEdge(
        name="orchestrator_to_network_conversation_summary",
        source_node=orchestrator_agent_node,
        source_output="conversation_summary",
        destination_node=network_agent_node,
        destination_input="conversation_summary",
    ),
    DataFlowEdge(
        name="orchestrator_to_account_conversation_summary",
        source_node=orchestrator_agent_node,
        source_output="conversation_summary",
        destination_node=account_agent_node,
        destination_input="conversation_summary",
    ),
    DataFlowEdge(
        name="network_to_orchestrator_conversation_summary",
        source_node=network_agent_node,
        source_output="conversation_summary",
        destination_node=orchestrator_agent_node,
        destination_input="conversation_summary",
    ),
    DataFlowEdge(
        name="device_to_orchestrator_conversation_summary",
        source_node=device_agent_node,
        source_output="conversation_summary",
        destination_node=orchestrator_agent_node,
        destination_input="conversation_summary",
    ),
    DataFlowEdge(
        name="account_to_orchestrator_conversation_summary",
        source_node=account_agent_node,
        source_output="conversation_summary",
        destination_node=orchestrator_agent_node,
        destination_input="conversation_summary",
    ),
]

Flow#

Finally, combine all components—nodes and edges—to construct the complete Flow.

from pyagentspec.flows.flow import Flow

flow = Flow(
    name="Oracle IT Assistant Flow",
    description="Flow of the Oracle IT assistant",
    start_node=start_node,
    nodes=[
        start_node,
        tool_node,
        orchestrator_agent_node,
        branching_node,
        network_agent_node,
        device_agent_node,
        account_agent_node,
        end_node,
    ],
    control_flow_connections=control_flow_edges,
    data_flow_connections=data_flow_edges,
    inputs=[username_property],
)

Agent Spec Serialization#

You can export the assistant configuration using the AgentSpecSerializer.

from pyagentspec.serialization import AgentSpecSerializer

serialized_flow = AgentSpecSerializer().to_json(flow)

Here is what the Agent Spec representation will look like ↓

Click here to see the assistant configuration.
{
  "component_type": "Flow",
  "id": "7372e2a2-7691-4406-93e6-2adee5af9aa5",
  "name": "Oracle IT Assistant Flow",
  "description": "Flow of the Oracle IT assistant",
  "metadata": {},
  "inputs": [
    {
      "title": "username",
      "type": "string"
    }
  ],
  "outputs": [],
  "start_node": {
    "$component_ref": "aa481a00-1e03-4597-8bba-d1dfd2475476"
  },
  "nodes": [
    {
      "$component_ref": "aa481a00-1e03-4597-8bba-d1dfd2475476"
    },
    {
      "$component_ref": "fe198288-9580-45ae-8a6b-35c2aca425a4"
    },
    {
      "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
    },
    {
      "$component_ref": "1bd5b090-a79d-4018-83ca-52add44f3f22"
    },
    {
      "$component_ref": "eb4996ef-36e3-4625-929b-e8ff00ffdab0"
    },
    {
      "$component_ref": "1dc49afa-b4fa-4caa-8511-d3f522368517"
    },
    {
      "$component_ref": "7b31834e-cd6c-4529-9c4c-35be300da332"
    },
    {
      "$component_ref": "5f87c9c1-89cb-46c8-9b0a-ca5b3cca4941"
    }
  ],
  "control_flow_connections": [
    {
      "component_type": "ControlFlowEdge",
      "id": "9538ffc7-64f9-4510-a0b3-d7093975f160",
      "name": "start_to_tool",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "aa481a00-1e03-4597-8bba-d1dfd2475476"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "fe198288-9580-45ae-8a6b-35c2aca425a4"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "3c118175-8003-47b0-8046-715ee1cc3ba0",
      "name": "tool_to_orchestrator",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "fe198288-9580-45ae-8a6b-35c2aca425a4"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "b857d37b-8f37-49b6-9a18-a9090098c08f",
      "name": "orchestrator_to_branching",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "1bd5b090-a79d-4018-83ca-52add44f3f22"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "d9d91a68-20ed-4793-9a46-380c050c76b0",
      "name": "branching_to_network",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "1bd5b090-a79d-4018-83ca-52add44f3f22"
      },
      "from_branch": "network",
      "to_node": {
        "$component_ref": "eb4996ef-36e3-4625-929b-e8ff00ffdab0"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "4a5299d1-a6ea-4231-9ad4-77d729553615",
      "name": "branching_to_device",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "1bd5b090-a79d-4018-83ca-52add44f3f22"
      },
      "from_branch": "device",
      "to_node": {
        "$component_ref": "1dc49afa-b4fa-4caa-8511-d3f522368517"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "3162de23-ff02-4d5a-97ec-871815754eee",
      "name": "branching_to_account",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "1bd5b090-a79d-4018-83ca-52add44f3f22"
      },
      "from_branch": "account",
      "to_node": {
        "$component_ref": "7b31834e-cd6c-4529-9c4c-35be300da332"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "15030553-3b2d-46e4-bfa8-147039a153cd",
      "name": "branching_to_end",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "1bd5b090-a79d-4018-83ca-52add44f3f22"
      },
      "from_branch": "default",
      "to_node": {
        "$component_ref": "5f87c9c1-89cb-46c8-9b0a-ca5b3cca4941"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "51d636d4-64bd-4106-a26f-7d78563064fa",
      "name": "network_to_orchestrator",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "eb4996ef-36e3-4625-929b-e8ff00ffdab0"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "c69ab90a-1eb9-4dce-ac45-94afa1eded9c",
      "name": "device_to_orchestrator",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "1dc49afa-b4fa-4caa-8511-d3f522368517"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "bf37df23-5cd7-4674-97ec-8e5e1f31c166",
      "name": "account_to_orchestrator",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "7b31834e-cd6c-4529-9c4c-35be300da332"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      }
    }
  ],
  "data_flow_connections": [
    {
      "component_type": "DataFlowEdge",
      "id": "d95d77f8-4033-4ce1-aab2-4dbe40a9c317",
      "name": "start_to_tool_username",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "aa481a00-1e03-4597-8bba-d1dfd2475476"
      },
      "source_output": "username",
      "destination_node": {
        "$component_ref": "fe198288-9580-45ae-8a6b-35c2aca425a4"
      },
      "destination_input": "username"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "38e2156b-b9f7-43c5-a8fb-b6bee95793f7",
      "name": "tool_to_orchestrator_user_information",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "fe198288-9580-45ae-8a6b-35c2aca425a4"
      },
      "source_output": "user_information",
      "destination_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      },
      "destination_input": "user_information"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "b85c0338-e594-43cd-9519-d119f46a050b",
      "name": "tool_to_orchestrator_topic",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      },
      "source_output": "topic",
      "destination_node": {
        "$component_ref": "1bd5b090-a79d-4018-83ca-52add44f3f22"
      },
      "destination_input": "topic"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "f909cbe7-5d60-4d2e-aad6-79498d3bc394",
      "name": "orchestrator_to_device_conversation_summary",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      },
      "source_output": "conversation_summary",
      "destination_node": {
        "$component_ref": "1dc49afa-b4fa-4caa-8511-d3f522368517"
      },
      "destination_input": "conversation_summary"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "af1edc28-a587-47bc-89e9-41c7704cf340",
      "name": "orchestrator_to_network_conversation_summary",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      },
      "source_output": "conversation_summary",
      "destination_node": {
        "$component_ref": "eb4996ef-36e3-4625-929b-e8ff00ffdab0"
      },
      "destination_input": "conversation_summary"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "a42cc15b-6b8a-433f-9f77-8100b23b5e61",
      "name": "orchestrator_to_account_conversation_summary",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      },
      "source_output": "conversation_summary",
      "destination_node": {
        "$component_ref": "7b31834e-cd6c-4529-9c4c-35be300da332"
      },
      "destination_input": "conversation_summary"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "64fa2382-1adb-416f-865d-91711aa3d445",
      "name": "network_to_orchestrator_conversation_summary",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "eb4996ef-36e3-4625-929b-e8ff00ffdab0"
      },
      "source_output": "conversation_summary",
      "destination_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      },
      "destination_input": "conversation_summary"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "aeb8e39f-dcee-4a84-b0e1-6ae243118596",
      "name": "device_to_orchestrator_conversation_summary",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "1dc49afa-b4fa-4caa-8511-d3f522368517"
      },
      "source_output": "conversation_summary",
      "destination_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      },
      "destination_input": "conversation_summary"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "8fc88f02-1050-472f-8a5e-e69290b1cbb6",
      "name": "account_to_orchestrator_conversation_summary",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "7b31834e-cd6c-4529-9c4c-35be300da332"
      },
      "source_output": "conversation_summary",
      "destination_node": {
        "$component_ref": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3"
      },
      "destination_input": "conversation_summary"
    }
  ],
  "$referenced_components": {
    "aa481a00-1e03-4597-8bba-d1dfd2475476": {
      "component_type": "StartNode",
      "id": "aa481a00-1e03-4597-8bba-d1dfd2475476",
      "name": "Start",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "username",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "username",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ]
    },
    "fe198288-9580-45ae-8a6b-35c2aca425a4": {
      "component_type": "ToolNode",
      "id": "fe198288-9580-45ae-8a6b-35c2aca425a4",
      "name": "Get user info tool Execution",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "username",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "user_information",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ],
      "tool": {
        "component_type": "ServerTool",
        "id": "70c941a4-0ec8-4240-8c8b-52a7c099ce37",
        "name": "Gather user information tool",
        "description": "Tool that gathers user information based on its username",
        "metadata": {},
        "inputs": [
          {
            "title": "username",
            "type": "string"
          }
        ],
        "outputs": [
          {
            "title": "user_information",
            "type": "string"
          }
        ]
      }
    },
    "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3": {
      "component_type": "AgentNode",
      "id": "17b0b7ed-13a9-4c7a-9fbc-65bc1b06eba3",
      "name": "Orchestrator Agent Execution",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "default": "(No conversation)",
          "title": "conversation_summary",
          "type": "string"
        },
        {
          "title": "user_information",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "default": "(No conversation)",
          "title": "conversation_summary",
          "type": "string"
        },
        {
          "title": "topic",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ],
      "agent": {
        "component_type": "Agent",
        "id": "6c270f25-1ecf-4df1-8a8b-ac8eeaec1ef7",
        "name": "Orchestrator Agent",
        "description": "Orchestrator agent that routes to different experts",
        "metadata": {},
        "inputs": [
          {
            "default": "(No conversation)",
            "title": "conversation_summary",
            "type": "string"
          },
          {
            "title": "user_information",
            "type": "string"
          }
        ],
        "outputs": [
          {
            "default": "(No conversation)",
            "title": "conversation_summary",
            "type": "string"
          },
          {
            "title": "topic",
            "type": "string"
          }
        ],
        "llm_config": {
          "$component_ref": "5d22f76d-2050-4f50-a2da-6ec3e1ea7750"
        },
        "system_prompt": "You are an orchestrator assistant, and you are part of a larger\nassistant called Oracle IT Assistant. You just have to understand the topic\nof the problem, do not try to solve it.\n\nCONTEXT\n-------\n\nThe information about the user you are talking to is the following (in json\nformat):\n```\n{{user_information}}\n```\n\nThe user might have already had a past conversation with the agent, here''s\nthe summary:\n```\n{{conversation_summary}}\n```\n\nTASK\n----\n\nYour task is to understand what is the CURRENT problem of the user, and based\non that, output the topic of the problem.\nDo not resolve the specific problem. Someone else will take care of the solution.\nYou just have to understand and output the topic of the problem. Don''t ask\nfor confirmation to the user, when you know the topic, go ahead.\nIf the user had a problem in the past conversation summary, ask if the user\nhas other problems to solve.\nDo not get the topic from the past conversation summary.\n\nThe available problem topics are:\n- network\n- device\n- account\n- unknown\n\nIf the user is done and wants to exit the conversation, you can output \"unknown\"\nas topic. In that case, thank the user and say goodbye.\n\nAnother output you should provide is a summary of the past conversation you\nhad with the user. It should be a short summary, 2 sentences at most.\n\nOnce you understood the topic, submit it as the output, submit the conversation\nsummary, and exit.\nRemember, do NOT try to solve the problem yourself.\n\nIf there''s no past conversation summary, do not talk about it in your messages.\n\nTry to personalize your messages when you talk to the user.\nBe always kind an nice.",
        "tools": []
      }
    },
    "5d22f76d-2050-4f50-a2da-6ec3e1ea7750": {
      "component_type": "VllmConfig",
      "id": "5d22f76d-2050-4f50-a2da-6ec3e1ea7750",
      "name": "meta.llama-3.3-70b-instruct",
      "description": null,
      "metadata": {},
      "default_generation_parameters": {},
      "url": "LLAMA_PLACEHOLDER_LINK",
      "model_id": "/storage/models/Llama-3.3-70B-Instruct"
    },
    "1bd5b090-a79d-4018-83ca-52add44f3f22": {
      "component_type": "BranchingNode",
      "id": "1bd5b090-a79d-4018-83ca-52add44f3f22",
      "name": "Branching",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "topic",
          "type": "string"
        }
      ],
      "outputs": [],
      "branches": [
        "account",
        "default",
        "device",
        "network"
      ],
      "mapping": {
        "account": "account",
        "device": "device",
        "network": "network"
      }
    },
    "eb4996ef-36e3-4625-929b-e8ff00ffdab0": {
      "component_type": "AgentNode",
      "id": "eb4996ef-36e3-4625-929b-e8ff00ffdab0",
      "name": "Network Agent Execution",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "conversation_summary",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "conversation_summary",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ],
      "agent": {
        "component_type": "Agent",
        "id": "66fd543f-3fc5-4bc2-bb67-9d5b702f0315",
        "name": "Network Agent",
        "description": "Expert in Oracle networking setup",
        "metadata": {},
        "inputs": [
          {
            "title": "conversation_summary",
            "type": "string"
          }
        ],
        "outputs": [
          {
            "title": "conversation_summary",
            "type": "string"
          }
        ],
        "llm_config": {
          "$component_ref": "5d22f76d-2050-4f50-a2da-6ec3e1ea7750"
        },
        "system_prompt": "You are an expert assistant, part of the Oracle IT support assistant.\nYour domain of expertise is computer networks.\n\nPlease assist the user in solving his problem.\n\n\nHere''s a summary of the ongoing conversation:\n\n{{conversation_summary}}\n\n\nWhen the user thinks that the problem is solved, or if you do not know how to\nsolve the problem, just submit and exit. Do not mention to the user that you\nare submitting, just thank the user.\n\n\nYou should provide as an output a summary of the past conversation you had with\nthe user. It should be a short summary, 2 sentences at most. Do NOT submit the\nconversation summary until the user says that the problem is solved.",
        "tools": []
      }
    },
    "1dc49afa-b4fa-4caa-8511-d3f522368517": {
      "component_type": "AgentNode",
      "id": "1dc49afa-b4fa-4caa-8511-d3f522368517",
      "name": "Device Agent Execution",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "conversation_summary",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "conversation_summary",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ],
      "agent": {
        "component_type": "Agent",
        "id": "4b2105f6-0d8a-45ee-8ea0-fb92582c574e",
        "name": "Device Agent",
        "description": "Expert in Oracle devices setup",
        "metadata": {},
        "inputs": [
          {
            "title": "conversation_summary",
            "type": "string"
          }
        ],
        "outputs": [
          {
            "title": "conversation_summary",
            "type": "string"
          }
        ],
        "llm_config": {
          "$component_ref": "5d22f76d-2050-4f50-a2da-6ec3e1ea7750"
        },
        "system_prompt": "You are an expert assistant, part of the Oracle IT support assistant.\nYour domain of expertise is electronic devices.\n\nPlease assist the user in solving his problem.\n\n\nHere''s a summary of the ongoing conversation:\n\n{{conversation_summary}}\n\n\nWhen the user thinks that the problem is solved, or if you do not know how to\nsolve the problem, just submit and exit. Do not mention to the user that you\nare submitting, just thank the user.\n\n\nYou should provide as an output a summary of the past conversation you had with\nthe user. It should be a short summary, 2 sentences at most. Do NOT submit the\nconversation summary until the user says that the problem is solved.",
        "tools": []
      }
    },
    "7b31834e-cd6c-4529-9c4c-35be300da332": {
      "component_type": "AgentNode",
      "id": "7b31834e-cd6c-4529-9c4c-35be300da332",
      "name": "Account Agent Execution",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "conversation_summary",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "conversation_summary",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ],
      "agent": {
        "component_type": "Agent",
        "id": "185731db-fa23-4abd-ac0c-26cc6ebd4f5b",
        "name": "Account Agent",
        "description": "Expert in Oracle Account setup",
        "metadata": {},
        "inputs": [
          {
            "title": "conversation_summary",
            "type": "string"
          }
        ],
        "outputs": [
          {
            "title": "conversation_summary",
            "type": "string"
          }
        ],
        "llm_config": {
          "$component_ref": "5d22f76d-2050-4f50-a2da-6ec3e1ea7750"
        },
        "system_prompt": "You are an expert assistant, part of the Oracle IT support assistant.\nYour domain of expertise is Oracle account setup.\n\nPlease assist the user in solving his problem.\n\n\nHere''s a summary of the ongoing conversation:\n\n{{conversation_summary}}\n\n\nWhen the user thinks that the problem is solved, or if you do not know how to\nsolve the problem, just submit and exit. Do not mention to the user that you\nare submitting, just thank the user.\n\n\nYou should provide as an output a summary of the past conversation you had with\nthe user. It should be a short summary, 2 sentences at most. Do NOT submit the\nconversation summary until the user says that the problem is solved.",
        "tools": []
      }
    },
    "5f87c9c1-89cb-46c8-9b0a-ca5b3cca4941": {
      "component_type": "EndNode",
      "id": "5f87c9c1-89cb-46c8-9b0a-ca5b3cca4941",
      "name": "End",
      "description": null,
      "metadata": {},
      "inputs": [],
      "outputs": [],
      "branches": [],
      "branch_name": "next"
    }
  },
  "agentspec_version": "25.4.1"
}

Recap#

This how-to guide covered how to:

  1. Define the configuration for an LLM model

  2. Define a tool to retrieve additional information about a user

  3. Define an orchestrator agent

  4. Define a set of worker agents

  5. Wrap all the components in a flow

  6. Export the Agent Spec configuration of the flow

Below is the complete code from this guide.
  1# LLama vLLM
  2
  3from pyagentspec.llms import VllmConfig
  4
  5llm_config = VllmConfig(
  6    model_id="/storage/models/Llama-3.3-70B-Instruct",
  7    url=" LLAMA_PLACEHOLDER_LINK",
  8    name="meta.llama-3.3-70b-instruct",
  9)
 10
 11
 12# Get user information ServerTool
 13
 14from pyagentspec.property import StringProperty
 15from pyagentspec.tools import ServerTool
 16
 17username_property = StringProperty(title="username")
 18user_information_property = StringProperty(title="user_information")
 19
 20tool = ServerTool(
 21    name="Gather user information tool",
 22    description="Tool that gathers user information based on its username",
 23    inputs=[username_property],
 24    outputs=[user_information_property],
 25)
 26
 27
 28# Orchestrator agent
 29
 30from pyagentspec.agent import Agent
 31from pyagentspec.property import StringProperty
 32
 33conversation_summary_property = StringProperty(
 34    title="conversation_summary", default="(No conversation)"
 35)
 36user_information_property = StringProperty(title="user_information")
 37topic_property = StringProperty(title="topic")
 38
 39orchestrator_agent = Agent(
 40    name="Orchestrator Agent",
 41    description="Orchestrator agent that routes to different experts",
 42    inputs=[conversation_summary_property, user_information_property],
 43    llm_config=llm_config,
 44    outputs=[conversation_summary_property, topic_property],
 45    system_prompt="""You are an orchestrator assistant, and you are part of a larger
 46assistant called Oracle IT Assistant. You just have to understand the topic
 47of the problem, do not try to solve it.
 48
 49CONTEXT
 50-------
 51
 52The information about the user you are talking to is the following (in json
 53format):
 54```
 55{{user_information}}
 56```
 57
 58The user might have already had a past conversation with the agent, here''s
 59the summary:
 60```
 61{{conversation_summary}}
 62```
 63
 64TASK
 65----
 66
 67Your task is to understand what is the CURRENT problem of the user, and based
 68on that, output the topic of the problem.
 69Do not resolve the specific problem. Someone else will take care of the solution.
 70You just have to understand and output the topic of the problem. Don''t ask
 71for confirmation to the user, when you know the topic, go ahead.
 72If the user had a problem in the past conversation summary, ask if the user
 73has other problems to solve.
 74Do not get the topic from the past conversation summary.
 75
 76The available problem topics are:
 77- network
 78- device
 79- account
 80- unknown
 81
 82If the user is done and wants to exit the conversation, you can output "unknown"
 83as topic. In that case, thank the user and say goodbye.
 84
 85Another output you should provide is a summary of the past conversation you
 86had with the user. It should be a short summary, 2 sentences at most.
 87
 88Once you understood the topic, submit it as the output, submit the conversation
 89summary, and exit.
 90Remember, do NOT try to solve the problem yourself.
 91
 92If there''s no past conversation summary, do not talk about it in your messages.
 93
 94Try to personalize your messages when you talk to the user.
 95Be always kind an nice.""",
 96    tools=[],
 97)
 98
 99# Expert agents
100
101from pyagentspec.agent import Agent
102from pyagentspec.property import StringProperty
103
104conversation_summary_property = StringProperty(title="conversation_summary")
105
106account_agent = Agent(
107    name="Account Agent",
108    description="Expert in Oracle Account setup",
109    llm_config=llm_config,
110    system_prompt="""You are an expert assistant, part of the Oracle IT support assistant.
111Your domain of expertise is Oracle account setup.
112
113Please assist the user in solving his problem.
114
115
116Here''s a summary of the ongoing conversation:
117
118{{conversation_summary}}
119
120
121When the user thinks that the problem is solved, or if you do not know how to
122solve the problem, just submit and exit. Do not mention to the user that you
123are submitting, just thank the user.
124
125
126You should provide as an output a summary of the past conversation you had with
127the user. It should be a short summary, 2 sentences at most. Do NOT submit the
128conversation summary until the user says that the problem is solved.""",
129    inputs=[conversation_summary_property],
130    outputs=[conversation_summary_property],
131)
132
133network_agent = Agent(
134    name="Network Agent",
135    description="Expert in Oracle networking setup",
136    llm_config=llm_config,
137    system_prompt="""You are an expert assistant, part of the Oracle IT support assistant.
138Your domain of expertise is computer networks.
139
140Please assist the user in solving his problem.
141
142
143Here''s a summary of the ongoing conversation:
144
145{{conversation_summary}}
146
147
148When the user thinks that the problem is solved, or if you do not know how to
149solve the problem, just submit and exit. Do not mention to the user that you
150are submitting, just thank the user.
151
152
153You should provide as an output a summary of the past conversation you had with
154the user. It should be a short summary, 2 sentences at most. Do NOT submit the
155conversation summary until the user says that the problem is solved.""",
156    inputs=[conversation_summary_property],
157    outputs=[conversation_summary_property],
158)
159
160device_agent = Agent(
161    name="Device Agent",
162    description="Expert in Oracle devices setup",
163    llm_config=llm_config,
164    system_prompt="""You are an expert assistant, part of the Oracle IT support assistant.
165Your domain of expertise is electronic devices.
166
167Please assist the user in solving his problem.
168
169
170Here''s a summary of the ongoing conversation:
171
172{{conversation_summary}}
173
174
175When the user thinks that the problem is solved, or if you do not know how to
176solve the problem, just submit and exit. Do not mention to the user that you
177are submitting, just thank the user.
178
179
180You should provide as an output a summary of the past conversation you had with
181the user. It should be a short summary, 2 sentences at most. Do NOT submit the
182conversation summary until the user says that the problem is solved.""",
183    inputs=[conversation_summary_property],
184    outputs=[conversation_summary_property],
185)
186
187# Flow Nodes
188
189from pyagentspec.flows.nodes import (
190    AgentNode,
191    BranchingNode,
192    EndNode,
193    StartNode,
194    ToolNode,
195)
196
197orchestrator_agent_node = AgentNode(
198    name="Orchestrator Agent Execution",
199    agent=orchestrator_agent,
200)
201
202account_agent_node = AgentNode(
203    name="Account Agent Execution",
204    agent=account_agent,
205)
206
207network_agent_node = AgentNode(
208    name="Network Agent Execution",
209    agent=network_agent,
210)
211
212device_agent_node = AgentNode(
213    name="Device Agent Execution",
214    agent=device_agent,
215)
216
217tool_node = ToolNode(
218    name="Get user info tool Execution",
219    tool=tool,
220)
221
222branching_node = BranchingNode(
223    name="Branching",
224    inputs=[topic_property],
225    mapping={
226        "account": "account",
227        "device": "device",
228        "network": "network",
229    },
230)
231
232start_node = StartNode(name="Start", inputs=[username_property])
233end_node = EndNode(name="End")
234
235
236# Control flow edges
237
238from pyagentspec.flows.edges import ControlFlowEdge
239
240control_flow_edges = [
241    ControlFlowEdge(name="start_to_tool", from_node=start_node, to_node=tool_node),
242    ControlFlowEdge(
243        name="tool_to_orchestrator", from_node=tool_node, to_node=orchestrator_agent_node
244    ),
245    ControlFlowEdge(
246        name="orchestrator_to_branching", from_node=orchestrator_agent_node, to_node=branching_node
247    ),
248    ControlFlowEdge(
249        name="branching_to_network",
250        from_node=branching_node,
251        to_node=network_agent_node,
252        from_branch="network",
253    ),
254    ControlFlowEdge(
255        name="branching_to_device",
256        from_node=branching_node,
257        to_node=device_agent_node,
258        from_branch="device",
259    ),
260    ControlFlowEdge(
261        name="branching_to_account",
262        from_node=branching_node,
263        to_node=account_agent_node,
264        from_branch="account",
265    ),
266    ControlFlowEdge(
267        name="branching_to_end", from_node=branching_node, to_node=end_node, from_branch="default"
268    ),
269    ControlFlowEdge(
270        name="network_to_orchestrator",
271        from_node=network_agent_node,
272        to_node=orchestrator_agent_node,
273    ),
274    ControlFlowEdge(
275        name="device_to_orchestrator", from_node=device_agent_node, to_node=orchestrator_agent_node
276    ),
277    ControlFlowEdge(
278        name="account_to_orchestrator",
279        from_node=account_agent_node,
280        to_node=orchestrator_agent_node,
281    ),
282]
283
284
285# Data flow edges
286
287from pyagentspec.flows.edges import DataFlowEdge
288
289data_flow_edges = [
290    DataFlowEdge(
291        name="start_to_tool_username",
292        source_node=start_node,
293        source_output="username",
294        destination_node=tool_node,
295        destination_input="username",
296    ),
297    DataFlowEdge(
298        name="tool_to_orchestrator_user_information",
299        source_node=tool_node,
300        source_output="user_information",
301        destination_node=orchestrator_agent_node,
302        destination_input="user_information",
303    ),
304    DataFlowEdge(
305        name="tool_to_orchestrator_topic",
306        source_node=orchestrator_agent_node,
307        source_output="topic",
308        destination_node=branching_node,
309        destination_input="topic",
310    ),
311    DataFlowEdge(
312        name="orchestrator_to_device_conversation_summary",
313        source_node=orchestrator_agent_node,
314        source_output="conversation_summary",
315        destination_node=device_agent_node,
316        destination_input="conversation_summary",
317    ),
318    DataFlowEdge(
319        name="orchestrator_to_network_conversation_summary",
320        source_node=orchestrator_agent_node,
321        source_output="conversation_summary",
322        destination_node=network_agent_node,
323        destination_input="conversation_summary",
324    ),
325    DataFlowEdge(
326        name="orchestrator_to_account_conversation_summary",
327        source_node=orchestrator_agent_node,
328        source_output="conversation_summary",
329        destination_node=account_agent_node,
330        destination_input="conversation_summary",
331    ),
332    DataFlowEdge(
333        name="network_to_orchestrator_conversation_summary",
334        source_node=network_agent_node,
335        source_output="conversation_summary",
336        destination_node=orchestrator_agent_node,
337        destination_input="conversation_summary",
338    ),
339    DataFlowEdge(
340        name="device_to_orchestrator_conversation_summary",
341        source_node=device_agent_node,
342        source_output="conversation_summary",
343        destination_node=orchestrator_agent_node,
344        destination_input="conversation_summary",
345    ),
346    DataFlowEdge(
347        name="account_to_orchestrator_conversation_summary",
348        source_node=account_agent_node,
349        source_output="conversation_summary",
350        destination_node=orchestrator_agent_node,
351        destination_input="conversation_summary",
352    ),
353]
354
355
356# Flow
357
358from pyagentspec.flows.flow import Flow
359
360flow = Flow(
361    name="Oracle IT Assistant Flow",
362    description="Flow of the Oracle IT assistant",
363    start_node=start_node,
364    nodes=[
365        start_node,
366        tool_node,
367        orchestrator_agent_node,
368        branching_node,
369        network_agent_node,
370        device_agent_node,
371        account_agent_node,
372        end_node,
373    ],
374    control_flow_connections=control_flow_edges,
375    data_flow_connections=data_flow_edges,
376    inputs=[username_property],
377)
378
379
380# Serialization
381
382from pyagentspec.serialization import AgentSpecSerializer
383
384serialized_flow = AgentSpecSerializer().to_json(flow)

Next steps#

Having learned how to build an orchestrator-assistant, you may now proceed to How to Execute Agent Spec Across Frameworks and try to run this assistant yourself.