How to Run Multiple Flows in Parallel#

Parallelism is a fundamental concept in computing that enables tasks to be processed concurrently, significantly enhancing system efficiency, scalability, and overall performance.

Agent Spec supports the execution of multiple Flows in parallel, using the ParallelFlowNode. This guide will show you how to:

To follow this guide, you need an LLM. Agent Spec supports several LLM API providers. Select an LLM from the options below:

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,
)

Basic implementation#

In this guide, we will create a Flow that generates a marketing message for a user. Taking the username that identifies the user as input, we will take advantage of the ParallelFlowNode to concurrently retrieve information about the user and the context, so that we can finally generate a personalized marketing welcome message.

We first define the following server tools that retrieve the desired information:

  • One tool that retrieves the current time;

  • One tool that retrieves the user information, like name and date of birth;

  • One tool that gathers the user’s purchase history;

  • One tool that looks for the current list of items on sale, which could be recommended to the user.

Warning

In ParallelFlowNode subflows, it’s important to avoid calls to ClientTools, as they would require to temporarily pause the execution to wait for client’s response. This is an operation that, based on the runtime implementation, might require to interrupt the execution of the flow, with undesired side effects on the final outcome.

from pyagentspec.property import DictProperty, ListProperty, StringProperty
from pyagentspec.tools import ServerTool

username_property = StringProperty(title="username")
user_info_property = DictProperty(title="user_info", value_type=StringProperty())
get_user_information_tool = ServerTool(
    name="get_user_information",
    description="Retrieve information about a user",
    inputs=[username_property],
    outputs=[user_info_property],
)

current_time_property = StringProperty(title="current_time")
get_current_time_tool = ServerTool(
    name="get_current_time",
    description="Return current time",
    inputs=[],
    outputs=[current_time_property],
)

user_purchases_property = ListProperty(title="user_purchases", item_type=DictProperty(value_type=StringProperty()))
get_user_last_purchases_tool = ServerTool(
    name="get_user_last_purchases",
    description="Retrieve the list of purchases made by a user",
    inputs=[username_property],
    outputs=[user_purchases_property],
)

items_on_sale_property = ListProperty(title="items_on_sale", item_type=DictProperty(value_type=StringProperty()))
get_items_on_sale_tool = ServerTool(
    name="get_items_on_sale",
    description="Retrieve the list of items currently on sale",
    inputs=[],
    outputs=[items_on_sale_property],
)

These tools simply gather information, therefore they can be easily parallelized. We create the flows that wrap the tools we just created, and we collect them all in a ParallelFlowNode for parallel execution. For simplicity, to create the subflows we use a helper that takes a single node and creates a flow containing only that node, and exposing its inputs and outputs.

from pyagentspec.flows.flow import Flow
from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge
from pyagentspec.flows.node import Node
from pyagentspec.flows.nodes import ParallelFlowNode, ToolNode, StartNode, EndNode

def create_one_node_flow(node: Node) -> Flow:
    """Create a flow that wraps the given node, having the same inputs and outputs"""
    flow_name = node.name + "_flow"
    start_node = StartNode(name=flow_name + "_start", inputs=node.inputs)
    end_node = EndNode(name=flow_name + "_end", outputs=node.outputs)
    return Flow(
        name=flow_name,
        start_node=start_node,
        nodes=[start_node, end_node, node],
        control_flow_connections=[
            ControlFlowEdge(name="c1", from_node=start_node, to_node=node),
            ControlFlowEdge(name="c2", from_node=node, to_node=end_node),
        ],
        data_flow_connections=[
            DataFlowEdge(
                name=f"din_{input_property.title}",
                source_node=start_node, source_output=input_property.title,
                destination_node=node, destination_input=input_property.title,
            )
            for input_property in node.inputs
        ] + [
            DataFlowEdge(
                name=f"dout_{output_property.title}",
                source_node=node, source_output=output_property.title,
                destination_node=end_node, destination_input=output_property.title,
            )
            for output_property in node.outputs
        ]
    )

get_current_time_flow = create_one_node_flow(
    ToolNode(name="get_current_time_step", tool=get_current_time_tool)
)
get_user_information_flow = create_one_node_flow(
    ToolNode(name="get_user_information_step", tool=get_user_information_tool)
)
get_user_last_purchases_flow = create_one_node_flow(
    ToolNode(name="get_user_last_purchases_step", tool=get_user_last_purchases_tool)
)
get_items_on_sale_flow = create_one_node_flow(
    ToolNode(name="get_items_on_sale_steo", tool=get_items_on_sale_tool)
)

parallel_flow_node = ParallelFlowNode(
    name="parallel_flow_node",
    subflows=[
        get_current_time_flow,
        get_user_information_flow,
        get_user_last_purchases_flow,
        get_items_on_sale_flow,
    ],
)

The ParallelFlowNode we created will expose all the outputs that the different inner flows generate. We use this information to ask an LLM to generate a personalized welcome message for the user, which should also have a marketing purpose.

from pyagentspec.llms import VllmConfig
from pyagentspec.flows.nodes import OutputMessageNode, LlmNode

llm_config = VllmConfig(
    name="vllm-llama-4-maverick",
    model_id="llama-4-maverick",
    url="http://url.to.my.vllm.server/llama4mav",
)

prompt = """# Instructions

You are a marketing expert. You have to write a welcome message for a user.

The message must contain:
- A first sentence of greetings, including user's name, and personalized in case it's user's birthday
- A proposal containing something to buy

The purchase proposal must be:
- aligned with user's purchase history
- part of the list of items on sale

# User information

{{user_info}}

Note that the current time to check the birthday is: {{current_time}}

The list of items purchased by the user is:
{{user_purchases}}

# Items on sale

{{items_on_sale}}

Please write the welcome message for the user.
Do not give me the instructions to do it, I want only the final message to send.
"""

prepare_marketing_message_node = LlmNode(
    name="prepare_marketing_message_node", prompt_template=prompt, llm_config=llm_config
)
output_message_node = OutputMessageNode(name="output_message_node", message="{{output}}")

Now that we have all the steps that compose our flow, we just put everything together to create it, and we execute it to generate our personalized message.

from pyagentspec.flows.flow import Flow

start_node = StartNode(name="start_node", inputs=[username_property])
end_node = EndNode(name="end_node")
flow = Flow(
    name="marketing_message_flow",
    start_node=start_node,
    nodes=[parallel_flow_node, prepare_marketing_message_node, output_message_node, start_node, end_node],
    control_flow_connections=[
        ControlFlowEdge(name="cfe1", from_node=start_node, to_node=parallel_flow_node),
        ControlFlowEdge(name="cfe2", from_node=parallel_flow_node, to_node=prepare_marketing_message_node),
        ControlFlowEdge(name="cfe3", from_node=prepare_marketing_message_node, to_node=output_message_node),
        ControlFlowEdge(name="cfe4", from_node=output_message_node, to_node=end_node),
    ],
    data_flow_connections=[
        DataFlowEdge(
            name="dfe_username",
            source_node=start_node, source_output="username",
            destination_node=parallel_flow_node, destination_input="username",
        )
    ] + [
        DataFlowEdge(
            name="dfe_" + property_.title,
            source_node=parallel_flow_node, source_output=property_.title,
            destination_node=prepare_marketing_message_node, destination_input=property_.title,
        )
        for property_ in [current_time_property, user_info_property, user_purchases_property, items_on_sale_property]
    ]
)

Notes about parallelization#

Not all sub-flows can be executed in parallel. Agent Spec does not forbid specific configurations for ParallelFlowNode subflows, but there are several precautions to take when parallelization is enabled, especially when sub-flows are supposed to access mutable shared resources (e.g., the conversation), or interrupt the normal execution of the flow (e.g., client tools).

For more information about parallel execution support in Agent Spec, please check the language specification. For guidelines about secure implementation of concurrent execution, instead, check our security guidelines.

Export the Agent Spec configuration#

The Agent Spec configuration is generated in JSON format. These configurations can be loaded and executed in Agent Spec-compatible systems such as the WayFlow runtime. See, for example, How to Execute Agent Spec Configurations with WayFlow.

from pyagentspec.serialization import AgentSpecSerializer

serialized_flow = AgentSpecSerializer().to_json(flow)

API Reference: AgentSpecSerializer

Here is what the Agent Spec representation will look like ↓

Click here to see the assistant configuration.
{
  "component_type": "Flow",
  "id": "61d93ff3-a5c2-42e1-a9a3-d913014a8d47",
  "name": "marketing_message_flow",
  "description": null,
  "metadata": {},
  "inputs": [
    {
      "title": "username",
      "type": "string"
    }
  ],
  "outputs": [],
  "start_node": {
    "$component_ref": "276f6084-b26d-4ee2-8cf1-238f3032654e"
  },
  "nodes": [
    {
      "$component_ref": "1954fcef-a669-417a-a1be-9bd5d138bf2a"
    },
    {
      "$component_ref": "db1adb9a-9ce9-4af5-bc4d-878acec77bd7"
    },
    {
      "$component_ref": "7e31a16a-357c-4121-b00e-549cb36b550b"
    },
    {
      "$component_ref": "276f6084-b26d-4ee2-8cf1-238f3032654e"
    },
    {
      "$component_ref": "cc8603e1-1c15-4a1d-9674-68a202049fd6"
    }
  ],
  "control_flow_connections": [
    {
      "component_type": "ControlFlowEdge",
      "id": "4bb948ac-b7b0-4383-af68-d212a836d468",
      "name": "cfe1",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "276f6084-b26d-4ee2-8cf1-238f3032654e"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "1954fcef-a669-417a-a1be-9bd5d138bf2a"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "ab9eb713-3ad8-4e6d-8afe-87af8b40121f",
      "name": "cfe2",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "1954fcef-a669-417a-a1be-9bd5d138bf2a"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "db1adb9a-9ce9-4af5-bc4d-878acec77bd7"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "73f33011-058b-4d63-a02a-c4e3f1b8fb30",
      "name": "cfe3",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "db1adb9a-9ce9-4af5-bc4d-878acec77bd7"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "7e31a16a-357c-4121-b00e-549cb36b550b"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "79c742e8-6f06-4391-baae-49b555d74908",
      "name": "cfe4",
      "description": null,
      "metadata": {},
      "from_node": {
        "$component_ref": "7e31a16a-357c-4121-b00e-549cb36b550b"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "cc8603e1-1c15-4a1d-9674-68a202049fd6"
      }
    }
  ],
  "data_flow_connections": [
    {
      "component_type": "DataFlowEdge",
      "id": "83c62b8a-56cd-46c0-9c92-5e6d90246361",
      "name": "dfe_username",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "276f6084-b26d-4ee2-8cf1-238f3032654e"
      },
      "source_output": "username",
      "destination_node": {
        "$component_ref": "1954fcef-a669-417a-a1be-9bd5d138bf2a"
      },
      "destination_input": "username"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "b0c1ad2d-0e11-40f3-8c84-4601bcf556cb",
      "name": "dfe_current_time",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "1954fcef-a669-417a-a1be-9bd5d138bf2a"
      },
      "source_output": "current_time",
      "destination_node": {
        "$component_ref": "db1adb9a-9ce9-4af5-bc4d-878acec77bd7"
      },
      "destination_input": "current_time"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "7933dfd3-7a12-4fcf-aac4-2510a5fce982",
      "name": "dfe_user_info",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "1954fcef-a669-417a-a1be-9bd5d138bf2a"
      },
      "source_output": "user_info",
      "destination_node": {
        "$component_ref": "db1adb9a-9ce9-4af5-bc4d-878acec77bd7"
      },
      "destination_input": "user_info"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "03b5a2b2-9359-4ad9-ba02-0d6281dd83f5",
      "name": "dfe_user_purchases",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "1954fcef-a669-417a-a1be-9bd5d138bf2a"
      },
      "source_output": "user_purchases",
      "destination_node": {
        "$component_ref": "db1adb9a-9ce9-4af5-bc4d-878acec77bd7"
      },
      "destination_input": "user_purchases"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "db2d9275-7937-4d1e-b1c1-417586886c10",
      "name": "dfe_items_on_sale",
      "description": null,
      "metadata": {},
      "source_node": {
        "$component_ref": "1954fcef-a669-417a-a1be-9bd5d138bf2a"
      },
      "source_output": "items_on_sale",
      "destination_node": {
        "$component_ref": "db1adb9a-9ce9-4af5-bc4d-878acec77bd7"
      },
      "destination_input": "items_on_sale"
    }
  ],
  "$referenced_components": {
    "1954fcef-a669-417a-a1be-9bd5d138bf2a": {
      "component_type": "ParallelFlowNode",
      "id": "1954fcef-a669-417a-a1be-9bd5d138bf2a",
      "name": "parallel_flow_node",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "username",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "current_time",
          "type": "string"
        },
        {
          "title": "user_info",
          "additionalProperties": {
            "type": "string"
          },
          "properties": {},
          "type": "object"
        },
        {
          "title": "user_purchases",
          "items": {
            "additionalProperties": {
              "type": "string"
            },
            "properties": {},
            "type": "object"
          },
          "type": "array"
        },
        {
          "title": "items_on_sale",
          "items": {
            "additionalProperties": {
              "type": "string"
            },
            "properties": {},
            "type": "object"
          },
          "type": "array"
        }
      ],
      "branches": [
        "next"
      ],
      "subflows": [
        {
          "component_type": "Flow",
          "id": "9c2736c2-9716-4c69-935a-b341adb0056c",
          "name": "get_current_time_step_flow",
          "description": null,
          "metadata": {},
          "inputs": [],
          "outputs": [
            {
              "title": "current_time",
              "type": "string"
            }
          ],
          "start_node": {
            "$component_ref": "6e2a3922-9993-4f66-b6b3-570c2821518e"
          },
          "nodes": [
            {
              "$component_ref": "6e2a3922-9993-4f66-b6b3-570c2821518e"
            },
            {
              "$component_ref": "cde5f5ec-c028-43b7-9d66-23e37f59403e"
            },
            {
              "$component_ref": "982f992c-76dd-4901-b810-aab2d1f610bd"
            }
          ],
          "control_flow_connections": [
            {
              "component_type": "ControlFlowEdge",
              "id": "47fbf5c0-2ba4-4f68-883f-1a0c205524d3",
              "name": "c1",
              "description": null,
              "metadata": {},
              "from_node": {
                "$component_ref": "6e2a3922-9993-4f66-b6b3-570c2821518e"
              },
              "from_branch": null,
              "to_node": {
                "$component_ref": "982f992c-76dd-4901-b810-aab2d1f610bd"
              }
            },
            {
              "component_type": "ControlFlowEdge",
              "id": "63dac3bd-cbfb-47bc-82d9-c9fc18cb086f",
              "name": "c2",
              "description": null,
              "metadata": {},
              "from_node": {
                "$component_ref": "982f992c-76dd-4901-b810-aab2d1f610bd"
              },
              "from_branch": null,
              "to_node": {
                "$component_ref": "cde5f5ec-c028-43b7-9d66-23e37f59403e"
              }
            }
          ],
          "data_flow_connections": [
            {
              "component_type": "DataFlowEdge",
              "id": "453e3bfe-0b46-4cd3-b38d-85cf5ac13efb",
              "name": "dout_current_time",
              "description": null,
              "metadata": {},
              "source_node": {
                "$component_ref": "982f992c-76dd-4901-b810-aab2d1f610bd"
              },
              "source_output": "current_time",
              "destination_node": {
                "$component_ref": "cde5f5ec-c028-43b7-9d66-23e37f59403e"
              },
              "destination_input": "current_time"
            }
          ],
          "$referenced_components": {
            "6e2a3922-9993-4f66-b6b3-570c2821518e": {
              "component_type": "StartNode",
              "id": "6e2a3922-9993-4f66-b6b3-570c2821518e",
              "name": "get_current_time_step_flow_start",
              "description": null,
              "metadata": {},
              "inputs": [],
              "outputs": [],
              "branches": [
                "next"
              ]
            },
            "cde5f5ec-c028-43b7-9d66-23e37f59403e": {
              "component_type": "EndNode",
              "id": "cde5f5ec-c028-43b7-9d66-23e37f59403e",
              "name": "get_current_time_step_flow_start",
              "description": null,
              "metadata": {},
              "inputs": [
                {
                  "title": "current_time",
                  "type": "string"
                }
              ],
              "outputs": [
                {
                  "title": "current_time",
                  "type": "string"
                }
              ],
              "branches": [],
              "branch_name": "next"
            },
            "982f992c-76dd-4901-b810-aab2d1f610bd": {
              "component_type": "ToolNode",
              "id": "982f992c-76dd-4901-b810-aab2d1f610bd",
              "name": "get_current_time_step",
              "description": null,
              "metadata": {},
              "inputs": [],
              "outputs": [
                {
                  "title": "current_time",
                  "type": "string"
                }
              ],
              "branches": [
                "next"
              ],
              "tool": {
                "component_type": "ServerTool",
                "id": "d5540249-4d2f-4f99-a807-14088ff9e14c",
                "name": "get_current_time",
                "description": "Return current time",
                "metadata": {},
                "inputs": [],
                "outputs": [
                  {
                    "title": "current_time",
                    "type": "string"
                  }
                ]
              }
            }
          }
        },
        {
          "component_type": "Flow",
          "id": "241296a7-0a4a-45d6-93e8-88b77f0ecd9e",
          "name": "get_user_information_step_flow",
          "description": null,
          "metadata": {},
          "inputs": [
            {
              "title": "username",
              "type": "string"
            }
          ],
          "outputs": [
            {
              "title": "user_info",
              "additionalProperties": {
                "type": "string"
              },
              "properties": {},
              "type": "object"
            }
          ],
          "start_node": {
            "$component_ref": "3dc5d3eb-d366-4de7-8afe-3d2d160c7dec"
          },
          "nodes": [
            {
              "$component_ref": "3dc5d3eb-d366-4de7-8afe-3d2d160c7dec"
            },
            {
              "$component_ref": "429967e3-5a64-4749-be07-57cc54afd39e"
            },
            {
              "$component_ref": "141c49b0-026c-4379-b3a8-24dafe756278"
            }
          ],
          "control_flow_connections": [
            {
              "component_type": "ControlFlowEdge",
              "id": "3fd8d404-e67c-491a-b3e8-31771f1da442",
              "name": "c1",
              "description": null,
              "metadata": {},
              "from_node": {
                "$component_ref": "3dc5d3eb-d366-4de7-8afe-3d2d160c7dec"
              },
              "from_branch": null,
              "to_node": {
                "$component_ref": "141c49b0-026c-4379-b3a8-24dafe756278"
              }
            },
            {
              "component_type": "ControlFlowEdge",
              "id": "d068501a-964e-4035-8f98-34d7b8350d43",
              "name": "c2",
              "description": null,
              "metadata": {},
              "from_node": {
                "$component_ref": "141c49b0-026c-4379-b3a8-24dafe756278"
              },
              "from_branch": null,
              "to_node": {
                "$component_ref": "429967e3-5a64-4749-be07-57cc54afd39e"
              }
            }
          ],
          "data_flow_connections": [
            {
              "component_type": "DataFlowEdge",
              "id": "b756b473-e40e-4a97-b25a-bcd457b95242",
              "name": "din_username",
              "description": null,
              "metadata": {},
              "source_node": {
                "$component_ref": "3dc5d3eb-d366-4de7-8afe-3d2d160c7dec"
              },
              "source_output": "username",
              "destination_node": {
                "$component_ref": "141c49b0-026c-4379-b3a8-24dafe756278"
              },
              "destination_input": "username"
            },
            {
              "component_type": "DataFlowEdge",
              "id": "37827be0-ab14-439f-8251-832cb18128e0",
              "name": "dout_user_info",
              "description": null,
              "metadata": {},
              "source_node": {
                "$component_ref": "141c49b0-026c-4379-b3a8-24dafe756278"
              },
              "source_output": "user_info",
              "destination_node": {
                "$component_ref": "429967e3-5a64-4749-be07-57cc54afd39e"
              },
              "destination_input": "user_info"
            }
          ],
          "$referenced_components": {
            "3dc5d3eb-d366-4de7-8afe-3d2d160c7dec": {
              "component_type": "StartNode",
              "id": "3dc5d3eb-d366-4de7-8afe-3d2d160c7dec",
              "name": "get_user_information_step_flow_start",
              "description": null,
              "metadata": {},
              "inputs": [
                {
                  "title": "username",
                  "type": "string"
                }
              ],
              "outputs": [
                {
                  "title": "username",
                  "type": "string"
                }
              ],
              "branches": [
                "next"
              ]
            },
            "429967e3-5a64-4749-be07-57cc54afd39e": {
              "component_type": "EndNode",
              "id": "429967e3-5a64-4749-be07-57cc54afd39e",
              "name": "get_user_information_step_flow_start",
              "description": null,
              "metadata": {},
              "inputs": [
                {
                  "title": "user_info",
                  "additionalProperties": {
                    "type": "string"
                  },
                  "properties": {},
                  "type": "object"
                }
              ],
              "outputs": [
                {
                  "title": "user_info",
                  "additionalProperties": {
                    "type": "string"
                  },
                  "properties": {},
                  "type": "object"
                }
              ],
              "branches": [],
              "branch_name": "next"
            },
            "141c49b0-026c-4379-b3a8-24dafe756278": {
              "component_type": "ToolNode",
              "id": "141c49b0-026c-4379-b3a8-24dafe756278",
              "name": "get_user_information_step",
              "description": null,
              "metadata": {},
              "inputs": [
                {
                  "title": "username",
                  "type": "string"
                }
              ],
              "outputs": [
                {
                  "title": "user_info",
                  "additionalProperties": {
                    "type": "string"
                  },
                  "properties": {},
                  "type": "object"
                }
              ],
              "branches": [
                "next"
              ],
              "tool": {
                "component_type": "ServerTool",
                "id": "2ab1ea62-0c8b-46ed-85ba-2d6dbbd65761",
                "name": "get_user_information",
                "description": "Retrieve information about a user",
                "metadata": {},
                "inputs": [
                  {
                    "title": "username",
                    "type": "string"
                  }
                ],
                "outputs": [
                  {
                    "title": "user_info",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "properties": {},
                    "type": "object"
                  }
                ]
              }
            }
          }
        },
        {
          "component_type": "Flow",
          "id": "d384b947-2c6c-4669-b4f1-92c2d515891e",
          "name": "get_user_last_purchases_step_flow",
          "description": null,
          "metadata": {},
          "inputs": [
            {
              "title": "username",
              "type": "string"
            }
          ],
          "outputs": [
            {
              "title": "user_purchases",
              "items": {
                "additionalProperties": {
                  "type": "string"
                },
                "properties": {},
                "type": "object"
              },
              "type": "array"
            }
          ],
          "start_node": {
            "$component_ref": "0bc49848-2a50-4b01-8b88-68794a3a5cad"
          },
          "nodes": [
            {
              "$component_ref": "0bc49848-2a50-4b01-8b88-68794a3a5cad"
            },
            {
              "$component_ref": "205dff1d-9af0-4a44-937e-4ef950fbc436"
            },
            {
              "$component_ref": "82e6d2c4-44b0-42f7-8cc8-ba54eb3c4780"
            }
          ],
          "control_flow_connections": [
            {
              "component_type": "ControlFlowEdge",
              "id": "9c1644a5-91e5-4124-97f2-e3e0a111b2b7",
              "name": "c1",
              "description": null,
              "metadata": {},
              "from_node": {
                "$component_ref": "0bc49848-2a50-4b01-8b88-68794a3a5cad"
              },
              "from_branch": null,
              "to_node": {
                "$component_ref": "82e6d2c4-44b0-42f7-8cc8-ba54eb3c4780"
              }
            },
            {
              "component_type": "ControlFlowEdge",
              "id": "ae0ff36d-b08e-496e-9dec-08e443ba8d92",
              "name": "c2",
              "description": null,
              "metadata": {},
              "from_node": {
                "$component_ref": "82e6d2c4-44b0-42f7-8cc8-ba54eb3c4780"
              },
              "from_branch": null,
              "to_node": {
                "$component_ref": "205dff1d-9af0-4a44-937e-4ef950fbc436"
              }
            }
          ],
          "data_flow_connections": [
            {
              "component_type": "DataFlowEdge",
              "id": "5f538977-456f-48e8-9c6d-04571895aa1d",
              "name": "din_username",
              "description": null,
              "metadata": {},
              "source_node": {
                "$component_ref": "0bc49848-2a50-4b01-8b88-68794a3a5cad"
              },
              "source_output": "username",
              "destination_node": {
                "$component_ref": "82e6d2c4-44b0-42f7-8cc8-ba54eb3c4780"
              },
              "destination_input": "username"
            },
            {
              "component_type": "DataFlowEdge",
              "id": "2e320aee-73dd-43f3-b3f1-623d41ad1fa8",
              "name": "dout_user_purchases",
              "description": null,
              "metadata": {},
              "source_node": {
                "$component_ref": "82e6d2c4-44b0-42f7-8cc8-ba54eb3c4780"
              },
              "source_output": "user_purchases",
              "destination_node": {
                "$component_ref": "205dff1d-9af0-4a44-937e-4ef950fbc436"
              },
              "destination_input": "user_purchases"
            }
          ],
          "$referenced_components": {
            "0bc49848-2a50-4b01-8b88-68794a3a5cad": {
              "component_type": "StartNode",
              "id": "0bc49848-2a50-4b01-8b88-68794a3a5cad",
              "name": "get_user_last_purchases_step_flow_start",
              "description": null,
              "metadata": {},
              "inputs": [
                {
                  "title": "username",
                  "type": "string"
                }
              ],
              "outputs": [
                {
                  "title": "username",
                  "type": "string"
                }
              ],
              "branches": [
                "next"
              ]
            },
            "205dff1d-9af0-4a44-937e-4ef950fbc436": {
              "component_type": "EndNode",
              "id": "205dff1d-9af0-4a44-937e-4ef950fbc436",
              "name": "get_user_last_purchases_step_flow_start",
              "description": null,
              "metadata": {},
              "inputs": [
                {
                  "title": "user_purchases",
                  "items": {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "properties": {},
                    "type": "object"
                  },
                  "type": "array"
                }
              ],
              "outputs": [
                {
                  "title": "user_purchases",
                  "items": {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "properties": {},
                    "type": "object"
                  },
                  "type": "array"
                }
              ],
              "branches": [],
              "branch_name": "next"
            },
            "82e6d2c4-44b0-42f7-8cc8-ba54eb3c4780": {
              "component_type": "ToolNode",
              "id": "82e6d2c4-44b0-42f7-8cc8-ba54eb3c4780",
              "name": "get_user_last_purchases_step",
              "description": null,
              "metadata": {},
              "inputs": [
                {
                  "title": "username",
                  "type": "string"
                }
              ],
              "outputs": [
                {
                  "title": "user_purchases",
                  "items": {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "properties": {},
                    "type": "object"
                  },
                  "type": "array"
                }
              ],
              "branches": [
                "next"
              ],
              "tool": {
                "component_type": "ServerTool",
                "id": "86f046df-a9b6-49ac-b73b-998d02ead1a3",
                "name": "get_user_last_purchases",
                "description": "Retrieve the list of purchases made by a user",
                "metadata": {},
                "inputs": [
                  {
                    "title": "username",
                    "type": "string"
                  }
                ],
                "outputs": [
                  {
                    "title": "user_purchases",
                    "items": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "properties": {},
                      "type": "object"
                    },
                    "type": "array"
                  }
                ]
              }
            }
          }
        },
        {
          "component_type": "Flow",
          "id": "8d7b7b24-58e7-4335-8a1f-f91587dee241",
          "name": "get_items_on_sale_steo_flow",
          "description": null,
          "metadata": {},
          "inputs": [],
          "outputs": [
            {
              "title": "items_on_sale",
              "items": {
                "additionalProperties": {
                  "type": "string"
                },
                "properties": {},
                "type": "object"
              },
              "type": "array"
            }
          ],
          "start_node": {
            "$component_ref": "9c97adb1-2c15-4f69-9a30-c7b46f4a598d"
          },
          "nodes": [
            {
              "$component_ref": "9c97adb1-2c15-4f69-9a30-c7b46f4a598d"
            },
            {
              "$component_ref": "e099a667-cf9d-402c-b07c-894502f28fb7"
            },
            {
              "$component_ref": "2e535a6e-ae5a-445b-8bda-d75b9adb7534"
            }
          ],
          "control_flow_connections": [
            {
              "component_type": "ControlFlowEdge",
              "id": "9c48f961-1541-4bf0-b571-7f19e1610a42",
              "name": "c1",
              "description": null,
              "metadata": {},
              "from_node": {
                "$component_ref": "9c97adb1-2c15-4f69-9a30-c7b46f4a598d"
              },
              "from_branch": null,
              "to_node": {
                "$component_ref": "2e535a6e-ae5a-445b-8bda-d75b9adb7534"
              }
            },
            {
              "component_type": "ControlFlowEdge",
              "id": "6f0954d1-8d9f-498c-bcb0-167f5f15488e",
              "name": "c2",
              "description": null,
              "metadata": {},
              "from_node": {
                "$component_ref": "2e535a6e-ae5a-445b-8bda-d75b9adb7534"
              },
              "from_branch": null,
              "to_node": {
                "$component_ref": "e099a667-cf9d-402c-b07c-894502f28fb7"
              }
            }
          ],
          "data_flow_connections": [
            {
              "component_type": "DataFlowEdge",
              "id": "bf4a0a50-d3a6-4ebf-bc0c-53b7d5f1eb6a",
              "name": "dout_items_on_sale",
              "description": null,
              "metadata": {},
              "source_node": {
                "$component_ref": "2e535a6e-ae5a-445b-8bda-d75b9adb7534"
              },
              "source_output": "items_on_sale",
              "destination_node": {
                "$component_ref": "e099a667-cf9d-402c-b07c-894502f28fb7"
              },
              "destination_input": "items_on_sale"
            }
          ],
          "$referenced_components": {
            "9c97adb1-2c15-4f69-9a30-c7b46f4a598d": {
              "component_type": "StartNode",
              "id": "9c97adb1-2c15-4f69-9a30-c7b46f4a598d",
              "name": "get_items_on_sale_steo_flow_start",
              "description": null,
              "metadata": {},
              "inputs": [],
              "outputs": [],
              "branches": [
                "next"
              ]
            },
            "e099a667-cf9d-402c-b07c-894502f28fb7": {
              "component_type": "EndNode",
              "id": "e099a667-cf9d-402c-b07c-894502f28fb7",
              "name": "get_items_on_sale_steo_flow_start",
              "description": null,
              "metadata": {},
              "inputs": [
                {
                  "title": "items_on_sale",
                  "items": {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "properties": {},
                    "type": "object"
                  },
                  "type": "array"
                }
              ],
              "outputs": [
                {
                  "title": "items_on_sale",
                  "items": {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "properties": {},
                    "type": "object"
                  },
                  "type": "array"
                }
              ],
              "branches": [],
              "branch_name": "next"
            },
            "2e535a6e-ae5a-445b-8bda-d75b9adb7534": {
              "component_type": "ToolNode",
              "id": "2e535a6e-ae5a-445b-8bda-d75b9adb7534",
              "name": "get_items_on_sale_steo",
              "description": null,
              "metadata": {},
              "inputs": [],
              "outputs": [
                {
                  "title": "items_on_sale",
                  "items": {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "properties": {},
                    "type": "object"
                  },
                  "type": "array"
                }
              ],
              "branches": [
                "next"
              ],
              "tool": {
                "component_type": "ServerTool",
                "id": "fa406981-3775-4415-a147-c68ee85102bb",
                "name": "get_items_on_sale",
                "description": "Retrieve the list of items currently on sale",
                "metadata": {},
                "inputs": [],
                "outputs": [
                  {
                    "title": "items_on_sale",
                    "items": {
                      "additionalProperties": {
                        "type": "string"
                      },
                      "properties": {},
                      "type": "object"
                    },
                    "type": "array"
                  }
                ]
              }
            }
          }
        }
      ]
    },
    "db1adb9a-9ce9-4af5-bc4d-878acec77bd7": {
      "component_type": "LlmNode",
      "id": "db1adb9a-9ce9-4af5-bc4d-878acec77bd7",
      "name": "prepare_marketing_message_node",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "user_purchases",
          "type": "string"
        },
        {
          "title": "items_on_sale",
          "type": "string"
        },
        {
          "title": "current_time",
          "type": "string"
        },
        {
          "title": "user_info",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "description": "Raw text generated by the LLM",
          "title": "generated_text",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ],
      "llm_config": {
        "component_type": "VllmConfig",
        "id": "5d2b8499-3d7a-494b-9e12-b61daf36a3f7",
        "name": "vllm-llama-4-maverick",
        "description": null,
        "metadata": {},
        "default_generation_parameters": null,
        "url": "http://url.to.my.vllm.server/llama4mav",
        "model_id": "llama-4-maverick"
      },
      "prompt_template": "# Instructions\n\nYou are a marketing expert. You have to write a welcome message for a user.\n\nThe message must contain:\n- A first sentence of greetings, including user's name, and personalized in case it's user's birthday\n- A proposal containing something to buy\n\nThe purchase proposal must be:\n- aligned with user's purchase history\n- part of the list of items on sale\n\n# User information\n\n{{user_info}}\n\nNote that the current time to check the birthday is: {{current_time}}\n\nThe list of items purchased by the user is:\n{{user_purchases}}\n\n# Items on sale\n\n{{items_on_sale}}\n\nPlease write the welcome message for the user.\nDo not give me the instructions to do it, I want only the final message to send.  \n"
    },
    "7e31a16a-357c-4121-b00e-549cb36b550b": {
      "component_type": "OutputMessageNode",
      "id": "7e31a16a-357c-4121-b00e-549cb36b550b",
      "name": "output_message_node",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "output",
          "type": "string"
        }
      ],
      "outputs": [],
      "branches": [
        "next"
      ],
      "message": "{{output}}"
    },
    "276f6084-b26d-4ee2-8cf1-238f3032654e": {
      "component_type": "StartNode",
      "id": "276f6084-b26d-4ee2-8cf1-238f3032654e",
      "name": "start_node",
      "description": null,
      "metadata": {},
      "inputs": [
        {
          "title": "username",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "username",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ]
    },
    "cc8603e1-1c15-4a1d-9674-68a202049fd6": {
      "component_type": "EndNode",
      "id": "cc8603e1-1c15-4a1d-9674-68a202049fd6",
      "name": "end_node",
      "description": null,
      "metadata": {},
      "inputs": [],
      "outputs": [],
      "branches": [],
      "branch_name": "next"
    }
  },
  "agentspec_version": "25.4.2"
}

Recap#

This guide covered how to define tasks for parallel execution in Agent Spec.

Below is the complete code from this guide.
  1from pyagentspec.property import DictProperty, ListProperty, StringProperty
  2from pyagentspec.tools import ServerTool
  3
  4username_property = StringProperty(title="username")
  5user_info_property = DictProperty(title="user_info", value_type=StringProperty())
  6get_user_information_tool = ServerTool(
  7    name="get_user_information",
  8    description="Retrieve information about a user",
  9    inputs=[username_property],
 10    outputs=[user_info_property],
 11)
 12
 13current_time_property = StringProperty(title="current_time")
 14get_current_time_tool = ServerTool(
 15    name="get_current_time",
 16    description="Return current time",
 17    inputs=[],
 18    outputs=[current_time_property],
 19)
 20
 21user_purchases_property = ListProperty(title="user_purchases", item_type=DictProperty(value_type=StringProperty()))
 22get_user_last_purchases_tool = ServerTool(
 23    name="get_user_last_purchases",
 24    description="Retrieve the list of purchases made by a user",
 25    inputs=[username_property],
 26    outputs=[user_purchases_property],
 27)
 28
 29items_on_sale_property = ListProperty(title="items_on_sale", item_type=DictProperty(value_type=StringProperty()))
 30get_items_on_sale_tool = ServerTool(
 31    name="get_items_on_sale",
 32    description="Retrieve the list of items currently on sale",
 33    inputs=[],
 34    outputs=[items_on_sale_property],
 35)
 36
 37
 38from pyagentspec.flows.flow import Flow
 39from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge
 40from pyagentspec.flows.node import Node
 41from pyagentspec.flows.nodes import ParallelFlowNode, ToolNode, StartNode, EndNode
 42
 43def create_one_node_flow(node: Node) -> Flow:
 44    """Create a flow that wraps the given node, having the same inputs and outputs"""
 45    flow_name = node.name + "_flow"
 46    start_node = StartNode(name=flow_name + "_start", inputs=node.inputs)
 47    end_node = EndNode(name=flow_name + "_start", outputs=node.outputs)
 48    return Flow(
 49        name=flow_name,
 50        start_node=start_node,
 51        nodes=[start_node, end_node, node],
 52        control_flow_connections=[
 53            ControlFlowEdge(name="c1", from_node=start_node, to_node=node),
 54            ControlFlowEdge(name="c2", from_node=node, to_node=end_node),
 55        ],
 56        data_flow_connections=[
 57            DataFlowEdge(
 58                name=f"din_{input_property.title}",
 59                source_node=start_node, source_output=input_property.title,
 60                destination_node=node, destination_input=input_property.title,
 61            )
 62            for input_property in node.inputs
 63        ] + [
 64            DataFlowEdge(
 65                name=f"dout_{output_property.title}",
 66                source_node=node, source_output=output_property.title,
 67                destination_node=end_node, destination_input=output_property.title,
 68            )
 69            for output_property in node.outputs
 70        ]
 71    )
 72
 73get_current_time_flow = create_one_node_flow(
 74    ToolNode(name="get_current_time_step", tool=get_current_time_tool)
 75)
 76get_user_information_flow = create_one_node_flow(
 77    ToolNode(name="get_user_information_step", tool=get_user_information_tool)
 78)
 79get_user_last_purchases_flow = create_one_node_flow(
 80    ToolNode(name="get_user_last_purchases_step", tool=get_user_last_purchases_tool)
 81)
 82get_items_on_sale_flow = create_one_node_flow(
 83    ToolNode(name="get_items_on_sale_steo", tool=get_items_on_sale_tool)
 84)
 85
 86parallel_flow_node = ParallelFlowNode(
 87    name="parallel_flow_node",
 88    subflows=[
 89        get_current_time_flow,
 90        get_user_information_flow,
 91        get_user_last_purchases_flow,
 92        get_items_on_sale_flow,
 93    ],
 94)
 95
 96
 97from pyagentspec.llms import VllmConfig
 98from pyagentspec.flows.nodes import OutputMessageNode, LlmNode
 99
100llm_config = VllmConfig(
101    name="vllm-llama-4-maverick",
102    model_id="llama-4-maverick",
103    url="http://url.to.my.vllm.server/llama4mav",
104)
105
106prompt = """# Instructions
107
108You are a marketing expert. You have to write a welcome message for a user.
109
110The message must contain:
111- A first sentence of greetings, including user's name, and personalized in case it's user's birthday
112- A proposal containing something to buy
113
114The purchase proposal must be:
115- aligned with user's purchase history
116- part of the list of items on sale
117
118# User information
119
120{{user_info}}
121
122Note that the current time to check the birthday is: {{current_time}}
123
124The list of items purchased by the user is:
125{{user_purchases}}
126
127# Items on sale
128
129{{items_on_sale}}
130
131Please write the welcome message for the user.
132Do not give me the instructions to do it, I want only the final message to send.
133"""
134
135prepare_marketing_message_node = LlmNode(
136    name="prepare_marketing_message_node", prompt_template=prompt, llm_config=llm_config
137)
138output_message_node = OutputMessageNode(name="output_message_node", message="{{output}}")
139
140
141from pyagentspec.flows.flow import Flow
142
143start_node = StartNode(name="start_node", inputs=[username_property])
144end_node = EndNode(name="end_node")
145flow = Flow(
146    name="marketing_message_flow",
147    start_node=start_node,
148    nodes=[parallel_flow_node, prepare_marketing_message_node, output_message_node, start_node, end_node],
149    control_flow_connections=[
150        ControlFlowEdge(name="cfe1", from_node=start_node, to_node=parallel_flow_node),
151        ControlFlowEdge(name="cfe2", from_node=parallel_flow_node, to_node=prepare_marketing_message_node),
152        ControlFlowEdge(name="cfe3", from_node=prepare_marketing_message_node, to_node=output_message_node),
153        ControlFlowEdge(name="cfe4", from_node=output_message_node, to_node=end_node),
154    ],
155    data_flow_connections=[
156        DataFlowEdge(
157            name="dfe_username",
158            source_node=start_node, source_output="username",
159            destination_node=parallel_flow_node, destination_input="username",
160        )
161    ] + [
162        DataFlowEdge(
163            name="dfe_" + property_.title,
164            source_node=parallel_flow_node, source_output=property_.title,
165            destination_node=prepare_marketing_message_node, destination_input=property_.title,
166        )
167        for property_ in [current_time_property, user_info_property, user_purchases_property, items_on_sale_property]
168    ]
169)
170
171from pyagentspec.serialization import AgentSpecSerializer
172
173serialized_flow = AgentSpecSerializer().to_json(flow)

Next steps#

Having learned how to perform generic parallel operations in Agent Spec, you may now proceed to How to Do Map and Reduce Operations in Flows.