O Texto É a Parte Fácil: o Pipeline Que Ilustra Artigos Escritos por IA
Gerar o texto já está resolvido. Ilustrá-lo, não. O pipeline de ponta a ponta — prompt roteador, busca de fotos, diagramas Mermaid, gráficos verificados, e o alt text, crédito e marcação ImageObject que transformam tudo em SEO.
Você é um desenvolvedor com uma meta de conteúdo: dezenas de artigos por mês, talvez centenas. O modelo de escrita cuida do rascunho, do esboço, da meta description, dos links internos. Depois o pipeline esbarra na única etapa que não tem modelo próprio — as imagens — e para. Não porque conseguir imagens seja difícil, mas porque nada na stack sabe qual imagem, de onde, com qual licença, descrita como.
Este artigo é essa etapa que falta, conectada de ponta a ponta: que tipo de imagem produzir para que tipo de seção, o prompt que transforma um rascunho pronto em briefings de busca, a chamada de API que retorna as fotos, o segundo modelo que desenha o que uma fotografia não consegue mostrar, e o montador que emite marcação que o Google consegue de fato ler. Dois fluxos de trabalho completos no final, prontos para copiar.
O texto está resolvido. A ilustração é onde tudo trava.
Faça a auditoria honesta de um pipeline de artigos automatizado. Esboço: resolvido. Rascunho: resolvido. Título, meta description, schema, links internos, tradução: resolvidos, todos pelo mesmo modelo, todos em texto. Depois:
| Etapa do pipeline | Status | O que realmente trava |
|---|---|---|
| Esboço e rascunho | Resolvido | Uma chamada de modelo, um prompt |
| Títulos, meta, schema, links | Resolvido | Texto entra, texto sai |
| Imagem de destaque (hero) | Travado | Precisa de um arquivo real, uma licença, dimensões e alt text — nada disso um modelo de texto consegue produzir |
| Imagens de seção | Travado | De três a cinco por artigo, cada uma diferente, nenhuma se repetindo pelo site |
| Gráficos e diagramas | Travado | Precisam ser precisos — a única imagem que uma busca não consegue retornar e um gerador não pode inventar |
A falha não é estética, é estrutural: o artigo vai ao ar com um placeholder de banco de imagens,
ou com a mesma foto dos últimos doze, ou com uma imagem gerada cuja mão de seis dedos é a
primeira coisa que o leitor vê. E a imagem não é decoração — a própria documentação de imagens do
Google é direta: o alt text “é o atributo mais importante quando se trata de fornecer metadados
para uma imagem”, e a orientação é usar elementos <img> reais com
alt descritivo em vez de fundos CSS, para que a imagem possa ser encontrada e
compreendida de fato.1
Quatro tipos de imagem, quatro tipos de modelo
O maior erro de design é tratar "a imagem" como um único problema com um único fornecedor. São quatro, e o roteador entre eles é uma linha de prompt, não um serviço:
| A seção precisa de… | Produza com | Por que não os outros |
|---|---|---|
| Uma cena do mundo realhero, situações humanas, lugares, objetos, gestos | Busca semântica de fotos (Pexafy) | Um gerador inventa os detalhes; um gráfico não tem nada para plotar |
| Números que você realmente tembenchmarks, preços, resultados de pesquisas, latência | Um modelo escrevendo código de plotagem, executado em sandbox | Não dá para confiar um valor a um modelo de imagem; uma foto não carrega dados |
| Uma estrutura ou um fluxoarquitetura, sequência, máquina de estados | Um modelo escrevendo Mermaid / Graphviz, renderizado de forma determinística | Bancos de fotos gratuitos não têm o diagrama do seu sistema |
| Seu produto na teladocumentação, changelog, tutoriais | Uma captura de tela automatizada por navegador (Playwright) | Nada mais consegue mostrar uma interface que só existe no seu build |
E a ilustração gerada? Ela mantém um lugar legítimo: a cena que não pode ser fotografada e não é dado — um mecanismo abstrato, um produto que ainda não existe, um estilo de ilustração próprio da casa. (O argumento completo em favor da fotografia real em vez de geração — velocidade em volume, precisão, o problema da uniformidade — está desenvolvido aqui.) O preço segue essa direção: desde 2 de agosto de 2026, o Artigo 50 do EU AI Act exige que provedores de sistemas generativos marquem saídas sintéticas em formato legível por máquina.2 Trata-se de uma obrigação para provedores e implementadores de IA, não de uma regra sobre o que um blog pode publicar — mas é o motivo pelo qual a proveniência da imagem no topo do seu artigo está cada vez mais sujeita à verificação do leitor, em vez de depender de confiança.
O pipeline, de ponta a ponta
Cinco etapas. Só a etapa 3 toca uma API de imagens, e só a etapa 4 é opcional:
┌─ 1. ESCREVER ─────────────────────────────────────────────────┐
tópico ──▶ LLM ──▶ draft.md (seções h2, front-matter)
└───────────────────────────────┬───────────────────────────────┘
┌─ 2. BRIEFING ─────────────────┴───────────────────────────────┐
draft.md ──▶ LLM ──▶ { hero: {...}, sections: [ {...} ] }
um objeto JSON: por slot, um kind +
um briefing de câmera ou uma especificação de dado/diagrama
└──────────┬──────────────────────────────────┬─────────────────┘
│ kind = "photo" │ kind = "chart" | "diagram"
▼ ▼
┌─ 3. BUSCAR ───────────────┐ ┌─ 4. DESENHAR (opcional) ─────┐
GET /search/photos LLM ──▶ mermaid | código de plotagem
← foto com crédito + ──▶ sandbox ──▶ .svg / .png
w/h, blur_hash, alt, (renderização determinística,
licença, URL de origem sem números inventados)
└──────────┬────────────────┘ └──────────────┬───────────────┘
└───────────────┬──────────────────┘
┌─ 5. MONTAR ───────────────┴───────────────────────────────────┐
<img> com width/height + fetchpriority | loading
alt escrito para um humano · crédito visível · ImageObject JSON-LD
photo_id armazenado para que nenhuma página compartilhe um hero
└───────────────────────────────────────────────────────────────┘
Duas propriedades importam mais do que o diagrama. A etapa 2 é um roteador: ela decide, por slot, qual produtor entra em ação, então você nunca pede um gráfico de barras a um banco de fotos. E é na etapa 5 que o SEO acontece — tudo o que o Google documenta sobre imagens (alt descritivo, metadados de licença, um hero seguro para o LCP) é emitido aqui, a partir de campos que a resposta de busca já trazia.
Etapa 2: transformar o rascunho em briefings visuais
Uma chamada de modelo sobre o rascunho finalizado, e o que ela retorna é um plano, não uma consulta. Como escrever um único brief fotográfico — por que o título do artigo é a pior entrada possível, como é um brief de câmera de 12 a 25 palavras e as formas como ele falha — está detalhado por completo em no guia de ilustração de um único artigo, e não é repetido aqui. O que um pipeline acrescenta é o roteamento: a mesma chamada precisa decidir, slot por slot, qual produtor executa — e um item de gráfico carrega dados onde um item de foto carrega uma cena.
# prompt de sistema — executado uma vez por rascunho finalizado
Você é o diretor de arte de uma publicação técnica. Leia o artigo e
retorne o plano visual: uma entrada para o hero, uma por seção H2.
Para cada entrada, escolha exatamente um kind:
"photo" uma cena real: alguém fazendo algo em algum lugar, um lugar,
um objeto, um gesto. O padrão para heroes.
"chart" a seção apresenta números que ESTÃO no artigo. Nunca
invente valores: copie-os em data, textualmente.
"diagram" a seção descreve uma estrutura, um fluxo ou uma sequência.
"none" a seção é curta, ou já traz um bloco de código.
Regras para entradas "photo" — o campo é query:
Escreva um briefing de câmera: uma cena que uma câmera poderia ter
capturado, de 12 a 25 palavras, em inglês, combinando com o clima da
seção. Diga o que está no quadro, nunca o tema. Sem texto, logos, marcas
ou pessoas famosas; sem metáforas invisíveis. (Regras completas, com exemplos e casos de falha:
pexafy.com/blog/illustrate-blog-articles-at-scale/)
NÃO escreva o alt text de uma entrada de foto: a imagem que você recebe é a
correspondência mais próxima do briefing, não a cena que você descreveu, então o alt
tem que ser escrito a partir da foto escolhida. Entradas de chart e diagram TÊM
alt — nesses casos você controla exatamente o que é renderizado.
Retorne apenas 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": "…" }
]
}
A linha kind faz a maior parte do trabalho, e a instrução data faz o
resto: um item de gráfico só pode carregar números que já aparecem no rascunho, de modo que o
modelo transcreve em vez de inventar. Aqui está o roteador aplicado a três seções reais de um
artigo técnico:
| Seção | kind | O que o roteador retornou |
|---|---|---|
| Hero — "Por que nosso build noturno leva 40 minutos" | photo |
"um desenvolvedor trabalhando até tarde numa mesa com dois monitores e um teclado mecânico num quarto escuro iluminado pelas telas" |
| "Para onde o tempo realmente vai" | chart |
data copiado do parágrafo: instalação 480 s, compilação 1080 s, testes 720 s, upload 120 s |
| "Como dividimos o grafo" | diagram |
flowchart LR do grafo de dependências dos jobs |
| "O que mudamos, e o que faríamos de novo" | photo |
"dois engenheiros diante de um quadro branco cheio de diagramas resolvendo um problema juntos" |
Etapa 3: as fotos voltam com crédito
Cada entrada kind: "photo" é uma requisição. O briefing do hero acima, executado
contra a API pública, retorna isto em 147 ms:
O último briefing de seção, uma cena completamente diferente, em 144 ms:
O que volta por foto é a parte que torna a etapa 5 possível — não apenas um arquivo:
{
"photo_id": "019e1c7f-0063-759e-b498-33ce1714e6c9", // armazene: sem repetições
"urls": { "small": "…?w=400", "regular": "…?w=1080",
"large": "…?w=1920" },
"width": 3000, "height": 1688, // → sem deslocamento de layout
"blur_hash": "LJ8gjv9rVq-6OFxanNNFI7xco$Na", // → placeholder real
"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 (…)" }
}
Etapa 4: o que uma fotografia não consegue dizer
Dois slots do plano não são pesquisáveis, e é exatamente aí que um segundo modelo ganha seu lugar — não para desenhar uma imagem, mas para escrever código que a desenha. A distinção importa: código é revisável, determinístico e não consegue alucinar a altura de uma barra.
Diagramas: texto entra, SVG sai
O Mermaid renderiza diagramas a partir de uma definição em texto simples,3 o que o torna o alvo mais seguro para um modelo: a saída é inspecionável, comparável no git e renderiza sempre da mesma forma. O roteador já retornou a especificação.
# o campo "spec" de uma entrada de diagrama, escrito em 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
# → um SVG que você pode revisar no PR, não uma imagem que precisa confiar
Gráficos: apenas números que o artigo já contém
Mesmo princípio, uma proteção extra. O roteador copiou os valores do rascunho; o modelo escreve o código de plotagem; o código roda em sandbox; o montador reverifica os valores renderizados contra os números de origem antes que o gráfico chegue perto de uma página.
import re, matplotlib
matplotlib.use("Agg") # headless: sem display no CI
import matplotlib.pyplot as plt
def assert_in_draft(value, draft: str) -> None:
"""Um número plotado precisa aparecer no artigo COMO NÚMERO."""
# A busca de substring é a armadilha aqui: "120" está dentro de "1200", e
# dentro de "?w=1200" — um `str(v) in draft` ingênuo passa em qualquer coisa.
# Combine em limites de palavra e aceite 1 234 / 1,234 / 1234.
body = re.sub(r"(?<=\d)[ ,](?=\d{3}\b)", "", draft) # remove separadores
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) # valor alucinado → sem gráfico
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) # artefato determinístico e revisável
return out
A proteção é curta, mas escreva-a com cuidado: uma verificação de substring não
funciona. "120" in draft é verdadeiro para um artigo que contém
1200 ou ?w=1200, então a versão ingênua passa em tudo e não protege
nada. Ancore em limites de dígitos, normalize separadores de milhar, e a classe de erro que faz
um artigo técnico ser destroçado nos comentários — um gráfico contradizendo o próprio parágrafo —
não consegue chegar à produção. Capturas de tela seguem o mesmo princípio: um
page.screenshot() automatizado contra seu build real é a única fonte confiável para
a sua própria interface, e continua verdadeiro conforme a interface muda.
Etapa 5: é na montagem que o SEO acontece
Tudo até aqui produziu arquivos e campos. Esta etapa os transforma em marcação — e vale a pena ser preciso aqui, porque três comportamentos documentados são decididos nestas poucas linhas.
<!-- Os bytes vêm de outra origem: pague o handshake antecipadamente -->
<link rel="preconnect" href="https://images.unsplash.com" crossorigin>
<!-- Elemento LCP: nunca lazy, sempre alta prioridade -->
<figure>
<img src="{urls.regular}"
width="{width}" height="{height}" <!-- elimina o deslocamento de layout -->
alt="{alt}" <!-- escrito DEPOIS da escolha -->
fetchpriority="high" decoding="async"
style="background:{color_hex}"> <!-- cor dominante, 1 campo -->
<figcaption>{attribution.html}</figcaption>
</figure>
<!-- Imagens de seção, abaixo da dobra: as configurações opostas -->
<img src="{urls.regular}" width="{width}" height="{height}"
alt="{alt}" loading="lazy" decoding="async">
Hotlink ou re-hospedagem? O trecho acima usa hotlink, que é a forma mais rápida
de publicar e a razão do preconnect: um hero remoto custa uma consulta DNS e um
handshake TLS no caminho crítico, e isso pode consumir o ganho que você acabou de comprar com o
fetchpriority. Re-hospedar remove totalmente a origem de terceiros, permite servir
AVIF/WebP nos seus próprios breakpoints, e resiste a uma URL de origem que muda — ao custo de
armazenamento, uma etapa de fetch no pipeline e a sua própria conta de CDN. Seja qual for a
escolha, color_hex oferece um placeholder para um campo (um blur hash é mais
bonito, mas precisa ser decodificado para um data URI primeiro — não é uma cor CSS). Colar um
hero bruto direto em background: é onde a maioria dos pipelines silenciosamente
entrega uma caixa cinza vazia.
-
Nunca faça lazy-load do hero. O web.dev é inequívoco: "Nunca faça lazy-load da
sua imagem LCP, pois isso sempre levará a um atraso desnecessário no carregamento de recursos e
terá um impacto negativo no LCP", e recomenda
fetchpriority="high"no elemento provável de ser o LCP — usado com moderação, em uma imagem.4 Um pipeline que carimbaloading="lazy"em todas as imagens, hero incluído, é a ferida autoinfligida mais comum de Core Web Vitals na publicação automatizada. -
Sempre emita
widtheheight— eles voltam na resposta, então não há desculpa; esse único par de atributos é o que permite ao navegador reservar o espaço e evita que o layout salte. Useblur_hashcomo placeholder enquanto o arquivo carrega. -
Escreva o alt depois da escolha, nunca antes. Este é o ponto sutil.
O campo
altdo plano descreve a cena que você pediu; a foto que você recebeu é a correspondência mais próxima, não essa cena. Publicar o texto do briefing como alt é exatamente a falha de acessibilidade que este pipeline deveria evitar — uma descrição de uma imagem que não está na página. Construa o alt a partir doalt_descriptionda foto escolhida, refinado em relação ao parágrafo em que ela está. A orientação do Google é "focar em criar conteúdo útil e rico em informações que use palavras-chave de forma adequada e dentro do contexto do conteúdo da página", e alerta que encher atributos alt de palavras-chave "resulta numa experiência de usuário negativa e pode fazer seu site ser visto como spam".1
A parte que quase ninguém automatiza: metadados de licença
O Google suporta dados estruturados ImageObject para licenciamento de imagens.
Exige contentUrl mais pelo menos um de creator, creditText,
copyrightNotice ou license, recomenda acquireLicensePage, e
imagens com informação de licença ficam elegíveis para o selo Licensable no Google
Imagens.5 Cada um desses campos já está na resposta de busca — então
emiti-lo é um template, não um projeto:
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "ImageObject",
"contentUrl": "{urls.large}", // obrigatório
"creator": { "@type": "Person",
"name": "{photographer_full_name}" },
"creditText": "{photographer_full_name} on {source}",
"license": "{LICENSE_URL[source]}", // a página própria do banco de imagens
"acquireLicensePage": "{source_image_url}" // a página da foto
}
</script>
# LICENSE_URL mapeia o campo `source` para a licença que de fato rege
# a foto — unsplash.com/license, pexels.com/license, pixabay.com/…
Um detalhe que vale acertar: license deve apontar para a licença que
rege aquela foto — a própria página de licença do banco de imagens de origem — não para
uma página resumo no seu domínio. O Google lê isso para decidir a elegibilidade do selo, e uma
URL autorreferencial é ao mesmo tempo um sinal mais fraco e difícil de defender como algo além de
um link de volta para você mesmo. Mantenha o seu próprio resumo de licença como uma página
interna para leitores; coloque a canônica na marcação.
E já que está no montador: dê ao arquivo um nome curto e descritivo em vez de
IMG_0042.jpg, e adicione a imagem a um sitemap — o formato de sitemap de imagens do
Google aceita até 1.000 imagens por URL de página.6 Ambos são uma
linha cada num pipeline e nenhum dos dois nunca é feito à mão.
Dois pipelines para copiar
As mesmas cinco etapas, dois formatos bem diferentes — um para artigos que você escreve, outro para documentação que precisa acompanhar um produto em produção. Escolha o que combina com o modo de falha que você reconhece.
1 · O blog de desenvolvedores em CI — Markdown no repositório
Os artigos vivem como Markdown, as imagens são commitadas junto a eles, e tudo roda no push. Determinístico, revisável no PR, sem dependência de runtime em nenhuma 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 # as imagens aparecem no PR
with: { commit-message: "chore(content): illustrate" }
Um humano ainda aprova o PR, e é esse o ponto: o pipeline propõe, o revisor dispõe, e o front-matter que ele escreveu é comparável (diffable).
A metade de busca é um único GET — a requisição, seu score_threshold e
os campos que ela retorna estão descritos linha por linha em
no guia de um único
artigo, por isso é importado aqui em vez de reimpresso. O que este arquivo acrescenta é tudo
aquilo de que um plano precisa e uma única foto não: a nova tentativa sobre um brief
que não retornou nada, a reserva de um photo_id para que nenhuma página compartilhe
uma imagem, e o alt escrito a partir da foto retornada.
import frontmatter
from photo_search import search # um único GET /search/photos; ignora ids em `used`
def find_photo(entry: dict, used: set) -> dict | None:
"""Busca; se o briefing era específico demais, amplia uma vez, depois desiste."""
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"]) # reserva: sem repetições no site inteiro
return photo
return None # quem chama decide: pular o slot ou falhar
def widen(query: str) -> str:
"""Remove a última cláusula — geralmente a específica demais."""
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: # nenhum hero é melhor que um ruim
raise SystemExit(f"{path}: no photo above threshold — rewrite the brief")
post["hero"] = { # tudo que o template precisa
"src": hero["urls"]["regular"], "w": hero["width"], "h": hero["height"],
# O alt descreve a foto que RECEBEMOS, nunca a cena que pedimos.
"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 como base, cortado para ~125 caracteres para leitores de tela.
Reenvie pelo modelo com o parágrafo se quiser um resultado melhor."""
base = photo.get("alt_description") or photo.get("description", "")
return base[:125].rstrip(" ,;")
2 · Documentação e changelog — capturas de tela primeiro, fotos por último
Inverta os padrões do roteador. Em documentação de produto, a imagem honesta é quase sempre a sua própria interface: um script Playwright que abre o build real, define um viewport fixo e captura exatamente o estado que o parágrafo descreve. Diagramas cobrem as páginas de arquitetura, e fotografias aparecem apenas nas páginas conceituais e de landing — onde uma captura de tela não diria nada.
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) # nitidez 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()
# Roda no mesmo job de CI que o build da documentação → a captura de tela nunca pode
# descrever uma versão da interface que já não existe mais.
Duas variantes que vale mencionar mas não valem sua própria receita. SEO
programático inverte o loop: com centenas de páginas geradas a partir de um banco de
dados, você não busca por página — uma requisição retorna até 100 fotos, então você busca por
cluster de tópico e atribui a partir de um pool, com uma restrição de unicidade fazendo a
deduplicação
(essa arquitetura, em detalhes).
E newsletters e cards de redes sociais precisam da mesma foto em quatro
recortes: mantenha o photo_id, peça o tamanho que precisa a partir de
urls, e só busque de novo quando um recorte realmente falhar.
O mesmo pipeline, como um único agente
Se um modelo já está escrevendo o rascunho, o caminho mais curto é dar a ele a ferramenta de busca
diretamente, em vez de transportar JSON entre processos. A Pexafy opera um servidor hospedado de
Model Context Protocol em mcp.pexafy.com/mcp. A configuração do
conector e as ferramentas que ele expõe são tratadas em outros lugares — a configuração desktop
e de editor em
o guia de artigo único,
a variante headless para um executor de CI em
o artigo sobre conteúdo em escala,
e como é o mercado mais amplo de conectores — quem oferece um servidor de imagens MCP, em que
condições — em o
estudo sobre infraestrutura de busca de imagens para agentes de IA.
O que vale mostrar aqui é o que acontece com este pipeline quando o agente detém as
ferramentas: ele deixa de ser cinco etapas e passa a ser uma única instrução.
Você Aqui está o rascunho. Monte o plano visual, ilustre-o e abra um PR.
Gráficos apenas com números que já estão no texto.
Agente → plano: 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 fotos · 147 ms · escolhida #1, 3000×1688, com crédito
→ mermaid-cli build/graph.mmd → static/img/graph.svg
→ chart.py §2 → 4 valores verificados contra o rascunho ✓
→ search_photos(q="two engineers standing at a whiteboard…")
← 16 fotos · 144 ms · escolhida #1
✓ 4 slots preenchidos · 2 chamadas de API · alt + crédito + ImageObject escritos
⚠ §5 não retornou nada acima de 0.5 — briefing abstrato demais, reescrito como
"a person at a kitchen table checking figures on a laptop"
Essa última linha é o motivo de valer a pena ter um agente nesse loop em vez de um script puro: o modo de falha da ilustração automatizada é um briefing ruim, e reescrever um briefing ruim é exatamente para isso que serve um modelo de linguagem. Mantenha as partes determinísticas — atribuição, deduplicação, a proteção numérica — em código.
Quanto custa um artigo ilustrado
Três slots de foto por artigo significam três requisições de busca, então o plano gratuito (5,000 requisições/mês) cobre 1,666 artigos por mês antes de qualquer questão de pagamento surgir — e se você agrupar as buscas por cluster de tópico em vez de por artigo, esse teto sobe outra ordem de grandeza. O detalhamento plano a plano, e a arquitetura de agrupamento que torna isso irrelevante, estão em o artigo complementar sobre ilustrar conteúdo em escala.
As duas chamadas de modelo por artigo — uma para o rascunho, outra para o plano visual — são alguns milhares de tokens e serão a linha mais barata do pipeline; as renderizações do Mermaid e do matplotlib não custam nada além de segundos de CI. O número que surpreende as pessoas é qual linha não é barata: gerar quatro imagens por artigo, quatrocentas imagens por mês, mais as tentativas que não vingaram — e o resultado ainda não traz fotógrafo, data nem URL de origem.
O que este pipeline não resolve
Uma seção honesta, porque a falha que ela evita é cara. Um artigo bem ilustrado ainda é um artigo: fotografia real, gráficos precisos e marcação correta melhoram uma página que merece existir. Eles não fazem conteúdo raso e produzido em massa ranquear. As políticas de spam do Google nomeiam o abuso de conteúdo em escala — gerar muitas páginas principalmente para manipular rankings e oferecer pouco valor aos usuários, independentemente de haver automação envolvida ou não — e a qualidade das ilustrações não é um fator nesse julgamento.7
Então o enquadramento que se sustenta é este: este pipeline é um piso de qualidade que você controla, aplicado a páginas que já têm um motivo para serem publicadas. Onde ele demonstravelmente compensa:
- Proveniência que o leitor pode verificar. Uma linha de crédito com um
fotógrafo real e uma URL de origem é uma afirmação que pode ser checada — e os mesmos campos
alimentam a marcação
ImageObjectque o Google lê. - Acessibilidade e Core Web Vitals. Alt text real, dimensões em todas as imagens, um hero que nunca faz lazy-load. Multiplique por todos os artigos que você publica e essa é a história de qualidade de imagem do site.
- Precisão onde é verificável. Um gráfico cujos números são conferidos contra o artigo, uma captura de tela gerada a partir do build em produção, um diagrama revisável como texto no PR — três imagens que não conseguem se desviar da verdade sem que um teste falhe.
Quanto à atribuição, o ponto específico deste pipeline é restrito:
o argumento em favor
de sempre exibir o crédito é desenvolvido em outro lugar, e o que um montador automatizado
acrescenta é que a mesma string attribution que ele imprime é o creditText
que os dados estruturados exigem. Um campo, dois lugares, emitidos na mesma passagem de template
— motivo pelo qual um pipeline tem ainda menos desculpa do que um humano para deixá-lo de fora.
Por onde começar
- Adicione o prompt roteador a qualquer coisa que já escreva seus rascunhos, e imprima o JSON sem agir sobre ele. Leia dez planos. Se os briefings nomeiam tópicos em vez de cenas, conserte o prompt antes de escrever qualquer integração.
- Conecte apenas os slots de foto. Um
GET /search/photospor briefing, e armazenephoto_iddesde o primeiro dia — deduplicação retroativa em 3.000 páginas em produção é uma migração, não uma coluna. - Emita width, height e alt no mesmo commit. É o trabalho de Core Web Vitals mais barato que você vai fazer.
- Depois adicione a etapa 4, diagramas antes de gráficos — Mermaid é texto, então é o único sem modo de falha além de um erro de sintaxe.
- Adicione a proteção numérica antes que o primeiro gráfico chegue a um leitor, não depois.
Referências e notas de rodapé
1 Google Search Central, Image SEO best practices: o alt
text é "o atributo mais importante quando se trata de fornecer metadados para uma imagem"; a
orientação é criar "conteúdo útil, rico em informações, que use palavras-chave de forma
adequada e dentro do contexto do conteúdo da página", evitar atributos alt lotados de
palavras-chave, usar elementos HTML <img> em vez de imagens CSS, e dar aos
arquivos nomes curtos porém descritivos.
2 AI Act da UE, Artigo 50 — obrigações de transparência aplicáveis a partir de 2 de agosto de 2026: provedores de sistemas que geram imagens, áudio, vídeo ou texto sintéticos devem marcar as saídas em formato legível por máquina e torná-las detectáveis como geradas artificialmente. Vincula provedores e implantadores de IA; não é uma regra sobre quais imagens um site pode publicar.
3 O Mermaid renderiza diagramas e gráficos a partir de definições em texto inspiradas em Markdown, o que torna a saída revisável e determinística.
4 web.dev, Optimize Largest Contentful Paint: "É uma boa
ideia definir fetchpriority="high" num elemento <img> se você
acha provável que ele seja o elemento LCP da sua página", usado com moderação; e "Nunca faça
lazy-load da sua imagem LCP, pois isso sempre levará a um atraso desnecessário no carregamento
de recursos e terá um impacto negativo no LCP."
5 Google Search Central, Image metadata (structured data):
ImageObject exige contentUrl mais pelo menos um de
creator, creditText, copyrightNotice ou
license; acquireLicensePage é recomendado, e imagens com informação de
licença podem ficar elegíveis para o selo Licensable no Google Imagens.
6 Google Search Central, Image sitemaps: sitemaps de imagens informam o Google sobre imagens num site, incluindo as encontradas via JavaScript, e aceitam até 1.000 imagens por URL de página.
7 Políticas de spam da Busca Google — scaled content abuse: gerar muitas páginas principalmente para manipular rankings e oferecer pouco valor aos usuários, seja criado por automação, esforço humano ou uma combinação dos dois.
Fontes verificadas em 17 de agosto de 2026: Image SEO best practices · Image metadata · Image sitemaps · Optimize LCP · Search spam policies · AI Act Article 50 · Mermaid · Pexafy API & MCP docs. Os tempos de busca (147 ms, 144 ms) e todas as fotos exibidas são respostas reais da API capturadas no mesmo dia.
Perguntas frequentes
Como ilustrar automaticamente artigos escritos por uma LLM?
GET /api/v1/search/photos, cerca de 150 ms). As entradas de gráfico e diagrama vão para um modelo que escreve código de plotagem ou Mermaid, renderizado de forma determinística. Nunca alimente o título do artigo em uma busca de imagens: títulos são abstratos e nenhuma fotografia os representa.Um site de documentação deve usar capturas de tela ou fotos de banco de imagens?
Como impedir que um pipeline de IA coloque números errados em um gráfico?
data da entrada do gráfico. Em seguida, valide isso em código antes da renderização — para cada valor, verifique se sua string aparece no artigo e gere um erro caso contrário. Nove linhas, e um gráfico que contradiz seu próprio parágrafo nunca chega a um leitor.Qual marcação de imagem um pipeline automatizado deve emitir para SEO?
alt descritivo escrito no contexto do parágrafo — o Google chama o alt text de o metadado mais importante de uma imagem e alerta contra o keyword stuffing. width e height em toda imagem, com fetchpriority="high" na imagem principal e nunca loading="lazy" nela, porque a imagem LCP não deve ter carregamento lento. E dados estruturados ImageObject com contentUrl, creator, creditText e license, que é o que torna uma imagem elegível para o selo Licensable no Google Imagens.Ilustrar artigos escritos por IA ajuda no ranqueamento?
Como executo a etapa de ilustração no CI sem perder o controle editorial?
photo_id, a verificação de que todo número plotado aparece no artigo, e uma falha explícita quando nenhuma foto ultrapassa o limite de pontuação. Nenhum hero é melhor do que um errado.