Subagentes definidos en markdown
definir subagentes como archivos markdown cargados desde disco, en vez de structs Go escritos a mano.
Objetivo: definir subagentes como archivos markdown cargados desde disco, en vez de structs Go escritos a mano.
Dificultad: media. Tiempo: 1–2 horas. Toca: internal/subagent/, main.go.
Lo que ya está en el repo
internal/subagent/registry.go define la interfaz Subagent (Name, Description, Run) y un registro Default. El único subagente concreto hoy es Research en internal/subagent/research.go — su system prompt, su subconjunto de herramientas y su presupuesto de iteraciones están todos cocidos en Go.
Eso está bien para un subagente. En el momento que quieras tres revisores, dos planners y un especialista en búsqueda de código, no vas a querer escribir un tipo Go por cada uno.
Lo que vas a construir
Un loader que escanee .harness/agents/*.md al arrancar, parsee cada archivo y lo registre con subagent.Default. Un archivo se ve así:
---
name: reviewer
description: Revisa un diff buscando bugs, errores sin manejar y problemas de seguridad. Devuelve una lista con viñetas.
tools: [read_file, bash]
max_turns: 6
---
Eres un revisor de código. Mira el diff o los archivos que el caller te indique.
Reporta:
- Bugs y fallos en tiempo de ejecución probables
- Errores no manejados / retornos ignorados
- Preocupaciones de seguridad (secretos, inyección, path traversal)
Sé específico. Cita números de línea. Sin preámbulo.
El frontmatter configura el wrapper; el cuerpo es el system prompt.
Pasos sugeridos
-
Añade un tipo
MarkdownSubagenteninternal/subagent/markdown.goque envuelva unagent.Agenty saque la config de un archivo parseado. ImplementaSubagentigual queResearch— la mayoría de la lógica enRunes idéntica. -
Escribe el loader. Una función
LoadDir(dir string, p provider.Provider, allTools *tool.Registry) ([]Subagent, error)que:- Lea cada
.mddel directorio. - Separe el frontmatter (entre líneas
---) del cuerpo. - Parsee YAML o un formato clave-valor mínimo — tú decides. Parseo
key: valuecon stdlib es suficiente; no tienes que tirar deyaml.v3. - Construya un
tool.Registryfiltrado con solo las herramientas listadas entools:. - Devuelva un
MarkdownSubagentpor archivo.
- Lea cada
-
Conéctalo en
main.go. Después de registrar los subagentes built-in, llama aLoadDiry registra cada uno devuelto. Si el directorio no existe, sálta — el arranque no puede fallar por eso. -
Añade un comando
/subagents reload. Re-escanea el directorio y reemplaza los agentes cargados desde markdown (deja los built-in en paz). Leecommands.gopara el patrón de registro de comandos. -
Valida. Rechaza archivos sin
nameodescriptioncon un error claro que apunte a la ruta del archivo. Un subagente sin nombre producirá una herramientadelegate_vacía.
Aceptación
- Sueltas
.harness/agents/reviewer.mden el directorio de trabajo, corresgo run ., y/subagentslistareviewerjunto aresearch. - El modelo puede despacharlo vía
delegate_reviewer(o la convención de nombres que uses). - Borrar el archivo y correr
/subagents reloadlo quita del registro. - Un archivo malformado (sin
name) imprime un error claro al arrancar pero no peta.
Extra
- Hot-reload con
fsnotify: los edits aparecen sin reiniciar. - Selección de modelo por agente en el frontmatter (
model: gpt-5-codex) — envuelve un Provider separado detrás del agente. - Un campo
extends:que deje que un agente herede el system prompt o las herramientas de otro.