Voice Tools¶
The voicetools package provides AI agent tools for controlling voice calls, enabling agents to transfer calls, place callers on hold, consult specialist agents, and manage conference calls.
Overview¶
Voice tools enable AI agents to:
- Transfer calls - Route to human agents or other queues
- Hold/Unhold - Manage caller wait states with music
- Consult agents - Query specialists without transferring
- Conference - Add participants to multi-party calls
Quick Start¶
import (
"github.com/plexusone/omniskill/voicetools"
"github.com/plexusone/omniskill/mcp/server"
)
// Create call context with transport and agent registry
callCtx := voicetools.NewCallContext(call, transport, agentRegistry)
// Create voice skill with all tools
voiceSkill := voicetools.NewVoiceSkill(callCtx)
// Register with MCP server
rt := server.New(impl, nil)
rt.RegisterSkill(voiceSkill)
// Or use tools directly
transferTool := voicetools.NewTransferCallTool(callCtx)
result, err := transferTool.Call(ctx, map[string]any{
"target": "+15551234567",
"warm": true,
})
Interfaces¶
CallContext¶
Provides access to call state and transport:
type CallContext interface {
GetCall() Call
GetTransport() TelephonyTransport
GetAgentRegistry() AgentRegistry
}
Call¶
Represents the current call:
type Call interface {
ID() string
From() string
To() string
Direction() string
Duration() time.Duration
Metadata() map[string]string
}
TelephonyTransport¶
Implements call control operations:
type TelephonyTransport interface {
Transfer(callID, target, announce string, warm bool) error
Hold(callID, music string) error
Unhold(callID string) error
Conference(callID string, participants []string) error
Mute(callID string) error
Unmute(callID string) error
Hangup(callID string) error
}
AgentRegistry¶
Provides access to specialist agents:
type AgentRegistry interface {
GetAgent(id string) (AgentConfig, bool)
ListAgents() []string
ConsultAgent(agentID, query string, context map[string]any) (string, error)
}
Available Tools¶
transfer_call¶
Transfer the current call to another number or agent queue:
// Tool parameters
{
"target": "+15551234567", // Required: E.164 number or queue ID
"announce": "Connecting...", // Optional: Message during transfer
"warm": true // Optional: Stay on line until answered
}
// Result
{
"status": "success",
"call_id": "CA123456",
"target": "+15551234567"
}
Usage by AI agent:
"I'll transfer you to our billing department now." Agent calls transfer_call with target="billing-queue"
hold_call¶
Place the caller on hold:
// Tool parameters
{
"music": "jazz" // Optional: Hold music style
}
// Result
{
"status": "success",
"call_id": "CA123456"
}
Usage by AI agent:
"Please hold while I look that up for you." Agent calls hold_call
unhold_call¶
Resume the call from hold:
consult_agent¶
Query a specialist AI agent without transferring the call:
// Tool parameters
{
"agent_id": "billing", // Required: Specialist agent ID
"query": "What is the customer balance?" // Required: Question for specialist
}
// Result
{
"status": "success",
"agent_id": "billing",
"response": "The customer's balance is $142.50 due on Jan 15."
}
Usage by AI agent:
"Let me check your balance..." Agent calls consult_agent to get info from billing specialist "Your current balance is $142.50, due on January 15th."
add_to_conference¶
Add participants to a conference call:
// Tool parameters
{
"participants": ["+15551234567", "+15559876543"] // Required: Numbers to add
}
// Result
{
"status": "success",
"call_id": "CA123456",
"participants": ["+15551234567", "+15559876543"]
}
Integration with Voice Gateways¶
Twilio¶
import (
"github.com/plexusone/omnivoice-core/callsystem/twilio"
"github.com/plexusone/omniskill/voicetools"
)
// Twilio transport implements TelephonyTransport
twilioGateway := twilio.NewGateway(config)
twilioGateway.OnCall(func(call twilio.Call) {
// Create call context
callCtx := voicetools.NewCallContext(
adaptCall(call),
adaptTransport(twilioGateway),
agentRegistry,
)
// Register voice tools
voiceSkill := voicetools.NewVoiceSkill(callCtx)
runtime.RegisterSkill(voiceSkill)
})
LiveKit¶
import (
"github.com/plexusone/omnivoice-core/callsystem/livekit"
"github.com/plexusone/omniskill/voicetools"
)
livekitGateway := livekit.NewGateway(config)
livekitGateway.OnParticipantJoined(func(participant livekit.Participant) {
callCtx := voicetools.NewCallContext(
adaptParticipant(participant),
adaptTransport(livekitGateway),
agentRegistry,
)
voiceSkill := voicetools.NewVoiceSkill(callCtx)
runtime.RegisterSkill(voiceSkill)
})
Agent Registry Implementation¶
type MyAgentRegistry struct {
agents map[string]*Agent
}
func (r *MyAgentRegistry) GetAgent(id string) (voicetools.AgentConfig, bool) {
agent, ok := r.agents[id]
if !ok {
return voicetools.AgentConfig{}, false
}
return voicetools.AgentConfig{
ID: agent.ID,
Name: agent.Name,
Description: agent.Description,
}, true
}
func (r *MyAgentRegistry) ListAgents() []string {
ids := make([]string, 0, len(r.agents))
for id := range r.agents {
ids = append(ids, id)
}
return ids
}
func (r *MyAgentRegistry) ConsultAgent(agentID, query string, context map[string]any) (string, error) {
agent, ok := r.agents[agentID]
if !ok {
return "", fmt.Errorf("agent not found: %s", agentID)
}
// Call the specialist agent
response, err := agent.Query(query, context)
return response, err
}
Error Handling¶
All tools return errors for:
- Missing required parameters
- Invalid parameter types
- Transport failures
- Agent not found (consult_agent)
result, err := tool.Call(ctx, params)
if err != nil {
switch {
case errors.Is(err, voicetools.ErrNoCallContext):
// CallContext not provided
case errors.Is(err, voicetools.ErrAgentNotFound):
// Specialist agent not registered
default:
// Transport or other error
}
}
Best Practices¶
- Validate before transfer - Confirm with user before transferring
- Use warm transfers - Stay on line when possible
- Announce holds - Tell caller what you're doing
- Consult before escalating - Try specialist agents first
- Handle errors gracefully - Inform user if action fails