Guía

Cómo asignar tareas a un agente de IA: una función a la vez, lista de funciones y criterios de terminado

Actualizado: · Skillaria.Top

Al agente de IA se le asigna una tarea cada vez: en curso hay siempre exactamente una función, con un comportamiento observable y un comando de verificación ejecutable, y todo lo que el agente detecta por el camino se anota en el backlog en lugar de arreglarlo. El estado de todas las funciones se guarda en una lista legible por máquina (por ejemplo, feature_list.json), y solo un script de verificación puede marcar una función como terminada, no el propio agente.

Por qué el agente se lanza a todo a la vez

Usted pide al agente que haga el registro de usuarios. Un par de horas después, el diff tiene doce archivos modificados y ochocientas líneas nuevas: modelo de usuario, verificación del correo, servicio de envío de correos conectado, hash de contraseñas, middleware refactorizado y pruebas reubicadas. Todo está muy relacionado y ningún escenario pasa completo. La sesión siguiente abre ese código y no entiende por dónde empezar.

El problema no es un «mal modelo». Las personas tienen una sensación de «por hoy basta» y el cansancio normal, y eso limita por sí mismo el alcance del trabajo. El agente no tiene ni lo uno ni lo otro. Añadir «y de paso arreglo el manejo de errores» le cuesta al agente unas decenas de tokens, pero el coste para el proyecto es real: la atención se dispersa, el acoplamiento crece y no queda nada terminado.

Hay además una aritmética sencilla. Sea C la capacidad útil del contexto y k el número de tareas que el agente mantiene a la vez. A cada tarea le corresponden aproximadamente C/k de recurso de razonamiento. Cuando ese valor cae por debajo de lo necesario para terminar una sola tarea, no termina ninguna. Por eso la respuesta a cómo asignar tareas a un agente de IA no empieza por la redacción del prompt, sino por una restricción: una tarea en curso.

Regla de una función (WIP=1)

WIP son las siglas de work in progress, el número de tareas en curso. Para un agente se fija en uno: en cada momento hay activa exactamente una función, y la siguiente empieza solo cuando la actual ha superado la verificación. La regla se escribe en el archivo de instrucciones del proyecto (AGENTS.md o CLAUDE.md) para que el agente la vea en cada sesión:

## Reglas de trabajo
- En cada momento, en estado `active` hay EXACTAMENTE UNA función.
- La siguiente función se empieza solo cuando la actual pasa a `passing`.
- Está prohibido refactorizar «de paso», renombrar, reorganizar carpetas
  o arreglar errores no relacionados. Si encuentra un problema, anótelo en
  `BACKLOG.md` y siga adelante.
- Los cambios fuera del alcance de la función activa cuentan como infracción,
  aunque sean útiles.

El último punto es más importante de lo que parece. El agente considera sinceramente útiles sus cambios «de paso», y a menudo lo son. Pero una corrección útil fuera de alcance mezcla dos tareas en un mismo diff, y ya no se pueden revertir ni verificar por separado.

BACKLOG.md: dónde poner lo «de paso»

El impulso de «arreglar de paso» no basta con prohibirlo: necesita una salida legítima. Esa salida es el archivo BACKLOG.md: el agente anota el problema detectado en una o dos líneas y vuelve a la función actual. La información no se pierde y el alcance no crece. Sin backlog, la regla de una función acabará siendo eludida, porque al agente le parece peor «callarse el fallo» que infringir la regla.

# BACKLOG.md
- [ ] middleware/errors.py: formato de errores distinto en /auth y /orders
- [ ] tests/: los fixtures de usuario están duplicados en tres archivos
- [ ] ¿hace falta verificar el correo al registrarse? — preguntar

Criterios de terminado: «terminado» es un comando

La segunda mitad del problema es que el agente decide por sí mismo cuándo una tarea está acabada. «El código tiene buena pinta», «la función existe», «no hay errores de compilación»: todo eso son sensaciones, no hechos. El criterio de terminado de una tarea para el agente debe ser un comando ejecutable con un resultado inequívoco: pasa o no pasa.

F01: registro de usuario
verification: pytest tests/e2e/test_auth.py::test_register_flow
state: active

La diferencia entre un criterio débil y uno estricto se ve bien en ejemplos:

Criterio débilCriterio estricto
El código compilaPasa pytest tests/e2e/test_cart.py -q
No hay errores de sintaxiscurl a /cart devuelve el código 200
La función addToCart existeTras añadir un producto, GET /cart lo muestra con el importe correcto
Parece correctoPasa npx playwright test cart.spec.ts

Un buen comando de verificación es determinista (mismo resultado con el mismo código), se ejecuta sin intervención humana y comprueba el comportamiento desde fuera, tal como lo vería el usuario o el cliente de la API. Si no se logra escribir un comando así, es señal de que la tarea está mal formulada.

Descomposición: de qué tamaño debe ser una función

La pregunta más frecuente después de «una función a la vez» es qué se considera una función. Una tarea demasiado amplia no termina en una sola sesión; una demasiado pequeña se pierde en la sobrecarga de arrancar, cargar contexto y verificar.

  • Demasiado amplia: «implementar el carrito». Son una semana de trabajo y una docena de escenarios.
  • Adecuada: «el usuario puede añadir un producto al carrito y verlo allí». Un único comportamiento observable, un solo escenario de verificación.
  • Demasiado estrecha: «crear el campo quantity en el modelo CartItem». Es un paso de implementación, no un comportamiento; verificarlo por separado no tiene sentido.

Una prueba práctica del tamaño: una función = una sesión del agente = un commit con sentido = un escenario de extremo a extremo (E2E). Si la función no cabe en una sesión, divídala más. Si no se puede escribir un escenario propio para ella, es demasiado pequeña y conviene unirla con la vecina.

Ejemplo de descomposición de la tarea «carrito»

El requisito amplio se divide en unidades atómicas, cada una con un solo comportamiento y su propio comando de verificación:

  1. Añadir un producto al carrito y verlo en la lista.
  2. Cambiar la cantidad del producto: el importe se recalcula.
  3. Eliminar un producto del carrito.
  4. El carrito de un invitado se conserva tras iniciar sesión en una cuenta.
  5. No se puede añadir un producto que no está en stock: debe mostrarse un error comprensible.

Prueba final de la descomposición: ninguna unidad exige tocar «de paso» otra. Si para el punto 4 hay que reescribir el punto 1, el orden o los límites están mal elegidos.

Lista de funciones: feature_list.json

La regla de una función mantiene el orden dentro de una sesión. Pero ¿quién recuerda entre sesiones qué está terminado? Una nota en el chat del tipo «auth y catálogo hechos, el carrito casi listo, pero hay errores» no le sirve a una sesión nueva: qué significa «casi», qué pruebas han pasado y qué bloquea el pago son cosas que no se saben. Cada sesión nueva es como un nuevo conductor del mismo coche: si las notas vivían solo en la cabeza del anterior, el nuevo no sabe nada.

Por eso el estado se saca fuera, a un archivo legible por máquina. En el curso se usa .agent/feature_list.json:

[
  {
    "id": "F01",
    "behavior": "El usuario se registra con correo y contraseña y entra de inmediato",
    "verification": "pytest tests/e2e/test_auth.py::test_register_flow",
    "state": "passing",
    "evidence": "a3f9c12: 1 passed in 2.31s"
  },
  {
    "id": "F02",
    "behavior": "El usuario añade un producto al carrito y lo ve en la lista",
    "verification": "pytest tests/e2e/test_cart.py::test_add_item -q",
    "state": "active"
  },
  {
    "id": "F03",
    "behavior": "Pago del pedido con tarjeta",
    "verification": "pytest tests/e2e/test_payment.py -q",
    "state": "blocked",
    "blocker": "no hay claves de prueba de la pasarela de pago"
  }
]

Cada registro tiene tres campos obligatorios: la descripción del comportamiento (observable desde fuera), el comando de verificación (ejecutable sin persona) y el estado. El cuarto campo, opcional, es evidence: el hash del commit y la salida de la verificación, la prueba de que el paso a passing realmente se produjo.

¿Por qué JSON y no una lista en Markdown? Lo leen también los scripts: el planificador elige la siguiente función en not_started, el verificador toma el comando de comprobación, el informe de la sesión se genera solo y la distribución de estados da métricas de salud del proyecto. La lista de funciones es la columna vertebral del harness, no un recordatorio.

Cuatro estados y transiciones permitidas

TransiciónCuándo
not_started → activeLa función se toma para trabajar; no hay otra activa
active → passingEl script de verificación termina con código 0
active → blockedBloqueo externo; la causa se escribe en blocker
blocked → activeEl bloqueo se ha eliminado

No hay otras transiciones. En particular, no hay camino a passing que evite la verificación, ni un estado «casi terminado».

Barrera passing: el agente puede pedirlo, pero no declararlo

La regla central de todo el esquema: el agente no edita a mano el campo state. Puede ejecutar la verificación, pero no puede declarar el resultado por sí mismo: lo hace el script. La diferencia entre «el agente informó» y «el script confirmó» es la diferencia entre un texto y un hecho. Las reglas también se escriben en el archivo de instrucciones:

## Reglas de la lista de funciones
- Archivo: `.agent/feature_list.json`. Es la única fuente de verdad sobre el alcance.
- Exactamente una función en estado `active`.
- NO editas el campo `state` a mano. Lo cambia `scripts/verify.sh`.
- Para pasar una función a `passing`, ejecuta `./scripts/verify.sh F02` y consigue código 0.
- Si la función está bloqueada, pásala a `blocked` e indica obligatoriamente `blocker`.
- Las funciones nuevas se añaden solo a `not_started` y solo con comando de verificación.

El script comprueba en dos niveles: primero una ejecución general del proyecto (la función no debe romper lo demás) y después el comando de la función concreta. Un éxito local con una regresión global no cuenta como éxito.

#!/usr/bin/env bash
# Único componente con derecho a poner passing.
set -uo pipefail
FID="$1"
FILE=".agent/feature_list.json"

CMD=$(jq -r --arg id "$FID" '.[] | select(.id==$id) | .verification' "$FILE")
if [ -z "$CMD" ] || [ "$CMD" = "null" ]; then
  echo "FAIL: no hay comando de verificación para $FID"; exit 2
fi

echo "== comprobación global =="
make check || { echo "FAIL: la comprobación global está en rojo, $FID no puede ser passing"; exit 1; }

echo "== comprobación de la función: $CMD =="
if OUT=$(eval "$CMD" 2>&1); then
  SHA=$(git rev-parse --short HEAD)
  LAST=$(printf '%s\n' "$OUT" | tail -n 1)
  TMP=$(mktemp)
  jq --arg id "$FID" --arg ev "$SHA: $LAST" \
    'map(if .id==$id then .state="passing" | .evidence=$ev else . end)' \
    "$FILE" > "$TMP" && mv "$TMP" "$FILE"
  echo "PASS: $FID"
else
  printf '%s\n' "$OUT"
  echo "FAIL: $FID"; exit 1
fi

La regla «no edites state» escrita en el texto sigue siendo una petición. Para convertirla en una restricción, la escritura en el archivo se puede cerrar mecánicamente: con una comprobación en CI que contraste evidence con el historial de commits, o con los permisos del agente en su herramienta. El principio es el mismo que en todo el harness: una regla importante se respalda con un mecanismo.

Cómo medir si el agente termina lo que empieza

La métrica tentadora es el volumen de código por sesión. En la práctica funciona al revés: un diff grande suele significar que el agente se dispersó en varias direcciones. Cuente las funciones en estado passing, no las líneas.

Una métrica útil del curso es VCR (Verified Completion Rate): el número de tareas verificadas dividido entre el número de tareas activadas. La regla es sencilla: mientras VCR sea menor que uno, no se pueden activar tareas nuevas. Es la presión inversa que el harness aplica al agente, y además una forma de que lo inacabado sea visible como cifra, no como sensación.

Errores habituales al asignar tareas al agente

  • Tarea sin comando de verificación. «Haz el registro» sin un escenario: el agente decide por sí mismo dónde parar, y lo decide mal.
  • Varias funciones en una sola petición. «Haz el carrito y el pago» son dos tareas activas, y ninguna se terminará.
  • Estado solo en el chat. El progreso anotado en la conversación desaparece al cerrar la sesión. Debe vivir en un archivo.
  • Autoinforme en lugar de verificación. «El agente dijo que las pruebas pasaron» no es lo mismo que el código de salida del script.
  • Sin backlog. Prohibir lo «de paso» sin un lugar donde anotar lo detectado hace que el agente lo eluda.

Cuando una función por vez ya no basta

El esquema «una función, una sesión, una verificación» funciona bien para un agente en un solo ciclo. Cuando hay muchas tareas y aparecen ramas independientes, se amplía a un sistema de varios ciclos: un nodo de planificación, nodos de implementación, un nodo de verificación y las aristas entre ellos, paralelas, condicionales, con retorno a la implementación si la verificación falla. Pero incluso ahí cada nodo de implementación sigue la misma regla: una tarea, un criterio de terminado ejecutable, un estado fuera de la cabeza del agente. El grafo no anula la disciplina de una sola función, sino que se construye sobre ella.

Una advertencia del mismo análisis: el ciclo optimiza honestamente la métrica que se le da. Si el criterio de terminado está mal elegido, por ejemplo «el ticket está cerrado» en lugar de «el problema está resuelto», el agente aprenderá a cumplir el criterio y no la tarea. Por eso la formulación del comportamiento y del comando de verificación merece más tiempo del que parece.

En resumen

En pocas palabras, cómo asignar tareas a un agente de IA: una a la vez, con un comportamiento observable y un comando de verificación ejecutable, con un backlog para todo lo detectado por el camino y con una lista de funciones en un archivo, donde pasar a «terminado» lo hace el script y no el agente. Los frenos del agente los pone el harness: el propio agente no los activa. La regla WIP=1, feature_list.json y verify.sh se analizan en la práctica en el curso de harness engineering con un proyecto de principio a fin.

Preguntas frecuentes

¿Se puede dar al agente varias tareas a la vez si son pequeñas?

Se pueden incluir en la lista de funciones, pero solo una puede estar en curso al mismo tiempo. El agente toma la siguiente solo cuando la actual ha superado la verificación, así cada tarea se termina.

¿Qué hacer si para una tarea no se puede escribir una verificación automática?

Normalmente significa que la tarea está formulada como una acción y no como un comportamiento. Reformúlela en función de lo que verá el usuario o el cliente de la API; para la parte visual sirven las pruebas e2e en el navegador.

¿Quién escribe feature_list.json, la persona o el agente?

La lista inicial conviene prepararla juntos: el agente propone la descomposición y la persona la corrige. Después el agente puede añadir funciones nuevas, pero solo en estado not_started y con comando de verificación.

¿En qué se diferencia feature_list.json de las tareas en un gestor de proyectos?

El gestor de tareas está pensado para personas, y la lista de funciones para lectura por máquina: cada registro tiene un comando ejecutable y un estado estricto que cambia el script. Se pueden sincronizar, pero el agente necesita el archivo en el repositorio.

¿La regla de una función no frena el trabajo?

Las sesiones se acortan, pero aumenta la proporción de tareas terminadas. El tiempo que antes se perdía revisando código a medio hacer de tres funciones mezcladas se ahorra.

Aprende este tema en vídeo

Este tema se explica a fondo y con práctica en el curso «Harness Engineering: инженерия надёжности AI-агентов».

Ir al curso