{
  "nbformat": 4,
  "nbformat_minor": 5,
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.13.0"
    },
    "blog_metadata": {
      "topic": "Fabric Data Agents Choosing Query Language by Context: The Governance Implications",
      "slug": "fabric-data-agents-choosing-query-language-by-context-the-go",
      "generated_by": "LinkedIn Post Generator + Azure OpenAI",
      "generated_at": "2026-07-10T12:05:43.398Z"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Fabric Data Agents Choosing Query Language by Context: The Governance Implications\n",
        "\n",
        "This notebook turns the blog post into a hands-on validation flow. It demonstrates how query-language selection in Fabric-style data agents becomes a governance control problem involving policy gates, auditability, semantic boundaries, and workspace outbound posture.\n",
        "\n",
        "The examples below simulate deterministic controls for tool selection, policy evaluation, lineage capture, and workspace compliance checks using Python."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "%pip install pandas"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "from dataclasses import dataclass, asdict\n",
        "from datetime import datetime\n",
        "import json\n",
        "import pandas as pd"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Governance flow as an execution control plane\n",
        "\n",
        "The original post uses a Mermaid diagram to show that the prompt is not the execution plan. This Python cell converts that idea into a structured graph representation so you can inspect the control points programmatically."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "flow_edges = [\n",
        "    (\"User prompt\", \"Agent policy + intent classification\"),\n",
        "    (\"Agent policy + intent classification\", \"Choose query language by context\"),\n",
        "    (\"Choose query language by context\", \"Warehouse / Lakehouse SQL endpoint\", \"SQL\"),\n",
        "    (\"Choose query language by context\", \"Semantic model\", \"DAX\"),\n",
        "    (\"Choose query language by context\", \"Event / telemetry store\", \"KQL\"),\n",
        "    (\"Choose query language by context\", \"Notebook / custom function\", \"Python tool\"),\n",
        "    (\"Warehouse / Lakehouse SQL endpoint\", \"Result + source lineage\"),\n",
        "    (\"Semantic model\", \"Result + source lineage\"),\n",
        "    (\"Event / telemetry store\", \"Result + source lineage\"),\n",
        "    (\"Notebook / custom function\", \"Result + source lineage\"),\n",
        "    (\"Result + source lineage\", \"Audit log: prompt, tool, path, objects\"),\n",
        "    (\"Agent policy + intent classification\", \"Governance checks\"),\n",
        "    (\"Governance checks\", \"Allowed outbound access?\"),\n",
        "    (\"Governance checks\", \"Approved connection rules?\")\n",
        "]\n",
        "\n",
        "flow_df = pd.DataFrame(flow_edges, columns=[\"from\", \"to\", \"label\"][: max(len(x) for x in flow_edges)])\n",
        "print(flow_df.fillna(\"\"))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Pseudo-audit record for a governed agent interaction\n",
        "\n",
        "This example captures the minimum evidence chain described in the post: prompt, selected tool, execution path, source objects, policy decision, and timestamp. The key validation point is that governance requires more than a transcript."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "from dataclasses import dataclass, asdict\n",
        "from datetime import datetime\n",
        "import json\n",
        "\n",
        "@dataclass\n",
        "class AgentAuditRecord:\n",
        "    prompt: str\n",
        "    selected_tool: str\n",
        "    execution_path: list[str]\n",
        "    source_objects: list[str]\n",
        "    policy_decision: str\n",
        "    timestamp_utc: str\n",
        "\n",
        "record = AgentAuditRecord(\n",
        "    prompt=\"Show gross margin by region for the last quarter.\",\n",
        "    selected_tool=\"DAX\",\n",
        "    execution_path=[\"intent:analytics\", \"policy:semantic-model-preferred\", \"tool:dax\"],\n",
        "    source_objects=[\"SemanticModel/Sales\", \"Table/Date\", \"Measure/Gross Margin\"],\n",
        "    policy_decision=\"allowed\",\n",
        "    timestamp_utc=datetime.utcnow().isoformat() + \"Z\",\n",
        ")\n",
        "\n",
        "print(json.dumps(asdict(record), indent=2))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Context-based query language selection with governance gates\n",
        "\n",
        "This example operationalizes the blog's core argument: policy should sit between prompt interpretation and tool selection. The function below chooses a language based on context, source hints, and outbound posture."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "def choose_language(prompt: str, sources: list[str], outbound_allowed: bool) -> str:\n",
        "    text = prompt.lower()\n",
        "    if \"telemetry\" in text or any(\"eventhouse\" in s.lower() for s in sources):\n",
        "        return \"KQL\"\n",
        "    if \"measure\" in text or any(\"semanticmodel\" in s.lower() for s in sources):\n",
        "        return \"DAX\"\n",
        "    if \"join\" in text or any(\"warehouse\" in s.lower() or \"lakehouse\" in s.lower() for s in sources):\n",
        "        return \"SQL\"\n",
        "    if outbound_allowed and \"python\" in text:\n",
        "        return \"Python\"\n",
        "    return \"SQL\"\n",
        "\n",
        "prompt = \"Use the semantic model measure for revenue by month.\"\n",
        "sources = [\"Workspace/Finance\", \"SemanticModel/Revenue\"]\n",
        "print({\"prompt\": prompt, \"selected_language\": choose_language(prompt, sources, outbound_allowed=False)})\n",
        "\n",
        "scenarios = [\n",
        "    {\n",
        "        \"prompt\": \"Show telemetry spikes for the last 24 hours\",\n",
        "        \"sources\": [\"Workspace/Ops\", \"Eventhouse/Telemetry\"],\n",
        "        \"outbound_allowed\": False,\n",
        "    },\n",
        "    {\n",
        "        \"prompt\": \"Use the semantic model measure for gross margin by region\",\n",
        "        \"sources\": [\"Workspace/Finance\", \"SemanticModel/Sales\"],\n",
        "        \"outbound_allowed\": False,\n",
        "    },\n",
        "    {\n",
        "        \"prompt\": \"Join warehouse sales and returns tables for last quarter\",\n",
        "        \"sources\": [\"Warehouse/SalesDW\", \"Lakehouse/Returns\"],\n",
        "        \"outbound_allowed\": False,\n",
        "    },\n",
        "    {\n",
        "        \"prompt\": \"Use python to enrich results with an external lookup\",\n",
        "        \"sources\": [\"Workspace/Sales\", \"Lakehouse/Leads\"],\n",
        "        \"outbound_allowed\": True,\n",
        "    },\n",
        "]\n",
        "\n",
        "results = []\n",
        "for s in scenarios:\n",
        "    results.append({\n",
        "        \"prompt\": s[\"prompt\"],\n",
        "        \"sources\": \", \".join(s[\"sources\"]),\n",
        "        \"outbound_allowed\": s[\"outbound_allowed\"],\n",
        "        \"selected_language\": choose_language(s[\"prompt\"], s[\"sources\"], s[\"outbound_allowed\"]),\n",
        "    })\n",
        "\n",
        "print(pd.DataFrame(results))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Sequence of governed execution\n",
        "\n",
        "The post also includes a sequence diagram showing how a user prompt flows through policy, tool execution, and audit logging. This cell models that sequence as ordered steps for validation and documentation."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "sequence_steps = [\n",
        "    {\"step\": 1, \"actor\": \"User\", \"action\": \"Natural-language prompt\"},\n",
        "    {\"step\": 2, \"actor\": \"Fabric Data Agent\", \"action\": \"Evaluate request\"},\n",
        "    {\"step\": 3, \"actor\": \"Governance Policy\", \"action\": \"Evaluate context, source, outbound posture\"},\n",
        "    {\"step\": 4, \"actor\": \"Fabric Data Agent\", \"action\": \"Receive allowed tools + constraints\"},\n",
        "    {\"step\": 5, \"actor\": \"Query Tool\", \"action\": \"Execute SQL/DAX/KQL based on context\"},\n",
        "    {\"step\": 6, \"actor\": \"Query Tool\", \"action\": \"Return result + referenced objects\"},\n",
        "    {\"step\": 7, \"actor\": \"Audit Log\", \"action\": \"Log prompt, tool, path, source objects\"},\n",
        "    {\"step\": 8, \"actor\": \"Fabric Data Agent\", \"action\": \"Return governed result to user\"},\n",
        "]\n",
        "\n",
        "print(pd.DataFrame(sequence_steps))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Minimal policy evaluator for approved tools and source sensitivity\n",
        "\n",
        "This example shows how deterministic controls can deny or allow execution before the agent runs a tool. It validates the idea that tool approval and source sensitivity are policy inputs, not after-the-fact debates."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "def evaluate_policy(selected_tool: str, source_objects: list[str]) -> dict:\n",
        "    sensitive = any(\"HR\" in obj or \"PII\" in obj for obj in source_objects)\n",
        "    approved_tools = {\"SQL\", \"DAX\", \"KQL\"}\n",
        "    if selected_tool not in approved_tools:\n",
        "        return {\"decision\": \"deny\", \"reason\": \"tool_not_approved\"}\n",
        "    if sensitive and selected_tool == \"Python\":\n",
        "        return {\"decision\": \"deny\", \"reason\": \"sensitive_data_python_blocked\"}\n",
        "    return {\"decision\": \"allow\", \"reason\": \"policy_pass\"}\n",
        "\n",
        "result = evaluate_policy(\n",
        "    selected_tool=\"DAX\",\n",
        "    source_objects=[\"SemanticModel/Finance\", \"Table/Region\", \"Measure/Net Sales\"],\n",
        ")\n",
        "print(result)\n",
        "\n",
        "test_cases = [\n",
        "    (\"DAX\", [\"SemanticModel/Finance\", \"Measure/Gross Margin\"]),\n",
        "    (\"SQL\", [\"Warehouse/SalesDW\", \"Table/FactSales\"]),\n",
        "    (\"Python\", [\"Lakehouse/HR\", \"Table/PII_Employees\"]),\n",
        "    (\"Python\", [\"Lakehouse/Sales\", \"Table/Orders\"]),\n",
        "    (\"KQL\", [\"Eventhouse/Telemetry\", \"Table/Incidents\"]),\n",
        "]\n",
        "\n",
        "policy_results = []\n",
        "for tool, objects in test_cases:\n",
        "    decision = evaluate_policy(tool, objects)\n",
        "    policy_results.append({\n",
        "        \"selected_tool\": tool,\n",
        "        \"source_objects\": \", \".join(objects),\n",
        "        \"decision\": decision[\"decision\"],\n",
        "        \"reason\": decision[\"reason\"],\n",
        "    })\n",
        "\n",
        "print(pd.DataFrame(policy_results))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Workspace outbound posture check in Python\n",
        "\n",
        "The blog includes a PowerShell-style posture check for agent-enabled workspaces. Since this notebook uses Python, the same logic is implemented below to validate which workspaces are compliant based on outbound access and approved connection rules."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "workspace_names = [\"Finance-Agents\", \"Sales-Agents\"]\n",
        "\n",
        "workspace_posture = [\n",
        "    {\"Name\": \"Finance-Agents\", \"AgentEnabled\": True, \"OutboundAccess\": \"Restricted\", \"ApprovedConnectionRules\": True},\n",
        "    {\"Name\": \"Sales-Agents\", \"AgentEnabled\": True, \"OutboundAccess\": \"Open\", \"ApprovedConnectionRules\": False},\n",
        "]\n",
        "\n",
        "posture_df = pd.DataFrame(workspace_posture)\n",
        "filtered = posture_df[(posture_df[\"Name\"].isin(workspace_names)) & (posture_df[\"AgentEnabled\"])]\n",
        "filtered = filtered.copy()\n",
        "filtered[\"Compliant\"] = (filtered[\"OutboundAccess\"] == \"Restricted\") & (filtered[\"ApprovedConnectionRules\"])\n",
        "print(filtered[[\"Name\", \"OutboundAccess\", \"ApprovedConnectionRules\", \"Compliant\"]])"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Governance inventory export in Python\n",
        "\n",
        "The original post also shows a PowerShell example that exports a governance inventory for agent-enabled workspaces. This Python version creates the same inventory, filters to agent-enabled workspaces, writes a CSV, and confirms the output path."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "inventory = [\n",
        "    {\"Workspace\": \"Finance-Agents\", \"Capacity\": \"F64\", \"AgentEnabled\": True, \"OutboundAccess\": \"Restricted\", \"RuleSet\": \"Approved-Default\"},\n",
        "    {\"Workspace\": \"Ops-Analytics\", \"Capacity\": \"F32\", \"AgentEnabled\": False, \"OutboundAccess\": \"Restricted\", \"RuleSet\": \"Approved-Default\"},\n",
        "    {\"Workspace\": \"Sales-Agents\", \"Capacity\": \"F64\", \"AgentEnabled\": True, \"OutboundAccess\": \"Open\", \"RuleSet\": \"Legacy-Exceptions\"},\n",
        "]\n",
        "\n",
        "inventory_df = pd.DataFrame(inventory)\n",
        "agent_inventory_df = inventory_df[inventory_df[\"AgentEnabled\"]].copy()\n",
        "path = \"fabric-agent-governance.csv\"\n",
        "agent_inventory_df.to_csv(path, index=False, encoding=\"utf-8\")\n",
        "print(f\"Exported governance inventory to {path}\")\n",
        "print(agent_inventory_df)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Workspace trust boundary and tool control flow\n",
        "\n",
        "Another Mermaid diagram in the post emphasizes that workspace outbound posture is part of the trust boundary. This cell represents that control flow as a simple edge list you can inspect or extend."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "workspace_flow = [\n",
        "    (\"Agent-enabled workspace\", \"Outbound access posture\"),\n",
        "    (\"Outbound access posture\", \"Approved connection rules only\", \"Restricted\"),\n",
        "    (\"Outbound access posture\", \"Governance exception review\", \"Open\"),\n",
        "    (\"Approved connection rules only\", \"Tool selected by context\"),\n",
        "    (\"Governance exception review\", \"Block or require approval\"),\n",
        "    (\"Tool selected by context\", \"Execute and log lineage\", \"SQL/DAX/KQL approved\"),\n",
        "    (\"Tool selected by context\", \"Check extra controls\", \"Python/custom tool\"),\n",
        "    (\"Check extra controls\", \"Allow only with explicit policy\"),\n",
        "]\n",
        "\n",
        "workspace_flow_df = pd.DataFrame(workspace_flow, columns=[\"from\", \"to\", \"label\"][: max(len(x) for x in workspace_flow)])\n",
        "print(workspace_flow_df.fillna(\"\"))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Compact lineage summary for response-time governance evidence\n",
        "\n",
        "This example creates a concise lineage summary from the selected tool and referenced source objects. It supports the blog's point that governance evidence should include execution path details, not just the user's prompt."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "def summarize_lineage(selected_tool: str, source_objects: list[str]) -> str:\n",
        "    unique_objects = sorted(set(source_objects))\n",
        "    return f\"tool={selected_tool}; objects={len(unique_objects)}; refs={', '.join(unique_objects)}\"\n",
        "\n",
        "summary = summarize_lineage(\n",
        "    selected_tool=\"SQL\",\n",
        "    source_objects=[\n",
        "        \"Warehouse/SalesDW\",\n",
        "        \"Table/FactSales\",\n",
        "        \"Table/DimDate\",\n",
        "        \"Table/FactSales\",\n",
        "    ],\n",
        ")\n",
        "print(summary)\n",
        "\n",
        "examples = [\n",
        "    (\"DAX\", [\"SemanticModel/Sales\", \"Measure/Gross Margin\", \"Table/Date\"]),\n",
        "    (\"KQL\", [\"Eventhouse/Telemetry\", \"Table/Incidents\", \"Table/Signals\"]),\n",
        "    (\"SQL\", [\"Warehouse/SalesDW\", \"Table/FactSales\", \"Table/DimDate\", \"Table/FactSales\"]),\n",
        "]\n",
        "\n",
        "for tool, objects in examples:\n",
        "    print(summarize_lineage(tool, objects))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## End-to-end validation scenario\n",
        "\n",
        "This final hands-on example combines language selection, policy evaluation, audit record creation, and lineage summarization into one deterministic flow. It demonstrates what governed execution could look like when policy sits between prompt and path selection."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "from dataclasses import dataclass, asdict\n",
        "from datetime import datetime\n",
        "import json\n",
        "\n",
        "@dataclass\n",
        "class AgentAuditRecord:\n",
        "    prompt: str\n",
        "    selected_tool: str\n",
        "    execution_path: list[str]\n",
        "    source_objects: list[str]\n",
        "    policy_decision: str\n",
        "    timestamp_utc: str\n",
        "\n",
        "def choose_language(prompt: str, sources: list[str], outbound_allowed: bool) -> str:\n",
        "    text = prompt.lower()\n",
        "    if \"telemetry\" in text or any(\"eventhouse\" in s.lower() for s in sources):\n",
        "        return \"KQL\"\n",
        "    if \"measure\" in text or any(\"semanticmodel\" in s.lower() for s in sources):\n",
        "        return \"DAX\"\n",
        "    if \"join\" in text or any(\"warehouse\" in s.lower() or \"lakehouse\" in s.lower() for s in sources):\n",
        "        return \"SQL\"\n",
        "    if outbound_allowed and \"python\" in text:\n",
        "        return \"Python\"\n",
        "    return \"SQL\"\n",
        "\n",
        "def evaluate_policy(selected_tool: str, source_objects: list[str]) -> dict:\n",
        "    sensitive = any(\"HR\" in obj or \"PII\" in obj for obj in source_objects)\n",
        "    approved_tools = {\"SQL\", \"DAX\", \"KQL\"}\n",
        "    if selected_tool not in approved_tools:\n",
        "        return {\"decision\": \"deny\", \"reason\": \"tool_not_approved\"}\n",
        "    if sensitive and selected_tool == \"Python\":\n",
        "        return {\"decision\": \"deny\", \"reason\": \"sensitive_data_python_blocked\"}\n",
        "    return {\"decision\": \"allow\", \"reason\": \"policy_pass\"}\n",
        "\n",
        "def summarize_lineage(selected_tool: str, source_objects: list[str]) -> str:\n",
        "    unique_objects = sorted(set(source_objects))\n",
        "    return f\"tool={selected_tool}; objects={len(unique_objects)}; refs={', '.join(unique_objects)}\"\n",
        "\n",
        "prompt = \"Show gross margin by region using the semantic model measure for the last quarter.\"\n",
        "sources = [\"Workspace/Finance\", \"SemanticModel/Sales\", \"Measure/Gross Margin\"]\n",
        "outbound_allowed = False\n",
        "\n",
        "selected_tool = choose_language(prompt, sources, outbound_allowed)\n",
        "policy = evaluate_policy(selected_tool, sources)\n",
        "execution_path = [\n",
        "    \"intent:analytics\",\n",
        "    \"policy:semantic-model-preferred\",\n",
        "    f\"tool:{selected_tool.lower()}\",\n",
        "    f\"outbound_allowed:{str(outbound_allowed).lower()}\"\n",
        "]\n",
        "\n",
        "record = AgentAuditRecord(\n",
        "    prompt=prompt,\n",
        "    selected_tool=selected_tool,\n",
        "    execution_path=execution_path,\n",
        "    source_objects=sources,\n",
        "    policy_decision=policy[\"decision\"],\n",
        "    timestamp_utc=datetime.utcnow().isoformat() + \"Z\",\n",
        ")\n",
        "\n",
        "print(\"Selected tool:\", selected_tool)\n",
        "print(\"Policy result:\", policy)\n",
        "print(\"Lineage summary:\", summarize_lineage(selected_tool, sources))\n",
        "print(json.dumps(asdict(record), indent=2))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Summary\n",
        "\n",
        "This notebook validated the blog's central claim: once a Fabric-style data agent can choose among semantic models, SQL, KQL, notebooks, or external tools, query-language selection becomes a governance decision rather than a convenience feature.\n",
        "\n",
        "Key takeaways:\n",
        "- Semantic-first paths are usually the safest default for governed business Q&A.\n",
        "- SQL, KQL, and Python introduce different control, audit, and semantic risks.\n",
        "- Workspace outbound posture is part of the trust boundary.\n",
        "- Prompt-only logging is insufficient; execution path evidence is required.\n",
        "- New tools and integrations should be treated as control-plane changes.\n",
        "\n",
        "## Next Steps\n",
        "\n",
        "1. Extend the policy evaluator with use-case tiers such as executive BI, operational analytics, and telemetry.\n",
        "2. Add ontology or semantic-priority flags to enforce governed meaning.\n",
        "3. Persist audit records to a table or log sink for downstream review.\n",
        "4. Build compliance checks for agent-enabled workspaces and approved connectors.\n",
        "5. Rate your current execution-path governance maturity from 1 to 5 and identify the next control to implement."
      ]
    }
  ]
}