Skip to main content
Para un agente que escribiste tú mismo, o un framework para el que Failproof AI no tiene adaptador. No hay nada que instrumentar: tú emites los eventos. Esta es la misma API que los cuatro adaptadores de framework utilizan internamente. Son tablas de traducción sobre ella.

Instalación

Sin extras ni dependencias.

Instrumentación

Léelo de arriba a abajo y verás lo que significa: Y lo que cada uno emite realmente: Todo lo que esté dentro puede omitir session_id y agent_id. Los ámbitos vinculan la identidad en variables de contexto y cada llamada a evento la lee de vuelta, por lo que nunca necesitas pasar ids a través de tus funciones. Los tres funcionan tanto con async with como con with. Anidar agentes construye el árbol. parent_id y la profundidad se calculan desde la pila:

Cómo se cierra un ámbito

agent() gestiona las excepciones por ti: El error se emite antes de agent_end, porque el panel cierra el span en agent_end y cualquier cosa posterior no se atribuye a nada. Una cancelación no es un fallo, por lo que las ejecuciones canceladas no contaminan la superficie de errores. La excepción siempre se relanza: un ámbito nunca la suprime.

Los métodos de evento

Quince métodos en seis familias. La mayoría vienen en pares: emites el abridor, luego el cierre, y el SDK mide el span entre ellos.
Prefiere los ámbitos — agent() y tool_call() — donde encajen. Garantizan el evento de cierre incluso cuando el cuerpo lanza una excepción. Recurre a estos métodos directamente cuando tu flujo de control no se anida, como una llamada a modelo dentro de un helper.
Las dos familias de humanos apuntan en direcciones opuestas.Ningún framework señaliza el segundo par, por lo que siempre es tuya la responsabilidad de emitirlo.
Pasa request_id cuando las llamadas al modelo se ejecuten de forma concurrente. Sin él, las solicitudes y respuestas se emparejan en orden de llegada por agente, y las llamadas concurrentes se emparejan incorrectamente, asociando cada respuesta con la solicitud equivocada.

Ejemplo

Un bucle de llamada a herramientas contra la API de OpenAI, sin ningún framework de agentes:
Esto produce los mismos seis tipos de eventos que te daría un adaptador. La versión ejecutable completa, con las definiciones de herramientas, se incluye en el repositorio del SDK bajo docs/manual/examples/.

Hilos y async

Las variables de contexto se propagan automáticamente a las tareas de asyncio. No se propagan a nuevos hilos, porque un hilo comienza con un contexto vacío.
Sin propagate(), los eventos del worker lanzan un TypeError que indica la corrección en lugar de terminar sin sesión. Esto es deliberado: un evento sin sesión es omitido por el ingest y respondido con 200, que es el fallo silencioso que la capa de identidad existe para prevenir.

Instrumentar un framework sin adaptador

Cada framework de agentes te ofrece los mismos tres puntos de enganche. Mapéalos y tendrás una traza completa — los cuatro adaptadores incluidos no hacen nada más que esto.
1

Delimita la ejecución

2

Delimita cada herramienta

En lo que el framework llame wrapper de herramienta o middleware.
3

Empareja cada llamada al modelo

¿Tienes un límite de nodo, paso o middleware que vale la pena ver? Envuélvelo en un par de hooks — hook_triggered / hook_completed — no en un agent() anidado. agent_id es una faceta de baja cardinalidad, y una entrada por nodo la satura. Los spans de hooks se renderizan igual y te dan latencia por nodo.
Manual y automático se combinan. Un adaptador que se ejecuta dentro de un ámbito escrito a mano se une a esa sesión y se convierte en hijo de ese agente, de modo que obtienes un único árbol en lugar de dos — útil cuando instrumentas un framework tú mismo junto a uno soportado.
Dos razones, y los tres puntos de enganche anteriores son la respuesta a ambas:
  • autogen-core no tiene mantenimiento desde septiembre de 2025.
  • AG2 no expone ningún punto de registro a nivel de proceso equivalente a los hooks de los otros frameworks, por lo que instrumentarlo significa envolver cada agente en cada punto de construcción.
Mapear los puntos de enganche a mano registra los mismos eventos, con la misma fidelidad, que un adaptador incluido.

Profundizando

Cómo funciona la grabación realmente. Nada de esto es necesario para empezar.
Toda grabación tiene la misma forma: un span se abre, el trabajo se anida dentro, y cada evento de apertura recibe uno de cierre.El par es la unidad. Cada evento de cierre lleva una duración que el SDK mide desde el de apertura.A continuación se muestra una ejecución real por framework — capturada de los ejemplos que se incluyen con el SDK, con el nombre del modelo normalizado. Fíjate en cuánto se obtiene de una sola llamada.
14 eventos
Los nodos se convierten en pares de hooks, por lo que obtienes latencia por nodo sin que saturen la lista de agentes.
No existe un evento de fin de sesión. Una sesión no es algo que cierras — es un grupo de eventos que comparten un session_id.El estado se deriva de la forma de la traza:Por tanto, una sesión termina cuando todos los pares están cerrados. Los adaptadores emiten agent_end por ti, y al finalizar cierran todo lo que siga abierto y lo marcan como incompleto — una ejecución que se interrumpió se resuelve como done con un hueco visible en lugar de quedar colgada.
Por eso una sesión puede abarcar dos llamadas. Un interrupt() de LangGraph pausa la ejecución, el span raíz permanece deliberadamente abierto, y la llamada de reanudación lo cierra. Ambas llamadas son una sola sesión.
session_id y agent_id son opcionales en todos los métodos de evento. Si se omiten, se resuelven desde el ámbito que los contiene:
Pasarlos explícitamente también funciona y tiene precedencia. Si no hay nada vinculado ni nada pasado, la llamada lanza un TypeError que indica la corrección en lugar de emitir un evento sin sesión, que el ingest omitiría respondiendo con 200.Los ámbitos vinculan la identidad en variables de contexto. Estas se propagan automáticamente a las tareas de asyncio pero no a los nuevos hilos — envuelve un worker en failproofai_sdk.propagate().

Quién genera cada id

Cómo los adaptadores resuelven session_id

Gana la primera coincidencia:
  1. Una opción session_id explícita
  2. Metadatos por llamada
  3. El ámbito session() que lo contiene
  4. Metadatos del framework
  5. El id de ejecución propio del framework
Nunca se inventa mientras exista alguna de esas — un id sintetizado dividiría una ejecución en varias sesiones.

Mantén agent_id con baja cardinalidad

Es la faceta principal en todas las superficies del panel, y una columna LowCardinality(String). Un valor por ejecución degrada la columna y llena el desplegable de filtros con una entrada por ejecución.Los adaptadores protegen esa columna por ti:El id real se conserva en fw_agent_id / fw_run_id, donde sigue siendo consultable sin ser una faceta.
Esta protección solo afecta a las etiquetas que eligió el framework. Un agent_id que pasas tú mismo — a event.*, o a failproofai_sdk.agent(...) — se registra exactamente como se dio. Reescribir silenciosamente un argumento explícito sería peor que la cardinalidad que previene, así que nombra tus propios spans en consecuencia.
Qué registra cada framework, medido desde las ejecuciones anteriores:Un guion indica que el framework no tiene ese concepto. human_pause y human_interrupt describen a una persona actuando sobre el agente, algo que ningún framework señaliza — emítelos tú mismo.
Un evento nunca llega solo. Uno abre un span, otro lo cierra, y el evento de cierre lleva una duración que el SDK mide desde el de apertura.
Un evento de apertura sin uno de cierre es un span que nunca termina. La sesión se muestra como aún en ejecución, para siempre, y su duración activa sigue creciendo. Este es el modo de fallo que hay que vigilar cuando se instrumenta a mano.

Reglas de correlación

  • Reutiliza el mismo tool_call_id, hook_id, pause_id o input_id para el evento de completado correspondiente.
  • El SDK calcula duration_ms para tool_result, hook_completed, agent_resume y human_input. Pasarlo a esos métodos lanza ValueError.
  • duration_ms se acepta en model_response, porque solo el llamador conoce la latencia real del proveedor. Debe ser un entero — un float lanza ValueError en el punto de llamada, porque el servidor lee la columna como un entero de 32 bits sin signo y almacenaría NULL para cualquier otro valor.
  • Las claves de correlación tienen ámbito por tipo y sesión, por lo que una llamada a herramienta y un hook pueden compartir un id sin problemas, y dos sesiones concurrentes pueden reutilizar los mismos ids sin colisionar. No tienen ámbito por agente: un par abierto bajo un agente y cerrado bajo otro sigue correlacionando, que es el caso habitual en frameworks multi-agente.
  • request_id empareja model_request con model_response. Sin él, los eventos de modelo se emparejan en orden por agente, por lo que las llamadas concurrentes se emparejan incorrectamente.
  • Un par dividido entre procesos sigue correlacionando en destino, pero el SDK no puede calcular su duración en proceso.
  • El mapa de pendientes almacena como máximo 10.000 inicios y desaloja la entrada más antigua cuando se llena.
Instalar failproofai-sdk instala todo, los cuatro adaptadores incluidos. Los extras incorporan el framework, no el adaptador.
import failproofai_sdk es contractualmente sin dependencias, verificado por un test que instala la wheel compilada con --no-deps y otro que demuestra que ningún framework llega a sys.modules.
No existe el atributo failproofai_sdk.crewai. Los adaptadores no se exponen deliberadamente en el paquete de nivel superior: acceder a uno importaría el framework como efecto secundario del acceso al atributo, rompiendo la promesa de cero dependencias. Usa instrument().
La detección automática lee sys.modules, no la lista de paquetes instalados, por lo que un framework que tienes instalado pero nunca has importado no se instrumenta y nunca se importa en tu nombre. Para ver qué está conectado:
instrument("crewai") en una máquina sin CrewAI no lanza una excepción. Registra una advertencia y devuelve (), por lo que un framework faltante nunca derrumba un proceso que también instrumenta otros.La advertencia incluye el ImportError subyacente, y ese mensaje indica el comando de instalación exacto — así que la corrección está en tus logs, no oculta.
Establece FAILPROOFAI_SDK_STRICT=1 para que lance una excepción en su lugar. Ese flag se lee una vez y se cachea, así que expórtalo antes de que inicie tu proceso, no lo establezcas a mitad de ejecución.
instrument() debe llamarse después de importar tu framework. La detección automática lee sys.modules, por lo que una llamada sin argumentos antes del import no encuentra nada, no instala nada y devuelve ().
Si te equivocas, el proceso se ejecuta con el SDK importado, el adaptador aparentemente instalado, y sin emitir un solo evento. Registra una advertencia que lo indica exactamente — así que comprueba tus logs primero cuando una ejecución no registra nada.
El spool es lo que hace esto seguro: tu agente nunca bloquea esperando la red, y una interrupción de Cloud significa un directorio en crecimiento en lugar de eventos perdidos.Cada flush escribe un archivo de lote, primero como .tmp, luego fsync, luego un renombrado atómico:
El daemon solo recoge .jsonl, por lo que nunca puede leer un archivo a medio escribir. El nombre lleva un timestamp, id de proceso y número de secuencia, por lo que dos procesos que hagan flush en el mismo milisegundo no colisionan. La cola tiene un límite de 10.000 eventos; a partir de ahí descarta los más antiguos y lo registra en el log.
collector.redact no se aplica a los eventos de tu SDK. Nunca los ve.
El daemon envía tus lotes. No los abre ni los reescribe.La redacción se ejecuta donde el daemon escribe sus propios eventos — no donde se envían los lotes. Así que un prompt o un argumento de herramienta que contenga una clave API la conservará al llegar.Esto es deliberado. Estas son tus propias llamadas de instrumentación, y reescribirlas en tránsito significaría que los eventos que recibes no son los que emitiste.
Controlas los payloads en el origen, en dos lugares:
  • Desactiva la captura de contenido en el adaptador. El nombre de la opción varía, y un adaptador no tiene ninguna — no es un único interruptor universal:
    • LangChain / LangGraph, Pydantic AI — capture_content=False
    • LlamaIndex — capture_messages=False
    • CrewAI — sin interruptor de contenido en absoluto; session_id es la única opción que lee, por lo que los prompts y las respuestas siempre se registran.
    instrument() descarta las opciones que un adaptador no lee, por lo que pasar el nombre incorrecto no lanza nada ni cambia nada.
  • No pases el secreto a input= desde el principio.
collector.redact no es un sustituto de ninguna de las dos opciones.
Un directorio de spool vacío es el estado saludable. No lo uses para verificar la entrega.
El daemon elimina cada lote en milisegundos después de enviarlo, por lo que un ls compite con el collector y muestra una fracción de lo que emitiste — indistinguible de un SDK que no registró nada.Para confirmar que los eventos realmente llegaron, comprueba el panel. Para ver cómo se llena el spool, detén el daemon primero.
Cada callback se ejecuta dentro de un wrapper cuyo único trabajo es relanzar, por lo que tu llamada está en exactamente un try y todo lo que hace el SDK ocurre fuera de él.El comportamiento por defecto es correcto en producción e incorrecto al depurar, porque solo puede demostrar que no se produjo un crash. Establece FAILPROOFAI_SDK_STRICT=1 para hacer visible un fallo suprimido.

Problemas comunes

Un evento de apertura no tiene uno de cierre: un model_request sin model_response, o un tool_use sin tool_result. Usa los ámbitos, que garantizan el par incluso cuando el cuerpo lanza una excepción. Si llamas a los métodos de evento directamente, usa try y finally.
Se mide desde el evento de apertura correspondiente, por lo que se rechaza en tool_result, hook_completed, agent_resume y human_input. Se acepta en model_response, porque solo tú conoces la latencia real del proveedor, y debe ser un entero.
El hilo nunca heredó el contexto. Envuelve el callable en failproofai_sdk.propagate(). Consulta Hilos y async.
Los campos extra se fusionan al final, por lo que uno con el nombre de un campo real como model o outcome lo sobreescribiría y cambiaría una columna almacenada. Usa un espacio de nombres propio; los adaptadores utilizan el prefijo fw_.
agent_id es una faceta de baja cardinalidad y pusiste un id de ejecución en ella. Usa un rol o nombre de nodo y guarda el id real en un campo del payload.

Siguiente paso

Cómo funciona

Pares, ids, ciclo de vida de sesión y entrega.

Leer una traza

Sigue la causalidad a través de la sesión que acabas de capturar.

Adaptadores de framework

LangGraph, CrewAI, LlamaIndex y Pydantic AI.