Ir al contenido
Mastering Claude

Inicio / Varios agentes y verificación adversa

Varios agentes y verificación adversa8 minPratique

Exitoso, fallido, interrumpido, sin rastro

Cuando un corte detiene una cola de tareas, un registro llevado fuera del proceso, escrito antes y después de cada tarea, separa cuatro estados: exitoso, fallido, interrumpido a mitad de camino y sin rastro de intento; confundirlos hace relanzar lo que puede haber actuado ya, o declarar fallido lo que nadie ha examinado.

Tomemos un caso típico. Una cadena de verificaciones procesa una cola de tareas una tras otra. Un tope de uso cae en mitad de la cola y detiene el proceso supervisor con los agentes que dirigía. Al reiniciar, el supervisor marca como fallidas todas las tareas sin resultado, incluidas aquellas que ningún agente había empezado.

Cuatro estados, porque un corte crea dos

Un sistema que solo conoce exitoso y fallido mete en fallido dos casos que produce el corte. La tarea interrumpida ha empezado: ha podido actuar en parte, y se inspecciona antes de relanzarla. La tarea sin rastro se relanza, una vez verificado que ningún otro proceso la ha tomado. Escribir la línea «en curso» antes de empezar es lo que las separa: sin ella, una tarea interrumpida se parece a una tarea nunca abordada.

La ausencia de línea establece una cosa acotada, como recuerda la lección sobre lo que una verificación rigurosa no ve: este registro no lleva ningún rastro de intento, y no dice nada de otro proceso que haya podido tomar la tarea.

La demostración

Los dos scripts se crean en una carpeta desechable. El primero escribe seis tareas en cola.json y luego simula un corte durante la cuarta. El segundo confronta esa lista de partida con el registro: es ella la que da a conocer las tareas sin rastro.

mkdir -p demo-cola && cd demo-cola
cat > procesar-cola.js << 'EOF'
// Fabrica la cola de partida y luego la procesa registrando antes y después de cada tarea.
const fs = require('fs');
const cola = ['tarea-1', 'tarea-2', 'tarea-3', 'tarea-4', 'tarea-5', 'tarea-6'];
fs.writeFileSync('cola.json', JSON.stringify(cola));
fs.writeFileSync('registro.jsonl', '');
const resultados = { 'tarea-1': 'exitoso', 'tarea-2': 'fallido', 'tarea-3': 'exitoso' };
for (const id of cola) {
  fs.appendFileSync('registro.jsonl', JSON.stringify({ id, estado: 'en curso' }) + '\n');
  if (id === 'tarea-4') process.exit(1); // corte simulado durante la cuarta tarea
  fs.appendFileSync('registro.jsonl', JSON.stringify({ id, estado: resultados[id] }) + '\n');
}
EOF
cat > leer-registro.js << 'EOF'
// Relee la cola de partida y el registro, y clasifica cada tarea en cuatro estados.
const fs = require('fs');
const cola = JSON.parse(fs.readFileSync('cola.json', 'utf8'));
const ultimo = {};
for (const linea of fs.readFileSync('registro.jsonl', 'utf8').split('\n')) {
  if (linea) { const e = JSON.parse(linea); ultimo[e.id] = e.estado; }
}
const clases = { 'exitoso': [], 'fallido': [], 'interrumpido': [], 'sin rastro': [] };
for (const id of cola) {
  const estado = ultimo[id];
  if (estado === undefined) clases['sin rastro'].push(id);
  else if (estado === 'en curso') clases['interrumpido'].push(id);
  else clases[estado].push(id);
}
for (const [nombre, ids] of Object.entries(clases)) console.log(nombre + ' : ' + (ids.join(', ') || '-'));
EOF
node procesar-cola.js
echo "código de salida : $?"
cat registro.jsonl
node leer-registro.js
código de salida : 1
{"id":"tarea-1","estado":"en curso"}
{"id":"tarea-1","estado":"exitoso"}
{"id":"tarea-2","estado":"en curso"}
{"id":"tarea-2","estado":"fallido"}
{"id":"tarea-3","estado":"en curso"}
{"id":"tarea-3","estado":"exitoso"}
{"id":"tarea-4","estado":"en curso"}
exitoso : tarea-1, tarea-3
fallido : tarea-2
interrumpido : tarea-4
sin rastro : tarea-5, tarea-6

El código de salida 1 señala la parada anormal. La tarea 4 lleva «en curso» sin resultado: interrumpida, no fallida.

Un rastro que sobrevive al proceso

Un balance guardado en la memoria del supervisor desaparece con él. El registro vive, por tanto, fuera del proceso: un archivo como aquí, una tabla de base de datos o una cola de mensajes externa también sirven. appendFileSync termina la escritura de la línea antes de pasar a lo siguiente: si el proceso se mata, las líneas ya escritas permanecen en el archivo. Un corte de corriente es otro caso, porque el sistema puede guardar las últimas líneas en memoria antes de ponerlas en el disco; una sincronización explícita, fs.fsyncSync en Node, fuerza esa escritura.

La lección sobre los tres estados de fallo separa, entre las tareas que han devuelto un resultado, el fallo recuperable que se reintentará y el fallo definitivo que sube a una persona; esta trata las tareas que el corte ha dejado sin resultado.

Figure 1

Lo que el registro anota, tarea por tarea

01
Escribir la lista de partida
Todas las tareas de la cola, antes de la primera.
02
Marcar el inicio
Una línea «en curso» por tarea, puesta antes de que el trabajo empiece.
03
Escribir el resultado
Exitoso o fallido, antes de pasar a la tarea siguiente.
04
Releer y clasificar
Exitoso, fallido, interrumpido o sin rastro, según la última línea de cada tarea, releída tras el corte frente a la lista de partida.
Corte02La tarea conserva su línea «en curso» sin resultado: se clasificará como interrumpida.
La secuencia muestra en qué momento se escribe cada línea; la rama muestra lo que deja un corte ocurrido durante una tarea.
Figure 2

Cuatro estados, cuatro gestos

EstadoRastro en el registroGesto siguiente
Exitoso«en curso» y luego «exitoso»Ninguno
Fallido«en curso» y luego «fallido»Tratar según el tipo de fallo
Interrumpido«en curso» y nada despuésInspeccionar los efectos parciales y luego relanzar
Sin rastroNinguna línea, tarea presente en la lista de partidaVerificar que ningún otro proceso la ha tomado y luego relanzar
Cada fila asocia un estado con el rastro que lo prueba en el registro y con el gesto que pide tras el corte.
Calíbralo tú mismo

Un proceso recorre una cola de seis tareas listadas en un archivo de partida. Escribe en un registro en disco una línea «en curso» antes de cada tarea y una línea de resultado después. Un tope de uso detiene el proceso. Releído tras la parada, el registro contiene para la tarea 1 «en curso» y luego «exitoso», para la tarea 2 «en curso» y luego «fallido», para la tarea 3 «en curso» y luego «exitoso», y para la tarea 4 «en curso». Las tareas 5 y 6 figuran en el archivo de partida.

Escribe en una frase lo que esta situación establece, y en una frase lo que no establece.

Lo que hay que recordar
  • Una tarea interrumpida ha empezado y ha podido actuar en parte, por lo que se inspecciona antes de relanzarla.
  • Una tarea sin rastro se reconoce confrontando la lista de partida con el registro, y no con el registro solo.
  • La línea escrita antes de empezar es lo que separa una tarea interrumpida de una tarea que el proceso no ha abordado.
  • Un registro útil vive fuera del proceso que lo escribe: archivo, base de datos o cola de mensajes externa.
Hazlo ahora

Toma un script que procese una lista de elementos, haz que escriba una línea «en curso» antes de cada elemento y una línea de resultado después, deténlo a mano en pleno medio y comprueba después que recuperas los cuatro estados confrontando su lista de partida con el registro.