Define a metric
Add a custom metric to the semantic layer so the agent uses it consistently across questions.
The semantic layer is what makes Arcus answer your questions in your language. A metric is the most common thing you'll add — a SQL fragment with a grain that the agent uses whenever a question implies it.
This guide covers what a metric is, how to define one, and how to test it.
What a metric is
A metric is a named, tenant-scoped SQL aggregation. Once defined, the agent uses it whenever a question references it (or any of its synonyms).
A simple example — define revenue once, and "what's our revenue?", "how much did we sell?", and "what was GMV?" all resolve to the same SQL. No more "which revenue column?" drift across questions.
The shape is small:
metrics:
revenue:
expr: SUM(gross_revenue)
table: mart.orders
grain: order
synonyms: [sales, gmv, top_line]
description: "Gross revenue. Includes returns and discounts. For net, use `net_revenue`."
Five fields:
expr— the SQL aggregation. Must be a single expression that aggregates rows of the named table.table— the source table. Must exist in the discovered schema.grain— what one row represents (order,customer,campaign, etc.). Used to pick the right join when a question pulls in a different dimension.synonyms— alternate names. The classifier matches on these.description— human-readable note. Shown to the agent in tool calls so it picks correctly when two metrics are close.
Add a metric
There are two paths.
Path A — guided UI
In Arcus, open Settings → Semantic layer → Metrics → Add metric. The form asks for the five fields above, with autocomplete on tables and column names.
Click Save. The metric is staged in a draft version of the layer. Click Publish to activate it for all chat threads.
Path B — edit YAML directly
For bulk changes, click Edit YAML in the same settings page. You'll get the full semantic layer in a YAML editor with syntax highlighting and validation.
version: 4
entities:
order:
table: mart.orders
primary_key: [date, sku, sales_channel]
metrics:
revenue:
expr: SUM(gross_revenue)
synonyms: [sales, gmv, top_line]
net_revenue:
expr: SUM(net_revenue)
synonyms: [net]
orders:
expr: COUNT(*)
aov:
expr: SUM(gross_revenue) / NULLIF(COUNT(*), 0)
synonyms: [average_order_value]
dimensions:
country:
column: country_code
synonyms: [geo, region]
channel:
column: sales_channel
synonyms: [platform, source]
The editor validates against our semantic layer schema — invalid YAML can't be saved.
Test a metric
Before publishing, click Test in playground. The playground lets you ask a sample question and see:
- Which metrics the agent matched.
- The SQL it generated.
- The result rows.
If the agent didn't pick your new metric when you expected, check:
- Synonyms — does the question use a word in the synonym list? Add it.
- Grain — is the question implying a different grain? You may need a different metric or a join.
- Description — is it clear enough that the agent disambiguates correctly when two metrics overlap?
Versioning
Every save creates a new version of the semantic layer. Old versions are kept — you can roll back from Settings → Semantic layer → Versions.
Threads opened against an older version still resolve correctly; the metric definition is bound at run time.
A real example
Say you want to define ROAS (return on ad spend) across multiple platforms.
Step 1 — pick the right grain. ROAS is usually computed per campaign, per day. So:
entities:
ad_spend:
table: mart.ad_spend_daily
primary_key: [date, platform, campaign_id]
metrics:
spend:
expr: SUM(spend_usd)
conversion_value:
expr: SUM(conversion_value_usd)
synonyms: [revenue_attributed, ad_revenue]
roas:
expr: SUM(conversion_value_usd) / NULLIF(SUM(spend_usd), 0)
synonyms: [return_on_ad_spend]
description: "Conversion value attributed by the platform divided by spend. Treats null spend as zero. For blended ROAS across platforms, use `blended_roas`."
Now "what's our ROAS on Meta last week?" resolves to:
SELECT
platform,
SUM(conversion_value_usd) / NULLIF(SUM(spend_usd), 0) AS roas
FROM mart.ad_spend_daily
WHERE platform = 'meta'
AND date >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY platform
The agent picked roas, scoped to Meta, and applied the time filter — without you writing SQL.
Best practices
- One metric per concept. If your team has three definitions of "active user," pick the canonical one for the metric and put the others in synonyms with descriptions explaining the difference.
- Use
NULLIFto guard ratios. Division-by-zero in SQL silently returns NULL, which becomes confusing in narratives. Wrap denominators. - Document edge cases in
description. The agent reads it. Ifrevenueexcludes returns, say so. If a metric only makes sense for certain dimensions, say so. - Keep the SQL simple. A metric expression should fit on one line. If it's getting complex, define a view in your warehouse and reference the view instead.