Skill 2 · 30–35%

Implement generative AI and agentic solutions

The largest skill area, and the one that most rewards hands-on practice. Everything here hangs off two primitives: the Responses API and the agent loop. Learn those, and tools, memory, orchestration and evaluation all slot into place.

Three objectives in this skill area:

2.1 Build generative applications by using Foundry

Connecting an application to a Foundry project

This is an explicit exam objective, and the answer is short: one endpoint, one credential, one client.

Python · project client
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient

project = AIProjectClient(
    endpoint="https://<resource>.services.ai.azure.com/api/projects/<project>",
    credential=DefaultAzureCredential(),
)
openai = project.get_openai_client()          # plain model calls
agent_client = project.get_openai_client(agent_name="policy-assistant")  # bound to an agent

Things that must be exactly right

  • The endpoint form is https://<resource>.services.ai.azure.com/api/projects/<project> — the /api/projects/ segment is not optional.
  • Package is azure-ai-projects 2.x (pip install "azure-ai-projects>=2.3.0" azure-identity). 1.x is classic and incompatible.
  • DefaultAzureCredential needs az login locally and a managed identity in Azure — plus a Foundry User role assignment. Missing the role is the usual cause of a 401/403 in a question.
  • For REST, the token scope is https://ai.azure.com/.default and routes live under /openai/v1/.

Deploying and consuming models

Once a model is deployed, its deployment name is what you pass as model=. The same call shape works across LLMs, small models, code models and multimodal models — which is exactly why the exam can ask you to swap models without changing code.

Python · consuming models
# Text
r = openai.responses.create(model="gpt-5-mini", input="Explain RAG in two sentences.")

# Multimodal: text + image in one input item
r = openai.responses.create(
    model="gpt-5",
    input=[{
        "role": "user",
        "content": [
            {"type": "input_text", "text": "What safety issues can you see on this site?"},
            {"type": "input_image", "image_url": "https://example.com/site.jpg"},
        ],
    }],
)

# Streaming, for perceived latency
with openai.responses.stream(model="gpt-5-mini", input=prompt) as stream:
    for event in stream:
        if event.type == "response.output_text.delta":
            print(event.delta, end="")

Implementing retrieval-augmented generation

flowchart LR
  subgraph ING["Ingestion — offline"]
    D["Source documents"] --> CH["Chunk<br/>+ enrich"] --> EMB["Embed"] --> IDX["Index<br/>text + vectors"]
  end
  subgraph RUN["Query time — online"]
    Q["User question"] --> RET["Retrieve<br/>hybrid + semantic rerank"]
    IDX --> RET
    RET --> CTX["Top-k grounding<br/>chunks + citations"]
    CTX --> P["Prompt:<br/>instructions + context + question"]
    P --> M["Model"] --> ANS["Grounded answer<br/>with citations"]
  end
RAG has two halves that fail independently: an offline ingestion pipeline and an online retrieval-and-generation path.

Three ways to do RAG in Foundry, in increasing order of control:

ApproachHowTrade-off
File search tool Upload files to the project, create a vector store, attach the file_search tool Fastest path; Foundry owns chunking, embedding and the vector store. Reuse a persistent vector store in production rather than creating one per run
Azure AI Search tool Connect an existing index and attach the azure_ai_search tool; answers come back with inline citations Full control of the index, filters, hybrid search and relevance. The index needs searchable/retrievable Edm.String fields and searchable Collection(Edm.Single) vector fields
Foundry IQ knowledge base Define knowledge sources, group them into a knowledge base, point agents at it Managed, permission-aware, reusable across agents; adds agentic retrieval with query planning
Python · RAG agent
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import PromptAgentDefinition

project = AIProjectClient(
    endpoint="https://my-foundry.services.ai.azure.com/api/projects/my-project",
    credential=DefaultAzureCredential(),
)

# A prompt agent is pure configuration: model + instructions + tools.
agent = project.agents.create_version(
    agent_name="policy-assistant",
    definition=PromptAgentDefinition(
        model="gpt-5-mini",
        instructions=(
            "You are an HR policy assistant for Contoso. "
            "Answer ONLY from the retrieved policy documents and always cite the "
            "document name. If the documents do not cover the question, say so "
            "and suggest contacting HR — never guess."
        ),
        tools=[
            {
                "type": "file_search",
                "vector_store_ids": ["vs_hr_policies"],
            }
        ],
    ),
)
print(agent.id, agent.name, agent.version)

The instructions are part of the RAG design

Notice what the system instructions above do: restrict the answer to retrieved content, require citations, and define the behaviour when the documents do not cover the question. Exam items about "the model invents policies that do not exist" want grounding plus an explicit refusal instruction, measured with the groundedness evaluator — not a lower temperature on its own.

Workflows, tool-augmented flows and multi-step reasoning

Foundry workflows preview orchestrate multiple agents in a repeatable process, with branching, variables and human steps, authored in the portal or as YAML in VS Code. Three starting templates map directly to the orchestration patterns:

PatternBehaviourTypical use
Human in the loopAsks the user a question and waits for input before continuingApproval requests, clarifying questions
SequentialPasses each agent's result to the next in a fixed orderPipelines, staged processing, auditable approval chains
Group chatPasses control between agents dynamically based on context or rulesEscalation, fallback, expert handoff

For deterministic, long-running or event-driven business processes, Azure Logic Apps is the complementary answer: Foundry hosts and grounds the agent, Logic Apps triggers it from events or schedules, waits on long-running approvals and exposes connectors as tools.

Evaluating models and applications

The outline calls out four things to evaluate for: fabrications, relevance, quality and safety. Those map onto evaluator families directly.

Concern in the questionEvaluator
"The answer states facts not in the source documents"Groundedness
"The answer is true but does not address the question"Relevance
"We retrieved the wrong documents"Retrieval / document retrieval
"The writing is rambling or incoherent"Coherence, fluency
"Could it produce harmful content?"Safety evaluators, plus the AI red teaming agent (PyRIT)
"Did the agent call the right tool with the right arguments?"Tool call accuracy
"Did the agent understand what was asked?"Intent resolution
"Did the agent finish the job and stay on task?"Task completion, task adherence
"It must satisfy our own domain rule"Custom evaluator
Python · evaluation
from azure.ai.evaluation import (
    GroundednessEvaluator, RelevanceEvaluator, evaluate,
)

model_config = {
    "azure_endpoint": AZURE_ENDPOINT,
    "azure_deployment": "gpt-5-mini",   # the judge model
}

results = evaluate(
    data="golden_questions.jsonl",       # query, context, response, ground_truth
    evaluators={
        "groundedness": GroundednessEvaluator(model_config),  # catches fabrications
        "relevance": RelevanceEvaluator(model_config),
    },
    azure_ai_project=PROJECT_ENDPOINT,   # publishes results to the portal
)

# Fail the CI job on regression rather than eyeballing a dashboard.
assert results["metrics"]["groundedness.groundedness"] >= 4.0

Evaluation vocabulary that shows up in distractors

Benchmarks compare candidate models on public or your own data — that is model selection. Evaluations score your application's outputs against a dataset — that is pre-production. Continuous evaluation samples live traffic and scheduled evaluation re-runs a fixed dataset to detect drift — that is post-production. Cluster analysis groups failures so you can see the pattern.

2.2 Build agents by using Foundry

Roles, goals, conversation tracking and tool schemas

An agent definition is four decisions, and the exam tests each separately.

DecisionWhere it livesWhat good looks like
Role and goal instructions Persona, scope, tone, the boundaries of what it will answer, what to do when it cannot, and how to cite
Model model — the deployment name Swappable without changing the rest of the definition
Conversation tracking A conversation id per user session; or previous_response_id for a simple chain; or your own store Server-side conversations are the default. Bring your own Cosmos DB when you must own the data
Tool schemas tools — JSON Schema per function, or built-in tool configs Precise names, descriptions that say when to use and when not to, enums over free strings, required set honestly
sequenceDiagram
  autonumber
  participant U as User
  participant R as Agent runtime
  participant M as Model
  participant T as Tool

  U->>R: responses.create(conversation, input)
  R->>R: Guardrail — user input
  R->>M: instructions + conversation items + tool schemas
  M-->>R: tool_call(name, arguments)
  R->>R: Guardrail — tool call (agents, preview)
  R->>T: Invoke tool
  T-->>R: Tool result
  R->>R: Guardrail — tool response (agents, preview)
  R->>M: Conversation + tool result
  M-->>R: Final message
  R->>R: Guardrail — output
  R-->>U: Response + citations, persisted as items
One agent turn. The guardrail runs at up to four points, and everything the agent does is persisted as items on the conversation.

Function calling

Function calling is client-side: the model decides that a function should be called and with which arguments; your code executes it and returns the result. That is why function calling cannot live inside a toolbox — the execution happens on your side.

Python · function tool
# A function tool is a JSON Schema the model can choose to call.
# The schema IS the prompt for the tool: names and descriptions decide
# whether the model picks it correctly.
create_ticket = {
    "type": "function",
    "name": "create_support_ticket",
    "description": (
        "Create a support ticket in ServiceNow. Use only after the user has "
        "confirmed the summary. Do not use for questions that documentation answers."
    ),
    "parameters": {
        "type": "object",
        "properties": {
            "title": {"type": "string", "description": "Short one-line summary"},
            "severity": {
                "type": "string",
                "enum": ["low", "medium", "high", "critical"],
                "description": "Business impact, not user frustration",
            },
            "description": {"type": "string"},
        },
        "required": ["title", "severity", "description"],
        "additionalProperties": False,
    },
}

response = openai.responses.create(
    model="gpt-5-mini",
    input="Raise a high severity ticket: the checkout page is returning 500s.",
    tools=[create_ticket],
)

for item in response.output:
    if item.type == "function_call":
        args = json.loads(item.arguments)
        result = servicenow.create(**args)
        # Feed the result back in, referencing the call id.
        response = openai.responses.create(
            model="gpt-5-mini",
            previous_response_id=response.id,
            input=[{
                "type": "function_call_output",
                "call_id": item.call_id,
                "output": json.dumps(result),
            }],
        )
print(response.output_text)

Tool descriptions are prompt engineering

"The agent calls the wrong tool" or "the agent calls the API when it should have answered from documentation" is almost never fixed by changing the model. The fix is better tool names and descriptions, enums instead of free-text parameters, and instructions that state the selection rule. At scale (dozens of tools), the answer is tool search in a toolbox, which hides tools behind tool_search and call_tool meta-tools so definitions stop consuming the context window.

Structured outputs

When downstream code consumes the output, constrain generation to a schema rather than parsing prose. This is also the answer to "produce structured JSON output" in Skill 4.

Python · structured output
from pydantic import BaseModel

class Ticket(BaseModel):
    title: str
    severity: str
    affected_service: str
    customer_impacting: bool

# Structured outputs constrain generation to the schema, so downstream code
# never has to parse free text or defend against a stray markdown fence.
response = openai.responses.parse(
    model="gpt-5-mini",
    input=email_body,
    text_format=Ticket,
)
ticket = response.output_parsed
print(ticket.severity, ticket.customer_impacting)

Tools and toolboxes

A toolbox curates tools once and exposes them through a single managed MCP-compatible endpoint, with central authentication, guardrails, observability and versioning. Agents point at the toolbox, so you can add, remove or reconfigure tools — or promote a new default version — without changing or redeploying agent code. Toolboxes are Foundry-homed but not Foundry-bound: any MCP-compatible runtime (Agent Framework, LangGraph, your own code) can consume one.

ToolDoesIn a toolbox?
MCPConnect a remote Model Context Protocol server✔
Web searchPublic web results. Start here before Grounding with Bing✔
Azure AI SearchRetrieve from your index with inline citations✔
File searchSearch project files and vector stores✔
Code interpreterRun code in a sandbox for data work and charts✔
OpenAPICall any REST API from its spec✔
Agent-to-agent (A2A)Call another agent over the A2A protocol✔
Browser automationDrive a real browser✔
Fabric IQ / Work IQStructured data in Fabric; Microsoft 365 work context✔
Tool search previewSurface only relevant tools at runtime✔ (toolbox only)
Skills previewVersioned, immutable multi-step workflows the agent invokes like a tool✔ (toolbox only)
Function callingYour own code, executed client-side✘ — client-side execution
Grounding with Bing / Bing Custom SearchReal-time public web with source attribution✘
Computer use, image generation, SharePoint, Fabric data agent, Azure FunctionsDirect integration only✘

Grounding with Bing has compliance strings attached

Grounding with Bing Search and Bing Custom Search are First Party Consumption Services governed by the Grounding with Bing terms, not the Microsoft Data Protection Addendum. Data sent to them flows outside the Azure compliance and geo boundary, and using them waives elevated Government Community Cloud commitments including data sovereignty. They also cost extra. If a scenario stresses data residency or sovereignty, Bing grounding is the wrong answer — and Bing Custom Search is the one that restricts grounding to a chosen subset of the web.

Tools vs skills is a distinction the outline implies: tools define what an agent can do; skills define how it performs a multi-step task. Skills are versioned and immutable, and agents discover them automatically through MCP resources at startup.

Conversation memory

Python · conversation state
# Server-side conversation: history is maintained for you.
conversation = openai.conversations.create()

r1 = openai.responses.create(conversation=conversation.id, input="Book me a room in Lisbon.")
r2 = openai.responses.create(conversation=conversation.id, input="Make it two nights.")
# r2 knows what "it" refers to.

# Lightweight alternative: chain by response id, no conversation object.
r1 = openai.responses.create(model="gpt-5-mini", input="Summarise this contract.")
r2 = openai.responses.create(model="gpt-5-mini", previous_response_id=r1.id, input="Now list the risks.")

Semantic memory is the layer above: a memory provider extracts durable facts and summaries from conversations, stores them by scope (typically a stable user or tenant id your application owns) and retrieves the relevant ones in later sessions. Extraction is asynchronous — if the next step depends on a newly written memory, you must wait for it. Keep exact transcripts in a history provider if you need complete records; memory deliberately stores facts, not the conversation.

Multi-agent orchestration

flowchart TB
  subgraph SEQ["Sequential"]
    A1["Extract"] --> A2["Validate"] --> A3["Summarise"]
  end
  subgraph CON["Concurrent"]
    C0["Fan out"] --> C1["Legal"] & C2["Finance"] & C3["Technical"]
    C1 & C2 & C3 --> C4["Aggregate"]
  end
  subgraph HAND["Handoff"]
    H0["Triage"] -->|"transfers ownership"| H1["Refunds"]
    H0 -->|"transfers ownership"| H2["Orders"]
  end
  subgraph TOOLS["Agent-as-tool"]
    T0["Primary agent<br/>keeps ownership"] --> T1["Specialist<br/>returns a result"]
    T1 --> T0
  end
Four patterns. The one the exam most often distinguishes is handoff (ownership transfers) versus agent-as-tool (ownership is retained).
PatternControl flowUse it when
SequentialFixed order, each output feeds the nextDeterministic pipelines, staged approvals, auditability
ConcurrentFan out in parallel, then aggregateIndependent perspectives on the same input; latency matters
HandoffAn agent transfers ownership of the whole task; the receiver has full context; no central authorityTriage and routing to specialists, dynamic escalation
Group chatAgents collaborate in a shared conversation, control passed by context or rulesDebate, review, fallback
MagenticA manager agent dynamically coordinates specialistsOpen-ended problems with no fixed plan
Agent-as-toolA primary agent delegates a subtask and keeps ownership; the specialist returns a resultA specialist capability inside a larger task the primary agent still owns

Handoff vs agent-as-tool, stated plainly

  • Control flow: handoff passes control permanently; agent-as-tool returns control to the caller.
  • Task ownership: the receiving agent owns the task after a handoff; the primary agent retains responsibility with agent-as-tool.
  • Context: handoff gives the receiver the full conversation; agent-as-tool passes only what the primary chooses to share.
  • Scenario cue: "route the customer to the refunds specialist who then handles the case" → handoff. "Ask the translation agent to translate this paragraph, then carry on" → agent-as-tool.

Autonomy, safeguards and approval controls

"Build autonomous or semi-autonomous workflows with safeguards and approval flow controls" is an objective in its own right. The mechanisms:

  • Approval-required tools — mark a tool so the workflow pauses and emits an approval request before it executes. This is the standard answer for refunds, purchases, deletions and anything irreversible.
  • Autonomous mode with turn limits — let an agent proceed without the user for a bounded number of turns, so it cannot loop indefinitely.
  • Human-in-the-loop request/response — a request port that pauses the workflow for free-form human input, not just approve/reject.
  • Checkpointing — persist workflow state so an approval that arrives hours or days later can resume the run. Essential for long-running processes.
  • Guardrails on tool call and tool response — scan what the agent sends to tools and what comes back.
  • Scoped agent identity and egress controls — limit what the agent can reach at all.
Python · approval gate
from typing import Annotated
from agent_framework import tool

# The workflow pauses and emits an approval request before this ever runs.
@tool(approval_mode="always_require")
def process_refund(order_number: Annotated[str, "Order number to refund"]) -> str:
    """Process a refund for the given order."""
    return f"Refund processed for {order_number}."

Monitoring agents and error analysis

  • Tracing shows every model call, tool invocation and decision in one timeline — the only practical way to debug "why did it do that?".
  • Agent evaluators — intent resolution, tool call accuracy, task adherence, task completion — turn behaviour into numbers.
  • Monitoring dashboards track latency, token consumption, error rates and quality scores in production.
  • Cluster analysis groups failing evaluation cases so you fix a class of problem rather than one row.
  • Agent optimizer preview evaluates behaviour and automatically generates better instructions, skills, tool descriptions and model selections.

Error analysis, in the order that works

Open the trace. Did it misunderstand (intent resolution low) → fix instructions. Did it pick the wrong tool (tool call accuracy low) → fix tool names, descriptions and schemas. Did the tool fail or return junk → fix the integration, not the prompt. Did it have the right data and still answer badly (groundedness low) → fix the prompt or the model. Did it stop early (task completion low) → check turn limits, token limits and instructions.

2.3 Optimise and operationalise

Prompt engineering

Techniques the exam names, and what each is actually for:

TechniqueWhat it doesReach for it when
Clear system instructionsSets persona, scope, tone, refusal behaviour and output contractAlways — it is the highest-leverage change available
Few-shot examplesDemonstrates the format and edge casesOutput shape or style is hard to describe but easy to show
Chain of thoughtAsks for reasoning steps before the answerMulti-step reasoning. Note: reasoning models do this internally — prompting them to "think step by step" adds little
Grounding / context injectionPuts retrieved facts in the promptAnything factual about private or recent data
Structured outputsConstrains generation to a JSON SchemaMachine-consumed output — more reliable than "respond in JSON"
Delimiters and spotlightingMarks untrusted content as data rather than instructionsAny prompt containing retrieved or user-supplied content — the XPIA defence
Prompt cachingReuses a stable prefix across callsLong, unchanging system prompts at volume — a cost and latency lever

Model parameters

ParameterEffectGuidance
temperatureRandomness of sampling. 0 is near-deterministic; higher is more variedLow for extraction, classification and RAG; higher for creative copy
top_pNucleus sampling — restricts to the top probability massTune one of temperature or top_p, not both
max_output_tokensCaps the response lengthAlso feeds the TPM rate-limit estimate, so an inflated value causes needless 429s
frequency_penaltyDiscourages repeating the same tokensFixes visible repetition
presence_penaltyEncourages introducing new topicsFixes a model that keeps circling one idea
stopSequences that end generationStructured or templated output
seedBest-effort reproducibilityTesting and evaluation runs
reasoning effortHow long a reasoning model thinksTrades latency and cost for accuracy

Determinism questions have a standard answer

"Responses vary between identical runs; we need consistency for a compliance workflow" → temperature 0 (or very low), optionally a seed, plus structured outputs to lock the shape. Temperature 0 is not a guarantee of identical text — "best effort", not "reproducible" — so if the question demands a stable schema, structured outputs is the stronger answer.

Reflection, chain-of-thought evaluation and self-critique

An explicit objective. These are patterns you implement, not switches you flip:

  • Self-critique — the model produces an answer, then a second pass criticises it against explicit criteria and revises. Costs an extra call or two; measurably improves quality on hard tasks.
  • Reflection loop — generate → evaluate → revise, repeated until a quality bar is met or an iteration cap is hit. Always set the cap.
  • Chain-of-thought evaluation — score the reasoning trace, not just the final answer, to find where a multi-step task went wrong.
  • LLM-as-judge — a separate model (the judge, configured in the evaluator) scores outputs. This is how the built-in AI-assisted evaluators work.
  • Agent optimizer preview — the managed version: it evaluates behaviour and rewrites instructions and tool descriptions for you.

Observability: tracing, tokens, safety, latency

Python · tracing setup
from azure.monitor.opentelemetry import configure_azure_monitor

# Traces go to the project's Application Insights connection.
connection_string = project.telemetry.get_application_insights_connection_string()
configure_azure_monitor(connection_string=connection_string)

# Opt in to recording prompt and completion content in traces.
# Off by default: the content may contain customer data.
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
SignalTells you
Traces (OpenTelemetry spans)The full call tree: model calls, tool calls, retrieval, decisions, errors
Token analyticsInput/output tokens per call and per user — the cost driver, and the input to right-sizing
Latency breakdownWhich span is slow: retrieval, a tool, or generation. Usually retrieval or a slow tool, not the model
Safety signalsGuardrail annotations and blocks, by category and severity
Quality scoresContinuous and scheduled evaluation results over time

Orchestrating multiple models and hybrid systems

The last bullet of the skill: "orchestrate multiple models, flows, or hybrid LLM and rules engines". The patterns worth naming:

  • Model routing / cascading — a cheap small model handles the easy majority and escalates hard cases to a larger model. The standard answer to "cut cost without losing quality".
  • Specialist models per stage — embeddings for retrieval, a reasoning model for planning, a fast model for the user-facing reply.
  • Hybrid LLM + rules — deterministic business rules decide anything regulated or numeric; the model handles language. If a question involves eligibility, pricing or compliance thresholds, the rules engine decides and the model explains — never the reverse.
  • Gateway-level orchestration — Azure API Management as an AI gateway for load balancing across deployments and regions, token limits and quota enforcement.

Skill 2 rapid-fire review

  • Prompt agent = declarative and fully managed. Hosted agent = your container, plus container compute cost. Responses API direct = ephemeral, no agent resource.
  • Conversations hold items; previous_response_id is the lightweight alternative.
  • Function calling executes client-side, which is why it cannot live in a toolbox.
  • Toolbox = one MCP endpoint, central auth, guardrails, versioning; update tools without redeploying agents.
  • Tool search solves "too many tools inflate the context window and confuse selection".
  • Handoff transfers ownership; agent-as-tool returns control.
  • Irreversible actions → approval-required tools plus checkpointing for long waits.
  • Fabrication → groundedness. Wrong tool → tool call accuracy. Misunderstood → intent resolution.
  • Tune temperature or top_p, not both; an inflated max_output_tokens causes 429s.
  • Grounding with Bing sends data outside the Azure compliance boundary.