إنشاء موقع لتوليد الصور بالذكاء الاصطناعي: قالب ومشروع على GitHub

قالب عملي لموقع توليد الصور بالذكاء الاصطناعي: حزمة Next.js، وبنية مشروع GitHub، ومسار الخادم الذي يستدعي API نموذج الصور، وصفحة الأوامر النصية، وفحوص الأمان، وحدود الطلبات، والنشر. كل ملف معروض كاملًا، فانسخه وأطلق موقعك.

إنشاء موقع لتوليد الصور بالذكاء الاصطناعي: قالب ومشروع على GitHub
Cristian Da Conceicao
مؤسس Picasso IA

معظم الشروحات حول كيفية إنشاء موقع لتوليد الصور بالذكاء الاصطناعي تنتهي بلقطة شاشة ووعد غامض. هذا الشرح ينتهي بمشروع يعمل: صندوق للأوامر النصية، ومسار خادم يستدعي نموذج صور، وحلقة استعلام، وعرض للنتيجة، وقائمة للنشر. كل الملفات تتسع في هذه الصفحة، لذا يمكنك لصقها في مستودع جديد، ودفعها إلى GitHub، والحصول على مولّد يعمل على الإنترنت في الظهيرة نفسها.

القالب صغير عن قصد. فيه أربعة ملفات فيها منطق حقيقي، ولا قاعدة بيانات فيه، ولا مزوّد لتسجيل الدخول، ولا مكتبة لإدارة الحالة. وهذا يجعله سهل القراءة في جلسة واحدة، وسهل التوسيع لاحقًا بالحسابات أو السجل أو الفيديو. إذا كنت تستطيع تشغيل npm install، فبإمكانك إطلاقه.

💡 إجابة سريعة: موقع توليد الصور عبارة عن نموذج إدخال، ومسار واحد على الخادم يخفي توكن API الخاص بك، وحلقة استعلام، ووسم <img>. كل ما عدا ذلك لمسات تجميلية.

ما الذي يفعله القالب

يستقبل الموقع النهائي أمرًا نصيًا، ويرسله إلى نموذج صور، وينتظر النتيجة، ويعرض الصورة مع رابط تنزيل. لا يرى المتصفح توكن API الخاص بك أبدًا، لأن كل طلب يمر عبر مسار الخادم الخاص بك.

هذا هو تدفق الطلب كاملًا:

  1. يكتب الزائر أمرًا نصيًا ويضغط الزر.
  2. ترسل الصفحة الأمر النصي إلى /api/generate على الخادم الخاص بك.
  3. يتحقق الخادم من صحته وينشئ مهمة على API الصور.
  4. تطلب الصفحة من /api/generate/{id} الحالة كل ثانيتين.
  5. عندما تتحول الحالة إلى succeeded، تعرض الصفحة رابط الصورة.

رسم تخطيطي بخط اليد على لوح أبيض يوضح بنية المتصفح والخادم ونموذج الصور

الميزات في لمحة

الميزةمتضمنةمكانها
نموذج الأوامر مع حد للأحرفنعمcomponents/Generator.tsx
مسار الخادم الذي ينشئ المهمةنعمapp/api/generate/route.ts
مسار الحالة للاستعلامنعمapp/api/generate/[id]/route.ts
حالات التحميل والخطأ والمهلةنعمcomponents/Generator.tsx
رابط التنزيل والسجل المحلينعميُضاف في خطوة المعرض
الحسابات والفوترة ومعارض المستخدمينلاأضفها بعد الإطلاق الأول

لمن يناسب: المطور المستقل يحصل على مشروع لملف أعماله يولّد صورًا فعلًا. والوكالات تحصل على أساس تستطيع وضع علامتها التجارية عليه لعميل خلال يوم. وفرق المنتجات تحصل على نموذج أولي لاختبار الطلب قبل الاستثمار في منصة كاملة.

مجموعة الأدوات وتخطيط المشروع

لابتوب في مقهى يعرض صفحة بسيطة لمولّد صور فيها حقل للأوامر النصية

اختيار مجموعة الأدوات

يستخدم القالب Next.js مع App Router و TypeScript و Tailwind CSS. إطار واحد يمنحك الصفحة ومسارات الخادم في المستودع نفسه، ولهذا يناسب المشروع الأول. وأي حزمة فيها خادم تعمل بالطريقة نفسها: React مع Express، أو SvelteKit، أو Nuxt، أو Node العادي.

أنشئ المشروع وضعه على GitHub في أربعة أوامر:

npx create-next-app@latest ai-image-generator --ts --app --tailwind --eslint
cd ai-image-generator
git init && git add . && git commit -m "Initial template"
gh repo create ai-image-generator --public --source=. --push

الواجهة الخلفية هي API لتحويل النص إلى صورة. تتبع PicassoIA API اصطلاح Replicate: ينشئ POST /v1/models/{owner}/{name}/predictions مهمة، بينما تعيد GET /v1/predictions/{id} حالتها. وقت كتابة هذا المقال، تقدم API أربعة نماذج: picassoia/picassoia-image، وpicassoia/picassoia-image-editor-pro، وpicassoia/picassoia-video، وpicassoia/seedance-2.5-lite. أنشئ توكنك من صفحة PicassoIA API.

شجرة المجلدات والمتغيرات

ai-image-generator/
├── app/
│   ├── api/
│   │   └── generate/
│   │       ├── route.ts          # creates the prediction
│   │       └── [id]/route.ts     # returns status and output
│   ├── layout.tsx
│   └── page.tsx                  # renders <Generator />
├── components/
│   └── Generator.tsx             # form, polling, result
├── .env.example
├── .gitignore
└── README.md

منظر من أعلى لمكتب فيه مخطط سلكي ولابتوب وهاتف يعرضان معرض صور

هناك متغيران للبيئة يحركان كل شيء:

# .env.example
PICASSOIA_API_TOKEN=pia_sk_replace_me
PICASSOIA_MODEL=picassoia/picassoia-image

انسخ الملف إلى .env.local والصق توكنك الحقيقي فيه. create-next-app يتجاهل كل ملفات .env*، لذا أضف السطر !.env.example إلى .gitignore إذا أردت أن يبقى ملف المثال في المستودع. لا تضع التوكن أبدًا في متغير يبدأ بالبادئة NEXT_PUBLIC_، لأن Next.js يرسل هذه المتغيرات إلى المتصفح.

كتابة مسار الخادم

أيدي مطور تكتب الكود على لابتوب أمام شاشة

إنشاء التنبؤ

// app/api/generate/route.ts
import { NextResponse } from "next/server";

const API = "https://api.picassoia.com/v1";
const MODEL = process.env.PICASSOIA_MODEL ?? "picassoia/picassoia-image";

export async function POST(req: Request) {
  const { prompt } = await req.json();
  if (typeof prompt !== "string" || prompt.length < 3 || prompt.length > 4000) {
    return NextResponse.json({ error: "Invalid prompt" }, { status: 400 });
  }

  const res = await fetch(`${API}/models/${MODEL}/predictions`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PICASSOIA_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ input: { prompt } }),
  });

  if (!res.ok) {
    return NextResponse.json({ error: "Upstream error" }, { status: 502 });
  }
  const prediction = await res.json();
  return NextResponse.json({ id: prediction.id });
}

الجسم يتبع اصطلاح Replicate، وهو كائن input يحمل الأمر النصي. كل صفحة نموذج تسرد حقولًا إضافية، مثل نسبة العرض إلى الارتفاع، يمكنك إضافتها بجانب prompt. فحص الحد الأقصى البالغ 4,000 حرف يطابق حد الأمر النصي الموثّق، فيحصل المستخدمون على خطأ واضح قبل أن يغادر الطلب خادمك.

الاستعلام حتى الانتهاء

التوليد غير متزامن. الاستدعاء الأول يعيد معرّفًا، وتطلب الحالة حتى تقول succeeded أو failed.

// app/api/generate/[id]/route.ts
import { NextResponse } from "next/server";

export async function GET(
  _req: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  if (!/^[A-Za-z0-9_-]+$/.test(id)) {
    return NextResponse.json({ error: "Bad id" }, { status: 400 });
  }

  const res = await fetch(`https://api.picassoia.com/v1/predictions/${id}`, {
    headers: { Authorization: `Bearer ${process.env.PICASSOIA_API_TOKEN}` },
    cache: "no-store",
  });
  const p = await res.json();
  const output = Array.isArray(p.output) ? p.output[0] : p.output;
  return NextResponse.json({
    status: p.status,
    output: output ?? null,
    error: p.error ?? null,
  });
}

فحص id مهم. من دونه، قد يمرر زائر ../ في المسار فيجعل خادمك يستدعي نقطة نهاية أخرى ومعه توكنك.

معالجة الأخطاء والحدود

ثلاثة أنواع من الإخفاق تظهر في الزيارات الحقيقية:

  1. إدخال غير صالح: أعد الخطأ 400 قبل أن تستهلك طلبًا.
  2. أخطاء في الواجهة الخلفية أو طوابير مزدحمة: أعد 502 أو 429 ودع الصفحة تعرض رسالة لإعادة المحاولة.
  3. مهام بطيئة: أوقف الاستعلام بعد دقيقتين تقريبًا وأخبر المستخدم.

تسمح API بعدد 5 طلبات تشغيل متزامنة لكل حساب، وتُشارَك بين كل التوكنات. إذا زار موقعك عشرة أشخاص في الوقت نفسه، ينتظر خمسة منهم. أضف قائمة انتظار صغيرة، أو عطّل الزر أثناء تشغيل المهمة.

الأعراضالسبب المحتملالحل
401 من APIتوكن خاطئ أو مفقودتحقق من .env.local وأعد تشغيل npm run dev
429 أو انتظار طويلكل 5 فتحات مشغولةضع الطلبات في طابور أو اعرض رسالة انتظار
حالة لا تتغير أبدًاالاستعلام عن معرّف خاطئسجّل المعرّف الذي يعيده الاستدعاء الأول
يعمل محليًا ويفشل على الإنترنتالمتغيرات غير مضبوطة على المضيفأضف المتغيرين في إعدادات المضيف

بناء صفحة الأوامر النصية

مصمم يرسم تخطيط بطاقة لمعرض صور على جهاز لوحي

النموذج والحالة

// components/Generator.tsx
"use client";
import { useState } from "react";

export default function Generator() {
  const [prompt, setPrompt] = useState("");
  const [image, setImage] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function generate() {
    setBusy(true);
    setError(null);
    setImage(null);
    try {
      const start = await fetch("/api/generate", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ prompt }),
      });
      if (!start.ok) throw new Error("Could not start the job");
      const { id } = await start.json();

      for (let i = 0; i < 60; i++) {
        await new Promise((r) => setTimeout(r, 2000));
        const poll = await (await fetch(`/api/generate/${id}`)).json();
        if (poll.status === "succeeded") return setImage(poll.output);
        if (poll.status === "failed" || poll.status === "canceled") {
          throw new Error(poll.error ?? "Generation failed");
        }
      }
      throw new Error("Timed out, try again");
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <main className="mx-auto max-w-3xl p-6">
      <textarea
        value={prompt}
        onChange={(e) => setPrompt(e.target.value)}
        maxLength={4000}
        placeholder="A lighthouse at dawn, 35mm film, soft fog"
        className="w-full rounded border p-3"
      />
      <button
        onClick={generate}
        disabled={busy || prompt.length < 3}
        className="mt-3 rounded bg-black px-4 py-2 text-white disabled:opacity-50"
      >
        {busy ? "Generating..." : "Generate"}
      </button>
      {error && <p className="mt-3 text-red-600">{error}</p>}
      {image && <img src={image} alt={prompt} className="mt-6 w-full rounded" />}
    </main>
  );
}

استورده في app/page.tsx واعرض <Generator />. شغّل npm run dev، وافتح localhost:3000، واكتب أمرًا نصيًا، وستحصل على مولّد يعمل.

المعرض والتنزيلات

أضف لمستين بعد أن تعمل الأساسيات. لُفّ الصورة برابط مع السمة download ليستطيع المستخدمون حفظ الملف. ثم خزّن كل نتيجة في localStorage، وهي مصفوفة من كائنات { prompt, url }، واعرضها أسفل النموذج في شبكة. بذلك يكون لديك سجل دون قاعدة بيانات.

💡 اجعل الأمر النصي نص البديل للصورة. يساعد قارئات الشاشة، ويمنح معرضك نصًا مفيدًا لمحركات البحث.

كيفية استخدام Imagen 4 على PicassoIA

قبل أن تُثبّت أسلوبًا في موقعك، جرّبه في مكان يكون فيه التكرار رخيصًا. موقع PicassoIA فيه ملعب تجريبي لكل نموذج، وعناصر التحكم تتبع النمط نفسه: حقل للأمر النصي، وبعض الإعدادات، وزر للتوليد.

صور فوتوغرافية مطبوعة مرتبة في شبكة بجانب لابتوب يعرض صفحة معرض

تشغيل أمر تجريبي

  1. افتح صفحة Imagen 4.
  2. الصق أمرًا نصيًا منظمًا: الموضوع، والمكان، والإضاءة، والعدسة، والملمس.
  3. اختر نسبة عرض إلى ارتفاع 16:9 إذا كان النموذج يدعمها.
  4. اضغط توليد وتحقق من النتيجة بالحجم الكامل.
  5. غيّر تفصيلة واحدة في كل مرة، ثم وَلّد من جديد.

أمر يعمل جيدًا للمخرجات الفوتوغرافية:

كوب سيراميك من القهوة السوداء على طاولة بلوط متآكلة، إضاءة نافذة من اليسار، عدسة 50 ملم عند f/2، عمق ميداني ضحل، وملمس خشب وبخار مرئيين، وألوان Kodak Portra 400.

كل جزء من هذا الأمر يؤدي وظيفة:

الجزءالمثاللماذا يفيد
الموضوعكوب سيراميك من القهوة السوداءيسمّي الشيء الواحد الذي تدور حوله الصورة
المكانطاولة بلوط متآكلةيمنح الخلفية مادة وأجواء
الإضاءةإضاءة نافذة من اليسارتحدد الظلال والاتجاه
العدسة50 ملم عند f/2تتحكم في عمق الميدان والمنظور
الملمسخشب وبخاريدفع النتيجة نحو صورة فوتوغرافية حقيقية

مقارنة ثلاثة نماذج

شغّل الأمر النصي نفسه عبر عدة نماذج قبل أن تقرر ما الذي سيدعم الموقع. تقدم API أربعة نماذج picassoia/* فقط وقت كتابة هذا المقال، لذا فالنماذج الأخرى في هذا الجدول مخصصة لاختيار الأسلوب في ملعب التجربة.

النموذجنقطة القوةالصفحة
Imagen 4تفاصيل فوتوغرافية طبيعيةافتح
FLUX 2 Proالتزام قوي بالأوامر النصيةافتح
Seedream 4.5ألوان غنية وصور أشخاصافتح
GPT Image 2نصوص داخل الصورافتح
FLUX Schnellمسودات سريعةافتح
PicassoIA Imageالنموذج الافتراضي في القالبافتح

احتفظ بالأمر الفائز كنص بدء في السمة placeholder، وحوّل أفضل ثلاثة أوامر لديك إلى رقائق أمثلة قابلة للنقر أسفل منطقة النص. الزوار الذين يرون مثالًا جيدًا يكتبون أوامر أفضل، والأوامر الأفضل تعني توليدًا مهدورًا أقل.

أضف نماذج لغوية والفيديو لاحقًا

كتابة الكود بنموذج لغوي

يمكنك بناء القالب كاملًا بمساعد برمجي. Claude Sonnet 5 وGPT 5.6 Sol مدرجان كلاهما لمهام البرمجة، وGemini 3.5 Flash مناسب للتعديلات السريعة. الصق شجرة المجلدات من هذا المقال واطلب ملفًا واحدًا في كل مرة، ثم اقرأ كل سطر قبل أن تعتمده.

مطوران يبرمجان معًا تطبيق ويب مع شبكة من الصور المولّدة

إعادة كتابة الأوامر القصيرة

معظم الزوار يكتبون خمس كلمات. يمكن لنموذج لغوي أن يوسّعها قبل استدعاء الصورة:

  1. أضف خطوة rewrite في route.ts ترسل نص المستخدم إلى نموذج مثل Kimi K2.6.
  2. وجّهه ليعيد أمرًا واحدًا فيه الموضوع والإضاءة والعدسة والملمس.
  3. أرسل هذا الأمر إلى نموذج الصور واعرض النسختين للمستخدم.

تحريك النتائج

المسارنان نفسهما يتعاملان مع الفيديو. غيّر PICASSOIA_MODEL إلى نموذج فيديو، مثل picassoia/seedance-2.5-lite، فتصبح المخرجات رابط MP4. اعرضه بعلامة <video controls> وارفع فترة الاستعلام، لأن الفيديو يستغرق وقتًا أطول من الصور. لمقارنة الخيارات أولًا، تعرض صفحة Seedance 2.5 Lite وصفحة Wan 3 ما ينتجه كل نموذج.

انشر دون مفاجآت

رفوف خوادم في ممر هادئ من مركز بيانات

الإشراف على الأوامر النصية

المولّد العام يُساء استخدامه خلال أيام. شغّل كل أمر نصي عبر نموذج إشراف على المحتوى قبل إنشاء التنبؤ. Llama Guard 4 12B مصمم لهذه المهمة: يصنّف الأمر بأنه آمن أو غير آمن، وتحجب الطلب عندما يقول إنه غير آمن. أضف أيضًا إشعارًا واضحًا بشروط الاستخدام أسفل النموذج.

حدود الطلبات والطوابير

الخطرالحل
زائر واحد يغمر المسار بالطلباتحدّد الطلبات لكل عنوان IP، مثلًا 10 في الساعة
زوار أكثر من الفتحات المتزامنةعطّل الزر أثناء تشغيل المهمة واعرض رسالة انتظار
تسريب التوكن في المتصفحأبقِ الاستدعاءات على الخادم ولا تستخدم NEXT_PUBLIC_ للأسرار أبدًا
تكلفة خارجة عن السيطرةحدّد عدد التوليدات اليومية ونبّه عند الوصول إلى 80%

يعمل عداد في الذاكرة على خادم واحد. أما على الاستضافة بلا خوادم، فلكل نسخة من الدالة ذاكرتها الخاصة، لذا استخدم مخزنًا مشتركًا للمحدِّد.

النشر والمراقبة

يتطلب النشر خمس خطوات:

  1. ادفع المستودع إلى GitHub.
  2. استورده إلى منصة الاستضافة التي تفضلها.
  3. أضف PICASSOIA_API_TOKEN وPICASSOIA_MODEL كمتغيرات بيئة.
  4. اضبط أقصى مدة للدالة لتكون كافية لمسار الاستعلام.
  5. افتح الرابط المباشر وولّد ثلاث صور تجريبية.

بعد الإطلاق، سجّل حالة كل مهمة (ولا تسجّل التوكن أبدًا)، وراجع معدل الفشل أسبوعيًا. الارتفاع المفاجئ يعني غالبًا تغييرًا في النموذج أو حدًا وصلت إليه. اكتب أيضًا README.md قصيرًا، يتضمن المتغيرين وأمر التشغيل ولقطة شاشة، لأن ملف README أول ما يراه الناس في مشروع GitHub.

ابنِ موقعك على Picasso IA

الآن لديك القطع: حزمة، وشجرة مجلدات، ومساران للخادم، وصفحة للأوامر، وقائمة للأمان. أسرع طريقة لجعل الموقع يبدو مكتملًا هي اختيار مظهره قبل كتابة المزيد من الكود.

فريق صغير يحتفل بإطلاق موقع توليد الصور الخاص به

افتح Picasso IA، وشغّل أمرك النصي الأول عبر Imagen 4 أو FLUX 2 Pro، واحفظ النتائج الثلاث التي تعجبك أكثر. ستصبح هذه الأوامر رقائق الأمثلة، وصفحة الهبوط، وأول حالات الاختبار لديك. ثم أنشئ توكن API الخاص بك من صفحة PicassoIA API، والصق الكود من هذا المقال في مستودع جديد، واضغط توليد. أول صورة على نطاقك الخاص هي اللحظة التي يصبح فيها المشروع حقيقيًا، فاجعل ذلك الأمر النصي جيدًا.

شارك هذا المقال

اختر لغتك

مقالات ذات صلة