Anotaciones semánticas de Saiku
El bloque genérico <Annotation> de Mondrian es una bolsa de clave/valor de forma libre adjunta a cualquier elemento del schema (Cube, Dimension, Hierarchy, Level, Measure, …). Saiku reserva el espacio de nombres saiku.semantic.* dentro de esa bolsa para un pequeño vocabulario tipado que impulsa:
- La capa AI Ask — las descripciones y sinónimos le dan al LLM contexto de negocio real; los tipos de agregación, granularidades y filtros requeridos mantienen cuerdas sus sugerencias de consulta.
- La protección de PII — el marcador
saiku.semantic.pii=truecambia un nivel o medida a modo “estructuralmente presente pero opaco” en las superficies orientadas a agentes y bloquea el drillthrough, la exposición por compartir/embed, y las rutas de descarga para que no filtren los valores subyacentes.
Todas las claves son opcionales. Un schema sin ninguna anotación saiku.semantic.* corre idénticamente a uno sin el bloque de anotación; el espacio de nombres es puramente aditivo.
De un vistazo
| Clave de anotación | Válida en | Tipo / valores | Propósito |
|---|---|---|---|
saiku.semantic.description | Dimension · Measure · Level | texto libre | Descripción de negocio; expuesta en /ai/schema para que el LLM tenga contexto |
saiku.semantic.synonyms | Dimension · Measure · Level | lista CSV | Nombres alternativos para que la IA pueda resolver “country” → “Store Country” |
saiku.semantic.unit | Measure | texto libre (p. ej. USD, kWh, count) | Cadena de unidad; fluye a la forma de celda de IA {value, formatted, unit} |
saiku.semantic.currency | Measure | código ISO 4217 (p. ej. USD, EUR) | Pista de moneda para medidas monetarias |
saiku.semantic.aggregation_kind | Measure | enum: sum · count · distinct-count · non-additive | Semántica de agregación. Impulsa la semántica de orden/total en la capa de IA. |
saiku.semantic.cardinality | Level | enum: low · medium · high | Pista de recuento de miembros. Impulsa la UX del selector y las pistas de coste de la IA. |
saiku.semantic.grain | Level | enum: year · quarter · month · week · day · hour · minute | Granularidad de tiempo para el modal de filtro de fecha y la inferencia de gráficos de series temporales |
saiku.semantic.required_filters | Level | CSV de pares (hierarchy, level) | Niveles que la IA debe incluir en filters siempre que se toque este nivel |
saiku.semantic.pii | Level · Measure | booleano (true / false) | Marcador PII — vea Protección de PII abajo |
Los valores desconocidos para los campos tipados por enum se registran en WARN con la lista permitida y por lo demás se ignoran — un error tipográfico no rompe el schema.
Dónde ponerlas
Las anotaciones se adjuntan a cualquier elemento del schema mediante un bloque hijo <Annotations> (XML) o una clave annotations: (YAML).
<Measure name="Quantity" column="quantity_units" aggregator="sum" formatString="#,##0"> <Annotations> <Annotation name="saiku.semantic.description">Total units prescribed (across all dispensings).</Annotation> <Annotation name="saiku.semantic.synonyms">units, quantity, scripts</Annotation> <Annotation name="saiku.semantic.unit">count</Annotation> <Annotation name="saiku.semantic.aggregation_kind">sum</Annotation> </Annotations></Measure>measures: - name: Quantity column: quantity_units aggregator: sum formatString: "#,##0" annotations: saiku.semantic.description: "Total units prescribed (across all dispensings)." saiku.semantic.synonyms: "units, quantity, scripts" saiku.semantic.unit: count saiku.semantic.aggregation_kind: sumLos niveles aceptan la misma forma <Annotations>:
<Level name="Year" column="cal_year" type="Numeric" uniqueMembers="true" levelType="TimeYears"> <Annotations> <Annotation name="saiku.semantic.description">Calendar year.</Annotation> <Annotation name="saiku.semantic.synonyms">annual, yearly, fiscal year, y</Annotation> <Annotation name="saiku.semantic.cardinality">low</Annotation> <Annotation name="saiku.semantic.grain">year</Annotation> </Annotations></Level>- name: Year column: cal_year type: Numeric uniqueMembers: true levelType: TimeYears annotations: saiku.semantic.description: "Calendar year." saiku.semantic.synonyms: "annual, yearly, fiscal year, y" saiku.semantic.cardinality: low saiku.semantic.grain: yearProtección de PII
Marcar un nivel o medida con saiku.semantic.pii=true lo inscribe en el stack de aplicación de PII de Saiku. La anotación es la declaración — Saiku superpone cuatro puntos de aplicación sobre ella.
1. Proyección del schema de IA — captions y muestras eliminados
El endpoint /ai/schema/{connection/catalog/schema/cube} alimenta al LLM la estructura del cubo. Para cualquier elemento marcado como PII elimina:
captiondescriptionsynonyms- miembros de muestra (los valores de ejemplo que la guía del agente suele mostrar)
El nivel o medida sigue apareciendo estructuralmente (el agente sabe que existe y puede referenciarlo por nombre) pero los valores en sí permanecen opacos. El agente puede planificar consultas que incluyan la columna PII en la proyección sin ver jamás filas reales ni miembros de muestra.
2. returns= de drillthrough bloqueado
Las peticiones de drillthrough llevan un parámetro returns= listando las columnas a exportar. Si algún token en returns= resuelve a un nivel o medida marcado como PII, el resolvedor lanza AiPiiException y el endpoint devuelve 400 con la columna ofensora nombrada en el cuerpo. El agente puede reintentar con una proyección más estrecha que omita la columna PII.
HTTP 400{ "error": "Column 'Prescriber' is annotated PII (saiku.semantic.pii=true) and cannot be returned via drillthrough."}3. Los tokens de embed/compartir auto-redactan
Cuando acuña un token de embed o de compartir vinculado a una consulta que toca un nivel marcado como PII, el inspector escala la política de redacción del token a FORCE_ON independientemente del por defecto del operador. Los receptores del token ven celdas redactadas incluso si el embed no se configuró explícitamente para redacción. Elimina el foot-gun de “olvidé activar la redacción” en enlaces compartidos.
4. Control de política de datos de IA
Las anotaciones PII son hechos a nivel de cubo. Se emparejan con la variable de entorno SAIKU_AI_POLICY, que es el dial a nivel de despliegue:
SAIKU_AI_POLICY | Qué ve la IA |
|---|---|
schema-only | Solo schema + miembros de muestra. Ningún valor de cellset llega al proveedor. |
aggregated | Los totales de celda agregados llegan al proveedor; las celdas a nivel de fila se eliminan. |
row-level | Las celdas completas llegan al proveedor. Las anotaciones PII aún redactan detalles específicos. |
La anotación dice “esto es sensible”; la política dice “cuántos datos sensibles dejamos pasar”. Juntas deciden qué ve la IA por turno. El por defecto es schema-only — los despliegues deben optar por los niveles más laxos.
Escáner pre-vuelo
PiiScanner corre sobre el schema de un cubo al arranque y registra líneas WARN para cualquier columna cuyo patrón de nombre parezca tener forma PII (coincide con *name*, *ssn*, *email*, *npi*, *dob*, etc.) pero no esté anotada. La advertencia incluye un fragmento XML listo para pegar para que el autor del schema pueda inscribir la columna sin buscar en la especificación:
WARN [PiiScanner] Likely PII column 'prescribername' on level [Prescriber].[Prescriber] is not annotated. Add: <Annotations> <Annotation name="saiku.semantic.pii">true</Annotation> </Annotations>El escáner nunca muta el schema — solo aflora candidatos. Los autores del schema deciden qué es realmente sensible.
Ejemplo trabajado — cubo demo Pharma
El schema demo Pharma se entrega con anotaciones PII en el nombre del prescriptor + NPI, y deja specialty + decile sin anotar (esos son operacionalmente seguros de compartir). Extracto de saiku-home/data/Pharma.xml:
<Dimension name="Prescriber" foreignKey="prescriberkey"> <Hierarchy hasAll="true" primaryKey="prescriberkey"> <Table name="dim_prescriber" schema="public"/> <Level name="Specialty" column="specialty" uniqueMembers="false"/> <Level name="Decile" column="decile" type="Numeric" uniqueMembers="false"/> <Level name="Prescriber" column="prescriberkey" nameColumn="prescribername" type="Numeric" uniqueMembers="true"> <Annotations> <Annotation name="saiku.semantic.pii">true</Annotation> </Annotations> </Level> </Hierarchy> <Hierarchy name="NPI" hasAll="true" primaryKey="prescriberkey"> <Table name="dim_prescriber" schema="public"/> <Level name="NPI" column="prescribernpi" type="Numeric" uniqueMembers="true"> <Annotations> <Annotation name="saiku.semantic.pii">true</Annotation> </Annotations> </Level> </Hierarchy></Dimension>Con esto en su sitio, un agente de IA que pregunta “break down Quantity by Specialty” tiene éxito normalmente, pero “break down Quantity by Prescriber and download the rows” o bien rechaza (bloqueo de drillthrough) o devuelve cubos agregados con los nombres de prescriptor suprimidos (dependiendo del nivel de política de IA).
Ejemplo trabajado — cubo FoodMart
FoodMart se entrega con el conjunto completo de anotaciones descriptivas (sin columnas PII — datos sintéticos). Extraído de saiku-home/data/FoodMart4.xml:
<Level name="Store Country" column="store_country" uniqueMembers="true"> <Annotations> <Annotation name="saiku.semantic.description">Store's country.</Annotation> <Annotation name="saiku.semantic.synonyms">nation, country code</Annotation> <Annotation name="saiku.semantic.cardinality">low</Annotation> </Annotations></Level><Level name="Year" column="the_year" type="Numeric" uniqueMembers="true" levelType="TimeYears"> <Annotations> <Annotation name="saiku.semantic.description">Calendar year.</Annotation> <Annotation name="saiku.semantic.synonyms">annual, yearly, fiscal year, y</Annotation> <Annotation name="saiku.semantic.cardinality">low</Annotation> <Annotation name="saiku.semantic.grain">year</Annotation> </Annotations></Level>El mismo patrón escala a través de cada dim + level. Cada línea es independientemente opcional, así que puede desplegar las anotaciones de forma incremental.
Validación y valores desconocidos
- Los campos tipados por enum (
aggregation_kind,cardinality,grain) validan contra su lista permitida. Los valores desconocidos registran unWARNcon el valor ofensor + la lista permitida y el campo queda sin definir por defecto. - Los campos booleanos (
pii) aceptantrue/falsede forma insensible a mayúsculas tras recortar. Cualquier otra cosa esfalse. Los autores del schema obtienen el por defecto conservador si escriben mal. - Los campos de texto libre (
description,unit,currency) se recortan; las cadenas vacías se vuelvennull. synonymsyrequired_filtersparsean como CSV; las entradas vacías se descartan.
Relacionado
- Exportar a Apache Ossie — cómo las anotaciones
saiku.semantic.*sobreviven al round-trip a YAML Ossie portable (description + synonyms se elevan aai_context; todo lo demás cabalga encustom_extensionscomo datos de proveedor SAIKU). - Extensiones conocidas de Ossie — el equivalente del lado de Ossie (
saiku.display,saiku.roles,saiku.pii) para modelos YAML. Misma intención, distinto formato de archivo. - Extensiones: funciones y formateadores — el bloque genérico
<Annotation>sobre el que se construyesaiku.semantic.*(y su convención de metadatos de locale). - Schemas YAML — referencia YAML completa, incluyendo
annotations:en cada elemento. - Control de acceso y roles — aplicación de acceso a nivel de schema (complementa las anotaciones PII: los ACL controlan quién puede consultar un cubo; las anotaciones PII controlan qué expone el resultado una vez que pueden).