Skip to content

Crossfield / For AI agents

Help AI agents use the reporting rules your team agreed on

Show an AI agent which metrics answer the question, let it inspect the calculation, and return useful errors when a request breaks the model’s rules.

An AI agent asked for sales before tax should not have to guess whether a column called total includes tax, which transactions count, or how to combine orders and receipt lines. Those decisions belong in the model. The agent’s job is to find the relevant definition and use it to answer the question.

This guide follows a recorded SQL session over the retail example. The same discovery and validation interfaces apply to the fulfillment and investment models. The session demonstrates the engine behavior without requiring a particular AI framework.

  1. DiscoverRead approved definitions and accepted inputs.
  2. AskChoose metrics, dimensions and a scope.
  3. CheckValidate the request and inspect its meaning.
  4. ExecuteRun the approved request through DuckDB.

If the request is rejected: revise it without changing the question, or ask the model owner for a definition.

The model supplies a contract the AI agent can inspect. Repair requires a supported alternative; a rejection does not authorize changing the business meaning.

Find the definition before writing the query

The request is for before-tax sales, grouped by store. The agent first finds the attached model, reads what its revenue metric means, and checks whether store is an allowed grouping. These are structured metadata queries, so the application does not have to extract the rules from a prompt or a paragraph of documentation.

See the discovery queries

These metadata calls let an AI agent find the model revision, metric meaning and allowed dimensions before constructing a request.

SELECT database_name, model_name, package_revision,
sql_reference, is_callable
FROM crossfield_models()
ORDER BY database_name;
SELECT name, sql_name, unit, comment
FROM crossfield_metrics()
WHERE database_name = 'jaffle_current'
ORDER BY name;
SELECT name, supported, compatible_dimensions
FROM crossfield_member_capabilities()
WHERE database_name = 'jaffle_current'
AND name = 'revenue';

Two attached models can both offer a metric called revenue and calculate it differently. Keep the selected model and revision with the query. In this example, revenue excludes tax and order_total includes it.

These metadata calls do not execute providers. A list of individually compatible dimensions is guidance, not approval of every possible combination. Validate the complete query.

Explain the request before executing it

The explanation identifies the definitions, dependencies and grouping and exports the generated SQL. It does not run the report. This gives the application an inspectable record of the intended calculation before it asks a provider to retrieve data.

Review how each result is calculated.

The store report combines revenue from orders with a count of receipt lines. Crossfield calculates each measure independently before joining the results by store. Reviewers can inspect that aggregation strategy in the generated SQL.

EXPLAIN (SEMANTIC) shows the selected definitions and generated SQL without executing the report or calling providers. This gives reviewers an explicit record of the calculation before execution.

How to inspect a semantic query →
OrdersOne revenue amount per purchase
Total revenue
Count purchases
Receipt linesOne record per purchased line
Count lines
at each store
Shared store dimensionRevenue + orders + receipt linesOne result per store
The generated plan keeps each calculation at its own grain before combining the answers. Receipt lines cannot multiply order revenue.
Inspect the semantic query

This request asks Crossfield to explain the retail report. It demonstrates how a reviewer can inspect the calculation without running the report.

SQL · Explain the store report
EXPLAIN (SEMANTIC)
SELECT store_name, order_count, item_count, revenue
FROM SEMANTIC jaffle_current.jaffle
GROUP BY store_name
ORDER BY store_name;
Read the complete generated SQL

This is the captured engine expansion behind the diagram. Look for the separate order and receipt-line aggregates, followed by their joins at the store dimension.

expanded_sql · Complete captured output
WITH "__crossfield_scope_24c10d4f-4e0f-4205-939c-a4ca24adfe0b"(
    __scope_column_0,
    __scope_column_1,
    __scope_column_2,
    __scope_column_3) AS (
        SELECT
            __scope_column_0 AS store_name,
            __scope_column_1 AS order_count,
            __scope_column_2 AS item_count,
            __scope_column_3 AS revenue
        FROM (
                WITH "__crossfield_fact_4c8b94a3-cdab-421d-b2d5-bd22199e99fd" AS (
                        SELECT
                            __cf_source_1."name" AS __cf_d0,
                            "system".main.count(__cf_source_0.id) AS __cf_metric_0,
                            "system".main.sum(__cf_source_0.subtotal) AS __cf_metric_1,
                            1 AS __cf_present
                        FROM (warehouse.main.orders AS __cf_source_0
                                INNER JOIN warehouse.main.stores AS __cf_source_1 ON ((__cf_source_0.store_id = __cf_source_1.id))
                            )
                        GROUP BY __cf_source_1."name"
                    ),
                    "__crossfield_fact_aa01cb34-1fb3-4c64-86f9-09e0cff0c5e4" AS (
                        SELECT
                            __cf_source_2."name" AS __cf_d0,
                            "system".main.count(__cf_source_0.id) AS __cf_metric_0,
                            1 AS __cf_present
                        FROM ((warehouse.main.items AS __cf_source_0
                                        INNER JOIN warehouse.main.orders AS __cf_source_1 ON ((__cf_source_0.order_id = __cf_source_1.id))
                                    )
                                INNER JOIN warehouse.main.stores AS __cf_source_2 ON ((__cf_source_1.store_id = __cf_source_2.id))
                            )
                        GROUP BY __cf_source_2."name"
                    )
                SELECT
                    __cf_keys.__cf_d0 AS store_name,
                    "__crossfield_fact_4c8b94a3-cdab-421d-b2d5-bd22199e99fd".__cf_metric_0 AS order_count,
                    "__crossfield_fact_aa01cb34-1fb3-4c64-86f9-09e0cff0c5e4".__cf_metric_0 AS item_count,
                    "__crossfield_fact_4c8b94a3-cdab-421d-b2d5-bd22199e99fd".__cf_metric_1 AS revenue
                FROM ((((
                                                SELECT __cf_d0
                                                FROM "__crossfield_fact_4c8b94a3-cdab-421d-b2d5-bd22199e99fd"
                                            )
                                        UNION (
                                                SELECT __cf_d0
                                                FROM "__crossfield_fact_aa01cb34-1fb3-4c64-86f9-09e0cff0c5e4"
                                            )
                                    ) AS __cf_keys
                                LEFT JOIN
                                    "__crossfield_fact_4c8b94a3-cdab-421d-b2d5-bd22199e99fd" ON ((__cf_keys.__cf_d0 IS NOT DISTINCT
                                                FROM "__crossfield_fact_4c8b94a3-cdab-421d-b2d5-bd22199e99fd".__cf_d0
                                            ))
                            )
                        LEFT JOIN
                            "__crossfield_fact_aa01cb34-1fb3-4c64-86f9-09e0cff0c5e4" ON ((__cf_keys.__cf_d0 IS NOT DISTINCT
                                        FROM "__crossfield_fact_aa01cb34-1fb3-4c64-86f9-09e0cff0c5e4".__cf_d0
                                    ))
                    )
                ORDER BY __cf_keys.__cf_d0
            ) AS __scope_input(__scope_column_0, __scope_column_1, __scope_column_2, __scope_column_3)
    )
SELECT
    __scope_column_0 AS store_name,
    __scope_column_1 AS order_count,
    __scope_column_2 AS item_count,
    __scope_column_3 AS revenue
FROM "__crossfield_scope_24c10d4f-4e0f-4205-939c-a4ca24adfe0b"

Captured from the matching build and retail fixture. Download the SQL · Download the full explanation as JSON. Generated aliases are not stable identities.

Execute the original semantic query to retain Crossfield’s validation. EXPLAIN (SQL) also binds and optimizes; binding can invoke provider callbacks. EXPLAIN ANALYZE executes.

Executing that semantic SELECT returns Brooklyn’s 19 orders, 36 lines and 255.00 before-tax revenue, and Philadelphia’s 506 orders, 798 lines and 6100.00. A successful explanation does not remove runtime checks for source identity, provider behavior or data-dependent contracts.

Correct a rejected query without changing the question

Select the current model and enable strict mode in this development session:

See the rejected request

The raw subtotal calculation is outside this strict session’s approved query interface. This is an intentional rejection, not a working report.

USE jaffle_current.semantic;
SET crossfield_mode = 'strict';
-- Deliberately rejected: ordinary raw-source aggregation.
SELECT SUM(subtotal) FROM warehouse.orders;

The recorded discovery session includes the native rejection. These fields are excerpted from its structured error:

Inspect the structured diagnostic

These fields come from the actual native error. Tools can recognize the rejection without parsing prose; they still need to preserve the user’s intended question.

{
"crossfield_code": "RAW_SOURCE",
"crossfield_stage": "compile",
"exception_message": "CROSSFIELD_RAW_SOURCE: Read approved definitions with FROM SEMANTIC; physical sources are not approved"
}

The approved equivalent for this before-tax question is:

See the approved query

Revenue is the model’s approved before-tax definition. This replacement preserves the subtotal question; replacing a tax-inclusive total would not.

SELECT revenue FROM SEMANTIC jaffle_current.jaffle;
-- 6355.00

SUM(total) asks a different question: the shop’s totals include tax. Its corresponding approved metric is order_total, with expected result 6730.75. Substituting revenue would silently change the question.

If the caller asks to apply an unapproved transformation to revenue, renaming the result does not grant that permission. If no approved definition preserves the requested calculation, report the missing capability and ask the model owner for a definition. A repair must preserve the filters, included records, and governing policy. If it cannot, the application should identify the missing definition for review.

Hosts can configure errors_as_json=true before entering strict mode and sealing the session. Diagnostics provide native codes and structured repair information, distinguishing a query correction from model-owner action, a source refresh or a runtime failure. Discovery reference describes the interfaces.

Find which inputs a model accepts

Some models need an input scope: a repository, an organization, a date range or another approved argument. Discover the complete signatures rather than reconstructing combinations from prose:

See signature discovery

This query returns complete callable signatures. It helps a client choose a valid scope without inferring mutually exclusive inputs from prose.

SELECT database_name, sql_name, signature_id, signature, parameters
FROM crossfield_signatures()
ORDER BY database_name, sql_name, signature_id;

A signature identifier is for metadata and diagnostics; callers do not supply it in SQL. DuckDB matches the declared argument names and types. Crossfield validates the approved values before execution. Optional inputs are keyword-only; a nullable required input is still required.

The runnable pipeline example demonstrates positional input and an alternative named scope over one set of semantic definitions. In a provider model, the chosen signature can select a model-owned retrieval pipeline. The caller need not choose a provider ID or write the dependency’s lateral join.

Set up the application’s controls

Strict mode controls supported SQL authoring, not the application around it. An unsealed caller can change modes. A trusted host must establish and seal the intended context if users must not be able to disable restrictions.

Keep authentication, source credentials, function/module loading and native isolation under host control. A permitted query can still select an approved metric or set of records that does not match the intended business question. Use the explanation’s canonical output identities, not a column label alone, when presenting a result as an approved metric.

Read the host boundaries and engine lifecycle rationale. These controls support an AI agent’s work; they are not a claim that every generated answer is correct.