Zum Inhalt springen

OSL — die Konsumschicht

Die OpenDome Semantic Layer (OSL) ist eine offene Spezifikation (Apache-2.0), die in deklarativem YAML ein semantisches Modell beschreibt, das zwei physische Ebenen umspannt: strukturierte Tabellen in Iceberg und Vektor-Collections in Lance. Eine Spec, eine Sprache, eine Governance-Kette.

Diese Seite legt das Vokabular und das mentale Modell fest. Für den normativen Vertrag siehe die Spezifikation; für die API-Oberfläche siehe die API-Referenz.

Die heutigen OSS-Semantikschichten (dbt MetricFlow, Cube, WrenAI) beschreiben relationale Tabellen. Keine behandelt Vektor-Collections als gleichberechtigte Bürger. Das erzwingt zwei parallele Modelle — das SQL-semantische und „das, was das RAG über den Kunden weiß“ — die schon am Tag nach dem Release auseinanderdriften.

OSL umspannt beide Ebenen:

  1. Strukturiert — Entities, Metriken, Dimensionen, Data Contracts. Hier ist OSL ein Superset von dbt MetricFlow: Jedes gültige MetricFlow-Projekt ist gültiges OSL.
  2. Unstrukturiert — Lance-Collections mit ihrem Chunk-Schema, Embeddings, Indizes, Metadata-Filtern und row-level ACL. Die OSL-only-Erweiterung.
  3. Die Brücke — die JointEntity, ein logisches Objekt (Customer, Policy, Order), das Iceberg-Spalten unter einem einzigen Primärschlüssel mit Lance-Facetten verbindet.

Einmal deklarieren. Governance, Lineage, APIs und Agenten konsumieren alle dasselbe Modell.

Das Modell

STRUKTURIERT · ICEBERGUNSTRUKTURIERT · LANCEJOINTENTITY
Eine einzige deklarative Spec umspannt beide physischen Ebenen; die JointEntity verbindet sie unter einem einzigen Schlüssel.

OSL beschreibt ein semantisches Modell über zwei physischen Substraten mit einer einzigen deklarativen Sprache. Jede Ebene hat ihre eigenen Primitiven, ihre FQN-Konvention, ihre Ausführungs-Engine und ihren Evolutionspfad. Die JointEntity ist das Stück, das sie zusammensetzt.

Iceberg-Tabellen, SQL-Queries, Aggregationen, Joins. Hier reicht OSL MetricFlow unverändert durch:

  • semantic_models/*.yml beschreiben physische Entities mit ihren Dimensionen und Measures.
  • metrics/*.yml definieren zertifizierte Metriken (simple, ratio, cumulative, derived).
  • saved_queries/*.yml paketieren wiederverwendbare Queries.
  • time_spine.yml definiert die zeitliche Granularität des Projekts.

Die Referenz-Engine (tenant-semantic-api) kompiliert diese YAML zu SQL über Trino, ausgeführt gegen Iceberg im Bucket des Tenants.

Lance-Collections mit indizierten Chunks. Hier definiert OSL neue Primitiven:

  • facets/*.yml (kind: UnstructuredFacet) beschreiben eine Lance-Collection: Dataset-Pfad, Chunk-Schema, Embedding-Modell, Indizes (Vektor + FTS + skalar), Metadata-Felder, row-level ACL und Retrieval-Limits.

Die Engine führt hybrides Retrieval (Vektor ⊕ BM25 ⊕ Metadata-Filter) über LanceDB aus und gibt Passagen mit Zitation zurück, wobei sie die deklarierten ACL und Limits respektiert.

  • joints/*.yml (kind: JointEntity) deklariert ein logisches Objekt, dessen Primärschlüssel von einem semantic_model stammt und das zusammengesetzte Attribute bereitstellt — einige aus der strukturierten Ebene (Spalten, Metriken), einige aus der unstrukturierten Ebene (Retrieval, gescoped durch den Schlüssel).

Die Brücke ist ehrlich: Jedes join_on mappt einen Entity-Schlüssel auf ein autoritatives, indiziertes Metadata-Feld der Facette. Ein Metadata-Feld mit provenance: derived kann niemals ein Join-Schlüssel sein — abgeleitete Extraktion passiert beim Ingest, nie zur Query-Zeit.

Ein Agent, der entity_get(Customer, customer_id=X) aufruft, erhält in einer einzigen Antwort die kanonischen Attribute der Entity, die zutreffenden vor-aggregierten Metriken und die nach customer_id=X gefilterten Lance-Chunks. Ein Aufruf, eine Policy-Entscheidung, ein Audit-Trail.

Obwohl die Spec separate Primitiven hat (SemanticModel, JointEntity), sieht der Konsument ein einziges Wort: Entity. Was eine Entity von einer anderen unterscheidet, ist nicht ihr kind, sondern ihre Komposition:

Komposition Was es ist Woher es kommt
structured Rein tabellarische Entity (nur Iceberg) ein SemanticModel, das weder Joint-Anker noch Brücke ist
structured+content Strukturierte Spalten, verbunden mit Lance-Facetten eine JointEntity mit unstrukturierten Beinen
content Ein eigenständiges Lance-Korpus, ohne strukturierten Anker eine UnstructuredFacet, die kein Joint absorbiert

Relationen zwischen Entities stammen aus zwei Quellen: gemeinsame MetricFlow-Entities (primary / foreign / unique) für strukturiert ↔ strukturiert benachbarte, und N-N über eine Brücke — eine Junction-Tabelle (SemanticModel mit ≥2 foreign Entities), die zu einer direkten N-N-Kante kollabiert, statt ein Knoten zu sein.

Dieses Vokabular ist das, was die domain-map (GET /osl/domain-map) bereitstellt: ein einziger entity-Knotentyp, getaggt nach Komposition, plus metric-Knoten und candidate-Knoten.

Familie Kind Zweck MetricFlow
Strukturierte Modellierung SemanticModel, Metric, SavedQuery, TimeSpine Iceberg-Tabellen + Metriken beschreiben ✅ identisch / erweitert
Unstrukturierte Modellierung UnstructuredFacet Eine Lance-Collection beschreiben ❌ OSL-only
Komposition JointEntity Beide Ebenen unter einer logischen Entity verbinden ❌ OSL-only
Katalog & Contracts Lexicon, DataContract, PolicyBinding Synonyme, SLAs, Hints für den PDP ❌ OSL-only

Jede Primitive trägt apiVersion: osl.opendome.eu/v1, ein kind und einen eindeutigen name. Die Identität eines Objekts ist (kind, name) — außer bei SemanticModel, dessen Identität (catalog, schema, table) ist.

OSL deklariert; die Engine führt aus. Dazwischen liegt ein Kompilierungsschritt.

1 · Author

Schreibe YAML von Hand oder aus dem Modeler. Konvention: project.yml + models/

  • metrics/ + facets/ + joints/ + lexicon/ + contracts/.

2 · Validate

osl validate führt JSON Schema pro Datei plus Cross-Reference aus (jeder referenzierte FQN muss existieren). Fehler in E0xx (Schema) oder E1xx (Resolve).

3 · Compile

osl compile emittiert eine einzige target/manifest.json — die kanonische, reproduzierbare Repräsentation, die die Engine bereitstellt und die Policy-Engine liest.

4 · Publish

osl publish lädt das Manifest auf die Engine des Tenants hoch. Atomar (Revert mit osl publish --revert), emittiert ein osl.publish Lineage-Event.

Von dort aus bedient die Engine Queries: REST für BI/Apps, MCP-Tools für Agenten.

OSL ist streng bei Namen, weil Policies von ihnen abhängen. Vier FQN-Räume:

Raum Form Beispiel
Strukturiert <catalog>.<schema>.<table>[:<column>] iceberg.curated.customer:email
Unstrukturiert lance://<collection>[/*][:<metadata_field>] lance://meeting_transcripts:attendees
Tools <tool_kind>:<resource> metrics:revenue, joints:Customer
Joint-exposed joint://<Entity>.<attribute> joint://Customer.recent_meeting_topics

FQNs sind case-sensitive und tragen keine Version — die Versionierung lebt im kompilierten, SHA-signierten Manifest. Die Policy-Engine konsumiert diese FQNs in ihrem 4-D-Tupel (data, action, actor, context). OSL erfindet keine parallele Zugriffssemantik; es emittiert die Fakten, die der PDP indexiert.

  • Keine SQL-Engine. Trino ist es; OSL gibt ihr Bedeutung.
  • Kein Pipeline-Orchestrator. Dagster führt Ingest-/Embedding-Jobs aus.
  • Keine Vektordatenbank. LanceDB ist es; OSL beschreibt die Collections.
  • Kein IAM. OPA / das Cell-Modell ist es; OSL emittiert Fakten, die der PDP konsumiert.
  • Kein BI-Tool. Metabase, Superset, Streamlit konsumieren OSL.
  • Kein Agent-Framework. LangGraph, MCP-Server konsumieren OSL.

OSL ist die gemeinsame Spec zwischen all diesen Teilen, nicht die Teile selbst.