AI Query API — kostki OLAP
AI Query API pozwala agentowi odpytywać kostkę OLAP Mondrian bez oglądania czy pisania MDX. Agent pobiera samoopisującą się schemę, wypełnia względem niej żądanie JSON, serwer waliduje każdą nazwę, wykonuje zapytanie i zwraca typowane rekordy. Jeśli nazwa jest błędna, błąd mówi agentowi dokładnie, co poprawić — bez prompt engineeringu.
To jest odpowiednik OLAP dla
AI Query API dla modeli Ossie (powierzchnia
SQL/semantyczno-YAML). Ta sama dyscyplina, te same zabezpieczenia; ta
działa na kostkach Mondrian zamiast na YAML-u Ossie. Na jej wierzchu
siedzi warstwa /ai/ask w języku naturalnym — endpoint
ask tłumaczy pytanie w prostym języku dokładnie na udokumentowane tu
ciało żądania.
Szybka orientacja
Trzy endpointy pokrywają ~90% zastosowań agentów:
| Endpoint | Cel |
|---|---|
GET /rest/saiku/api/ai/cubes | Wylistuj każdą kostkę, którą wywołujący może odpytać. |
GET /rest/saiku/api/ai/schema/{cubeId} | Samoopisująca się schema — miary, wymiary, hierarchie, poziomy, przykładowi członkowie, synonimy i JSON Schema żądania. |
POST /rest/saiku/api/ai/query | Wykonaj typowane żądanie. Domyślnie odpowiedź w formacie rekordów; macierz na ?format=matrix. |
Endpointy z długiego ogona (udokumentowane na powiązanych stronach):
| Endpoint | Cel |
|---|---|
GET /rest/saiku/api/ai/members/search | Wyszukiwanie podłańcuchowe członków na poziomie. |
POST /rest/saiku/api/ai/scenario/whatif | Symulacja write-back what-if. |
POST /rest/saiku/api/ai/query/execute-async | Zgłoś do wykonania w tle (odpytuj status / result). |
POST /rest/saiku/api/ai/anomaly | Uruchamia zapytanie, po czym oznacza anomalie wzdłuż osi czasu. |
POST /rest/saiku/api/ai/forecast | Rzutuje przyszłe punkty (ETS / ARIMA / Prophet). |
POST /rest/saiku/api/ai/ask | Zapytanie w języku naturalnym. Wymaga klucza LLM. |
cubeId wszędzie to czwórka connection/catalog/schema/cubeName
połączona przez /. Wszystkie endpointy wymagają uwierzytelnionej
sesji; endpointy POST wymagają pary cookie/nagłówek CSRF.
Krok 1 — wylistuj kostki
GET /rest/saiku/api/ai/cubes[ { "connectionName": "unknown_foodmart", "catalog": "FoodMart", "schema": "FoodMart", "cubeName": "Sales", "cubeCaption": "Sales", "defaultMeasure": "Unit Sales", "measureCount": 8 }]Czwórka connectionName/catalog/schema/cubeName to identyfikator
kostki używany wszędzie indziej.
Krok 2 — pobierz typowaną schemę
GET /rest/saiku/api/ai/schema/unknown_foodmart/FoodMart/FoodMart/SalesNie koduj ukośników w URL — szablon ścieżki akceptuje wielosegmentową
formę connection/catalog/schema/cubeName bezpośrednio. Odpowiedź jest
gęsta — to właśnie czyni to API samoopisującym się:
{ "cubeId": "unknown_foodmart/FoodMart/FoodMart/Sales", "measures": { "store sales": { "name": "Store Sales", "uniqueName": "[Measures].[Store Sales]", "description": "Net retail revenue in USD across all transactions.", "synonyms": ["revenue", "turnover", "top-line"], // accepted as `name` on input "unit": "USD", "aggregationKind": "sum" } // …8 measures total… }, "measureAliases": { "revenue": "store sales", "turnover": "store sales" }, "dimensions": { "time": { "name": "Time", "uniqueName": "[Time]", "hierarchies": { "time by": { "name": "Time By", "levels": { "quarter": { "name": "Quarter", "synonyms": ["quarterly", "qtr"], // accepted as `level` on input "sampleMembers": [ { "caption": "Q1", "uniqueName": "[Time].[Time By].[Quarter].&[Q1]" } ] } } } } } }}Synonimy i aliasy nazw wyświetlanych są akceptowane jako wejście
wszędzie tam, gdzie kanoniczne name — agent może powiedzieć
"revenue", a serwer rozwiązuje to na Store Sales.
Krok 3 — wykonaj zapytanie
„Pokaż Store Sales i Unit Sales według Product Family, top 3 według Store Sales.”
POST /rest/saiku/api/ai/queryContent-Type: application/json{ "cube": "unknown_foodmart/FoodMart/FoodMart/Sales", "measures": [{ "name": "Store Sales" }, { "name": "Unit Sales" }], "rows": [{ "dimension": "Product", "hierarchy": "Products", "level": "Product Family" }], "order": [{ "by": "Store Sales", "direction": "desc" }], "limit": 3}cube akceptuje albo 4-segmentową formę obiektową, albo zwartą formę
łańcuchową "connection/catalog/schema/cube".
Odpowiedź (200):
{ "status": "SUCCESS", "format": "records", "metadata": { "generatedMdx": "SELECT NON EMPTY {[Measures].[Store Sales], [Measures].[Unit Sales]} ON COLUMNS, NON EMPTY TopCount([Product].[Products].[Product Family].Members, 3, [Measures].[Store Sales]) ON ROWS FROM [Sales]", "freshness": { "computedAtMillis": 1715798421042, "cached": false } }, "data": [ { "Product Family": "Food", "Store Sales": { "value": 409035.59, "formatted": "409,035.59", "unit": null }, "Unit Sales": { "value": 191940.0, "formatted": "191,940", "unit": null } } ], "totalRows": 3, "runtimeMs": 421}Każdy wiersz to samoopisujący się obiekt z kluczem będącym czytelnym dla człowieka nagłówkiem kolumny. Każda komórka numeryczna to typowana koperta:
value— sparsowana liczba (do matematyki / sortowania / wykresów)formatted— wstępnie sformatowany łańcuch wyświetlania Mondriana (do UI)unit— wyniuchana z sformatowanego łańcucha (USD,GBP,EUR,JPY,%) lubnull
generatedMdx jest odbijane do debugowania; agenci zwykle je ignorują.
Format macierzy
Klienci indeksowani pozycyjnie rezygnują z rekordów przez
?format=matrix — odpowiedź niesie matrix zamiast data, każdy
wiersz z kluczem będącym indeksem kolumny jako łańcuch, komórki nadal w
typowanej kopercie {value, formatted, unit}.
Prywatność: k-anonimowość
Gdy ai.kAnonymity jest ustawione (domyślnie 5; 0 wyłącza), serwer
maskuje wartości miar małych komórek zanim wynik przekroczy granicę AI
— każdy wiersz, którego miara liczności w wyniku spada poniżej k, ma
swoje komórki miar zamaskowane z suppressed: true. Dotyczy rekordów i
macierzy oraz pochodnych endpointów /ai/anomaly + /ai/forecast.
Krok 4 — walidacja uczy agenta
Podaj nazwę, która się nie rozwiązuje, a serwer zwraca 400 z ciałem, z którego agent może się sam skorygować — bez proszenia o ponowienie:
{ "status": "VALIDATION_ERROR", "error": "Unknown measure 'Made Up Measure'", "field": "measures[].name", "available": ["Unit Sales", "Store Cost", "Store Sales", "Profit", "Customer Count"]}Agent czyta field (co było błędne), czyta available[] (legalne
wartości), poprawia i ponawia. Działają dwie warstwy: walidator
kształtu (JSON Schema — brakujące pola, złe typy, naruszenia enum, ze
ścieżkami pól indeksowanymi tablicowo w rodzaju filters[0].op) oraz
walidator semantyczny (rozwiązywanie kostki — nazwy poprawne
kształtem, ale nieistniejące). Kontrakt jest identyczny w obu
przypadkach: czytaj field, czytaj available[], poprawiaj, ponawiaj.
Dokąd dalej
- AI Ask API — warstwa języka naturalnego produkująca
powyższe ciało żądania, plus endpointy towarzyszące
members/search,scenario/whatifipii-suggestions. - Agent Spaces — persony, które ograniczają tę powierzchnię do zbioru kostek z listy dozwolonych.
- Agent Skills — przepływy autorstwa administratora, wykrywalne z każdego ask.
- Serwer MCP — ta sama typowana powierzchnia dla
zewnętrznych hostów agentów (
list_cubes,describe_cube,run_query, …). - AI Query API dla modeli Ossie — odpowiednik SQL/semantyczno-YAML.
- AI inference API — inna powierzchnia: projektowo-czasowy projektant kostek (profile → propose → render), a nie wykonywanie zapytań.