How to Develop a Flow with Conditional Branches#
This guide demonstrates how to:
Define properties to document the input and output types of nodes in a flow
Configure an LLM model
Define different types of nodes for a flow
Define edges for controlling the flow (including branching and loops) and passing data between nodes
Define a flow and export its Agent Spec configuration
The example illustrates an assistant which generates code, reviews the generated code, and either submits or regenerates it based on the review. This flow pattern can be applied to other use cases.
Basic implementation#
1. Define properties#
Properties are used to annotate node configurations with the expected input and output types. This helps enforce data consistency throughout the flow.
from pyagentspec.property import Property
user_request_property = Property(
json_schema=dict(
title="user_request",
type="string",
)
)
code_property = Property(
json_schema=dict(
title="code",
type="string",
default="",
)
)
review_property = Property(
json_schema=dict(
title="review",
type="string",
default="",
)
)
is_code_ready_property = Property(
json_schema=dict(
title="is_code_ready",
type="boolean",
default=False,
)
)
API Reference: Property
2. Define the LLM model#
Flows typically contain nodes that rely on messages generated by a Large Language Model (LLM). To define these nodes, you must provide the LLM configuration. Several backend options are supported, including 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",
)
API Reference: VllmConfig
3. Define nodes#
The StartNode
specifies the expected input values, while the EndNode
defines the outputs returned upon successful execution of the flow.
from pyagentspec.flows.nodes import EndNode, StartNode
start_node = StartNode(
name="Start node",
inputs=[user_request_property],
)
end_node = EndNode(
name="End node",
outputs=[code_property],
)
API Reference: StartNode and EndNode
Nodes of type LlmNode
are configured with a prompt and an LlmConfig
to generate a new message from a language model.
This message is based on the prompt.
The generated message can be, for example, code or a review of previously generated code, as shown in the examples below:
from pyagentspec.flows.nodes import LlmNode
generate_code_node = LlmNode(
name="Generate code node",
prompt_template="""You are a great code python software engineer.
Please write the python code to satisfy the following user request: "{{user_request}}".
You previously generated the following snippet of code:
```
{{code}}
```
Take inspiration from the snippet of code.
The code reviewer gave the following feedback:
{{review}}
Take also into account the comments in the review
Write only the python code.
""",
llm_config=llm_config,
inputs=[user_request_property, code_property, review_property],
outputs=[code_property],
)
review_code_node = LlmNode(
name="Review code node",
prompt_template="""You are a great code python software engineer, and a highly skilled code reviewer.
Please review the following snippet of python code:
```
{{code}}
```
""",
llm_config=llm_config,
inputs=[code_property],
outputs=[review_property],
)
API Reference: LlmNode
A BranchingNode
can take different paths through the execution of a flow.
In the example below, it is used in conjunction with an LlmNode
that evaluates whether the generated code should be accepted or regenerated.
from pyagentspec.flows.nodes import BranchingNode, LlmNode
is_code_ready_decision_node = LlmNode(
name="Check if code is ready node",
prompt_template="""You are a software engineer with 20 years of experience. You have to take a decision.
Based on the following code review, do you think that the code is ready to be deployed?
```
{{review}}
```
Please answer only with `yes` or `no`.
""",
llm_config=llm_config,
inputs=[review_property],
outputs=[is_code_ready_property],
)
is_code_ready_branching_node = BranchingNode(
name="Is code ready branching node",
mapping={
"yes": "yes",
"no": "no",
},
inputs=[is_code_ready_property],
outputs=[],
)
4. Define the control and data flow edges#
There are two types of edges in a flow:
ControlFlowEdges
control the sequence of nodes that gets executed.DataFlowEdges
define how data is passed from one node to another.
This manner of defining control and data flow edges allows for advanced behaviors such as conditional branching and looping, as illustrated in the example below.
In the control edges defined below, notice that two edges are connected to the BranchingNode
(is_code_ready_branching_to_end_control_edge
) due to the two possible branches of the node.
from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
control_flow_edges = [
ControlFlowEdge(
name="start_to_generate_code_control_edge",
from_node=start_node,
to_node=generate_code_node,
),
ControlFlowEdge(
name="generate_code_to_review_control_edge",
from_node=generate_code_node,
to_node=review_code_node,
),
ControlFlowEdge(
name="review_to_is_code_ready_decision_control_edge",
from_node=review_code_node,
to_node=is_code_ready_decision_node,
),
ControlFlowEdge(
name="is_code_ready_decision_to_is_code_ready_branching_control_edge",
from_node=is_code_ready_decision_node,
to_node=is_code_ready_branching_node,
),
ControlFlowEdge(
name="is_code_ready_branching_to_end_control_edge",
from_node=is_code_ready_branching_node,
from_branch="yes",
to_node=end_node,
),
ControlFlowEdge(
name="is_code_ready_branching_to_generate_code_control_edge",
from_node=is_code_ready_branching_node,
from_branch="no",
to_node=generate_code_node,
),
]
API Reference: ControlFlowEdge
In the data edges below, note that the code
output from the generate_code_node
is passed to three different nodes:
end_node
- because the generated code is the final output of the flow.generate_code_node
itself - to include previously generated code in the prompt and improve the next version.review_code_node
- because the generated code is used to generate a code review.
from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
data_flow_edges = [
DataFlowEdge(
name="start_to_generate_code_user_request_data_edge",
source_node=start_node,
source_output="user_request",
destination_node=generate_code_node,
destination_input="user_request",
),
DataFlowEdge(
name="generate_code_to_end_code_data_edge",
source_node=generate_code_node,
source_output="code",
destination_node=end_node,
destination_input="code",
),
DataFlowEdge(
name="generate_code_to_generate_code_code_data_edge",
source_node=generate_code_node,
source_output="code",
destination_node=generate_code_node,
destination_input="code",
),
DataFlowEdge(
name="generate_code_to_review_code_data_edge",
source_node=generate_code_node,
source_output="code",
destination_node=review_code_node,
destination_input="code",
),
DataFlowEdge(
name="review_code_to_generate_code_review_data_edge",
source_node=review_code_node,
source_output="review",
destination_node=generate_code_node,
destination_input="review",
),
DataFlowEdge(
name="review_code_to_is_code_ready_review_data_edge",
source_node=review_code_node,
source_output="review",
destination_node=is_code_ready_decision_node,
destination_input="review",
),
DataFlowEdge(
name="review_code_to_is_code_ready_flag_data_edge",
source_node=is_code_ready_decision_node,
source_output="is_code_ready",
destination_node=is_code_ready_branching_node,
destination_input="is_code_ready",
),
]
API Reference: DataFlowEdge
5. Define and export the Flow#
With all nodes and edges defined, you can now assemble them into a complete flow.
from pyagentspec.flows.flow import Flow
final_assistant_flow = Flow(
name="Generate code and review flow",
description="Flow that given a user request, generates a python code snippet to satisfy it and passes it through a code review before returning it",
start_node=start_node,
nodes=[
start_node,
generate_code_node,
review_code_node,
is_code_ready_decision_node,
is_code_ready_branching_node,
end_node,
],
control_flow_connections=control_flow_edges,
data_flow_connections=data_flow_edges,
)
The Agent Spec configuration is generated in JSON format. 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.
from pyagentspec.serialization import AgentSpecSerializer
if __name__ == "__main__":
serialized_agent = AgentSpecSerializer().to_json(final_assistant_flow)
print(serialized_agent)
API Reference: AgentSpecSerializer
Here is what the Agent Spec representation will look like ↓
Click here to see the assistant configuration.
{
"component_type": "Flow",
"id": "0facfa1b-11ac-4fcd-b95b-61955a94353e",
"name": "Generate code and review flow",
"description": "Flow that given a user request, generates a python code snippet to satisfy it and passes it through a code review before returning it",
"metadata": {},
"inputs": [
{
"title": "user_request",
"type": "string"
}
],
"outputs": [
{
"title": "code",
"type": "string",
"default": ""
}
],
"start_node": {
"$component_ref": "df5db1b8-528f-4fc9-918f-889677bf154b"
},
"nodes": [
{
"$component_ref": "df5db1b8-528f-4fc9-918f-889677bf154b"
},
{
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
},
{
"$component_ref": "52049362-86df-400d-ab95-112ce0d045dc"
},
{
"$component_ref": "e7c5ca9a-a008-43c0-93f2-efc337a01d90"
},
{
"$component_ref": "075642ba-b177-428d-939f-3b1e16def02c"
},
{
"$component_ref": "3fb86623-80d5-40aa-9362-88b778c61e9b"
}
],
"control_flow_connections": [
{
"component_type": "ControlFlowEdge",
"id": "e98a0f0f-6372-4f4b-8c5c-1e61c5fdb677",
"name": "start_to_generate_code_control_edge",
"description": null,
"metadata": {},
"from_node": {
"$component_ref": "df5db1b8-528f-4fc9-918f-889677bf154b"
},
"from_branch": null,
"to_node": {
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
}
},
{
"component_type": "ControlFlowEdge",
"id": "22705617-f433-4f66-bb75-8ff1e35fed1c",
"name": "generate_code_to_review_control_edge",
"description": null,
"metadata": {},
"from_node": {
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
},
"from_branch": null,
"to_node": {
"$component_ref": "52049362-86df-400d-ab95-112ce0d045dc"
}
},
{
"component_type": "ControlFlowEdge",
"id": "c65c4d53-42cd-43fb-9faf-1138a23f49dc",
"name": "review_to_is_code_ready_decision_control_edge",
"description": null,
"metadata": {},
"from_node": {
"$component_ref": "52049362-86df-400d-ab95-112ce0d045dc"
},
"from_branch": null,
"to_node": {
"$component_ref": "e7c5ca9a-a008-43c0-93f2-efc337a01d90"
}
},
{
"component_type": "ControlFlowEdge",
"id": "770ee2ef-9ec4-42e3-b323-767219d3ebe2",
"name": "is_code_ready_decision_to_is_code_ready_branching_control_edge",
"description": null,
"metadata": {},
"from_node": {
"$component_ref": "e7c5ca9a-a008-43c0-93f2-efc337a01d90"
},
"from_branch": null,
"to_node": {
"$component_ref": "075642ba-b177-428d-939f-3b1e16def02c"
}
},
{
"component_type": "ControlFlowEdge",
"id": "9763ebfe-e4ce-41e1-98fe-e1c34ca60705",
"name": "is_code_ready_branching_to_end_control_edge",
"description": null,
"metadata": {},
"from_node": {
"$component_ref": "075642ba-b177-428d-939f-3b1e16def02c"
},
"from_branch": "yes",
"to_node": {
"$component_ref": "3fb86623-80d5-40aa-9362-88b778c61e9b"
}
},
{
"component_type": "ControlFlowEdge",
"id": "f5186c2a-f5df-4a41-a116-a6a1aa869d68",
"name": "is_code_ready_branching_to_generate_code_control_edge",
"description": null,
"metadata": {},
"from_node": {
"$component_ref": "075642ba-b177-428d-939f-3b1e16def02c"
},
"from_branch": "no",
"to_node": {
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
}
}
],
"data_flow_connections": [
{
"component_type": "DataFlowEdge",
"id": "1ba462c6-905f-4f1b-b26c-c8d5cb95df06",
"name": "start_to_generate_code_user_request_data_edge",
"description": null,
"metadata": {},
"source_node": {
"$component_ref": "df5db1b8-528f-4fc9-918f-889677bf154b"
},
"source_output": "user_request",
"destination_node": {
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
},
"destination_input": "user_request"
},
{
"component_type": "DataFlowEdge",
"id": "4f2924f2-fb71-4a11-abad-204a7cdcaeca",
"name": "generate_code_to_end_code_data_edge",
"description": null,
"metadata": {},
"source_node": {
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
},
"source_output": "code",
"destination_node": {
"$component_ref": "3fb86623-80d5-40aa-9362-88b778c61e9b"
},
"destination_input": "code"
},
{
"component_type": "DataFlowEdge",
"id": "8c2d7b8f-2bdd-4119-a047-2f692d54c4ec",
"name": "generate_code_to_generate_code_code_data_edge",
"description": null,
"metadata": {},
"source_node": {
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
},
"source_output": "code",
"destination_node": {
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
},
"destination_input": "code"
},
{
"component_type": "DataFlowEdge",
"id": "e299c1e1-570f-4b8a-aa75-0a4b2af3bb22",
"name": "generate_code_to_review_code_data_edge",
"description": null,
"metadata": {},
"source_node": {
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
},
"source_output": "code",
"destination_node": {
"$component_ref": "52049362-86df-400d-ab95-112ce0d045dc"
},
"destination_input": "code"
},
{
"component_type": "DataFlowEdge",
"id": "6ab1d280-197b-4f9f-85d6-b12512e63b6c",
"name": "review_code_to_generate_code_review_data_edge",
"description": null,
"metadata": {},
"source_node": {
"$component_ref": "52049362-86df-400d-ab95-112ce0d045dc"
},
"source_output": "review",
"destination_node": {
"$component_ref": "a97259f8-8be3-42ac-9909-e21cdd07e9a5"
},
"destination_input": "review"
},
{
"component_type": "DataFlowEdge",
"id": "e01a059a-3745-46f6-a249-b080bca5466d",
"name": "review_code_to_is_code_ready_review_data_edge",
"description": null,
"metadata": {},
"source_node": {
"$component_ref": "52049362-86df-400d-ab95-112ce0d045dc"
},
"source_output": "review",
"destination_node": {
"$component_ref": "e7c5ca9a-a008-43c0-93f2-efc337a01d90"
},
"destination_input": "review"
},
{
"component_type": "DataFlowEdge",
"id": "9e499cf0-cf9f-44c8-8ad8-02ce8bb75832",
"name": "review_code_to_is_code_ready_flag_data_edge",
"description": null,
"metadata": {},
"source_node": {
"$component_ref": "e7c5ca9a-a008-43c0-93f2-efc337a01d90"
},
"source_output": "is_code_ready",
"destination_node": {
"$component_ref": "075642ba-b177-428d-939f-3b1e16def02c"
},
"destination_input": "is_code_ready"
}
],
"$referenced_components": {
"a97259f8-8be3-42ac-9909-e21cdd07e9a5": {
"component_type": "LlmNode",
"id": "a97259f8-8be3-42ac-9909-e21cdd07e9a5",
"name": "Generate code node",
"description": null,
"metadata": {},
"inputs": [
{
"title": "user_request",
"type": "string"
},
{
"title": "code",
"type": "string",
"default": ""
},
{
"title": "review",
"type": "string",
"default": ""
}
],
"outputs": [
{
"title": "code",
"type": "string",
"default": ""
}
],
"branches": [
"next"
],
"llm_config": {
"$component_ref": "4401c9a2-d5d3-409e-a2c1-e7f8a83c4570"
},
"prompt_template": "You are a great code python software engineer.\n\n Please write the python code to satisfy the following user request: \"{{user_request}}\".\n\n You previously generated the following snippet of code:\n ```\n {{code}}\n ```\n Take inspiration from the snippet of code.\n\n The code reviewer gave the following feedback:\n {{review}}\n Take also into account the comments in the review\n\n Write only the python code.\n "
},
"4401c9a2-d5d3-409e-a2c1-e7f8a83c4570": {
"component_type": "VllmConfig",
"id": "4401c9a2-d5d3-409e-a2c1-e7f8a83c4570",
"name": "Vllm model",
"description": null,
"metadata": {},
"default_generation_parameters": null,
"url": "vllm_url",
"model_id": "model_id"
},
"df5db1b8-528f-4fc9-918f-889677bf154b": {
"component_type": "StartNode",
"id": "df5db1b8-528f-4fc9-918f-889677bf154b",
"name": "Start node",
"description": null,
"metadata": {},
"inputs": [
{
"title": "user_request",
"type": "string"
}
],
"outputs": [
{
"title": "user_request",
"type": "string"
}
],
"branches": [
"next"
]
},
"52049362-86df-400d-ab95-112ce0d045dc": {
"component_type": "LlmNode",
"id": "52049362-86df-400d-ab95-112ce0d045dc",
"name": "Review code node",
"description": null,
"metadata": {},
"inputs": [
{
"title": "code",
"type": "string",
"default": ""
}
],
"outputs": [
{
"title": "review",
"type": "string",
"default": ""
}
],
"branches": [
"next"
],
"llm_config": {
"$component_ref": "4401c9a2-d5d3-409e-a2c1-e7f8a83c4570"
},
"prompt_template": "You are a great code python software engineer, and a highly skilled code reviewer.\n Please review the following snippet of python code:\n ```\n {{code}}\n ```\n "
},
"e7c5ca9a-a008-43c0-93f2-efc337a01d90": {
"component_type": "LlmNode",
"id": "e7c5ca9a-a008-43c0-93f2-efc337a01d90",
"name": "Check if code is ready node",
"description": null,
"metadata": {},
"inputs": [
{
"title": "review",
"type": "string",
"default": ""
}
],
"outputs": [
{
"title": "is_code_ready",
"type": "boolean",
"default": false
}
],
"branches": [
"next"
],
"llm_config": {
"$component_ref": "4401c9a2-d5d3-409e-a2c1-e7f8a83c4570"
},
"prompt_template": "You are a software engineer with 20 years of experience. You have to take a decision.\n Based on the following code review, do you think that the code is ready to be deployed?\n ```\n {{review}}\n ```\n Please answer only with `yes` or `no`.\n "
},
"075642ba-b177-428d-939f-3b1e16def02c": {
"component_type": "BranchingNode",
"id": "075642ba-b177-428d-939f-3b1e16def02c",
"name": "Is code ready branching node",
"description": null,
"metadata": {},
"inputs": [
{
"title": "is_code_ready",
"type": "boolean",
"default": false
}
],
"outputs": [],
"branches": [
"default",
"no",
"yes"
],
"mapping": {
"yes": "yes",
"no": "no"
}
},
"3fb86623-80d5-40aa-9362-88b778c61e9b": {
"component_type": "EndNode",
"id": "3fb86623-80d5-40aa-9362-88b778c61e9b",
"name": "End node",
"description": null,
"metadata": {},
"inputs": [
{
"title": "code",
"type": "string",
"default": ""
}
],
"outputs": [
{
"title": "code",
"type": "string",
"default": ""
}
],
"branches": [],
"branch_name": "next"
}
},
"agentspec_version": "25.4.1"
}
component_type: Flow
id: c00190a0-6fc4-4f28-9577-1bf9493304c6
name: Generate code and review flow
description: Flow that given a user request, generates a python code snippet to satisfy
it and passes it through a code review before returning it
metadata: {}
inputs:
- title: user_request
type: string
outputs:
- title: code
type: string
default: ''
start_node:
$component_ref: e486b685-8af7-40d0-87c9-3d8a8f8ac9ca
nodes:
- $component_ref: e486b685-8af7-40d0-87c9-3d8a8f8ac9ca
- $component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
- $component_ref: 05e0070f-20d6-4087-842f-0bc0797e07a7
- $component_ref: 942a3daf-c3d2-40a1-b986-0a50e0027d74
- $component_ref: d2771d05-cea1-4d91-a7ee-e1789db6598e
- $component_ref: e44296e1-cd8e-41f1-a75e-d1e2dc34bf17
control_flow_connections:
- component_type: ControlFlowEdge
id: 07a6797d-c175-4242-890a-eca87087e6aa
name: start_to_generate_code_control_edge
description: null
metadata: {}
from_node:
$component_ref: e486b685-8af7-40d0-87c9-3d8a8f8ac9ca
from_branch: null
to_node:
$component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
- component_type: ControlFlowEdge
id: 57f5a41d-ad00-4b99-89f7-d2e6905b9aa1
name: generate_code_to_review_control_edge
description: null
metadata: {}
from_node:
$component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
from_branch: null
to_node:
$component_ref: 05e0070f-20d6-4087-842f-0bc0797e07a7
- component_type: ControlFlowEdge
id: 777ee015-8902-49f9-ae85-50f40c1efe14
name: review_to_is_code_ready_decision_control_edge
description: null
metadata: {}
from_node:
$component_ref: 05e0070f-20d6-4087-842f-0bc0797e07a7
from_branch: null
to_node:
$component_ref: 942a3daf-c3d2-40a1-b986-0a50e0027d74
- component_type: ControlFlowEdge
id: 2f6792cb-2dbe-4301-8291-adf4184345fc
name: is_code_ready_decision_to_is_code_ready_branching_control_edge
description: null
metadata: {}
from_node:
$component_ref: 942a3daf-c3d2-40a1-b986-0a50e0027d74
from_branch: null
to_node:
$component_ref: d2771d05-cea1-4d91-a7ee-e1789db6598e
- component_type: ControlFlowEdge
id: 1aeb743f-646d-4c03-9ebb-6594ce5c1022
name: is_code_ready_branching_to_end_control_edge
description: null
metadata: {}
from_node:
$component_ref: d2771d05-cea1-4d91-a7ee-e1789db6598e
from_branch: 'yes'
to_node:
$component_ref: e44296e1-cd8e-41f1-a75e-d1e2dc34bf17
- component_type: ControlFlowEdge
id: fdcc989d-6d81-4299-8e71-60d57469f24f
name: is_code_ready_branching_to_generate_code_control_edge
description: null
metadata: {}
from_node:
$component_ref: d2771d05-cea1-4d91-a7ee-e1789db6598e
from_branch: 'no'
to_node:
$component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
data_flow_connections:
- component_type: DataFlowEdge
id: c99890dc-a45c-4dfb-99f2-95eb064f7cc9
name: start_to_generate_code_user_request_data_edge
description: null
metadata: {}
source_node:
$component_ref: e486b685-8af7-40d0-87c9-3d8a8f8ac9ca
source_output: user_request
destination_node:
$component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
destination_input: user_request
- component_type: DataFlowEdge
id: fd313b9f-4c2e-4f5b-b56e-485392201054
name: generate_code_to_end_code_data_edge
description: null
metadata: {}
source_node:
$component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
source_output: code
destination_node:
$component_ref: e44296e1-cd8e-41f1-a75e-d1e2dc34bf17
destination_input: code
- component_type: DataFlowEdge
id: f2376f1c-2a99-44f6-8b6d-2c7c63426999
name: generate_code_to_generate_code_code_data_edge
description: null
metadata: {}
source_node:
$component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
source_output: code
destination_node:
$component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
destination_input: code
- component_type: DataFlowEdge
id: 4ed023a6-edb7-490e-9231-f6b3c8851631
name: generate_code_to_review_code_data_edge
description: null
metadata: {}
source_node:
$component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
source_output: code
destination_node:
$component_ref: 05e0070f-20d6-4087-842f-0bc0797e07a7
destination_input: code
- component_type: DataFlowEdge
id: 8e19ed78-4fb5-4fc8-bbd2-a6f7a84a7193
name: review_code_to_generate_code_review_data_edge
description: null
metadata: {}
source_node:
$component_ref: 05e0070f-20d6-4087-842f-0bc0797e07a7
source_output: review
destination_node:
$component_ref: fe833040-7c2b-4ccb-948b-438231a81e6c
destination_input: review
- component_type: DataFlowEdge
id: b94e7966-1556-498f-a0fc-30d407ec7def
name: review_code_to_is_code_ready_review_data_edge
description: null
metadata: {}
source_node:
$component_ref: 05e0070f-20d6-4087-842f-0bc0797e07a7
source_output: review
destination_node:
$component_ref: 942a3daf-c3d2-40a1-b986-0a50e0027d74
destination_input: review
- component_type: DataFlowEdge
id: edc7ff9d-8e67-4aab-9128-355a6ab10d31
name: review_code_to_is_code_ready_flag_data_edge
description: null
metadata: {}
source_node:
$component_ref: 942a3daf-c3d2-40a1-b986-0a50e0027d74
source_output: is_code_ready
destination_node:
$component_ref: d2771d05-cea1-4d91-a7ee-e1789db6598e
destination_input: is_code_ready
$referenced_components:
e486b685-8af7-40d0-87c9-3d8a8f8ac9ca:
component_type: StartNode
id: e486b685-8af7-40d0-87c9-3d8a8f8ac9ca
name: Start node
description: null
metadata: {}
inputs:
- title: user_request
type: string
outputs:
- title: user_request
type: string
branches:
- next
fe833040-7c2b-4ccb-948b-438231a81e6c:
component_type: LlmNode
id: fe833040-7c2b-4ccb-948b-438231a81e6c
name: Generate code node
description: null
metadata: {}
inputs:
- title: user_request
type: string
- title: code
type: string
default: ''
- title: review
type: string
default: ''
outputs:
- title: code
type: string
default: ''
branches:
- next
llm_config:
$component_ref: cd9d2aa4-9506-4013-b85c-7c77b275e059
prompt_template: "You are a great code python software engineer.\n\n Please\
\ write the python code to satisfy the following user request: \"{{user_request}}\"\
.\n\n You previously generated the following snippet of code:\n ```\n\
\ {{code}}\n ```\n Take inspiration from the snippet of code.\n\n \
\ The code reviewer gave the following feedback:\n {{review}}\n Take\
\ also into account the comments in the review\n\n Write only the python\
\ code.\n "
cd9d2aa4-9506-4013-b85c-7c77b275e059:
component_type: VllmConfig
id: cd9d2aa4-9506-4013-b85c-7c77b275e059
name: Vllm model
description: null
metadata: {}
default_generation_parameters: null
url: vllm_url
model_id: model_id
05e0070f-20d6-4087-842f-0bc0797e07a7:
component_type: LlmNode
id: 05e0070f-20d6-4087-842f-0bc0797e07a7
name: Review code node
description: null
metadata: {}
inputs:
- title: code
type: string
default: ''
outputs:
- title: review
type: string
default: ''
branches:
- next
llm_config:
$component_ref: cd9d2aa4-9506-4013-b85c-7c77b275e059
prompt_template: "You are a great code python software engineer, and a highly\
\ skilled code reviewer.\n Please review the following snippet of python\
\ code:\n ```\n {{code}}\n ```\n "
942a3daf-c3d2-40a1-b986-0a50e0027d74:
component_type: LlmNode
id: 942a3daf-c3d2-40a1-b986-0a50e0027d74
name: Check if code is ready node
description: null
metadata: {}
inputs:
- title: review
type: string
default: ''
outputs:
- title: is_code_ready
type: boolean
default: false
branches:
- next
llm_config:
$component_ref: cd9d2aa4-9506-4013-b85c-7c77b275e059
prompt_template: "You are a software engineer with 20 years of experience. You\
\ have to take a decision.\n Based on the following code review, do you think\
\ that the code is ready to be deployed?\n ```\n {{review}}\n ```\n\
\ Please answer only with `yes` or `no`.\n "
d2771d05-cea1-4d91-a7ee-e1789db6598e:
component_type: BranchingNode
id: d2771d05-cea1-4d91-a7ee-e1789db6598e
name: Is code ready branching node
description: null
metadata: {}
inputs:
- title: is_code_ready
type: boolean
default: false
outputs: []
branches:
- default
- 'no'
- 'yes'
mapping:
'yes': 'yes'
'no': 'no'
e44296e1-cd8e-41f1-a75e-d1e2dc34bf17:
component_type: EndNode
id: e44296e1-cd8e-41f1-a75e-d1e2dc34bf17
name: End node
description: null
metadata: {}
inputs:
- title: code
type: string
default: ''
outputs:
- title: code
type: string
default: ''
branches: []
branch_name: next
agentspec_version: 25.4.1
Recap#
This how-to guide covered how to:
Define properties to document the input and output types of nodes in a flow
Configure an LLM model
Define different types of nodes for a flow
Define edges for controlling the flow (including branching and loops) and passing data between nodes
Define a flow and export its Agent Spec configuration
Below is the complete code from this guide.
1from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge
2from pyagentspec.flows.edges.dataflowedge import DataFlowEdge
3from pyagentspec.flows.flow import Flow
4from pyagentspec.flows.nodes import BranchingNode, EndNode, LlmNode, StartNode
5from pyagentspec.llms.vllmconfig import VllmConfig
6from pyagentspec.property import Property
7from pyagentspec.serialization import AgentSpecSerializer
8
9user_request_property = Property(
10 json_schema=dict(
11 title="user_request",
12 type="string",
13 )
14)
15
16code_property = Property(
17 json_schema=dict(
18 title="code",
19 type="string",
20 default="",
21 )
22)
23
24review_property = Property(
25 json_schema=dict(
26 title="review",
27 type="string",
28 default="",
29 )
30)
31
32is_code_ready_property = Property(
33 json_schema=dict(
34 title="is_code_ready",
35 type="boolean",
36 default=False,
37 )
38)
39
40
41llm_config = VllmConfig(
42 name="Vllm model",
43 url="vllm_url",
44 model_id="model_id",
45)
46
47
48start_node = StartNode(
49 name="Start node",
50 inputs=[user_request_property],
51)
52
53end_node = EndNode(
54 name="End node",
55 outputs=[code_property],
56)
57
58
59generate_code_node = LlmNode(
60 name="Generate code node",
61 prompt_template="""You are a great code python software engineer.
62
63 Please write the python code to satisfy the following user request: "{{user_request}}".
64
65 You previously generated the following snippet of code:
66 ```
67 {{code}}
68 ```
69 Take inspiration from the snippet of code.
70
71 The code reviewer gave the following feedback:
72 {{review}}
73 Take also into account the comments in the review
74
75 Write only the python code.
76 """,
77 llm_config=llm_config,
78 inputs=[user_request_property, code_property, review_property],
79 outputs=[code_property],
80)
81
82review_code_node = LlmNode(
83 name="Review code node",
84 prompt_template="""You are a great code python software engineer, and a highly skilled code reviewer.
85 Please review the following snippet of python code:
86 ```
87 {{code}}
88 ```
89 """,
90 llm_config=llm_config,
91 inputs=[code_property],
92 outputs=[review_property],
93)
94
95is_code_ready_decision_node = LlmNode(
96 name="Check if code is ready node",
97 prompt_template="""You are a software engineer with 20 years of experience. You have to take a decision.
98 Based on the following code review, do you think that the code is ready to be deployed?
99 ```
100 {{review}}
101 ```
102 Please answer only with `yes` or `no`.
103 """,
104 llm_config=llm_config,
105 inputs=[review_property],
106 outputs=[is_code_ready_property],
107)
108
109is_code_ready_branching_node = BranchingNode(
110 name="Is code ready branching node",
111 mapping={
112 "yes": "yes",
113 "no": "no",
114 },
115 inputs=[is_code_ready_property],
116 outputs=[],
117)
118
119control_flow_edges = [
120 ControlFlowEdge(
121 name="start_to_generate_code_control_edge",
122 from_node=start_node,
123 to_node=generate_code_node,
124 ),
125 ControlFlowEdge(
126 name="generate_code_to_review_control_edge",
127 from_node=generate_code_node,
128 to_node=review_code_node,
129 ),
130 ControlFlowEdge(
131 name="review_to_is_code_ready_decision_control_edge",
132 from_node=review_code_node,
133 to_node=is_code_ready_decision_node,
134 ),
135 ControlFlowEdge(
136 name="is_code_ready_decision_to_is_code_ready_branching_control_edge",
137 from_node=is_code_ready_decision_node,
138 to_node=is_code_ready_branching_node,
139 ),
140 ControlFlowEdge(
141 name="is_code_ready_branching_to_end_control_edge",
142 from_node=is_code_ready_branching_node,
143 from_branch="yes",
144 to_node=end_node,
145 ),
146 ControlFlowEdge(
147 name="is_code_ready_branching_to_generate_code_control_edge",
148 from_node=is_code_ready_branching_node,
149 from_branch="no",
150 to_node=generate_code_node,
151 ),
152]
153
154
155data_flow_edges = [
156 DataFlowEdge(
157 name="start_to_generate_code_user_request_data_edge",
158 source_node=start_node,
159 source_output="user_request",
160 destination_node=generate_code_node,
161 destination_input="user_request",
162 ),
163 DataFlowEdge(
164 name="generate_code_to_end_code_data_edge",
165 source_node=generate_code_node,
166 source_output="code",
167 destination_node=end_node,
168 destination_input="code",
169 ),
170 DataFlowEdge(
171 name="generate_code_to_generate_code_code_data_edge",
172 source_node=generate_code_node,
173 source_output="code",
174 destination_node=generate_code_node,
175 destination_input="code",
176 ),
177 DataFlowEdge(
178 name="generate_code_to_review_code_data_edge",
179 source_node=generate_code_node,
180 source_output="code",
181 destination_node=review_code_node,
182 destination_input="code",
183 ),
184 DataFlowEdge(
185 name="review_code_to_generate_code_review_data_edge",
186 source_node=review_code_node,
187 source_output="review",
188 destination_node=generate_code_node,
189 destination_input="review",
190 ),
191 DataFlowEdge(
192 name="review_code_to_is_code_ready_review_data_edge",
193 source_node=review_code_node,
194 source_output="review",
195 destination_node=is_code_ready_decision_node,
196 destination_input="review",
197 ),
198 DataFlowEdge(
199 name="review_code_to_is_code_ready_flag_data_edge",
200 source_node=is_code_ready_decision_node,
201 source_output="is_code_ready",
202 destination_node=is_code_ready_branching_node,
203 destination_input="is_code_ready",
204 ),
205]
206
207
208final_assistant_flow = Flow(
209 name="Generate code and review flow",
210 description="Flow that given a user request, generates a python code snippet to satisfy it and passes it through a code review before returning it",
211 start_node=start_node,
212 nodes=[
213 start_node,
214 generate_code_node,
215 review_code_node,
216 is_code_ready_decision_node,
217 is_code_ready_branching_node,
218 end_node,
219 ],
220 control_flow_connections=control_flow_edges,
221 data_flow_connections=data_flow_edges,
222)
223
224
225if __name__ == "__main__":
226 serialized_agent = AgentSpecSerializer().to_json(final_assistant_flow)
227 print(serialized_agent)
Next steps#
Having learned how to develop a flow with conditional branches, you may now proceed to How to Execute Agent Spec Configuration with WayFlow.