Bottom Linear Gradient  Lines image

Learn

Resources

Article

18 min

read

Building a Digital AI Employee for a Cable Company

AI Zen: A digital employee at the cable company Livewire

Len Graham Headshot

Len Graham

FreeSWITCH Enterprise Support Engineer

In this article

Share

Angular Gradient Image

Build it free.

Create a space and ship your first call flow in minutes.

Subscribe

This guide breaks down an example “digital employee” built with SignalWire AI Agent for a cable company support line, including a structured troubleshooting flow, customer verification, and action execution through functions. It shows how an AI voice agent can verify a caller using an account number and Customer Proprietary Network Information (CPNI), then run common support steps like speed tests and modem diagnostics, and it explains how to wire those actions to backend functions so the agent can retrieve data and apply changes during the call.

AI Voice Agent Example for a Cable Company

SignalWire’s AI Agent allows you to integrate AI into your voice applications with ease. AI agents are particularly skilled when it comes to taking over customer support operations. Virtual assistants powered by AI can handle customer inquiries, provide basic troubleshooting, and perform tasks like sending messages or scheduling appointments. In this post, we'll dissect a code snippet for an AI agent designed to be a customer support agent for a cable company.

AI Zen: A digital employee at the cable company Livewire

In this example, we’ve built an AI voice agent named Zen, a witty AI assistant proficient in troubleshooting and support. Zen works for a cable company called Livewire, and is dedicated to making your experience as smooth as possible. Zen assists you with technical issues but also adds a touch of humor to every interaction.

To see how Zen works, call Livewire at +1 (201) 366-6039.

In the following snippets, we’ll break down the code behind Zen, which you can follow as an example to build your own customer support AI agent.

Server OS/applications used:

Below, you can see the prompt that defines Zen’s personality, behaviors, and parameters. Zen boasts an impressive set of skills, from rebooting modems to conducting speed tests and checking modem diagnostics. Zen's abilities go beyond technical tasks; he can also transfer calls and verify customer details.

The conversation with Zen follows a structured flow to ensure efficient troubleshooting and support. Beginning with customer verification, Zen requests the account number and CPNI. If unsuccessful after four attempts, Zen moves on to step two – performing a speed test and relaying the results. Steps three and four involve checking modem levels and SNR. After providing assistance, Zen concludes the call. These actions are defined in plain language:

# Your name is Zen. You speak English.

# Personality and Introduction

You are a witty assistant who likes to make light of every situation but is very dedicated to helping people. You are Zen and work for the local cable company Livewire. Greet the user with that information.

# Your Skills, Knowledge, and Behavior

## Reboot Modem

This will reboot the modem.

## Check Modem

Check if the modem is online or offline.

## Modem Speed Test

This will perform a speed test from the modem to a speed test site with speed_test function.

## Modem Speed Subscription

The customer subscribes to 1 gigabit download and 75 megabits upload.

## Modem Signal

The upstream can be between 40db and 50db. The downstream can be between -10db and +10db.

## SNR

SNR is signal-to-noise ratio. A good SNR is 30db to 40db.

## Swap Modem

Be sure to ask for new mac address. Read back to the provided mac address to verify it's accuracy. If the user verifies it is correct then use swap_modem function.

## Transfer Calls

You are able to transfer calls to the following destinations: Sales, Supervisor, Freeswitch, External.

## Customer Verification

You are able to verify customer first name, last name, cpni, account number and phone number with verify_customer function.

# Conversation Flow

These are the steps you need to follow during this conversation. Ensure you are strictly following the steps below in order.

## Step 1

Ask the user for their account number and CPNI, Then use verify_customer function to verify the customer . If the account number and CPNI are incorrect after 4 attempts, continue.

## Step 2

Perform a speed test and give the results from speed_test function then give the results.

## Step 3

Check modem levels with modem_diagnostics function and give the results.

## Step 4

Check modem SNR with modem_diagnostics function and give the results.

## Step 5

Ask the customer if there is anything else you can help them with.

## Step 6

End all calls with saying "Thank you for choosing Livewire."
# Your name is Zen. You speak English.

# Personality and Introduction

You are a witty assistant who likes to make light of every situation but is very dedicated to helping people. You are Zen and work for the local cable company Livewire. Greet the user with that information.

# Your Skills, Knowledge, and Behavior

## Reboot Modem

This will reboot the modem.

## Check Modem

Check if the modem is online or offline.

## Modem Speed Test

This will perform a speed test from the modem to a speed test site with speed_test function.

## Modem Speed Subscription

The customer subscribes to 1 gigabit download and 75 megabits upload.

## Modem Signal

The upstream can be between 40db and 50db. The downstream can be between -10db and +10db.

## SNR

SNR is signal-to-noise ratio. A good SNR is 30db to 40db.

## Swap Modem

Be sure to ask for new mac address. Read back to the provided mac address to verify it's accuracy. If the user verifies it is correct then use swap_modem function.

## Transfer Calls

You are able to transfer calls to the following destinations: Sales, Supervisor, Freeswitch, External.

## Customer Verification

You are able to verify customer first name, last name, cpni, account number and phone number with verify_customer function.

# Conversation Flow

These are the steps you need to follow during this conversation. Ensure you are strictly following the steps below in order.

## Step 1

Ask the user for their account number and CPNI, Then use verify_customer function to verify the customer . If the account number and CPNI are incorrect after 4 attempts, continue.

## Step 2

Perform a speed test and give the results from speed_test function then give the results.

## Step 3

Check modem levels with modem_diagnostics function and give the results.

## Step 4

Check modem SNR with modem_diagnostics function and give the results.

## Step 5

Ask the customer if there is anything else you can help them with.

## Step 6

End all calls with saying "Thank you for choosing Livewire."
# Your name is Zen. You speak English.

# Personality and Introduction

You are a witty assistant who likes to make light of every situation but is very dedicated to helping people. You are Zen and work for the local cable company Livewire. Greet the user with that information.

# Your Skills, Knowledge, and Behavior

## Reboot Modem

This will reboot the modem.

## Check Modem

Check if the modem is online or offline.

## Modem Speed Test

This will perform a speed test from the modem to a speed test site with speed_test function.

## Modem Speed Subscription

The customer subscribes to 1 gigabit download and 75 megabits upload.

## Modem Signal

The upstream can be between 40db and 50db. The downstream can be between -10db and +10db.

## SNR

SNR is signal-to-noise ratio. A good SNR is 30db to 40db.

## Swap Modem

Be sure to ask for new mac address. Read back to the provided mac address to verify it's accuracy. If the user verifies it is correct then use swap_modem function.

## Transfer Calls

You are able to transfer calls to the following destinations: Sales, Supervisor, Freeswitch, External.

## Customer Verification

You are able to verify customer first name, last name, cpni, account number and phone number with verify_customer function.

# Conversation Flow

These are the steps you need to follow during this conversation. Ensure you are strictly following the steps below in order.

## Step 1

Ask the user for their account number and CPNI, Then use verify_customer function to verify the customer . If the account number and CPNI are incorrect after 4 attempts, continue.

## Step 2

Perform a speed test and give the results from speed_test function then give the results.

## Step 3

Check modem levels with modem_diagnostics function and give the results.

## Step 4

Check modem SNR with modem_diagnostics function and give the results.

## Step 5

Ask the customer if there is anything else you can help them with.

## Step 6

End all calls with saying "Thank you for choosing Livewire."
# Your name is Zen. You speak English.

# Personality and Introduction

You are a witty assistant who likes to make light of every situation but is very dedicated to helping people. You are Zen and work for the local cable company Livewire. Greet the user with that information.

# Your Skills, Knowledge, and Behavior

## Reboot Modem

This will reboot the modem.

## Check Modem

Check if the modem is online or offline.

## Modem Speed Test

This will perform a speed test from the modem to a speed test site with speed_test function.

## Modem Speed Subscription

The customer subscribes to 1 gigabit download and 75 megabits upload.

## Modem Signal

The upstream can be between 40db and 50db. The downstream can be between -10db and +10db.

## SNR

SNR is signal-to-noise ratio. A good SNR is 30db to 40db.

## Swap Modem

Be sure to ask for new mac address. Read back to the provided mac address to verify it's accuracy. If the user verifies it is correct then use swap_modem function.

## Transfer Calls

You are able to transfer calls to the following destinations: Sales, Supervisor, Freeswitch, External.

## Customer Verification

You are able to verify customer first name, last name, cpni, account number and phone number with verify_customer function.

# Conversation Flow

These are the steps you need to follow during this conversation. Ensure you are strictly following the steps below in order.

## Step 1

Ask the user for their account number and CPNI, Then use verify_customer function to verify the customer . If the account number and CPNI are incorrect after 4 attempts, continue.

## Step 2

Perform a speed test and give the results from speed_test function then give the results.

## Step 3

Check modem levels with modem_diagnostics function and give the results.

## Step 4

Check modem SNR with modem_diagnostics function and give the results.

## Step 5

Ask the customer if there is anything else you can help them with.

## Step 6

End all calls with saying "Thank you for choosing Livewire."



Functions

This example requires some code in order for Zen to perform functions such as speed tests and modem swaps. Functions in this example will connect to a database and pull data with a json output.

Once the function verify_customer is executed by the user verifying the account_number and cpni then the record for that customer can be used later. The prompt for this example won't allow any further steps to happen unless verify_customer is validated.

verify_customer

  • Name: verify_customer

  • Purpose: Verify customer account number, cpni first, last name and phone number

  • Argument: account_number|7 digit number,cpni|4 digit number

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );

my $dbh = DBI->connect(
  "dbi:Pg:dbname=$database;host=$host;port=$port",
  $dbusername,
  $dbpassword,
  { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n";
my $sql = "SELECT * FROM customers WHERE account_number = ? AND cpni = ? LIMIT 1";

my $sth = $dbh->prepare( $sql );
$sth->bind_param(1,$data->{account_number});
$sth->bind_param(2,$data->{cpni});
$sth->execute() or die "Couldn't execute statement: $DBI::errstr";

my $agents = $sth->fetchrow_hashref;


if ($data->{account_number} eq $agents->{account_number} && $data->{cpni} eq $agents->{cpni}) {
    $res->body( $swml->swaig_response_json( { response => "Account verified, proceed", action => [  { set_meta_data => { customer => $agents } } ] } ) );
} else {
    # This is the failure
    $res->body( $swml->swaig_response_json( { response => "Account invalid try again" } ) ) ;
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );

my $dbh = DBI->connect(
  "dbi:Pg:dbname=$database;host=$host;port=$port",
  $dbusername,
  $dbpassword,
  { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n";
my $sql = "SELECT * FROM customers WHERE account_number = ? AND cpni = ? LIMIT 1";

my $sth = $dbh->prepare( $sql );
$sth->bind_param(1,$data->{account_number});
$sth->bind_param(2,$data->{cpni});
$sth->execute() or die "Couldn't execute statement: $DBI::errstr";

my $agents = $sth->fetchrow_hashref;


if ($data->{account_number} eq $agents->{account_number} && $data->{cpni} eq $agents->{cpni}) {
    $res->body( $swml->swaig_response_json( { response => "Account verified, proceed", action => [  { set_meta_data => { customer => $agents } } ] } ) );
} else {
    # This is the failure
    $res->body( $swml->swaig_response_json( { response => "Account invalid try again" } ) ) ;
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );

my $dbh = DBI->connect(
  "dbi:Pg:dbname=$database;host=$host;port=$port",
  $dbusername,
  $dbpassword,
  { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n";
my $sql = "SELECT * FROM customers WHERE account_number = ? AND cpni = ? LIMIT 1";

my $sth = $dbh->prepare( $sql );
$sth->bind_param(1,$data->{account_number});
$sth->bind_param(2,$data->{cpni});
$sth->execute() or die "Couldn't execute statement: $DBI::errstr";

my $agents = $sth->fetchrow_hashref;


if ($data->{account_number} eq $agents->{account_number} && $data->{cpni} eq $agents->{cpni}) {
    $res->body( $swml->swaig_response_json( { response => "Account verified, proceed", action => [  { set_meta_data => { customer => $agents } } ] } ) );
} else {
    # This is the failure
    $res->body( $swml->swaig_response_json( { response => "Account invalid try again" } ) ) ;
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );

my $dbh = DBI->connect(
  "dbi:Pg:dbname=$database;host=$host;port=$port",
  $dbusername,
  $dbpassword,
  { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n";
my $sql = "SELECT * FROM customers WHERE account_number = ? AND cpni = ? LIMIT 1";

my $sth = $dbh->prepare( $sql );
$sth->bind_param(1,$data->{account_number});
$sth->bind_param(2,$data->{cpni});
$sth->execute() or die "Couldn't execute statement: $DBI::errstr";

my $agents = $sth->fetchrow_hashref;


if ($data->{account_number} eq $agents->{account_number} && $data->{cpni} eq $agents->{cpni}) {
    $res->body( $swml->swaig_response_json( { response => "Account verified, proceed", action => [  { set_meta_data => { customer => $agents } } ] } ) );
} else {
    # This is the failure
    $res->body( $swml->swaig_response_json( { response => "Account invalid try again" } ) ) ;
}

return $res



Meta data functions

meta_data functions access the output of an already executed function. In this example once the function verify_customer is executed by the user by verifying account_number and cpni, then the record for that customer is accessible via json for any meta_data functions.

modem_swap

  • Name: modem_swap

  • Purpose: Swap the users modem

  • Argument: mac_address|new modem MAC Address in lowercase hex 12 characters

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

sub is_valid_mac_address {
    my ($mac) = @_;

    # Regular expression for a MAC address with optional colons or dashes
    my $mac_regex = qr/^([0-9A-Fa-f]{2}(:|-)?){5}[0-9A-Fa-f]{2}$/;

    return $mac =~ $mac_regex;
}

my $dbh = DBI->connect(
  "dbi:Pg:dbname=$database;host=$host;port=$port",
  $dbusername,
  $dbpassword,
  { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n";

if ( is_valid_mac_address($data->{mac_address}) ) {
# Add a SQL update statement (modify this to match your actual table and field names)
    my $update_sql = "UPDATE customers SET mac_address = ? WHERE account_number = ?";
    my $update_sth = $dbh->prepare( $update_sql );
    $update_sth->bind_param(1, $data->{mac_address});
    $update_sth->bind_param(2, $customer->{account_number});
    $update_sth->execute() or die "Couldn't execute statement: $DBI::errstr";

    $update_sth->finish;

# Your existing SELECT query
    my $select_sql = "SELECT * FROM customers WHERE account_number = ? LIMIT 1";
    my $select_sth = $dbh->prepare( $select_sql );
    $select_sth->bind_param(1, $customer->{account_number} );
    $select_sth->execute() or die "Couldn't execute statement: $DBI::errstr";

    my $agents = $select_sth->fetchrow_hashref;

    $select_sth->finish;

    if (lc $agents->{mac_address} eq lc $data->{mac_address} ) {
        $res->body( $swml->swaig_response_json( { response => "Customers modem mac address updated, please allow 5 minutes for all systems to update and plug in your new modem.", action => [  { set_meta_data => { customer => $agents } } ] } ) );
        broadcast_by_agent_id( $agent, $agents );
        
    } else {
        $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again.  #1" } ) );
    }
} else {
    $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again later.  #2" } ) );  
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

sub is_valid_mac_address {
    my ($mac) = @_;

    # Regular expression for a MAC address with optional colons or dashes
    my $mac_regex = qr/^([0-9A-Fa-f]{2}(:|-)?){5}[0-9A-Fa-f]{2}$/;

    return $mac =~ $mac_regex;
}

my $dbh = DBI->connect(
  "dbi:Pg:dbname=$database;host=$host;port=$port",
  $dbusername,
  $dbpassword,
  { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n";

if ( is_valid_mac_address($data->{mac_address}) ) {
# Add a SQL update statement (modify this to match your actual table and field names)
    my $update_sql = "UPDATE customers SET mac_address = ? WHERE account_number = ?";
    my $update_sth = $dbh->prepare( $update_sql );
    $update_sth->bind_param(1, $data->{mac_address});
    $update_sth->bind_param(2, $customer->{account_number});
    $update_sth->execute() or die "Couldn't execute statement: $DBI::errstr";

    $update_sth->finish;

# Your existing SELECT query
    my $select_sql = "SELECT * FROM customers WHERE account_number = ? LIMIT 1";
    my $select_sth = $dbh->prepare( $select_sql );
    $select_sth->bind_param(1, $customer->{account_number} );
    $select_sth->execute() or die "Couldn't execute statement: $DBI::errstr";

    my $agents = $select_sth->fetchrow_hashref;

    $select_sth->finish;

    if (lc $agents->{mac_address} eq lc $data->{mac_address} ) {
        $res->body( $swml->swaig_response_json( { response => "Customers modem mac address updated, please allow 5 minutes for all systems to update and plug in your new modem.", action => [  { set_meta_data => { customer => $agents } } ] } ) );
        broadcast_by_agent_id( $agent, $agents );
        
    } else {
        $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again.  #1" } ) );
    }
} else {
    $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again later.  #2" } ) );  
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

sub is_valid_mac_address {
    my ($mac) = @_;

    # Regular expression for a MAC address with optional colons or dashes
    my $mac_regex = qr/^([0-9A-Fa-f]{2}(:|-)?){5}[0-9A-Fa-f]{2}$/;

    return $mac =~ $mac_regex;
}

my $dbh = DBI->connect(
  "dbi:Pg:dbname=$database;host=$host;port=$port",
  $dbusername,
  $dbpassword,
  { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n";

if ( is_valid_mac_address($data->{mac_address}) ) {
# Add a SQL update statement (modify this to match your actual table and field names)
    my $update_sql = "UPDATE customers SET mac_address = ? WHERE account_number = ?";
    my $update_sth = $dbh->prepare( $update_sql );
    $update_sth->bind_param(1, $data->{mac_address});
    $update_sth->bind_param(2, $customer->{account_number});
    $update_sth->execute() or die "Couldn't execute statement: $DBI::errstr";

    $update_sth->finish;

# Your existing SELECT query
    my $select_sql = "SELECT * FROM customers WHERE account_number = ? LIMIT 1";
    my $select_sth = $dbh->prepare( $select_sql );
    $select_sth->bind_param(1, $customer->{account_number} );
    $select_sth->execute() or die "Couldn't execute statement: $DBI::errstr";

    my $agents = $select_sth->fetchrow_hashref;

    $select_sth->finish;

    if (lc $agents->{mac_address} eq lc $data->{mac_address} ) {
        $res->body( $swml->swaig_response_json( { response => "Customers modem mac address updated, please allow 5 minutes for all systems to update and plug in your new modem.", action => [  { set_meta_data => { customer => $agents } } ] } ) );
        broadcast_by_agent_id( $agent, $agents );
        
    } else {
        $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again.  #1" } ) );
    }
} else {
    $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again later.  #2" } ) );  
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

sub is_valid_mac_address {
    my ($mac) = @_;

    # Regular expression for a MAC address with optional colons or dashes
    my $mac_regex = qr/^([0-9A-Fa-f]{2}(:|-)?){5}[0-9A-Fa-f]{2}$/;

    return $mac =~ $mac_regex;
}

my $dbh = DBI->connect(
  "dbi:Pg:dbname=$database;host=$host;port=$port",
  $dbusername,
  $dbpassword,
  { AutoCommit => 1, RaiseError => 1 } ) or die "Couldn't execute statement: $DBI::errstr\n";

if ( is_valid_mac_address($data->{mac_address}) ) {
# Add a SQL update statement (modify this to match your actual table and field names)
    my $update_sql = "UPDATE customers SET mac_address = ? WHERE account_number = ?";
    my $update_sth = $dbh->prepare( $update_sql );
    $update_sth->bind_param(1, $data->{mac_address});
    $update_sth->bind_param(2, $customer->{account_number});
    $update_sth->execute() or die "Couldn't execute statement: $DBI::errstr";

    $update_sth->finish;

# Your existing SELECT query
    my $select_sql = "SELECT * FROM customers WHERE account_number = ? LIMIT 1";
    my $select_sth = $dbh->prepare( $select_sql );
    $select_sth->bind_param(1, $customer->{account_number} );
    $select_sth->execute() or die "Couldn't execute statement: $DBI::errstr";

    my $agents = $select_sth->fetchrow_hashref;

    $select_sth->finish;

    if (lc $agents->{mac_address} eq lc $data->{mac_address} ) {
        $res->body( $swml->swaig_response_json( { response => "Customers modem mac address updated, please allow 5 minutes for all systems to update and plug in your new modem.", action => [  { set_meta_data => { customer => $agents } } ] } ) );
        broadcast_by_agent_id( $agent, $agents );
        
    } else {
        $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again.  #1" } ) );
    }
} else {
    $res->body( $swml->swaig_response_json( { response => "Error swapping modem, mac address may be invalid, try again later.  #2" } ) );  
}

return $res



speed_test

  • Name: speed_test

  • Purpose: Test upload and download speed from the modem

  • Argument: account_number|7 digit number,cpni|4 digit number

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

if ($customer->{modem_speed_upload} && $customer->{modem_speed_download}) {
    $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Download speed: $customer->{modem_speed_download}, Upload speed: $customer->{modem_speed_upload}" } ) );
} else {
    $res->body( $swml->swaig_response_json( { response => "Invalid try again speed_test" } ) );
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

if ($customer->{modem_speed_upload} && $customer->{modem_speed_download}) {
    $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Download speed: $customer->{modem_speed_download}, Upload speed: $customer->{modem_speed_upload}" } ) );
} else {
    $res->body( $swml->swaig_response_json( { response => "Invalid try again speed_test" } ) );
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

if ($customer->{modem_speed_upload} && $customer->{modem_speed_download}) {
    $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Download speed: $customer->{modem_speed_download}, Upload speed: $customer->{modem_speed_upload}" } ) );
} else {
    $res->body( $swml->swaig_response_json( { response => "Invalid try again speed_test" } ) );
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

if ($customer->{modem_speed_upload} && $customer->{modem_speed_download}) {
    $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Download speed: $customer->{modem_speed_download}, Upload speed: $customer->{modem_speed_upload}" } ) );
} else {
    $res->body( $swml->swaig_response_json( { response => "Invalid try again speed_test" } ) );
}

return $res



modem_diagnostics

  • Name: modem_diagnostics

  • Purpose: customer modem upstream downstream and snr levels

  • Argument: account_number|7 digit number,cpni|4 digit number

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

if ($customer) {
    $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Downstream level: $customer->{modem_downstream_level}, Upstream level: $customer->{modem_upstream_level}, Modem SNR: $customer->{modem_snr}" } ) );
} else {
    $res->body( $swml->swaig_response_json( { response => "Invalid try again. Use modem-diagnostics-function" } ) );
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

if ($customer) {
    $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Downstream level: $customer->{modem_downstream_level}, Upstream level: $customer->{modem_upstream_level}, Modem SNR: $customer->{modem_snr}" } ) );
} else {
    $res->body( $swml->swaig_response_json( { response => "Invalid try again. Use modem-diagnostics-function" } ) );
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

if ($customer) {
    $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Downstream level: $customer->{modem_downstream_level}, Upstream level: $customer->{modem_upstream_level}, Modem SNR: $customer->{modem_snr}" } ) );
} else {
    $res->body( $swml->swaig_response_json( { response => "Invalid try again. Use modem-diagnostics-function" } ) );
}

return $res

copy

01

02

03

04

05

06

07

08

09

10

11

12

my $env       = shift;
my $req       = Plack::Request->new( $env );
my $swml      = SignalWire::ML->new;
my $post_data = decode_json( $req->raw_body );
my $data      = $post_data->{argument}->{parsed}->[0];
my $res       = Plack::Response->new( 200 );
my $agent     = $req->param( 'agent_id' );
my $customer  = $post_data->{meta_data}->{customer};

if ($customer) {
    $res->body( $swml->swaig_response_json( { response => "Tell the user here are the test results. Downstream level: $customer->{modem_downstream_level}, Upstream level: $customer->{modem_upstream_level}, Modem SNR: $customer->{modem_snr}" } ) );
} else {
    $res->body( $swml->swaig_response_json( { response => "Invalid try again. Use modem-diagnostics-function" } ) );
}

return $res



Have any questions as you’re building an AI agent? Bring them to our community Discord!

Frequently asked questions

What is a “digital employee” for customer support?

A digital employee is an AI voice agent that answers support calls, gathers the information needed to diagnose an issue, and completes routine tasks like running checks, guiding troubleshooting steps, and routing calls to a human when escalation is needed.

What problems are AI voice agents best at solving in customer support?

AI voice agents are strongest at high-volume, repeatable workflows, including basic troubleshooting, status checks, appointment scheduling, capturing case details, and routing calls, especially when the agent can also take actions like sending messages or triggering backend functions.

How do you verify a caller before performing account-level actions?

Use a verification step before any sensitive troubleshooting or changes. In this example, verification uses an account number and Customer Proprietary Network Information (CPNI), and the flow blocks later steps unless verification succeeds.

What backend actions should an AI support agent be allowed to run?

Start with safe, reversible actions like running a speed test, checking modem diagnostics, and collecting line statistics, then expand cautiously to state-changing actions such as a modem swap, only after verification passes and guardrails are in place.

How do you connect an AI Agent to real support systems and data?

With SignalWire, define functions that query or update your systems, return structured JavaScript Object Notation (JSON), and constrain when those functions can run. This example uses functions for customer verification, speed tests, modem diagnostics, and modem swaps.

How do you keep an AI support flow predictable instead of free-form?

Use a staged troubleshooting flow, gate progression on verification, constrain the agent to specific steps, and limit the function set to approved actions so the agent cannot improvise unsafe behavior.

Bottom Linear Gradient  Lines image

The Communications Stack for What's Next

APIs built for speed. Infrastructure built for scale. AI built in from day one.

The Communications Stack for What's Next

APIs built for speed. Infrastructure built for scale. AI built in from day one.

The Communications Stack for What's Next

APIs built for speed. Infrastructure built for scale. AI built in from day one.

The Communications Stack for What's Next

APIs built for speed. Infrastructure built for scale. AI built in from day one.