AI-103: Speech Solutions — Voice Agents, Custom Speech and Speech Translation
Implementing speech solutions for AI-103 means giving apps and agents ears and a voice: converting speech to text and text to speech with Azure Speech in Foundry Tools, running spoken agent conversations through the Voice Live API, improving recognition with phrase lists and custom speech models, letting audio-capable models reason over audio directly, and translating speech with the Speech SDK or language models. The skill is choosing the right path for each requirement — live or recorded audio, recognition or output, a trained model or a runtime fix — and configuring it correctly.
On this page10 sections
- Which speech-to-text path fits a workload?
- How do you turn an agent's text replies into speech?
- How do you control how an agent pronounces names and acronyms?
- How does the Voice Live API make speech an agent modality?
- Phrase list or custom speech model?
- How do you train, test and use a custom speech model?
- Which voice gives an agent a brand or personal voice?
- How do models reason over audio directly, and how do you connect to them?
- How do you translate speech with the Speech SDK and Live Interpreter?
- How do language models translate speech?
- Choose between real-time recognition, fast transcription, batch transcription and LLM Speech by latency, volume, output form and custom-model hosting.
- Synthesize agent replies with the Speech SDK, control pace and pauses with SSML, pick telephony output formats, and fix pronunciation with SSML elements or a custom lexicon.
- Use the Voice Live API as an agent's speech modality, including connecting it to a Foundry Agent Service agent with Microsoft Entra ID.
- Decide between a phrase list and a custom speech model, pick training data by error type, test with WER, and call the model from the SDK, batch jobs and Voice Live.
- Select professional custom voice, personal voice or a prebuilt voice for an agent's output.
- Send audio to audio-capable models for multimodal reasoning and connect to the GPT Realtime API with the right transport and credential pattern.
- Translate speech with the Speech SDK, Live Interpreter, LLM Speech or a transcribe-then-translate pipeline.
Which speech-to-text path fits a workload?
Azure Speech in Foundry Tools offers four speech-to-text paths, and the right one follows from where the audio is and how fast the text is needed: real-time recognition with the Speech SDK for live audio, the fast transcription API for one file at a time with a synchronous answer, batch transcription for large sets of stored files, and LLM Speech (the enhanced mode of fast transcription) when a language model should shape the output.
| Path | How it works | Fits | Watch for |
|---|---|---|---|
Real-time (Speech SDK SpeechRecognizer) | Streams audio from a microphone, stream or file; recognize_once_async returns the first utterance (it stops at the first pause), continuous recognition keeps going until stopped | Live conversations, captions, voice commands, agents | A custom model needs a deployed custom endpoint (endpoint_id) |
| Fast transcription (REST) | One multipart request per file, synchronous, faster than real time | "Transcribe this recording now" with no streaming client | Returns display-form text only; per-file size and length limits; base models only (no custom speech model), though a phrase list is supported |
| Batch transcription (REST) | Asynchronous job over a list of file URLs or a whole storage container; you poll and collect results later | Thousands of recordings, nightly or overnight jobs | No synchronous answer; results include the lexical form; can reference a custom model without a hosted endpoint |
| LLM Speech (fast transcription enhanced mode) | Same synchronous request, with enhancedMode settings that add a task (transcribe or translate) and a prompt | Translation into a chosen language, prompted output style | Can't apply your trained custom speech model |
Two output forms matter. The display form has punctuation, capitalization and formatted numbers ("$25.50"); the lexical form is the raw recognized words ("twenty five dollars fifty"). Downstream tools that need lexical text rule out fast transcription. Cost and hosting also decide: a custom speech model must be deployed to a paid custom endpoint for real-time recognition and speech translation, while batch transcription references the model directly. Fast transcription and LLM Speech have no custom-model option at all: they run on base models, optionally helped by a phrase list. Batch transcription's language identification works only with base models; to pair language detection with a custom model, use real-time recognition (see the custom speech section).
For agents, real-time is the usual path for the conversation itself, while fast or batch transcription handles recordings the agent must analyse afterwards.
How do you turn an agent's text replies into speech?
Text to speech in the Speech SDK is a SpeechSynthesizer built from a SpeechConfig: you pick the voice with speech_synthesis_voice_name (for example en-US-AvaMultilingualNeural), then call speak_text_async for plain text or speak_ssml_async for SSML markup. Each config property has one job, and the near-twins are easy to mix up:
| Property | What it does |
|---|---|
speech_synthesis_voice_name | Selects a specific voice for synthesis |
speech_synthesis_language | Takes a locale only and falls back to that locale's default voice |
speech_recognition_language | Sets the locale for speech to text, not output |
endpoint_id | Points at a custom deployment (a custom speech model, or a custom voice) |
SSML controls delivery. Wrap text in a prosody element with a rate (and pitch or volume) to change pace, insert a break with a time such as 500ms for a pause at an exact point, and use mstts:express-as to apply a speaking style (cheerful, calm and so on) where the voice supports it. A style changes tone, not timing. SSML only works through speak_ssml_async; sending markup to speak_text_async makes the voice read the tags as text.
Output format must match the destination. set_speech_synthesis_output_format chooses the encoding. Riff* formats are WAV files with a header; Raw* formats have no header, which streaming and telephony gateways often require. Narrowband telephony runs at 8 kHz with G.711 companding, which comes in two variants: mu-law (North America and Japan, Raw8Khz8BitMonoMULaw) and A-law (Europe and most other regions, Raw8Khz8BitMonoALaw). Lowering the sample rate changes audio quality, not speaking speed.
Voices come as standard neural voices, higher-fidelity HD voices, and multilingual voices that can speak several languages; all are prebuilt and shared by every customer, so none of them is a unique brand voice.
How do you control how an agent pronounces names and acronyms?
Pronunciation of output speech is fixed on the text-to-speech side, with SSML elements for one-off cases and a custom lexicon for rules that should apply everywhere. Recognition-side tools (phrase lists, custom speech pronunciation data) only change what the service hears, so they never fix a voice that mispronounces a word.
| Tool | Effect | Scope |
|---|---|---|
<sub alias="World Wide Web Consortium">W3C</sub> | Speaks the alias instead of the written text | That one place in one SSML document |
<say-as interpret-as="…"> | Tells the voice how to read the content (date, characters, number, name) | One place; still reads the written text itself |
<phoneme alphabet="ipa" ph="…"> | Gives an exact phonetic pronunciation | One place |
| Custom lexicon (PLS file) | Maps graphemes to a phoneme or to an alias | Every occurrence in every reply that uses the lexicon |
A lexicon is an XML Pronunciation Lexicon Specification file, not SSML. Each lexeme holds one or more grapheme entries and either a phoneme (in an alphabet such as IPA) or an alias. An alias suits acronyms: a lexeme for SLA with the alias "service level agreement" makes every SLA spoken as the phrase. Matching is case-sensitive, so a lexeme for "Contoso" doesn't cover "CONTOSO"; add a grapheme or lexeme for each casing that appears. A lexicon covers one locale, is hosted at a URL (for example Azure Blob Storage) and is cached by the service, so edits take a short while to appear.
You reference the lexicon from SSML with a lexicon element. In the Voice Live API, the voice configuration accepts custom_lexicon_url, which applies the lexicon to every reply for standard and custom voices without editing prompts or SSML.
How does the Voice Live API make speech an agent modality?
The Voice Live API is a managed speech-to-speech interface: in one WebSocket session it combines speech recognition, a generative model and text to speech, so an agent can hold a spoken conversation without you orchestrating three services. It adds the conversational audio features a hand-built chain lacks:
- Noise suppression for busy environments and echo cancellation so the agent doesn't hear (and answer) its own voice on a speakerphone.
- Interruption detection and advanced end-of-turn detection, so the agent doesn't cut in when a caller pauses mid-sentence.
- Avatars for a talking visual front end, plus the choice of standard, HD, custom and personal voices.
- Natively supported models that are fully managed (nothing to deploy), or your own model deployment.
You configure the session with a session.update event: instructions, the voice object, input_audio_transcription (the recognition model, such as azure-speech or an OpenAI transcription model like gpt-4o-transcribe or whisper-1, plus phrase_list and custom_speech), and turn-detection, noise and echo settings.
Agent mode connects Voice Live to an existing Foundry Agent Service agent instead of a bare model. The connection names the agent (agent_name) and its Foundry project (project_name), optionally a version or a conversation to resume, and the agent brings its own instructions, model and tools, so nothing is copied into the session. Agent invocation requires Microsoft Entra ID authentication; API keys aren't supported for it.
| Option | What you build | Choose it when |
|---|---|---|
| Voice Live API | One managed session: STT + model + TTS with noise, echo, turn detection, avatars | Least engineering effort for a voice agent, including a Foundry agent |
| GPT Realtime API | A direct low-latency audio session with a gpt-realtime deployment you create | You want the speech-native model itself and manage the rest |
| Speech SDK chain | Your own code linking recognition, a model and synthesis | You need full control and accept building echo and turn handling yourself |
Phrase list or custom speech model?
A phrase list is the fast, no-training fix for a modest set of misrecognized words; a custom speech model is the trained fix for large vocabularies, acoustic conditions, or workloads where phrase lists don't apply.
| Phrase list | Custom speech model | |
|---|---|---|
| Setup | Supplied at run time: in the SDK, PhraseListGrammar.from_recognizer(recognizer) then addPhrase; in Voice Live, phrase_list under input_audio_transcription | Upload data, train, test, then deploy (or reference from batch) |
| Time to value | Immediate, same day | Hours to days, plus data collection |
| Size | Best for a small to moderate list; at a couple of thousand phrases, move to custom speech | Thousands of terms, whole domains |
| Fixes | Vocabulary only (biases recognition toward listed words) | Vocabulary, pronunciation, accents and noise (with audio data), display formatting |
| Where it works | Real-time, fast transcription, LLM Speech, and Voice Live with azure-speech | Real-time recognition and speech translation (via custom endpoint), batch (directly), Voice Live (custom_speech) |
| Where it doesn't | Batch transcription; Voice Live with whisper-1 or the gpt-4o-transcribe family | Fast transcription and LLM Speech (base models only); OpenAI transcription models |
The "where it doesn't" row explains a common symptom: a phrase list that changes nothing. In a batch job, tuning the list can't help; the answer is a custom model trained on text that uses the terms. In Voice Live, switching input_audio_transcription to azure-speech lets the phrase list take effect. Putting names in an agent's system prompt doesn't help either, because the model sees the words only after they've already been transcribed.
How do you train, test and use a custom speech model?
A custom speech model is a base model adapted with your data; you choose the data type by the kind of error, measure it with word error rate (WER), and then make the app actually call it.
Choosing training data
| Error you see | Data to use |
|---|---|
| Domain words missed, audio is clean (drug names, product names) | Plain text sentences that use the terms in context (or structured text for patterned utterances) |
| Ordinary words missed because of noise, accents or channel | Audio recorded in those conditions with human-labeled transcripts |
| Words with unusual pronunciations recognized wrongly | Pronunciation data (spoken form for a written term) |
| Display text formatted the wrong way (a product name shown as "contoso cloud" instead of "ContosoCloud") | Custom display text formatting rules |
Text data is cheap to collect and targets vocabulary; audio data is the only way to improve acoustic robustness. Pronunciation data here affects recognition, not how a voice speaks.
Testing with WER
WER compares what a model recognizes from audio with a human-labeled transcript of the same audio, so an accuracy test needs audio paired with human-labeled transcripts. Audio alone supports only listening to results, and text data can't score anything. Measure the base model first; if its WER is acceptable, you may not need a custom model at all, and the same test set compares later models.
Using the model
- Real-time recognition and speech translation (Speech SDK): deploy the model to a custom endpoint, then set
endpoint_idon theSpeechConfig(orSpeechTranslationConfig) to that endpoint's ID. Withoutendpoint_id, the service uses the base model for the locale, however good the trained model is. - Fast transcription and LLM Speech: there is no custom-model or custom-endpoint option; they use base models, and a phrase list is the runtime fix for vocabulary.
- Batch transcription: reference the model in the job; no endpoint is needed.
- Language detection with a custom model: build
AutoDetectSourceLanguageConfigfromSourceLanguageConfigobjects, each with a locale and optionally a custom endpoint ID. The custom model then applies only when its language is detected, and one recognizer handles the call. A globalendpoint_idwould force the custom model on every caller. - Voice Live: set
custom_speechunderinput_audio_transcriptionto a dictionary keyed by locale (for examplezh-CN) whose values are custom model IDs; other locales stay on the base model. It works only with theazure-speechtranscription model, and the model must be available on the same Foundry resource that calls Voice Live, so a model trained on another resource has to be copied over first.
Which voice gives an agent a brand or personal voice?
A unique brand voice comes from professional custom voice; a voice that copies an individual end user comes from personal voice; everything else is a prebuilt standard or HD voice.
| Professional custom voice | Personal voice | Prebuilt (standard / HD) | |
|---|---|---|---|
| Purpose | One unique brand or character voice | Replicate a specific user's own voice | General-purpose voices shared by all customers |
| Data | Studio-quality recordings from a voice talent | A short speech sample plus the speaker's recorded consent statement | None |
| Access | Limited access, application required | Limited access, approved use cases only | Open |
Voice Live voice type | azure-custom with name and the deployment's endpoint_id | azure-personal with name and a base model | azure-standard with name |
Personal voice fits scenarios such as voice banking for patients who will lose their speech, or speaking translated output in each speaker's own voice. A custom speech model is not a voice at all: it improves recognition. In Voice Live, the custom voice deployment must be on the resource the session calls, and only the voice type, name and (for a custom voice) endpoint_id decide which voice speaks; other voice settings change how that voice speaks, not who it is.
How do models reason over audio directly, and how do you connect to them?
Multimodal reasoning from audio means sending the audio itself to an audio-capable model rather than a transcript, so the model can use tone, emotion and non-speech sounds (sirens, alarms, laughter) that any text conversion throws away. Sentiment on a transcript, diarization or a transcription prompt still work on words only.
Audio in chat completions
Audio-capable chat models such as gpt-4o-audio-preview and gpt-4o-mini-audio-preview accept an audio content part next to text in a user message:
{"type": "input_audio",
"input_audio": {"data": b64_audio, "format": "wav"}}
The data is base64-encoded audio, not a file path or URL, and it is not an image_url part or base64 pasted into text. modalities=["text"] asks for a text reply; for a spoken reply as well, set modalities=["text", "audio"] and pass audio={"voice": "alloy", "format": "wav"}. This is request/response: good for analysing a recording, not for a live conversation.
The GPT Realtime API
For live, low-latency speech with a speech-native model, the GPT Realtime API streams audio both ways with a gpt-realtime deployment. The transport follows from the client:
| Transport | Use it for |
|---|---|
| WebRTC | Browser and mobile clients; lowest latency for audio |
| WebSockets | Server-to-server streaming (higher latency for client audio) |
| SIP | Telephony |
A browser must never hold a long-lived key or Entra ID token. The standard WebRTC pattern: your backend, using its own key or Entra ID credential, mints a short-lived ephemeral token from /openai/v1/realtime/client_secrets; the browser uses it to post its SDP offer to /openai/v1/realtime/calls. For stricter rules, the backend can also proxy the SDP negotiation, so no credential of any kind reaches the browser. The negotiation returns a Location header for the call, which the backend can use to open an observer WebSocket to record or control the session's events.
How do you translate speech with the Speech SDK and Live Interpreter?
Speech translation in the Speech SDK recognizes speech in one language and returns text (and optionally audio) in one or more others, using a SpeechTranslationConfig and a TranslationRecognizer.
- The source is a language-locale string:
speech_recognition_language = "ja-JP". - Each target is a language code:
add_target_language("en"),add_target_language("es"). One recognition pass then fillsresult.translations, keyed by those codes. - Spoken output through
voice_nameand thesynthesizingevent works only for a single target language, and the voice should match that language.
To speak several languages from one recognition pass, add every target, then send each text translation to a SpeechSynthesizer with a voice in that language. Running one recognizer per language recognizes the same audio repeatedly.
Live Interpreter, part of real-time speech translation, handles conversations where nobody can choose the language in advance: it continuously identifies the spoken language with open-range detection (no candidate list; in Python an empty AutoDetectSourceLanguageConfig), follows speakers who switch language mid-session without restarting, and can speak the translation in the speaker's own personal voice (limited access). Standard translation with continuous language identification still needs candidate locales chosen up front.
How do language models translate speech?
Language models translate speech in three ways, and the choice depends on the target language and whether text must be reviewed in between.
| Approach | Output | Fits | Limits |
|---|---|---|---|
LLM Speech (fast transcription with enhancedMode: task translate, a targetLanguage such as ko or de, optional prompt) | Translated text from one synchronous call per file | Audio straight to a chosen language, with a prompt steering style such as brand-name rendering | Audio in, text out in one step; prompts unrelated to speech output are disregarded |
| OpenAI transcription models (whisper's audio translations operation) | English text | Any supported language into English | Translates only into English, whatever the prompt asks; per-file size cap |
| Transcribe, then translate with a GPT model | Text in any language, from approved source text | A review step before translation, glossaries, re-translating without reprocessing audio | Two steps; you manage prompts and glossary |
The two-step pattern wins whenever the source transcript is a durable artifact: compliance can approve it, and when a glossary changes you re-run only the text translation. Direct audio-to-text translation (LLM Speech, the Speech SDK translation recognizer, whisper) reprocesses the audio every time and leaves no approved transcript in between.
Text analysis of the resulting transcript (entities, summaries, sentiment, structured output) belongs to the language-model text analysis topic of this domain.
Tip. Expect short scenarios — a contact center, a kiosk, a dictation app or a voice agent — with one deciding requirement such as least engineering effort, no training data, no hosted endpoint, one synchronous call, recognize the audio only once, no credential in the browser, or output in the speaker's own voice, followed by "what should you use or configure?". Many items hinge on whether a problem sits on the recognition side or the output side, and on which feature works on which path (phrase lists, custom models, language detection). Short Python snippets ask which SpeechConfig property, SSML element, content part or call arguments complete the code, and several items ask you to select TWO actions that must both be done.
- Live audio → real-time Speech SDK; one file, synchronous → fast transcription (display form only); many stored files → batch transcription (lexical form available, custom model without an endpoint).
- Select a voice with speech_synthesis_voice_name; SSML (prosody rate, break time, styles) needs speak_ssml_async; Raw formats have no header, and 8 kHz mu-law is North American/Japanese telephony while A-law is European.
- Output pronunciation is fixed on the TTS side: sub for a one-off spoken alias, a case-sensitive PLS custom lexicon (phoneme or alias) for every reply, custom_lexicon_url in Voice Live.
- Voice Live is the managed speech-to-speech layer: noise suppression, echo cancellation, end-of-turn detection, avatars; agent mode takes agent_name + project_name and requires Entra ID.
- Phrase lists are runtime, no-training boosts for modest lists; they don't work in batch transcription or with whisper/gpt-4o-transcribe in Voice Live — use custom speech or azure-speech instead.
- Custom speech data follows the error: text for vocabulary, audio + human-labeled transcripts for noise and accents (and for WER testing), plus pronunciation and display formatting rules.
- A custom speech model needs a custom endpoint and endpoint_id for real-time recognition and speech translation, batch references it directly, and fast transcription/LLM Speech take none; per-language custom models go in SourceLanguageConfig; Voice Live custom_speech maps locale to model ID, azure-speech only, same Foundry resource.
- Professional custom voice = one brand voice from studio data (azure-custom + endpoint_id); personal voice = a user's own voice from a short sample with consent (azure-personal).
- Audio reasoning needs the audio itself (input_audio parts to gpt-4o-audio-preview); Realtime API uses WebRTC for clients with a backend-minted ephemeral token, WebSockets server to server.
- Speech SDK translation: locale source, language-code targets, event synthesis for one target only; Live Interpreter adds open-range detection and personal voice; whisper translates only into English.
Frequently asked questions
When should I use fast transcription instead of batch transcription?
Use fast transcription when you need the transcript of an individual file back in a single synchronous REST call within seconds, and display-form text is enough. Use batch transcription when you have large numbers of stored recordings that can be processed asynchronously as one job, when you need the lexical form of the transcript, or when you want to use a custom speech model without paying for a hosted custom endpoint.
Why doesn't my phrase list improve recognition?
Phrase lists apply only to some recognition paths. They work with real-time recognition, fast transcription, LLM Speech and the Voice Live API when the transcription model is azure-speech. They aren't available in batch transcription and have no effect in Voice Live with whisper-1 or the gpt-4o-transcribe models. For batch workloads, train a custom speech model on text that uses the terms; in Voice Live, switch input_audio_transcription to azure-speech.
How do I make a Speech SDK app use my custom speech model?
Deploy the trained model to a custom endpoint, then set endpoint_id on the SpeechConfig to that endpoint's ID. Without endpoint_id the service uses the base model for the configured locale. Batch transcription is the exception: a batch job can reference the custom model directly without a deployed endpoint. Fast transcription and LLM Speech can't use a custom speech model; they run on base models, with a phrase list as the runtime vocabulary fix.
How do I connect a Foundry Agent Service agent to the Voice Live API?
Open the Voice Live connection in agent mode with the agent's name and the name of its Foundry project, optionally a version or an existing conversation, and authenticate with a Microsoft Entra ID token credential. The agent supplies its own instructions, model and tools, so nothing needs copying into the session. Key-based authentication isn't supported for agent invocation.
What is the difference between a custom lexicon and custom speech pronunciation data?
A custom lexicon is a text-to-speech feature: a PLS file that tells a voice how to pronounce graphemes, using a phoneme or an alias, and it matches words case-sensitively. Custom speech pronunciation data is a speech-to-text feature that helps the recognizer understand terms with unusual pronunciations. If an agent mispronounces a name in its replies, fix it with a lexicon or SSML, not with recognition-side data.
Which voice option should I use for a unique brand voice?
Professional custom voice, trained on studio-quality recordings from a voice talent, creates a unique brand or character voice; it is a limited-access feature that requires an application. Personal voice instead replicates an individual user's own voice from a short sample with a recorded consent statement. Prebuilt standard and HD voices are shared by every customer, so they aren't unique.
How can a model hear tone and background sounds in a call recording?
Send the recording itself to an audio-capable model such as gpt-4o-audio-preview, as an input_audio content part with base64 data and a format, alongside the question. Converting the call to text first loses tone, emotion and non-speech sounds such as sirens, so transcript-based sentiment or diarization can't recover them.
Can whisper translate speech into languages other than English?
No. The audio translations operation of whisper translates speech from supported languages into English text only, and a prompt asking for another language doesn't change that. For another target language, use LLM Speech's translate task with a targetLanguage, Speech SDK speech translation, or transcribe the audio and translate the text with a GPT model.
Source
This lesson covers the "Implement text analysis solutions" domain of the official AI-103 exam guide. Vendors revise their guides — check the source for the current version.
- Microsoft AI-103 study guide — Microsoft Learn
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.