# Data handling for agent workflows
Supported in ADKPython v2.0.0Beta
Structuring and managing data between agents and graph-based notes is critical for building reliable processes with ADK. This guide explains data handling within graph-based workflows and collaboration agents, including how information is transmitted and received between graph nodes using ***Events***. It covers the essential parameters for events, data, content, and state, and explains how to implement structured data transfer for both function and agent nodes using data format schemas and specific instruction syntax. !!! example "Beta Release" ADK 2.0 is a Beta release and may cause breaking changes when used with prior versions of ADK. Do not use ADK 2.0 if you require backwards compatibility, such as in production environments. We encourage you to test this release and we welcome your [feedback](https://github.com/google/adk-python/issues/new?template=feature_request.md&labels=v2)! ## Workflow graph Events Within a graph-based workflow, you pass data using ***Events***. All execution *nodes* in a workflow graph consume and emit Events. This section covers the basics of transmitting and receiving data between nodes in a ***Workflow***. Events have specific parameters for transmitting different types of data between nodes. The key parameters for node data handling are as follows: - **`output`**: Parameter for passing information between *nodes*. - **`message`**: Data intended as a response to a user. - **`state`**: Data automatically persisted across nodes via ***Events*** throughout an ADK session. Events also carry additional information about the workflow, including the source node of the Event. ### Node input and output with Events Each node in a graph receives and transmits data through the ***Event*** class. Use the ***yield*** syntax to hand off data to the next node, as shown in the following code snippet: ```python from google.adk import Event def my_function_node(node_input: str): output_value = node_input.upper() return Event(output=output_value) # "THE RESULT" ``` Use the ***return*** syntax when outputting ***Event*** data that does not require additional processing. When emitting data that requires additional processing, or if you are generating more than one data item, you can use more than one ***yield*** command. Each ***yield*** call adds to a list of data objects on the Event which is passed to the next node of a graph. A ***return*** or ***yield*** command without a parameter passes a `None` value to the next node. ### Event `output` parameter The ***output*** parameter of an ***Event*** is the standard way to pass data to the next node of a graph. The next node receives a ***node input*** object containing the data, as shown in the following code sample: ```python def my_function_node_1(): return Event(output="The Result") def my_function_node_2(node_input: str): output_value = node_input.lower() return Event(output=output_value) # "the result" ``` You can pass longer, structured data in a serializable format, as shown in this code sample: ```python def my_function_node_3(): yield Event( output={ "city_name": "Paris", "city_time": "10:10 AM", }, ) ``` !!! warning "Caution: Event.output limitation" Nodes are only allowed to emit a single ***Event.output*** data payload per execution. This limitation means that while you can more than one ***yield*** in a node, having two or more ***yield*** commands with an ***Event.output*** results in a runtime error. ### Event `message` parameter The ***message*** parameter of an ***Event*** is used to pass data intended as a user response. In general, you should not use the ***message*** parameter in your agent code unless it is specifically to provide information to a user or request information from a user. The following code example show how to provide information to a user during workflow execution: ```python async def user_message(node_input: str): """Tell user research process is starting.""" yield Event(message="Beginning research process...") ``` ### Event `state` parameter The ***state*** parameter of an ***Event*** is used to maintain a small set of data values during an entire ADK session. Values in the state parameter automatically persist between Nodes and are meant for guiding the execution of more complex workflows. Nodes can modify state values, and the modified state values are available to downstream Nodes.The following code example shows how state is persisted across nodes: ```python async def init_state_node(attempts: int = 0): yield Event( state={ "attempts": attempts, }, ) async def task_attempt_node(node_input: Content, attempts: int): yield Event( state={ "attempts": attempts + 1, }, ) async def read_state_node(ctx: Context): print(f"attempts state: {ctx.state}") # attempts state: attempts: 1 root_agent = Workflow( name="root_agent", edges=[("START", init_state_node, task_attempt_node, read_state_node)], ) ``` !!! warning "Caution: `state` property data limitations" The state parameter *should not be used to persist large amounts of data* between nodes. Use artifacts or other data persistence mechanisms, such as database Tools, to persist large data resources during the life cycle of a Workflow. ## Constrain node data input and output with schemas You can set input and output data schemas to constrain the input and output data formats of any node, including ***FunctionNodes*** and **Agents**. The following parameters are optional settings for any node. You can set both or either one of these parameters on any workflow node as required by your agent project. - **`input_schema`**: Set the expected input schema using a class that extends ***BaseModel***. - **`output_schema`**: Set the required output schema using a class that extends ***BaseModel***. The code example below shows how to set both input and output schemas for a subagent. ```python from google.adk import Agent from pydantic import BaseModel class FlightSearchInput(BaseModel): origin: str # Airport code "SFO" destination: str # Airport code "CDG" departure_date: date # date(2026, 3, 15) passengers: int = 1 # Number of passengers class FlightSearchOutput(BaseModel): flights: list[Flight] cheapest_price: float flight_searcher = Agent( name="flight_searcher", instruction="Search for available flights.", input_schema=FlightSearchInput, output_schema=FlightSearchOutput, tools=[search_flights_api], mode="single_turn", ... ) assistant = Agent( name="assistant", instruction="You help users plan trips.", sub_agents=[flight_searcher], ... ) ``` ## Access structured data in agents When you pass structured data into an agent from subagent or a workflow node, such as a Function Node, you can use specific syntax to add that data into the agent's instructions. Specifically, you can use the curly braces `{ }` to select the input schema properties, or `< >` to specify the input schema properties, the `from` keyword, and the name of the node providing the data. The following code snippet shows two ways to include data passed through an agent ***input schema***: ```python class CityTime(BaseModel): time_info: str # time information city: str # city name def lookup_time_function(city: str): """Simulate returning the current time in the specified city.""" return Event(output=CityTime(time_info='10:10 AM', city=city)) city_report_agent = Agent( name="city_report_agent", model="gemini-flash-latest", input_schema=CityTime, # data selection based on class and parameter # instruction=""" # Return a sentence in the following format: # It is {CityTime.time_info} in {CityTime.city} right now. # """, # more restrictive data selection based on source node name instruction=""" Return a sentence in the following format: It is in right now. """, ) root_agent = Workflow( name="root_agent", edges=[ (START, city_generator_agent, lookup_time_function, city_report_agent) ], ) ``` For a complete, but simplified version of this workflow, see [Graph-based agent workflows](/workflows/#get-started).