
🔴 Avanzado · Arquitectura · 8 min de lectura
Mi agente trabaja con historiales clínicos. Si algo sale mal, lo que necesito para depurarlo es justo lo que no puedo mirar. Y «lo miro solo esta vez» no existe: o tengo permiso o no lo tengo.
El problema
Jacob Lauritzen, director de tecnología de Legora, lo ha planteado esta semana con una frase que vale por un artículo entero: su empresa no puede leer ni un solo rastro de lo que su agente hace para un abogado. Esos datos son de los clientes del despacho, no del despacho, y desde luego no de Legora.
Suena a caso raro de un sector muy regulado. No lo es. Si trabajas con historiales clínicos, con nóminas, con expedientes o con cualquier dato de un cliente de tu cliente, estás exactamente ahí. En España eso es media consultoría.
Y rompe el ciclo con el que trabajamos todos. Lo normal es: algo falla, miras el rastro, encuentras el turno donde se torció, escribes un caso de prueba con esa entrada, lo arreglas. Quita el primer paso y los otros tres se caen. No puedes escribir el caso de prueba porque no conoces la entrada. No puedes reproducir el fallo porque no sabes qué pasó. Y la persona que sí lo vio es un abogado que te dice «me dio una respuesta rara el martes».
La trampa en la que cae casi todo el mundo es intentar recuperar el acceso: anonimizar, tokenizar, pedir consentimiento. A veces se puede. Pero cuando no se puede, hay que diseñar para eso en vez de pelearse con ello.
Cómo se resuelve hoy
La idea que lo ordena todo: si no puedes sacar los datos, mete la evaluación dentro. Cuatro capas, de menos a más esfuerzo.
Telemetría sin contenido. Lo que no es el dato del cliente sí lo puedes ver, y es más de lo que parece: qué herramientas llamó el agente y en qué orden, cuántos turnos tardó, si reintentó, si pidió permiso, cuánto contexto usó, si terminó o se rindió. La forma de una ejecución sin su contenido. Suena pobre y no lo es: la mayoría de los fallos de agentes son de forma, no de contenido. Un agente que normalmente resuelve en tres turnos y de repente tarda once, o que llama a la misma herramienta cinco veces seguidas, te está contando que algo va mal sin enseñarte ni una palabra del expediente.
Señales que viajan con el usuario. Un botón de «esto está mal» que registre el identificador de la sesión, la categoría del problema y nada más. Después, el cliente mira su propio rastro y te cuenta lo que quiera. Es lento y es humano, y suele ser lo único que hay.
Un conjunto de evaluación sintético que tú sí puedas ver. Esta es la que más rinde y la que más cuesta montar. Se construyen casos realistas pero inventados, con la ayuda de un experto del dominio, y se cuidan precisamente los casos raros que no se te ocurrirían. No sustituye a los datos reales, pero es lo único que puedes ejecutar mil veces, versionar y meter en la integración continua.
Evaluación federada. El código de evaluación va a donde están los datos, se ejecuta ahí y solo salen métricas agregadas. Es la respuesta buena y la más cara: exige que el cliente ejecute algo tuyo en su casa, y eso es una conversación de contratos antes que de código.
El cambio de mentalidad que hay detrás de las cuatro: dejas de depurar casos concretos y pasas a vigilar distribuciones. No vas a saber por qué falló esa ejecución. Vas a saber que el martes la tasa de ejecuciones largas se dobló.
Ejemplo
Un registro de telemetría que puedes guardar sin guardar nada del cliente. Lo importante es lo que no tiene.
import hashlib
import time
from dataclasses import dataclass, field, asdict
@dataclass
class HuellaDeEjecucion:
"""La forma de una ejecucion, sin una sola palabra de su contenido.
Regla de oro: si un campo pudiera contener texto del cliente, no esta
aqui. Ni el prompt, ni la respuesta, ni los nombres de fichero, ni los
mensajes de error en bruto (que arrastran rutas y a veces datos).
"""
sesion: str # identificador opaco, para que el cliente lo cruce
tarea: str # la categoria, de una lista cerrada tuya
turnos: int = 0
herramientas: list[str] = field(default_factory=list) # nombres, no argumentos
reintentos: int = 0
permisos_pedidos: int = 0
tokens_contexto: int = 0
segundos: float = 0.0
desenlace: str = "en_curso" # ok | rendido | error | cortado_por_usuario
clase_de_error: str | None = None # el TIPO de excepcion, nunca su mensaje
def huella_de_entrada(texto: str) -> str:
"""Para saber si dos ejecuciones partieron de lo mismo sin saber de que.
Permite detectar que un mismo caso falla siempre, o que un cliente repite
la misma consulta diez veces porque no le convence la respuesta.
"""
return hashlib.sha256(texto.encode("utf-8")).hexdigest()[:16]
def registrar(huella: HuellaDeEjecucion, destino) -> None:
datos = asdict(huella)
# Cinturon y tirantes: nada que no este en la lista blanca sale de aqui.
permitidos = set(HuellaDeEjecucion.__dataclass_fields__)
destino.write({c: v for c, v in datos.items() if c in permitidos})
Y lo que de verdad miras, que no es una ejecución sino la distribución de todas:
def alarmas(huellas: list[HuellaDeEjecucion], base: dict) -> list[str]:
"""Compara esta semana con la linea base. Lo que importa son los cambios.
`base` son las mismas metricas de un periodo que dabas por bueno.
"""
avisos = []
n = len(huellas)
if n < 30:
return ["muestra demasiado pequena para decir nada"]
largas = sum(1 for h in huellas if h.turnos > base["turnos_p95"]) / n
if largas > 2 * base["proporcion_largas"]:
avisos.append(f"ejecuciones largas: {largas:.1%} (base {base['proporcion_largas']:.1%})")
rendidos = sum(1 for h in huellas if h.desenlace == "rendido") / n
if rendidos > base["proporcion_rendidos"] + 0.05:
avisos.append(f"el agente se rinde mas: {rendidos:.1%}")
repetidas = len(huellas) - len({h.sesion for h in huellas})
if repetidas > n * 0.1:
avisos.append(f"{repetidas} reintentos del usuario sobre la misma sesion")
return avisos or ["sin cambios respecto a la base"]
La regla práctica para decidir qué campo puede entrar en ese registro, y que vale para cualquier dominio regulado:
Antes de anadir un campo, preguntate: ¿podria un juez decir que este
campo contiene informacion de un cliente del cliente?
- Nombre de la herramienta llamada ....... no
- Argumentos de la herramienta ........... SI, fuera
- Numero de turnos ....................... no
- Mensaje de error en bruto .............. SI, fuera (lleva rutas y datos)
- Tipo de la excepcion ................... no
- Tamano del contexto en tokens .......... no
- Huella del prompt (hash) ............... no, y sirve para agrupar
- Primeros 100 caracteres de la respuesta SI, fuera. Siempre.
Errores habituales
Guardar el mensaje de error entero «porque es técnico». Es el error más común y el más fácil de cometer. Un mensaje de excepción arrastra rutas de fichero, nombres de documento, a veces un fragmento del propio dato. Guarda el tipo de la excepción, no su texto.
Creer que anonimizar resuelve el problema. Un expediente anonimizado con la fecha, el juzgado y el importe sigue identificando a una persona para quien conozca el caso. Si la respuesta a «¿puedo mirarlo?» es que no, anonimizar no la convierte en un sí.
Montar el conjunto sintético sin un experto del dominio. Los casos que se le ocurren a quien programa son los fáciles. Los que rompen el agente son los que solo conoce quien hace ese trabajo todos los días. Dos tardes con esa persona valen más que doscientos casos generados.
Vigilar medias en vez de colas. La media de turnos no se mueve aunque el 5% de las ejecuciones se haya ido al infierno. Mira percentiles altos y proporciones, como en el ejemplo, no promedios.
Qué vigilar
🟢 La telemetría sin contenido se puede montar hoy, en una tarde, y es lo que más información da por unidad de esfuerzo. Si tu agente trabaja con datos que no puedes ver y no tienes esto, empieza por aquí.
🟡 Los conjuntos de evaluación sintéticos construidos con el experto del dominio. Cada vez se habla más de ellos y cada vez hay mejores herramientas para generarlos, pero la parte que decide sigue siendo la conversación con la persona que conoce el trabajo.
🟡 La evaluación federada, que ejecuta tu código de evaluación donde viven los datos y solo devuelve agregados. Técnicamente está resuelto; lo que falta es que sea normal pedirlo en un contrato.
🔴 Que exista una forma estándar de que un cliente te devuelva información útil sobre un fallo sin enseñarte el dato. Hoy eso es un correo de un abogado diciendo que el martes pasó algo raro. Si alguien estandariza ese canal, media consultoría respira.



