Kelly Wade · Director of Analytics, Dutch imkellywade@gmail.com LinkedIn

Building an AI data analyst the whole company trusts

Anyone at Dutch asks a data question in Slack and gets a governed answer in seconds. Making those answers trustworthy enough for the whole company to run on took real work, and it is what this case study covers.

A Slack exchange: someone asks Dash how last month's refund rate looked, Dash shows a working state, then replies with the governed monthly rate and a link to explore it in the Sigma dashboard.
Sanitized exchange; figure is illustrative.
90%company-wide adoption
secondsto a governed answer
nightlyregressions caught before users
~70governed metrics, one definition
1 daymissing metric to live & governed
The story

From a growing queue to answers the whole company trusts

The queue

Every data question funneled to a small BI team.

A wrong answer

The bot answered confidently, and was quietly wrong.

Controls

Each failure became a permanent, automated control.

Evals

A nightly suite grades every answer against truth.

Trust

The whole company asks directly, and relies on it.

See it in Slack

What talking to Dash looks like

The answer up top is the simple case. Two harder ones matter more: following up in context, and what happens when the bot doesn't know.

A Slack thread where a person asks Dash how many vet calls happen after hours, gets an answer, then follows up asking about the last 12 months, and Dash answers the narrower question while keeping the earlier context.
Follow up like a conversation. It holds the earlier context and narrows the answer.
In the dash-data-alert channel, Dash posts a Potential data gap for a question it could not answer. The data team replies resolved, Dash confirms the original asker was notified, then messages the asker that the metric now has a validated definition.
The whole loop closes. Dash flags what it can't answer, the data team resolves it, and Dash tells the original asker the metric now exists.

Sanitized exchanges; figures are illustrative.

The problem

Centralized BI, growing queue

Dutch runs centralized BI on purpose. End users don't write SQL, and my team governs every data model, so definitions stay consistent across the company. The tradeoff is a queue, because every question about revenue, consults, refunds or retention landed on a small team, and the queue grew faster than we did.

An AI analyst was the obvious answer and the obvious risk. A wrong dashboard looks broken, but a wrong AI answer looks exactly like a right one, delivered with confidence to someone who has no way to check it.

The architecture

Everything lives in the warehouse

The Slack bot is a thin webhook with no intelligence of its own. The answering logic sits inside Snowflake, next to the data, in two governed layers built with dbt.

askSlack

a person asks a question in plain English

routeCortex Agent

routes to 1 of 8 semantic views, one per business domain

defineSemantic views

synonyms, metric definitions and verified queries the AI must use

computeMetric views

~70 dbt models, one recipe per metric, shared with every dashboard

Maturation

Every failure became a guardrail

Early on I spot-checked one answer against finance. It came back confidently, plausibly wrong.

"I only caught it because I happened to look. Nothing in our process would have caught it otherwise."

The system got sturdier every month. Each failure mode surfaced once, then closed for good.

IncidentControl
month 1
A dbt rebuild silently broke 6 of 8 semantic viewsShared lineage now breaks the PR; a contract check guards 59 models, 517 columns
month 2
A renamed event value made a booking metric read zero for weeksDead-metric tests fire when activity exists upstream but the metric reads nothing
month 2
A blank-SKU join inflated cost of goods past revenueReconciliation tests tie outputs to finance numbers and fail the build
month 3
"Last 30 days" shifted by a day, a quiet 2% error on spendVerified queries fix the window in SQL, so a rewording can't move it
month 4
Latest-month refund rate read the partial month and came back too lowThe nightly suite caught it; "latest month" is pinned to complete months
Evaluation

Evals are the product

Underneath all of it is a three-tier evaluation stack, every check graded against truth SQL written independently of the AI layer. The eval code gets the same review and fix discipline as anything else in production, because it needed it. The grader once failed a correct answer over a "$3.14M" it read as 3.14.

Nightly accuracy regression06:30 PT
active_membersexact match
consults_completed_yesterday
refund_rate_latest_month
blended_cac_30d0.3% from truth
gross_profit_last_month
dash said "$3.14M". the parser read 3.14. the bot was right, our eval was wrong. we treat the eval as production code too.
10 / 11 passedfix shipped by 9am
SV Drift Audit #98 Success · 5m 48s
sv-drift-audit.yml · on: schedule
Audit SV column references against live schema1m 0s
Verified queries reference only declared columns19s
Health-check base views (empty / slow / error)3m 24s
Accuracy regression (ask Dash, compare to truth)5m 44s
Reconcile grants.sql with SV references and live grants37s
This suite runs every night on a schedule in CI. Recreated from a real production run; no internal data shown.
Nightly 11

Golden questions

Asked in natural language, graded against independent truth SQL within tight tolerances. Failures page Slack before the workday starts.

Weekly 33

Paraphrases

The same metrics in alternate wordings real users typed. The model is stochastic, so a wording fails only on two consecutive misses. A failing wording becomes a verified query, permanently pinned.

Monthly ~

Sampled real traffic

A standing audit pulls real questions from the usage log each month and verifies the answers. The nightly and weekly checks use questions we wrote. This one uses the questions people actually asked. Weak spots turn into new verified queries.

Safeguards

Built to be trusted, not just fast

On top of the guardrails from those incidents and the nightly evals, a fixed set of protections ships with every answer.

PII guardrail

Never returns names, emails or anything that identifies a person.

Cited sources

Every answer names the table or view the number came from.

Freshness

Answers about today carry an "as of" timestamp and a note on the pipeline.

Accuracy alerts

When someone flags a wrong answer, the data team is notified in #dash-data-alert automatically.

Query timeout

A hard timeout stops a runaway query from tying up the warehouse.

Stays in its lane

Off-topic questions get a polite redirect instead of a guess.

Deployment

Every change ships as a pull request

The AI layer is configuration, and configuration drifts when humans paste it into consoles. We moved the semantic views into dbt, wired a deploy-on-merge workflow, and retired the console path. Git equals live by construction, and review happens on a pull request like any other code change.

The new path proved itself on day one by refusing to deploy over a permission gap a manual deploy would have hidden for weeks.

Before

edit SQL in a console editor

paste, save, hope git stays current

drift audited after the fact, nightly

After

open a pull request

merge = deploy, via CI as a service role

drift structurally impossible

Extending it

New questions, same spine

Once the governance held, we could add new kinds of questions without loosening any of it. Each new ability rides the same layers, tests and deploy path.

Find the right view

Dashboard discovery

People kept asking which dashboard a number lives on. The bot now searches across 110 production dashboards and returns the best matches, each with a one-line note on what it covers and a link to open it.

Beyond the finance metrics

Content engagement

We pulled product-analytics events into the governed layer, so the bot answers how content performs by audience. It is tested and deployed exactly like every other metric.

Results

What changed

Lessons

What I'd tell another data leader