一键导入
一键导入
| name | gen-metrics |
| description | Generate MetricFlow metrics from natural language business descriptions |
| tags | ["metrics","metricflow"] |
| version | 1.2.0 |
| user_invocable | false |
| disable_model_invocation | false |
| allowed_agents | ["gen_metrics"] |
Guide the user through metric generation using natural language business descriptions.
Before anything else, call list_metrics() to get all metrics already in the knowledge base. Build an existing metric catalog JSON array with each metric's exact name, type, description when available, and subject_path when available. Use this throughout the remaining phases to:
Only inspect and edit semantic model YAML files under the current datasource directory shown in the system prompt, such as subject/semantic_models/<current_datasource>/.... Do not reuse or sync YAML files from sibling datasource directories; those files are outside the active MetricFlow adapter scope.
Analyze the user's request and confirm the generation scope before proceeding. When ask_user is available, call it to confirm the metric name(s), business meaning, and calculation logic. When ask_user is not available (for example workflow or batch mode), infer from the provided SQL/request and stop only if the scope is materially ambiguous.
question + sql columns) → follow Step 1-batch belowStep 1a: Inspect the table — Call describe_table(table_name) to understand the columns and types. Optionally call read_query to sample data.
Step 1b: Ask for reference SQL (optional) — When ask_user is available, use it to ask:
"Do you have any existing SQL queries for this table that show the aggregations you care about? You can paste them here, or skip if not available."
When ask_user is not available, skip this question and infer SQL/aggregation context from the user's request, attached files, or discovered query/table evidence. If that is not enough, stop and explain the missing information instead of calling ask_user.
If the user provides SQL, parse it to extract:
SUM(amount) / COUNT(DISTINCT user_id) AS arppu → candidate metric arppu)SUM(amount) → candidate measure total_amount, COUNT(*) → candidate measure record_count)If the provided SQL contains no metric-producing output, keep filter-only or detail-query evidence as filters, dimensions, segments, or view evidence instead of generating fake metrics.
If the user skips, proceed to Step 1c using only table structure and the user's description.
Step 1c: Propose metric candidates — Based on the table structure, reference SQL (if provided), and user's request, identify potential metric scenarios. See "Metric type detection rules" below.
Step 1d: Confirm scope — when ask_user is available, call it to confirm and present proposed metrics with multi_select: true (see Step 1-batch-d for format). If ask_user is not available, proceed with the confirmed/inferred scope from the input.
Step 1-batch-a: Parse SQL queries
read_file to load it, then parse by file type:
.sql: split by ; or blank-line separators to extract individual statements.csv / .tsv: identify the SQL column by header name (common names: sql, query, SQL, statement) or by content heuristic (column values contain SQL keywords like SELECT, FROM, GROUP BY). The description/question column is any remaining text column. If column roles are ambiguous, call ask_user when available to confirm which column is SQL; otherwise stop and explain the missing column mapping.ask_user when available to clarify the file structure before proceeding; otherwise stop and explain the supported file formats or required structure.describe_table for each unique table found in the SQL queriesStep 1-batch-b: Mine metric candidates from SQL ASTs
Call analyze_metric_candidates_from_history with all parsed SQL queries and existing_metric_catalog_json from Phase 0. Use its output to preserve final business metric expressions and their dependencies:
non_metric_evidence, not metric YAML.query_classification is metric_plus_derived_datasource or derived_datasource_recommendations is non-empty, do not generate a direct metric from blocked_direct_metric_candidates; first model the recommended sql_query data source or materialized view, then define metrics on that data source.requires_name_translation: true, treat name as a technical fallback only. Also inspect every source_alias: when the alias appears generated or lacks business meaning, do not use it as the final MetricFlow name. In interactive mode, ask the user to confirm if the business meaning is unclear; in batch/bootstrap mode, infer a clear English snake_case name from the SQL expression, question, table/column context, and external knowledge without stopping.literal_mappings is present, keep the literal value exactly as it appears in SQL predicates/CASE/sql_query output. Only MetricFlow object names may be translated or normalized.time_grain_evidence is present, expose an equivalent time dimension in any derived data source. Do not replace a projected date such as CURDATE() AS part_dt or DATE(create_time) AS part_dt with raw create_time as the primary time dimension.post_aggregation_constraints is present, keep each HAVING/post-aggregation condition as a query constraint, metric usage note, or later derived data source. Do not silently drop it or push it into a base measure.derived_metric_candidates as second-stage metrics over existing metrics. Do not mix them into base semantic model or measure generation.identity_metric_references show existing metrics selected without new business formula; do not generate new metrics for them.Step 1-batch-c: Business metric principle
From N SQL queries, propose a focused set of business metrics. Ask yourself for each candidate:
Step 1-batch-d: Confirm with the user when possible
ask_user is available, present the mined business metric candidates as options with multi_select: truequestions as an actual array argument, not a JSON string. Example tool arguments:
{
"questions": [
{
"title": "Metrics",
"question": "I analyzed N SQL queries and identified the following metric candidates. Select which ones to generate:",
"options": ["paid_arppu - SUM(paid_amount) / COUNT(DISTINCT user_id)", "gross_margin_rate - (SUM(revenue) - SUM(cost)) / SUM(revenue)"],
"multi_select": true
}
]
}
ask_user is not available, proceed with the mined metrics only if the input makes the scope unambiguous; otherwise stop and explain what needs to be provided.measure_proxy metric referencing that measure by stringmeasure_proxy metric referencing that measure by stringratio typeexpr type combining measuresderived type combining metricscumulative typeDetection keywords:
gen_sqlIMPORTANT: Do NOT proceed to Phase 2 with materially ambiguous scope. Use ask_user when available; otherwise stop and explain what information is needed.
For each table involved in the metric:
check_semantic_object_exists(name="{table_name}", kind="table") to check if a semantic model exists.read_file to read the existing semantic model YAMLedit_file to add them, then validate_semanticIf the semantic model is missing, follow the gen-semantic-model workflow when that skill is available. In brief: inspect table structure with describe_table, discover joins with analyze_table_relationships when multiple tables are involved, use analyze_column_usage_patterns for likely measures and dimensions, write the semantic model YAML under the semantic model directory shown in the system prompt, then run validate_semantic and fix issues until it passes before continuing.
When the metric involves multiple tables (detected from JOIN in SQL or user description), choose the modeling strategy based on SQL complexity:
Strategy A: Identifier-based JOIN (default — use when possible)
Use when: simple equi-JOIN between 2-3 tables via foreign keys, ≤ 2 JOIN hops.
data_source with sql_tableidentifiers (same name, one PRIMARY, one FOREIGN)analyze_table_relationships results to set up correct identifier linkagesorders.customer_id (FOREIGN) links to customers.customer_id (PRIMARY) — both identifiers share name: customerStrategy B: sql_query pre-joined data source (complex cases)
Use when: non-equi JOINs, > 2 hop joins, subqueries, LATERAL/CROSS joins, complex ON conditions, or window functions in the JOIN.
data_source with sql_query containing the pre-joined SQLdata_source:
name: order_customer_summary
sql_query: >
SELECT o.order_id, o.amount, o.order_date,
c.name as customer_name, c.segment
FROM schema.orders o
JOIN schema.customers c ON o.customer_id = c.id
measures:
- name: total_revenue
agg: SUM
expr: amount
dimensions:
- name: customer_name
type: CATEGORICAL
- name: order_date
type: TIME
type_params:
is_primary: true
time_granularity: DAY
Decision rule: Default to Strategy A. Switch to Strategy B only if the JOIN cannot be expressed as simple identifier matching (e.g., composite keys, non-equi conditions, 3+ hop joins, or subquery-based logic).
File paths: All write_file / edit_file / read_file calls use paths relative to the filesystem sandbox root. Always use the semantic model directory shown in the system prompt so subsequent reads find the file. For example:
subject/semantic_models/<current_datasource>/{table_name}.ymlsubject/semantic_models/<current_datasource>/metrics/{table_name}_metrics.ymlBare filenames are silently normalized by the host, but the prefixed form is preferred for clarity. Absolute paths are also tolerated.
Do not read, edit, or pass metric_file / semantic_model_file paths from another datasource directory such as subject/semantic_models/other_datasource/....
Check existing: Call check_semantic_object_exists(name="{metric_name}", kind="metric") for each metric confirmed in Phase 1. If it already exists, inform the user and skip it.
Write metric YAML: Use write_file to save each metric definition to subject/semantic_models/<current_datasource>/metrics/{table_name}_metrics.yml.
measure_proxy, keep type_params.measure as a string measure name.metric: YAML document. Do not emit unnamed metric: blocks or wrap metrics inside another object.Validate (MUST PASS): Call validate_semantic to check the metric YAML.
edit_file and retry until it passes.Dry-run SQL: Call query_metrics(metrics=["{metric_name}"], dry_run=True) to generate the SQL.
{"{metric_name}": "SELECT ..."}After all generated metrics have passed validation and dry-run:
metric_sqls_jsonend_metric_generation(metric_file, semantic_model_file, metric_sqls_json) ONCE to sync them to Knowledge Base while you can still fix publish errorsend_metric_generationPhase 1 confirms the generation scope; validation plus dry-run are the acceptance gate before syncing.
Explicit metric files: Write explicit metric YAML files under the semantic model directory's metrics/ subdirectory instead of relying on create_metric: true. Runtime-generated metrics are not part of the persisted metric catalog.
Metric name must match measure name: For a measure_proxy metric, the metric name should typically equal the measure name (or be a clear derivative). The type_params.measure must exactly match a measure name from the semantic model. Do NOT invent unrelated names (e.g., measure activity_count → metric name should be activity_count, NOT total_activity_count or activity_count_metric).
Filtered metrics: Model reusable filter logic as a conditional measure in the semantic model, such as expr: "CASE WHEN status = 'completed' THEN 1 ELSE 0 END" with agg: SUM, then write type_params.measure: completed_order_count in the metric YAML.
Check before creating: ALWAYS call check_semantic_object_exists(name="{metric_name}", kind="metric") before writing a new metric. If the metric already exists, skip it.
Verify names after validation: After validate_semantic succeeds and the adapter reloads, call list_metrics to see the exact metric names available. Use these exact names when calling query_metrics.
Every metric needs explicit YAML: Whether it's a simple aggregation, filtered variant, ratio, expr, derived, or cumulative — write a metric: entry in the metrics YAML file so it can be persisted and discovered later.
Derived metrics are second-stage: Generate non-derived metrics first, validate them, refresh the metric catalog with list_metrics, then generate derived_metric_candidates only when every referenced metric exists in the refreshed catalog or was generated earlier in the same batch.
ask_user when it is available.validate_semantic and ensure it passes before proceeding to the next phase. If it fails, fix and retry until it passes.end_metric_generation without another user confirmation. The final JSON metric_file is only a last-resort fallback.expr: "1" — never use expr: {column} with COUNT (use COUNT_DISTINCT for that).type: TIME with is_primary: true).non_additive_dimension to prevent incorrect time aggregation.end_metric_generation; the final JSON metric_file is only a last-resort fallback.Create database tables from SQL (CTAS) or natural language descriptions
Generate MetricFlow semantic models from database tables with validation and Knowledge Base publishing
Activate when the gen_job agent detects that the source and target databases differ. Covers cross-database transfer lifecycle - type mapping via adapter Mixin hints, DDL generation, data transfer via transfer_query_result, and lightweight reconciliation.
Execution guide for Airflow scheduled jobs — troubleshooting, updating, conn_id conventions, and cron references
Scheduler validator driven by ValidationHook — read-only static verification of scheduled jobs (schedule correctness, configuration, runtime context already collected by deterministic hook). Does not trigger test runs.
Create, view, and manage Grafana dashboards with panels and datasources