Pular para o conteúdo

Anotações semânticas do Saiku

O bloco genérico <Annotation> do Mondrian é um saco livre de chave/valor anexado a qualquer elemento de schema (Cube, Dimension, Hierarchy, Level, Measure, …). O Saiku reserva o namespace saiku.semantic.* dentro desse saco para um pequeno vocabulário tipado que dirige:

  1. A camada AI Ask — descrições e synonyms dão ao LLM contexto de negócio real; aggregation kinds, grains e required filters mantêm suas sugestões de query sãs.
  2. Proteção de PII — o marcador saiku.semantic.pii=true vira um level ou measure para modo “estruturalmente presente mas opaco” em superfícies voltadas ao agente e bloqueia drillthrough, exposição por share/embed e caminhos de download de vazarem os valores subjacentes.

Todas as chaves são opcionais. Um schema sem nenhuma anotação saiku.semantic.* roda identicamente a um sem o bloco de anotação; o namespace é puramente aditivo.

Em resumo

Chave de anotaçãoVálida emTipo / valoresPropósito
saiku.semantic.descriptionDimension · Measure · Leveltexto livreDescrição de negócio; exposta em /ai/schema para que o LLM tenha contexto
saiku.semantic.synonymsDimension · Measure · Levellista CSVNomes alternativos para que a AI possa resolver “country” → “Store Country”
saiku.semantic.unitMeasuretexto livre (ex.: USD, kWh, count)String de unidade; flui para o formato de célula AI {value, formatted, unit}
saiku.semantic.currencyMeasurecódigo ISO 4217 (ex.: USD, EUR)Dica de moeda para measures monetárias
saiku.semantic.aggregation_kindMeasureenum: sum · count · distinct-count · non-additiveSemântica de agregação. Dirige semântica de sort/total na camada AI.
saiku.semantic.cardinalityLevelenum: low · medium · highDica de contagem de membros. Dirige UX do picker e dicas de custo da AI.
saiku.semantic.grainLevelenum: year · quarter · month · week · day · hour · minuteGrão de tempo para o modal de filtro de data e inferência de gráfico de série temporal
saiku.semantic.required_filtersLevelCSV de pares (hierarchy, level)Levels que a AI deve incluir em filters sempre que este level é tocado
saiku.semantic.piiLevel · Measurebooleano (true / false)Marcador de PII — veja Proteção de PII abaixo

Valores desconhecidos para os fields de tipo enum são logados em WARN com a lista permitida e caso contrário ignorados — um typo não quebra o schema.

Onde colocá-las

Anotações se anexam a qualquer elemento de schema via um bloco filho <Annotations> (XML) ou uma chave 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>

Levels aceitam o mesmo formato <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>

Proteção de PII

Marcar um level ou measure com saiku.semantic.pii=true o inscreve na pilha de enforcement de PII do Saiku. A anotação é a declaração — o Saiku sobrepõe quatro pontos de enforcement em cima dela.

1. Projeção do schema AI — captions e samples removidos

O endpoint /ai/schema/{connection/catalog/schema/cube} alimenta o LLM com a estrutura do cubo. Para qualquer elemento marcado como PII ele remove:

  • caption
  • description
  • synonyms
  • sample members (os valores de exemplo que a orientação do agente geralmente mostra)

O level ou measure ainda aparece estruturalmente (o agente sabe que existe e pode referenciá-lo por nome) mas os valores em si permanecem opacos. O agente pode planejar queries que incluam a coluna PII na projeção sem nunca ver linhas reais ou sample members.

2. Drillthrough returns= bloqueado

Requisições de drillthrough carregam um parâmetro returns= listando as colunas a exportar. Se qualquer token em returns= resolve para um level ou measure marcado como PII, o resolver lança AiPiiException e o endpoint retorna 400 com a coluna ofensora nomeada no corpo. O agente pode tentar de novo com uma projeção mais estreita que omite a coluna PII.

HTTP 400
{
"error": "Column 'Prescriber' is annotated PII (saiku.semantic.pii=true) and cannot be returned via drillthrough."
}

3. Tokens de embed/share auto-redigem

Quando você cunha um token de embed ou share vinculado a uma query que toca um level marcado como PII, o inspetor escala a política de redação do token para FORCE_ON independentemente do default do operador. Destinatários do token veem células redigidas mesmo se o embed não foi explicitamente configurado para redação. Elimina o footgun “esqueci de ligar a redação” em links compartilhados.

4. Gating de política de dados da AI

Anotações PII são fatos de nível-cubo. Elas se pareiam com a variável de ambiente SAIKU_AI_POLICY, que é o dial de nível-deployment:

SAIKU_AI_POLICYO que a AI vê
schema-onlySó schema + sample members. Nenhum valor de cellset alcança o provider.
aggregatedTotais de células agregadas alcançam o provider; células row-level removidas.
row-levelCélulas completas alcançam o provider. Anotações PII ainda redigem especificidades.

A anotação diz “isto é sensível”; a política diz “isto é o quanto de dados sensíveis deixamos passar”. Juntas elas decidem o que a AI vê por turno. O default é schema-only — deployments devem optar por camadas mais frouxas.

Scanner pré-voo

PiiScanner roda sobre o schema de um cubo no startup e loga linhas WARN para qualquer coluna cujo padrão de nome pareça ter formato de PII (casa contra *name*, *ssn*, *email*, *npi*, *dob*, etc.) mas não é anotada. O aviso inclui um snippet XML pronto para colar para que o autor do schema possa optar pela coluna sem procurar pela spec:

WARN [PiiScanner] Likely PII column 'prescribername' on level [Prescriber].[Prescriber] is not annotated. Add:
<Annotations>
<Annotation name="saiku.semantic.pii">true</Annotation>
</Annotations>

O scanner nunca muta o schema — ele apenas expõe candidatos. Autores de schema decidem o que é realmente sensível.

Exemplo trabalhado — cubo demo Pharma

O schema demo Pharma vem com anotações PII em nome do prescriber + NPI, e deixa specialty + decile não-anotados (esses são operacionalmente seguros de compartilhar). Trecho 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>

Com isso no lugar, um agente AI perguntando “break down Quantity by Specialty” tem sucesso normalmente, mas “break down Quantity by Prescriber and download the rows” ou recusa (bloqueio de drillthrough) ou retorna buckets agregados com nomes de prescriber suprimidos (dependendo da camada de política da AI).

Exemplo trabalhado — cubo FoodMart

FoodMart vem com o conjunto descritivo completo de anotações (sem colunas PII — dados sintéticos). Retirado 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>

O mesmo padrão escala através de cada dim + level. Cada linha é independentemente opcional, então você pode distribuir as anotações incrementalmente.

Validação e valores desconhecidos

  • Fields de tipo enum (aggregation_kind, cardinality, grain) validam contra sua lista permitida. Valores desconhecidos logam um WARN com o valor ofensor + a lista permitida e o field default para não-definido.
  • Fields booleanos (pii) aceitam true / false de forma case-insensitive após trim. Qualquer outra coisa é false. Autores de schema recebem o default conservador se digitarem errado.
  • Fields de texto livre (description, unit, currency) são aparados; strings vazias viram null.
  • synonyms e required_filters parseiam como CSV; entradas vazias são descartadas.

Relacionado

  • Exportando para Apache Ossie — como as anotações saiku.semantic.* sobrevivem ao round-trip em YAML Ossie portável (description + synonyms sobem para ai_context; todo o resto viaja em custom_extensions como dados de fornecedor SAIKU).
  • Extensões well-known do Ossie — o equivalente do lado Ossie (saiku.display, saiku.roles, saiku.pii) para modelos YAML. Mesma intenção, formato de arquivo diferente.
  • Extensões: funções e formatters — o bloco genérico <Annotation> sobre o qual saiku.semantic.* constrói (e sua convenção de metadados de locale).
  • Schemas YAML — referência YAML completa, incluindo annotations: em cada elemento.
  • Controle de acesso e roles — enforcement de acesso em nível de schema (complementa anotações PII: ACLs controlam quem pode consultar um cubo; anotações PII controlam o que o resultado expõe uma vez que podem).