Anthropic published a blog article last week, “How Anthropic enables self-service data analytics with Claude”, describing an approach to one of the hardest problems in enterprise AI: how do you get an AI agent to reliably answer business questions against a complex analytics platform?
The problem will be familiar to anyone who’s connected Claude, Gemini or another LLM to Looker and asked something seemingly straightforward like:
The answers usually come back fast, confident and well-formatted. The difficulty is knowing whether they’re actually correct.
In most organisations, a mature semantic model contains dozens or hundreds of metrics, multiple date definitions, several customer counting methodologies and more than one reasonable way to calculate the same business concept. “Revenue” might mean booked revenue, recognised revenue, net revenue or gross revenue. “Active customer” might mean active in the last 30 days, active in the current billing period or currently subscribed. The AI agent has to select the right definition from several plausible alternatives and, when it gets that choice wrong, there’s often no obvious indication that anything has gone wrong.
A coding agent such as Claude Code can be creative within boundaries defined by tests and documentation. The generated code either passes the tests or it doesn’t, providing an objective feedback loop. An analytics agent has no such luxury. Its job is to arrive at the one correct number and there is no compiler, test suite or runtime error waiting to tell it when the answer is wrong.
In-essence, Anthropic’s blog sets-out three underlying reasons why analytics agents struggle to come-up with consistently-correct answers:
Anthropic’s fix is a four-layer stack, and the bottom two layers will look familiar to anyone who’s built a modern data stack over the past ten years: a foundational data layer of organised, curated, performant datasets and a sources-of-truth semantic layer that the business, via analytics agents and its BI tools, query against.

What is new are the two new layers on top, and they’re built for agents rather than for human analysts.
The data foundation and the source-of-truth layer beneath them shrink the field of plausible answers down until there’s a single governed one left standing. Skills (the agent’s procedural knowledge of which sources to consult, in what order and what a finished answer is supposed to look like) make sure it actually finds and uses that governed answer rather than going off-piste.
Skills are the key ingredient: without them, accuracy in Anthropic’s own testing never got above 21%; with them it sat consistently north of 95%. Contrast this approach with the one we’d been championing recently, providing example queries as ‘golden’ versions, which only added a few percent to accuracy in Anthropic’s own testing.
Validation is how they catch what’s still slipping through; offline evals built from question-answer pairs that flag the critical gaps, ablation experiments that isolate one variable at a time and online checks like adversarial review, provenance footers and harvesting corrections as they come in.
None of these four layers, apart from the naming of the last two, are new on their own. We’ve been building semantic layers, dbt governance, documented query patterns for years, long before anyone came up with the concept of an LLM.
What the Anthropic post does usefully is show how they behave differently when the thing consuming them is an agent rather than a human analyst who can lean on their own judgement to paper over the gaps.
Taking the concepts in the Agentic Analytics Stack and applying them to the architecture and data flow of a BigQuery, Looker and dbt modern data stack and you get something like the diagram below:

Data foundations. At the data warehouse and semantic model layer, the cure for concept-entity ambiguity is to have fewer tables, with better governance and control of sprawl, naming and duplication of names and concepts. If total_revenue resolves to one source instead of eight, most of the retrieval errors and hallucinations simply have nowhere to come from. In-practice that means deprecating near-duplicate models aggressively, dimensional modelling with the grain written down explicitly and clear and unambiguious labelling in-terms an LLM can easily-understand.
Sources of truth. If you’re a Looker customer this layer already exists in that your LookML explores define the metrics, dimensions and joins in one place. The challenge is to make the agent route through them rather than around them, so that if a metric is defined in a LookML explore, the agent calls that definition instead of hand-rolling SQL against the raw tables. That single bit of discipline rules-out the largest category of wrong answers we see.

Domain skills. This is the bit that’s most directly liftable into our own work. Anthropic’s design approach writes a structured Markdown reference file per domain (which tables live there, what they mean, how they join, what the common questions look like) and crucially keeps those files in the same repository as the dbt code that builds the tables; doing that turns documentation from a separate workstream that’s permanently three months out of date into a code-review discipline.
The knowledge skill then works as a router, narrowing the warehouse from hundreds of tables to the dozen that matter for a given question and the query-workflow skill encodes the steps, clarifying what’s being asked, checking the semantic layer, reading the domain reference, running it and sending it to adversarial review along with the provenance footer.
Validation. The adversarial review sub-agent (a second Claude instance whose entire job is to challenge the answer before it reaches you) bought in Anthropic’s testing a 6% accuracy gain, at 32% more tokens and 72% more latency. Whether that trade is worth making is a per-client call, not a universal one.
And arguably even more important is the offline eval harness: question-answer pairs per domain, run against every schema change through CI (continuous integration) test. Skip this and accuracy starts to drift, with Anthropic seeing theirs fall from 95% to 65% inside a month with no active maintenance.
Provenance footers (the source tier the number came from, how fresh the data is, who owns the domain) don’t fix a wrong answer, but they let whoever’s reading it judge how far to trust it. A figure that came out of a governed LookML measure is a very different animal from one an agent improvised in SQL against an undocumented table, and the footer is what tells them which one they’re looking at.
So how might this work in-practice? Let’s consider an example of a mid-size online fashion retailer, six years old, with their data platform built-on Snowflake with data loaded using Fivetran, modelled in dbt and surfaced in Looker.
Their data warehouse got built incrementally, over many years and with a table added whenever somebody needed one. Metrics were defined team-by-team with little central co-ordination and no real way for users to understand which table to use and when.
Their experience so far with connecting analytics agents such as Claude to Looker via it’s new MCP Server was that answers were correct around a third of the time; not because Claude couldn’t add-up or understand the question, but because their warehouse and semantic layer structure gave it too many ways to get the answer wrong.
Wire is our structured delivery system for data platform engagements, built on top of Claude Code and Gemini CLI and outlined previously in our blog “Introducing the Wire Framework: The “Secret Sauce” Behind Our AI-Augmented Analytics Project Delivery”
Installed as a plugin for Gemini CLI or, as shown below, for Claude Code:
/plugin marketplace add rittmananalytics/wire-plugin
/plugin install wire@rittman-analytics
/reload-plugins
it encodes analytics engineering methodology as workflow specifications and covers the full delivery lifecycle from Discovery (Shape Up + RA Canonical SOP), Requirements, Design, Development, Testing, Deployment, Enablement, Platform Migration and now, Anthropic’s Agentic Data Stack. Wire is being used today by our team and our clients’ data teams to accelerate and extend their analytics development capability and you can read more about it using the two links below:
A new type of project (or “release”) that this toolkit supports is Anthropic’s Agentic Analytics Stack, implementing its four-layer architecture more or less directly: governed canonical datasets, per-domain knowledge files sitting next to the dbt models, LookML-first routing through the Looker MCP server, adversarial review on every answer and an offline eval harness wired into CI.
In this example we’d start first with the goals and scope for this agentic analytics layer implementation, written-down as a markdown document stored in the same github repo that contains the dbt and LookML code for their data platform.
# Statement of Work
## Background
UK online fashion retailer on Snowflake + dbt + Looker. ~180 tables in the production analytics schema, built incrementally since 2019. A first Claude SQL agent managed ~35% accuracy on commercial questions. Root cause is governance: multiple revenue definitions across views, no canonical customer metric, at least two broken models still referenced by dashboards.
## Objectives
1. Audit the dataset for governance maturity — duplicates, naming, ambiguity.
2. Design a canonical model: one table per entity, deprecation schedule, tiering policy.
3. Add canonical LookML measures per domain, grouped under Canonical Metrics.
4. Generate per-domain DOMAIN_REFERENCE.md files colocated with the dbt marts.
5. Deliver an installable Wire skill: LookML-first routing via Looker MCP, provenance on every answer.
6. Build an eval suite — 10+ Q&A pairs per domain, CI runner, per-domain thresholds.
## Scope
In scope — five domains and their primary tables:
Revenue orders_fact, order_items_fact, returns_fact, refunds_fact
Marketing campaign_spend_fact, campaign_performance_fact, attribution_fact
Customer customers_dim, customer_orders_fact, customer_lifetime_value_fact
Inventory products_dim, inventory_snapshots_fact, stock_movements_fact
Operations fulfilment_fact, carrier_performance_fact, zendesk_tickets_fact
Out of scope: finance/ERP integration, wholesale channel, B2B accounts (Phase 2).
## Success criteria
- Aggregate accuracy >= 85% at launch against agreed question set
- Core commercial questions answered without analyst involvement
- Eval suite in CI catching regressions within 24 hours
## Warehouse
Snowflake (production database: ANALYTICS), ~180 tables across 4 schemas, dbt Core on Snowflake adapter, Looker as BI tool with existing LookML project.
## Release type
Use the Wire Framework agentic_data_stack release type lifecycle.
The agentic_data_stack release type runs a fixed five-phase workflow, designed to be run over an existing data platform implementation:

Phase 1: Audit. Three parallel audits are run on that existing data platform: dataset (governance grade, duplication groups, deprecation candidates), metric (existing definitions, conflicts, coverage gaps) and query (stakeholder question patterns, LookML explore coverage). Output is three reports and a prioritised findings list, with a human review gate before design begins.
Phase 2: Design. Two design artefacts: a governance design (canonical table designations, deprecation schedule with sunset timelines, tiering policy) and a semantic layer design (metric specifications, calculation definitions, grain documentation, conflict resolutions), and another human review gate before build begins.
Phase 3: Build. Five sequential workstreams: canonical dbt model updates, LookML view and explore updates, per-domain DOMAIN_REFERENCE.md files colocated with the dbt marts, and an installable SKILL.md encoding the three-tier routing rule and hard-refusal list. Again, there’s a human review gate on each workstream before proceeding.
Phase 4: Validation. An eval suite (question-answer pairs per domain with a CI runner and per-domain accuracy thresholds) and adversarial review configuration. And another human review gate before the launch gate runs.
Phase 5: Launch gate. Running of per-domain accuracy validation against the SOW thresholds, where domains that clear proceed and domains that don’t loop back to the relevant build workstream are identified for a targeted fix.
For demo purposes, we run the first two phases using Autopilot, an experimental feature in Wire that lets the agent take decisions that would normally be human-in-the-loop.
/wire:autopilot docs/sow.md
The audit phase would then grade the dataset and surface duplication groups. In a warehouse of this age and history, the sort of things we’d expect it to highlight might include:
marketing_attribution_fact references session_touchpoints_fact, which got renamed to web_sessions_fact eight months ago during a dbt refactor. The model builds and returns no rows. Ask anything about marketing attribution and you get back a confident zero. It goes onto the hard-refusal list immediately, no sunset window, and the dbt fix goes into the next sprint.orders_fact versus orders_mart. Both cover the same order history, but orders_mart was built eighteen months ago to add return adjustments and has drifted since — it applies return deductions at the order level rather than the line-item level, so any question about revenue by product category produces different numbers depending on which table you happen to hit. orders_fact with the correct join to returns_fact is canonical; orders_mart goes on the 90-day sunset list.customer_lifetime_value_fact and clv_summary both exist, built by different analysts at different times, on different lookback windows and different return-adjustment logic. Neither is documented. The audit flags both for consolidation under a single canonical definition, to be settled in the design phase.Running Wire in Autopilot mode generates the governance design, proposes canonical table designations, deprecation timelines and metric definitions for all five domains and surfaces it at the review gate. And this is the point where letting Wire run in autonomous mode has to stop.
The governance design resolves the metric conflicts the audit found using technical signals such as table age, column coverage, row-count consistency. That’s correct as far as it goes, but the metric definitions it proposes encode business decisions, and those need a different kind of input before any build begins.
Take revenue, for example. The governance design can spot that four definitions exist and propose consolidating around one but what it can’t do is work out which one matches what the finance director actually signs off at month-end; whether recognised revenue excludes pending returns, how cancelled orders are treated, whether gift-card redemptions are in or out. That filter is a conversation with finance, not something the audit can derive on its own.
So before we go past the design review gate, we’d run a working session with the domain owners:
The output is a set of specific annotations against the governance design document with filter values confirmed, definitions pinned down and every conflict resolved with a named business owner against the decision. Only then would we run the command to record that review and its findings, the step before we start the build.
/wire:governance-design-review
This matters because of how the adversarial review layer works downstream. The adversarial sub-agent (a second Claude instance challenging every answer before it reaches the user) is a routing guard, and a good one.

It checks whether the answer used the correct source tier, steered clear of refused tables, applied the filters the domain reference says are required, and produced a number that’s at least plausible. What it can’t check is whether the filter value itself is right. Encode gross revenue where the business means recognised revenue, and the adversarial agent will happily wave through wrong answers as correctly sourced. Getting those definitions right at the review gate is the thing that makes the adversarial layer worth having later.
With the governance design confirmed by the domain owners, we’d come out of autopilot and run the build commands individually — each artefact reviewed before the next one gets generated, with the canonical models first:
/wire:canonical-models-generate northbrook-fashion
/wire:canonical-models-validate northbrook-fashion
/wire:canonical-models-review northbrook-fashion
The validate step runs dbt tests against the canonical model changes, while the review step presents the deprecation plan as concrete commitments: orders_mart gets 90 days; marketing_attribution_fact goes straight onto the hard-refusal list with no sunset window, because a silent zero-row return is worse than an honest error — at least an error stops someone acting on a made-up number.
Now we generate the LookML canonical measures, one workstream per domain, updating the existing view files and adding a Canonical Metrics field group to each:
/wire:lookml-views-generate northbrook-fashion
/wire:lookml-views-validate northbrook-fashion
Before reviewing, we’d check the generated LookML back against the domain-owner session notes. For Revenue: does net_revenue_gbp exclude returns at line-item level, using the logic the finance director signed off? For Customer: does active_customers_30d use the purchase-based definition rather than any old site visit? The LookML validator catches broken field references; the session notes catch wrong business logic. A correction at this stage is a one-line LookML edit, while the same correction after the eval suite is in CI costs you a sprint.
Domain reference files and agent configuration then get generated together:
/wire:knowledge-skill-generate northbrook-fashion
/wire:agent-config-generate northbrook-fashion
This generates five DOMAIN_REFERENCE.md files, the documents the agent reads before answering any question in a domain; for example:
# Domain Reference: Revenue & Finance
**Domain owner**: Head of Finance (CFO sign-off)
**Last reviewed**: 2026-05-14
**Mart table**: `northbrook_fashion.marts.fct_revenue`
**LookML explore**: `revenue` (Looker instance: northbrook.looker.com)
**Refresh cadence**: daily at 06:00 UTC (Shopify → Snowflake via Fivetran)
---
## Canonical measure definitions
### Net revenue
Gross order value minus refunds, minus discount codes applied at checkout.
VAT excluded. Currency: GBP.
**Required filter**: `order_status NOT IN ('cancelled', 'pending_payment')`
# Domain Reference: Revenue & Finance
**Domain owner**: Head of Finance (CFO sign-off)
**Last reviewed**: 2026-05-14
**Mart table**: `northbrook_fashion.marts.fct_revenue`
**LookML explore**: `revenue` (Looker instance: northbrook.looker.com)
**Refresh cadence**: daily at 06:00 UTC (Shopify → Snowflake via Fivetran)
---
## Canonical measure definitions
### Net revenue
Gross order value minus refunds, minus discount codes applied at checkout.
VAT excluded. Currency: GBP.
**Required filter**: `order_status NOT IN ('cancelled', 'pending_payment')`
**Required filter**: `is_test_order = false`
**LookML measure**: `revenue.net_revenue_gbp`
**Grain**: one row per order line item
Do not use `gross_revenue` unless the question explicitly asks for revenue before
refunds. When a user asks "what is our revenue", always return net revenue.
### Recognised revenue
Revenue recognised on the date of dispatch, not date of order placement.
For orders placed before month-end but dispatched after, recognised revenue
falls in the following month.
**LookML measure**: `revenue.recognised_revenue_gbp`
**Date field to use**: `dispatched_at`, not `created_at`
If a user asks for "revenue in March" they almost always mean recognised revenue
for March. Clarify if ambiguous.
### Refund rate
Refunds as a percentage of gross revenue in the same period.
Numerator: `refund_amount_gbp`. Denominator: `gross_revenue_gbp`.
Do not use net revenue as the denominator — this inflates the rate.
---
## Terms the business uses and what they mean in the data
| Business term | Maps to | Notes |
|---|---|---|
| "Revenue" | `net_revenue_gbp` | Always net of refunds and discounts |
| "Sales" | `gross_revenue_gbp` | Gross — before refunds |
| "Returns" | `refund_amount_gbp` | Includes partial refunds |
| "Discount" | `discount_code_amount_gbp` | Checkout codes only — excludes sale pricing |
| "Sale pricing" | not in warehouse | Markdown from RRP is in Shopify product data, not in this mart |
| "AOV" | `net_revenue_gbp / order_count` | Always calculated from net revenue |
---
## Data not in the warehouse
| Metric | Where to find it | Notes |
|---|---|---|
| Revenue targets / budget | Google Sheets (Finance team) | Not ingested. Direct users there. |
| Contribution margin | Not calculated | COGS not available in Snowflake |
| Revenue by channel (paid vs organic) | `fct_orders` joined to `dim_attribution` | Not in this mart — use the marketing domain reference |
If a user asks whether revenue is tracking to target, tell them targets are not
in the warehouse and direct them to the Finance team's Google Sheet.
---
## Ambiguous fields — handle with care
`dispatched_at`. Only use `created_at` if the user explicitly asks for orders
placed in a period.
**International orders**: this mart includes all geographies. GBP conversion
uses the exchange rate at order placement (Shopify rate). If a user asks for
EUR or USD figures, tell them the mart stores GBP only.
**Marketplace orders**: Northbrook sells a small volume via ASOS Marketplace.
These orders are not in Shopify and are not in this mart. If revenue figures
seem low relative to expectations, check whether the user is expecting
marketplace revenue to be included.
---
## Routing rules for this domain
1. Always try the `revenue` LookML explore first via the Looker MCP server.
The explore pre-applies the `is_test_order = false` and order status filters.
2. If the explore cannot answer the question (e.g. requires a join not modelled
in LookML), fall back to `marts.fct_revenue`. Apply the required filters
manually — they are not automatic at the mart layer.
3. Do not query `raw.shopify_orders` or `raw.shopify_order_lines` directly.
These tables are on the hard-refusal list for revenue questions — they
include test orders, cancelled orders, and contain gross amounts only.
---
## Adversarial review checklist for this domain
Before returning any revenue figure, the adversarial sub-agent should verify:
- [ ] `is_test_order = false` filter applied (or Looker explore used, which applies it)
- [ ] `order_status` filter excludes cancelled and pending payment orders
- [ ] Date field used is `dispatched_at` unless question specifies order placement date
- [ ] Currency is GBP (or conversion caveat included)
- [ ] Refunds are excluded from "revenue" responses (net, not gross)
---
The key design choices worth noting for the article context: the "data not in the warehouse" section is what makes the adversarial review meaningful for questions like "are we tracking to target?" — without it, the agent would
either hallucinate a target or return a confusing partial answer. The routing rules section tells the agent exactly which table is on the hard-refusal list and why, so adversarial review has a concrete rule to check against
rather than having to infer it from schema inspection.
We’d send each one to its domain owner for a quick read-through before running the review command; not a forensic audit, just a sanity check that the canonical explore names, the measure descriptions and the common question patterns match what they actually know to be true.
The adversarial sub-agent reads these same files when it’s challenging answers, so if the Revenue reference encodes the wrong return-adjustment logic, the adversarial agent takes that wrong logic as its benchmark and the failure stays invisible until it surfaces in production, which is the worst possible place to find it.
/wire:eval-suite-generate northbrook-fashion
Autopilot generated question-answer pairs across the five domains, but the trouble is that those answers are derived from the same LookML definitions generated back in Phase 3. This means in-practice that what the eval suite tests, as generated by this Autopilot run, is whether the agent answers consistently with its own definitions and not whether those definitions are right against business reality. It can be perfectly self-consistent and uniformly wrong.
So before running /wire:eval-suite-review, we’d ask the domain owners to contribute some anchor questions, three to five per domain where they know the right answer from a source they trust independently of the warehouse; for example:
These become fixed points in the harness, questions the agent has to get right against external ground truth, not just against itself. The generated questions fill out the coverage; the anchor questions are the actual benchmark.
We’d expect this to take an afternoon across all five domains. The domain owners are answering questions they already know the answers to and writing them down — not reviewing YAML or writing SQL. That’s the whole point; it’s their afternoon, not the data team’s week.
The Wire Framework’s Analytic Analytics Data Stack workflow then builds the parts an agent needs: canonical dbt tables with deprecation dates, LookML explores updated with the confirmed canonical measures grouped under “Canonical Metrics”, and DOMAIN_REFERENCE.md files reviewed by the domain owners.
An installable SKILL.md then enforces three-tier routing where LookML explores via the Looker MCP first, canonical dbt marts when finer grain is needed and hard-refusal list for the tables that must never be queried.

Following a handful of representative questions through the skill shows what all that governance work actually delivers for your organisation; for example:
orders_mart and apply return deductions at order level, producing a number that doesn’t tie to the finance close, or it might go to orders_fact directly and pick up gross rather than net. With the skill: the first call goes to the net_revenue_gbp measure in the Revenue LookML explore, line-item return logic confirmed by finance, and orders_mart is on the never-query list.active_customers_30d measure, defined as customers with a completed purchase in the last 30 days, sourced from customers_dim, with the exact lookback window signed off by the commercial team.marketing_attribution_fact is on the hard-refusal list, so the skill returns an explicit error and explains the model is broken pending a dbt fix — rather than the confident zero that looks for all the world like a real answer.net_revenue_gbp from the Revenue explore and active_customers_30d from the Customer explore as two scoped calls and divides them in the agent layer. It doesn’t go off and attempt a speculative join across the domain boundary.return_rate_by_category measure in the Revenue explore, which joins returns_fact to order_items_fact and products_dim at line-item grain. Without that canonical measure, an agent hand-rolling the SQL would join at order level as often as not and produce category figures that are quietly wrong.Two things sit underneath every one of those answers.
Data sources: [Tier 1 / LookML] Revenue explore — net_revenue_gbp
Filters: order_status = 'complete'; return_status != 'pending'; period March 2026
Caveat: excludes orders with open return requests (counted on resolution)
Freshness: orders_fact max(order_date) = 2026-06-06
The launch gate stage then runs the eval suite domain by domain and reports accuracy against the SOW thresholds.
/wire:launch-gate-validate northbrook-fashion
From running this process, all five domains will tend to clear 85% once the governance working sessions and the anchor questions are in place, though the first pass without those steps will almost always throw up at least one domain with a definition gap that needs a targeted fix before it clears. A typical result looks like this:
| Domain | Pass rate | Threshold |
|---|---|---|
| Customer | 91% | 85% |
| Revenue | 90% | 85% |
| Operations | 88% | 85% |
| Marketing | 87% | 85% |
| Inventory | 86% | 85% |
Marketing at 87% is where we’d look first; Attribution questions have a habit of surfacing edge cases in the filter logic — whether brand spend is separated out from performance spend, whether view-through conversions are counted — that the working session ought to have nailed down but doesn’t always cover exhaustively. This would be a LookML measure correction rather than a data problem, and it goes into the follow-up sprint rather than blocking the gate.
The overall shape of it though is from roughly a third of questions coming back usable to the high eighties across five domains, and the key takeaway is that almost all of the calendar time sits in the domain working sessions and the anchor-question review, not in the automated build steps.
The mechanical work, through in our case our Wire Framework plugin to Claude Code, is fast and getting faster. The governance conversations are where the time goes, and they’re also where the accuracy comes from, and you can’t buy your way out of them with a better model.
The Anthropic post is well worth reading in full. The most valuable thing in it isn’t the architecture diagram, useful as that is; it’s the evidence that, for this class of problem at least, you get a far better return on money spent governing your data than on money spent chasing a cleverer model.
Get yourself down to one revenue table and you’ll beat any amount of prompt engineering and if you’re starting from forty, no prompt on earth is going to save you. The agentic_data_stack release type is available in Wire Framework from v3.7.5; run /wire:new, select “Agentic Data Stack”, and you’re off.
Rittman Analytics is a boutique data analytics consultancy that helps ambitious, digital-native businesses scale-up their approach to data, analytics and generative AI.
We’re authorised delivery partners for Google Cloud along with Oracle, Segment, Cube, Dagster, Preset, dbt Labs and Fivetran and are experts at helping you design an analytics solution that’s right for your organisation’s needs, use-cases and budget and working with you and your data team to successfully implement it.
If you’re looking for some help and assistance with your AI initiative or would just like to talk shop and share ideas and thoughts on what’s going on in your organisation and the wider data analytics world, contact us now to organise a 100%-free, no-obligation call — we’d love to hear from you!