Escribir custom skills: plantillas que se mantienen seguras
La compilación de un skill a una entrada de contexto resaltada: whitelist, pasada única, scoping ×3, DLP antes de compilar y el freemium max_skills (3 o ilimitado).

Instrucciones que se llaman, no código en el que uno debe confiar
Un custom skill es una plantilla de prompt reutilizable: escrita una vez, invocada desde el chat como comando slash repetidamente. La apuesta de diseño está en la frase "plantilla de prompt": un skill no contiene llamadas HTTP, ni E/S de ficheros, ni lógica condicional, ni sandbox - no hay nada de lo que huir, así que la función es air-gap-safe por construcción. Lo que un skill contiene es un pipeline de compilación fijo con fronteras reales: parámetros en whitelist contra un esquema, una única pasada de reemplazo, enmascaramiento antes de compilar, y una inyección que llega como dato no fiable.
Este artículo recorre el camino de escritura de punta a punta: cómo se escribe y compila una plantilla, la gramática del comando slash, los tres niveles de scoping, la frontera de privilegio que mantiene honesta la salida compilada, y la forma de licencia (max_skills - 3 en Community, ilimitado en Enterprise).
La plantilla y sus reglas
Una plantilla es una cadena simple con placeholders {{param}}:
Translate to {{targetLang}}: {{input}}El pipeline de compilación impone cuatro reglas (clavadas en el esquema):
- Whitelist - solo las claves declaradas por
inputSchema.propertiesdel skill (más sus parámetros por defecto) sustituyen. Un placeholder desconocido queda literal en la salida - jamás resuelto desde el entorno, el contexto del sistema o cualquier otra cosa. - Pasada única - corre exactamente una pasada de reemplazo. Un valor sustituido que contenga formas de marcador
{...}no se re-expande nunca; la inyección vía plantilla muere aquí a nivel estructural. - Coherencia - cada placeholder de la plantilla necesita su entrada en el input-schema; create/patch sin = un 400 nombrado.
- Rechazo de marcadores - el texto estático de la plantilla (sin placeholders) no debe llevar formas de marcadores de tool-call interproveedor; se rechazan en create/patch. Los valores de los parámetros llevan su propia defensa más abajo.
La gramática del comando slash
Escribir / abre la paleta de skills (el autocompletado); una fila elegida inserta su slug sin enviar. Al enviar, el cliente parsea los argumentos y manda la llamada junto con el mensaje tecleado:
- Posicional:
/translate Ciao mondo- los tokens se fusionan en el primer campo requerido. - Pares:
/translate input=Ciao- rellenos nombrados; los tramos entre comillas (input="Ciao mondo") llevan espacios. - Mixto y por defecto:
input="Ciao" targetLang=Spanish, o una clave requerida que toma su defecto declarado.
Dos comportamientos hacen la gramática digna de confianza en vez de astuta:
- La regla jamás-error - una
/palabradesconocida, un nombre builtin o una fila deshabilitada resbala a un mensaje normal: sin llamada, sin error, sin nada oculto. - El servidor re-valida todo - las claves desconocidas se ignoran; el slug se re-resuelve en el servidor con el mismo filtro de scoping en cada invocación (la guardia IDOR).
La lista de slugs reservados (los comandos fijos del chat + los nombres de los builtin) hace imposible el camuflaje: un skill custom nunca puede tomar un nombre reservado.
El camino de la petición: resolver, enmascarar, compilar, envolver
"/slug params" → POST chat stream { message, skillCall }
→ resolveInvocableSkill (en el servidor, filtrado por scoping)
→ máscara DLP ANTES de compilar (los valores se escanean cuando el DLP está activo)
→ compileTemplate (reemplazo en whitelist, una pasada) + el envoltorio spotlight
→ UNA entrada de contexto de usuario resaltada en el orquestadorEl orden con el DLP es el detalle que carga el peso: los parámetros se enmascaran antes de la compilación. Compilados en el orden inverso, un texto escrito en la plantilla podría esconder un parámetro al escáner; con este orden, el prompt compilado lleva solo valores enmascarados. (El pipeline DLP en sí - las máscaras numeradas, los originales cifrados, el unmask del admin - es su propia historia.)
La frontera de privilegio
La salida compilada de una plantilla es dato suministrado por el usuario, no instrucciones. Entra en la conversación como una entrada de contexto resaltada - etiquetada, contada en el presupuesto como cualquier otro tool result, y clavada por un test de privilegio: un skill custom puede añadir contexto pero nunca puede añadir tools, permisos ni entradas de registro. La transcripción conserva el mensaje tecleado; solo el prompt compilado entra como contexto del skill.
Por eso "sin sandbox" es una característica y no una limitación: la superficie de poder de la plantilla - lo que puede influir - es exactamente una entrada de contexto, y nada más.
Scoping: tres niveles, derivados en el servidor
- Personal - solo lo resuelve el autor (cualquier usuario con el permiso de crear).
- Workspace - quienquiera que chatee en ese workspace (creado por editor+; un viewer recibe un 403 explícito). La fila guardada lleva un user id a null - una fila estampada por el creador escaparía entre workspaces, y el esquema cierra esa clase.
- Global - todo el mundo en la organización (creado solo por admins).
El scoping reclamado por el cliente jamás se confía: el servidor deriva el nivel de la forma guardada y filtra la resolucin por chat. Las ediciones y las supresiones valen desde el siguiente chat - sin hooks, sin reinicios. El delete es una tumba suave, y el slug se queda libre enseguida: el nombre de un skill borrado es recreable en el momento.
El dato freemium: max_skills
El límite de licencia refleja las otras barreras numéricas de la plataforma: 3 custom skills en Community, ilimitado en Enterprise - la fila del census F-SKILL-1, fundamentada en las constantes de licencia:
- Impuesto solo en el CREATE - ponerle barreras a las invocaciones interrumpiría el chat a mitad por una razón de licencia, un diseño rechazado; el recuento vive en el provisionamiento.
- El recuento va por la organización sobre las filas custom - los builtin y las filas del marketplace nunca cuentan.
- Sobre el límite, la forma de paywall estándar nombra la función y el tier - y la salida es autoservicio: cada usuario puede borrar sus propios skills para liberar cuota, sin intervenir el admin.
Esa última propiedad es el contrato freemium hecho honestamente: el límite es visible en la UI de gestión, el 402 se explica solo, y la salida no cuesta ticket de soporte.
Para seguir
La guía lleva la referencia completa del operador - endpoints, la ventana del formulario, la semántica de la test-preview - en /es/docs/custom-skills:
- Custom skills - la guía
- Datos de este post: filas
FACT-CENSUS.mdF-SKILL-1 (los custom skills + el freemiummax_skills) y F-DLP-1 (la capa de enmascaramiento que este camino atraviesa)
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) - en el estado documental fijado por el census (SHA aca5b8ff).
