diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/README.md b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/README.md index aa4a963f3..e4e559edd 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/README.md +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/README.md @@ -79,11 +79,17 @@ Complete the following before the hack session: > Your Foundry project, gpt-5.4-mini model deployment, and your **own Fabric F2 capacity** are provisioned for you by the lab automation. In **Challenge 1** you stand up your own Fabric workspace and publish your **Fabric Data Agent** by running one notebook (Run All). You'll be handed a project endpoint and your capacity name at the start of the hack. Web Search is optional and may need project-level enablement. > [!TIP] -> **You're ready to start when**, in the Foundry portal, you can: (1) see `gpt-5.4-mini` under **Models + endpoints** with status *Succeeded*, and (2) add the **Fabric Data Agent** tool to an agent and create the `inventory-hack-agent` connection (you do this in Challenge 2). If `gpt-5.4-mini` is missing, flag your facilitator before starting. +> **You're ready to start when**, in the Foundry portal, you can: (1) see `gpt-5.4-mini` in your **model deployments** with status *Succeeded*, and (2) add the **Fabric Data Agent** tool to an agent and create the `inventory-hack-agent` connection (you do this in Challenge 2). If `gpt-5.4-mini` is missing, flag your facilitator before starting. > [!IMPORTANT] > **Facilitators:** there is **no shared Fabric backend** — [`labautomation/deploy-lab.ps1`](labautomation/deploy-lab.ps1) provisions each attendee's Foundry project, model, and their **own Fabric F2 capacity**, and each attendee builds their workspace + Data Agent in Challenge 1. Ensure: the platform service principal has **Owner** on the attendee resource groups; [`lab-defaults.json`](labautomation/lab-defaults.json) sets **`groups: ["M365-E5-Users"]`** so attendees get a Power BI Pro license for the Fabric portal; the **lab tenant has Fabric enabled** (tenant-admin setting *“Users can create Fabric items / workspaces”* — **not** settable via `lab-defaults.json`); and the subscription has **Fabric F-SKU quota** (F2 = 2 CU; ~256 attendees per 512-CU subscription). See [`labautomation/README.md`](labautomation/README.md). +> [!NOTE] +> **Facilitator guidance (from run feedback):** +> - **Audience & expectations:** pitch this at a **no-code / low-code** audience (e.g. technical decision-makers, level 150–200) and set that expectation in the invite — it is *not* a hardcore-developer hack. +> - **Token throughput:** the model deploys at **200K TPM** and the lab packs **5 attendees per subscription**. If attendees hit rate limits, prefer **one subscription per attendee** (or raise the deployment's TPM). See [`labautomation/README.md`](labautomation/README.md). +> - **Fabric capacity:** the per-attendee **F2** covers this hack. If attendees hit **capacity throttling** (e.g. the parquet-load contention or intermittent Data Agent failures), bump to **F4** — either **resize live** (F SKUs support on-demand resize, no redeploy) or deploy with **`-FabricSkuName F4`**, and raise `estimatedDailyCostsUsd` to match (each SKU step roughly doubles the Fabric cost). For the **ontology** feature, F2 is likely undersized — size up (F4+) for that path. + ### Challenge overview | Challenge | Title | Duration | Key learning | diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-01.md b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-01.md index f6269e4a6..3d8faafe5 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-01.md +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-01.md @@ -39,16 +39,20 @@ The data lives in **Microsoft Fabric** (a governed Lakehouse with inventory, dem 2. **Explore the Foundry portal.** Navigate to [ai.azure.com](https://ai.azure.com) and open **your** Foundry project — its `FoundryProjectEndpoint` is on your lab dashboard. - - Locate the **model deployments** — confirm `gpt-5.4-mini` is deployed. - - Open **Agents → Playground** and send a test message: *"What can you do?"* - - Open **Tracing** in the left navigation — this is where you will inspect agent decisions in Challenges 3 and 4. + + > [!IMPORTANT] + > **Shared tenant — make sure you open *your* project.** Every attendee's projects are listed together at [ai.azure.com](https://ai.azure.com) and the names look alike. To find yours, match the **`FoundryProjectEndpoint`** on your lab dashboard (the **Credentials** tab of the MicroHack portal) to the project's **Parent resource** shown in Foundry. Work only in that project. + + - Locate your **model deployments** — confirm `gpt-5.4-mini` is deployed. + - Open a **deployed model → Playground** and send a test message: *"What can you do?"* (In the current Foundry portal you open the Playground from a model or an agent — there is no top-level *Agents → Playground* menu.) + - You will inspect agent decisions in Challenges 3 and 4 via **Traces → Response view** on an agent (not a top-level *Tracing* menu). ![Models and endpoints view in the Foundry portal showing gpt-5.4-mini deployed with status Succeeded](../images/challenge-00-model-deployments.png) ![Agents playground with the test message "What can you do?" and the agent's response](../images/challenge-00-playground-what-can-you-do.png) > [!NOTE] - > **The model name in the screenshots is only for illustration.** Some captures may show `gpt-4o-mini` (or another model) selected — that's just what was deployed when the screenshot was taken. Use **whatever model is deployed in your project** (check **Models + endpoints**, e.g. `gpt-5.4-mini`). The model is swappable; every step in this hack works the same regardless of which chat model is deployed. + > **The model name in the screenshots is only for illustration.** Some captures may show `gpt-4o-mini` (or another model) selected — that's just what was deployed when the screenshot was taken. Use **whatever model is deployed in your project** (check your **model deployments** list, e.g. `gpt-5.4-mini`). The model is swappable; every step in this hack works the same regardless of which chat model is deployed. 3. **Provision your own Fabric workspace and lakehouse.** *(~5 min)* @@ -59,7 +63,10 @@ The data lives in **Microsoft Fabric** (a governed Lakehouse with inventory, dem ![Fabric left navigation with Workspaces open and the New workspace button highlighted](../images/challenge-00-new-workspace-button.png) - Name it `inventory-hack`, expand **Advanced**, and under **Workspace type** select **Fabric**. A **Details** dropdown appears — select your **`FabricCapacityName`** (the `invcap…` value from your dashboard). Click **Apply**. + Name it **`inventory-hack-`** (e.g. `inventory-hack-labuser-0001`) — Fabric workspace names must be **unique across the whole tenant**, so a plain `inventory-hack` is rejected once another attendee has taken it. Expand **Advanced**, and under **Workspace type** select **Fabric**. A **Details** dropdown appears — select your **`FabricCapacityName`** (the `invcap…` value from your dashboard). Click **Apply**. + + > [!NOTE] + > Wherever the rest of this hack refers to the **`inventory-hack`** workspace, it means **your** uniquely-named workspace. The Lakehouse (`InventoryLakehouse`) and Data Agent (`inventory-hack-agent`) names live *inside* your workspace, so keep those exactly as written. ![Create a workspace dialog with Workspace type set to Fabric and the attendee's F2 capacity selected under Details](../images/challenge-00-create-workspace.png) @@ -91,6 +98,9 @@ The data lives in **Microsoft Fabric** (a governed Lakehouse with inventory, dem Keep the schema as **`dbo`**, set the table name to the file name without the `.parquet` extension, and click **Load**. For example, load `demandhistory.parquet` into a table named `demandhistory`. Repeat this for all seven files. + > [!NOTE] + > **Load the files one at a time.** *Load to tables* runs as a background job; starting the next load before the previous one finishes can fail with a *load already in progress* error. If a load errors, open **Monitor** (left rail), **cancel** any still-running load activity, then retry that file into a new table. + ![The Load file to new table dialog using the dbo schema and demandhistory table name](../images/challenge-00-lakehouse-load-table.png) **c. Verify the tables.** @@ -112,7 +122,7 @@ The data lives in **Microsoft Fabric** (a governed Lakehouse with inventory, dem Open **Setup -> Agent instructions** and enter the following instructions: ```text - You answer questions about Zava retail inventory. Use these tables: Inventory (onHand, reorderPoint, safetyStock per product per location), Products (name, category, unitCost, leadTimeDays, supplierId), Stores (retail stores and warehouses with region/city/state), DemandHistory (weekly units sold per product per store), ExternalSignals (market signals with affectedCategories), Suppliers, and ReplenishmentOrders (past purchase/transfer orders). Always return exact numbers. Flag a product as CRITICAL when onHand is below safetyStock. + You answer questions about Zava retail inventory. Use these tables: Inventory (onHand, reorderPoint, safetyStock per product per location), Products (name, category, unitCost, leadTimeDays, supplierId), Stores (retail stores and warehouses with region/city/state), DemandHistory (weekly units sold per product per store), ExternalSignals (market signals with affectedCategories), Suppliers, and ReplenishmentOrders (past purchase/transfer orders). Category values are stored as lowercase snake_case: garden_and_lawn, outdoor_power_tools, paint_and_supplies, smart_home. When a question names a category in plain English (e.g. "Outdoor Power Tools"), map it to the exact snake_case value before filtering; the same applies to affectedCategories in ExternalSignals. Prefer filtering by productId/SKU (e.g. P004) when the question names specific products. Always return exact numbers. Flag a product as CRITICAL when onHand is below safetyStock. ``` Save the Data Agent. @@ -127,24 +137,27 @@ The data lives in **Microsoft Fabric** (a governed Lakehouse with inventory, dem **h. Get your two IDs.** - After publishing, click the **Settings** gear in the data agent toolbar. Select **Publishing**, then copy the **Published URL** using the copy icon. + After publishing, click the **Settings** gear in the data agent toolbar. Select **Model Context Protocol (MCP)**, then copy the **MCP server URL** using the copy icon. + + > [!NOTE] + > Earlier builds of Fabric exposed a **Publishing → Published URL** here. That field has been replaced by the **Model Context Protocol** page — use the **MCP server URL** instead; it carries the same two IDs. ![The Settings gear in the data agent toolbar](../images/challenge-00-agent-settings.png) - ![The Publishing settings page with the Published URL copy button highlighted](../images/challenge-00-agent-url.png) + ![The Model Context Protocol settings page with the MCP server URL copy button highlighted](../images/challenge-00-agent-url.png) Paste the URL into a text editor. It has this format: ```text - https://api.fabric.microsoft.com/v1/workspaces//dataagents//aiassistant/openai + https://api.fabric.microsoft.com/v1/mcp/workspaces//dataagents//agent ``` Copy and note these two values: - **Workspace ID** — the value between `/workspaces/` and `/dataagents/` - - **Agent ID** — the value between `/dataagents/` and `/aiassistant/` + - **Agent ID** — the value between `/dataagents/` and `/agent` You paste both IDs into the Fabric Data Agent tool in Challenge 2. - ![Published URL with the Workspace ID and Agent ID segments identified](../images/challenge-00-agent-ids.png) + ![MCP server URL with the Workspace ID and Agent ID segments identified](../images/challenge-00-agent-ids.png) **i. Verify your agent answers.** *(1 min)* Open the data agent's **Test data agent** pane and ask: *"How many Leaf Blower X2 units are on hand at the Portland and Seattle stores, and are they below safety stock?"* You should get exact numbers with **CRITICAL** flags — proof that your tables are selected and the agent is published. @@ -166,7 +179,7 @@ The data lives in **Microsoft Fabric** (a governed Lakehouse with inventory, dem ![The imported notebook open with an Imported successfully confirmation popover](../images/challenge-00-notebook-imported.png) **b. Attach your Lakehouse to the notebook.** - Open the imported notebook. In the left **Explorer** pane click **Add** (Lakehouses) → **Existing Lakehouse**. In the **OneLake catalog** dialog, tick **`InventoryLakehouse`** and click **Add** — if more than one appears, pick the row whose **Location** is **`inventory-hack`** (your workspace). It becomes the notebook's **default** Lakehouse. **Without this, the table-write cells fail.** + Open the imported notebook. In the left **Explorer** pane click **Add data items → From OneLake catalog**. In the dialog, tick **`InventoryLakehouse`** and click **Add** — if more than one appears, pick the row whose **Location** is **your `inventory-hack-…` workspace**. It becomes the notebook's **default** Lakehouse. **Without this, the table-write cells fail.** ![OneLake catalog dialog selecting InventoryLakehouse in the inventory-hack workspace as the notebook's lakehouse](../images/challenge-00-attach-lakehouse.png) @@ -237,13 +250,16 @@ The data lives in **Microsoft Fabric** (a governed Lakehouse with inventory, dem | Symptom | Fix | |---------|-----| -| `gpt-5.4-mini` is missing under **Models + endpoints** | Flag your facilitator before starting — the model deployment is part of your provisioned lab. | +| `gpt-5.4-mini` is missing from your **model deployments** | Flag your facilitator before starting — the model deployment is part of your provisioned lab. | | Your **`FabricCapacityName`** isn't selectable when creating the workspace | The F2 capacity must be **running** — resume it (Azure portal → your Fabric capacity → **Resume**). | | The setup notebook errors on `%pip install` or the publish cell | Preview SDK drift — re-run the failed cell; if a method name differs, check the [SDK reference](https://learn.microsoft.com/fabric/data-science/fabric-data-agent-sdk). Make sure your F2 capacity is **running**. | | **Fabric Data Agent** isn't in the tool catalogue (previewing ahead) | Your F2 capacity must be running, and the setup notebook must have **published** the agent. | | The agent replies *"I'm unable to access the data source"* (or *"No tables selected yet"*) | Its tables aren't selected. The setup notebook selects all seven automatically, but if it didn't: open the data agent → **Data** tab → tick **all** tables under `InventoryLakehouse → dbo` → **Publish**, then re-test. | +| Loading a `.parquet` file fails with a *load already in progress* error | Load the files **one at a time**. Open **Monitor** (left rail), **cancel** the still-running load activity, then retry that file into a new table. | +| The agent returns nothing when you name a category (e.g. *"Outdoor Power Tools"*) | Category values are lowercase snake_case (`outdoor_power_tools`). Ask by the exact value or by **SKU/productId** (e.g. `P004`), and confirm the agent instructions include the category-mapping line from step 4(a)f / the setup notebook. | | The publish cell raises **`No tables were selected (schema may still be syncing)`** | Expected on a **cold F2** — the 7 tables wrote fine, but the Lakehouse **SQL analytics endpoint** hasn't exposed them yet. Open `InventoryLakehouse` → **SQL analytics endpoint** → confirm all 7 tables under `dbo`, then **re-run the publish cell** (idempotent) or tick the tables in the data agent's **Explorer** and **Publish**. | | Ticking tables in the data agent shows **`Error updating data agent data source selection(s)`** | Same root cause — the SQL endpoint schema isn't fully synced. Wait until all 7 tables show under the Lakehouse's **SQL analytics endpoint**, **refresh** the data agent page (F5), then re-tick and **Publish**. If it persists, remove and re-add the `InventoryLakehouse` data source (**+ Add data source → OneLake catalog**), tick the 7 tables, and **Publish**. | +| The agent answers in the **Test data agent** pane but a Foundry agent later errors **`Stage configuration not found`** | The test pane runs the **draft/Preview** runtime; Foundry consumes the **published** version. On a cold F2 the notebook's publish can leave the agent in draft. Open `inventory-hack-agent` → confirm all 7 tables → click **Publish**, then retry. | ![The data agent's Data tab showing InventoryLakehouse tables unselected and the agent replying that it cannot access the data source](../images/challenge-00-no-tables.png) diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-02.md b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-02.md index 6bcd1a469..55abb8c9f 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-02.md +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-02.md @@ -28,20 +28,27 @@ Your agent must sense this signal from the web, query the governed inventory dat Your role is to detect real-world signals that could affect product demand and reconcile them against the company's current inventory position. - IMPORTANT - tool use: You have a Fabric Data Agent tool connected to the governed Zava - inventory data (Inventory, Products, Stores, DemandHistory, ExternalSignals, Suppliers, - ReplenishmentOrders). For ANY question about stock, on-hand units, reorder points, safety - stock, sales velocity, or any inventory number, you MUST call the Fabric Data Agent and - answer from its result. Never answer inventory questions from memory, and never use Web - Search for internal inventory data (Web Search is only for external market signals). - - When given a scenario or event: + IMPORTANT - tool use (non-negotiable): You have a Fabric Data Agent tool connected to the + governed Zava inventory data (Inventory, Products, Stores, DemandHistory, ExternalSignals, + Suppliers, ReplenishmentOrders). EVERY response that gives a demand assessment MUST include + at least one Fabric Data Agent call - this is mandatory and NOT conditional on Web Search. + For ANY inventory number (stock, on-hand units, reorder points, safety stock, sales + velocity), you MUST call the Fabric Data Agent and answer from its result. Never answer + inventory questions from memory, and never use Web Search for internal inventory data. + + Do NOT conclude with "I couldn't verify inventory" or "based on external signals only" as a + substitute for calling the tool - the Fabric Data Agent is available to you, so call it. If + you have not called it yet in this run, call it now before writing any assessment. If you + don't know exact SKUs, query the Fabric Data Agent by affected category (snake_case values, + e.g. outdoor_power_tools) or by productId/SKU (e.g. P004). + + When given a scenario or event, do these steps in order: 1. Use Web Search to find relevant market signals, news, and trend data about the affected product categories. Cite your sources. - 2. Use the Fabric Data Agent to query current stock levels and recent sales velocity for - the relevant SKUs and warehouses. + 2. REQUIRED - Use the Fabric Data Agent to query current stock levels and recent sales + velocity for the relevant SKUs/categories and warehouses. Do not skip this step. 3. Synthesise both sources into a demand assessment: state whether current stock is - adequate, at risk, or critically exposed — with a clear reason for each conclusion. + adequate, at risk, or critically exposed - grounded in the Fabric numbers you retrieved. 4. Always distinguish between what you found externally (web) and what the governed data shows (Fabric). Never blend them without attribution. @@ -71,6 +78,9 @@ Your agent must sense this signal from the web, query the governed inventory dat This is the first agent where you attach the Fabric Data Agent, so you'll **create** the `inventory-hack-agent` connection here using the two IDs from Challenge 1. Every later challenge just selects it. +> [!IMPORTANT] +> **Use the *Fabric Data Agent* connector — not auto-suggested function tools.** When you create an agent from instructions, the New Foundry portal may **auto-generate custom `fx` function stubs** (e.g. `query_inventory`, `list_low_stock`, `get_external_signals`). These are **empty placeholders** — they don't reach your Lakehouse, so the agent replies that it *"can't verify inventory"* even with perfect instructions. **Delete any such `fx` functions** (each tool row → **⋮ → Remove**) and add the **Fabric Data Agent** tool as below instead. + 1. In the agent editor, expand **Tools** and select **Add**. 2. Choose **Fabric Data Agent** from the tool catalogue. 3. Create the connection with the two IDs your **setup notebook printed in Challenge 1**: @@ -97,6 +107,9 @@ This is the first agent where you attach the Fabric Data Agent, so you'll **crea - A clear demand assessment: adequate / at risk / critically exposed. ![Playground response citing a web source and a Fabric inventory query result with a demand assessment](../images/challenge-01-playground.png) + + > [!TIP] + > **Agent answered from web only and said it "couldn't verify inventory"?** It skipped the Fabric Data Agent call — `gpt-5.4-mini` is a reasoning model and occasionally skips an available tool. Recover it by replying: *"Call the Fabric Data Agent now and pull current stock and sales velocity for the affected SKUs (e.g. P004, P006) before giving your assessment."* If it keeps skipping, set **tool choice = required** in the agent's tool/run settings so a tool call is mandatory. The strengthened instructions above make this rare. 4. Ask a follow-up question: *"Which store or warehouse has the lowest stock of outdoor power tools relative to its reorder point?"* 5. Ask: *"What external signals in the last 30 days could affect demand for outdoor power tools in the Pacific Northwest?"* @@ -114,6 +127,9 @@ This is the first agent where you attach the Fabric Data Agent, so you'll **crea | **Web Search** isn't in the tool catalogue | It's in Public Preview and may need project-level enablement — ask your facilitator, or skip it and rely on the `ExternalSignals` table via the Fabric Data Agent. | | **Fabric Data Agent** isn't in the catalogue | The integration needs **your** F2 Fabric capacity to be running — resume it (Azure portal → your Fabric capacity → **Resume**) and confirm the setup notebook published the agent. | | The agent answers inventory questions from memory | Strengthen the *IMPORTANT – tool use* line in the instructions; it must call the Fabric Data Agent for any stock number. | +| The agent gives a demand assessment from web signals only (says it *"couldn't verify inventory"*) | It skipped the Fabric call. Reply *"Call the Fabric Data Agent now for current stock + sales velocity of the affected SKUs before assessing,"* or set **tool choice = required** in the agent's tool settings. `gpt-5.4-mini` (a reasoning model) sometimes skips available tools. | +| The agent has `fx` functions like `query_inventory` / `list_low_stock` instead of the Fabric Data Agent | The portal **auto-generated stub functions** from your instructions — they're empty and never reach your Lakehouse. Remove them (each tool row → **⋮ → Remove**) and add the **Fabric Data Agent** connector (Part C). | +| The run fails with **`Stage configuration not found`** (or *configuration not found*) | Your Fabric Data Agent works in Fabric's **Test data agent** pane but isn't **published** — the Foundry tool consumes the *published* stage, not the draft/Preview runtime. In Fabric, open `inventory-hack-agent` → confirm the 7 tables → click **Publish**, then retry. | | The connection dialog asks for IDs you don't have | Copy the **Workspace ID** and **Agent ID** your setup notebook printed in its last cell (Challenge 1). | ## 🚀 Go further diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-03.md b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-03.md index 61230666e..728d13550 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-03.md +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-03.md @@ -38,22 +38,31 @@ Your Inventory Optimisation Agent answers these questions by querying the Fabric you MUST call the Fabric Data Agent and answer from its result. Never answer from memory and never invent numbers. + Data model note: DemandHistory records WEEKLY units sold per SKU per RETAIL STORE - there + are no sales rows for distribution warehouses. Convert to a daily rate with + average_daily_sales = average weekly units / 7. Query demand at the STORES where the SKU + sells; do NOT query "warehouses" for sales, or average daily sales comes back as 0. + For each at-risk or critically exposed SKU: - 1. Use the Fabric Data Agent to query current stock level, reorder point, and - average daily sales for that SKU across all warehouses. + 1. Use the Fabric Data Agent to query current stock level, reorder point, and average + weekly units sold for that SKU at each stocking location (its stores). Derive + average_daily_sales = average weekly units / 7. 2. Calculate the suggested reorder quantity using this rule: reorder_qty = max(0, (30-day_demand - current_stock)) where 30-day_demand = average_daily_sales × 30. - 3. Identify the warehouse with the lowest stock relative to demand — that is the + 3. Identify the location with the lowest stock relative to demand - that is the priority replenishment location. 4. Return a structured recommendation table: - | SKU | Warehouse | Current Stock | Suggested Reorder Qty | Priority | + | SKU | Location | Current Stock | Suggested Reorder Qty | Priority | 5. Flag any SKU where current stock is already below the reorder point as CRITICAL. - If you cannot find a SKU in the data, say so clearly — do not invent numbers. + If you cannot find a SKU in the data, say so clearly - do not invent numbers. ``` 5. Add the **Fabric Data Agent** tool and select the existing **`inventory-hack-agent`** connection (created in Challenge 2). Do **not** add Web Search — this agent works only with internal data. + + > [!NOTE] + > If the portal auto-added custom `fx` function stubs (e.g. `query_inventory`), **remove them** and use only the **Fabric Data Agent** connector — see Challenge 2, Part C. 6. Click **Save**. ### Part B — Run the agent (15 min) @@ -61,7 +70,10 @@ Your Inventory Optimisation Agent answers these questions by querying the Fabric 1. Open the **Agents playground**. 2. Paste the demand assessment you received in Challenge 2 as your first message. Copy it directly from your Challenge 2 playground chat, for example: - > *"Outdoor power tools are critically exposed. Leaf blowers and chainsaws show demand uplift signals but stock at both Chicago and Dallas warehouses is near reorder point. The Portland and Seattle stores are already below safety stock on leaf blowers."* + > *"Outdoor power tools are critically exposed. For SKU P004 at Portland and P006 at Seattle, query current stock, reorder point, and average weekly units sold, then return the reorder recommendation table (SKU, Location, Current Stock, Suggested Reorder Qty, Priority). Flag any SKU below its reorder point as CRITICAL."* + + > [!TIP] + > **Phrasing matters — name the SKUs.** A purely prose prompt (e.g. *"leaf blowers and chainsaws are exposed"*) often makes the Fabric Data Agent generate a query that filters on a friendly category label and returns **nothing**. Naming the **SKUs/productIds** (e.g. `P004`, `P006`) and the exact fields you want — as in the example above — reliably returns data. If a prompt comes back empty, re-ask it by SKU. 3. The agent should query Fabric and return a recommendation table. 4. Ask a follow-up: *"Show me only the CRITICAL items."* @@ -74,7 +86,7 @@ Your Inventory Optimisation Agent answers these questions by querying the Fabric > [!IMPORTANT] > This is the most important part of the challenge. Understanding what an agent did — and why — is essential for building trust with stakeholders and catching errors before they reach production. -1. In the left navigation, click **Tracing**. +1. Open the `inventory-optimisation-agent` and select **Traces → Response view**. (In the current Foundry portal, tracing lives on the agent as **Traces** — there is no top-level *Tracing* menu.) 2. Find the most recent run of `inventory-optimisation-agent`. 3. Open the trace and locate: - The **model call** — what instructions and context were sent to gpt-5.4-mini? @@ -83,6 +95,9 @@ Your Inventory Optimisation Agent answers these questions by querying the Fabric - The **final generation** — how did the model synthesise the data into the recommendation? ![Expanded trace view showing the model call, Fabric tool call, tool response, and final generation spans](../images/challenge-02-trace.png) + + > [!NOTE] + > The **Traces → Response view** on the agent gives the full span tree shown above. The quick **Traces** link at the bottom of a playground chat opens a lighter, differently-laid-out view of the same run — either works, but the Response view is the one this challenge describes. 4. Answer these questions by reading the trace: - Did the agent query Fabric once or multiple times? Why? - Was the reorder quantity calculation visible in the trace? @@ -99,10 +114,13 @@ Your Inventory Optimisation Agent answers these questions by querying the Fabric | Symptom | Fix | |---------|-----| -| The run doesn't appear under **Tracing** | Give it a few seconds and refresh; make sure you ran the agent from the playground, not just saved it. | +| The run doesn't appear under **Traces** | Give it a few seconds and refresh; make sure you ran the agent from the playground, not just saved it. | | The agent invents stock numbers | Reinforce the *IMPORTANT – tool use* instruction — every inventory number must come from a Fabric Data Agent call. | +| The agent replies it can't reach Fabric / the tool call errors intermittently | Known preview flake. Start a **new playground session** and retry the same prompt (it usually succeeds within a try or two); confirm your F2 capacity is **running**. | +| A prompt returns no data (empty table) | The generated query likely filtered on a friendly category label. Re-ask by **SKU/productId** (e.g. `P004`) and name the exact fields; ensure the Data Agent instructions include the snake_case category-mapping line from Challenge 1. | | No item is flagged **CRITICAL** | Use a scenario/SKU that is genuinely below reorder point (e.g. leaf blowers at Portland/Seattle), or ask the agent to list items below their reorder point. | | The reorder quantity looks wrong | Check the trace — confirm the agent used `average_daily_sales × 30 − current_stock` and pulled real numbers from Fabric. | +| Every **Suggested Reorder Qty is 0** (agent says average daily sales is 0) | The agent queried demand *"across warehouses"* — but `DemandHistory` only records sales at **retail stores** (weekly units). The instructions must query demand **at the stores** and convert weekly→daily (÷ 7). Re-check the Part A instruction block. | ## 🚀 Go further diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-04.md b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-04.md index 7bdfdf24d..4a1ecab65 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-04.md +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/challenges/challenge-04.md @@ -44,7 +44,7 @@ This human-in-the-loop pattern is critical for **agentic AI in enterprise settin Date: Requested by: Inventory Optimisation Agent ───────────────────────────────────────────── - | Line | SKU | Warehouse | Qty | Unit Cost (est.) | Line Total | + | Line | SKU | Location | Qty | Unit Cost (est.) | Line Total | ───────────────────────────────────────────── TOTAL ESTIMATED VALUE: $X,XXX ───────────────────────────────────────────── @@ -81,6 +81,9 @@ You only need one tool here: 2. On the **Configured** tab, choose **Fabric Data Agent** and connect the existing **`inventory-hack-agent`** connection — the agent needs it to look up unit costs from the `Products` table when building the PO. 3. Select **Add tool**, then **Save**. +> [!NOTE] +> If the portal auto-added custom `fx` function stubs (e.g. `query_inventory`), **remove them** and use only the **Fabric Data Agent** connector — see Challenge 2, Part C. + > [!NOTE] > **Want a *real* submit action?** In production you'd add an **OpenAPI tool** (New Foundry) or a custom function that writes to the `ReplenishmentOrders` table / your ERP. That needs a hosted endpoint, so it's out of scope for this no-code lab — see **Part E — Stretch** at the end of this challenge. @@ -97,7 +100,7 @@ You only need one tool here: - Then reply `YES` — verify the agent responds with the simulated submission confirmation (`✅ Purchase order PO-... submitted to ERP.`). ![Playground showing the MODIFY 1 50 adjustment followed by a YES approval and the submission confirmation](../images/challenge-03-approval-flow.png) -5. Open **Tracing** and find this run. Trace the complete sequence: +5. Open the agent's **Traces → Response view** and find this run. Trace the complete sequence: - Model call (format PO from the recommendation) → **Fabric tool call** (unit-cost lookup from `Products`) → model call (present proposal) → model call (apply MODIFY, re-present) → model call (confirm submission after YES). 6. **Discuss with your team:** At which step did the human add value that an automated pipeline could not? diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/README.md b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/README.md index a135fea6c..ede45bcc4 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/README.md +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/README.md @@ -4,6 +4,8 @@ This folder holds the screenshots embedded throughout the challenge guides and w > **Model name in captures is illustrative.** Where a screenshot shows a selected chat model (e.g. `gpt-4o-mini` or `gpt-5.4-mini`), that name reflects only what was deployed when the capture was taken. The model is **swappable** — attendees use whatever their facilitator provisioned. You do **not** need to re-shoot screenshots when the deployed model changes; challenge-01 tells attendees this explicitly. +> **Portal UI captures pending re-shoot.** The Fabric/Foundry portals changed after some captures were taken; the guide text is already updated for the current UI. Re-capture when convenient: the Data Agent **Settings → Model Context Protocol → MCP server URL** page (replaces the retired *Publishing → Published URL*; used by `challenge-00-agent-settings.png`, `challenge-00-agent-url.png`, `challenge-00-agent-ids.png`) and the agent **Traces → Response view** (`challenge-02-trace.png`, `challenge-03-trace.png`). + | Filename | Challenge | Shows | |----------|-----------|-------| | `challenge-00-model-deployments.png` | 0 | Foundry **Models + endpoints** with `gpt-5.4-mini` deployed, status **Succeeded**. | @@ -25,7 +27,7 @@ This folder holds the screenshots embedded throughout the challenge guides and w | `challenge-01-add-tool.png` | 1 | The tool catalogue in the agent editor with **Web Search** and **Fabric Data Agent** available. | | `challenge-01-playground.png` | 1 | Playground response with a Fabric inventory result (and a web source if Web Search is on) ending in a demand assessment. | | `challenge-02-recommendation.png` | 2 | Reorder **recommendation table** (SKU / Warehouse / Current Stock / Suggested Reorder Qty / Priority) with a **CRITICAL** item. | -| `challenge-02-trace.png` | 2 | **Tracing** detail expanded to the model call, Fabric tool call, tool response, and final generation spans. | +| `challenge-02-trace.png` | 2 | **Traces → Response view** detail expanded to the model call, Fabric tool call, tool response, and final generation spans. | | `challenge-03-approval-flow.png` | 3 | The `MODIFY 1 50` → `YES` approval exchange and the simulated submission confirmation. | | `challenge-03-trace.png` | 3 | Trace of the approval run: model calls plus the Fabric unit-cost lookup across propose → MODIFY → YES. | | `challenge-04-orchestrator.png` | 4 *(stretch)* | The `inventory-planning-workflow` canvas: Start → demand-sensing → inventory-optimisation → replenishment-action. | diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-ids.png b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-ids.png index 987cc4036..60904cad8 100644 Binary files a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-ids.png and b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-ids.png differ diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-settings.png b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-settings.png index 2a1435e69..8f4e55239 100644 Binary files a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-settings.png and b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-settings.png differ diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-url.png b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-url.png index 7a8b43e19..515ffa587 100644 Binary files a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-url.png and b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-00-agent-url.png differ diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-02-trace.png b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-02-trace.png index bd3e7925c..580aaa222 100644 Binary files a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-02-trace.png and b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-02-trace.png differ diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-03-trace.png b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-03-trace.png index 406dcc7ee..801efc1b7 100644 Binary files a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-03-trace.png and b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/images/challenge-03-trace.png differ diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/README.md b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/README.md index 1451c764f..514633b78 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/README.md +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/README.md @@ -8,8 +8,8 @@ Everything for this hack is provisioned per attendee by [`deploy-lab.ps1`](deplo |----------|------|-------| | Azure AI Foundry account | `Microsoft.CognitiveServices/accounts` | One per attendee (AIServices kind) | | Foundry project | `…/projects/inventory-hack` | System-assigned managed identity; provisioning is verified before the lab continues | -| gpt-5.4-mini model deployment | GlobalStandard, 100K TPM | Used by all three hack agents | -| Fabric **F2 capacity** | `Microsoft.Fabric/capacities` | One per attendee; attendee set as **capacity admin** | +| gpt-5.4-mini model deployment | GlobalStandard, 200K TPM | Used by all three hack agents | +| Fabric capacity (default **F2**) | `Microsoft.Fabric/capacities` | One per attendee; attendee set as **capacity admin**. SKU via `-FabricSkuName` (F2 default; use F4 if attendees hit throttling). | The script grants the **Foundry User** role to each attendee and to the Foundry project's managed identity at the Foundry account scope. It also sets each attendee as **admin of their own F2 capacity** (resolving their Entra object ID to a UPN via the platform `Get-MhhLabUser` helper), so they can create a workspace, assign it to the capacity, and publish their own Data Agent — no shared state, no cross-attendee contention. @@ -38,7 +38,8 @@ Attendees use these in the Foundry and Fabric portals — no SDK or code require - The deploying identity (the platform **service principal**) needs **Owner** on each attendee resource group so it can create resources and assign the **Foundry User** role to the attendee and project managed identity — the platform grants this automatically for `resourcegroup` deployments. - **`groups: ["M365-E5-Users"]`** in [`lab-defaults.json`](lab-defaults.json) — each attendee's lab Entra user gets a **Microsoft 365 E5** license (includes **Power BI Pro**), which is what lets them sign into the Fabric portal ([app.fabric.microsoft.com](https://app.fabric.microsoft.com)) and create a workspace. Without a Fabric/Power BI license the attendee cannot open the portal. - **The lab tenant must have Fabric enabled** — a Fabric **tenant-admin** setting (*“Users can create Fabric items”* / workspaces). This is **not** settable via `lab-defaults.json`; the tenant administrator for the lab tenant must turn it on, or attendees cannot create a workspace even with a license. -- **Fabric F-SKU quota** in a preferred region (F2 = 2 CU; a 512-CU subscription supports ~256 attendees). `swedencentral` and `norwayeast` are good defaults. +- **Fabric F-SKU quota** in a preferred region (F2 = 2 CU → ~256 attendees per 512-CU subscription; F4 = 4 CU halves that). `swedencentral` and `norwayeast` are good defaults. +- **Model TPM vs. subscription packing** — each attendee's `gpt-5.4-mini` deploys at **200K TPM** (GlobalStandard capacity 200) and [`lab-defaults.json`](lab-defaults.json) packs **`labsPerSubscription: 5`** (5 × 200 = 1,000, right at the ~1,000 GlobalStandard quota ceiling per region). If attendees hit token rate limits, request a GlobalStandard quota increase or use **one subscription per attendee**. - The **`Microsoft.Fabric`** resource provider is registered on the subscription (the script registers it if needed). > [!NOTE] @@ -46,6 +47,6 @@ Attendees use these in the Foundry and Fabric portals — no SDK or code require ## Cost estimate -- **Fabric F2 capacity** (~$0.36/hr pay-as-you-go) is the dominant cost — roughly **$8–9/attendee/day** if left running. Attendees (or facilitators) should **suspend** the capacity when idle. -- gpt-5.4-mini (GlobalStandard, 100K TPM) — a few cents of tokens per attendee across the hack's ~50 agent calls; Foundry account base ~$2/day. +- **Fabric capacity** (default **F2**, ~$0.36/hr pay-as-you-go) is the dominant cost — roughly **$8–9/attendee/day** if left running. Attendees (or facilitators) should **suspend** the capacity when idle. Bumping to **F4** (`-FabricSkuName F4`, or a live **on-demand resize** when throttling appears) roughly **doubles** this — update `estimatedDailyCostsUsd` in [`lab-defaults.json`](lab-defaults.json) accordingly. +- gpt-5.4-mini (GlobalStandard, 200K TPM) — a few cents of tokens per attendee across the hack's ~50 agent calls; Foundry account base ~$2/day. - Total ≈ **$9/attendee/day** — reflected in [`lab-defaults.json`](lab-defaults.json) (`estimatedDailyCostsUsd`). diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/deploy-lab.ps1 b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/deploy-lab.ps1 index 71b39bcbf..54e344a9e 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/deploy-lab.ps1 +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/deploy-lab.ps1 @@ -8,8 +8,8 @@ Provisions: - Azure AI Services account (AIServices kind — this is the Foundry account) - Azure AI Foundry project inside the account - - gpt-5.4-mini model deployment (capacity 100 GlobalStandard = 100K TPM) - - A per-attendee Fabric F2 capacity, with the attendee set as capacity admin + - gpt-5.4-mini model deployment (capacity 200 GlobalStandard = 200K TPM) + - A per-attendee Fabric capacity (default F2; -FabricSkuName to override), with the attendee set as capacity admin Each attendee gets their OWN Fabric capacity (no shared backend, no shared Spark contention). The attendee then creates a workspace, assigns it to their @@ -19,7 +19,7 @@ Returns to the attendee dashboard: - Foundry project endpoint (format: https://{name}.services.ai.azure.com/api/projects/{proj}) - Model deployment name - - The name of their Fabric F2 capacity (used when they create their workspace) + - The name of their Fabric capacity (used when they create their workspace) The platform pre-sets the Az context to $SubscriptionId. For 'resourcegroup' deployments it also pre-creates the resource group; for 'subscription' @@ -40,6 +40,12 @@ .PARAMETER AllowedEntraUserIds Entra user object IDs for this lab — passed in by the platform. + +.PARAMETER FabricSkuName + Per-attendee Fabric capacity SKU (default F2). Bump to F4 if attendees hit + capacity throttling. If you override it, ALSO raise estimatedDailyCostsUsd in + lab-defaults.json to match — Fabric is the dominant cost and each SKU step + roughly doubles it. #> param( [Parameter(Mandatory=$true)] @@ -53,7 +59,14 @@ param( [string[]]$PreferredLocation = @(), - [string[]]$AllowedEntraUserIds = @() + [string[]]$AllowedEntraUserIds = @(), + + # Per-attendee Fabric capacity SKU. Default F2 keeps cost low; bump to F4 if + # attendees hit capacity throttling. If you override this, ALSO raise + # estimatedDailyCostsUsd in lab-defaults.json (Fabric is the dominant cost; + # each SKU step roughly doubles it). + [ValidateSet('F2','F4','F8','F16','F32','F64')] + [string]$FabricSkuName = "F2" ) $scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition @@ -86,7 +99,7 @@ $foundryAccountName = "inv-$stableHash" $foundryProjectName = "inventory-hack" $modelDeployment = "gpt-5.4-mini" -# Per-attendee Fabric F2 capacity name (lowercase alphanumeric, 3-63 chars). +# Per-attendee Fabric capacity name (lowercase alphanumeric, 3-63 chars). $fabricCapacityName = "invcap$stableHash" Write-Host "[INFO] Deploying Foundry account '$foundryAccountName' in '$effectiveRG'..." @@ -271,7 +284,7 @@ Grant-FoundryUserRole ` -PrincipalDescription "project managed identity '$($projectResource.identity.principalId)'" # --------------------------------------------------------------------------- -# gpt-5.4-mini model deployment (ACCOUNT-scoped, GlobalStandard capacity 100). +# gpt-5.4-mini model deployment (ACCOUNT-scoped, GlobalStandard capacity 200). # Matches the pro-code inventory hack: a low-cost GPT-5 model whose Foundry model # card confirms Functions/Tools + Structured Outputs, on GlobalStandard in EU # regions, with the longest support horizon among the low-cost models @@ -283,8 +296,10 @@ Grant-FoundryUserRole ` # Fabric tool, set tool choice = required in the run settings (the challenge # instructions carry an "IMPORTANT - tool use" block for exactly this). # -# Capacity 100 = 100K TPM per attendee. Keep labsPerSubscription x 100 <= the -# region's GlobalStandard quota (~1,000) - 4 labs/subscription fits comfortably. +# Capacity 200 = 200K TPM per attendee. Keep labsPerSubscription x 200 <= the +# region's GlobalStandard quota (~1,000). At 5 labs/subscription that is 5 x 200 = +# 1,000, i.e. right at the ceiling - request a quota increase, or use one +# subscription per attendee, if attendees hit rate limits. # Model deployments are ACCOUNT-scoped (not project-scoped): the ARM path is # .../accounts/{account}/deployments/{deployment} - there is no /projects/ segment. # --------------------------------------------------------------------------- @@ -296,7 +311,7 @@ $deploymentUri = "/subscriptions/$SubscriptionId/resourceGroups/$effectiveRG" + $existingDeploy = Invoke-AzRestMethod -Method GET -Path $deploymentUri -ErrorAction SilentlyContinue if ($existingDeploy.StatusCode -ne 200) { $deploymentBody = @{ - sku = @{ name = "GlobalStandard"; capacity = 100 } + sku = @{ name = "GlobalStandard"; capacity = 200 } properties = @{ model = @{ format = "OpenAI"; name = "gpt-5.4-mini"; version = "2026-03-17" } } @@ -313,10 +328,12 @@ if ($existingDeploy.StatusCode -ne 200) { } # --------------------------------------------------------------------------- -# Per-attendee Fabric F2 capacity (ARM REST — no az CLI / Fabric extension needed). +# Per-attendee Fabric capacity (ARM REST — no az CLI / Fabric extension needed). # The attendee is set as capacity ADMIN so they can create a workspace, assign it # to this capacity, and Run All the setup notebook themselves (Challenge 1). -# F2 = 2 CU; a 512-CU subscription supports ~256 attendees. +# Default F2 = 2 CU; a 512-CU subscription supports ~256 attendees. F4 doubles CU +# (halving attendees/subscription) — override with -FabricSkuName and raise +# estimatedDailyCostsUsd in lab-defaults.json to match. # # Capacity admin members must be UPNs (or service principals) — bare object IDs are # rejected — so resolve each attendee object ID to a UPN with the platform helper @@ -336,7 +353,7 @@ foreach ($uid in $AllowedEntraUserIds) { } if ($adminUpns.Count -eq 0) { - Write-Warning "No attendee UPNs resolved — skipping Fabric capacity creation. Create the F2 capacity manually and set the attendee as capacity admin, or re-run once the lab-user cache is available." + Write-Warning "No attendee UPNs resolved — skipping Fabric capacity creation. Create the capacity manually and set the attendee as capacity admin, or re-run once the lab-user cache is available." } else { # Ensure the Microsoft.Fabric resource provider is registered (idempotent). $prov = Invoke-AzRestMethod -Method GET -Path "/subscriptions/$SubscriptionId/providers/Microsoft.Fabric?api-version=2021-04-01" @@ -350,10 +367,10 @@ if ($adminUpns.Count -eq 0) { Write-Host "[OK] Fabric capacity '$fabricCapacityName' already exists in '$fabricCapacityRegion' — skipping." } else { foreach ($region in $candidateRegions) { - Write-Host "[INFO] Creating Fabric F2 capacity '$fabricCapacityName' in '$region'..." + Write-Host "[INFO] Creating Fabric $FabricSkuName capacity '$fabricCapacityName' in '$region'..." $capBody = @{ location = $region - sku = @{ name = "F2"; tier = "Fabric" } + sku = @{ name = $FabricSkuName; tier = "Fabric" } properties = @{ administration = @{ members = @($adminUpns) } } } | ConvertTo-Json -Depth 6 $capUri = "/subscriptions/$SubscriptionId/resourceGroups/$effectiveRG" + @@ -371,7 +388,7 @@ if ($adminUpns.Count -eq 0) { } while (($cap.Properties.provisioningState -ne "Succeeded") -and ($elapsed -lt 180)) if ($cap.Properties.provisioningState -eq "Succeeded") { $fabricCapacityRegion = $region - Write-Host "[OK] Fabric F2 capacity '$fabricCapacityName' created in '$region'." + Write-Host "[OK] Fabric $FabricSkuName capacity '$fabricCapacityName' created in '$region'." break } Write-Warning "Fabric capacity provisioning did not succeed in '$region' — trying next region." diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/lab-defaults.json b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/lab-defaults.json index 6ccfb3b38..b5a19162f 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/lab-defaults.json +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/labautomation/lab-defaults.json @@ -2,7 +2,7 @@ "$schema": "https://raw.githubusercontent.com/microsoft/MicroHack/refs/heads/main/lab-defaults-schema.json", "groups": ["M365-E5-Users"], "deploymentType": "resourcegroup", - "labsPerSubscription": 6, + "labsPerSubscription": 5, "preferredLocation": "swedencentral, westeurope, norwayeast", "estimatedDailyCostsUsd": 9.0 } diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/setup/Setup-InventoryDataAgent.ipynb b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/setup/Setup-InventoryDataAgent.ipynb index e50b1a200..1e3270367 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/setup/Setup-InventoryDataAgent.ipynb +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/setup/Setup-InventoryDataAgent.ipynb @@ -172,7 +172,7 @@ "if not notebookutils.runtime.context.get(\"defaultLakehouseId\"):\n", " raise RuntimeError(\n", " \"No default Lakehouse is attached to this notebook. In the Explorer pane (left), \"\n", - " \"click Add > Existing Lakehouse > InventoryLakehouse (in your inventory-hack workspace), \"\n", + " \"click Add data items > From OneLake catalog > InventoryLakehouse (in your inventory-hack-... workspace), \"\n", " \"then Run all again.\"\n", " )\n", "\n", @@ -229,6 +229,9 @@ " \"DemandHistory (weekly units sold per product per store), \"\n", " \"ExternalSignals (market signals with affectedCategories), \"\n", " \"Suppliers, and ReplenishmentOrders (past purchase/transfer orders). \"\n", + " \"Category values are stored as lowercase snake_case: garden_and_lawn, outdoor_power_tools, paint_and_supplies, smart_home. \"\n", + " \"When a question names a category in plain English (e.g. 'Outdoor Power Tools'), map it to the exact snake_case value before filtering; the same applies to affectedCategories in ExternalSignals. \"\n", + " \"Prefer filtering by productId/SKU (e.g. P004) when the question names specific products. \"\n", " \"Always return exact numbers. Flag a product as CRITICAL when onHand is below safetyStock.\"\n", ")\n", "\n", diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/walkthrough/challenge-01/solution-01.md b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/walkthrough/challenge-01/solution-01.md index aed06a09b..76c1936ac 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/walkthrough/challenge-01/solution-01.md +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/walkthrough/challenge-01/solution-01.md @@ -25,16 +25,16 @@ Understand the inventory scenario, provision your own Fabric workspace + Data Ag ### Foundry portal navigation 1. Sign in at [ai.azure.com](https://ai.azure.com) with the credentials provided by your facilitator. -2. Select your project from the **Projects** list. -3. In the left navigation: **Models + endpoints** → confirm `gpt-5.4-mini` is listed with status **Succeeded**. -4. **Agents → Playground** → send "Hello" → confirm a response is returned. +2. Select your project — in a shared tenant, match the **`FoundryProjectEndpoint`** from your lab dashboard (Credentials tab) to the project's **Parent resource** to be sure it's yours. +3. Open your **model deployments** → confirm `gpt-5.4-mini` is listed with status **Succeeded**. +4. Open a **deployed model → Playground** → send "Hello" → confirm a response is returned. (The current portal opens the Playground from a model or agent, not a top-level *Agents → Playground* menu.) 5. You attach the **Fabric Data Agent** as a *tool* on each agent you build — the `inventory-hack-agent` connection is created the first time (Challenge 2) via **Tools → Add → Fabric Data Agent**, using the **Workspace ID and Agent ID your setup notebook printed** in Challenge 1. No standalone connection page is needed. ### Provision your Fabric workspace + Data Agent (Challenge 1 Task 3) -1. **Create the workspace on your capacity.** [app.fabric.microsoft.com](https://app.fabric.microsoft.com) → **Workspaces → + New workspace** → name `inventory-hack` → expand **Advanced** → **Workspace type: Fabric** → under **Details** select your **`FabricCapacityName`** (the `invcap…` value from your dashboard) → **Apply**. If the capacity isn't listed, resume it in Azure (your Fabric capacity → **Resume**) and confirm you're signed in as your lab user. +1. **Create the workspace on your capacity.** [app.fabric.microsoft.com](https://app.fabric.microsoft.com) → **Workspaces → + New workspace** → name **`inventory-hack-`** (workspace names are **tenant-unique**, so plain `inventory-hack` is rejected once taken) → expand **Advanced** → **Workspace type: Fabric** → under **Details** select your **`FabricCapacityName`** (the `invcap…` value from your dashboard) → **Apply**. If the capacity isn't listed, resume it in Azure (your Fabric capacity → **Resume**) and confirm you're signed in as your lab user. 2. **Create the Lakehouse.** **+ New item → Lakehouse**, named exactly **`InventoryLakehouse`**. -3. **Import + attach.** Download **`Setup-InventoryDataAgent.ipynb`** from the repo's **`setup/`** folder (or your facilitator's link), then **Import → Notebook → From this computer → Upload** in the workspace toolbar. Open it and, in the **Explorer**, **Add → Existing Lakehouse → `InventoryLakehouse`** as the **default** — if names collide, pick the one whose **Location** is your `inventory-hack` workspace (the write cells fail without a default Lakehouse). +3. **Import + attach.** Download **`Setup-InventoryDataAgent.ipynb`** from the repo's **`setup/`** folder (or your facilitator's link), then **Import → Notebook → From this computer → Upload** in the workspace toolbar. Open it and, in the **Explorer**, **Add data items → From OneLake catalog → `InventoryLakehouse`** as the **default** — if names collide, pick the one whose **Location** is your `inventory-hack-…` workspace (the write cells fail without a default Lakehouse). 4. **Run all** (~5–10 min). The kernel restarts once after the `%pip` cell — expected. It writes 7 tables and publishes `inventory-hack-agent`. 5. Copy the **Workspace ID** and **Agent ID** the final cell prints — you'll use them in Challenge 2. diff --git a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/walkthrough/challenge-03/solution-03.md b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/walkthrough/challenge-03/solution-03.md index 73a135d85..2ec127110 100644 --- a/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/walkthrough/challenge-03/solution-03.md +++ b/03-Azure/01-04-AI/02_Inventory_Planning_Fabric/walkthrough/challenge-03/solution-03.md @@ -16,7 +16,7 @@ This agent should have **only the Fabric Data Agent tool** — no Web Search. If ### Reading the trace — what to look for -In **Tracing**, each run shows a tree of spans: +In the agent's **Traces → Response view**, each run shows a tree of spans: ``` run @@ -34,17 +34,18 @@ run | Symptom | Cause | Fix | |---------|-------|-----| -| Agent returns "I don't have enough information" | Product/category words don't match Fabric values | Ask by the real product name or category, e.g. "Leaf Blower X2" or "outdoor power tools" | +| Agent returns "I don't have enough information" or an empty table | Product/category words don't match Fabric values | Ask by **SKU/productId** (e.g. `P004`) or the exact snake_case category value (`outdoor_power_tools`), not a friendly label like "Outdoor Power Tools" | | Reorder quantity seems wrong | Agent used a different formula | Remind the agent of the formula in a follow-up: "Use the rule: reorder_qty = max(0, 30-day demand - current stock)" | -| Trace not appearing | Tracing may take 30–60 seconds to update | Refresh the Tracing page | +| Trace not appearing | Tracing may take 30–60 seconds to update | Refresh the agent's **Traces → Response view** | ### Sample recommendation table +> Illustrative — exact quantities depend on the agent's rounding. `DemandHistory` is **weekly per retail store**, so `average_daily_sales = average weekly units / 7` (~0.8/day for P004). 30-day demand is then ~24, so a store with 6–8 on hand yields a small positive reorder and a **CRITICAL** flag (on hand below safety stock 30). + ``` -| SKU | Product | Location | Current Stock | Suggested Reorder Qty | Priority | -|------|----------------|-----------|---------------|-----------------------|----------| -| P004 | Leaf Blower X2 | Seattle | 6 | 54 | CRITICAL | -| P004 | Leaf Blower X2 | Portland | 8 | 52 | CRITICAL | -| P005 | Chainsaw 16in | Portland | 5 | 31 | CRITICAL | -| P004 | Leaf Blower X2 | Chicago | 45 | 15 | At Risk | +| SKU | Product | Location | Current Stock | Suggested Reorder Qty | Priority | +|------|----------------|----------|---------------|-----------------------|----------| +| P004 | Leaf Blower X2 | Seattle | 6 | 18 | CRITICAL | +| P004 | Leaf Blower X2 | Portland | 8 | 16 | CRITICAL | +| P006 | Hedge Trimmer | Portland | 12 | 8 | CRITICAL | ```