Voice interfaces are becoming an important part of modern AI applications. Instead of typing a question and waiting for a text response, users can speak naturally and receive a spoken response in real time.
A traditional voice workflow often looks like this:
User Speech
|
v
Speech-to-Text
|
v
LLM
|
v
Text Response
|
v
Text-to-Speech
|
v
Audio Response
This approach works, but every stage can introduce latency.
A real-time voice agent is designed around a more continuous interaction:
User Voice
|
v
Real-Time Voice Session
|
+--> Speech Understanding
+--> AI Reasoning
+--> Tool Calling
+--> Audio Generation
|
v
Assistant Voice
Azure Voice Live is designed for building real-time voice-agent experiences where speech, conversational AI, and audio interaction can be coordinated through a live session.
For .NET developers, the interesting engineering problem is not simply converting speech into text. It is building a voice interaction that feels responsive while still handling authentication, conversation state, interruption, tool execution, errors, and observability correctly.
This article explains the architecture and implementation considerations for building real-time voice agents with .NET and Azure Voice Live.
Introduction
A voice agent has a very different performance profile from a conventional chat application.
With text chat, a few seconds of latency may still be acceptable.
With voice, users expect the system to begin responding quickly and naturally.
Consider this interaction:
User:
"What's the status of my order?"
|
v
Voice Agent
|
+--> Understand speech
|
+--> Identify customer
|
+--> Call order API
|
+--> Generate response
|
v
"Your order is out for delivery."
The system must coordinate multiple operations while maintaining the conversational experience.
A useful voice-agent architecture separates the system into several logical components:
Client
|
v
Voice Session
|
+-------------+-------------+
| | |
v v v
Audio Conversation Tools
Input State / APIs
| | |
+-------------+-------------+
|
v
AI Model
|
v
Audio Output
The goal is to make these components work as one continuous interaction.
What Is Azure Voice Live?
Azure Voice Live provides a real-time interface for building voice-enabled AI applications.
Instead of treating speech recognition, language generation, and speech synthesis as completely separate application requests, a live voice session can coordinate the interaction more directly.
The conceptual flow is:
Microphone
|
v
Live Voice Session
|
+--> Input Audio
|
+--> AI Processing
|
+--> Tool Calls
|
+--> Output Audio
|
v
Speaker
This architecture is particularly useful for:
Customer-service agents
Voice assistants
Appointment systems
Enterprise copilots
Interactive support systems
Voice-enabled applications
Agentic workflows
The exact service configuration depends on the model, deployment, authentication mechanism, and SDK version being used.
Why Real-Time Voice Is Different
A normal AI request is usually request-response:
Request
|
v
Wait
|
v
Response
Voice interaction is continuous:
Speech
|
+--> Partial Input
|
+--> User Pause
|
+--> Agent Response
|
+--> User Interrupts
|
+--> Agent Stops
|
+--> New Response
This creates additional requirements.
A voice agent needs to understand:
When the user starts speaking
When the user stops speaking
Whether the user interrupted the assistant
When to begin generating a response
When to stop generating audio
How to maintain conversation state
How to handle tool execution while preserving natural interaction
High-Level .NET Architecture
A .NET application can act as the orchestration layer around the live voice connection.
A typical architecture might look like:
Browser / Mobile Client
|
v
Audio Stream
|
v
ASP.NET Core Backend
|
+------------+------------+
| |
v v
Voice Live Session Application APIs
|
+--> AI Model
|
+--> Tools
|
+--> Conversation
For applications that need very low latency, audio may be exchanged through a persistent real-time connection rather than traditional HTTP request-response calls.
Project Structure
A production .NET application can separate responsibilities:
VoiceAgent/
|
+-- Program.cs
+-- Voice/
| +-- VoiceSessionManager.cs
| +-- VoiceEventHandler.cs
| +-- AudioProcessor.cs
|
+-- Agents/
| +-- VoiceAgent.cs
| +-- AgentInstructions.cs
|
+-- Tools/
| +-- OrderTool.cs
| +-- CustomerTool.cs
|
+-- Models/
| +-- VoiceSessionOptions.cs
| +-- ConversationState.cs
|
+-- Infrastructure/
+-- Telemetry.cs
+-- Authentication.cs
This separation becomes useful as the voice agent grows beyond a prototype.
Configure the Application
Store configuration outside application code.
{
"VoiceAgent": {
"Endpoint": "...",
"Deployment": "...",
"Model": "..."
}
}
Bind configuration through options:
public sealed class VoiceAgentOptions
{
public string Endpoint { get; set; } = string.Empty;
public string Deployment { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
}
Then register the configuration:
builder.Services.Configure<VoiceAgentOptions>(
builder.Configuration.GetSection("VoiceAgent"));
In production, credentials should not be stored directly in source control or committed configuration files.
Use an appropriate secret-management mechanism and managed identity where supported.
Establishing a Voice Session
The central abstraction is a voice session.
Conceptually:
public interface IVoiceSession
{
Task ConnectAsync(
CancellationToken cancellationToken);
Task SendAudioAsync(
ReadOnlyMemory<byte> audio,
CancellationToken cancellationToken);
Task CloseAsync(
CancellationToken cancellationToken);
}
The exact implementation depends on the Voice Live SDK and transport being used.
The application should keep session management separate from business logic.
Session Lifecycle
A useful session lifecycle is:
Created
|
v
Connecting
|
v
Connected
|
v
Listening
|
v
Processing
|
v
Speaking
|
+---- User Interrupts
| |
| v
+------ Listening
|
v
Closing
|
v
Closed
Representing these states explicitly can make error handling much easier.
public enum VoiceSessionState
{
Created,
Connecting,
Connected,
Listening,
Processing,
Speaking,
Closing,
Closed,
Failed
}
Audio Input
The client captures microphone audio and sends it through the live session.
The backend should avoid unnecessary transformations.
Conceptually:
Microphone
|
v
Audio Frames
|
v
Transport
|
v
Voice Session
The smaller and more predictable the audio pipeline, the easier it is to control latency.
Audio format compatibility is important.
The client, transport, and voice service must agree on the expected format, encoding, sampling characteristics, and framing.
Audio Output
The response follows the opposite direction:
AI Response
|
v
Generated Audio
|
v
Audio Frames
|
v
Client
|
v
Speaker
The application should stream output rather than waiting for the complete response when the platform supports incremental audio delivery.
That allows the user to hear the response earlier.
Streaming Matters
Consider two implementations.
Buffered
Generate Entire Response
|
v
Send Complete Audio
|
v
User Hears Response
Streaming
Generate First Audio
|
v
Play Immediately
|
+--> Generate More
|
+--> Play More
Streaming generally provides a better conversational experience because perceived latency is lower.
Measure Time to First Audio
For voice systems, total response time is not the only useful metric.
Measure:
User Stops Speaking
|
v
Time to First Audio
|
v
Complete Response
The first metric is often more important to perceived responsiveness.
Track:
Time to speech detection
Time to response generation
Time to first audio
Total response duration
Tool execution duration
A useful telemetry model is:
public sealed record VoiceTurnMetrics(
TimeSpan InputDuration,
TimeSpan TimeToFirstAudio,
TimeSpan TotalResponseTime,
TimeSpan ToolExecutionTime);
Voice Activity Detection
A voice agent needs to determine when the user is speaking and when the turn is complete.
The conceptual flow is:
Audio Input
|
v
Voice Activity Detection
|
+--> User Speaking
|
+--> Silence
|
v
Turn Detection
Incorrect turn detection can make the agent feel unnatural.
If the system waits too long, users experience awkward pauses.
If it ends the turn too early, the agent may respond before the user finishes speaking.
User Interruption
One of the most important voice-agent features is interruption, often called barge-in.
Example:
Assistant:
"Your order is currently..."
User:
"Wait, which order?"
Assistant:
[Stops speaking]
User:
"Order number 123."
The application should be able to stop or cancel the current response when the user begins a new turn.
Conceptually:
Assistant Audio
|
v
User Starts Speaking
|
v
Cancel Output
|
v
Process New Input
This is much more difficult to achieve with a simple buffered request-response architecture.
Conversation State
A voice agent needs conversation state just like a text agent.
For example:
public sealed class ConversationState
{
public string? CustomerId { get; set; }
public string? CurrentOrderId { get; set; }
public List<string> RecentTopics { get; } = [];
}
Do not put every piece of application state into the model conversation.
Separate:
Conversation Context
from:
Application State
For example, customer authorization should come from trusted application state rather than from the model deciding which customer identity to use.
Tool Calling
Voice agents become significantly more useful when they can call application APIs.
Consider:
User:
"Where is my order?"
|
v
Voice Agent
|
v
get_order_status
|
v
Order API
|
v
Order Status
|
v
Voice Response
A tool should have a clear contract.
public sealed record OrderStatusRequest(
string OrderId);
public sealed record OrderStatusResponse(
string Status,
DateTimeOffset? EstimatedDelivery);
The model should not directly access your database.
Instead:

Join the conversation! Your thoughts help the community grow.