1 · Author
Escribe YAML a mano o desde el modelador. Convención: project.yml +
models/ + metrics/ + facets/ + joints/ + lexicon/ + contracts/.
La OpenDome Semantic Layer (OSL) es una especificación abierta (Apache-2.0) que describe, en YAML declarativo, un modelo semántico que abarca dos planos físicos: tablas estructuradas en Iceberg y colecciones vectoriales en Lance. Una spec, un lenguaje, una cadena de gobernanza.
Esta página fija el vocabulario y el modelo mental. Para el contrato normativo ver la especificación; para la superficie de API ver la referencia.
Las capas semánticas OSS de hoy (dbt MetricFlow, Cube, WrenAI) describen tablas relacionales. Ninguna trata las colecciones vectoriales como ciudadanos de primera clase. Eso fuerza dos modelos paralelos — el semántico-SQL y “lo que el RAG sabe del cliente” — que divergen al día siguiente de ponerlos en producción.
OSL abarca ambos planos:
JointEntity, un objeto lógico (Customer, Policy,
Order) que une columnas de Iceberg con facetas Lance bajo una sola clave.Declaras una vez. La gobernanza, el lineage, las APIs y los agentes consumen el mismo modelo.
El modelo
OSL describe un modelo semántico sobre dos sustratos físicos con un solo lenguaje
declarativo. Cada plano tiene sus primitivas, su convención de FQN, su motor de
ejecución y su path de evolución. La JointEntity es la pieza que los compone.
Tablas Iceberg, queries SQL, agregaciones, joins. Aquí OSL pasa por MetricFlow sin modificaciones:
semantic_models/*.yml describen entidades físicas con sus dimensiones y measures.metrics/*.yml definen métricas certificadas (simple, ratio, cumulative, derived).saved_queries/*.yml empaquetan consultas reusables.time_spine.yml define el grano temporal del proyecto.El motor de referencia (tenant-semantic-api) compila estas YAML a SQL sobre
Trino, ejecutado contra Iceberg en el bucket del tenant.
Colecciones Lance con chunks indexados. Aquí OSL define primitivas nuevas:
facets/*.yml (kind: UnstructuredFacet) describe una colección Lance: su
dataset path, chunk schema, modelo de embedding, índices (vector + FTS +
escalar), campos de metadata, ACL row-level y límites de retrieval.El motor ejecuta retrieval híbrido (vector ⊕ BM25 ⊕ filtro de metadata) sobre LanceDB y devuelve passages con citación, respetando la ACL y los límites declarados.
joints/*.yml (kind: JointEntity) declara un objeto lógico cuya clave
primaria proviene de un semantic_model y que expone atributos compuestos —
unos del plano estructurado (columnas, métricas), otros del no estructurado
(retrieval scoped por la clave).El puente es honesto: cada join_on mapea una clave de entidad a un campo de
metadata autoritativo e indexado de la faceta. Un campo con provenance: derived nunca puede ser clave de join — la extracción derivada ocurre en
ingest, jamás en query time.
Un agente que invoca entity_get(Customer, customer_id=X) recibe, en una sola
respuesta, los atributos canónicos de la entidad, las métricas pre-agregadas que
apliquen y los chunks Lance filtrados por customer_id=X. Una llamada, una
decisión de política, una traza de auditoría.
Aunque la spec tenga primitivas separadas (SemanticModel, JointEntity), quien
consume OSL ve una sola palabra: Entity. Lo que distingue una Entity de
otra no es su kind sino su composición:
| Composición | Qué es | De dónde sale |
|---|---|---|
structured |
Entity puramente tabular (solo Iceberg) | un SemanticModel que no es ancla de joint ni bridge |
structured+content |
Columnas estructuradas unidas a facetas Lance | un JointEntity con piernas no estructuradas |
content |
Un corpus Lance autónomo, sin ancla estructurada | una UnstructuredFacet que ningún joint absorbe |
Las relaciones entre Entities salen de dos sitios: entidades MetricFlow
compartidas (primary / foreign / unique) para vecinos estructurado ↔
estructurado, y N-N vía bridge — una tabla puente (SemanticModel con ≥2
entidades foreign) que colapsa a una arista N-N directa en vez de ser un nodo.
Este vocabulario es el que sirve el domain-map (GET /osl/domain-map): un
único node kind entity etiquetado por composición, más nodos metric y candidate.
| Familia | Kind | Propósito | MetricFlow |
|---|---|---|---|
| Modelado estructurado | SemanticModel, Metric, SavedQuery, TimeSpine |
Describir tablas Iceberg + métricas | ✅ idéntico / extendido |
| Modelado no estructurado | UnstructuredFacet |
Describir una colección Lance | ❌ OSL-only |
| Composición | JointEntity |
Unir ambos planos bajo una entidad lógica | ❌ OSL-only |
| Catálogo y contratos | Lexicon, DataContract, PolicyBinding |
Sinonimia, SLAs, hints para la PDP | ❌ OSL-only |
Toda primitiva lleva apiVersion: osl.opendome.eu/v1, un kind y un name
único. La identidad de un objeto es (kind, name) — salvo SemanticModel, cuya
identidad es (catalog, schema, table).
OSL declara; el motor ejecuta. Entre ambos hay un paso de compilación.
1 · Author
Escribe YAML a mano o desde el modelador. Convención: project.yml +
models/ + metrics/ + facets/ + joints/ + lexicon/ + contracts/.
2 · Validate
osl validate corre JSON Schema por archivo más cross-reference (cada FQN
referenciado debe existir). Errores en E0xx (schema) o E1xx (resolve).
3 · Compile
osl compile emite un único target/manifest.json — la representación
canónica y reproducible que el motor sirve y la policy engine lee.
4 · Publish
osl publish sube el manifest al motor del tenant. Atómico (revert con
osl publish --revert), emite un evento de lineage osl.publish.
A partir de ahí el motor sirve consultas: REST para BI/apps, tools MCP para agentes.
OSL es estricto con los nombres porque las políticas dependen de ellos. Cuatro espacios de FQN:
| Espacio | Forma | Ejemplo |
|---|---|---|
| Estructurado | <catalog>.<schema>.<table>[:<column>] |
iceberg.curated.customer:email |
| No estructurado | 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 |
Los FQN son case-sensitive y no llevan versión — la versionada vive en el
manifest compilado y firmado con SHA. La policy engine consume estos FQNs en su
tupla 4-D (data, action, actor, context). OSL no inventa una semántica de
acceso paralela; emite los hechos que la PDP indexa.
OSL es la spec compartida entre todas esas piezas, no las piezas.