Metrics Definition System and Validation Operations Guide
This document is intended for Analytics Agent semantic governance and operations personnel. It explains how the three mechanisms — metrics, Answer Builders, and the knowledge base — work together, and covers the validation and operations work required before delivery.
Analytics Agent provides three definition mechanisms — Metrics, Answer Builder, and Knowledge Base — which together form the context that the LLM uses to understand business semantics. This document explains the applicable boundaries of each, how they work together, and the validation and operations work required before delivery.
Overview
All three are DSLs (domain-specific languages) for the LLM. The LLM reads them to understand business semantics and then generates SQL on its own. They are not executable instructions for the database — this is the prerequisite for understanding all the rules that follow.
| Mechanism | Form | Role |
|---|---|---|
| Metrics | Single-table aggregation expression | Definition — fixes single-table metric calculation logic using aggregation expressions, and tells the LLM "this domain has this metric" |
| Answer Builder | Multi-table SQL template + dimension declarations | Analysis template — fixes cross-table JOIN paths and aggregation definitions using SQL templates, and tells the LLM "this concept supports drilling down by these dimensions" |
| Knowledge Base | Markdown documents | Free text — supplements business rules, formula descriptions, and analysis scenarios |
Metrics
When to Use
Use metrics (rather than Answer Builder) when all of the following conditions are met:
- All fields come from a single table
- The expression is a pure aggregation (SUM/COUNT/AVG/MAX/MIN) — no subqueries, no window functions
- String comparison values in the expression are directly available on the GPT view (no dependency on virtual columns)
- No programmatic time-series API calls needed (
metric_calculate)
Creating
Notes
Do not reference virtual columns. Virtual columns (column virtual set) are not persisted to the GPT view — the following will throw an error:
Do not use correlated subqueries. GPT views do not support outer references in correlated subqueries, causing silent errors:
Complete Examples
The following metrics represent typical best-practice usage:
Answer Builder
When to Use
Use Answer Builder when any of the following conditions are met:
- Cross-table: The metric calculation or dimensions involve multiple tables
- Dimension drill-down needed: Users need "see Y by X" type analysis
- Subqueries/window functions needed: The expression exceeds pure aggregation capability
- Lock JOIN path needed: Eliminate ambiguity in multi-table scenarios
Creating
An Answer Builder contains a DSL (--content) and a SQL template (--sql):
DSL Field Reference
| Field | Required | Description |
|---|---|---|
chartParams[].name | Yes | Placeholder name, referenced in SQL as ${name}. Convention: dims and filters |
chartParams[].type | Yes | dimension or filter |
chartParams[].fromTableRefs[].tableName | Yes | Full GPT view path |
chartParams[].fromTableRefs[].columns | Yes | Available dimension/filter columns. Must match actual column names in the GPT view and be resolvable in the SQL template FROM/JOIN |
outputColumns[].name | Yes | Must exactly match the AS alias in SQL |
outputColumns[].metricName | Yes | Unique within the domain. Recommend ab_ prefix to avoid conflicts with metric names |
outputColumns[].type | Yes | Singdata data type |
outputColumns[].stdTypeName | Yes | Standard type (int / double / string) |
relatedTables | Yes | Full paths of all GPT views used in the SQL |
SQL Template Writing Rules
String constants: use chr() concatenation
Due to shell quote conflicts, string constants must be concatenated using chr():
Common ASCII codes: c=99, o=111, m=109, p=112, l=108, e=101, t=116, d=100, '=39, -=45
${dims} placement rules
Window functions: place inside a subquery, wrap with outer SELECT *:
CTEs:
SQL Template Examples
The following examples cover common complexity levels from single-table to multi-table JOIN.
Example 1: Single table + filter (simplest pattern)
Scenario: Count orders and GMV by channel and fulfillment type, with optional order status filter.
Corresponding chartParams: dims can be channel, fulfillment_type, pay_method; filters can be order_status, channel.
Example 2: Two-table JOIN + dimension drill-down
Scenario: Count GMV and orders by store dimensions (name/city/trade zone/format).
Corresponding chartParams: dims can be store_name, city, city_tier, trade_zone_type, store_format.
Key point: dimension columns come from dim_store, measures come from fact_order — the typical pattern for cross-table dimension drill-down.
Example 3: Three-table JOIN (order → item → product)
Scenario: Count sales volume and GMV by product category and date.
Corresponding chartParams: dims can be category_l1, category_l2, sku_name, year, month.
Key point: three-table chain JOIN (fact_order → fact_order_item → dim_sku), aggregation on the item table.
Example 4: Subquery + ROW_NUMBER ranking
Scenario: Count orders by channel and rank them. Because ${dims} cannot share the same column with ROW_NUMBER's PARTITION BY, the window function is placed in the inner subquery and the outer level uses SELECT * to wrap it.
Corresponding chartParams: dims can be channel, fulfillment_type, order_status.
Key points: (1) Window function placed in the inner subquery, outer SELECT * wraps it — this is the only reliable pattern for using window functions in an AB; (2) ${dims} is used directly in the inner layer for GROUP BY and SELECT, in the same scope as ROW_NUMBER.
Example 5: INNER JOIN subquery — member pre-aggregation
Scenario: Analyze repurchase rate by membership tier — first use a subquery to count orders per member, then JOIN to the member dimension.
Corresponding chartParams: dims can be member_tier, register_channel, city.
Key points: (1) Use INNER JOIN (not LEFT JOIN) to ensure the denominator is "members with purchase history"; (2) the subquery only does pre-aggregation and does not reference ${dims} — dims in the outer layer come directly from dim_member; (3) this is the standard pattern for replacing correlated subqueries on GPT views.
Example 6: Fact table + multiple dimension table join — stockout analysis
Scenario: Analyze stockout days, stockout rate, and out-of-stock duration by product and month.
Corresponding chartParams: dims can be sku_name, category_l1, category_l2, year, month, stockout_reason.
Key points: (1) Fact table (stockout) JOINs two dimension tables simultaneously (dim_sku + dim_date); (2) stockout_flag serves both as a filter condition and a count object inside the aggregation function.
Validate Before Creating
Metrics and Answer Builder Coexistence
When You Need Both
| Scenario | Metric provides | Answer Builder provides |
|---|---|---|
| Coupon redemption rate | Global single value (Dashboard) | Drill-down by campaign/channel/time |
| Store profit | Global average profit (summary) | Comparison by store/city/format |
| Repurchase rate | Global repurchase rate (single value) | Breakdown by membership tier/channel/city |
Calculation Definition Consistency
When both coexist, the calculation definitions must stay consistent. Recommend noting the source in AB's outputColumns:
Knowledge Base
Recommended Content
| Document | Content |
|---|---|
| metric-definitions.md | Calculation formulas, filter conditions, and units for all metrics |
| business-rules.md | Enumeration value meanings, membership tiers, order statuses |
| analysis-scenarios.md | SQL templates for 3–5 typical analysis scenarios |
| data-model.md | Table structure, JOIN relationship diagram, core fields |
Three-Source Consistency Rule
Recommended workflow:
- First define the calculation definition in KB (as documentation)
- Then create the metric (register as a calculation object)
- If dimension drill-down is needed, create an Answer Builder (declare dimensions)
- In the AB's description, back-reference the KB section
- When there is a conflict between the three sources, KB takes precedence — update metric and AB accordingly
Testing and Validation
Bypass Validation Method
The Agent may rewrite expressions or ignore AB templates. The only reliable validation is SQL bypass testing:
- Ask the Agent a question and get a response
- Extract key values from the Agent's response
- Write equivalent SQL using the same data source, JOIN path, and filter conditions
- Compare values — accurate to two decimal places
Unreliable Validation Methods
- Agent returned no error → may return a plausible but incorrect value
- Value is within a reasonable range → a 2% deviation is invisible to the naked eye
- Agent referenced KB text → may have referenced it but calculated using a different formula
- AB validation passed → validation only checks syntax; the Agent may not execute it
Testing Checklist
| Validation item | Method |
|---|---|
| Expression correctness | SQL bypass comparison value by value |
| JOIN path correctness | Query both paths with same-name columns separately; confirm Agent chose the right table |
| Filter condition completeness | Boundary test: "including cancelled" vs "completed only" |
| Dimension drill-down availability | Ask Agent "see Y by X" |
| Period-over-period baseline | Cross-period comparison to validate time window |
Operations Best Practices
This chapter describes the standardized operational process for disambiguation, latent issue elimination, and bypass testing of analysis domains using CZ-CLI. In all command examples, replace <domain-id>, <datasource-id>, and table IDs with your own domain's actual values.
Disambiguation
Ambiguity occurs when the same symbol maps to multiple different meanings in the system. A typical example: a same-named column represents different concepts across tables (e.g., city means both store city and member's home city), and the Agent may choose the wrong JOIN path without reporting an error.
Full column semantic scan
Extract key fields for each column: attrCode (column name), semanticType (type), description (description), intendedTypes (intended uses).
Cross-table duplicate column name detection
Deduplicate all column names and find those appearing in more than one table:
Compare semantics group by group
For each group of duplicate-named columns, compare their description to determine whether they are synonymous (used for JOIN) or have different meanings (need disambiguation):
| Judgment | Example | Action |
|---|---|---|
| Synonymous (JOIN key) | member_key in dim_member and fact_order | No action needed |
| Different meanings (different business concepts) | city in dim_store (store city) vs dim_member (member's home city) | Needs disambiguation |
| Granularity confusion | discount_amount in fact_order (order level) vs order_item (SKU level) | Needs annotation |
Data validation
For columns with different meanings, query whether different JOIN paths produce different results:
Execute disambiguation: Update column descriptions to annotate JOIN paths and usage differences:
Latent Issue Elimination
Latent issues are defects where the system currently reports no error, but will produce systematically wrong results under certain conditions. Unlike ambiguity — ambiguity is "the Agent doesn't know which path to take" — latent issues are "the Agent thinks it chose correctly, but the result is wrong."
Metric expression audit
Check each metric's expression for the following defects:
| Defect | Detection method | Fix |
|---|---|---|
| Missing order_status filter | Check expression for WHERE completed or equivalent CASE WHEN | Add status filter to both numerator and denominator |
| Virtual column reference | Query the column; if it reports "cannot resolve," the virtual column is not persisted | Change to inline CASE WHEN |
| No division-by-zero protection | Division expression has no NULLIF on denominator | Add NULLIF(..., 0) |
| Correlated subquery | Expression contains (SELECT ... WHERE outer.col = inner.col) | Correlated subqueries are unreliable on GPT views — use Answer Builder with GROUP BY instead |
Semantic type consistency check: Detect whether the same type of columns (Boolean/flag) are consistently labeled. Check items include semanticType (CATEGORICAL/CONTINUOUS), dimension (true/false), intendedTypes (DIM/FILTER/MEASURE). A common issue is flag columns being mislabeled as MEASURE instead of DIM — cross-compare and standardize.
Dead dimension detection: For columns labeled as CATEGORICAL DIM, check the actual value distribution. Dimensions with NULL rate > 90% or distinct values <= 1 should be hidden:
Cross-table data consistency check: Verify that enumeration values for the same business concept are consistent across different tables (e.g., channel names). Inconsistency causes cross-table aggregation mismatches:
GPT view correlated subquery validation: For metrics containing subqueries, execute them against both the physical table and the GPT view and compare whether results are consistent:
Bypass Testing
Bypass Testing means building an independent SQL validation channel outside the Agent's output path — recalculating results using the same data source and calculation logic, then comparing each value. This is the only reliable validation method — no errors from the Agent does not mean the results are correct.
Ask the Agent and record key values
Extract verifiable value assertions from the Agent's response, for example: Standard member repurchase rate 90.32%, Gold member repurchase rate 97.33%, Overall repurchase rate 92.19%.
Write equivalent SQL
Use the same calculation definition the Agent claims to use (if stated) or one consistent with the domain definition:
Compare values one by one
| Tier | Agent | SQL | Deviation | Judgment |
|---|---|---|---|---|
| Standard | 90.32% | 90.32% | 0 | Pass |
| Gold | 97.33% | 97.33% | 0 | Pass |
| Overall | 92.19% | 92.19% | 0 | Pass |
Deviation classification
| Deviation range | Classification | Action |
|---|---|---|
| = 0 | Exact match | Pass |
| 0 < <= 1% | Rounding or minor calculation definition difference | Record reason |
| 1% < <= 5% | Calculation definition difference | Annotate and evaluate acceptability |
| > 5% | Likely error | Investigate Agent SQL generation path |
Common Pitfalls
| Pitfall | Description | How to avoid |
|---|---|---|
| GPT view correlated subquery silent error | Metrics with correlated subqueries lose outer references in GPT views, returning wrong values without errors | Do not use subqueries in metrics |
| Virtual columns not persisted | Columns created with column virtual set are not available in GPT views | Use inline CASE WHEN in metric expressions |
| Ambiguous columns | Same-named columns with different meanings (e.g., city) cause the Agent to follow the wrong JOIN path | Annotate JOIN path in column descriptions; use AB to lock JOIN for cross-table metrics |
| Three-source conflict | Metrics, AB, and KB may define different calculation definitions; the LLM decides freely which one to use | Write calculation definitions in only one place (KB); the other two reference KB |
Pre-Creation Checklist
Metrics
- Single table, pure aggregation, no subqueries, no window functions
- No virtual column references
- Chinese aliases added
- Description explains calculation definition and filter conditions
- SQL bypass validation confirms values are correct
Answer Builder
- analysis-name clearly describes the business concept
- chartParams.fromTableRefs column names match the GPT view
- outputColumns.name exactly matches the SQL AS alias
- outputColumns.metricName uses
ab_prefix and is unique within the domain - relatedTables includes all tables
- SQL string constants use chr() concatenation
- ${dims} and ${filters} are positioned correctly
- NULLIF division-by-zero protection is in place
- validate passes
- Agent question + SQL bypass validation completed
Knowledge Base
- Calculation definition documentation covers all metrics in the domain
- Business rules documentation covers all enumeration values
- Analysis scenario documentation includes 3–5 SQL templates
- KB formulas are consistent with metric/AB
- Correct usage of ambiguous columns is annotated
Known Limitations and Workarounds
| Limitation | Workaround |
|---|---|
| GPT views do not support correlated subqueries | Use Answer Builder with GROUP BY instead |
| Virtual columns not persisted | Use inline CASE WHEN in metric expressions |
| Agent may not execute AB SQL templates | Write AB SQL to standard; verify with bypass testing |
| String constants require chr() concatenation | Memorize common ASCII codes |
| ${dims} column name fails across subquery boundaries | Wrap with SELECT * FROM (subquery) |
| metric rule cannot be configured via CLI | Use Answer Builder for dimension drill-down |
| No priority rules among three sources | KB as single source of truth; metric/AB reference KB |
Related Documents
- Answer Builder SQL Template Guide: In-depth guide on Answer Builder DSL structure, scenario patterns, and error reference
- Driving Agent Analysis Domain Modeling with CZ-CLI: Complete workflow for automated analysis domain modeling with CZ-CLI
- Configure Knowledge: Knowledge Base configuration best practices
- Troubleshooting Q&A Accuracy Issues: Systematic troubleshooting for Q&A accuracy problems
