Back to Blog
4 min read

Power BI Smart Narratives: AI-Generated Insights

Smart narratives in Power BI automatically generate natural language summaries of your data. This AI-powered feature helps users quickly understand key insights without being data experts.

What Are Smart Narratives?

Smart narratives analyze your visuals and data to produce:

  • Key metric summaries
  • Trend descriptions
  • Outlier highlights
  • Comparative insights

Adding a Smart Narrative

In Power BI Desktop, add the Smart Narrative visual:

{
  "visualType": "smartNarrative",
  "config": {
    "dataRoles": [
      {
        "name": "Values",
        "values": ["Sales[Total Revenue]", "Sales[Quantity]"]
      },
      {
        "name": "Category",
        "values": ["Product[Category]"]
      },
      {
        "name": "Time",
        "values": ["Date[Date]"]
      }
    ],
    "settings": {
      "summaryLevel": "detailed",
      "includeComparisons": true,
      "highlightOutliers": true
    }
  }
}

Customizing Narrative Content

Use the visual’s configuration options:

{
  "smartNarrativeSettings": {
    "summary": {
      "enabled": true,
      "type": "autoSummarize"
    },
    "trends": {
      "enabled": true,
      "period": "monthly"
    },
    "outliers": {
      "enabled": true,
      "threshold": 2.0
    },
    "comparisons": {
      "enabled": true,
      "compareWith": "previousPeriod"
    },
    "formatting": {
      "numberFormat": "#,##0",
      "percentFormat": "0.0%",
      "dateFormat": "MMM yyyy"
    }
  }
}

Dynamic Text with DAX

Create custom dynamic text that complements smart narratives:

// Custom narrative measure
Sales Narrative =
VAR TotalSales = [Total Sales]
VAR PreviousMonthSales = CALCULATE([Total Sales], PREVIOUSMONTH('Date'[Date]))
VAR Growth = DIVIDE(TotalSales - PreviousMonthSales, PreviousMonthSales)
VAR TopProduct = TOPN(1, ALL(Product[Name]), [Total Sales])
VAR TopProductName = MAXX(TopProduct, Product[Name])
VAR TopProductSales = MAXX(TopProduct, [Total Sales])

RETURN
"Total sales reached " & FORMAT(TotalSales, "$#,##0") &
", which is " & FORMAT(ABS(Growth), "0.0%") &
IF(Growth >= 0, " higher ", " lower ") &
"than last month. " &
"The top performing product was " & TopProductName &
" with " & FORMAT(TopProductSales, "$#,##0") & " in sales."

// Conditional narrative
Performance Summary =
VAR CurrentSales = [Total Sales]
VAR Target = [Sales Target]
VAR Achievement = DIVIDE(CurrentSales, Target)

RETURN
SWITCH(TRUE(),
    Achievement >= 1.1, "Excellent! Sales exceeded target by " & FORMAT(Achievement - 1, "0%") & ".",
    Achievement >= 1.0, "Target achieved! Sales met expectations.",
    Achievement >= 0.9, "Close to target. Sales are at " & FORMAT(Achievement, "0%") & " of goal.",
    "Below target. Immediate action needed to reach " & FORMAT(Target, "$#,##0") & " goal."
)

Formatting Smart Narratives

{
  "formatting": {
    "general": {
      "font": {
        "family": "Segoe UI",
        "size": 12
      },
      "color": "#333333"
    },
    "emphasis": {
      "positive": {
        "color": "#107C10",
        "bold": true
      },
      "negative": {
        "color": "#D83B01",
        "bold": true
      },
      "neutral": {
        "color": "#0078D4",
        "bold": false
      }
    },
    "numbers": {
      "style": "currency",
      "decimals": 0,
      "thousandsSeparator": true
    }
  }
}

Combining with Q&A

Enable natural language queries alongside narratives:

{
  "linguisticSchema": {
    "entities": [
      {
        "name": "Sales",
        "terms": ["revenue", "income", "sales amount"],
        "measures": ["Total Sales", "Average Sale"]
      },
      {
        "name": "Products",
        "terms": ["items", "goods", "merchandise"],
        "attributes": ["Category", "Name", "Price"]
      }
    ],
    "relationships": [
      {
        "from": "Sales",
        "to": "Products",
        "terms": ["of", "for", "by"]
      }
    ]
  }
}

Embedding Narratives in Paginated Reports

<!-- Paginated report with dynamic narrative -->
<Textbox Name="NarrativeSummary">
  <CanGrow>true</CanGrow>
  <Paragraphs>
    <Paragraph>
      <TextRuns>
        <TextRun>
          <Value>=
            "This report covers sales data from " &amp;
            Format(Parameters!StartDate.Value, "MMMM d, yyyy") &amp;
            " to " &amp;
            Format(Parameters!EndDate.Value, "MMMM d, yyyy") &amp;
            ". Total revenue was " &amp;
            Format(Sum(Fields!Revenue.Value, "SalesDataset"), "C0") &amp;
            " across " &amp;
            CountDistinct(Fields!CustomerID.Value, "SalesDataset") &amp;
            " customers."
          </Value>
          <Style>
            <FontSize>12pt</FontSize>
          </Style>
        </TextRun>
      </TextRuns>
    </Paragraph>
  </Paragraphs>
</Textbox>

API Integration

Generate narratives programmatically:

import requests

class SmartNarrativeGenerator:
    def __init__(self, access_token: str):
        self.base_url = "https://api.powerbi.com/v1.0/myorg"
        self.headers = {
            "Authorization": f"Bearer {access_token}",
            "Content-Type": "application/json"
        }

    def generate_summary(self, dataset_id: str, visual_config: dict):
        """Generate a summary for the given visual configuration."""
        # Execute DAX query to get data
        dax_query = self._build_summary_query(visual_config)
        result = self._execute_query(dataset_id, dax_query)

        # Generate narrative from results
        narrative = self._create_narrative(result, visual_config)
        return narrative

    def _create_narrative(self, data: dict, config: dict) -> str:
        """Create natural language narrative from data."""
        total = data.get("total", 0)
        previous = data.get("previous", 0)
        change = (total - previous) / previous if previous else 0

        parts = []

        # Summary
        parts.append(f"Total {config['metric_name']} reached {total:,.0f}")

        # Trend
        if change > 0:
            parts.append(f"an increase of {change:.1%} from the previous period")
        elif change < 0:
            parts.append(f"a decrease of {abs(change):.1%} from the previous period")

        # Top performer
        if data.get("top_category"):
            parts.append(f"{data['top_category']} was the top performer")

        return ", ".join(parts) + "."

Smart narratives transform raw data into understandable stories, making Power BI reports more accessible to all users regardless of their analytical expertise.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.