टेक्स्ट आसान हिस्सा है: वह पाइपलाइन जो AI-लिखित लेखों को इलस्ट्रेट करती है
टेक्स्ट जनरेट करना सुलझा हुआ मामला है। उसे इलस्ट्रेट करना नहीं। एंड-टू-एंड पाइपलाइन — राउटर प्रॉम्प्ट, फ़ोटो सर्च, Mermaid डायग्राम, वेरिफाइड चार्ट, और वह alt टेक्स्ट, क्रेडिट व ImageObject मार्कअप जो इसे SEO में बदल देता है।
आप एक डेवलपर हैं जिसके पास एक कंटेंट लक्ष्य है: महीने में दर्जनों लेख, शायद सैकड़ों। राइटिंग मॉडल ड्राफ्ट, आउटलाइन, मेटा डिस्क्रिप्शन, इंटरनल लिंक्स — सब संभाल लेता है। फिर पाइपलाइन उस एक चरण से टकराती है जिसका अपना कोई मॉडल नहीं है — इमेजेस — और रुक जाती है। इसलिए नहीं कि तस्वीरें पाना मुश्किल है, बल्कि इसलिए कि पूरे स्टैक में कुछ भी नहीं जानता कि कौन सी तस्वीर, कहाँ से, किस लाइसेंस के साथ, कैसे वर्णित की जाए।
यह लेख वही गायब चरण है, सिरे से सिरे तक जोड़ा हुआ: किस तरह के सेक्शन के लिए किस तरह का विज़ुअल बनाना है, वह प्रॉम्प्ट जो एक तैयार ड्राफ्ट को सर्च ब्रीफ में बदलता है, वह API कॉल जो फोटो लौटाती है, वह दूसरा मॉडल जो वह खींचता है जो एक फोटोग्राफ नहीं कह सकता, और वह असेंबलर जो ऐसा मार्कअप बनाता है जिसे Google वाकई पढ़ सके। अंत में दो पूरे वर्कफ़्लो, चुराने के लिए तैयार।
टेक्स्ट सुलझ गया है। इलस्ट्रेशन वह जगह है जहाँ रुकावट आती है।
एक स्वचालित लेख-पाइपलाइन का ईमानदार ऑडिट करें। आउटलाइन: सुलझा हुआ। ड्राफ्ट: सुलझा हुआ। टाइटल, मेटा डिस्क्रिप्शन, स्कीमा, इंटरनल लिंक, अनुवाद: सुलझा हुआ, सब एक ही मॉडल द्वारा, सब टेक्स्ट में। फिर:
| पाइपलाइन चरण | स्थिति | वास्तव में क्या रोकता है |
|---|---|---|
| आउटलाइन और ड्राफ्ट | सुलझा हुआ | एक मॉडल कॉल, एक प्रॉम्प्ट |
| टाइटल, मेटा, स्कीमा, लिंक | सुलझा हुआ | टेक्स्ट अंदर, टेक्स्ट बाहर |
| हीरो इमेज | रुका हुआ | असली फ़ाइल, लाइसेंस, डाइमेंशन और alt टेक्स्ट चाहिए — इनमें से कुछ भी एक टेक्स्ट मॉडल पैदा नहीं कर सकता |
| सेक्शन इमेजेस | रुका हुआ | प्रति लेख तीन से पाँच, हर एक अलग, साइट पर कहीं भी दोहराई न जाए |
| चार्ट और डायग्राम | रुका हुआ | सटीक होना ज़रूरी है — यह वह अकेला विज़ुअल है जो एक सर्च वापस नहीं ला सकती और जिसे एक जेनरेटर गढ़ नहीं सकता |
यह विफलता सौंदर्यबोध की नहीं, संरचनात्मक है: लेख एक स्टॉक प्लेसहोल्डर के साथ प्रकाशित होता है,
या पिछले बारह लेखों जैसी ही तस्वीर के साथ, या एक जेनरेट की गई इमेज के साथ जिसका छह-उँगलियों वाला
हाथ पाठक की पहली नज़र में पड़ता है। और तस्वीर सजावट नहीं है — Google का अपना इमेज दस्तावेज़ीकरण
इसे साफ़ शब्दों में कहता है: alt टेक्स्ट “किसी इमेज के लिए मेटाडेटा प्रदान करने के मामले में सबसे
महत्वपूर्ण एट्रिब्यूट है”, और मार्गदर्शन यह है कि CSS बैकग्राउंड की बजाय वर्णनात्मक
alt के साथ असली <img> एलिमेंट इस्तेमाल किए जाएँ, ताकि इमेज को
ढूँढा और समझा ही जा सके।1
चार तरह के विज़ुअल, चार तरह के मॉडल
सबसे बड़ी डिज़ाइन गलती यह है कि “इमेज” को एक प्रोवाइडर वाली एक समस्या मान लिया जाए। यह चार समस्याएँ हैं, और इनके बीच का राउटर कोई सेवा नहीं बल्कि प्रॉम्प्ट की एक पंक्ति है:
| सेक्शन को चाहिए… | इससे बनाएँ | बाकी क्यों नहीं |
|---|---|---|
| असली दुनिया का एक दृश्यहीरो, मानवीय स्थितियाँ, जगहें, वस्तुएँ, इशारे | सिमेंटिक फोटो सर्च (Pexafy) | एक जेनरेटर विवरण गढ़ लेता है; एक चार्ट के पास प्लॉट करने को कुछ नहीं होता |
| ऐसे नंबर जो आपके पास वाकई हैंबेंचमार्क, प्राइसिंग, सर्वे रिज़ल्ट, लेटेंसी | प्लॉटिंग कोड लिखने वाला एक मॉडल, सैंडबॉक्स में चलाया गया | एक इमेज मॉडल पर वैल्यू के लिए भरोसा नहीं किया जा सकता; एक फोटो डेटा नहीं ढो सकती |
| एक संरचना या एक प्रवाहआर्किटेक्चर, सीक्वेंस, स्टेट मशीन | Mermaid / Graphviz लिखने वाला मॉडल, डिटरमिनिस्टिक रूप से रेंडर किया गया | मुफ़्त फोटो लाइब्रेरीज़ के पास आपके सिस्टम का कोई डायग्राम नहीं है |
| स्क्रीन पर आपका प्रोडक्टडॉक्स, चेंजलॉग, ट्यूटोरियल | एक स्क्रिप्टेड ब्राउज़र स्क्रीनशॉट (Playwright) | कुछ और वह UI नहीं दिखा सकता जो सिर्फ़ आपके बिल्ड में मौजूद है |
और जनरेट किया गया इलस्ट्रेशन? वह एक ईमानदार स्लॉट बनाए रखता है: वह दृश्य जिसे फोटो नहीं खींचा जा सकता और जो डेटा भी नहीं है — कोई अमूर्त तंत्र, ऐसा प्रोडक्ट जो अभी अस्तित्व में नहीं है, या इलस्ट्रेशन की वह शैली जो आपके अपने ब्रैंड की है। (जनरेशन के मुकाबले असली फोटोग्राफी का पूरा तर्क — बड़े पैमाने पर गति, सटीकता, एकरूपता की समस्या — यहाँ दिया गया है।) दिशा को कीमत में ढालें: 2 अगस्त 2026 से, EU AI Act के आर्टिकल 50 के तहत जनरेटिव सिस्टम प्रदाताओं को सिंथेटिक आउटपुट को मशीन-रीडेबल फॉर्मेट में चिह्नित करना अनिवार्य है।2 यह AI प्रदाताओं और डिप्लॉयर पर एक दायित्व है, न कि यह नियम कि कोई ब्लॉग क्या प्रकाशित कर सकता है — लेकिन इसी वजह से आपके लेख के ऊपर लगी इमेज की उत्पत्ति अब कुछ ऐसा बनती जा रही है जिसे पाठक भरोसे पर छोड़ने के बजाय स्वयं जाँच सकता है।
पूरी पाइपलाइन, सिरे से सिरे तक
पाँच चरण। केवल चरण 3 किसी इमेज API को छूता है, और केवल चरण 4 वैकल्पिक है:
┌─ 1. WRITE ────────────────────────────────────────────────────┐
topic ──▶ LLM ──▶ draft.md (h2 sections, front-matter)
└───────────────────────────────┬───────────────────────────────┘
┌─ 2. BRIEF ────────────────────┴───────────────────────────────┐
draft.md ──▶ LLM ──▶ { hero: {...}, sections: [ {...} ] }
one JSON object: per slot, a kind +
either a camera brief or a data/diagram spec
└──────────┬──────────────────────────────────┬─────────────────┘
│ kind = "photo" │ kind = "chart" | "diagram"
▼ ▼
┌─ 3. SEARCH ───────────────┐ ┌─ 4. DRAW (optional) ─────────┐
GET /search/photos LLM ──▶ mermaid | plotting code
← credited photo + ──▶ sandbox ──▶ .svg / .png
w/h, blur_hash, alt, (deterministic render, no
licence, source URL invented numbers)
└──────────┬────────────────┘ └──────────────┬───────────────┘
└───────────────┬──────────────────┘
┌─ 5. ASSEMBLE ─────────────┴───────────────────────────────────┐
<img> with width/height + fetchpriority | loading
alt written for a human · visible credit · ImageObject JSON-LD
photo_id stored so no two pages share a hero
└───────────────────────────────────────────────────────────────┘
डायग्राम से ज़्यादा दो गुण मायने रखते हैं। चरण 2 एक राउटर है: यह हर स्लॉट के लिए तय करता है कि कौन सा प्रोड्यूसर चलेगा, ताकि आप किसी फोटो लाइब्रेरी से कभी बार चार्ट न माँगें। और चरण 5 वह जगह है जहाँ SEO टिका है — Google जो कुछ भी इमेज के बारे में दस्तावेज़ीकृत करता है (वर्णनात्मक alt, लाइसेंस मेटाडेटा, LCP-सुरक्षित हीरो) वह यहीं बनाया जाता है, उन फ़ील्ड्स से जो सर्च रिस्पॉन्स पहले ही ले आया था।
चरण 2: ड्राफ्ट को विज़ुअल ब्रीफ में बदलना
तैयार ड्राफ्ट पर एक मॉडल कॉल, और जो वह लौटाता है वह एक क्वेरी नहीं बल्कि एक प्लान होता है। एक अकेला फोटो ब्रीफ कैसे लिखें — क्यों लेख का शीर्षक सबसे खराब संभव इनपुट है, 12-से-25-शब्दों वाला कैमरा ब्रीफ कैसा दिखता है, और यह किन तरीकों से विफल हो सकता है — यह पूरी तरह एक लेख को इलस्ट्रेट करने की गाइड में बताया गया है, इसलिए यहाँ इसे दोहराया नहीं गया। एक पाइपलाइन जो अतिरिक्त चीज़ जोड़ती है वह है रूटिंग: उसी कॉल को स्लॉट-दर-स्लॉट यह तय करना होता है कि कौन-सा प्रोड्यूसर चलेगा — और जहाँ फोटो एंट्री एक दृश्य वहन करती है, वहीं चार्ट एंट्री डेटा वहन करती है।
# system prompt — run once per finished draft
You are the art director of a technical publication. Read the article and
return the visual plan: one entry for the hero, one per H2 section.
For each entry choose exactly one kind:
"photo" a real scene: someone doing something somewhere, a place,
an object, a gesture. The default for heroes.
"chart" the section states numbers that are IN the article. Never
invent values: copy them into data, verbatim.
"diagram" the section describes a structure, a flow or a sequence.
"none" the section is short, or already carries a code block.
Rules for "photo" entries — the field is query:
एक कैमरा ब्रीफ लिखें: एक ऐसा दृश्य जिसे कैमरा कैद कर सकता था, 12 से 25 शब्दों में,
अंग्रेज़ी में, जो सेक्शन के मूड से मेल खाता हो। फ़्रेम में जो कुछ है उसका नाम लें,
विषय का नहीं। कोई टेक्स्ट, लोगो, ब्रांड या मशहूर लोग नहीं; कोई अदृश्य
रूपक नहीं। (पूर्ण नियम, उदाहरणों और विफलता के मामलों सहित:
pexafy.com/blog/illustrate-blog-articles-at-scale/)
Do NOT write the alt text of a photo entry: the picture you get back is the
closest match to the brief, not the scene you described, so its alt has to be
written from the chosen photo. Chart and diagram entries DO carry an alt —
there you control exactly what is rendered.
Return JSON only:
{
"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 लाइन करती है, और बाकी काम data निर्देश करता है: कोई
चार्ट एंट्री केवल वही संख्याएँ वहन कर सकती है जो पहले से ड्राफ्ट में मौजूद हों, ताकि मॉडल गढ़ने के
बजाय ट्रांसक्राइब करे। यह रहा एक डेवलपर लेख के तीन असली सेक्शनों पर काम करता रूटर:
| सेक्शन | kind | राउटर ने क्या लौटाया |
|---|---|---|
| हीरो — “हमारा नाइटली बिल्ड 40 मिनट क्यों लेता है” | photo |
“a developer working late at a desk with two monitors and a mechanical keyboard in a dark room lit by the screens” |
| “समय वास्तव में कहाँ जाता है” | chart |
पैराग्राफ़ से कॉपी किया गया data: install 480 s, compile 1080 s, test 720 s, upload 120 s |
| “हमने ग्राफ़ को कैसे बाँटा” | diagram |
जॉब डिपेंडेंसी ग्राफ़ का flowchart LR |
| “हमने क्या बदला, और दोबारा क्या करेंगे” | photo |
“two engineers standing at a whiteboard covered in diagrams working through a problem together” |
चरण 3: तस्वीरें क्रेडिट के साथ वापस आती हैं
हर kind: "photo" प्रविष्टि एक अनुरोध है। ऊपर वाला हीरो ब्रीफ, पब्लिक API पर चलाया
गया, यह 147 ms में लौटाता है:
अंतिम सेक्शन ब्रीफ, बिल्कुल अलग दृश्य, 144 ms में:
प्रति फोटो जो वापस आता है वही चरण 5 को संभव बनाता है — केवल एक फ़ाइल नहीं:
{
"photo_id": "019e1c7f-0063-759e-b498-33ce1714e6c9", // store it: no repeats
"urls": { "small": "…?w=400", "regular": "…?w=1080",
"large": "…?w=1920" },
"width": 3000, "height": 1688, // → no layout shift
"blur_hash": "LJ8gjv9rVq-6OFxanNNFI7xco$Na", // → real placeholder
"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 (…)" }
}
चरण 4: जो एक फोटोग्राफ नहीं कह सकती
प्लान में दो स्लॉट सर्च करने योग्य नहीं हैं, और यही ठीक वह जगह है जहाँ दूसरा मॉडल अपनी जगह बनाता है — तस्वीर खींचने के लिए नहीं, बल्कि ऐसा कोड लिखने के लिए जो उसे खींचता है। यह अंतर मायने रखता है: कोड की समीक्षा की जा सकती है, यह डिटरमिनिस्टिक है और किसी बार की ऊँचाई में मतिभ्रम नहीं कर सकता।
डायग्राम: टेक्स्ट अंदर, SVG बाहर
Mermaid एक सादे टेक्स्ट परिभाषा से डायग्राम रेंडर करता है,3 जो इसे किसी मॉडल के लिए सबसे सुरक्षित लक्ष्य बनाता है: आउटपुट निरीक्षण-योग्य है, git में diff किया जा सकता है, और हर बार वैसे ही रेंडर होता है। राउटर स्पेक पहले ही लौटा चुका है।
# the "spec" field of a diagram entry, written to 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
# → an SVG you can review in the PR, not a picture you have to trust
चार्ट: केवल वे नंबर जो लेख में पहले से मौजूद हैं
वही सिद्धांत, एक अतिरिक्त सुरक्षा। राउटर ने वैल्यूज़ ड्राफ्ट से कॉपी कीं; मॉडल प्लॉटिंग कोड लिखता है; कोड सैंडबॉक्स में चलता है; असेंबलर चार्ट को किसी पेज के पास आने की अनुमति देने से पहले रेंडर की गई वैल्यूज़ को मूल नंबरों के मुक़ाबले फिर से जाँचता है।
import re, matplotlib
matplotlib.use("Agg") # headless: no display in CI
import matplotlib.pyplot as plt
def assert_in_draft(value, draft: str) -> None:
"""A plotted number must appear in the article AS A NUMBER."""
# Substring matching is the trap here: "120" is inside "1200", and
# inside "?w=1200" — a naive `str(v) in draft` passes on anything.
# Match on word boundaries, and accept 1 234 / 1,234 / 1234.
body = re.sub(r"(?<=\d)[ ,](?=\d{3}\b)", "", draft) # strip separators
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) # hallucinated value → no chart
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) # deterministic, reviewable artefact
return out
यह सुरक्षा-जाँच छोटी है, लेकिन इसे सावधानी से लिखें: सबस्ट्रिंग जाँच काम नहीं करती।
"120" in draft उस लेख के लिए भी सही है जिसमें 1200 या
?w=1200 मौजूद है, इसलिए सरल संस्करण हर चीज़ पर पास हो जाता है और किसी चीज़ की रक्षा
नहीं करता। अंक-सीमाओं पर आधार बनाएँ, हज़ार-विभाजक सामान्य करें, और वह त्रुटि जो एक तकनीकी लेख को
कमेंट्स में तार-तार करवा देती है — एक चार्ट जो अपने ही पैराग्राफ का खंडन करे — प्रोडक्शन तक नहीं
पहुँच सकती। स्क्रीनशॉट भी उसी सिद्धांत का पालन करते हैं: आपके असली बिल्ड के सामने चलाया गया
स्क्रिप्टेड page.screenshot() आपके अपने UI के लिए सत्य का एकमात्र स्रोत है, और
जैसे-जैसे UI बदलता है यह सच बना रहता है।
चरण 5: असेंबली ही वह जगह है जहाँ SEO टिका है
अब तक जो कुछ हुआ उसने फ़ाइलें और फ़ील्ड्स बनाईं। यह चरण उन्हें मार्कअप में बदलता है — और यहाँ सटीक होना ज़रूरी है, क्योंकि तीन दस्तावेज़ीकृत व्यवहार इन्हीं कुछ पंक्तियों में तय होते हैं।
<!-- The bytes come from another origin: pay the handshake early -->
<link rel="preconnect" href="https://images.unsplash.com" crossorigin>
<!-- LCP element: never lazy, always high priority -->
<figure>
<img src="{urls.regular}"
width="{width}" height="{height}" <!-- kills layout shift -->
alt="{alt}" <!-- written AFTER the pick -->
fetchpriority="high" decoding="async"
style="background:{color_hex}"> <!-- dominant colour, 1 field -->
<figcaption>{attribution.html}</figcaption>
</figure>
<!-- Section images, below the fold: the opposite settings -->
<img src="{urls.regular}" width="{width}" height="{height}"
alt="{alt}" loading="lazy" decoding="async">
हॉटलिंक या री-होस्ट? ऊपर वाला स्निपेट हॉटलिंक करता है, जो भेजने का सबसे तेज़
तरीका है और preconnect का कारण भी: एक रिमोट हीरो को क्रिटिकल पाथ पर एक DNS लुकअप
और एक TLS हैंडशेक की कीमत चुकानी पड़ती है, और यह उस लाभ को खा सकता है जो आपने अभी
fetchpriority से खरीदा। री-होस्टिंग थर्ड-पार्टी ओरिजिन को पूरी तरह हटा देती है,
आपको अपने ब्रेकपॉइंट्स पर AVIF/WebP परोसने देती है, और अपस्ट्रीम URL बदलने पर भी टिकी रहती है
— कीमत है स्टोरेज, पाइपलाइन में एक फ़ेच चरण और आपका अपना CDN बिल। आप जो भी चुनें,
color_hex आपको एक फ़ील्ड के लिए प्लेसहोल्डर देता है (ब्लर हैश ज़्यादा सुंदर होता
है, लेकिन उसे पहले data URI में डिकोड करना पड़ता है — यह CSS रंग नहीं है)। एक धुंधले हीरो को
सीधे background: में कॉपी कर देना वह जगह है जहाँ ज़्यादातर पाइपलाइनें चुपचाप एक
खाली ग्रे बॉक्स भेज देती हैं।
-
हीरो को कभी लेज़ी-लोड न करें। web.dev स्पष्ट है: “अपनी LCP इमेज को कभी
lazy-load न करें, क्योंकि इससे हमेशा अनावश्यक रिसोर्स लोड देरी होगी, और LCP पर नकारात्मक असर
पड़ेगा”, और यह उस एलिमेंट पर
fetchpriority="high"की सिफ़ारिश करता है जिसके LCP होने की संभावना हो — इसे मितव्ययिता से, एक ही इमेज पर इस्तेमाल करें।4 जो पाइपलाइन हर इमेज पर, हीरो सहित,loading="lazy"छाप देती है, वह स्वचालित प्रकाशन में सबसे आम स्वयं-लगाया गया Core Web Vitals घाव है। -
हमेशा
widthऔरheightउत्सर्जित करें — ये रिस्पॉन्स में वापस आते हैं, इसलिए कोई बहाना नहीं है; यही एक एट्रिब्यूट जोड़ी है जो ब्राउज़र को जगह आरक्षित करने देती है और लेआउट को उछलने से रोकती है। फ़ाइल लोड होते समय प्लेसहोल्डर के रूप मेंblur_hashइस्तेमाल करें। -
alt को पिक के बाद लिखें, पहले कभी नहीं। यह सूक्ष्म वाला बिंदु है।
प्लान की
altफ़ील्ड उस दृश्य का वर्णन करती है जो आपने माँगा था; जो फोटो आपको मिली वह निकटतम मिलान है, वह दृश्य नहीं। ब्रीफ के टेक्स्ट को alt के रूप में भेज देना ठीक वही एक्सेसिबिलिटी विफलता है जिसे यह पाइपलाइन टालने वाली थी — एक ऐसी तस्वीर का वर्णन जो पेज पर है ही नहीं। चुनी गई फोटो केalt_descriptionसे alt बनाएँ, उस पैराग्राफ़ के अनुरूप परिष्कृत करके जिसमें वह बैठती है। Google का मार्गदर्शन है “ऐसी उपयोगी, सूचना-समृद्ध सामग्री बनाने पर ध्यान दें जो कीवर्ड्स का उचित उपयोग करे और पेज की सामग्री के संदर्भ में हो”, और यह चेतावनी देता है कि alt एट्रिब्यूट में कीवर्ड ठूँसने से “एक नकारात्मक उपयोगकर्ता अनुभव बनता है और आपकी साइट को स्पैम के रूप में देखा जा सकता है।”1
वह हिस्सा जिसे लगभग कोई भी स्वचालित नहीं करता: लाइसेंस मेटाडेटा
Google इमेज लाइसेंसिंग के लिए ImageObject स्ट्रक्चर्ड डेटा को सपोर्ट करता है। इसे
contentUrl के साथ-साथ creator, creditText,
copyrightNotice या license में से कम से कम एक की ज़रूरत होती है,
acquireLicensePage की सिफ़ारिश करता है, और लाइसेंस जानकारी वाली इमेजेस Google Images
में Licensable बैज के लिए योग्य बन सकती हैं।5 इनमें से हर
फ़ील्ड पहले से सर्च रिस्पॉन्स में है — इसलिए इसे उत्सर्जित करना एक प्रोजेक्ट नहीं, एक टेम्पलेट है:
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "ImageObject",
"contentUrl": "{urls.large}", // required
"creator": { "@type": "Person",
"name": "{photographer_full_name}" },
"creditText": "{photographer_full_name} on {source}",
"license": "{LICENSE_URL[source]}", // the library's own page
"acquireLicensePage": "{source_image_url}" // the photo's page
}
</script>
# LICENSE_URL maps the `source` field to the licence that actually governs
# the photo — unsplash.com/license, pexels.com/license, pixabay.com/…
एक विवरण जिसे सही करना ज़रूरी है: license को उस लाइसेंस की ओर इशारा करना चाहिए
जो उस तस्वीर को नियंत्रित करता है — सोर्स लाइब्रेरी के अपने लाइसेंस पेज की ओर — आपके
डोमेन के किसी सारांश पेज की ओर नहीं। Google इसे पढ़कर बैज-योग्यता तय करता है, और एक
स्व-संदर्भित URL सिग्नल के रूप में कमज़ोर भी है और खुद तक वापस जाने वाले लिंक के अलावा किसी
और चीज़ के रूप में बचाव करना मुश्किल भी। पाठकों के लिए अपना खुद का लाइसेंस सारांश एक इंटरनल
पेज के रूप में रखें; मार्कअप में कैनोनिकल वाला डालें।
और जब आप असेंबलर में ही हैं: फ़ाइल को IMG_0042.jpg की बजाय एक छोटा वर्णनात्मक नाम
दें, और इमेज को एक साइटमैप में जोड़ें — Google का इमेज साइटमैप फ़ॉर्मेट प्रति पेज URL 1,000 तक
इमेजेस स्वीकार करता है।6 दोनों काम एक पाइपलाइन में एक-एक पंक्ति
के हैं और दोनों हाथ से कभी नहीं किए जाते।
चुराने के लिए दो पाइपलाइनें
वही पाँच चरण, दो बिल्कुल अलग आकार — एक उन लेखों के लिए जो आप लिखते हैं, एक उस डॉक्यूमेंटेशन के लिए जिसे एक चलते हुए प्रोडक्ट से मेल खाना चाहिए। वह चुनें जिसकी विफलता का तरीका आप पहचानते हैं।
1 · CI में डेव ब्लॉग — रेपो में Markdown
लेख Markdown के रूप में रहते हैं, इमेजेस उनके साथ ही कमिट की जाती हैं, और पूरा सिस्टम पुश पर चलता है। डिटरमिनिस्टिक, PR में समीक्षा-योग्य, किसी 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 # images land in the PR
with: { commit-message: "chore(content): illustrate" }
एक इंसान अभी भी PR को स्वीकृत करता है, और यही मुद्दा है: पाइपलाइन प्रस्ताव रखती है, समीक्षक निपटान करता है, और इसने जो फ्रंट-मैटर लिखा वह diff-योग्य है।
सर्च वाला आधा हिस्सा एक GET है — रिक्वेस्ट, उसका score_threshold और वे
फील्ड जो वह लौटाता है, यह सब
सिंगल-आर्टिकल
गाइड में लाइन-दर-लाइन लिखा गया है, इसलिए यहाँ उसे दोबारा छापने के बजाय इम्पोर्ट किया गया है।
यह फ़ाइल जो अतिरिक्त जोड़ती है वह वह सब कुछ है जिसकी एक प्लान को ज़रूरत होती है और एक अकेली
फोटो को नहीं: किसी ब्रीफ पर रिट्राई जो कुछ नहीं लौटाई, किसी photo_id पर दावा ताकि
कोई दो पेज एक ही तस्वीर साझा न करें, और वह alt टेक्स्ट जो मिली हुई फोटो से लिखा गया है।
import frontmatter
from photo_search import search # एक GET /search/photos; `used` में मौजूद ids को छोड़ देता है
def find_photo(entry: dict, used: set) -> dict | None:
"""Search; if the brief was too specific, widen it once, then give up."""
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"]) # claim it: no repeats site-wide
return photo
return None # caller decides: skip the slot, or fail
def widen(query: str) -> str:
"""Drop the last clause — usually the over-specific one."""
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: # no hero is better than a bad one
raise SystemExit(f"{path}: no photo above threshold — rewrite the brief")
post["hero"] = { # everything the template needs
"src": hero["urls"]["regular"], "w": hero["width"], "h": hero["height"],
# The alt describes the photo we GOT, never the scene we asked for.
"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 as the base, trimmed to ~125 chars for screen readers.
Send it back through the model with the paragraph if you want better."""
base = photo.get("alt_description") or photo.get("description", "")
return base[:125].rstrip(" ,;")
2 · डॉक्स और चेंजलॉग — पहले स्क्रीनशॉट, आखिर में तस्वीरें
राउटर की डिफ़ॉल्ट सेटिंग्स को उलट दें। प्रोडक्ट डॉक्यूमेंटेशन में ईमानदार विज़ुअल लगभग हमेशा आपका अपना UI होता है: एक Playwright स्क्रिप्ट जो असली बिल्ड खोलती है, एक निश्चित व्यूपोर्ट सेट करती है और पैराग्राफ़ जो स्थिति बताता है वही कैप्चर करती है। डायग्राम आर्किटेक्चर पेजों को कवर करते हैं, और फोटोग्राफ केवल कॉन्सेप्चुअल और लैंडिंग पेजों पर दिखाई देते हैं — जहाँ एक स्क्रीनशॉट कुछ नहीं कहेगा।
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-crisp
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()
# Runs in the same CI job as the docs build → the screenshot can never
# describe a version of the UI that no longer exists.
दो वैरिएंट, जिनका नाम लेना ज़रूरी है पर जिनकी अपनी अलग रेसिपी नहीं है।
प्रोग्रामैटिक SEO लूप को उलट देता है: डेटाबेस से जेनरेट किए गए सैकड़ों पेजों के
साथ आप प्रति पेज सर्च नहीं करते — एक अनुरोध 100 तक फोटो लौटाता है, इसलिए आप प्रति
टॉपिक क्लस्टर सर्च करते हैं और एक पूल से असाइन करते हैं, uniqueness constraint
डुप्लिकेशन हटाने का काम करता है
(वह आर्किटेक्चर, पूरा)।
और न्यूज़लेटर और सोशल कार्ड्स को चार क्रॉप में एक ही फोटो चाहिए: बस
photo_id सहेजें, urls से चाहिए वाला साइज़ माँगें, और तभी दोबारा सर्च
करें जब कोई क्रॉप वाकई विफल हो जाए।
वही पाइपलाइन, एक एजेंट के रूप में
यदि कोई मॉडल पहले से ही ड्राफ्ट लिख रहा है, तो सबसे छोटा रास्ता यह है कि प्रोसेस के बीच JSON को इधर-उधर भेजने के बजाय उसे सर्च टूल सीधे दे दिया जाए। Pexafy एक होस्टेड Model Context
Protocol सर्वर mcp.pexafy.com/mcp पर चलाता है। कनेक्टर सेटअप और इसके द्वारा उपलब्ध टूल्स का विवरण अन्यत्र दिया गया है — डेस्कटॉप और एडिटर सेटअप
सिंगल-आर्टिकल गाइड में,
CI रनर के लिए हेडलेस वेरिएंट
स्केल पर कंटेंट के आर्टिकल में,
और व्यापक कनेक्टर मार्केट कैसा दिखता है — कौन MCP इमेज सर्वर देता है, किन शर्तों पर —
यह AI एजेंट्स के लिए इमेज सर्च इंफ्रास्ट्रक्चर के अध्ययन में है।
यहाँ दिखाने लायक बात यह है कि एक बार एजेंट के पास टूल्स आ जाने पर इस पाइपलाइन का क्या होता है: यह पाँच चरण होने के बजाय एक ही इंस्ट्रक्शन बन जाता है।
You Here is the draft. Build the visual plan, illustrate it, and open a PR.
Charts only from numbers already in the text.
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 photos · 147 ms · picked #1, 3000×1688, credited
→ mermaid-cli build/graph.mmd → static/img/graph.svg
→ chart.py §2 → 4 values checked against the draft ✓
→ search_photos(q="two engineers standing at a whiteboard…")
← 16 photos · 144 ms · picked #1
✓ 4 slots filled · 2 API calls · alt + credit + ImageObject written
⚠ §5 returned nothing above 0.5 — brief too abstract, rewritten as
"a person at a kitchen table checking figures on a laptop"
यह आखिरी पंक्ति वह वजह है जिसके चलते इस लूप में एक एजेंट रखना एक शुद्ध स्क्रिप्ट से बेहतर है: स्वचालित इलस्ट्रेशन की विफलता का तरीका एक ख़राब ब्रीफ है, और एक ख़राब ब्रीफ को फिर से लिखना ठीक वही है जिसके लिए एक भाषा मॉडल बना है। डिटरमिनिस्टिक हिस्सों — असाइनमेंट, डुप्लिकेशन हटाना, न्यूमेरिक गार्ड — को कोड में ही रखें।
एक इलस्ट्रेटेड लेख की कीमत क्या है
प्रति लेख तीन फोटो स्लॉट का मतलब है तीन सर्च अनुरोध, इसलिए फ्री प्लान (5,000 अनुरोध/माह) 1,666 लेख प्रति माह कवर कर लेता है, इससे पहले कि भुगतान का कोई सवाल उठे — और यदि आप प्रति लेख के बजाय प्रति टॉपिक क्लस्टर सर्च को पूल करें, तो यह सीमा एक और क्रम आगे बढ़ जाती है। प्लान-दर-प्लान विवरण, और वह पूलिंग आर्किटेक्चर जो इसे बेमानी बना देता है, यहाँ हैं: बड़े पैमाने पर कंटेंट इलस्ट्रेट करने वाला साथी लेख.
प्रति लेख दो मॉडल कॉल्स — एक ड्राफ्ट के लिए, एक विज़ुअल प्लान के लिए — कुछ हज़ार टोकन के बराबर हैं और पाइपलाइन की सबसे सस्ती पंक्ति होंगी; Mermaid और matplotlib रेंडर की कीमत कुछ नहीं, बस CI सेकंड लगते हैं। जो नंबर लोगों को चौंकाता है वह यह है कि कौन सी पंक्ति सस्ती नहीं है: प्रति लेख चार इमेजेस जेनरेट करना, महीने में चार सौ इमेजेस, साथ ही वे प्रयास जो सफल नहीं हुए — और आउटपुट के पास फिर भी कोई फोटोग्राफर नहीं, कोई तारीख नहीं और कोई सोर्स URL नहीं होता।
यह पाइपलाइन क्या ठीक नहीं करती
एक ईमानदार सेक्शन, क्योंकि जिस विफलता को यह रोकता है वह महँगी है। एक अच्छी तरह इलस्ट्रेटेड लेख फिर भी एक लेख ही है: असली फोटोग्राफी, सटीक चार्ट और सही मार्कअप उस पेज को बेहतर बनाते हैं जिसका अस्तित्व में होना जायज़ है। वे पतली, बड़े पैमाने पर बनाई गई सामग्री को रैंक नहीं कराते। Google की स्पैम नीतियाँ scaled content abuse को नाम देती हैं — मुख्यतः रैंकिंग में हेरफेर करने के लिए बहुत सारे पेज जेनरेट करना और उपयोगकर्ताओं को बहुत कम मूल्य देना, चाहे इसमें ऑटोमेशन शामिल हो या न हो — और उस निर्णय में इलस्ट्रेशन की गुणवत्ता कोई कारक नहीं है।7
तो जो फ़्रेमिंग टिकती है: यह पाइपलाइन एक गुणवत्ता-तल है जिसे आप नियंत्रित करते हैं, उन पेजों पर लागू, जिनके प्रकाशित होने का पहले से एक कारण है। जहाँ यह प्रदर्शन-योग्य रूप से फ़ायदा पहुँचाता है:
- ऐसा उद्गम जिसे पाठक सत्यापित कर सके। एक असली फोटोग्राफर और एक सोर्स URL
वाली क्रेडिट लाइन एक ऐसा दावा है जिसकी जाँच की जा सकती है — और वही फ़ील्ड्स उस
ImageObjectमार्कअप को खिलाती हैं जिसे Google पढ़ता है। - एक्सेसिबिलिटी और Core Web Vitals। असली alt टेक्स्ट, हर इमेज पर डाइमेंशन, एक हीरो जिसे कभी लेज़ी-लोड नहीं किया जाता। आपके द्वारा प्रकाशित हर लेख से गुणा करें, और यही साइट की इमेज-गुणवत्ता कहानी बन जाती है।
- जहाँ जाँच संभव हो वहाँ सटीकता। एक चार्ट जिसके नंबर लेख के मुक़ाबले जाँचे गए हैं, चलते हुए बिल्ड से जेनरेट किया गया एक स्क्रीनशॉट, PR में टेक्स्ट के रूप में समीक्षा-योग्य एक डायग्राम — तीन विज़ुअल जो एक टेस्ट फ़ेल हुए बिना सच से भटक नहीं सकते।
क्रेडिट के मामले में, पाइपलाइन-विशिष्ट बिंदु संकीर्ण है:
क्रेडिट को हमेशा
दिखाने का तर्क कहीं और दिया गया है, और एक ऑटोमेटेड असेंबलर जो अतिरिक्त जोड़ता है वह यह है कि जो
attribution स्ट्रिंग वह प्रिंट करता है वही स्ट्रक्चर्ड डेटा के लिए जरूरी
creditText भी है। एक फील्ड, दो जगह, एक ही टेम्पलेट पास में उत्पन्न — यही वजह है कि
किसी इंसान के मुकाबले पाइपलाइन के पास इसे छोड़ने का बहाना और भी कम बचता है।
कहाँ से शुरू करें
- राउटर प्रॉम्प्ट जोड़ें जो कुछ भी पहले से आपके ड्राफ्ट लिखता है, और उस पर कार्रवाई किए बिना JSON प्रिंट करें। दस प्लान पढ़ें। यदि ब्रीफ दृश्यों के बजाय टॉपिक्स के नाम लेती हैं, तो किसी भी इंटीग्रेशन को लिखने से पहले प्रॉम्प्ट ठीक करें।
- केवल फोटो स्लॉट को जोड़ें। प्रति ब्रीफ एक
GET /search/photos, और पहले दिन से हीphoto_idसहेजें — 3,000 लाइव पेजों पर बाद में जोड़ी गई डुप्लिकेशन-रोकथाम एक माइग्रेशन है, कोई कॉलम नहीं। - width, height और alt उसी कमिट में उत्सर्जित करें। यह अब तक का सबसे सस्ता Core Web Vitals काम है जो आप कभी करेंगे।
- फिर चरण 4 जोड़ें, चार्ट से पहले डायग्राम — Mermaid टेक्स्ट है, इसलिए एक सिंटैक्स एरर के अलावा इसका कोई विफलता तरीका नहीं है।
- न्यूमेरिक गार्ड जोड़ें पहला चार्ट पाठक तक पहुँचने से पहले, बाद में नहीं।
संदर्भ और फ़ुटनोट्स
1 Google Search Central, Image SEO best practices: alt
टेक्स्ट “किसी इमेज के लिए मेटाडेटा प्रदान करने के मामले में सबसे महत्वपूर्ण एट्रिब्यूट है”;
मार्गदर्शन है “ऐसी उपयोगी, सूचना-समृद्ध सामग्री” बनाना “जो कीवर्ड्स का उचित उपयोग करे और पेज
की सामग्री के संदर्भ में हो”, कीवर्ड-ठूँसे गए alt एट्रिब्यूट से बचना, CSS इमेज के बजाय HTML
<img> एलिमेंट का इस्तेमाल करना, और फ़ाइलों को छोटे पर वर्णनात्मक नाम देना।
2 EU AI Act, Article 50 — 2 अगस्त 2026 से लागू पारदर्शिता दायित्व: सिंथेटिक इमेज, ऑडियो, वीडियो या टेक्स्ट जेनरेट करने वाले सिस्टम के प्रदाताओं को आउटपुट को मशीन-पठनीय फ़ॉर्मेट में चिह्नित करना और उन्हें कृत्रिम रूप से जेनरेट किए गए के रूप में पहचान-योग्य बनाना ज़रूरी है। यह AI प्रदाताओं और डिप्लॉयर्स को बाँधता है; यह कोई नियम नहीं है कि कोई वेबसाइट कौन सी इमेजेस प्रकाशित कर सकती है।
3 Mermaid Markdown-प्रेरित टेक्स्ट परिभाषाओं से डायग्राम और चार्ट रेंडर करता है, जो आउटपुट को समीक्षा-योग्य और डिटरमिनिस्टिक बनाता है।
4 web.dev, Optimize Largest Contentful Paint: “यदि आपको
लगता है कि कोई <img> एलिमेंट आपके पेज का LCP एलिमेंट होने वाला है, तो उस
पर fetchpriority="high" सेट करना अच्छा विचार है”, इसे मितव्ययिता से इस्तेमाल करें;
और “अपनी LCP इमेज को कभी lazy-load न करें, क्योंकि इससे हमेशा अनावश्यक रिसोर्स लोड देरी होगी,
और LCP पर नकारात्मक असर पड़ेगा।”
5 Google Search Central, Image metadata (structured data):
ImageObject को contentUrl के साथ-साथ creator,
creditText, copyrightNotice या license में से कम से कम
एक की ज़रूरत होती है; acquireLicensePage की सिफ़ारिश की जाती है, और लाइसेंस
जानकारी वाली इमेजेस Google Images में Licensable बैज के लिए योग्य बन सकती हैं।
6 Google Search Central, Image sitemaps: इमेज साइटमैप Google को किसी साइट पर मौजूद इमेजेस के बारे में सूचित करते हैं, जिनमें JavaScript के ज़रिए मिलने वाली इमेजेस भी शामिल हैं, और प्रति पेज URL 1,000 तक इमेजेस स्वीकार करते हैं।
7 Google Search spam policies — scaled content abuse: मुख्यतः रैंकिंग में हेरफेर करने और उपयोगकर्ताओं को बहुत कम मूल्य देने के लिए बहुत सारे पेज जेनरेट करना, चाहे वे ऑटोमेशन, मानवीय प्रयास या दोनों के संयोजन से बनाए गए हों।
स्रोतों की जाँच 17 अगस्त 2026 को की गई: Image SEO best practices · Image metadata · Image sitemaps · Optimize LCP · Search spam policies · AI Act Article 50 · Mermaid · Pexafy API & MCP docs. सर्च की टाइमिंग (147 ms, 144 ms) और दिखाई गई हर तस्वीर उसी दिन कैप्चर की गई असली API प्रतिक्रियाएँ हैं।
अक्सर पूछे जाने वाले प्रश्न
मैं LLM द्वारा लिखे गए लेखों को स्वचालित रूप से कैसे इलस्ट्रेट करूँ?
GET /api/v1/search/photos, लगभग 150 ms) को भेजते हैं। चार्ट और डायग्राम एंट्रीज़ उस मॉडल के पास जाती हैं जो प्लॉटिंग कोड या Mermaid लिखता है, जिसे डिटरमिनिस्टिक रूप से रेंडर किया जाता है। लेख के टाइटल को कभी इमेज सर्च में न डालें: टाइटल अमूर्त होते हैं और कोई फ़ोटोग्राफ़ उन्हें नहीं दिखाता।क्या डॉक्यूमेंटेशन साइट को स्क्रीनशॉट या स्टॉक फ़ोटो इस्तेमाल करने चाहिए?
मैं AI पाइपलाइन को चार्ट में गलत संख्याएँ डालने से कैसे रोकूँ?
data फ़ील्ड में। फिर रेंडर करने से पहले इसे कोड में असर्ट करें — हर वैल्यू के लिए, चेक करें कि उसकी स्ट्रिंग लेख में मौजूद है, और अन्यथा एरर उठाएँ। नौ लाइनें, और एक चार्ट जो अपने ही पैराग्राफ के विरुद्ध जाता है, कभी किसी पाठक तक नहीं पहुँच सकता।SEO के लिए एक स्वचालित पाइपलाइन को कौन सा इमेज मार्कअप जनरेट करना चाहिए?
alt — Google alt टेक्स्ट को सबसे महत्वपूर्ण इमेज मेटाडेटा कहता है और कीवर्ड स्टफिंग के खिलाफ चेतावनी देता है। हर इमेज पर width और height, हीरो पर fetchpriority="high" के साथ और उस पर कभी loading="lazy" नहीं, क्योंकि LCP इमेज को लेज़ी लोड नहीं किया जाना चाहिए। और ImageObject स्ट्रक्चर्ड डेटा जिसमें contentUrl, creator, creditText और license हों, जो एक इमेज को Google Images में Licensable बैज के लिए योग्य बनाता है।क्या AI-लिखित लेखों को इलस्ट्रेट करने से उनकी रैंकिंग में मदद मिलती है?
एडिटोरियल कंट्रोल खोए बिना CI में इलस्ट्रेशन स्टेप कैसे चलाऊं?
photo_id से डीडुप्लीकेशन, यह पुष्टि कि प्लॉट किया गया हर नंबर आर्टिकल में मौजूद है, और जब कोई फ़ोटो स्कोर थ्रेशोल्ड पार नहीं करती तो एक हार्ड फ़ेलियर। कोई हीरो न होना गलत हीरो से बेहतर है।