Metin İşin Kolay Kısmı: Yapay Zekâ Yazımı Makaleleri Görselleştiren Pipeline

Metin üretmek çözülmüş bir problem. Onu görselleştirmek değil. Uçtan uca pipeline — yönlendirici prompt, fotoğraf arama, Mermaid diyagramları, doğrulanmış grafikler ve bunu SEO'ya dönüştüren alt metin, kredi ve ImageObject markup'ı.

Renkli lambalarla aydınlatılmış, kod dolu iki monitörü olan bir masada yazan bir programcı.
Fotoğraf: Unsplash

Bir içerik hedefi olan bir geliştiricisiniz: ayda düzinelerce, belki yüzlerce makale. Yazma modeli taslağı, ana hatları, meta açıklamasını, iç bağlantıları hallediyor. Sonra hat, kendine ait modeli olmayan tek adıma çarpıyor — görseller — ve duruyor. Fotoğraf bulmak zor olduğu için değil, yığındaki hiçbir şey hangi fotoğrafın, nereden, hangi lisansla, nasıl tanımlanacağını bilmediği için.

Bu makale, o eksik adımın uçtan uca kablolanmış hâli: hangi bölüm için hangi tür görselin üretileceği, bitmiş bir taslağı arama brief'lerine dönüştüren prompt, fotoğrafları döndüren API çağrısı, bir fotoğrafın anlatamayacağını çizen ikinci model ve Google'ın gerçekten okuyabileceği işaretlemeyi üreten derleyici. Sonda çalmaya hazır iki eksiksiz iş akışı var.

Metin çözüldü. İllüstrasyon takıldığı yer.

Otomatik bir makale hattının dürüst denetimini yapın. Ana hat: çözüldü. Taslak: çözüldü. Başlık, meta açıklama, şema, iç bağlantılar, çeviri: hepsi çözüldü, hepsi aynı model tarafından, hepsi metin olarak. Sonra:

Hat adımı Durum Asıl engelleyen
Ana hat & taslak Çözüldü Bir model çağrısı, bir prompt
Başlıklar, meta, şema, bağlantılar Çözüldü Metin girer, metin çıkar
Hero görseli Engellendi Gerçek bir dosya, bir lisans, boyutlar ve alt metin gerektirir — bunların hiçbiri bir metin modeli tarafından üretilemez
Bölüm görselleri Engellendi Makale başına üç ila beş, her biri farklı, sitede hiçbiri tekrarlanmıyor
Grafikler & diyagramlar Engellendi Doğru olmalı — bir aramanın döndüremeyeceği ve bir üreticinin uyduramayacağı tek görsel türü

Bu başarısızlık estetik değil, yapısaldır: makale bir stok yer tutucu ile, ya da son on iki makaleyle aynı fotoğrafla, ya da altı parmaklı elinin okuyucunun gördüğü ilk şey olduğu üretilmiş bir görselle yayınlanır. Ve görsel süsleme değildir — Google'ın kendi görsel belgeleri bunu açıkça ortaya koyar: alt metin “bir görsel için meta veri sağlama söz konusu olduğunda en önemli özelliktir” ve tavsiye edilen, görselin bulunabilmesi ve anlaşılabilmesi için CSS arka planları yerine açıklayıcı alt içeren gerçek <img> öğeleri kullanmaktır.1

Dört tür görsel, dört tür model

En büyük tasarım hatası, “görseli” tek bir sağlayıcıya sahip tek bir problem olarak ele almaktır. Aslında dörttür ve aralarındaki yönlendirici bir servis değil, bir satırlık prompt'tur:

Bölümün ihtiyacı… Şununla üret Diğerleri neden olmaz
Gerçek dünyadan bir sahnehero, insan durumları, yerler, nesneler, jestler Semantik fotoğraf arama (Pexafy) Bir üretici ayrıntıları uydurur; bir grafiğin çizecek bir şeyi yoktur
Gerçekten sahip olduğunuz sayılarkıyaslamalar, fiyatlandırma, anket sonuçları, gecikme Bir sandbox'ta çalıştırılan, çizim kodu yazan bir model Bir görsel modeline bir değer konusunda güvenilemez; bir fotoğraf veri taşıyamaz
Bir yapı veya akışmimari, sıra, durum makinesi Mermaid / Graphviz yazan, deterministik olarak render edilen bir model Ücretsiz fotoğraf kütüphanelerinde sizin sisteminizin diyagramı yoktur
Ekrandaki ürününüzdokümantasyon, changelog, öğreticiler Betikle alınan bir tarayıcı ekran görüntüsü (Playwright) Sadece sizin derlemenizde var olan bir UI'ı başka hiçbir şey gösteremez

Ve üretilmiş illüstrasyon? Tek bir dürüst kullanım alanı bırakıyor: fotoğrafı çekilemeyecek ve veri olmayan sahne — soyut bir mekanizma, henüz var olmayan bir ürün, sahibi olduğunuz bir ev illüstrasyon stili. (Üretim yerine gerçek fotoğrafçılık için tam gerekçe — hacimde hız, doğruluk, tekrarlılık sorunu — burada anlatılıyor.) Gidişat yönünde bir işaret: 2 Ağustos 2026 tarihinden itibaren, AB Yapay Zeka Yasası'nın 50. Maddesi, üretici (generatif) sistem sağlayıcılarının sentetik çıktıları makine tarafından okunabilir bir biçimde işaretlemesini zorunlu kılıyor.2 Bu, bir blogun ne yayınlayabileceğine ilişkin bir kural değil, yapay zeka sağlayıcıları ve dağıtıcılarına yönelik bir yükümlülük — ama tam da bu yüzden, makalenizin en üstündeki görselin kaynağı, okuyucunun güvenmesi gereken bir şey olmaktan çıkıp kontrol edebileceği bir şey haline geliyor.

Hat, uçtan uca

Beş aşama. Sadece aşama 3 bir görsel API'sine dokunur ve sadece aşama 4 isteğe bağlıdır:

Şeklî böyle — bir makale girer, yayınlanabilir bir makale çıkar
┌─ 1. YAZ ────────────────────────────────────────────────────┐
   konu ──▶ LLM ──▶ draft.md  (h2 bölümleri, ön-madde)
└───────────────────────────────┬───────────────────────────────┘
┌─ 2. BRIEF ────────────────────┴───────────────────────────────┐
   draft.md ──▶ LLM ──▶ { hero: {...}, sections: [ {...} ] }
                     tek JSON nesnesi: her slot için bir kind +
                     ya bir kamera brief'i ya da bir veri/diyagram şeması
└──────────┬──────────────────────────────────┬─────────────────┘
           │ kind = "photo"                │ kind = "chart" | "diagram"
           ▼                               ▼
┌─ 3. ARA ───────────────┐   ┌─ 4. ÇİZ (isteğe bağlı) ─────────┐
   GET /search/photos          LLM ──▶ mermaid | çizim kodu
   ← kaynağı belirtilmiş foto +   ──▶ sandbox ──▶ .svg / .png
     g/y, blur_hash, alt,       (deterministik render, uydurma
     lisans, kaynak URL          sayı yok)
└──────────┬────────────────┘   └──────────────┬───────────────┘
           └───────────────┬──────────────────┘
┌─ 5. DERLE ─────────────┴───────────────────────────────────┐
   genişlik/yükseklik + fetchpriority | loading içeren <img>
   bir insan için yazılmış alt · görünür kredi · ImageObject JSON-LD
   iki sayfanın aynı hero'yu paylaşmaması için photo_id saklanır
└───────────────────────────────────────────────────────────────┘

Diyagramdan daha önemli olan iki özellik var. Aşama 2 bir yönlendiricidir: her slot için hangi üreticinin çalışacağına karar verir, böylece bir fotoğraf kütüphanesinden asla bir çubuk grafik istemezsiniz. Ve SEO'nun yaşadığı yer aşama 5'tir — Google'ın görseller hakkında belgelediği her şey (açıklayıcı alt, lisans meta verisi, LCP açısından güvenli bir hero) burada, arama yanıtının zaten taşıdığı alanlardan üretilir.

Aşama 2: taslağı görsel brief'lerine dönüştür

Bitmiş taslak üzerinde tek bir model çağrısı, ve döndürdüğü şey bir sorgu değil bir plandır. Tek bir fotoğraf brifi nasıl yazılır — makale başlığının neden mümkün olan en kötü girdi olduğu, 12 ile 25 kelimelik bir kamera brifinin neye benzediği ve bunun nasıl başarısız olduğu — tek bir makaleyi görsellendirme kılavuzunda ayrıntılı olarak anlatılıyor ve burada tekrarlanmıyor. Bir boru hattının eklediği şey yönlendirmedir: aynı çağrı, yuva yuva, hangi üreticinin çalışacağına karar vermek zorundadır — ve bir grafik girdisi veri taşırken bir fotoğraf girdisi sahne taşır.

Yönlendirici prompt — olduğu gibi kopyalayın
# sistem promptu — bitmiş her taslak için bir kez çalıştırın
Sen teknik bir yayının sanat yönetmenisin. Makaleyi oku ve
görsel planı döndür: hero için bir giriş, her H2 bölümü için bir tane.

Her giriş için tam olarak bir kind seç:
  "photo"    gerçek bir sahne: birinin bir yerde bir şey yapması, bir yer,
              bir nesne, bir jest. Hero'lar için varsayılan.
  "chart"    bölüm makalenin İÇİNDE olan sayılar belirtiyor. Asla
              değer uydurma: data içine olduğu gibi kopyala.
  "diagram"  bölüm bir yapıyı, akışı veya diziyi tanımlıyor.
  "none"     bölüm kısa veya zaten bir kod bloğu taşıyor.

"photo" girişleri için kurallar — alan query:
Bir kamera notu yazın: bir kameranın çekebileceği bir sahne, 12 ile 25 kelime
   arasında, İngilizce olarak, bölümün havasına uygun. Kadrajda ne olduğunu
   belirtin, konuyu değil. Metin, logo, marka veya ünlü kişi yok; görünmez
   metafor yok. (Örnekler ve başarısız durumlarla birlikte tüm kurallar:
   pexafy.com/blog/illustrate-blog-articles-at-scale/)

Bir fotoğraf girişinin alt metnini YAZMA: geri aldığınız görsel, brief'e
en yakın eşleşmedir, tanımladığınız sahne değildir, bu yüzden alt metni
seçilen fotoğraftan yazılmalıdır. Grafik ve diyagram girişleri alt TAŞIR —
orada tam olarak neyin render edildiğini siz kontrol edersiniz.

Sadece JSON döndür:
{
  "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": "…" }
  ]
}

kind satırı işin çoğunu yapar, data talimatı da gerisini tamamlar: bir grafik girdisi yalnızca taslakta zaten geçen sayıları taşıyabilir, böylece model icat etmek yerine aktarır. İşte yönlendiricinin gerçek bir geliştirici makalesinin üç bölümü üzerindeki hali:

Bölüm kind Yönlendiricinin döndürdüğü
Hero — “Gece derlememiz neden 40 dakika sürüyor” photo “iki monitörü ve mekanik klavyesi olan, ekranların aydınlattığı karanlık bir odada masada geç saatte çalışan bir geliştirici”
“Zaman aslında nereye gidiyor” chart Paragraftan kopyalanan data: kurulum 480 s, derleme 1080 s, test 720 s, yükleme 120 s
“Grafiği nasıl böldük” diagram İş bağımlılık grafiğinin flowchart LR'ı
“Neyi değiştirdik ve tekrar ne yapardık” photo “diyagramlarla kaplı bir beyaz tahta önünde bir sorunu birlikte çözen iki mühendis”

Aşama 3: fotoğraflar kaynağı belirtilmiş şekilde geri gelir

Her kind: "photo" girişi bir isteğe karşılık gelir. Yukarıdaki hero brief'i, herkese açık API'ye karşı çalıştırıldığında, bunu 147 ms içinde döndürür:

GET /search/photos — hero brief · “masada geç saatte çalışan bir geliştirici…” · 147 ms
Motor anlam bazında sıralar, bu yüzden uzun cümle kümeyi boşaltmak yerine daraltır. Tam olarak bu aramayı çalıştırın →

Son bölüm brief'i, tamamen farklı bir sahne, 144 ms içinde:

GET /search/photos — bölüm brief'i · “diyagramlarla kaplı bir beyaz tahta önünde iki mühendis…” · 144 ms
Aynı makale, aynı çalıştırma, kimsenin hero ile karıştırmayacağı bir sahne — çünkü brief makale başına değil bölüm başına yazıldı. Bunu da çalıştırın →

Her fotoğraf için geri gelen şey, aşama 5'i mümkün kılan kısımdır — sadece bir dosya değil:

Bir sonuç, derleyicinin tükettiği alanlara indirgenmiş
{
  "photo_id":  "019e1c7f-0063-759e-b498-33ce1714e6c9",   // sakla: tekrar yok
  "urls": { "small": "…?w=400", "regular": "…?w=1080",
             "large": "…?w=1920" },
  "width": 3000, "height": 1688,          // → düzen kayması yok
  "blur_hash": "LJ8gjv9rVq-6OFxanNNFI7xco$Na",   // → gerçek yer tutucu
  "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 (…)" }
}

Aşama 4: bir fotoğrafın anlatamayacağı şey

Plandaki iki slot aranabilir değildir ve tam olarak burada ikinci bir model yerini hak eder — bir görsel çizmek için değil, onu çizen kodu yazmak için. Bu ayrım önemlidir: kod incelenebilir, deterministiktir ve bir çubuk yüksekliğini halüsinasyon göremez.

Diyagramlar: metin girer, SVG çıkar

Mermaid, diyagramları düz metin bir tanımdan render eder,3 bu da onu bir model için en güvenli hedef yapar: çıktı incelenebilir, git'te fark alınabilir ve her seferinde aynı şekilde render edilir. Yönlendirici şemayı zaten döndürmüştü.

diagram.sh — modeli şemayı yazdı, CLI onu render eder
# bir diyagram girişinin "spec" alanı, build/graph.mmd'ye yazılır
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
# → PR'da inceleyebileceğiniz bir SVG, güvenmek zorunda olduğunuz bir görsel değil

Grafikler: yalnızca makalenin zaten içerdiği sayılar

Aynı ilke, bir ekstra koruma. Yönlendirici değerleri taslaktan kopyaladı; model çizim kodunu yazar; kod bir sandbox'ta çalışır; derleyici grafiğin bir sayfaya yaklaşmasına izin verilmeden önce render edilen değerleri kaynak sayılarla yeniden kontrol eder.

chart.py — aktarılan veriyi çiz, ardından doğrula
import re, matplotlib
matplotlib.use("Agg")                # başsız: CI'da görüntü yok
import matplotlib.pyplot as plt

def assert_in_draft(value, draft: str) -> None:
    """Çizilen bir sayı makalede BİR SAYI OLARAK görünmelidir."""
    # Buradaki tuzak alt dize eşleştirmesidir: "120", "1200" içindedir ve
    # "?w=1200" içinde de — saf bir `str(v) in draft` her şeyi geçer.
    # Kelime sınırlarına göre eşleştir ve 1 234 / 1,234 / 1234'ü kabul et.
    body = re.sub(r"(?<=\d)[  ,](?=\d{3}\b)", "", draft)   # ayırıcıları temizle
    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)       # halüsinasyon değeri → grafik yok

    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)                    # deterministik, incelenebilir çıktı
    return out

Koruma kısa, ama dikkatle yazın: bir alt dize kontrolü işe yaramaz. "120" in draft, 1200 veya ?w=1200 içeren bir makale için doğrudur, yani saf sürüm her şeyi geçer ve hiçbir şeyi korumaz. Basamak sınırlarına dayanın, binlik ayırıcıları normalleştirin ve bir teknik makaleyi yorumlarda parçalara ayıran hata sınıfı — kendi paragrafıyla çelişen bir grafik — üretime ulaşamaz. Ekran görüntüleri aynı ilkeyi izler: gerçek derlemenize karşı betikle alınan bir page.screenshot(), kendi UI'ınız için tek gerçek kaynaktır ve UI değiştikçe doğru kalır.

Aşama 5: SEO'nun yaşadığı yer derleme

Şimdiye kadar her şey dosya ve alan üretti. Bu aşama bunları işaretlemeye dönüştürür — ve burada hassas olmakta fayda var, çünkü belgelenmiş üç davranış bu birkaç satırda karar veriliyor.

Hero, arama yanıtından üretilir — hiçbir şey uydurulmaz
<!-- Baytlar başka bir kaynaktan gelir: el sıkışmayı erkenden öde -->
<link rel="preconnect" href="https://images.unsplash.com" crossorigin>

<!-- LCP öğesi: asla lazy değil, her zaman yüksek öncelik -->
<figure>
  <img src="{urls.regular}"
       width="{width}" height="{height}"        <!-- düzen kaymasını öldürür -->
       alt="{alt}"                                <!-- seçimden SONRA yazılır -->
       fetchpriority="high" decoding="async"
       style="background:{color_hex}">   <!-- baskın renk, 1 alan -->
  <figcaption>{attribution.html}</figcaption>
</figure>

<!-- Bölüm görselleri, katlamanın altında: tam tersi ayarlar -->
<img src="{urls.regular}" width="{width}" height="{height}"
     alt="{alt}" loading="lazy" decoding="async">

Hotlink mi barındırma mı? Yukarıdaki parça hotlink yapar, ki bu yayınlanacak en hızlı şeydir ve preconnect'in nedeni budur: uzaktaki bir hero, kritik yolda bir DNS aramasına ve bir TLS el sıkışmasına mal olur ve bu, fetchpriority ile satın aldığınız kazancı yiyebilir. Yeniden barındırmak üçüncü taraf kaynağı tamamen ortadan kaldırır, kendi kesim noktalarınızda AVIF/WebP sunmanıza izin verir ve bir üst akış URL'sinin değişmesine dayanıklı kalır — depolama, hatta bir getirme adımı ve kendi CDN faturanız pahasına. Hangisini seçerseniz seçin, color_hex tek bir alan için bir yer tutucu sağlar (bir blur hash daha şık ama önce bir veri URI'sine kod çözülmesi gerekir — bir CSS rengi değildir). Kaba bir hero'yu doğrudan background: içine kopyalamak, çoğu hattın sessizce boş gri bir kutu yayınladığı yerdir.

  1. Hero'yu asla lazy-load yapmayın. web.dev açık: “LCP görselinizi asla lazy-load yapmayın, çünkü bu her zaman gereksiz kaynak yükleme gecikmesine yol açar ve LCP üzerinde olumsuz bir etkisi olur” ve LCP olması muhtemel öğede — dikkatlice, tek bir görselde kullanılarak — fetchpriority="high" önerir.4 Hero dahil her görsele loading="lazy" damgalayan bir hat, otomatik yayıncılıkta en yaygın kendi kendine verilen Core Web Vitals yarasıdır.
  2. Her zaman width ve height üretin — bunlar yanıtta geri gelir, dolayısıyla mazeret yoktur; tarayıcının alanı ayırmasını sağlayan ve düzenin zıplamasını durduran şey tam olarak bu özellik çiftidir. Dosya yüklenirken yer tutucu olarak blur_hash kullanın.
  3. Alt metni seçimden sonra yazın, asla önce değil. Bu incelikli olan. Planın alt alanı istediğiniz sahneyi tanımlar; aldığınız fotoğraf en yakın eşleşmedir, o sahne değildir. Brief'in metnini alt olarak yayınlamak, bu hattın önlemesi gereken tam olarak erişilebilirlik hatasıdır — sayfada olmayan bir görselin açıklaması. Alt metni, seçilen fotoğrafın alt_description'ından oluşturun, içinde bulunduğu paragrafa göre inceltin. Google'ın tavsiyesi “anahtar kelimeleri uygun şekilde kullanan ve sayfanın içeriği bağlamında olan yararlı, bilgi açısından zengin içerik oluşturmaya odaklanmak”tır ve alt özniteliklerini anahtar kelimeyle doldurmanın “olumsuz bir kullanıcı deneyimine yol açacağı ve sitenizin spam olarak görülmesine neden olabileceği” konusunda uyarır.1

Neredeyse hiç kimsenin otomatikleştirmediği kısım: lisans meta verisi

Google, görsel lisanslama için ImageObject yapılandırılmış verisini destekler. contentUrl artı creator, creditText, copyrightNotice veya license'dan en az birini gerektirir, acquireLicensePage'i önerir ve lisans bilgisi olan görseller Google Görseller'de Lisanslanabilir rozeti için uygun hâle gelir.5 Bu alanların her biri zaten arama yanıtındadır — bu yüzden onu yayınlamak bir proje değil, bir şablondur:

ImageObject JSON-LD, API yanıtından doldurulmuş
<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "ImageObject",
  "contentUrl": "{urls.large}",                 // zorunlu
  "creator": { "@type": "Person",
                "name": "{photographer_full_name}" },
  "creditText": "{photographer_full_name} on {source}",
  "license": "{LICENSE_URL[source]}",           // kütüphanenin kendi sayfası
  "acquireLicensePage": "{source_image_url}"     // fotoğrafın sayfası
}
</script>

# LICENSE_URL, `source` alanını fotoğrafı gerçekten yöneten lisansa eşler
# — unsplash.com/license, pexels.com/license, pixabay.com/…

Doğru yapmaya değer bir ayrıntı: license, sizin alan adınızdaki bir özet sayfaya değil, o fotoğrafı yöneten lisansa — kaynak kütüphanenin kendi lisans sayfasına — işaret etmelidir. Google, rozet uygunluğuna karar vermek için onu okur ve kendine referans veren bir URL, hem bir sinyal olarak daha zayıftır hem de kendinize geri dönen bir bağlantıdan başka bir şey olarak savunulması zordur. Kendi lisans özetinizi okuyucular için dahili bir sayfa olarak tutun; kanonik olanı işaretlemeye koyun.

Derleyicideyken: dosyaya IMG_0042.jpg yerine kısa, açıklayıcı bir isim verin ve görseli bir site haritasına ekleyin — Google'ın görsel site haritası biçimi, sayfa URL'si başına 1.000'e kadar görsel kabul eder.6 İkisi de bir hatta birer satırdır ve hiçbiri elle yapılmaz.

Çalmak için iki hat

Aynı beş aşama, çok farklı iki şekil — biri yazdığınız makaleler için, biri çalışan bir ürünle eşleşmesi gereken dokümantasyon için. Başarısızlık modunu tanıdığınızı seçin.

1 · CI'da geliştirici blogu — depodaki Markdown

Makaleler Markdown olarak bulunur, görseller yanlarına commit edilir ve her şey push'ta çalışır. Deterministik, PR'da incelenebilir, hiçbir API'ye çalışma zamanı bağımlılığı yok:

.github/workflows/illustrate.yml
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   # görseller PR'a düşer
        with: { commit-message: "chore(content): illustrate" }

Bir insan yine de PR'ı onaylar, mesele de bu: hat önerir, inceleyici karar verir ve yazdığı ön-madde fark alınabilirdir.

Arama kısmı tek bir GET'tir — isteği, score_threshold'u ve döndürdüğü alanlar tek makale kılavuzunda satır satır yazılıdır, dolayısıyla burada yeniden basılmak yerine içe aktarılır. Bu dosyanın eklediği şey, bir planın ihtiyaç duyduğu ve tek bir fotoğrafın ihtiyaç duymadığı her şeydir: sonuç döndürmeyen bir brif için yeniden deneme, iki sayfanın aynı görseli paylaşmaması için bir photo_id üzerinde hak talebi ve gelen fotoğraftan yazılan alt metin.

plan_to_pr.py — bağlayıcı: görsel plan girer, ön bilgi (front-matter) çıkar
import frontmatter
from photo_search import search   # tek bir GET /search/photos; `used` içindeki id'leri atlar

def find_photo(entry: dict, used: set) -> dict | None:
    """Ara; brief çok özgülse bir kez genişlet, sonra vazgeç."""
    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"])   # talep et: site genelinde tekrar yok
            return photo
    return None                        # çağıran karar verir: slotu atla veya başarısız ol

def widen(query: str) -> str:
    """Son cümleyi bırak — genellikle aşırı özgül olan."""
    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:                    # hero olmaması, kötü bir hero'dan iyidir
        raise SystemExit(f"{path}: no photo above threshold — rewrite the brief")

    post["hero"] = {                    # şablonun ihtiyaç duyduğu her şey
        "src": hero["urls"]["regular"], "w": hero["width"], "h": hero["height"],
        # Alt, ALDIĞIMIZ fotoğrafı tanımlar, asla istediğimiz sahneyi değil.
        "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'ı temel al, ekran okuyucular için ~125 karaktere kırp.
    Daha iyisini istiyorsanız paragrafla birlikte modele geri gönderin."""
    base = photo.get("alt_description") or photo.get("description", "")
    return base[:125].rstrip(" ,;")

2 · Dokümantasyon & changelog — önce ekran görüntüleri, en son fotoğraflar

Yönlendiricinin varsayılanlarını tersine çevirin. Ürün dokümantasyonunda dürüst görsel neredeyse her zaman kendi UI'ınızdır: gerçek derlemeyi açan, sabit bir görünüm alanı ayarlayan ve paragrafın tanımladığı tam durumu yakalayan bir Playwright betiği. Diyagramlar mimari sayfaları kapsar ve fotoğraflar yalnızca kavramsal ve açılış sayfalarında görünür — bir ekran görüntüsünün hiçbir şey söylemeyeceği yerlerde.

shots.py — ekran görüntüsü üretilir, asla tanımlanmaz
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)   # retina keskinliği
    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()
# Dokümantasyon derlemesiyle aynı CI işinde çalışır → ekran görüntüsü asla
# artık var olmayan bir UI sürümünü tanımlayamaz.

Adı verilmeye değer ama kendi tarifini hak etmeyen iki varyant. Programatik SEO döngüyü tersine çevirir: bir veritabanından üretilen yüzlerce sayfayla sayfa başına arama yapmazsınız — bir istek 100'e kadar fotoğraf döndürür, bu yüzden konu kümesi başına arama yapıp bir havuzdan atarsınız, tekilleştirmeyi bir benzersizlik kısıtlaması yapar (o mimari, tam olarak). Ve bültenler ve sosyal medya kartları, aynı fotoğrafı dört kırpımda gerektirir: photo_id'yi saklayın, urls'dan ihtiyacınız olan boyutu isteyin ve bir kırpım gerçekten başarısız olduğunda ancak yeniden arama yapın.

Aynı hat, tek bir ajan olarak

Taslağı zaten bir model yazıyorsa, en kısa yol JSON'u süreçler arasında dolaştırmak yerine arama aracını doğrudan modele vermektir. Pexafy, mcp.pexafy.com/mcp adresinde barındırılan bir Model Context Protocol sunucusu çalıştırır. Bağlayıcı kurulumu ve sunduğu araçlar başka yerlerde ele alınmıştır — masaüstü ve editör kurulumu tekil makale kılavuzunda, bir CI çalıştırıcısı için başsız (headless) varyant ölçekte içerik hakkındaki makalede, ve daha geniş bağlayıcı pazarının neye benzediği — kimin bir MCP görsel sunucusu sunduğu, hangi şartlarla — AI ajanları için görsel arama altyapısı incelemesinde anlatılmıştır. Burada göstermeye değer olan şey, ajan araçları elinde tuttuğunda bu ardışık düzenin başına gelenlerdir: beş aşama olmaktan çıkar ve tek bir talimata dönüşür.

Tek bir talimat, tüm plan yürütülür
You  İşte taslak. Görsel planı oluştur, illüstre et ve bir PR aç.
     Grafikler yalnızca metinde zaten olan sayılardan.

Agent  → plan: 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ğraf · 147 ms · #1 seçildi, 3000×1688, kaynağı belirtildi
      → mermaid-cli build/graph.mmd → static/img/graph.svg
      → chart.py §2 → taslağa karşı doğrulanan 4 değer ✓
      → search_photos(q="two engineers standing at a whiteboard…")
      ← 16 fotoğraf · 144 ms · #1 seçildi

      ✓ 4 slot dolduruldu · 2 API çağrısı · alt + kredi + ImageObject yazıldı
      ⚠ §5, 0.5 üzerinde hiçbir şey döndürmedi — brief çok soyuttu, şu şekilde
        yeniden yazıldı: "bir mutfak masasında dizüstü bilgisayarda rakamları
        kontrol eden bir kişi"

Bu son satır, saf bir betik yerine bu döngüde bir ajanın bulundurulmaya değer olmasının nedenidir: otomatik illüstrasyonun başarısızlık modu kötü bir brief'tir ve kötü bir brief'i yeniden yazmak tam olarak bir dil modelinin işidir. Deterministik parçaları — atama, tekilleştirme, sayısal koruma — kodda tutun.

Bir illüstre makale neye mal olur

Makale başına üç fotoğraf slotu üç arama isteği anlamına gelir, dolayısıyla ücretsiz plan (ayda 5,000 istek), ayda 1,666 makaleyi herhangi bir ödeme sorusu ortaya çıkmadan kapsar — ve aramaları makale başına değil konu kümesi başına havuzlarsanız, bu tavan bir kat daha büyüklük sırası hareket eder. Plan bazında dökümü ve bunu önemsiz kılan havuzlama mimarisi ölçekte içeriği illüstre etme üzerine yardımcı makalede.

Makale başına iki model çağrısı — biri taslak için, biri görsel plan için — birkaç bin token tutar ve hattaki en ucuz satır olacaktır; Mermaid ve matplotlib render'ları CI saniyeleri dışında hiçbir şeye mal olmaz. İnsanları şaşırtan sayı, hangi satırın ucuz olmadığıdır: makale başına dört görsel üretmek, ayda dört yüz görsel, artı işi başaramayan denemeler — ve çıktı yine de hiçbir fotoğrafçı, tarih veya kaynak URL taşımaz.

Bu hattın çözmediği şey

Dürüst bir bölüm, çünkü önlediği başarısızlık pahalıdır. İyi illüstre edilmiş bir makale hâlâ bir makaledir: gerçek fotoğrafçılık, doğru grafikler ve doğru işaretleme, var olmayı hak eden bir sayfayı iyileştirir. İnce, kitlesel olarak üretilmiş içeriğin sıralanmasını sağlamazlar. Google'ın spam politikaları ölçekli içerik istismarını adlandırır — sıralamaları manipüle etmek amacıyla birçok sayfa üretmek ve kullanıcılara çok az değer sunmak, otomasyon dahil olsun olmasın — ve illüstrasyonların kalitesi bu değerlendirmede bir faktör değildir.7

Yani ayakta kalan çerçeve şudur: bu hat, zaten yayınlanmak için bir nedeni olan sayfalara uygulanan kontrol ettiğiniz bir kalite tabanıdır. Belirgin şekilde işe yaradığı yerler:

  • Okuyucunun doğrulayabileceği köken. Gerçek bir fotoğrafçı ve kaynak URL'si olan bir kredi satırı, kontrol edilebilir bir iddiadır — ve aynı alanlar Google'ın okuduğu ImageObject işaretlemesini besler.
  • Erişilebilirlik ve Core Web Vitals. Gerçek alt metin, her görselde boyutlar, asla lazy-load edilmeyen bir hero. Yayınladığınız her makaleyle çarpın ve bu, sitenin görsel kalitesi hikâyesi hâline gelir.
  • Kontrol edilebilir olan yerde doğruluk. Sayıları makaleye karşı doğrulanan bir grafik, çalışan derlemeden üretilmiş bir ekran görüntüsü, PR'da metin olarak incelenebilir bir diyagram — bir testin başarısız olmadan gerçeklerden sapamayacağı üç görsel.

Atıf konusunda, boru hattına özel nokta dardır: kredinin her zaman gösterilmesi gerektiği argümanı başka yerde yapılıyor, ve otomatik bir birleştiricinin eklediği şey, bastığı aynı attribution dizesinin, yapılandırılmış verinin ihtiyaç duyduğu creditText olmasıdır. Bir alan, iki yer, aynı şablon geçişinde yayınlanır — bu yüzden bir boru hattının bunu es geçmek için bir insandan daha bile az bahanesi vardır.

Nereden başlanır

  1. Yönlendirici promptu ekleyin taslaklarınızı zaten yazan şeye ve JSON'u ona göre hareket etmeden yazdırın. On plan okuyun. Brief'ler sahne yerine konu adlandırıyorsa, herhangi bir entegrasyon yazmadan önce prompt'u düzeltin.
  2. Yalnızca fotoğraf slotlarını kablolayın. Brief başına bir GET /search/photos ve ilk günden itibaren photo_id'yi saklayın — 3.000 canlı sayfada sonradan uygulanan tekilleştirme bir sütun değil, bir geçiştir.
  3. Genişlik, yükseklik ve alt üretin aynı commit'te. Yapacağınız en ucuz Core Web Vitals çalışmasıdır.
  4. Sonra aşama 4'ü ekleyin, grafiklerden önce diyagramlar — Mermaid metindir, bu yüzden bir sözdizimi hatasının ötesinde başarısızlık modu olmayan tek olanıdır.
  5. Sayısal korumayı ekleyin ilk grafik bir okuyucuya ulaşmadan önce, sonra değil.

Kaynaklar & dipnotlar

1 Google Search Central, Görsel SEO en iyi uygulamaları: alt metin “bir görsel için meta veri sağlama söz konusu olduğunda en önemli özelliktir”; tavsiye “anahtar kelimeleri uygun şekilde kullanan ve sayfanın içeriği bağlamında olan yararlı, bilgi açısından zengin içerik” oluşturmak, anahtar kelimeyle doldurulmuş alt özniteliklerinden kaçınmak, CSS görselleri yerine HTML <img> öğeleri kullanmak ve dosyalara kısa ama açıklayıcı isimler vermektir.

2 AB Yapay Zeka Yasası, Madde 50 — 2 Ağustos 2026'dan itibaren geçerli şeffaflık yükümlülükleri: sentetik görsel, ses, video veya metin üreten sistem sağlayıcıları, çıktıları makine tarafından okunabilir bir biçimde işaretlemeli ve yapay olarak üretildiğinin tespit edilebilir olmasını sağlamalıdır. Yapay zeka sağlayıcılarını ve dağıtıcılarını bağlar; bir web sitesinin hangi görselleri yayınlayabileceğine dair bir kural değildir.

3 Mermaid, diyagramları ve grafikleri Markdown'dan esinlenilmiş metin tanımlarından render eder, ki bu çıktıyı incelenebilir ve deterministik yapan şeydir.

4 web.dev, En Büyük İçerik Boyaması'nı Optimize Etme: “Bir <img> öğesinin sayfanızın LCP öğesi olma olasılığı yüksekse üzerine fetchpriority="high" ayarlamak iyi bir fikirdir”, dikkatlice kullanılır; ve “LCP görselinizi asla lazy-load yapmayın, çünkü bu her zaman gereksiz kaynak yükleme gecikmesine yol açar ve LCP üzerinde olumsuz bir etkisi olur.”

5 Google Search Central, Görsel meta verisi (yapılandırılmış veri): ImageObject, contentUrl artı creator, creditText, copyrightNotice veya license'dan en az birini gerektirir; acquireLicensePage önerilir ve lisans bilgisi taşıyan görseller Google Görseller'de Lisanslanabilir rozeti için uygun hâle gelebilir.

6 Google Search Central, Görsel site haritaları: görsel site haritaları, JavaScript aracılığıyla bulunanlar dahil olmak üzere bir sitedeki görseller hakkında Google'ı bilgilendirir ve sayfa URL'si başına 1.000'e kadar görsel kabul eder.

7 Google Arama spam politikaları — ölçekli içerik istismarı: otomasyon, insan çabası veya bir kombinasyonuyla oluşturulmuş olsun olmasın, sıralamaları manipüle etmek amacıyla birçok sayfa üretmek ve kullanıcılara çok az değer sunmak.

Sıkça sorulan sorular

Bir LLM tarafından yazılan makaleleri otomatik olarak nasıl görselleştiririm?
Yazım ile yayınlama arasına bir model çağrısı ekleyin: modelden bir görsel plan döndürmesini isteyin — her görsel yuvası için bir giriş, her biri fotoğraf, grafik, diyagram veya hiçbiri olarak etiketlenmiş. Fotoğraf girişleri, bir kameranın çekebileceği bir sahnenin 12-25 kelimelik bir açıklamasını taşır; bunu semantik görsel arama API'sine gönderirsiniz (GET /api/v1/search/photos, yaklaşık 150 ms). Grafik ve diyagram girişleri, çizim kodu veya Mermaid yazan bir modele gönderilir ve deterministik olarak render edilir. Makale başlığını asla bir görsel aramaya beslemeyin: başlıklar soyuttur ve hiçbir fotoğraf onları tasvir etmez.
Bir dokümantasyon sitesi ekran görüntüsü mü yoksa stok fotoğraf mı kullanmalı?
Yönlendiricinin varsayılanlarını tersine çevirin: ürün dokümantasyonunda dürüst görsel neredeyse her zaman kendi arayüzünüzdür. Gerçek derlemeyi açan, görünüm alanını sabitleyen ve paragrafın tarif ettiği tam durumu yakalayan bir Playwright betiği, dokümantasyon derlemesiyle aynı CI işinde çalışır, böylece bir ekran görüntüsü artık var olmayan bir arayüz sürümünü asla gösteremez. Diyagramlar mimari sayfaları taşır, fotoğraflar ise yalnızca kavramsal ve açılış sayfalarında görünür; burada bir ekran görüntüsü hiçbir şey ifade etmezdi.
Bir yapay zekâ pipeline'ının bir grafiğe yanlış sayılar koymasını nasıl önlerim?
Planın icat etmek yerine kopyalamasını sağlayın: yönlendirici, yalnızca taslakta zaten görünen değerleri grafik girişinin data alanına kopyalayabilir. Ardından render etmeden önce bunu kodda doğrulayın — her değer için, dizesinin makalede göründüğünü kontrol edin, yoksa hata verin. Dokuz satır, ve kendi paragrafıyla çelişen bir grafik asla bir okuyucuya ulaşamaz.
Otomatikleştirilmiş bir pipeline, SEO için hangi görsel markup'ını üretmeli?
Üç şey, hepsi arama yanıtının zaten içerdiği alanlardan gelir. Paragrafın bağlamında yazılmış açıklayıcı bir alt — Google, alt metnini en önemli görsel meta verisi olarak tanımlar ve anahtar kelime doldurmaya karşı uyarır. Her görselde width ve height, hero görselinde fetchpriority="high" ve asla üzerinde loading="lazy" olmamalı, çünkü LCP görseli geç yüklenmemelidir (lazy load edilmemelidir). Ve contentUrl, creator, creditText ve license içeren ImageObject yapılandırılmış verisi, bu da bir görselin Google Görseller'de Lisanslanabilir rozetine hak kazanmasını sağlayan şeydir.
Yapay zekâ tarafından yazılmış makaleleri görselleştirmek sıralamada yardımcı olur mu?
Tek başına değil, ve burada net olmakta fayda var. Google'ın spam politikaları, ölçeklendirilmiş içerik kötüye kullanımını, otomasyon dahil olsun veya olmasın, kullanıcılara az değer sunarken sıralamaları manipüle etmek amacıyla çok sayıda sayfa üretmek olarak tanımlar; illüstrasyonlar bu değerlendirmeyi değiştirmez. İyi bir pipeline'ın satın aldığı şey, zaten var olmayı hak eden sayfalarda bir kalite alt sınırıdır: doğrulanabilir kaynak, erişilebilir alt metin, otomasyona rağmen ayakta kalan Core Web Vitals ve gerçeklikten sapamayan grafikler ve ekran görüntüleri.
Editoryal kontrolü kaybetmeden görselleştirme adımını CI'da nasıl çalıştırırım?
İşi, içerik dosyalarınıza dokunan bir pull request üzerinde tetikleyin, görselleri ve ön bilgiyi (front-matter) yazmasına izin verin ve dala doğrudan commit yapmak yerine bir pull request açmasını sağlayın — hat öneride bulunur, bir insan onaylar ve yazdığı her alan karşılaştırılabilir olur. Şu üç şeyi modelde değil, kodda deterministik tutun: photo_id ile tekrarları ayıklama, grafiğe dökülen her sayının makalede geçtiğine dair doğrulama ve hiçbir fotoğraf puan eşiğini geçemediğinde sert bir hata verme. Hiç görsel olmaması, yanlış bir görselden daha iyidir.

Anahtar kelime avından vazgeçin. Ne demek istediğinizi anlatın.

9M+ ücretsiz kullanılabilen görseli anlamına göre arayın — herhangi bir dilde, 100 ms'nin altında.