Duration 1.5 hours
Day 4 of 5

Learning Objectives

By the end of this module, students will be able to:

  • Store and retrieve per-call state
  • Use global data for shared information
  • Implement session persistence patterns
  • Pass state between function calls

Topics

1. State in Voice AI (15 min)

The Challenge

Each function call is stateless - how do you remember:

  • Customer’s name from earlier
  • Items they mentioned
  • Progress through a workflow

State Types

Type Scope Persistence
Global Data All calls Agent lifetime
Call Metadata Single call Call duration
Function State Single function Function execution

2. Global Data (25 min)

Setting Global Data

agent.set_global_data({
    "company": "TechCorp",
    "hours": "9 AM to 5 PM",
    "support_email": "help@techcorp.com"
})

Accessing in Functions

@agent.tool(description="Get company info")
def get_company_info(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    data = agent.get_global_data()
    return SwaigFunctionResult(
        f"We are {data['company']}, open {data['hours']}."
    )

Dynamic Global Data

class MyAgent(AgentBase):
    def __init__(self):
        super().__init__(name="my-agent")

        # Update based on time
        from datetime import datetime
        hour = datetime.now().hour

        self.set_global_data({
            "greeting": "Good morning" if hour < 12 else "Good afternoon",
            "is_business_hours": 9 <= hour < 17
        })

3. Call Metadata (25 min)

Setting Metadata

Use the set_meta_data action:

@agent.tool(description="Store customer name")
def store_name(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    name = args.get("name", "")
    return (
        SwaigFunctionResult(f"Nice to meet you, {name}!")
        .update_global_data( {"customer_name": name})
    )

Accessing Metadata

Metadata is passed in raw_data to subsequent functions:

@agent.tool(description="Personalized greeting")
def greet_customer(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    raw_data = raw_data or {}
    global_data = raw_data.get("global_data", {})
    name = global_data.get("customer_name", "there")
    return SwaigFunctionResult(f"Hello again, {name}!")

Building Up State

@agent.tool(description="Add item to cart")
def add_to_cart(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    item = args.get("item", "")
    raw_data = raw_data or {}
    global_data = raw_data.get("global_data", {})
    cart = global_data.get("cart", [])
    cart.append(item)

    return (
        SwaigFunctionResult(f"Added {item}. You have {len(cart)} items.")
        .update_global_data( {"cart": cart})
    )

4. State Patterns (20 min)

Pattern: Customer Context

class ContextAwareAgent(AgentBase):
    def __init__(self):
        super().__init__(name="context-agent")
        self._setup_functions()

    def _setup_functions(self):
        @self.tool(description="Identify the customer")
        def identify_customer(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
            phone = args.get("phone", "")
            # Look up customer
            customer = lookup_customer_by_phone(phone)

            if customer:
                return (
                    SwaigFunctionResult(
                        f"Hello {customer['name']}! How can I help?"
                    )
                    .update_global_data( {
                        "customer_id": customer["id"],
                        "customer_name": customer["name"],
                        "customer_tier": customer["tier"]
                    })
                )
            return SwaigFunctionResult("I don't recognize this number.")

        @self.tool(description="Get customer's orders")
        def get_orders(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
            raw_data = raw_data or {}
            global_data = raw_data.get("global_data", {})
            customer_id = global_data.get("customer_id")

            if not customer_id:
                return SwaigFunctionResult(
                    "I need to identify you first. What's your phone number?"
                )

            orders = fetch_orders(customer_id)
            return SwaigFunctionResult(f"You have {len(orders)} orders.")

Pattern: Workflow Progress

@self.tool(description="Move to next step")
def next_step(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    raw_data = raw_data or {}
    global_data = raw_data.get("global_data", {})
    current_step = global_data.get("workflow_step", 0)
    next_step_num = current_step + 1

    steps = ["name", "email", "phone", "confirm"]

    if next_step_num >= len(steps):
        return SwaigFunctionResult("All information collected!")

    return (
        SwaigFunctionResult(f"Now let's get your {steps[next_step_num]}.")
        .update_global_data( {"workflow_step": next_step_num})
    )

5. Best Practices (15 min)

Keep State Minimal

# Good - essential data only
.update_global_data( {
    "customer_id": "12345",
    "verified": True
})

# Bad - too much data
.update_global_data( {
    "entire_customer_record": {...huge object...}
})

Initialize State Early

@self.tool(description="Start conversation")
def start_call(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    return (
        SwaigFunctionResult("Welcome! How can I help?")
        .update_global_data( {
            "call_start": datetime.now().isoformat(),
            "items_discussed": [],
            "actions_taken": []
        })
    )

State Cleanup

@self.tool(description="End call")
def end_call(args: dict, raw_data: dict = None) -> SwaigFunctionResult:
    raw_data = raw_data or {}
    global_data = raw_data.get("global_data", {})

    # Log state for analytics
    log_call_summary({
        "duration": calculate_duration(global_data.get("call_start")),
        "items": global_data.get("items_discussed"),
        "actions": global_data.get("actions_taken")
    })

    return SwaigFunctionResult("Thank you for calling!")

Key Takeaways

  1. Global data for constants - Company info, settings
  2. Metadata for call state - Customer ID, progress
  3. set_meta_data action - Store state during call
  4. raw_data contains metadata - Access in functions
  5. Keep state minimal - Only essential data

Preparation for Lab 2.6

  • Think about what state your agent needs
  • Identify what should be global vs per-call
  • Plan state flow through conversation

Lab Preview

In Lab 2.6, you will:

  1. Implement customer identification with state
  2. Build a shopping cart with metadata
  3. Track workflow progress
  4. Test state across function calls

Back to top

SignalWire AI Agents Certification Program