Przejdź do głównej zawartości

Agent Skills

Agent Skills to pliki markdown z frontmatterem YAML, które żyją w saiku-home/skills/. Launcher skanuje je leniwie przy każdym żądaniu i wstrzykuje katalog do system promptu LLM, więc każda tura AI Ask — MDX lub Ossie — może skierować do przepływu autorstwa administratora zamiast improwizować.

Mała funkcja, nieproporcjonalna wartość: pozwala operatorom skodyfikować kanoniczne pytania swojego zespołu („rollup dla zarządu z tego kwartału”, „kohorta churn”, „raport porównania sklepów”) bez posiadania kolejnego narzędzia. Skill żyje w repo, wersjonuje się z modelem semantycznym i przechodzi code review jak wszystko inne.

Dostarczone w saiku v4.7 jako saiku#1426.

Dwie ścieżki wywołania

Format pliku

saiku-home/skills/weekly-foodmart-rollup.md
---
name: weekly-foodmart-rollup
description: |
Weekly revenue rollup for the FoodMart Sales cube: total Store Sales
by Product Family for the last 7 days, compared to the prior 7 days.
Flags any family with a >20% swing.
cube: unknown_foodmart/FoodMart/FoodMart/Sales
---
## Steps
1. Query total `[Measures].[Store Sales]` and `[Measures].[Unit Sales]`
broken down by `[Product].[Product Family]` for the last 7 days
(`[Time].[Weekly].[Week].&[latest]`).
2. Query the same shape for the prior 7 days
(`[Time].[Weekly].[Week].&[latest - 1]`).
3. Present the result as a two-column table with a `Δ vs prior` percentage
column derived per family.
4. Highlight any Product Family whose `Δ vs prior` swings by more than 20%
in either direction.

Frontmatter

PoleWymaganeTypUwagi
nametakstringkebab-case, [a-z][a-z0-9-]{0,63}. Używane jako slug slash.
descriptiontakstringJedna linia lub blok scalar. Pokazywane w /ai/skills i prompcie LLM.
cubeniestringReferencja connection/catalog/schema/cubeName. Ogranicza skill.

Nieznane klucze najwyższego poziomu są odrzucane. Literówka (descripton) ujawnia się jako ustrukturyzowany błąd UNKNOWN_FIELD — nigdy jako cichy bezimienny skill.

Ciało

Wszystko po zamykającej granicy --- to ciało. Markdown jest zalecany, ale niewymagany; ciało jest traktowane jako nieprzezroczysty tekst i wklejane dosłownie do promptu LLM przy dopasowaniu polecenia slash. Trzymaj je zwięzłe — LLM musi przeczytać cały plik.

Polecenie slash

  1. Użytkownik (lub widżet DimSum w twoim imieniu) wysyła ask zaczynający się od /:

    Okno terminala
    curl -sS -X POST -H 'Content-Type: application/json' \
    -u admin:admin \
    http://localhost:8080/saiku/api/ai/ask \
    -d @- <<'EOF'
    {
    "question": "/weekly-foodmart-rollup for Q4 instead of this week",
    "cube": {
    "connectionName":"unknown_foodmart",
    "catalog":"FoodMart",
    "schema":"FoodMart",
    "cubeName":"Sales"
    }
    }
    EOF
  2. Usługa parsuje slash i wyszukuje weekly-foodmart-rollup w katalogu.

  3. Trafienie → ask wysłany do LLM to:

    Skill: weekly-foodmart-rollup
    ## Steps
    1. Query total [Measures].[Store Sales] …
    User follow-up: for Q4 instead of this week
  4. LLM uruchamia kroki, stosuje kontynuację („Q4 instead of this week”) do filtra czasu i emituje AiQueryRequest jak zwykle.

  5. Pudło → ask podróżuje niezmieniony. LLM widzi go jako zwykły prompt z wiodącym ukośnikiem. Nic się nie psuje.

Język naturalny

Katalog ląduje w system prompcie LLM jako:

Available skills (admin-authored workflows). When the user's question
closely matches one of these — or when the message starts with
`/<skill-name>` — use the skill's steps to structure your response
instead of freewheeling:
- /weekly-foodmart-rollup: Weekly revenue rollup for the FoodMart Sales cube…
- /store-comp-report: Store-level comp vs same period last year…
- /churn-cohort: 30/60/90 day churn cohort split by acquisition channel…

Model kieruje samodzielnie. Jeśli użytkownik pyta „jak sklepy wypadły w porównaniu z zeszłym rokiem?”, matcher języka naturalnego wybiera /store-comp-report; użytkownik nigdy nie widzi decyzji o kierowaniu.

Powierzchnia REST

Wszystkie trzy endpointy siedzą pod standardową ścieżką bazową AI Ask.

GET /rest/saiku/api/ai/skills

Katalog — zwarte podsumowania {name, description, cube}.

Okno terminala
curl -sS -u admin:admin \
http://localhost:8080/saiku/api/ai/skills | jq
{
"skills": [
{
"name": "weekly-foodmart-rollup",
"description": "Weekly revenue rollup for the FoodMart Sales cube: total Store Sales …",
"cube": "unknown_foodmart/FoodMart/FoodMart/Sales"
}
]
}

GET /rest/saiku/api/ai/skills?errors=true

Ten sam kształt, plus tablica errors[] listująca każdy plik, który nie sparsował się w tym skanie. Każdy wpis niesie stabilny kod maszynowy, więc operatorzy naprawiają zły frontmatter bez czytania logów serwera.

{
"skills": [ /* … */ ],
"errors": [
{
"path": "broken.md",
"code": "MISSING_FRONTMATTER",
"message": "expected leading `---` YAML frontmatter block"
}
]
}

Stabilne kody błędów:

KodKiedy
EMPTY_SKILLPlik jest pusty lub całkowicie białe znaki.
MISSING_FRONTMATTERBrak wiodącej granicy ---.
EMPTY_BODYFrontmatter sparsowany, ale brak markdown po zamykającej granicy.
MALFORMED_YAMLParser YAML odrzucił frontmatter.
MISSING_FIELDWymagane pole (name / description) nieobecne.
BLANK_FIELDWymagane pole obecne, ale puste / same białe znaki.
TYPE_MISMATCHPole obecne, ale zły typ (name: 42).
INVALID_NAMEname nie pasuje do [a-z][a-z0-9-]{0,63}.
UNKNOWN_FIELDFrontmatter zawiera pole spoza schemy.
DUPLICATE_NAMEDwa pliki skilli zadeklarowały tę samą name.
IO_ERRORNie udało się odczytać pliku (poziom systemu plików).

GET /rest/saiku/api/ai/skills/{name}

Pełne ciało jednego skilla — surowy markdown. Przydatne dla menu slash w UI, które podglądasz przepływ, zanim użytkownik naciśnie wyślij.

POST /rest/saiku/api/ai/skills/refresh

Wymuś ponowny skan (omija sprawdzenie sygnatury mtime). Zwraca świeże liczniki, więc operatorzy mogą na oko ocenić przeładowanie.

Okno terminala
curl -sS -u admin:admin -X POST \
http://localhost:8080/saiku/api/ai/skills/refresh
{ "skills": 4, "errors": 0 }

Dołączony przykład

Świeże instalacje Saiku umieszczają działający przykład przy pierwszym starcie — zobacz weekly-foodmart-rollup.md. Operatorzy dodają własne obok; zasiany jest idempotentny (ląduje tylko, gdy plik docelowy jeszcze nie istnieje).

Jak działa skan

  • Podkatalogi są przechodzone rekurencyjnie.
  • Pliki nie-.md są ignorowane (błąkający się README.txt nie zawiesi skanu).
  • Zepsute pliki nie kładą katalogu — ParseException na jednym pliku pozostawia pozostałe nienaruszone.
  • Duplikaty na name produkują błąd DUPLICATE_NAME na drugim pliku do załadowania; pierwszy wygrywa.

Ask-i Ossie też je podchwytują

Katalog skilli to pojedynczy magazyn per-launcher serwowany pod /rest/saiku/api/ai/skills. Każdy skill, którego pole cube: nazywa referencję Ossie (np. pharma/Pharma/Pharma/Sales), jest naturalnie ograniczony przez tę referencję, gdy kierowany przez /ai/ossie/ask. MDX i Ossie dzielą ten sam prymityw.

Dokąd dalej