Lab: Run the layer and add a metric
Query the semantic layer directly, verify it matches your raw SQL, then define a brand-new metric end to end.
Confidence that the layer's numbers match hand-written SQL, plus a new metric you defined yourself in metrics.yml.
Work from the starter-kit folder with your venv active, then start a Python session:
cd course-assets/semantic-agent
source .venv/bin/activate
python3 # then paste the snippets belowStep 1 — Query the layer
from semantic_layer import SemanticLayer
L = SemanticLayer()
print(L.query(["revenue", "gross_margin"]))
print(L.query(["revenue", "aov"], ["channel"], order_by="revenue"))
print(L.query(["revenue"], ["date"], time_grain="month", order_by="date", descending=False))Two parameters worth noticing: order_by sorts the result rows by any requested metric or dimension (here by revenue, then by date), and descending controls the direction — it defaults to True (biggest first), so the monthly query passes descending=False to get months in chronological order.
Step 2 — Browse the full catalog
import json
print(json.dumps(L.catalog(), indent=2))catalog() is the machine-readable menu of everything the layer can answer — the same thing the agent's list_metrics tool will return in Part 2. It defines 11 metrics and 9 dimensions — more than the 7 metrics you modeled on paper in Module 2. The extras are active_subscriptions, units_sold, gross_profit, and marketing_spend: supporting measures the ratios need, plus a few the business would obviously ask for.
Step 3 — Verify against your raw SQL
- 1Compare the layer's total revenue to the raw-SQL number from Module 1 — both should be $743,772.80. Exact match means your definitions are faithful.
- 2Compare revenue-by-channel. The layer's numbers should equal your hand-written GROUP BY to the penny.
- 3Deliberately break something — remove the completed-only filter from revenue in metrics.yml — and watch the total change. Then put it back. You've just felt why the filter belongs in the definition.
Step 4 — Add a new metric
Add refund_rate = refunded orders ÷ all orders. It's a ratio of two measures you'll define. Add to metrics.yml:
refunded_orders:
kind: measure
base: orders
sql: "COUNT(DISTINCT CASE WHEN orders.status='refunded' THEN orders.order_id END)"
all_orders:
kind: measure
base: orders
sql: "COUNT(DISTINCT orders.order_id)"
refund_rate:
kind: ratio
numerator: refunded_orders
denominator: all_orders
format: percent- 1Add the three definitions above.
- 2Query
L.query(["refund_rate"])andL.query(["refund_rate"], ["platform"]). - 3You defined a new governed metric — sliceable by any dimension — without writing a single query. That's the payoff.
Notice you added business capability by editing config, not code. Every dashboard and, soon, your AI agent can now answer refund-rate questions — consistently — because the definition lives in one place.