How to Connect Assistants to Your Data#

python-icon Download Python Script

Python script/notebook for this guide.

Datastores how-to script

Prerequisites

This guide assumes familiarity with

Agents rely on access to relevant data to function effectively. Without a steady stream of high-quality data, AI models are unable to learn, reason, or make informed decisions. Connecting an Agent or Flow to data sources is therefore a critical step in developing functional AI systems. This connection enables agents to perceive their environment, update their knowledge or the underlying data source, and adapt to changing conditions.

In this tutorial, you will:

  • Define Entities that an Agent or Flow can access and manipulate.

  • Populate a Datastore with entities for development and testing.

  • Use Datastores in Flows and Agents to create two types of inventory management assistants.

To ensure reproducibility of this tutorial, you will use an in-memory data source.

Concepts shown in this guide#

Note

The InMemoryDatastore is mainly suitable for testing and development, or other use-cases where data persistence across assistants and conversations is not a requirement. For production use-cases, the OracleDatabaseDatastore provides a robust and scalable persistence layer in Oracle Database.

Note that there are a few key differences between an in-memory and a database Datastore: - With database Datastores, all tables relevant to the assistant must already be created in the database prior to connecting to it. - You may choose to only model a subset of the tables available in the database via the Entity construct. - Database Datastores offer an additional query method (and the corresponding DatastoreQueryStep), that enables flexible execution of SQL queries that cannot be modelled by the list operation on the in-memory datastore

Datastores in Flows#

In this section, you will build a simple Flow that performs operations on an inventory database based on user input. This Flow helps users keep product descriptions up to date by leveraging an LLM for the creative writing component.

Step 1. Add imports and LLM configuration#

Import the required packages:

 1from textwrap import dedent
 2
 3from wayflowcore.controlconnection import ControlFlowEdge
 4from wayflowcore.dataconnection import DataFlowEdge
 5from wayflowcore.datastore import Entity, InMemoryDatastore
 6from wayflowcore.datastore.entity import nullable
 7from wayflowcore.flow import Flow
 8from wayflowcore.property import (
 9    AnyProperty,
10    DictProperty,
11    FloatProperty,
12    IntegerProperty,
13    ObjectProperty,
14    StringProperty,
15)
16from wayflowcore.steps import InputMessageStep, OutputMessageStep, PromptExecutionStep
17from wayflowcore.steps.datastoresteps import (
18    DatastoreCreateStep,
19    DatastoreDeleteStep,
20    DatastoreListStep,
21    DatastoreUpdateStep,
22)

In this assistant, you need to use an LLM. WayFlow supports several LLM API providers. Select an LLM from the options below:

from wayflowcore.models import OCIGenAIModel

if __name__ == "__main__":

    llm = OCIGenAIModel(
        model_id="provider.model-id",
        service_endpoint="https://url-to-service-endpoint.com",
        compartment_id="compartment-id",
        auth_type="API_KEY",
    )

Important

API keys should not be stored anywhere in the code. Use environment variables and/or tools such as python-dotenv.

Step 2. Define data#

Start by defining the schema for your data using the Entity construct. In this example, you will manage products in an inventory, so a single collection is sufficient. Datastores also support managing multiple collections at the same time if needed.

 1product = Entity(
 2    properties={
 3        "ID": IntegerProperty(description="Unique product identifier"),
 4        # Descriptions can be helpful if an LLM needs to fill these fields,
 5        # or generally disambiguate non-obvious property names
 6        "title": StringProperty(description="Brief summary of the product"),
 7        "description": StringProperty(),
 8        "price": FloatProperty(default_value=0.1),
 9        # Use nullable to define optional properties
10        "category": nullable(StringProperty()),
11    },
12)

Next, create an InMemoryDataStore. For simplicity, you will use dummy data:

 1datastore = InMemoryDatastore(schema={"products": product})
 2
 3dummy_data = [
 4    {
 5        "ID": 0,
 6        "title": "Broccoli",
 7        "description": "Healty and delicious cruciferous vegetable!",
 8        "price": 1.5,
 9        "category": "Produce",
10    },
11    {
12        "ID": 1,
13        "title": "Oranges",
14        "description": "Vitamin C-filled citrus fruits",
15        "price": 1.8,
16        "category": "Produce",
17    },
18    {
19        "ID": 2,
20        "title": "Shampoo",
21        "description": "Shiny smooth hair in just 10 applications!",
22        "price": 4.5,
23        "category": "Personal hygene",
24    },
25    {
26        "ID": 3,
27        "title": "Crushed ice",
28        "description": "Cool any drink in seconds!",
29        "price": 4.5,
30    },
31]
32
33# Create supports both bulk-creation, as well as single element creation
34datastore.create(collection_name="products", entities=dummy_data[:-1])
35datastore.create(collection_name="products", entities=dummy_data[-1])

Step 3. Create datastore steps#

Now that the Datastore is set up, create steps to perform different operations in the flow. In this case, the assistant only needs to retrieve the current description of products and update it. See the list of all available Datastore steps for more details.

 1datastore_list_step = DatastoreListStep(
 2    datastore,
 3    name="product_list_step",
 4    collection_name="products",
 5    where={"title": "{{user_requested_product}}"},
 6    limit=1,
 7    unpack_single_entity_from_list=True,
 8)
 9
10datastore_update_step = DatastoreUpdateStep(
11    datastore,
12    name="product_update_step",
13    collection_name="products",
14    where={"title": "{{user_requested_product}}"}
15)

Key points:

  • Use where to filter which product to list and update. Configure it with a variable so that the user can dynamically choose the product title.

  • In the DatastoreListStep, limit and unpack_single_entity_from_list are used to assume that product titles are unique, making the output concise and easy to handle.

The input to the DatastoreListStep is the title of the product to retrieve, and the output is a single object containing the corresponding product data. The input to the DatastoreUpdateStep includes both the title of the product to update and the updates to apply, in the form of a dictionary containing the properties and the corresponding values. The output will be the new properties that were updated.

Step 4. Create the Flow#

Now define the Flow for this assistant.

After the user enters which product they want to update, and how the description should be updated, the datastore is queried to find the matching product. The LLM is prompted with the product details and the user’s instructions. The output is used to update the data, and the new result is returned back to the user.

Click to see the rest of the code
  1# We create the steps needed by our flow
  2USER_INPUT_STEP = "user_product_input_step"
  3USER_TASK_INPUT_STEP = "user_task_input_step"
  4LLM_REWRITE_STEP = "llm_rewrite_step"
  5USER_OUTPUT_STEP = "user_output_step"
  6
  7user_input_message_template = dedent(
  8    """I am an inventory Assistant, designed to help you keep product descriptions up-to-date.
  9    What product would you like to update? Please provide its title.
 10    """
 11)
 12
 13user_task_message_template = "How would you like to update the description? I will help you rewrite it according to your instructions"
 14
 15rewrite_description_prompt_template = dedent(
 16    """You are an inventory assistant.
 17
 18    Your task:
 19        - Based on the product details given below, rewrite the description according to the user's request
 20    Important:
 21        - Be helpful and concise in your messages
 22        - Only provide the new description as an output, and nothing else
 23
 24    Here is the User's request:
 25    - {{ user_request }}
 26
 27    Here is the product description:
 28    - {{ product }}
 29    """
 30)
 31
 32user_input_step = InputMessageStep(
 33    name=USER_INPUT_STEP,
 34    message_template=user_input_message_template,
 35)
 36
 37user_task_input_step = InputMessageStep(
 38    name=USER_TASK_INPUT_STEP,
 39    message_template=user_task_message_template,
 40)
 41
 42llm_rewrite_step = PromptExecutionStep(
 43    name=LLM_REWRITE_STEP,
 44    prompt_template=rewrite_description_prompt_template,
 45    llm=llm,
 46    input_descriptors=[
 47        DictProperty("product", key_type=StringProperty(), value_type=AnyProperty())
 48    ],
 49    output_descriptors=[
 50        ObjectProperty(
 51            name=PromptExecutionStep.OUTPUT, properties={"description": StringProperty()}
 52        )
 53    ],
 54)
 55
 56user_output_step = OutputMessageStep(
 57    name=USER_OUTPUT_STEP,
 58    message_template="The product has been updated with the following description: {{ answer['description'] }}",
 59)
 60
 61assistant = Flow(
 62    begin_step=user_input_step,
 63    control_flow_edges=[
 64        ControlFlowEdge(source_step=user_input_step, destination_step=user_task_input_step),
 65        ControlFlowEdge(source_step=user_task_input_step, destination_step=datastore_list_step),
 66        ControlFlowEdge(source_step=datastore_list_step, destination_step=llm_rewrite_step),
 67        ControlFlowEdge(source_step=llm_rewrite_step, destination_step=datastore_update_step),
 68        ControlFlowEdge(source_step=datastore_update_step, destination_step=user_output_step),
 69        ControlFlowEdge(source_step=user_output_step, destination_step=None),
 70    ],
 71    data_flow_edges=[
 72        # The first title given by the user is mapped to the datastore steps for listing and updating
 73        DataFlowEdge(
 74            user_input_step,
 75            InputMessageStep.USER_PROVIDED_INPUT,
 76            datastore_list_step,
 77            "user_requested_product",
 78        ),
 79        DataFlowEdge(
 80            user_input_step,
 81            InputMessageStep.USER_PROVIDED_INPUT,
 82            datastore_update_step,
 83            "user_requested_product",
 84        ),
 85        # The task and product detail are given to the LLM in the prompt execution step
 86        DataFlowEdge(
 87            user_task_input_step,
 88            InputMessageStep.USER_PROVIDED_INPUT,
 89            llm_rewrite_step,
 90            "user_request",
 91        ),
 92        DataFlowEdge(datastore_list_step, DatastoreListStep.ENTITIES, llm_rewrite_step, "product"),
 93        # The generated update is applied on the datastore, and echoed back to the user
 94        DataFlowEdge(
 95            llm_rewrite_step,
 96            PromptExecutionStep.OUTPUT,
 97            datastore_update_step,
 98            DatastoreUpdateStep.UPDATE,
 99        ),
100        DataFlowEdge(llm_rewrite_step, PromptExecutionStep.OUTPUT, user_output_step, "answer"),
101    ],
102)

Note the use of structured generation in the PromptExecutionStep (via the output_descriptors parameter). This ensures that the LLM generates exactly the structure expected by the DatastoreUpdateStep.

Finally, verify that the Flow works:

 1conversation = assistant.start_conversation()
 2conversation.execute()
 3conversation.append_user_message("Broccoli")
 4
 5conversation.execute()
 6conversation.append_user_message(
 7    "Shoppers don't know what 'cruciferous' means, we should find a catchier description."
 8)
 9
10conversation.execute()
11print(conversation.get_last_message().content)

Datastores in Agents#

This section assumes you have completed the previous steps on using datastores in Flows.

The Flow you built earlier is quite helpful and reliable because it performs a single, specialized task. Next, you will see how to define an Agent for inventory management when the task is not defined in advance. This Agent will be able to interpret the user’s requests and autonomously decide which actions on the Datastore are required to fulfill each task.

Step 1. Add imports and LLM configuration#

Add the additional imports needed to use Agents:

1from wayflowcore.agent import Agent

Step 2. Create Datastore Flows for an Agent#

To use the Datastore in an agent, create flows for the different operations you want the agent to perform. In the simplest setup, you can define one flow per basic Datastore operation. The agent will then determine the correct sequence of actions to achieve the user’s goal.

Define flows for:

 1AGENT_PROMPT = dedent(
 2    """
 3    You are an inventory assistant. Your task is to help the user with their requests by using the available tools.
 4    If you are unsure about the action to take, or you don't have the right tool, simply tell the user so and follow their guidance.
 5    """
 6)
 7
 8create_product_flow = Flow.from_steps(
 9    [DatastoreCreateStep(datastore, "products")],
10    name="Create product",
11    description="Creates a new product in the data source",
12)
13list_products_flow = Flow.from_steps(
14    [DatastoreListStep(datastore, "products")],
15    name="List all products",
16    description="Lists all products in the data source.",
17)
18list_one_product_flow = Flow.from_steps(
19    [datastore_list_step],
20    name="List single product",
21    description="Lists a single product in the data source by its title.",
22)
23update_product_flow = Flow.from_steps(
24    [datastore_update_step],
25    name="Update product",
26    description="Updates a product in the data source by its title.",
27)
28delete_product_flow = Flow.from_steps(
29    [DatastoreDeleteStep(datastore, "products", where={"title": "{{product_title}}"})],
30    name="Delete product",
31    description="Delete a product in the data source by its title.",
32)

Notice the provided descriptions for the flows to help the agent understand the objective of each operation.

Additionally, you can incorporate more complex behaviors into these flows. For example, you could ask for user confirmation before deleting entities, or you could provide the user with an overview, and an editing option of the updates made by the agent before they are applied.

Step 3. Create the Agent#

Finally, create the inventory management agent by combining the LLM, the datastore flows, and a custom instruction:

 1AGENT_PROMPT = dedent(
 2    """
 3    You are an inventory assistant. Your task is to help the user with their requests by using the available tools.
 4    If you are unsure about the action to take, or you don't have the right tool, simply tell the user so and follow their guidance.
 5    """
 6)
 7
 8create_product_flow = Flow.from_steps(
 9    [DatastoreCreateStep(datastore, "products")],
10    name="Create product",
11    description="Creates a new product in the data source",
12)
13list_products_flow = Flow.from_steps(
14    [DatastoreListStep(datastore, "products")],
15    name="List all products",
16    description="Lists all products in the data source.",
17)
18list_one_product_flow = Flow.from_steps(
19    [datastore_list_step],
20    name="List single product",
21    description="Lists a single product in the data source by its title.",
22)
23update_product_flow = Flow.from_steps(
24    [datastore_update_step],
25    name="Update product",
26    description="Updates a product in the data source by its title.",
27)
28delete_product_flow = Flow.from_steps(
29    [DatastoreDeleteStep(datastore, "products", where={"title": "{{product_title}}"})],
30    name="Delete product",
31    description="Delete a product in the data source by its title.",
32)

This agent can now respond to the user and perform actions on the data on their behalf.

Refer to the WayFlow Agents Tutorial to see how to run this Agent.

Agent Spec Exporting/Loading#

You can export the assistant configuration to its Agent Spec configuration using the AgentSpecExporter.

from wayflowcore.agentspec import AgentSpecExporter

serialized_agent = AgentSpecExporter().to_json(agent)

Here is what the Agent Spec representation will look like ↓

Click here to see the assistant configuration.
{
  "component_type": "ExtendedAgent",
  "id": "99f1d854-0f10-473a-ad2a-3558b8977244",
  "name": "agent_d18c1f13__auto",
  "description": "",
  "metadata": {
    "__metadata_info__": {}
  },
  "inputs": [],
  "outputs": [],
  "llm_config": {
    "component_type": "VllmConfig",
    "id": "b10b47da-03bc-4bb3-a024-b2d8330a5be8",
    "name": "LLAMA_MODEL_ID",
    "description": null,
    "metadata": {
      "__metadata_info__": {}
    },
    "default_generation_parameters": null,
    "url": "LLAMA_API_URL",
    "model_id": "LLAMA_MODEL_ID"
  },
  "system_prompt": "\nYou are an inventory assistant. Your task is to help the user with their requests by using the available tools.\nIf you are unsure about the action to take, or you don't have the right tool, simply tell the user so and follow their guidance.\n",
  "tools": [],
  "toolboxes": [],
  "context_providers": null,
  "can_finish_conversation": false,
  "max_iterations": 10,
  "initial_message": "Hi! How can I help you?",
  "caller_input_mode": "always",
  "agents": [],
  "flows": [
    {
      "component_type": "Flow",
      "id": "96d3782c-552c-429c-bd7e-4f4af61ff713",
      "name": "Create product",
      "description": "Creates a new product in the data source",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [
        {
          "type": "object",
          "additionalProperties": {},
          "key_type": {
            "type": "string"
          },
          "title": "entity"
        }
      ],
      "outputs": [
        {
          "type": "object",
          "additionalProperties": {},
          "key_type": {
            "type": "string"
          },
          "title": "created_entity"
        }
      ],
      "start_node": {
        "$component_ref": "79b6331a-5543-4310-8d8b-1d9f1337b025"
      },
      "nodes": [
        {
          "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
        },
        {
          "$component_ref": "79b6331a-5543-4310-8d8b-1d9f1337b025"
        },
        {
          "$component_ref": "ae2f0460-cba8-499e-a993-62b946e56d40"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "fe4ef560-e504-4dc8-a298-7dbb3c067a6f",
          "name": "__StartStep___to_step_0_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "79b6331a-5543-4310-8d8b-1d9f1337b025"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "a16d091c-0d0c-4d29-9104-7761bdbb4cd3",
          "name": "step_0_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "ae2f0460-cba8-499e-a993-62b946e56d40"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "58dbd841-e0a5-4c87-ac0c-08a34625323b",
          "name": "__StartStep___entity_to_step_0_entity_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "79b6331a-5543-4310-8d8b-1d9f1337b025"
          },
          "source_output": "entity",
          "destination_node": {
            "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
          },
          "destination_input": "entity"
        },
        {
          "component_type": "DataFlowEdge",
          "id": "92415266-d5a4-40ca-b8e6-57e01ec002a5",
          "name": "step_0_created_entity_to_None End node_created_entity_data_flow_edge",
          "description": null,
          "metadata": {},
          "source_node": {
            "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
          },
          "source_output": "created_entity",
          "destination_node": {
            "$component_ref": "ae2f0460-cba8-499e-a993-62b946e56d40"
          },
          "destination_input": "created_entity"
        }
      ],
      "$referenced_components": {
        "331ca41a-8738-4751-a87e-5cc48b8a3e23": {
          "component_type": "PluginDatastoreCreateNode",
          "id": "331ca41a-8738-4751-a87e-5cc48b8a3e23",
          "name": "step_0",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entity"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "created_entity"
            }
          ],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "ENTITY": "entity",
          "CREATED_ENTITY": "created_entity",
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "79b6331a-5543-4310-8d8b-1d9f1337b025": {
          "component_type": "StartNode",
          "id": "79b6331a-5543-4310-8d8b-1d9f1337b025",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entity"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entity"
            }
          ],
          "branches": [
            "next"
          ]
        },
        "ae2f0460-cba8-499e-a993-62b946e56d40": {
          "component_type": "EndNode",
          "id": "ae2f0460-cba8-499e-a993-62b946e56d40",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "created_entity"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "created_entity"
            }
          ],
          "branches": [],
          "branch_name": "next"
        }
      }
    },
    {
      "component_type": "Flow",
      "id": "ba1cd504-bef6-4af3-9498-cbf6e50a0bca",
      "name": "List all products",
      "description": "Lists all products in the data source.",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [],
      "outputs": [
        {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": {},
            "key_type": {
              "type": "string"
            }
          },
          "title": "entities"
        }
      ],
      "start_node": {
        "$component_ref": "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff"
      },
      "nodes": [
        {
          "$component_ref": "0c924103-23f5-4698-b69e-c553250186ab"
        },
        {
          "$component_ref": "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff"
        },
        {
          "$component_ref": "098f2832-4672-44eb-a060-ea9514ceefc5"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "a0392cfe-bed3-44cb-84c0-20a30f1cbe18",
          "name": "__StartStep___to_step_0_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "0c924103-23f5-4698-b69e-c553250186ab"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "3290795e-59ca-46f8-8a96-5a265d707c13",
          "name": "step_0_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "0c924103-23f5-4698-b69e-c553250186ab"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "098f2832-4672-44eb-a060-ea9514ceefc5"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "308b92b6-70f4-4e9c-b41e-1563bc449a24",
          "name": "step_0_entities_to_None End node_entities_data_flow_edge",
          "description": null,
          "metadata": {},
          "source_node": {
            "$component_ref": "0c924103-23f5-4698-b69e-c553250186ab"
          },
          "source_output": "entities",
          "destination_node": {
            "$component_ref": "098f2832-4672-44eb-a060-ea9514ceefc5"
          },
          "destination_input": "entities"
        }
      ],
      "$referenced_components": {
        "098f2832-4672-44eb-a060-ea9514ceefc5": {
          "component_type": "EndNode",
          "id": "098f2832-4672-44eb-a060-ea9514ceefc5",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "outputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "branches": [],
          "branch_name": "next"
        },
        "0c924103-23f5-4698-b69e-c553250186ab": {
          "component_type": "PluginDatastoreListNode",
          "id": "0c924103-23f5-4698-b69e-c553250186ab",
          "name": "step_0",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [],
          "outputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "where": null,
          "limit": null,
          "unpack_single_entity_from_list": false,
          "ENTITIES": "entities",
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff": {
          "component_type": "StartNode",
          "id": "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [],
          "outputs": [],
          "branches": [
            "next"
          ]
        }
      }
    },
    {
      "component_type": "Flow",
      "id": "bad00138-48b3-4e7c-84a9-51bd385a38a2",
      "name": "List single product",
      "description": "Lists a single product in the data source by its title.",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [
        {
          "description": "\"user_requested_product\" input variable for the template",
          "type": "string",
          "title": "user_requested_product"
        }
      ],
      "outputs": [
        {
          "type": "object",
          "additionalProperties": {},
          "key_type": {
            "type": "string"
          },
          "title": "entities"
        }
      ],
      "start_node": {
        "$component_ref": "76d19c9a-241f-4a4d-bbac-2bcbd3359498"
      },
      "nodes": [
        {
          "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
        },
        {
          "$component_ref": "76d19c9a-241f-4a4d-bbac-2bcbd3359498"
        },
        {
          "$component_ref": "aa8090d6-2220-40ad-ab16-6adb1bdead7b"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "e37ae190-e7f6-4090-8091-c5eddadf7a9d",
          "name": "__StartStep___to_product_list_step_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "76d19c9a-241f-4a4d-bbac-2bcbd3359498"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "80680849-e51b-435d-aee6-cdb7e6571ee7",
          "name": "product_list_step_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "aa8090d6-2220-40ad-ab16-6adb1bdead7b"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "dab6eb95-057c-4adc-85dd-2a40e14713eb",
          "name": "__StartStep___user_requested_product_to_product_list_step_user_requested_product_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "76d19c9a-241f-4a4d-bbac-2bcbd3359498"
          },
          "source_output": "user_requested_product",
          "destination_node": {
            "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
          },
          "destination_input": "user_requested_product"
        },
        {
          "component_type": "DataFlowEdge",
          "id": "82d0a256-6c3d-45a8-a6bf-c1c949069927",
          "name": "product_list_step_entities_to_None End node_entities_data_flow_edge",
          "description": null,
          "metadata": {},
          "source_node": {
            "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
          },
          "source_output": "entities",
          "destination_node": {
            "$component_ref": "aa8090d6-2220-40ad-ab16-6adb1bdead7b"
          },
          "destination_input": "entities"
        }
      ],
      "$referenced_components": {
        "d1b1f436-b91f-435c-867c-ecf9b5b187a1": {
          "component_type": "PluginDatastoreListNode",
          "id": "d1b1f436-b91f-435c-867c-ecf9b5b187a1",
          "name": "product_list_step",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entities"
            }
          ],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "where": {
            "title": "{{user_requested_product}}"
          },
          "limit": 1,
          "unpack_single_entity_from_list": true,
          "ENTITIES": "entities",
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "76d19c9a-241f-4a4d-bbac-2bcbd3359498": {
          "component_type": "StartNode",
          "id": "76d19c9a-241f-4a4d-bbac-2bcbd3359498",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            }
          ],
          "outputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            }
          ],
          "branches": [
            "next"
          ]
        },
        "aa8090d6-2220-40ad-ab16-6adb1bdead7b": {
          "component_type": "EndNode",
          "id": "aa8090d6-2220-40ad-ab16-6adb1bdead7b",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entities"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entities"
            }
          ],
          "branches": [],
          "branch_name": "next"
        }
      }
    },
    {
      "component_type": "Flow",
      "id": "a3047067-dea3-4cc9-a922-c26d35d97c48",
      "name": "Update product",
      "description": "Updates a product in the data source by its title.",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [
        {
          "description": "\"user_requested_product\" input variable for the template",
          "type": "string",
          "title": "user_requested_product"
        },
        {
          "type": "object",
          "additionalProperties": {},
          "key_type": {
            "type": "string"
          },
          "title": "update"
        }
      ],
      "outputs": [
        {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": {},
            "key_type": {
              "type": "string"
            }
          },
          "title": "entities"
        }
      ],
      "start_node": {
        "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
      },
      "nodes": [
        {
          "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
        },
        {
          "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
        },
        {
          "$component_ref": "9074c861-1cf4-4bd9-88dd-3fe209b2c218"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "64436400-797d-4d35-92df-78fc557006f2",
          "name": "__StartStep___to_product_update_step_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "5e55b5d7-7bf8-435a-9044-f05f1b377671",
          "name": "product_update_step_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "9074c861-1cf4-4bd9-88dd-3fe209b2c218"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "43c29773-0a4b-4145-9c0d-ad5238410e9d",
          "name": "__StartStep___user_requested_product_to_product_update_step_user_requested_product_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
          },
          "source_output": "user_requested_product",
          "destination_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          },
          "destination_input": "user_requested_product"
        },
        {
          "component_type": "DataFlowEdge",
          "id": "8e5f0ea8-8ea1-42ba-bdba-95a74082c908",
          "name": "__StartStep___update_to_product_update_step_update_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
          },
          "source_output": "update",
          "destination_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          },
          "destination_input": "update"
        },
        {
          "component_type": "DataFlowEdge",
          "id": "1bd34870-1e52-4896-bfaf-5a71006da687",
          "name": "product_update_step_entities_to_None End node_entities_data_flow_edge",
          "description": null,
          "metadata": {},
          "source_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          },
          "source_output": "entities",
          "destination_node": {
            "$component_ref": "9074c861-1cf4-4bd9-88dd-3fe209b2c218"
          },
          "destination_input": "entities"
        }
      ],
      "$referenced_components": {
        "487a708b-0553-4bb5-b015-948d2a97ede6": {
          "component_type": "PluginDatastoreUpdateNode",
          "id": "487a708b-0553-4bb5-b015-948d2a97ede6",
          "name": "product_update_step",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            },
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "update"
            }
          ],
          "outputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "where": {
            "title": "{{user_requested_product}}"
          },
          "ENTITIES": "entities",
          "UPDATE": "update",
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "8c74a4c5-fdf0-447e-956f-59498da1dc09": {
          "component_type": "StartNode",
          "id": "8c74a4c5-fdf0-447e-956f-59498da1dc09",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            },
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "update"
            }
          ],
          "outputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            },
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "update"
            }
          ],
          "branches": [
            "next"
          ]
        },
        "9074c861-1cf4-4bd9-88dd-3fe209b2c218": {
          "component_type": "EndNode",
          "id": "9074c861-1cf4-4bd9-88dd-3fe209b2c218",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "outputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "branches": [],
          "branch_name": "next"
        }
      }
    },
    {
      "component_type": "Flow",
      "id": "ebae9461-df3a-4544-9cfd-dd2a2e13e34e",
      "name": "Delete product",
      "description": "Delete a product in the data source by its title.",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [
        {
          "description": "\"product_title\" input variable for the template",
          "type": "string",
          "title": "product_title"
        }
      ],
      "outputs": [],
      "start_node": {
        "$component_ref": "13d561e4-1c49-4808-974c-d82311479998"
      },
      "nodes": [
        {
          "$component_ref": "9cd4c282-5290-477d-b9c1-f069a599aa68"
        },
        {
          "$component_ref": "13d561e4-1c49-4808-974c-d82311479998"
        },
        {
          "$component_ref": "49384f2b-1825-4c6b-a876-86e8803b0014"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "7b91ecf7-f24e-4ff3-8df4-f6769847e7a4",
          "name": "__StartStep___to_step_0_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "13d561e4-1c49-4808-974c-d82311479998"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "9cd4c282-5290-477d-b9c1-f069a599aa68"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "e666dfe3-6041-4141-b21b-afedfa74f3ab",
          "name": "step_0_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "9cd4c282-5290-477d-b9c1-f069a599aa68"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "49384f2b-1825-4c6b-a876-86e8803b0014"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "a731fb73-ad3b-4a22-9f0b-35e9f1d63131",
          "name": "__StartStep___product_title_to_step_0_product_title_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "13d561e4-1c49-4808-974c-d82311479998"
          },
          "source_output": "product_title",
          "destination_node": {
            "$component_ref": "9cd4c282-5290-477d-b9c1-f069a599aa68"
          },
          "destination_input": "product_title"
        }
      ],
      "$referenced_components": {
        "9cd4c282-5290-477d-b9c1-f069a599aa68": {
          "component_type": "PluginDatastoreDeleteNode",
          "id": "9cd4c282-5290-477d-b9c1-f069a599aa68",
          "name": "step_0",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"product_title\" input variable for the template",
              "type": "string",
              "title": "product_title"
            }
          ],
          "outputs": [],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "where": {
            "title": "{{product_title}}"
          },
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "13d561e4-1c49-4808-974c-d82311479998": {
          "component_type": "StartNode",
          "id": "13d561e4-1c49-4808-974c-d82311479998",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"product_title\" input variable for the template",
              "type": "string",
              "title": "product_title"
            }
          ],
          "outputs": [
            {
              "description": "\"product_title\" input variable for the template",
              "type": "string",
              "title": "product_title"
            }
          ],
          "branches": [
            "next"
          ]
        },
        "49384f2b-1825-4c6b-a876-86e8803b0014": {
          "component_type": "EndNode",
          "id": "49384f2b-1825-4c6b-a876-86e8803b0014",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [],
          "outputs": [],
          "branches": [],
          "branch_name": "next"
        }
      }
    }
  ],
  "agent_template": {
    "component_type": "PluginPromptTemplate",
    "id": "ea261004-a6d8-4380-af2a-4e5adbaf7b9c",
    "name": "",
    "description": null,
    "metadata": {
      "__metadata_info__": {}
    },
    "messages": [
      {
        "role": "system",
        "contents": [
          {
            "type": "text",
            "content": "{%- if __TOOLS__ -%}\nEnvironment: ipython\nCutting Knowledge Date: December 2023\n\nYou are a helpful assistant with tool calling capabilities. Only reply with a tool call if the function exists in the library provided by the user. If it doesn't exist, just reply directly in natural language. When you receive a tool call response, use the output to format an answer to the original user question.\n\nYou have access to the following functions. To call a function, please respond with JSON for a function call.\nRespond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.\nDo not use variables.\n\n[{% for tool in __TOOLS__%}{{tool.to_openai_format() | tojson}}{{', ' if not loop.last}}{% endfor %}]\n{%- endif -%}\n"
          }
        ],
        "tool_requests": null,
        "tool_result": null,
        "display_only": false,
        "sender": null,
        "recipients": [],
        "time_created": "2025-09-02T15:58:42.673848+00:00",
        "time_updated": "2025-09-02T15:58:42.673852+00:00"
      },
      {
        "role": "system",
        "contents": [
          {
            "type": "text",
            "content": "{%- if custom_instruction -%}Additional instructions:\n{{custom_instruction}}{%- endif -%}"
          }
        ],
        "tool_requests": null,
        "tool_result": null,
        "display_only": false,
        "sender": null,
        "recipients": [],
        "time_created": "2025-09-02T15:58:42.673915+00:00",
        "time_updated": "2025-09-02T15:58:42.673916+00:00"
      },
      {
        "role": "user",
        "contents": [],
        "tool_requests": null,
        "tool_result": null,
        "display_only": false,
        "sender": null,
        "recipients": [],
        "time_created": "2025-09-02T15:58:42.660189+00:00",
        "time_updated": "2025-09-02T15:58:42.660446+00:00"
      },
      {
        "role": "system",
        "contents": [
          {
            "type": "text",
            "content": "{% if __PLAN__ %}The current plan you should follow is the following: \n{{__PLAN__}}{% endif %}"
          }
        ],
        "tool_requests": null,
        "tool_result": null,
        "display_only": false,
        "sender": null,
        "recipients": [],
        "time_created": "2025-09-02T15:58:42.673985+00:00",
        "time_updated": "2025-09-02T15:58:42.673986+00:00"
      }
    ],
    "output_parser": {
      "component_type": "PluginJsonToolOutputParser",
      "id": "8d8b00f0-2c13-4e0f-894c-a46ad91eccbc",
      "name": "jsontool_outputparser",
      "description": null,
      "metadata": {
        "__metadata_info__": {}
      },
      "tools": null,
      "component_plugin_name": "OutputParserPlugin",
      "component_plugin_version": "25.4.0.dev0"
    },
    "inputs": [
      {
        "description": "\"__TOOLS__\" input variable for the template",
        "title": "__TOOLS__"
      },
      {
        "description": "\"custom_instruction\" input variable for the template",
        "type": "string",
        "title": "custom_instruction"
      },
      {
        "description": "\"__PLAN__\" input variable for the template",
        "type": "string",
        "title": "__PLAN__",
        "default": ""
      },
      {
        "type": "array",
        "items": {},
        "title": "__CHAT_HISTORY__"
      }
    ],
    "pre_rendering_transforms": null,
    "post_rendering_transforms": [
      {
        "component_type": "PluginRemoveEmptyNonUserMessageTransform",
        "id": "f964b648-4850-43ca-983b-6bf05ec6fe59",
        "name": "removeemptynonusermessage_messagetransform",
        "description": null,
        "metadata": {
          "__metadata_info__": {}
        },
        "component_plugin_name": "MessageTransformPlugin",
        "component_plugin_version": "25.4.0.dev0"
      },
      {
        "component_type": "PluginCoalesceSystemMessagesTransform",
        "id": "b0c702f1-14a8-4692-b746-490f3cc2a406",
        "name": "coalescesystemmessage_messagetransform",
        "description": null,
        "metadata": {
          "__metadata_info__": {}
        },
        "component_plugin_name": "MessageTransformPlugin",
        "component_plugin_version": "25.4.0.dev0"
      },
      {
        "component_type": "PluginLlamaMergeToolRequestAndCallsTransform",
        "id": "466f3fef-697b-4e2e-804b-cc9788186b0d",
        "name": "llamamergetoolrequestandcalls_messagetransform",
        "description": null,
        "metadata": {
          "__metadata_info__": {}
        },
        "component_plugin_name": "MessageTransformPlugin",
        "component_plugin_version": "25.4.0.dev0"
      }
    ],
    "tools": null,
    "native_tool_calling": false,
    "response_format": null,
    "native_structured_generation": true,
    "generation_config": null,
    "component_plugin_name": "PromptTemplatePlugin",
    "component_plugin_version": "25.4.0.dev0"
  },
  "component_plugin_name": "AgentPlugin",
  "component_plugin_version": "25.4.0.dev0",
  "$referenced_components": {
    "4f8e1008-2f20-456c-9250-679146e88192": {
      "component_type": "PluginInMemoryDatastore",
      "id": "4f8e1008-2f20-456c-9250-679146e88192",
      "name": "PluginInMemoryDatastore",
      "description": null,
      "metadata": {},
      "datastore_schema": {
        "products": {
          "description": "",
          "title": "",
          "properties": {
            "description": {
              "type": "string"
            },
            "ID": {
              "description": "Unique product identifier",
              "type": "integer"
            },
            "title": {
              "description": "Brief summary of the product",
              "type": "string"
            },
            "price": {
              "type": "number",
              "default": 0.1
            },
            "category": {
              "anyOf": [
                {
                  "type": "null"
                },
                {
                  "type": "string"
                }
              ],
              "default": null
            }
          }
        }
      },
      "component_plugin_name": "DatastorePlugin",
      "component_plugin_version": "25.4.0.dev0"
    }
  },
  "agentspec_version": "25.4.1"
}

You can then load the configuration back to an assistant using the AgentSpecLoader.

from wayflowcore.agentspec import AgentSpecLoader

agent = AgentSpecLoader().load_json(serialized_agent)

Note

This guide uses the following extension/plugin Agent Spec components:

  • PluginPromptTemplate

  • PluginDatastoreCreateNode

  • PluginDatastoreUpdateNode

  • PluginDatastoreListNode

  • PluginDatastoreDeleteNode

  • PluginJsonToolOutputParser

  • PluginRemoveEmptyNonUserMessageTransform

  • PluginCoalesceSystemMessagesTransform

  • PluginLlamaMergeToolRequestAndCallsTransform

  • PluginInMemoryDatastore

  • ExtendedAgent

See the list of available Agent Spec extension/plugin components in the API Reference

Next steps#

Having learned how to connect WayFlow assistants to data sources, you may now proceed to:

Full code#

Click on the card at the top of this page to download the full code for this guide or copy the code below.

  1# Copyright © 2025 Oracle and/or its affiliates.
  2#
  3# This software is under the Universal Permissive License
  4# %%[markdown]
  5# WayFlow Code Example - How to Connect to Your Data
  6# --------------------------------------------------
  7
  8# How to use:
  9# Create a new Python virtual environment and install the latest WayFlow version.
 10# ```bash
 11# python -m venv venv-wayflowcore
 12# source venv-wayflowcore/bin/activate
 13# pip install --upgrade pip
 14# pip install "wayflowcore==26.1" 
 15# ```
 16
 17# You can now run the script
 18# 1. As a Python file:
 19# ```bash
 20# python howto_datastores.py
 21# ```
 22# 2. As a Notebook (in VSCode):
 23# When viewing the file,
 24#  - press the keys Ctrl + Enter to run the selected cell
 25#  - or Shift + Enter to run the selected cell and move to the cell below# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl) or Apache License
 26# 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0), at your option.
 27
 28
 29
 30# %%[markdown]
 31## Imports
 32
 33# %%
 34from textwrap import dedent
 35
 36from wayflowcore.controlconnection import ControlFlowEdge
 37from wayflowcore.dataconnection import DataFlowEdge
 38from wayflowcore.datastore import Entity, InMemoryDatastore
 39from wayflowcore.datastore.entity import nullable
 40from wayflowcore.flow import Flow
 41from wayflowcore.property import (
 42    AnyProperty,
 43    DictProperty,
 44    FloatProperty,
 45    IntegerProperty,
 46    ObjectProperty,
 47    StringProperty,
 48)
 49from wayflowcore.steps import InputMessageStep, OutputMessageStep, PromptExecutionStep
 50from wayflowcore.steps.datastoresteps import (
 51    DatastoreCreateStep,
 52    DatastoreDeleteStep,
 53    DatastoreListStep,
 54    DatastoreUpdateStep,
 55)
 56
 57# %%[markdown]
 58## Define the llm
 59
 60# %%
 61from wayflowcore.models import VllmModel
 62
 63llm = VllmModel(
 64    model_id="LLAMA_MODEL_ID",
 65    host_port="LLAMA_API_URL",
 66)
 67
 68# %%[markdown]
 69## Create entities
 70
 71# %%
 72product = Entity(
 73    properties={
 74        "ID": IntegerProperty(description="Unique product identifier"),
 75        # Descriptions can be helpful if an LLM needs to fill these fields,
 76        # or generally disambiguate non-obvious property names
 77        "title": StringProperty(description="Brief summary of the product"),
 78        "description": StringProperty(),
 79        "price": FloatProperty(default_value=0.1),
 80        # Use nullable to define optional properties
 81        "category": nullable(StringProperty()),
 82    },
 83)
 84
 85# %%[markdown]
 86## Create datastore
 87
 88# %%
 89datastore = InMemoryDatastore(schema={"products": product})
 90
 91dummy_data = [
 92    {
 93        "ID": 0,
 94        "title": "Broccoli",
 95        "description": "Healty and delicious cruciferous vegetable!",
 96        "price": 1.5,
 97        "category": "Produce",
 98    },
 99    {
100        "ID": 1,
101        "title": "Oranges",
102        "description": "Vitamin C-filled citrus fruits",
103        "price": 1.8,
104        "category": "Produce",
105    },
106    {
107        "ID": 2,
108        "title": "Shampoo",
109        "description": "Shiny smooth hair in just 10 applications!",
110        "price": 4.5,
111        "category": "Personal hygene",
112    },
113    {
114        "ID": 3,
115        "title": "Crushed ice",
116        "description": "Cool any drink in seconds!",
117        "price": 4.5,
118    },
119]
120
121# Create supports both bulk-creation, as well as single element creation
122datastore.create(collection_name="products", entities=dummy_data[:-1])
123datastore.create(collection_name="products", entities=dummy_data[-1])
124
125# %%[markdown]
126## Create Datastore step
127
128# %%
129datastore_list_step = DatastoreListStep(
130    datastore,
131    name="product_list_step",
132    collection_name="products",
133    where={"title": "{{user_requested_product}}"},
134    limit=1,
135    unpack_single_entity_from_list=True,
136)
137
138datastore_update_step = DatastoreUpdateStep(
139    datastore,
140    name="product_update_step",
141    collection_name="products",
142    where={"title": "{{user_requested_product}}"}
143)
144
145# %%[markdown]
146## Create flow
147
148# %%
149# We create the steps needed by our flow
150USER_INPUT_STEP = "user_product_input_step"
151USER_TASK_INPUT_STEP = "user_task_input_step"
152LLM_REWRITE_STEP = "llm_rewrite_step"
153USER_OUTPUT_STEP = "user_output_step"
154
155user_input_message_template = dedent(
156    """I am an inventory Assistant, designed to help you keep product descriptions up-to-date.
157    What product would you like to update? Please provide its title.
158    """
159)
160
161user_task_message_template = "How would you like to update the description? I will help you rewrite it according to your instructions"
162
163rewrite_description_prompt_template = dedent(
164    """You are an inventory assistant.
165
166    Your task:
167        - Based on the product details given below, rewrite the description according to the user's request
168    Important:
169        - Be helpful and concise in your messages
170        - Only provide the new description as an output, and nothing else
171
172    Here is the User's request:
173    - {{ user_request }}
174
175    Here is the product description:
176    - {{ product }}
177    """
178)
179
180user_input_step = InputMessageStep(
181    name=USER_INPUT_STEP,
182    message_template=user_input_message_template,
183)
184
185user_task_input_step = InputMessageStep(
186    name=USER_TASK_INPUT_STEP,
187    message_template=user_task_message_template,
188)
189
190llm_rewrite_step = PromptExecutionStep(
191    name=LLM_REWRITE_STEP,
192    prompt_template=rewrite_description_prompt_template,
193    llm=llm,
194    input_descriptors=[
195        DictProperty("product", key_type=StringProperty(), value_type=AnyProperty())
196    ],
197    output_descriptors=[
198        ObjectProperty(
199            name=PromptExecutionStep.OUTPUT, properties={"description": StringProperty()}
200        )
201    ],
202)
203
204user_output_step = OutputMessageStep(
205    name=USER_OUTPUT_STEP,
206    message_template="The product has been updated with the following description: {{ answer['description'] }}",
207)
208
209assistant = Flow(
210    begin_step=user_input_step,
211    control_flow_edges=[
212        ControlFlowEdge(source_step=user_input_step, destination_step=user_task_input_step),
213        ControlFlowEdge(source_step=user_task_input_step, destination_step=datastore_list_step),
214        ControlFlowEdge(source_step=datastore_list_step, destination_step=llm_rewrite_step),
215        ControlFlowEdge(source_step=llm_rewrite_step, destination_step=datastore_update_step),
216        ControlFlowEdge(source_step=datastore_update_step, destination_step=user_output_step),
217        ControlFlowEdge(source_step=user_output_step, destination_step=None),
218    ],
219    data_flow_edges=[
220        # The first title given by the user is mapped to the datastore steps for listing and updating
221        DataFlowEdge(
222            user_input_step,
223            InputMessageStep.USER_PROVIDED_INPUT,
224            datastore_list_step,
225            "user_requested_product",
226        ),
227        DataFlowEdge(
228            user_input_step,
229            InputMessageStep.USER_PROVIDED_INPUT,
230            datastore_update_step,
231            "user_requested_product",
232        ),
233        # The task and product detail are given to the LLM in the prompt execution step
234        DataFlowEdge(
235            user_task_input_step,
236            InputMessageStep.USER_PROVIDED_INPUT,
237            llm_rewrite_step,
238            "user_request",
239        ),
240        DataFlowEdge(datastore_list_step, DatastoreListStep.ENTITIES, llm_rewrite_step, "product"),
241        # The generated update is applied on the datastore, and echoed back to the user
242        DataFlowEdge(
243            llm_rewrite_step,
244            PromptExecutionStep.OUTPUT,
245            datastore_update_step,
246            DatastoreUpdateStep.UPDATE,
247        ),
248        DataFlowEdge(llm_rewrite_step, PromptExecutionStep.OUTPUT, user_output_step, "answer"),
249    ],
250)
251
252# %%[markdown]
253## Execute flow
254
255# %%
256conversation = assistant.start_conversation()
257conversation.execute()
258conversation.append_user_message("Broccoli")
259
260conversation.execute()
261conversation.append_user_message(
262    "Shoppers don't know what 'cruciferous' means, we should find a catchier description."
263)
264
265conversation.execute()
266print(conversation.get_last_message().content)
267
268# %%[markdown]
269## Import agents
270
271# %%
272from wayflowcore.agent import Agent
273
274# %%[markdown]
275## Create agent flows
276
277# %%
278AGENT_PROMPT = dedent(
279    """
280    You are an inventory assistant. Your task is to help the user with their requests by using the available tools.
281    If you are unsure about the action to take, or you don't have the right tool, simply tell the user so and follow their guidance.
282    """
283)
284
285create_product_flow = Flow.from_steps(
286    [DatastoreCreateStep(datastore, "products")],
287    name="Create product",
288    description="Creates a new product in the data source",
289)
290list_products_flow = Flow.from_steps(
291    [DatastoreListStep(datastore, "products")],
292    name="List all products",
293    description="Lists all products in the data source.",
294)
295list_one_product_flow = Flow.from_steps(
296    [datastore_list_step],
297    name="List single product",
298    description="Lists a single product in the data source by its title.",
299)
300update_product_flow = Flow.from_steps(
301    [datastore_update_step],
302    name="Update product",
303    description="Updates a product in the data source by its title.",
304)
305delete_product_flow = Flow.from_steps(
306    [DatastoreDeleteStep(datastore, "products", where={"title": "{{product_title}}"})],
307    name="Delete product",
308    description="Delete a product in the data source by its title.",
309)
310
311# %%[markdown]
312## Create agent
313
314# %%
315agent = Agent(
316    llm=llm,
317    flows=[
318        create_product_flow,
319        list_products_flow,
320        list_one_product_flow,
321        update_product_flow,
322        delete_product_flow,
323    ],
324    custom_instruction=AGENT_PROMPT,
325)
326
327# %%[markdown]
328## Export config to Agent Spec
329
330# %%
331from wayflowcore.agentspec import AgentSpecExporter
332
333serialized_agent = AgentSpecExporter().to_json(agent)
334
335# %%[markdown]
336## Load Agent Spec config
337
338# %%
339from wayflowcore.agentspec import AgentSpecLoader
340
341agent = AgentSpecLoader().load_json(serialized_agent)