El informe de error dice que el sitio que construyó estaba mal.
Tienes la conversación. No tienes el turno: ni qué herramientas se ejecutaron, ni cuánto tardó nada, ni qué estaba pensando el modelo, ni cuánto costó. Y no puedes reejecutarlo, porque reejecutarlo produce un turno distinto.
Esa es toda la dificultad. En la depuración habitual, reproducir es el primer paso. Aquí no está disponible, y lo que registraste en su momento es todo lo que tendrás jamás.
Nombres y tamaños, nunca entradas
Antes de decidir qué registrar, decide qué no debe registrarse nunca.
// Deliberately logs the tool name and nothing else from the payload: the
// arguments carry the business description, the email and the address.
Los argumentos de las herramientas son los datos más ricos del sistema, los más tentadores y los más peligrosos. Son exactamente lo que escribió un usuario: su correo, su dirección, su teléfono, su descripción del negocio. Una línea de registro que los contiene es dato personal en un sistema con retención larga, acceso de lectura amplio y ningún rastro de consentimiento.
La regla que ha aguantado: nombres y tamaños, nunca entradas ni resultados.
Log::info('ai_widget.round_trip', [
'iteration' => $iteration,
'duration_ms' => $durationMs,
'stop_reason' => $stopReason,
'tool_count' => count($tools),
'tools' => $tools,
// A proxy for how much of the time was generation rather than transport:
// a long reply legitimately takes longer.
'text_chars' => array_sum(array_map(
fn (array $block): int => mb_strlen((string) ($block['text'] ?? '')),
$textBlocks
)),
]);
Qué herramientas se ejecutaron, cuántas, cuánto tardaron, por qué se detuvo y cuántos caracteres volvieron en lugar de los caracteres mismos. Ese último es el truco que merece robarse: text_chars responde a «¿fue una respuesta larga o una lenta?» sin almacenar ni una palabra.
Durante un incidente tendrás la tentación de añadir las cargas «sólo temporalmente». No lo hagas. El registro temporal sobrevive al incidente, y el reloj de retención arrancó cuando lo desplegaste.
Una línea estructurada por ida y vuelta
La unidad es la ida y vuelta, no el turno: un turno son muchas llamadas de formas distintas, y promediarlas esconde lo que necesitas.
iteration es lo que hace reconstruible un turno después: cuatro líneas con iteraciones del 1 al 4 te cuentan la forma de todo aquello. Qué herramientas se dispararon y cuándo. Si paró porque había terminado o porque un tope se agotó. Dónde se fue el tiempo.
Emparéjalo con una línea de resumen por turno —la contabilidad de tokens del capítulo 4— y entre ambas puedes responder casi cualquier pregunta sobre una conversación que no puedes reejecutar.