diff --git a/notebooks/media/microsoft-iq-in-foundry/01-architecture.png b/notebooks/media/microsoft-iq-in-foundry/01-architecture.png new file mode 100644 index 0000000..b3a338c Binary files /dev/null and b/notebooks/media/microsoft-iq-in-foundry/01-architecture.png differ diff --git a/notebooks/microsoft-iq-in-foundry.ipynb b/notebooks/microsoft-iq-in-foundry.ipynb new file mode 100644 index 0000000..1128f69 --- /dev/null +++ b/notebooks/microsoft-iq-in-foundry.ipynb @@ -0,0 +1,985 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e99230b7", + "metadata": {}, + "source": [ + "![The Microsoft IQ platform — unified intelligence for enterprise AI. Four knowledge layers shown side by side: Work IQ (“how your employees work”) grounds on context about people, collaboration, and workflows; Fabric IQ (“how your business operates”) grounds on business entities, systems of record, and actions; Foundry IQ (“how your agents unlock knowledge”) grounds on policies, authoritative documents, and knowledge bases; and Web IQ (“how you connect to web intelligence”) grounds on context from the web, news, images, and video.](media/microsoft-iq-in-foundry/01-architecture.png)\n", + "\n", + "# Build a Microsoft IQ knowledge layer in Foundry IQ\n", + "\n", + "[**Microsoft IQ**](https://learn.microsoft.com/en-us/microsoft-iq/) is a family\n", + "of knowledge layers that give AI agents the context they need to act with\n", + "judgment. Each IQ grounds a different slice of reality:\n", + "\n", + "| Microsoft IQ | What it grounds on |\n", + "|---|---|\n", + "| [**Work IQ**](https://learn.microsoft.com/en-us/microsoft-iq/) | The live understanding of *how your people work* — mail, chats, files, and meetings across Microsoft 365. |\n", + "| [**Fabric IQ**](https://learn.microsoft.com/en-us/microsoft-iq/) | The live state of *your business* as a semantic **ontology** in Microsoft Fabric — here, an **airline ontology** (flights, routes, aircraft, crews). |\n", + "| [**Web IQ**](https://aka.ms/WebIQLearn) | Fresh, real-world intelligence from the **open web** — web, news, videos, and browse. |\n", + "| [**Foundry IQ**](https://learn.microsoft.com/en-us/azure/search/agentic-retrieval-overview) | The **federation layer**. It curates the other IQs into one Knowledge Base and grounds any agent. |\n", + "\n", + "**Foundry IQ is the layer you build here.** It is built on Azure AI Search's\n", + "information-retrieval ranking and **agentic-retrieval** pipelines: a *Knowledge\n", + "Base* (KB) fans a single natural-language question out across many *Knowledge\n", + "Sources* (KS), then plans, retrieves, reranks, and **synthesizes a cited\n", + "answer**. Wire Work IQ, Fabric IQ, and Web IQ in as three federated sources and\n", + "you get one knowledge layer that grounds *any* agent with work, business, and\n", + "web knowledge at once.\n", + "\n", + "The arc of this recipe: **three Microsoft IQ sources → one Foundry IQ Knowledge\n", + "Base → a query that proves each IQ answers, plus a cross-source query that joins\n", + "them**. We stop at retrieval — the closing section shows how to point any agent\n", + "at the resulting MCP endpoint.\n", + "\n", + "> **Preview status & region.** These federated KS types (Work IQ, Fabric\n", + "> ontology, Web IQ via MCP) are **preview** on the `2026-05-01-preview` Search\n", + "> API. Your Search service must live in a\n", + "> [preview region](https://learn.microsoft.com/azure/search/search-region-support)\n", + "> — this recipe was authored against **West Central US**. GA timing and region\n", + "> coverage change; check\n", + "> [What's new](https://learn.microsoft.com/azure/search/whats-new) first.\n", + "\n", + "> **Note on running this notebook.** Every section degrades gracefully: any IQ\n", + "> whose credential/entitlement is missing prints a `[skipped]` line and the rest\n", + "> still runs top-to-bottom. Cells ship with **outputs cleared** — they call live\n", + "> Azure / Foundry / Microsoft Grounding endpoints, so run them against your own\n", + "> resources.\n" + ] + }, + { + "cell_type": "markdown", + "id": "74db974b", + "metadata": {}, + "source": [ + "## 1 · Prerequisites\n", + "\n", + "Each Microsoft IQ has its own access path. Foundry IQ (the KB) only needs an\n", + "Azure AI Search service + a chat model; the three IQs you federate in have the\n", + "gates below.\n", + "\n", + "| Requirement | Notes |\n", + "|---|---|\n", + "| **Azure AI Search service** (Foundry IQ) | In a [region that provides agentic retrieval](https://learn.microsoft.com/azure/search/search-region-support) (e.g. **West Central US**). Use *Search Service Contributor* (keyless, recommended) or an admin key. |\n", + "| **Microsoft Foundry project** | One chat model deployment (e.g. `gpt-4.1`, `gpt-4.1-mini`, `gpt-4o`) — the KB planner/synthesizer. |\n", + "| **Work IQ access** *(gated + licensed)* | Work IQ retrieval is **off by default**. Register the `EnableFoundryIQWithWorkIQ` feature flag, re-register the `Microsoft.Search` provider, and have a tenant admin submit the [Work IQ access request form](https://aka.ms/foundry-iq-work-iq-admin-consent-form). Each querying user needs a **Microsoft 365 Copilot license**, and the search service + Work IQ + users must share **one Entra tenant**. How-to: [Work IQ knowledge source](https://learn.microsoft.com/en-us/azure/search/agentic-knowledge-source-how-to-work-iq?pivots=python). |\n", + "| **Fabric IQ ontology** | A Fabric workspace with the [ontology tenant settings](https://learn.microsoft.com/fabric/iq/ontology/overview-tenant-settings) enabled and an [ontology item](https://learn.microsoft.com/fabric/iq/ontology/tutorial-1-create-ontology) — in the **same Entra tenant** as the search service. How-to: [Fabric ontology knowledge source](https://learn.microsoft.com/en-us/azure/search/agentic-knowledge-source-how-to-fabric-ontology?pivots=python). |\n", + "| **Web IQ access** *(waitlist)* | Web IQ is available through a **waitlist** — [join here](https://aka.ms/webiq-waitlist) ([learn more](https://aka.ms/WebIQLearn)). Once approved you get an API key for the **remote Microsoft Grounding MCP server** at `https://api.microsoft.ai/v3/mcp`. |\n", + "\n", + "> **One token, two per-user IQs.** Both Work IQ and Fabric IQ enforce\n", + "> permissions at query time via an **on-behalf-of (OBO)** token — you pass the\n", + "> *signed-in user's* token (audience `https://search.azure.com/.default`) on the\n", + "> retrieve call. §7 mints it for you. Web IQ uses its own stored `x-apikey`.\n", + "\n", + "### Configure your environment\n", + "\n", + "Set these in your shell, or drop them into a local `.env` next to this notebook\n", + "(the repo `.gitignore` already excludes `.env`). **Secrets are read from the\n", + "environment — never written into this notebook.**\n", + "\n", + "```bash\n", + "SEARCH_ENDPOINT=https://.search.windows.net\n", + "SEARCH_API_KEY=\n", + "AOAI_ENDPOINT=https://.openai.azure.com\n", + "AOAI_API_KEY=\n", + "AOAI_GPT_DEPLOYMENT=gpt-4.1 # or gpt-4.1-mini, gpt-4o, ...\n", + "\n", + "# Secret — required for Web IQ (§5). Leave blank to skip that source.\n", + "WEB_IQ_MCP_API_KEY=\n", + "\n", + "# Fabric IQ ontology IDs (defaults below point at a sample airline ontology).\n", + "FABRIC_WORKSPACE_ID=\n", + "FABRIC_ONTOLOGY_ID=\n", + "```\n", + "\n", + "### Install dependencies\n", + "\n", + "This recipe pins the public-preview `azure-search-documents` build that exposes\n", + "the `2026-05-01-preview` Knowledge Base / Knowledge Source surface.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a905bde", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "# Foundry IQ rides on the public-preview azure-search-documents SDK, which\n", + "# exposes the 2026-05-01-preview Knowledge Base / Knowledge Source surface.\n", + "# It ships on PyPI -- no extra package feed required.\n", + "import importlib.metadata as _md\n", + "\n", + "try:\n", + " _ok = _md.version(\"azure-search-documents\").startswith(\"12.1.0b\")\n", + "except _md.PackageNotFoundError:\n", + " _ok = False\n", + "\n", + "if not _ok:\n", + " %pip install --quiet \\\n", + " \"azure-search-documents==12.1.0b1\" \\\n", + " \"azure-identity>=1.19.0\" \\\n", + " \"azure-core>=1.32.0\" \\\n", + " \"python-dotenv>=1.0.1\"\n" + ] + }, + { + "cell_type": "markdown", + "id": "dc3f9792", + "metadata": {}, + "source": [ + "## 2 · Configure clients + helpers\n", + "\n", + "One `SearchIndexClient` + one `AzureKeyCredential` are reused throughout:\n", + "\n", + "- `env(name, required=...)` — env-var lookup (with `.env` support) and a\n", + " friendly error. **Secrets like `WEB_IQ_MCP_API_KEY` are read here — never\n", + " hard-coded.**\n", + "- `skip(section, reason)` — prints a `[skipped]` line so a missing\n", + " credential never breaks the run.\n", + "- `summarize_ks(name)` — prints the SDK status of a KS right after create.\n", + "- `created_ks` — every IQ section appends to it on success; §6 builds the\n", + " single Foundry IQ KB from whatever it finds.\n", + "- `created_resources` — a dict the cleanup section (§9) walks in dependency\n", + " order.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ec21e53", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from typing import Optional\n", + "\n", + "from azure.core.credentials import AzureKeyCredential\n", + "from azure.search.documents import __version__ as search_sdk_version\n", + "from azure.search.documents.indexes import SearchIndexClient\n", + "from dotenv import load_dotenv\n", + "\n", + "load_dotenv(override=True)\n", + "\n", + "\n", + "def env(name: str, *, required: bool = True, default: Optional[str] = None) -> Optional[str]:\n", + " value = os.getenv(name, default)\n", + " if required and not value:\n", + " raise RuntimeError(f\"Missing required env var: {name}\")\n", + " return value or None\n", + "\n", + "\n", + "def foundry_resource_uri(endpoint: str) -> str:\n", + " # Strip any /openai/... path so we have the bare Foundry resource URI.\n", + " return endpoint.split(\"/openai/\", 1)[0].rstrip(\"/\")\n", + "\n", + "\n", + "# ---- Required ------------------------------------------------------------\n", + "SEARCH_ENDPOINT = env(\"SEARCH_ENDPOINT\")\n", + "SEARCH_API_KEY = env(\"SEARCH_API_KEY\")\n", + "AOAI_ENDPOINT = foundry_resource_uri(env(\"AOAI_ENDPOINT\"))\n", + "AOAI_API_KEY = env(\"AOAI_API_KEY\")\n", + "\n", + "GPT_DEPLOYMENT = env(\"AOAI_GPT_DEPLOYMENT\", required=False, default=\"gpt-4.1\")\n", + "GPT_MODEL = env(\"AOAI_GPT_MODEL\", required=False, default=GPT_DEPLOYMENT)\n", + "\n", + "# ---- Resource names ------------------------------------------------------\n", + "KS_WORK_IQ = \"miq-ks-work-iq\"\n", + "KS_FABRIC_IQ = \"miq-ks-fabric-iq\"\n", + "KS_WEB_IQ = \"miq-ks-web-iq\"\n", + "KB_NAME = \"miq-foundry-iq-kb\"\n", + "\n", + "credential = AzureKeyCredential(SEARCH_API_KEY)\n", + "index_client = SearchIndexClient(endpoint=SEARCH_ENDPOINT, credential=credential)\n", + "\n", + "# ---- Trackers consumed by the KB section and cleanup --------------------\n", + "created_ks: list[str] = []\n", + "created_resources: dict[str, str] = {}\n", + "\n", + "print(f\"azure-search-documents : {search_sdk_version}\")\n", + "print(f\"Search service : {SEARCH_ENDPOINT}\")\n", + "print(f\"Foundry endpoint : {AOAI_ENDPOINT}\")\n", + "print(f\"Chat deployment : {GPT_DEPLOYMENT} ({GPT_MODEL})\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3d5a77a", + "metadata": {}, + "outputs": [], + "source": [ + "from azure.core.exceptions import ResourceNotFoundError\n", + "\n", + "\n", + "def skip(section: str, reason: str) -> None:\n", + " print(f\"[skipped] {section}: {reason}\")\n", + "\n", + "\n", + "def summarize_ks(name: str) -> None:\n", + " \"\"\"Print the SDK status of a Knowledge Source after create.\"\"\"\n", + " try:\n", + " status = index_client.get_knowledge_source_status(name)\n", + " except Exception as exc: # pragma: no cover - best-effort summary\n", + " print(f\" status: \")\n", + " return\n", + " sync = getattr(status, \"synchronization_status\", None) or \"?\"\n", + " last_state = getattr(status, \"last_synchronization_state\", None)\n", + " last = getattr(last_state, \"status\", None) if last_state else \"n/a\"\n", + " stats = getattr(status, \"statistics\", None) or {}\n", + " print(f\" synchronizationStatus={sync} lastSync={last} stats={stats}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "eb8ad8e3", + "metadata": {}, + "source": [ + "## 3 · Work IQ — how your people work\n", + "\n", + "[**Work IQ**](https://learn.microsoft.com/en-us/microsoft-iq/) is the live\n", + "understanding of how your organization works: the mail, chats, files, and\n", + "meetings across Microsoft 365. As a Foundry IQ Knowledge Source it grounds\n", + "agents in *your* tenant's work content.\n", + "\n", + "| | |\n", + "|---|---|\n", + "| **`kind`** | `workIQ` |\n", + "| **Grounds on** | Microsoft 365 work content (mail, chats, files, meetings) via Work IQ. |\n", + "| **Pipeline** | Federated — queries Work IQ live at retrieve time; nothing is indexed. |\n", + "| **Auth model** | **Per-user OBO.** Each retrieve passes the caller's token via `x-ms-query-source-authorization` (audience `https://search.azure.com/.default`); the engine exchanges it for a Work IQ token and enforces that user's M365 permissions. |\n", + "\n", + "The typed model is intentionally tiny — `workIQ` takes **only** a `name` and\n", + "`description` (no parameters object). Creation requires the gated access from\n", + "§1, so we wrap it in `try/except` and `skip()` cleanly if your tenant isn't\n", + "enabled yet.\n", + "\n", + "> **Gated + licensed.** Register the `EnableFoundryIQWithWorkIQ` feature flag,\n", + "> re-register `Microsoft.Search`, and have a tenant admin complete the\n", + "> [access request form](https://aka.ms/foundry-iq-work-iq-admin-consent-form).\n", + "> Each querying user needs a **Microsoft 365 Copilot license**.\n", + "\n", + "> **Latency.** Work IQ can take **40–60 seconds or more** to respond. Keep\n", + "> `max_runtime_in_seconds` at **120+** on the retrieve call (we use 180 in §7).\n", + "\n", + "[Create a Work IQ knowledge source (Python)](https://learn.microsoft.com/en-us/azure/search/agentic-knowledge-source-how-to-work-iq?pivots=python)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e173463", + "metadata": {}, + "outputs": [], + "source": [ + "from azure.search.documents.indexes.models import WorkIQKnowledgeSource\n", + "\n", + "# Work IQ takes no parameters object -- just name + description (kind=\"workIQ\").\n", + "ks = WorkIQKnowledgeSource(\n", + " name=KS_WORK_IQ,\n", + " description=\"Work IQ -- tenant mail, chats, files, and meetings via Microsoft 365.\",\n", + ")\n", + "try:\n", + " index_client.create_or_update_knowledge_source(ks)\n", + " created_ks.append(KS_WORK_IQ)\n", + " print(f\"Created {KS_WORK_IQ}\")\n", + " summarize_ks(KS_WORK_IQ)\n", + "except Exception as exc:\n", + " skip(KS_WORK_IQ, f\"create failed (tenant may not be entitled for Work IQ): {exc}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "ed995252", + "metadata": {}, + "source": [ + "## 4 · Fabric IQ — the live state of your business\n", + "\n", + "[**Fabric IQ**](https://learn.microsoft.com/en-us/microsoft-iq/) represents the\n", + "live state of your business as a semantic **ontology** in Microsoft Fabric —\n", + "entities, relationships, properties, and rules. Here it's an **airline\n", + "ontology** (flights, routes, aircraft, crews), so agents answer in business\n", + "terms instead of reasoning over raw tables.\n", + "\n", + "| | |\n", + "|---|---|\n", + "| **`kind`** | `fabricOntology` |\n", + "| **Grounds on** | A semantic ontology in Microsoft Fabric — an airline ontology. |\n", + "| **Pipeline** | Federated — queries the ontology graph in Fabric live. |\n", + "| **Auth model** | **Two layers.** *Creation:* the search service's **managed identity** needs access to the Fabric workspace. *Retrieval:* a per-user `query_source_authorization` OBO token (audience `https://search.azure.com/.default`), which the engine exchanges for a Fabric-scoped token (see §7). |\n", + "\n", + "A Fabric ontology is addressed by a **workspace id** + **ontology id** (both\n", + "visible in the Fabric portal URL of the ontology item). Set them via\n", + "`FABRIC_WORKSPACE_ID` / `FABRIC_ONTOLOGY_ID`.\n", + "\n", + "> **Reasoning-effort constraint.** Fabric Ontology sources **don't support the\n", + "> `minimal`** retrieval reasoning effort — use **`low`** or **`medium`**. Our KB\n", + "> in §6 uses `low`.\n", + "\n", + "> **Response shape.** Fabric references carry `sourceData.fabricAnswer` (a\n", + "> natural-language answer) and `sourceData.fabricRawData` (the grounding data as\n", + "> CSV). Set `include_reference_source_data=True` (we do, in §7) to receive them.\n", + "\n", + "> **Managed-identity gotcha.** If creation or retrieval returns a `403`/access\n", + "> error, grant the search service identity **Viewer** (or higher) on the Fabric\n", + "> workspace, then re-run.\n", + "\n", + "[Create a Fabric ontology knowledge source (Python)](https://learn.microsoft.com/en-us/azure/search/agentic-knowledge-source-how-to-fabric-ontology?pivots=python)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27bd6322", + "metadata": {}, + "outputs": [], + "source": [ + "from azure.search.documents.indexes.models import (\n", + " FabricOntologyKnowledgeSource,\n", + " FabricOntologyKnowledgeSourceParameters,\n", + ")\n", + "\n", + "# Point this at YOUR ontology. Both IDs are in the Fabric ontology item URL:\n", + "# https://app.fabric.microsoft.com/groups//ontologies/\n", + "# This recipe uses an airline ontology as the running example; substitute your own.\n", + "FABRIC_WORKSPACE_ID = env(\"FABRIC_WORKSPACE_ID\", required=False)\n", + "FABRIC_ONTOLOGY_ID = env(\"FABRIC_ONTOLOGY_ID\", required=False)\n", + "\n", + "if not (FABRIC_WORKSPACE_ID and FABRIC_ONTOLOGY_ID):\n", + " skip(KS_FABRIC_IQ, \"set FABRIC_WORKSPACE_ID and FABRIC_ONTOLOGY_ID to your own ontology to enable Fabric IQ\")\n", + "else:\n", + " ks = FabricOntologyKnowledgeSource(\n", + " name=KS_FABRIC_IQ,\n", + " description=\"Fabric IQ -- a business ontology (here, an airline ontology: flights, routes, aircraft, crews) in Microsoft Fabric.\",\n", + " fabric_ontology_parameters=FabricOntologyKnowledgeSourceParameters(\n", + " workspace_id=FABRIC_WORKSPACE_ID,\n", + " ontology_id=FABRIC_ONTOLOGY_ID,\n", + " ),\n", + " )\n", + " try:\n", + " index_client.create_or_update_knowledge_source(ks)\n", + " created_ks.append(KS_FABRIC_IQ)\n", + " print(f\"Created {KS_FABRIC_IQ} (workspace={FABRIC_WORKSPACE_ID}, ontology={FABRIC_ONTOLOGY_ID})\")\n", + " summarize_ks(KS_FABRIC_IQ)\n", + " except Exception as exc:\n", + " skip(KS_FABRIC_IQ, f\"create failed (check Search MI access to the Fabric workspace): {exc}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "a4229991", + "metadata": {}, + "source": [ + "## 5 · Web IQ — fresh real-world web intelligence\n", + "\n", + "[**Web IQ**](https://aka.ms/WebIQLearn) gives agents fresh, real-world\n", + "intelligence from the open web — web pages, news, videos, and browse — through\n", + "the **remote Microsoft Grounding MCP server** at `https://api.microsoft.ai/v3/mcp`.\n", + "Foundry IQ federates it in as an MCP Server Knowledge Source.\n", + "\n", + "> **Access is waitlisted.** You must [**join the Web IQ waitlist**](https://aka.ms/webiq-waitlist)\n", + "> to get an API key. Once approved, set it as `WEB_IQ_MCP_API_KEY` (secret).\n", + "> Until then, this section `skip()`s and the rest of the notebook still runs.\n", + "\n", + "| | |\n", + "|---|---|\n", + "| **`kind`** | `mcpServer` |\n", + "| **Grounds on** | The live web via the remote Web IQ / Microsoft Grounding MCP server. |\n", + "| **Tools** | `web`, `news`, `videos`, `browse` — each reranked and capped at 4096 output tokens. |\n", + "| **Auth model** | A **stored header** (`x-apikey`) carrying your Web IQ MCP key. |\n", + "| **Env vars** | `WEB_IQ_MCP_API_KEY` (**secret** — read from the environment, never committed). |\n", + "\n", + "> **Why stored headers and not a Foundry connection?** The MCP server supports\n", + "> both a `foundryConnection` auth variant and a `storedHeaders` variant. As of\n", + "> this preview, the `foundryConnection` form returns **HTTP 502 \"Could not\n", + "> resolve the tool manifest\"** on KB Retrieve (a known preview bug). The\n", + "> `storedHeaders` form (below) sends `x-apikey` directly and works reliably.\n", + "\n", + "[MCP Server knowledge source (Python)](https://learn.microsoft.com/en-us/azure/search/agentic-knowledge-source-overview)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "599cc298", + "metadata": {}, + "outputs": [], + "source": [ + "from azure.search.documents.indexes.models import (\n", + " McpServerKnowledgeSource,\n", + " McpServerKnowledgeSourceParameters,\n", + " McpServerStoredHeadersAuthentication,\n", + " McpServerStoredHeadersParameters,\n", + ")\n", + "\n", + "WEB_IQ_MCP_SERVER_URL = \"https://api.microsoft.ai/v3/mcp\" # public URL -- fine to commit\n", + "\n", + "# The API key is a SECRET: read it from the environment, never hard-code it.\n", + "web_iq_key = env(\"WEB_IQ_MCP_API_KEY\", required=False)\n", + "\n", + "if not web_iq_key:\n", + " skip(KS_WEB_IQ, \"set WEB_IQ_MCP_API_KEY (your Web IQ MCP key) to enable Web IQ -- join the waitlist at https://aka.ms/webiq-waitlist\")\n", + "else:\n", + " web_iq_tools = [\n", + " {\"name\": \"web\", \"outputParsing\": {\"kind\": \"auto\"}, \"inclusionMode\": \"reranked\", \"maxOutputTokens\": 4096},\n", + " {\"name\": \"news\", \"outputParsing\": {\"kind\": \"auto\"}, \"inclusionMode\": \"reranked\", \"maxOutputTokens\": 4096},\n", + " {\"name\": \"videos\", \"outputParsing\": {\"kind\": \"auto\"}, \"inclusionMode\": \"reranked\", \"maxOutputTokens\": 4096},\n", + " {\"name\": \"browse\", \"outputParsing\": {\"kind\": \"auto\"}, \"inclusionMode\": \"reranked\", \"maxOutputTokens\": 4096},\n", + " ]\n", + " ks = McpServerKnowledgeSource(\n", + " name=KS_WEB_IQ,\n", + " description=\"Web IQ -- Microsoft Grounding MCP server (web, news, videos, browse).\",\n", + " mcp_server_parameters=McpServerKnowledgeSourceParameters(\n", + " server_url=WEB_IQ_MCP_SERVER_URL,\n", + " # storedHeaders auth (x-apikey). NOT foundryConnection -- that variant\n", + " # currently 502s (\"Could not resolve the tool manifest\") on KB Retrieve.\n", + " authentication=McpServerStoredHeadersAuthentication(\n", + " stored_headers_parameters=McpServerStoredHeadersParameters(\n", + " headers={\"x-apikey\": web_iq_key},\n", + " ),\n", + " ),\n", + " tools=web_iq_tools,\n", + " ),\n", + " )\n", + " try:\n", + " index_client.create_or_update_knowledge_source(ks)\n", + " created_ks.append(KS_WEB_IQ)\n", + " print(f\"Created {KS_WEB_IQ} ({WEB_IQ_MCP_SERVER_URL})\")\n", + " summarize_ks(KS_WEB_IQ)\n", + " except Exception as exc:\n", + " skip(KS_WEB_IQ, f\"create failed: {exc}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "669feac0", + "metadata": {}, + "source": [ + "## 6 · Build the Foundry IQ Knowledge Base\n", + "\n", + "One **Foundry IQ** Knowledge Base consumes whichever of the three Microsoft IQ\n", + "sources came up. The KB:\n", + "\n", + "| Setting | What it controls |\n", + "|---|---|\n", + "| `models` | The Azure OpenAI chat model that **plans** subqueries and **synthesizes** the answer. |\n", + "| `knowledge_sources` | The federated IQs in scope — Work IQ, Fabric IQ, Web IQ. |\n", + "| `retrieval_reasoning_effort` | `low` / `medium` — how hard the planner thinks before fanning out. |\n", + "| `output_mode` | `extractiveData` (raw chunks) or **`answerSynthesis`** (cited NL answer). |\n", + "\n", + "We pick **`answerSynthesis`** + **`low`** — the production-friendly default.\n", + "(`low` also satisfies Fabric IQ, which **rejects `minimal`**.) The cell raises\n", + "only if *none* of the three IQs were created.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9cfa3c77", + "metadata": {}, + "outputs": [], + "source": [ + "from azure.search.documents.indexes.models import (\n", + " AzureOpenAIVectorizerParameters,\n", + " KnowledgeBase,\n", + " KnowledgeBaseAzureOpenAIModel,\n", + " KnowledgeSourceReference,\n", + ")\n", + "from azure.search.documents.knowledgebases.models import (\n", + " KnowledgeRetrievalLowReasoningEffort,\n", + " KnowledgeRetrievalOutputMode,\n", + ")\n", + "\n", + "if not created_ks:\n", + " raise RuntimeError(\n", + " \"No Knowledge Sources were created -- cannot build a KB. Enable at least \"\n", + " \"one of Work IQ / Fabric IQ / Web IQ above.\"\n", + " )\n", + "\n", + "# Build the Foundry IQ KB over whatever Microsoft IQ sources came up this run.\n", + "kb_sources = list(created_ks)\n", + "print(f\"Building Foundry IQ KB '{KB_NAME}' over {len(kb_sources)} federated source(s):\")\n", + "for n in kb_sources:\n", + " print(f\" - {n}\")\n", + "\n", + "gpt_params = AzureOpenAIVectorizerParameters(\n", + " resource_url=AOAI_ENDPOINT,\n", + " deployment_name=GPT_DEPLOYMENT,\n", + " api_key=AOAI_API_KEY,\n", + " model_name=GPT_MODEL,\n", + ")\n", + "\n", + "kb = KnowledgeBase(\n", + " name=KB_NAME,\n", + " description=\"Foundry IQ KB federating Work IQ, Fabric IQ (airline ontology), and Web IQ.\",\n", + " models=[KnowledgeBaseAzureOpenAIModel(azure_open_ai_parameters=gpt_params)],\n", + " knowledge_sources=[KnowledgeSourceReference(name=n) for n in kb_sources],\n", + " retrieval_reasoning_effort=KnowledgeRetrievalLowReasoningEffort(),\n", + " output_mode=KnowledgeRetrievalOutputMode.ANSWER_SYNTHESIS,\n", + " answer_instructions=(\n", + " \"Answer using only the retrieved content across all sources. \"\n", + " \"When a question spans work content, the airline ontology, and the web, \"\n", + " \"integrate them faithfully and preserve the [ref_id:N] citations.\"\n", + " ),\n", + ")\n", + "index_client.create_or_update_knowledge_base(kb)\n", + "created_resources[\"kb\"] = KB_NAME\n", + "print(f\"\\nFoundry IQ Knowledge Base '{KB_NAME}' is ready.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "8f161948", + "metadata": {}, + "source": [ + "## 7 · Query the knowledge layer\n", + "\n", + "This is the payoff. We run **four** retrievals against the one Foundry IQ KB:\n", + "\n", + "1. **7a Work IQ** — a work question, scoped to Work IQ.\n", + "2. **7b Fabric IQ** — an airline-ontology question, scoped to Fabric IQ.\n", + "3. **7c Web IQ** — a fresh-web question, scoped to Web IQ.\n", + "4. **7d Cross-source** — one question that **joins ≥2 IQs**, with all sources in\n", + " scope.\n", + "\n", + "Each cell prints the synthesized answer, the **reference count per source**, and\n", + "a sample extract — so you can *see* each Microsoft IQ grounding the answer.\n", + "\n", + "> **Per-user IQs need a caller token.** Both **Work IQ** and **Fabric IQ** are\n", + "> identity-scoped: each retrieve must carry a user token via\n", + "> `query_source_authorization` (audience `https://search.azure.com`). **Web IQ**\n", + "> authenticates with its own stored `x-apikey`, so it grounds with or without\n", + "> the token. The setup cell below mints the signed-in user's token with\n", + "> `DefaultAzureCredential`. See\n", + "> [Retrieve from a knowledge base (Python)](https://learn.microsoft.com/en-us/azure/search/agentic-retrieval-how-to-retrieve?pivots=python).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75d6eec2", + "metadata": {}, + "outputs": [], + "source": [ + "import json as _json\n", + "\n", + "from azure.identity import DefaultAzureCredential\n", + "from azure.search.documents.knowledgebases import KnowledgeBaseRetrievalClient\n", + "from azure.search.documents.knowledgebases.models import (\n", + " FabricOntologyKnowledgeSourceParams,\n", + " KnowledgeBaseMessage,\n", + " KnowledgeBaseMessageTextContent,\n", + " KnowledgeBaseRetrievalRequest,\n", + " McpServerKnowledgeSourceParams,\n", + " WorkIQKnowledgeSourceParams,\n", + ")\n", + "\n", + "retrieval_client = KnowledgeBaseRetrievalClient(\n", + " endpoint=SEARCH_ENDPOINT,\n", + " credential=credential,\n", + " knowledge_base_name=KB_NAME,\n", + ")\n", + "\n", + "\n", + "def user_query_authorization() -> Optional[str]:\n", + " \"\"\"Mint the signed-in user's token for the per-user IQs (Work IQ, Fabric IQ).\"\"\"\n", + " try:\n", + " token = DefaultAzureCredential().get_token(\"https://search.azure.com/.default\").token\n", + " print(\"query_source_authorization : acquired user token\")\n", + " return token\n", + " except Exception as exc: # noqa: BLE001 -- best-effort; Web IQ still works\n", + " print(f\"query_source_authorization : unavailable ({exc}); Work IQ + Fabric IQ will return no references\")\n", + " return None\n", + "\n", + "\n", + "query_auth = user_query_authorization()\n", + "\n", + "\n", + "def ks_params_for(name: str, reranker_threshold: Optional[float] = None):\n", + " \"\"\"Per-kind retrieve params for the federated IQ wired into the KB.\"\"\"\n", + " common = dict(\n", + " knowledge_source_name=name,\n", + " include_references=True,\n", + " include_reference_source_data=True,\n", + " )\n", + " if reranker_threshold is not None:\n", + " common[\"reranker_threshold\"] = reranker_threshold\n", + " if name == KS_WORK_IQ:\n", + " return WorkIQKnowledgeSourceParams(**common)\n", + " if name == KS_FABRIC_IQ:\n", + " return FabricOntologyKnowledgeSourceParams(**common)\n", + " if name == KS_WEB_IQ:\n", + " return McpServerKnowledgeSourceParams(**common)\n", + " return None\n", + "\n", + "\n", + "def retrieve(question: str, sources: list[str], *, reranker_threshold=None, max_runtime_seconds: int = 180):\n", + " \"\"\"Run one KB retrieval, scoped to `sources` (a subset of the KB's IQs).\"\"\"\n", + " params = [ks_params_for(n, reranker_threshold) for n in sources]\n", + " request = KnowledgeBaseRetrievalRequest(\n", + " messages=[\n", + " KnowledgeBaseMessage(\n", + " role=\"user\",\n", + " content=[KnowledgeBaseMessageTextContent(text=question)],\n", + " )\n", + " ],\n", + " knowledge_source_params=[p for p in params if p],\n", + " include_activity=True,\n", + " max_runtime_in_seconds=max_runtime_seconds,\n", + " )\n", + " # query_source_authorization auths the per-user IQs (Work IQ, Fabric IQ).\n", + " return retrieval_client.retrieve(request, query_source_authorization=query_auth)\n", + "\n", + "\n", + "def answer_text(result) -> str:\n", + " parts = []\n", + " for message in (result.response or []):\n", + " for content in (message.content or []):\n", + " text = getattr(content, \"text\", None)\n", + " if text:\n", + " parts.append(text)\n", + " return \"\\n\\n\".join(parts)\n", + "\n", + "\n", + "def describe_reference(ref) -> str:\n", + " \"\"\"Pull a human-readable snippet out of a reference, per IQ type.\"\"\"\n", + " rtype = getattr(ref, \"type\", None)\n", + " src = ref.source_data or {}\n", + " if not isinstance(src, dict):\n", + " return str(src)[:240]\n", + " if rtype == \"fabricOntology\": # Fabric IQ\n", + " bits = []\n", + " if src.get(\"fabricAnswer\"):\n", + " bits.append(str(src[\"fabricAnswer\"]))\n", + " if src.get(\"fabricRawData\"):\n", + " bits.append(\"data: \" + str(src[\"fabricRawData\"])[:160])\n", + " return \" | \".join(bits) or _json.dumps(src)[:240]\n", + " if rtype == \"workIQ\": # Work IQ\n", + " texts = [e.get(\"text\") for e in (src.get(\"extracts\") or []) if e.get(\"text\")]\n", + " more = [a.get(\"seeMoreWebUrl\") for a in (src.get(\"attributions\") or []) if a.get(\"seeMoreWebUrl\")]\n", + " out = (\" \".join(texts) or src.get(\"content\") or src.get(\"text\") or \"\")[:200]\n", + " if more:\n", + " out += f\" (see more: {more[0]})\"\n", + " return out or _json.dumps(src)[:240]\n", + " return (src.get(\"title\") or \"\") + \" \" + (src.get(\"content\") or src.get(\"text\") or _json.dumps(src))[:200]\n", + "\n", + "\n", + "def refs_by_type(result) -> dict:\n", + " counts: dict = {}\n", + " for r in (result.references or []):\n", + " t = getattr(r, \"type\", None)\n", + " counts[t] = counts.get(t, 0) + 1\n", + " return counts\n", + "\n", + "\n", + "def report(label: str, result, *, max_answer: int = 600, max_refs: int = 3) -> None:\n", + " if result is None:\n", + " print(f\"=== {label} ===\\n[skipped] source not created this run\\n\")\n", + " return\n", + " refs = result.references or []\n", + " print(f\"=== {label} ===\")\n", + " print(f\"references: {len(refs)} by_type: {refs_by_type(result)}\")\n", + " ans = answer_text(result).strip()\n", + " if ans:\n", + " print(f\"\\nANSWER\\n------\\n{ans[:max_answer]}{'...' if len(ans) > max_answer else ''}\")\n", + " for ref in refs[:max_refs]:\n", + " snippet = describe_reference(ref).strip().replace(\"\\n\", \" \")\n", + " print(f\" [ref_id:{ref.id}] type={getattr(ref, 'type', None)!r} :: {snippet[:200]}\")\n", + " print()\n" + ] + }, + { + "cell_type": "markdown", + "id": "fab74957", + "metadata": {}, + "source": [ + "### 7a · Work IQ — a work question\n", + "\n", + "Scope the retrieve to Work IQ alone and ask about internal work content. A\n", + "non-empty `references` list (type `workIQ`) proves Work IQ grounded the answer.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6d2442f5", + "metadata": {}, + "outputs": [], + "source": [ + "work_question = (\n", + " \"Summarize what we've discussed internally about transatlantic route \"\n", + " \"planning, long-haul fleet decisions, or premium-cabin strategy.\"\n", + ")\n", + "\n", + "if KS_WORK_IQ in kb_sources:\n", + " res_work = retrieve(work_question, [KS_WORK_IQ])\n", + "else:\n", + " res_work = None\n", + " skip(\"7a Work IQ\", \"Work IQ source not created -- enable it in §3\")\n", + "\n", + "report(\"7a · Work IQ\", res_work)\n" + ] + }, + { + "cell_type": "markdown", + "id": "097018a8", + "metadata": {}, + "source": [ + "### 7b · Fabric IQ — an airline-ontology question\n", + "\n", + "Scope to Fabric IQ and ask a **narrow, aggregate** question the ontology can\n", + "answer in business terms. We pass `reranker_threshold=0.0` so on-topic ontology\n", + "rows aren't filtered out.\n", + "\n", + "> **Keep Fabric questions narrow.** A broad ontology question (\"list every\n", + "> aircraft *and* its routes\") can trip a **\"data is too large to process in a\n", + "> single request\"** response — Fabric tries to pull a whole entity table.\n", + "> Aggregate or scoped questions (\"how many aircraft *by manufacturer*?\") return\n", + "> clean `fabricAnswer` + `fabricRawData`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cbc2f1ae", + "metadata": {}, + "outputs": [], + "source": [ + "fabric_question = (\n", + " \"Using our airline ontology, how many aircraft are in the fleet, grouped by \"\n", + " \"manufacturer?\"\n", + ")\n", + "\n", + "if KS_FABRIC_IQ in kb_sources:\n", + " # reranker_threshold=0.0 keeps on-ontology rows that a higher bar would drop.\n", + " # A narrow, aggregate question avoids the \"data too large\" Fabric error you\n", + " # get when a broad query tries to pull an entire entity table at once.\n", + " res_fabric = retrieve(fabric_question, [KS_FABRIC_IQ], reranker_threshold=0.0)\n", + "else:\n", + " res_fabric = None\n", + " skip(\"7b Fabric IQ\", \"Fabric IQ source not created -- enable it in §4\")\n", + "\n", + "report(\"7b · Fabric IQ\", res_fabric)\n" + ] + }, + { + "cell_type": "markdown", + "id": "385afa20", + "metadata": {}, + "source": [ + "### 7c · Web IQ — a fresh-web question\n", + "\n", + "Scope to Web IQ and ask something only the live web can answer. References of\n", + "type `web` / `mcpServer` prove Web IQ grounded the answer. (Web IQ needs no user\n", + "token — it uses its stored `x-apikey`.)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b432927", + "metadata": {}, + "outputs": [], + "source": [ + "web_question = (\n", + " \"What's the latest public news on long-haul, transatlantic aircraft and \"\n", + " \"route announcements from major carriers?\"\n", + ")\n", + "\n", + "if KS_WEB_IQ in kb_sources:\n", + " res_web = retrieve(web_question, [KS_WEB_IQ])\n", + "else:\n", + " res_web = None\n", + " skip(\"7c Web IQ\", \"Web IQ source not created -- set WEB_IQ_MCP_API_KEY in §5 (waitlist: https://aka.ms/webiq-waitlist)\")\n", + "\n", + "report(\"7c · Web IQ\", res_web)\n" + ] + }, + { + "cell_type": "markdown", + "id": "3a47adcd", + "metadata": {}, + "source": [ + "### 7d · Cross-source — join the IQs\n", + "\n", + "Now the hero query. One question that **no single IQ can answer alone**: it pairs\n", + "our **fleet composition by manufacturer** (Fabric IQ) with the **latest public\n", + "news** on new long-haul aircraft and transatlantic routes (Web IQ), and folds in\n", + "any **internal context** (Work IQ). All sources are in scope; the planner fans\n", + "out, reranks, and synthesizes one cited answer. The activity trace shows how the\n", + "turn was decomposed.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa73e00a", + "metadata": {}, + "outputs": [], + "source": [ + "cross_question = (\n", + " \"I'm prepping a transatlantic route-planning review. Using our airline \"\n", + " \"ontology, what is our fleet composition by manufacturer? Then combine that \"\n", + " \"with the latest public news on new long-haul aircraft and transatlantic \"\n", + " \"routes — which manufacturers in the news do we already operate? Add \"\n", + " \"anything we've discussed internally.\"\n", + ")\n", + "\n", + "# reranker_threshold=0.0 lets each IQ's top row survive the shared rerank so the\n", + "# answer is grounded across sources, not dominated by one.\n", + "res_cross = retrieve(cross_question, kb_sources, reranker_threshold=0.0)\n", + "report(\"7d · Cross-source\", res_cross, max_answer=900, max_refs=6)\n", + "\n", + "# The planner activity trace: how the one turn was decomposed across the IQs.\n", + "activity = [a.as_dict() if hasattr(a, \"as_dict\") else dict(a) for a in (res_cross.activity or [])]\n", + "print(\"PLANNER ACTIVITY (truncated)\")\n", + "print(\"----------------------------\")\n", + "print(_json.dumps(activity, indent=2)[:2500])\n" + ] + }, + { + "cell_type": "markdown", + "id": "db559cc5", + "metadata": {}, + "source": [ + "## 8 · Take it further — point any agent at the knowledge layer\n", + "\n", + "You now have one **Foundry IQ Knowledge Base** federating Work IQ, Fabric IQ, and\n", + "Web IQ. Every KB also exposes a **Model Context Protocol (MCP) endpoint**, so the\n", + "same knowledge layer grounds *any* MCP-compatible agent — no re-plumbing:\n", + "\n", + "```\n", + "{SEARCH_ENDPOINT}/knowledgebases/{KB_NAME}/mcp?api-version=2026-05-01-preview\n", + "```\n", + "\n", + "The MCP server exposes a single `knowledge_base_retrieve` tool. Point any of\n", + "these consumers at it:\n", + "\n", + "- **Foundry Agent Service** — create a `RemoteTool` project connection to the KB\n", + " MCP URL (`authType=ProjectManagedIdentity`) and bind it as an `mcp` tool that\n", + " allow-lists `knowledge_base_retrieve`. See\n", + " [Build an end-to-end agentic retrieval solution](https://learn.microsoft.com/en-us/azure/search/agentic-retrieval-how-to-create-pipeline)\n", + " and [Retrieve from a knowledge base](https://learn.microsoft.com/en-us/azure/search/agentic-retrieval-how-to-retrieve?pivots=python).\n", + "- **Microsoft Agent Framework** — register the same MCP endpoint as a tool on\n", + " your agent and let the framework orchestrate retrieval-augmented turns.\n", + "- **GitHub Copilot** (VS Code + CLI) — drop the endpoint into `.vscode/mcp.json`:\n", + "\n", + "```json\n", + "{\n", + " \"servers\": {\n", + " \"foundry-iq-kb\": {\n", + " \"type\": \"http\",\n", + " \"url\": \"https://.search.windows.net/knowledgebases//mcp?api-version=2026-05-01-preview\",\n", + " \"headers\": { \"api-key\": \"${input:search_api_key}\" }\n", + " }\n", + " },\n", + " \"inputs\": [\n", + " { \"id\": \"search_api_key\", \"type\": \"promptString\", \"description\": \"Azure AI Search query or admin key\", \"password\": true }\n", + " ]\n", + "}\n", + "```\n", + "\n", + "Run **MCP: Reload Servers**, switch Copilot Chat to **Agent** mode, and it will\n", + "call `knowledge_base_retrieve` to ground answers (with `[ref_id:N]` citations) on\n", + "your federated Microsoft IQ layer. The `${input:...}` reference keeps the key out\n", + "of the committed file.\n", + "\n", + "> **One Knowledge Base, many consumers.** The same URL + header works for the\n", + "> Copilot CLI, Claude Desktop, and any other MCP host.\n" + ] + }, + { + "cell_type": "markdown", + "id": "87754c74", + "metadata": {}, + "source": [ + "## 9 · Clean up\n", + "\n", + "The KB and the three Knowledge Sources are stateful. We delete them in\n", + "dependency order (best-effort, each guarded) so a re-run starts clean. Comment\n", + "this out to keep the KB around for the §8 consumers.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ecc4614", + "metadata": {}, + "outputs": [], + "source": [ + "# 1) Delete the KB.\n", + "if created_resources.get(\"kb\"):\n", + " try:\n", + " index_client.delete_knowledge_base(created_resources[\"kb\"])\n", + " print(f\"Deleted KB {created_resources['kb']}\")\n", + " except ResourceNotFoundError:\n", + " pass\n", + " except Exception as exc:\n", + " print(f\"KB delete: {exc}\")\n", + "\n", + "# 2) Delete every KS we created (Work IQ, Fabric IQ, Web IQ).\n", + "for ks_name in created_ks:\n", + " try:\n", + " index_client.delete_knowledge_source(ks_name)\n", + " print(f\"Deleted KS {ks_name}\")\n", + " except ResourceNotFoundError:\n", + " pass\n", + " except Exception as exc:\n", + " print(f\"KS {ks_name} delete: {exc}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "e53efd92", + "metadata": {}, + "source": [ + "## 10 · Next steps\n", + "\n", + "You built a **Microsoft IQ knowledge layer** — Work IQ, Fabric IQ, and Web IQ\n", + "federated by **Foundry IQ** into one Knowledge Base, proven to answer from each\n", + "IQ and across them. From here:\n", + "\n", + "- **Tour every KS type.** The companion recipe\n", + " [Mastering Foundry IQ](mastering-foundry-iq) walks indexed, uploaded, and\n", + " federated sources end-to-end.\n", + "- **Mix in indexed sources.** Add a Search Index / Blob / OneLake KS alongside\n", + " these federated IQs for a hybrid KB.\n", + "- **Ground an agent.** Wire the KB's MCP endpoint into Foundry Agent Service,\n", + " the Microsoft Agent Framework, or GitHub Copilot (§8).\n", + "\n", + "### Reference docs\n", + "\n", + "- [Microsoft IQ](https://learn.microsoft.com/en-us/microsoft-iq/)\n", + "- [Work IQ knowledge source](https://learn.microsoft.com/en-us/azure/search/agentic-knowledge-source-how-to-work-iq?pivots=python)\n", + "- [Fabric ontology knowledge source](https://learn.microsoft.com/en-us/azure/search/agentic-knowledge-source-how-to-fabric-ontology?pivots=python)\n", + "- [Web IQ](https://aka.ms/WebIQLearn) · [waitlist](https://aka.ms/webiq-waitlist)\n", + "- [Retrieve from a knowledge base](https://learn.microsoft.com/en-us/azure/search/agentic-retrieval-how-to-retrieve?pivots=python)\n", + "- [Agentic retrieval overview](https://learn.microsoft.com/azure/search/agentic-retrieval-overview)\n", + "- [Create a Knowledge Base](https://learn.microsoft.com/azure/search/agentic-retrieval-how-to-create-knowledge-base)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/registry.yaml b/registry.yaml index a3da615..5460900 100644 --- a/registry.yaml +++ b/registry.yaml @@ -174,3 +174,18 @@ - audio-generation - multimodal - responsible-ai +- slug: microsoft-iq-in-foundry + path: notebooks/microsoft-iq-in-foundry.ipynb + title: "Microsoft IQ in Foundry" + description: "Build a Microsoft IQ knowledge layer in Foundry IQ — federate Work IQ, Fabric IQ, and Web IQ into one Knowledge Base, then query each IQ plus a cross-source question." + date: "2026-05-29" + authors: + - github: farzad528 + tags: + - iq + - retrieval + - grounding + - azure-ai-search + - mcp + - agents + - agent-service