Por qué la salida de mi CLI no es XML (y cómo acabé reinventando TOON sin saberlo)

| Última modificación: 4 de mayo de 2026 | Tiempo de Lectura: 6 minutos
Premios Blog KeepCoding 2025

Co-Fundador de KeepCoding

TL;DR: Cuando tu consumidor principal es un LLM, XML y JSON desperdician tokens repitiendo estructura en cada elemento. Un formato posicional compacto reduce el consumo un 50%. Resulta que esa idea ya tenía nombre: TOON (Token-Oriented Object Notation). Misma presión selectiva — tokens caros y claves repetidas — misma solución.


Anthropic usa XML para todo. Sus system prompts están envueltos en <instructions>, sus ejemplos en <example>, sus herramientas en <function>. Si trabajas con Claude, vives rodeado de tags.

Así que cuando le pedí a Claude que diseñara la salida de una CLI pensada para ser consumida por LLMs, la tentación obvia era: XML. Si al modelo le dan la información en XML, ¿no debería la CLI devolvérsela en XML?

Va a ser que no.

El problema: repetición compulsiva

Imagina que tu CLI lista issues de un tracker de proyectos. Tienes 50 issues. En XML, cada una se ve así:

<issue>
  <id>PROD-587</id>
  <state>Backlog</state>
  <labels>backend</labels>
  <title>Importar sesiones desde backup del NAS</title>
  <age_days>14</age_days>
</issue>

Bonito. Autodescriptivo. Cada campo tiene su nombre. Un parser XML sabe exactamente qué es cada cosa.

Ahora multiplica eso por 50 issues. Los tags <issue>, <id>, <state>, <labels>, <title>, <age_days> se repiten 50 veces cada uno. Son 300 tags que no aportan información nueva tras la primera repetición. Aproximadamente 70 tokens por issue, 3.500 tokens para listar 50 issues.

JSON mejora algo — elimina los tags de cierre — pero sigue repitiendo las claves:

{
  "id": "PROD-587",
  "state": "Backlog",
  "labels": ["backend"],
  "title": "Importar sesiones desde backup del NAS",
  "age_days": 14
}

Cada línea repite "id":, "state":, "labels":, "title":, "age_days":. Unos 50 tokens por issue, 2.500 para las 50.

¿Y si eliminamos toda la repetición?

PROD-587 [Backlog] backend — Importar sesiones desde backup del NAS (14d)

25 tokens. Sin claves. Sin tags. Sin llaves ni comillas. 1.250 tokens para 50 issues.

Ojo al dato: la mitad que JSON, menos de un tercio que XML. Para hacer lo mismo.

«Pero un LLM necesita estructura»

Aquí es donde la gente me mira raro. «¿Y cómo sabe el LLM qué es cada campo?»

Pregunta legítima. Y la respuesta es: igual que tú lo sabes.

Mira esta línea:

PROD-587 [Backlog] backend — Importar sesiones desde backup del NAS (14d)

¿Necesitas que alguien te diga que PROD-587 es el ID? ¿Que [Backlog] es el estado? ¿Que lo que viene después del guión largo es el título? No. Lo deduces por la posición y el formato visual.

Los LLMs hacen exactamente lo mismo. Son máquinas de reconocer patrones en texto. Un formato posicional consistente — ID primero, estado entre corchetes, labels sueltos, título tras el guión, metadatos entre paréntesis — lo entienden a la primera. No necesitan <state>Backlog</state> para saber que «Backlog» es un estado.

La clave es distinguir dos operaciones que parecen iguales pero no lo son:

LEER es lo que hace un LLM. Tiene contexto, entiende semántica, deduce estructura. Un humano leyendo un informe no necesita que cada palabra lleve una etiqueta — entiende por posición y convención.

PARSEAR es lo que hace un programa. No tiene contexto, no entiende semántica, necesita delimitadores explícitos para extraer campos. Un jq '.state' necesita la clave "state" porque no sabe qué es un estado.

XML y JSON están diseñados para parsear. Son formatos de intercambio entre máquinas que no entienden el contenido. Los LLMs leen. La estructura explícita es redundante para ellos — y esa redundancia cuesta tokens.

El espectro: cuándo usar qué

No estoy diciendo que XML y JSON sean malos. Son malos para este caso. Para entendernos, aquí va el espectro:

Formato Tokens/issue Autodescriptivo Mejor para
XML ~70 Total APIs SOAP, configs, documentos con schema
JSON ~50 Total APIs REST, intercambio entre servicios
JSONL ~50 Total Scripts, jq, pipelines de datos
Posicional ~25 No LLMs, humanos, dashboards compactos

La regla es simple: si el consumidor entiende contexto (humano, LLM), puedes eliminar la estructura explícita. Si el consumidor no entiende contexto (script, parser), necesitas claves.

Por eso mi CLI tiene dos modos: el formato posicional compacto por defecto (para el LLM y para ti en la terminal) y --json para cuando necesitas pasar la salida por jq o procesarla con un script.

El giro: alguien ya había pensado en esto

Aquí viene la parte buena.

Unas semanas después de adoptar el formato que Claude había propuesto, alguien me preguntó: «¿Has mirado TOON?»

TOONToken-Oriented Object Notation — es un formato que apareció en noviembre de 2025. Su premisa: un encoding compacto del modelo de datos JSON, diseñado específicamente para minimizar tokens en prompts de LLMs.

¿Cómo se ve? Así:

issues[3]{id,state,labels,title,age_days}:
 PROD-587,Backlog,backend,Importar sesiones desde backup del NAS,14
 PROD-612,Todo,tokamak,Fix auth token refresh,2
 PROD-501,Done,frontend,Migrar base de datos a nuevo schema,30

Fíjate. Una cabecera con los campos ({id,state,labels,title,age_days}) y el número de elementos ([3]). Después, solo datos posicionales separados por comas. Sin repetir claves. Sin tags. Sin comillas en strings.

Es exactamente lo que Claude había propuesto para mi CLI — pero con una cabecera explícita que hace el formato autodescriptivo.

La versión de mi CLI:

PROD-587 [Backlog] backend — Importar sesiones desde backup del NAS (14d)
PROD-612 [Todo] tokamak — Fix auth token refresh (2d)
PROD-501 [Done] frontend — Migrar base de datos a nuevo schema (30d)

Esencialmente lo mismo. Formato posicional, sin repetición de claves, ~25 tokens por línea. La diferencia: TOON añade una cabecera de schema; el formato que elegí para mi CLI usa convenciones visuales (corchetes para estado, guión largo para separar el título).

Evolución convergente

En biología hay un concepto precioso: la evolución convergente. Especies que no están emparentadas desarrollan rasgos similares porque enfrentan la misma presión selectiva. Los ojos del pulpo y los del humano son estructuralmente parecidos, pero evolucionaron de forma totalmente independiente. Misma presión — necesidad de ver — misma solución.

TOON y el formato de mi CLI son evolución convergente aplicada al diseño de software. La presión selectiva es idéntica: tokens caros, claves que se repiten, consumidor que entiende por posición. La solución convergente: eliminar la repetición y confiar en el orden.

La diferencia entre TOON y lo que hace mi CLI es la cabecera. Y esa diferencia importa.

Sin cabecera, el formato funciona bien cuando el LLM ya tiene contexto sobre qué es cada campo (porque se lo he dicho en el system prompt o porque el patrón es obvio). Pero si alguien ve la salida por primera vez, tiene que adivinar. TOON resuelve eso: la cabecera dice una vez qué campos hay, y después el LLM no necesita que se lo repitan.

Es la diferencia entre un contrato implícito y un contrato explícito. Y en ingeniería, los contratos explícitos suelen ganar a la larga.

¿Debería migrar a TOON?

Me lo he preguntado. Y la respuesta honesta es: depende de quién consuma tu salida.

Si tu CLI la consume siempre el mismo agente con el mismo system prompt que describe el formato, el formato posicional sin cabecera funciona bien. Es más compacto (te ahorras la línea de cabecera) y el contexto ya está dado.

Si tu CLI la pueden consumir agentes distintos o humanos sin contexto previo, TOON es mejor. Una línea extra de cabecera es un coste marginal que compra autodescripción.

En mi caso, el consumidor es siempre el mismo agente con un CLAUDE.md que describe el formato. Así que me quedo con mi ñapa posicional. Pero si empaquetase la CLI para uso público, migraría a TOON sin dudarlo.

Lo que aprendí

Tres cosas:

1. No diseñes para parsers si tu consumidor no es un parser. XML y JSON son geniales para máquinas que necesitan claves explícitas. Los LLMs no son esa máquina. Diseña para cómo tu consumidor procesa la información, no para cómo tú crees que debería procesarla.

2. La repetición es el enemigo. En una lista de 50 elementos, cada clave que se repite 50 veces es basura informativa. Es como poner «Nombre:» delante de cada nombre en una lista de la compra. Después del segundo, tu cerebro ya no lo lee — pero sigue ocupando espacio.

3. Si tu solución converge con algo que ya existe, probablemente vas por buen camino. No digo que siempre haya que reinventar la rueda. Digo que si sale redonda — la propongas tú o la proponga tu herramienta — es una buena señal.

La próxima vez que diseñes el output de una herramienta que va a consumir un LLM, hazte una pregunta antes de llegar a serde_json::to_string(): ¿mi consumidor necesita parsear esto, o solo leerlo?

Si la respuesta es «leerlo», cada clave repetida es un token que estás quemando por nada. Y los tokens, como el dinero en la barra del bar, se van más rápido de lo que crees.


Read this article in English.

Noticias recientes del mundo tech


¡CONVOCATORIA ABIERTA!

Desarrollo de apps móviles ios & Android

Full Stack Bootcamp

Clases en Directo | Profesores en Activo | Temario 100% actualizado

Descárgate también el informe de tendencias en el mercado laboral 2026.

Fórmate con planes adaptados a tus objetivos y logra resultados en tiempo récord.
KeepCoding Bootcamps
Resumen de privacidad

Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.