Skip to main content
Agents process input events and yield output events to control the conversation.

What is an Agent?

An Agent controls the input/output event loop. The process method receives events (user speech, call start, etc.) and yields responses. An Agent can be:
  1. A class with a process method
  2. A function with the same signature (env, event) -> AsyncIterable[OutputEvent]
How an Agent works:
  • Events arrive (user speaks, call starts, button pressed)
  • SDK calls agent.process(env, event)
  • Agent yields output events (speech, tool calls, handoffs)
  • SDK handles audio, LLM calls, and state management

LlmAgent

Use the built-in LlmAgent which wraps 100+ LLM providers via LiteLLM:

Prompting

system_prompt to define your agent’s personality and introduction for the greeting:

Supported Models

ProviderModel Examples
Anthropicanthropic/claude-haiku-4-5-20251001, anthropic/claude-sonnet-4-5
OpenAIgpt-5.4, gpt-5.2
Googlegemini/gemini-2.5-flash-preview-09-2025, gemini/gemini-3.0-preview
And 100+ more via LiteLLM

LlmConfig Options

OptionTypeDescription
system_promptstrThe system prompt defining agent behavior
introductionOptional[str]Message sent on call start. None or "" to wait for r
temperatureOptional[float]Sampling temperature
max_tokensOptional[int]Maximum tokens per response
top_pOptional[float]Nucleus sampling threshold
stopOptional[List[str]]Stop sequences
seedOptional[int]Random seed for reproducibility
presence_penaltyOptional[float]Presence penalty for token generation
frequency_penaltyOptional[float]Frequency penalty for token generation
num_retriesintNumber of retries on failure (default: 2)
fallbacksOptional[List[str]]Fallback models if primary fails
timeoutOptional[float]Request timeout in seconds
reasoning_effortOptional[str]none, low, medium, or high. Dependent on provider.
extraDict[str, Any]Provider-specific options passed through to LiteLLM

History Management

LlmAgent exposes a history attribute for structured control over the conversation history the LLM sees. Adding entries:
Replacing history segments:

Per-Turn Overrides

process() accepts keyword arguments that apply to just that turn without mutating the agent:
Only explicitly set LlmConfig fields take effect — unset fields fall through to the agent’s stored config. To change tools permanently (e.g., enabling end_call after a certain point), modify agent.tools directly instead of using per-turn overrides.

Controlling the Conversational Loop

Use event filters to control when your agent’s process method runs, and which events can interrupt it.

Default Behavior

This means: agent greets on call start, responds when user finishes speaking, and can be interrupted.

Customizing Filters

Return a tuple from get_agent to override defaults:

Common Customizations

More responsive (process partial transcriptions):
This makes your agent start processing before the user finishes speaking, creating a more responsive experience. Uninterruptible turns: If you want a single message to complete without being interrupted by the user, mark the output as interruptible=False when sending it with AgentSendText.
Custom logic with functions:
For advanced patterns like guardrails, routing, and agent wrappers, see Advanced Patterns.

Handling Incoming Calls

When a call arrives, you can inspect caller information and configure how your agent responds before it starts.
  1. A call arrives from a web client or telephony provider
  2. Your pre_call_handler receives a CallRequest with caller details
  3. You return configuration (voice, language) or reject the call
  4. Your get_agent function creates an agent using the enriched request

Parsing the CallRequest

Contains information about the incoming call:
FieldTypeDescription
call_idstrUnique identifier for the call
from_strCaller identifier (phone number or client ID)
tostrCalled number or agent ID
agent_call_idstrAgent call ID for logging/correlation
metadataOptional[dict]Custom data passed from your client application
agentAgentConfigPrompts configured in Playground or via API
The agent field contains an AgentConfig with:
FieldTypeDescription
system_promptOptional[str]System prompt configured in Playground or via the WebSocket API
introductionOptional[str]Introduction message configured in Playground or via the WebSocket API

Returning a PreCallResult

Use pre_call_handler to set voice, language, or reject calls before your agent starts:
Your client application can pass metadata (user ID, language preference, account tier) in the call request. Your pre_call_handler reads this and configures TTS/STT accordingly.

Configuration Options

TTS Options:
OptionTypeDescription
voice_idstringVoice identifier (UUID)
modelstringTTS model (sonic-3.5, sonic-3, sonic-turbo)
languagestringLanguage code (en, es, hi, etc.)
pronunciation_dict_idstringCustom pronunciation dictionary ID
STT Options:
OptionTypeDescription
languagestringLanguage code for speech recognition

Rejecting Calls

Return None to reject a call with a 403 status:

Custom Pronunciations

Use a pronunciation dictionary to control how specific words are spoken:

Accessing call metadata in your Agent logic

The CallRequest is available in get_agent:
LlmConfig.from_call_request() handles the priority chain automatically:
  1. CallRequest.agent.system_prompt value (if set)
  2. Your fallback value (if provided)
  3. SDK default
Using CallRequest lets you iterate on system prompts from the Playground instantly, while code handles the technical configuration and fallback defaults.

Letting The User Speak First

Set introduction to an empty string to wait for the user to speak first:

Custom Agent Function

For advanced use cases, you can build agents from scratch as functions:

Custom Agent Class

Or as classes with state:
Most developers can use LlmAgent with tools rather than building custom agents from scratch! Custom agents are powerful when you need full control over the event processing logic without LLM reasoning.