Zum Inhalt springen

Agent Skills

Agent Skills sind Markdown-Dateien mit YAML-Frontmatter, die in saiku-home/skills/ leben. Der Launcher scannt sie bei jeder Anfrage lazy und injiziert den Katalog in den LLM-System-Prompt, sodass jeder AI-Ask-Turn — MDX oder Ossie — zu einem von Admins verfassten Workflow routen kann, statt frei zu improvisieren.

Kleines Feature, überproportionaler Wert: Es lässt Operatoren die kanonischen Fragen ihres Teams kodifizieren („der Exec-Rollup dieses Quartals”, „die Churn-Kohorte”, „der Store-Comp-Report”), ohne noch ein weiteres Tool besitzen zu müssen. Der Skill lebt im Repo, versioniert mit dem Semantikmodell und wird wie alles andere Code-reviewt.

Ausgeliefert in saiku v4.7 als saiku#1426.

Zwei Aufrufpfade

Dateiformat

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

FeldErforderlichTypAnmerkungen
namejastringkebab-case, [a-z][a-z0-9-]{0,63}. Als Slash-Slug verwendet.
descriptionjastringEine Zeile oder Block-Skalar. Gezeigt in /ai/skills und im LLM-Prompt.
cubeneinstringconnection/catalog/schema/cubeName-Ref. Beschränkt den Skill.

Unbekannte Top-Level-Schlüssel werden abgelehnt. Ein Tippfehler (descripton) taucht als strukturierter UNKNOWN_FIELD-Fehler auf — nie als stiller namenloser Skill.

Body

Alles nach dem schließenden ----Zaun ist der Body. Markdown wird empfohlen, aber nicht gefordert; der Body wird als undurchsichtiger Text behandelt und bei einem Slash-Command-Treffer wörtlich in den LLM-Prompt eingefügt. Halten Sie ihn knapp — das LLM muss die ganze Datei lesen.

Slash-Command

  1. Der Nutzer (oder das DimSum-Widget in Ihrem Namen) postet eine Anfrage, die mit / beginnt:

    Terminal-Fenster
    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. Der Service parst den Slash und schlägt weekly-foodmart-rollup im Katalog nach.

  3. Treffer → die an das LLM gesendete Anfrage lautet:

    Skill: weekly-foodmart-rollup
    ## Steps
    1. Query total [Measures].[Store Sales] …
    User follow-up: for Q4 instead of this week
  4. Das LLM führt die Schritte aus, wendet die Folge („Q4 instead of this week”) auf den Zeitfilter an und gibt wie gewohnt einen AiQueryRequest aus.

  5. Fehltreffer → die Anfrage reist unverändert. Das LLM sieht sie als gewöhnlichen Prompt mit einem führenden Slash. Nichts bricht.

Natürliche Sprache

Der Katalog landet im LLM-System-Prompt als:

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…

Das Modell routet von selbst. Wenn der Nutzer „how did stores compare to last year?” fragt, wählt der Natürlichsprach-Matcher /store-comp-report; der Nutzer sieht die Routing-Entscheidung nie.

REST-Oberfläche

Alle drei Endpunkte sitzen unter dem Standard-AI-Ask-Basispfad.

GET /rest/saiku/api/ai/skills

Der Katalog — kompakte {name, description, cube}-Zusammenfassungen.

Terminal-Fenster
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

Gleiche Form, plus ein errors[]-Array, das jede Datei auflistet, die bei diesem Scan nicht geparst werden konnte. Jeder Eintrag trägt einen stabilen Maschinencode, sodass Operatoren kaputtes Frontmatter korrigieren, ohne Server-Logs zu lesen.

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

Stabile Fehlercodes:

CodeWann
EMPTY_SKILLDatei ist leer oder nur Leerraum.
MISSING_FRONTMATTERKein führender ----Zaun.
EMPTY_BODYFrontmatter geparst, aber kein Markdown nach dem schließenden Zaun.
MALFORMED_YAMLYAML-Parser hat das Frontmatter abgelehnt.
MISSING_FIELDErforderliches Feld (name / description) nicht vorhanden.
BLANK_FIELDErforderliches Feld vorhanden, aber leer / nur Leerraum.
TYPE_MISMATCHFeld vorhanden, aber falscher Typ (name: 42).
INVALID_NAMEname passt nicht zu [a-z][a-z0-9-]{0,63}.
UNKNOWN_FIELDFrontmatter enthält ein Feld, das nicht im Schema ist.
DUPLICATE_NAMEZwei Skill-Dateien deklarierten denselben name.
IO_ERRORDatei konnte nicht gelesen werden (Dateisystem-Ebene).

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

Vollständiger Body eines Skills — das rohe Markdown. Praktisch für ein UI-Slash-Menü, das den Workflow vorschaut, bevor der Nutzer auf Senden drückt.

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

Erzwingt einen erneuten Scan (umgeht die mtime-Signaturprüfung). Gibt die frischen Zahlen zurück, damit Operatoren das Neuladen mit einem Blick prüfen können.

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

Mitgeliefertes Beispiel

Frische Saiku-Installationen platzieren beim ersten Boot ein funktionierendes Beispiel — siehe weekly-foodmart-rollup.md. Operatoren fügen ihre eigenen daneben hinzu; das geseedete ist idempotent (landet nur, wenn die Zieldatei noch nicht existiert).

Wie der Scan funktioniert

  • Unterverzeichnisse werden rekursiv durchlaufen.
  • Nicht-.md-Dateien werden ignoriert (eine verirrte README.txt bringt den Scan nicht zum Absturz).
  • Kaputte Dateien reißen den Katalog nicht runter — eine ParseException auf einer Datei lässt die anderen intakt.
  • Duplikate auf name erzeugen einen DUPLICATE_NAME-Fehler auf der zweiten zu ladenden Datei; die erste gewinnt.

Ossie-Anfragen nehmen diese auch auf

Der Skill-Katalog ist ein einzelner Store pro Launcher, bereitgestellt unter /rest/saiku/api/ai/skills. Jeder Skill, dessen cube:-Feld eine Ossie-Ref benennt (z. B. pharma/Pharma/Pharma/Sales), wird natürlicherweise durch diese Ref beschränkt, wenn er über /ai/ossie/ask geroutet wird. MDX und Ossie teilen sich dasselbe Primitiv.

Wohin als Nächstes