Volver a las notas

llms.txt v2: lo que de verdad lee un agente

23 de octubre de 2026·
llms.txtaiseomarkdown
Demo en vivoAbrir a pantalla completa

La propuesta de llms.txt tuvo su primera gran revisión en agosto de 2026, y los cambios responden a la pregunta evidente que antes no tenía respuesta: dada una página, ¿cómo encuentra un agente el llms.txt que la describe? Leí el texto nuevo, pasé el /llms.txt de esta web por el pequeño parser de referencia que vive en el repositorio de la propuesta, y se cayó. La demo de arriba enseña las dos cosas: qué saca de verdad un parser del archivo y cómo va un agente desde una página hasta su llms.txt y su versión en Markdown.

Qué es llms.txt, en un párrafo

Es un archivo Markdown que da a los agentes una puerta de entrada seleccionada a un sitio: un nombre, un resumen corto, algo de contexto y listas de enlaces a contenido pensado para LLMs. Jeremy Howard publicó el original en septiembre de 2024. Sigue siendo una propuesta "abierta a la comunidad", no un estándar del W3C ni del IETF. El texto deja claro para qué sirve: robots.txt dice qué acceso automatizado es aceptable, mientras que llms.txt "se usa bajo demanda, cuando un agente necesita información sobre un tema mientras ayuda a un usuario". Está pensado para la inferencia, no para el entrenamiento.

Qué cambia en la v2

La página de cambios enumera cuatro cosas:

  • Cualquier ruta, gana la más específica. Un archivo en /docs/llms.txt cubre todo lo que hay bajo /docs/, y "cuando se aplica más de un archivo, los agentes deben usar el más específico". Esto es lo que permite participar a un sitio de proyecto en GitHub Pages, que no puede tocar la raíz del host.
  • Dos formas de URL en Markdown. La v1 solo contemplaba añadir .md (page.html.md). La v2 también permite sustituir la extensión (page.md). Las URLs sin nombre de archivo usan index.html.md o index.md.
  • Descubrimiento con link relations. rel="alternate" type="text/markdown" apunta a la versión Markdown de una página y rel="describedby" apunta al llms.txt que la cubre, ya sea con elementos <link> o con una cabecera HTTP.
  • ## Optional pierde su significado especial. La herramienta de expansión de contexto de la v1 ya no forma parte de la propuesta, así que Optional es ahora solo una convención para enlaces secundarios.

La forma con cabecera es la interesante, porque la puedes añadir en la configuración del servidor o del CDN sin tocar ni una página, y además funciona con archivos que no son HTML. Este es el ejemplo de la propia propuesta:

Link: </docs/page.html.md>; rel="alternate"; type="text/markdown", </docs/llms.txt>; rel="describedby"

Y su equivalente en el <head> de la página:

<link rel="alternate" type="text/markdown" href="/docs/page.html.md">
<link rel="describedby" href="/docs/llms.txt">

La pestaña "Cómo lo encuentra" de la demo construye esa cabecera para cada página de un pequeño sitio de ejemplo. Estas son las dos funciones que hay detrás, que ejecuté en Node con las salidas que ves:

// Los llms.txt que cubren una ruta, del más específico al menos.
function coveringLlms(path, files) {
  return files
    .filter(f => path.startsWith(f.slice(0, f.lastIndexOf('/') + 1)))
    .sort((a, b) => b.length - a.length);
}

// page.html -> page.html.md ('append') o page.md ('replace').
function markdownUrl(path, mode) {
  if (path.endsWith('/')) return path + (mode === 'append' ? 'index.html.md' : 'index.md');
  return mode === 'append' ? path + '.md' : path.replace(/\.[^./]+$/, '') + '.md';
}

const files = ['/llms.txt', '/docs/llms.txt'];
coveringLlms('/docs/api/fetch.html', files); // ['/docs/llms.txt', '/llms.txt']
markdownUrl('/docs/api/fetch.html', 'replace'); // '/docs/api/fetch.md'
markdownUrl('/docs/', 'append');                // '/docs/index.html.md'

Desmarca "existe /docs/llms.txt" y las páginas de la documentación vuelven al archivo raíz.

El formato es más estricto de lo que parece

La propuesta dice que el archivo sigue "un formato preciso que permite métodos de procesamiento fijos (es decir, técnicas de programación clásicas como parsers y expresiones regulares)". Por orden: un H1 con el nombre (lo único obligatorio), un blockquote con un resumen corto, cero o más párrafos o listas sin encabezados y, después, secciones H2 cuyas listas llevan enlaces [nombre](url), opcionalmente seguidos de : y unas notas.

El repositorio todavía incluye un parser de unas veinte líneas, llms_txt/miniparse.py, y la primera pestaña de la demo es una traducción a JavaScript línea a línea. Pasé los dos por los mismos siete archivos, incluidos el llms.txt de esta web y el de la propia propuesta, y devuelven el mismo resultado en todos los casos. Esto es lo que encontré dándole entradas reales:

from miniparse import parse_llms_txt

parse_llms_txt("# My site\n\n> What it is.\n\nOne line of details.\n\n## Docs\n\n- [Guide](https://example.com/guide.md): start here\n")
# {'title': 'My site', 'summary': 'What it is.', 'info': 'One line of details.',
#  'sections': {'Docs': [{'title': 'Guide', 'url': 'https://example.com/guide.md', 'desc': 'start here'}]}}
  • Cada línea no vacía de una sección tiene que contener un enlace [nombre](url). Una línea como - GitHub: https://github.com/me lanza AttributeError. Y también un elemento de lista largo partido en una segunda línea sangrada, porque el parser divide las secciones línea a línea. El /llms.txt de esta web hace las dos cosas, y por eso el ejemplo "richi.codes hoy" se abre en rojo.
  • Del resumen solo cuenta la primera línea. Un blockquote repartido en varias líneas > se queda con la primera como resumen y el resto acaba en los detalles.
  • El resumen necesita detalles detrás. Si no hay nada entre el blockquote y el primer ##, el resumen sale como None y el blockquote acaba en info. Si antes del primer ## solo hay un H1, el parser lanza AttributeError. Hasta el llms.txt del propio repositorio de la propuesta sale con summary: None por este motivo.
  • ### también corta. El patrón es ^##, así que ### Getting started se convierte en una sección llamada # Getting started. De todas formas, la spec no permite encabezados antes del primer H2.

A un agente que lea el archivo como Markdown normal nada de esto le importa. Pero si una herramienta lo analiza como la propuesta invita a hacerlo, estos detalles deciden si tus enlaces sobreviven. El ejemplo "richi.codes arreglado" enseña el arreglo barato: una línea por enlace, enlaces Markdown de verdad y un párrafo corto después del resumen.

Qué vigilar

  • Quién lo consume de verdad. La propuesta enumera plataformas de documentación que generan el archivo y dice que los laboratorios de IA publican llms.txt para su propia documentación para desarrolladores. Publicar un archivo no es lo mismo que leerlo, así que mira qué documentan los agentes que te importan antes de tratar llms.txt como un factor de posicionamiento.
  • La cabecera Link. Es el cambio de la v2 más barato de adoptar: una línea de configuración del servidor, y los agentes pueden ir de cualquier página a su versión Markdown y a su llms.txt.
  • El archivo de esta web. La demo deja claro que el /llms.txt de aquí tiene elementos de lista con URLs sueltas y líneas partidas. Merece la pena arreglarlo.

Fuentes