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,
)
from pyagentspec.llms import VllmConfig
llm_config = VllmConfig(
name="Llama 3.1 8B instruct",
url="VLLM_URL",
model_id="model-id",
)
from pyagentspec.llms import OllamaConfig
llm_config = OllamaConfig(
name="Ollama Config",
model_id="model-id",
)
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"
}
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"
}
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"
}
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"
}
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: