Agent Skills
› Datus-ai/Datus-agent
› metricflow-semantic-authoring
metricflow-semantic-authoring
GitHub生成MetricFlow语义模型YAML,包含字段分类、验证及知识库发布。通过检查表结构定义指标和维度,确保时间维度正确性,输出符合规范的语义模型配置。
Trigger Scenarios
需要创建或更新MetricFlow语义模型
根据数据库表结构生成YAML配置
验证语义模型的字段分类规则
Install
npx skills add Datus-ai/Datus-agent --skill metricflow-semantic-authoring -g -y
SKILL.md
Frontmatter
{
"name": "metricflow-semantic-authoring",
"tags": [
"semantic-model",
"metricflow"
],
"version": "2.0.0",
"description": "MetricFlow semantic model authoring specification — YAML structure, field classification, validation, and Knowledge Base publishing",
"allowed_agents": [
"gen_semantic_model",
"gen_metrics"
],
"user_invocable": false,
"disable_model_invocation": false
}
MetricFlow Semantic Authoring
Author production-ready MetricFlow semantic model YAML for one or more database tables, validate it, and publish it to the Knowledge Base.
Source inspection
- After SQL preflight, call
inspect_semantic_sources(tables=[...])with the physical tables that need live DDL, schema, and relationship evidence. Batch all known tables when practical. - Do not repeat
describe_tablefor a table whose combined inspection succeeded. Use a separate schema call only to recover a specific table reported withddl_errororschema_error. - When
profile_semantic_model_evidenceis available because explicit historical/distribution profiling was requested, use it only for additional distribution and historical evidence; the combined source inspection remains the canonical physical schema snapshot for this run.
Field classification rules
Classify each column by actual data type and analytical usage, not by display convenience alone:
- Put DECIMAL/NUMERIC/INTEGER/FLOAT/DOUBLE or equivalent fields in
measureswhen they represent measured quantities, scores, rates, prices, amounts, durations, counts, or percentages and historical SQL uses them in arithmetic, range predicates, numeric ordering, or numeric aggregates such asAVG,SUM,MIN,MAX,STD,VARIANCE,CORR, orCOVAR. - Do not model an aggregatable numeric business value as a categorical dimension just because it is selected, displayed, filtered, sorted, or appears in a WHERE clause. A DECIMAL field used by
AVG(<field>)should be a measure with an appropriate aggregate such asAVG. - Put text/enums/labels/booleans and numeric-coded category fields in
dimensionswhen their values identify groups or labels and averaging or summing the raw value has no business meaning. Explain code semantics (e.g. "status: 1=Active, 2=Inactive") in the description. - Put primary keys, foreign keys, unique entity IDs, and opaque IDs in
identifiers. Do not classify identifiers as measures merely because their physical storage type is integer. - Do not define the same column/name under both
identifiersanddimensions. - If schema type and SQL usage appear to conflict, prefer concrete SQL usage plus column comments/profiler evidence, and ask only when the modeling choice is business-critical.
- Define measures only for reusable aggregations. Use
expr: "1"for row-count measures withagg: COUNT. For measures, useaggfor the aggregation type; do not add atypefield to measure entries.
Time dimension correctness
- Define
type: TIMEonly for a physical DATE/TIME/TIMESTAMP column, or for asql_queryalias / SQL expression that is guaranteed to return a DATE/TIME/TIMESTAMP value. - Do NOT mark numeric surrogate keys such as
*_date_sk,*_date_key,*_dt_key, or integer YYYYMMDD keys astype: TIME. Model them as identifiers or categorical dimensions unless you explicitly convert them to a real date. - If a fact table derives its business date by joining a calendar/date dimension, prefer a
sql_querydata source that joins the date dimension, selects the real date column with a clear alias, and uses that alias as the primary time dimension. - When the user explicitly asks to preserve a durable result that normal semantic objects cannot represent, create or update a meaningful
sql_querydata source using the complete request SQL as evidence. Encode YAML with a literal block (|-when the statement has no trailing newline), never a folded>block. agg_time_dimensionon measures must point to that real date/time dimension, not a numeric surrogate key.- Include a primary time dimension only when a reliable DATE/TIME/TIMESTAMP column or expression exists; never force one.
Descriptions
Populate description fields for ALL measures, dimensions, and identifiers by COMBINING all available information:
- Start with DDL comments and preserve their original language — DO NOT TRANSLATE (Chinese comments stay Chinese).
- Append compact filter/group/aggregate evidence from
inspect_semantic_sources.tables[].sql_usage. - Append compact observed distribution evidence from
profile_semantic_model_evidencewhen available: null rate, numeric ranges and percentiles, date spans/freshness/duration, distinct counts, representative stable values, referential coverage, common filter templates, and enum/code mappings. - Describe what a column means first, then add concise observed evidence. Avoid long SQL snippets or procedural query instructions.
- ALWAYS wrap
descriptionvalues in double quotes ("). Escape special characters:"→\",\→\\.
Example:
- name: status
type: CATEGORICAL
expr: status
description: "Order status; commonly filtered with =/IN; representative values include paid/refund"
Multi-table workflow
When the request covers multiple tables:
- Submit all required physical tables in one
inspect_semantic_sourcescall when practical. - Use its declared foreign keys first, exact request-SQL JOINs second, and low-confidence column-name hints only as fallback.
- Generate one YAML file per table. Use the
entityfield to reference other tables in singular form (customer, notcustomers);type: PRIMARYfor the table's primary key,type: FOREIGNfor columns that join to other tables. Linked identifiers share the samename(one PRIMARY, one FOREIGN). - Write ALL files before validation, then validate them together.
File paths
- Save files under
subject/semantic_models/<current_datasource>/{table_name}.yml, relative to the filesystem sandbox root (use the exact prefix shown in the system prompt Workspace section). - If a semantic model already exists, update it with
edit_fileinstead of rewriting.
Validate and publish
- Call
validate_semantic(scope="semantic_model"). If validation fails, fix the YAML withedit_fileand validate again; repeat until it passes. Common errors:- PostgreSQL column case sensitivity: wrap uppercase column names in double quotes (e.g.,
expr: '"SP_POP_TOTL"') - Column not found: check column names match the DDL exactly
- Duplicate semantic element name: remove the column from either
identifiersordimensions - Invalid YAML syntax: check indentation and quoting
- PostgreSQL column case sensitivity: wrap uppercase column names in double quotes (e.g.,
- Only after validation succeeds, publish via
publish_semantic_modelwith all generated file paths. Do not publish before validation passes; do not manually write Knowledge Base summary files.
Document structure rules
- A semantic model file defines
data_source:document(s). Do NOT add a top-levelmetrics:list to the same YAML document — MetricFlow requires exactly one top-level object type per YAML document. Explicitmetric:documents belong to the metrics workflow. - Do not rely on
create_metric: truefor persisted metrics; runtime metrics are not part of the Knowledge Base metric catalog. - All
namefields follow^[a-z][a-z0-9_]*[a-z0-9]$(snake_case, no double underscores). - Choose exactly ONE of
sql_table(schema-qualified) orsql_query(databases without schema, or custom joins). - Preserve original language everywhere (Chinese text remains Chinese) for optimal vector search.
MetricFlow semantic model structure specification
data_source:
# === Required Fields ===
name: string (required) # Data source name, pattern: ^[a-z][a-z0-9_]*[a-z0-9]$
# === Optional Metadata Fields ===
description: string # Data source description
display_name: string # Display name
owners: # List of owners
- email@domain.com
tier: string|integer # Data tier
# === Data Source Definition (Choose ONE) ===
sql_table: schema.table_name # For databases with schema support (PostgreSQL, Snowflake, Redshift, BigQuery)
# OR
sql_query: | # For databases without schema (SQLite, DuckDB) or custom queries
SELECT * FROM table_name
WHERE condition = 'value'
# === Core Components ===
measures: # Measure definitions (array)
- name: string (required) # Measure name
agg: enum (required) # SUM|MIN|MAX|AVERAGE|COUNT_DISTINCT|COUNT|PERCENTILE|MEDIAN|SUM_BOOLEAN
description: string # Description - put extracted comments here
expr: string|integer|boolean # Expression, defaults to column name
agg_time_dimension: string # Aggregation time dimension
agg_params: # Aggregation parameters (for PERCENTILE)
percentile: number
use_discrete_percentile: boolean
use_approximate_percentile: boolean
create_metric: boolean # Runtime MetricFlow metric only; not persisted to the Knowledge Base metric catalog
create_metric_display_name: string
non_additive_dimension: # Non-additive dimension (snapshot/balance measures)
name: string
window_choice: MIN|MAX
window_groupings: [string]
dimensions: # Dimension definitions (array)
- name: string (required)
type: enum (required) # CATEGORICAL|TIME
description: string # Description - put extracted comments/enums here
expr: string|boolean
is_partition: boolean
type_params: # Required for TIME type
is_primary: boolean # Exactly one primary time dimension per data_source
time_granularity: enum (required) # DAY|WEEK|MONTH|QUARTER|YEAR
time_format: string
validity_params: # For SCD Type 2
is_start: boolean
is_end: boolean
identifiers: # Identifier definitions (array)
- name: string (required)
type: enum (required) # PRIMARY|UNIQUE|FOREIGN|NATURAL
description: string
expr: string|boolean
entity: string # Associated entity name (singular form)
role: string
identifiers: # Composite identifiers
- name: string
expr: string|boolean
ref: string
# === Mutability Configuration ===
mutability:
type: enum (required) # IMMUTABLE|APPEND_ONLY|FULL_MUTATION|DS_APPEND_ONLY
type_params:
min: string
max: string
update_cron: string
along: string
PostgreSQL column name case sensitivity (CRITICAL for PostgreSQL)
- PostgreSQL converts unquoted identifiers to lowercase. If a column was created with quotes (e.g.,
"SP_POP_TOTL"), it retains uppercase and MUST be quoted when queried. - In
exprfields, wrap uppercase column names with double quotes: wrongexpr: SP_POP_TOTL, correctexpr: '"SP_POP_TOTL"'. - Check the DDL: if column names contain uppercase letters, they likely need quoting. This applies to measures, dimensions, and identifiers
exprfields.
Example
data_source:
name: my_transactions
description: Transaction data with customer and order details
owners:
- data-team@company.com
sql_table: analytics.transactions
measures:
- name: total_amount
agg: SUM
expr: transaction_amount
- name: transaction_count
agg: SUM
expr: "1"
- name: unique_customers
agg: COUNT_DISTINCT
expr: customer_id
dimensions:
- name: transaction_date
type: TIME
type_params:
is_primary: true
time_granularity: DAY
- name: payment_method
type: CATEGORICAL
- name: is_refund
type: CATEGORICAL
expr: "CASE WHEN amount < 0 THEN 'Yes' ELSE 'No' END"
description: "Refund status (1:Refunded, 0:Normal)" # Keep original enums in description
identifiers:
- name: transaction
type: PRIMARY
expr: transaction_id
- name: customer
type: FOREIGN
expr: customer_id
- name: order
type: FOREIGN
expr: order_id
mutability:
type: APPEND_ONLY
Version History
- 8fb79f6 Current 2026-08-20 12:34


