## ¿Qué es la negociación de contenido?

La negociación de contenido significa servir respuestas distintas según lo que pida el cliente. Para los agentes de IA, esto significa devolver texto estructurado (JSON, markdown o texto plano) en lugar de HTML pesado cuando la solicitud viene de un agente.

## Por qué importa

Los agentes de IA descartan los encabezados HTTP antes de que el modelo vea el contenido. El modelo nunca ve Content-Type, códigos de estado o cadenas de redirecciones — solo ve el cuerpo en texto. Servir HTML obliga a los agentes a parsear la estructura del DOM, extraer texto y descartar el layout — desperdiciando tokens y perdiendo significado.

## Qué verificamos

**El UA de agente recibe contenido no-HTML** — Enviamos una solicitud con un User-Agent conocido de IA (por ejemplo, `ClaudeBot`) y verificamos si el Content-Type de la respuesta es algo distinto de `text/html`. Los sitios que detectan UAs de agentes y sirven `text/markdown`, `text/plain` o `application/json` pasan esta verificación.

**Accept: JSON devuelve JSON** — Enviamos `Accept: application/json` y verificamos que la respuesta sea JSON válido. Esto permite a los agentes programáticos acceder directamente a datos estructurados.

**Accept: text devuelve texto** — Enviamos `Accept: text/plain` y verificamos que la respuesta sea texto plano o markdown. Es el formato más simple para que los modelos de lenguaje lo consuman.

**Accept: markdown devuelve markdown** — Enviamos `Accept: text/markdown` y verificamos que la respuesta sea contenido markdown (Content-Type `text/markdown` o cuerpo en markdown). Este es el formato preferido por los agentes cuando quieren estructura (encabezados, listas, enlaces) sin el peso del HTML.

## Cómo implementarlo

Detecta los User-Agents de agentes y los encabezados `Accept` en el middleware de tu servidor:

```javascript
app.get('/', (req, res) => {
  const ua = (req.get('user-agent') || '').toLowerCase();
  const accept = req.get('accept') || '';
  const isAgent = /claude|gpt|anthropic|perplexity|gptbot/i.test(ua);

  // Markdown tiene precedencia cuando se solicita explícitamente
  if (accept.includes('text/markdown')) {
    return res.type('text/markdown').sendFile('[llms.txt](/kb/es/llms-txt)');
  }

  if (accept.includes('application/json')) {
    return res.json({ name: 'My Service', api: '/openapi.json' });
  }

  if (isAgent || accept.includes('text/plain')) {
    return res.type('text/plain').sendFile('llms.txt');
  }

  res.sendFile('index.html');
});
```

### Detalles de markdown

`Accept: text/markdown` es una convención más nueva (popularizada por el ecosistema llms.txt) y aún no implementada universalmente. Devolver markdown cuando se solicita significa:

- El cuerpo es markdown válido (encabezados, listas, enlaces — sin etiquetas HTML)
- El encabezado Content-Type es `text/markdown` (o `text/plain` con cuerpo en markdown)
- La misma URL que sirve HTML al navegador puede servir markdown al agente — la ruta no cambia, solo la representación

Para páginas de documentación, posts de blog y páginas de contenido, normalmente ya tienes una fuente en markdown. Sirve esa fuente directamente cuando se envía `Accept: text/markdown`, en lugar de convertir primero a HTML.

## Madurez de la especificación

**Establecida.** La negociación de contenido está definida en HTTP mismo ([RFC 9110](https://www.rfc-editor.org/rfc/rfc9110)). La convención de devolver formatos amigables para agentes según el User-Agent o Accept es ampliamente usada por sitios conscientes de la IA.

## Más información

- [RFC 9110 §12: Content Negotiation](https://www.rfc-editor.org/rfc/rfc9110#name-content-negotiation)
- [llmstxt.org](https://llmstxt.org/) — Convención Markdown-para-LLMs

## Relacionado

- [OpenAPI](/kb/es/openapi)
