Back to BlogEngineering

Before you connect AI to your data, teach it your business language

Tal Cohen| CTO & Co-founderAugust 30, 2026
Before you connect AI to your data, teach it your business language

Why we created a lightweight business ontology, and why a skill is not enough

Every builder eventually hits the same wall with AI.

You connect Claude or ChatGPT to your data. You add a few skills. You give it the database schema. You write a careful prompt. Then you ask what sounds like a simple business question:

"Why did revenue decline last month?"

The AI comes back with a polished report. The logic looks clean. The SQL looks plausible. The charts look convincing.

Then the CFO points out the problem: the answer mixed gross merchandise value with net settled revenue, included test orders, and counted canceled subscriptions.

The AI did not fail because it could not calculate. It failed because it did not understand the business language.

It could access Shopify, Google Analytics, ad platforms, the CRM, and the warehouse. It could write SQL. It may even have had a marketing analytics skill.

But it still did not know what "revenue" means in that company.

Is revenue gross merchandise value or net revenue after refunds? Does it include subscription renewals? Should the comparison use the previous month, the same period last year, or a campaign target? Does "mobile" include mobile web, the iOS app, the Android app, and traffic labeled mweb?

What an ontology is

An ontology is a shared, machine readable model of a business.

It defines the things the business cares about like: customers, orders, campaigns, subscriptions, products. It defines the measures used to run the business: revenue, conversion rate, customer acquisition cost, retention. It also defines how those things relate and which names refer to the same idea.

Most companies already have an ontology. It is just scattered.

Parts of it live in dashboard names, SQL queries, spreadsheet formulas, documentation, Skills, and people's heads. That works until two people, two teams, or two AI workflows use the same word differently.

Finance reports net revenue. Growth reports campaign revenue. Merchandising reports product sales. All three may be correct, but they are not answering the same question.

AI makes this problem visible because it has to choose a definition. If the definition is not explicit, it infers one from column names, dashboards, old SQL, and prompts. The answer may sound credible while using the wrong metric, timeframe, segment, or source.

Business Ontology
Business Ontology

What a skill cannot provide

A skill can tell AI what to do.

For example:

"When asked about revenue, compare the current period with the previous period. Break the result down by channel, campaign, device, and product category. Summarize the main drivers."

That is useful, but it assumes the AI already knows the definitions behind the task.

It still needs agreed answers to basic questions:

Which metric is revenue?

Which transactions should be excluded?

What counts as a completed order?

Which channels belong to Paid Social?

Does a canceled subscription count as an active customer?

Can marketing and sales data be combined for this analysis?

A skill packages instructions and resources for a repeatable workflow. The business definitions beneath that workflow need to live somewhere else.

The skill answers: "What should I do?"

The ontology answers: "What are we talking about?"

AI needs both.

The issue appears before AI arrives

The problem is not new. AI simply makes it faster and harder to notice.

Before AI, this led to dashboard arguments, spreadsheet reconciliation, and Email threads about whose number was right. With AI, the same ambiguity can produce a polished answer before anyone notices that the underlying definition was wrong.

If the meaning is not explicit, the AI guesses.

What existing products solve

Products such as dbt, Snowflake, Databricks, Microsoft Fabric, Palantir, Cube, and similar semantic layer tools help teams define metrics, dimensions, and relationships for consistent queries.

They are valuable when the organization already has a modeled data platform and a team ready to manage business logic around tables, columns, joins, and SQL.

For many companies, that is not where the work starts.

They may have a warehouse, a CRM, an e-commerce platform, marketing tools, product analytics, spreadsheets, and several AI tools. The definitions still live across dashboards, SQL, documents, and people. They need a way to give AI business context before they complete a major semantic modeling project.

That is the gap we wanted to solve: a small, easy to maintain, business language that people can review, AI can parse, and developers can map to the systems already in use.

Separate business meaning from physical data

Our language has four parts:

Entities, such as customer, order, campaign, subscription, and product.

KPIs, such as revenue, conversion rate, customer acquisition cost, and retention.

Relationships, such as an order belonging to a customer or a campaign driving sessions.

Value maps, so mweb, mobile_web, and phone can all mean mobile.

It deliberately excludes physical table names, SQL joins, and warehouse-specific column names. Those belong in a separate mapping layer.

The business definition can remain stable when the company replaces a platform, renames a column, or adds another source.

Before: business knowledge in a paragraph

This is how the same information often appears today:

"Net revenue means paid Shopify orders minus refunds. Exclude test orders and cancelled orders. We compare month-to-date with the equivalent number of completed days in the prior month. For marketing reporting, group Facebook, Instagram, and Meta Audience Network under Paid Social. Mobile includes mobile web and iOS/Android app traffic. Revenue is affected by sessions, conversion rate, and average order value."

That paragraph is useful, but different people can apply it differently. A skill may include only part of it. An AI may miss a condition. A developer may encode another version in SQL.

After: the same knowledge in a shared contract

Instead of copying the same paragraph into every prompt, dashboard, and implementation, the knowledge becomes a structured contract.

Here is a shortened example:

ontology_id: ecommerce
ontology_version: "0.1"
formula_schema_version: "1"
entities:
  - id: customer
    entity_type: definition
    description: a person or business that has placed an order.
    synonyms: [buyer, shopper]
    identity_attributes: [customer.customer_id]
    attributes:
      - {id: customer.customer_id, role: dimension, data_type: string}
  - id: session
    entity_type: event
    description: a visit to the e-commerce site or app.
    identity_attributes: [session.session_id]
    time_attributes: [session.started_at]
    attributes:
      - {id: session.session_id, role: dimension, data_type: string}
      - {id: session.started_at, role: time, data_type: datetime}
      - {id: session.channel, role: dimension, data_type: string, value_map: value_map.channel}
      - {id: session.device, role: dimension, data_type: string, value_map: value_map.device}
  - id: order
    entity_type: event
    description: an e-commerce purchase record.
    synonyms: [purchase, transaction]
    identity_attributes: [order.order_id]
    time_attributes: [order.completed_at]
    attributes:
      - {id: order.order_id, role: dimension, data_type: string}
      - {id: order.completed_at, role: time, data_type: datetime}
      - {id: order.customer_id, role: dimension, data_type: string, reference: customer.customer_id}
      - {id: order.session_id, role: dimension, data_type: string, reference: session.session_id, required: false}
      - {id: order.status, role: dimension, data_type: string, value_map: value_map.order_status}
      - id: order.net_revenue_amount
        role: measure
        data_type: decimal
        unit: currency
        description: revenue amount after refunds for completed, non-test orders; zero or null for ineligible orders.
kpis:
  - id: sessions
    description: number of distinct e-commerce sessions.
    unit: count
    expr_ast:
      type: agg
      function: count_distinct
      scope_entity: session
      arg: {type: ref, kind: attribute, id: session.session_id}
  - id: completed_orders
    description: number of distinct completed e-commerce orders.
    unit: count
    expr_ast:
      type: filter
      input:
        type: agg
        function: count_distinct
        scope_entity: order
        arg: {type: ref, kind: attribute, id: order.order_id}
      where:
        type: eq
        left: {type: ref, kind: attribute, id: order.status}
        right: {type: value_ref, value_map: value_map.order_status, id: completed}
  - id: conversion_rate
    description: percentage of sessions that result in a completed order.
    unit: percent
    expr_ast:
      type: mul
      left:
        type: safe_div
        numerator: {type: ref, kind: kpi, id: completed_orders}
        denominator: {type: ref, kind: kpi, id: sessions}
        on_zero: null
      right: {type: const, value: 100, data_type: decimal}
  - id: average_order_value
    description: average net revenue per completed order.
    synonyms: [aov]
    unit: currency
    expr_ast:
      type: safe_div
      numerator: {type: ref, kind: kpi, id: net_revenue}
      denominator: {type: ref, kind: kpi, id: completed_orders}
      on_zero: null
  - id: net_revenue
    description: net revenue from completed, non-test orders after refunds.
    synonyms: [revenue, ecommerce revenue, sales]
    unit: currency
    influenced_by: [sessions, conversion_rate, average_order_value]
    expr_ast:
      type: agg
      function: sum
      scope_entity: order
      arg: {type: ref, kind: attribute, id: order.net_revenue_amount}
relationships:
  - {from: order, relationship: belongs_to, to: customer, cardinality: "n:1"}
  - {from: order, relationship: occurs_within, to: session, cardinality: "n:1"}
value_maps:
  - id: value_map.order_status
    reference: order.status
    description: canonical lifecycle status of an e-commerce order.
    values:
      - {value: completed, synonyms: [paid, fulfilled]}
      - {value: cancelled, synonyms: [canceled, voided]}
      - {value: pending, synonyms: [processing, open]}
  - id: value_map.channel
    reference: session.channel
    description: canonical acquisition channel for an e-commerce session.
    values:
      - {value: paid_social, synonyms: [facebook, instagram, meta, meta audience network]}
  - id: value_map.device
    reference: session.device
    description: canonical device category for an e-commerce session.
    values:
      - {value: mobile, synonyms: [mobile_web, mweb, phone, ios, android]}

The ontology defines net_revenue and mobile. The mapping layer identifies the Shopify fields, warehouse tables, API calls, and transformations that supply them.

Keeping that implementation outside the ontology prevents physical details from leaking into the business language.

A business leader does not need to write YAML. They can describe the definitions in ordinary language. A user interface or AI can turn those definitions into a structured ontology that tools can validate and use.

Example: a SaaS company asks, "How many active customers do we have?"

A SaaS company may have a CRM, billing platform, product analytics tool, and customer-success system.

Sales may count signed contracts. Billing may count paying subscriptions. Product may count accounts with a user active in the last 30 days. Customer Success may count accounts that completed onboarding.

A skill called "SaaS Health Report" can apply one of those definitions. It cannot decide which one the company intends.

The ontology makes the distinction clear:

kpis:
  - id: active_customer
    description: a paying subscription account with at least one active user in the prior 30 days.
    unit: count
    influenced_by: [paying_subscriptions, product_usage]
    expr_ast:
      type: filter
      input:
        type: agg
        function: count_distinct
        scope_entity: customer
        arg: {type: ref, kind: attribute, id: customer.customer_id}
      where:
        type: and
        args:
          - type: eq
            left: {type: ref, kind: attribute, id: subscription.status}
            right: {type: value_ref, value_map: value_map.subscription_status, id: active}
          - type: eq
            left: {type: ref, kind: attribute, id: subscription.is_paying}
            right: {type: const, value: true, data_type: boolean}
          - type: gte
            left: {type: ref, kind: attribute, id: product_usage.last_active_at}
            right:
              type: date_add
              unit: day
              amount: {type: const, value: -30, data_type: integer}
              input: {type: current_date}
  - id: at_risk_customer
    description: an active customer whose product usage has declined materially and whose renewal is due within 90 days.
    unit: count
    influenced_by: [active_customer, product_usage, renewal]
    expr_ast:
      type: filter
      input:
        type: agg
        function: count_distinct
        scope_entity: customer
        arg: {type: ref, kind: attribute, id: customer.customer_id}
      where:
        type: and
        args:
          - type: eq
            left: {type: ref, kind: attribute, id: subscription.status}
            right: {type: value_ref, value_map: value_map.subscription_status, id: active}
          - type: eq
            left: {type: ref, kind: attribute, id: subscription.is_paying}
            right: {type: const, value: true, data_type: boolean}
          - type: gte
            left: {type: ref, kind: attribute, id: product_usage.last_active_at}
            right:
              type: date_add
              unit: day
              amount: {type: const, value: -30, data_type: integer}
              input: {type: current_date}
          - type: lt
            left: {type: ref, kind: attribute, id: product_usage.current_period_usage}
            right:
              type: mul
              left: {type: ref, kind: attribute, id: product_usage.previous_period_usage}
              right: {type: const, value: 0.7, data_type: decimal}
          - type: lte
            left: {type: ref, kind: attribute, id: subscription.renewal_date}
            right:
              type: date_add
              unit: day
              amount: {type: const, value: 90, data_type: integer}
              input: {type: current_date}
value_maps:
  - id: value_map.subscription_status
    reference: subscription.status
    description: canonical lifecycle status of a subscription.
    values:
      - {value: active, synonyms: [live, current, enabled]}
      - {value: cancelled, synonyms: [canceled, terminated]}
      - {value: paused, synonyms: [suspended, on_hold]}
      - {value: expired, synonyms: [ended, lapsed]}

The CEO can then ask separately about paying customers, active product customers, and customers at risk. A decline can be traced to churn, lower usage, or an incomplete data feed.

Example: deciding which campaigns to scale

A growth team asks:

"Which campaigns should we scale?"

A generic skill can rank campaigns by return on ad spend. A decision also depends on what revenue means, how long conversions are attributed after a click, which campaigns have enough volume, and whether the campaign has budget, inventory, or audience constraints.

The ontology can define campaign, channel, spend, attributed revenue, conversion, customer acquisition cost, return on ad spend, and the relationships between them. It can also state that paid_social includes Facebook and Instagram, while preserving platform-level values for drill-down.

That supports an answer such as:

"Campaign A has the highest short-term ROAS, but only 14 conversions. Campaign B has lower ROAS but produced 240 new customers at a stable acquisition cost and still has budget headroom. Scale B first. Keep monitoring A until there is enough evidence."

Building AI that survives change

Builders need a stable contract between AI and business data.

Without one, every prompt, skill, connector, dashboard, and application must rediscover what the business means. Definitions drift. A warehouse migration breaks instructions that refer to old fields. A new source introduces another name for the same customer, campaign, or device.

A standalone ontology gives builders a portable business model. They can use it with Claude, ChatGPT, BI tools, internal applications, or an agentic platform such as Luke.

Organizations can maintain their definitions once, test them, version them, and use them across tools.

The full language design, schema, and examples are available in the project GitHub repository.

This article covers the business layer. The next article will cover the mapping layer: how to connect these stable business concepts to physical sources, transformations, and joins without embedding warehouse details in the ontology.

AI won't break because your data warehouse is messy, it will break because your business definitions are ambiguous. If two humans in your company can't agree on what a "revenue" is over coffee, you can't expect an LLM to guess correctly at 2:00 AM.

Start small. Pick the three metrics that cause the most fights in your executive meetings, lock down their ontology, and hand your AI the Rosetta Stone it actually needs.

Have a question? need help?