Instrumentar un framework de GenAI para OpenTelemetry implica localizar puntos de extensión aprovechables, mapear sus operaciones a las convenciones semánticas, verificar la telemetría resultante y mantener todo ello al ritmo de los cambios del framework. Como cada framework nuevo repite el ciclo, el coste de ingeniería se acumula: el artículo señala que incluso un colaborador experimentado puede invertir días en un solo framework, y que implementaciones independientes suelen divergir en la elección de atributos, unidades y límites de los spans, lo que dificulta consultar la telemetría de forma uniforme.

Los autores han estado evaluando si un equipo de agentes puede hacerse cargo de buena parte de ese trabajo. En su sistema, los agentes examinan un framework, proponen un plan de instrumentación, lo implementan conforme a las Semantic Conventions for GenAI de OpenTelemetry y comprueban la telemetría emitida con OpenTelemetry Weaver, mientras ingenieros revisan el plan y el pull request resultante.

Los componentes

Tres piezas forman el sistema: las convenciones semánticas de GenAI, Weaver y un orquestador.

  • Las convenciones semánticas de GenAI definen el contrato: nombres de spans y atributos, unidades y qué campos transportan el modelo, los tokens, los mensajes y las llamadas a herramientas. Los plugins generados deben seguir esas definiciones.
  • Weaver actúa como validador. Su capacidad live-check abre un listener OTLP, pasa la telemetría de una instrumentación en ejecución por un conjunto de advisors y la compara con el registro de convenciones semánticas resuelto. Los advisors integrados cubren lo básico (missing_attribute, type_mismatch); las políticas Rego de OTel por defecto añaden reglas de nomenclatura y formato, y se pueden aportar políticas propias con --advice-policies. Cada hallazgo tiene un nivel — violation, improvement o information — y Weaver termina con código distinto de cero cuando el informe contiene una violación.
  • El orquestador planifica a los agentes y publica el pull request. El equipo usa una plataforma de tareas multiagente autoalojada, pero señala que un orquestador solo necesita clonar un repositorio, ejecutar los agentes y abrir un pull request.

Arquitectura

Ante el repositorio de un framework — LangChain sirve de ejemplo — el sistema produce un pull request con la instrumentación generada.

flowchart LR
    repo[("Target framework<br/>repository")] --> team
    subgraph team["Agent team"]
        direction TB
        lead["Team Lead"]
        research["Research"]
        plan["Plan Review"]
        code["Coding"]
        review["Code Review<br/>& Tests"]
        e2e["E2E & Deploy"]
        observe["Observation"]
        lead --- research
        lead --- plan
        lead --- code
        lead --- review
        lead --- e2e
        lead --- observe
    end
    team --> pr["Pull request with<br/>generated instrumentation"]
    semconv["GenAI semantic<br/>conventions"] -.->|"target spec"| team
    weaver["Weaver<br/>live-check"] -.->|"conformance signal"| team

El orquestador reparte el trabajo entre agentes especializados:

  • Team Lead Agent: inicia la ejecución, ordena el trabajo de los demás agentes y posee el estado del bucle.
  • Instrumentation Research Agent: clona el framework objetivo, estudia su arquitectura y busca primero puntos de extensión públicos como hooks y callbacks. Tras revisar implementaciones existentes, redacta un informe de investigación con estrategias de instrumentación candidatas y considera el wrapping de métodos solo cuando esos puntos de extensión no pueden observar el ciclo de vida requerido.
  • Plan Review Agent: puntúa las candidatas por complejidad, mantenibilidad y cobertura, elige la mejor y escribe un execution-plan.md detallado que indica qué puntos de extensión usar y de dónde debe salir cada atributo. Los autores lo describen como un paso de generación de especificaciones y el punto natural para la revisión humana.
  • Coding Agent: implementa execution-plan.md contra las convenciones semánticas de GenAI, apoyándose en las utilidades GenAI compartidas.
  • Code Review & Test Validation Agent: revisa el código generado, escribe pruebas unitarias e itera con el Coding Agent hasta resolver los comentarios y que las pruebas pasen.
  • E2E Generation & Deployment Agent: trabaja en paralelo con la codificación, preparando el harness de pruebas de integración para que esté listo cuando lo esté el plugin.
  • Observation Agent: ejecuta el harness en Kubernetes, captura la salida OTLP, la entrega a Weaver para la validación semántica y vigila la carga de trabajo en el tiempo en busca de fugas de memoria y otras regresiones en ejecución.

Ingeniería del bucle

El Team Lead Agent programa cada paso y los demás agentes le devuelven el control cuando el plan debe cambiar.

flowchart TB
    research["1. Instrumentation research<br/><i>clone, study, propose strategies</i>"]
    plan["2. Plan review & scoring<br/><i>produces execution-plan.md</i>"]
    code["3. Coding<br/><i>implements against semconv</i>"]
    tests["4. Code review & unit tests"]
    e2e["5. E2E integration test<br/>on Kubernetes"]
    check["6. Weaver live-check<br/><i>semantic validation</i>"]
    soak["7. Soak & runtime watch<br/><i>memory, leaks, regressions</i>"]
    pr["Pull request"]
    research --> plan --> code --> tests --> e2e --> check
    check -->|"violation"| code
    check -->|"clean"| soak
    soak -->|"regression"| code
    soak -->|"stable"| pr

Tras la revisión de código y las pruebas unitarias, los ejemplos del framework se empaquetan en imágenes de contenedor, se instrumentan y se ejecutan en un clúster de Kubernetes de pruebas mientras Weaver inspecciona los datos OTLP emitidos. Una violación devuelve el trabajo al Coding Agent; una ejecución limpia pasa a una prueba de estabilidad que vigila fugas de memoria y otras regresiones. Un ingeniero puede interrumpir al Team Lead para modificar el plan o repetir un paso.

Cómo es la instrumentación generada

Dos decisiones marcan el código generado.

La primera es que execution-plan.md fija los puntos de instrumentación antes de escribir cualquier código, y al Coding Agent no se le ordena envolver métodos: la fase de investigación prefiere hooks y callbacks públicos cuando cubren el ciclo de vida requerido. El OpenAI Agents SDK, por ejemplo, expone lifecycle hooks, por lo que esa estrategia prevalece allí. El plugin de CrewAI descrito en el artículo admite versiones hasta la 0.80, cuyos callbacks públicos no exponían el ciclo completo de inicio y fin que necesitan esos spans, de modo que su plan eligió el wrapping de métodos; los autores indican que las versiones más recientes de CrewAI ofrecen una API de event listeners más amplia que debería evaluarse primero para esas versiones. El plan de wrapping se expresa como una lista explícita y revisable de objetivos:

_CREWAI_UNINSTRUMENT_TARGETS = (
    ("crewai.crew", "Crew", "kickoff"),
    ("crewai.crew", "Crew", "kickoff_async"),
    ("crewai.flow.flow", "Flow", "kickoff"),
    ("crewai.flow.flow", "Flow", "kickoff_async"),
    ("crewai.agent", "Agent", "execute_task"),
    ("crewai.task", "Task", "execute_sync"),
    ("crewai.tools.tool_usage", "ToolUsage", "_use"),
)

Los revisores pueden contrastar esos objetivos con la API del framework.

La segunda es que los agentes mapean los objetos del framework a utilidades GenAI compartidas en lugar de escribir ellos mismos los nombres de atributos. opentelemetry-util-genai reside ahora en el repositorio OpenTelemetry Python GenAI, bajo util/opentelemetry-util-genai, y su método handler.invoke_local_agent() crea un AgentInvocation para un agente que corre en el mismo proceso.

Un adaptador mínimo para el ejemplo de CrewAI — escrito contra la API upstream y no un extracto del plugin generado original — sería así, donde wrapped es el método Agent.execute_task original ya ligado, instance es el agente de CrewAI y args y kwargs son los argumentos del método (se omiten el registro del wrapper y la configuración del SDK):

from opentelemetry.util.genai.handler import get_telemetry_handler
def wrap_execute_task(wrapped, instance, args, kwargs):
    handler = get_telemetry_handler()
    with handler.invoke_local_agent(agent_name=instance.role) as invocation:
        if invocation.should_capture_content:
            invocation.agent_description = instance.goal
        return wrapped(*args, **kwargs)

La utilidad abre un span INTERNAL, lo convierte en actual durante la llamada y lo cierra al salir del gestor de contexto. Si el método envuelto lanza una excepción, la utilidad registra el fallo y deja propagar la excepción. El wrapper consulta should_capture_content antes de copiar el objetivo del agente, que puede contener instrucciones proporcionadas por el usuario.

Las utilidades compartidas gestionan los nombres de atributos y el ciclo de vida de la telemetría, pero la instrumentación aún debe mapear correctamente los datos del framework; el equipo valida esos mapeos con pruebas y revisión humana además de las comprobaciones de registro de Weaver.

Los plugins generados originales usaban las extensiones del equipo sobre un paquete de utilidades anterior en alibaba/loongsuite-python, incluido el tipo InvokeAgentInvocation específico del fork. El ejemplo anterior usa la API upstream para que los lectores puedan partir de las utilidades de OpenTelemetry, y migrar los plugins a esas API forma parte del trabajo de upstreaming descrito más adelante.

Verificación del resultado con Weaver

Weaver compara los datos OTLP emitidos con el registro de convenciones semánticas resuelto, pero el Observation Agent no lo invoca directamente. Ejecuta el escenario a través de un conformance runner que arranca la carga, la apunta a un endpoint OTLP y entrega lo que emite a weaver registry live-check. Un ejemplo público, ejecutado desde la raíz del repositorio semantic-conventions-conformance, es:

otel-conformance scenarios/gen-ai/python/crewai/openinference

Su conformance.yaml declara el runner y la instrumentación:

runner: genai-conformance
instrumented_library: crewai
instrumentation_library: openinference-instrumentation-crewai

El artículo subraya que este escenario público demuestra el runner con OpenInference, no con el plugin de LoongSuite del equipo, y que el wrapper genai-conformance fija el pin del registro de convenciones semánticas y las políticas de advice para que las ejecuciones repetidas usen las mismas definiciones.

Cada escenario corre entonces bajo su propio live-check y una violación hace fallar la ejecución. Ese es el comportamiento por defecto; --report-only es la salida opcional, que degrada los hallazgos semánticos a advertencias pero sigue fallando en un escenario que se cae o no produce nada medible.

En una ejecución distinta con el plugin de LoongSuite del equipo, una entidad de muestra devolvió este hallazgo:

{
  "live_check_result": {
    "all_advice": [
      {
        "id": "span_status_ok_set_by_instrumentation",
        "context": { "status_code": "ok" },
        "message": "Span 'execute_tool ping' has status.code='ok'; instrumentations must leave status UNSET on success (OK is reserved for application code).",
        "level": "violation",
        "signal_type": "span",
        "signal_name": "execute_tool ping"
      }
    ],
    "highest_advice_level": "violation"
  }
}

La política de advice agnóstica del dominio del runner marca una instrumentación que fija el estado del span en OK — el Coding Agent lo había hecho en un span de herramienta — y el código de salida distinto de cero de Weaver devolvió el hallazgo para su corrección.

El mismo runner produce una matriz de cobertura que muestra qué atributos definidos por el registro emite un plugin y cuáles le faltan. La cobertura es medición, no bloqueo, por lo que esas ejecuciones usan --report-only: un atributo ausente se registra, no supone una ruptura del build. La matriz cubre 29 atributos en 14 instrumentaciones Python de LoongSuite, y la live conformance matrix comunitaria presenta la misma señal para los escenarios públicos, aunque actualmente no incluye los plugins de LoongSuite.

Por separado, Weaver live-check reporta como violaciones missing_attribute los atributos que un plugin emite y que el registro no define. Cuando el equipo acepta temporalmente una violación conocida, anota su id y la razón en expected_violations; la ejecución también falla si Weaver deja de reportar esa violación, señal de que la entrada está obsoleta y debe eliminarse. La cobertura solo cuenta atributos definidos por el registro, de modo que las extensiones propias no elevan la puntuación de un plugin, y cuando varios frameworks necesitan el mismo atributo que el registro no define, el equipo propone añadirlo a las convenciones semánticas de GenAI.

El Coding Agent había añadido atributos gen_ai.crewai.* al plugin de CrewAI para detalles ausentes de las convenciones semánticas de GenAI; como la guía de nomenclatura de OpenTelemetry advierte de que ese prefijo podría chocar con futuros atributos estándar, los autores afirman que los próximos prompts deberían exigir que los agentes emitan solo atributos de GenAI definidos por el registro.

Lo publicado hasta ahora

El sistema ha impulsado el desarrollo integral de instrumentación para una variedad de frameworks de GenAI, SDK de agentes y benchmarks de agentes. Los plugins generados se encuentran en los paquetes instrumentation-loongsuite de Loongsuite Python, la distribución Apache-2.0 de Alibaba de la instrumentación Python de OpenTelemetry, y cada uno fue revisado por una persona en la fase de pull request. Actualmente cubren frameworks y SDK como CrewAI, LangChain, LangGraph, LiteLLM, AutoGen, AgentScope, Dify, Google ADK, el Claude Agent SDK, el Microsoft Agent Framework, MCP y mem0, junto a benchmarks de agentes como BFCL-v4, WebArena y MiniSWEAgent. Las comprobaciones de conformidad ayudan a mantener la coherencia de su telemetría entre frameworks.

El equipo también trabaja para llevar esta instrumentación al proyecto OpenTelemetry Python GenAI; la discusión está abierta en opentelemetry-python-genai#185 y los primeros pull requests de instrumentación ya están en revisión.

Lo que no funcionó

La primera versión carecía de la comprobación de Weaver: un modelo revisor juzgaba si la telemetría seguía la especificación, pero a veces aprobaba salidas que violaban las convenciones. El equipo sustituyó ese juicio por una comparación con el registro y un código de salida distinto de cero ante violaciones.

El Claude Agent SDK planteó un límite distinto: su paquete Python envuelve la CLI de código cerrado Claude Code, donde ocurren el bucle del agente, el despacho de herramientas y las llamadas al modelo. El plugin puede informar de lo que cruza la frontera del proceso, pero no capturar detalles dentro de la CLI, y ni la investigación ni la planificación recuperan datos que el framework no expone.

La planificación también sigue exigiendo conocimiento del framework: el primer execution-plan.md suele omitir detalles del ciclo de vida del framework, por lo que un ingeniero que lo conoce revisa el plan antes de la implementación. Según los autores, los agentes son más fiables implementando un plan revisado que eligiendo por sí solos la estrategia de instrumentación.

Dónde importa la revisión humana

Las convenciones semánticas fijan nombres de atributos y tipos de span, pero mapear las operaciones de un framework a ellas requiere criterio. Algunos valores tienen origen directo: gen_ai.tool.name y gen_ai.tool.call.id provienen del objeto de llamada a herramienta, mientras que gen_ai.usage.input_tokens y gen_ai.usage.output_tokens provienen de los datos de uso del proveedor. Otros mapeos exigen más juicio: CrewAI expone crews, tasks, agents y flows, y la instrumentación debe decidir cuáles de sus operaciones representan una operación invoke_agent y cuáles necesitan un span distinto, una decisión que requiere revisión humana.

El artículo cierra con tres invitaciones para mantenedores y colaboradores: abrir un issue en el repositorio de las convenciones semánticas de GenAI al detectar una laguna que los agentes puedan encontrar, unirse a las discusiones del SIG de GenAI Semantic Conventions and Instrumentation para influir en lo que se estandarice a continuación, y contribuir a OpenTelemetry Python GenAI conforme las instrumentaciones generadas avanzan hacia upstream.