مع StoryPixAI كان هدفي هو إنشاء تطبيق ويب تفاعلي يسمح للمستخدمين بإنشاء قصص للأطفال، والتي تُثري بالصور المولدة بواسطة نماذج الذكاء الاصطناعي. لتحقيق ذلك، استخدمت عدة خدمات من AWS مثل Lambda، API Gateway، DynamoDB، S3 وCognito للمصادقة. كود البنية التحتية يُدار باستخدام Terraform، والنشر مؤتمت عبر GitLab CI. في هذا المقال، سأكشف لكم كواليس هذا المشروع المثير، من الخيارات التكنولوجية إلى التحديات التي واجهتها.

المقدمة

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

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

في هذا المقال من المدونة، سأشارك معكم اختياراتي التكنولوجية والتحديات التي واجهتها خلال هذه المغامرة المثيرة.

ولكن قبل كل شيء، قطعة ترويق!

لإعطائكم فكرة عن قدرة StoryPixAI، إليكم بعض القصص المولدة تلقائيًا بلغات متعددة.
كل قصة مرفقة برسومات، مما يجعل السرد أكثر جذبًا للأطفال:

الذكاء الاصطناعي في خدمة الإبداع: رحلة من التجارب

بدأت مغامرتي مع StoryPixAI بإثبات مفهوم بسيط: وظيفة Lambda التي تتفاعل مع OpenAI لإنشاء النصوص وDALL-E لإنشاء الصور. هذا النجاح الأول دفعني للاستكشاف المزيد من نماذج الذكاء الاصطناعي عبر Bedrock من AWS.

GPT-4 و GPT-4-o: الرواة المرنون

منذ بداية المشروع، كان GPT-4 من OpenAI اختيارًا بديهيًا لإنشاء النصوص. قدرة النموذج على فهم تفاصيل اللغة الطبيعية وإنتاج نصوص مترابطة وإبداعية مكنتني من إنشاء قصص جذابة، متوافقة مع عمر واهتمامات الأطفال. استطعت التجربة مع أساليب كتابة مختلفة، من القصص الخيالية إلى مغامرات الفضاء، مرورًا بقصص الحيوانات والقصص الخيالية.

عندما تم إطلاق GPT-4-o، قمت بسرعة بدمج هذا النموذج الجديد في StoryPixAI. لقد تأثرت بسرعته المتزايدة في التوليد، مما سمح بتقليل وقت الانتظار بشكل كبير، وبالتحسين الملحوظ في جودة القصص المُولَّدة، مع قصص أكثر سلاسة وتماسكاً وابتكاراً. أصبح GPT-4-0 بهذه الطريقة أداة رئيسية لـ StoryPixAI، مقدماً تجربة مستخدم أسرع وأكثر متعة.

DALL-E 3: الرسام المرجعي

إذا كانت نماذج توليد النصوص تقدم نتائج مرضية، فإن اختيار أداة توليد الصور أثبت أنه أكثر أهمية. بعد العديد من المحاولات، تم اعتماد DALL-E 3 كالنموذج المرجعي لـ StoryPixAI. قدرته على إنشاء رسوم توضيحية أصلية ومفصلة ومناسبة تمامًا للقصص التي يولدها GPT-4 كانت عاملًا حاسمًا في نجاح المشروع.

Bedrock من AWS: الباب المفتوح نحو التجريب

رغبةً في عدم الاقتصار على OpenAI، استخدمت Bedrock من AWS لدمج نماذج أخرى من الذكاء الاصطناعي التوليدي بسهولة في StoryPixAI. سمحت لي هذه المنصة باختبار Claude من Anthropic وMistral لتوليد النصوص، وStable Diffusion لإنشاء الصور.

على الرغم من أن هذه النماذج أعطت نتائج مثيرة للاهتمام، فقد اخترت في النهاية التركيز على GPT-4 وGPT-4-0 لسرعتهما وجودة توليد النصوص، وعلى DALL-E 3 لقدرتها على إنتاج رسومات توضيحية تتناسب تمامًا مع القصص. من المهم أن نلاحظ أن اللبنة الأساسية المستخدمة لتوليد الصور يعمل على تفاصيلها النموذج النصي نفسه إلى حد كبير، مما يضمن الاتساق بين الرواية والرسمة.

تحدي الـ API غير التزامني وDynamoDB

بمجرد التحقق من صحة PoC، شرعت في إنشاء API لجعل StoryPixAI متاحًا عبر واجهة الويب. هنا واجهت أول تحدٍ رئيسي: حد مهلة API Gateway. للتغلب على هذا القيد وللسماح بتوليد قصص أطول وأكثر تعقيداً، كان علي وضع بنية غير تزامنية.

دخل Amazon DynamoDB إلى اللعب في هذه المرحلة. استخدمت قاعدة البيانات NoSQL هذه لتخزين مهام توليد القصص الجارية، بالإضافة إلى نتائجها عند الانتهاء. بفضل هذا النهج، يمكن لـ API العودة بإجابة فورية للمستخدم، الذي يمكنه بعد ذلك مراجعة حالة طلبه واستعادة القصة المُولَّدة بمجرد جاهزيتها.

CORS وواجهة المستخدم: عقبات تجب التغلب عليها

كانت إعداد واجهة الويب مصدرًا للتحديات أيضًا. كان علي التعرف على تفاصيل CORS (مشاركة الموارد عبر المنشأ) للسماح للواجهة الأمامية الخاصة بي بالتواصل مع API. كما قضيت وقتًا في تحسين تجربة المستخدم بإضافة ميزات مثل اختيار نماذج الذكاء الاصطناعي وأنماط الصور.

التوجية: فن يجب إتقانه

خلال تطوير StoryPixAI، قمت بتحسين مهاراتي في التوجية، هذا الفن لصياغة الاستفسارات الصحيحة لتوجيه نماذج الذكاء الاصطناعي. تعلمت كيفية تعديل توجيهاتي بناءً على النماذج المستخدمة، ومعايير القصة، وتوقعات المستخدمين. كانت هذه الخطوة حاسمة للحصول على نتائج عالية الجودة وضمان تجربة مستخدم مرضية.

بنية تحتية قوية ومؤتمتة على AWS

تعتمد StoryPixAI على بنية تحتية لا خوادم مستضافة على Amazon Web Services (AWS)، مما يوفر مزيجًا مثاليًا من المرونة والقابلية للتوسع وتحسين التكاليف. هذه البنية، المؤتمتة بالكامل بفضل Terraform وGitLab CI/CD، تمكن من نشر التطبيق بسرعة وموثوقية.

خدمات AWS في قلب StoryPixAI

alt text

تركز بنية StoryPixAI على الخدمات التالية من AWS: * أمازون S3 (خدمة التخزين البسيط): تخزين الملفات الثابتة لموقع الويب (HTML, CSS, JavaScript) والقصص المُنتَجة بالإضافة إلى الصور المرتبطة بها.

  • أمازون CloudFront: شبكة لتوزيع المحتوى (CDN) التي تسرع توزيع محتوى StoryPixAI للمستخدمين حول العالم عن طريق تخزينه مؤقتًا في مواقع جغرافية قريبة منهم.
  • بوابة API الخاصة بأمازون (Amazon API Gateway): هي المدخل الآمن للتطبيق. تدير طلبات المستخدمين، تضمن مصادقتهم عن طريق Amazon Cognito، وتوجيههم إلى الوظائف المناسبة في Lambda.
  • AWS Lambda: وظائف بدون خادم تشكل محرك StoryPixAI. تقوم بترتيب عملية توليد القصص، إنشاء الصور، إدارة المهام غير المتزامنة والتفاعل مع DynamoDB والخدمات الأخرى من AWS.
  • أمازون DynamoDB: قاعدة بيانات NoSQL مرنة وعالية الأداء تُستخدم لتخزين المعلومات الأساسية لتشغيل التطبيق.
  • أمازون Cognito: خدمة إدارة الهوية والوصول التي تؤمن التطبيق بتمكين المستخدمين من تسجيل الدخول والتحكم في أذوناتهم. تضمن أن المستخدمين المصدقين فقط يمكنهم الوصول إلى ميزات توليد القصص.
  • أمازون Bedrock: منصة تبسط الوصول واستخدام نماذج الذكاء الاصطناعي التوليدية من مزودين مختلفين، مثل Anthropic (كلود) وStability AI (Stable Diffusion). يسهّل Bedrock دمج هذه النماذج في التطبيق دون الحاجة لإدارة البنية التحتية الأساسية لها.
  • خدمات أخرى من AWS: يستخدم StoryPixAI أيضًا خدمات AWS الأخرى، مثل IAM (Identity and Access Management) لإدارة الأذونات الدقيقة للوصول إلى الموارد، CloudWatch للرصد والسجلات (وهي ضرورية لاستكشاف الأخطاء وإصلاحها وتحليل الأداء)، وأنظمة مدير تخزين المعلمات (SSM Parameter Store) لتخزين المعلومات الحساسة مثل مفاتيح API، مما يضمن أمان التطبيق.

تيرافورم: الأتمتة في خدمة البنية التحتية

لإدارة هذه البنية التحتية المعقدة، اخترت تيرافورم، أداة Infrastructure as Code (IaC) التي تتيح وصف البنية التحتية في شكل كود تصريحي. بفضل تيرافورم، تمكنت من أتمتة إنشاء وتعديل وإزالة موارد AWS، مما يضمن بيئة متسقة، قابلة للتكرار وسهلة الإدارة. وهذا يبسط بشكل كبير عملية النشر ويقلل من خطر الأخطاء البشرية.

GitLab CI/CD: نشر سهل وموثوق

لضمان نشر مستمر وموثوق لـ StoryPixAI، قمت بإنشاء مسار CI/CD (التكامل المستمر/النشر المستمر) على GitLab. يقوم هذا المسار بأتمتة الاختبارات، البناء والنشر للتطبيق مع كل تعديل في الشيفرة المصدرية، مما يتيح اكتشاف الأخطاء وإصلاحها بسرعة وتقديم ميزات جديدة بثقة. يضمن هذا المنهج أن التطبيق دائماً محدث ويقلل من أوقات التوقف.

هذه المجموعة من AWS، تيرافورم وGitLab CI/CD أتاحت لي بناء بنية تحتية قوية، قابلة للتوسعة وسهلة الصيانة، مما يتركني مزيد من الوقت للتركيز على الجانب الإبداعي للمشروع وتحسين تجربة المستخدم.

البنية التحتية العامة لمشروع StoryPixAI

قبل الغوص في الشيفرة، إليك نظرة عامة على بنية التطبيق:

  1. موقع ثابت على S3: موقع ويب ثابت مستضاف على دلو S3، يمكن الوصول إليه عبر CloudFront للتوزيع العالمي.
  2. بوابة API: تعرض نقاط النهاية لتوليد القصص والتحقق من الحالة.
  3. وظائف Lambda:
    • StoryPixAI.py: يولد القصة والصور المرتبطة بها.
    • status_checker.py: يتحقق من حالة التوليد في DynamoDB.
  4. DynamoDB: يخزن حالة مهام التوليد. إس 3 : يخزن الصور المولدة وصفحات HTML الناتجة.
  5. كوغنيتو : يدير مصادقة المستخدمين لتأمين واجهة برمجة التطبيقات.

وظيفة لامبدا StoryPixAI.py

نظرة عامة

وظيفة StoryPixAI.py هي قلب التطبيق. هي مسؤولة عن:

  • توليد قصة بناءً على موجه المستخدم.
  • إنشاء تعليمات مفصلة لتوجيه نموذج الذكاء الاصطناعي في توليد القصة.
  • استخراج ملخصات لكل مشهد أو عنصر رئيسي في القصة.
  • توليد صور متطابقة مع هذه الملخصات.
  • دمج النص والصور في صفحة HTML.
  • تخزين النتيجة في S3 وتحديث الحالة في DynamoDB.

تحليل الكود

الواردات والتكوين الأولي

import json
import boto3
import base64
import os
import re
from datetime import datetime
from openai import OpenAI
import logging

# Configuration du logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)

generate_images = False
region_name = os.getenv("AWS_REGION", "us-east-1")

# Création d'un client SSM
ssm = boto3.client("ssm", region_name=region_name)

# Obtention de la clé API OpenAI depuis le SSM
parameter = ssm.get_parameter(Name="/openaikey", WithDecryption=True)

في هذا القسم، أقوم باستيراد الوحدات اللازمة، تكوين المُسجل (logger) للتصحيح، واسترداد مفتاح API الخاص بـ OpenAI المخزن في AWS Systems Manager Parameter Store (SSM). هذا يتيح تأمين المفتاح وعدم تخزينه بشكل واضح في الكود.

الوظائف المساعدة

تصحيح العلامات
def correct_resume_tags(text):
    # Corrige les balises 'résumé', 'resume', 'titre' et leurs variantes en 'resume' et 'titre' respectivement dans le texte généré.

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

استخراج الملخصات
def extract_summaries(text):
    # Extrait les résumés du texte en utilisant des balises spécifiques.

تستخدم هذه الوظيفة التعابير العادية لاستخراج أجزاء النص المحددة بين [resume] و[end_resume]. هذه الملخصات ستُستخدم كإشارات لتوليد الصور.

توليد التعليمات للصور
def generate_image_instructions(prompt, style, language):
    # Génère les instructions pour la création d'images.

تقوم هذه الوظيفة بتنسيق الموجه لتوجيه نموذج توليد الصور بواسطة تضمين النمط واللغة.

تحديث DynamoDB
def update_dynamodb(request_id, status, result_url=None):
    # Met à jour une entrée dans la table DynamoDB avec l'ID de la requête, le statut et l'URL du résultat.

تقوم هذه الوظيفة بتحديث جدول TaskStatus لتتبع حالة التوليد، وهو أمر أساسي لوظيفة status_checker.py.

تحليل متعمق لـ generate_story_instructions

وظيفة generate_story_instructions هي قلب المشروع. تقوم بتوليد مجموعة من التعليمات المفصلة التي سيتم تمريرها إلى نموذج الذكاء الاصطناعي لتوجيه توليد القصة.

def generate_story_instructions(prompt, language):
    """
    Génère les instructions pour créer une histoire captivante pour enfants.

    Args:
        prompt (str): Texte source pour inspirer l'histoire.
        language (str): Langue de l'histoire.

    Returns:
        str: Instructions formatées pour la génération de l'histoire.
    """
    language_description = get_language_description(language)
    return f"""
    Crée une histoire unique de 1000 à 1500 mots, captivante et riche en descriptions visuelles pour enfants uniquement dans la langue "{language_description}", inspirée par : "{prompt}". Cette histoire doit mêler aventure, magie, et enseigner des valeurs importantes telles que l'amitié, le courage, la persévérance, l'empathie, et la gentillesse.

    L'histoire peut aborder des thèmes comme : l'amitié entre un enfant humain et un animal merveilleux, la découverte d'un monde magique caché, un long voyage vers une contrée enchantée, un enfant qui découvre qu'il/elle possède des pouvoirs magiques spéciaux et doit apprendre à les maîtriser, une quête pour sauver une créature légendaire en danger, un voyage à travers des royaumes féeriques pour briser un sortilège ancien, une aventure sous-marine dans un monde marin peuplé de sirènes et de créatures fantastiques, une mission pour réunir des objets magiques dispersés afin d'empêcher un grand cataclysme, une compétition amicale entre enfants dotés de capacités extraordinaires dans une école de sorcellerie, etc.
    L'histoire peut également explorer : l'acceptation de soi à travers un personnage unique comme un enfant métamorphe, la découverte d'une ancienne civilisation perdue et de ses secrets, une épopée pour retrouver des parents disparus dans un monde parallèle, une lutte contre les forces des ténèbres menaçant d'engloutir un royaume enchanté, etc.
    N'hésites pas à combiner plusieurs de ces idées pour créer une trame narrative riche et captivante. Tu peux aussi t'inspirer de contes ou légendes traditionnels et leur donner un nouvel éclairage fantastique adapté aux enfants.
    Raconte l'histoire au présent pour une immersion maximale.

    Instructions spécifiques :
    - Utilise des phrases courtes et simples, adaptées pour des enfants de moins de 10 ans.
    - Intègre des dialogues dynamiques et réalistes pour rendre l'histoire vivante.
    - Choisis des mots simples pour une meilleure compréhension par de jeunes lecteurs.
    - Crée des personnages diversifiés en termes d'âge, de genre, d'origine ethnique et de capacités. Assure-toi que l'apparence des personnages (cheveux, yeux, taille, etc.) est précisée au niveau du résumé si jamais ils doivent y apparaître pour être cohérent avec le texte de l'histoire.
    - Attribue des traits de personnalité uniques, des intérêts, des peurs et des rêves à chaque personnage pour une caractérisation approfondie.
    - Développe les personnages et leurs relations tout au long de l'histoire en montrant leurs interactions, leurs moments de partage et leur évolution.
    - Crée des conflits émotionnels et intellectuels, au-delà des défis physiques.
    - Décris en détail les défis physiques et les actions des personnages pour les surmonter. Par exemple, lorsqu'ils traversent la forêt, mentionne les branches qui les gênent, les racines sur lesquelles ils trébuchent, la végétation dense qu'ils doivent écarter. Montre leur fatigue, leurs efforts pour avancer, les émotions qu'ils ressentent face à ces difficultés.
    - Fais échouer les personnages principaux à un moment donné. Montre comment ils gèrent cet échec et essaient à nouveau. Décris en détail leurs sentiments de doute, de frustration ou de découragement, et comment ils puisent dans leur détermination et leur amitié pour surmonter cet obstacle. Assure-toi que l'échec est significatif et impacte réellement la progression de l'histoire.
    - Crée des conflits entre les personnages principaux, ou entre les personnages principaux et les personnages secondaires.
    - Ajoute des rebondissements et des défis supplémentaires pour maintenir l'intérêt des jeunes lecteurs. Décris en détail la réaction des personnages face à ces rebondissements, leurs émotions, leurs doutes et leurs efforts pour s'adapter à la nouvelle situation.
    - Résous les conflits de manière créative et non violente, en mettant l'accent sur le pouvoir de la communication et de la coopération.
    - Développe les antagonistes en leur donnant des motivations claires, des traits de personnalité distincts et des capacités redoutables qui les rendent réellement menaçants pour les héros. Décris en détail leurs actions pour contrecarrer ou mettre en échec les héros à plusieurs reprises au cours de l'histoire. Montre comment leur présence et leurs actions sèment le doute, la peur ou le découragement chez les héros avant qu'ils ne parviennent à les surmonter.
    - Assure-toi que le récit comporte une structure narrative claire avec une introduction captivante, de l'action, des conflits, et une résolution.
    - Ajoute un objectif clair pour les personnages à atteindre et un accomplissement significatif à la fin de l'histoire.
    - Inclue des moments de réflexion ou d'émotion pour permettre aux lecteurs de se connecter aux personnages et à leurs aventures.
    - Varie les interactions entre les personnages pour éviter les répétitions et maintenir l'intérêt.
    - Maintiens un bon rythme dans l'histoire en alternant des scènes d'action, de réflexion et d'émotion. Ajoute des éléments de suspense pour maintenir l'intérêt des jeunes lecteurs.
    - Utilise abondamment des descriptions visuelles riches en couleurs, en textures et en formes pour stimuler l'imagination des enfants et créer un monde immersif.
    - Inclue des descriptions sensorielles pour enrichir l'expérience narrative (sons, odeurs, textures).
    - Chaque personnage doit avoir une motivation claire et des traits de caractère distincts.
    - Assure-toi que chaque chapitre se termine par un cliffhanger ou une question ouverte pour maintenir l'intérêt des lecteurs.
    - Ajoute des éléments éducatifs subtils (faits scientifiques, connaissances culturelles) pour enrichir l'histoire sans alourdir le récit.
    - Enrichis les descriptions sensorielles pour permettre aux lecteurs de vraiment "voir", "entendre" et "ressentir" l'environnement des personnages.
    - Personnalise l'histoire avec des noms ou des éléments familiers pour une connexion émotionnelle plus forte.
    - Intègre des questions de réflexion et d'interaction pour engager les enfants.
    - Ajoute des éléments d'humour et des jeux de mots pour rendre l'histoire amusante.
    - Utilise des illustrations mentales vives et détaillées pour stimuler l'imagination.
    - Intègre une leçon morale ou un message éducatif de manière naturelle dans le récit.
    - Intègre des messages positifs et encourageants dans tes histoires, comme l'importance de croire en soi, de poursuivre ses rêves et de surmonter les obstacles.
    - Ajoute des éléments d'humour et de légèreté dans tes histoires pour les rendre plus amusantes et agréables à lire pour les enfants.
    - Intègre des éléments éducatifs dans tes histoires de manière subtile et ludique, comme des métaphores pour enseigner des concepts scientifiques ou des voyages dans différents pays pour enseigner la géographie et les cultures.
    - Ajoute des éléments interactifs dans tes histoires, comme des questions aux enfants, des choix qui influencent l'histoire, ou des petits défis ou jeux à réaliser.

    Ajoute des difficultés et des obstacles significatifs pour rendre l'histoire plus engageante et permettre aux héros de montrer leur courage et leur ingéniosité :
    - Développe les antagonistes en leur donnant des motivations claires, des traits de personnalité distincts et des capacités redoutables qui les rendent réellement menaçants pour les héros. Décris en détail leurs actions pour contrecarrer ou mettre en échec les héros à plusieurs reprises au cours de l'histoire. Montre comment leur présence et leurs actions sèment le doute, la peur ou le découragement chez les héros avant qu'ils ne parviennent à les surmonter.
    - Décris chaque affrontement au niveau quasi "temps réel", avec les actions, réactions, émotions, blessures, etc. détaillées pas à pas, presque comme si on y assistait. Intègre des éléments de surprise, de retournements inattendus au cours de ces affrontements pour augmenter le suspense. Montre comment les capacités et l'ingéniosité des antagonistes poussent les héros dans leurs derniers retranchements.
    - Lorsque les héros échouent, prends le temps de décrire en détail leurs émotions négatives (déception, frustration, colère, tristesse, etc.) et leurs doutes intérieurs. Montre qu'ils remettent en question leur capacité à poursuivre leur quête à la suite de ces échecs cuisants. Fais en sorte qu'ils aient besoin d'un véritable déclic intérieur, motivé par l'amitié ou leurs valeurs, pour se relever et persévérer. Montre comment cela impacte leurs relations entre eux (reproches, disputes, tensions, ou au contraire un élan de solidarité).
    - Décris les affrontements physiques ou psychologiques étape par étape, en montrant les actions, réactions et émotions ressenties de part et d'autre. N'hésite pas à inclure des blessures, de la souffrance ou de la peur pour les héros lors de ces affrontements acharnés. Fais en sorte que la victoire des héros ne soit jamais acquise d'avance et nécessite des sacrifices ou des prises de risque de leur part.
    - Crée des situations où les héros doivent collaborer et utiliser leurs compétences spécifiques pour réussir.
    - Intègre des moments de doute ou de découragement pour montrer la persévérance des héros. Décris leurs luttes internes et comment ils trouvent la force de continuer. Fais en sorte que les héros aient besoin d'un véritable déclic intérieur, motivé par l'amitié ou leurs valeurs, pour se relever et persévérer.
    - Ajoute des moments où l'amitié ou la confiance entre les héros est mise à rude épreuve par les difficultés rencontrées. Montre comment ils doivent surmonter leurs doutes, leur colère ou leur rancune les uns envers les autres pour rester soudés. Décris leurs prises de conscience, leurs excuses et leur cheminement pour renouer des liens forts malgré l'adversité.
    - Place les héros dans des situations où ils doivent faire un choix difficile qui aura des conséquences douloureuses (abandonner un compagnon, renoncer à un rêve, etc.). Montre leur dilemme intérieur, leur déchirement avant de faire ce choix douloureux pour un plus grand bien. N'aie pas peur d'inclure des pertes, des renoncements ou des traumatismes marquants issus de ces choix cornéliens.
    - Fais en sorte que les personnages apprennent et grandissent à travers les difficultés qu'ils rencontrent.
    - Ajoute des rebondissements inattendus qui changent la direction de l'histoire et maintiennent l'intérêt des lecteurs. Décris en détail la réaction des personnages face à ces rebondissements, leurs émotions, leurs doutes et leurs efforts pour s'adapter à la nouvelle situation.
    - Fais en sorte que les antagonistes infligent de véritables blessures physiques et/ou psychologiques aux héros au cours des affrontements. Décris ces blessures, la douleur ressentie, l'impact sur leur moral et leurs capacités à avancer. Montre leur résolution, leur courage pour continuer malgré ces handicaps.
    - Assure-toi que chaque défi est pertinent pour l'histoire et contribue au développement des personnages.
    - Décris en détail chaque énigme ou défi rencontré par les personnages. Par exemple, si les enfants doivent résoudre des énigmes chantées par les vents, précise le contenu de ces énigmes et la manière dont les enfants trouvent les réponses grâce à leur persévérance ou à l'aide de personnages secondaires.
    - Lorsque les personnages surmontent un obstacle, montre le processus complet de leurs tentatives, incluant les échecs et les efforts qu'ils font avant de réussir. Par exemple, détaille comment ils essaient plusieurs méthodes pour résoudre une énigme ou surmonter un défi avant de finalement trouver la solution.
    - Intègre des dialogues et des interactions entre les personnages et les gardiens ou les antagonistes qui posent des défis. Par exemple, si un enfant des vents protège un objet précieux, décris la conversation où il teste la patience des héros et les réactions des enfants face à ce test.
    - Ajoute des descriptions des émotions et des pensées des personnages lorsqu'ils font face à des épreuves difficiles, montrant leur détermination, leurs doutes, et comment ils surmontent ces sentiments pour réussir.
    - Assure-toi que chaque défi est clairement expliqué avec des indices et des solutions logiques que les enfants peuvent comprendre et suivre. Par exemple, spécifie les indices que les héros utilisent pour résoudre les énigmes et comment ces indices les mènent à la solution.


    IMPORTANT : Ne traduisez ni modifiez pas les balises suivantes :
    [titre]Ton titre ici[end_titre] (balises de titre)
    [resume] et [end_resume] (balises de résumé)
    N'ajoutez aucune autre balise que celles spécifiées ci-dessus.

    Voici comment structurer les descriptions visuelles inspirées par : "{prompt}" :
    - Commence chaque description avec la balise [resume] et finis avec la balise [end_resume]. Ne traduisez ni modifiez pas ces balises.
    - Les descriptions doivent se concentrer exclusivement sur les éléments visuels sans inclure d'actions ou de dialogues des personnages.
    - Chaque élément clé mentionné dans le prompt initial doit être décrit de manière unique et détaillée.
    - Ne mentionne chaque élément (personnage, animal, lieu, objet clé) qu'une seule fois dans les descriptions visuelles. Une fois qu'un élément a été décrit, ne le mentionne plus dans les descriptions suivantes, même indirectement.
    - Utilise des descriptions riches en couleurs, en textures et en formes pour stimuler l'imagination visuelle.
    - Inclue des éléments fantastiques, magiques ou surréalistes pour rendre les scènes plus intéressantes et mémorables.
    - Veille à ce que chaque description soit suffisamment détaillée pour permettre la création d'une illustration complète.

    Exemple de structure de descriptions visuelles (ces exemples sont seulement pour référence, ne les utilisez pas tels quels dans l'histoire) :
    [resume]Un koala super sympa avec une fourrure douce et grise, des yeux pétillants et un sourire amical. Il est assis sur une branche d'eucalyptus, grignotant des feuilles et observant son environnement avec curiosité.[end_resume]
    [resume]Un escargot très méchant avec une coquille noire et luisante, et des yeux perçants qui semblent voir à travers tout. Il se déplace lentement mais de manière menaçante, laissant derrière lui une traînée de bave visqueuse.[end_resume]
    [resume]Un arbre magique avec des feuilles d'un bleu profond qui brillent comme des étoiles. Des oiseaux de toutes les couleurs chantent autour des branches, ajoutant une mélodie enchantée à l'atmosphère mystique.[end_resume]

    Assure-toi que chaque description visuelle est riche, détaillée et entièrement nouvelle, sans aucune répétition d'éléments précédents. Évite d'utiliser les exemples fournis ci-dessus et crée des descriptions fraîches pour chaque scène.

    La conclusion de l'histoire doit renforcer les thèmes de l'aventure et de l'amitié avec une touche plus percutante, et être accompagnée d'une dernière description visuelle marquante.
    [resume]Visualise le chemin de retour à travers un paysage unique et magique, différent pour chaque histoire. Par exemple, un pont arc-en-ciel, un sentier lumineux sous une pluie d'étoiles filantes, des pas dans le sable avec un soleil couchant, etc. Assure-toi que la description finale est riche en détails visuels et évoque une atmosphère enchantée et inoubliable.[end_resume]

    Pour varier les débuts d'histoire et éviter la répétition, choisis parmi les exemples suivants, ou laisse libre cours à ton imagination :
    - Une classe à l'école, un voyage en famille, une fête d'anniversaire, une visite chez les grands-parents, un jour de pluie où les enfants jouent à l'intérieur, une sortie en nature, etc.
    - La découverte d'un livre magique, une rencontre inattendue avec un personnage mystérieux, un rêve étrange qui devient réalité, un message secret trouvé dans une bouteille, un animal parlant qui apparaît soudainement, etc.
    - Des personnages principaux différents : une fratrie, des amis, un enfant et son grand-parent, un groupe de camarades de classe, etc.
    - Des lieux de départ variés : une maison en ville, une cabane dans les bois, un appartement au bord de la mer, une ferme, une école, etc.
    - Déclencheur de l'aventure variés aussi : un portail vers un monde magique, un objet mystérieux trouvé dans le grenier, un événement étrange comme une éclipse ou une étoile filante, un animal parlant qui a besoin d'aide, un visiteur de l'espace, etc.

    Cette structure aide à créer un récit harmonieux et visuellement riche, propice à l'illustration et captivant pour les enfants.
    Attention, je te rappelle la langue cible de l'histoire : "{language_description}"
    """

بناء الموجه

تم تصميم الموجه لتزويد نموذج الذكاء الاصطناعي بجميع المعلومات اللازمة لتوليد قصة متماسكة وتعليمية ومناسبة للأطفال.

  • اللغة: يمكن للمعلمة language_description تحديد لغة القصة، مما يضمن أن النص الناتج سيكون باللغة المطلوبة.

  • الموضوع: يتم دمج موجه المستخدم في التعليمات ليكون بمثابة أساس للقصة.

  • الطول: تم تحديد نطاق من 1000 إلى 1500 كلمة للتحكم بطول القصة.

  • العناصر الأساسية: تشجع التعليمات على تضمين عناصر مثل المغامرة والسحر والقيم التعليمية المهمة.

تفاصيل التعليمات

التعليمات المقدمة للنموذج تفصيلية للغاية لتوجيه التوليد بدقة.

إليك تحليل أجزاء مختلفة من الموجه:

  1. الهيكل السردي: يُطلب من النموذج تنظيم القصة بحيث يكون لها بداية جذابة، ووسط غني بالأحداث، وخاتمة مرضية.

  2. الوصف البصري: يجب أن تكون القصة غنية بالأوصاف البصرية لتحفيز خيال الأطفال.

  3. الشخصيات: يتم تشجيع تطوير شخصيات محبوبة بصفات مميزة.

  4. علامات محددة: تستخدم علامات مثل [titre]... [end_titre] و[resume]... [end_resume] لتحديد العنوان والأوصاف البصرية.

  5. العناصر الخيالية: يُدعى النموذج إلى تضمين عناصر سحرية أو خيالية لجعل القصة أكثر جاذبية.

  6. القيم التعليمية: يجب أن تعلم القصة قيماً هامة.

دور العلامات

تلعب العلامات دورًا حاسمًا في معالجة النص الناتج لاحقًا.

  • [titre]… [end_titre]: يحدد عنوان القصة. مما يسمح باستخراجها بسهولة لعرضها بشكل مناسب في واجهة المستخدم.

  • [resume]… [end_resume] : تحيط الأوصاف المرئية المفصلة للمشاهد الرئيسية في القصة. سيتم استخدام هذه الملخصات كحواثي لتوليد الصور.

المعالجة بعد التوليد

بمجرد أن يقوم نموذج الذكاء الاصطناعي بتوليد القصة باتباع هذه التعليمات، ينفذ الرمز الخطوات التالية:

  1. تصحيح العلامات : تضمن وظيفة correct_resume_tags أن جميع العلامات مهيأة بشكل صحيح للاستخراج.

  2. استخراج الملخصات : تستخدم وظيفة extract_summaries العلامات [resume] و [end_resume] لاستخراج الأوصاف المرئية.

  3. توليد الصور : يتم تمرير كل ملخص إلى وظيفة generate_image لإنشاء صورة مطابقة.

  4. إنشاء محتوى HTML : يتم دمج نص القصة والصور المولدة لإنشاء صفحة HTML كاملة.

التأثير على التوليد

بتوفير هذه التعليمات التفصيلية، يتم إرشاد النموذج لكي:

  • احترام الشكل : باستخدام العلامات المحددة، ينتج النموذج نصًا منظمًا يسهل معالجته تلقائيًا.

  • توليد محتوى مناسب : تضمن القيود على اللغة والأسلوب والمواضيع أن تكون القصة مناسبة للجمهور المستهدف.

  • تسهيل توليد الصور : عن طريق استخراج أوصاف مرئية دقيقة، نحصل على حوثيات ذات جودة عالية لتوليد الصور.

إدارة العلامات من قبل النموذج

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

توليد القصة

بمجرد إنشاء التعليمات التفصيلية بواسطة الدالة generate_story_instructions، الخطوة التالية هي تمرير هذه التعليمات إلى نموذج الذكاء الاصطناعي لكي ينشئ القصة.

def generate_story(prompt, model_type, model_id, language, api_key=None, region_name="us-east-1"):
    instruction = generate_story_instructions(prompt, language)

    if model_type == "openai":
        client = OpenAI(api_key=api_key)
        try:
            response = client.chat.completions.create(
                model=model_id,
                messages=[
                    {
                        "role": "system",
                        "content": "Vous êtes un assistant AI expert des histoires pour enfant.",
                    },
                    {"role": "user", "content": instruction},
                ],
            )
            first_choice_message = response.choices[0].message
            return first_choice_message.content
        except Exception as e:
            return f"Une erreur est survenue : {e}"

    # Gestion des autres modèles (Mistral, Anthropic, Meta) via Amazon Bedrock

التفاعل مع نموذج OpenAI

  • عميل OpenAI : أُنشئ عميل OpenAI باستخدام مفتاح API الذي تم استعادته سابقًا.

  • تحفيز : يتلقى النموذج سلسلة من الرسائل:

    • رسالة نظام توضح أن المساعد خبير في قصص الأطفال.
    • رسالة المستخدم تحتوي على التعليمات التفصيلية المولدة.
  • استجابة النموذج : يقوم النموذج بتوليد قصة بناءً على التعليمات المقدمة.

إدارة الأخطاء

إذا حدث استثناء أثناء استدعاء واجهة برمجة التطبيقات OpenAI، يتم التقاطه، ويتم إرجاع رسالة خطأ.

استخراج الملخصات والعلامات

بعد توليد القصة، الخطوة التالية هي استخراج الأوصاف المرئية باستخدام العلامات المحددة.

def correct_resume_tags(text):
    # Corrige les balises 'résumé', 'resume', 'titre' et leurs variantes en 'resume' et 'titre' respectivement dans le texte généré.

def extract_summaries(text):
    pattern = r"\[resume\](.*?)\[end_resume\]"
    summaries = re.findall(pattern, text, re.DOTALL)
    return summaries

تصحيح العلامات

قد يقوم النموذج أحيانًا بتغيير طفيف في العلامات (على سبيل المثال، إضافة لهجات). تضمن وظيفة correct_resume_tags أن تكون جميع العلامات متسقة ومهيأة بشكل صحيح.

استخراج الملخصات

تستخدم وظيفة extract_summaries تعبيرًا منتظمًا للعثور على كل الأمثلة للنص بين العلامات [resume] و [end_resume]. هذه الملخصات هي الأوصاف المرئية المفصلة التي سيتم استخدامها لتوليد الصور.

توليد الصور

بمجرد استخراج الملخصات، يتم استخدام كل ملخص لتوليد صورة مطابقة.

def generate_image_for_each_summary(summaries, model, bucket_name, seed, style, size, quality, language):
    images_urls = []
    for summary in summaries:
        image_data = generate_image(summary, model, seed, style, size, quality, language)
        if image_data is not None:
            image_url = upload_to_s3(image_data, bucket_name)
            images_urls.append(image_url)
        else:
            images_urls.append("")
    return images_urls

وظيفة generate_image

تستدعي وظيفة generate_image واجهة برمجة التطبيقات لنموذج توليد الصور (على سبيل المثال، OpenAI DALL·E) لإنشاء صورة من الملخص.

def generate_image(prompt, model, seed, style, size, quality, language):
    width, height = extract_dimensions(size)

    if model == "openai":
        client = OpenAI(api_key=parameter["Parameter"]["Value"])
        adjusted_prompt = generate_image_instructions(prompt, style, language)

        try:
            response = client.images.generate(
                prompt=adjusted_prompt,
                model=os.environ.get("OPENAI_IMAGE_MODEL"),
                n=1,
                size=size,
                response_format="b64_json",
                quality=quality,
                user="user_id",
            )
            image_data = response.data[0].b64_json
            return image_data
        except Exception as e:
            logger.error(f"Error generating image with OpenAI: {str(e)}", exc_info=True)
            return None

    # Gestion des autres modèles (Titan, Stable Diffusion) via Amazon Bedrock

توليد التعليمات للصور تقوم الدالة generate_image_instructions بتكييف الملخص لإنشاء موجه مناسب لتوليد الصور.

def generate_image_instructions(prompt, style, language):
    language_description = get_language_description(language)
    return f"""
    Génère un dessin pour enfant dans le style "{style}" basé sur cette description en langue "{language_description}" : {prompt}.
    La scène doit être purement visuelle, sans aucun texte, et conçue pour éveiller l'émerveillement chez les jeunes spectateurs.
    """
  • النمط: يتم تضمين النمط المحدد من قبل المستخدم (على سبيل المثال “ألوان مائية”، “كارتون”) في الموجه للتأثير على عرض الصورة.

  • اللغة: يتم تكييف الوصف مع اللغة المختارة، مما يمكن النموذج من فهم الفروق الثقافية الدقيقة.

  • تعليمات واضحة: من خلال توضيح أن يكون المشهد بصريًا بحتًا، نتجنب أن يضيف النموذج نصوصًا أو عناصر غير مرغوب فيها في الصورة.

التفاعل مع API OpenAI لإنشاء الصور

  • استدعاء API: يتم استخدام الدالة client.images.generate لتوليد الصورة.

  • بارامترات هامة:

    • موجه: يتم تمرير الموجه المعدل إلى API.
    • نموذج: النموذج المحدد لتوليد الصور.
    • الحجم: حجم الصورة (على سبيل المثال “1024x1024”).
    • الجودة: جودة الصورة (العادية، HD).
    • تنسيق الاستجابة: يتم إرجاع الصور بتنسيق base64 لتسهيل التخزين والتلاعب.

إدارة الأخطاء

الأخطاء أثناء توليد الصور يتم التقاطها وتسجيلها، مما يتيح تشخيص المشاكل.

إنشاء محتوى HTML

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

def create_html_with_images(text_data, images_urls, generate_images=True):
    """
    Crée un contenu HTML en intégrant le texte et les images générées.
    """
    # Extraction du titre
    title_match = re.search(r"\[titre\](.*?)\[end_titre\]", text_data)
    if title_match is not None:
        title = title_match.group(1)
        text_data = text_data.replace(title_match.group(0), "")
    else:
        title = "Histoire Générée par l'IA"

    # Initialisation du contenu HTML
    html_content = """
    <html>
    <head>
        <title>Histoire générée par l'IA</title>
        <meta charset='UTF-8'>
        <style>
            /* Styles CSS pour une présentation agréable */
            body { font-family: Arial, sans-serif; margin: 20px; }
            .title { text-align: center; font-size: 2em; margin-bottom: 20px; }
            .center { text-align: center; }
            img { max-width: 100%; height: auto; margin: 20px 0; }
            p { text-align: justify; line-height: 1.6; }
        </style>
    </head>
    <body>
    """
    html_content += f'<div class="title">{title}</div>\n'

    # Séparation du texte en segments basés sur les résumés
    summaries = extract_summaries(text_data)
    segments = re.split(r"\[resume\].*?\[end_resume\]", text_data, flags=re.DOTALL)

    # Assemblage du texte et des images
    for i, segment in enumerate(segments):
        formatted_segment = segment.strip().replace("\n", "<br>")
        html_content += f"<p>{formatted_segment}</p>\n"
        if generate_images and i < len(images_urls) and images_urls[i]:
            image_url = images_urls[i]
            html_content += f'<div class="center"><img src="{image_url}" alt="Image générée"></div>\n'

    html_content += "</body></html>"
    return html_content

شرح مفصل

  1. استخراج العنوان:

    • يستخدم تعبير منتظم للعثور على النص بين العلامات [titre] و [end_titre].
    • يزيل العلامات من النص الرئيسي بعد الاستخراج.
    • إذا لم يتم العثور على عنوان، يتم استخدام عنوان افتراضي.
  2. تهيئة HTML:

    • يبدأ محتوى HTML بعلامتي <html>, <head>, و <body>.
    • يتم تضمين أنماط CSS لتحسين العرض (الخطوط، الهوامش، المحاذاة).
  3. تجزئة النص:

    • يتم تقسيم النص إلى مقاطع باستخدام العلامتين [resume] و [end_resume].
    • تمثل المقاطع الأجزاء الخاصة بالقصة دون الملخصات.
  4. التجميع:

    • يتم إدراج كل مقطع من النص في فقرة <p>.
    • إذا تم تفعيل توليد الصور ووجود صورة مقابلة، تدرج الصورة بعد الفقرة.
    • يتم توسيط الصور وتكييفها مع حجم الشاشة لتحسين تجربة المستخدم.
  5. الإتمام:

    • يتم إضافة علامات الإغلاق </body> و </html> لاكمال وثيقة HTML.

لماذا هذه الطريقة؟

  • محاذاة النص والصور: بإدراج الصور بعد المقاطع النصية المقابلة، يتم إثراء القصة بصريًا، وهذا مهم بشكل خاص للأطفال.

  • المرونة: إذا اختار المستخدم عدم توليد صور، يتعامل الكود مع هذا الوضع بإدراج النص فقط.

  • إتاحة الوصول: باستخدام علامات دلالية وأنماط مناسبة، يصبح المحتوى متاحًا على أجهزة مختلفة (أجهزة الكمبيوتر، الأجهزة اللوحية، الهواتف الذكية).

التحميل على S3 وتحديث الحالة

بعد أن يتم توليد محتوى HTML، من الضروري جعله متاحًا للمستخدم. يتم ذلك بتحميل الملف على حاوية S3 مهيئة لاستضافة مواقع الويب الثابتة.

def upload_to_s3(content, bucket_name, content_type="image/jpeg"):
    """
    Télécharge le contenu sur S3 et retourne l'URL publique.
    """
    s3_client = boto3.client("s3")
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
    if content_type == "image/jpeg":
        file_name = f"generated_images/{timestamp}.jpg"
        content_to_upload = base64.b64decode(content)
    else:
        file_name = f"generated_content/{timestamp}.html"
        content_to_upload = content.encode("utf-8")
        content_type = "text/html; charset=utf-8"

    try:
        s3_client.put_object(
            Bucket=bucket_name,
            Key=file_name,
            Body=content_to_upload,
            ContentType=content_type,
            ACL='public-read'
        )
        return f"https://{os.environ['CLOUDFRONT_DOMAIN']}/{file_name}"
    except Exception as e:
        logger.error(f"Error uploading content to S3: {e}", exc_info=True)
        raise

تفاصيل تقنية

  • تسمية الملفات:

    • يتم تسمية الملفات باستخدام الطابع الزمني لضمان التفرد.
    • يتم تخزين الصور في مجلد generated_images/ وملفات HTML في generated_content/.
  • التحميل على S3:

    • استخدام عميل S3 من boto3 للتعامل مع الخدمة.
    • يتم ترميز المحتوى أو فك ترميزه بناءً على النوع (صورة أو نص).
    • المعامل ACL='public-read' يجعل الملف متاحًا للعامة. - بناء عنوان URL:
    • يتم بناء عنوان URL العام باستخدام النطاق المكون من CloudFront، مما يسمح بتوزيع سريع وآمن للمحتوى.
  • إدارة الاستثناءات:

    • في حالة حدوث خطأ أثناء التحميل، يتم تسجيل الاستثناء وطرحه ليتم معالجته بواسطة lambda_handler.

وظيفة رئيسية lambda_handler

وظيفة lambda_handler هي نقطة الدخول لوظيفة Lambda. تنسق جميع الخطوات الموضحة سابقا.

def lambda_handler(event, context):
    """
    Point d'entrée de la fonction Lambda.
    """
    try:
        # Récupération des données de la requête
        request_id = event.get("requestId")
        body = json.loads(event.get("body", "{}"))
        prompt = body.get("text", "Texte par défaut")
        # Récupération des autres paramètres (modèles, langue, etc.)

        # Mise à jour du statut dans DynamoDB
        update_dynamodb(request_id, "Processing")

        # Génération de l'histoire
        text_data = generate_story(prompt, story_generation_model, model_id, language, api_key)

        # Correction des balises et extraction des résumés
        text_data = correct_resume_tags(text_data)
        summaries = extract_summaries(text_data)

        # Génération des images
        images_urls = []
        if generate_images and summaries:
            images_urls = generate_image_for_each_summary(
                summaries, image_generation_model, bucket_name, seed, style_with_spaces, size, quality, language
            )

        # Création du contenu HTML
        html_content = create_html_with_images(text_data, images_urls, generate_images)

        # Upload du contenu sur S3
        result_url = upload_to_s3(html_content, bucket_name, content_type="text/html")

        # Mise à jour du statut avec le lien du résultat
        update_dynamodb(request_id, "link", result_url)

        # Retour de la réponse HTTP
        return {
            "isBase64Encoded": False,
            "statusCode": 200,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"requestId": request_id, "resultUrl": result_url}),
        }

    except Exception as e:
        logger.error(f"Erreur lors de l'exécution de la fonction lambda: {str(e)}", exc_info=True)
        update_dynamodb(request_id, "Failed")
        return {
            "statusCode": 500,
            "body": json.dumps({"message": "Internal server error"}),
            "headers": {"Content-Type": "application/json"},
        }

توضيح

  • معالجة الطلب:

    • يجلب المعلومات الضرورية من الحدث (event) المستلم.
    • تشمل معلمات الطلب السطر الإرشادي، النماذج المختارة، اللغة، إلخ.
  • تحديث الوضع:

    • قبل بدء المعالجة، يتم تحديث الوضع إلى “Processing” في DynamoDB.
  • توليد القصة:

    • استدعاء generate_story مع المعلمات المناسبة.
  • استخراج ومعالجة:

    • يتم تصحيح العلامات واستخراج الملخصات لتوليد الصور.
  • توليد الصور:

    • إذا تم تفعيل توليد الصور، يتم توليد الصور المقابلة وتجميع عناوين URL.
  • إنشاء محتوى HTML:

    • يتم دمج النصوص والصور لإنشاء المحتوى النهائي لـ HTML.
  • التحميل على S3:

    • يتم تحميل محتوى HTML على S3 ويتم الحصول على عنوان URL للنتيجة.
  • تحديث الوضع النهائي:

    • يتم تحديث الوضع إلى “link” مع عنوان URL للنتيجة في DynamoDB.
  • إرجاع الرد:

    • يشمل الرد requestId وعنوان URL للنتيجة، مما يمكن العميل من التحقق من الوضع أو الوصول مباشرة إلى المحتوى.
  • إدارة الاستثناءات:

    • في حالة حدوث خطأ، يتم تحديث الوضع إلى “Failed” ويُرجع رد بـ HTTP 500.

وظيفة Lambda status_checker.py

نظرة عامة

وظيفة status_checker.py تتيح للمستخدمين التحقق من حالة طلبهم لتوليد القصة. تستعلم DynamoDB لاسترجاع الحالة الحالية وإذا كانت متاحة، عنوان URL للنتيجة.

تحليل الكود

import boto3
import json
from botocore.exceptions import ClientError

def lambda_handler(event, context):
    """
    Fonction Lambda pour vérifier le statut d'une demande.
    """
    # Initialisation de DynamoDB
    dynamodb = boto3.resource("dynamodb")
    table = dynamodb.Table("TaskStatus")

    # Définition des en-têtes HTTP
    headers = {
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "GET,OPTIONS",
        "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
    }

    # Récupération du requestId depuis les paramètres de la requête
    query_params = event.get("queryStringParameters")
    if not query_params or "requestId" not in query_params:
        return {
            "statusCode": 400,
            "body": json.dumps({"message": "Missing requestId"}),
            "headers": headers,
        }

    request_id = query_params["requestId"]
    try:
        # Récupération de l'élément dans DynamoDB
        response = table.get_item(Key={"requestId": request_id})
        if "Item" in response:
            item = response["Item"]
            return {
                "statusCode": 200,
                "body": json.dumps(
                    {
                        "status": item.get("status", "Unknown"),
                        "resultUrl": item.get("resultUrl", ""),
                    }
                ),
                "headers": headers,
            }
        else:
            return {
                "statusCode": 404,
                "body": json.dumps({"message": "Request ID not found"}),
                "headers": headers,
            }
    except ClientError as e:
        return {
            "statusCode": 500,
            "body": json.dumps({"message": str(e)}),
            "headers": headers,
        }

تفاصيل

  • جلب requestId:

    • requestId أساسي لتحديد طلب المستخدم المحدد.
  • استعلام DynamoDB:

    • تحاول الوظيفة جلب العنصر المقابل لـ requestId.
    • إذا كان العنصر موجوداً، يتم استخراج الحالة و resultUrl.
  • إنشاء الرد:

    • إذا كانت الحالة متاحة، يتم إرجاعها مع عنوان URL للنتيجة.
    • إذا لم يتم العثور على العنصر، يتم إرجاع خطأ 404.
    • في حالة حدوث خطأ أثناء الاستعلام عن قاعدة البيانات، يتم إرجاع خطأ 500 برسالة مناسبة.
  • رؤوس HTTP:

    • يتم تحديد الرؤوس لتمكين طلبات CORS من الموقع الإلكتروني.

التكامل مع API Gateway

تكوين نقاط النهاية

يقوم API Gateway بنشر نقطتي نهاية رئيسيتين للتفاعل مع وظائف Lambda:

  1. /generate-image:

    • الطريقة: POST
    • الوصف: يتيح للمستخدمين بدء توليد قصة وربما الصور المرتبطة.
    • التكامل: متصل بوظيفة Lambda StoryPixAI.py.
  2. /check-status:

    • الطريقة: GET
    • الوصف: يتيح للمستخدمين التحقق من حالة طلبهم بتوفير requestId.
    • التكامل: متصل بوظيفة Lambda status_checker.py.

المصادقة بـ Cognito

لتأمين API والتحكم في الوصول إلى الموارد، قمت بدمج Amazon Cognito.

  • User Pool:

    • يدير معلومات تعريف المستخدمين.
    • يتيح التسجيل، تسجيل الدخول وإدارة المستخدمين.
  • Authorizer:

    • مكون في API Gateway للتحقق من رموز JWT الصادرة عن Cognito.
    • يضمن أن الطلبات المصدقة فقط يمكنها الوصول إلى النقاط المحمية. - الدمج على API Gateway:
    • نقاط النهاية /generate-image و /check-status محمية بواسطة authorizer كوجنيتو.
    • يجب على العملاء تضمين رمز المصادقة في رؤوس الطلبات الخاصة بهم (Authorization).

موقع ثابت على S3 والتفاعل مع API

هيكل الموقع

يعمل الموقع الثابت كواجهة مستخدم للتطبيق.

  • index.html:

    • يحتوي على النموذج الذي يسمح للمستخدمين بإدخال النص، اختيار خيارات التوليد، وإرسال طلبهم.
    • يتضمن النصوص اللازمة للتفاعل مع API وإدارة المصادقة.
  • storypixai.js:

    • يحتوي على كود جافا سكريبت لإدارة التفاعلات مع API.
    • يدير المصادقة مع كوجنيتو، إرسال النموذج، متابعة الحالة، وعرض النتائج.

تدفق عمل المستخدم

  1. الاتصال:

    • يسجل المستخدم الدخول عبر نموذج تسجيل الدخول المدمج.
    • يتم التحقق من المعلومات عبر كوجنيتو.
  2. إرسال الطلب:

    • يملأ المستخدم النموذج بالنص والخيارات المطلوبة.
    • عند الإرسال، يتم إرسال طلب POST إلى نقطة النهاية /generate-image بالبيانات.
  3. المعالجة غير المتزامنة:

    • ترجع API فورًا requestId.
    • يتم تنفيذ عملية التوليد في الخلفية.
  4. التحقق من الحالة:

    • يقوم الموقع بفحص دوري لنقطة النهاية /check-status موفرًا requestId.
    • بمجرد استقبال حالة “link”، يتم عرض عنوان URL للنتيجة للمستخدم.
  5. عرض النتيجة:

    • يمكن للمستخدم النقر على الرابط للوصول إلى القصة المولدة بالصور.

إدارة الطلبات والاستجابات

  • الطلبات المصادق عليها:

    • تشمل جميع الطلبات المرسلة إلى API رمز المصادقة.
    • يتم إدارة الرمز بواسطة SDK كوجنيتو المضمن في الموقع.
  • إدارة الحالات:

    • الحالات المحتملة هي “معالجة”، “link”، “فشل”.
    • يقوم الموقع بتعديل واجهته بناءً على الحالة المستلمة (مثلاً، عرض مؤشر تحميل، رسالة خطأ، رابط إلى النتيجة).

التفاعلات بين المكونات

إليك كيف تتفاعل المكونات المختلفة:

  • موقع الويب ↔️ API Gateway:

    • يرسل الموقع طلبات إلى نقاط النهاية المكشوفة بواسطة API Gateway.
    • تدرج رموز المصادقة لتأمين الطلبات.
  • API Gateway ↔️ وظائف لامبدا:

    • يقوم API Gateway باستدعاء وظائف لامبدا المقابلة حسب الطلبات المستلمة.
  • وظائف لامبدا ↔️ DynamoDB:

    • تتفاعل وظائف لامبدا StoryPixAI.py و status_checker.py مع DynamoDB لتحديث واسترجاع حالة الطلبات.
  • وظيفة لامبدا ↔️ S3:

    • تقوم وظيفة StoryPixAI.py برفع الصور المولدة والمحتوى HTML إلى S3.
  • CloudFront ↔️ S3:

    • يُستخدم CloudFront لتوزيع المحتوى المخزن على S3 بشكل سريع وآمن.
    • تشير عناوين URL المقدمة للمستخدمين إلى نطاق CloudFront.
  • المستخدم ↔️ موقع الويب:

    • يتفاعل المستخدم مع الموقع لإرسال الطلبات ومشاهدة النتائج.

مثال على النتائج في سجلات Cloudwatch بعد طلب استعلام

إليك مثال على سجل النتائج بعد طلب استعلام لتتمكن من رؤية الصيغة الخام للبيانات المولدة:

[INFO]	2024-07-22T19:13:49.764Z	4ec7d759-2fd2-49ca-b929-4f4d12629c73	Texte généré par l'ia : [titre]Tom et Zoé à l'aventure ![end_titre]

Une belle matinée de printemps, Tom et Zoé se retrouvent chez leur grand-mère pour les vacances. Tom a des cheveux bruns et ébouriffés, des yeux verts pétillants et une tache de rousseur sur le nez. Zoé, elle, a de longs cheveux blonds tressés, des yeux bleus comme le ciel et toujours un sourire aux lèvres.

Ce jour-là, alors qu'ils jouent dans le jardin, ils découvrent quelque chose d'étrange près du vieux puits. "Regarde, Zoé, cette lumière étrange !", s'exclame Tom.

"On dirait un passage secret...", murmure Zoé avec fascination. Ils se regardent, surexcités par la perspective d'une aventure. Ils s'approchent prudemment et tombent sur un escalier en colimaçon menant sous terre. Sans hésiter, ils commencent à descendre.

L'escalier les mène à une forêt lumineuse où les arbres sont couverts de feuilles dorées et où des fleurs scintillent de toutes les couleurs de l'arc-en-ciel. Le sol est tapissé de mousse douce et le chant mélodieux des oiseaux résonne autour d'eux.

[resume]Un arbre gigantesque au centre de la clairière, avec des racines enchevêtrées formant des arches naturelles. Ses feuilles changent de couleur au gré du vent, passant du vert émeraude au violet profond. Autour de ses branches, des lucioles dansent et éclairent l'atmosphère d'une lumière douce et féerique.[end_resume]

Alors qu'ils explorent les environs émerveillés, un petit renard roux avec une touffe blanche sur la queue surgit devant eux. "Bonjour, je m'appelle Félix. Êtes-vous perdus ?"

"Non, pas vraiment. Nous cherchons simplement à explorer !" répondent-ils en chœur.

"Alors, vous êtes au bon endroit. Mais attention, quelque chose de précieux est en danger ici. Un méga escargot vole toutes les salades du jardin magique et il faut l'arrêter ! Voulez-vous m'aider ?" demande Félix.

Tom et Zoé, enthousiastes devant cette mission, acceptent sans hésiter.

Félix les guide à travers des sentiers sinueux, où les branches des arbres semblent former des arches protectrices au-dessus de leurs têtes. La route devient de plus en plus difficile à mesure qu'ils s'enfoncent dans la forêt.

[resume]Une rivière cristalline aux eaux claires comme le verre, dans laquelle nagent des poissons multicolores. Les rives sont bordées de galets ronds et lisses, et des nénuphars aux fleurs roses flottent doucement à la surface.[end_resume]

"

Regardez là-bas, derrière ce buisson," chuchote Félix, en pointant une direction. Derrière les plantes, ils aperçoivent une trace de bave visqueuse brillamment éclairée.

"Ça doit être l'escargot," murmure Tom.

Ils suivent la piste de bave jusqu'à une clairière où ils tombent face à face avec le méga escargot. Il est énorme, avec une coquille noire et luisante et des yeux perçants qui semblent voir à travers tout.

"Je suis le protecteur de ces salades !" s'exclame l'escargot d'une voix grondante. "Elles m'appartiennent toutes !"

[resume]Le méga escargot est si grand que sa coquille ressemble à une petite montagne arrondie. Elle est noire avec des motifs argentés en spirale qui brillent sous le soleil. Ses antennes sont longues et frémissent à chaque mouvement. Il laisse derrière lui une traînée de bave qui scintille comme des cristaux de glace.[end_resume]

"Mais ces salades nourrissent tout le monde ici," réplique Zoé courageusement. "Il faut partager !"

L'escargot se met à rire et glisse vers eux lentement mais de manière menaçante. Tom et Zoé échangent un regard, ils savent qu'ils doivent utiliser leur intelligence et leur courage pour résoudre ce problème.

"Il y a sans doute un moyen de convaincre l'escargot !" dit Félix. "Utilisons la magie de cette forêt pour lui montrer une meilleure voie."

Zoé, qui découvre soudain qu'elle possède un pouvoir magique, ferme les yeux et se concentre. Elle sent une énergie chaude circuler en elle. Elle lève la main et des lianes lumineuses surgissent du sol, s'enroulant doucement autour de l'escargot sans lui faire de mal.

"Je vais créer un jardin immense juste pour toi," annonce Zoé, "mais tu devras promettre de partager avec tout le monde ici."

L'escargot, touché par la bonté de Zoé, hésite puis accepte. "Je ne savais pas que j'avais blessé autant de monde. Merci de me montrer un autre chemin."

Les lianes lumineuses dessinent alors un magnifique jardin rempli de salades et d'autres délices pour l'escargot. Cependant, le jardin ne s’ouvre que s’il appelle les autres créatures pour partager.

[resume]Un jardin magnifique avec des salades immenses, leurs feuilles vert tendre et croquantes. Des carottes orange vif et des courgettes vertes s'y mêlent, baignant dans une lumière dorée. Des papillons aux ailes irisées volent autour, ajoutant une touche de magie à ce lieu merveilleux.[end_resume]

En voyant cela, l'escargot laisse échapper une larme de reconnaissance et appelle instantanément les animaux de la forêt pour voir le miracle. Les habitants de la forêt acclament Tom et Zoé. Un énorme festin est organisé en leur honneur.

"Merci d'avoir sauvé notre jardin et notre amitié !" s'exclame Félix avec émotion.

Puis, ils se disent au revoir et, guidés par Félix, Tom et Zoé retrouvent le chemin de la maison. Au moment de passer le portail magique, ils se retournent une dernière fois pour admirer le spectacle enchanteur.

[resume]Un pont arc-en-ciel scintillant traverse le ciel, connectant la forêt magique à leur monde. Les couleurs brisées de l'arc iridescent se mélangent sous leurs regards émerveillés, illuminant la verdure environnante sous une lumière douce et chaleureuse. Chaque pas sur le pont résonne d'une mélodie cristalline.[end_resume]

Ils reprennent leur place dans le jardin de leur grand-mère, main dans la main, renforcés par cette aventure. "Tom, tu penses qu'on reverra Félix ?" demande Zoé rêveusement.

"J'espère bien ! Et qui sait quelle nouvelle aventure nous attend !" répond Tom en souriant.

La journée se termine sous le ciel étoilé, et leur amitié est plus forte que jamais, une étoile brillante dans l'univers de leurs rêves et de leurs aventures.

Les défis et les épreuves leur ont appris des valeurs précieuses : l'amitié, le partage, la persévérance, et surtout, la gentillesse.

Et c'est ainsi que Tom et Zoé grandissent, un peu plus chaque jour, devenant eux-mêmes des héros dans leurs cœurs d'enfants.

[resume]Des étoiles filantes traversent un ciel de velours noir, chaque trainée lumineuse ajoutant une touche de mystère à la nuit. Sur le chemin du retour, chaque pas dans le sable semble faire briller les grains comme des diamants sous la douce lumière de la lune. Une douce brise apporte l'odeur salée de la mer, ponctuée par le murmure des vagues au loin.[end_resume]

Fin.

التكامل المستمر مع GitLab CI/CD

لضمان تطوير ونشر سلس لتطبيق StoryPixAI، قمت بإعداد مسار تكامل مستمر (CI) ونشر مستمر (CD) باستخدام GitLab CI/CD. يقوم هذا الإعداد بأتمتة عمليات البناء والنشر، مما يضمن جودة وموثوقية الكود مع كل تعديل.

إعداد المسار

يتم تعريف المسار في ملف .gitlab-ci.yml في جذر المشروع. إليك نظرة عامة على هيكله:

stages:
  - Pré-requis optionel
  - Vérifications
  - Déploiements
  - Management
  - Suppressions

variables:
  TERRAFORM_VERSION: "1.5.7-*"
  TF_VAR_region: $AWS_DEFAULT_REGION
``` هذه التهيئة تحدد المراحل المختلفة لخط التجميع والمتغيرات العامة المستخدمة في عملية CI/CD.

### المهام الرئيسية

يتضمن خط التجميع عدة مهام رئيسية:

1. **التحقق من Terraform**:
   ```yaml
   التحقق من Terraform:
     stage: التحقق
     when: manual
     script:
       - /bin/bash -c "source export.sh && terraform_plan"

هذه المهمة تنفذ terraform plan للتحقق من التغيرات المخطط لها في البنية التحتية بدون تطبيقها.

  1. نشر Terraform:

    نشر Terraform:
      stage: النشر
      when: manual
      dependencies:
        - التحقق من Terraform
      script:
        - /bin/bash -c "source export.sh && terraform_apply"
    

    بعد التحقق، هذه المهمة تطبق التغييرات في البنية التحتية بتنفيذ terraform apply.

  2. حذف Terraform:

    حذف Terraform:
      stage: الحذف
      when: manual
      script:
        - /bin/bash -c "source export.sh && terraform_destroy"
    

    هذه المهمة تسمح بحذف البنية التحتية عند الضرورة، بتنفيذ terraform destroy.

  3. إدارة مفاتيح OpenAI:

    مفتاح OpenAI - إضافة:
      stage: متطلبات اختيارية
      when: manual
      script:
        - |
          KEYS_FOUND=false
          if [ -n "$OPENAI_KEY" ]; then
            /bin/bash -c "source export.sh && manage_openai_key put $OPENAI_KEY"
            KEYS_FOUND=true
          fi     
          if [ "$KEYS_FOUND" = false ]; then
            echo "لا توجد أي مفاتيح."
            exit 1
          fi      
    
    مفتاح OpenAI - حذف:
      stage: الحذف
      when: manual
      script:
        - /bin/bash -c "source export.sh && manage_openai_key delete"
    

    هذه المهام تدير إضافة وحذف مفاتيح API لـ OpenAI في AWS Parameter Store بأمان.

بيئة التشغيل

تُنفذ كل مهمة في حاوية Docker مبنية على Ubuntu 22.04، مع تثبيت Terraform و AWS CLI:

.terraform_template: &terraform_template
  image:
    name: ubuntu:22.04
  before_script:
    - apt-get update
    - apt-get install -y gnupg software-properties-common curl
    - curl -fsSL https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
    - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep 'VERSION_CODENAME' /etc/os-release | cut -d'=' -f2) main" | tee /etc/apt/sources.list.d/hashicorp.list
    - apt-get update
    - apt-get install -y terraform=${TERRAFORM_VERSION} python3-pip bash jq zip
    - pip3 install awscli >> /dev/null

مميزات هذا النهج لـ CI/CD

  1. الأتمتة: كل تعديل على الكود يطلق خط التجميع تلقائيًا، مما يضمن التحقق والنشر المتواصل.

  2. التحكم اليدوي: المهام الحاسمة مثل النشر والحذف مهيأة بشكل يدوي (when: manual)، مما يوفر تحكماً إضافياً قبل التنفيذ.

  3. إدارة آمنة للأسرار: التكامل مع AWS Parameter Store لإدارة مفاتيح API يضمن التعامل الآمن مع المعلومات الحساسة.

  4. المرونة: الهيكلية المكونة من مراحل تسمح بتنفيذ منظم ومنطقي لمختلف خطوات خط التجميع.

  5. إعادة الإنتاج: استخدام بيئة Docker معيارية يضمن أن الإنشاءات والاختبارات يمكن إعادة إنتاجها على أنظمة مختلفة.

تسمح هذه التهيئة لـ CI/CD ليس فقط بأتمتة نشر StoryPixAI، بل أيضًا بالحفاظ على مستوى عالٍ من الجودة والموثوقية خلال دورة التطوير.

الخاتمة

كانت StoryPixAI أكثر بكثير من مجرد مشروع تقني. لقد كانت مغامرة حقيقية في عالم الذكاء الاصطناعي التوليدي، مما سمح لي بدمج شغفي بالتكنولوجيا مع الرغبة في إنشاء قصص سحرية لأولادي.

هذا المشروع أتاح لي الفرصة لاستكشاف جوانب متنوعة من الذكاء الاصطناعي، من تصميم واجهة مستخدم بديهية إلى إتقان prompting، مرورًا بإنشاء بنية تحتية سحابية قوية باستخدام AWS و Terraform. كان كل خطوة مصدرًا للتعلم، مواجهةً تحديات فنية محفزة وإجباريًا على توسيع مهاراتي في تطوير البرمجيات المتكامل و DevOps.

آمل أن يكون هذا المنشور قد أعطاكم لمحة عن كواليس هذه المغامرة المثيرة.

النقاط الرئيسية

  • تعليمات مفصلة:

    • تعليمات واضحة ومنظمة تتيح الحصول على نتائج متسقة وعالية الجودة من نماذج الذكاء الاصطناعي. - الهندسة المعمارية المعيارية :
    • يلعب كل مكون (موقع ويب، بوابة API، Lambda، DynamoDB، S3، Cognito) دورًا محددًا، مما يسهل صيانة النظام وتطوره.
  • الأمان وقابلية التوسع :

    • يضمن استخدام الخدمات المدارة من AWS أمانًا قويًا وقدرة على التكيف مع الطلب المتزايد.

رابط المشروع : StoryPixAI

تمت ترجمة هذا المستند من النسخة fr إلى اللغة ar باستخدام النموذج gpt-4o. لمزيد من المعلومات حول عملية الترجمة، راجع https://gitlab.com/jls42/ai-powered-markdown-translator