$npx -y skills add dbt-labs/dbt-agent-skills --skill answering-natural-language-questions-with-dbtWrites and executes SQL queries against the data warehouse using dbt's Semantic Layer or ad-hoc SQL to answer business questions. Use when a user asks about analytics, metrics, KPIs, or data (e.g., "What were total sales last quarter?", "Show me top customers by revenue"). NOT fo
| 1 | # Answering Natural Language Questions with dbt |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Answer data questions using the best available method: semantic layer first, then SQL modification, then model discovery, then manifest analysis. Always exhaust options before saying "cannot answer." |
| 6 | |
| 7 | **Use for:** Business questions from users that need data answers |
| 8 | - "What were total sales last month?" |
| 9 | - "How many active customers do we have?" |
| 10 | - "Show me revenue by region" |
| 11 | |
| 12 | **Not for:** |
| 13 | - Validating model logic during development |
| 14 | - Testing dbt models or semantic layer definitions |
| 15 | - Building or modifying dbt models |
| 16 | - `dbt run`, `dbt test`, or `dbt build` workflows |
| 17 | |
| 18 | ## Decision Flow |
| 19 | |
| 20 | ```mermaid |
| 21 | flowchart TD |
| 22 | start([Business question received]) |
| 23 | check_sl{Semantic layer tools available?} |
| 24 | list_metrics[list_metrics] |
| 25 | metric_exists{Relevant metric exists?} |
| 26 | get_dims[get_dimensions] |
| 27 | sl_sufficient{SL can answer directly?} |
| 28 | query_metrics[query_metrics] |
| 29 | answer([Return answer]) |
| 30 | try_compiled[get_metrics_compiled_sql<br/>Modify SQL, execute_sql] |
| 31 | check_discovery{Model discovery tools available?} |
| 32 | try_discovery[get_mart_models<br/>get_model_details<br/>Write SQL, execute] |
| 33 | check_manifest{In dbt project?} |
| 34 | try_manifest[Analyze manifest/catalog<br/>Write SQL] |
| 35 | cannot([Cannot answer]) |
| 36 | suggest{In dbt project?} |
| 37 | improvements[Suggest semantic layer changes] |
| 38 | done([Done]) |
| 39 | |
| 40 | start --> check_sl |
| 41 | check_sl -->|yes| list_metrics |
| 42 | check_sl -->|no| check_discovery |
| 43 | list_metrics --> metric_exists |
| 44 | metric_exists -->|yes| get_dims |
| 45 | metric_exists -->|no| check_discovery |
| 46 | get_dims --> sl_sufficient |
| 47 | sl_sufficient -->|yes| query_metrics |
| 48 | sl_sufficient -->|no| try_compiled |
| 49 | query_metrics --> answer |
| 50 | try_compiled -->|success| answer |
| 51 | try_compiled -->|fail| check_discovery |
| 52 | check_discovery -->|yes| try_discovery |
| 53 | check_discovery -->|no| check_manifest |
| 54 | try_discovery -->|success| answer |
| 55 | try_discovery -->|fail| check_manifest |
| 56 | check_manifest -->|yes| try_manifest |
| 57 | check_manifest -->|no| cannot |
| 58 | try_manifest -->|SQL ready| answer |
| 59 | answer --> suggest |
| 60 | cannot --> done |
| 61 | suggest -->|yes| improvements |
| 62 | suggest -->|no| done |
| 63 | improvements --> done |
| 64 | ``` |
| 65 | |
| 66 | ## Quick Reference |
| 67 | |
| 68 | | Priority | Condition | Approach | Tools | |
| 69 | |----------|-----------|----------|-------| |
| 70 | | 1 | Semantic layer active | Query metrics directly | `list_metrics`, `get_dimensions`, `query_metrics` | |
| 71 | | 2 | SL active but minor modifications needed (missing dimension, custom filter, case when, different aggregation) | Modify compiled SQL | `get_metrics_compiled_sql`, then `execute_sql` | |
| 72 | | 3 | No SL, discovery tools active | Explore models, write SQL | `get_mart_models`, `get_model_details`, then `show`/`execute_sql` | |
| 73 | | 4 | No MCP, in dbt project | Analyze artifacts, write SQL | Read `target/manifest.json`, `target/catalog.json` | |
| 74 | |
| 75 | ## Approach 1: Semantic Layer Query |
| 76 | |
| 77 | When `list_metrics` and `query_metrics` are available: |
| 78 | |
| 79 | 1. `list_metrics` - find relevant metric |
| 80 | 2. `get_dimensions` - verify required dimensions exist |
| 81 | 3. `query_metrics` - execute with appropriate filters |
| 82 | |
| 83 | If semantic layer can't answer directly (missing dimension, need custom logic) → go to Approach 2. |
| 84 | |
| 85 | ## Approach 2: Modified Compiled SQL |
| 86 | |
| 87 | When semantic layer has the metric but needs minor modifications: |
| 88 | |
| 89 | - Missing dimension (join + group by) |
| 90 | - Custom filter not available as a dimension |
| 91 | - Case when logic for custom categorization |
| 92 | - Different aggregation than what's defined |
| 93 | |
| 94 | 1. `get_metrics_compiled_sql` - get the SQL that would run (returns raw SQL, not Jinja) |
| 95 | 2. Modify SQL to add what's needed |
| 96 | 3. `execute_sql` to run the raw SQL |
| 97 | 4. **Always suggest** updating the semantic model if the modification would be reusable |
| 98 | |
| 99 | ```sql |
| 100 | -- Example: Adding sales_rep dimension |
| 101 | WITH base AS ( |
| 102 | -- ... compiled metric logic (already resolved to table names) ... |
| 103 | ) |
| 104 | SELECT base.*, reps.sales_rep_name |
| 105 | FROM base |
| 106 | JOIN analytics.dim_sales_reps reps ON base.rep_id = reps.id |
| 107 | GROUP BY ... |
| 108 | |
| 109 | -- Example: Custom filter |
| 110 | SELECT * FROM (compiled_metric_sql) WHERE region = 'EMEA' |
| 111 | |
| 112 | -- Example: Case when categorization |
| 113 | SELECT |
| 114 | CASE WHEN amount > 1000 THEN 'large' ELSE 'small' END as deal_size, |
| 115 | SUM(amount) |
| 116 | FROM (compiled_metric_sql) |
| 117 | GROUP BY 1 |
| 118 | ``` |
| 119 | |
| 120 | **Note:** The compiled SQL contains resolved table names, not `{{ ref() }}`. Work with the raw SQL as returned. |
| 121 | |
| 122 | ## Approach 3: Model Discovery |
| 123 | |
| 124 | When no semantic layer but `get_all_models`/`get_model_details` available: |
| 125 | |
| 126 | 1. `get_mart_models` - start with marts |