Przejdź do głównej zawartości

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:

EndpointCel
GET /rest/saiku/api/ai/cubesWylistuj 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/queryWykonaj typowane żądanie. Domyślnie odpowiedź w formacie rekordów; macierz na ?format=matrix.

Endpointy z długiego ogona (udokumentowane na powiązanych stronach):

EndpointCel
GET /rest/saiku/api/ai/members/searchWyszukiwanie podłańcuchowe członków na poziomie.
POST /rest/saiku/api/ai/scenario/whatifSymulacja write-back what-if.
POST /rest/saiku/api/ai/query/execute-asyncZgłoś do wykonania w tle (odpytuj status / result).
POST /rest/saiku/api/ai/anomalyUruchamia zapytanie, po czym oznacza anomalie wzdłuż osi czasu.
POST /rest/saiku/api/ai/forecastRzutuje przyszłe punkty (ETS / ARIMA / Prophet).
POST /rest/saiku/api/ai/askZapytanie 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/Sales

Nie 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/query
Content-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, %) lub null

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/whatif i pii-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 APIinna powierzchnia: projektowo-czasowy projektant kostek (profile → propose → render), a nie wykonywanie zapytań.