Skip to main content

Modelling with Axi

Who can do this

Administrators with the Semantic models permission, on a plan that includes Axi.

Everything you can do by hand in Settings → Workspace → Data — write a table's description, name its columns, declare a primary or foreign key, add a measure, hide the columns your loader added — you can also ask Axi to do, from inside the section.

Axi never saves anything. Its changes land in the same unsaved draft your own typing goes into: every row it changed carries a blue dot in the column list, so does its table in the tree, and Save model stays the one thing that writes to the semantic layer. Until you press it, nothing any chart, widget or answer uses has changed.

Because nothing is saved until you say so, Axi does not ask before it acts. Ask for a change and it is in your draft by the end of the answer, with what Axi assumed in one sentence — review it, correct it, or take the whole batch back out with Discard. It only asks first when your request reads two ways, when the change would replace text you wrote yourself, or when your data contradicts what you asked.

Opening Axi​

Open a connection's Tables, Metrics or Relationships section and use Ask Axi at the top of the left rail, or press Cmd/Ctrl + K. The window docks at the bottom right of the screen: the rail — its table tree and its Save button — and the column list stay visible and clickable while you talk, so you can watch the draft change.

Esc closes it. Closing it does not stop Axi: a request still in progress finishes, and its changes still land in your draft. The conversation follows you between tables, metrics and relationships, and there is one conversation history per connection — the New and history buttons are in the window's header.

Axi always knows which table you have open and which column you have selected; the chips above the message box say so. It also sees your unsaved edits, so it answers about what is on your screen rather than about what is stored.

Beside those chips is a picker that says what Axi is pointed at: This table, or Whole connection. See Describing a whole connection.

What Axi can propose​

Ask in your own words. For example:

  • "Describe this table and its columns" — a description for the table, and display names for the columns that have none.
  • "Hide the technical columns" — the loader's bookkeeping columns (_fivetran_synced, _airbyte_ab_id, payload_raw) hidden from field pickers.
  • "Add a measure summing amount" — a new measure, named the way the rest of the model is named.
  • "Revenue should only count paid orders" — the business rule baked into the measure itself; see below.
  • "Create a conversion rate metric" — a metric, checked against the engine before it is offered to you.
  • "country holds ISO country codes" — the geography tag that lets a column be drawn on a map.
  • "Mark created_at as the main date" — the model's primary time axis.
  • "id is the primary key, and customer_id points at customers" — keys, including the reference that makes the join work. Axi checks a key on your data before declaring it; see below.
  • "order_tags is a bridge table" — a key that joins in both directions, so the table it points at can group by this one's fields too.

The descriptions and display names you save from here don't stay in this section: they are what Axi reads when it answers questions on a dashboard, in View mode and on Home, to match your team's words to your tables and columns — see Why it matters for Axi.

Two things Axi will not do. It will not change anything imported from dbt — that definition belongs in your dbt project, and Axi says so instead. And it will not replace text you wrote yourself: a description you typed is left alone unless you ask again explicitly.

The main date and the map​

Two settings decide what a table can do in a chart, and they are the easiest to forget:

  • The main date — the primary time axis. Trends, date filters, by month and period comparisons on the table all follow it; a table with dates but no main one cannot be charted over time without someone picking a date each time.
  • The geography of a column naming countries, US states, French regions or départements. Without it, no map can draw the column.

Axi looks for both whenever it describes or sets up a table, and puts them in the same batch: it picks the date that says when each row happened — the order date, not the date the row was loaded — and reads the values of the columns that look geographic before tagging them with the identifier they actually use (an ISO code, a name, an INSEE code…). A column whose values are not on a map, such as sales regions, is left untagged. When the table you have open is missing either, the empty Axi window suggests it: Set this table's main date, Tag the geographic columns.

To work through a whole connection rather than one table, see below.

Measures and metrics​

A measure says how a column aggregates. Axi can write the parts of one that are easy to get wrong and easy to forget:

  • Conditions — "revenue counts paid orders only". The rule lives on the measure, so every chart, widget and answer built on it agrees, and nobody has to remember to add a filter.
  • The date it is counted by — a measure is dated by its table's main date unless you say otherwise. "Count shipped revenue by ship date, not order date" pins it to another column.
  • Never summing across time — a balance or a stock level is not the sum of its months. Ask for the last value of each period instead, optionally per account or per store.

A metric is a business definition — revenue, conversion rate, revenue MTD — and Axi will propose one when you ask. Say what you want it to mean: "a conversion rate: purchases over visits", "revenue month to date", "margin as (revenue − cost) / revenue", "revenue against the same month last year".

Two things happen before a metric reaches your draft:

  • It is compiled before it lands. The definition goes through the same check the Metrics page shows you — does it plan, which tables can it then be grouped by, along which date. A metric that does not compile never reaches your draft: it comes back on the card as a skipped row saying so, the rest of the batch still lands, and Axi corrects the definition.
  • It is filed where it belongs. A metric is stored on the table owning its measure, and Axi puts it there whatever table you had open. The card names the table to save.

A metric Axi adds is in your draft at once, like any other change: it is listed in the Metrics section with its blue dot, the Metrics entry of the rail counts it and carries the dot too, and the page of the table it is stored on lists it under its name. Show on the card opens it.

Axi writes the measure and the metric in the same breath, in that order — a metric can only read a measure that exists, and the measure does not need saving first. Ask for "revenue" and Axi creates it with the obvious definition, then says what it assumed — paid orders only, tax excluded — so you can correct it, rather than asking you first.

Metrics imported from dbt stay read-only, like everything else dbt owns.

Describing a whole connection​

A connection nobody has modelled yet is the case Axi is most useful for, and it has its own way in. Open Axi without asking anything and, when most of the connection is still bare, the first suggestions are about the connection rather than the table in front of you:

  • Set up this connection — 3 of 42 tables have a description · 5 have keys
  • Describe the 39 tables with no description
  • Set up the keys of this connection — 12 columns look like foreign keys and join to nothing yet

All three start a sweep. Axi looks up which tables are still missing a description, a primary key, a main date or a foreign key, reads their columns, and works through them five at a time, in name order — one proposal card per table, which also tags the geographic columns it finds along the way. It then tells you what is left; the last card of the batch carries a Continue button that takes the next five, picking up after the last table of the batch, and typing "continue" does the same.

It does not repeat itself. A table it described a moment ago is no longer missing a description, even though you have not saved yet — Axi sees your draft, so the next batch is the next tables. A table a batch could not finish — no column your data shows to be unique, say, or a key its values don't support — isn't offered again at the head of every batch either: the sweep moves past it, and once it is done Axi names what is still missing and why. What the warehouse already declares counts too: a table it describes, or a key it declares, is not a gap — and Axi doesn't write over a column description that came from the warehouse.

Pointing Axi at the connection​

The picker above the message box normally reads This table, and Axi answers about whatever the page has open. Switch it to Whole connection and Axi stops looking at that table altogether: the chips collapse to the connection's name, the suggestions become the connection-wide ones above, and a request like "set the foreign keys on this connection" is answered by sweeping the tables that need them rather than the one you happen to be standing on.

The choice stays where you put it while you move between tables, metrics and relationships — a sweep that re-narrowed itself the moment you clicked Show on a proposal would be no use. It goes back to This table when you switch connections.

Foreign keys are a gap like any other. A table can have a description and a primary key and still join to nothing, because its customer_id and store_id columns were never declared. Axi counts those as missing and sweeps them with the rest — checked on your data first, as always.

Descriptions it writes for a sweep follow the ones already on the connection: same language, same register. If there are none yet, it uses the language you are writing to it in.

Keys cost queries

A sweep that declares keys checks each candidate on your data, and the budget is around twenty checks per message. On BigQuery those are billed column scans, each capped at 10 GB; on Snowflake they run on the warehouse and bill its time — if that matters, sweep descriptions first and ask for keys a few tables at a time.

On a large connection​

On a connection with hundreds of tables, Axi still knows every one of them. It sees the table you have open in full and every other table in one line — on a very large connection, the less relevant ones only by what they are missing, or counted per dataset. It finds the rest by what they hold: ask about the table of customer reviews and it searches the names, display names and descriptions of your tables and columns. It can also find which column holds a value you name ("where are the Lyon stores?"), and check how two tables join — or why they don't — the way every chart will join them.

A very wide table is shown with its most useful columns first: the one you selected, the dates, the places, the keys and the ones your team described. Axi reads the others by name when it needs them.

Saving several tables at once​

A sweep leaves unsaved changes on tables you never opened, so the left rail grows a Save 5 tables button at its foot. It counts every table with unsaved changes, wherever they came from — Axi's proposals and your own typing alike — and it is there in Tables, Metrics and Relationships, because a draft does not belong to the page you happen to be on.

A batch is not all-or-nothing. Each table is saved on its own, so if one of them was changed by someone else while you worked, the others are still saved and Axiome names the one that was not. Reload that table to see the current version, then ask again.

Keys are checked before they are declared​

A wrong description is a bad sentence. A wrong foreign key silently corrupts every join built on it — so Axi does not declare a key on the strength of its name. Before it proposes one, it reads your data:

  • For a primary key, it samples up to 50,000 rows of the column and counts how many values it saw, how many were different, and how many were empty. A key whose values repeat, or that is sometimes empty, is not a key.
  • For a foreign key, it takes up to 10,000 distinct values of the column and counts how many of them exist in the column it would point at. Anything short of all of them means the join would quietly drop rows.

You can watch it happen: while Axi works, each read it finishes is listed in the window — Sampled 50,000 rows of orders, Checking customer_id against customers.id… — so a long turn is never a silent one. See Meet Axi.

Those numbers appear under the change on the proposal card, and again in the inspector — 4,812 of 4,812 sampled rows distinct, 9,998 of 10,000 sampled values found in customers.id. They come from the query, not from Axi: it reads them to decide, and cannot round them or make them up.

They are samples, not proofs. A column that looks unique across 50,000 rows can still repeat in the ten millionth, so read them as strong evidence and keep the judgement yours.

A foreign key can point at a primary key that is only in your draft. When the table it points at has no primary key yet, Axi checks that one too and declares both in the same batch, the primary key first — there is nothing to save in between.

A key Axi has not checked comes back greyed on the card, marked Not checked on your data yet — say "set it anyway" and it will, noting in its answer that it did not look. And a column that looks like a key pointing at a table that is not on this connection is reported rather than proposed: Axi tells you what it appears to point at and stops, because a reference to a table Axiome cannot see would never resolve into a join.

The proposal card​

Each batch of changes appears in the chat as a card, above Axi's answer:

  • One row per change, saying what changed and, under it, what it rests on — the numbers from a key check, Checked on the data when Axi ran a query, From the names and types when it is reading the schema.
  • Show opens the column list and the inspector on the row that change touched.
  • Skipped rows, greyed, with the reason — Owned by your dbt project, This already has text, Already true, Not checked on your data yet.
  • Discard takes the whole batch back out of the draft. If you have edited the same draft since, Axiome warns you first: discarding restores the draft to what it was before Axi touched it, so your later edits go too.

In the column list, every row with unsaved changes carries a blue dot beside its name — whether Axi or you made the change — and a collapsed group of nested fields carries it when one of its fields changed, so a batch that touched three columns of a wide table is found at a glance. In the inspector, anything Axi put in the draft carries a Proposed by Axi line. Both disappear when you save the model — at that point it is simply part of the model.

A card in a conversation you have reopened is not in your draft: it reads Not in this draft, and offers Apply again.

Your draft is safe to leave​

Unsaved edits now survive leaving the section and reloading the tab. When you come back, Axiome restores them and tells you so.

This holds for a whole sweep: five tables' worth of proposals survive a reload the same way one table's do.

If the model was saved elsewhere in the meantime — by a colleague, or by you in another tab — the stale draft is dropped rather than silently re-applied over that save, and the notice says which tables were affected. Closing the tab still discards unsaved work.

Reading the data costs warehouse queries​

Axi samples your data before it claims anything about it, which means real queries against your warehouse. They are read-only, capped, and abandoned after ten seconds; each candidate key is checked once per conversation turn, at most ten per request.

On BigQuery they are billed like any other query: a sample scans the columns it reads. A key check reads one column of the table — and a foreign-key check also reads one column of the table it points at, in full, because a value missing from a sample of the target would look unmatched when it is simply elsewhere. Checking ten keys across a wide connection is therefore ten to twenty column scans. If that matters to you, ask Axi to work one table at a time rather than sweeping a whole connection.

On Snowflake they run on the connection's warehouse, four key checks at a time, and bill its time rather than the bytes they read: a sweep wakes a suspended warehouse and keeps it running while it checks (What it costs).

Key checks, and reading a column's values or its profile, are value lookups: on BigQuery each may bill at most 10 GB, and one that would bill more is refused before it runs, at no charge (Value lookups). A large table partitioned by date — or one BigQuery only lets you query with a date filter — is sampled over its latest 30 days of data, and Axi says so when it reports the numbers. A foreign-key check still reads the column it points at in full, so when that table only answers date-filtered queries, or is too large to read, the key isn't checked: Axi tells you why, and declares it only if you say so.

📖 Metrics · Semantic layer overview · Keys and joins · Dimensions and measures