Blog
Tutorial

Authoring custom skills: templates that stay safe

How Simmetric Chat's custom skills compile a stored prompt template into a spotlighted, untrusted context entry - the whitelist and single-pass rules, the three scoping levels, DLP masking before compile, and the max_skills freemium (3 or unlimited).

Simmetric Team8 min di lettura12/10/2026
Authoring custom skills: templates that stay safe

Istruzioni richiamabili, non codice da fidare

Un custom skill è un template di prompt riutilizzabile: scritto una volta, invocato ripetutamente dal chat come slash command. La scommessa di progetto sta nella frase "prompt template": uno skill non contiene chiamate HTTP, né I/O su file, né logica condizionale, né sandbox - non c'è nulla da cui fuggire, quindi la funzione è air-gap-safe per costruzione. Ciò che uno skill contiene è una pipeline di compilazione fissata con confini reali: parametri in whitelist contro uno schema, un passaggio di sostituzione unico, il mascheramento prima della compilazione, e un'iniezione che arriva come dato non fidato.

Questo articolo percorre il percorso di scrittura dal primo all'ultimo punto: come un template viene scritto e compilato, la grammatica dello slash command, i tre livelli di scoping, il confine di privilegio che tiene onesto l'output compilato, e la forma di licenza (max_skills - 3 in Community, illimitati in Enterprise).

Il template e le sue regole

Un template è una stringa semplice con segnaposto {{param}}:

text
Translate to {{targetLang}}: {{input}}

La pipeline di compilazione applica quattro regole (pintate allo schema):

  • Whitelist - sostituiscono solo le chiavi dichiarate da inputSchema.properties dello skill (più i suoi parametri di default). Un segnaposto ignoto resta letterale nell'output - mai risolto dall'ambiente, dal contesto di sistema o da qualsiasi altra cosa.
  • Passaggio unico - corre esattamente un passaggio di sostituzione. Un valore sostituito che contiene a sua volta forme di marker {...} non si ri-espande mai; l'iniezione via template muore qui a livello strutturale.
  • Coerenza - ogni segnaposto del template deve avere l'ingresso corrispondente nell'input-schema; create/patch senza = un 400 nominato.
  • Rifiuto dei marker - il testo statico del template (a segnaposto tolti) non deve contenere forme di marker dei tool-call cross-provider; rifiutate a create/patch. I valori dei parametri hanno la loro difesa dedicata più sotto.

La grammatica dello slash command

Il tasto / apre la palette degli skill (l'autocompletamento); una riga selezionata inserisce lo slug senza inviare. All'invio, il client analizza gli argomenti e manda la chiamata insieme al messaggio battuto:

  • Posizionale: /translate Ciao mondo - i token si fondono nel primo campo richiesto.
  • Per coppie: /translate input=Ciao - riempimenti nominali; gli intervalli tra virgolette (input="Ciao mondo") portano gli spazi.
  • Misto e default: input="Ciao" targetLang=Spanish, oppure una chiave richiesta che prende il suo default dichiarato.

Due comportamenti rendono la grammatica fidabile anziché furba:

  • La regola mai-errore - un /parola non riconosciuto, un nome builtin o una riga disabilitata scivola in un messaggio normale: nessuna chiamata, nessun errore, nulla nascosto.
  • Il server ri-valida tutto - le chiavi sconosciute si ignorano; lo slug si ri-risolve lato server con lo stesso filtro di scope a ogni invocazione (la guardia IDOR).

La lista degli slug riservati (i comandi fissi del chat + i nomi delle skill builtin) rende impossibile l'ombreggiatura: uno skill custom non può mai prendere un nome riservato.

Il percorso della richiesta: risolvi, maschera, compila, incarta

text
"/slug params" → POST chat stream { message, skillCall }
→ resolveInvocableSkill (server-side, con filtro di scope)
→ maschera DLP PRIMA della compilazione (i valori scannerizzati quando il DLP è attivo)
→ compileTemplate (sostituzione in whitelist, un passaggio) + l'involucro spotlight
→ UNA voce di contesto utente spotlightata dentro l'orchestratore

L'ordine col DLP è il dettaglio portante: i parametri si mascherano prima della compilazione. Compilando nell'ordine inverso, un testo scritto nel template potrebbe nascondere un parametro allo scanner; con questo ordine, il prompt compilato contiene solo valori mascherati. (La pipeline DLP di per sé - le maschere numerate, gli originali cifrati, l'unmask per gli admin - è un'altra storia.)

Il confine di privilegio

L'output compilato di un template è dato fornito dall'utente, non istruzioni. Entra nella conversazione come una voce di contesto spotlightata - etichettata, conteggiata nel budget come qualunque altro tool result, e piantata da un test di privilegio: uno skill custom può aggiungere contesto ma non può mai aggiungere tool, permessi o voci di registry. La trascrizione conserva il messaggio battuto; solo il prompt compilato entra come contesto dello skill.

Ecco perché "nessuna sandbox" è una caratteristica e non un limite: la superficie di potenza del template - ciò che può influenzare - è esattamente una voce di contesto, e nient'altro.

Scoping: tre livelli, derivati lato server

  • Personale - lo risolve solo l'autore (qualsiasi utente col permesso di creazione).
  • Workspace - chiunque chatt in quel workspace (creato da editor+; un viewer riceve un 403 esplicito). La riga persistita porta un user id a null - una riga timbrata dal creatore perderebbe attraverso i workspace, e lo schema chiude quella classe.
  • Globale - tutti nell'organizzazione (creato solo dagli admin).

Lo scope rivendicato dal client non viene mai fidato: il server deriva il livello dalla forma persistita e filtra la risoluzione per chat. Le modifiche e le cancellazioni valgono dal chat successivo - nessun hook, nessun riavvio. La cancellazione è una tomba soft, e lo slug si libera subito: il nome di uno skill eliminato è ricreabile sul posto.

Il fatto freemium: max_skills

Il limite di licenza rispecchia gli altri gate numerici della piattaforma: 3 skill personalizzati in Community, illimitati in Enterprise - la riga del census F-SKILL-1, fondata sulle costanti di licenza:

  • Applicato solo alla CREATE - gateare le invocazioni interromperebbe il chat a metà per un motivo di licenza, progetto rifiutato; il conteggio sta al provisioning.
  • Il conteggio è organizzazione-ampie sulle righe custom - le builtin e le righe del marketplace non contano mai.
  • Sopra il limite, la forma a paywall standard nomina la funzione e il tier - e la via d'uscita è self-service: ogni utente può eliminare i propri skill per liberare quota, senza intervento dall'amministrazione.

Quell'ultima proprietà è il contratto freemium fatto onestamente: il limite è visibile nella UI di gestione, il 402 spiega sé stesso, e la via d'uscita non costa un ticket di supporto.

Per continuare

La guida porta il riferimento operativo completo - endpoint, il dialog del form, le semantiche della test-preview - su /it/docs/custom-skills:

Provenance: Simmetric Chat docs/CUSTOM_SKILLS.md (v0.28.1, Phase 190 SKIL-01..05), packages/shared/src/schemas/skill.schema.ts, license.ts (max_skills) - allo stato documentale fissato dal census (SHA aca5b8ff).

Articoli correlati