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).

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}}:
Translate to {{targetLang}}: {{input}}La pipeline di compilazione applica quattro regole (pintate allo schema):
- Whitelist - sostituiscono solo le chiavi dichiarate da
inputSchema.propertiesdello 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
/parolanon 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
"/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'orchestratoreL'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:
- Custom skills - la guida
- La versione in linguaggio semplice
- Fatti in questo post: righe
FACT-CENSUS.mdF-SKILL-1 (gli skill personalizzati + il freemiummax_skills) e F-DLP-1 (il layer di mascheramento che questo percorso attraversa)
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).
