Il testo è la parte facile: la pipeline che illustra gli articoli scritti dall'AI
Generare il testo è un problema risolto. Illustrarlo no. La pipeline end-to-end — prompt router, ricerca foto, diagrammi Mermaid, grafici verificati, e il testo alternativo, credito e markup ImageObject che la trasformano in SEO.
Siete sviluppatori con un obiettivo di contenuti: decine di articoli al mese, forse centinaia. Il modello di scrittura gestisce la bozza, la scaletta, la meta description, i link interni. Poi la pipeline si scontra con l'unico passaggio che non ha un modello proprio — le immagini — e si ferma. Non perché le immagini siano difficili da ottenere, ma perché niente nello stack sa quale immagine, da dove, con quale licenza, descritta come.
Questo articolo è quel passaggio mancante, cablato dall'inizio alla fine: che tipo di visual produrre per che tipo di sezione, il prompt che trasforma una bozza finita in brief di ricerca, la chiamata API che restituisce le foto, il secondo modello che disegna ciò che una fotografia non può, e l'assemblatore che emette markup che Google può effettivamente leggere. Due flussi di lavoro completi alla fine, pronti da copiare.
Il testo è risolto. L'illustrazione è dove si blocca.
Fate l'audit onesto di una pipeline articoli automatizzata. Scaletta: risolta. Bozza: risolta. Titolo, meta description, schema, link interni, traduzione: risolti, tutti dallo stesso modello, tutti in forma testuale. Poi:
| Fase della pipeline | Stato | Cosa blocca davvero |
|---|---|---|
| Scaletta e bozza | Risolto | Una chiamata al modello, un prompt |
| Titoli, meta, schema, link | Risolto | Testo in ingresso, testo in uscita |
| Immagine hero | Bloccato | Serve un file reale, una licenza, dimensioni e testo alt — nessuno dei quali un modello testuale può produrre |
| Immagini di sezione | Bloccato | Da tre a cinque per articolo, ciascuna diversa, nessuna che si ripeta nel sito |
| Grafici e diagrammi | Bloccato | Devono essere accurati — l'unico visual che una ricerca non può restituire e che un generatore non deve inventare |
Il fallimento non è estetico, è strutturale: l'articolo viene pubblicato con un placeholder stock, o
con la stessa foto degli ultimi dodici articoli, o con un'immagine generata la cui mano a sei dita è la
prima cosa che un lettore nota. E l'immagine non è decorazione — la stessa documentazione di Google sulle immagini
lo dice chiaramente: il testo alt “è l'attributo più importante quando si tratta di fornire metadati
per un'immagine”, e la linea guida è usare veri elementi <img> con
alt descrittivo invece di sfondi CSS, così che l'immagine possa essere trovata e
compresa.1
Quattro tipi di visual, quattro tipi di modello
L'errore di progettazione più grande è trattare “l'immagine” come un problema unico con un unico fornitore. Sono quattro, e il router tra di essi è una riga di prompt, non un servizio:
| La sezione richiede… | Producetela con | Perché non gli altri |
|---|---|---|
| Una scena dal mondo realehero, situazioni umane, luoghi, oggetti, gesti | Ricerca fotografica semantica (Pexafy) | Un generatore inventa i dettagli; un grafico non ha nulla da tracciare |
| Numeri che avete davverobenchmark, prezzi, risultati di sondaggi, latenza | Un modello che scrive codice di plotting, eseguito in una sandbox | A un modello di immagini non si può affidare un valore; una foto non può contenere dati |
| Una struttura o un flussoarchitettura, sequenza, macchina a stati | Un modello che scrive Mermaid / Graphviz, renderizzato in modo deterministico | Le librerie di foto gratuite non hanno un diagramma del vostro sistema |
| Il vostro prodotto sullo schermodocumentazione, changelog, tutorial | Uno screenshot browser scriptato (Playwright) | Nient'altro può mostrare una UI che esiste solo nella vostra build |
E l'illustrazione generata? Conserva un ruolo onesto: la scena che non può essere fotografata e non è un dato — un meccanismo astratto, un prodotto che non esiste ancora, uno stile illustrativo proprietario della casa editrice. (L'argomentazione completa a favore della fotografia reale rispetto alla generazione — velocità su volumi, accuratezza, il problema della somiglianza — è esposta qui.) Il prezzo va nella direzione di questa tendenza: dal 2 agosto 2026, l'Articolo 50 dell'EU AI Act richiede ai fornitori di sistemi generativi di contrassegnare gli output sintetici in un formato leggibile da macchina.2 Si tratta di un obbligo per i fornitori e i deployer di IA, non di una regola su cosa un blog possa pubblicare — ma è per questo che la provenienza dell'immagine in cima al tuo articolo è sempre più qualcosa che un lettore può verificare invece di dare per scontato.
La pipeline, dall'inizio alla fine
Cinque fasi. Solo la fase 3 tocca un'API di immagini, e solo la fase 4 è opzionale:
┌─ 1. SCRIVI ───────────────────────────────────────────────────┐
argomento ──▶ LLM ──▶ draft.md (sezioni h2, front-matter)
└───────────────────────────────┬───────────────────────────────┘
┌─ 2. BRIEF ────────────────────┴───────────────────────────────┐
draft.md ──▶ LLM ──▶ { hero: {...}, sections: [ {...} ] }
un oggetto JSON: per slot, un kind +
un brief camera o una spec dati/diagramma
└──────────┬──────────────────────────────────┬─────────────────┘
│ kind = "photo" │ kind = "chart" | "diagram"
▼ ▼
┌─ 3. CERCA ────────────────┐ ┌─ 4. DISEGNA (opzionale) ─────┐
GET /search/photos LLM ──▶ mermaid | codice di plotting
← foto con credito + ──▶ sandbox ──▶ .svg / .png
w/h, blur_hash, alt, (rendering deterministico, nessun
licenza, URL sorgente numero inventato)
└──────────┬────────────────┘ └──────────────┬───────────────┘
└───────────────┬──────────────────┘
┌─ 5. ASSEMBLA ─────────────┴───────────────────────────────────┐
<img> con width/height + fetchpriority | loading
alt scritto per un umano · credito visibile · ImageObject JSON-LD
photo_id memorizzato così nessuna pagina condivide un hero
└───────────────────────────────────────────────────────────────┘
Due proprietà contano più del diagramma. La fase 2 è un router: decide per ogni slot quale produttore eseguire, così non chiedete mai a una libreria di foto un grafico a barre. E la fase 5 è dove sta la SEO — tutto ciò che Google documenta sulle immagini (alt descrittivo, metadati di licenza, un hero sicuro per l'LCP) viene emesso qui, da campi che la risposta di ricerca già conteneva.
Fase 2: trasformare la bozza in brief visivi
Una singola chiamata al modello sulla bozza finita, e ciò che restituisce è un piano piuttosto che una query. Come scrivere un singolo brief fotografico — perché il titolo dell'articolo è il peggior input possibile, come si presenta un brief da 12 a 25 parole per la fotocamera, e i modi in cui può fallire — è spiegato per intero in la guida all'illustrazione di un singolo articolo, e non viene ripetuto qui. Ciò che una pipeline aggiunge è il routing: la stessa chiamata deve decidere, slot per slot, quale produttore eseguire — e una voce di tipo grafico porta dati dove una voce fotografica porta una scena.
# system prompt — eseguito una volta per bozza finita
Sei il direttore artistico di una pubblicazione tecnica. Leggi l'articolo e
restituisci il piano visivo: una voce per l'hero, una per ogni sezione H2.
Per ogni voce scegli esattamente un kind:
"photo" una scena reale: qualcuno che fa qualcosa da qualche parte, un luogo,
un oggetto, un gesto. Il default per gli hero.
"chart" la sezione riporta numeri che sono NELL'articolo. Non
inventare mai valori: copiali in data, testualmente.
"diagram" la sezione descrive una struttura, un flusso o una sequenza.
"none" la sezione è breve, o contiene già un blocco di codice.
Regole per le voci "photo" — il campo è query:
Scrivi una descrizione per la fotocamera: una scena che una fotocamera avrebbe
potuto catturare, da 12 a 25 parole, in inglese, coerente con il tono della
sezione. Indica cosa si vede nell'inquadratura, mai l'argomento. Niente testo,
loghi, marchi o persone famose; niente metafore invisibili. (Regole complete, con esempi e casi di errore:
pexafy.com/blog/illustrate-blog-articles-at-scale/)
NON scrivere il testo alt di una voce photo: l'immagine che ottenete è la
corrispondenza più vicina al brief, non la scena che avete descritto, quindi il suo alt va
scritto a partire dalla foto scelta. Le voci chart e diagram PORTANO invece un alt —
lì controllate esattamente cosa viene renderizzato.
Restituisci solo JSON:
{
"hero": { "kind": "photo", "query": "…", "orientation": "landscape" },
"sections": [
{ "h2": "…", "kind": "photo", "query": "…" },
{ "h2": "…", "kind": "chart", "title": "…", "unit": "ms",
"data": [ {"label": "…", "value": 0} ], "alt": "…" },
{ "h2": "…", "kind": "diagram", "spec": "flowchart LR; …", "alt": "…" }
]
}
La riga kind fa gran parte del lavoro, e l'istruzione data fa il resto:
una voce di tipo grafico può contenere solo numeri che compaiono già nella bozza, così il modello
trascrive invece di inventare. Ecco il router applicato a tre sezioni reali di un articolo per
sviluppatori:
| Sezione | kind | Cosa ha restituito il router |
|---|---|---|
| Hero — “Perché la nostra build notturna richiede 40 minuti” | photo |
“uno sviluppatore che lavora fino a tardi a una scrivania con due monitor e una tastiera meccanica in una stanza buia illuminata dagli schermi” |
| “Dove va davvero il tempo” | chart |
data copiati dal paragrafo: installazione 480 s, compilazione 1080 s, test 720 s, upload 120 s |
| “Come dividiamo il grafo” | diagram |
flowchart LR del grafo delle dipendenze dei job |
| “Cosa abbiamo cambiato, e cosa rifaremmo” | photo |
“due ingegneri in piedi davanti a una lavagna coperta di diagrammi che risolvono un problema insieme” |
Fase 3: le foto tornano con credito
Ogni voce kind: "photo" è una richiesta. Il brief hero qui sopra, eseguito contro l'API
pubblica, restituisce questo in 147 ms:
L'ultimo brief di sezione, una scena completamente diversa, in 144 ms:
Ciò che torna per ogni foto è la parte che rende possibile la fase 5 — non solo un file:
{
"photo_id": "019e1c7f-0063-759e-b498-33ce1714e6c9", // memorizzalo: niente ripetizioni
"urls": { "small": "…?w=400", "regular": "…?w=1080",
"large": "…?w=1920" },
"width": 3000, "height": 1688, // → niente layout shift
"blur_hash": "LJ8gjv9rVq-6OFxanNNFI7xco$Na", // → placeholder reale
"alt_description": "Person types on keyboard in front of dual monitors…",
"photographer_full_name": "Jakub Żerdzicki", // → ImageObject.creator
"source": "Unsplash", "license_type": "free",
"source_image_url": "https://unsplash.com/photos/…",
"attribution": { "html": "<span…>Photo by …</span>",
"plain": "Photo by Jakub Żerdzicki on Unsplash (…)" }
}
Fase 4: ciò che una fotografia non può dire
Due slot del piano non sono ricercabili, ed è esattamente qui che un secondo modello si guadagna il suo posto — non per disegnare un'immagine, ma per scrivere codice che la disegna. La distinzione conta: il codice è verificabile, deterministico e non può allucinare l'altezza di una barra.
Diagrammi: testo in ingresso, SVG in uscita
Mermaid renderizza diagrammi da una definizione in testo semplice,3 il che lo rende il bersaglio più sicuro per un modello: l'output è ispezionabile, confrontabile con diff in git, e viene renderizzato allo stesso modo ogni volta. Il router ha già restituito la spec.
# il campo "spec" di una voce diagram, scritto in build/graph.mmd
cat build/graph.mmd
flowchart LR
install["install deps · 480s"] --> compile["compile · 1080s"]
compile --> test["test suite · 720s"]
compile --> upload["upload artifacts · 120s"]
npx -y @mermaid-js/mermaid-cli -i build/graph.mmd -o static/img/graph.svg
# → un SVG che potete rivedere nella PR, non un'immagine di cui fidarvi
Grafici: solo numeri che l'articolo già contiene
Stesso principio, una protezione in più. Il router ha copiato i valori dalla bozza; il modello scrive il codice di plotting; il codice gira in una sandbox; l'assemblatore ricontrolla i valori renderizzati contro i numeri sorgente prima che il grafico possa avvicinarsi a una pagina.
import re, matplotlib
matplotlib.use("Agg") # headless: nessun display in CI
import matplotlib.pyplot as plt
def assert_in_draft(value, draft: str) -> None:
"""Un numero tracciato deve comparire nell'articolo COME NUMERO."""
# Il matching per sottostringa è la trappola qui: "120" è dentro "1200", e
# dentro "?w=1200" — un ingenuo `str(v) in draft` passa su qualsiasi cosa.
# Fai matching sui confini di parola, e accetta 1 234 / 1.234 / 1234.
body = re.sub(r"(?<=\d)[ ,](?=\d{3}\b)", "", draft) # rimuovi separatori
if not re.search(rf"(?<![\d.]){re.escape(str(value))}(?![\d.])", body):
raise ValueError(f"{value} is not stated in the article — refusing to plot")
def render_chart(entry: dict, draft: str, out: str) -> str:
labels = [d["label"] for d in entry["data"]]
values = [d["value"] for d in entry["data"]]
for v in values:
assert_in_draft(v, draft) # valore allucinato → nessun grafico
fig, ax = plt.subplots(figsize=(8, 4.5), dpi=160)
ax.barh(labels, values)
ax.set_xlabel(entry["unit"])
ax.set_title(entry["title"])
fig.tight_layout()
fig.savefig(out) # artefatto deterministico, verificabile
return out
La protezione è breve, ma scrivetela con cura: un controllo per sottostringa non funziona.
"120" in draft è vero per un articolo che contiene 1200 o
?w=1200, quindi la versione ingenua passa su tutto e non protegge nulla. Ancoratevi ai
confini delle cifre, normalizzate i separatori delle migliaia, e la classe di errore che fa smontare un articolo
tecnico nei commenti — un grafico che contraddice il proprio paragrafo — non può raggiungere la
produzione. Gli screenshot seguono lo stesso principio: un page.screenshot() scriptato
contro la vostra build reale è l'unica fonte di verità per la vostra UI, e resta vero mentre la UI
cambia.
Fase 5: l'assemblaggio è dove vive la SEO
Tutto ciò che è avvenuto finora ha prodotto file e campi. Questa fase li trasforma in markup — ed è utile essere precisi qui, perché tre comportamenti documentati si decidono in queste poche righe.
<!-- I byte provengono da un'altra origine: paga l'handshake in anticipo -->
<link rel="preconnect" href="https://images.unsplash.com" crossorigin>
<!-- Elemento LCP: mai lazy, sempre priorità alta -->
<figure>
<img src="{urls.regular}"
width="{width}" height="{height}" <!-- elimina il layout shift -->
alt="{alt}" <!-- scritto DOPO la scelta -->
fetchpriority="high" decoding="async"
style="background:{color_hex}"> <!-- colore dominante, 1 campo -->
<figcaption>{attribution.html}</figcaption>
</figure>
<!-- Immagini di sezione, sotto la piega: le impostazioni opposte -->
<img src="{urls.regular}" width="{width}" height="{height}"
alt="{alt}" loading="lazy" decoding="async">
Hotlink o re-hosting? Lo snippet sopra fa hotlink, che è la cosa più rapida da
pubblicare e il motivo del preconnect: un hero remoto costa una risoluzione DNS e un handshake
TLS sul percorso critico, e questo può erodere il guadagno appena ottenuto con
fetchpriority. Il re-hosting elimina del tutto l'origine di terze parti, vi permette di servire
AVIF/WebP ai vostri breakpoint, e sopravvive a un cambio di URL a monte — al costo di
storage, un passaggio di fetch nella pipeline e la vostra bolletta CDN. Qualunque scegliate,
color_hex vi dà un placeholder per un campo (un blur hash è più elegante, ma
va decodificato prima in un data URI — non è un colore CSS). Copiare un hero approssimativo
direttamente in background: è dove la maggior parte delle pipeline pubblica silenziosamente un riquadro grigio vuoto.
-
Non caricare mai l'hero in lazy loading. web.dev è inequivocabile: “Non caricare mai in lazy loading la tua
immagine LCP, poiché questo porterà sempre a un ritardo inutile nel caricamento delle risorse, e avrà un
impatto negativo sull'LCP”, e raccomanda
fetchpriority="high"sull'elemento probabilmente LCP — usato con parsimonia, su una sola immagine.4 Una pipeline che applicaloading="lazy"a ogni immagine, hero incluso, è la ferita autoinflitta più comune ai Core Web Vitals nella pubblicazione automatizzata. -
Emettete sempre
widtheheight— tornano nella risposta, quindi non c'è scusa; quella singola coppia di attributi è ciò che permette al browser di riservare lo spazio e impedisce al layout di saltare. Usateblur_hashcome placeholder mentre il file si carica. -
Scrivete l'alt dopo la scelta, mai prima. Questo è il punto sottile.
Il campo
altdel piano descrive la scena che avete chiesto; la foto che avete ottenuto è la corrispondenza più vicina, non quella scena. Pubblicare il testo del brief come alt è esattamente il fallimento di accessibilità che questa pipeline dovrebbe evitare — una descrizione di un'immagine che non è quella in pagina. Costruite l'alt a partire dall'alt_descriptiondella foto scelta, raffinato contro il paragrafo in cui si trova. La linea guida di Google è di “concentrarsi sulla creazione di contenuti utili e informativi che usano le parole chiave in modo appropriato e nel contesto del contenuto della pagina”, e avverte che riempire gli attributi alt di parole chiave “si traduce in un'esperienza utente negativa e può far sì che il vostro sito venga considerato spam”.1
La parte che quasi nessuno automatizza: i metadati di licenza
Google supporta i dati strutturati ImageObject per la licenza delle immagini. Richiede
contentUrl più almeno uno tra creator, creditText,
copyrightNotice o license, raccomanda acquireLicensePage,
e le immagini con informazioni di licenza diventano idonee al badge Licensable in Google
Images.5 Ognuno di questi campi è già nella risposta di ricerca
— quindi emetterlo è un template, non un progetto:
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "ImageObject",
"contentUrl": "{urls.large}", // obbligatorio
"creator": { "@type": "Person",
"name": "{photographer_full_name}" },
"creditText": "{photographer_full_name} on {source}",
"license": "{LICENSE_URL[source]}", // la pagina propria della libreria
"acquireLicensePage": "{source_image_url}" // la pagina della foto
}
</script>
# LICENSE_URL mappa il campo `source` alla licenza che governa davvero
# la foto — unsplash.com/license, pexels.com/license, pixabay.com/…
Un dettaglio che vale la pena curare: license dovrebbe puntare alla licenza che
governa quella foto — la pagina di licenza propria della libreria sorgente — non a una pagina riassuntiva sul
vostro dominio. Google la legge per decidere l'idoneità al badge, e un URL autoreferenziale è sia
più debole come segnale sia difficile da difendere come qualcosa di diverso da un link verso voi stessi. Tenete
il vostro riassunto della licenza come pagina interna per i lettori; mettete quello canonico nel markup.
E già che siete nell'assemblatore: date al file un nome breve e descrittivo invece di
IMG_0042.jpg, e aggiungete l'immagine a una sitemap — il formato di sitemap immagini di Google accetta
fino a 1.000 immagini per URL di pagina.6 Entrambe sono una riga ciascuna in una
pipeline e nessuna delle due viene mai fatta a mano.
Due pipeline da copiare
Stesse cinque fasi, due forme molto diverse — una per gli articoli che scrivete, una per la documentazione che deve corrispondere a un prodotto funzionante. Scegliete quella il cui modo di fallire riconoscete.
1 · Il blog per sviluppatori in CI — Markdown nel repository
Gli articoli vivono come Markdown, le immagini vengono committate accanto ad essi, e il tutto gira a ogni push. Deterministico, verificabile nella PR, nessuna dipendenza a runtime da alcuna API:
on: { pull_request: { paths: ["content/**.md"] } }
jobs:
illustrate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install -r requirements.txt
- run: python plan_to_pr.py $(git diff --name-only origin/main -- 'content/*.md')
env:
PEXAFY_API_KEY: ${{ secrets.PEXAFY_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- uses: peter-evans/create-pull-request@v6 # le immagini arrivano nella PR
with: { commit-message: "chore(content): illustrate" }
Un umano approva comunque la PR, ed è il punto: la pipeline propone, il revisore dispone, e il front-matter che ha scritto è confrontabile con diff.
La metà relativa alla ricerca è una singola GET — la richiesta, il suo
score_threshold e i campi che restituisce sono descritti riga per riga in
la guida al singolo
articolo, quindi qui viene importata anziché ristampata. Ciò che questo file aggiunge è tutto
ciò di cui un piano ha bisogno e una singola foto no: il ritentativo su un brief che non
ha restituito nulla, la rivendicazione su un photo_id in modo che nessuna pagina
condivida un'immagine con un'altra, e l'alt scritto a partire dalla foto ottenuta.
import frontmatter
from photo_search import search # una GET /search/photos; salta gli id in `used`
def find_photo(entry: dict, used: set) -> dict | None:
"""Cerca; se il brief era troppo specifico, allargalo una volta, poi rinuncia."""
orientation = entry.get("orientation", "landscape")
for query in (entry["query"], widen(entry["query"])):
photo = search(query, orientation, used)
if photo:
used.add(photo["photo_id"]) # riservala: niente ripetizioni nel sito
return photo
return None # decide il chiamante: salta lo slot, o fallisci
def widen(query: str) -> str:
"""Elimina l'ultima clausola — di solito quella troppo specifica."""
return query.rsplit(" in ", 1)[0] if " in " in query else query
def illustrate(path: str, plan: dict, used: set) -> None:
post = frontmatter.load(path)
hero = find_photo(plan["hero"], used)
if hero is None: # nessun hero è meglio di uno cattivo
raise SystemExit(f"{path}: no photo above threshold — rewrite the brief")
post["hero"] = { # tutto ciò di cui il template ha bisogno
"src": hero["urls"]["regular"], "w": hero["width"], "h": hero["height"],
# L'alt descrive la foto che ABBIAMO OTTENUTO, mai la scena che avevamo chiesto.
"alt": alt_for(hero, section=post.get("title", "")),
"bg": hero["color_hex"], "credit": hero["attribution"]["html"],
"creator": hero["photographer_full_name"], "id": hero["photo_id"],
"source": hero["source"],
"source_url": hero["source_image_url"], # → acquireLicensePage
}
open(path, "w").write(frontmatter.dumps(post))
def alt_for(photo: dict, section: str) -> str:
"""alt_description come base, ridotto a ~125 caratteri per gli screen reader.
Rimandalo al modello insieme al paragrafo se vuoi un risultato migliore."""
base = photo.get("alt_description") or photo.get("description", "")
return base[:125].rstrip(" ,;")
2 · Documentazione e changelog — prima gli screenshot, poi le foto
Invertite le impostazioni predefinite del router. Nella documentazione di prodotto il visual onesto è quasi sempre la vostra UI: uno script Playwright che apre la build reale, imposta un viewport fisso e cattura lo stato esatto che il paragrafo descrive. I diagrammi coprono le pagine di architettura, e le fotografie compaiono solo nelle pagine concettuali e landing — dove uno screenshot non direbbe nulla.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1440, "height": 900},
device_scale_factor=2) # nitidezza retina
page.goto("http://localhost:3000/dashboard")
page.get_by_role("button", name="New API key").click()
page.screenshot(path="static/img/docs/new-api-key.png")
browser.close()
# Gira nello stesso job CI della build della documentazione → lo screenshot non può mai
# descrivere una versione della UI che non esiste più.
Due varianti che vale la pena nominare ma non meritano una ricetta propria. La SEO programmatica
inverte il ciclo: con centinaia di pagine generate da un database non si cerca per pagina —
una richiesta restituisce fino a 100 foto, quindi si cerca per cluster di argomento e si assegna da un
pool, con un vincolo di unicità che fa la deduplicazione
(quell'architettura, per intero).
E le newsletter e le card social hanno bisogno della stessa foto in quattro ritagli: mantenete
il photo_id, richiedete la dimensione che vi serve da urls, e cercate di nuovo
solo quando un ritaglio fallisce davvero.
La stessa pipeline, come un unico agente
<<
Se un modello sta già scrivendo la bozza, la strada più breve è dargli direttamente lo strumento
di ricerca invece di far transitare JSON tra processi diversi. Pexafy gestisce un server
Model Context Protocol ospitato all'indirizzo mcp.pexafy.com/mcp.
La configurazione del connettore e gli strumenti che espone sono trattati altrove — la
configurazione desktop ed editor in
la guida per il singolo articolo,
la variante headless per un runner CI in
l'articolo sui contenuti su larga scala,
e come si presenta il più ampio mercato dei connettori — chi offre un server MCP per immagini, a
quali condizioni — in
lo studio
sull'infrastruttura di ricerca immagini per agenti AI.
Ciò che vale la pena mostrare qui è cosa succede a questa pipeline una volta che l'agente
dispone degli strumenti: smette di essere cinque fasi e diventa un'unica istruzione.
Tu Ecco la bozza. Costruisci il piano visivo, illustralo e apri una PR.
Grafici solo con numeri già presenti nel testo.
Agente → piano: hero=photo · §2=chart · §3=diagram · §4=photo
→ search_photos(q="a developer working late at a desk with two
monitors and a mechanical keyboard in a dark room…")
← 16 foto · 147 ms · scelta la #1, 3000×1688, con credito
→ mermaid-cli build/graph.mmd → static/img/graph.svg
→ chart.py §2 → 4 valori verificati contro la bozza ✓
→ search_photos(q="two engineers standing at a whiteboard…")
← 16 foto · 144 ms · scelta la #1
✓ 4 slot riempiti · 2 chiamate API · alt + credito + ImageObject scritti
⚠ §5 non ha restituito nulla sopra 0.5 — brief troppo astratto, riscritto come
"a person at a kitchen table checking figures on a laptop"
Quest'ultima riga è il motivo per cui vale la pena avere un agente in questo ciclo invece di uno script puro: la modalità di fallimento dell'illustrazione automatizzata è un brief scadente, e riscrivere un brief scadente è esattamente a cosa serve un modello linguistico. Mantenete le parti deterministiche — assegnazione, deduplicazione, la protezione numerica — nel codice.
Quanto costa un articolo illustrato
Tre slot foto per articolo significano tre richieste di ricerca, quindi il piano free (5,000 richieste/mese) copre 1,666 articoli al mese prima che si ponga qualsiasi questione di pagamento — e se raggruppate le ricerche per cluster di argomento invece che per articolo, quel tetto si sposta di un altro ordine di grandezza. La ripartizione piano per piano, e l'architettura di raggruppamento che la rende superflua, sono in l'articolo compagno sull'illustrare contenuti su larga scala.
Le due chiamate al modello per articolo — una per la bozza, una per il piano visivo — sono qualche migliaio di token e saranno la voce più economica della pipeline; i rendering Mermaid e matplotlib non costano nulla se non secondi di CI. Il numero che sorprende è quale voce non è economica: generare quattro immagini per articolo, quattrocento immagini al mese, più i tentativi che non hanno superato la selezione — e l'output continua a non portare né fotografo, né data, né URL sorgente.
Cosa questa pipeline non risolve
Una sezione onesta, perché il fallimento che previene è costoso. Un articolo ben illustrato è comunque un articolo: fotografia reale, grafici accurati e markup corretto migliorano una pagina che merita di esistere. Non fanno posizionare contenuti mediocri prodotti in massa. Le politiche antispam di Google nominano l'abuso di contenuti su larga scala — generare molte pagine principalmente per manipolare il ranking e offrire poco valore agli utenti, che l'automazione sia coinvolta o meno — e la qualità delle illustrazioni non è un fattore in quel giudizio.7
Quindi l'inquadramento che regge è questo: questa pipeline è un pavimento di qualità che controllate, applicato a pagine che hanno già un motivo per essere pubblicate. Dove paga in modo dimostrabile:
- Provenienza che un lettore può verificare. Una didascalia con un fotografo reale e un
URL sorgente è un'affermazione verificabile — e gli stessi campi alimentano il markup
ImageObjectche Google legge. - Accessibilità e Core Web Vitals. Alt testuale reale, dimensioni su ogni immagine, un hero mai caricato in lazy loading. Moltiplicate per ogni articolo che pubblicate e questa è la storia della qualità delle immagini del sito.
- Accuratezza dove è verificabile. Un grafico i cui numeri sono verificati contro l'articolo, uno screenshot generato dalla build in esecuzione, un diagramma verificabile come testo nella PR — tre visual che non possono divergere dalla verità senza che un test fallisca.
Sull'attribuzione, il punto specifico della pipeline è ristretto:
l'argomentazione a
favore del mostrare sempre il credito è esposta altrove, e ciò che un assemblatore
automatizzato aggiunge è che la stessa stringa attribution che stampa è il
creditText di cui i dati strutturati hanno bisogno. Un campo, due luoghi, emessi
nello stesso passaggio di template — motivo per cui una pipeline ha ancora meno scuse di un
essere umano per ometterlo.
Da dove iniziare
- Aggiungete il prompt router a qualunque cosa scriva già le vostre bozze, e stampate il JSON senza agire su di esso. Leggete dieci piani. Se i brief nominano argomenti invece di scene, correggete il prompt prima di scrivere qualsiasi integrazione.
- Collegate solo gli slot foto. Un
GET /search/photosper brief, e memorizzatephoto_idfin dal primo giorno — la deduplicazione applicata retroattivamente su 3.000 pagine in produzione è una migrazione, non una colonna. - Emettete width, height e alt nello stesso commit. È il lavoro sui Core Web Vitals più economico che farete mai.
- Poi aggiungete la fase 4, diagrammi prima dei grafici — Mermaid è testo, quindi è quello con nessuna modalità di fallimento oltre un errore di sintassi.
- Aggiungete la protezione numerica prima che il primo grafico raggiunga un lettore, non dopo.
Riferimenti e note
1 Google Search Central, Image SEO best practices: il testo alt
è “l'attributo più importante quando si tratta di fornire metadati per un'immagine”; la linea guida
è creare “contenuti utili e informativi che usano le parole chiave in modo appropriato e nel contesto
del contenuto della pagina”, evitare attributi alt riempiti di parole chiave, usare elementi HTML
<img> invece di immagini CSS, e dare ai file nomi brevi ma descrittivi.
2 AI Act dell'UE, Articolo 50 — obblighi di trasparenza applicabili dal 2 agosto 2026: i fornitori di sistemi che generano immagini, audio, video o testo sintetici devono contrassegnare gli output in un formato leggibile da macchina e renderli rilevabili come artificialmente generati. Vincola i fornitori e i deployer di IA; non è una regola su quali immagini un sito web possa pubblicare.
3 Mermaid renderizza diagrammi e grafici da definizioni testuali ispirate al Markdown, il che rende l'output verificabile e deterministico.
4 web.dev, Optimize Largest Contentful Paint: “È una buona
idea impostare fetchpriority="high" su un elemento <img> se pensate
che sia probabile che sia l'elemento LCP della vostra pagina”, usato con parsimonia; e “Non caricate mai in lazy loading la vostra
immagine LCP, poiché ciò porterà sempre a un ritardo inutile nel caricamento delle risorse, e avrà un
impatto negativo sull'LCP.”
5 Google Search Central, Image metadata (structured data):
ImageObject richiede contentUrl più almeno uno tra
creator, creditText, copyrightNotice o
license; acquireLicensePage è raccomandato, e le immagini con
informazioni di licenza possono diventare idonee al badge Licensable in Google Images.
6 Google Search Central, Image sitemaps: le sitemap immagini informano Google sulle immagini di un sito, incluse quelle trovate tramite JavaScript, e accettano fino a 1.000 immagini per URL di pagina.
7 Politiche antispam di Google Search — abuso di contenuti su larga scala: generare molte pagine principalmente per manipolare il ranking e offrire poco valore agli utenti, che siano create tramite automazione, sforzo umano o una combinazione dei due.
Fonti verificate il 17 agosto 2026: Image SEO best practices · Image metadata · Image sitemaps · Optimize LCP · Search spam policies · AI Act Article 50 · Mermaid · Pexafy API & MCP docs. I tempi di ricerca (147 ms, 144 ms) e ogni foto mostrata sono risposte API reali catturate lo stesso giorno.
Domande frequenti
Come illustro automaticamente gli articoli scritti da un LLM?
GET /api/v1/search/photos, circa 150 ms). Le voci grafico e diagramma vanno a un modello che scrive codice di plotting o Mermaid, renderizzato in modo deterministico. Non inserire mai il titolo dell'articolo in una ricerca di immagini: i titoli sono astratti e nessuna fotografia li rappresenta.Un sito di documentazione dovrebbe usare screenshot o foto stock?
Come impedisco a una pipeline AI di inserire numeri sbagliati in un grafico?
data della voce grafico. Poi verificalo nel codice prima del rendering — per ogni valore, controlla che la sua stringa compaia nell'articolo e solleva un errore altrimenti. Nove righe, e un grafico che contraddice il proprio paragrafo non potrà mai raggiungere un lettore.Quale markup delle immagini dovrebbe emettere una pipeline automatizzata per la SEO?
alt descrittivo scritto nel contesto del paragrafo — Google definisce il testo alternativo il metadato più importante dell'immagine e mette in guardia contro il keyword stuffing. width e height su ogni immagine, con fetchpriority="high" sulla hero image e mai loading="lazy" su di essa, perché l'immagine LCP non deve essere caricata in modo lazy. E dati strutturati ImageObject con contentUrl, creator, creditText e license, che è ciò che rende un'immagine idonea al badge Licensable in Google Immagini.Illustrare articoli scritti dall'AI aiuta il posizionamento?
Come eseguo lo step di illustrazione in CI senza perdere il controllo editoriale?
photo_id, la verifica che ogni numero rappresentato nel grafico compaia nell'articolo, e un fallimento netto quando nessuna foto supera la soglia di punteggio. Nessuna immagine di copertina è meglio di una sbagliata.