Why Your Semantic Layer Matters More Than Your AI Agent
A governed semantic layer is the non-negotiable foundation for AI analytics, transforming intent into correct metrics while preventing the 'silent failure' of valid but wrong results.
Deep dive
- Separates warehouse schema (Physical Layer) from business definitions (Model Layer) to ensure changes to either do not cause cascading failures.
- AI reads user intent and translates it to a semantic query, while a deterministic engine generates the final SQL.
- Metadata is treated as infrastructure, including certification levels and disambiguation hints, to provide operational guidance to the AI.
- Employs strict eligibility checks for aggregate tables to prevent partial or incorrect data usage.
- Recommends AI for generating draft models to overcome the blank-page problem, but insists on human-in-the-loop validation.
Decoder
- Disambiguation hint: Metadata that helps a system choose between similar terms, like clarifying if 'revenue' refers to 'net' or 'gross' amounts.
- Grain: The level of detail at which data is stored in a table; joining tables with different grains (e.g., day vs. hour) often results in incorrect math.
Original article
Why Your Semantic Layer Matters More Than Your AI Agent
How I built a governed semantic model that eliminates silent analytics failures: the ones where the query succeeds and the number is wrong.
A data warehouse knows what columns exist. A semantic model knows what they mean. This is the story of how I built the layer between the two, and why I believe the model, not the AI agent, is the product.
Key takeaways
- The most common silent analytics failure is not a broken query. It is a successful query that returns the wrong number. A semantic model makes this structurally impossible.
- The AI interprets user intent and translates it into a semantic query. A deterministic engine generates the SQL. Same inputs, same output, every time. No LLM is involved in SQL generation.
- The model is organized in two layers: a physical layer that mirrors the warehouse schema, and a model layer that defines what the data means. They change independently, owned by different people.
- Every piece of metadata provided to the AI (disambiguation hints, semantic types, canonical filter values) was added to fix a specific, observed failure. Metadata is infrastructure, not documentation.
- AI can compress semantic model development from months to weeks by generating structured drafts for human review, eliminating the blank-page problem.
The problem nobody solves with better prompts
Every data platform eventually confronts the same question: where does the definition of "revenue" live? Is it in the SQL query an analyst wrote last quarter? In the transformation pipeline a data engineer maintains? In the dashboard someone configured two years ago that nobody wants to touch? In the Slack thread where someone explained to the CFO that "net revenue" doesn't include refunds but "total revenue" does?
In most organizations, the answer is "all of the above." Metric definitions are scattered across queries, dashboards, documentation, and tribal knowledge. The same column is interpreted differently by different teams. Hand an LLM the raw warehouse schema and it will confidently join two tables that shouldn't be joined at the grain of the question, because nothing in the schema says otherwise.
A user asks: "Show me Net Revenue by Consumer Region for last quarter."
Seven words, three layers of ambiguity. "Net Revenue" is not a column. It is a derived metric spanning three tables with different grains. "Consumer Region" requires a historically-versioned join where the correct row depends on when the order was placed, not today's date. "Last quarter" could mean the calendar quarter or the fiscal quarter, depending on which team is asking. An LLM will make a confident choice for each ambiguity. It will be wrong about at least one, and the resulting number will look plausible.
The silent failure mode
A SQL syntax error is visible: the query fails, the user retries. A wrong metric definition is invisible: the query succeeds, the number lands in a slide deck, and a decision is made on a figure that was never correct.
The semantic model exists to make this second failure mode structurally impossible.
More schema documentation does not close this gap. You can annotate every column with a description, and an LLM still will not know that two columns in different tables represent the same concept, or that a dimension table requires a time-windowed join. What you need is not more metadata on the schema. You need a separate layer that defines what the data means, independent of how it is stored.
The approach: separate what changes for different reasons
The central design principle is simple: things that change for different reasons should live in different places.
Warehouse schemas change when data teams add new tables, introduce new columns, migrate to a new warehouse, or restructure data models. Business definitions change when analysts refine what "net revenue" means or a new team needs a different lens on the same data. These are different rates of change, driven by different people for different reasons. Mixing them in the same layer means every schema change risks breaking a metric definition, and every metric change requires touching schema files.
I separated them into two layers.
Two-Layer Architecture
The physical layer is a complete mirror of the warehouse schema: every table, every column, every join path between tables. It is the single source of truth about what the warehouse contains. If a column is not declared here, the system cannot reference it. If a join is not declared here, the system cannot perform it.
The model layer sits above it and defines what the data means. Business domains like "delivery" or "subscription" are modeled as entities that pull from multiple physical tables, define metrics with precise formulas, and compose through inheritance. An analyst thinks in domains, and the model layer mirrors that vocabulary exactly.
What this separation buys you
When the warehouse changes (a column is renamed, a table is migrated): only the physical layer is updated. Every metric and business definition continues to work untouched.
When a business definition changes ("net revenue" now includes a new deduction category): only the model layer is updated. The warehouse schema files are untouched.
When a new team needs a different lens (a Finance perspective on the same delivery data): one new file is added. The core domain model it inherits from does not change.
Each change touches exactly one layer. This is the core design constraint, and it is what makes the system maintainable as the number of metrics, teams, and use cases grows.
Everything is defined in version-controlled configuration files, reviewed in pull requests the same way application code is. A metric definition change is a two-line diff. The alternative (metric definitions stored in a database, managed through a UI) trades reviewability for convenience. I chose reviewability.
How the model solves the hard problems
A key architectural decision: the AI interprets the user's question, translates it into a semantic query (selecting the right metrics, dimensions, filters, and date ranges from the governed catalog) and hands it to the semantic engine, which handles all SQL generation. The AI understands intent. The engine guarantees correctness. Same semantic query in, same SQL out, every time.
When multiple metrics share the same name
A user asks for "revenue." The model contains total revenue, net revenue, gross revenue, and revenue per order. Without guidance, the agent picks one (silently wrong most of the time) or asks a clarifying question when the answer is obvious from context.
I solved this by making every metric carry more than just a formula. Each metric has a certification level (is this verified by the data team or still experimental?), a semantic type (is this a rate that should never be summed, a duration that should be averaged, or a cumulative total?), and free-form disambiguation hints written by the metric author. The agent reads these hints in context and resolves ambiguity without asking. Each of these metadata fields was added to fix a specific, observed failure. They are not documentation; they are operational guidance for the AI.
When joins are ambiguous or historically versioned
In a parcel delivery business, a delivery has two consumers: the person who placed the order and the person who receives it. Both live in the same consumer table. "Show me revenue by consumer region" is ambiguous: which consumer?
And even once you pick the right consumer, their region may have changed over time. The consumer table has multiple historical rows per person. Joining without accounting for the time dimension silently shifts revenue between regions, or worse, multiplies it.
I solved both problems in the model. Every join between tables is explicitly declared with its cardinality, its join logic, and whether it requires time-windowed matching. The query generator does not infer joins from column names or foreign keys. If a join is not declared, it does not happen. For historically versioned dimensions, the system automatically resolves the correct row based on when the transaction occurred, not the current date.
When summary models exist and the AI can't tell when to use them over the fact table
Pre-aggregated tables exist for performance. But they carry restrictions. A daily delivery aggregate might be pre-built excluding cancelled deliveries. If a user asks for "revenue by delivery status" and the system routes to that aggregate, the cancelled row is missing entirely. The total is lower than reality.
I solved this with a strict eligibility check. Before routing any query to an aggregate, the system evaluates whether the aggregate can safely answer the entire question. The decision is binary: the aggregate serves the whole query, or the system falls back to the full table. There is no partial routing, no hybrid mode. It is better to be correct and slower than fast and wrong.
When the AI guesses filter values
"Show me cancelled orders." Is the filter value "cancelled", "Cancelled", "CANCELLED", or "cancel"? A wrong guess produces a valid query that returns zero rows. The user assumes there are no cancelled orders.
I solved this by declaring the exact valid values for every enumerated column in the physical layer. These canonical values are extracted when the model loads and provided to the AI as part of its operating context. The agent matches against exact strings.
When the AI misunderstands how a metric should be aggregated
"What's the total cancellation rate across all regions?" If the agent sums individual region rates instead of computing a weighted average, the resulting number is meaningless.
I solved this by tagging metrics with semantic types: rate metrics should be displayed as percentages and never summed, duration metrics should be compared with averages, cumulative metrics should not be double-counted. These rules flow into the AI's context as behavioral guidance.
Why you can trust it
Errors are caught when the model is defined, not when a user runs a query. Every model goes through structural validation before it can serve a single query. Circular dependencies, missing fields, broken references—all are rejected immediately when the model author saves their work.
The query generator is deterministic and traceable. Given the same inputs, it produces the same SQL every time. No probabilistic reasoning. A human reading the generated SQL can follow every clause back to a specific model definition.
The model detects when the warehouse changes under it. The system compares its definitions against the live database and reports exactly where they disagree: which columns were renamed, which were added, which changed type. This turns an invisible problem into a visible, actionable one.
Getting started: how AI solves the cold-start problem
Building a semantic model by hand is prohibitively expensive. For an organization with hundreds of tables and thousands of columns, this is months of work.
I believe AI can compress this timeline from months to weeks, not by replacing human judgment, but by eliminating the blank-page problem. An AI-powered Studio Agent reads the physical data model and generates a structured draft of the semantic model: proposed metric definitions, inferred join paths, temporal rules, and dimension hierarchies. Every proposal carries supporting evidence.
Building a semantic model from scratch is authoring a document from a blank page. Reviewing a structured, evidence-backed draft is reviewing a pull request. The cognitive load is fundamentally different. The person doing the review still needs domain expertise, but they are correcting and approving, not creating from nothing.
Keeping it clean: preventing metric proliferation
Getting started is one problem. Keeping the model healthy as it grows is another. Without guardrails, teams create variations instead of reusing existing definitions. The guardrails are structural. When a new metric is proposed, the system checks for semantic overlap. Equivalent metrics with different filters get redirected to the existing metric with a filter parameter.
The model is the product
The semantic model is not scaffolding you build so the AI can do interesting work. The model is the interesting work. It encodes institutional knowledge about what the data means, how metrics relate to each other, which dimensions make sense together, and where the edge cases hide. The AI agent is a delivery mechanism for that knowledge. The model is the substance.
Every improvement to the model directly improves every query the system will ever generate. In my experience, improving the model delivers higher returns than any amount of prompt engineering. The model is the leverage point.
"A great model with a mediocre agent produces better answers than a mediocre model with a great agent."
Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi