Aller au contenu

Compétences d'agent

Les compétences d’agent (Agent Skills) sont des fichiers markdown avec frontmatter YAML qui vivent dans saiku-home/skills/. Le launcher les scanne paresseusement à chaque requête et injecte le catalogue dans le system prompt du LLM, pour que n’importe quel tour d’AI Ask — MDX ou Ossie — puisse être routé vers un workflow créé par l’admin plutôt que d’improviser.

Petite fonctionnalité, valeur disproportionnée : elle permet aux opérateurs de codifier les questions canoniques de leur équipe (« le rollup exec de ce trimestre », « la cohorte de churn », « le rapport de comparaison de magasins ») sans posséder encore un autre outil. La compétence vit dans le dépôt, est versionnée avec le modèle sémantique et passe en revue de code comme tout le reste.

Livré dans saiku v4.7 comme saiku#1426.

Deux chemins d’invocation

Format de fichier

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

ChampRequisTypeNotes
nameouistringkebab-case, [a-z][a-z0-9-]{0,63}. Utilisé comme slug de slash.
descriptionouistringUne ligne ou un scalaire de bloc. Affiché dans /ai/skills et le prompt du LLM.
cubenonstringRéférence connection/catalog/schema/cubeName. Délimite la compétence.

Les clés de premier niveau inconnues sont rejetées. Une faute de frappe (descripton) fait surface sous forme d’erreur structurée UNKNOWN_FIELD — jamais comme une compétence silencieuse et sans nom.

Corps

Tout ce qui suit la clôture --- est le corps. Le markdown est recommandé mais pas requis ; le corps est traité comme du texte opaque et collé verbatim dans le prompt du LLM lors d’une correspondance de slash-command. Gardez-le concis — le LLM doit lire le fichier entier.

Slash command

  1. L’utilisateur (ou le widget DimSum en votre nom) poste une question commençant par / :

    Fenêtre de terminal
    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. Le service parse le slash et cherche weekly-foodmart-rollup dans le catalogue.

  3. Correspondance → la question envoyée au LLM est :

    Skill: weekly-foodmart-rollup
    ## Steps
    1. Query total [Measures].[Store Sales] …
    User follow-up: for Q4 instead of this week
  4. Le LLM exécute les étapes, applique le suivi (« Q4 instead of this week ») au filtre temporel et émet une AiQueryRequest comme d’habitude.

  5. Absence de correspondance → la question voyage inchangée. Le LLM la voit comme un prompt ordinaire avec un slash en tête. Rien ne casse.

Langage naturel

Le catalogue atterrit dans le system prompt du LLM sous la forme :

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…

Le modèle route de lui-même. Si l’utilisateur demande « how did stores compare to last year? », le matcher en langage naturel choisit /store-comp-report ; l’utilisateur ne voit jamais la décision de routage.

Surface REST

Les trois endpoints se trouvent sous le chemin de base standard d’AI Ask.

GET /rest/saiku/api/ai/skills

Le catalogue — des résumés compacts {name, description, cube}.

Fenêtre de terminal
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

Même forme, plus un tableau errors[] listant chaque fichier qui a échoué à parser lors de ce scan. Chaque entrée porte un code machine stable pour que les opérateurs corrigent un mauvais frontmatter sans lire les logs du serveur.

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

Codes d’erreur stables :

CodeQuand
EMPTY_SKILLLe fichier est vide ou entièrement composé d’espaces.
MISSING_FRONTMATTERPas de clôture --- en tête.
EMPTY_BODYLe frontmatter a été parsé mais pas de markdown après la clôture.
MALFORMED_YAMLLe parseur YAML a rejeté le frontmatter.
MISSING_FIELDChamp requis (name / description) non présent.
BLANK_FIELDChamp requis présent mais vide / composé uniquement d’espaces.
TYPE_MISMATCHChamp présent mais du mauvais type (name: 42).
INVALID_NAMEname ne correspond pas à [a-z][a-z0-9-]{0,63}.
UNKNOWN_FIELDLe frontmatter contient un champ absent du schéma.
DUPLICATE_NAMEDeux fichiers de compétence ont déclaré le même name.
IO_ERRORImpossible de lire le fichier (niveau système de fichiers).

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

Corps complet d’une compétence — le markdown brut. Pratique pour un menu slash d’UI qui prévisualise le workflow avant que l’utilisateur n’appuie sur envoyer.

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

Force un rescan (contourne la vérification de signature mtime). Retourne les compteurs frais pour que les opérateurs puissent contrôler le rechargement.

Fenêtre de terminal
curl -sS -u admin:admin -X POST \
http://localhost:8080/saiku/api/ai/skills/refresh
{ "skills": 4, "errors": 0 }

Exemple fourni

Les installations fraîches de Saiku déposent un exemple fonctionnel au premier boot — voir weekly-foodmart-rollup.md. Les opérateurs ajoutent les leurs à côté ; celui qui est semé est idempotent (il n’atterrit que quand le fichier cible n’existe pas déjà).

Comment fonctionne le scan

  • Les sous-répertoires sont parcourus récursivement.
  • Les fichiers non .md sont ignorés (un README.txt égaré ne fera pas planter le scan).
  • Les fichiers cassés ne mettent pas à terre le catalogue — une ParseException sur un fichier laisse les autres intacts.
  • Les doublons sur name produisent une erreur DUPLICATE_NAME sur le second fichier chargé ; le premier l’emporte.

Les questions Ossie les récupèrent aussi

Le catalogue de compétences est un unique store par launcher servi à /rest/saiku/api/ai/skills. Toute compétence dont le champ cube: nomme une référence Ossie (par ex. pharma/Pharma/Pharma/Sales) est naturellement délimitée par cette référence lorsqu’elle est routée à travers /ai/ossie/ask. MDX et Ossie partagent la même primitive.

Où aller ensuite