Implement Generative AI Quality Assurance and Observability
Evaluate quality and safety with Foundry's built-in evaluators, then observe cost, performance, and failures in production with tracing.
Quality evaluation with built-in evaluators
The `azure-ai-evaluation` SDK's built-in evaluators are the standard, out-of-the-box way to score a generative AI application's outputs against recognized quality dimensions, without writing custom scoring logic for the common cases.
General-purpose and RAG evaluators
- `CoherenceEvaluator` and `FluencyEvaluator` score logical consistency and natural-language quality respectively, independent of whether there's any retrieved context involved — useful for any generated text, not just RAG.
- `GroundednessEvaluator` measures how well a response is supported by retrieved context, returning a 1-5 model-judged score; `GroundednessProEvaluator` measures the same thing but as a binary pass/fail using the Azure AI Content Safety service instead of an LLM judge, and doesn't require a model deployment to run.
- `RelevanceEvaluator` scores how well the response actually answers the query; `RetrievalEvaluator` scores the retrieval step itself (did the system fetch relevant context at all, independent of what the model then did with it) — a RAG pipeline commonly needs both, since a bad final answer could stem from bad retrieval, bad generation, or both.
- `DocumentRetrievalEvaluator` and `ResponseCompletenessEvaluator` require a `ground_truth` to compare against, unlike most of the other AI-assisted evaluators — a data-requirement distinction worth remembering when assembling an evaluation dataset.
Running evaluations
- The `evaluate()` function runs multiple evaluators together against a dataset in one call: `evaluate(data="data.jsonl", evaluators={"groundedness": groundedness_evaluator, "relevance": relevance_evaluator})`.
- The dictionary key used for each evaluator is significant, not cosmetic — it has to match the documented keyword (`"groundedness"`, `"relevance"`, `"coherence"`, and so on) for that evaluator's results to be recognized and rendered correctly in the Foundry portal's evaluation UI.
- Composite evaluators bundle several individual ones for convenience: `QAEvaluator` runs groundedness, relevance, coherence, fluency, similarity, and F1 score together for a query/response pair; this is the fast way to get a broad quality snapshot without wiring up each evaluator individually.
Evaluation data and levels
- Built-in evaluators accept either simple query/response pairs or full conversations in JSONL — and every evaluator declares which `evaluation_level` it supports (`turn`, scoring one exchange, or `conversation`, scoring the whole multi-turn interaction); a single evaluation run can't mix evaluators that support different levels.
- Test datasets should combine production traffic samples (so evaluation reflects real usage patterns) with synthetically generated edge cases (so evaluation also covers scenarios production hasn't hit yet, like adversarial phrasing or rare intents).
Common confusion
- `GroundednessEvaluator` and `RelevanceEvaluator` sound similar but measure different failure modes: a response can be highly relevant to the question asked while being completely ungrounded (fabricated, not actually supported by the retrieved context), and conversely can be perfectly grounded in the context while failing to actually address what the user asked — a comprehensive RAG evaluation needs both, since neither one alone catches the other's failure mode.
Risk and safety evaluation
Safety evaluators exist alongside quality evaluators as a distinct, mandatory category — a response can score perfectly on groundedness and relevance while still containing genuinely harmful content, so responsible AI practice treats the two categories as complementary, not substitutable.
Content-harm evaluators
- `ViolenceEvaluator`, `SexualEvaluator`, `SelfHarmEvaluator`, and `HateUnfairnessEvaluator` each detect a specific category of harmful content in a response, backed by the Azure AI Content Safety service rather than a general-purpose LLM judge.
- `ContentSafetyEvaluator` is the composite that runs all four together for a single combined output — the safety-category equivalent of `QAEvaluator` bundling the quality metrics.
- Because these evaluators call the Content Safety service rather than a deployed chat model, running them requires `azure_ai_project` configuration pointing at the Foundry project (instead of the `model_config` a quality evaluator like `GroundednessEvaluator` needs) — a setup detail that trips people up when reusing evaluation code between the two categories.
Attack- and leakage-oriented evaluators
- `IndirectAttackEvaluator` (also called XPIA, cross-prompt injection attack) measures whether a response fell for a jailbreak or malicious instruction that was smuggled in through *retrieved context* rather than the user's own message — the scenario where an attacker plants an instruction inside a document the RAG pipeline later retrieves and the model unwittingly follows.
- `ProtectedMaterialEvaluator` flags unauthorized reproduction of copyrighted or protected content in a response; `CodeVulnerabilityEvaluator` scans generated code for security issues; `UngroundedAttributesEvaluator` catches fabricated details the model inferred about a user or entity that weren't actually present in the input.
- Agent-specific safety evaluators (`ProhibitedActionsEvaluator`, `SensitiveDataLeakageEvaluator`) extend this same category to autonomous agent behavior — whether an agent stayed within its allowed actions and whether it exposed information it shouldn't have.
Combining evaluators for comprehensive coverage
- A typical RAG application's evaluation suite combines Retrieval, Groundedness, and Relevance (quality) with Content Safety (Violence/Sexual/Self-Harm/Hate) as a baseline; an agent application layers on Tool Call Accuracy, Task Adherence, and Intent Resolution.
- Safety evaluation isn't a one-time gate before launch — the same evaluators run as part of ongoing automated evaluation (batch runs against production or synthetic traffic), since model updates, prompt changes, and new attack patterns can all reopen a previously-closed safety gap.
Common confusion
- Prompt-injection defenses and `IndirectAttackEvaluator` protect against different entry points that are easy to conflate: a *direct* jailbreak attempt sits in the user's own message and is a content-moderation/prompt-engineering problem, while an *indirect* attack (XPIA) is smuggled in through retrieved documents, tool outputs, or other context the model treats as trustworthy background rather than untrusted user input — which is exactly why it needs its own dedicated evaluator instead of being caught by standard content-safety checks on the user's message.
Observability: monitoring, tracing, and cost
Evaluation tells you whether outputs are good *before* wide release; observability is what tells you what's actually happening to a live application, in production, as real users and real cost accumulate against it.
What to monitor in production
- Latency, throughput, and error rate are the standard health signals for any production service, generative AI included — but token usage (input and output tokens per request) is the metric unique to LLM-backed applications, since it drives cost directly in a way a typical API call's compute cost doesn't vary request-to-request nearly as much.
- Azure Monitor, paired with a Foundry project's built-in continuous monitoring, is the standard combination: Azure Monitor collects and alerts on the platform-level metrics, while Foundry's monitoring view is scoped specifically to model deployments and agent behavior.
- A production troubleshooting workflow typically starts from a cheap, fast metric-based alert (latency spike, error-rate spike) and only then pivots to detailed tracing to find the specific root cause — going straight to trace-level detail for every request would be far too much data to sift through as a first step.
Distributed tracing with OpenTelemetry
- A single agent response can fan out across a retrieval call, one or more model calls, and custom business logic — OpenTelemetry represents that as a *trace* made of *spans*, each span being one unit of work with its own duration and attributes, nested in a parent-child hierarchy that shows exactly which downstream call is responsible for overall latency.
- The Azure Monitor OpenTelemetry Distro exports this span data to an Application Insights resource via its connection string; many Azure SDKs (and Foundry's own agent SDKs) emit spans automatically for their operations, so a meaningful amount of tracing detail shows up without writing any manual instrumentation.
- Custom spans, added by hand around business logic auto-instrumentation doesn't cover, share the same operation ID as everything else in that request — which is what lets Application Insights stitch spans emitted by completely different processes back into one coherent end-to-end trace.
Cost-aware performance tuning
- Because token consumption is directly billed, an observability setup for a generative AI application typically tracks token usage per request/user/feature as its own dimension, not just as a component of overall spend — this is what turns "our bill went up" into "this specific feature's prompts are unusually long."
- Decisions this feeds into include prompt-length reduction, switching a low-stakes step to a smaller/cheaper model, moving predictable high-volume traffic to a provisioned throughput deployment (trading reserved cost for latency guarantees), or adding caching for repeated queries.
Common confusion
- Enabling an OpenTelemetry SDK and exporting traces *somewhere* isn't the same as those traces reaching Application Insights — the exporter must be explicitly configured with the Azure Monitor connection string, and initializing it *before* other instrumented libraries load matters; getting the order or configuration wrong is a common reason traces come out incomplete or don't appear in the Foundry/Application Insights view at all.