Entity YAML
Entity YAML files define the features, metrics, join rules, and examples for a single business entity.
First time here? If you haven't read the other file-type docs yet, start with knowledge-md.md to understand the scoping model, then task-instructions-md.md for SQL guidance patterns — both are shorter and establish context this file builds on. Come back here when you're ready to model entities.
Quick Reference — Feature Types
field
Pulls a column directly from a source table
Any direct attribute — name, status, amount
metric
Aggregates from a related entity (feature chaining)
Per-entity totals — total revenue, order count
first_last
Gets the first or last value ordered by another field
Most recent plan, first order date
formula
Derives a value from other features on the same entity
Ratios, tiers, days-since calculations
Each type is documented in detail below.
Top-Level Structure
name: {entity_name}
description: {one or two sentence description}
key_source: {warehouse_table} # primary table — defines entity identity
keys: # fields that uniquely identify a row
- {field_name}
features: # attributes — see Feature Types below
- ...
metrics: # aggregation logic — see Entity Metrics below
- ...
related_sources: # secondary warehouse tables joined to the key_source
{table_name}:
...
examples: # entity-level query examples
- ...name
Yes
Entity identifier — referenced in queries as FROM <name>
description
Yes
Human-readable summary for agent context
key_source
Yes
The primary warehouse table (schema.db.table format)
keys
Yes
List of fields that form the primary key
features
Yes
List of feature definitions
metrics
No
List of entity metric definitions
related_sources
No
Secondary tables for enrichment
examples
No
Entity-scoped query examples
Name and Description
When a user asks a question, the agent decides which entities are relevant based on their name and description. These are the two fields the agent reads first — before looking at features or metrics.
name is the entity identifier. It appears in queries (FROM order), in relationships, and in metric feature references. Keep it short, lowercase, and unambiguous.
description is what the agent uses to decide whether this entity is relevant to the question. A vague description means the agent may miss the entity entirely or pick the wrong one. A good description answers:
What does this entity represent?
What questions or topics is it used for?
What not to put in description
The description should only explain what this entity represents and what questions it answers. Two things that don't belong here:
Filtering rules or SQL instructions — "always exclude
is_inactive = truerecords when analyzing performance" belongs in task instructions, not here.References to other entities — "use the
playerentity for career aggregates" is navigation guidance, not a description of what this entity is. The agent selects entities based on question relevance, not cross-entity pointers in descriptions.
If your project has multiple entities that could answer similar questions (e.g., order and order_item), the description is how the agent chooses between them. Be explicit about what the entity represents and what questions it answers.
The source Field
source FieldEvery feature (except formula) has a source field that tells the agent where to get the data. source always means "where this data comes from" — it can be a warehouse table or an entity.
Feature type
source can be
Format
field
A warehouse table/view or an entity
schema.db.table or entity name
first_last
A warehouse table/view or an entity
schema.db.table or entity name
metric
An entity only
entity name (e.g., order)
formula
—
No source — references features on this entity
metric features require an entity as source because they need aggregation logic (metrics:) that only exists on entities, not on raw tables.
When source is an entity (for field and first_last features), the join between the two entities must be defined in entities_relationships.yml. Specify which join to use with join_name if more than one join exists for that entity pair.
Feature Types
field — Direct Column
field — Direct ColumnPulls a column from a source without aggregation. A source can be one of two things:
A warehouse table or view where each row on this entity maps to at most one source row. For example: enriching the
customerentity withcustomer_last_signup_datefrom a warehouse table that is not the customer'skey_source.A Lynk entity where each row on this entity maps to at most one source row. For example: enriching the
orderentity with the customer name from thecustomerentity (each order has exactly one customer).
Declare the related_source or the entity relationship before defining the field feature. If the source isn't declared first, the feature won't resolve.
name
Feature name — what users see and query
data_type
string, number, boolean, datetime
source
Where the field comes from. Either a warehouse table (schema.db.table) or an entity name.
field
The column name in the source table (may differ from name)
join_name
Which join to use. For a related_source table, the join defined on that source; for an entity source, a named join on the relationship. Omit (or set null) to use the default.
filters
Pre-filters applied to the source before retrieving the field. Omit if no filters.
metric — Aggregated Value from a Related Entity
metric — Aggregated Value from a Related EntityPulls an aggregated value from a metric defined on a related entity. This is feature chaining.
source
The related entity whose metric to use. Must be an entity name (e.g., order), not a raw table name — metric: is only defined on entities.
metric
The metric name from the related entity's metrics: section
join_name
Which relationship join to use. Omit to use the default join for this entity pair.
filters
Pre-filters applied to the related entity before aggregating. Use a sql expression with {source}.{field_name} references. Omit if no filters.
For the full mechanic — feature chaining, filtered metric features, which join is used, and metric-over-metric composition — see Metrics.
first_last — First or Last Value from a Source
first_last — First or Last Value from a SourceRetrieves the first or last value from a set of rows, ordered by a specified field. Useful for "most recent plan", "first order date", "signup source".
method
first = smallest sort value; last = largest sort value
sort_by
The field to order rows by before selecting
field
The field to return from that row
offset
Optional. Which position to take — 1 returns the first/last row, 2 returns the second-from-first/last, etc. Defaults to 1 if omitted.
formula — Derived Value
formula — Derived ValueComputes a value from other features on the same entity. References other features using {feature_name} syntax.
Formula features can reference any feature on the same entity — field, first_last, formula, or metric. They cannot reference features on other entities.
Entity Metrics
Entity metrics define how to aggregate rows of this entity. They are the targets of metric features on other entities.
name
Metric identifier — referenced by METRIC('name') in queries
description
Explains what it measures and how to use it
sql
Aggregation expression. References entity features with {feature_name} and can reference other metrics on the same entity via METRIC('name') (metric-over-metric composition). See Metrics for the full list of allowed patterns and dialect notes.
Entities are the source of truth. Raw warehouse tables are inputs — they exist to enrich entities, not to be queried directly. Metrics are defined on entities because entities are where business meaning lives. A raw table has columns; an entity has features, definitions, and metrics that the agent can reason about.
If you need a metric on data that currently lives only in a raw table, you have two options:
Create a new entity from that table — if the table's level of granularity doesn't exist as an entity yet. Define the entity, bring in the fields as features, and add the metric there.
Use an existing entity — if an entity already exists at the same level of granularity, create a relationship between that entity and the raw table, bring the fields in as features via
related_sources, and define the metric as a rollup on those features.
In both cases, the metric ends up on an entity — which is the only place the agent can find and use it.
For what's allowed inside a metric's sql: — including conditional aggregation, dialect-specific constructs, and metric-over-metric composition via METRIC('name') — see Metrics.
related_sources
related_sourcesA related_source is a secondary warehouse table bolted onto an entity to enrich it with additional columns. The table is not an entity itself — it feeds columns into one. Each entry in related_sources: defines the join from the entity's key_source to the secondary table.
Why define one:
Pull dimension or lookup columns from a separate table (e.g. a country name from a country reference table).
Enrich an entity with CRM, billing, or third-party columns that don't warrant their own entity.
Bring in flat reference tables (mapping tables, code-to-label lookups) whose rows have no independent business meaning.
What features you can build from a related_source:
field
Yes
Pulls a single column through the defined join
first_last
Yes
Picks one row from the related table ordered by a field
metric
No
Aggregations require the data to live in an entity — promote the table to an entity instead
formula
No
Formulas reference features on the same entity, never tables
If you need to count, sum, or average rows from this table, do not use related_sources — promote it to an entity.
Only use related_sources for tables that are NOT the key_source of any entity. If the table already has its own entity, define the join in entities_relationships.yml instead — not here. related_sources is strictly for enrichment tables without their own entity representation.
Entity vs. related_source — the granularity test:
The decision rule above is about features. This one is about the table itself: what level of granularity does this table represent? Create a new entity if the table represents a business concept at its own level of granularity (something you'd ask questions about on its own — e.g. order, subscription, session) or if other entities need to relate to it via the relationship graph. Otherwise — if it's flat enrichment with no independent business meaning — use related_sources.
How to pick the key_source for an entity:
A table is the right key_source for an entity if it contains all instances of the concept and each instance appears exactly once. For a customer entity, the customers table where every customer has exactly one row is the right key_source. Other tables at the same customer level (e.g., a CRM enrichment table with one row per customer) connect as related_sources. Tables at a finer granularity (e.g., orders, one row per order per customer) connect as relationships — they can be aggregated up to the customer level via metric features.
Join types for related_sources:
sql
Explicit SQL join condition using {source} and {destination}
lookup
Multi-hop join through intermediate tables
When to Use This File
Create or update an entity YAML file when a concept meets one of these conditions:
You're modeling a new business concept the agent should be able to query
You're adding or changing a metric, feature, or dimension on an existing entity
The agent produces wrong results for a specific entity — field choices, joins, or calculations need fixing
Examples:
"We want the agent to answer questions about orders — revenue, volume, channel performance" → create an
orderentity"Users keep asking about customer health but there's no entity for it yet — model
customerwith ARR, NPS, and churn status" → create acustomerentity"Add a new metric to
customer— total revenue from the last 90 days" → add a filtered metric feature to the existing entity"The agent is joining the wrong table for revenue questions — there are two revenue fields and it keeps picking the wrong one" → add entity-level task instructions and improve feature descriptions
"We onboarded a new data source for product usage — model it as a
sessionentity so the agent can answer engagement questions" → create a new entity from the source table
Best Practices
Write descriptions that tell the agent when to use this entity. The agent selects entities based on name and description — a vague description means missed or wrong matches. Answer: what does this entity represent, and what questions should it answer?
Write feature descriptions that tell the agent when to use a field. When two date fields exist on an entity (e.g., created_at and completed_at), the description should state which one to use for filtering — for example, "use this for all date filtering, not created_at".
Define entity metrics before referencing them in metric features on other entities. Metric features on customer that reference order metrics depend on those metrics being defined on the order entity. Build the source entity first.
Prefer metric features for cross-entity aggregation. If you need a per-customer revenue total, define it as a metric feature — not a formula that approximates it. Metric features use the relationship join; formulas do not.
Use first_last for single values from a many-side entity. If you need "most recent order date" on customer, use first_last — not a formula. Formulas cannot aggregate across rows.
Keep key_source as the primary grain table. Enrichment from secondary tables belongs in related_sources. The key_source defines the entity's identity — one row = one entity instance.
Common Pitfalls
Avoid these common pitfalls when defining entity features and metrics.
Using a raw table name in a metric feature's source
Defining metrics on related_sources Sources cannot have metrics. If you need to aggregate from a source, create an entity whose key_source is that table, define the metrics on the entity, then use a metric feature.
Circular formula references Formula features can only reference features that are already computed at the same level. A formula cannot reference another formula that references it back.
Missing relationship for a metric feature Metric features require a relationship between the two entities in entities_relationships.yml. If the relationship does not exist, the metric feature cannot be resolved.
Defining entity aliases in the entity YAML Aliases — the different names business users use to refer to an entity — belong in the entity knowledge file, not here. The entity YAML defines schema, features, and metrics. The knowledge file is where the agent learns how users naturally refer to this entity in questions.
Using {feature_name} curly braces in the examples: section's expected_output Curly-brace {feature_name} is required in feature-definition SQL — formula sql:, entity-metric sql:, metric/first_last filter sql:, and join sql: — because Lynk resolves those references at compile time. But the examples: section's expected_output is a Lynk SQL query, not feature-definition SQL, so features are referenced by name without braces. See Lynk SQL for the full query-side rules.
Putting filtering rules, SQL instructions, or cross-entity references in the entity description The description field is for what the entity represents and what questions it answers — nothing more. Filtering rules like "always exclude is_inactive = true" belong in task instructions. Cross-entity pointers like "use the player entity for career aggregates" don't belong here either — the agent selects entities based on question relevance, not navigation hints embedded in descriptions.
Full Examples
These examples cover three different companies. Example 1 is Grove (B2B SaaS) — the customer entity, which uses metric features to pull subscription data from the subscription entity. Example 2 is Bly (e-commerce) — the order entity, standalone with its own metrics. Example 3 is Arcadia (mobile gaming) — the player entity, which uses metric features from purchase and formula-based segmentation. Reading all three shows the same pattern — entity metrics → feature chaining → query examples — in three distinct business contexts.
Example 1 — Grove (B2B SaaS), customer entity
customer entityThe customer entity includes metric features from the subscription entity (active subscription count and total MRR). The customer-subscription relationship must be defined in entities_relationships.yml for these features to resolve.
Example 2 — Bly (E-commerce), order entity
order entityThe order entity is standalone — all metrics are computed directly from order-level features. Note that order_date is the time field, not created_at.
Example 3 — Arcadia (Mobile gaming), player entity
player entityThis example shows formula-based segmentation (player_segment derived from rolling 30-day spend), two metric features from purchase (lifetime and filtered), and a MEDIUM query that identifies high-value players at churn risk. The player-purchase relationship must be defined in entities_relationships.yml for the metric features to resolve.
Last updated