Markdown contro HTML per gli LLM è una falsa polemica: tre fasi diverse, una sola conversazione


Il dibattito su quale formato testuale convenga agli LLM si è riaperto a inizio maggio 2026 con tre voci che sembrano dire cose opposte. L’8 maggio Thariq Shihipar, dal team Claude Code di Anthropic, pubblica un post amplificato il giorno stesso da Simon Willison: per gli artifact che un agente produce a beneficio umano, HTML batte Markdown. Tre giorni prima, il 5 maggio, esce mdream di Harlan Zw, che fa esattamente l’opposto: converte HTML in Markdown spendendo il 50% di token in meno. A marzo Cloudflare aveva già lanciato Markdown for Agents, con risparmi dell’80% sulla stessa pipeline.

Letta come una polemica unica, la discussione è incomprensibile: tre attori seri arrivano a conclusioni opposte sullo stesso oggetto. Letta come quello che è davvero, cioè tre risposte a tre problemi diversi scambiate per una sola questione, diventa una mappa utile per chi sta progettando workflow agentici in produzione. Le tre fasi sono ingestion, output ricco ed editing collaborativo. Le prime due hanno un vincitore. La terza no.




Ingestion: Markdown vince perché ogni token conta

Il polo dell’ingestion è quello dove l’agente legge documenti per rispondere a una domanda. Qui il vincolo è banale: ogni token in input costa, sia in cash che in degrado del recall sul context window. Markdown vince per pura economia, con margini a tre cifre percentuali.

Il caso Cloudflare è il più visibile. I crawler AI mandano un header Accept: text/markdown, gli edge server convertono al volo l’HTML in Markdown, restituiscono il contenuto con un header diagnostico x-markdown-tokens. L’esempio citato nell’annuncio: un blog post da 16.180 token in HTML diventa 3.150 token in Markdown. John Mueller di Google ha definito la mossa “a stupid idea”, sostenendo che la conversione rimuove contesto e struttura. Cloudflare ha tirato dritto, il mercato ha dato ragione a Cloudflare.

Sotto la cifra del risparmio c’è un argomento meno citato ma più solido. Gli LLM sono pretrained su miliardi di token di prosa e codice contro una frazione di HTML soup. Quando l’input è HTML, il modello paga due volte: più token in conto e meno allineamento con la distribuzione di training. La densità semantica del Markdown si paga in espressività, si guadagna in comprensione.

Tre attori seri, tre risposte opposte. Stanno misurando facce diverse di un oggetto chiamato con lo stesso nome.

mdream spinge nella stessa direzione di Cloudflare ma dal lato applicativo: 6 KB di JavaScript, 50% di token in meno rispetto a Turndown, 3 volte più veloce, capacità di processare 1,8 MB di HTML in 60 millisecondi. Microsoft il 17 aprile ha rilasciato MarkItDown, converter universale che porta verso Markdown file Office, PDF, immagini, audio. La filosofia operativa coincide: per dare contesto a un modello, qualsiasi cosa è meglio in Markdown.

Output ricco: HTML vince quando il destinatario è umano

Il polo dell’output è dove sta Shihipar. La tesi è che quando l’agente produce un artifact destinato a un essere umano nel team, HTML è meglio di Markdown su quattro dimensioni concrete: information density, visual clarity, sharing, two-way interaction.

La densità informativa è la più ovvia: HTML ha tabelle vere, SVG, CSS, script, layout assoluti, immagini reali. Markdown ha header, liste, fence di codice. Per una specifica con diagrammi e mockup, Markdown costringe a ripiegare su ASCII art o sulla stima dei colori con caratteri Unicode. La leggibilità visiva è il secondo asse: un Markdown da cento righe in su nessuno lo legge, un HTML renderizzato con tab, illustrazioni e link interni viene effettivamente aperto.

Il punto sullo sharing è il più sottovalutato. Il browser apre l’HTML nativamente, un Markdown va portato in un editor, in un viewer o convertito a parte. La probabilità che un writeup di pull request venga davvero letto è molto più alta in HTML. Lo stesso vale per l’interazione bidirezionale: slider, knob, copy button che riportano JSON dentro al prompt successivo. Un Markdown è un testo, un HTML è una piccola interfaccia usa-e-getta.

Shihipar è onesto sui costi: HTML costa due-quattro volte il tempo di generazione, i diff in pull request sono rumorosi, e in token consuma di più. La giustificazione esplicita è che il context da un milione di token di Opus 4.7 rende quel costo invisibile in cambio di artifact che effettivamente qualcuno legge. Per spec leggibili dal team con diagrammi veri, l’argomento regge.

Editing collaborativo: la casella vuota che nessuno sta misurando

Le due fasi precedenti sono coperte. La terza no. Tutta la letteratura della polemica corrente assume un workflow single-shot: un prompt, un output, fine. Esiste però un secondo mestiere che non è nei benchmark e che sta arrivando: più agenti diversi che modificano lo stesso documento nel tempo. Knowledge base interne aggiornate da agenti per mesi, runbook operativi mantenuti da SRE bot, spec di RFC redatte da swarm di agenti, checklist di incidente con audit trail.

Quattro voci sul polo input, una sul polo output, zero sul polo editing. Il dibattito ha un punto cieco.

Lo scenario operativo richiede cinque proprietà che nessuno dei formati popolari offre tutte insieme: ID stabili per blocco editabile, forma canonica byte-stable, parser deterministico, edit operations come dati replicabili, leggibilità a riga di comando. Markdown è un disastro su quasi tutte: gli heading anchor non sono universali, CommonMark e GFM divergono sugli edge case, i diff testuali sono fragili. HTML va meglio sugli ID e sul DOM standard ma costa caro in token e i diff nei pull request sono illeggibili. JSON va bene sull’audit ma è mediocre da leggere a vista.

Il quadrante mancante è “abbastanza compatto da non bruciare context window, abbastanza strutturato per edit chirurgici, abbastanza umano da leggerlo in cat“. Nessuno dei tre formati popolari sta in quel quadrante.

Tre fasi non sono una sola: il debate ha un punto cieco

La tabella temporale della polemica conferma lo squilibrio. Il 5 marzo Cloudflare lancia Markdown for Agents (polo input). Il 17 aprile Microsoft rilascia MarkItDown (polo input). Tra marzo e aprile codersera misura lo stato di llms.txt: solo il 10,13% dei domini lo adotta, nessun effetto SEO misurabile, ma tutti i devtool seri lo shippano lo stesso (limbo, polo input). Il 5 maggio esce mdream (polo input). L’8 maggio Shihipar e Willison piazzano l’unica voce del polo output. Quattro contro uno, zero sul terzo polo.

Per chi deve scegliere oggi un formato in produzione la conclusione è netta: Markdown per l’ingestion, HTML quando serve un artifact ricco da condividere, fine della discussione. Per il 95% dei casi reali, la risposta è già scritta. Il problema dell’editing multi-agente è ancora un caso di nicchia, ma non resterà tale per molto: chi sta deployando swarm di agenti in produzione ci sbatterà nei prossimi dodici mesi.

AGD: un prototipo Rust per riempire la casella vuota

L’esperimento AGD (Agent Document) è un prototipo Rust open source rilasciato come v0.1.0 con licenza MIT su github.com/Pinperepette/agd. Eredita il line-oriented di Markdown e aggiunge tre proprietà: ID di blocco stabili, sintassi LL(1) deterministica, operazioni di edit come dati JSON. La grammatica completa sta in 50 righe di EBNF nel file grammar/agd.ebnf, il parser in circa mille righe di Rust.

La sintassi gira su tre regole. Primo: ogni blocco inizia con @ a colonna zero. Markdown ha cinque sigilli diversi (#, *, >, -, indentazione), AGD ne ha uno solo, e il lexer diventa una macchina a sette stati in 150 righe. Secondo: ID opzionale sempre in coda, nella forma [#nome], che rende il blocco indirizzabile per identificatore stabile e non per offset di byte. Terzo: niente chiusure, una riga vuota o un nuovo @ chiudono il blocco precedente. L’equivalente di diventa @h1 ....

// metadati e attributi key=value
@meta title="Welcome" author=alice

// heading con ID stabile
@h1 Hello [#intro]
@p Body text. Inline: *bold*, _italic_, `code`.

// lista
@ul [#features]
- token-efficient vs HTML
- LL(1) parser
- stable per-block IDs

// fence verbatim per codice
@code lang=python [#hello]
~~~
def hello():
    return "world"
~~~

// cross-reference
@ref #hello

La proprietà load-bearing è la determinismo del parser: lo stesso file dato a due implementazioni diverse deve produrre lo stesso AST. È la garanzia che HTML offre tramite DOM standard e che Markdown non offre, dato che CommonMark, GFM e MDX divergono su casi limite.

Le promesse non misurate sono sempre sbagliate

Le stime token a vista, fatte prima di scrivere codice, si sono rivelate sbagliate di segno. La promessa iniziale era −5 a −10% rispetto a Markdown, la misura su corpora da 100 a 100mila blocchi ha dato un +18 a +22%: AGD costa di più, non meno. L’errore stava nel tokenizer: @h1 tokenizza come 3 token con BPE cl100k_base, mentre # seguito da spazio è un token solo. Su mille blocchi sono già duemila token di prefisso ingestiti per niente.

Le altre stime hanno tenuto meglio. Rispetto a HTML, AGD risparmia il 16-19% contro la promessa del 30-40%: direzione giusta, ridotta a metà. Rispetto a JSON, il risparmio è del 56-59% contro la promessa del 50%: leggermente meglio del previsto. La stima del parser a 200 righe è diventata mille righe Rust, equivalente a circa 300 righe Python una volta sottratto l’overhead di linguaggio.

La banda 18-22% sul confronto con Markdown è molto stretta e scala in modo ortogonale dalla dimensione del documento. Il sovrapprezzo è il costo onesto degli ID espliciti e dei prefissi @. Non c’è un trucchetto sintattico per recuperarlo. La domanda diventa se il +20% di token serva a comprare qualcosa che gli altri formati non possono offrire. La risposta misurata sta su due numeri.

L’index batte il find lineare di tre ordini di grandezza

Il primo numero riguarda l’efficienza dell’edit per ID. Il design ingenuo era una funzione Document::find(&id) con scansione lineare. Per cento blocchi va bene, per centomila no. Il benchmark misurato, riproducibile con cargo run --release --bin agd-bench, dà cifre che convertono il design space.

Blocchi Linear find Indexed find Speedup
100 80 ns 25 ns 3x
1.000 1,6 µs 26 ns 62x
10.000 21 µs 36 ns 583x
100.000 283 µs 108 ns 2625x

Senza index, ogni edit operation costa 283 microsecondi solo per trovare il blocco target su un documento da 100mila entry. Con l’index, 108 nanosecondi. Tre ordini di grandezza di differenza. La conseguenza architetturale è meno scontata del numero: chiunque adotti AZD seriamente deve costruirsi un DocumentIndex e mantenerlo in memoria. Non è un’ottimizzazione, è la differenza tra usabile e inutilizzabile.

Il pattern parse-once, edit-many gira a 500mila operazioni al secondo. Lo stesso flusso via CLI subprocess crolla a 76 op/sec.

Il pattern “parse-once, edit-many” gira a 500mila operazioni al secondo via library API. Lo stesso flusso invocato via CLI in subprocess gira a 76 op/sec. La differenza è di 6500 volte. L’implicazione operativa è chiara: AGD ha senso solo se mantieni il documento parseato in memoria dentro un servizio, non se invochi la CLI in loop. Qualunque adozione seria assume la forma di un demone che mantiene i documenti in memoria, espone le edit operation via socket o RPC, ricostruisce l’index dopo ogni write batch.

Il lab a tre vie: Redis Streams, tre agenti, tre formati

Il secondo numero arriva dal lab multi-agente costruito per verificare la tesi. Stesso scenario operativo, un piano di risposta a incidente di security, reso in tre formati: Markdown, HTML, AGD. Tre processi agent indipendenti (analyst, responder, auditor) emettono 12 edit operation su Redis Streams in parallelo. Tre demoni, uno per formato, consumano FIFO con XREADGROUP, applicano l’operazione alla loro versione del documento (chiave Redis Hash), confermano con XACK. Lo stream funziona da audit trail nativo: il replay da XRANGE 0 + ricostruisce lo stato.

Il workload contiene una operazione cattiva alla riga 4: l’analyst rinomina la sezione “Findings” in “Initial findings”. Cinque secondi dopo, l’auditor aggiunge un finding tardivo targettando l’ID logico findings. Il rename ha cambiato il nome ma non l’ID. Markdown non ha un meccanismo di ID stabile, HTML e AGD sì. Il risultato su mediana di cinque run:

Metrica Markdown HTML AGD
Ops applicate 11/12 12/12 12/12
Ops not_found 1 0 0
Stream length (audit) 12 12 12
Replay ricostruisce stato
Dimensione finale (byte) 527 895 697
Latenza media per op 118 µs 1,21 ms 2,3 µs
Throughput (op/sec) ~8.500 ~830 ~430.000

Markdown perde un’operazione perché il regex su ## Findings non trova più la sezione dopo il rename. Lo stream registra l’operazione come target_not_found: l’audit è onesto, lo stato finale è incompleto. HTML e AGD applicano tutte e dodici le operazioni: gli id resistono alla rinominazione del titolo. Il replay funziona ovunque: lo stream è autoritativo, rieseguendo le ops dall’offset zero si ottiene byte-per-byte lo stesso stato live su tutti e tre i formati. L’audit log Redis è l’unica cosa da conservare, lo stato live è una funzione del log.

La latenza è dove succede l’interessante. AGD applica un’operazione in 2,3 microsecondi mediana: 500 volte più veloce di HTML su BeautifulSoup, 50 volte più veloce di Markdown via regex puro. Il merito non è solo del formato, è del fatto che il demone agdd è scritto in Rust, parsea il documento una volta all’avvio, costruisce un DocumentIndex, applica ogni operazione come mutazione strutturale tipata. Una versione Rust dei demoni HTML e Markdown ridurrebbe il gap senza annullarlo: HTML deve comunque riserializzare l’intero DOM ad ogni mutazione, Markdown deve cercare per nome senza ID stabili.

Selective retrieval: il fattore 14 che inverte il segno

Il +20% di token rispetto a Markdown è il prezzo che AGD paga quando carichi l’intero documento nel context. La metà della storia che il dibattito non racconta è cosa succede quando l’agente non carica tutto. Un agente in stile Claude Code che cerca un blocco specifico in un documento da diecimila blocchi può caricare prima il Table of Contents (la lista degli ID), scegliere il blocco rilevante, leggere solo quello. Markdown non può: senza ID stabili universali, devi cercare per nome di sezione o caricare tutto.

Blocchi AGD selective (TOC + 1 blocco) Markdown whole-doc Speedup
100 231 token 2.074 token 9,0x
1.000 1.720 token 20.502 token 11,9x
10.000 14.902 token 217.170 token 14,6x

Lo speedup cresce con la scala. A diecimila blocchi, una richiesta mirata su AGD costa un quindicesimo di un caricamento completo Markdown. A centomila blocchi diventa circa un ventesimo. Il punto di pareggio, cioè dove AGD ripaga il +20% del primo caricamento, arriva dopo la seconda lookup mirata sullo stesso documento. In quasi tutti i workflow agentici reali quel breakeven cade nei primi minuti di interazione.

Le due cifre vanno lette insieme. Il +20% è il costo nel regime “carica tutto, paga proporzionale”. Il fattore 14 è lo sconto nel regime “leggi solo l’indice, paga la lista”. Quale dei due conta dipende dal pattern di accesso dell’agente, non dal formato in sé. Se l’agente carica sempre l’intero documento, Markdown vince del 20%. Se fa lookup mirati, AGD si ripaga dopo due query e poi scala in modo non lineare.

Quando AGD ha senso, quando non ne ha

Sui casi reali la decisione è abbastanza meccanica. Markdown vince ancora in tre scenari: RAG con ingestion whole-doc (carichi sempre tutto, fai zero lookup mirati), output ricco per umani (vince HTML perché AGD non è renderizzabile in browser), documentazione statica (Markdown ha quindici anni di tooling tra Hugo, Jekyll, MDX e GitHub render, AGD ha zero ecosistema).

Sul prompt engineering verso Claude la situazione è di pareggio: Anthropic spinge tag XML come delimitatori, il modello è trained su quelli, AGD potrebbe funzionare ma non sfrutta il pretraining. AGD vince invece in quattro scenari: selective retrieval su documenti di grandi dimensioni (TOC + un blocco contro caricamento intero), editing multi-agente con audit e replay, documenti di lunga vita dove la forma canonica byte-stable rende i diff utili in code review, workflow dove le edit operation vanno loggate e replicate.

Conta i casi: tre vittorie per altri formati, un pareggio, quattro per AGD. Cambia il regime di accesso, cambia il vincitore.

Nota pragmatica per chi non vuole adottare un formato nuovo: il 90% del valore di AGD si ottiene combinando Markdown con kramdown anchors (la sintassi {#stable-id} in coda agli heading). GitHub li renderizza, Pandoc li capisce, l’ecosistema resta intatto. Si perdono la forma canonica rigorosa e il parser LL(1) puro, ma probabilmente non li si userà mai a quel livello di rigore. AGD ha senso quando perdere quei due è un costo reale, non teorico, e oggi quei costi pochi li sentono.

Quello che resta in mano

La polemica Markdown contro HTML di questa settimana è viva e fondata, ma confonde tre fasi diverse del workflow degli agenti. La fase di ingestion è coperta da Markdown e dai converter ottimizzati (Cloudflare, mdream, MarkItDown). La fase di output ricco per umani è coperta da HTML e dalla spinta di Anthropic via Claude Code. La terza fase, l’editing collaborativo di documenti che durano nel tempo, non ha un formato dedicato. AGD è un esperimento che prova a riempire quel buco.

Il valore di un prototipo come AGD per chi non lo userà mai non sta nei suoi 89 test, nel proptest robusto o nel lab Redis che gira in cinque secondi. Sta nella mappa del design space che restituisce. Il +20% di token cambia segno appena l’agente smette di leggere tutto e inizia a chiedere blocchi specifici, e questo non si capisce finché non lo si misura. Il determinismo del parser è una proprietà che non si sa di volere finché non si vede un regex Markdown rompersi su un rename in produzione.

La domanda operativa da portarsi a casa non è “quale formato adotto domani”. Per il 95% dei workflow agentici la risposta è già scritta: Markdown in input, HTML quando serve un artifact ricco. La domanda interessante è cosa succede quando il documento dura mesi e cinque agenti diversi lo modificano in parallelo. Per quel mestiere nessuno dei formati popolari è progettato. Chi sta deployando swarm di agenti in produzione adesso quella casella la troverà vuota, e dovrà decidere se ricostruire qualcosa simile ad AGD oppure accettare le frizioni di Markdown con kramdown anchors.

L’intero codice del lab sta in scripts/ogni-blocco-ha-un-nome nel repository di riferimento, in due versioni: lab.py sequenziale single-process e v2_redis/run_lab.sh con tre processi concorrenti su Redis Streams. I benchmark del prototipo AGD sono in benches/BENCHMARKS.md e benches/RETRIEVAL.md, riproducibili con due comandi cargo run --release. La specifica del formato è dogfooded: il file spec/AGD-SPEC.agd è scritto in AGD, non in Markdown.


#Adessonews seleziona nella rete articoli di particolare interesse.
Se vuoi leggere l’articolo completo clicca sul seguente link
 Andrea Amani

Source link

Di