How to Build Flows with Structured LLM Generation#

This guide explains how to generate structured output using Large Language Models (LLMs) within flows in Agent Spec. Structured generation ensures outputs are actionable and usable, enabling seamless integration into subsequent steps of your flow or application.

This guide will show you how to:

  • Configure LLMs to generate structured outputs within flows.

  • Handle simple and complex JSON objects using specific schemas.

Basic Implementation#

Flows in Agent Spec often include nodes that depend on messages or data generated by a Large Language Model (LLM). To define these nodes, you must specify the LLM configuration. Multiple backend options are supported, such as the OCI Generative AI service or a self-hosted model using vLLM.

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

Let’s walk through an example of summarizing an article. Start by defining the content to be summarized:

article = """Sea turtles are ancient reptiles that have been around for over 100 million years. 
             They play crucial roles in marine ecosystems, such as maintaining healthy seagrass beds and coral reefs. 
             Unfortunately, they are under threat due to poaching, habitat loss, and pollution. 
             Conservation efforts worldwide aim to protect nesting sites and reduce bycatch in fishing gear."""

Now, define the flow for this summarization task:

from pyagentspec.flows.nodes import EndNode, LlmNode, StartNode
from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
from pyagentspec.flows.flow import Flow
from pyagentspec.property import StringProperty

# Inputs and outputs
input_1 = StringProperty(title="article")
output_1 = StringProperty(title="summary")

# Nodes
start_node = StartNode(name="start_node", inputs=[input_1])
summarize_node = LlmNode(
    name="summarize_node",
    prompt_template="""Summarize this article in 10 words:\n {{article}}""",
    llm_config=llm_config,
    outputs=[output_1]
)
end_node = EndNode(name="end_node", outputs=[output_1])

# Control flow edges
control_edge_1 = ControlFlowEdge(
    name="control_edge_1", from_node=start_node, to_node=summarize_node
)
control_edge_2 = ControlFlowEdge(
    name="control_edge_2", from_node=summarize_node, to_node=end_node
)

# Data flow edges
data_edge_1 = DataFlowEdge(
    name="data_edge_1",
    source_node=start_node,
    source_output="article",
    destination_node=summarize_node,
    destination_input="article",
)
data_edge_2 = DataFlowEdge(
    name="data_edge_2",
    source_node=summarize_node,
    source_output="summary",
    destination_node=end_node,
    destination_input="summary",
)

# Flow
flow = Flow(
    name="Ten words summary",
    start_node=start_node,
    nodes=[start_node, summarize_node, end_node],
    control_flow_connections=[control_edge_1, control_edge_2],
    data_flow_connections=[data_edge_1, data_edge_2],
    inputs=[input_1],
    outputs=[output_1],
)

This flow can be exported to json by:

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": "706309d4-b73c-4b6a-930e-0703c8074f5f",
    "name": "Ten words summary",
    "description": null,
    "metadata": {},
    "inputs": [
        {
            "title": "article",
            "type": "string"
        }
    ],
    "outputs": [
        {
            "title": "summary",
            "type": "string"
        }
    ],
    "start_node": {
        "$component_ref": "96a49908-e3dc-48ab-9541-15fcc43024af"
    },
    "nodes": [
        {
            "$component_ref": "96a49908-e3dc-48ab-9541-15fcc43024af"
        },
        {
            "$component_ref": "80b98b71-d921-4478-b661-e3f84f61bb8e"
        },
        {
            "$component_ref": "e06346e7-5da1-48e4-b417-cd336e9803ef"
        }
    ],
    "control_flow_connections": [
        {
            "component_type": "ControlFlowEdge",
            "id": "f51e3d03-f2a7-4972-8b44-3709f5e7c6f7",
            "name": "control_edge_1",
            "description": null,
            "metadata": {},
            "from_node": {
                "$component_ref": "96a49908-e3dc-48ab-9541-15fcc43024af"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "80b98b71-d921-4478-b661-e3f84f61bb8e"
            }
        },
        {
            "component_type": "ControlFlowEdge",
            "id": "d3377598-fa1d-4e58-bbd2-ed0bc0b6e8f2",
            "name": "control_edge_2",
            "description": null,
            "metadata": {},
            "from_node": {
                "$component_ref": "80b98b71-d921-4478-b661-e3f84f61bb8e"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "e06346e7-5da1-48e4-b417-cd336e9803ef"
            }
        }
    ],
    "data_flow_connections": [
        {
            "component_type": "DataFlowEdge",
            "id": "9bf0e3c9-e5d4-430d-9ec9-204b613eeb36",
            "name": "data_edge_1",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "96a49908-e3dc-48ab-9541-15fcc43024af"
            },
            "source_output": "article",
            "destination_node": {
                "$component_ref": "80b98b71-d921-4478-b661-e3f84f61bb8e"
            },
            "destination_input": "article"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "ba04a336-89d1-4ec3-b903-5fc746573b08",
            "name": "data_edge_2",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "80b98b71-d921-4478-b661-e3f84f61bb8e"
            },
            "source_output": "summary",
            "destination_node": {
                "$component_ref": "e06346e7-5da1-48e4-b417-cd336e9803ef"
            },
            "destination_input": "summary"
        }
    ],
    "$referenced_components": {
        "96a49908-e3dc-48ab-9541-15fcc43024af": {
            "component_type": "StartNode",
            "id": "96a49908-e3dc-48ab-9541-15fcc43024af",
            "name": "start_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "outputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "branches": [
                "next"
            ]
        },
        "80b98b71-d921-4478-b661-e3f84f61bb8e": {
            "component_type": "LlmNode",
            "id": "80b98b71-d921-4478-b661-e3f84f61bb8e",
            "name": "summarize_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "outputs": [
                {
                    "title": "summary",
                    "type": "string"
                }
            ],
            "branches": [
                "next"
            ],
            "llm_config": {
                "component_type": "VllmConfig",
                "id": "127e0dc5-582a-484c-a011-b76b2c373880",
                "name": "llm",
                "description": null,
                "metadata": {},
                "default_generation_parameters": null,
                "url": "url",
                "model_id": "model_id"
            },
            "prompt_template": "Summarize this article in 10 words:\n {{article}}"
        },
        "e06346e7-5da1-48e4-b417-cd336e9803ef": {
            "component_type": "EndNode",
            "id": "e06346e7-5da1-48e4-b417-cd336e9803ef",
            "name": "end_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "title": "summary",
                    "type": "string"
                }
            ],
            "outputs": [
                {
                    "title": "summary",
                    "type": "string"
                }
            ],
            "branches": [],
            "branch_name": "next"
        }
    },
    "agentspec_version": "25.4.1"
}

It can be loaded into any Agent Spec-compatible system, such as the WayFlow runtime. See, for example, How to Execute Agent Spec Configuration with WayFlow.

Structured Generation with Flows#

Generating raw text within a flow is often not useful, as it can be challenging to utilize in subsequent steps. Instead, let us consider generating data that adheres to a specific schema for easier processing. Let us define three outputs that the flow can produce.

Instead of generating only a summary, you can provide three distinct outputs to the LlmNode for generation.

from pyagentspec.flows.nodes import EndNode, LlmNode, StartNode
from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
from pyagentspec.flows.flow import Flow
from pyagentspec.property import ListProperty, StringProperty

input_1 = StringProperty(title="article")

# Desired outputs
animal_output = StringProperty(
    title="animal_name",
    description="name of the animal",
)

danger_level_output = StringProperty(
    title="danger_level",
    description='level of danger of the animal. Can be "HIGH", "MEDIUM" or "LOW"',
)

threats_output = ListProperty(
    title="threats",
    description="list of threats for the animal",
    item_type=StringProperty(title="threat"),
)

# Nodes
start_node = StartNode(name="start_node", inputs=[input_1])
summarize_node = LlmNode(
    name="summarize_node",
    prompt_template="""Extract from the following article the name of the animal, its danger level and the threats it's subject to. The article:\n\n {{article}}""",
    llm_config=llm_config,
    outputs=[animal_output, danger_level_output, threats_output]
)
end_node = EndNode(name="end_node", outputs=[animal_output, danger_level_output, threats_output])

The rest of the pieces in the flow remain the same:

# Control flow edges
control_edge_1 = ControlFlowEdge(
    name="control_edge_1", from_node=start_node, to_node=summarize_node
)
control_edge_2 = ControlFlowEdge(
    name="control_edge_2", from_node=summarize_node, to_node=end_node
)

# Data flow edges
data_edge_1 = DataFlowEdge(
    name="data_edge_1",
    source_node=start_node,
    source_output="article",
    destination_node=summarize_node,
    destination_input="article",
)
data_edge_2 = DataFlowEdge(
    name="data_edge_2",
    source_node=summarize_node,
    source_output="animal_name",
    destination_node=end_node,
    destination_input="animal_name",
)
data_edge_3 = DataFlowEdge(
    name="data_edge_3",
    source_node=summarize_node,
    source_output="danger_level",
    destination_node=end_node,
    destination_input="danger_level",
)
data_edge_4 = DataFlowEdge(
    name="data_edge_4",
    source_node=summarize_node,
    source_output="threats",
    destination_node=end_node,
    destination_input="threats",
)

# Flow
flow = Flow(
    name="Animal and its danger level and exposed threats",
    start_node=start_node,
    nodes=[start_node, summarize_node, end_node],
    control_flow_connections=[control_edge_1, control_edge_2],
    data_flow_connections=[data_edge_1, data_edge_2, data_edge_3, data_edge_4],
    inputs=[input_1],
    outputs=[animal_output, danger_level_output, threats_output],
)

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": "52fb7bab-9989-49c0-930e-77b728b97f4e",
    "name": "Animal and its danger level and exposed threats",
    "description": null,
    "metadata": {},
    "inputs": [
        {
            "title": "article",
            "type": "string"
        }
    ],
    "outputs": [
        {
            "description": "name of the animal",
            "title": "animal_name",
            "type": "string"
        },
        {
            "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
            "title": "danger_level",
            "type": "string"
        },
        {
            "description": "list of threats for the animal",
            "title": "threats",
            "items": {
                "title": "threat",
                "type": "string"
            },
            "type": "array"
        }
    ],
    "start_node": {
        "$component_ref": "d544d463-9a8d-4bbb-8982-609d8dc663cb"
    },
    "nodes": [
        {
            "$component_ref": "d544d463-9a8d-4bbb-8982-609d8dc663cb"
        },
        {
            "$component_ref": "94cb0f73-df32-4635-87dd-24966b081c3e"
        },
        {
            "$component_ref": "5ea3f769-c3af-4e34-8aff-5f9a35976074"
        }
    ],
    "control_flow_connections": [
        {
            "component_type": "ControlFlowEdge",
            "id": "66cc3822-70ea-431d-a39a-e6cf7912396c",
            "name": "control_edge_1",
            "description": null,
            "metadata": {},
            "from_node": {
                "$component_ref": "d544d463-9a8d-4bbb-8982-609d8dc663cb"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "94cb0f73-df32-4635-87dd-24966b081c3e"
            }
        },
        {
            "component_type": "ControlFlowEdge",
            "id": "735b6eaf-f2ca-4fca-bf46-7ca356caae54",
            "name": "control_edge_2",
            "description": null,
            "metadata": {},
            "from_node": {
                "$component_ref": "94cb0f73-df32-4635-87dd-24966b081c3e"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "5ea3f769-c3af-4e34-8aff-5f9a35976074"
            }
        }
    ],
    "data_flow_connections": [
        {
            "component_type": "DataFlowEdge",
            "id": "983c3dc0-6e94-4d42-b095-3962a5eff33b",
            "name": "data_edge_1",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "d544d463-9a8d-4bbb-8982-609d8dc663cb"
            },
            "source_output": "article",
            "destination_node": {
                "$component_ref": "94cb0f73-df32-4635-87dd-24966b081c3e"
            },
            "destination_input": "article"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "2cc9c735-2d6b-4594-a417-726d51ae8dc6",
            "name": "data_edge_2",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "94cb0f73-df32-4635-87dd-24966b081c3e"
            },
            "source_output": "animal_name",
            "destination_node": {
                "$component_ref": "5ea3f769-c3af-4e34-8aff-5f9a35976074"
            },
            "destination_input": "animal_name"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "5f6d0b8b-bdb9-4072-b788-5c54540e4cd4",
            "name": "data_edge_3",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "94cb0f73-df32-4635-87dd-24966b081c3e"
            },
            "source_output": "danger_level",
            "destination_node": {
                "$component_ref": "5ea3f769-c3af-4e34-8aff-5f9a35976074"
            },
            "destination_input": "danger_level"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "26dde279-b7c7-4486-be20-220a366d8596",
            "name": "data_edge_4",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "94cb0f73-df32-4635-87dd-24966b081c3e"
            },
            "source_output": "threats",
            "destination_node": {
                "$component_ref": "5ea3f769-c3af-4e34-8aff-5f9a35976074"
            },
            "destination_input": "threats"
        }
    ],
    "$referenced_components": {
        "d544d463-9a8d-4bbb-8982-609d8dc663cb": {
            "component_type": "StartNode",
            "id": "d544d463-9a8d-4bbb-8982-609d8dc663cb",
            "name": "start_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "outputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "branches": [
                "next"
            ]
        },
        "94cb0f73-df32-4635-87dd-24966b081c3e": {
            "component_type": "LlmNode",
            "id": "94cb0f73-df32-4635-87dd-24966b081c3e",
            "name": "summarize_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "outputs": [
                {
                    "description": "name of the animal",
                    "title": "animal_name",
                    "type": "string"
                },
                {
                    "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                    "title": "danger_level",
                    "type": "string"
                },
                {
                    "description": "list of threats for the animal",
                    "title": "threats",
                    "items": {
                        "title": "threat",
                        "type": "string"
                    },
                    "type": "array"
                }
            ],
            "branches": [
                "next"
            ],
            "llm_config": {
                "component_type": "VllmConfig",
                "id": "127e0dc5-582a-484c-a011-b76b2c373880",
                "name": "llm",
                "description": null,
                "metadata": {},
                "default_generation_parameters": null,
                "url": "url",
                "model_id": "model_id"
            },
            "prompt_template": "Extract from the following article the name of the animal, its danger level and the threats it's subject to. The article:\n\n {{article}}"
        },
        "5ea3f769-c3af-4e34-8aff-5f9a35976074": {
            "component_type": "EndNode",
            "id": "5ea3f769-c3af-4e34-8aff-5f9a35976074",
            "name": "end_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "description": "name of the animal",
                    "title": "animal_name",
                    "type": "string"
                },
                {
                    "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                    "title": "danger_level",
                    "type": "string"
                },
                {
                    "description": "list of threats for the animal",
                    "title": "threats",
                    "items": {
                        "title": "threat",
                        "type": "string"
                    },
                    "type": "array"
                }
            ],
            "outputs": [
                {
                    "description": "name of the animal",
                    "title": "animal_name",
                    "type": "string"
                },
                {
                    "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                    "title": "danger_level",
                    "type": "string"
                },
                {
                    "description": "list of threats for the animal",
                    "title": "threats",
                    "items": {
                        "title": "threat",
                        "type": "string"
                    },
                    "type": "array"
                }
            ],
            "branches": [],
            "branch_name": "next"
        }
    },
    "agentspec_version": "25.4.1"
}

Complex JSON Objects#

In some scenarios, you may need to generate an object that conforms to a specific JSON schema. This can be achieved as follows:

from pyagentspec.flows.nodes import EndNode, LlmNode, StartNode
from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
from pyagentspec.flows.flow import Flow
from pyagentspec.property import StringProperty, Property

input_1 = StringProperty(title="article")

# Output schema
animal_json_schema = {
    "title": "animal_object",
    "description": "information about the animal",
    "type": "object",
    "properties": {
        "animal_name": {
            "type": "string",
            "description": "name of the animal",
            "default": "",
        },
        "danger_level": {
            "type": "string",
            "description": 'level of danger of the animal. Can be "HIGH", "MEDIUM" or "LOW"',
            "default": "",
        },
        "threats": {
            "type": "array",
            "description": "list of threats for the animal",
            "items": {"type": "string"},
            "default": [],
        },
    },
}
animal_object = Property(json_schema=animal_json_schema)

# Nodes
start_node = StartNode(name="start_node", inputs=[input_1])
summarize_node = LlmNode(
    name="summarize_node",
    prompt_template="""Extract from the following article the name of the animal, its danger level and the threats it's subject to. The article:\n\n {{article}}""",
    llm_config=llm_config,
    outputs=[animal_object]
)
end_node = EndNode(name="end_node", outputs=[animal_object])

Note the difference from the previous example: there were three separate outputs, whereas here, a single object encapsulates all data as specified by the schema. Rest of the flow remain the same.

# Control Flow edges
control_edge_1 = ControlFlowEdge(
    name="control_edge_1", from_node=start_node, to_node=summarize_node
)
control_edge_2 = ControlFlowEdge(
    name="control_edge_2", from_node=summarize_node, to_node=end_node
)

# Data flow edges
data_edge_1 = DataFlowEdge(
    name="data_edge_1",
    source_node=start_node,
    source_output="article",
    destination_node=summarize_node,
    destination_input="article",
)
data_edge_2 = DataFlowEdge(
    name="data_edge_2",
    source_node=summarize_node,
    source_output="animal_object",
    destination_node=end_node,
    destination_input="animal_object",
)

# Flow
flow = Flow(
    name="Animal and its danger level and exposed threats",
    start_node=start_node,
    nodes=[start_node, summarize_node, end_node],
    control_flow_connections=[control_edge_1, control_edge_2],
    data_flow_connections=[data_edge_1, data_edge_2],
    inputs=[input_1],
    outputs=[animal_object],
)

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": "a3872d14-d9fa-4af6-a1f8-97d76dacec72",
    "name": "Animal and its danger level and exposed threats",
    "description": null,
    "metadata": {},
    "inputs": [
        {
            "title": "article",
            "type": "string"
        }
    ],
    "outputs": [
        {
            "description": "information about the animal",
            "title": "animal_object",
            "type": "object",
            "properties": {
                "animal_name": {
                    "description": "name of the animal",
                    "type": "string",
                    "default": ""
                },
                "danger_level": {
                    "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                    "type": "string",
                    "default": ""
                },
                "threats": {
                    "description": "list of threats for the animal",
                    "type": "array",
                    "items": {
                        "type": "string"
                    },
                    "default": []
                }
            }
        }
    ],
    "start_node": {
        "$component_ref": "8e78f954-748e-4007-a242-89971bbef05d"
    },
    "nodes": [
        {
            "$component_ref": "8e78f954-748e-4007-a242-89971bbef05d"
        },
        {
            "$component_ref": "991da1c9-5b84-44cd-aee8-c00b5f833e11"
        },
        {
            "$component_ref": "c7a932a5-1690-4537-98c7-d6e03e4c80ec"
        }
    ],
    "control_flow_connections": [
        {
            "component_type": "ControlFlowEdge",
            "id": "7b034c11-5532-488c-9245-02cdaf843828",
            "name": "control_edge_1",
            "description": null,
            "metadata": {},
            "from_node": {
                "$component_ref": "8e78f954-748e-4007-a242-89971bbef05d"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "991da1c9-5b84-44cd-aee8-c00b5f833e11"
            }
        },
        {
            "component_type": "ControlFlowEdge",
            "id": "31fc7e5e-9c91-401d-b42d-33dbfd266366",
            "name": "control_edge_2",
            "description": null,
            "metadata": {},
            "from_node": {
                "$component_ref": "991da1c9-5b84-44cd-aee8-c00b5f833e11"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "c7a932a5-1690-4537-98c7-d6e03e4c80ec"
            }
        }
    ],
    "data_flow_connections": [
        {
            "component_type": "DataFlowEdge",
            "id": "3e2ff8af-1c93-4ad3-8438-729469adb611",
            "name": "data_edge_1",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "8e78f954-748e-4007-a242-89971bbef05d"
            },
            "source_output": "article",
            "destination_node": {
                "$component_ref": "991da1c9-5b84-44cd-aee8-c00b5f833e11"
            },
            "destination_input": "article"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "cef7465f-55be-4745-b7cc-c0096e2a228d",
            "name": "data_edge_2",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "991da1c9-5b84-44cd-aee8-c00b5f833e11"
            },
            "source_output": "animal_object",
            "destination_node": {
                "$component_ref": "c7a932a5-1690-4537-98c7-d6e03e4c80ec"
            },
            "destination_input": "animal_object"
        }
    ],
    "$referenced_components": {
        "8e78f954-748e-4007-a242-89971bbef05d": {
            "component_type": "StartNode",
            "id": "8e78f954-748e-4007-a242-89971bbef05d",
            "name": "start_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "outputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "branches": [
                "next"
            ]
        },
        "991da1c9-5b84-44cd-aee8-c00b5f833e11": {
            "component_type": "LlmNode",
            "id": "991da1c9-5b84-44cd-aee8-c00b5f833e11",
            "name": "summarize_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "outputs": [
                {
                    "description": "information about the animal",
                    "title": "animal_object",
                    "type": "object",
                    "properties": {
                        "animal_name": {
                            "description": "name of the animal",
                            "type": "string",
                            "default": ""
                        },
                        "danger_level": {
                            "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                            "type": "string",
                            "default": ""
                        },
                        "threats": {
                            "description": "list of threats for the animal",
                            "type": "array",
                            "items": {
                                "type": "string"
                            },
                            "default": []
                        }
                    }
                }
            ],
            "branches": [
                "next"
            ],
            "llm_config": {
                "component_type": "VllmConfig",
                "id": "127e0dc5-582a-484c-a011-b76b2c373880",
                "name": "llm",
                "description": null,
                "metadata": {},
                "default_generation_parameters": null,
                "url": "url",
                "model_id": "model_id"
            },
            "prompt_template": "Extract from the following article the name of the animal, its danger level and the threats it's subject to. The article:\n\n {{article}}"
        },
        "c7a932a5-1690-4537-98c7-d6e03e4c80ec": {
            "component_type": "EndNode",
            "id": "c7a932a5-1690-4537-98c7-d6e03e4c80ec",
            "name": "end_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "description": "information about the animal",
                    "title": "animal_object",
                    "type": "object",
                    "properties": {
                        "animal_name": {
                            "description": "name of the animal",
                            "type": "string",
                            "default": ""
                        },
                        "danger_level": {
                            "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                            "type": "string",
                            "default": ""
                        },
                        "threats": {
                            "description": "list of threats for the animal",
                            "type": "array",
                            "items": {
                                "type": "string"
                            },
                            "default": []
                        }
                    }
                }
            ],
            "outputs": [
                {
                    "description": "information about the animal",
                    "title": "animal_object",
                    "type": "object",
                    "properties": {
                        "animal_name": {
                            "description": "name of the animal",
                            "type": "string",
                            "default": ""
                        },
                        "danger_level": {
                            "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                            "type": "string",
                            "default": ""
                        },
                        "threats": {
                            "description": "list of threats for the animal",
                            "type": "array",
                            "items": {
                                "type": "string"
                            },
                            "default": []
                        }
                    }
                }
            ],
            "branches": [],
            "branch_name": "next"
        }
    },
    "agentspec_version": "25.4.1"
}

Structured Generation with Agents#

For more advanced use cases, you may need to invoke additional tools within your flow to process or act on data. You can configure an agent which can do this and it can be used to generate specific structured outputs as shown below:

from pyagentspec.agent import Agent
from pyagentspec.flows.nodes import EndNode, AgentNode, StartNode
from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
from pyagentspec.flows.flow import Flow
from pyagentspec.property import ListProperty, StringProperty

input_1 = StringProperty(title="article")

# Desired outputs
animal_output = StringProperty(
    title="animal_name",
    description="name of the animal",
)

danger_level_output = StringProperty(
    title="danger_level",
    description='level of danger of the animal. Can be "HIGH", "MEDIUM" or "LOW"',
)

threats_output = ListProperty(
    title="threats",
    description="list of threats for the animal",
    item_type=StringProperty(title="threat"),
)

# Nodes
start_node = StartNode(name="start_node", inputs=[input_1])
agent = Agent(
    name="Summarizing agent",
    llm_config=llm_config,
    system_prompt="""Extract from the following article the name of the animal, its danger level and the threats it's subject to. The article:\n\n {{article}}""",
    human_in_the_loop=False,
    outputs=[animal_output, danger_level_output, threats_output]
)
summarize_node = AgentNode(
    name="summarize_node",
    agent=agent,
    outputs=[animal_output, danger_level_output, threats_output]
)
end_node = EndNode(name="end_node", outputs=[animal_output, danger_level_output, threats_output])

Here, the AgentNode is used, which incorporates an Agent and supports outputs similar to LlmNode. However, the agent offers additional capabilities beyond simple generation which can be useful in many scenarios.

Again, rest of the flow construction looks the same:

# Control flow edges
control_edge_1 = ControlFlowEdge(
    name="control_edge_1", from_node=start_node, to_node=summarize_node
)
control_edge_2 = ControlFlowEdge(
    name="control_edge_2", from_node=summarize_node, to_node=end_node
)

# Data flow edges
data_edge_1 = DataFlowEdge(
    name="data_edge_1",
    source_node=start_node,
    source_output="article",
    destination_node=summarize_node,
    destination_input="article",
)
data_edge_2 = DataFlowEdge(
    name="data_edge_2",
    source_node=summarize_node,
    source_output="animal_name",
    destination_node=end_node,
    destination_input="animal_name",
)
data_edge_3 = DataFlowEdge(
    name="data_edge_3",
    source_node=summarize_node,
    source_output="danger_level",
    destination_node=end_node,
    destination_input="danger_level",
)
data_edge_4 = DataFlowEdge(
    name="data_edge_4",
    source_node=summarize_node,
    source_output="threats",
    destination_node=end_node,
    destination_input="threats",
)

# Flow
flow = Flow(
    name="Animal and its danger level and exposed threats",
    start_node=start_node,
    nodes=[start_node, summarize_node, end_node],
    control_flow_connections=[control_edge_1, control_edge_2],
    data_flow_connections=[data_edge_1, data_edge_2, data_edge_3, data_edge_4],
    inputs=[input_1],
    outputs=[animal_output, danger_level_output, threats_output],
)

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": "a7eea168-e593-4ee0-8da9-2e198e2054c1",
    "name": "Animal and its danger level and exposed threats",
    "description": null,
    "metadata": {},
    "inputs": [
        {
            "title": "article",
            "type": "string"
        }
    ],
    "outputs": [
        {
            "description": "name of the animal",
            "title": "animal_name",
            "type": "string"
        },
        {
            "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
            "title": "danger_level",
            "type": "string"
        },
        {
            "description": "list of threats for the animal",
            "title": "threats",
            "items": {
                "title": "threat",
                "type": "string"
            },
            "type": "array"
        }
    ],
    "start_node": {
        "$component_ref": "b16a4c2f-8502-4fe1-9a69-2f720f0d6418"
    },
    "nodes": [
        {
            "$component_ref": "b16a4c2f-8502-4fe1-9a69-2f720f0d6418"
        },
        {
            "$component_ref": "197c0a3b-5661-4ea9-bae5-d1a201ab3197"
        },
        {
            "$component_ref": "baa3b5c3-9016-42b8-a059-4b749894eae4"
        }
    ],
    "control_flow_connections": [
        {
            "component_type": "ControlFlowEdge",
            "id": "010817c2-4e2a-4cc8-a0b2-e5a86d64c920",
            "name": "control_edge_1",
            "description": null,
            "metadata": {},
            "from_node": {
                "$component_ref": "b16a4c2f-8502-4fe1-9a69-2f720f0d6418"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "197c0a3b-5661-4ea9-bae5-d1a201ab3197"
            }
        },
        {
            "component_type": "ControlFlowEdge",
            "id": "595021fc-8d0e-4896-8944-b63acedad793",
            "name": "control_edge_2",
            "description": null,
            "metadata": {},
            "from_node": {
                "$component_ref": "197c0a3b-5661-4ea9-bae5-d1a201ab3197"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "baa3b5c3-9016-42b8-a059-4b749894eae4"
            }
        }
    ],
    "data_flow_connections": [
        {
            "component_type": "DataFlowEdge",
            "id": "dc171b1e-002c-4a75-a4ef-ac20a54ef142",
            "name": "data_edge_1",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "b16a4c2f-8502-4fe1-9a69-2f720f0d6418"
            },
            "source_output": "article",
            "destination_node": {
                "$component_ref": "197c0a3b-5661-4ea9-bae5-d1a201ab3197"
            },
            "destination_input": "article"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "639e85ae-4674-45df-af8f-57408cd2414b",
            "name": "data_edge_2",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "197c0a3b-5661-4ea9-bae5-d1a201ab3197"
            },
            "source_output": "animal_name",
            "destination_node": {
                "$component_ref": "baa3b5c3-9016-42b8-a059-4b749894eae4"
            },
            "destination_input": "animal_name"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "1cbcf149-11bf-4dbc-8280-70a64b743af0",
            "name": "data_edge_3",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "197c0a3b-5661-4ea9-bae5-d1a201ab3197"
            },
            "source_output": "danger_level",
            "destination_node": {
                "$component_ref": "baa3b5c3-9016-42b8-a059-4b749894eae4"
            },
            "destination_input": "danger_level"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "c012de76-b170-47b0-9c2b-acc730e619c6",
            "name": "data_edge_4",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "197c0a3b-5661-4ea9-bae5-d1a201ab3197"
            },
            "source_output": "threats",
            "destination_node": {
                "$component_ref": "baa3b5c3-9016-42b8-a059-4b749894eae4"
            },
            "destination_input": "threats"
        }
    ],
    "$referenced_components": {
        "b16a4c2f-8502-4fe1-9a69-2f720f0d6418": {
            "component_type": "StartNode",
            "id": "b16a4c2f-8502-4fe1-9a69-2f720f0d6418",
            "name": "start_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "outputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "branches": [
                "next"
            ]
        },
        "197c0a3b-5661-4ea9-bae5-d1a201ab3197": {
            "component_type": "AgentNode",
            "id": "197c0a3b-5661-4ea9-bae5-d1a201ab3197",
            "name": "summarize_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "title": "article",
                    "type": "string"
                }
            ],
            "outputs": [
                {
                    "description": "name of the animal",
                    "title": "animal_name",
                    "type": "string"
                },
                {
                    "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                    "title": "danger_level",
                    "type": "string"
                },
                {
                    "description": "list of threats for the animal",
                    "title": "threats",
                    "items": {
                        "title": "threat",
                        "type": "string"
                    },
                    "type": "array"
                }
            ],
            "branches": [
                "next"
            ],
            "agent": {
                "component_type": "Agent",
                "id": "857ebdca-d1e3-47e0-9b5f-813859a84d68",
                "name": "Summarizing agent",
                "description": null,
                "metadata": {},
                "inputs": [
                    {
                        "title": "article",
                        "type": "string"
                    }
                ],
                "outputs": [
                    {
                        "description": "name of the animal",
                        "title": "animal_name",
                        "type": "string"
                    },
                    {
                        "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                        "title": "danger_level",
                        "type": "string"
                    },
                    {
                        "description": "list of threats for the animal",
                        "title": "threats",
                        "items": {
                            "title": "threat",
                            "type": "string"
                        },
                        "type": "array"
                    }
                ],
                "llm_config": {
                    "component_type": "VllmConfig",
                    "id": "127e0dc5-582a-484c-a011-b76b2c373880",
                    "name": "llm",
                    "description": null,
                    "metadata": {},
                    "default_generation_parameters": null,
                    "url": "url",
                    "model_id": "model_id"
                },
                "system_prompt": "Extract from the following article the name of the animal, its danger level and the threats it's subject to. The article:\n\n {{article}}",
                "tools": [],
                "toolboxes": [],
                "human_in_the_loop": false
            }
        },
        "baa3b5c3-9016-42b8-a059-4b749894eae4": {
            "component_type": "EndNode",
            "id": "baa3b5c3-9016-42b8-a059-4b749894eae4",
            "name": "end_node",
            "description": null,
            "metadata": {},
            "inputs": [
                {
                    "description": "name of the animal",
                    "title": "animal_name",
                    "type": "string"
                },
                {
                    "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                    "title": "danger_level",
                    "type": "string"
                },
                {
                    "description": "list of threats for the animal",
                    "title": "threats",
                    "items": {
                        "title": "threat",
                        "type": "string"
                    },
                    "type": "array"
                }
            ],
            "outputs": [
                {
                    "description": "name of the animal",
                    "title": "animal_name",
                    "type": "string"
                },
                {
                    "description": "level of danger of the animal. Can be \"HIGH\", \"MEDIUM\" or \"LOW\"",
                    "title": "danger_level",
                    "type": "string"
                },
                {
                    "description": "list of threats for the animal",
                    "title": "threats",
                    "items": {
                        "title": "threat",
                        "type": "string"
                    },
                    "type": "array"
                }
            ],
            "branches": [],
            "branch_name": "next"
        }
    },
    "agentspec_version": "25.4.1"
}

Recap#

In this guide, you learned how to perform structured generation using Agent Spec, including configuring LLMs, handling JSON schemas, and integrating with agents for advanced use cases.

Below is the complete code from this guide.
from pyagentspec.llms import VllmConfig

llm_config = VllmConfig(
    name="llm",
    model_id="model_id",
    url="url",
)

article = """Sea turtles are ancient reptiles that have been around for over 100 million years. 
             They play crucial roles in marine ecosystems, such as maintaining healthy seagrass beds and coral reefs. 
             Unfortunately, they are under threat due to poaching, habitat loss, and pollution. 
             Conservation efforts worldwide aim to protect nesting sites and reduce bycatch in fishing gear."""

# EXAMPLE - Summary of article
from pyagentspec.flows.nodes import EndNode, LlmNode, StartNode
from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
from pyagentspec.flows.flow import Flow
from pyagentspec.property import StringProperty

# Inputs and outputs
input_1 = StringProperty(title="article")
output_1 = StringProperty(title="summary")

# Nodes
start_node = StartNode(name="start_node", inputs=[input_1])
summarize_node = LlmNode(
    name="summarize_node",
    prompt_template="""Summarize this article in 10 words:\n {{article}}""",
    llm_config=llm_config,
    outputs=[output_1]
)
end_node = EndNode(name="end_node", outputs=[output_1])

# Control flow edges
control_edge_1 = ControlFlowEdge(
    name="control_edge_1", from_node=start_node, to_node=summarize_node
)
control_edge_2 = ControlFlowEdge(
    name="control_edge_2", from_node=summarize_node, to_node=end_node
)

# Data flow edges
data_edge_1 = DataFlowEdge(
    name="data_edge_1",
    source_node=start_node,
    source_output="article",
    destination_node=summarize_node,
    destination_input="article",
)
data_edge_2 = DataFlowEdge(
    name="data_edge_2",
    source_node=summarize_node,
    source_output="summary",
    destination_node=end_node,
    destination_input="summary",
)

# Flow
flow = Flow(
    name="Ten words summary",
    start_node=start_node,
    nodes=[start_node, summarize_node, end_node],
    control_flow_connections=[control_edge_1, control_edge_2],
    data_flow_connections=[data_edge_1, data_edge_2],
    inputs=[input_1],
    outputs=[output_1],
)

from pyagentspec.serialization import AgentSpecSerializer

serialized_flow = AgentSpecSerializer().to_json(flow)

# EXAMPLE - With 3 outputs
from pyagentspec.flows.nodes import EndNode, LlmNode, StartNode
from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
from pyagentspec.flows.flow import Flow
from pyagentspec.property import ListProperty, StringProperty

input_1 = StringProperty(title="article")

# Desired outputs
animal_output = StringProperty(
    title="animal_name",
    description="name of the animal",
)

danger_level_output = StringProperty(
    title="danger_level",
    description='level of danger of the animal. Can be "HIGH", "MEDIUM" or "LOW"',
)

threats_output = ListProperty(
    title="threats",
    description="list of threats for the animal",
    item_type=StringProperty(title="threat"),
)

# Nodes
start_node = StartNode(name="start_node", inputs=[input_1])
summarize_node = LlmNode(
    name="summarize_node",
    prompt_template="""Extract from the following article the name of the animal, its danger level and the threats it's subject to. The article:\n\n {{article}}""",
    llm_config=llm_config,
    outputs=[animal_output, danger_level_output, threats_output]
)
end_node = EndNode(name="end_node", outputs=[animal_output, danger_level_output, threats_output])

# Control flow edges
control_edge_1 = ControlFlowEdge(
    name="control_edge_1", from_node=start_node, to_node=summarize_node
)
control_edge_2 = ControlFlowEdge(
    name="control_edge_2", from_node=summarize_node, to_node=end_node
)

# Data flow edges
data_edge_1 = DataFlowEdge(
    name="data_edge_1",
    source_node=start_node,
    source_output="article",
    destination_node=summarize_node,
    destination_input="article",
)
data_edge_2 = DataFlowEdge(
    name="data_edge_2",
    source_node=summarize_node,
    source_output="animal_name",
    destination_node=end_node,
    destination_input="animal_name",
)
data_edge_3 = DataFlowEdge(
    name="data_edge_3",
    source_node=summarize_node,
    source_output="danger_level",
    destination_node=end_node,
    destination_input="danger_level",
)
data_edge_4 = DataFlowEdge(
    name="data_edge_4",
    source_node=summarize_node,
    source_output="threats",
    destination_node=end_node,
    destination_input="threats",
)

# Flow
flow = Flow(
    name="Animal and its danger level and exposed threats",
    start_node=start_node,
    nodes=[start_node, summarize_node, end_node],
    control_flow_connections=[control_edge_1, control_edge_2],
    data_flow_connections=[data_edge_1, data_edge_2, data_edge_3, data_edge_4],
    inputs=[input_1],
    outputs=[animal_output, danger_level_output, threats_output],
)

from pyagentspec.serialization import AgentSpecSerializer
serialized_flow = AgentSpecSerializer().to_json(flow)

# EXAMPLE - Complex JSON
from pyagentspec.flows.nodes import EndNode, LlmNode, StartNode
from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
from pyagentspec.flows.flow import Flow
from pyagentspec.property import StringProperty, Property

input_1 = StringProperty(title="article")

# Output schema
animal_json_schema = {
    "title": "animal_object",
    "description": "information about the animal",
    "type": "object",
    "properties": {
        "animal_name": {
            "type": "string",
            "description": "name of the animal",
            "default": "",
        },
        "danger_level": {
            "type": "string",
            "description": 'level of danger of the animal. Can be "HIGH", "MEDIUM" or "LOW"',
            "default": "",
        },
        "threats": {
            "type": "array",
            "description": "list of threats for the animal",
            "items": {"type": "string"},
            "default": [],
        },
    },
}
animal_object = Property(json_schema=animal_json_schema)

# Nodes
start_node = StartNode(name="start_node", inputs=[input_1])
summarize_node = LlmNode(
    name="summarize_node",
    prompt_template="""Extract from the following article the name of the animal, its danger level and the threats it's subject to. The article:\n\n {{article}}""",
    llm_config=llm_config,
    outputs=[animal_object]
)
end_node = EndNode(name="end_node", outputs=[animal_object])

# Control Flow edges
control_edge_1 = ControlFlowEdge(
    name="control_edge_1", from_node=start_node, to_node=summarize_node
)
control_edge_2 = ControlFlowEdge(
    name="control_edge_2", from_node=summarize_node, to_node=end_node
)

# Data flow edges
data_edge_1 = DataFlowEdge(
    name="data_edge_1",
    source_node=start_node,
    source_output="article",
    destination_node=summarize_node,
    destination_input="article",
)
data_edge_2 = DataFlowEdge(
    name="data_edge_2",
    source_node=summarize_node,
    source_output="animal_object",
    destination_node=end_node,
    destination_input="animal_object",
)

# Flow
flow = Flow(
    name="Animal and its danger level and exposed threats",
    start_node=start_node,
    nodes=[start_node, summarize_node, end_node],
    control_flow_connections=[control_edge_1, control_edge_2],
    data_flow_connections=[data_edge_1, data_edge_2],
    inputs=[input_1],
    outputs=[animal_object],
)

from pyagentspec.serialization import AgentSpecSerializer

serialized_flow = AgentSpecSerializer().to_json(flow)

# EXAMPLE - With Agents
from pyagentspec.agent import Agent
from pyagentspec.flows.nodes import EndNode, AgentNode, StartNode
from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
from pyagentspec.flows.flow import Flow
from pyagentspec.property import ListProperty, StringProperty

input_1 = StringProperty(title="article")

# Desired outputs
animal_output = StringProperty(
    title="animal_name",
    description="name of the animal",
)

danger_level_output = StringProperty(
    title="danger_level",
    description='level of danger of the animal. Can be "HIGH", "MEDIUM" or "LOW"',
)

threats_output = ListProperty(
    title="threats",
    description="list of threats for the animal",
    item_type=StringProperty(title="threat"),
)

# Nodes
start_node = StartNode(name="start_node", inputs=[input_1])
agent = Agent(
    name="Summarizing agent",
    llm_config=llm_config,
    system_prompt="""Extract from the following article the name of the animal, its danger level and the threats it's subject to. The article:\n\n {{article}}""",
    human_in_the_loop=False,
    outputs=[animal_output, danger_level_output, threats_output]
)
summarize_node = AgentNode(
    name="summarize_node",
    agent=agent,
    outputs=[animal_output, danger_level_output, threats_output]
)
end_node = EndNode(name="end_node", outputs=[animal_output, danger_level_output, threats_output])

# Control flow edges
control_edge_1 = ControlFlowEdge(
    name="control_edge_1", from_node=start_node, to_node=summarize_node
)
control_edge_2 = ControlFlowEdge(
    name="control_edge_2", from_node=summarize_node, to_node=end_node
)

# Data flow edges
data_edge_1 = DataFlowEdge(
    name="data_edge_1",
    source_node=start_node,
    source_output="article",
    destination_node=summarize_node,
    destination_input="article",
)
data_edge_2 = DataFlowEdge(
    name="data_edge_2",
    source_node=summarize_node,
    source_output="animal_name",
    destination_node=end_node,
    destination_input="animal_name",
)
data_edge_3 = DataFlowEdge(
    name="data_edge_3",
    source_node=summarize_node,
    source_output="danger_level",
    destination_node=end_node,
    destination_input="danger_level",
)
data_edge_4 = DataFlowEdge(
    name="data_edge_4",
    source_node=summarize_node,
    source_output="threats",
    destination_node=end_node,
    destination_input="threats",
)

# Flow
flow = Flow(
    name="Animal and its danger level and exposed threats",
    start_node=start_node,
    nodes=[start_node, summarize_node, end_node],
    control_flow_connections=[control_edge_1, control_edge_2],
    data_flow_connections=[data_edge_1, data_edge_2, data_edge_3, data_edge_4],
    inputs=[input_1],
    outputs=[animal_output, danger_level_output, threats_output],
)

from pyagentspec.serialization import AgentSpecSerializer
serialized_flow = AgentSpecSerializer().to_json(flow)

Next Steps#

Having learned how to build flows with structured LLM generation, you may now proceed to: