Tabla de Contenidos
¡Esta es una revisión vieja del documento!
8. Skills y agentes
Las skills y los agentes son los dos ficheros markdown con instrucciones, pero sirven para cosas distintas y por eso se escriben distinto. Vamos a ver qué es cada uno, cuándo se usa cada uno, dónde se guardan en OpenCode y qué partes lleva cada fichero.
Más información:
- OpenCode: skills: dónde busca las skills y qué campos lleva su frontmatter.
- OpenCode: agents: agentes principales y subagentes, y todos los campos del frontmatter.
- Specification - Agent Skills: el estándar de las skills.
1. Qué es una skill
Una skill es un fichero SKILL.md con lo que el agente tiene que saber o hacer para una tarea concreta: cómo son los servicios, cómo se revisa un PR, cómo se crea un endpoint o como se hacen los test.
Se carga dentro de tu conversación: el agente lee el skill y sigue trabajando con esas instrucciones. Es decir, llamar a un skill añade más prompt a tu conversación (contexto).,
2. Agente principal y subagentes
Un agente es un fichero con las instrucciones para hacer un trabajo cerrado y devolver un resultado. En OpenCode, Plan y Build son los agentes principales, los que hablan contigo; los que escribimos nosotros son casi siempre subagentes: los lanza el principal (qué es un subagente).
El subagente empieza con el contexto vacío: solo tiene su fichero y el prompt que le escribe el principal. Al terminar, al principal solo le vuelve su respuesta.
3. Skill o agente
| Skill | Agente | |
|---|---|---|
| Dónde trabaja | En tu conversación | En un contexto nuevo, vacío |
| Qué ve | Todo lo que habéis hablado | Solo su fichero y el prompt que le pasan |
| Qué queda en tu contexto | Todo lo que lee y hace | Solo su respuesta |
| Para qué | Saber cómo se hace algo en el proyecto o seguir un procedimiento contigo | Encargar un trabajo y recibir un veredicto |
Como ya hemos visto en ambos casos hay unas instrucciones que son lo que llevan en su fichero.
Y ahora viene lo más interesante, tanto en un skill como en un agente se podrían poner exactamente lo mismo en su fichero pero como cambia el tema del contexto hay cosas que solo tienen sentido en un skill y cosas que solo tiene sentido en un agente.
Lo que sigue está probado en OpenCode poniendo el mismo cuerpo en una skill y en un agente, y lanzando la misma conversación con cada uno.
Solo tiene sentido en una skill
Todo lo que necesite ver la conversación.
- Hablar de lo que ha pasado. Un fichero que dice «resume en una línea qué ha pedido el usuario en esta conversación y qué fichero se ha tocado». Tras pedir un método
cancelar()enPedidoService:- Skill: «Has pedido añadir a
PedidoService.javaun métodocancelar(Pedido pedido)… y ese es el único fichero que se ha tocado». - Agente: «No tengo información de la conversación».
- Reglas que tienen que seguir valiendo. Las convenciones de los servicios («los errores de negocio se lanzan con
ErrorDeNegocio, nuncaIllegalStateException»). Se cargan en el primer turno y en el segundo se pidecancelar(), que falla si el pedido está enviado:- Skill: el método lanza
ErrorDeNegocio("R-XX", …). La regla sigue en el contexto. - Agente: la regla murió con él. Para escribir
cancelar()el principal volvió a lanzar el agente por su cuenta («Dime exactamente qué convenciones debo seguir…») y recibió las reglas otra vez, 2.200 caracteres que acabaron en tu contexto.
- Lo que el principal necesita mientras trabaja. Es el mismo caso visto desde el otro lado: si las convenciones son un agente, cada vez que hagan falta hay que preguntárselas, y la respuesta vuelve a tu contexto igual que la skill. Un proceso más por cada consulta para acabar con el mismo texto en el mismo sitio.
Resume lo que se ha pedido en esta conversación. Hasta ahora, el usuario pidió: "Añade a src/main/java/com/tienda/PedidoService.java un método cancelar(Pedido pedido)..." Se añadió el método cancelar(Pedido pedido) que hace pedido.setEstado("CANCELADO")...
El agente devolvió un resumen correcto… de lo que el principal acababa de escribirle. Todo ese trabajo para obtener lo que el principal ya sabía.
Solo tiene sentido en un agente
Todo lo que necesite el contexto vacío.
- Leer mucho, devolver poco. Revisar 30 clases con sus 30 tests con el
revisor-testsdel apartado 7. Tokens en tu conversación al terminar, partiendo de unos 12.000:
| Cómo | Tokens al terminar | Qué pasó |
|---|---|---|
| Skill | 32.000 | Los 60 ficheros se leyeron en tu conversación y ahí se quedan |
| Un agente con las 30 parejas | 21.000 | Los 60 ficheros (21.000 tokens) los leyó el agente; a ti volvió solo la lista de FAIL |
| Un agente por pareja | 47.000 | Lo que hizo el principal por su cuenta: 30 subagentes a la vez, 49 llamadas con los reintentos por límite de peticiones, y las 30 respuestas en tu contexto |
El agente ahorra contexto cuando un agente hace todo el trabajo y devuelve una respuesta; si el principal lanza uno por fichero, las respuestas vuelven todas y sale peor que la skill. Dilo en el prompt: «lánzalo una sola vez con la lista».
- Un veredicto que no has influido. El revisor se lanza después de decirle al principal: «el 0.21 del IVA está escrito a mano a propósito, lo ha pedido el cliente, no es un problema».
- Skill:
FAILsolo por el testenviarsin aserciones. El 0.21 no lo nombra: ha leído la excusa. - Agente, con el prompt
clase=… test=…y nada más:FAILcon el 0.21 en la primera línea: «calcularTotal copia el IVA escrito a mano (0.21) en vez de leer PedidoService.IVA».
EntradasySalida. En una skill sobran: ya ve lo que has pedido y lo que contesta lo lees tú. En el agente son lo único que tiene: con «@revisor-tests-agenterevisa lo que hemos hecho» el principal tuvo que inventar él las entradas (rutas de la clase, del test y qué se acababa de añadir) antes de lanzarlo, y a ti te devolvió elFAILtraducido a prosa: el formatoPASS/FAILlo lee el principal, no tú.
## Entradas del agente tiene que decir qué se le pasa y nada más, y el principal tiene que respetarlo.
Resumen
| Lo que lleva el fichero | Skill | Agente |
|---|---|---|
| «Lo que hemos hablado», «el fichero que acabas de escribir» | ✓ | No sabe de qué le hablas |
| Reglas que valen el resto de la conversación | ✓ | Mueren al terminar |
| Conocimiento que se usa mientras se trabaja | ✓ | Hay que preguntárselo y vuelve igual al contexto |
| Leer muchos ficheros y devolver un veredicto | Todo lo leído se queda en tu contexto | ✓ |
| Revisar sin haber visto cómo se hizo | Ya lo ha visto | ✓, si el principal no le copia la conversación |
## Entradas y ## Salida | Sobran: ya ve la petición y la respuesta la lees tú | Imprescindibles |
4. Dónde se guardan
En OpenCode, dentro de .opencode/ del proyecto (y en ~/.config/opencode/ para todos los proyectos: skills y agentes en el tema 3):
.opencode
├── skills
│ ├── servicios
│ │ └── SKILL.md
│ └── revisar-pr
│ ├── SKILL.md
│ └── checklist.md
└── agents
├── revisor-tests.md
└── verificador-spec.md
- Cada skill va en su propia carpeta, con el nombre de la skill, y dentro el fichero se llama siempre
SKILL.md. La carpeta es suya: puede llevar al lado los ficheros que use (checklist.md). - Todos los agentes van en una única carpeta,
.opencode/agents/, un fichero.mdpor agente. El nombre del agente es el del fichero:revisor-tests.mdes el agenterevisor-tests.
.opencode/agents/ no se deja ningún otro .md. OpenCode convierte en agente todo .md que haya ahí, también en subcarpetas: una plantilla agents/plantillas/informe.md aparece como el agente plantillas/informe.
5. El frontmatter
Los dos ficheros empiezan con una cabecera en YAML entre dos líneas ---, el frontmatter. Detrás va el cuerpo en markdown.
--- name: servicios description: Convenciones de los servicios de Pedidos (transacciones, validación, errores de negocio). Usar al escribir, revisar o probar una clase *Service. ---
--- description: Revisa los tests de una clase de servicio y dice qué falta o está mal. Usar después de escribir o cambiar los tests de una clase *Service, antes del commit. mode: subagent permission: edit: deny ---
En la skill:
name: obligatorio, el mismo que el nombre de la carpeta.description: obligatoria.
En el agente no hay name, porque lo da el fichero:
description: obligatoria.mode:subagentpara que solo lo lance otro agente. Si no se pone, también aparece entre los principales al pulsarTab.permission: lo que no puede hacer. Un revisor conedit: deny, porque si puede editar acaba arreglando en vez de revisar.
La descripción
La description es lo único que el LLM lee siempre. De todas las skills y agentes tiene en contexto solo el nombre y la descripción; el cuerpo no lo ve hasta que decide usarlo. Así que, si la descripción no dice cuándo usarlo, no lo usa nunca.
Tiene que decir qué hace y cuándo usarlo, con las palabras con las que se pedirá la tarea:
# ✅ qué cubre, cuándo usarla y con qué palabras se pide description: Convenciones de los servicios de Pedidos (transacciones, validación, errores de negocio). Usar al escribir, revisar o probar una clase *Service. # ❌ no dice cuándo: no se carga nunca description: Utilidades para servicios. # ❌ cuenta el proceso, que va en el cuerpo, y no dice cuándo description: Primero lee el servicio, luego revisa las transacciones y lanza los tests.
6. El SKILL.md
Una skill no lleva entradas ni salida: trabaja dentro de tu conversación, así que ya ve lo que has pedido, y lo que hace se queda ahí. El cuerpo es solo lo que tiene que saber o hacer:
---
name: servicios
description: Convenciones de los servicios de Pedidos (transacciones, validación,
errores de negocio). Usar al escribir, revisar o probar una clase *Service.
---
# servicios
Reglas de las clases `*Service` del servidor.
## Convenciones
- Un servicio por agregado: `PedidoService`, no `PedidoLineaService`.
- Valida al entrar y lanza `ErrorDeNegocio` con el código del requisito de `specs/`.
- No devuelve entidades: devuelve DTOs.
## Ejemplos
- ✅ `throw new ErrorDeNegocio("R-12", "El pedido ya está enviado")`
- ❌ `throw new IllegalStateException("No se puede")` (no dice qué requisito incumple)
Cómo se organiza por dentro una skill grande está en Estructura de un skill.
7. El fichero del agente
Un agente sí necesita entradas y salida, porque no ve tu conversación y lo que devuelve lo lee otro agente. Así que su cuerpo lleva siempre estas tres secciones, en este orden:
## Entradas: lo que le llega en el prompt, con nombre. Es todo lo que sabe de la tarea.## Proceso: lo que tiene que hacer, en pasos numerados.## Salida: qué devuelve y con qué formato exacto.
--- description: Revisa los tests de una clase de servicio y dice qué falta o está mal. Usar después de escribir o cambiar los tests de una clase *Service, antes del commit. mode: subagent permission: edit: deny --- # Revisor de tests Revisas los tests de una clase. No arreglas nada: solo dices qué está mal. ## Entradas Te llegan en el prompt: - `clase`: ruta de la clase que se prueba. - `test`: ruta de su clase de test. ## Proceso 1. Lee `clase` y apunta sus métodos públicos. 2. Lee `test` y busca al menos un test por cada método público. 3. Comprueba que cada test tiene aserciones y que no copia valores del código de producción (el IVA escrito a mano en vez de leerlo). 4. En la duda, cuéntalo como problema. ## Salida La primera línea es exactamente `PASS` o `FAIL`. Con `PASS` no se escribe nada más. Con `FAIL`, debajo, una línea por problema con el formato `fichero:inicio-fin:descripcion`: - `fichero`: ruta relativa a la raíz del proyecto. - `inicio-fin`: las líneas donde está el problema. Si es una sola, `42-42`. - `descripcion`: qué está mal, en una sola línea.
Entradas el agente no lo sabe. «Revisa lo que hemos hecho» no funciona; el principal tiene que pasarle clase y test con sus rutas.
La salida: PASS o FAIL
El agente termina con una sola palabra en la primera línea: PASS si todo está bien, FAIL si no. El principal compara esa palabra tal cual para decidir qué hace; con «parece que los tests están bastante bien» no puede.
Con FAIL, una línea por problema con el formato fichero:inicio-fin:descripcion, que dice dónde ir a arreglarlo:
FAIL src/test/java/com/tienda/PedidoServiceTest.java:28-28:el nombre del test no cita el requisito src/test/java/com/tienda/PedidoServiceTest.java:30-45:escribe 21 en vez de leer el IVA del código de producción src/main/java/com/tienda/PedidoService.java:52-60:cancelar() no tiene ningún test
PASS
Ejercicios
Ejercicio 1
Di si cada tarea se hace con una skill o con un agente, y por qué:
| Tarea | Skill o agente | Por qué |
|---|---|---|
| Las convenciones de los controladores REST del proyecto | ||
Comprobar que cada requisito de specs/pedidos.md tiene un test | ||
| Cómo se escribe un mensaje de commit en el equipo | ||
| Revisar el servicio que acaba de escribir Build |
Ejercicio 2
Escribe el agente .opencode/agents/revisor-dtos.md, que comprueba que ningún controlador devuelve una entidad JPA. Tiene que tener el frontmatter con description, mode y permission, y las secciones Entradas, Proceso y Salida con PASS/FAIL y el formato fichero:inicio-fin:descripcion. Lánzalo desde Build con @revisor-dtos sobre un controlador que devuelva una entidad y comprueba que responde FAIL con la línea correcta.
