Image for post Debugging en Apps LLM: Técnicas para Fallos Silenciosos

Debugging en Apps LLM: Técnicas para Fallos Silenciosos


Las aplicaciones basadas en LLMs fallan de formas que los logs tradicionales no capturan: respuestas incorrectas con HTTP 200, contexto truncado sin aviso y tool calls que nunca se ejecutan. Este artículo cubre los patrones de fallo más frecuentes y cómo detectarlos con Langfuse y OpenTelemetry antes de que lleguen al usuario.

El problema: tu app devuelve 200 OK, pero miente

Tu API responde. No hay excepciones en los logs. El usuario recibe un texto fluido y convincente. Pero la respuesta es incorrecta.

Este es el escenario más peligroso en aplicaciones basadas en LLMs: el fallo silencioso. En software tradicional, un bug genera un stack trace o un código de error. En una app de LLM, el modelo produce una respuesta gramaticalmente perfecta pero factualmente errónea, sin ninguna señal de alerta. La aplicación funciona, pero la información es falsa.

Si trabajas con RAG, agentes autónomos o chatbots en producción, esto no es un caso hipotético. Es cuestión de tiempo. Y las herramientas de debugging que usas para APIs REST o microservicios no sirven aquí.

¿Qué es un fallo silencioso en LLMs?

Un fallo silencioso en LLMs es una respuesta que parece correcta a nivel técnico (HTTP 200, JSON válido, texto coherente) pero contiene información errónea, incompleta o inventada, sin que ningún sistema de monitorización lo detecte.

A diferencia de un crash o un timeout, el fallo silencioso no deja rastro en tus dashboards convencionales. Prometheus ve latencia normal. Grafana muestra requests exitosos. Pero el usuario ha recibido una alucinación.

Los tres patrones de fallo que debes conocer

Context rot: degradación antes del límite

El modelo no necesita agotar su ventana de contexto para empezar a fallar. La atención del LLM se concentra en el inicio y el final del input, procesando con menos fiabilidad la información intermedia. El resultado: instrucciones ignoradas, datos relevantes descartados y respuestas que contradicen el propio contexto proporcionado.

En un pipeline RAG típico, el system prompt consume miles de tokens, los chunks recuperados ocupan otros tantos y el historial de conversación crece con cada turno. La información que el modelo necesita acaba enterrada en la zona de menor atención, antes de alcanzar el límite de la ventana.

Truncación silenciosa

Algunos proveedores limitan el output a un máximo de tokens (por ejemplo, 16.384) y devuelven finish_reason=length. Pero ciertos frameworks aceptan la respuesta parcial sin registrar ningún warning. Tu código sigue ejecutándose, los logs no muestran errores, pero el LLM trabaja con un contexto incompleto y produce resultados cada vez menos fiables.

Con agentes multi-paso, el problema se amplifica. Una herramienta devuelve 20.000 tokens de JSON, desborda el contexto disponible, y el agente continúa operando con información recortada sin avisar.

Tool calls fantasma

Para invocar una herramienta, el LLM necesita generar un JSON que cumpla el schema definido. Pero los modelos, especialmente los más pequeños o cuantizados, fallan al generar JSON válido: una coma extra, un bracket omitido o una estructura malformada. La llamada nunca se ejecuta, pero el modelo continúa como si hubiera recibido la respuesta. En el peor caso, inventa el resultado de la herramienta.

Este patrón es difícil de detectar porque no genera excepciones. El flujo del agente parece normal en los logs convencionales.

Instrumentar tu app con Langfuse y OpenTelemetry

Langfuse es una plataforma open source de observabilidad para LLMs. Su SDK v3 está basado en OpenTelemetry, lo que permite enviar trazas a múltiples destinos de forma simultánea. Puedes hacer self-hosting con Docker Compose o usar su cloud. Vamos paso a paso.

Paso 1: Instalar dependencias

pip install langfuse openai

Paso 2: Configurar variables de entorno

# .env
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_BASE_URL=https://cloud.langfuse.com
OPENAI_API_KEY=sk-...

Si usas una instancia self-hosted, cambia LANGFUSE_BASE_URL a la URL de tu servidor.

Paso 3: Decorar funciones con @observe()

from langfuse import observe
from langfuse.openai import openai


@observe()
def recuperar_contexto(query: str) -> list[str]:
    """Busca en el vector store y devuelve chunks relevantes."""
    resultados = vector_store.search(query, top_k=5)
    return [doc.text for doc in resultados]


@observe()
def generar_respuesta(query: str, contexto: list[str]) -> str:
    """Genera respuesta usando el contexto recuperado."""
    contexto_texto = "\n".join(contexto)
    response = openai.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Responde solo con información del contexto."},
            {"role": "user", "content": f"Contexto:\n{contexto_texto}\n\nPregunta: {query}"}
        ],
        temperature=0  # Determinista para reproducibilidad
    )
    return response.choices[0].message.content


@observe()
def pipeline_rag(query: str) -> str:
    """Pipeline completo: recuperación + generación."""
    contexto = recuperar_contexto(query)
    return generar_respuesta(query, contexto)

Cada @observe() genera un span en la traza. Langfuse captura inputs, outputs, latencia y uso de tokens cuando usas el wrapper de OpenAI. El resultado es una traza completa del pipeline, desglosada por función.

Paso 4: Añadir métricas semánticas

Un 200 OK no significa que la respuesta sea correcta. Necesitas métricas que evalúen la calidad semántica de cada interacción.

from langfuse import get_client

langfuse_client = get_client()


@observe()
def pipeline_rag_con_metricas(query: str) -> str:
    contexto = recuperar_contexto(query)
    respuesta = generar_respuesta(query, contexto)

    # Evaluar relevancia del contexto recuperado
    langfuse_client.score(
        name="context_relevance",
        value=calcular_similitud_coseno(query, contexto),
        comment="Similaridad promedio entre query y chunks"
    )

    # Detectar posible truncación
    langfuse_client.score(
        name="response_completeness",
        value=1.0 if len(respuesta) > 50 else 0.5,
        comment="Respuestas cortas pueden indicar truncación"
    )

    return respuesta

Depurar un fallo real: el caso del contexto perdido

Escenario: un chatbot RAG funciona bien en las primeras interacciones, pero empieza a alucinar a partir del turno 7-8 de la conversación. No hay errores en los logs. La latencia es normal.

Con Langfuse, el proceso de debugging sigue estos pasos:

  1. Revisar la traza: abrir el trace del turno 8 en el dashboard de Langfuse. Ver el uso de tokens por span.
  2. Detectar el patrón: en el turno 8, el total de tokens alcanza 45.000. El system prompt (2.000 tokens) + historial (35.000) + chunks RAG (8.000) comprimen la información relevante en la zona intermedia del prompt.
  3. Verificar la causa: los chunks recuperados son correctos (alta similaridad coseno), pero el modelo los ignora porque están en la zona de menor atención.
  4. Aplicar el fix: resumir el historial de conversación cada 5 turnos y colocar los chunks RAG al final del prompt, justo antes de la pregunta del usuario.

Sin trazas detalladas por span, este bug es invisible. Los logs solo muestran "request OK, 200, 1.2s". Como exploramos al hablar de la jerarquía real en agentes IA en producción, la observabilidad no es un extra: es parte de la arquitectura.

Herramientas de observabilidad LLM: comparativa

El ecosistema de observabilidad LLM ha madurado considerablemente. Estas son las opciones principales a marzo de 2026:

HerramientaTipoSelf-hostedOTEL nativoIdeal para
LangfuseOpen sourceSí (Docker/K8s)Sí (SDK v3)Equipos con control total
LangSmithComercialNoLimitadoProyectos LangChain
Arize PhoenixOpen sourceRAG, model drift
HeliconeComercialNoNo (proxy)Tracking de costes
SigNozOpen sourceNativoAPM unificado + LLM

Si tu stack no depende de LangChain, Langfuse es la opción más flexible. Su base en OpenTelemetry permite enviar trazas también a Datadog o Jaeger para correlacionar con métricas de infraestructura. Si buscas comparar cómo diferentes modelos manejan estas trazas, los benchmarks con uso real ofrecen una perspectiva más fiable que las métricas sintéticas.

En Producción

Pasar del tutorial a un sistema de observabilidad LLM en producción tiene matices que un ejemplo no cubre.

Sampling, no 100% de trazas. En producción con volumen, trazar el 100% de las requests consume almacenamiento y coste de ingesta innecesarios. Configura un sample rate del 10-20% para tráfico normal y 100% para errores o latencias altas.

Redacción de PII. Las trazas contienen los prompts y respuestas completas, incluyendo datos personales de usuarios. Langfuse soporta un callback de data masking que redacta emails, teléfonos y tarjetas antes de almacenar la traza. Es un endpoint FastAPI que recibe el span OTEL y devuelve la versión limpia.

Alertas semánticas. Configura alertas sobre métricas que importan: score de relevancia por debajo de umbral, finish_reason=length, picos de tokens (suelen indicar loops infinitos en agentes). Las alertas de latencia y error rate son necesarias, pero no suficientes.

Costes. Langfuse self-hosted con Docker Compose es gratuito. El cloud empieza en unos 25€/mes para equipos pequeños. El overhead de latencia del SDK v3 es inferior a 2ms por request porque las trazas se envían de forma asíncrona.

Reproducibilidad. Para depurar un fallo, necesitas reproducirlo. Usa temperature=0 y seeds fijos en tus tests. En producción, guarda el estado completo de la request (prompt, contexto, parámetros del modelo) en cada traza para poder hacer replay después.

Errores comunes y depuración

Error: "El modelo alucina solo en conversaciones largas"
Causa: Context rot. Los chunks relevantes quedan en la zona intermedia del prompt, donde la atención del modelo es menor.
Solución: Resumir el historial cada N turnos. Colocar los chunks de RAG al final del prompt, justo antes de la pregunta.

Error: "La herramienta devuelve null pero el agente sigue respondiendo"
Causa: Tool call fantasma. JSON malformado que el framework ignora.
Solución: Validar el schema de tool calls con Pydantic antes de ejecutar. Loguear finish_reason de cada respuesta del modelo.

Error: "Las respuestas se acortan progresivamente sin error"
Causa: Truncación silenciosa por max_tokens del proveedor.
Solución: Verificar finish_reason en cada respuesta. Configurar alertas cuando finish_reason=length.

Preguntas frecuentes

¿Puedo usar OpenTelemetry sin Langfuse?

Sí. OpenTelemetry es un estándar abierto. Puedes enviar trazas a Jaeger, Datadog, SigNoz o cualquier backend compatible con OTEL. Langfuse añade semántica específica de LLMs (tokens, costes, scores de calidad) sobre el estándar, pero no es obligatorio para empezar.

¿Necesito observabilidad LLM si mi app es un chatbot simple?

Depende del impacto de un fallo. Si el chatbot responde preguntas internas de baja criticidad, los fallos silenciosos son tolerables. Si gestiona datos de clientes o influye en decisiones de negocio, la observabilidad no es opcional. El coste de configuración (2-4 horas) compensa el riesgo.

¿Cómo detecto alucinaciones de forma automática?

No existe una solución perfecta. Las aproximaciones más prácticas combinan LLM-as-a-judge (un segundo modelo evalúa la respuesta contra el contexto), scores de relevancia con embeddings, y feedback explícito del usuario. Langfuse soporta las tres vías como scores asociados a cada traza.

Los fallos más peligrosos en apps LLM no son los que generan excepciones, sino los que pasan desapercibidos. Context rot, truncación silenciosa y tool calls fantasma requieren una capa de observabilidad diseñada para sistemas basados en modelos de lenguaje. Langfuse con OpenTelemetry cubre ese hueco sin vendor lock-in, tanto en arquitecturas multi-agente como en pipelines RAG simples.

La clave está en asumir que el modelo va a fallar y diseñar la instrumentación para detectarlo antes que el usuario. Si ya usas OpenTelemetry para tu infraestructura, añadir trazas de LLM es un paso natural. Si no, Langfuse con Docker Compose te da una base funcional en una tarde.

¿Has depurado algún fallo silencioso en tu pipeline de LLM? Cuéntame qué herramientas usas en Twitter @sergiomarquezp_. Próximamente, exploraremos cómo los tests basados en análisis de impacto AST pueden prevenir regresiones antes de que los agentes lleguen a producción.