Adnotacje semantyczne Saiku
Generyczny blok <Annotation> Mondriana to worek klucz/wartość o dowolnej formie dołączony do dowolnego elementu schemy (Cube, Dimension, Hierarchy, Level, Measure, …). Saiku rezerwuje przestrzeń nazw saiku.semantic.* wewnątrz tego worka na małe typowane słownictwo, które napędza:
- Warstwę AI Ask — opisy i synonimy dają LLM realny kontekst biznesowy; rodzaje agregacji, ziarna i wymagane filtry utrzymują jego propozycje zapytań w ryzach.
- Ochronę PII — marker
saiku.semantic.pii=trueprzełącza poziom lub miarę w tryb „strukturalnie obecny, ale nieprzezroczysty” na powierzchniach zwróconych do agentów i blokuje drillthrough, ekspozycję share/embed oraz ścieżki pobierania przed wyciekiem wartości bazowych.
Wszystkie klucze są opcjonalne. Schema bez żadnych adnotacji saiku.semantic.* działa identycznie jak jedna bez bloku adnotacji; przestrzeń nazw jest czysto addytywna.
W skrócie
| Klucz adnotacji | Ważny na | Typ / wartości | Cel |
|---|---|---|---|
saiku.semantic.description | Dimension · Measure · Level | wolny tekst | Opis biznesowy; ujawniany w /ai/schema, aby LLM miał kontekst |
saiku.semantic.synonyms | Dimension · Measure · Level | lista CSV | Alternatywne nazwy, aby AI mogło rozwiązać „country” → „Store Country” |
saiku.semantic.unit | Measure | wolny tekst (np. USD, kWh, count) | Łańcuch jednostki; wpływa do kształtu komórki AI {value, formatted, unit} |
saiku.semantic.currency | Measure | kod ISO 4217 (np. USD, EUR) | Wskazówka waluty dla miar pieniężnych |
saiku.semantic.aggregation_kind | Measure | enum: sum · count · distinct-count · non-additive | Semantyka agregacji. Napędza semantykę sortowania/sumy w warstwie AI. |
saiku.semantic.cardinality | Level | enum: low · medium · high | Wskazówka liczby członków. Napędza UX pickera i wskazówki kosztu AI. |
saiku.semantic.grain | Level | enum: year · quarter · month · week · day · hour · minute | Ziarno czasu dla modala filtra dat i wnioskowania wykresu szeregu czasowego |
saiku.semantic.required_filters | Level | CSV par (hierarchy, level) | Poziomy, które AI musi zawrzeć w filters zawsze, gdy ten poziom jest dotknięty |
saiku.semantic.pii | Level · Measure | boolean (true / false) | Marker PII — zobacz Ochrona PII niżej |
Nieznane wartości dla pól typu enum są logowane na poziomie WARN z dozwoloną listą i poza tym ignorowane — literówka nie psuje schemy.
Gdzie je umieścić
Adnotacje dołączają się do dowolnego elementu schemy przez potomny blok <Annotations> (XML) lub klucz 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: sumPoziomy akceptują ten sam kształt <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: yearOchrona PII
Oznaczenie poziomu lub miary przez saiku.semantic.pii=true włącza je do stosu wymuszania PII w Saiku. Adnotacja jest deklaracją — Saiku nakłada na nią cztery punkty wymuszania.
1. Projekcja schemy AI — captiony i próbki usunięte
Endpoint /ai/schema/{connection/catalog/schema/cube} podaje LLM strukturę kostki. Dla każdego elementu oznaczonego PII usuwa:
captiondescriptionsynonyms- przykładowych członków (przykładowe wartości, które wskazówki dla agenta zwykle pokazują)
Poziom lub miara nadal pojawia się strukturalnie (agent wie, że istnieje, i może się do niej odwołać po nazwie), ale same wartości pozostają nieprzezroczyste. Agent może planować zapytania zawierające kolumnę PII w projekcji bez oglądania faktycznych wierszy czy przykładowych członków.
2. Drillthrough returns= zablokowany
Żądania drillthrough niosą parametr returns= listujący kolumny do eksportu. Jeśli którykolwiek token w returns= rozwiązuje się na poziom lub miarę oznaczoną PII, resolver rzuca AiPiiException, a endpoint zwraca 400 z nazwaną w ciele obrażającą kolumną. Agent może spróbować ponownie z węższą projekcją, która pomija kolumnę PII.
HTTP 400{ "error": "Column 'Prescriber' is annotated PII (saiku.semantic.pii=true) and cannot be returned via drillthrough."}3. Tokeny embed/share auto-redagują
Gdy wybijasz token embedu lub share związany z zapytaniem, które dotyka poziomu oznaczonego PII, inspektor eskaluje politykę redakcji tokenu do FORCE_ON niezależnie od domyślnej operatora. Odbiorcy tokenu widzą zredagowane komórki, nawet jeśli embed nie był jawnie skonfigurowany na redakcję. Eliminuje pułapkę „zapomniałem włączyć redakcję” na współdzielonych linkach.
4. Bramkowanie polityki danych AI
Adnotacje PII to fakty na poziomie kostki. Łączą się w parę ze zmienną środowiskową SAIKU_AI_POLICY, która jest pokrętłem na poziomie wdrożenia:
SAIKU_AI_POLICY | Co widzi AI |
|---|---|
schema-only | Tylko schema + przykładowi członkowie. Żadne wartości cellsetu nie docierają do dostawcy. |
aggregated | Zagregowane sumy komórek docierają do dostawcy; komórki na poziomie wierszy usunięte. |
row-level | Pełne komórki docierają do dostawcy. Adnotacje PII nadal redagują szczegóły. |
Adnotacja mówi „to jest wrażliwe”; polityka mówi „tyle wrażliwych danych przepuszczamy”. Razem decydują, co AI widzi na turę. Domyślnie to schema-only — wdrożenia muszą świadomie włączyć luźniejsze poziomy.
Skaner pre-flight
PiiScanner przebiega po schemie kostki przy starcie i loguje linie WARN dla każdej kolumny, której wzorzec nazwy wygląda na kształt PII (dopasowuje się do *name*, *ssn*, *email*, *npi*, *dob* itd.), ale nie jest adnotowana. Ostrzeżenie zawiera gotowy do wklejenia fragment XML, aby autor schemy mógł włączyć kolumnę bez polowania po specyfikacji:
WARN [PiiScanner] Likely PII column 'prescribername' on level [Prescriber].[Prescriber] is not annotated. Add: <Annotations> <Annotation name="saiku.semantic.pii">true</Annotation> </Annotations>Skaner nigdy nie mutuje schemy — jedynie ujawnia kandydatów. Autorzy schemy decydują, co jest faktycznie wrażliwe.
Przykład — kostka demo Pharma
Schema demo Pharma dostarczana jest z adnotacjami PII na nazwie prescribera + NPI, a pozostawia specialty + decile bez adnotacji (te są operacyjnie bezpieczne do udostępniania). Wyciąg z 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>Z tym na miejscu agent AI pytający „break down Quantity by Specialty” odnosi sukces normalnie, ale „break down Quantity by Prescriber and download the rows” albo odmawia (blokada drillthrough), albo zwraca zagregowane kubełki ze stłumionymi nazwami prescriberów (zależnie od poziomu polityki AI).
Przykład — kostka FoodMart
FoodMart dostarczany jest z pełnym opisowym zestawem adnotacji (brak kolumn PII — dane syntetyczne). Zaczerpnięte z 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>Ten sam wzorzec skaluje się na każdy wymiar + poziom. Każda linia jest niezależnie opcjonalna, więc możesz wdrażać adnotacje przyrostowo.
Walidacja i nieznane wartości
- Pola typu enum (
aggregation_kind,cardinality,grain) walidują się względem swojej dozwolonej listy. Nieznane wartości logująWARNz obrażającą wartością + dozwoloną listą, a pole domyślnie przyjmuje wartość nieustawioną. - Pola boolean (
pii) akceptujątrue/falsebez rozróżniania wielkości liter po przycięciu. Cokolwiek innego tofalse. Autorzy schemy dostają konserwatywną wartość domyślną, jeśli się pomylą. - Pola wolnego tekstu (
description,unit,currency) są przycinane; puste łańcuchy stają sięnull. synonymsirequired_filtersparsują się jako CSV; puste wpisy są odrzucane.
Powiązane
- Eksport do Apache Ossie — jak adnotacje
saiku.semantic.*przeżywają round-trip do przenośnego YAML-a Ossie (description + synonyms podnoszą się doai_context; wszystko inne jedzie wcustom_extensionsjako dane dostawcy SAIKU). - Dobrze znane rozszerzenia Ossie — odpowiednik po stronie Ossie (
saiku.display,saiku.roles,saiku.pii) dla modeli YAML. Ten sam zamiar, inny format pliku. - Rozszerzenia: funkcje i formattery — generyczny blok
<Annotation>, na którym buduje sięsaiku.semantic.*(i jego konwencja metadanych lokalizacji). - Schemy YAML — pełna referencja YAML, w tym
annotations:na każdym elemencie. - Kontrola dostępu i role — wymuszanie dostępu na poziomie schemy (uzupełnia adnotacje PII: ACL-e bramkują kto może odpytać kostkę; adnotacje PII bramkują co wynik ujawnia, gdy już mogą).