Ir al contenido principal
Laravel, thinking fast.
$maxIterations = 10;

Un tope de iteraciones por sí solo no basta. Diez iteraciones de una herramienta rápida son unos segundos; diez de una lenta superan cualquier tiempo de espera de job. Por eso hay un presupuesto de reloj a su lado, y gana el que se agote primero.

Ambos tienen que ser observables. Alcanzar el tope de iteraciones se reporta, no se traga en silencio:

if (now()->greaterThan($deadline)) {
    report(new \RuntimeException('Hit its wall-clock budget after '.$iteration.' iterations'));
    break;
}

Un bucle que para calladamente en diez produce una respuesta sutilmente incompleta con aspecto de normal. Si no puedes distinguir «terminó» de «se quedó sin presupuesto» en tus registros, no puedes saber si el tope es el correcto.

Qué pasa después del bucle

Cache::forget($this->checkpointKey());
Cache::forget(self::cancelKey($this->cacheKey));

// The turn is over, so there is no step in flight to report.
$state['activity'] = '';

$this->publish($state, $history, done: true);

$this->recordUsage($iteration);

Todas las salidas —normal, cancelada, sin presupuesto, negada, truncada— aterrizan aquí. Es deliberado: break y no return en todo el bucle, para que la limpieza, la publicación final y la contabilidad de uso no puedan saltarse según qué salida se dispare.

Limpiar activity importa más de lo que parece. Es el último paso que dio el modelo, y dejarlo puesto hace que una conversación terminada muestre para siempre un rótulo rancio de «comprobó la URL».

Lo que sostiene este capítulo

  • stop_reason es control de flujo, no diagnóstico. Un bucle que lo ignora funciona en el caso común y falla en silencio en otros cuatro.
  • Maneja pause_turn antes de la salida por «sin herramientas». Llega sin llamadas a herramientas, así que una condición de salida ingenua trata un turno pausado como terminado.
  • Ordena las ramas de más específica a menos, con todos los «continuar» por encima de los «salir».
  • Dale al truncamiento su propia rama y repórtalo. Si no, una respuesta cortada se muestra como una completa.
  • Una negativa no es un error y no debe reintentarse nunca.
  • Añade el contenido del asistente literalmente. Extrae para mostrar, almacena el original: aplanarlo pierde las llamadas a herramientas.
  • Filtra bloques por tipo, no por posición. Espera varios bloques de texto, o ninguno.
  • Pon tope a iteraciones y a reloj, y reporta cuando cualquiera se agote: un tope silencioso es indistinguible de un turno terminado.
  • Sal con break, nunca con return, para que la limpieza, la publicación final y la contabilidad se ejecuten en todos los caminos.