+1 (726) 227-2971

Making Your LookML Model AI-Ready: Labels, Descriptions, Synonyms, and Tags

Every AI feature Google has attached to Looker — Gemini in Looker, the Conversational Analytics API, agents talking to an MCP server — reads the same thing: your LookML. The pitch is that a governed semantic layer is what makes AI answers trustworthy. That is true, but only to the degree that your model actually says what the fields mean. A model full of total_amt_2, flag_c and dimensions with no descriptions is a semantic layer in name only, and an LLM asked to pick fields from it will guess.

This tutorial is about the unglamorous work that decides whether AI-assisted analytics succeeds on your instance: metadata hygiene. It is all standard LookML, it pays off for human self-service users too, and it is the single highest-return piece of work we do on most engagements.

What the model exposes to an LLM

When Gemini or a Conversational Analytics agent answers a question against an explore, it is working from a serialised description of that explore, not from your warehouse. Practically, that means:

  • Explore name, label and description.
  • Every non-hidden field: name, label, type, description, and often the group label and value format.
  • Joins and their relationships, which decide what can be asked together.
  • Filters and access controls that will be applied whether or not the model knows about them.

Two consequences follow. First, anything not written down is invisible: business rules living in a Slack thread cannot be inferred. Second, noise is expensive. Three hundred fields where forty matter means the model spends its selection budget discriminating between revenue, revenue_net, revenue_net_v2 and revenue_old. Field choice is where AI answers go wrong most often, and it is the part you control directly.

Step 1: prune before you polish

Do not start by writing descriptions. Start by deleting things from the exposed surface.

explore: orders {
  label: "Orders & Revenue"
  description: "Order-level facts: bookings, net revenue, margin, fulfilment status. Grain is one row per order line."

  # Only expose curated field sets to explores used by AI and self-service
  fields: [
    ALL_FIELDS*,
    -orders.revenue_old,
    -orders.internal_batch_id,
    -users.hashed_email
  ]

  join: users {
    type: left_outer
    relationship: many_to_one
    sql_on: ${orders.user_id} = ${users.id} ;;
  }
}

Techniques worth applying in order:

  1. hidden: yes on primary keys, foreign keys, technical flags and staging dimensions nobody filters on.
  2. fields: sets on the explore to whitelist a curated subset — far more maintainable than hiding fields one at a time when the same view feeds several explores.
  3. Retire duplicate measures. If revenue and net_revenue both exist, one of them should either be deleted or renamed so the difference is obvious in the name itself.
  4. Use hidden: yes on whole explores that exist only to support NDTs or admin content, so AI never selects them.

A useful rule of thumb: an explore intended for conversational use should expose something like 20–60 fields. If yours exposes 400, that is your finding.

Step 2: labels a human would say out loud

Labels are what the model matches question wording against. Write them the way your business talks.

view: orders {
  label: "Orders"

  dimension_group: created {
    label: "Order Placed"
    type: time
    timeframes: [raw, date, week, month, quarter, fiscal_year]
    sql: ${TABLE}.created_at ;;
  }

  measure: net_revenue {
    label: "Net Revenue"
    group_label: "Revenue"
    description: "Gross order value minus discounts, returns and cancellations. Excludes tax and shipping. Reported in USD at the daily rate. Use this for all external revenue reporting."
    type: sum
    sql: ${net_revenue_usd} ;;
    value_format_name: usd_0
  }

  measure: revenue_gross {
    label: "Gross Revenue"
    group_label: "Revenue"
    description: "Order value before discounts and returns. Internal use only — does not tie to the finance close."
    type: sum
    sql: ${gross_revenue_usd} ;;
    value_format_name: usd_0
  }
}

Guidelines that matter more than they look:

  • label — no snake_case, no abbreviations the business does not use. "Net Revenue", not "Net Rev Amt".
  • group_label / group_item_label — cluster related measures. Groups give an LLM a hint that these fields are alternatives to each other.
  • view_label — controls which section a joined view's fields appear under. Rename users to "Customer" if that is the word your business uses.
  • label_from_parameter — for templated fields, so the visible name reflects the chosen parameter rather than a generic name.
  • Avoid two fields with identical labels in the same explore. If both orders.status and shipments.status are exposed, label them "Order Status" and "Shipment Status".

Step 3: descriptions that carry the business rule

A description is the only place in your model where you can write prose. Use it for the things a name cannot express. A good description answers four questions:

  1. What is it? The definition in one sentence.
  2. What does it exclude? Tax, cancelled orders, internal test accounts, a specific region.
  3. What unit and grain? USD, minor units, percent as 0–1 or 0–100, one row per order line versus per order.
  4. When should it not be used? "Deprecated — use Net Revenue" is the single most valuable sentence you can add.
  measure: conversion_rate {
    label: "Conversion Rate"
    description: "Converted sessions divided by total sessions, expressed as a fraction (0.043 = 4.3%). Denominator excludes bot traffic and internal IPs. Not additive across date ranges — never sum this field."
    type: number
    sql: ${converted_sessions} / NULLIF(${total_sessions}, 0) ;;
    value_format_name: percent_1
  }

  dimension: is_test_account {
    hidden: yes
    type: yesno
    sql: ${TABLE}.is_internal ;;
  }

Two habits to enforce in review: every exposed measure has a description, and every deprecated field says so in its first five words. You can police the first with a LookML data test convention or a lint rule in your CI pipeline — see our CI for LookML tutorial for where to hook that in.

Step 4: synonyms and vocabulary via tags

Business users and LLMs both ask for "churn", "attrition" and "logo loss" when your field is called "Cancelled Accounts". LookML's tags parameter attaches arbitrary strings to a field; those tags travel with the field's metadata through the API and are a practical place to park synonyms and classification.

  measure: cancelled_accounts {
    label: "Cancelled Accounts"
    description: "Distinct accounts whose subscription reached a cancelled state in the period. Commonly called churn or attrition."
    tags: ["churn", "attrition", "logo_loss", "kpi", "certified"]
    type: count_distinct
    sql: ${account_id} ;;
  }

Tags are also how we mark a certified core of fields (tags: ["certified"]) so that agent configuration and downstream tools can prefer them. Keep the tag vocabulary short and documented in the project README, otherwise it rots.

Step 5: filter suggestions and value hygiene

An AI agent that generates a filter on Order Status = "complete" when your warehouse stores COMPLETE returns zero rows and a confident wrong answer. Make dimension values discoverable and stable:

  • Keep suggestable on for low-cardinality dimensions so suggestions come from real values; disable it on high-cardinality columns to protect performance, and use suggest_dimension to point at a curated lookup instead.
  • Normalise case in the model, not in the question: map raw values to a clean set with a case or SQL INITCAP/mapping, and let the raw column stay hidden.
  • Set case_sensitive: no on the connection or handle casing explicitly; do not rely on the asker to guess.
  • Use value_format_name everywhere. Formats are part of the metadata and stop percentages being rendered as 0.043 in a chat answer.

Step 6: give the agent explicit instructions

Model metadata is necessary but not sufficient. Both Gemini in Looker and the Conversational Analytics API let you supply additional context alongside the explore — business glossary entries, rules such as "fiscal year starts in February", and example question/answer pairs ("golden queries") that demonstrate the correct field choices for common asks. Check the current Google Cloud docs for the exact configuration surface, as this part of the product moves quickly.

Treat that context like code: keep it in the same Git repository as the LookML, review it in pull requests, and re-test when the model changes. The failure mode is an agent instruction file that still describes last quarter's explore.

Step 7: measure whether it worked

Metadata work is easy to hand-wave. Make it measurable:

  • Build a small evaluation set — 25 to 50 real questions from your business, each with the field list and filter set a senior analyst would use. Run them after each change and score field selection, not just whether a chart appeared.
  • Use the System Activity model to find which fields are actually used, and prune or document the top unused ones; see Monitoring Looker with System Activity.
  • Track a simple coverage metric from the API: percentage of exposed measures with a non-empty description. Pull it from the lookml_model_explore endpoint and chart it. It is crude, and it moves teams.
  • Confirm your row-level security still holds through the AI path — an agent is just another query client, and it inherits user attributes and access grants. Our row-level security patterns tutorial covers the tests.

A one-week plan

If you want a concrete starting point, this is roughly the sequence we run with clients:

  1. Day 1 — pick one explore that matters to the business. Do not start with all of them.
  2. Day 2 — prune. Hide keys and technical fields, whitelist with fields:, delete or clearly deprecate duplicates.
  3. Day 3 — labels and group labels, including view_label on joins so sections read as business objects.
  4. Day 4 — descriptions for every exposed measure and every ambiguous dimension, with exclusions, units and grain.
  5. Day 5 — tags and synonyms, suggestion hygiene, value formats.
  6. Day 6 — agent context: glossary, rules, golden queries, in Git.
  7. Day 7 — evaluation set, run it, record the score, then repeat the loop on the next explore.

The output is not only better AI answers. A pruned, labelled, documented explore is exactly what makes human self-service work, which is why this exercise is worth doing even if you never switch a single AI feature on.

Vistelio's Looker consultants do this as a focused engagement or as part of a broader Looker health check and technical audit, usually alongside the Conversational Analytics and MCP work described elsewhere in these tutorials. If your semantic layer needs to be ready for AI questions from the business, get in touch.