diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index fcaeac4..3984251 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -# Contoso Travel Concierge Workshop — dev container image. +# Agent Optimization Workshop — dev container image. # # Base image is pinned here so we can customize freely later (system packages, # CLIs, tools) without touching devcontainer.json. Post-create actions (env @@ -12,7 +12,12 @@ ENV DEBIAN_FRONTEND=noninteractive \ # System tools we use across labs: curl (already present), jq (parsing outputs), # git (already present), zip/unzip (bundling artifacts). -RUN apt-get update && apt-get install -y --no-install-recommends \ +# +# The base image pre-configures the Yarn apt repo, whose signing key has since +# rotated and now breaks `apt-get update` (NO_PUBKEY 62D54FD4003F6525). We don't +# use Yarn, so remove that source before updating. +RUN rm -f /etc/apt/sources.list.d/yarn.list /usr/share/keyrings/yarn*.gpg \ + && apt-get update && apt-get install -y --no-install-recommends \ jq \ zip \ unzip \ diff --git a/.devcontainer/README.md b/.devcontainer/README.md deleted file mode 100644 index 8a751b5..0000000 --- a/.devcontainer/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Devcontainer - -Reproducible dev environment for the workshop. - -- **Base image:** `mcr.microsoft.com/devcontainers/python:1-3.13-bookworm` -- **Customization:** all in [`Dockerfile`](./Dockerfile) — edit here to add - system packages, CLIs, or tools. -- **First-run setup:** all in [`post-create.sh`](./post-create.sh) — edit here - to add Python deps, `azd` extensions, one-time verifications. - -Rebuild the container after editing either file: **VS Code → Command Palette → -Dev Containers: Rebuild Container.** - -## What's installed - -| Tool | Purpose | -|------|---------| -| Python 3.13 | Runtime | -| `az` (Azure CLI) | Authentication, resource inspection | -| `azd` (Azure Developer CLI) | Provisioning + hosted agent deploy | -| `gh` (GitHub CLI) | Repo operations | -| `jq`, `zip`, `unzip` | Shell utilities | - -## Persistent coach state - -Coach progress lives at `~/.contoso-coach/progress.json`. The devcontainer -mounts a named volume there so bookmarks and completion history survive -container rebuilds. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 56db2b0..ac1085a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,25 +1,18 @@ { - "name": "Contoso Travel Concierge Workshop", + "name": "Agent Optimization Workshop", "build": { "dockerfile": "Dockerfile", "context": "." }, "remoteUser": "vscode", "postCreateCommand": "bash .devcontainer/post-create.sh", + "customizations": { "vscode": { "extensions": [ "ms-python.python", - "ms-python.vscode-pylance", "ms-azuretools.vscode-docker", - "ms-azuretools.vscode-azurecontainerapps", - "ms-azuretools.vscode-bicep", - "github.copilot", - "github.copilot-chat", - "github.vscode-github-actions", - "davidanson.vscode-markdownlint", - "bierner.markdown-mermaid", - "redhat.vscode-yaml" + "ms-windows-ai-studio.windows-ai-studio" ], "settings": { "python.defaultInterpreterPath": "/usr/local/bin/python", @@ -34,8 +27,13 @@ "remoteEnv": { "PYTHONDONTWRITEBYTECODE": "1" }, - "mounts": [ - // Persist coach progress between container rebuilds - "source=${localWorkspaceFolderBasename}-coach,target=/home/vscode/.contoso-coach,type=volume" - ] + "features": { + "ghcr.io/devcontainers/features/azure-cli:1": {}, + "ghcr.io/devcontainers/features/copilot-cli:1": {}, + "ghcr.io/azure/azure-dev/azd:0": {}, + "ghcr.io/devcontainers/features/github-cli:1": {} + } + + // Persist coach progress between container rebuilds + // "mounts": [ "source=${localWorkspaceFolderBasename}-coach,target=/home/vscode/.contoso-coach,type=volume"] } diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index ab1c0e4..63ae198 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -8,6 +8,7 @@ set -euo pipefail log() { printf "\n▸ %s\n" "$*"; } +# Install Dependencies log "Installing workshop Python dependencies" pip install --upgrade pip if [[ -f requirements.txt ]]; then @@ -17,17 +18,26 @@ if [[ -f requirements-dev.txt ]]; then pip install -r requirements-dev.txt fi -log "Ensuring azd extensions are up to date" +# Update Tools +log "Ensuring azd + the hosted-agents extension are ready" if command -v azd >/dev/null 2>&1; then azd version || true - # Hosted-agents extension used in Fundamentals lab 05. - # TODO(nitya): pin exact extension version once labs stabilize. - azd ext install azure.ai.agents || true - azd ext list || true + # Hosted-agent commands (`azd ai agent`, `azd deploy` for host: azure.ai.agent) + # come from the azure.ai.agents extension used in Fundamentals lab 05. + azd config set alpha.extensions on >/dev/null 2>&1 || true + if azd extension list --installed --output json 2>/dev/null | grep -q "azure.ai.agents"; then + echo "azure.ai.agents already installed — upgrading to the latest version" + azd extension upgrade azure.ai.agents || true + else + echo "Installing azure.ai.agents extension" + azd extension install azure.ai.agents || true + fi + azd extension list --installed || true else echo "⚠️ azd not installed — skipping extension setup" fi +# Prepare Environment log "Making workshop scripts executable" chmod +x scripts/*.sh 2>/dev/null || true @@ -35,9 +45,10 @@ log "Preparing coach progress directory" mkdir -p /home/vscode/.contoso-coach touch /home/vscode/.contoso-coach/.keep -log "Running spec tests as a smoke check" -if command -v pytest >/dev/null 2>&1; then - pytest -q || echo "⚠️ spec tests reported issues — see output above" -fi +# Python Dependencies +# log "Running spec tests as a smoke check" +# if command -v pytest >/dev/null 2>&1; then +# pytest -q || echo "⚠️ spec tests reported issues — see output above" +# fi -log "Devcontainer ready. Open .github/PLAN.md or README.md to get started." +log "Devcontainer ready. Open README.md to get started." diff --git a/.github/PLAN.md b/.github/PLAN.md index 466de4e..cf40da4 100644 --- a/.github/PLAN.md +++ b/.github/PLAN.md @@ -248,8 +248,11 @@ guardrail. ## Open questions -- Exact model list (agent model, evaluator model, embedding?) — decide during - Fundamentals authoring +- ~~Exact model list (agent model, evaluator model, embedding?)~~ **Decided:** + concierge `gpt-5.4-mini` (version `2026-03-17`) + judge `gpt-5.4` (version + `2026-03-05`), both GlobalStandard, auto-deployed by `azd up` + (`infra/main.bicep`). Model region/version/SKU reference: + - Whether to port the LAB540 CSVs as-is (rebranded to Contoso) or curate a smaller eval-friendly subset diff --git a/.gitignore b/.gitignore index 83972fa..d99a8b0 100644 --- a/.gitignore +++ b/.gitignore @@ -195,9 +195,9 @@ cython_debug/ .abstra/ # Visual Studio Code -# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore -# and can be added to the global gitignore or merged into this file. However, if you prefer, +# and can be added to the global gitignore or merged into this file. However, if you prefer, # you could uncomment the following to ignore the entire vscode folder # .vscode/ # Temporary file for partial code execution diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2a2b41d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,20 @@ +# Contributing + +> 🚧 This document is a work in progress. Please wait for final guidance to be released. + +## Process + +This is a **living resource** that is built and maintained with the help of coding agents. Follow these steps when you want to propose an addition or modification: + +1. Update [`.github/PLAN.md`](./.github/PLAN.md) with the intent +2. Update the relevant spec in [`specs/`](./specs/) so tests know the new truth +3. Update or add tests in [`tests/`](./tests/) +4. Implement the change +5. `pytest -q` passes → open a PR + +CI runs the spec suite on every PR +([`.github/workflows/verify-course.yml`](./.github/workflows/verify-course.yml)). + +## Feedback + +Issues and PRs welcome. See [`LICENSE`](./LICENSE) for terms. diff --git a/README.md b/README.md index de72331..b2b0e1b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,22 @@ +
+ + + + +🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛ + +### 🚧 Work in Progress 🚧 + +**This workshop is under active development and testing.**.
+Please wait till this banner is removed before using it for self-guided or instructor-led delivery. + +🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛🟨⬛ + + +
+
+
+
# Build, Evaluate & Optimize AI Agents with Microsoft Foundry & GitHub Copilot @@ -6,24 +25,22 @@
-> ⚠️ **Fictional scenario.** The Contoso Travel Concierge agent, its dataset -> (flights, hotels, car rentals), and all company names used in this workshop -> are entirely **fictitious** and created for educational purposes only. They -> do not represent — and should not be interpreted as representing — any real -> travel provider, product, price, or availability. -> 🤖 **Built with coding agents + human-in-the-loop.** This workshop was -> developed collaboratively using AI coding agents (GitHub Copilot and related -> tools) under continuous human oversight and review. All content, code, -> datasets, and pedagogy have been curated and validated by a human maintainer -> before publication. Contributions follow the same process. + +> **Scenario.** Contoso is a fictitious enterprise retail company serving customers worldwide. Contoso Travel Concierge is their internal travel planning tool, used by employees to book flights, cars and hotels - and manage expenses - while ensuring they are in compliance with company travel policies. +> +> **Workshop.** This workshop was built with the help of coding agents grounded in our docs, with human oversight and review for correctness and consistency. If you find any discrepancies or have questions, [please file an appropriate issue](https://github.com/microsoft-foundry/agent-optimization-workshop/issues) for our attention. + +
## Overview -A hands-on course that walks you through the full **Agent DevOps loop** on -Microsoft Foundry — from provisioning to deployment to continuous optimization -— using a single running scenario: the **Contoso Travel Concierge**, a -fictitious multi-agent travel assistant. +Building an AI agent on Microsoft Foundry can be fairly straightforward. Create a new project, deploy a relevant model, configure agent instructions and tools - then deploy it to get an endpoint you can send requests to (from a UI-based playground or a code-based client). + +But agent behaviors can be non-deterministic. Building reliable agents - that meet required cost, latency and quality targets - requires end-to-end observability. Start with evaluations that assess the quality, performance and safety of your agent with built-in and custom metrics. Use tracing (logs) to debug issues and build your intuition for where your agent incurs cost and latency. And use application insights to understand your agent performance in production, at scale. + +Real-world deployments can also help identify agent drift from the desired performance targets. For instance, models may evolve or new edge cases may be revealed from actual usage. Keeping your agent operating correctly while consistemtly meeting desired criteria - requires _continuous optimization_, reflected by the **Agent DevOps loop** shown below. + ```mermaid flowchart LR @@ -33,6 +50,9 @@ flowchart LR Monitor --> Protect([Protect]) --> Evaluate ``` +This repository provides a series of hands-on labs that walks you through this loop on Microsoft Foundry — from provisioning to deployment to continuous optimization — usingthe **Contoso Travel Concierge** multi-agent scenario. + + The course is organized in three phases: | Phase | What you do | Time | @@ -41,30 +61,50 @@ The course is organized in three phases: | **Core Labs** | Observe → Evaluate → Optimize → Monitor on the Prompt Agent; capstone on the Hosted Agent | ~90 min | | **More Labs** | Single-question deep dives against the deployed agents | 20–30 min each | -A repo-scoped [**workshop-coach**](./.github/agents/workshop-coach.agent.md) -GitHub Copilot agent supports self-guided learners — it tracks your progress, -guides you through the next step, and never does the task for you. -Full course design lives in [**`.github/PLAN.md`**](./.github/PLAN.md). +> **EXPERIMENTAL**:
+> The repository is configured with a [**workshop-coach**](./.github/agents/workshop-coach.agent.md) agent (using GitHub Copilot) that self-guided learners can use to ask for explanations or debug issues, without losing track of their progress. The coach guides you through the next step but never does the task for you, so you learn by doing. + +
## Quickstart -The fastest path from zero to running the first lab: +The repository is configured with a [`.devcontainer/`](./.devcontainer/) that defines the default Python environment and dependencies required to run the exercises. The fastest way to get started and running your first lab is: + +1. **Fork the repo** to your profile to get a sandbox you can modify +1. **Launch the Dev Container** using GitHub Codespaces (browser) or Docker Desktop (device) + +This should automatically invoke the scripts to install Python dependencies, update required tooling, and initialize environment for lab execution. Wait till you see an active VS Code terminal - and use these commands to verify installation status. + +```bash +# Python 3.13+ +python --version -1. **Open in a Codespace or Dev Container.** The [`.devcontainer/`](./.devcontainer/) - provisions Python 3.13, `az`, `azd`, `gh`, and coach state persistence. -2. **Install workshop deps** (post-create runs this automatically): - ```bash - pip install -r requirements.txt - ``` -3. **Open [`labs/fundamentals/00-overview.md`](./labs/fundamentals/00-overview.md)** - and follow the trail. +# Azure CLI ("azure-cli": "2.89.0" or higher) +az version + +# Azure Developer CLI (azd version 1.30.0 or higher) +azd version + +# GitHub Copilot CLI (GitHub Copilot CLI 1.0.78. or higher) +copilot version + +# GitHub CLI (gh version 2.97.0 or higher) +gh --version +``` -Prefer local setup? You need Python 3.11+, `az`, `azd`, and `gh` on your PATH. +> Prefer local setup? Create a virtual environment and install the tools manually. You will need Python 3.13+, `az`, `azd`, `copilot` and `gh` on your PATH. + +You are ready to get started on the first lab. +- **Open [`labs/fundamentals/00-overview.md`](./labs/fundamentals/00-overview.md)** + and follow the trail from there. + + +
## Core Labs -Every lab teaches **one node** of the Agent DevOps loop. Complete them in order. +The core labs track takes you through the steps of the Agent DevOps loop using our Contoso Travel Concierge scenario. Every lab teaches **one node** of the Agent DevOps loop. Complete them in order. | # | Lab | Loop node | |---|-----|-----------| @@ -77,9 +117,11 @@ Every lab teaches **one node** of the Agent DevOps loop. Complete them in order. Fundamentals prerequisites: [`labs/fundamentals/`](./labs/fundamentals/). +
+ ## More Labs -Extensible library of one-question deep dives — take in any order after Core. +This section provides an evolving library of labs that offer "one-question deep dives". You should be able to explore them in any order once you complete the Core Labs. Watch for frequent updates to this section to learn new features or best practies. | # | Lab | Loop node | |---|-----|-----------| @@ -89,19 +131,8 @@ Extensible library of one-question deep dives — take in any order after Core. | 4 | [Datasets from real traces](./labs/more/trace-driven-datasets.md) | Evaluate | | … | … see [`labs/more/README.md`](./labs/more/README.md) for the full index | | -## Contributing - -This is a **living resource**. When you propose a change: - -1. Update [`.github/PLAN.md`](./.github/PLAN.md) with the intent -2. Update the relevant spec in [`specs/`](./specs/) so tests know the new truth -3. Update or add tests in [`tests/`](./tests/) -4. Implement the change -5. `pytest -q` passes → open a PR - -CI runs the spec suite on every PR -([`.github/workflows/verify-course.yml`](./.github/workflows/verify-course.yml)). +
## Feedback -Issues and PRs welcome. See [`LICENSE`](./LICENSE) for terms. +Have questions, contributions or feedback? Watch for updates to the [CONTRIBUTING](./CONTRIBUTING.md) guide. diff --git a/artifacts/evaluators/reference/.gitkeep b/artifacts/evaluators/reference/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/artifacts/prompts/reference/prompt-agent-baseline-v1.md b/artifacts/prompts/reference/prompt-agent-baseline-v1.md index acfeb92..8eb41e4 100644 --- a/artifacts/prompts/reference/prompt-agent-baseline-v1.md +++ b/artifacts/prompts/reference/prompt-agent-baseline-v1.md @@ -18,9 +18,9 @@ Tokyo, Rome, and Cancún. Be warm, professional, and concise. You have three attached datasets: -- **flights.csv** — flights (id, airline, route, cabin, price, seats) -- **hotels.csv** — hotels (id, name, city, stars, nightly price, amenities) -- **car_rentals.csv** — rental vehicles (id, company, city, type, daily price) +- **Flights** — flights (id, airline, route, cabin, price, seats) +- **Hotels** — hotels (id, name, city, stars, nightly price, amenities) +- **Car rentals** — rental vehicles (id, company, city, type, daily price) When travelers ask travel questions, look up matching rows from the relevant dataset before answering. diff --git a/azure.yaml b/azure.yaml new file mode 100644 index 0000000..06af0c9 --- /dev/null +++ b/azure.yaml @@ -0,0 +1,37 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json +name: contoso-travel-concierge +metadata: + template: agent-optimization-workshop@1.0.0 +requiredVersions: + extensions: + azure.ai.agents: '>=1.0.0-beta.4' +infra: + provider: bicep + path: infra + module: main +services: + # Hosted agent (Lab 05). Deployed with `azd deploy contoso-travel-concierge` + # into the Foundry project provisioned by infra/. Requires hosted-agent + # hosting to be enabled first (see labs/fundamentals/05-deploy-hosted-agent.md): + # azd env set ENABLE_HOSTED_AGENTS true && azd provision + contoso-travel-concierge: + project: src + host: azure.ai.agent + language: python + kind: hosted + name: contoso-travel-concierge + description: Contoso Travel Concierge — an Agent Framework orchestrator that delegates to Flight, Hotel, and Car Rental specialist agents. + codeConfiguration: + runtime: python_3_13 + entryPoint: main.py + dependencyResolution: remote_build + container: + resources: + cpu: "0.25" + memory: 0.5Gi + environmentVariables: + - name: AZURE_AI_MODEL_DEPLOYMENT_NAME + value: ${AZURE_AI_MODEL_DEPLOYMENT_NAME} + protocols: + - protocol: responses + version: 2.0.0 diff --git a/data/json/car_rentals.json b/data/json/car_rentals.json new file mode 100644 index 0000000..53ed607 --- /dev/null +++ b/data/json/car_rentals.json @@ -0,0 +1,152 @@ +[ + { + "rental_id": "CT-CR-001", + "company": "Contoso Wheels", + "city": "Paris", + "car_type": "Economy", + "price_per_day_usd": "45.00", + "available": "true", + "pickup_date": "2025-08-15", + "return_date": "2025-08-30" + }, + { + "rental_id": "CT-CR-002", + "company": "Contoso Wheels", + "city": "Paris", + "car_type": "SUV", + "price_per_day_usd": "85.00", + "available": "true", + "pickup_date": "2025-08-15", + "return_date": "2025-08-30" + }, + { + "rental_id": "CT-CR-003", + "company": "EuroDrive", + "city": "Paris", + "car_type": "Luxury", + "price_per_day_usd": "150.00", + "available": "true", + "pickup_date": "2025-08-15", + "return_date": "2025-08-30" + }, + { + "rental_id": "CT-CR-004", + "company": "Contoso Wheels", + "city": "London", + "car_type": "Economy", + "price_per_day_usd": "50.00", + "available": "true", + "pickup_date": "2025-09-01", + "return_date": "2025-09-15" + }, + { + "rental_id": "CT-CR-005", + "company": "Contoso Wheels", + "city": "London", + "car_type": "SUV", + "price_per_day_usd": "95.00", + "available": "true", + "pickup_date": "2025-09-01", + "return_date": "2025-09-15" + }, + { + "rental_id": "CT-CR-006", + "company": "BritCars", + "city": "London", + "car_type": "Luxury", + "price_per_day_usd": "175.00", + "available": "false", + "pickup_date": "2025-09-01", + "return_date": "2025-09-15" + }, + { + "rental_id": "CT-CR-007", + "company": "Contoso Wheels", + "city": "Tokyo", + "car_type": "Economy", + "price_per_day_usd": "55.00", + "available": "true", + "pickup_date": "2025-10-05", + "return_date": "2025-10-20" + }, + { + "rental_id": "CT-CR-008", + "company": "NipponRide", + "city": "Tokyo", + "car_type": "Minivan", + "price_per_day_usd": "90.00", + "available": "true", + "pickup_date": "2025-10-05", + "return_date": "2025-10-20" + }, + { + "rental_id": "CT-CR-009", + "company": "NipponRide", + "city": "Tokyo", + "car_type": "Luxury", + "price_per_day_usd": "200.00", + "available": "true", + "pickup_date": "2025-10-05", + "return_date": "2025-10-20" + }, + { + "rental_id": "CT-CR-010", + "company": "Contoso Wheels", + "city": "Rome", + "car_type": "Economy", + "price_per_day_usd": "40.00", + "available": "true", + "pickup_date": "2025-11-01", + "return_date": "2025-11-15" + }, + { + "rental_id": "CT-CR-011", + "company": "ItaliaWheels", + "city": "Rome", + "car_type": "SUV", + "price_per_day_usd": "80.00", + "available": "true", + "pickup_date": "2025-11-01", + "return_date": "2025-11-15" + }, + { + "rental_id": "CT-CR-012", + "company": "ItaliaWheels", + "city": "Rome", + "car_type": "Luxury", + "price_per_day_usd": "160.00", + "available": "false", + "pickup_date": "2025-11-01", + "return_date": "2025-11-15" + }, + { + "rental_id": "CT-CR-013", + "company": "Contoso Wheels", + "city": "Canc\u00fan", + "car_type": "Economy", + "price_per_day_usd": "35.00", + "available": "true", + "pickup_date": "2025-12-20", + "return_date": "2026-01-05" + }, + { + "rental_id": "CT-CR-014", + "company": "SolRentals", + "city": "Canc\u00fan", + "car_type": "SUV", + "price_per_day_usd": "70.00", + "available": "true", + "pickup_date": "2025-12-20", + "return_date": "2026-01-05" + }, + { + "rental_id": "CT-CR-015", + "company": "SolRentals", + "city": "Canc\u00fan", + "car_type": "Minivan", + "price_per_day_usd": "65.00", + "available": "true", + "pickup_date": "2025-12-20", + "return_date": "2026-01-05" + } +] diff --git a/data/json/flights.json b/data/json/flights.json new file mode 100644 index 0000000..9a2169f --- /dev/null +++ b/data/json/flights.json @@ -0,0 +1,242 @@ +[ + { + "flight_id": "CT-FL-001", + "airline": "Contoso Skyways", + "origin": "Seattle", + "destination": "Paris", + "departure_date": "2025-08-15", + "departure_time": "08:30", + "arrival_time": "04:30+1", + "price_usd": "750.00", + "cabin_class": "Economy", + "available_seats": "42" + }, + { + "flight_id": "CT-FL-002", + "airline": "Contoso Skyways", + "origin": "Seattle", + "destination": "Paris", + "departure_date": "2025-08-15", + "departure_time": "14:00", + "arrival_time": "10:00+1", + "price_usd": "1450.00", + "cabin_class": "Business", + "available_seats": "12" + }, + { + "flight_id": "CT-FL-003", + "airline": "Contoso Skyways", + "origin": "Paris", + "destination": "Seattle", + "departure_date": "2025-08-22", + "departure_time": "11:00", + "arrival_time": "14:00", + "price_usd": "780.00", + "cabin_class": "Economy", + "available_seats": "38" + }, + { + "flight_id": "CT-FL-004", + "airline": "Contoso Skyways", + "origin": "Paris", + "destination": "Seattle", + "departure_date": "2025-08-22", + "departure_time": "18:00", + "arrival_time": "21:00", + "price_usd": "1520.00", + "cabin_class": "Business", + "available_seats": "8" + }, + { + "flight_id": "CT-FL-005", + "airline": "Atlantic Jet", + "origin": "New York", + "destination": "London", + "departure_date": "2025-09-01", + "departure_time": "19:00", + "arrival_time": "07:00+1", + "price_usd": "620.00", + "cabin_class": "Economy", + "available_seats": "55" + }, + { + "flight_id": "CT-FL-006", + "airline": "Atlantic Jet", + "origin": "New York", + "destination": "London", + "departure_date": "2025-09-01", + "departure_time": "22:00", + "arrival_time": "10:00+1", + "price_usd": "2100.00", + "cabin_class": "First", + "available_seats": "4" + }, + { + "flight_id": "CT-FL-007", + "airline": "Atlantic Jet", + "origin": "London", + "destination": "New York", + "departure_date": "2025-09-10", + "departure_time": "09:00", + "arrival_time": "12:00", + "price_usd": "650.00", + "cabin_class": "Economy", + "available_seats": "48" + }, + { + "flight_id": "CT-FL-008", + "airline": "Atlantic Jet", + "origin": "London", + "destination": "New York", + "departure_date": "2025-09-10", + "departure_time": "15:00", + "arrival_time": "18:00", + "price_usd": "1380.00", + "cabin_class": "Business", + "available_seats": "10" + }, + { + "flight_id": "CT-FL-009", + "airline": "Pacific Wings", + "origin": "San Francisco", + "destination": "Tokyo", + "departure_date": "2025-10-05", + "departure_time": "13:00", + "arrival_time": "17:00+1", + "price_usd": "890.00", + "cabin_class": "Economy", + "available_seats": "60" + }, + { + "flight_id": "CT-FL-010", + "airline": "Pacific Wings", + "origin": "San Francisco", + "destination": "Tokyo", + "departure_date": "2025-10-05", + "departure_time": "23:00", + "arrival_time": "05:00+2", + "price_usd": "2450.00", + "cabin_class": "Business", + "available_seats": "6" + }, + { + "flight_id": "CT-FL-011", + "airline": "Pacific Wings", + "origin": "Tokyo", + "destination": "San Francisco", + "departure_date": "2025-10-15", + "departure_time": "10:00", + "arrival_time": "06:00", + "price_usd": "920.00", + "cabin_class": "Economy", + "available_seats": "52" + }, + { + "flight_id": "CT-FL-012", + "airline": "Pacific Wings", + "origin": "Tokyo", + "destination": "San Francisco", + "departure_date": "2025-10-15", + "departure_time": "22:00", + "arrival_time": "18:00", + "price_usd": "2500.00", + "cabin_class": "First", + "available_seats": "3" + }, + { + "flight_id": "CT-FL-013", + "airline": "EuroStar Air", + "origin": "Chicago", + "destination": "Rome", + "departure_date": "2025-11-01", + "departure_time": "16:00", + "arrival_time": "08:00+1", + "price_usd": "710.00", + "cabin_class": "Economy", + "available_seats": "45" + }, + { + "flight_id": "CT-FL-014", + "airline": "EuroStar Air", + "origin": "Chicago", + "destination": "Rome", + "departure_date": "2025-11-01", + "departure_time": "20:00", + "arrival_time": "12:00+1", + "price_usd": "1680.00", + "cabin_class": "Business", + "available_seats": "9" + }, + { + "flight_id": "CT-FL-015", + "airline": "EuroStar Air", + "origin": "Rome", + "destination": "Chicago", + "departure_date": "2025-11-10", + "departure_time": "07:00", + "arrival_time": "12:00", + "price_usd": "740.00", + "cabin_class": "Economy", + "available_seats": "40" + }, + { + "flight_id": "CT-FL-016", + "airline": "EuroStar Air", + "origin": "Rome", + "destination": "Chicago", + "departure_date": "2025-11-10", + "departure_time": "14:00", + "arrival_time": "19:00", + "price_usd": "1700.00", + "cabin_class": "Business", + "available_seats": "7" + }, + { + "flight_id": "CT-FL-017", + "airline": "SunBird Airlines", + "origin": "Denver", + "destination": "Canc\u00fan", + "departure_date": "2025-12-20", + "departure_time": "06:00", + "arrival_time": "11:30", + "price_usd": "480.00", + "cabin_class": "Economy", + "available_seats": "70" + }, + { + "flight_id": "CT-FL-018", + "airline": "SunBird Airlines", + "origin": "Denver", + "destination": "Canc\u00fan", + "departure_date": "2025-12-20", + "departure_time": "10:00", + "arrival_time": "15:30", + "price_usd": "950.00", + "cabin_class": "Business", + "available_seats": "15" + }, + { + "flight_id": "CT-FL-019", + "airline": "SunBird Airlines", + "origin": "Canc\u00fan", + "destination": "Denver", + "departure_date": "2025-12-28", + "departure_time": "14:00", + "arrival_time": "18:30", + "price_usd": "510.00", + "cabin_class": "Economy", + "available_seats": "65" + }, + { + "flight_id": "CT-FL-020", + "airline": "SunBird Airlines", + "origin": "Canc\u00fan", + "destination": "Denver", + "departure_date": "2025-12-28", + "departure_time": "08:00", + "arrival_time": "12:30", + "price_usd": "980.00", + "cabin_class": "Business", + "available_seats": "11" + } +] diff --git a/data/json/hotels.json b/data/json/hotels.json new file mode 100644 index 0000000..131faf7 --- /dev/null +++ b/data/json/hotels.json @@ -0,0 +1,182 @@ +[ + { + "hotel_id": "CT-HT-001", + "name": "Le Marais Grand Hotel", + "city": "Paris", + "country": "France", + "star_rating": "5", + "price_per_night_usd": "320.00", + "amenities": "WiFi,Pool,Spa,Restaurant,Gym", + "available_rooms": "8", + "check_in_date": "2025-08-15", + "check_out_date": "2025-08-30" + }, + { + "hotel_id": "CT-HT-002", + "name": "Montmartre Budget Inn", + "city": "Paris", + "country": "France", + "star_rating": "3", + "price_per_night_usd": "95.00", + "amenities": "WiFi,Breakfast", + "available_rooms": "22", + "check_in_date": "2025-08-15", + "check_out_date": "2025-08-30" + }, + { + "hotel_id": "CT-HT-003", + "name": "Seine River Suites", + "city": "Paris", + "country": "France", + "star_rating": "4", + "price_per_night_usd": "210.00", + "amenities": "WiFi,Restaurant,Gym,River View", + "available_rooms": "14", + "check_in_date": "2025-08-15", + "check_out_date": "2025-08-30" + }, + { + "hotel_id": "CT-HT-004", + "name": "The Westminster Palace", + "city": "London", + "country": "United Kingdom", + "star_rating": "5", + "price_per_night_usd": "380.00", + "amenities": "WiFi,Pool,Spa,Restaurant,Concierge", + "available_rooms": "6", + "check_in_date": "2025-09-01", + "check_out_date": "2025-09-15" + }, + { + "hotel_id": "CT-HT-005", + "name": "Camden Town Hostel", + "city": "London", + "country": "United Kingdom", + "star_rating": "2", + "price_per_night_usd": "65.00", + "amenities": "WiFi,Shared Kitchen", + "available_rooms": "30", + "check_in_date": "2025-09-01", + "check_out_date": "2025-09-15" + }, + { + "hotel_id": "CT-HT-006", + "name": "Kensington Gardens Hotel", + "city": "London", + "country": "United Kingdom", + "star_rating": "4", + "price_per_night_usd": "245.00", + "amenities": "WiFi,Restaurant,Gym,Parking", + "available_rooms": "11", + "check_in_date": "2025-09-01", + "check_out_date": "2025-09-15" + }, + { + "hotel_id": "CT-HT-007", + "name": "Shibuya Sky Tower Hotel", + "city": "Tokyo", + "country": "Japan", + "star_rating": "5", + "price_per_night_usd": "290.00", + "amenities": "WiFi,Pool,Spa,Restaurant,Rooftop Bar", + "available_rooms": "10", + "check_in_date": "2025-10-05", + "check_out_date": "2025-10-20" + }, + { + "hotel_id": "CT-HT-008", + "name": "Asakusa Traditional Ryokan", + "city": "Tokyo", + "country": "Japan", + "star_rating": "4", + "price_per_night_usd": "180.00", + "amenities": "WiFi,Hot Spring,Traditional Breakfast,Garden", + "available_rooms": "5", + "check_in_date": "2025-10-05", + "check_out_date": "2025-10-20" + }, + { + "hotel_id": "CT-HT-009", + "name": "Shinjuku Business Hotel", + "city": "Tokyo", + "country": "Japan", + "star_rating": "3", + "price_per_night_usd": "110.00", + "amenities": "WiFi,Breakfast,Laundry", + "available_rooms": "25", + "check_in_date": "2025-10-05", + "check_out_date": "2025-10-20" + }, + { + "hotel_id": "CT-HT-010", + "name": "Roma Colosseum View", + "city": "Rome", + "country": "Italy", + "star_rating": "4", + "price_per_night_usd": "230.00", + "amenities": "WiFi,Restaurant,Rooftop Terrace,Breakfast", + "available_rooms": "12", + "check_in_date": "2025-11-01", + "check_out_date": "2025-11-15" + }, + { + "hotel_id": "CT-HT-011", + "name": "Trastevere Boutique B&B", + "city": "Rome", + "country": "Italy", + "star_rating": "3", + "price_per_night_usd": "125.00", + "amenities": "WiFi,Breakfast,Garden", + "available_rooms": "9", + "check_in_date": "2025-11-01", + "check_out_date": "2025-11-15" + }, + { + "hotel_id": "CT-HT-012", + "name": "Vatican Luxury Residence", + "city": "Rome", + "country": "Italy", + "star_rating": "5", + "price_per_night_usd": "410.00", + "amenities": "WiFi,Pool,Spa,Restaurant,Concierge,Gym", + "available_rooms": "4", + "check_in_date": "2025-11-01", + "check_out_date": "2025-11-15" + }, + { + "hotel_id": "CT-HT-013", + "name": "Canc\u00fan Beachfront Resort", + "city": "Canc\u00fan", + "country": "Mexico", + "star_rating": "5", + "price_per_night_usd": "350.00", + "amenities": "WiFi,Pool,Beach Access,Spa,All-Inclusive,Kids Club", + "available_rooms": "20", + "check_in_date": "2025-12-20", + "check_out_date": "2026-01-05" + }, + { + "hotel_id": "CT-HT-014", + "name": "Playa Del Sol Hotel", + "city": "Canc\u00fan", + "country": "Mexico", + "star_rating": "4", + "price_per_night_usd": "195.00", + "amenities": "WiFi,Pool,Beach Access,Restaurant", + "available_rooms": "16", + "check_in_date": "2025-12-20", + "check_out_date": "2026-01-05" + }, + { + "hotel_id": "CT-HT-015", + "name": "Centro Canc\u00fan Budget Stay", + "city": "Canc\u00fan", + "country": "Mexico", + "star_rating": "2", + "price_per_night_usd": "72.00", + "amenities": "WiFi,Breakfast,Parking", + "available_rooms": "35", + "check_in_date": "2025-12-20", + "check_out_date": "2026-01-05" + } +] diff --git a/infra/README.md b/infra/README.md index 107c608..ce21d99 100644 --- a/infra/README.md +++ b/infra/README.md @@ -24,8 +24,11 @@ If you're following the **portal path** instead - Naming derives from the `azd` environment name; abbreviations come from `abbreviations.json`. This keeps every resource under a single prefix so cleanup is one `azd down` away. -- Model deployment is **not** provisioned by this template on purpose. - Learners deploy models in `labs/fundamentals/03-deploy-models.md` so they - learn the model-picker flow. +- Model deployment **is** provisioned by this template. The golden path + auto-deploys **`gpt-5.4-mini`** (concierge) and **`gpt-5.4`** as + `gpt-5.4-judge` (both Global Standard, 100k TPM) so the `azd up` path needs + no portal step. To use different models, set the `AI_PROJECT_DEPLOYMENTS` env + var to a custom JSON array before `azd up` (see + `labs/fundamentals/03-deploy-models.md`). diff --git a/infra/main.bicep b/infra/main.bicep index 2c47b46..e49b7e8 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -52,7 +52,38 @@ param aiProjectConnectionCredentialsJson string = '{}' @description('List of resources to create and connect to the AI project') param aiProjectDependentResourcesJson string = '[]' -var aiProjectDeployments = json(aiProjectDeploymentsJson) +// Golden-path default: deploy the concierge model (gpt-5.4-mini) and a separate +// judge deployment on gpt-5.4 (used by AI-assisted evaluators in Core Lab 02) +// automatically so `azd up` needs no portal model step. Override by setting +// AI_PROJECT_DEPLOYMENTS to a custom JSON array to use different models. +var providedAiProjectDeployments = json(aiProjectDeploymentsJson) +var defaultAiProjectDeployments = [ + { + name: 'gpt-5.4-mini' + model: { + name: 'gpt-5.4-mini' + format: 'OpenAI' + version: '2026-03-17' + } + sku: { + name: 'GlobalStandard' + capacity: 100 + } + } + { + name: 'gpt-5.4-judge' + model: { + name: 'gpt-5.4' + format: 'OpenAI' + version: '2026-03-05' + } + sku: { + name: 'GlobalStandard' + capacity: 100 + } + } +] +var aiProjectDeployments = empty(providedAiProjectDeployments) ? defaultAiProjectDeployments : providedAiProjectDeployments var aiProjectConnections = json(aiProjectConnectionsJson) var aiProjectConnectionCreds = json(aiProjectConnectionCredentialsJson) var aiProjectDependentResources = json(aiProjectDependentResourcesJson) @@ -88,7 +119,7 @@ param existingApplicationInsightsResourceId string = '' param existingAppInsightsConnectionName string = '' // Tags that should be applied to all resources. -// +// // Note that 'azd-service-name' tags should be applied separately to service host resources. // Example usage: // tags: union(tags, { 'azd-service-name': }) @@ -184,6 +215,14 @@ output AZURE_AI_FOUNDRY_PROJECT_ID string = useExistingAiProject ? existingAiPro output AZURE_AI_ACCOUNT_NAME string = useExistingAiProject ? existingAiProject.outputs.aiServicesAccountName : aiProject.outputs.aiServicesAccountName output AZURE_AI_PROJECT_NAME string = useExistingAiProject ? existingAiProject.outputs.projectName : aiProject.outputs.projectName +// Primary model deployment name (first entry in the deployments list). Consumed by +// the hosted agent and referenced in labs/fundamentals/03-deploy-models.md. +output AZURE_AI_MODEL_DEPLOYMENT_NAME string = length(aiProjectDeployments) > 0 ? aiProjectDeployments[0].name : '' + +// Judge model deployment name (second entry, if present). Used by AI-assisted +// evaluators in Core Lab 02. +output AZURE_AI_JUDGE_DEPLOYMENT_NAME string = length(aiProjectDeployments) > 1 ? aiProjectDeployments[1].name : '' + // Endpoints output AZURE_AI_PROJECT_ENDPOINT string = useExistingAiProject ? existingAiProject.outputs.AZURE_AI_PROJECT_ENDPOINT : aiProject.outputs.AZURE_AI_PROJECT_ENDPOINT // Alias expected by the Foundry hosting runtime inside the container; keeping it diff --git a/labs/core/05-capstone-hosted.md b/labs/core/05-capstone-hosted.md index e819f72..03ce7cf 100644 --- a/labs/core/05-capstone-hosted.md +++ b/labs/core/05-capstone-hosted.md @@ -1,6 +1,6 @@ # Core Lab 05 — Capstone: apply the loop to the Hosted Agent -> **What you'll do:** Repeat Core Labs 01–04, but this time against the containerized **`contoso-concierge`** hosted agent. You drive it end-to-end. +> **What you'll do:** Repeat Core Labs 01–04, but this time against the containerized **`contoso-travel-concierge`** hosted agent. You drive it end-to-end. > **Time:** ~45 min · **Prerequisites:** [Core Lab 04](./04-monitor-portal.md) ## 🎯 Goal @@ -27,7 +27,7 @@ flowchart LR |---|---|---| | Structure | Single agent + attached knowledge | Concierge + 3 specialist sub-agents + tools | | Prompt lives in | Portal Instructions field | `src/instructions/concierge.md` (file, versioned) | -| Redeploy | Save in portal | `azd deploy contoso-concierge` | +| Redeploy | Save in portal | `azd deploy contoso-travel-concierge` | | Trace | One agent turn | Concierge → specialist → tool, per sub-agent | | Reset | Re-paste baseline | `./scripts/reset.sh` | @@ -41,25 +41,25 @@ Do each step yourself. Refer back to the linked lab if you get stuck. 1. **Reset to the pristine baseline.** ```bash ./scripts/reset.sh - azd deploy contoso-concierge --no-prompt + azd deploy contoso-travel-concierge --no-prompt ``` 2. **Observe.** Playground the three prompts from - [Core Lab 01](./01-observe-portal.md) against `contoso-concierge`. Open the + [Core Lab 01](./01-observe-portal.md) against `contoso-travel-concierge`. Open the **Trajectory** view for the multi-part prompt — this is where the Hosted Agent shines. Note which sub-agent looks weakest. 3. **Evaluate.** Following [Core Lab 02](./02-evaluate-portal.md), run a batch evaluation on - `contoso-concierge` against + `contoso-travel-concierge` against `artifacts/datasets/reference/evaluation-data-v1.jsonl`. Note the top failure pattern. 4. **Optimize.** Following [Core Lab 03](./03-optimize-skills.md), drive the `microsoft-foundry` Observe skill in Copilot Chat against - `contoso-concierge`. Let it edit `src/instructions/concierge.md`. + `contoso-travel-concierge`. Let it edit `src/instructions/concierge.md`. > 💡 Snapshot the new prompt into the agent's versioned history: > @@ -69,12 +69,12 @@ Do each step yourself. Refer back to the linked lab if you get stuck. 5. **Redeploy.** ```bash - azd deploy contoso-concierge --no-prompt + azd deploy contoso-travel-concierge --no-prompt ``` 6. **Monitor.** Following [Core Lab 04](./04-monitor-portal.md), open the Monitor tab for - `contoso-concierge`. Confirm the optimization landed and metrics moved. + `contoso-travel-concierge`. Confirm the optimization landed and metrics moved. 7. **Clean up (when you're truly done).** ```bash @@ -88,7 +88,7 @@ Do each step yourself. Refer back to the linked lab if you get stuck. - `src/instructions/concierge.md` differs from `src/instructions/versions/instructions-0.md`. - `src/instructions/versions/` has at least one new numbered snapshot. -- A batch re-evaluation of `contoso-concierge` shows the target metric +- A batch re-evaluation of `contoso-travel-concierge` shows the target metric **higher** than the pre-optimization run. - You can point at **which sub-agent's behavior changed** (flight, hotel, or car rental) based on the trajectory before vs. after. diff --git a/labs/fundamentals/00-overview.md b/labs/fundamentals/00-overview.md index 5de207e..21c71b7 100644 --- a/labs/fundamentals/00-overview.md +++ b/labs/fundamentals/00-overview.md @@ -54,6 +54,17 @@ Travel Concierge** orchestrates specialist sub-agents that read three CSVs: Every Core Lab targets one node of the loop. Together they teach the complete cycle you'll run whenever the agent underperforms. +| Node | What happens here | +|------|-------------------| +| **Plan** | Define the scenario, success criteria, and target cost/latency/quality metrics. | +| **Build** | Create the agent — model, instructions, and tools. | +| **Evaluate** | Assess quality, performance, and safety with built-in and custom metrics. | +| **Deploy** | Publish the agent to an endpoint you can call from a UI or code. | +| **Monitor** | Trace runs and watch production behavior at scale with Application Insights. | +| **Optimize** | Tune instructions, model, or tools to close gaps against the targets, then re-evaluate. | +| **Protect** | Guard against unsafe or adversarial inputs (red-teaming, guardrails) before re-evaluating. | + + ## ✅ Verify Answer these to yourself (no tool required): diff --git a/labs/fundamentals/01-provision-azd.md b/labs/fundamentals/01-provision-azd.md index 0458cab..b17cfa3 100644 --- a/labs/fundamentals/01-provision-azd.md +++ b/labs/fundamentals/01-provision-azd.md @@ -1,6 +1,9 @@ # Lab 01 — Provision Foundry with `azd up` -> **What you'll do:** Stand up a Foundry project, model, container registry, and Application Insights in your Azure subscription with a single command. +> **What you'll do:** Stand up a Foundry project, deploy the `gpt-5.4-mini` and +> `gpt-5.4-judge` models, and wire up Application Insights and Log Analytics in +> your Azure subscription with a single command. The container registry and +> hosted agent come later in [Lab 05](./05-deploy-hosted-agent.md). > **Time:** ~15 min · **Prerequisites:** [Lab 00](./00-overview.md) > > ⏩ **Taking the portal path instead?** Skip to [Lab 02](./02-provision-portal.md). @@ -29,12 +32,13 @@ flowchart LR Confirm you have: 1. An **Azure subscription** where you can create resources. -2. **`gpt-5.4-mini`** Global Standard quota in one of these regions: +2. **`gpt-5.4-mini`** and **`gpt-5.4`** Global Standard quota in one of these + regions: - **`eastus2`** ⭐ (default) - **`swedencentral`** (EU alternate) - **`northcentralus`** (US backup) 3. Either the workshop **devcontainer** open (see `.devcontainer/README.md`) - or `az`, `azd`, and Python 3.11+ installed locally. + or `az`, `azd`, and Python 3.13+ installed locally. > ⚠️ **Cost:** provisioning runs in **your** subscription and incurs cost. > You'll tear it all down with `azd down` at the end. @@ -46,50 +50,91 @@ Confirm you have: az login --use-device-code ``` Complete the device-code flow in the browser, then pick the subscription - you want to deploy into. + you want to deploy into. You can verify if you are logged in at any time using: + + ```bash + az account show + ``` 2. **Sign in with `azd`.** ```bash azd auth login --use-device-code ``` - Complete the same device-code flow. + Complete the same device-code flow. You can verify if you are logged in at any time using: + + ```bash + azd auth status + ``` 3. **Create an `azd` environment.** + + Run this from the **repo root** (where [`azure.yaml`](../../azure.yaml) + lives) so `azd` can find the project: + ```bash azd env new contoso-travel ``` This tags all resources under a single prefix so cleanup later is one `azd down` away. + > ✅ **Verify:** the command creates a `.azure/contoso-travel/` folder in the + > repo root. If you see it (e.g. via `ls -a .azure`), the environment was + > created successfully. + + > ⚠️ **Gotcha:** if you see `ERROR: no project exists; to create a new + > project, run 'azd init'`, you're not in the repo root. `cd` to the folder + > that contains `azure.yaml` and rerun the command. + 4. **Deploy.** ```bash azd up ``` - When prompted, choose region **`eastus2`** (or one of the alternates above). + You'll be prompted twice: + - **Select an Azure Subscription** — pick the subscription to deploy into. + - **`aiDeploymentsLocation` infrastructure parameter** — choose + **`(US) East US 2 (eastus2)`** (or one of the alternates above). `azd up` provisions the Bicep in [`../../infra/`](../../infra/): - - resource group + - resource group (`rg-contoso-travel`) - Foundry account + project - - `gpt-5.4-mini` model deployment - - Container Registry (for the hosted agent image) - - Log Analytics + Application Insights - - Storage account - - AI Search + Bing grounding connections + - **`gpt-5.4-mini`** (concierge) + **`gpt-5.4-judge`** model deployments + - Log Analytics workspace + Application Insights + - a Foundry project connection to Application Insights - Then it builds and pushes the hosted-agent container and publishes it as - **`contoso-concierge`**. First run: **5–10 minutes**. + First run: **~2 minutes**. - + You'll see output similar to: -5. **Read the outputs.** - The tail of `azd up` prints: - - the **Foundry project endpoint**, - - the **agent name** (`contoso-concierge`), - - a **direct link to the playground**. + ```text + (✓) Done: Resource group: rg-contoso-travel + (✓) Done: Foundry: ai-account-xxxxxxxxxxxxx + (✓) Done: Log Analytics workspace: logs-xxxxxxxxxxxxx + (✓) Done: Foundry project: ai-account-xxxxxxxxxxxxx/ai-project-contoso-travel + (✓) Done: Azure AI Services Model Deployment: ai-account-xxxxxxxxxxxxx/gpt-5.4-mini + (✓) Done: Azure AI Services Model Deployment: ai-account-xxxxxxxxxxxxx/gpt-5.4-judge + (✓) Done: Application Insights: appi-xxxxxxxxxxxxx + (✓) Done: Foundry project connection: .../appi-xxxxxxxxxxxxx + + SUCCESS: Your application was provisioned and deployed to Azure in 1 minute. + ``` + + > 💡 **Want different models?** The golden path deploys `gpt-5.4-mini` and a + > `gpt-5.4-judge`. To swap them, set the `AI_PROJECT_DEPLOYMENTS` env var + > before `azd up` — see [Lab 03](./03-deploy-models.md). + + > 💡 **What's *not* here yet:** the Container Registry and the hosted agent + > are **not** provisioned by `azd up` — you create them when you deploy the + > hosted agent in [Lab 05](./05-deploy-hosted-agent.md). - Copy these into your notes — you'll use them in every later lab. + + +5. **Read the outputs.** + The tail of `azd up` prints the **Foundry project endpoint** and related + environment values. Copy the endpoint into your notes — you'll use it in + every later lab. (The agent name and playground link appear once you deploy + the hosted agent in [Lab 05](./05-deploy-hosted-agent.md).) > 💡 **Tip:** `azd env get-values` prints everything `azd` knows about your > environment. Later labs use this to pick up endpoints automatically. @@ -103,30 +148,43 @@ Confirm you have: Run: ```bash -azd env get-values | grep -E "AZURE_AI_PROJECT_ENDPOINT|AZURE_AI_MODEL_DEPLOYMENT_NAME" +azd env get-values | grep -E "AZURE_AI_PROJECT_ENDPOINT|AZURE_AI_PROJECT_NAME|AZURE_AI_MODEL_DEPLOYMENT_NAME" ``` -Expected: two lines — the project endpoint URL and `gpt-5.4-mini`. If both -print, provisioning succeeded. +Expected: three lines — the project endpoint URL, the project name, and the +model deployment name. If all print, provisioning succeeded. You'll see output +similar to: + +```text +AZURE_AI_PROJECT_ENDPOINT="https://ai-account-xxxxxxxxxxxxx.services.ai.azure.com/api/projects/ai-project-contoso-travel" +AZURE_AI_PROJECT_NAME="ai-project-contoso-travel" +AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini" +``` Then open , go to **Resource groups**, and confirm the -`rg-contoso-travel-*` group contains a Foundry account, a container registry, -Application Insights, Log Analytics, and a storage account. +`rg-contoso-travel` group contains a Foundry account, a Foundry project, +the `gpt-5.4-mini` and `gpt-5.4-judge` model deployments, Application Insights, +and a Log Analytics workspace. + +> 💡 The Container Registry, AI Search, and Storage show up **later** — after +> [Lab 05](./05-deploy-hosted-agent.md) (hosted agent + registry). ## 🧠 Recap -- `azd up` provisioned Foundry + models + observability + hosting in one shot. +- `azd up` provisioned the Foundry substrate — account, project, the + `gpt-5.4-mini` and `gpt-5.4-judge` models, and observability (App Insights + + Log Analytics) — in one shot. - Environment values are stored per-`azd env` and reused by later labs. -- You now have a **live hosted agent** ready to open in Lab 06. +- Next you'll **confirm the model deployments** and learn how to change them. ## ➡️ Next **[Lab 03 — Deploy required models](./03-deploy-models.md)** to confirm the model deployment came up, or jump ahead to -**[Lab 05 — Deploy the hosted agent](./05-deploy-hosted-agent.md)** if you -want to redeploy the container. +**[Lab 05 — Deploy the hosted agent](./05-deploy-hosted-agent.md)** to ship the +container. If you're planning to use the **Prompt Agent** (Core Labs 01–04), continue with **[Lab 04 — Create the Prompt Agent](./04-create-prompt-agent.md)**. diff --git a/labs/fundamentals/03-deploy-models.md b/labs/fundamentals/03-deploy-models.md index 2b03f4b..89dc4bd 100644 --- a/labs/fundamentals/03-deploy-models.md +++ b/labs/fundamentals/03-deploy-models.md @@ -1,13 +1,15 @@ # Lab 03 — Deploy the required models -> **What you'll do:** Deploy `gpt-5.4-mini` (and optionally a judge model) into your Foundry project. +> **What you'll do:** Deploy `gpt-5.4-mini` and the `gpt-5.4-judge` model into your Foundry project. > **Time:** ~10 min · **Prerequisites:** [Lab 01](./01-provision-azd.md) **or** [Lab 02](./02-provision-portal.md) ## 🎯 Goal -Deploy the language model(s) your agents and evaluators will call. This is the -one step where the CLI path and the portal path do the same thing — because -`azd up` already deployed the primary model for you. +Deploy the language model(s) your agents and evaluators will call. On the +`azd` path this already happened — `azd up` deployed `gpt-5.4-mini` and +`gpt-5.4-judge` for you — so this lab is mostly about **confirming** the +deployments and knowing how to add or swap models. On the portal path you +deploy the model by hand here. ## 🧭 Where this fits @@ -27,43 +29,63 @@ flowchart LR | Deployment name | Purpose | Default region | |---|---|---| | `gpt-5.4-mini` | Concierge + specialist reasoning | `eastus2` | -| `gpt-5.4` (optional) | Judge model for AI-assisted evaluators in Core Lab 02 | `eastus2` | +| `gpt-5.4-judge` (`gpt-5.4`) | Judge model for AI-assisted evaluators in Core Lab 02 | `eastus2` | -> 💡 The judge model is optional. If your quota is tight, reuse `gpt-5.4-mini` -> as the judge and skip the second deployment. +> 💡 On the `azd` path both `gpt-5.4-mini` and the `gpt-5.4-judge` are deployed +> for you. On the portal path the judge is optional — if your quota is tight, +> reuse `gpt-5.4-mini` as the judge and skip the second deployment. ## 📋 Steps **If you provisioned with `azd up` (Lab 01):** -1. Confirm the deployment already exists: +1. Confirm the deployments already exist: ```bash - azd env get-values | grep AZURE_AI_MODEL_DEPLOYMENT_NAME + azd env get-values | grep -E "AZURE_AI_MODEL_DEPLOYMENT_NAME|AZURE_AI_JUDGE_DEPLOYMENT_NAME" ``` - You should see `AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"`. - ✅ You're done — skip to **Verify**. + You should see `AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"` and + `AZURE_AI_JUDGE_DEPLOYMENT_NAME="gpt-5.4-judge"`. +1. (Optional) List every deployment in Azure to see both: + ```bash + az cognitiveservices account deployment list \ + --resource-group "$(azd env get-value AZURE_RESOURCE_GROUP)" \ + --name "$(azd env get-value AZURE_AI_ACCOUNT_NAME)" -o table + ``` + You should see both `gpt-5.4-mini` and `gpt-5.4-judge`. +1. ✅ You're done — skip to **Verify**. + +> 💡 **Want different models?** The golden path deploys `gpt-5.4-mini` and +> `gpt-5.4-judge`. To change them, set the `AI_PROJECT_DEPLOYMENTS` env var to a +> JSON array **before** `azd up` (or set it and rerun `azd provision`). Setting +> it replaces the default entirely, so include **every** model you want: +> +> ```bash +> azd env set AI_PROJECT_DEPLOYMENTS '[{"name":"gpt-5.4-mini","model":{"name":"gpt-5.4-mini","format":"OpenAI","version":"2026-03-17"},"sku":{"name":"GlobalStandard","capacity":100}},{"name":"gpt-5.4-judge","model":{"name":"gpt-5.4","format":"OpenAI","version":"2026-03-05"},"sku":{"name":"GlobalStandard","capacity":100}}]' +> azd provision +> ``` **If you provisioned with the portal (Lab 02):** 1. Open the Foundry portal and select your project. -2. In the sidebar, click **My assets → Models + endpoints**. -3. Click **+ Deploy model → Deploy base model**. - +1. Ensure that the "New Foundry" toggle is active. +1. Switch to the "Build" section in the nav bar. +1. Click on the "Models" tab in the sidebar (it may show as "Deployments") +1. Click the purple "Deploy" button - select "Deploy a base model" - -4. Search for **`gpt-5.4-mini`** and select it. -5. Set: - - **Deployment name:** `gpt-5.4-mini` (match the value your labs reference) - - **Deployment type:** **Global Standard** - - **Tokens per Minute (TPM):** the maximum your quota allows (≥ 100k recommended) -6. Click **Deploy** and wait ~1 minute for the deployment to reach *Succeeded*. - + 1. Search for **`gpt-5.4-mini`** and select it. + 1. Select the "custom settings" from the Deploy drop-down + 1. Set the values: + - **Deployment name:** `gpt-5.4-mini` (match the value your labs reference) + - **Deployment type:** **Global Standard** + - **Tokens per Minute (TPM):** the maximum your quota allows (≥ 100k recommended) + 1. Click **Deploy** and wait ~1 minute for the deployment to reach *Succeeded*. **Optional — judge model:** -7. Repeat steps 3–6 with **`gpt-5.4`** and deployment name `gpt-5.4-judge`. - You'll reference this in Core Lab 02. +1. Repeat the deploy steps above with **`gpt-5.4`** and deployment name + `gpt-5.4-judge`. +1. You'll reference this in Core Lab 02. > ⚠️ **Gotcha:** if the deploy button is greyed out, you're out of quota. > Try a different region from the supported list, or request a quota increase @@ -71,15 +93,27 @@ flowchart LR ## ✅ Verify -In the Foundry portal, open **My assets → Models + endpoints**. You should see: +In the Foundry portal, visit the **Build** tab and click **Models**. You should +see: -- `gpt-5.4-mini` — state **Succeeded** -- (optional) `gpt-5.4-judge` — state **Succeeded** +- `gpt-5.4-mini` — Deployment status: **Succeeded** +- `gpt-5.4-judge` — Deployment status: **Succeeded** + (auto-deployed on the `azd` path; optional on the portal path) -Then, if you have `azd`: +If you used `azd`, confirm **both** deployments from the CLI: ```bash -azd env get-values | grep AZURE_AI_MODEL +az cognitiveservices account deployment list \ + --resource-group "$(azd env get-value AZURE_RESOURCE_GROUP)" \ + --name "$(azd env get-value AZURE_AI_ACCOUNT_NAME)" \ + --query "[].name" -o tsv +``` + +Expected output: + +```text +gpt-5.4-mini +gpt-5.4-judge ``` @@ -89,8 +123,9 @@ azd env get-values | grep AZURE_AI_MODEL - Models are **deployments** inside a Foundry project — a name, a base model, and a TPM budget. - Deployment names are the identifiers your agents and evaluators reference. -- The `azd` path deploys the model for you via Bicep; - the portal path is a two-minute UI flow. +- The `azd` path deploys `gpt-5.4-mini` and `gpt-5.4-judge` for you via Bicep + (override with `AI_PROJECT_DEPLOYMENTS`); the portal path is a two-minute UI + flow. ## ➡️ Next diff --git a/labs/fundamentals/04-create-prompt-agent.md b/labs/fundamentals/04-create-prompt-agent.md index 6caa394..1666c17 100644 --- a/labs/fundamentals/04-create-prompt-agent.md +++ b/labs/fundamentals/04-create-prompt-agent.md @@ -28,65 +28,139 @@ instructions (a system prompt), attached tools/knowledge (files, functions, grounding), and a model — but no container. It's the fastest path to a working agent and the natural starting point for observability. -Contrast with the **Hosted Agent** you already deployed via `azd up` -(`contoso-concierge`), which is a containerized multi-agent orchestrator you -control in code — deeper power, more moving parts. +Contrast with the **Hosted Agent** (`contoso-travel-concierge`) you'll deploy later in +[Lab 05](./05-deploy-hosted-agent.md), which is a containerized, code-first +orchestrator that can coordinate multiple specialist sub-agents — deeper power, +more moving parts. -The workshop uses **both** so you can compare their observability, evaluation, -and optimization surfaces. +### Prompt vs. Hosted — when to use which + +| | **Prompt Agent** | **Hosted Agent** | +|---|---|---| +| **Authored in** | Portal UI (no code) | Code (`src/`), shipped as a container | +| **Shape** | Single agent: one prompt + tools + model | Multi-agent orchestrator (a concierge routing to specialists) | +| **Logic** | Declarative — the model + instructions do the work | Imperative — you control routing, state, and framework code | +| **Iteration speed** | Seconds — edit prompt, save a version | Build → push image → deploy | +| **Best for** | Rapid prototyping, demos, single-purpose assistants, quickly validating a prompt/knowledge idea | Complex workflows, custom frameworks, multi-step tool orchestration, production systems that need code-level control and testing | +| **Trade-off** | Less control over orchestration and runtime behavior | More infra + moving parts to build, deploy, and maintain | + +> 💡 **Rule of thumb:** start with a **Prompt Agent** to prove the idea fast; graduate +> to a **Hosted Agent** when you need multi-agent orchestration, framework code, or +> production-grade control. + +### Why this workshop ends up with two agents + +By the end of the Fundamentals you'll have **both** flavors of the same Contoso +Travel Concierge: + +1. the **Prompt Agent** you create here (Lab 04), and +2. the **Hosted Agent** you deploy next (Lab 05). + +That's deliberate: running the same scenario in both forms lets you compare how +each behaves across the Agent DevOps loop — the **observability**, **evaluation**, +and **optimization** surfaces differ between a portal-authored prompt and a +code-first container, and seeing both makes those differences concrete. ## 📋 Steps 1. **Open the Foundry portal and select your project.** + Make sure the **New Foundry** toggle (top nav) is on. 2. **Create a new agent.** - Sidebar → **My assets → Agents** → **+ New agent** → **Prompt agent**. - Name it **`contoso-travel-concierge-prompt`**. + In the top nav choose **Build**, then select **Agents** in the left sidebar. + Click **New agent ▾**, then **Build an agent**. - + ![Build → Agents → New agent → Build an agent](./images/04-create-agent-01.png) -3. **Pick the model.** - Under **Model**, choose the deployment you created in Lab 03 - (`gpt-5.4-mini`). Leave temperature at the default for now. + In the **Create an agent** dialog, set **Agent name** to + **`contoso-travel-concierge-prompt`**, then click **Create and open + playground**. -4. **Paste the baseline instructions.** + ![The “Create an agent” dialog with the agent name field](./images/04-create-agent-02.png) + + The agent opens in the **Playground**, with tabs for **Details**, **Traces**, + **Monitor**, **Evaluation**, and **Optimize** across the top — you'll use + these in later labs. + + ![The new prompt agent open in the Playground](./images/04-create-agent-03.png) + +3. **Verify the selected model.** + The playground picks a default deployment. Next to **Model**, confirm it's + **`gpt-5.4-mini`** (Global Standard) and change it if needed. Leave the + default generation settings for now. + +4. **Paste the baseline instructions and save.** Under **Instructions**, paste the contents of [`../../artifacts/prompts/reference/prompt-agent-baseline-v1.md`](../../artifacts/prompts/reference/prompt-agent-baseline-v1.md). + The agent still shows **Version 1**, and the **Save** button becomes enabled + once you edit the instructions. + + ![Instructions pasted at Version 1 with the Save button enabled](./images/04-create-agent-04.png) + + Click **Save** (top-right). The version increments to **Version 2** and + **Save** greys out again until your next change. + + ![Agent now at Version 2 after saving, Save button disabled](./images/04-create-agent-05.png) > 💡 This is intentionally the *underperforming baseline* — it works for > simple questions but fails on the eval set. That gap is what Core Lab 03 > closes. -5. **Attach the Contoso datasets as knowledge (files).** - Under **Knowledge → Files**, upload: - - `data/flights.csv` - - `data/hotels.csv` - - `data/car_rentals.csv` +5. **Attach the Contoso datasets (as a vector index).** + In the **Tools** panel, click **Upload files**. The **Attach files** dialog + uploads into a **vector index** — leave **Index option** set to + **Create a new index**. + + ![The Attach files dialog: create a new vector index, with the supported file types](./images/04-create-agent-06.png) + + > ⚠️ **CSV isn't supported.** The index accepts `.json`, `.txt`, `.md`, + > `.pdf`, and code files — but **not** `.csv`. Upload the ready-made JSON + > versions instead (pre-generated from the CSVs in `data/`): + > - [`../../data/json/flights.json`](../../data/json/flights.json) + > - [`../../data/json/hotels.json`](../../data/json/hotels.json) + > - [`../../data/json/car_rentals.json`](../../data/json/car_rentals.json) + + Give the index a **Vector index name** (e.g. `contoso-travel-index`), then + **browse for files** (or drag-and-drop) and add the three JSON files. Wait + for each to show **Success**, then click **Attach**. - + ![The Attach files dialog with the three JSON files uploaded and named index](./images/04-create-agent-07.png) -6. **Save the agent.** - Click **Save** or **Deploy**. Wait for the agent to reach *Ready*. + The tool now appears in the **Tools** panel as **File search → Vector store** + with an **Indexing** status. Wait for indexing to finish before you test. -7. **Smoke-test in the playground.** - Click **Try in playground** and ask: + ![Tools panel showing File search with a Vector store that is Indexing](./images/04-create-agent-08.png) + + Click **Save** — the agent advances to **Version 3**. (The collapsible + **Knowledge** section also lists the index.) Optionally use **Publish** to + make a version callable from outside the playground. + +6. **Smoke-test in the playground.** + In the **Chat** panel on the right, ask: > *"What business-class flights are available from Chicago to Rome under $2500?"* - You should get a grounded answer citing entries from `flights.csv`. Now try: + You should get a grounded answer citing entries from the flights data. + + ![Playground answering the flights question with grounded results](./images/04-create-agent-09.png) + + Now try: > *"Plan a weekend in Tokyo."* Notice how the baseline agent asks a lot of clarifying questions instead of proposing an itinerary — that's exactly the failure mode Core Lab 03 will fix. -> ⚠️ **Gotcha:** if the agent's answers don't reference the CSV data, your -> files probably didn't finish indexing. Wait a minute and re-ask, or reattach. + ![Playground asking clarifying questions instead of proposing an itinerary](./images/04-create-agent-10.png) + +> ⚠️ **Gotcha:** if the agent's answers don't reference the dataset, the vector +> index probably didn't finish building. Wait a minute and re-ask, or re-upload +> the `.json` files. ## ✅ Verify - The Foundry portal shows `contoso-travel-concierge-prompt` under - **My assets → Agents** with status **Ready**. + **Build → Agents** with status **Ready**. - The playground returns grounded answers that reference specific flight IDs (e.g., `CT-FL-...`). @@ -95,8 +169,9 @@ and optimization surfaces. - A Prompt Agent = instructions + tools/knowledge + model — all portal-authored. - You seeded the intentionally-weak **baseline v1** prompt so Core Lab 03 has something to improve. -- You now have **two agents** deployed: the Prompt Agent and the Hosted Agent. +- You now have the **Prompt Agent** live. You'll deploy the **Hosted Agent** + next in Lab 05. ## ➡️ Next -**[Lab 05 — Deploy (or redeploy) the Hosted Agent](./05-deploy-hosted-agent.md)** +**[Lab 05 — Deploy the Hosted Agent](./05-deploy-hosted-agent.md)** diff --git a/labs/fundamentals/05-deploy-hosted-agent.md b/labs/fundamentals/05-deploy-hosted-agent.md index f1ce804..50b5d19 100644 --- a/labs/fundamentals/05-deploy-hosted-agent.md +++ b/labs/fundamentals/05-deploy-hosted-agent.md @@ -1,6 +1,6 @@ -# Lab 05 — Deploy (or redeploy) the Hosted Agent +# Lab 05 — Deploy the Hosted Agent -> **What you'll do:** Confirm the containerized **Contoso Travel Concierge** hosted agent is live, or redeploy it after a code change. +> **What you'll do:** Deploy the containerized **Contoso Travel Concierge** hosted agent to Foundry. > **Time:** ~10 min · **Prerequisites:** [Lab 03](./03-deploy-models.md) ## 🎯 Goal @@ -20,23 +20,57 @@ flowchart LR > 🧭 **This lab covers:** _Deploy_ — publishing a containerized agent to Foundry. -## Two ways to be here +## Before you start -- 🟢 **You ran `azd up`** in Lab 01 → the hosted agent is **already deployed**. - This lab is a **verification pass** + a quick tour of what got deployed. -- 🟠 **You took the portal path** in Lab 02 → you have a project but no hosted - agent yet. Follow the portal upload flow below. +The hosted agent is **not** deployed by `azd up` in Lab 01 — that step only +provisioned the Foundry substrate and models. Deploying the container is its own +deliberate step, which is what you do here. -## 📋 Steps — verifying the `azd up` deployment +The hosted agent is declared as a **service** in [`azure.yaml`](../../azure.yaml) +(`host: azure.ai.agent`). Deploying it needs two things that Lab 01 didn't set +up: the `azure.ai.agents` **azd extension** (the CLI commands) and the +**hosted-agent hosting** infrastructure (a container registry + agent capability +host). You'll enable both below. -1. **Check that `azd` sees the hosted agent.** +## 📋 Steps — deploy with `azd` + +1. **Make sure the hosted-agents extension is installed.** + The `azd ai agent` commands and hosted `azd deploy` come from the + **`azure.ai.agents`** azd extension. The workshop devcontainer installs it + for you — but to check (and install if missing) manually: + ```bash + azd extension list --installed | grep azure.ai.agents \ + || azd extension install azure.ai.agents + ``` + Keep it current with `azd extension upgrade azure.ai.agents`. + +2. **Enable hosted-agent hosting and provision it.** + The container registry and agent capability host aren't created by the + default `azd up`. Turn them on, then provision: + ```bash + azd env set ENABLE_HOSTED_AGENTS true + azd provision + ``` + This adds the registry + capability host to your existing resource group. + +3. **Deploy the hosted agent.** + ```bash + azd deploy contoso-travel-concierge + ``` + `azd` reads the `contoso-travel-concierge` service in + [`azure.yaml`](../../azure.yaml), zips `src/`, and lets Foundry build and + publish it. Wait for it to report **Ready**. + + + +4. **Check that `azd` sees the hosted agent.** ```bash - azd ai agent show contoso-concierge + azd ai agent show contoso-travel-concierge ``` You should see the agent name, endpoint, status **Ready**, and the model deployment it uses. -2. **Look at what was deployed.** +5. **Look at what was deployed.** ```bash ls src/ ``` @@ -48,24 +82,24 @@ flowchart LR - `instructions/versions/instructions-0.md` — the immutable baseline seed - `data/*.csv` — the Contoso datasets bundled into the image -3. **Invoke it once.** +6. **Invoke it once.** ```bash - azd ai agent invoke contoso-concierge \ + azd ai agent invoke contoso-travel-concierge \ '{"input": "What business-class flights leave Chicago for Rome?"}' ``` You should get a JSON response with a grounded answer. -4. **Open it in the playground.** - In the Foundry portal → **My assets → Agents → contoso-concierge → Try in +7. **Open it in the playground.** + In the Foundry portal → **Build → Agents → contoso-travel-concierge → Try in playground**. Ask the same question. Same answer, richer trace. ## 📋 Steps — deploying via the portal (portal path only) -1. **Open the portal → My assets → Agents → + New agent → Hosted agent**. +1. **Open the portal → Build → Agents → + New agent → Hosted agent**. 2. Provide: - - **Agent name:** `contoso-concierge` + - **Agent name:** `contoso-travel-concierge` - **Container image:** upload or point to a registry image built from `src/` - **Model deployment:** `gpt-5.4-mini` (from Lab 03) 3. Click **Deploy** and wait for status **Ready**. @@ -81,7 +115,7 @@ flowchart LR Any time you edit `src/main.py` or `src/instructions/concierge.md`: ```bash -azd deploy contoso-concierge --no-prompt +azd deploy contoso-travel-concierge --no-prompt ``` To go back to the pristine baseline first: @@ -92,8 +126,8 @@ To go back to the pristine baseline first: ## ✅ Verify -- `azd ai agent show contoso-concierge` prints status **Ready** with an endpoint URL. -- The Foundry portal shows the agent under **My assets → Agents** with type **Hosted**. +- `azd ai agent show contoso-travel-concierge` prints status **Ready** with an endpoint URL. +- The Foundry portal shows the agent under **Build → Agents** with type **Hosted**. - A single `invoke` (curl or `azd ai agent invoke`) returns a grounded JSON response. ## 🧠 Recap diff --git a/labs/fundamentals/06-verify.md b/labs/fundamentals/06-verify.md index 3be6e41..a3b2a0c 100644 --- a/labs/fundamentals/06-verify.md +++ b/labs/fundamentals/06-verify.md @@ -46,7 +46,7 @@ This question: 2. **Ask the Hosted Agent.** - Portal → **My assets → Agents → `contoso-concierge` → Try in playground** + Portal → **My assets → Agents → `contoso-travel-concierge` → Try in playground** → paste the same question. Expected: a grounded answer plus a **trace** in the right pane showing the diff --git a/labs/fundamentals/images/.gitkeep b/labs/fundamentals/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/labs/fundamentals/images/04-create-agent-01.png b/labs/fundamentals/images/04-create-agent-01.png new file mode 100644 index 0000000..80b6ad4 Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-01.png differ diff --git a/labs/fundamentals/images/04-create-agent-02.png b/labs/fundamentals/images/04-create-agent-02.png new file mode 100644 index 0000000..7517e31 Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-02.png differ diff --git a/labs/fundamentals/images/04-create-agent-03.png b/labs/fundamentals/images/04-create-agent-03.png new file mode 100644 index 0000000..b765023 Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-03.png differ diff --git a/labs/fundamentals/images/04-create-agent-04.png b/labs/fundamentals/images/04-create-agent-04.png new file mode 100644 index 0000000..a42be92 Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-04.png differ diff --git a/labs/fundamentals/images/04-create-agent-05.png b/labs/fundamentals/images/04-create-agent-05.png new file mode 100644 index 0000000..f7cc56c Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-05.png differ diff --git a/labs/fundamentals/images/04-create-agent-06.png b/labs/fundamentals/images/04-create-agent-06.png new file mode 100644 index 0000000..a926ea7 Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-06.png differ diff --git a/labs/fundamentals/images/04-create-agent-07.png b/labs/fundamentals/images/04-create-agent-07.png new file mode 100644 index 0000000..9dcbbb2 Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-07.png differ diff --git a/labs/fundamentals/images/04-create-agent-08.png b/labs/fundamentals/images/04-create-agent-08.png new file mode 100644 index 0000000..e23b302 Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-08.png differ diff --git a/labs/fundamentals/images/04-create-agent-09.png b/labs/fundamentals/images/04-create-agent-09.png new file mode 100644 index 0000000..fc51a0e Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-09.png differ diff --git a/labs/fundamentals/images/04-create-agent-10.png b/labs/fundamentals/images/04-create-agent-10.png new file mode 100644 index 0000000..16d1995 Binary files /dev/null and b/labs/fundamentals/images/04-create-agent-10.png differ diff --git a/labs/more/trace-driven-datasets.md b/labs/more/trace-driven-datasets.md index 688e2e0..ca76c76 100644 --- a/labs/more/trace-driven-datasets.md +++ b/labs/more/trace-driven-datasets.md @@ -42,7 +42,7 @@ The best next test case is the one you just barely got away with in prod.