An IVR with Neo4j and F# -- Part 1
Part 1: building the Neo4j graph database backend
Why am I doing this?
I develop something called the Interactive Customer Management System (ICMS). This lets customers enter orders for petroleum products from oil and gas terminals in India. Customers can get the status of their orders via SMS and IVR (interactive voice response). Developing a low-cost and simple IVR system is not easy. I do have one but it is old and I don't have the source code (long story), so I am trying to develop my own using Neo4j as the backend, and a web API and/or a web application for setup and management so that users can setup their own IVR sequences.
Why Neo4j (graph database)?
Because graphs are everywhere!
An IVR is a natural graph. It is a sequence of steps with various paths from the start to the end. My old system is a SQL database and I can barely make heads or tails of it, but I can clearly see in my mind, on on my whiteboard, what the IVR sequence graph would look like. Easy! Plus I wanted an excuse to use Neo4j.
Modelling a sequence
A sequence is a path through the menus and data entry points. The entire IVR graph is made of a number of sub-graphs or sequences.
For the IVR to work with my ICMS, it has to support two major sequences:
- An authentication sequence where the caller identifies himself and verifies himself with a PIN. This means interaction with a database to retrieve customer information and do verification. I will not store that in the graph.
- Get the customer's current order status. If the customer has more than one order then he should be able to page through each order using IVR options.
As you can see this is a very simple setup, but I want to eventually make the system general enough that others could use it without any customizations and to be able to build more complex interactions.
I started by modelling the authentication sequence.
We start with the welcome message, which also tells the caller to enter his customer number. If the customer number is recognized we can move on to the next step. Here we ask the caller for his PIN number. If the PIN number is correct then we can move on to the next order status sequence. If the PIN is wrong, then the caller gets 3 retries before getting the goodbye message and the system hangs up. If the customer number is not recognized, the caller is asked to retry. He gets 3 chances to retry before getting the goodbye message and the system hangs up.
This helped me come up with a few things to use to model a sequence.
The graph
This model is missing a couple of notions that I will need in order to build out a real IVR system,including the order status sequence. I will discuss that in a later post.
NODES
START & END
{
id: int
message: string
title: string
}
There is only one START node and one END node. The START node is the welcome message or starting menu list and the END node is the goodbye message. The id is an arbitrary id for node and unique per label. Message is the message that will be sent to the caller and title is for display purposes.
ENTRY
{
id: int
message: string
title: string
}
ENTRY is used as a step where the caller must enter some information like the customer number or PIN.
RETRY
{
id: int
message: string
title: string
retries: int
}
RETRY describes what happens when the previous step fails and how many retries the caller gets.
RELATIONSHIPS (EDGES)
GOTO -- GOTO the selected node
FAIL -- previous entry failed
SUCCESS -- previous entry succeeded
The codez
//START SEQUENCE HERE WITH A WELCOME MESSAGE AND THE MENU ITEMS OR THE FIRST STEP
CREATE (n:START { id: 1, title:'WELCOME', message: 'Welcome to the IVR.' }) RETURN n
//END SEQUENCE ENDS HERE
CREATE (n:END {id: 1, title:'GOODBYE',message:'Thank you. Goodbye'}) RETURN n
//BUILD AUTHENTICATION SEQUENCE
//USER IDENTIFIES HIMSELF WITH THE CUSTOMER NUMBER
CREATE (n:ENTRY { id: 1, title:'CUSTOMER NUMBER', message: 'Please enter your customer number.' }) RETURN n
//RETRY CUSTOMER NUMBER
CREATE (n:RETRY {id: 1, retries:3, title:'RETRY CUSTOMER NUMBER', message: 'Customer number not found. Please try again.'}) RETURN n
//MAKE THE RELATIONSHIP -- ENTERING THE CUSTOMER NUMBER IS THE FIRST STEP
MATCH (a:START { id: 1 }), (b:ENTRY { id: 1 })
CREATE (a)-[:GOTO]->(b)
RETURN a, b
//USER VERIFIES IDENTITY WITH PIN
CREATE (n:ENTRY {id: 2, title:'PIN', message:'Please enter your PIN.'}) RETURN n
//RETRY PIN
CREATE (n:RETRY {id: 2, retries: 3, title:'RETRY PIN',message:'Incorrect PIN. Please try again.'}) RETURN n
//SUCCESSFULLY ENTERED A KNOWN CUSTOMER NUMBER GO TO NEXT STEP -- ENTER PIN
MATCH (a:ENTRY { id: 1 }), (b:ENTRY { id: 2 })
CREATE (a)-[:SUCCESS]->(b)
RETURN a, b
//FAILED TO ENTER A KNOWN CUSTOMER NUMBER -- RETRY 3 TIMES
MATCH (a:ENTRY {id : 1}), (b:RETRY {id: 1})
CREATE (a)-[:FAIL]->(b)
RETURN a, b
//FAILED TO ENTER THE CORRECT PIN -- RETRY 3 TIMES
MATCH (a:ENTRY {id:2}), (b:RETRY {id: 2})
CREATE (a)-[:FAIL]->(b)
RETURN a, b
//CUSTOMER NUMBER RETRIES EXCEEDED -- GOODBYE
MATCH (a:RETRY {id:1}), (b:END {id:1})
CREATE (a)-[:FAIL]->(b)
RETURN a, b
//PIN NUMBER RETRIES EXCEEDED -- GOODBYE
MATCH (a:RETRY {id:2}), (b:END {id:1})
CREATE (a)-[:FAIL]->(b)
RETURN a, b
//END AUTHENTICATION SEQUENCE
How does this work over a phone line?
The simplest way to get started is basically like this:
Connect your server to a voice modem. Sound output will go out over this voice modem.
Write a program to answer calls on the voice modem and output text-to-speech to the voice modem.
I will discuss this in a later post.
What's next?
I am thinking about ways to extend the graph to support the order status sequence. Once I have that worked out I would like to generalize the graph so I can build arbitrary sequences and plug them all together. Ultimately I would like to build a UI that lets users build their own IVR graphs. I plan on using F# for the backend, possibly using Suave for a web API and some kind of SPA frontend with drag and drop javascript. Or just MVC/Razor and drag and drop javascript.
This is very ambitious but I am going to try and have fun with it.
Full Stack .NET Programmer and Ham
Cover image credit: https://sciencevsmagic.net/fractal/#0060,0090,1,1,0,0,1