Block Agent Context Variables

This guide describes how to implement variables within your Block Agent. Learn more about:


 

Overview

Context variables enable agents to remember what happened, adapt to what they know, and make intelligent decisions based on accumulated information.

These variables are key-value pairs that represent the current state and accumulated knowledge of a conversation session. They form the backbone of intelligent conversation flow, serving as accessible information sources that enable:

  • Adaptive Content: Dynamic prompts that change based on conversation state

  • Intelligent Routing: Data-driven Edge Condition evaluation

  • Session Memory: Persistent information across conversation Turns

  • Personalization: User-specific and interaction-specific customization

 

Think of context variables as the agent's working memory—constantly updated, always available, and essential for coherent multi-turn conversations.

 

Types of Context Variables

Context variables are broadly categorized into two types based on their life-cycle:

  • Transient Variables

    • Transient variables are short-lived—they exist only for the duration of a single Turn. When the next Turn begins, they're gone unless explicitly passed again.

    • Sources of Transient Variables:

      • Flow arguments (flow_args) passed in the API request.

      • Top-level fields in the user_message for wait Turns

  • Persistent Variables

    • Persistent variables accumulate over the course of the conversation and remain available across all Turns in the session.

    • Sources of Persistent Variables:

      • Tool call outputs (all parameters captured by tools)

      • Policy object passed in API requests

      • The special data.* field in wait Turn responses

 

Flow Arguments

Flow arguments ("flow_args") are variables that are passed to the agent at run-time.

These variables provide contextual information for the current interaction only. As transient variables, the data changes every Turn.

 

Popular arguments include:

  • arg_context_date_time to specify the current date and time for scheduling appoints (i.e., "Friday 2025-11-07 10:00 AM").

  • arg_context to include knowledge base content with responding to callers (i.e., "Office Hours: Mon-Fri 8am-5pm...").

  • arg_ani_match to include caller lookup results from a CRM (i.e., { "patient_id": "P123", ... }).

 

Passing Flow Arguments via API Request

{
	"user_message": "hi",
	"flow_args": {
		"arg_brand_name": "Dr. Teeth Main Street",
		"arg_office_address": "2001 W Alameda, Denver, CO, 80223",
		"arg_context_date": "2025-01-15",
		"arg_context_time": "10:30 AM",
		"arg_current_day_of_week": "Wednesday",
		"arg_ANI": "123-456-7891",
		"arg_ani_match": {
			"patient_id": "P12345",
			"first_name": "John",
			"date_of_birth": "1985-06-15"
		},
		"arg_existing_appointments": {
			"matches": [
				{"date": "2025-01-20T14:00:00", "type": "cleaning"}
			]
		}
	}
}

 

Flow Arguments in Turns

Once passed, flow arguments become available as Jinja2 variables in your Turn content:

- turn_id: '000005'
	name: greeting_intent
	content: |-
		{% if arg_ani_match is defined and arg_ani_match %}
			{% if arg_existing_appointments.matches|length > 1 %}
			Rephrase: This is Sofia! Welcome back! I see you have 
			{{arg_existing_appointments.matches|length}} upcoming appointments. 
			Are you calling about one of those today?
			{% else %}
			Rephrase: This is Sofia! Welcome back! I see you have an upcoming appointment. Are you calling about that today?
			{% endif %}
		{% else %}        
		Rephrase: This is Sofia! Most of our patients use this line to quickly confirm or change appointments. What do you need today?
		{% endif %}

 

In this example:

  • arg_ani_match is checked to see if the caller was recognized

  • arg_existing_appointments.matches provides the list of upcoming appointments

  • The greeting adapts based on what we know about this caller

 

Flow Arguments Across Turns

The sample below demonstrates the transient nature of Flow Arguments.

Turn 1 Request:
{
	"flow_args": {
		"arg_context_date": "2025-01-15",
		"arg_special_offer": "New Year 20% discount"
	}
}
→ Turn 1 can access: arg_context_date, arg_special_offer ✓

Turn 2 Request:
{
	"flow_args": {
		"arg_context_date": "2025-01-15"
		// arg_special_offer NOT passed
	}
}
→ Turn 2 can access: arg_context_date ✓
→ Turn 2 CANNOT access: arg_special_offer ✗ (gone!)

 

If you need data to persist, you can either:

  • Pass it again in every request

  • Store it in your Policy instead

  • Capture it via Tool calls

 

For more information about using Block Agent flow arguments with API interactions check out this page.

For more information about using flow arguments on the AI Studio Block Agent Canvas, check out this page.

 

Wait Turn Responses

When a wait Turn receives a response from an external system, the user_message carries structured data that becomes available as context variables.

Sample Basic Wait Turn Response

{
	"user_message": {
		"success": true,
		"date_of_birth_lookup": "schedule_appointment",
		"patient_id": "P12345"
	}
}

 

In this example:

  • success, date_of_birth_lookup, and patient_id all become transient context variables

  • They're available for Edge evaluation and the immediate next Turn

  • They won't persist to subsequent Turns

 

The data.* Field

The data field is special—anything nested within it becomes persistent and accessible across all subsequent Turns.

The data.* pattern solves a common challenge: after a wait Turn returns availability data, subsequent Turns (like appointment selection, confirmation, and booking) all need access to that same data. Without persistence, you'd have to pass the entire appointment list in every flow_args, which would be cumbersome and error-prone.

 

Sample Data Configuration

{
	"user_message": {
		"success": true,
		"availability_lookup": true,
		"data": {
			"available_appointments": {
				"matches": [
					{
						"appointment_date": "2025-01-15T09:00:00",
						"duration": 60,
						"provider_id": "P001"
					},
					{
						"appointment_date": "2025-01-16T14:00:00",
						"duration": 60,
						"provider_id": "P002"
					}
				]
			},
			"availability_for_date": "2025-01-15",
			"patient_name": "John Smith"
		}
	}
}

 

Some of the fields are transient (current Turn only) and some of the fields are persistent (all subsequent Turns):

Transient Data Persistent Data
success data.available_appointments
availability_lookup data.availability_for_date
  data.patient_name

 

Accessing Persistent Data in Turns

In subsequent Turns, access persistent data using the data. prefix.

Sample Configuration

 

Tool Outputs

Every time a Tool is called, its captured parameters become persistent context variables. This is one of the most powerful aspects of Block Agents, data collection naturally builds up your conversation context.

Sample Tool Definition

- type: function
  function:
	name: collect_date_of_birth
	description: 'Collect the full date of birth including year, month, and day.'
	parameters:
		type: object
		properties:
			birth_year:
				type: string
				description: The year of birth in numeric YYYY format.
			birth_month:
				type: string
				description: The month of birth in numeric MM format.
			birth_day:
				type: string
				description: The day of birth in numeric DD format.
			date_of_birth:
				type: string
				description: Complete date of birth in ISO format YYYY-MM-DD.
		required:
			- date_of_birth

 

Tool Execution Result

When the LLM calls this Tool with the user's response "June fifteenth nineteen eighty five", the following values are captured:

{
	"collect_date_of_birth": true,
	"birth_year": "1985",
	"birth_month": "06",
	"birth_day": "15",
	"date_of_birth": "1985-06-15"
}

 

All of these become persistent context variables:

  • birth_year → "1985"

  • birth_month → "06"

  • birth_day → "15"

  • date_of_birth → "1985-06-15"

 

Accessing Tool Outputs in Later Turns

In any subsequent Turn, you can reference these captured values:

- turn_id: '900012'
  name: confirmation
  content: |-
	{% if date_of_birth is defined %}
	I have verified your date of birth as {{ date_of_birth }}.
	{% endif %}
    
	{% if appointment_name is defined and appointment_date_time is defined %}
	Your {{ appointment_name }} appointment is scheduled for 
	{{ appointment_date_time }}.
	{% endif %}

 

Accumulation Over Time

Tool outputs accumulate throughout the conversation:

After Turn 1 (Greeting):
	- schedule_appointment: true
	- intent_utterance: "I need to schedule a cleaning"
	- language_name: "English"

After Turn 2 (New/Existing):
	- schedule_appointment: true
	- intent_utterance: "I need to schedule a cleaning"
	- language_name: "English"
	- patient_status: "returning_patient"  ← NEW

After Turn 3 (DOB Collection):
	- schedule_appointment: true
	- intent_utterance: "I need to schedule a cleaning"
	- language_name: "English"
	- patient_status: "returning_patient"
	- birth_year: "1985"  ← NEW
	- birth_month: "06"  ← NEW
	- birth_day: "15"  ← NEW
	- date_of_birth: "1985-06-15"  ← NEW

After Turn 4 (Appointment Type):
	- (all previous values)
	- appointment_name: "cleaning"  ← NEW
	- appointment_type_id: "APT001"  ← NEW

 

This accumulation enables rich contextual awareness as the conversation progresses.

 

Policies

Policy values are persistent by design. They are passed once (typically at the start of the conversation) and available throughout the entire session.

Sample Policy

{
	"user_message": "hello",
	"policy": {
		"office_name": "Dr.Teeth Main Street",
		"office_id": "LOC001",
		"transfer_number": "+11234567892",
		"enable_spanish": true,
		"business_hours": {
			"monday": "8:00 AM - 7:00 PM",
			"tuesday": "8:00 AM - 7:00 PM",
			"wednesday": "8:00 AM - 7:00 PM",
			"thursday": "8:00 AM - 7:00 PM",
			"friday": "8:00 AM - 5:00 PM"
		},
		"allowed_appointment_types": ["cleaning", "consultation", "emergency"]
	}
}

 

Accessing Your Policy in Turns

Policy values are accessed via the policy. prefix:

content: |-
	{% if policy.enable_spanish %}
	(Spanish support is available for this call)
	{% endif %}
  
	Welcome to {{ policy.office_name }}!
  
	Our business hours are:
	{% for day, hours in policy.business_hours.items() %}
	- {{ day|title }}: {{ hours }}
	{% endfor %}
  
	{% if policy.allowed_appointment_types %}
	I can help you schedule: {{ policy.allowed_appointment_types|join(', ') }}.
	{% endif %}

 

Policies vs. Flow Arguments

The table below guides when to use which context variable type, depending on your scenario:

Scenario Use Policy Use Flow Arguments
Office configuration X  
Transfer phone numbers X  
Feature flags X  
Current date/time   X
Caller lookup results   X
Real-time availability   X
Dynamic pricing   X

 

Jinja Templates

Jinja is a powerful templating language that allows you to create dynamic content by embedding logic and variables directly into your Block Agent. Instead of writing static, one-size-fits-all configurations, you can create intelligent, context-aware instructions that adapt based on run-time data.

The true power of context variables emerges when combined with Jinja2 templating. This combination enables sophisticated, dynamic content generation that adapts to the full conversation state.

 

Conditional Content Based on Context

Adapt behavior based on what you know about the caller:

content: |-
	{% if arg_ani_match is defined and arg_ani_match %}
		{# Recognized caller - personalized greeting #}
		{% if arg_existing_appointments is defined and 
				arg_existing_appointments.matches|length > 0 %}
			Welcome back, {{ arg_ani_match.first_name }}! I see you have 
			{{ arg_existing_appointments.matches|length }} upcoming appointment{% if arg_existing_appointments.matches|length > 1 %}s{% endif %}. 
			Are you calling about one of those?
		{% else %}
			Welcome back, {{ arg_ani_match.first_name }}! How can I help you today?
		{% endif %}
		{% else %}
			{# Unknown caller - generic greeting #}
			This is Sofia! How can I help you today?
		{% endif %}

 

Iterating Over Collected Data

Present options based on available data:

 

Building on Previous Tool Outputs

Reference accumulated data from earlier Turns:

content: |-
	{# These are all persistent from previous tool calls #}
	{% if schedule_appointment is defined and schedule_appointment %}
		{% if date_of_birth is defined %}
		I've verified your identity using your date of birth.
		{% endif %}
    
		{% if appointment_name is defined %}
		You're scheduling a {{ appointment_name }} appointment.
		{% endif %}
    
		{% if appointment_date_time is defined %}
		The selected time is {{ appointment_date_time }}.
		{% endif %}
    
		Let me confirm the booking...
	{% endif %}

 

Validation and Error Handling

Check for required data before proceeding:

content: |-
	{# Validate we have everything needed #}
	{% set missing_fields = [] %}
  
	{% if not first_name or not first_name %}
		{% set _ = missing_fields.append("first name") %}
	{% endif %}
  
	{% if not last_name or not last_name %}
		{% set _ = missing_fields.append("last name") %}
	{% endif %}
  
	{% if not date_of_birth or not date_of_birth %}
		{% set _ = missing_fields.append("date of birth") %}
	{% endif %}
  
	{% if missing_fields|length > 0 %}
	I still need to collect: {{ missing_fields|join(', ') }}.
	{% else %}
	Great! I have all the information I need. Let me proceed.
	{% endif %}

 

Conditional Edge Logic with Context

Use context variables in transition_prompt for dynamic Edge behavior:

- edge_id: '1'
  name: schedule_appointment_start
  type: edge
  condition:
	source: tools
	expected_value:
		schedule_appointment: true
		arg_is_existing_patient: true
  transition_prompt: |-
	{%- if arg_is_existing_patient is defined and arg_is_existing_patient -%}
	Use this when the user wants to schedule an appointment.
	{%- else -%}
	NO TRANSITION
	{%- endif -%}
  connect_to:
	turn_id: '100005'

 

Complex Data Calculations

Leverage context for date-based logic:

content: |-
	{# Parse the context date #}
	{%- set date_parts = arg_context_date.split('-') -%}
	{%- set year = date_parts[0]|int -%}
	{%- set month = date_parts[1]|int -%}
	{%- set day = date_parts[2]|int -%}
  
	{# Calculate next business day #}
	{%- set weekday = (day + ((13 * (month + 1)) // 5) + year + (year // 4) + 5) % 7 -%}
  
	{% if weekday == 5 %}
		{# Saturday - next available is Monday #}
		The soonest I can book is Monday.
	{% elif weekday == 6 %}
		{# Sunday - next available is Monday #}
		The soonest I can book is Monday.
	{% else %}
		I can check availability starting tomorrow.
	{% endif %}
  
	Today's date is {{ arg_context_date }}.

 

Frequently Used Jinja Filters for Context Variables

When working with context variables, these Jinja filters (using pipe operators | ) are particularly useful:

Filter

Purpose

Example

default(value)

Provide fallback

{{ name\\|default('Guest') }}

length

Count items

{{ appointments\\|length }}

join(sep)

Combine list items

{{ types\\|join(', ') }}

first or last

Get first/last item

{{ dates\\|first }}

upper or lower

Case conversion

{{ status\\|upper }}

title

Title case

{{ name\\|title }}

round(n)

Round numbers

{{ price\\|round(2) }}

int or string

Type conversion

{{ count\\|int }}

 

Sample with Filters

content: |-
	{# Safely access with defaults #}
	Hello {{ first_name|default('valued patient')|title }}!
  
	{% if data.available_appointments.matches is defined %}
		{% set appts = data.available_appointments.matches %}
		I found {{ appts|length }} appointment{{ 's' if appts|length > 1 else '' }}.
    
		The earliest is {{ appts|first|attr('appointment_date') }}.
		The latest is {{ appts|last|attr('appointment_date') }}.
	{% endif %}

 

For more information about Jinja variables, check out the basics here and more in depth information here.

 

Tips

  • Always check for undefined variables before accessing.

    {# Good - defensive checking #}
    {% if arg_ani_match is defined and arg_ani_match.first_name is defined %}
    	Hello {{ arg_ani_match.first_name }}!
    {% endif %}
    
    {# Or use defaults #}
    Hello {{ arg_ani_match.first_name|default('there') }}!

  • Use clear meaningful variable names.

    {# Good - clear naming #}
    {% if patient_has_verified_identity %}
    {% if appointment_requires_deposit %}
    
    {# Avoid - unclear #}
    {% if flag1 %}
    {% if status == 1 %}

  • Separate logic from presentation.

    {# Good - calculate first, then present #}
    {% set has_appointments = data.appointments is defined and data.appointments|length > 0 %}
    {% set appointment_count = data.appointments|length if has_appointments else 0 %}
    
    {% if has_appointments %}
    	You have {{ appointment_count }} upcoming appointments.
    {% else %}
    	You don't have any upcoming appointments.
    {% endif %}

  • Handle missing data gracefully.

    content: |-
    	{% if data is not defined or data.available_appointments is not defined %}
    		I'm still loading the available appointments...
    	{% elif data.available_appointments.matches|length == 0 %}
    		I couldn't find any available appointments for that date.
    	{% else %}
    		Here are your options:
    		{# ... present appointments ... #}
    	{% endif %}

  • Use Jinja filters for safe, clean data transformations.

  • Remember when to use each context variable type:

    • Use flow_args for data that changes every Turn

    • Use policy for configuration that persists

    • Use data.* in wait responses for persistent external data

    • Tool outputs naturally accumulate. Design Tools to capture what you'll need later.