SaveMyCert
Log in
5 of 5 free questions left today·for 30 a day
Implement text analysis solutions

AI-103: Text Analysis, PII, Content Safety and Translation with Foundry

18 min readAI-103 · Implement text analysis solutionsUpdated

Language model text analysis is the work of pulling entities, topics, summaries, sentiment, safety and sensitive-data signals, and translations out of text, either with prompted Foundry models that return structured JSON or with Foundry Tools such as Azure Language, Azure AI Content Safety and Azure Translator. This lesson shows how to choose between a prebuilt tool, a custom model and a prompted model for each task, how to make generated JSON reliable with strict schemas, how to summarize documents larger than a context window, how to detect sentiment, tone, harmful content and PII, how Translator API version 2026-06-06 chooses between NMT, LLM and custom models, and how to customize model output for domain tasks such as compliance summarization.

What you’ll learn
  • Choose between prebuilt NER, custom NER and a prompted model with a strict JSON schema for entity extraction, based on categories, labelled data and the need for offsets.
  • Write strict structured-output schemas that use enums for fixed taxonomies and null unions for absent values, and diagnose strict-mode validation errors.
  • Summarize documents larger than the context window with chunked map-reduce and hierarchical reduction.
  • Configure sentiment and tone detection, Content Safety text analysis (severity levels, blocklists) and Azure Language PII detection and redaction.
  • Configure Azure Translator 2026-06-06 targets (deploymentName, tone, gender, allowFallback, textType) and choose between adaptive datasets, reference pairs and Custom Translator models.
  • Decide when an LLM-powered translation flow beats Translator, and customize model output for domain tasks with instructions, examples, schemas or fine-tuning.

Which tool should extract entities: prebuilt NER, custom NER or a prompted model?

Use prebuilt named entity recognition (NER) in Azure Language when the entities are standard types, custom NER when they are your own categories and you need offsets and a retrainable model, and a prompted language model with a strict schema when the categories are your own but you have no labelled data and need only the values. Prebuilt and custom NER are both core Azure Language features, which Microsoft recommends for new development.

ApproachCategoriesTraining dataCharacter offsetsBest when
Prebuilt NERPredefined (Person, Organization, DateTime, Quantity and others)NoneYesStandard entity types, least effort
Custom NER projectYour own labelled categoriesLabelled documentsYes, each mention with offset and lengthOwn categories, audit needs exact spans, the model must be retrained as categories grow
Prompted Foundry model + strict JSON schemaAnything you can describeNone (a few examples help)Not reliableOwn categories, no labels, a tight deadline, values only
Fine-tuned modelAnything in the training setA labelled training set and a training jobNot reliableRarely the first choice for extraction; a base model can already follow a schema

Two near-misses come up often. Custom text classification labels a whole document; it doesn't find mentions inside the text. Key phrase extraction returns the main phrases of the text with no categories and can't be trained on your labels. Neither is an entity extractor.

The deciding facts are usually three: are the categories standard or your own, does labelled data exist, and does a downstream system need exact character offsets? Generated JSON from a language model gives values, not dependable span positions, and a few-shot prompt is not a model a data team can retrain.

How do you get reliable structured JSON from a language model?

Use structured outputs: send a JSON schema with the request (in the Responses API, text.format with type set to json_schema and strict set to true) so decoding is constrained to the schema. Instructions in a prompt ask for a shape; a strict schema guarantees it.

Strict mode has rules that a schema must follow, or the request fails with a schema validation error:

  • Every property is listed in required, on every object, nested objects included. Removing a field from required does not make it optional; it makes the schema invalid.
  • additionalProperties is false on every object, including nested ones.
  • Optional values are null unions. Declare a field that may be absent as {"type": ["string", "null"]} and tell the model to return null when the document doesn't state it. Without a null option, the model has no valid way to say "missing" and tends to invent a plausible value.
schema = {
    "type": "object",
    "properties": {
        "invoice_number": {"type": "string"},
        "due_date": {"type": ["string", "null"]},
        "currency": {"type": "string", "enum": ["USD", "EUR", "GBP"]},
    },
    "required": ["invoice_number", "due_date", "currency"],
    "additionalProperties": False,
}

Match the fix to the failure:

SymptomFixWhat doesn't fix it
Labels outside a fixed list (topics, product names, taxonomy categories)An enum in a strict schemaListing the labels in the system message, temperature 0
Spelling variants of known namesAn enum of the canonical namesKey phrase extraction, which keeps the writer's spelling
A missing value replaced with a fabricated oneA null union plus an instruction to use nullA regex pattern, which shapes the invented value but doesn't stop it
Wrong handling of an edge case (for example, competitor products extracted as your own)Few-shot examples that show the edge caseHigher temperature or a larger output token limit
Output must match a schema but JSON mode is in useSwitch to json_schema with strictJSON mode (json_object), which guarantees valid JSON only, not the fields

Lowering temperature makes output more repeatable but never restricts it to allowed values; raising temperature adds randomness and makes consistency worse. Turning strict off makes an invalid schema accepted but gives up the adherence guarantee.

How do you summarize a document larger than the context window?

Split it, summarize each part, then combine the partial summaries: a map-reduce pattern. A model can only read what fits in its context window, so every part of the document must go through some request if the summary has to cover all of it.

  1. Map: split on natural boundaries (sections, agenda items, chapters) so each chunk fits the window. Keep each chunk's reference (section number, paragraph) with its summary so points stay traceable.
  2. Reduce: combine the chunk summaries into one summary in a final request.
  3. Reduce in levels when needed: if all the chunk summaries together overflow the window, combine them in groups, then combine the group summaries. This hierarchical reduce scales with document size, because the number of levels grows with the input while each request stays within the window.

The test for any proposed shortcut is simple: does every part of the input still reach a model request? Anything that discards, samples or skips input, however it is labelled, produces a summary of part of the document. Settings on the output side, such as the output token limit, control how long the response may be and have no effect on how much input fits. A phrase list is not a summary either: key phrase extraction (a legacy Azure Language feature) returns topics, not what was discussed or decided.

When do you use Azure Language sentiment analysis versus a language model?

Use Azure Language sentiment analysis for an existing app that already calls it; use a language model with a schema for new builds and for labels the service doesn't have, such as tone or sarcasm. Microsoft's Azure Language overview classifies sentiment analysis and opinion mining as legacy capabilities: supported for existing implementations, not recommended for new development.

What the service returns: sentence-level sentiment (positive, negative or neutral) and a document-level label that can also be mixed, each with confidence scores. With the Python TextAnalyticsClient, calling analyze_sentiment(documents, show_opinion_mining=True) adds opinion mining: for each sentence, the targets (aspects such as "battery" or "screen") and the assessments about them, each with its own sentiment. For an existing app that needs per-feature sentiment, that one flag is the smallest change. Changing model_version doesn't add aspects or new label types.

RequirementFitsWhy the other fails
Per-aspect sentimentOpinion mining links each assessment to its target in one callSeparate entity or phrase extraction plus sentence scoring doesn't link an opinion to the feature it is about
Tone labels (sarcastic, frustrated, appreciative)A language model with a sentiment field and a tone enumSentiment analysis has a fixed label set and no tone output
Sarcasm ("Oh wonderful, another password reset email that never arrives")A language model, which reads the comment in contextSentiment analysis scores literal wording, so sarcastic praise reads positive
Sentiment that must avoid legacy featuresA language model whose schema returns sentiment per aspectEvery Azure Language sentiment path (sentiment analysis, opinion mining, custom text classification) is legacy, and sentence-level scores can't separate two aspects in one sentence

Sentiment is not safety. Azure AI Content Safety rates harm categories; it says nothing about whether a comment is positive or sarcastic.

How do you detect safety issues in text with Azure AI Content Safety?

Call the Content Safety text analysis API (text:analyze) on any text you want to screen: it returns a severity for each harm category (Hate, Sexual, Violence, SelfHarm) and can check custom blocklists in the same call. It works on arbitrary text, with no language model in the path.

  • categories limits which harm categories are analyzed. It doesn't change the severity scale.
  • outputType: FourSeverityLevels (the default) returns 0, 2, 4 or 6; EightSeverityLevels returns 0 to 7. A policy that separates severity 4 from 5 needs the eight-level scale. On the default scale, low is 2, medium is 4 and high is 6.
  • blocklistNames names custom text blocklists to check, for terms the classifiers score as safe but your policy bans: community slang, counterfeit brand names, banned product terms.
  • haltOnBlocklistHit: when true, a blocklist match stops further harm analysis for that text (saving latency). It defaults to false, and it does nothing unless a blocklist is named.
{
  "text": "<message>",
  "blocklistNames": ["banned-terms"],
  "outputType": "FourSeverityLevels"
}

Text analysis or model guardrails?

Guardrails (content filters) on a Foundry model deployment screen the requests and responses of that deployment. If text never goes to a model, such as listings published straight from a web form or a nightly export job, a guardrail never sees it. Calling Content Safety text analysis directly is cheaper and simpler than routing the text through a chatbot deployment just to trigger its guardrails. Configuring deployment guardrails, Prompt Shields and protected material detection is covered in the responsible AI lesson; note here only that Prompt Shields detects prompt injection and jailbreak attempts, not hate or sexual content.

How do you detect and redact sensitive content (PII)?

Use Azure Language PII detection, a core feature: it finds sensitive categories such as Person, PhoneNumber, Address, CreditCardNumber and US Social Security numbers, and returns redacted text alongside a list of entities, each with its category, offset, length and confidence score.

responses = text_client.recognize_pii_entities(
    support_tickets,
    categories_filter=[
        PiiEntityCategory.PHONE_NUMBER,
        PiiEntityCategory.EMAIL,
    ],
)
for ticket in responses:
    print(ticket.redacted_text)
    for entity in ticket.entities:
        print(entity.category, entity.confidence_score)
  • By default every supported category for the language is detected and masked, so names, addresses and organizations disappear too. categories_filter (piiCategories in REST) restricts detection, and therefore redaction, to the categories you list.
  • domain_filter set to protected health information (phi) adds health-domain entities. It doesn't narrow results to specific categories.
  • Filtering the returned entity list after the call doesn't change redacted_text, which the service already masked for every detected category.
  • disable_service_logs controls whether the service logs input for troubleshooting; it doesn't change detection.
  • Confidence scores per entity let you route low-confidence detections to a human reviewer, something a prompted rewrite ("replace identifiers with [REDACTED]") can't give you.
OptionUse it forNot a substitute because
Azure Language PII detectionMasking sensitive data in any text, with categories and scores—
Prebuilt NERGeneral entitiesNot built for sensitive data; removing Person entities leaves phone, card and record numbers
PII guardrail on a model deploymentModel inputs and outputsSees only traffic to that deployment; a job that calls no model is never screened
Prompting a model to redactQuick masking inside a model flowNo per-entity category or confidence score
Content Safety or Translator profanity settingsHarm and profanityDon't find personal data

How does Azure Translator text translation work in API version 2026-06-06?

In Azure Translator API version 2026-06-06 (GA, not backward compatible with v3), each entry in a request's targets array names a language and, optionally, the system that translates it through deploymentName:

deploymentNameSystemNotes
Omitted or generalStandard neural machine translation (NMT)The default; character-priced, suited to high volume
An LLM deployment, such as GPT-5.1LLM-based translationNeeds a Foundry resource linked to Translator; enables tone and gender
A custom model's category IDYour Custom Translator (custom NMT) modelTrained on your parallel sentences

Having a GPT deployment or a custom model available changes nothing on its own: a target that sets only a language code goes to general.

Target parameters to know

  • tone (formal, informal, neutral) and gender (female, male, neutral) apply to LLM-based translation only. Requesting a formal register therefore needs both an LLM deploymentName and the parameter.
  • allowFallback defaults to true: if the named model doesn't support the language pair, the service quietly uses the general system. Set it to false when only LLM output is acceptable, so the request fails instead.
  • textType defaults to plain; set html when the input contains markup so elements are preserved. Text marked as not to be translated stays as it is.
  • Settings such as script (writing system) and profanity handling don't affect markup, tone or fallback.

Private endpoints

Microsoft documents that LLM-based translation isn't available when the Translator resource uses a private endpoint. NMT keeps working privately but has no tone control, and tone is an LLM-only parameter. Register control on private networking therefore can't come from Translator itself; it comes from instructions to a language model whose deployment is itself reachable privately (next sections).

What was removed

Version 2026-06-06 dropped the v3 Detect, BreakSentence and Dictionary methods. To identify a text's language without translating it, Microsoft points to Azure Language language detection, a core Azure Language feature. Leaving out the source language in a translate call does return a detected language, but only as part of a paid translation.

Translating documents with their formatting

Translator document translation keeps a file's structure and layout. It has two modes:

ModeInput and outputStorageFits
Synchronous (single file)One file in the request, the translated file in the responseNo Blob Storage neededA user uploads one document and waits for it
Asynchronous batchMany files read from and written to containersAzure Blob Storage source and target containersBulk translation of document sets

Translating extracted text as plain text loses the document's layout, and transliteration converts between scripts (for example into Latin script) without translating anything. For HTML strings sent to text translation, set textType to html rather than using document translation.

How do you make translations follow your terminology?

Pick the adaptation by the system you're allowed to use and how much data you have: adaptive custom translation and reference text pairs steer LLM translation without training; a Custom Translator model is a trained NMT model.

OptionTranslation systemDataTrainingHow a target uses it
Adaptive custom translation datasetLLMPrealigned source/target sentence pairs, indexed in minutesNoneadaptiveDatasetId plus an LLM deploymentName
Reference text pairsLLMUp to five inline pairs per targetNonereferenceTextPairs
Custom Translator modelNMTParallel sentences (thousands)A training rundeploymentName set to the model's category ID
Fine-tuned GPT modelLLMTraining setA training job to run and maintainThe tuned deployment's name
  • Precedence: the two LLM adaptation inputs aren't combined. When referenceTextPairs is supplied, adaptiveDatasetId is ignored. To keep a dataset's terminology and add a new term, add the new pairs to the dataset instead of passing them inline.
  • Volume and lead time: inline reference pairs are capped per target, so a large glossary belongs in an adaptive dataset, which is indexed rather than trained and is usable quickly. A Custom Translator model needs a training run.
  • Which system is allowed: adaptive datasets, reference pairs and fine-tuned GPT models all steer a language model. Custom Translator is the only adaptation that stays on NMT.
  • Neither adaptation adds language support: an unsupported pair still falls back to NMT unless allowFallback is false.

When should you build an LLM-powered translation flow instead of using Translator?

Build an LLM-powered flow, a Foundry model prompted to translate, when the requirement is something Translator's fixed parameters can't express; use Translator NMT when volume and cost dominate. Translator exposes settings (target languages, tone, gender, text type, adaptation), not free-form instructions.

RequirementBetter fit
Throughput and cost per character matter mostTranslator, general NMT, which is priced per character rather than per token
Creative adaptation (voice, idiom, wordplay) rather than literal meaningA prompted model, because Translator offers no style instructions beyond tone and gender
Rules about the output itself (length budgets, tokens that must survive, a specific response shape)A prompted model with those rules in the instructions and an output schema; Translator has no parameter for them
A capability Translator restricts in your configuration (for example LLM features behind a private endpoint)A model deployment that meets the same network rule, with the behaviour set in instructions
Terminology control without any language modelCustom Translator model

Translator can handle parts of these jobs: several targets in one request, text marked as not to be translated. The deciding requirement is usually the one Translator can't express. Prompted translation adds token cost at scale, so don't use it where NMT already meets the need.

How do you customize model output for domain tasks such as compliance summarization?

Start with system instructions and a few examples, enforce structure with a strict schema, and reach for supervised fine-tuning only when long few-shot prompts are needed on every call to keep a house style. Each layer fixes a different problem.

  • Inconsistent format or invented categories mean the model hasn't been told what to produce. Define the types and the format in the system message and add a couple of worked examples. This is fast and needs no training set.
  • Verifiable grounding for compliance work: a citation rule in the instructions (every finding cites the paragraph it comes from) plus a schema with a required source_paragraph field per finding, so a finding without a source can't be returned. A required field is what makes the rule enforceable: guidance in a prompt shapes output but can't reject a finding that lacks a source, and any tool that brings in outside content works against grounding in the supplied document.
  • A taxonomy that changes (for example quarterly) belongs in a schema enum: decoding is constrained to current values and editing the enum takes effect on the next request. Fine-tuning would bake in old categories and need a new training run per change, and a fine-tuned model can still emit an off-list tag.
  • Fine-tuning fits when thousands of approved input and output pairs exist, the knowledge is already in the request, and the cost and latency of sending long examples with every call are the problem. It teaches format, tone and style so the prompt can shrink. The test for alternatives: any approach that still puts the examples into each request keeps their token cost, however the examples are chosen, cached or priced; only moving the behaviour into the model's weights removes them.
TechniqueChangesDoesn't change
System instructionsWhat to produce, rules, registerGuarantees about structure
Few-shot examplesEdge-case behaviour, layout, boundariesGuarantees; token cost per call
Strict schema (enum, required fields)Guaranteed fields and allowed valuesStyle or judgement
Supervised fine-tuningBuilt-in style and format, shorter promptsFresh facts; quick taxonomy updates

Tip. Expect short scenarios that describe a company's text workload and one or two constraints, then ask what to use, change or configure. The deciding detail is usually a constraint: standard or custom categories, labelled data or none, offsets needed or not, an existing app or a new build that must avoid legacy features, whether a language model is in the path, a private endpoint, NMT-only compliance, a same-day or no-training rule, cost at volume, or a per-string length limit. Several items show short Python or JSON request bodies — TextAnalyticsClient calls, Responses API json_schema definitions, Content Safety analyze requests, Translator targets — and ask which parameter or schema change fixes a stated symptom, so know what each parameter does and what it doesn't. Some items ask you to select two actions that together meet a requirement.

Key takeaways
  • Standard entities: prebuilt NER. Own categories with labels, offsets and retraining: custom NER. Own categories, no labels, values only: a prompted model with a strict schema.
  • Strict structured outputs need every property in required and additionalProperties false on every object; express optional values as a null union.
  • Fixed label lists belong in an enum; instructions and temperature 0 reduce but never prevent off-list values.
  • Content larger than the context window is chunked and summarized in levels; truncation, partial input and max_output_tokens all lose coverage.
  • Sentiment analysis and opinion mining are legacy Azure Language features for existing apps; tone, sarcasm and new builds use a language model with a schema.
  • Content Safety text analysis screens any text: EightSeverityLevels gives 0–7, blocklists catch policy terms, haltOnBlocklistHit (default false) skips further analysis on a hit. Model guardrails see only model traffic.
  • PII detection returns redacted text plus each entity's category and confidence score; categories_filter narrows what is masked.
  • In Translator 2026-06-06, deploymentName defaults to general NMT; tone and gender need an LLM deployment; allowFallback false makes unsupported pairs fail; LLM translation is unavailable behind a private endpoint.
  • referenceTextPairs (up to five) override adaptiveDatasetId; a Custom Translator model is the NMT-only route to your terminology.
  • Try instructions and examples first, enforce with a schema, and fine-tune only when long per-call examples are the cost problem.

Frequently asked questions

When should I use custom NER instead of prompting a language model to extract entities?

Use custom NER in Azure Language when you have labelled examples of your own entity categories, need the exact character offset of every mention, and want a model you can retrain as categories change. Prompt a Foundry model with a strict JSON schema when you have no labelled data, need results quickly and only need the extracted values, because generated JSON doesn't give dependable offsets.

How do I make a field optional in strict structured outputs?

Keep the field in the required list and give it a union type that includes null, such as ["string", "null"], then tell the model to return null when the source doesn't contain the value. In strict mode every property must be required and every object needs additionalProperties set to false, so removing a field from required makes the schema invalid.

Is Azure Language sentiment analysis still recommended for new apps?

No. Microsoft lists sentiment analysis and opinion mining as legacy Azure Language capabilities, supported for existing implementations but not recommended for new development. Existing apps can keep using them, including adding opinion mining with show_opinion_mining; new builds, and anything needing tone or sarcasm labels, use a language model with an output schema.

What is the difference between Content Safety text analysis and a model deployment guardrail?

Content Safety text analysis is an API you call on any text, returning harm-category severities and custom blocklist matches. A guardrail on a Foundry model deployment screens only the requests and responses of that deployment. Text that never goes to a model, such as form submissions or batch exports, needs the text analysis API.

How do I redact only some kinds of personal data with Azure Language?

Pass categories_filter (piiCategories in the REST API) to PII detection with the categories you want, such as phone numbers and email addresses. The service then detects and masks only those categories in the redacted text. Filtering the returned entity list afterwards doesn't change the redacted text.

Which model does Azure Translator 2026-06-06 use if I don't set deploymentName?

It uses general, the reserved name for Translator's standard neural machine translation system. LLM translation happens only when a target names an LLM deployment such as GPT-5.1, and a Custom Translator model only when the target names that model's category ID.

Why is Translator ignoring my adaptive dataset?

If a target also supplies referenceTextPairs, Translator uses the inline pairs and ignores adaptiveDatasetId; the two aren't combined. Add new terms to the adaptive dataset instead of passing them inline when you want the dataset's terminology applied.

How do I detect the language of text now that Translator removed the Detect method?

Use Azure Language language detection, which identifies the language of text without translating it. Translator API version 2026-06-06 removed the Detect, BreakSentence and Dictionary methods, and Microsoft points to Azure Language for detection.

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.

Test yourself on this topic
Practice now

Sign up free to mark lessons complete, bookmark topics and track your exam readiness.

Spotted a mistake in this lesson?