প্রকৃত ছবি দিয়ে মাসে ১০,০০০ পেজ ইলাস্ট্রেট করুন — মাত্র ৫০০ API কলে

প্রতিটি ছবির জন্য নয়, বরং প্রতিটি টপিক ক্লাস্টারের জন্য একবার সার্চ করুন, আর ৪০,০০০ API কল হয়ে যায় ৫০০টি। পুল-অ্যান্ড-অ্যাসাইন প্যাটার্ন, রেট লিমিট সামলাতে সক্ষম ওয়ার্কার, এবং ছবি দিয়ে কী ঠিক করা যায় না তার সৎ আলোচনা।

শেয়ার করুন
একটি ব্যস্ত অফিস যেখানে সহকর্মীরা লম্বা শেয়ার করা ডেস্কে পাশাপাশি বসে কম্পিউটারে কাজ করছেন।
ছবি সৌজন্যে Unsplash

কনটেন্ট-ইলাস্ট্রেশন সমস্যার এমন একটি সংস্করণ আছে যা ভালো রুচি দিয়ে সমাধান করা যায় না: আপনি একটা ছবি বাছাই করছেন না, আপনি মাসে ৮০০টি টপিক ক্লাস্টার, ৯টি লোকেল এবং ১৪টি ক্লায়েন্ট সাইট জুড়ে ৪০,০০০টি ইমেজ স্লট পূরণ করছেন, এবং তাদের প্রতিটির লাইসেন্স, ক্রেডিট, সাইজ ঠিক থাকতে হবে এবং পাশেরটির থেকে ভিন্ন হতে হবে। এই ভলিউমে প্রশ্নটা আর “কোন ছবি?” থাকে না, প্রশ্নটা হয়ে যায় “স্থাপত্য (architecture) কী হবে?”

এই আর্টিকেলটি সেই API-ভিত্তিক উত্তর: এমন একটি রিকোয়েস্ট প্যাটার্ন যা কল সংখ্যা দুই অর্ডার অফ ম্যাগনিটিউড কমিয়ে দেয়, এমন একটি ওয়ার্কার যা রেট লিমিট সহ্য করে টিকে থাকে, এমন একটি ডিডুপ্লিকেশন যা ১০,০০০ পৃষ্ঠার সাইটকে একই স্টক ফটোর দেয়াল দেখতে হওয়া থেকে রক্ষা করে — এবং ছবি আপনার জন্য কী করতে পারে না তার একটি সৎ আলোচনা।

১০,০০০ আর্টিকেলে প্রকৃত বাধাটি কোথায়

ভলিউমে প্রকাশ করা টিমগুলো — প্রোগ্রাম্যাটিক SEO এস্টেট, মার্কেটপ্লেস ক্যাটাগরি পেজ, অ্যাগ্রিগেটর, অ্যাফিলিয়েট নেটওয়ার্ক, একাধিক ক্লায়েন্টের কনটেন্ট চালানো এজেন্সি, একটি আর্টিকেলকে ২০টি ভাষায় রূপান্তরিত করা লোকালাইজেশন পাইপলাইন — সবাই একই ক্রমে একই তিনটি দেয়ালে আটকায়:

  1. কল সংখ্যা। প্রতি ইমেজ স্লটের জন্য একটি সার্চ মানে ১০,০০০টি চার-ছবি-ওয়ালা আর্টিকেলের জন্য ৪০,০০০টি API কল। প্রতিটি প্রোভাইডার আপনাকে এই সংখ্যার ভিত্তিতেই দাম নির্ধারণ, থ্রটল এবং রিভিউ করে।
  2. পুনরাবৃত্তি। মোটামুটি ত্রিশতম পৃষ্ঠার কাছাকাছি একই ছবি আবার দেখা দিতে শুরু করে। তিন হাজারতম পৃষ্ঠায় গিয়ে আপনার সাইটের একটি ভিজ্যুয়াল স্বাক্ষর তৈরি হয়ে যায়: বাল্কে তৈরি।
  3. সমন্বয়। একটি ক্লাস্টারের একশোটি আর্টিকেলকে একটি সিরিজের মতো দেখাতে হবে, একশোটি অসংশ্লিষ্ট Pinterest বোর্ডের মতো নয় — এবং একই আর্টিকেলের পরবর্তী লোকেলে একই ছবি পুনরায় ব্যবহার করা উচিত, অন্য ভাষায় আবার সার্চ করা নয়।

খেয়াল করুন কোনটি এই তালিকায় নেই: ভালো ছবি খুঁজে পাওয়া। একটি সিমান্টিক ইঞ্জিন প্রায় ১৩০ মিলিসেকেন্ডে ব্যবহারযোগ্য প্রার্থীদের একটি পৃষ্ঠা ফেরত দেয়। রিট্রিভাল সমস্যাটি সমাধান হয়েই গেছে; ডিস্ট্রিবিউশন সমস্যা তো হয়নি। নিচের সবকিছুই ডিস্ট্রিবিউশন নিয়ে।

নির্দিষ্ট করে এই ভলিউমে কেন প্রকৃত ছবি

জেনারেশনের বদলে ফটোগ্রাফির পক্ষে যুক্তি ভলিউম বাড়ার সাথে সাথে আরও শক্তিশালী হয়, যার কারণগুলো মূলত নান্দনিক নয়, বরং অপারেশনাল।

মাসে ৪০,০০০ ছবিতে জেনারেট করা সার্চ করা
প্রতি ছবির সময় সেকেন্ড থেকে মিনিট, তার সাথে প্রত্যাখ্যাত প্রচেষ্টাগুলোও একটি রিকোয়েস্ট (~১৩০ ms) পুরো একটি ক্লাস্টার কভার করে
খরচের নির্ধারক প্রতি ছবিতে, চিরকাল প্রতি রিকোয়েস্টে — এবং একটি রিকোয়েস্ট ~২৫টি আর্টিকেলকে সার্ভ করে
আপনি যে মেটাডেটা পান কিছুই না। alt টেক্সট এবং ডাইমেনশন আপনাকে নিজেই লিখতে হয় ডাইমেনশন, প্রধান রঙ, ব্লার হ্যাশ, ক্যাপশন খসড়া, ক্রেডিট লাইন
প্রোভেন্যান্স ২ আগস্ট ২০২৬ থেকে EU AI Act অনুযায়ী মেশিন-চিহ্নিত সিনথেটিক1 একজন নামসহ ফটোগ্রাফার, একটি তারিখ, একটি সোর্স URL যা পাঠক খুলে দেখতে পারেন
ব্যর্থতার ধরন বিশ্বাসযোগ্য কিন্তু ভুল খুঁটিনাটি, অভিন্ন হাউস স্টাইল ব্রিফের সাথে কিছুই মেলেনি — আপনি শূন্য ফলাফল পান, যা সামলানো সম্ভব

মেটাডেটার সারিটিই পাইপলাইন নির্ধারণ করে। প্রতিটি সার্চ ফলাফল এমনিতেই width, height, blur_hash, color_hex, alt_description এবং একটি রেডি-মেড attribution.html স্ট্রিং বহন করে — যা একটি টেমপ্লেটিং ইঞ্জিনের জন্য ঠিক সেই পেলোড যা প্লেসহোল্ডার এবং ক্রেডিটসহ লেআউট-শিফট-মুক্ত একটি <img> তৈরি করতে দরকার। জেনারেটেড ইমেজ আপনাকে শুধু একটি ফাইল দেয় এবং বাকি ছয়টি ফিল্ড আবিষ্কার করার দায়িত্ব আপনার উপর ছেড়ে দেয়।

যেখানে জেনারেশন এখনও স্কেলে জিতে যায়। বিমূর্ত ট্যাক্সোনমির (“ক্লাউড মাইগ্রেশন”, “ইনডেক্স ফান্ড”) জন্য ক্যাটাগরি-স্তরের ইলাস্ট্রেশন, ডায়াগ্রাম, এবং ব্র্যান্ডের কারণে পুরো এস্টেট জুড়ে পুনরাবৃত্ত রাখতে চাওয়া একটি হাউস স্টাইল। বড় পাবলিশাররা সাধারণত যে বিভাজনে এসে থামেন: বাস্তব জগতে যা কিছু আছে তার জন্য ফটোগ্রাফ, যা শুধু যুক্তিতে বিদ্যমান তার জন্য জেনারেটেড আর্ট। আমরা এই বিভাজনের পূর্ণ যুক্তি দিয়েছি প্রতিটি প্রকাশিত আর্টিকেলকে কীভাবে ইলাস্ট্রেট করবেন সেই আর্টিকেলে।

একটি রিকোয়েস্ট, একশো ছবি

এটিই একমাত্র পরিবর্তন যা গণনাটির পুরো চেহারা বদলে দেয়। সার্চ এন্ডপয়েন্ট per_page ১০০ পর্যন্ত নেয়, এবং কার্সর পেজিনেশন আপনাকে একই র‍্যাঙ্কড ফলাফল সেটের মধ্য দিয়ে এগিয়ে যেতে দেয়। তাই কাজের একক একটি ছবি নয়, একটি পুল।

একটি রিকোয়েস্ট → একটি সম্পূর্ণ ক্লাস্টারের জন্য পুল
curl -sG "https://api.pexafy.com/api/v1/search/photos" \
  -H "X-Api-Key: $PEXAFY_API_KEY" \
  --data-urlencode "q=a woman signing paperwork with an insurance agent at a kitchen table in her home" \
  --data-urlencode "per_page=100" \
  --data-urlencode "orientation=landscape" \
  --data-urlencode "score_threshold=0.5" \
  --data-urlencode "fields=photo_id,urls,width,height,blur_hash,alt_description,attribution"

# → { "success": true, "data": [ …১০০টি পর্যন্ত ছবি… ],
#     "pagination": { "per_page": 100, "has_more": true, "next_cursor": "eyJyc…" },
#     "meta": { "took_ms": 122.33, "request_id": "7ff1fb9b-…" } }

এখানে তিনটি প্যারামিটার চুপচাপ বড় ভূমিকা পালন করছে স্কেলে:

  • fields — একটি স্পার্স ফিল্ডসেট। আপনার টেমপ্লেট যে সাতটি ফিল্ড রেন্ডার করে সেগুলো চান, তাহলে রেসপন্স আর প্রতিটি ছবির দীর্ঘ AI বিবরণ পাঠানো বন্ধ করে দেয়। ৪০,০০০ ছবি জুড়ে এটাই স্ট্রিম হওয়া বিল্ড আর সোয়াপ হওয়া বিল্ডের মধ্যে পার্থক্য।
  • score_threshold — একটি ১০০-ফলাফলের পৃষ্ঠার লেজের অংশ, সংজ্ঞা অনুযায়ীই, মাথার অংশের চেয়ে কম প্রাসঙ্গিক। একটি সীমা নির্ধারণ মানে একটি পাতলা ক্লাস্টার ১০০টি মাঝারি মানের ছবির বদলে ৩৪টি ছবি ফেরত দেয়, এবং আপনার পাইপলাইন সেটি প্রকাশ করার বদলে সেই মতো প্রতিক্রিয়া জানাতে পারে।
  • next_cursor — যখন একটি ক্লাস্টারের সত্যিই ৩০০টি ছবি দরকার হয়, তখন ওভারল্যাপ করা তিনটি আলাদা কোয়েরি চালানোর বদলে একই র‍্যাঙ্কড সেট পেজিনেট করুন।

আর এই হলো গণনাটি, মাসে চারটি করে ছবিসহ ১০,০০০ পৃষ্ঠা প্রকাশ করা একটি সাইটের জন্য:

কৌশল রিকোয়েস্ট / মাস ফ্রি প্ল্যানের রেট লিমিটে একবারে সম্পন্ন করতে20 রিকোয়েস্ট/মিনিট ফ্রি মাসিক কোটায় ফিট হয়?
প্রতি ইমেজ স্লটে একটি সার্চ ৪০,০০০ 33 hours না — একটি পেইড প্ল্যান দরকার
প্রতি আর্টিকেলে একটি সার্চ ১০,০০০ 8 hours না — একটি পেইড প্ল্যান দরকার
~২০টি আর্টিকেলের একটি ক্লাস্টারে একটি সার্চএই আর্টিকেলের প্যাটার্ন ৫০০ 25 minutes হ্যাঁ

তিনটি সারিতেই একই ৪০,০০০টি প্রকাশিত ছবি। একমাত্র যা পরিবর্তিত হয়েছে তা হলো লুপটি কোথায় বসানো হয়েছে।

পুল আর অ্যাসাইন: যে প্যাটার্ন স্কেল করে

পুরো আর্কিটেকচারটি দুটি পর্যায়ে বিভক্ত যা ভিন্ন ভিন্ন ফ্রিকোয়েন্সিতে চলে, এবং তাদের মধ্যে একটি টেবিল থাকে:

এর গঠন
                     ┌─────────── সাপ্তাহিক চলে, ~৫০০ রিকোয়েস্ট ─────────┐
  topic clusters ──▶ POOL   প্রতি ক্লাস্টারে একবার সার্চ, per_page=100
                     │       └─▶ প্রতি ক্লাস্টারে ১০০টি ছবির সারি সংরক্ষণ
                     └──────────────────┬───────────────────────────────┘
                                        ▼
                              image_pool টেবিল
                        (cluster, photo_id, urls, blur_hash,
                         alt, credit, used_by_page, used_at)
                                        │
                     ┌──────────────────┴──── প্রতি প্রকাশনায় চলে, ০ রিকোয়েস্ট ──┐
  article ─────────▶ ASSIGN  এই ক্লাস্টারের জন্য সেরা অব্যবহৃত
                     │        সারি বেছে নিন, ব্যবহৃত চিহ্নিত করুন, রেন্ডার করুন
                     └────────────────────────────────────────────────────────┘

এতে যা পাওয়া যায়, ভলিউমে গুরুত্বের ক্রম অনুসারে:

  1. প্রকাশনা কখনো API-তে আটকে থাকে না। অ্যাসাইনমেন্ট একটি ডেটাবেস রিড। আপনার CMS সেভ হুক, আপনার স্ট্যাটিক বিল্ড এবং আপনার ভোর ৩টার বাল্ক ইম্পোর্ট — সবই স্থানীয় গতিতে, অফলাইনে, পথে কোনো রেট লিমিট ছাড়াই চলে।
  2. ডিডুপ্লিকেশন বিনামূল্যে এবং নিখুঁত। used_by_page IS NULL এই পুরো ফিচার। দুটি পৃষ্ঠা একই ছবি নিতে পারে না, কারণ অ্যাসাইনমেন্ট একটি ট্রানজ্যাকশন, কোনো র‍্যাঙ্কিং হিউরিস্টিক নয়।
  3. পুনরায় চালানো সস্তা এবং আইডেম্পোটেন্ট। একটি ক্লাস্টারের পুল কম পড়লে বা আপনি নতুন ফটোগ্রাফি চাইলে (after_date, বা sort_by=newest) সেটি রিফ্রেশ করুন — একটি রিকোয়েস্ট, এবং ইতিমধ্যে প্রকাশিত কিছুই নড়ে না।
  4. লোকেল বিনামূল্যে আসে। একটি আর্টিকেলের ২০টি অনুবাদ আপনার মডেলে একটি পৃষ্ঠা, ২০টি রেন্ডারিং সহ; তারা একই বরাদ্দকৃত photo_id শেয়ার করে এবং আপনাকে কখনো দুবার সার্চ করতে হয় না। আপনি যখন সত্যিই স্থানীয়ভাবে তোলা লুক চান, তখন সেই ক্লাস্টারের জন্য পুল কোয়েরিটি টার্গেট ভাষায় চালান — ইঞ্জিন ১০০টিরও বেশি ভাষায় একটি বাক্য নেয়।

কোডে ওয়ার্কার

মোটামুটি ষাট লাইন। পুল ফিলারই একমাত্র অংশ যা নেটওয়ার্কের সাথে কথা বলে, তাই এটিই একমাত্র অংশ যেখানে সতর্ক থাকতে হয় — কনকারেন্সি সীমাবদ্ধ, 429 সম্মানিত, ফলাফল একটি ট্রানজ্যাকশনে লেখা।

pool.py — বিনয়ের সাথে প্রতি ক্লাস্টারে একটি পুল পূরণ করুন
import asyncio, os, time, httpx

SEARCH = "https://api.pexafy.com/api/v1/search/photos"
KEY    = os.environ["PEXAFY_API_KEY"]
FIELDS = "photo_id,urls,width,height,blur_hash,alt_description,attribution"

# ফ্রি = 20 রিকোয়েস্ট/মিনিট। এক নিচে থাকুন। পেসার GLOBAL: কনকারেন্সি
# গেটের ভেতরে পার-টাস্ক স্লিপ থাকলে N ওয়ার্কার N× হারে ফায়ার করবে।
PER_MIN = 19
gate    = asyncio.Semaphore(4)         # সচল কানেকশন
_lock   = asyncio.Lock()
_slot   = 0.0                          # শেয়ার্ড টাইমলাইনে পরবর্তী ফাঁকা মুহূর্ত

async def pace():
    """পুরো ফ্লিট জুড়ে প্রতি 60/PER_MIN সেকেন্ডে একটি রিকোয়েস্ট স্লট দিন।"""
    global _slot
    async with _lock:
        now  = time.monotonic()
        _slot = max(now, _slot) + 60 / PER_MIN
        wait  = _slot - now
    if wait > 0:
        await asyncio.sleep(wait)

class QuotaExhausted(Exception): ...   # মাসিক: রিট্রাই করে লাভ নেই

async def fill_pool(client, cluster) -> list[dict]:
    """একটি রিকোয়েস্ট → একটি টপিক ক্লাস্টারের জন্য ১০০টি পর্যন্ত ক্রেডিটযুক্ত ছবি।"""
    for attempt in range(4):
        await pace()
        async with gate:
            r = await client.get(
                SEARCH,
                headers={"X-Api-Key": KEY},
                params={
                    "q": cluster["camera_brief"],   # একটি দৃশ্য, কিওয়ার্ড নয়
                    "per_page": 100,
                    "orientation": "landscape",
                    "score_threshold": 0.5,
                    "fields": FIELDS,
                },
                timeout=15,
            )
        if r.status_code == 429:
            retry_after = r.headers.get("Retry-After")
            if retry_after is None:            # Retry-After নেই ⇒ মাসিক কোটা,
                raise QuotaExhausted(cluster["id"])   # পুরো রান বন্ধ করুন
            await asyncio.sleep(int(retry_after))   # প্রতি মিনিটে: এটা পরিষ্কার হয়ে যায়
            continue
        r.raise_for_status()
        return r.json()["data"]
    return []                                  # লগ করুন; পুল পুরনো সারিগুলো ধরে রাখে

async def refill(clusters):
    async with httpx.AsyncClient(http2=True) as client:
        pools = await asyncio.gather(*(fill_pool(client, c) for c in clusters))
    for cluster, photos in zip(clusters, pools):
        db.upsert_pool(cluster["id"], photos)     # photo_id-তে ON CONFLICT DO NOTHING

এখানে দুটি খুঁটিনাটি বিষয় আছে যা একটি ওয়ার্কারকে অ্যাটেন্ডেড ছাড়া চলার আর আপনাকে মাঝরাতে জাগিয়ে তোলার মধ্যকার পার্থক্য গড়ে দেয়। পেসারটি গ্লোবাল, প্রতি-টাস্ক নয়: কনকারেন্সি গেটের ভেতরে স্লিপ বসানো একটি ক্লাসিক ভুল — চারটি ওয়ার্কার প্রত্যেকে তাদের নিজের রিকোয়েস্টের পর ৩ সেকেন্ড থামলে প্রতি ৩ সেকেন্ডে চারটি রিকোয়েস্ট তৈরি হয়, প্রায় প্রতি মিনিটে ৭৫টি, এবং ফ্রি প্ল্যান সাথে সাথে 429 ফেরত দেওয়া শুরু করে। আর দুই ধরনের 429 একই প্রাণী নয়: প্রতি-মিনিটেরটি Retry-After বহন করে এবং নিজে থেকে পরিষ্কার হয়ে যায়, মাসিক-কোটারটি এটি বহন করে না এবং কখনো করবেও না — সেটি রিট্রাই করা একটি দেয়ালের বিরুদ্ধে লুপ চালানোর মতো।

অ্যাসাইনমেন্ট, যা প্রতি প্রকাশনায় চলে, নেটওয়ার্ক কখনো ছোঁয় না:

assign.py — নির্ধারক, ট্রানজ্যাকশনাল, কোথাও কোনো পুনরাবৃত্তি নেই
# স্বতন্ত্রতা কোয়েরির সতর্কতা দিয়ে নয়, স্কিমা দিয়ে নিশ্চিত করা হয়।
# একটি ছবি একাধিক ক্লাস্টার পুলে থাকতে পারে; এটি শুধু একবার PUBLISHED হতে পারে।
# CREATE UNIQUE INDEX one_use_per_photo ON image_pool (photo_id)
#        WHERE used_by_page IS NOT NULL;

def assign_hero(page_id: str, cluster_id: str) -> dict | None:
    for _ in range(5):                # রেস হারলেই পরবর্তী ছবি নেওয়া হয়
        try:
            return db.query_one("""
                UPDATE image_pool p SET used_by_page = %s, used_at = NOW()
                WHERE p.id = (
                    SELECT c.id FROM image_pool c
                    WHERE c.cluster_id = %s AND c.used_by_page IS NULL
                      AND NOT EXISTS (              -- অন্য কোনো ক্লাস্টার ব্যবহার করেছে?
                          SELECT 1 FROM image_pool u
                          WHERE u.photo_id = c.photo_id
                            AND u.used_by_page IS NOT NULL
                      )
                    ORDER BY c.rank ASC
                    LIMIT 1 FOR UPDATE SKIP LOCKED  -- প্যারালাল ওয়ার্কারের সাথে নিরাপদ
                )
                RETURNING photo_id, urls, width, height, blur_hash, alt, credit
            """, [page_id, cluster_id])
        except UniqueViolation:      # দুটি ক্লাস্টার একই মিলিসেকেন্ডে দাবি করেছে
            continue
    return None                     # পুল শুকনো → ব্রিফ প্রশস্ত করুন, রিফিল করুন

# রেন্ডারিং: প্রতিটি অ্যাট্রিবিউট সারি থেকে আসে — কোনো লেআউট শিফট নেই, অনুমান নেই।
# <img src="{urls[regular]}" width="{width}" height="{height}"
#      alt="{alt}" loading="lazy" style="background:{blur_placeholder}">
# <figcaption>{credit}</figcaption>

দুটি ব্যর্থতার ধরন, দুটি প্রক্রিয়া, এবং দুটোই দরকার। FOR UPDATE SKIP LOCKED একটি ক্লাস্টারের মধ্যে কনকারেন্সি সামলায়: প্যারালালভাবে পৃষ্ঠা তৈরি করুন — এবং এই ভলিউমে আপনি করবেনই — এবং দুটি ওয়ার্কার একই মিলিসেকেন্ডে একই শীর্ষ-র‍্যাঙ্কড ছবির জন্য হাত বাড়ায়।

আংশিক ইউনিক ইনডেক্স এমন একটি জিনিস সামলায় যা সবাই ভুলে যায়: একই ছবি বৈধভাবেই একাধিক ক্লাস্টারের পুলে দেখা দিতে পারে, কারণ পার্শ্ববর্তী ক্লাস্টারগুলো (“ভাড়াটেদের জন্য হোম ইনস্যুরেন্স”, “...জমিদারদের জন্য”) ওভারল্যাপিং ফলাফল ফেরত দেয়। প্রতি-ক্লাস্টার used_by_page IS NULL চেক এই বিষয়ে অন্ধ — প্রতিটি ক্লাস্টার মনে করে ছবিটি খালি আছে। NOT EXISTS কোয়েরিটিকে সৎ রাখে, এবং ইনডেক্স কনকারেন্সিতে এটিকে ভুল হতে দেওয়া অসম্ভব করে তোলে: রেসে হেরে যাওয়া পক্ষ একটি ভায়োলেশন পায়, পুনরায় চেষ্টা করে, এবং পরবর্তী ছবি নেয়। এটি ছাড়া, “কোনো দুটি পৃষ্ঠা একই হিরো শেয়ার করে না” একটি দাবি মাত্র, নিশ্চয়তা নয়।

বিল্ড চলাকালীন একটি পুল শুকিয়ে গেলে, সবচেয়ে ভালো ফলব্যাক হলো পুনরাবৃত্তি না করে প্রশস্ত করা: ক্যামেরা ব্রিফের শেষ ধারাটি বাদ দিন, পুল কোয়েরিটি একবার আবার চালান, এবং তারপরই কেবল সবচেয়ে পুরনো-বরাদ্দকৃত ছবিটি পুনরায় ব্যবহার করুন — এই কঠোর নিয়মসহ যে এটি কখনোই একই ক্লাস্টারের কোনো পৃষ্ঠায় বসবে না।

একটি ক্লাস্টার, একটি পুল: একটি প্রকৃত কোয়েরি

একটি তুলনামূলক এস্টেট নিন: হোম ইনস্যুরেন্স, বিশটি পৃষ্ঠা — “ভাড়াটেদের জন্য হোম ইনস্যুরেন্স”, “...জমিদারদের জন্য”, “একটি পলিসি আসলে কী কভার করে”, বারোটি শহর পৃষ্ঠা, তিনটি ক্লেইম গাইড। ক্লাস্টারের জন্য একটি ক্যামেরা ব্রিফ, একটি রিকোয়েস্ট, এবং এই হলো পুলের মাথা, ১২২ ms-এ ফেরত এসেছে:

GET /search/photos — “a woman signing paperwork with an insurance agent at a kitchen table in her home” · ১২২ ms
পুলের মাথা: একটি র‍্যাঙ্কড ফলাফল সেট থেকে ছয়টি স্বতন্ত্র দৃশ্য — একটি স্বাক্ষর, একটি ফর্মের ক্লোজ-আপ, একটি পলিসি ব্যাখ্যা করা হচ্ছে, ডেস্কের কাগজপত্র, একজন এজেন্টের সাথে একটি দম্পতি, কফির সাথে একটি তুলনা। এটি বিশটি পৃষ্ঠার ইতিমধ্যে ছয়টি কভার করছে; পুলিং রিকোয়েস্ট per_page=100 চাইছে এবং বাকিগুলো রেখে দিচ্ছে। এই ঠিক সার্চটি চালান →

কোডের চেয়ে ব্রিফগুলোই বেশি গুরুত্বপূর্ণ। বাস্তব ক্লাস্টারের এস্টেটে দুটি নিয়ম টিকে যায়: ব্রিফটি ক্লাস্টার-এর জন্য লিখুন, পৃষ্ঠার জন্য নয় (প্রোগ্রাম্যাটিক সেটে পৃষ্ঠার শিরোনাম প্রায় অভিন্ন, তাই প্রতি-পৃষ্ঠা ব্রিফ প্রায় অভিন্ন ছবি ফেরত দেয়), এবং টপিক নয় বরং এমন একটি দৃশ্য বর্ণনা করুন যা একটি ক্যামেরা তুলতে পারত — home insurance কেবল ডকুমেন্ট ক্লোজ-আপ আর কিছুই ফেরত দেয় না। এই ব্রিফগুলো তৈরি করার কপি-পেস্ট করার মতো প্রম্পট আমরা প্রকাশ করেছি প্রতিটি পোস্ট ইলাস্ট্রেট করার আর্টিকেলে।

২০০-পৃষ্ঠার সিরিজকে ভিজ্যুয়ালি সংগতিপূর্ণ রাখা

পুনরাবৃত্তির বিপরীত ব্যর্থতা হলো অসংগতি: একটি ক্লাস্টারের বিশটি পৃষ্ঠা, প্রতিটিতে প্রযুক্তিগতভাবে সঠিক একটি ছবি, বিশটি ভিন্ন ভিজ্যুয়াল রেজিস্টারে। সমাধান একটি মাত্র এন্ডপয়েন্ট — GET /photos/{id}/similar — পিলার পেজের জন্য আপনি যে ছবিটি অনুমোদন করেছেন সেটির উপর একবার চালান:

GET /photos/019e143f…/similar — অনুমোদিত হিরোর মতো আরও · ৪৮ ms
একই আলো, একই ঘর, একই পোশাক, ভিন্ন মুহূর্ত — কারণ ভিজ্যুয়াল সাদৃশ্য শুধু একই বিষয় নয়, একই ফটোগ্রাফারের সেশনের বাকি অংশও খুঁজে বের করে। এগুলোকে একটি ক্লাস্টার জুড়ে বরাদ্দ করুন এবং সিরিজটি জোড়াতালি দিয়ে বানানো নয়, বরং কমিশন করা মনে হবে।

ব্র্যান্ড সংগতি প্রয়োজন হলে পুল কোয়েরিতে যুক্ত করার মতো আরও দুটি লিভার আছে: color_hex সহ একটি color_tolerance পুরো এস্টেটকে একটি প্যালেটের মধ্যে রাখে, এবং photographer একটি ক্লাস্টারকে একজন নির্দিষ্ট ফটোগ্রাফারের কাজের সাথে আটকে দেয়। দুটোই সাধারণ কোয়েরি প্যারামিটার — সার্চ ফিল্টারে কোনো প্ল্যান-ভিত্তিক বাধা নেই।

রেট লিমিট, কোটা, এবং প্রকৃত খরচ

প্ল্যানটি বার্স্ট-এর ভিত্তিতে বেছে নিন, ভলিউমের ভিত্তিতে নয়। একবার পুল করা শুরু করলে, প্রায় সবার জন্যই মাসিক কোটা আর সীমাবদ্ধতা হয়ে থাকে না; আপনার প্ল্যান নির্ধারণ করে একটি সম্পূর্ণ রিফ্রেশ কত দ্রুত শেষ হতে হবে তার উপর।

প্ল্যান রিকোয়েস্ট / মাস= রিফ্রেশযোগ্য ক্লাস্টার রেট লিমিট API কী ১,০০০টি ক্লাস্টার রিফ্রেশওয়াল-ক্লক, একবার
Free — $0 5,000 20 / মিনিট 1 50 min
Starter — $5 25,000 30 / মিনিট 3 34 min
Pro — $19 100,000 60 / মিনিট 5 17 min
Team — $99এজেন্সি: প্রতি ক্লায়েন্টে একটি কী 500,000 200 / মিনিট 25 5 min
Business — $249 1,000,000 300 / মিনিট unlimited 4 min
Enterprise — $599 2,000,000 500 / মিনিট unlimited 2 min

প্রথম কলামটি ক্লাস্টার হিসেবে পড়ুন: একটি পুলিং রিকোয়েস্ট একটি ক্লাস্টার পূরণ করে, তাই মাসিক কোটা হলো আপনি কতগুলো ক্লাস্টার রিফ্রেশ করতে পারেন, এবং শেষ কলামটি হলো ১,০০০-ক্লাস্টারের একটি এস্টেট জুড়ে সেই প্ল্যানের রেট লিমিটে একটি সম্পূর্ণ পাস করতে কত সময় লাগে। দুটোই প্রাইসিং ক্যাটালগ থেকে লাইভ ভ্যালু — প্রাইসিং পৃষ্ঠায় একই রেট লিমিট প্রতি মিনিটের বদলে প্রতি ঘণ্টায় উল্লেখ করা আছে।

তাহলে মাসে ১০,০০০ পৃষ্ঠার একটি এস্টেট, পুলড প্যাটার্নে, ~৫০০টি রিকোয়েস্ট ব্যয় করে এবং কিছুই পরিশোধ করে না। যে জিনিসটি টিমগুলোকে টেবিলের উপরের দিকে ঠেলে দেয় তা খুব কমই ভলিউম হয় — বরং তিনটি জিনিসের একটি: একটি বিদ্যমান এস্টেটের সম্পূর্ণ পুনঃ-ইলাস্ট্রেশন একরাতে, প্রতি-ক্লায়েন্ট কী আইসোলেশন (একটি এজেন্সি চায় প্রতিটি ক্লায়েন্টের জন্য একটি কী যাতে শেয়ার্ড-সিক্রেট জিমন্যাস্টিক্স ছাড়াই ব্যবহার শনাক্তযোগ্য হয়), অথবা ঘণ্টার বদলে মিনিটে পরিমাপ করা একটি বিল্ড উইন্ডো।

দুটি অপারেশনাল খুঁটিনাটি যা এক রাতের ডিবাগিং বাঁচায়। প্রতিটি রেসপন্সে X-RateLimit-Limit এবং X-RateLimit-Remaining থাকে, তাই একটি ওয়ার্কার অনুমান না করে নিজেকে গতিতে রাখতে পারে। আর একটি প্রকৃত প্রতি-মিনিট রেট লিমিট এমন 429 Retry-After বহন করে, অথচ একটি মাসিক-কোটা ব্লক ইচ্ছাকৃতভাবে এটি বহন করে না — সেটি রিট্রাই করে পরিষ্কার হবে না, এবং যে ওয়ার্কার এই দুটির পার্থক্য বোঝে সে এমন একটি এন্ডপয়েন্টে আঘাত করা বন্ধ করে দেয় যার আর কিছু দেওয়ার নেই।

যখন এজেন্ট প্রকাশনার কাজ করে

আপনার কনটেন্ট যদি একটি এজেন্ট দিয়ে তৈরি হয় — এবং এই ভলিউমে ক্রমবর্ধমানভাবে তা হয়েই থাকে — পুলিং লেয়ারটি হারিয়ে যায় না, এটি স্থানান্তরিত হয়। এজেন্টকে সার্চ টুল দিন এবং এটি ক্লাস্টার ব্রিফটি কনটেক্সটে রাখা অবস্থায়ই পুল পূরণ করে ফেলবে, Model Context Protocol সার্ভার mcp.pexafy.com/mcp ব্যবহার করে: তিনটি টুল, search_photos (একটি বাক্য), search_photos_by_image (একটি রেফারেন্স ইমেজ, ঐচ্ছিকভাবে একটি বাক্যসহ) এবং get_similar_photos (উপরে উল্লেখিত সিরিজ-সংগতির টুল)।

একটি হেডলেস এজেন্টে যুক্ত করুন — একটি কানেক্টর, একটি কী
# Claude Code / CI রানার
claude mcp add --transport http pexafy https://mcp.pexafy.com/mcp \
  --header "Authorization: Bearer $PEXAFY_API_KEY"

# অথবা .mcp.json কমিট করুন যাতে ফ্লিটের প্রতিটি ওয়ার্কার এটি উত্তরাধিকারসূত্রে পায়
{
  "mcpServers": {
    "pexafy": {
      "type": "http",
      "url": "https://mcp.pexafy.com/mcp",
      "headers": { "Authorization": "Bearer YOUR_API_KEY" }
    }
  }
}

স্কেলে যে নির্দেশনা কাজ করে তা প্রতি-আর্টিকেল নয়, একটি ব্যাচ নির্দেশনা — এবং এটি উপরের দুটি পর্যায়ের সাথে হুবহু মিলে যায়:

একটি ক্লাস্টার রিফ্রেশ, এজেন্ট দ্বারা চালিত
You  এখানে কনটেন্ট ক্যালেন্ডার থেকে ১২টি টপিক ক্লাস্টার আছে। প্রতিটির জন্য,
     একটি ক্যামেরা ব্রিফ লিখুন, ১০০টি ল্যান্ডস্কেপ ছবি টানুন, এবং
     পুলটি Postgres-এ লিখুন। 0.5-এর উপরে ৪০টির কম ফলাফল ফেরত দেয়
     এমন যেকোনো ক্লাস্টার ফ্ল্যাগ করুন — সেই ব্রিফগুলো নতুন করে লিখতে হবে।

Agent  → search_photos(q="a woman signing paperwork with an insurance
           agent at a kitchen table in her home", orientation="landscape")
      ← ১০০টি ছবি · ১২২ ms · থ্রেশহোল্ডের উপরে ৮৭টি
      … আরও ১১টি ক্লাস্টার …

      ✓ ১১টি পুল লেখা হয়েছে (১,০৪৩টি ছবি, সবগুলো ক্রেডিটসহ)
      ⚠ "index fund rebalancing" → ১২টি ফলাফল। ব্রিফটি বিমূর্ত;
        প্রস্তাবনা: "a person at a kitchen table checking figures on a
        laptop with a notebook and coffee beside them"

এই শেষ লাইনটিই এজেন্টকে এই লুপে রাখার কারণ, একটি স্ক্রিপ্ট নয়: প্রোগ্রাম্যাটিক ইলাস্ট্রেশনের ব্যর্থতার ধরন হলো একটি খারাপ ব্রিফ, আর একটি খারাপ ব্রিফ ঠিক এমন একটি জিনিস যা একটি ভাষা মডেল লক্ষ্য করে নতুন করে লিখতে পারে। স্ক্রিপ্টগুলো অ্যাসাইনমেন্টের দায়িত্বে থাকে, যেখানে নির্ধারণযোগ্যতা গুরুত্বপূর্ণ এবং সৃজনশীলতা নয়।

ছবি যা ঠিক করতে পারে না

একটি সৎ আলোচনা, কারণ এটি যে ব্যর্থতার বর্ণনা দেয় তা ব্যয়বহুল। প্রকৃত, ক্রেডিটসহ ফটোগ্রাফি একটি পৃষ্ঠাকে উন্নত করে। এটি পাতলা কনটেন্টকে ভালো কনটেন্টে রূপান্তরিত করে না, এবং কোনো ইমেজ পাইপলাইন সার্চ ইঞ্জিন কীভাবে সেই পৃষ্ঠাগুলোকে বিচার করে তা পরিবর্তন করে না যেগুলো সাহায্য করার বদলে র‍্যাঙ্ক করার জন্যই বাল্কে তৈরি হয়। Google-এর স্প্যাম নীতি এটি সরাসরি উল্লেখ করে: স্কেলড কনটেন্ট অ্যাবিউজ কম মূল্যের অনেক পৃষ্ঠা তৈরি করাকে কভার করে অটোমেশন জড়িত থাকুক বা না থাকুক, এবং সেই পৃষ্ঠাগুলোর ইলাস্ট্রেশন এই বিচারের ক্ষেত্রে অপ্রাসঙ্গিক।2

তাই কার্যকর ফ্রেমিং সংকীর্ণ এবং সত্য: ফটোগ্রাফি এমন একটি পৃষ্ঠায় আপনি নিয়ন্ত্রণ করেন এমন একটি মান সংকেত যা ইতিমধ্যে বিদ্যমান থাকার যোগ্যতা রাখে। যেখানে এটি প্রমাণযোগ্যভাবে কাজে আসে:

  • একজন পাঠক যাচাই করতে পারেন এমন প্রোভেন্যান্স। একজন ফটোগ্রাফারের নাম এবং একটি সোর্স URL সহ একটি ক্রেডিট লাইন এমন একটি দাবি যা যাচাই করা যায় — একজন অজানা লেখকের একটি পৃষ্ঠায় একটি অ্যাট্রিবিউশন-ছাড়া ছবির ঠিক বিপরীত।
  • অ্যাক্সেসিবিলিটি ও Core Web Vitals। রেসপন্স থেকে পাওয়া width/height লেআউট শিফট দূর করে, blur_hash একটি প্রকৃত প্লেসহোল্ডার দেয়, এবং alt_description alt টেক্সটের একটি খসড়া যা আপনার টেমপ্লেট উদ্ভাবন করার বদলে উন্নত করতে পারে। ৪০,০০০টি স্লট দিয়ে গুণ করলে এটাই সাইটের সম্পূর্ণ ইমেজ-কোয়ালিটি গল্প।
  • বাল্কে তৈরি বলে না দেখানো। ডিডুপ্লিকেশন এবং সিরিজ-সংগতিই একটি এস্টেটকে ভিজ্যুয়াল স্বাক্ষর তৈরি হওয়া থেকে থামায়। এটি একটি বাস্তব উপলব্ধিগত খরচ যার বাস্তব পরিণতি আছে, এবং এটি সম্পূর্ণভাবে আপনার নিয়ন্ত্রণে।

আর লাইসেন্স এখনও ছবিটির উপর প্রযোজ্য। Pexafy API শর্তাবলীতে অ্যাট্রিবিউশন বাধ্যতামূলক নয় — প্রতিটি ফলাফল রেন্ডার করার জন্য প্রস্তুত attribution.html এবং attribution.plain সহ আসে — কিন্তু মূল লাইব্রেরি দ্বারা সংযুক্ত লাইসেন্স আপনার সেই ছবির ব্যবহারের উপর প্রযোজ্য, এবং মাসে ৪০,০০০ ছবিতে, স্বয়ংক্রিয়ভাবে ক্রেডিট রেন্ডার করা পরে অডিট করার চেয়ে সস্তা।

সোমবার থেকে কোথায় শুরু করবেন

  1. আপনার ব্যাকলগকে ক্লাস্টারে ভাগ করুন ১৫–৩০টি পৃষ্ঠার, যারা যুক্তিসঙ্গতভাবে একটি ফটো শুট শেয়ার করতে পারে। এটিই একমাত্র সত্যিকারের ম্যানুয়াল ধাপ, এবং এটি একটি প্রজেক্ট নয়, একটি স্প্রেডশিট।
  2. প্রতি ক্লাস্টারে একটি ক্যামেরা ব্রিফ লিখুন — একটি দৃশ্য, ১২ থেকে ২৫ শব্দ। একটি মডেল দিয়ে সেগুলো তৈরি করুন, তারপর পড়ুন; যে ব্রিফগুলো দৃশ্যের বদলে একটি টপিকের নাম দেয় সেগুলো এক নজরেই দেখা যায়।
  3. একটি পুল পূরণ করুন একটি একক per_page=100 রিকোয়েস্ট দিয়ে এবং এর মাথাটা চোখ দিয়ে দেখুন। শীর্ষ দশটি ব্যবহারযোগ্য না হলে, ব্রিফটি ভুল — ইঞ্জিন নয়।
  4. used_by_page কলামটি যোগ করুন কিছু প্রকাশ করার আগেই। এখন এটি পাঁচ মিনিটের কাজ, পরে ৩,০০০টি লাইভ পৃষ্ঠা জুড়ে একটি মাইগ্রেশন।
  5. পুরো জিনিসটি ফ্রি প্ল্যানে চালান যতক্ষণ না একটি বিল্ড উইন্ডো আপনাকে উপরে ঠেলে দেয়। মাসে ৫০০টি রিকোয়েস্টে, তাতে বেশ কিছুটা সময় লাগবে।

তথ্যসূত্র ও পাদটীকা

1 EU AI Act, অনুচ্ছেদ ৫০ — স্বচ্ছতার বাধ্যবাধকতা কার্যকর ২ আগস্ট ২০২৬ থেকে: সিনথেটিক ইমেজ, অডিও, ভিডিও বা টেক্সট তৈরি করা সিস্টেমের প্রোভাইডারদের অবশ্যই মেশিন-পাঠযোগ্য ফরম্যাটে আউটপুট চিহ্নিত করতে হবে এবং সেগুলোকে কৃত্রিমভাবে তৈরি হিসেবে শনাক্তযোগ্য করতে হবে। এটি AI প্রোভাইডার ও ডিপ্লয়ারদের বাধ্য করে; এটি কোনো ওয়েবসাইট কোন ছবি প্রকাশ করতে পারবে তার নিয়ম নয়।

2 Google Search স্প্যাম নীতি — স্কেলড কনটেন্ট অ্যাবিউজ: মূলত র‍্যাঙ্কিং ম্যানিপুলেট করার জন্য অনেক পৃষ্ঠা তৈরি করা এবং ব্যবহারকারীদের সামান্য মূল্য প্রদান করা, তা অটোমেশন, মানুষের প্রচেষ্টা বা উভয়ের সমন্বয়ে তৈরি হোক না কেন। ইলাস্ট্রেশনের মান সেই মূল্যায়নে কোনো নিয়ামক নয়, এবং ঠিক এই কারণেই এই আর্টিকেলটি দুটোকে আলাদা রাখে।

প্রায়শই জিজ্ঞাসিত প্রশ্ন

হাজার হাজার প্রোগ্রাম্যাটিক SEO পেজের জন্য আমি কীভাবে ছবি পাব?
প্রতিটি পেজের জন্য নয়, বরং প্রতিটি টপিক ক্লাস্টারের জন্য একবার সার্চ করুন। per_page=100 সহ একটি একক GET /api/v1/search/photos রিকোয়েস্ট একটি ক্লাস্টারের জন্য ১০০টি পর্যন্ত ক্রেডিটেড ছবি ফিরিয়ে দেয়; আপনি সেগুলো একটি পুল টেবিলে সংরক্ষণ করেন এবং প্রকাশের সময় প্রতি পেজে একটি করে অ্যাসাইন করেন। প্রতি পেজে চারটি ছবিসহ মাসে ১০,০০০ পেজের একটি এস্টেটের জন্য ৪০,০০০ এর বদলে প্রায় ৫০০টি রিকোয়েস্ট লাগে, যা ফ্রি প্ল্যানের (মাসে ৫,০০০ রিকোয়েস্ট) মধ্যেই ধরে যায়।
বড় আকারের কনটেন্ট প্রোডাকশনের জন্য সেরা স্টক ফটো API কোনটি?
মাসে কয়েক হাজার ছবির বেশি ক্ষেত্রে যেসব বিষয় গুরুত্বপূর্ণ তা হলো: একটি রিকোয়েস্টে কত ছবি ফেরত আসে, একাধিক লাইব্রেরি একটি নরমালাইজড স্কিমায় ফেরত আসে কিনা, প্রতি মিনিটে রেট লিমিট কত, এবং প্রোডাকশন অ্যাক্সেস পেতে অ্যাপ্লিকেশন রিভিউ প্রয়োজন কিনা। Pexafy একটি স্কিমায় ৯টি ফ্রি লাইব্রেরি জুড়ে প্রতি রিকোয়েস্টে ১০০টি পর্যন্ত ছবি ফেরত দেয়, সঙ্গে সঙ্গে একটি কার্যকর কী ইস্যু করে, এবং ফ্রি প্ল্যানে মিনিটে ২০টি থেকে বিজনেস প্ল্যানে মিনিটে ৩০০টি পর্যন্ত রিকোয়েস্টের অনুমতি দেয়। আমরা একটি নিবেদিত নিবন্ধে এই মানদণ্ডে প্রতিটি ফ্রি API তুলনা করেছি।
একই স্টক ফটো একাধিক পেজে দেখা যাওয়া কীভাবে বন্ধ করব?
আপনি প্রকাশিত প্রতিটি ছবির photo_id সংরক্ষণ করুন এবং পুল থেকে ছবি ট্রানজ্যাকশনালভাবে দাবি করুন — SQL-এ, একটি UPDATE … WHERE used_by_page IS NULL … FOR UPDATE SKIP LOCKED। এভাবে ডিডুপ্লিকেশন সম্ভাব্যতার পরিবর্তে সঠিক হয়ে যায়, এবং সমান্তরাল বিল্ড ওয়ার্কারগুলো একই সেরা-র‍্যাঙ্কড ছবির জন্য প্রতিযোগিতা করতে পারে না। এটি মাত্র একটি কলাম, এবং হাজার হাজার লাইভ পেজ জুড়ে পরে এটি যোগ করার চেয়ে প্রথম দিনেই যোগ করা অনেক সস্তা।
একটি API রিকোয়েস্ট সর্বোচ্চ কতগুলো ছবি ফেরত দিতে পারে?
১০০টি পর্যন্ত, per_page=100 ব্যবহার করে, এবং কার্সার পেজিনেশন আপনাকে আরও গভীর পুলের জন্য একই র‍্যাঙ্কড রেজাল্ট সেট ধরে হাঁটতে দেয়। এটিকে score_threshold এর সাথে মেলান যাতে একটি পাতলা ক্লাস্টার ১০০টি আলগা ম্যাচের বদলে ৪০টি শক্তিশালী ম্যাচ ফেরত দেয়, এবং fields এর সাথে যাতে শুধু সেই অ্যাট্রিবিউটগুলো ফেরত আসে যা আপনার টেমপ্লেট রেন্ডার করে, যা বড় আকারে রেসপন্স ছোট রাখে।
বড় আকারে তৈরি পেজে প্রকৃত ছবি কি র‍্যাঙ্কিং ভালো করতে সাহায্য করে?
না — এবং এই বিষয়ে স্পষ্ট হওয়া গুরুত্বপূর্ণ। গুগলের স্প্যাম নীতিতে স্কেলড কনটেন্ট অ্যাবিউজ-কে সংজ্ঞায়িত করা হয়েছে ব্যবহারকারীদের জন্য সামান্য মূল্যসহ প্রাথমিকভাবে র‍্যাঙ্কিং ম্যানিপুলেট করার উদ্দেশ্যে অনেক পেজ তৈরি করা হিসেবে, অটোমেশন জড়িত থাকুক বা না থাকুক; সেসব পেজের ইলাস্ট্রেশন এই মূল্যায়ন বদলায় না। প্রকৃত, ক্রেডিটেড ফটোগ্রাফি এমন পেজে একটি মান-নির্দেশক যেগুলো ইতিমধ্যেই থাকার যোগ্য: এতে যাচাইযোগ্য উৎস থাকে, Core Web Vitals ও অ্যাক্সেসিবিলিটি রক্ষাকারী width, height, blur hash এবং alt text সরবরাহ করে, এবং একটি এস্টেটকে মাস-প্রোডিউসড দেখাতে বাধা দেয়।
একটি AI এজেন্ট কি স্বয়ংক্রিয়ভাবে একটি ইমেজ পুল পূরণ করতে পারে?
হ্যাঁ, Pexafy-এর হোস্টেড MCP (Model Context Protocol) সার্ভার mcp.pexafy.com/mcp-এর মাধ্যমে, যা search_photos, search_photos_by_image এবং photo_similar প্রকাশ করে। এজেন্টকে ক্লাস্টারের একটি তালিকা দিন এবং এটি প্রতিটির জন্য একটি করে ক্যামেরা ব্রিফ লেখে, পুলগুলো টেনে আনে এবং যেসব ব্রিফ খুব কম শক্তিশালী ম্যাচ ফেরত দিয়েছে সেগুলো চিহ্নিত করে — যা আসলে প্রোগ্রাম্যাটিক ইলাস্ট্রেশনের ব্যর্থতার প্রকৃত ধরন। অ্যাসাইনমেন্ট আপনার স্ক্রিপ্টেই থাকে, যেখানে সৃজনশীলতার চেয়ে নির্ধারণযোগ্যতা বেশি গুরুত্বপূর্ণ।

কীওয়ার্ড খোঁজা বন্ধ করুন। আপনি যা বোঝাতে চান তা বর্ণনা করুন।

যেকোনো ভাষায়, ১০০ ms-এর কম সময়ে 9M+ মুক্ত-ব্যবহারযোগ্য ছবি অর্থ দিয়ে অনুসন্ধান করুন।