Image de couverture : Une URL, deux lecteurs : servir HTML aux humains et Markdown aux agents IA avec le Content Negotiation
IA & Ingénierie

Une URL, deux lecteurs : servir HTML aux humains et Markdown aux agents IA avec le Content Negotiation

24 June 2026
5 min de lecture
8 vues
Sébastien Muler

Le web a désormais deux types de lecteurs

Pendant des années, les équipes de développement ont construit leurs APIs en suivant un schéma bien établi : une route pour l'interface web, une autre pour l'API mobile, une troisième pour les intégrations tierces. Aujourd'hui, un nouveau lecteur s'invite à la table — les agents IA — et la tentation est forte de repartir sur le même schéma : créer un endpoint dédié, une authentification séparée, des sérialiseurs spécifiques.

Cet article présente une approche différente, inspirée d'un article publié par Russell Jones sur DEV.to : utiliser la négociation de contenu HTTP pour servir, depuis une seule URL, une page HTML à un utilisateur humain et du Markdown structuré à un agent IA. Une seule définition de contenu, un seul point d'entrée, deux formats de sortie.


Qu'est-ce que le Content Negotiation ?

La négociation de contenu est un mécanisme HTTP standard (défini dans la RFC 7231) qui permet au client d'indiquer au serveur le format de réponse qu'il préfère, via l'en-tête Accept.

Concrètement :

  • Un navigateur envoie Accept: text/html → le serveur répond avec une page HTML.
  • Un agent IA envoie Accept: text/markdown → le serveur répond avec du Markdown propre.

Même URL. Même ressource. Même logique métier. Format différent.

C'est exactement ce que fait Symfony nativement avec son composant HttpFoundation et le système de formats. Rien de nouveau à inventer — juste une convention HTTP qu'on exploite intelligemment.


Pourquoi le Markdown plutôt que JSON pour les agents IA ?

La réflexe des développeurs est souvent de répondre à cette question avec du JSON : c'est structuré, parseable, universel. Mais pour un agent IA — notamment un LLM — le Markdown présente des avantages concrets :

1. Moins de tokens consommés Le HTML est verbeux : balises, attributs, scripts, styles inline... Un contenu de 1 000 mots en HTML peut facilement peser 5 à 10 fois plus en tokens qu'en Markdown. À l'échelle, c'est un coût direct.

2. Meilleure lisibilité sémantique Les LLMs ont été entraînés sur d'énormes corpus incluant du Markdown (GitHub, Reddit, Stack Overflow...). Ils comprennent nativement les titres ##, les listes - item, le code ```. Pas besoin d'un prompt supplémentaire pour expliquer la structure.

3. Zéro bruit Fini les menus de navigation, les footers, les bannières cookies, les scripts analytics. Le Markdown ne contient que le contenu utile.


Implémenter le Content Negotiation en Symfony/PHP

Voici comment appliquer ce principe dans un projet Symfony existant, sans créer de nouvelles routes.

Déclarer le format Markdown

Première étape : enregistrer le type MIME text/markdown comme format reconnu par Symfony.

// config/packages/framework.php ou via services.yaml
// Dans un EventListener ou directement dans le kernel
$request->setFormat('markdown', 'text/markdown');

Ou via la configuration YAML :

# config/packages/framework.yaml
framework:
    request:
        formats:
            markdown: 'text/markdown'

Adapter le contrôleur

Dans votre contrôleur, il suffit d'interroger le format demandé et de brancher la réponse en conséquence :

#[Route('/articles/{slug}', name: 'article_show')]
public function show(string $slug, Request $request): Response
{
    $article = $this->repository->findBySlug($slug);

    if ($request->getPreferredFormat('html') === 'markdown') {
        return new Response(
            $this->markdownRenderer->render($article),
            200,
            ['Content-Type' => 'text/markdown; charset=utf-8']
        );
    }

    return $this->render('article/show.html.twig', [
        'article' => $article,
    ]);
}

Générer le Markdown depuis vos entités

Un service simple suffit pour transformer une entité en Markdown structuré :

class ArticleMarkdownRenderer
{
    public function render(Article $article): string
    {
        return sprintf(
            "# %s\n\n**Auteur :** %s  \n**Date :** %s\n\n---\n\n%s\n",
            $article->getTitle(),
            $article->getAuthor()->getName(),
            $article->getPublishedAt()->format('d/m/Y'),
            $article->getBody()
        );
    }
}

Résultat : un document Markdown propre, sans bruit, prêt à être consommé par un agent.


Ce que ça change concrètement pour votre architecture

Avant : deux systèmes parallèles

Sans cette approche, rendre un site "AI-Ready" implique souvent :

  • Un endpoint /api/articles/{id} dédié aux agents
  • Une authentification API séparée (token, OAuth...)
  • Des sérialiseurs JSON spécifiques à maintenir
  • Une documentation Swagger/OpenAPI à jour
  • Des tests supplémentaires

Deux systèmes à faire évoluer en parallèle, deux fois plus de risques de divergence.

Après : une seule surface de code

Avec le Content Negotiation :

  • ✅ Une seule route, un seul contrôleur
  • ✅ La même logique d'autorisation s'applique aux deux formats
  • ✅ Les évolutions du modèle se propagent automatiquement
  • ✅ Zéro dette technique supplémentaire

L'article source de Russell Jones pousse ce concept encore plus loin avec le framework Waaseyaa (en alpha), où la définition du type de contenu génère automatiquement les deux formats sans qu'on écrive la moindre ligne de contrôleur. Un aperçu de ce que pourrait être la prochaine génération de frameworks orientés agents.


Conclusion

Rendre votre application "AI-Ready" n'implique pas nécessairement de tout refactoriser. Le Content Negotiation HTTP est une convention vieille de 30 ans qui s'applique parfaitement au nouveau contexte des agents IA.

En Symfony, l'implémentation tient en quelques dizaines de lignes : déclarer le format text/markdown, adapter le contrôleur, créer un renderer dédié. Vos URLs existantes deviennent instantanément lisibles par les LLMs, sans coût de maintenance supplémentaire.

La question à se poser n'est plus "dois-je construire une API pour les agents ?" mais "est-ce que mes contenus sont bien structurés pour être sérialisés proprement ?" C'est un investissement qui bénéficie autant aux humains qu'aux machines.

💡 Article original de Russell Jones : One URL, two readers: serving HTML to people and Markdown to agents — publié sur DEV.to.

Partager cet article