AVR (Agent Voice Response) supports function calling capabilities, allowing developers to create custom tools that can be invoked during real-time speech-to-speech conversations. This guide explains how to create, implement, and use custom function calls in the AVR system.
The function calling system in AVR consists of two main directories:
avr_tools/
- Project-provided tools (core functionality)tools/
- User custom tools (extensible functionality)Tools are automatically loaded at runtime and made available to the LLM for function calling.
AVR comes with two essential built-in function calls that are automatically available in every conversation:
avr_transfer
- Call Transfer ToolPurpose: Transfers the current call to a specific extension, commonly used to connect users with human operators.
Usage: The AI agent can automatically transfer calls when:
Parameters:
transfer_extension
(required): The extension number to transfer totransfer_context
(optional): The context/department for the transfertransfer_priority
(optional): Priority level for the transferExample AI Usage: "I understand you'd like to speak with a human operator. Let me transfer you to our customer service team."
avr_hangup
- Call Termination ToolPurpose: Forces the virtual agent to end the conversation and hang up the call.
Usage: The AI agent can automatically hang up when:
Parameters: None required
Example AI Usage: "Your appointment has been successfully scheduled. Thank you for calling, and have a great day!"
These default tools ensure that every AVR deployment has the essential call management capabilities without requiring additional development.
Each tool must follow a specific structure defined by the following properties:
module.exports = {
name: "tool_name", // Unique identifier for the tool
description: "Tool description", // Human-readable description
input_schema: {}, // JSON Schema for input validation
handler: async (uuid, args) => {} // Function to execute
};
name
(string, required)"get_weather"
, "avr_transfer"
, "avr_hangup"
description
(string, required)"Retrieves weather information for a specific location"
input_schema
(object, required)handler
(function, required)uuid
and args
Create a new JavaScript file in the tools/
directory:
touch path/to/tools/my_custom_tool.js
// tools/my_custom_tool.js
module.exports = {
name: "my_custom_tool",
description: "Performs a custom operation based on user input",
input_schema: {
type: "object",
properties: {
parameter1: {
type: "string",
description: "First parameter description"
},
parameter2: {
type: "number",
description: "Second parameter description"
}
},
required: ["parameter1"] // parameter2 is optional
},
handler: async (uuid, { parameter1, parameter2 }) => {
try {
// Your custom logic here
const result = await performCustomOperation(parameter1, parameter2);
return `Operation completed successfully: ${result}`;
} catch (error) {
return `Error occurred: ${error.message}`;
}
}
};
After adding a new tool, restart the AVR services for changes to take effect.
input_schema: {
type: "object",
properties: {
location: {
type: "string",
description: "The name of the location"
}
},
required: ["location"]
}
input_schema: {
type: "object",
properties: {
name: {
type: "string",
description: "User's full name"
},
age: {
type: "number",
description: "User's age in years"
},
isActive: {
type: "boolean",
description: "Whether the user account is active"
}
},
required: ["name", "age"]
}
input_schema: {
type: "object",
properties: {
items: {
type: "array",
items: {
type: "string"
},
description: "List of items to process"
}
},
required: ["items"]
}
handler: async (uuid, args) => {
// uuid: Session identifier for tracking
// args: Object containing the validated input parameters
}
uuid
(string): Unique session identifier for the current conversation
"550e8400-e29b-41d4-a716-446655440000"
args
(object): Destructured parameters from the input schema
input_schema
{ location: "New York" }
The handler must return a string that will be:
Always implement proper error handling in your handlers:
handler: async (uuid, args) => {
try {
// Your logic here
const result = await someAsyncOperation(args);
return `Success: ${result}`;
} catch (error) {
console.error(`Error in ${this.name}:`, error);
return `I encountered an error: ${error.message}`;
}
}
// tools/get_weather.js
const axios = require('axios');
module.exports = {
name: "get_weather",
description: "Retrieves current weather information for a specified location",
input_schema: {
type: "object",
properties: {
location: {
type: "string",
description: "The city or location name"
},
units: {
type: "string",
enum: ["celsius", "fahrenheit"],
description: "Temperature units (default: celsius)"
}
},
required: ["location"]
},
handler: async (uuid, { location, units = "celsius" }) => {
try {
// Simulate weather API call
const weather = await getWeatherData(location);
return `The weather in ${location} is currently ${weather.condition} with a temperature of ${weather.temperature}°${units === "fahrenheit" ? "F" : "C"}.`;
} catch (error) {
return `I'm sorry, I couldn't retrieve the weather for ${location}. Please try again.`;
}
}
};
// tools/avr_transfer.js
require("dotenv").config();
const axios = require("axios");
module.exports = {
name: "avr_transfer",
description: "Transfers a call to a specific extension.",
input_schema: {
type: "object",
properties: {
transfer_extension: {
type: "string",
description: "The transfer extension to transfer the call to.",
},
transfer_context: {
type: "string",
description: "The context to transfer the call to.",
},
transfer_priority: {
type: "string",
description: "The priority of the transfer.",
},
},
required: ["transfer_extension"],
},
handler: async (
uuid,
{ transfer_extension, transfer_context, transfer_priority }
) => {
console.log("Transfering call to:", transfer_extension);
console.log("UUID:", uuid);
try {
const url = process.env.AMI_URL || "http://127.0.0.1:6006";
const res = await axios.post(`${url}/transfer`, {
uuid,
exten: transfer_extension,
context: transfer_context || "demo",
priority: transfer_priority || 1,
});
console.log("Transfer response:", res.data);
return res.data.message;
} catch (error) {
console.error("Error during transfer:", error.message);
return `Error during transfer: ${error.message}`;
}
},
};
avr_
Tool not loading
Handler not executing
Parameter validation errors
Your tools can access environment variables through process.env
:
handler: async (uuid, args) => {
const apiKey = process.env.MY_API_KEY;
const baseUrl = process.env.MY_SERVICE_URL;
// Use these in your tool logic
}
Function calls in AVR provide a powerful way to extend the system's capabilities. By following this guide, you can create robust, reliable tools that enhance the user experience and provide valuable functionality during voice conversations.
The built-in avr_transfer
and avr_hangup
tools ensure that every deployment has essential call management capabilities, while the extensible tools/
directory allows you to add custom functionality specific to your use case.
For additional support and examples, refer to the existing tools in the avr_tools/
directory and the main AVR documentation.