AI-103 Part 4: Text Analysis Solutions

Part 4 of 5 — 10–15% of the AI-103 exam. It covers Azure AI Language Service (sentiment, NER, PII, key phrases, language detection), Conversational Language Understanding (CLU), Custom NER, Azure AI Speech (STT/TTS, speaker diarization), and Azure AI Translator.

Text and Speech Services Landscape

Azure AI Language Service

The Azure AI Language service is a managed NLP service that provides pre-trained analysis capabilities accessible through the TextAnalyticsClient in the azure-ai-textanalytics SDK. Each capability is invoked as a separate method call on the same client, and you can batch multiple documents in a single request.

Built-in Language Capabilities

Feature SDK method Output Typical use case
Sentiment Analysis analyze_sentiment() positive/neutral/negative + confidence scores at document and sentence level Product reviews, support ticket triage
Named Entity Recognition recognize_entities() Entity text, category (Person, Location, Organization, Date, Quantity, URL) Metadata extraction, document tagging
Key Phrase Extraction extract_key_phrases() List of significant noun phrases Topic summarization, search indexing
PII Detection recognize_pii_entities() PII entities detected; redacted text with *** masking GDPR compliance, log sanitization
Linked Entities recognize_linked_entities() Entity + Wikipedia URL + confidence Knowledge graph enrichment
Language Detection detect_language() ISO 639-1 language code + confidence Route multilingual content to the right pipeline
Text Summarization begin_abstract_summary() / begin_extract_summary() Abstractive: generated summary; Extractive: selected key sentences with ranking scores Document summaries, news digests, meeting notes
Question Answering Custom Question Answering (Language Studio project) Answer + confidence score from a curated Q&A knowledge base FAQ bots, support knowledge bases

Python Example: Sentiment, Key Phrases, and Entities

from azure.ai.textanalytics import TextAnalyticsClient
from azure.core.credentials import AzureKeyCredential

client = TextAnalyticsClient(
    endpoint="https://my-language.cognitiveservices.azure.com/",
    credential=AzureKeyCredential("YOUR_KEY")
)

documents = [
    "The Azure AI Language service is incredibly powerful and easy to use.",
    "The documentation was confusing and the setup took too long.",
]

# Sentiment — document-level AND sentence-level scores
sentiment_results = client.analyze_sentiment(documents, show_opinion_mining=True)
for doc in sentiment_results:
    print(f"Document sentiment: {doc.sentiment}")
    print(f"  Positive: {doc.confidence_scores.positive:.2f}, "
          f"Neutral: {doc.confidence_scores.neutral:.2f}, "
          f"Negative: {doc.confidence_scores.negative:.2f}")
    for sentence in doc.sentences:
        print(f"  Sentence '{sentence.text}' → {sentence.sentiment}")

# Key phrases
kp_results = client.extract_key_phrases(documents)
for doc in kp_results:
    print(f"Key phrases: {', '.join(doc.key_phrases)}")

# Named Entity Recognition
ner_results = client.recognize_entities(
    ["Satya Nadella announced Azure AI at Microsoft Build in Seattle."]
)
for doc in ner_results:
    for entity in doc.entities:
        print(f"  {entity.text} ({entity.category}, confidence: {entity.confidence_score:.2f})")

# PII detection — also returns redacted text
pii_results = client.recognize_pii_entities(
    ["Call John Smith at 425-555-1234 or john@example.com"]
)
for doc in pii_results:
    print(f"  Redacted: {doc.redacted_text}")
    for entity in doc.entities:
        print(f"    PII: {entity.text} ({entity.category})")

Custom NLP — CLU and Custom NER

Conversational Language Understanding (CLU)

CLU is the successor to LUIS (Language Understanding Intelligent Service). It classifies user utterances into intents and extracts entities, making it the foundation for chatbots and voice assistants. CLU projects are created and trained in Language Studio. The model learns from labeled examples that pair utterances with their intent and entity annotations.

Intents represent the user's goal — what they want to do. For example: BookFlight, GetWeather, CancelOrder. Entities are the specific values extracted from the utterance that parameterize the action — for example the destination city and departure date in a BookFlight intent.

CLU differs from the built-in NER feature: NER identifies and categorizes named entities (persons, locations, organizations) without any notion of user intent. CLU is specifically for intent classification plus entity extraction in a conversational context, where you define your own set of intents and entity types for your domain.

from azure.ai.language.conversations import ConversationAnalysisClient
from azure.core.credentials import AzureKeyCredential

client = ConversationAnalysisClient(
    endpoint="https://my-language.cognitiveservices.azure.com/",
    credential=AzureKeyCredential("YOUR_KEY")
)

result = client.analyze_conversation(
    task={
        "kind": "Conversation",
        "analysisInput": {
            "conversationItem": {
                "text": "Book a flight to Helsinki next Friday",
                "id": "1",
                "participantId": "user1"
            }
        },
        "parameters": {
            "projectName": "travel-assistant",
            "deploymentName": "production",
            "verbose": True
        }
    }
)

prediction = result["result"]["prediction"]
print(f"Top intent: {prediction['topIntent']}")
print(f"Confidence: {prediction['intents'][0]['confidence']:.2f}")
for entity in prediction.get("entities", []):
    print(f"  Entity: {entity['text']} → {entity['category']}")

Custom NER

Custom NER lets you train a model to extract domain-specific entities that the built-in NER does not cover — for example, extracting legal clause references from contracts, or extracting drug names and dosages from medical notes. The workflow is the same as CLU: label your training documents in Language Studio, train the model, evaluate F1 score per entity type, then deploy and call via the same Conversations API endpoint.

Microsoft recommends a minimum of 10 labeled examples per entity type for training, though more is always better. After training you review precision, recall, and F1 score per entity type and add more examples where the model is underperforming.

Exam tip: CLU replaces LUIS. LUIS was retired on March 31, 2026 (the LUIS portal became unavailable on October 31, 2025). If an exam scenario mentions LUIS, it is referring to the legacy service — the correct modern answer is CLU. Both CLU and Custom NER are trained through Language Studio and deployed to the same Azure AI Language endpoint.

Azure AI Speech

The Azure AI Speech service handles all audio-to-text and text-to-audio scenarios. It uses the azure-cognitiveservices-speech SDK (also called the Speech SDK). Unlike the Language service's REST-friendly batch model, the Speech SDK is typically used for real-time streaming scenarios where you need continuous recognition or low-latency synthesis.

Speech-to-Text (STT)

There are two recognition modes. Single-utterance recognition (recognize_once_async()) listens until a pause is detected and returns one result — suitable for command-and-control scenarios. Continuous recognition streams audio and fires callbacks for each recognized segment — suitable for transcribing meetings, interviews, or phone calls.

import azure.cognitiveservices.speech as speechsdk

speech_config = speechsdk.SpeechConfig(
    subscription="YOUR_KEY",
    region="eastus"
)
speech_config.speech_recognition_language = "en-US"

# Single utterance from microphone
audio_config = speechsdk.audio.AudioConfig(use_default_microphone=True)
recognizer = speechsdk.SpeechRecognizer(
    speech_config=speech_config,
    audio_config=audio_config
)

result = recognizer.recognize_once_async().get()

if result.reason == speechsdk.ResultReason.RecognizedSpeech:
    print(f"Recognized: {result.text}")
elif result.reason == speechsdk.ResultReason.NoMatch:
    print("No speech could be recognized.")
elif result.reason == speechsdk.ResultReason.Canceled:
    cancellation = result.cancellation_details
    print(f"Canceled: {cancellation.reason}, {cancellation.error_details}")

# From audio file
file_config = speechsdk.audio.AudioConfig(filename="recording.wav")
file_recognizer = speechsdk.SpeechRecognizer(
    speech_config=speech_config,
    audio_config=file_config
)
result2 = file_recognizer.recognize_once_async().get()
print(result2.text)

Text-to-Speech (TTS)

TTS converts text to spoken audio using neural voices. Azure has over 400 neural voices across 140+ languages. You can use SSML (Speech Synthesis Markup Language) to control prosody — pauses, rate, pitch, emphasis, and voice style (cheerful, sad, newscast, etc.). Custom Neural Voice lets you train a voice model on recordings of a specific speaker, subject to access approval.

import azure.cognitiveservices.speech as speechsdk

speech_config = speechsdk.SpeechConfig(subscription="YOUR_KEY", region="eastus")
speech_config.speech_synthesis_voice_name = "en-US-JennyNeural"

# Synthesize to speaker
synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config)
result = synthesizer.speak_text_async("Hello from Azure AI Speech!").get()

# SSML for prosody control
ssml = """

  
    
      Welcome to the Azure AI certification study guide.
    
  

"""
result2 = synthesizer.speak_ssml_async(ssml).get()

Speaker Diarization

Speaker diarization identifies which speaker is talking at each point in a multi-speaker recording. It is configured through the SpeakerDiarizationConfig on the AutoDetectSourceLanguageConfig or through the ConversationTranscriber. The result includes a speaker ID for each recognized segment. Note that diarization is only available through the Speech SDK — the REST batch transcription API also supports it for audio files.

Speech Translation

The Speech translation feature converts spoken audio directly to text in a different language in real time. It uses a TranslationRecognizer configured with source language and one or more target translation languages. This is distinct from using STT followed by Translator — the combined speech translation is optimized for conversational latency.

Exam tip: Speaker diarization requires the Speech SDK — it is not available through the simple REST API. For batch audio file transcription with diarization, use the batch transcription REST API with diarization settings in the JSON body.

Azure AI Translator

Azure AI Translator is a separate cognitive service for text and document translation. It supports over 100 languages and handles auto-detection of the source language when you omit it from the request. The REST API is straightforward and does not require an SDK, though the azure-ai-translation-text package is available.

Translator Capabilities

  • Text translation: Translate text strings in a single request; supports multiple target languages in one call
  • Document translation: Translate entire documents (Word, PDF, HTML, etc.) while preserving the original formatting; runs as an asynchronous batch job
  • Auto-language detection: If you omit the from parameter, the service detects the source language automatically
  • Transliteration: Convert text between scripts without translating the meaning — for example, convert Arabic text to Latin characters (romanization)
  • Custom Translator: Train a custom translation model on domain-specific parallel corpus (e.g., medical or legal texts) to improve accuracy for specialized vocabulary
import requests, uuid

key = "YOUR_TRANSLATOR_KEY"
endpoint = "https://api.cognitive.microsofttranslator.com"
location = "eastus"

headers = {
    "Ocp-Apim-Subscription-Key": key,
    "Ocp-Apim-Subscription-Region": location,
    "Content-type": "application/json",
    "X-ClientTraceId": str(uuid.uuid4())
}

# Translate to multiple target languages in one call
body = [{"text": "Hello, how are you today?"}]
params = {
    "api-version": "3.0",
    # "from": "en",  # omit to use auto-detection
    "to": ["fi", "de", "ja"]
}

response = requests.post(
    f"{endpoint}/translate",
    params=params,
    headers=headers,
    json=body
)
translations = response.json()

for translation in translations[0]["translations"]:
    print(f"{translation['to']}: {translation['text']}")

# Transliteration (Arabic → Latin)
body2 = [{"text": "مرحبا"}]
params2 = {
    "api-version": "3.0",
    "language": "ar",
    "fromScript": "Arab",
    "toScript": "Latn"
}
response2 = requests.post(f"{endpoint}/transliterate", params=params2, headers=headers, json=body2)
print(response2.json()[0]["text"])  # → "mrhba"

Exam tip: When you know the source language, specify it in the from parameter rather than relying on auto-detection. Auto-detection adds a small latency and costs a minor additional detection call. For high-volume production workloads, always specify the source language explicitly.

LLM-Powered Text Analysis

The updated AI-103 study guide explicitly covers using generative AI for text analysis tasks — not just the classical Azure AI Language endpoints. For many scenarios, prompting GPT-4o or Phi-4 with structured output instructions is more flexible than training a custom NER or CLU model, especially for domain-specific or low-volume tasks.

Structured Entity Extraction with JSON Mode

Use response_format={"type": "json_object"} with a clear schema in the system prompt to extract structured entities from text without a custom NER model. This is practical for domain-specific fields — contract clause types, medical terminology, financial line items — where the Azure AI Language prebuilt models do not have sufficient coverage.

from openai import AzureOpenAI

client = AzureOpenAI(
    azure_endpoint="https://<resource>.openai.azure.com/",
    api_key="<key>",
    api_version="2024-08-01-preview"
)

system_prompt = """Extract the following fields from the contract clause and return as JSON:
{
  "clause_type": string,
  "parties": [string],
  "effective_date": string or null,
  "obligation": string
}"""

response = client.chat.completions.create(
    model="gpt-4o",
    response_format={"type": "json_object"},
    messages=[
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": clause_text}
    ]
)
import json
extracted = json.loads(response.choices[0].message.content)
print(extracted)

When to Use Azure AI Language vs. LLM Prompting

Factor Azure AI Language LLM Prompting (GPT-4o/Phi-4)
Cost per call Low (fixed per text record) Higher (token-based)
Latency Low (dedicated endpoint) Higher (LLM generation time)
Domain coverage General + custom training required for specialist terms Broad — handles domain-specific fields via prompt description
Output predictability Deterministic structured response Variable unless JSON mode is enforced
Best for High-volume, standard NLP tasks (sentiment, generic NER, PII) Low-volume, complex, or novel extraction tasks

Speech as an Agent Modality

The updated study guide covers integrating Speech into agentic pipelines — not just standalone STT/TTS. When an agent interacts with a user via voice, the speech pipeline feeds directly into the agent's reasoning loop.

STT → Agent → TTS Pipeline

import azure.cognitiveservices.speech as speechsdk

speech_config = speechsdk.SpeechConfig(
    subscription="<key>",
    region="eastus"
)
speech_config.speech_synthesis_voice_name = "en-US-AvaMultilingualNeural"

# Step 1: Transcribe user speech
recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config)
result = recognizer.recognize_once()
user_text = result.text  # "What are today's sales figures?"

# Step 2: Pass to agent (Azure AI Agent Service or custom)
agent_response = agent.run(user_text)  # returns text

# Step 3: Synthesise response as speech
synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config)
synthesizer.speak_text_async(agent_response).get()

Batch Transcription for Agent Inputs

For agents that process historical call recordings — customer service QA, compliance review — use the batch transcription REST API rather than the real-time Speech SDK. Batch transcription submits audio files stored in Azure Blob Storage and returns transcript files asynchronously. Enable speaker diarization in the batch job to tag each segment with a speaker label, then feed the labelled transcript as structured context to the LLM.

Exam note: Real-time streaming STT uses the Speech SDK's SpeechRecognizer. Batch transcription for stored audio files uses the REST API endpoint /speechtotext/v3.1/transcriptions. Speaker diarization is configured differently for each: it's a property in the SDK's SpeechConfig vs. a diarization field in the batch job JSON body.

Exam Tips & Key Takeaways

  • CLU replaces LUIS. Any exam question referencing LUIS is about a deprecated service. The current service for intent classification and entity extraction in conversational scenarios is Conversational Language Understanding (CLU), accessed through the Azure AI Language endpoint.
  • PII detection has two modes. recognize_pii_entities() returns both the detected PII entities (for auditing) and redacted_text (with PII replaced by asterisks or category labels). Choose based on whether you need to inspect the PII or just remove it.
  • Speaker diarization requires the Speech SDK. It is not available through the simple one-shot REST API. Use the Speech SDK with a ConversationTranscriber, or the batch transcription REST API for file-based scenarios.
  • Custom NER: minimum 10 examples per entity type. Below this threshold the model training may succeed but precision will be unreliable. Aim for 50+ examples per entity type for production quality.
  • Translator auto-detect adds cost and latency. Specify the source language when known to reduce both. For multi-language input where you genuinely do not know the language, combine the Language service's detect_language() with Translator.
  • Batch document translation is asynchronous. Unlike text translation which is synchronous, document translation submits a job and you poll for completion — the output is written to an Azure Blob Storage container you specify in the request.

Further Learning – Microsoft Learn