Przejdź do głównej zawartości

OSL — warstwa konsumpcji

OpenDome Semantic Layer (OSL) to otwarta specyfikacja (Apache-2.0), która opisuje — w deklaratywnym YAML-u — model semantyczny obejmujący dwie fizyczne płaszczyzny: tabele ustrukturyzowane w Iceberg i kolekcje wektorowe w Lance. Jedna specyfikacja, jeden język, jeden łańcuch nadzoru.

Ta strona ustala słownictwo i model myślowy. Po normatywny kontrakt sięgnij do specyfikacji; po powierzchnię API zajrzyj do referencji.

Dzisiejsze warstwy semantyczne OSS (dbt MetricFlow, Cube, WrenAI) opisują tabele relacyjne. Żadna nie traktuje kolekcji wektorowych jako obywateli pierwszej kategorii. To wymusza dwa równoległe modele — semantyczno-SQL-owy i „to, co RAG wie o kliencie“ — które rozjeżdżają się następnego dnia po wdrożeniu.

OSL obejmuje obie płaszczyzny:

  1. Ustrukturyzowana — encje, metryki, wymiary, kontrakty danych. Tutaj OSL jest nadzbiorem dbt MetricFlow: każdy poprawny projekt MetricFlow jest poprawnym OSL.
  2. Nieustrukturyzowana — kolekcje Lance z ich chunk schema, embeddingami, indeksami, filtrami metadanych i ACL na poziomie wiersza. Rozszerzenie OSL-only.
  3. MostJointEntity, obiekt logiczny (Customer, Policy, Order), który łączy kolumny Iceberg z fasetami Lance pod jednym kluczem głównym.

Deklarujesz raz. Nadzór, lineage, API i agenci konsumują ten sam model.

Model

USTRUKTURYZOWANE · ICEBERGNIEUSTRUKTURYZOWANE · LANCEJOINTENTITY
Jedna deklaratywna specyfikacja obejmuje obie fizyczne płaszczyzny; JointEntity łączy je pod jednym kluczem.

OSL opisuje model semantyczny nad dwoma fizycznymi substratami jednym deklaratywnym językiem. Każda płaszczyzna ma własne prymitywy, własną konwencję FQN, własny silnik wykonawczy i własną ścieżkę ewolucji. JointEntity to element, który je składa.

Tabele Iceberg, zapytania SQL, agregacje, joiny. Tutaj OSL przepuszcza MetricFlow bez zmian:

  • semantic_models/*.yml opisują encje fizyczne wraz z ich wymiarami i measures.
  • metrics/*.yml definiują certyfikowane metryki (simple, ratio, cumulative, derived).
  • saved_queries/*.yml pakują wielokrotnie używane zapytania.
  • time_spine.yml definiuje ziarno czasowe projektu.

Silnik referencyjny (tenant-semantic-api) kompiluje te pliki YAML do SQL nad Trino, wykonywanego na Iceberg w bucketcie tenanta.

Kolekcje Lance z zaindeksowanymi chunkami. Tutaj OSL definiuje nowe prymitywy:

  • facets/*.yml (kind: UnstructuredFacet) opisuje kolekcję Lance: jej dataset path, chunk schema, model embeddingu, indeksy (wektor + FTS + skalarny), pola metadanych, ACL na poziomie wiersza i limity retrievalu.

Silnik wykonuje hybrydowy retrieval (wektor ⊕ BM25 ⊕ filtr metadanych) nad LanceDB i zwraca fragmenty z cytowaniem, respektując zadeklarowane ACL i limity.

  • joints/*.yml (kind: JointEntity) deklaruje obiekt logiczny, którego klucz główny pochodzi z semantic_model i który eksponuje atrybuty złożone — część z płaszczyzny ustrukturyzowanej (kolumny, metryki), część z nieustrukturyzowanej (retrieval ograniczony do tego klucza).

Most jest uczciwy: każde join_on mapuje klucz encji na autorytatywne, zaindeksowane pole metadanych fasety. Pole z provenance: derived nigdy nie może być kluczem joina — derywowana ekstrakcja dzieje się przy ingest, nigdy w query time.

Agent wywołujący entity_get(Customer, customer_id=X) otrzymuje w jednej odpowiedzi kanoniczne atrybuty encji, mające zastosowanie metryki preagregowane oraz chunki Lance odfiltrowane po customer_id=X. Jedno wywołanie, jedna decyzja polityki, jeden ślad audytu.

Choć specyfikacja ma osobne prymitywy (SemanticModel, JointEntity), kto konsumuje OSL, widzi jedno słowo: Entity. To, co odróżnia jedną Entity od drugiej, to nie jej kind, lecz jej kompozycja:

Kompozycja Czym jest Skąd pochodzi
structured Czysto tabelaryczna Entity (tylko Iceberg) SemanticModel, który nie jest ani kotwicą jointa, ani mostem
structured+content Kolumny ustrukturyzowane połączone z fasetami Lance JointEntity z nogami nieustrukturyzowanymi
content Samodzielny korpus Lance, bez kotwicy ustrukturyzowanej UnstructuredFacet, którego żaden joint nie wchłania

Relacje między Entities pochodzą z dwóch miejsc: współdzielonych encji MetricFlow (primary / foreign / unique) dla sąsiadów ustrukturyzowany ↔ ustrukturyzowany oraz N-N przez most — tabela łącząca (SemanticModel z ≥2 encjami foreign), która zwija się do bezpośredniej krawędzi N-N zamiast być węzłem.

To słownictwo serwuje domain-map (GET /osl/domain-map): jeden rodzaj węzła entity oznaczony kompozycją, plus węzły metric i candidate.

Rodzina Kind Cel MetricFlow
Modelowanie ustrukturyzowane SemanticModel, Metric, SavedQuery, TimeSpine Opisać tabele Iceberg + metryki ✅ identyczne / rozszerzone
Modelowanie nieustrukturyzowane UnstructuredFacet Opisać kolekcję Lance ❌ OSL-only
Kompozycja JointEntity Połączyć obie płaszczyzny pod jedną encją logiczną ❌ OSL-only
Katalog i kontrakty Lexicon, DataContract, PolicyBinding Synonimika, SLA, hinty dla PDP ❌ OSL-only

Każdy prymityw niesie apiVersion: osl.opendome.eu/v1, kind i unikalny name. Tożsamość obiektu to (kind, name) — z wyjątkiem SemanticModel, którego tożsamość to (catalog, schema, table).

OSL deklaruje; silnik wykonuje. Między nimi jest krok kompilacji.

1 · Author

Pisz YAML ręcznie albo z modelera. Konwencja: project.yml + models/ + metrics/ + facets/ + joints/ + lexicon/ + contracts/.

2 · Validate

osl validate uruchamia JSON Schema per plik plus cross-reference (każdy referowany FQN musi istnieć). Błędy w E0xx (schema) lub E1xx (resolve).

3 · Compile

osl compile emituje pojedynczy target/manifest.json — kanoniczną, odtwarzalną reprezentację, którą serwuje silnik i czyta policy engine.

4 · Publish

osl publish wgrywa manifest do silnika tenanta. Atomowo (revert przez osl publish --revert), emituje zdarzenie lineage osl.publish.

Od tego momentu silnik serwuje zapytania: REST dla BI/aplikacji, narzędzia MCP dla agentów.

OSL jest rygorystyczny co do nazw, bo polityki od nich zależą. Cztery przestrzenie FQN:

Przestrzeń Forma Przykład
Ustrukturyzowana <catalog>.<schema>.<table>[:<column>] iceberg.curated.customer:email
Nieustrukturyzowana lance://<collection>[/*][:<metadata_field>] lance://meeting_transcripts:attendees
Narzędzia <tool_kind>:<resource> metrics:revenue, joints:Customer
Joint-exposed joint://<Entity>.<attribute> joint://Customer.recent_meeting_topics

FQN-y rozróżniają wielkość liter i nie niosą wersji — wersjonowanie żyje w skompilowanym, podpisanym SHA manifeście. Policy engine konsumuje te FQN-y w swojej 4-wymiarowej krotce (data, action, actor, context). OSL nie wymyśla równoległej semantyki dostępu; emituje fakty, które PDP indeksuje.

  • Nie jest silnikiem SQL. Jest nim Trino; OSL nadaje mu znaczenie.
  • Nie jest orkiestratorem. Dagster wykonuje Joby ingest/embedding.
  • Nie jest bazą wektorową. Jest nią LanceDB; OSL opisuje kolekcje.
  • Nie jest systemem IAM. Jest nim OPA / cell-model; OSL emituje fakty, które konsumuje PDP.
  • Nie jest narzędziem BI. Metabase, Superset, Streamlit konsumują OSL.
  • Nie jest frameworkiem agentowym. LangGraph, serwery MCP konsumują OSL.

OSL to wspólna specyfikacja między wszystkimi tymi częściami, a nie same części.