Microsoft IQ in Foundry

Build your own knowledge layer in Foundry IQ: start with your own files (zero setup), then layer in Web IQ, Work IQ, and Fabric IQ, and query every source with one call.

Author

Farzad Sunavala's avatar
Farzad Sunavala
Principal Product Manager
@farzad528

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.

Build your own knowledge layer with Microsoft IQ

Deploy Foundry IQ — create an Azure AI Search service

No Foundry IQ yet? Click the button to create an Azure AI Search service — the resource Foundry IQ runs on — then come back and run §1.

Preview. Microsoft IQ knowledge sources in Foundry IQ are in preview. They run on the 2026-05-01-preview Search API and work in any Azure AI Search region — there is no preview-region requirement. Cells ship with outputs cleared and call live services; run them against your own resources.

Microsoft IQ is an enterprise intelligence layer that unifies your data, business knowledge, and work context into one shared context for AI — so agents make faster, better-grounded decisions. Each IQ grounds a different slice of that context:

Microsoft IQ What it grounds on
Fabric IQ Unified data, semantic models, and ontologies in Microsoft Fabric — the live state of your business.
Foundry IQ Enterprise knowledge and retrieval — the federation layer you build here.
Work IQ Work context across Microsoft 365 — mail, chats, files, and meetings.
Web IQ Fresh, real-world intelligence from the open web — web and news.

Foundry IQ is the layer that federates them: one Knowledge Base that fans a single question across every source, then plans, retrieves, reranks, and returns one cited answer. It runs on Azure AI Search agentic retrieval.

You'll build it one layer at a time — each layer makes your agent smarter:

Layer Grounds on Setup
📁 Your files (start here) Documents you upload straight into Foundry IQ None — runs out of the box
🌐 Web IQ The live web — web + news Waitlist
👥 Work IQ Microsoft 365 mail, chats, files, meetings Gated + licensed
📊 Fabric IQ Your business as an ontology in Microsoft Fabric An ontology or data agent

Start with the file hero — it works with zero setup. Then add the optional layers you have access to; each one re-queries the Knowledge Base so you can see the new intelligence. Every optional layer skips cleanly if you're not set up for it, so the notebook always runs top to bottom.

For a full, deployable reference that combines Work IQ, Foundry IQ, and Fabric IQ end to end, see the Microsoft IQ Solution Accelerator.

1 · Setup

You need an Azure AI Search service and a Microsoft Foundry project with two deployments:

  • a chat model (e.g. gpt-4.1) — for query planning and answer synthesis, and
  • an embedding model (e.g. text-embedding-3-large) — for vectorizing your file content.

Set these in your shell, or drop them in a local .env next to this notebook — secrets are read from the environment, never written into the notebook:

SEARCH_ENDPOINT=https://<your-search-service>.search.windows.net
SEARCH_API_KEY=<your-search-admin-key>            # omit to go keyless (see below)
AOAI_ENDPOINT=https://<your-foundry-resource>.openai.azure.com
AOAI_API_KEY=<your-azure-openai-key>              # omit to go keyless (see below)
AOAI_GPT_DEPLOYMENT=gpt-4.1                        # chat — query planning + synthesis
AOAI_EMBEDDING_DEPLOYMENT=text-embedding-3-large  # embeddings — vectorize file content

# Optional — each unlocks one extra layer (leave blank to skip):
WEB_IQ_MCP_API_KEY=<your-web-iq-mcp-key>          # Web IQ
FABRIC_WORKSPACE_ID=<your-fabric-workspace-id>    # Fabric IQ
FABRIC_ONTOLOGY_ID=<your-fabric-ontology-id>      # Fabric IQ

Prefer keyless? Assign your identity Search Service Contributor on the search service and Cognitive Services User on the Foundry resource, then leave SEARCH_API_KEY and AOAI_API_KEY blank — the next cell falls back to DefaultAzureCredential. (Web IQ has no managed identity, so its MCP source always authenticates with the x-apikey key.)

%%capture
# Foundry IQ rides on the public-preview azure-search-documents SDK (ships on PyPI).
import importlib.metadata as _md

try:
    _ok = _md.version("azure-search-documents").startswith("12.1.0b")
except _md.PackageNotFoundError:
    _ok = False

if not _ok:
    %pip install --quiet --pre "azure-search-documents==12.1.0b1" \
        "azure-identity>=1.17.1" "openai>=1.40.0" "python-dotenv>=1.0.1" "tenacity>=8.2.0"

The next cell is the whole engine: it reads your config, then defines two helpers you'll use for the rest of the notebook — build_kb(sources) (create/update the Foundry IQ Knowledge Base over a set of sources) and ask(question) (send one natural-language question and print the cited answer plus which sources grounded it). Each layer below just creates a Knowledge Source, appends it, and calls these two.

import os
from pathlib import Path
from typing import Optional

from azure.core.credentials import AzureKeyCredential
from azure.core.exceptions import ResourceNotFoundError
from azure.identity import DefaultAzureCredential
from azure.search.documents import __version__ as sdk_version
from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents.indexes.models import (
    AzureOpenAIVectorizerParameters,
    KnowledgeBase,
    KnowledgeBaseAzureOpenAIModel,
    KnowledgeSourceReference,
)
from azure.search.documents.knowledgebases import KnowledgeBaseRetrievalClient
from azure.search.documents.knowledgebases.models import (
    FabricOntologyKnowledgeSourceParams,
    FileKnowledgeSourceParams,
    KnowledgeBaseMessage,
    KnowledgeBaseMessageTextContent,
    KnowledgeBaseRetrievalRequest,
    KnowledgeRetrievalLowReasoningEffort,
    KnowledgeRetrievalOutputMode,
    KnowledgeSourceAzureOpenAIVectorizer,
    KnowledgeSourceIngestionParameters,
    McpServerKnowledgeSourceParams,
    WorkIQKnowledgeSourceParams,
)
from dotenv import load_dotenv
from tenacity import retry, retry_if_exception, stop_after_attempt, wait_exponential

load_dotenv(override=True)


def env(name: str, *, required: bool = True, default: Optional[str] = None) -&gt; Optional[str]:
    value = os.getenv(name, default)
    if required and not value:
        raise RuntimeError(f"Missing required env var: {name}")
    return value or None


def skip(layer: str, reason: str) -&gt; None:
    print(f"[skipped] {layer}: {reason}")


# ---- Config --------------------------------------------------------------
SEARCH_ENDPOINT = env("SEARCH_ENDPOINT")
SEARCH_API_KEY = env("SEARCH_API_KEY", required=False)  # blank -> managed identity
AOAI_ENDPOINT = env("AOAI_ENDPOINT").split("/openai/", 1)[0].rstrip("/")
AOAI_API_KEY = env("AOAI_API_KEY", required=False)      # blank -> managed identity
GPT_DEPLOYMENT = env("AOAI_GPT_DEPLOYMENT", required=False, default="gpt-4.1")
EMBEDDING_DEPLOYMENT = env("AOAI_EMBEDDING_DEPLOYMENT", required=False, default="text-embedding-3-large")

KS_FILES = "miq-ks-files"
KS_WEB_IQ = "miq-ks-web-iq"
KS_WORK_IQ = "miq-ks-work-iq"
KS_FABRIC_IQ = "miq-ks-fabric-iq"
KB_NAME = "miq-foundry-iq-kb"

# Key if provided, else keyless via Microsoft Entra (DefaultAzureCredential).
credential = AzureKeyCredential(SEARCH_API_KEY) if SEARCH_API_KEY else DefaultAzureCredential()
index_client = SearchIndexClient(endpoint=SEARCH_ENDPOINT, credential=credential)
kb_client = KnowledgeBaseRetrievalClient(
    endpoint=SEARCH_ENDPOINT, credential=credential, knowledge_base_name=KB_NAME,
)

kb_sources: list[str] = []  # grows as you add layers; drives the KB and cleanup


def _aoai(deployment: str) -&gt; AzureOpenAIVectorizerParameters:
    # api_key=None -> the search service uses its managed identity for Azure OpenAI.
    return AzureOpenAIVectorizerParameters(
        resource_url=AOAI_ENDPOINT, deployment_name=deployment,
        api_key=AOAI_API_KEY, model_name=deployment,
    )


def file_ingestion_parameters() -&gt; KnowledgeSourceIngestionParameters:
    """Minimal extraction + Foundry embedding for the uploaded-files layer."""
    return KnowledgeSourceIngestionParameters(
        content_extraction_mode="minimal",
        embedding_model=KnowledgeSourceAzureOpenAIVectorizer(azure_open_ai_parameters=_aoai(EMBEDDING_DEPLOYMENT)),
    )


# Work IQ + Fabric IQ enforce per-user permissions: pass the signed-in user's
# token on retrieve. Best-effort -- Web IQ + files work without it.
try:
    USER_TOKEN = DefaultAzureCredential().get_token("https://search.azure.com/.default").token
except Exception as exc:  # noqa: BLE001
    USER_TOKEN = None
    print(f"(no user token: {exc}; Work IQ + Fabric IQ will return no references)")


def build_kb(sources: list[str]) -&gt; None:
    """Create/update the one Foundry IQ Knowledge Base over `sources`."""
    kb = KnowledgeBase(
        name=KB_NAME,
        description="Foundry IQ Knowledge Base layering your files, Web IQ, Work IQ, and Fabric IQ.",
        models=[KnowledgeBaseAzureOpenAIModel(azure_open_ai_parameters=_aoai(GPT_DEPLOYMENT))],
        knowledge_sources=[KnowledgeSourceReference(name=n) for n in sources],
        retrieval_reasoning_effort=KnowledgeRetrievalLowReasoningEffort(),
        output_mode=KnowledgeRetrievalOutputMode.ANSWER_SYNTHESIS,
        retrieval_instructions=(
            "Route each subquery to the source most likely to answer it: use the files "
            "source for uploaded company documents; Web IQ for current events and public "
            "information; Work IQ for internal people and collaboration context; Fabric IQ "
            "for live business facts from the ontology. Use several when a question spans more than one."
        ),
        answer_instructions="Answer only from the retrieved content and keep the [ref_id:N] citations.",
    )
    index_client.create_or_update_knowledge_base(kb)
    print(f"Knowledge Base '{KB_NAME}' now covers {len(sources)} source(s): {', '.join(sources)}")


def _ks_params(name: str):
    common = dict(knowledge_source_name=name, include_references=True, include_reference_source_data=True)
    if name == KS_FILES:
        return FileKnowledgeSourceParams(**common)
    if name == KS_WEB_IQ:
        return McpServerKnowledgeSourceParams(knowledge_source_name=name, include_references=True)
    if name == KS_WORK_IQ:
        return WorkIQKnowledgeSourceParams(**common)
    if name == KS_FABRIC_IQ:
        return FabricOntologyKnowledgeSourceParams(reranker_threshold=0.0, **common)
    return None


# A KB update takes a few seconds to reach the retrieve path, so a query fired
# immediately after build_kb() can transiently 400 with "must match a Knowledge
# Base Knowledge Source name". tenacity retries just that case while it propagates.
@retry(
    retry=retry_if_exception(lambda e: "must match" in str(e)),
    wait=wait_exponential(multiplier=1, min=3, max=20),
    stop=stop_after_attempt(5),
    reraise=True,
)
def _retrieve(request):
    return kb_client.retrieve(request, query_source_authorization=USER_TOKEN)


def ask(question: str, *, max_runtime_seconds: int = 180) -&gt; None:
    """Send one question to the KB; print the cited answer and grounding sources."""
    request = KnowledgeBaseRetrievalRequest(
        messages=[KnowledgeBaseMessage(role="user", content=[KnowledgeBaseMessageTextContent(text=question)])],
        knowledge_source_params=[p for p in (_ks_params(n) for n in kb_sources) if p],
        include_activity=True,
        max_runtime_in_seconds=max_runtime_seconds,
    )
    result = _retrieve(request)
    answer = "\n\n".join(
        c.text for m in (result.response or []) for c in (m.content or []) if getattr(c, "text", None)
    ).strip()
    grounded: dict = {}
    for r in (result.references or []):
        t = getattr(r, "type", None)
        grounded[t] = grounded.get(t, 0) + 1
    print(f"Q: {question}\n\n{answer or '(no answer)'}\n\ngrounded by: {grounded}")


def enable(ks_name: str, question: str, *, max_runtime_seconds: int = 180) -&gt; None:
    """Add a just-created Knowledge Source to the KB, re-query, and (if the query
    fails) roll the source back out so later layers stay clean."""
    kb_sources.append(ks_name)
    try:
        build_kb(kb_sources)
        ask(question, max_runtime_seconds=max_runtime_seconds)
    except Exception as exc:  # noqa: BLE001
        kb_sources.remove(ks_name)
        if kb_sources:
            build_kb(kb_sources)
        skip(ks_name, f"created the source but the query failed: {str(exc)[:140]}")


auth_mode = "api key" if SEARCH_API_KEY else "managed identity (keyless)"
print(f"azure-search-documents {sdk_version}  |  search: {SEARCH_ENDPOINT}  |  auth: {auth_mode}")
print(f"chat: {GPT_DEPLOYMENT}  |  embedding: {EMBEDDING_DEPLOYMENT}")

2 · Start with your files 📁

The fastest knowledge layer: upload files straight into Foundry IQ — no storage account, no indexer, no data source. Foundry IQ extracts and embeds each file for you (kind="file"). To run with zero setup, the cell below writes a tiny built-in company brief and uploads that; point ZAVA_FILE at your own PDF / DOCX / HTML / TXT to use real documents.

Create the Knowledge Source, upload, build_kb(), and ask() — your agent can already answer from your files.

Learn more: Knowledge sources in Foundry IQ

from azure.search.documents.indexes.models import FileKnowledgeSource, FileKnowledgeSourceParameters

# Built-in sample so the hero runs with zero setup. Replace with your own files
# by setting ZAVA_FILE to a local path.
sample = Path("data/microsoft-iq-in-foundry/zava-brief.md")
sample.parent.mkdir(parents=True, exist_ok=True)
sample.write_text(
    "# Zava Air \u2014 Company Brief\n\n"
    "Zava Air is a mid-size carrier specializing in transatlantic routes between "
    "North America and Europe. Our strategy is to standardize on fuel-efficient "
    "widebody aircraft and grow premium-cabin capacity on long-haul corridors.\n\n"
    "## Fleet strategy\n"
    "- Consolidate around two widebody families to simplify maintenance and crew training.\n"
    "- Prioritize range and fuel efficiency for transatlantic missions.\n"
    "- Retire older narrowbodies as widebody deliveries arrive.\n\n"
    "## Network\n"
    "Primary hubs anchor the transatlantic network; we add seasonal frequency on the "
    "highest-demand premium corridors.\n",
    encoding="utf-8",
)
file_path = Path(env("ZAVA_FILE", required=False, default=str(sample))).expanduser()

# 1) Create the File Knowledge Source.
index_client.create_or_update_knowledge_source(
    FileKnowledgeSource(
        name=KS_FILES,
        description="Your own files, uploaded straight into Foundry IQ -- no storage account.",
        file_parameters=FileKnowledgeSourceParameters(ingestion_parameters=file_ingestion_parameters()),
    )
)


# 2) Upload the file (pass filename so Foundry can label the blob). The upload
#    triggers a synchronous embed pass, which can transiently 429 on shared
#    embedding deployments -- tenacity retries that with exponential backoff.
@retry(
    retry=retry_if_exception(lambda e: "429" in str(e)),
    wait=wait_exponential(multiplier=1, min=2, max=30),
    stop=stop_after_attempt(5),
    reraise=True,
)
def upload_file():
    with file_path.open("rb") as fh:
        return index_client.upload_knowledge_source_file(KS_FILES, fh.read(), filename=file_path.name)


uploaded = upload_file()
print(f"Uploaded {file_path.name} ({uploaded.file_size_bytes:,} bytes)")

# 3) Build the KB over just your files, and ask.
kb_sources.append(KS_FILES)
build_kb(kb_sources)
ask("What does Zava Air do, and what is its fleet strategy?")

3 · Add Web IQ 🌐

Web IQ grounds your agent in the live web — web and news — through the remote Microsoft Grounding MCP server, with nothing to index. It authenticates with a stored x-apikey header.

Get access: Web IQ is waitlistedjoin the waitlist. Once approved, set WEB_IQ_MCP_API_KEY and re-run. No key yet? This layer skips and the notebook keeps going.

Learn more: Web IQ

from azure.search.documents.indexes.models import (
    McpServerKnowledgeSource,
    McpServerKnowledgeSourceParameters,
    McpServerStoredHeadersAuthentication,
    McpServerStoredHeadersParameters,
)

web_key = env("WEB_IQ_MCP_API_KEY", required=False)  # secret -- env only
if not web_key:
    skip("Web IQ", "set WEB_IQ_MCP_API_KEY to add live web grounding -- waitlist: https://aka.ms/webiq-waitlist")
else:
    tools = [
        {"name": t, "outputParsing": {"kind": "auto"}, "inclusionMode": "reranked", "maxOutputTokens": 4096}
        for t in ("web", "news")
    ]
    index_client.create_or_update_knowledge_source(
        McpServerKnowledgeSource(
            name=KS_WEB_IQ,
            description="Web IQ -- Microsoft Grounding MCP server (web, news).",
            mcp_server_parameters=McpServerKnowledgeSourceParameters(
                server_url="https://api.microsoft.ai/v3/mcp",
                # storedHeaders auth: send the Web IQ x-apikey directly.
                authentication=McpServerStoredHeadersAuthentication(
                    stored_headers_parameters=McpServerStoredHeadersParameters(headers={"x-apikey": web_key}),
                ),
                tools=tools,
            ),
        )
    )
    enable(KS_WEB_IQ, "What's the latest public news on transatlantic routes and long-haul aircraft?")

4 · Add Work IQ 👥

Work IQ grounds your agent in how your people work — Microsoft 365 mail, chats, files, and meetings — enforcing each user's permissions at query time. The typed model is tiny: just a name and description.

Get access: Work IQ is gated + licensed. Register the EnableFoundryIQWithWorkIQ flag, re-register Microsoft.Search, have a tenant admin complete the access request form, and give each user a Microsoft 365 Copilot license. Not entitled yet? This layer skips. (Work IQ can take 40–60s to answer.)

Learn more: Work IQ knowledge source

from azure.search.documents.indexes.models import WorkIQKnowledgeSource

try:
    index_client.create_or_update_knowledge_source(
        WorkIQKnowledgeSource(name=KS_WORK_IQ, description="Work IQ -- M365 mail, chats, files, meetings.")
    )
except Exception as exc:  # noqa: BLE001
    skip("Work IQ", f"tenant not entitled yet ({exc}) -- request access: https://aka.ms/foundry-iq-work-iq-admin-consent-form")
else:
    enable(KS_WORK_IQ, "Summarize what we've discussed internally about transatlantic route planning.")

5 · Add Fabric IQ 📊

Fabric IQ grounds your agent in the live state of your business in Microsoft Fabric. You can bring either a Fabric ontology (entities, relationships, rules) or a Fabric data agent as a knowledge source. This recipe uses an ontology as the example; point it at your own.

Get access: you need a Fabric ontology item (or data agent) in the same Entra tenant as your Search service. Both IDs are in the ontology item URL — .../groups/<workspace-id>/ontologies/<ontology-id> — set them as FABRIC_WORKSPACE_ID / FABRIC_ONTOLOGY_ID. Not set? This layer skips.

Learn more: Fabric ontology knowledge source · Fabric data agent knowledge source

from azure.search.documents.indexes.models import (
    FabricOntologyKnowledgeSource,
    FabricOntologyKnowledgeSourceParameters,
)

workspace_id = env("FABRIC_WORKSPACE_ID", required=False)
ontology_id = env("FABRIC_ONTOLOGY_ID", required=False)
if not (workspace_id and ontology_id):
    skip("Fabric IQ", "set FABRIC_WORKSPACE_ID and FABRIC_ONTOLOGY_ID (from your ontology item URL) to add live business data")
else:
    index_client.create_or_update_knowledge_source(
        FabricOntologyKnowledgeSource(
            name=KS_FABRIC_IQ,
            description="Fabric IQ -- your business ontology in Microsoft Fabric.",
            fabric_ontology_parameters=FabricOntologyKnowledgeSourceParameters(
                workspace_id=workspace_id, ontology_id=ontology_id,
            ),
        )
    )
    enable(KS_FABRIC_IQ, "From our ontology, how many aircraft are in the fleet, grouped by manufacturer?")

6 · Ask across every layer

Now the payoff. Send one question that no single layer can answer alone — Foundry IQ plans the subqueries, routes each to the right Microsoft IQ (steered by the retrieval_instructions from §1), reranks, and synthesizes one cited answer across whatever layers you enabled.

ask(
    "I'm prepping a transatlantic route-planning review. What is our fleet strategy "
    "and composition (from our files and our ontology), the latest public news on "
    "long-haul aircraft, and anything we've discussed internally?"
)

7 · Take it further — ground a Foundry agent

Your Knowledge Base also exposes a Model Context Protocol (MCP) endpoint, so the same Microsoft IQ layer grounds any MCP-compatible agent — no re-plumbing:

{SEARCH_ENDPOINT}/knowledgebases/{KB_NAME}/mcp?api-version=2026-05-01-preview

It exposes one knowledge_base_retrieve tool. The most direct next step is to connect this Knowledge Base — with all your IQs — to a Foundry agent:

  • Foundry Agent Service — connect the Knowledge Base to an agent in a few clicks or with the SDK. See Connect Foundry IQ to an agent.
  • Microsoft Agent Framework — register the MCP endpoint as a tool on your agent.
  • GitHub Copilot (VS Code) — add the URL to .vscode/mcp.json with an api-key header, then use Agent mode.

8 · Clean up

The Knowledge Base and Knowledge Sources are stateful. Delete them so a re-run starts clean — comment this out to keep the KB for the §7 consumers.

try:
    index_client.delete_knowledge_base(KB_NAME)
    print(f"Deleted KB {KB_NAME}")
except ResourceNotFoundError:
    pass
except Exception as exc:  # noqa: BLE001
    print(f"KB delete: {exc}")

for ks_name in (KS_FILES, KS_WEB_IQ, KS_WORK_IQ, KS_FABRIC_IQ):
    try:
        index_client.delete_knowledge_source(ks_name)
        print(f"Deleted KS {ks_name}")
    except ResourceNotFoundError:
        pass
    except Exception as exc:  # noqa: BLE001
        print(f"KS {ks_name} delete: {exc}")

9 · Next steps

You built a Microsoft IQ knowledge layer — starting from your own files, then layering in Web IQ, Work IQ, and Fabric IQ, all federated by Foundry IQ into one Knowledge Base you query with a single ask(). From here:

Reference docs

Tags

iq retrieval grounding azure-ai-search mcp agents agent-service