JSON es la caverna de Platón
Un validador de JSON responde a una sola pregunta: ¿está bien formado? Durante veinte años tratamos esa respuesta como si significara 'el dato es correcto'. Con un humano en cada extremo colaba. Sin él, no.
- #json
- #epistemologia
- #agentes
- #datos
La provocación
Un validador de JSON es, probablemente, el test más inútil de tu pipeline: pasa en verde con especial entusiasmo justo cuando el dato está mal.
Comprueba una cosa —que las llaves cierren, que las comas estén donde toca, que las
comillas emparejen— y sin embargo, en casi todas las arquitecturas que he visto, ese
200 OK con JSON válido se trata como si significara el dato es correcto. No lo
significa. Nunca lo significó.
Las sombras en la pared
En el mito de la caverna, unos prisioneros ven sombras proyectadas en un muro y toman las sombras por las cosas. No están equivocados sobre lo que ven: la sombra existe, es nítida, es reproducible, dos prisioneros distintos ven la misma. Están equivocados sobre lo que es.
Mira esta sombra:
{ "valor": 100 }
Es JSON válido. ¿100 qué? ¿Euros, pesos, miligramos, un porcentaje, grados, unidades en
stock, milisegundos? El formato no lo sabe. El validador dice que sí. El esquema, si lo
tienes, dice number, que es exactamente la misma sombra con el contorno más marcado.
Añade contexto y el problema no desaparece: se disfraza.
{ "fecha": "01/02/2026", "importe": 1500, "cliente": "Acme" }
¿1 de febrero o 2 de enero? ¿1500 con IVA o sin? ¿"Acme" es un nombre para mostrar o una
clave foránea? Todo esto es válido. Todo esto es ambiguo. Y la ambigüedad no está en tu
código: está en el formato.
Por qué esto no dolía antes
Porque siempre hubo alguien fuera de la caverna.
El significado nunca viajó dentro del JSON. Viajaba en la documentación, en el Confluence, en el hilo de Slack donde preguntaste "oye, ¿la fecha es ISO o europea?" y alguien te contestó. JSON transportaba una forma lo bastante regular como para que una persona en cada extremo inyectara el significado a mano.
Funcionaba, y era barato. Pero conviene ver lo que era en realidad: un sistema de dos piezas, el formato y el humano. Y hemos quitado el humano.
Qué pasa cuando el que lee es un modelo
Un LLM que recibe {"fecha": "01/02/2026"} no tiene Confluence, ni Slack, ni al compañero
que lleva seis años en el proyecto. Tiene el nombre de la clave y una distribución de
probabilidad. Así que infiere. Y esa inferencia sale por el otro lado con el mismo
aplomo con el que saldría un dato medido, porque a la salida hay otro JSON, y otro JSON
tampoco tiene sitio donde decir "esto me lo estoy inventando a medias".
Ese es el mecanismo de buena parte de lo que llamamos alucinación. No es que el modelo
mienta: es que el formato le exige comprometerse. Si un agente está un 20 % seguro de
la dirección de un cliente, JSON le obliga a serializarla como una cadena absoluta. La
duda existía antes de JSON.stringify y no existe después. La certeza no se pierde por el
camino: se destruye en la serialización.
"Para eso están los esquemas"
Es la objeción obvia, y es la que menos aguanta.
JSON Schema, OpenAPI, Zod, Pydantic: todos hacen la misma cosa, que es restringir la
sintaxis con más precisión. z.number() no dice euros. z.number().describe("euros") es
un comentario para humanos: no se propaga cuando sumas, no se comprueba en la frontera
siguiente, no impide sumar euros con pesos y obtener un número perfectamente válido.
| Lo que el esquema garantiza | Lo que necesitabas saber |
|---|---|
| Es un número | Es dinero, en una moneda concreta, y no se puede sumar con otra |
| Es una cadena con formato fecha | Es un instante, en una zona horaria, y hasta cuándo sigue siendo cierto |
| El campo está presente | El dato se midió, se dedujo, o se lo inventó alguien |
| Cumple el contrato | El emisor estaba seguro de él |
Un esquema es la sombra dibujada con más detalle. Sigue siendo una sombra.
Las tres cosas que JSON estructuralmente no puede llevar
- Ontología. Qué clase de cosa es esto.
stringeint32son categorías de memoria, no del mundo.Moneda,Medida,Instantesí lo son, y ninguna cabe en JSON salvo como acuerdo verbal entre dos equipos. - Certeza y procedencia. Cuánto se lo cree quien lo envía, y de dónde salió. Un valor medido por un sensor y otro completado por un modelo son indistinguibles una vez serializados.
- Validez temporal. Hasta cuándo sigue siendo verdad. "El CEO de Acme es Ana" fue cierto. La verdad caduca; el campo, no.
Puedes añadir claves: "confidence": 0.2, "source": "sensor_3", "validUntil": "…".
Hazlo, es mejor que nada. Pero mira bien lo que has construido: una convención que nada
obliga a leer. El siguiente servicio de la cadena puede ignorarla entera y seguir siendo
perfectamente conforme al contrato. No es física, es decoración.
Donde más se nota: las cadenas de agentes
Un agente llama a otro, que llama a una herramienta, que devuelve un JSON que alimenta a un
tercero. Cuatro saltos. En el salto 1 alguien estaba un 70 % seguro. En el salto 4 esa
cifra es un number en un campo, y nadie recuerda —porque no hay dónde recordarlo— si
salió de un sensor calibrado, de una base de datos, o de un modelo que la produjo por
completar la frase.
El sistema no falla ruidosamente. Falla en verde, con todos los contratos cumplidos y todos los tests pasando. Qué hacer al respecto cuando son varios agentes de inteligencia artificial pasándose trabajo lo desarrollo en sistemas multiagente.
Pruébalo: sácalo de la caverna
Una convención que nada obliga a leer es decoración. Así que aquí tienes una frontera que sí obliga: un ejercicio que no te deja guardar hasta que el JSON diga, sin que nadie tenga que adivinarlo, en qué unidad está cada número, qué fecha es cada fecha, de dónde sale, cuánto se lo cree quien lo envía y hasta cuándo vale.
Fíjate en la primera comprobación. Con el JSON de partida ya está en verde: es la única que miraría un validador. Todas las demás están en rojo, y a él le dan igual.
Ejercicio
Escribe un JSON que una IA no tenga que adivinar
Un agente va a leer este JSON sin documentación, sin Slack y sin nadie a quien preguntar. Reescríbelo para que no tenga que adivinar nada. Sigue siendo JSON corriente: no hace falta ningún formato nuevo, solo convenciones.
Lo que comprueba un validador JSON
Pasa: Está bien formado
Es lo único que miraría un validador. Todo lo de abajo le da igual.
Lo que necesita un modelo para no adivinar
Falla: Ningún número va sin su unidad
Sin unidad: «importe» (1500). ¿Euros, miligramos, horas? Pon cada número junto a su unidad: { "amount": 1500, "currency": "EUR" } o { "value": 5, "unit": "mg" }.
Falla: Ninguna fecha admite dos lecturas
«fecha» = "01/02/2026": ¿día/mes o mes/día? Un modelo elegirá una de las dos con total aplomo. Usa ISO 8601: "2026-02-01".
Falla: Ningún null ambiguo
«notas» es null. ¿No existe, no se sabe o no aplica? Para un modelo son tres cosas distintas: omite el campo o dilo con un valor explícito (por ejemplo, una lista vacía).
Falla: Dice de dónde sale (source)
Falta "source" en la raíz: el sistema y el registro de donde sale el dato, p. ej. "erp/facturas#8812". Sin eso, un número medido y uno inventado son indistinguibles.
Falla: Dice cuánto se lo cree (confidence)
Falta "confidence" en la raíz: un número entre 0 y 1. Un 1 dice «lo medí»; un 0.6, «lo deduje y puedo equivocarme».
Falla: Dice hasta cuándo vale (validUntil)
Falta "validUntil" en la raíz: el instante (ISO 8601 con zona) a partir del cual el dato deja de ser cierto. La verdad caduca; el campo, no, salvo que se lo digas.
Cuando no falle ninguna comprobación podrás guardarlo como rama.
Cuando pase todo, guárdalo como rama: queda en el historial colectivo del artículo, y
cualquiera puede abrirla y forkearla para mejorarla. Ojo con una cosa, que es la trampa de
este mismo ejercicio: estas comprobaciones miran que el significado esté dicho, no que
sea verdad. Puedes declarar "confidence": 1 sobre algo que te acabas de inventar y el
ejercicio no se enterará. Un validador, por listo que sea, sigue dentro de la caverna. Esa
objeción la desarrollo entera en En defensa de JSON.
La conclusión incómoda
JSON no está roto. Hace exactamente aquello para lo que se diseñó, y lo hace muy bien: mover estructura de A a B sin opinar sobre su significado. Eso viene de lejos: en 1948 Shannon fundó la teoría de la información diciendo con toda intención que los aspectos semánticos de la comunicación eran irrelevantes para el problema de ingeniería. Tenía razón. Para un cable.
El problema no es el formato. El problema es que montamos encima de él un sistema cognitivo y quitamos la pieza que ponía el significado sin sustituirla por nada.
Salir de la caverna no consiste en validar mejor. Consiste en que el dato lleve encima su
tipo ontológico, su certeza, su procedencia y su caducidad, y en que el runtime esté
obligado a respetarlos. Eso es exactamente lo que propone
ΛD, y lo que ya comprueba el compilador de
AXON (axon-lang), el lenguaje de programación que construyo para llevar esa idea
a código.
Y porque una tesis que nadie ataca es solo un eslogan, escribí también la mejor defensa que se me ocurre de lo contrario.
Para llevarte
- "JSON válido" contesta una pregunta sintáctica y ninguna semántica.
- Durante veinte años bastó porque había un humano en cada extremo poniendo el significado.
- Ese humano ya no está, y el modelo que lo sustituye rellena el hueco adivinando.
- Los esquemas afinan el contorno de la sombra. No la convierten en el objeto.