Agents de GitHub Copilot al repositori: menys feina humana, més consistència
Com mantenir skills, agents i handoffs al repositori per automatitzar workflows amb GitHub Copilot. Exemple real amb agent orquestrador i dos agents d'acció.
Fa un temps que faig servir GitHub Copilot per escriure codi, però la part que m'ha canviat la manera de treballar no és l'autocompletat: és la possibilitat de definir agents personalitzats que viuen al repositori i s'encadenen entre ells.
La idea és senzilla: si un agent sap llegir el context del projecte (l'estratègia de contingut, les convencions del codi, l'estat de les tasques) i pot invocar un altre agent quan acaba, deixes de fer de pont entre passos manuals. La feina humana es redueix a revisar i aprovar, no a executar.
El que explico aquí és el patró que faig servir a Tenza: agents definits en fitxers .md al directori .github/, skills com a documents de domini, i handoffs que connecten els passos del flux. Tot al repositori. Tot reproducible.
Tres peces: agents, skills i handoffs
Abans d'entrar en exemples, val la pena entendre com s'organitzen les tres peces.
Un agent és un fitxer .agent.md a .github/agents/. Defineix qui és l'agent, quines eines pot fer servir, i les instruccions de comportament en format Markdown. GitHub Copilot pot invocar-lo directament des del chat amb @NomAgent.
Una skill és un document de domini a .github/skills/<nom>/SKILL.md. No és un agent: no s'invoca directament. Un agent el llegeix quan necessita coneixement especialitzat. La diferència és important: les skills contenen el com (runbooks, convencions, exemples), mentre que l'agent conté el qui fa què.
Un handoff és un bloc de metadades al final d'un agent que declara quins agents poden continuar el flux:
handoffs:
- label: Revisa el SEO de l'article
agent: seo-review
prompt: Revisa i completa el frontmatter de l'article per optimitzar el SEO.
Quan l'agent acaba, Copilot mostra el botó "Revisa el SEO de l'article". Un clic, i el context passa al següent agent sense que hagis d'escriure res.
Per qué tot al repositori
La primera versió d'aquest sistema la tenia en fitxers locals fora del repo. Funcionava, però tenia un problema: quan obria el projecte des d'una altra màquina, o quan havia de documentar el flux per a algú nou, res d'allò existia.
La regla que segueixo a Tenza és: si no és al repo, no existeix. Això vol dir:
- Els agents estan a
.github/agents/ - Les skills estan a
.github/skills/ - Les instruccions globals estan a
.github/copilot-instructions.md - L'estat del projecte (tasques, roadmap, pròxim pas) estan a
docs/
Quan un agent llegeix docs/tasks.md o content/strategy.md, no fa magia: llegeix fitxers del repo que tu controles. Pots editar-los, fer-los servir en commits, incloure'ls a les PRs. El context de l'agent és el mateix que el context del projecte.
L'exemple: flux de publicació d'articles
El flux que he construït a Tenza té tres passos: escriure l'article, revisar el SEO, i afegir-lo a la newsletter. Cada pas té el seu agent. El primer agent invoca el segon via handoff, i el segon invoca el tercer.
@Blog Article → @SEO Review → @Newsletter
Però darrere d'aquest flux hi ha un quart element que fa de connector invisible: la documentació automàtica.
L'agent orquestrador: @Project Update
Tinc un agent que no escriu ni publica res. El que fa és llegir l'estat actual del projecte i actualitzar els tres fitxers de seguiment: docs/tasks.md, docs/roadmap.md i docs/next-steps.md.
---
name: Project Update
description: >
Actualitza la documentació del projecte després de completar una tasca.
Llegeix el context de la sessió, afegeix l'entrada a tasks.md, marca
ítems del roadmap com a completats, i reescriu next-steps.md amb el
pròxim pas clar.
tools: [read, edit]
---
## Instruccions
1. Llegeix `docs/tasks.md`, `docs/roadmap.md` i `docs/next-steps.md`.
2. Demana a l'usuari: "Què s'ha fet en aquesta sessió?"
3. Afegeix una entrada datada a `docs/tasks.md` amb el resum.
4. Marca com a completats els ítems del roadmap corresponents.
5. Reescriu `docs/next-steps.md` amb el pròxim pas en format accionable.
handoffs:
- label: Fer deploy
agent: deploy
prompt: Fes commit dels canvis i executa make deploy.
La gràcia d'aquest agent és que externalitza la memòria del projecte. En lloc de recordar on érem, els fitxers docs/ sempre reflecteixen l'estat actual. Quan obro el projecte l'endemà, docs/next-steps.md ja em diu exactament on continuar.
L'agent d'acció 1: @Blog Article
Aquest agent fa una sola cosa: llegir el brief d'un article i escriure'n el cos. Rep la ruta del fitxer .md com a argument, llegeix el frontmatter per entendre el títol, la descripció i el brief, i escriu el cos en Markdown respectant el to del projecte.
La clau és que no inventa el context: el llegeix de content/strategy.md. Això vol dir que si canvio el públic objectiu o el to de marca al fitxer d'estratègia, tots els articles futurs s'adaptaran automàticament sense tocar cap agent.
---
name: Blog Article
description: >
Genera el cos complet d'un article de blog a partir del frontmatter.
Llegeix content/strategy.md per adaptar-se al to i l'audiència del projecte.
argument-hint: "Ruta al fitxer .md de l'article"
tools: [read, edit]
---
## Instruccions
1. Llegeix `content/strategy.md`.
2. Llegeix el fitxer de l'article: extreu title, description, brief, readingTime.
3. Escriu el cos de l'article respectant l'estructura i el to.
4. No modifiquis el frontmatter.
handoffs:
- label: Revisar SEO
agent: seo-review
prompt: Completa el frontmatter SEO d'aquest article.
El handoff al final és el que tanca el cercle: quan l'agent acaba d'escriure, Copilot et mostra "Revisar SEO" com a botó. Un clic, i l'agent SEO rep el context de l'article que s'acaba d'escriure.
L'agent d'acció 2: @SEO Review
L'agent de revisió SEO llegeix l'article complet i emplena els camps del frontmatter que falten: keywords, faq, i newsletter_snippet. Després crea un fitxer de snippet pendent a apps/tenza/content/newsletter/pending/ perquè l'agent de newsletter el pugui recollir més tard.
---
name: SEO Review
description: >
Completa el frontmatter SEO d'un article: keywords, faq, newsletter_snippet.
Crea el fitxer pendent per a la cua de newsletter.
tools: [read, edit]
---
## Instruccions
1. Llegeix `content/strategy.md` per als keyword seeds.
2. Llegeix l'article complet (frontmatter + cos).
3. Escriu `keywords` (3-6 frases long-tail).
4. Genera `faq` (3-5 preguntes reals que cercaria un usuari).
5. Escriu `newsletter_snippet` (2-3 frases que poden anar soles en un correu).
6. Crea `apps/tenza/content/newsletter/pending/<slug>.md`.
handoffs:
- label: Crear newsletter
agent: newsletter
prompt: Crea una newsletter amb els snippets pendents.
- label: Actualitzar documentació
agent: project-update
prompt: Actualitza tasks.md, roadmap.md i next-steps.md.
Fixa't que al final hi ha dos handoffs: un per continuar cap a la newsletter, i un per tornar a l'agent orquestrador. Això és deliberat: el flux no és lineal. De vegades voldràs escriure tres articles abans de fer cap newsletter. El handoff et dona l'opció, no t'obliga.
El patró docs/ com a memòria d'estat
Una cosa que m'ha sorprès és fins a quin punt els fitxers docs/ funcionen com a memòria d'agent. docs/tasks.md és el registre de tot el que s'ha fet, ordenat per data. docs/roadmap.md és el pla. docs/next-steps.md conté sempre una sola cosa: el pròxim pas accionable.
Qualsevol agent pot llegir aquests fitxers per entendre l'estat del projecte sense que li hagis d'explicar res. Quan invoco @Blog Article, l'agent pot llegir docs/next-steps.md i saber quins articles estan pendents. Quan invoco @Project Update, pot comparar el roadmap amb les tasques per identificar automàticament els ítems completats.
El patró que segueixo és tenir docs/next-steps.md amb una sola entrada: el títol del pròxim pas en text, seguit de la comanda exacta per executar-lo:
# Pròxim pas: Escriure l'article sobre agents de Copilot
@Blog Article apps/tenza/content/blog/copilot-agents.md
No és documentació per a humans futurs. És un prompt pre-escrit per a l'agent de la pròxima sessió.
Com encadenar-ho tot
La manera pràctica d'usar aquest sistema no és invocar cada agent per separat. És partir d'un únic punt d'entrada i deixar que els handoffs et portin.
Flux típic quan escric un article nou:
- Creo el fitxer
.mdamb el frontmatter (títol, descripció, brief). - Invoco
@Blog Article apps/tenza/content/blog/nom-article.md. - Llegeixo el cos generat, edito el que calgui.
- Faig clic al handoff "Revisar SEO".
- L'agent SEO emplena keywords, faq i snippet, i crea el fitxer pendent.
- Faig clic al handoff "Actualitzar documentació".
- L'agent orquestrador actualitza els tres fitxers
docs/. git commit+make deploy.
Vuit passos, però dos o tres els faig jo: llegir, editar si cal, i fer el commit. La resta és navegació entre agents.
El que no han de fer els agents
Hi ha una temptació de fer agents que facin massa coses. Un agent que escriu l'article, comprova el SEO, envia la newsletter, fa el commit i el deploy. Sembla eficient, però és un error.
Un agent que fa massa coses és difícil de depurar, difícil de reutilitzar, i quan falla no saps on. La regla que segueixo és: un agent, una responsabilitat. Si un agent pot invocar el següent via handoff, no cal que faci la feina del següent.
L'altra cosa que els agents no han de fer és guardar estat intern. Tot l'estat ha de viure en fitxers del repositori: els docs/, el frontmatter dels articles, els fitxers pendents de newsletter. Si tanques VS Code i tornes l'endemà, l'estat ha de ser recuperable llegint el repo, no la memòria de la sessió.
Conclusió
El canvi real no és tenir agents més intel·ligents. És tenir un sistema on la feina d'execució (escriure, formatar, actualitzar docs) la fan els agents, i la feina humana és jutjar, decidir i aprovar.
La combinació de .github/agents/, .github/skills/, i docs/ com a memòria d'estat és el que ho fa possible. Tot al repositori, tot versionat, tot reproducible des de zero. Que és exactament el que ha de ser qualsevol peça d'infraestructura — fins i tot la que gestiona el coneixement.
Construint en públic
Apunta't per rebre les actualitzacions del projecte — stack, decisions, mètriques i aprenentatges.