Blog
Tutoriales

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

Simmetric Team8 min de lectura12/10/2026
Escribir custom skills: plantillas que se mantienen seguras

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}}:

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

El pipeline de compilación impone cuatro reglas (clavadas en el esquema):

  • Whitelist - solo las claves declaradas por inputSchema.properties del 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 /palabra desconocida, 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

text
"/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 orquestador

El 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.md F-SKILL-1 (los custom skills + el freemium max_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).

Artículos relacionados