Ask three AI analysts “What was last quarter’s revenue?” and you may get three different numbers. One query sums gross order value. Another subtracts refunds. A third filters out test accounts. The model is not broken. It simply has no shared definition of “revenue.” A semantic layer supplies that definition once, in code, and forces every human and every LLM to use it.
Quick Takeaways
- A semantic layer is a governed catalog of metrics, dimensions, entities, and joins that sits between raw tables and every consumer, including LLMs.
- Text-to-SQL against raw schemas fails mostly on business logic ambiguity, not on syntax. Metric definitions fix the cause.
- Letting the LLM request metrics (
revenuebyregion) instead of writing SQL makes answers deterministic, auditable, and permission-aware. - Start with 10-20 certified metrics, a metadata-rich YAML spec, and an evaluation set before you expand coverage.
| Approach | Accuracy on Business Questions | Governance | Auditability | Setup Cost |
|---|---|---|---|---|
| Raw text-to-SQL | Low to moderate | None | Poor | Low |
| Text-to-SQL + schema docs | Moderate | Weak | Moderate | Low-Medium |
| Semantic layer + LLM | High | Strong | Strong | Medium-High |
What a Semantic Layer Actually Is
A semantic layer translates business concepts into computable definitions. It stores four building blocks:
- Entities: the objects the business cares about (customer, order, subscription).
- Dimensions: attributes used to slice data (region, plan tier, order date).
- Measures: aggregations over columns (
SUM(amount),COUNT(DISTINCT user_id)). - Metrics: governed business calculations built from measures (net revenue, churn rate, ARPU).
The layer compiles a metric request into SQL for your warehouse. The consumer never touches join logic or filter conditions.
Semantic Layer vs. Data Catalog vs. dbt Models
These get confused constantly. They solve different problems.
| Component | Primary Job | Defines Metric Logic? | Serves Queries? |
|---|---|---|---|
| Data catalog | Discovery, lineage, ownership | No | No |
| dbt models | Transform and materialize tables | Partially (in SQL) | No |
| Semantic layer | Define and serve metrics | Yes | Yes (API/SQL) |
| BI tool model | Visualization-specific logic | Yes, but siloed | Only inside that tool |
The BI-silo problem matters most. When metric logic lives inside one dashboard tool, your AI agent cannot reuse it, and your numbers drift.
Why AI Analysts Fail Without Governed Metrics
LLMs write fluent SQL. The failures are semantic, and they cluster into five patterns.
1. Ambiguous Metric Definitions
“Active users” might mean logged in within 30 days, performed a key action, or held a paid seat. The LLM guesses. A guess that sounds confident is worse than an error message.
2. Wrong Joins and Fan-Out
Joining orders to order_items and then summing orders.total multiplies revenue by items per order. This is the classic fan-out bug. A semantic layer encodes join cardinality, so aggregation happens at the correct grain.
3. Inconsistent Filters
Excluding internal accounts, test orders, or canceled subscriptions is tribal knowledge. Raw schemas do not encode it.
4. Schema Sprawl
Production warehouses hold thousands of tables with cryptic names like fct_ord_v3_final. Stuffing them all into a prompt degrades retrieval and accuracy.
5. No Row-Level Security Awareness
A model with warehouse credentials can answer questions the asker should not see. Governance must be enforced below the LLM, not requested in the prompt.
How an LLM Uses a Semantic Layer
There are two integration patterns.
| Pattern | How It Works | Strength | Weakness |
|---|---|---|---|
| Metric-request (recommended) | LLM emits structured query: metrics, dimensions, filters | Deterministic SQL, no fan-out | Limited to modeled metrics |
| Context-injection | Semantic definitions are fed into the prompt; LLM writes SQL | Flexible for ad hoc analysis | Still prone to logic errors |
The metric-request pattern is safer. The LLM chooses what to compute. The semantic layer decides how.
User question
→ LLM maps to metric + dimensions + filters (structured JSON)
→ Semantic layer compiles governed SQL
→ Warehouse executes with user permissions
→ LLM narrates result + cites metric definition
Defining Governed Metrics: A Working Example
The example below uses the MetricFlow-style YAML spec popularized by dbt. Syntax varies by tool, but the structure carries over to Cube, AtScale, and others.
# models/semantic/orders.yml
semantic_models:
- name: orders
description: "One row per customer order. Grain: order_id."
model: ref('fct_orders')
defaults:
agg_time_dimension: ordered_at
entities:
- name: order_id
type: primary
- name: customer_id
type: foreign
dimensions:
- name: ordered_at
type: time
type_params:
time_granularity: day
- name: order_status
type: categorical
- name: region
type: categorical
measures:
- name: gross_amount
agg: sum
expr: order_total
- name: refund_amount
agg: sum
expr: refunded_total
metrics:
- name: net_revenue
label: "Net Revenue"
description: >
Gross order value minus refunds. Excludes test and internal orders.
Owner: finance-analytics. Certified: 2026-08-01.
type: derived
type_params:
expr: gross_amount - refund_amount
metrics:
- name: gross_amount
filter: "{{ Dimension('order__order_status') }} != 'test'"
- name: refund_amount
The description field is not decoration. LLMs read it. Write descriptions that state inclusions, exclusions, grain, and ownership.
Building the AI Query Layer in Python
This example shows the metric-request pattern. The LLM returns a structured request, and a thin wrapper validates it against an allow-list before compiling and running it.
import json
from pydantic import BaseModel, Field, ValidationError
from typing import List, Optional
# Allow-list pulled from the semantic layer's metadata API
CERTIFIED_METRICS = {"net_revenue", "active_customers", "churn_rate"}
ALLOWED_DIMENSIONS = {"region", "order_status", "plan_tier", "metric_time"}
class MetricQuery(BaseModel):
metrics: List[str] = Field(..., min_length=1)
group_by: List[str] = []
where: Optional[str] = None
limit: int = Field(default=100, le=1000) # cap result size
def validate_query(raw_llm_output: str) -> MetricQuery:
"""Parse LLM JSON and reject anything outside the governed catalog."""
try:
query = MetricQuery(**json.loads(raw_llm_output))
except (json.JSONDecodeError, ValidationError) as exc:
raise ValueError(f"Malformed metric request: {exc}")
unknown_metrics = set(query.metrics) - CERTIFIED_METRICS
unknown_dims = set(query.group_by) - ALLOWED_DIMENSIONS
if unknown_metrics or unknown_dims:
raise ValueError(
f"Rejected. Unknown metrics: {unknown_metrics}, dimensions: {unknown_dims}"
)
return query
# Example LLM output
llm_output = '{"metrics": ["net_revenue"], "group_by": ["region"], "limit": 20}'
query = validate_query(llm_output)
print(query.model_dump())
Pass the validated object to your semantic layer’s API (for example, dbt Semantic Layer’s GraphQL or JDBC interface, or Cube’s REST endpoint). Run it under the end user’s identity so row-level policies apply.
Evaluating Your AI Analyst
Do not ship on vibes. Build a golden question set of 50-100 business questions with verified answers.
import pandas as pd
def evaluate(golden: pd.DataFrame, ask_fn, tol: float = 0.001) -> dict:
"""
golden columns: question, expected_value
ask_fn: callable returning a numeric answer from the AI analyst
"""
results = []
for _, row in golden.iterrows():
try:
predicted = ask_fn(row["question"])
correct = abs(predicted - row["expected_value"]) <= tol * abs(row["expected_value"])
except Exception:
predicted, correct = None, False
results.append({"question": row["question"], "predicted": predicted, "correct": correct})
df = pd.DataFrame(results)
return {
"execution_accuracy": df["correct"].mean(), # exact-answer match rate
"failure_rate": df["predicted"].isna().mean() # crashes or rejected queries
}
Track these metrics per release:
| Metric | What It Measures | Target |
|---|---|---|
| Execution accuracy | Answer matches ground truth | >90% on certified metrics |
| Refusal rate | Correctly declines unmodeled questions | High on out-of-scope items |
| Latency (p95) | End-to-end response time | <10 seconds |
| Metric coverage | Share of questions mapped to a certified metric | Rising over time |
A good AI analyst refuses gracefully. If someone asks for a metric that does not exist, the right response is “not defined yet,” not an invented calculation.
Tool Landscape
| Tool | Style | Strength | Trade-off |
|---|---|---|---|
| dbt Semantic Layer (MetricFlow) | Code-first YAML | Tight dbt integration, version control | Requires dbt workflow |
| Cube | Code-first, API-driven | Caching, rich REST/GraphQL/SQL APIs | Separate service to operate |
| AtScale | Enterprise virtualization | Broad BI compatibility | Higher cost and complexity |
| Looker (LookML) | BI-native | Mature modeling language | Logic tied to Looker ecosystem |
| Warehouse-native (e.g., Snowflake semantic views) | In-warehouse | Fewer moving parts | Tied to one vendor |
Verify current features and pricing before choosing. This category changes fast.
Real-World Use Cases
Use Case 1: Finance Self-Service
A finance team gets daily questions about revenue by segment. Before the semantic layer, analysts hand-wrote SQL and reconciled mismatches weekly. After certifying net_revenue, an AI assistant answers segment questions directly, and every answer links to the metric definition and its owner.
Use Case 2: Product Analytics Without Fan-Out Errors
A SaaS product team asks, “What is weekly active usage by plan tier?” The raw schema joins events to subscriptions with multiple rows per user. The semantic layer defines active_users as COUNT(DISTINCT user_id) at the correct grain, which removes the duplicate counting a raw text-to-SQL model would produce.
Use Case 3: Multi-Tenant Row-Level Security
A B2B platform lets customers query their own usage through a chatbot. The semantic layer injects a tenant filter from the authenticated session. The LLM never sees other tenants’ rows, and prompt injection cannot override a policy enforced below the model.
Implementation Roadmap
- Audit the top 20 questions. Pull them from BI query logs and Slack requests.
- Select 10-20 metrics. Prioritize those that cause the most disputes.
- Assign owners. Every certified metric needs a named steward.
- Write rich descriptions. Include synonyms (“sales,” “turnover”) so the LLM maps language correctly.
- Build the golden set. Verify answers manually against finance or ops sources.
- Wire the metric-request pattern. Validate every LLM output against the allow-list.
- Monitor and expand. Log unanswered questions and convert frequent ones into new metrics.
Common Pitfalls
- Modeling too much too soon. Certify a small core first.
- Skipping synonyms. Users say “customers,” “accounts,” “clients.” Map them.
- Letting the LLM bypass the layer. Provide no raw-SQL fallback for governed domains.
- Ignoring metric versioning. Redefining “churn” silently breaks trend lines. Version and announce changes.
- Using a shared service account. It defeats row-level security and audit trails.
FAQ
What is a semantic layer in AI?
A semantic layer is a governed definition layer that maps business terms like “revenue” or “churn” to exact calculations, joins, and filters. In AI systems, it lets an LLM request a named metric instead of inventing SQL, which makes answers consistent and auditable.
Does a semantic layer improve text-to-SQL accuracy?
Yes. Published vendor benchmarks and practitioner reports consistently show accuracy gains when LLMs query governed metrics instead of raw tables, because the layer removes ambiguity about definitions, joins, and filters. Gains depend on your schema and question mix, so measure with your own golden question set.
Can an LLM replace a semantic layer?
No. An LLM can draft metric definitions or suggest synonyms, but it cannot enforce a single source of truth. Governance requires deterministic, versioned definitions that a human owner has certified.
Which semantic layer is best for AI analysts?
It depends on your stack. dbt Semantic Layer fits dbt-centric teams, Cube suits API-first applications, and warehouse-native options reduce infrastructure. Choose the tool that exposes metadata and a query API your LLM wrapper can call.




