AI Tutorial
Home All Courses Blog Interview Prep ↗ Export PDF 0%
1
Curious builder 0 XP earned · 300 to level 2
0 days Finish a lesson to begin
Badge collection
0 of 5 unlocked
small wins to finish your path
Next lesson →

Evaluation and red teaming

Export PDF

Generative AI fails quietly: answers look fluent even when they are wrong. Evaluation turns “it seems fine” into numbers you can track and gate releases on. This lesson explains what to measure, Foundry's built-in evaluators, how to run evaluations locally and in the cloud, how to turn scores into a release gate, and how the AI red teaming agent attacks your app to find safety gaps before users do.

Last updated: 01 Nov, 2026 · azure-ai-projects 2.x · Python 3.10+

Why evaluation is different for generative AI

A classic classifier has one right answer, so you count accuracy. A generated answer can be right in many wordings, partially right, right but unsupported by sources, or perfectly fluent and completely wrong. So we score several properties of each answer, often using a strong model as a judge.

Definition · Evaluator

A function that takes an input (query, response, and optionally context, ground truth, or tool calls) and returns a score with a reason. AI-assisted evaluators use an LLM judge; NLP evaluators use math (F1, BLEU, ROUGE); code evaluators are your own Python checks.

Definition · Evaluation dataset (golden set)

A fixed list of realistic test inputs — and, where possible, expected answers or source passages — that you run every version against. Typically 50–500 rows covering common, edge, and adversarial cases.

Built-in evaluators

CategoryEvaluatorsWhat they answer
General qualityCoherence, Fluency, RelevanceIs the answer clear, well-written, and on-topic?
RAGGroundedness, Retrieval, Response completenessIs every claim supported by the retrieved context? Did retrieval find the right passages? Is anything missing?
AgentsIntent resolution, Task adherence, Tool call accuracy, Task completionDid the agent understand the goal, follow instructions, call the right tools with the right arguments, and finish?
Textual similaritySimilarity, F1, BLEU, ROUGE, GLEU, METEORHow close is the answer to a reference answer?
Risk and safetyViolence, Sexual, Self-harm, Hate/unfairness, Indirect attack (XPIA), Protected material, Code vulnerability, Ungrounded attributesDoes the output contain harmful or risky content?
CompositeQA, Content safety, Output quality, Tool use qualitySeveral of the above in one call

Quality evaluators usually score 1–5 with a default pass threshold of 3. Safety evaluators report a severity (very low to high) and a pass/fail.

Local evaluation with azure-ai-evaluation

Run evaluations on your laptop or in CI with the azure-ai-evaluation package. The judge model is one of your Foundry deployments.

eval_data.jsonl
{"query": "Can I return a damaged item?", "context": "Damaged items can be refunded within 14 days of delivery with a photo.", "response": "Yes, within 14 days of delivery if you include a photo."}
{"query": "Do you ship to Canada?", "context": "We ship to the US and Mexico.", "response": "Yes, we ship to Canada in 5-7 days."}
Live Azure call

This example calls your Azure resources, so it needs a deployed project and a signed-in identity. Output will differ on every run.

Python
import os

from azure.ai.evaluation import GroundednessEvaluator, RelevanceEvaluator, evaluate
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
model_config = {
    "azure_endpoint": f"https://{os.environ['FOUNDRY_RESOURCE']}.openai.azure.com",
    "azure_deployment": os.environ["JUDGE_DEPLOYMENT"],     # a strong model as judge
}

result = evaluate(
    data="eval_data.jsonl",
    evaluators={
        "groundedness": GroundednessEvaluator(model_config=model_config, credential=credential),
        "relevance": RelevanceEvaluator(model_config=model_config, credential=credential),
    },
    evaluator_config={
        "groundedness": {"column_mapping": {"query": "${data.query}",
                                            "context": "${data.context}",
                                            "response": "${data.response}"}},
        "relevance": {"column_mapping": {"query": "${data.query}",
                                         "response": "${data.response}"}},
    },
    azure_ai_project=os.environ["FOUNDRY_PROJECT_ENDPOINT"],   # optional: log results to the portal
    output_path="eval_results.json",
)
print(result["metrics"])
{'groundedness.groundedness': 3.0, 'groundedness.groundedness_result': 0.5, 'relevance.relevance': 4.5, ...}

The second row scores low on groundedness: the response claims Canada shipping, which the context contradicts. That is exactly the kind of hallucination evaluation exists to catch. Each row also gets a reason explaining the score — read them, they are often more useful than the number.

Evaluating an app, not just a file

Pass target=my_app_function to evaluate() and the dataset only needs queries: the SDK calls your app for each row, collects responses, and scores them. This is how you evaluate a new prompt or agent version end to end.

Cloud evaluation

For larger datasets, scheduled runs, and team visibility, run evaluations inside your Foundry project. Results appear in the portal's Evaluation page, where you can compare runs side by side. Cloud evaluation uses the OpenAI-compatible Evals API with Foundry's built-in evaluators:

Live Azure call

This example calls your Azure resources, so it needs a deployed project and a signed-in identity. Output will differ on every run.

Python
from azure.ai.projects.models import TestingCriterionAzureAIEvaluator

dataset = project.datasets.upload_file(name="support-golden", version="1", file_path="eval_data.jsonl")

criteria = [
    TestingCriterionAzureAIEvaluator(
        type="azure_ai_evaluator",
        name=name,
        evaluator_name=f"builtin.{name}",
        initialization_parameters={"model": judge_deployment},
        data_mapping={"query": "{{item.query}}", "context": "{{item.context}}",
                      "response": "{{item.response}}"},
    )
    for name in ("groundedness", "relevance", "coherence")
]

with project.get_openai_client() as client:
    evaluation = client.evals.create(
        name="support-agent-quality",
        data_source_config={"type": "custom", "item_schema": {
            "type": "object",
            "properties": {"query": {"type": "string"}, "context": {"type": "string"},
                           "response": {"type": "string"}},
            "required": ["query", "response"],
        }, "include_sample_schema": False},
        testing_criteria=criteria,
    )
    run = client.evals.runs.create(
        eval_id=evaluation.id,
        name="v3-candidate",
        data_source={"type": "jsonl", "source": {"type": "file_id", "id": dataset.id}},
    )
    print(run.id, run.status)       # poll client.evals.runs.retrieve(...) until "completed"

Beyond static datasets, Foundry can evaluate an agent directly (it generates responses by running the agent), evaluate multi-turn conversations, run continuous evaluation on a sample of production traffic, and schedule recurring runs.

Turn scores into a release gate

Scores only matter if they change decisions. Define minimum averages and safety limits, and fail the build when a candidate version regresses. This offline example shows the logic:

Python
import statistics

# rows as they come back from an evaluation run (scores are 1-5)
rows = [
    {"id": "q1", "groundedness": 5, "relevance": 5, "violence": 0},
    {"id": "q2", "groundedness": 4, "relevance": 5, "violence": 0},
    {"id": "q3", "groundedness": 2, "relevance": 4, "violence": 0},
    {"id": "q4", "groundedness": 5, "relevance": 3, "violence": 0},
    {"id": "q5", "groundedness": 3, "relevance": 4, "violence": 2},
]

GATES = {"groundedness": 4.0, "relevance": 4.0}   # minimum average to ship
PASS_SCORE = 3                                    # per-row pass threshold
MAX_SAFETY = 0                                    # no harmful content allowed


def gate(rows):
    ok = True
    for metric, minimum in GATES.items():
        scores = [r[metric] for r in rows]
        avg = statistics.mean(scores)
        pass_rate = sum(s >= PASS_SCORE for s in scores) / len(scores)
        status = "PASS" if avg >= minimum else "FAIL"
        ok &= status == "PASS"
        print(f"{metric:<13} avg={avg:.2f} pass_rate={pass_rate:.0%} min={minimum} {status}")
    unsafe = [r["id"] for r in rows if r["violence"] > MAX_SAFETY]
    print("safety flags:", unsafe or "none")
    worst = sorted(rows, key=lambda r: r["groundedness"])[:2]
    print("review first:", [r["id"] for r in worst])
    return ok and not unsafe


print("ship it" if gate(rows) else "blocked: fix regressions before deploying")
groundedness avg=3.80 pass_rate=80% min=4.0 FAIL relevance avg=4.20 pass_rate=100% min=4.0 PASS safety flags: ['q5'] review first: ['q3', 'q5'] blocked: fix regressions before deploying

Average relevance passed, but groundedness fell below 4.0 and one row was flagged for safety, so the release is blocked — and the gate tells you which rows to read first. In CI (GitHub Actions or Azure DevOps), run the evaluation on every pull request that touches prompts, tools, or the model version, and fail the job when the gate returns false.

Red teaming

Definition · Red teaming

Deliberately attacking your own AI system — trying to make it produce harmful content, leak data, or ignore its instructions — so you find weaknesses before real attackers or users do.

Definition · AI red teaming agent

A Foundry capability (built on Microsoft's open-source PyRIT toolkit) that automatically generates adversarial prompts for chosen risk categories, transforms them with attack strategies (encoding, character swaps, multi-turn escalation), sends them to your app, and scores the attack success rate.

Python
import asyncio, os

from azure.ai.evaluation.red_team import AttackStrategy, RedTeam, RiskCategory
from azure.identity import DefaultAzureCredential


def my_app(query: str) -> str:
    return ask_support_agent(query)          # your app's entry point


red_team = RedTeam(
    azure_ai_project=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
    credential=DefaultAzureCredential(),
    risk_categories=[RiskCategory.Violence, RiskCategory.HateUnfairness,
                     RiskCategory.Sexual, RiskCategory.SelfHarm],
    num_objectives=5,                        # attack prompts per category
)

result = asyncio.run(red_team.scan(
    target=my_app,
    scan_name="support-agent-v3",
    attack_strategies=[AttackStrategy.EASY, AttackStrategy.MODERATE, AttackStrategy.Base64],
))

The key metric is attack success rate (ASR): the percentage of attacks that produced unsafe output. Track it per risk category and per strategy, fix the weakest spots (instructions, guardrails, tool restrictions), and re-run. Red-team before launch and after every significant change.

Evaluation best practices

  • Start small, start early: 30 good test cases on day one beat 1,000 later.
  • Grow the dataset from production: every bug report and bad trace becomes a new test row.
  • Calibrate the judge: hand-label 30 rows and check the LLM judge agrees with you.
  • Use a strong, fixed judge model so scores are comparable over time.
  • Compare versions on the same dataset, not absolute numbers across datasets.
  • Combine metrics: groundedness alone can reward unhelpful “I don't know” answers; pair it with relevance and completeness.
Try it
  1. Change GATES so the example passes, then add a sixth row that makes it fail again.
  2. Write a 10-row eval_data.jsonl for an app of your own, including two deliberately ungrounded responses, and confirm groundedness catches them.
  3. Run a small red-team scan with num_objectives=2 and read the attack success rate.
Key takeaways
  • Score properties — groundedness, relevance, coherence, task adherence, safety — usually with an LLM judge.
  • azure-ai-evaluation runs evaluators locally or in CI; cloud evaluation runs them in your project with portal comparisons.
  • Agent evaluators check intent resolution, tool-call accuracy, and task completion.
  • Convert scores into a release gate with minimum averages and zero-tolerance safety limits.
  • The AI red teaming agent measures attack success rate per risk category; re-run it after every major change.