Your Semantic Model Is the API: Giving Agents Certified Metrics
Why AI agents invent private definitions of revenue and inventory, and how a governed semantic model with grain, owners and refusals gives them one contract.
This article is published in English.
Two numbers, both official
A planning standup opened with a slide titled "Last week, sales". The night before, two copilots had each been asked the same sentence, and both had returned charts with SQL attached. One figure landed 4% above plan and the other 3% below it.
Nobody in the room blamed hallucinated columns. The warehouse really did have a sales table with a net_amt column and dates. The queries compiled, the join keys existed, and from the database's point of view both answers were legal.
Finance then showed up with a third figure, one that had already been frozen for the weekly board letter. It counted recognized revenue by invoice date, subtracted credit notes, and kept out both employee purchases and wholesale. Copilot one had gone by order date and left employee store traffic in. Copilot two had gone by ship date and taken the region from the ship-to address rather than from the selling organization.
Asked in the same thread which date each had used, one pointed to order_dt and the other to ship_dt. Neither mentioned credit notes. On whether employee purchases were included, one said it hadn't seen a filter for them, and the other made up a story about a channel flag that turned out to be a marketing attribution field with no link to employee stores.
Obedience was the issue here. Both agents acted like a capable analyst having a rough day: they spotted a column whose name resembled the question and summed it. A human would normally pause and ask which sales number was wanted, whereas an agent reads a plausible column as a green light to finish.
Afterward the Slack thread held three charts and one finance number. Nobody pasted a metric name, only screenshots, and that's a useful warning sign. When an argument is about pictures, the contract never got called.
Give an agent a warehouse and a friendly prompt, and it will invent the business. The invention looks like competence and lands in Slack, where people argue about model weights when they should be arguing about the missing contract. Such arguments are easy to file as a culture problem, yet they come from a missing interface, and the standup is only where the failure becomes audible.
One handy way to picture the semantic model is as an API, and not in the REST-trend sense. Think of an interface that rules out private interpretation. When someone asks for last week's sales, the caller has no say over which date column to use. It runs recognized_revenue at a stated grain, or else it receives a refusal. SQL can still be generated by the engine, but the agent does not write it.
Picturing endpoints and API keys is fine if that helps. What matters is something narrower: an operation with a name and a certified meaning that any permitted caller can invoke.
APIs also come with owners, versions and breaking-change rules. A measure without an owner is a rumor that happens to compute. When finance changes how credit notes are handled, that should be a version bump that every caller feels, or else the copies are back. Printing the metric version in the footer of the weekly letter for a month sounds pedantic, but it turns the "which revenue" conversation from a personality conflict into a version question.
The query was perfect, the meaning wasn't
Give a modern model a schema dump plus a revenue question and the SQL usually comes out tidy enough to have been impressive ten years back. That part is no longer where the risk sits.
Revenue is not a column. Even one company can carry booked, invoiced, delivered and recognized revenue, along with whatever the tills captured before returns. Credit notes might net out or might not. Intercompany sales might count or might not. "Netherlands" could mean the billing country, the delivery country, or the sales organization owning the account, and "last month" could be a calendar month or a fiscal period closing on a Sunday.
No part of this is about JOIN syntax. A textbook join can still produce an answer that is wrong for the decision riding on it.
Inventory behaves the same way. On-hand stock isn't available-to-promise, and available-to-promise isn't what the allocation engine will actually release to e-commerce tonight. An agent that answers "can we promise Friday" from on-hand quantities will sound operational and still strand a customer.
In one case an agent reassured a warehouse lead about a style because it had added up qty_on_hand over every location, damaged cage included, even though planning had already flagged that cage as unusable. The SQL was tidy and the meaning was a guess. The lead had physically checked the cage earlier that day, and the agent had nothing but a table.
Flash reporting shows the same gap. Store operations wants units that rang the till before 4 p.m., and finance wants recognized revenue after credits. In a meeting both will say "yesterday's sales". An agent given that sentence picks a timestamp column and a money column and stops, and the argument begins after the chart appears.
Merchandise planning has the same trap. Demand differs from point-of-sale data, and POS differs from net sales once returns come off. A style may show as sold out in the e-commerce promise engine while a table visible to the agent still lists stock, since that table also holds in-transit units, QC holds and the pack-and-hold quantity buying reserved for a wholesale drop. Let the agent answer "do we have it" from the wrong fact and the company overpromises by morning, then burns the afternoon explaining a stockout the table never showed.
Calling this a prompt failure misses the point, since the prompt "do we have it" was fine. What was missing was an available_to_promise object with a documented grain: the set of locations, the hold types excluded, whether in-transit stock counts, and the snapshot time it reflects. Absent that, each caller improvises its own supply chain.
This is why the old comfort of self-service BI doesn't carry over. Ten people looking at one dashboard at least share the same flawed number. Ten agents writing ten private queries each sound locally reasonable, and the organization ends up with a cloud of fluent disagreement and no number.
An analyst missing context asks a follow-up question. An agent missing context completes the sentence. In demos that was a curiosity, and in production it's a hazard, because the same stack that made data easy to reach made it easy to query without agreeing on anything.
Two decades went into making data reachable via warehouses, lakehouses, ELT, notebooks and APIs, so the bottleneck sits elsewhere now. Whether software can see the table is settled. Whether software gets to decide what the table means is not.
What a meaning contract holds
A glossary falls short because it can simply be misread, while a contract either runs or gets rejected. The operations a usable semantic model names are the ones the business has already fought over and settled:
- Recognized revenue has a precise definition, and certain rows are never part of it.
- An active customer is defined by a rule, not by a gut feeling.
- The customer-to-region relationship is the one finance is prepared to stand behind.
- For this KPI the period is cut by invoice date and never by order date.
- Some dimensions are legal to slice by, and the rest are decoration.
Underneath there are measures, dimensions, hierarchies, calculated fields, metadata and row filters, and which vendor's file holds them doesn't matter. Functionally, a slice of business language has been captured in a form software can call. Until recently the main caller was a report. The new caller never gets bored, never walks over to the analyst's desk and feels no shame about being confidently wrong.
Grain is what teams skip because it feels like modeling homework, yet it's the API field. Declare none and every caller invents one. It reduces to four questions that need answers in the same place:
- Which date closes the period.
- Which entity the number describes, whether selling org, ship-to or billed-to.
- Which rows are excluded, such as employees, wholesale, intercompany and test orders.
- Which security rules still apply when the caller is an agent.
Missing from the model, those answers end up in a Slack thread, which means nowhere.
A margin metric shows how this goes. The formula was good but the date was sloppy. One caller used order date because the fact table defaulted to it, and another used invoice date because the finance view did. Both reported "margin", and they diverged by two points in a week when freight costs spiked. The agent took the blame, though the real defect was the missing grain. Adding invoice date, a freight inclusion rule and an owner fixed it, and the following week's letters matched. Nobody thanked the model, but the pings stopped.
The route to aim for is dull by design: intent, bind, enforce, execute, answer. At no point does the model vote on what revenue means. Picking which certified metric matches the question is acceptable, because that is classification. Reassembling a metric out of raw tables is interpolation, and interpolation is how two official numbers show up before the first coffee.
The trace tells the story. If the agent selects columns by name similarity, it's a scavenger. If it calls recognized_revenue with a period and a region, it's a caller. The second trace is less cinematic, and it's the one to bring to an audit.
Metadata stopped being documentation
Descriptions used to be an afterthought, filled in once the dashboard shipped and only if the sprint had hours left, which it seldom did. A measure named [NS Adj LY %] was acceptable because the three people who edited the file knew its history.
An agent doesn't know the folklore. Microsoft's guidance for preparing semantic models for data agents says as much: names, descriptions, relationships and reduced ambiguity dominate answer quality, and that holds even outside that stack. These strings are now binding input. A blank description doesn't stop the agent from answering, it just binds to a rumor.
The shift is easy to underestimate because the file looks identical. There's still a model with measures in it. What changed is the consumer: a human developer infers, while an agent binds, and a sloppy bind still produces an answer with a chart attached.
A cryptic measure name now counts as a production defect, the same as an undocumented public function. Nobody would ship fn_x3() as a billing API, yet for a decade it passed as a metrics API because only people called it.
The platforms placed the same bet
Strip the brand colors from the product pages and a pattern shows. Microsoft, Snowflake, Databricks and ThoughtSpot are all investing in semantic layers, governance and what they call trusted business context. Their pitch isn't a flashy demo of a model typing out SQL.
All of them are betting that a model ought to reason over certified meaning rather than over whichever column happens to sit closest to a word in the question. Fabric-style data agents answering across warehouses, lakehouses and Power BI semantic models express that bet. So do Cortex-style analysts that favor a semantic view over a raw schema dump, and so do metric views and catalogs that elevate a KPI to a platform object. ThoughtSpot has been selling "ask the data" over a modeled layer for years, and the rest of the market understood why once software became a possible caller.
A strategy of picking the smartest model and pointing it at the lake runs against the direction of the platforms already being paid for. They're putting a meaning layer in front of the lake because models got good at SQL, since fluency raised the cost of disagreement without removing it. Buying another copilot doesn't create a definition of customer. It creates more surfaces that can disagree.
A protocol doesn't define revenue
MCP appears in these same discussions, and the protocol sometimes gets mistaken for a strategy. It does have real value. Take a Fabric data agent published as an MCP server, still in preview when the docs were last read: it shows that the place where a question is typed does not have to be the place where the logic is maintained. Qlik and other vendors have gone the same direction, serving governed analytics to assistants that never launch the original tool. The surface for asking and the surface for meaning can come apart.
Still, marrying a protocol is unwise, since protocols get replaced. The lasting principle is that meaning is a service and the chatbot is a client.
Exposing a messy Power BI file through MCP only makes the mess remote. The agent in Claude, Copilot or an internal harness will still bind to [NS Adj LY %] if that's what was published.
Headless BI went through this already. Teams put a thin SQL interface over the same undocumented measures and then blamed the new client when figures shifted. The protocol did its job, and what it carried was folklore with a network route.
So the protocol stays in the plumbing: finding tools, passing context, calling across processes. Meaning is handled separately, through binding the metric, rejecting illegal grain and applying row security. Superb plumbing over a definition nobody would defend in front of finance is still folklore, just with a URL.
The old BI debate asked which tool to standardize on. With agents as callers, the better question is where the trusted analytical context lives and which callers may hit it. Reporting can stay in one product while agents are served from a governed model closer to the warehouse, or the reverse. What can't work is letting every caller keep a private replica of revenue and then acting surprised when the numbers drift apart.
Dashboards became one caller among several
Dashboards were once the only serious consumer, which is no longer the case. People long assumed a semantic model belonged to one BI tool: Power BI models fed Power BI reports, and LookML models fed Looks. That packaging was sensible while humans were the sole audience and the report was the product.
Today one contract can serve dashboards, Excel, operational apps, copilots, chat, agents, and a job that files a ticket when a metric leaves its band. Dashboards stay useful, yet they are no longer the purpose of the model, because a chart is just one client of the definition.
This is a welcome change. Charts age quickly, whereas the definitions of revenue, margin, occupancy and churn ought to move slowly. When those definitions are properly managed, a new copilot next quarter does not restart the join argument, and an agent hunting anomalies can reuse the metric behind the weekly letter. That reuse is where the payoff lies. A good-looking dashboard whose logic is buried in a visual-level measure leads nowhere, because a visual cannot be invoked.
Every review brings up a rebuttal: if models write SQL, why keep funding measures, and why not leave it to the agent? The agent increasingly can do it. From the business side, though, that capability is exactly how things break. Access became cheap while agreement stayed expensive. Ten employees watching one dashboard share a number even when it is wrong, but ten agents hitting a warehouse produce ten local truths, each delivered with the same confidence.
A cheap version of the experiment shows it. Take one warehouse, one question and three agent configurations with no shared metric, and the result is three revenue figures with three stories about why each is the grown-up one. One used bookings because a table name contained order. One double-counted kits because it confused line grain with header grain. One came close to finance by using invoice date but still included wholesale. Any of them posted to a VP thread would have meant a week spent litigating the chatbot and not the definition.
Cheap access without a contract lets more people, and now more agents, coin private truths. That explains why modeling grew more important, not less, just as SQL generation got good.
One stack isn't enough
The next trap to watch for is the souvenir semantic layer: certified in one tool and folklore in every other. A team might spend a year on an excellent model inside the BI product everyone shows off, then wire an agent straight to the warehouse on the grounds that the warehouse is the source of truth. All that happened is that the ambiguity relocated. The dashboard reports recognized revenue, the agent reports net_amt, and finance keeps its spreadsheet. The organization gained another dialect and no extra clarity.
The check is easy to state: when a metric definition fails to carry over from the warehouse to the BI tool and on to the agent, only the label was renamed, and the dispute is still alive.
Portability does not demand a fourth platform. The requirement is that a caller outside the original file can run the same operation and receive the same grain. The vehicle could be a warehouse-native metric object honored by every engine, a headless metrics service, or a closely governed semantic model reached through a query API or an MCP adapter. A simple test settles it: edit the definition once and see whether every caller follows. Whatever stays put is a copy.
Warehouse-native metrics look attractive when most of the data lives on one platform, with a familiar catch. The definition is certified in that spot, and an agent aimed at a different warehouse or a replica never encounters it. Federated approaches try to hold one meaning across engines, and they break down once the so-called universal layer turns into one more copy with nicer YAML.
A slightly ugly portable metric beats a gorgeous one trapped in a file that only three developers can compile, because agents won't wait for that file. They'll query around it.
What an agent-ready model looks like
Most models that get inherited were built for a person who already knew the office gossip. A developer understands why a set-analysis expression drops a channel; an agent that sees only the output cannot. A DAX measure with a joke name works as a private handshake and reads as noise to a caller trying to bind. One real file used [NS Adj LY %] to mean net sales, adjusted, last year, percent change, minus wholesale, minus the pop-up store finance dislikes. Three people could decode it, and the fourth reader was an agent, which read NS as net sales and went no further.
The checklist isn't exotic. It's the old BI craft with a higher price for sloppiness:
- Honest names. Borrow the vocabulary the business already argues in, so
recognized_revenuewins overNS_Adj. If two teams use different words, choose one inside the model and record the other as an alias in the description instead of hiding the dispute in an abbreviation. - Usage clauses. State where the metric applies, for instance a note that the metric suits board reporting, is wrong for same-store flash numbers, and leaves out wholesale. Once optional, that paragraph now separates a bind from a guess.
- Declared relationships. Customer, invoice and subscription should be linked explicitly, so the agent doesn't go on a join adventure because two ids are both called
cust_id. - A small surface. Every technical column left visible is a chance to skip the metric. Hide the raw facts that callers must not re-aggregate. It's politically hard, and it's worth doing, because an API that exposes every private field isn't an API.
- Owners and versions. Mark which metrics are official and which are experiments, who changes them, and what "v3" means when last quarter's board letter used v2.
- Security that travels. An agent should never be able to union its way into a wider set of rows than a person sees in the standard report. When row-level rules exist only in the BI service while the agent hits the warehouse directly, the result is a bypass, not a copilot.
Consider recognized_revenue at a retailer. Its formula is the invoiced amount net of credits, converted from the transaction currency at the accounting rate on the invoice date. The grain is invoice date plus selling organization, not ship-to. It excludes employee stores, wholesale, test orders and intercompany sales. The legal dimensions are selling org, product hierarchy and channel as set by order type, and the marketing last-click table is off limits. Finance controller owns it, and the version is whichever one fed the board letter last Friday.
Suppose an agent asks for "sales by Instagram". That dimension does not exist on this metric, so the right answer is a refusal or a handoff to another certified metric owned by marketing. A scavenger would join the last-click table and label the result insight. The join might even be technically valid, yet it describes a second business.
A refusal is better than silence, and silence is better than a private definition. Teams dislike building refusals because the demos look worse, but demos that fail closed match how a real company works.
No LLM trick is involved here. Old BI craft, the kind that justified universes and governed data models back then, does all the work. Only the consumer has changed: sloppiness once yielded a wrong dashboard that someone might spot, and now yields a wrong action at machine speed.
The work that doesn't disappear
Assuming this works, some of the ticket queue does shrink. A request to split a figure by region is something an agent can handle when region is a legal dimension of that metric. A request to add last year is a time-intelligence matter that was either modeled earlier or never was.
It would be wrong to tell a BI team its job has vanished, because the work has relocated. Somebody still has to rule on what each KPI may mean and where the official version sits. Somebody has to settle which relationships are right, which sources deserve trust, and which exceptions apply. Somebody also owns who sees which rows, which models count as authoritative, how a change gets tested, and how lineage and ownership surface in review.
That work is less fun than a polished visual, and most weeks shipping a chart is more tempting. Still, the compounding value sits on the boring side. Visuals are disposable, while a definition that both the board letter and the overnight agent can call is an asset.
Expect fewer tickets that quietly mean "please be a GUI" and more fights that quietly mean "please be a legislature". People who only wanted to build charts may find this period feels like a demotion. People who wanted numbers to hold up when software touches them will find the thankless work has become the main job.
Failure modes that keep recurring
These keep getting filed as model-quality issues, and the grain deserves a check before anyone swaps the model.
- The column scavenger. The agent grabs the first field with
revin its name. No metric gets invoked, although the trace looks busy. - The private grain. Every caller chooses its own date, and each is convinced it answered "last month" correctly.
- The wiki contract. The actual rule is written on a Confluence page nobody touched since a reorg, and the model itself only has a column comment reading "sales".
- The stack souvenir. The sole certified metric sits in a BI file, and the agent talks to the warehouse because that was the easy demo route.
- The security sidestep. The reporting service enforces row filters, but the agent runs under a service principal that sees all, since the demo otherwise broke. One copilot gave a regional manager national totals because the warehouse role carried no
selling_orgconstraint. The Monday dashboard that manager normally used would have rejected the query. The agent seemed more capable because it had bypassed the single control that counted. - The copy farm. Three "semantic layers" live in three tools, none of them matching, each presented as the fix for the last.
If two of these sound familiar, chatbot evals shouldn't be the first move. Start by naming the metric and writing its grain into the model, then require the agent to call it, and only after that start measuring.
A quick test for reviews: request last month's revenue, then follow up on the details. Which date column did it pick? What exclusions went in? Were employee purchases counted? An agent that has to invent its answers is a SQL generator that happens to have access.
A second test edits the official exclusion list, for example by finally removing employee purchases from recognized revenue, then checks who updates on the same day. When the dashboard changes and the agent stays put, the real API turns out to be the hand-compiled file, and the agent is only a tourist.
Watch also for the "helpful" agent that quietly adds a join when the certified metric lacks a dimension. That's the moment the contract dies. The correct behavior is to say the dimension isn't on this metric. Product managers dislike that sentence, but it beats a board letter that has to be retracted.
Don't start with the chatbot
Most programs open with the visible tooling: which model, which vector database, whether to speak MCP, which copilot seats to buy. If the goal is dependable answers about one particular company, a blunter starting point works better.
Where does revenue get defined in a form software can invoke? Which customer classification is the authoritative one? Where does gross margin logic sit right now: a SQL view, a DAX measure, a Qlik expression, a notebook, or one person's memory? An answer of "several of those" means the core problem is not generation. The interface that software is about to pound on was never built.
A practical sequence looks like this:
- Find the fight. Choose one metric that two teams already dispute, with no need for a sprawling ontology.
- Write the contract: formula, grain, exclusions, owner and the dimensions that are legal.
- Serve it through a single secured query path that both a report and an agent can use.
- Add a caller, and only at this point put a copilot or agent on top of that metric.
Without a pointer to the official revenue operation, the chatbot is advertising a semantics problem nobody has named.
A team once had to deliver a planning copilot before the next steering committee, and for the first two weeks it deliberately set the copilot aside. The work went into available_to_promise, with its rules written down on one page. Stock under QC hold was left out, and so was the damaged cage. Pack-and-hold quantities counted only for wholesale orders, and in-transit units counted only once the ASN had been received. Those rules went into the model the warehouse job already used, and for inventory questions the copilot was allowed to call that metric and nothing else. The first demo was quieter. By week two nobody carried a third number into standup, which was the win, and the model vendor stayed the same.
Dashboards are staying. A designed visual still conveys a weekly shape better than a chat bubble. They simply stopped being the only destination, and the same context now has to hold up when called by a person, a report, and an automated process that will not sense when a figure looks off.
The interface that already exists
The standup with two official numbers explains the whole thing. The models weren't confused in any interesting sense. The organization was, and the agents copied that confusion into sentences that sounded like knowledge.
When software starts consuming a certified description of how the company views its data, tables cease to be the interface and the named metric replaces them.
A prompt asking the agent to be careful with revenue fades quickly. What lasts is a model that never allows revenue to be rebuilt ad hoc.
Whatever gets published is what the agent will call. Expose raw tables and it makes up a business; expose a named operation and it may at last pick the one analysts already settled.
The flashy caller still has appeal: a copilot that chases an anomaly at 6 a.m., or an agent that opens a ticket when available-to-promise drops through a band. Those stay honest only as clients of the same operations the humans already agreed on.
Begin with the metric that already starts arguments and write the four grain questions against it. Aim one dashboard and one agent at that object, and leave the rest of the lake untouched until the two callers agree for a month. Grow the catalog afterward.