Build A Function Call

Learn how to build a Function Call to use with your Agent.

This guide walks you through building function calls for your Voice Agent. We start with a basic weather lookup example and then move to a more complex customer management system.

Basic Example: Weather Lookup

This example demonstrates a simple client-side function call. When a user asks about the weather, the agent extracts the location and requests that your client fetch the data.

1. Define the Function in Settings

Include the function definition in your Settings message under the agent.think object.

get_weather only reads data, so it is safe to dispatch as soon as the model asks for it. A function that changes something should set defer_until_eot: true instead, as end_call does later in this guide. See defer_until_eot.

JSON
{
"type": "Settings",
"agent": {
"think": {
"provider": {
"type": "open_ai",
"model": "gpt-4o-mini"
},
"prompt": "You are a helpful AI assistant that can provide weather information.",
"functions": [
{
"name": "get_weather",
"description": "Get the current weather for a specific location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city or location to get weather for"
}
},
"required": ["location"]
}
}
]
}
}
}

2. Implement the Function Logic

Your client application must implement the logic to handle the get_weather request.

export const getWeather = async (location: string): Promise<string | null> => {
const apiKey = import.meta.env.VITE_OPENWEATHER_API_KEY;
try {
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${apiKey}`
);
if (!response.ok) {
throw new Error('Failed to fetch weather data');
}
const data = await response.json();
return `The current weather in ${data.name} is ${data.weather[0].description} with a temperature of ${data.main.temp}°K.`;
} catch (err) {
console.error(err);
return null;
}
};

Advanced Example: Customer Management System

This section walks you through building a more complex function call system for a demo Voice Agent application. For the complete code, see the code repository.

Getting Started

You’ll create two files:

  • agent_functions.py: Contains async functions and configuration for customer lookups, appointments, and order management.
  • agent_templates.py: Defines the agent prompt, settings, and the AgentTemplates factory class for industry-specific configuration.

Prerequisites

  • Python 3.7+
  • Familiarity with Python
  • An understanding of how to use Python Virtual environments.
  • Familiarity with the Deepgram Voice Agent API

Create agent_functions.py

This guide doesn’t cover the development of the business logic for this application. Please see business_logic.py for more details.

First, create a file called: agent_functions.py. Then in agent_functions.py set up the dependencies and import the business logic.

import json
from datetime import datetime, timedelta
import asyncio
from business_logic import (
get_customer,
get_customer_appointments,
get_customer_orders,
schedule_appointment,
get_available_appointment_slots,
prepare_agent_filler_message,
prepare_farewell_message
)

Implement the Functions

We’ll implement the following functions in agent_functions.py to handle customer and appointment management:

Function NamePurpose
find_customerLookup by phone, email, or ID; normalizes input
get_appointmentsRetrieve all appointments for a customer
get_ordersRetrieve order history for a customer
create_appointmentSchedule a new appointment
check_availabilityFind available appointment slots
agent_fillerProvide conversational filler (requires websocket param)
end_callEnd the conversation gracefully (requires websocket param)

Find Customer

async def find_customer(params):
"""Look up a customer by phone, email, or ID."""
phone = params.get("phone")
email = params.get("email")
customer_id = params.get("customer_id")
result = await get_customer(phone=phone, email=email, customer_id=customer_id)
return result

Get Appointments

async def get_appointments(params):
"""Get appointments for a customer."""
customer_id = params.get("customer_id")
if not customer_id:
return {"error": "customer_id is required"}
result = await get_customer_appointments(customer_id)
return result

Get Orders

async def get_orders(params):
"""Get orders for a customer."""
customer_id = params.get("customer_id")
if not customer_id:
return {"error": "customer_id is required"}
result = await get_customer_orders(customer_id)
return result

Create Appointment

async def create_appointment(params):
"""Schedule a new appointment."""
customer_id = params.get("customer_id")
date = params.get("date")
service = params.get("service")
if not all([customer_id, date, service]):
return {"error": "customer_id, date, and service are required"}
result = await schedule_appointment(customer_id, date, service)
return result

Check Availability

async def check_availability(params):
"""Check available appointment slots."""
start_date = params.get("start_date")
end_date = params.get("end_date", (datetime.fromisoformat(start_date) + timedelta(days=7)).isoformat())
if not start_date:
return {"error": "start_date is required"}
result = await get_available_appointment_slots(start_date, end_date)
return result

Agent Filler

async def agent_filler(websocket, params):
"""
Handle agent filler messages while maintaining proper function call protocol.
"""
result = await prepare_agent_filler_message(websocket, **params)
return result

End Call

Ending a call cannot be undone, so this function sets defer_until_eot: true in its definition below. The agent holds the call until the user’s turn is confirmed, and discards it if the user keeps speaking. See defer_until_eot.

async def end_call(websocket, params):
"""
End the conversation and close the connection.
"""
farewell_type = params.get("farewell_type", "general")
result = await prepare_farewell_message(websocket, farewell_type)
return result

Create Function Definitions

Next in agent_functions.py we’ll setup FUNCTION_DEFINITIONS which is an array that defines the API contract for the Voice Agent system. It specifies all available operations, their parameters, and usage guidelines.

Each function definition follows a JSON Schema format with:

  • Name
  • Description
  • Parameters specification
  • Required fields
  • Enumerated values where applicable
# Function definitions that will be sent to the Voice Agent API
FUNCTION_DEFINITIONS = [
{
"name": "agent_filler",
"description": """Use this function to provide natural conversational filler before looking up information.
ALWAYS call this function first with message_type='lookup' when you're about to look up customer information.
After calling this function, you MUST immediately follow up with the appropriate lookup function (e.g., find_customer).""",
"parameters": {
"type": "object",
"properties": {
"message_type": {
"type": "string",
"description": "Type of filler message to use. Use 'lookup' when about to search for information.",
"enum": ["lookup", "general"]
}
},
"required": ["message_type"]
}
},
{
"name": "find_customer",
"description": """Look up a customer's account information. Use context clues to determine what type of identifier the user is providing:
Customer ID formats:
- Numbers only (e.g., '169', '42') → Format as 'CUST0169', 'CUST0042'
- With prefix (e.g., 'CUST169', 'customer 42') → Format as 'CUST0169', 'CUST0042'
Phone number recognition:
- Standard format: '555-123-4567' → Format as '+15551234567'
- With area code: '(555) 123-4567' → Format as '+15551234567'
- Spoken naturally: 'five five five, one two three, four five six seven' → Format as '+15551234567'
- International: '+1 555-123-4567' → Use as is
- Always add +1 country code if not provided
Email address recognition:
- Spoken naturally: 'my email is john dot smith at example dot com' → Format as '[email protected]'
- With domain: '[email protected]' → Use as is
- Spelled out: 'j o h n at example dot com' → Format as '[email protected]'""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer's ID. Format as CUSTXXXX where XXXX is the number padded to 4 digits with leading zeros. Example: if user says '42', pass 'CUST0042'"
},
"phone": {
"type": "string",
"description": """Phone number with country code. Format as +1XXXXXXXXXX:
- Add +1 if not provided
- Remove any spaces, dashes, or parentheses
- Convert spoken numbers to digits
Example: 'five five five one two three four five six seven' → '+15551234567'"""
},
"email": {
"type": "string",
"description": """Email address in standard format:
- Convert 'dot' to '.'
- Convert 'at' to '@'
- Remove spaces between spelled out letters
Example: 'j dot smith at example dot com' → '[email protected]'"""
}
}
}
},
{
"name": "get_appointments",
"description": """Retrieve all appointments for a customer. Use this function when:
- A customer asks about their upcoming appointments
- A customer wants to know their appointment schedule
- A customer asks 'When is my next appointment?'
Always verify you have the customer's account first using find_customer before checking appointments.""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer's ID in CUSTXXXX format. Must be obtained from find_customer first."
}
},
"required": ["customer_id"]
}
},
{
"name": "get_orders",
"description": """Retrieve order history for a customer. Use this function when:
- A customer asks about their orders
- A customer wants to check order status
- A customer asks questions like 'Where is my order?' or 'What did I order?'
Always verify you have the customer's account first using find_customer before checking orders.""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer's ID in CUSTXXXX format. Must be obtained from find_customer first."
}
},
"required": ["customer_id"]
}
},
{
"name": "create_appointment",
"description": """Schedule a new appointment for a customer. Use this function when:
- A customer wants to book a new appointment
- A customer asks to schedule a service
Before scheduling:
1. Verify customer account exists using find_customer
2. Check availability using check_availability
3. Confirm date/time and service type with customer before booking""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer's ID in CUSTXXXX format. Must be obtained from find_customer first."
},
"date": {
"type": "string",
"description": "Appointment date and time in ISO format (YYYY-MM-DDTHH:MM:SS). Must be a time slot confirmed as available."
},
"service": {
"type": "string",
"description": "Type of service requested. Must be one of the following: Consultation, Follow-up, Review, or Planning",
"enum": ["Consultation", "Follow-up", "Review", "Planning"]
}
},
"required": ["customer_id", "date", "service"]
}
},
{
"name": "check_availability",
"description": """Check available appointment slots within a date range. Use this function when:
- A customer wants to know available appointment times
- Before scheduling a new appointment
- A customer asks 'When can I come in?' or 'What times are available?'
After checking availability, present options to the customer in a natural way, like:
'I have openings on [date] at [time] or [date] at [time]. Which works better for you?'""",
"parameters": {
"type": "object",
"properties": {
"start_date": {
"type": "string",
"description": "Start date in ISO format (YYYY-MM-DDTHH:MM:SS). Usually today's date for immediate availability checks."
},
"end_date": {
"type": "string",
"description": "End date in ISO format. Optional - defaults to 7 days after start_date. Use for specific date range requests."
}
},
"required": ["start_date"]
}
},
{
"name": "end_call",
"description": """End the conversation and close the connection. Call this function when:
- User says goodbye, thank you, etc.
- User indicates they're done ("that's all I need", "I'm all set", etc.)
- User wants to end the conversation
Examples of triggers:
- "Thank you, bye!"
- "That's all I needed, thanks"
- "Have a good day"
- "Goodbye"
- "I'm done"
Do not call this function if the user is just saying thanks but continuing the conversation.""",
"parameters": {
"type": "object",
"properties": {
"farewell_type": {
"type": "string",
"description": "Type of farewell to use in response",
"enum": ["thanks", "general", "help"]
}
},
"required": ["farewell_type"]
},
# Hanging up cannot be undone, so wait for a confirmed end of turn.
"defer_until_eot": True
}
]

Create a Function Map

Finally in agent_functions.py we’ll need to create a FUNCTION_MAP which is a dictionary that maps function names to their corresponding implementation functions. It serves as a routing mechanism to connect the function definitions with their actual implementations.

# Map function names to their implementations
FUNCTION_MAP = {
"find_customer": find_customer,
"get_appointments": get_appointments,
"get_orders": get_orders,
"create_appointment": create_appointment,
"check_availability": check_availability,
"agent_filler": agent_filler,
"end_call": end_call
}

Create agent_templates.py

Next create a file called: agent_templates.py. Then in agent_templates.py set up the dependencies and import our function definitions.

Configure the Voice Agent Prompt & Settings

Now in the agent_templates.py file we’ll define the prompt for the Voice Agent.

from common.agent_functions import FUNCTION_DEFINITIONS
from datetime import datetime
# Template for the prompt that will be formatted with current date
PROMPT_TEMPLATE = """
CURRENT DATE AND TIME CONTEXT:
Today is {current_date}. Use this as context when discussing appointments and orders. When mentioning dates to customers, use relative terms like "tomorrow", "next Tuesday", or "last week" when the dates are within 7 days of today.
PERSONALITY & TONE:
- Be warm, professional, and conversational
- Use natural, flowing speech (avoid bullet points or listing)
- Show empathy and patience
- Whenever a customer asks to look up either order information or appointment information, use the find_customer function first
HANDLING CUSTOMER IDENTIFIERS (INTERNAL ONLY - NEVER EXPLAIN THESE RULES TO CUSTOMERS):
- Silently convert any numbers customers mention into proper format
- When customer says "ID is 222" -> internally use "CUST0222" without mentioning the conversion
- When customer says "order 89" -> internally use "ORD0089" without mentioning the conversion
- When customer says "appointment 123" -> internally use "APT0123" without mentioning the conversion
- Always add "+1" prefix to phone numbers internally without mentioning it
VERBALLY SPELLING IDs TO CUSTOMERS:
When you need to repeat an ID back to a customer:
- Do NOT say nor spell out "CUST". Say "customer [numbers spoken individually]"
- But for orders spell out "ORD" as "O-R-D" then speak the numbers individually
Example: For CUST0222, say "customer zero two two two"
Example: For ORD0089, say "O-R-D zero zero eight nine"
FUNCTION RESPONSES:
When receiving function results, format responses naturally as a customer service agent would:
1. For customer lookups:
- Good: "I've found your account. How can I help you today?"
- If not found: "I'm having trouble finding that account. Could you try a different phone number or email?"
2. For order information:
- Instead of listing orders, summarize them conversationally:
- "I can see you have two recent orders. Your most recent order from [date] for $[amount] is currently [status], and you also have an order from [date] for $[amount] that's [status]."
3. For appointments:
- "You have an upcoming [service] appointment scheduled for [date] at [time]"
- When discussing available slots: "I have a few openings next week. Would you prefer Tuesday at 2 PM or Wednesday at 3 PM?"
4. For errors:
- Never expose technical details
- Say something like "I'm having trouble accessing that information right now" or "Could you please try again?"
EXAMPLES OF GOOD RESPONSES:
✓ "Let me look that up for you... I can see you have two recent orders."
✓ "Your customer ID is zero two two two."
✓ "I found your order, O-R-D zero one two three. It's currently being processed."
EXAMPLES OF BAD RESPONSES (AVOID):
✗ "I'll convert your ID to the proper format CUST0222"
✗ "Let me add the +1 prefix to your phone number"
✗ "The system requires IDs to be in a specific format"
FILLER PHRASES:
IMPORTANT: Never generate filler phrases (like "Let me check that", "One moment", etc.) directly in your responses.
Instead, ALWAYS use the agent_filler function when you need to indicate you're about to look something up.
Examples of what NOT to do:
- Responding with "Let me look that up for you..." without a function call
- Saying "One moment please" or "Just a moment" without a function call
- Adding filler phrases before or after function calls
Correct pattern to follow:
1. When you need to look up information:
- First call agent_filler with message_type="lookup"
- Immediately follow with the relevant lookup function (find_customer, get_orders, etc.)
2. Only speak again after you have the actual information to share
Remember: ANY phrase indicating you're about to look something up MUST be done through the agent_filler function, never through direct response text.
"""

Next in the same file we’ll define the settings for the Voice Agent.

VOICE = "aura-2-thalia-en"
# this gets updated by the agent template
FIRST_MESSAGE = ""
# audio settings
USER_AUDIO_SAMPLE_RATE = 48000
USER_AUDIO_SECS_PER_CHUNK = 0.05
USER_AUDIO_SAMPLES_PER_CHUNK = round(USER_AUDIO_SAMPLE_RATE * USER_AUDIO_SECS_PER_CHUNK)
AGENT_AUDIO_SAMPLE_RATE = 16000
AGENT_AUDIO_BYTES_PER_SEC = 2 * AGENT_AUDIO_SAMPLE_RATE
VOICE_AGENT_URL = "wss://agent.deepgram.com/v1/agent/converse"
# For EU data processing, use: "wss://api.eu.deepgram.com/v1/agent/converse"
# For AU data processing, use: "wss://api.au.deepgram.com/v1/agent/converse"
# For India data processing, use: "wss://api.in.deepgram.com/v1/agent/converse"
AUDIO_SETTINGS = {
"input": {
"encoding": "linear16",
"sample_rate": USER_AUDIO_SAMPLE_RATE,
},
"output": {
"encoding": "linear16",
"sample_rate": AGENT_AUDIO_SAMPLE_RATE,
"container": "none",
},
}
LISTEN_SETTINGS = {
"provider": {
"type": "deepgram",
"model": "nova-3",
}
}
THINK_SETTINGS = {
"provider": {
"type": "open_ai",
"model": "gpt-4o-mini",
"temperature": 0.7,
},
"prompt": PROMPT_TEMPLATE,
"functions": FUNCTION_DEFINITIONS,
}
SPEAK_SETTINGS = {
"provider": {
"type": "deepgram",
"model": VOICE,
}
}
AGENT_SETTINGS = {
"language": "en",
"listen": LISTEN_SETTINGS,
"think": THINK_SETTINGS,
"speak": SPEAK_SETTINGS,
"greeting": FIRST_MESSAGE,
}
SETTINGS = {"type": "Settings", "audio": AUDIO_SETTINGS, "agent": AGENT_SETTINGS}

Finally in the same file we’ll define the factory class AgentTemplates which will be used to configure the Voice Agent. This class will be used to configure the Voice Agent for different industries.

class AgentTemplates:
PROMPT_TEMPLATE = PROMPT_TEMPLATE
def __init__(
self,
industry="tech_support",
voiceModel="aura-2-thalia-en",
voiceName="",
):
self.voiceName = voiceName
self.voiceModel = voiceModel
self.personality = ""
self.company = ""
self.first_message = ""
self.capabilities = ""
self.industry = industry
self.prompt = self.PROMPT_TEMPLATE.format(
current_date=datetime.now().strftime("%A, %B %d, %Y")
)
self.voice_agent_url = VOICE_AGENT_URL
self.settings = SETTINGS
self.user_audio_sample_rate = USER_AUDIO_SAMPLE_RATE
self.user_audio_secs_per_chunk = USER_AUDIO_SECS_PER_CHUNK
self.user_audio_samples_per_chunk = USER_AUDIO_SAMPLES_PER_CHUNK
self.agent_audio_sample_rate = AGENT_AUDIO_SAMPLE_RATE
self.agent_audio_bytes_per_sec = AGENT_AUDIO_BYTES_PER_SEC
match self.industry:
case "tech_support":
self.tech_support()
case "healthcare":
self.healthcare()
case "banking":
self.banking()
case "pharmaceuticals":
self.pharmaceuticals()
case "retail":
self.retail()
self.first_message = f"Hello! I'm {self.voiceName} from {self.company} customer service. {self.capabilities} How can I help you today?"
self.settings["agent"]["speak"]["provider"]["model"] = self.voiceModel
self.settings["agent"]["think"]["prompt"] = self.prompt
self.settings["agent"]["greeting"] = self.first_message
self.prompt = self.personality + "\n\n" + self.prompt
def tech_support(
self, company="TechStyle", agent_voice="aura-2-thalia-en", voiceName=""
):
if voiceName == "":
voiceName = self.get_voice_name_from_model(agent_voice)
self.voiceName = voiceName
self.company = company
self.voiceModel = agent_voice
self.personality = f"You are {self.voiceName}, a friendly and professional customer service representative for {self.company}, an online electronics and accessories retailer. Your role is to assist customers with orders, appointments, and general inquiries."
self.capabilities = "I'd love to help you with your order or appointment."
def healthcare(
self, company="HealthFirst", agent_voice="aura-2-andromeda-en", voiceName=""
):
if voiceName == "":
voiceName = self.get_voice_name_from_model(agent_voice)
self.voiceName = voiceName
self.company = company
self.voiceModel = agent_voice
self.personality = f"You are {self.voiceName}, a compassionate and knowledgeable healthcare assistant for {self.company}, a leading healthcare provider. Your role is to assist patients with appointments, medical inquiries, and general health information."
self.capabilities = "I can help you schedule appointments or answer questions about our services."
def banking(
self, company="SecureBank", agent_voice="aura-2-apollo-en", voiceName=""
):
if voiceName == "":
voiceName = self.get_voice_name_from_model(agent_voice)
self.voiceName = voiceName
self.company = company
self.voiceModel = agent_voice
self.personality = f"You are {self.voiceName}, a professional and trustworthy banking representative for {self.company}, a secure financial institution. Your role is to assist customers with account inquiries, transactions, and financial services."
self.capabilities = (
"I can assist you with your account or any banking services you need."
)
def pharmaceuticals(
self, company="MedLine", agent_voice="aura-2-helena-en", voiceName=""
):
if voiceName == "":
voiceName = self.get_voice_name_from_model(agent_voice)
self.voiceName = voiceName
self.company = company
self.voiceModel = agent_voice
self.personality = f"You are {self.voiceName}, a professional and trustworthy pharmaceutical representative for {self.company}, a secure pharmaceutical company. Your role is to assist customers with account inquiries, transactions, and appointments. You MAY NOT provide medical advice."
self.capabilities = "I can assist you with your account or appointments."
def retail(self, company="StyleMart", agent_voice="aura-2-aries-en", voiceName=""):
if voiceName == "":
voiceName = self.get_voice_name_from_model(agent_voice)
self.voiceName = voiceName
self.company = company
self.voiceModel = agent_voice
self.personality = f"You are {self.voiceName}, a friendly and attentive retail associate for {self.company}, a trendy clothing and accessories store. Your role is to assist customers with product inquiries, orders, and style recommendations."
self.capabilities = (
"I can help you find the perfect item or check on your order status."
)
def travel(self, company="TravelTech", agent_voice="aura-2-arcas-en", voiceName=""):
if voiceName == "":
voiceName = self.get_voice_name_from_model(agent_voice)
self.voiceName = voiceName
self.company = company
self.voiceModel = agent_voice
self.personality = f"You are {self.voiceName}, a friendly and professional customer service representative for {self.company}, a tech-forward travel agency. Your role is to assist customers with travel bookings, appointments, and general inquiries."
self.capabilities = (
"I'd love to help you with your travel bookings or appointments."
)
@staticmethod
def get_available_industries():
"""Return a dictionary of available industries with display names"""
return {
"tech_support": "Tech Support",
"healthcare": "Healthcare",
"banking": "Banking",
"pharmaceuticals": "Pharmaceuticals",
"retail": "Retail",
"travel": "Travel",
}
def get_voice_name_from_model(self, model):
return model.split("-")[2].split("-")[0].capitalize()

Call the functions from client.py

This guide doesn’t cover the development of the client for this application. Please see client.py for more details.

In the client.py file we’ll need reference agent_templates.py which will define the settings for the Voice Agent.

settings = self.agent_templates.settings