1 · Author
Scrivi YAML a mano o dal modeler. Convenzione: project.yml + models/ +
metrics/ + facets/ + joints/ + lexicon/ + contracts/.
La OpenDome Semantic Layer (OSL) è una specifica aperta (Apache-2.0) che descrive, in YAML dichiarativo, un modello semantico che abbraccia due piani fisici: tabelle strutturate in Iceberg e collezioni vettoriali in Lance. Una spec, un linguaggio, una catena di governance.
Questa pagina fissa il vocabolario e il modello mentale. Per il contratto normativo vedi la specifica; per la superficie di API vedi il riferimento.
Gli strati semantici OSS di oggi (dbt MetricFlow, Cube, WrenAI) descrivono tabelle relazionali. Nessuno tratta le collezioni vettoriali come cittadini di prima classe. Questo costringe a due modelli paralleli — quello semantico-SQL e “ciò che il RAG sa del cliente” — che divergono il giorno dopo averli messi in produzione.
OSL abbraccia entrambi i piani:
JointEntity, un oggetto logico (Customer, Policy,
Order) che unisce colonne di Iceberg a facet Lance sotto un’unica chiave.Dichiari una volta. La governance, il lineage, le API e gli agenti consumano tutti lo stesso modello.
Il modello
OSL descrive un modello semantico su due substrati fisici con un solo linguaggio
dichiarativo. Ogni piano ha le sue primitive, la sua convenzione di FQN, il suo
motore di esecuzione e il suo percorso di evoluzione. La JointEntity è il pezzo
che li compone.
Tabelle Iceberg, query SQL, aggregazioni, join. Qui OSL passa attraverso MetricFlow senza modifiche:
semantic_models/*.yml descrivono entità fisiche con le loro dimensioni e measure.metrics/*.yml definiscono metriche certificate (simple, ratio, cumulative, derived).saved_queries/*.yml impacchettano query riutilizzabili.time_spine.yml definisce la granularità temporale del progetto.Il motore di riferimento (tenant-semantic-api) compila questi YAML in SQL su
Trino, eseguito contro Iceberg nel bucket del tenant.
Collezioni Lance con chunk indicizzati. Qui OSL definisce nuove primitive:
facets/*.yml (kind: UnstructuredFacet) descrivono una collezione Lance: il
suo dataset path, chunk schema, modello di embedding, indici (vector + FTS +
scalare), campi di metadata, ACL row-level e limiti di retrieval.Il motore esegue retrieval ibrido (vector ⊕ BM25 ⊕ filtro di metadata) su LanceDB e restituisce passages con citazione, rispettando l’ACL e i limiti dichiarati.
joints/*.yml (kind: JointEntity) dichiara un oggetto logico la cui chiave
primaria proviene da un semantic_model e che espone attributi composti —
alcuni dal piano strutturato (colonne, metriche), altri dal non strutturato
(retrieval scoped per la chiave).Il ponte è onesto: ogni join_on mappa una chiave di entità a un campo di
metadata autorevole e indicizzato del facet. Un campo con provenance: derived non può mai essere una chiave di join — l’estrazione derivata avviene
in ingest, mai in query time.
Un agente che invoca entity_get(Customer, customer_id=X) riceve, in un’unica
risposta, gli attributi canonici dell’entità, le metriche pre-aggregate
applicabili e i chunk Lance filtrati per customer_id=X. Una chiamata, una
decisione di policy, una traccia di audit.
Anche se la spec ha primitive separate (SemanticModel, JointEntity), chi
consuma OSL vede una sola parola: Entity. Ciò che distingue una Entity da
un’altra non è il suo kind ma la sua composizione:
| Composizione | Cos’è | Da dove arriva |
|---|---|---|
structured |
Entity puramente tabellare (solo Iceberg) | un SemanticModel che non è né ancora di joint né ponte |
structured+content |
Colonne strutturate unite a facet Lance | un JointEntity con gambe non strutturate |
content |
Un corpus Lance autonomo, senza ancora strutturata | un UnstructuredFacet che nessun joint assorbe |
Le relazioni tra Entity arrivano da due posti: entità MetricFlow condivise
(primary / foreign / unique) per i vicini strutturato ↔ strutturato, e
N-N tramite ponte — una tabella di giunzione (SemanticModel con ≥2 entità
foreign) che collassa a un arco N-N diretto invece di essere un nodo.
Questo vocabolario è ciò che serve il domain-map (GET /osl/domain-map): un
unico node kind entity etichettato per composizione, più nodi metric e candidate.
| Famiglia | Kind | Scopo | MetricFlow |
|---|---|---|---|
| Modellazione strutturata | SemanticModel, Metric, SavedQuery, TimeSpine |
Descrivere tabelle Iceberg + metriche | ✅ identico / esteso |
| Modellazione non strutturata | UnstructuredFacet |
Descrivere una collezione Lance | ❌ OSL-only |
| Composizione | JointEntity |
Unire entrambi i piani sotto un’unica entità logica | ❌ OSL-only |
| Catalogo e contratti | Lexicon, DataContract, PolicyBinding |
Sinonimi, SLA, hint per il PDP | ❌ OSL-only |
Ogni primitiva porta apiVersion: osl.opendome.eu/v1, un kind e un name
univoco. L’identità di un oggetto è (kind, name) — tranne SemanticModel, la
cui identità è (catalog, schema, table).
OSL dichiara; il motore esegue. Tra i due c’è un passo di compilazione.
1 · Author
Scrivi YAML a mano o dal modeler. Convenzione: project.yml + models/ +
metrics/ + facets/ + joints/ + lexicon/ + contracts/.
2 · Validate
osl validate esegue JSON Schema per file più il cross-reference (ogni FQN
referenziato deve esistere). Errori in E0xx (schema) o E1xx (resolve).
3 · Compile
osl compile emette un unico target/manifest.json — la rappresentazione
canonica e riproducibile che il motore serve e la policy engine legge.
4 · Publish
osl publish carica il manifest sul motore del tenant. Atomico (revert con
osl publish --revert), emette un evento di lineage osl.publish.
Da lì il motore serve le query: REST per BI/app, tool MCP per gli agenti.
OSL è rigoroso sui nomi perché le policy dipendono da essi. Quattro spazi di FQN:
| Spazio | Forma | Esempio |
|---|---|---|
| Strutturato | <catalog>.<schema>.<table>[:<column>] |
iceberg.curated.customer:email |
| Non strutturato | 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 |
Gli FQN sono case-sensitive e non portano versione — la versione vive nel
manifest compilato e firmato con SHA. La policy engine consuma questi FQN nella
sua tupla 4-D (data, action, actor, context). OSL non inventa una semantica di
accesso parallela; emette i fatti che il PDP indicizza.
OSL è la spec condivisa tra tutti questi pezzi, non i pezzi.