> For the complete documentation index, see [llms.txt](https://docs.carto.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.carto.com/carto-user-manual/ai-agents/semantic-model.md).

# Semantic model

A semantic model is a structured layer of business meaning over the data sources on your map. It tells an AI Agent what your tables and columns mean, how your datasets relate, how your metrics are calculated, and how your spatial data is structured, so the Agent writes correct SQL and applies your definitions consistently.

It builds on [Apache Ossie](https://ossie.apache.org/), the open standard for describing what data means, extended by CARTO with spatial context.

<figure><img src="/files/dSx7u1jywYs4NQYebeJj" alt=""><figcaption></figcaption></figure>

### Creating a semantic model

There are three ways to add a semantic model when configuring an AI Agent:

* **Write or edit it directly.** The Agent Configuration dialog includes a built-in editor where you define datasets, fields, metrics, relationships, and spatial context, following the Apache Ossie structure.
* **Import a YAML file.** Bring in an existing Ossie-based semantic model as YAML.
* **Let the** [**Agent Config Assistant**](/carto-user-manual/ai-agents/agent-config-assistant.md) **build or edit it.** Describe your data in plain language and the Assistant writes or updates the semantic model for you.

Whichever way you choose, CARTO validates that the model is well-formed YAML.

### Semantic model structure

A semantic model is YAML. It declares a `version` and a `semantic_model` list, and each entry describes:

* **Datasets** and their **fields** — the tables and columns on your map, with plain-language meaning.
* **Relationships** — how datasets join.
* **Metrics** — reusable calculations the Agent applies consistently, so the same question returns the same answer.

These follow the Apache Ossie standard; see the [Apache Ossie documentation](https://ossie.apache.org/) for the full field reference. The [example](#semantic-model-example) below shows them in context.

{% hint style="info" %}
An Ossie relationship joins two datasets on matching column values, like a shared ID. Some spatial joins have no shared column, for example towers within 10 km of a fire, or points inside a polygon. Ossie cannot express that geometric condition, so describe the spatial join in the relationship's `ai_context` (for example, "join with ST\_DWITHIN within 10 km") and the Agent uses it when writing the query.
{% endhint %}

### The CARTO spatial extension

The only CARTO-specific part of the model is a field-level `custom_extensions` entry with `vendor_name: CARTO`, added only on fields that carry spatial data. Non-spatial fields carry no extension. Its `data` value is a string containing serialized JSON with a single `spatial_data` object.

**`type`** (required) — the kind of spatial data in the field:

* `point`, `line`, `polygon` — vector geometries.
* `raster` — raster data.
* `spatial_index` — an H3 or Quadbin index column.

**`srid`** — the EPSG code of the coordinate system, for example `4326` (WGS84). Include it for geometry types (`point`, `line`, `polygon`, `raster`). Not used for `spatial_index`.

**`geography`** — include it **only** when the field is a nested geographic level in an administrative hierarchy (for example country → state → county). Do not add it to per-feature geometry such as a store point, an event path, or a route.

* `level` — the name of this level, for example `census_block_group`.
* `hierarchy.parent` — the adjacent coarser level, for example `census_tract`.
* `hierarchy.children` — the adjacent finer levels, for example `["census_block"]`.

**`spatial_index`** (required when `type` is `spatial_index`):

* `system` — `h3` or `quadbin`.
* `resolution` — the index resolution as an integer (for example `8`). Include it only when you know it; do not guess.
* `hierarchy` — optional, the adjacent roll-up resolutions as integers, for example `{ "parent": 7, "children": [9] }` for an H3 resolution-8 cell.

**Examples**

```yaml
# Point geometry
data: '{"spatial_data": {"type": "point", "srid": 4326}}'

# Polygon at a nested geographic level
data: '{"spatial_data": {"type": "polygon", "srid": 4326, "geography": {"level": "state", "hierarchy": {"parent": "country", "children": ["county"]}}}}'

# Quadbin spatial index with roll-up levels
data: '{"spatial_data": {"type": "spatial_index", "spatial_index": {"system": "quadbin", "resolution": 12, "hierarchy": {"parent": 11, "children": [13]}}}}'
```

{% hint style="warning" %}
`data` is always a string containing serialized JSON, not a nested YAML object. Quote the whole object.
{% endhint %}

#### Semantic model example

A model with two datasets: census block group polygons and the same demographics on an H3 grid. Note the field-level CARTO `spatial_data` extension on the spatial fields, and the population-weighted metric.

{% code overflow="wrap" %}

```yaml
version: "0.1.1"
semantic_model:
  - name: "US Census demographics"
    description: "Census demographics in two geographic forms: block group polygons and an H3 grid."
    datasets:
      # Dataset 1: Census block groups (polygon geometry)
      - name: "census_block_groups"
        source: "carto-dw.demographics.census_block_groups"
        fields:
          - name: "geom"
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: "geom"
            description: "Census Block Group polygon boundary (WGS84)"
            custom_extensions:
              - vendor_name: CARTO
                data: '{"spatial_data": {"type": "polygon", "srid": 4326, "geography": {"level": "census_block_group", "hierarchy": {"parent": "census_tract", "children": []}}}}'
          - name: "median_income"
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: "median_income"
            description: "Median household income (USD)"
            ai_context:
              instructions: "When aggregating across block groups, use a population-weighted average: SUM(median_income * pop) / SUM(pop)."
              synonyms: ["income", "household income"]
          - name: "pop"
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: "pop"
            description: "Total population in the block group"

      # Dataset 2: Same demographics on the H3 grid (resolution 8)
      - name: "census_demographics_h3"
        source: "carto-dw.demographics.census_demographics_h3"
        fields:
          - name: "h3"
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: "h3"
            description: "H3 cell index at resolution 8 — spatial join key"
            custom_extensions:
              - vendor_name: CARTO
                data: '{"spatial_data": {"type": "spatial_index", "spatial_index": {"system": "h3", "resolution": 8, "hierarchy": {"parent": 7, "children": [9]}}}}'

    relationships:
      - name: "bg_to_h3"
        from: "census_block_groups"
        to: "census_demographics_h3"
        from_columns: ["geoid"]
        to_columns: ["geoid"]
        ai_context: "Same demographics in two geographic forms; join on the shared geoid."

    metrics:
      - name: "avg_income_weighted"
        expression:
          dialects:
            - dialect: ANSI_SQL
              expression: "SUM(census_block_groups.median_income * census_block_groups.pop) / SUM(census_block_groups.pop)"
        description: "Population-weighted average household income"
```

{% endcode %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.carto.com/carto-user-manual/ai-agents/semantic-model.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
