Met StoryPixAI was mijn doel om een interactieve webapplicatie te creëren waarmee gebruikers verhalen voor kinderen kunnen genereren, verrijkt met afbeeldingen die zijn gegenereerd door kunstmatige intelligentiemodellen. Hiervoor heb ik verschillende AWS-services gebruikt, zoals Lambda, API Gateway, DynamoDB, S3 en Cognito voor authenticatie. De infrastructuurcode wordt beheerd met Terraform en de deployment is geautomatiseerd via GitLab CI. In dit artikel onthul ik de geheimen van dit spannende project, van technologische keuzes tot de uitdagingen die ik ben tegengekomen.
Inleiding
Als ervaren cloud-infrastructuurarchitect en DevOps-specialist ben ik altijd gefascineerd geweest door nieuwe technologieën en hun potentieel om ons dagelijks leven te transformeren. De opkomst van generatieve AI heeft een groeiende nieuwsgierigheid bij mij opgewekt, en ik voelde de behoefte om in deze opwindende wereld te duiken.
Zo werd StoryPixAI geboren, een persoonlijk project waarmee ik de oneindige mogelijkheden van AI kon verkennen om gepersonaliseerde verhalen en magische illustraties voor kinderen te creëren. Dit project stelde me in staat om me in te leven in de rol van full-stack ontwikkelaar, prompt engineer, product owner en zelfs UX/UI-ontwerper, terwijl ik mijn passie voor technologie deelde met mijn dierbaren.
In dit blogbericht zal ik mijn technologische keuzes en de uitdagingen die ik heb overwonnen gedurende dit spannende avontuur met jullie delen.
Maar eerst, een voorproefje!
Om je een voorproefje te geven van het potentieel van StoryPixAI, hier zijn enkele automatisch gegenereerde verhalen in verschillende talen.
Elk verhaal wordt begeleid door illustraties, waardoor het verhaal nog meeslepender wordt voor kinderen:
- Frans: Tom, Zoé en het Koninkrijk Argentor
- Engels: De magische zoektocht van prinses Lea en de gigantische slakkenroof
- Spaans: Het grote avontuur van Roger en Coco de clown
- Duits: De gekke houthakker en het magische avontuur
- Italiaans: Het meisje en de magische eenhoorn in het betoverde bos
- Portugees: De betoverde reis van Lucas en zijn vrienden
AI in dienst van creativiteit: een experimentele reis
Mijn avontuur met StoryPixAI begon met een eenvoudige Proof of Concept (PoC): een Lambda-functie die interageerde met OpenAI voor het genereren van tekst en DALL-E voor het creëren van afbeeldingen. Dit eerste succes moedigde me aan om verder te gaan en andere AI-modellen via Bedrock van AWS te verkennen.
GPT-4 en GPT-4-o: de wendbare verhalenvertellers
Vanaf het begin van het project was GPT-4 van OpenAI een voor de hand liggende keuze voor het genereren van tekst. Zijn vermogen om de nuances van natuurlijke taal te begrijpen en coherente en creatieve verhalen te produceren, stelde me in staat om boeiende verhalen te creëren die aansluiten bij de leeftijd en interesses van kinderen. Ik kon experimenteren met verschillende schrijfstijlen, van sprookjes tot ruimteavonturen, en van dierenverhalen tot fantastische vertellingen.
Toen GPT-4-0 werd gelanceerd, integreerde ik dit nieuwe model snel in StoryPixAI. Ik was onder de indruk van zijn verhoogde generatie snelheid, die het wachten op de generatie aanzienlijk heeft verkort, en door de opmerkelijke verbetering van de kwaliteit van de gegenereerde verhalen, met nog vloeiender, coherenter en fantasierijkere vertellingen. GPT-4-0 is zo een belangrijke troef geworden voor StoryPixAI, waardoor een snellere en aangenamere gebruikerservaring wordt geboden.
DALL-E 3: de referentie-illustrator
Hoewel de tekstgeneratiemodellen bevredigende resultaten opleverden, bleek de keuze van het beeldgeneratiehulpmiddel crucialer. Na vele tests kwam DALL-E 3 naar voren als het referentiemodel voor StoryPixAI. Zijn vermogen om originele, gedetailleerde illustraties te maken die perfect passen bij de verhalen gegenereerd door GPT-4 was een doorslaggevende factor in het succes van het project.
Bedrock van AWS: de deur naar experimentatie
Omdat ik me niet wilde beperken tot OpenAI, heb ik Bedrock van AWS gebruikt om gemakkelijk andere generatieve AI-modellen te integreren in StoryPixAI. Dit platform stelde me in staat om Claude van Anthropic en Mistral te testen voor tekstgeneratie, en Stable Diffusion voor beeldcreatie.
Hoewel deze modellen interessante resultaten opleverden, heb ik uiteindelijk gekozen om me te concentreren op GPT-4 en GPT-4-0 vanwege hun snelheid en kwaliteit van tekstgeneratie, en op DALL-E 3 vanwege zijn vermogen om illustraties te produceren die perfect passen bij de verhalen. Het is belangrijk op te merken dat de prompt gebruikt om de afbeeldingen te genereren grotendeels wordt opgesteld door het tekstmodel zelf, wat zorgt voor consistentie tussen het verhaal en de illustratie.
De uitdaging van de asynchrone API en DynamoDB
Toen de PoC gevalideerd was, begon ik met het maken van een API om StoryPixAI toegankelijk te maken via een webinterface. Het was op dit punt dat ik mijn eerste grote uitdaging tegenkwam: de tijdslimiet van API Gateway. Om deze beperking te omzeilen en de generatie van langere en complexere verhalen mogelijk te maken, moest ik een asynchrone architectuur opzetten.
Amazon DynamoDB kwam toen in beeld. Ik gebruikte deze NoSQL-database om taken voor verhaal generatie in uitvoering op te slaan, evenals hun resultaten zodra ze voltooid waren. Dankzij deze aanpak kon de API een onmiddellijke reactie aan de gebruiker geven, die vervolgens de status van zijn verzoek kon bekijken en het gegenereerde verhaal kon ophalen zodra het klaar was.
CORS en de gebruikersinterface: obstakels om te overwinnen
Het opzetten van de webinterface bracht ook uitdagingen met zich mee. Ik moest me vertrouwd maken met de subtiliteiten van CORS (Cross-Origin Resource Sharing) om mijn frontend te laten communiceren met de API. Ik besteedde ook tijd aan het verbeteren van de gebruikerservaring door functies toe te voegen zoals de selectie van AI-modellen en afbeeldingsstijlen.
Het prompten: een kunst die te beheersen is
Gedurende de ontwikkeling van StoryPixAI heb ik mijn vaardigheden in prompten verfijnd, de kunst om de juiste verzoeken te formuleren om de AI-modellen te sturen. Ik heb geleerd mijn prompts aan te passen op basis van de gebruikte modellen, de verhaalparameters en de verwachtingen van de gebruikers. Deze stap was cruciaal om kwalitatieve resultaten te behalen en een bevredigende gebruikerservaring te garanderen.
Een robuuste en geautomatiseerde infrastructuur op AWS
StoryPixAI rust op een serverloze infrastructuur gehost op Amazon Web Services (AWS), die een ideale combinatie van flexibiliteit, schaalbaarheid en kostenoptimalisatie biedt. Deze architectuur, volledig geautomatiseerd dankzij Terraform en GitLab CI/CD, stelt een snelle en betrouwbare implementatie van de applicatie mogelijk.
De AWS-services in het hart van StoryPixAI
De architectuur van StoryPixAI draait om de volgende AWS-services: * Amazon S3 (Simple Storage Service): Opslag van de statische bestanden van de website (HTML, CSS, JavaScript) en de gegenereerde verhalen en hun bijbehorende illustraties.
- Amazon CloudFront: Een content delivery network (CDN) dat de distributie van de inhoud van StoryPixAI versnelt naar gebruikers over de hele wereld door het in cache te plaatsen op geografisch nabijgelegen locaties.
- Amazon API Gateway: De beveiligde toegangspoort van de applicatie. Het beheert gebruikersaanvragen, verzorgt authenticatie via Amazon Cognito en leidt ze door naar de juiste Lambda-functies.
- AWS Lambda: Serverless functies die de motor van StoryPixAI vormen. Ze orkestreren het genereren van verhalen, het maken van afbeeldingen, het beheren van asynchrone taken en de interactie met DynamoDB en andere AWS-services.
- Amazon DynamoDB: Een flexibele en krachtige NoSQL-database die wordt gebruikt om essentiële informatie voor de werking van de applicatie op te slaan.
- Amazon Cognito: Een identiteits- en toegangsbeheer service die de applicatie beveiligt door gebruikers in staat te stellen in te loggen en hun toegangsrechten te controleren. Het zorgt ervoor dat alleen geauthenticeerde gebruikers toegang hebben tot de mogelijkheden voor het genereren van verhalen.
- Amazon Bedrock: Een platform dat de toegang en het gebruik van generatieve AI-modellen van verschillende leveranciers, zoals Anthropic (Claude) en Stability AI (Stable Diffusion), vereenvoudigt. Bedrock maakt het gemakkelijk om deze modellen in de applicatie te integreren zonder dat de onderliggende infrastructuur hoeft te worden beheerd.
- Andere AWS-diensten: StoryPixAI maakt ook gebruik van andere AWS-diensten, zoals IAM (Identity and Access Management) voor het fijnmazige beheer van toegangsrechten tot middelen, CloudWatch voor monitoring en logboeken (cruciaal voor debugging en prestatieanalyse), en Systems Manager Parameter Store (SSM Parameter Store) voor het opslaan van gevoelige informatie zoals API-sleutels, waarmee de veiligheid van de applicatie wordt gewaarborgd.
Terraform: automatisering ten dienste van de infrastructuur
Om deze complexe infrastructuur te beheren, heb ik gekozen voor Terraform, een Infrastructure as Code (IaC) tool die infrastructuur beschrijft in de vorm van declaratieve code. Met Terraform kon ik de creatie, wijziging en vernietiging van AWS-middelen automatiseren, waardoor een samenhangende, reproduceerbare en eenvoudig te beheren omgeving werd gegarandeerd. Dit vereenvoudigt het implementatieproces aanzienlijk en vermindert het risico op menselijke fouten.
GitLab CI/CD: vloeiende en probleemloze implementaties
Om een continue en betrouwbare implementatie van StoryPixAI te garanderen, heb ik een CI/CD-pipeline (Continuous Integration / Continuous Deployment) op GitLab opgezet. Deze pipeline automatiseert het testen, bouwen en implementeren van de applicatie bij elke wijziging in de broncode, waardoor fouten snel kunnen worden opgespoord en gecorrigeerd en nieuwe functies met vertrouwen kunnen worden geleverd. Deze aanpak zorgt ervoor dat de applicatie altijd up-to-date is en de uitvaltijd minimaal is.
Deze combinatie van AWS, Terraform en GitLab CI/CD heeft me in staat gesteld om een robuuste, schaalbare en gemakkelijk te onderhouden infrastructuur te bouwen, zodat ik meer tijd kan besteden aan het creatieve aspect van het project en het verbeteren van de gebruikerservaring.
Overzicht van de StoryPixAI-architectuur
Voordat we de code induiken, volgt hier een overzicht van de architectuur van de applicatie:
- Statische website op S3: Een statische website gehost op een S3-bucket, toegankelijk via CloudFront voor wereldwijde distributie.
- API Gateway: Stelt endpoints bloot voor het genereren van verhalen en het controleren van de status.
- Lambda-functies:
StoryPixAI.py:
Genereert het verhaal en de bijbehorende afbeeldingen.status_checker.py:
Controleert de generatie status in DynamoDB.
- DynamoDB: Slaat de status op van de generatie taken.
- S3 : Slaat de gegenereerde afbeeldingen en resulterende HTML-pagina’s op.
- Cognito : Beheert gebruikersauthenticatie om de API te beveiligen.
Lambda-functie StoryPixAI.py
Algemeen Overzicht
De functie StoryPixAI.py
is het hart van de applicatie. Het is verantwoordelijk voor:
- Genereren van een verhaal gebaseerd op een gebruikersprompt.
- Maken van gedetailleerde instructies om het AI-model te leiden bij het genereren van het verhaal.
- Uittrekken van samenvattingen voor elke scène of sleutelelement van het verhaal.
- Genereren van afbeeldingen die overeenkomen met deze samenvattingen.
- Combineren van de tekst en afbeeldingen in een HTML-pagina.
- Opslaan van het resultaat in S3 en het bijwerken van de status in DynamoDB.
Code Uiteenzetting
Imports en Initiële Configuratie
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)
In dit gedeelte importeer ik de benodigde modules, configureer ik de logger voor debuggen, en haal ik de OpenAI API-sleutel op die is opgeslagen in de AWS Systems Manager Parameter Store (SSM). Dit zorgt ervoor dat de sleutel veilig wordt bewaard en niet als platte tekst in de code wordt opgeslagen.
Hulpfuncties
Correctie van Tags
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é.
Deze functie zorgt ervoor dat de tags die worden gebruikt om de samenvattingen en titels te markeren, uniform zijn. Dit is cruciaal voor de correcte extractie van samenvattingen later.
Extractie van Samenvattingen
def extract_summaries(text):
# Extrait les résumés du texte en utilisant des balises spécifiques.
Het gebruikt reguliere expressies om de tekstgedeelten te extraheren die worden gemarkeerd door [resume]
en [end_resume]
. Deze samenvattingen zullen dienen als prompts voor de afbeeldingsgeneratie.
Genereren van Instructies voor de Afbeeldingen
def generate_image_instructions(prompt, style, language):
# Génère les instructions pour la création d'images.
Deze functie formatteert de prompt zodanig dat het het afbeeldingsgeneratiemodel leidt door de stijl en taal op te nemen.
Bijwerken van 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.
Het werkt de TaskStatus
-tabel bij om de status van de generatie bij te houden, wat essentieel is voor de functie status_checker.py
.
Diepgaande Analyse van generate_story_instructions
De functie generate_story_instructions
is de kern van het project. Het genereert een reeks gedetailleerde instructies die aan het AI-model worden doorgegeven om het verhaal te genereren.
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}"
"""
Constructie van de Prompt
De prompt is ontworpen om het AI-model alle benodigde informatie te geven om een samenhangend, educatief en kindvriendelijk verhaal te genereren.
-
Taal : De parameter
language_description
maakt het mogelijk om de taal van het verhaal op te geven, zodat de gegenereerde tekst in de gewenste taal is. -
Thema : De gebruikersprompt is geïntegreerd in de instructies om als basis voor het verhaal te dienen.
-
Lengte : Een bereik van 1000 tot 1500 woorden wordt opgegeven om de lengte van het verhaal te beheersen.
-
Belangrijke Elementen : De instructies moedigen de opname van elementen zoals avontuur, magie en belangrijke educatieve waarden aan.
Details van de Instructies
De instructies die aan het model worden gegeven zijn zeer gedetailleerd om de generatie nauwkeurig te begeleiden.
Hier is een analyse van de verschillende delen van de prompt:
-
Narratieve Structuur : Het model wordt gevraagd om het verhaal te structureren met een boeiende start, een rijk ontwikkelde middensectie, en een bevredigende conclusie.
-
Visuele Beschrijvingen : Het verhaal moet rijk zijn aan visuele beschrijvingen om de verbeelding van kinderen te prikkelen.
-
Personages : Het bevordert de ontwikkeling van sympathieke personages met afzonderlijke persoonlijkheden.
-
Specifieke Tags : Tags zoals
[titre]... [end_titre]
en[resume]... [end_resume]
worden gebruikt om de titel en visuele beschrijvingen te markeren. -
Magische Elementen : Het model wordt aangemoedigd om magische of fantasie-elementen op te nemen om het verhaal aantrekkelijker te maken.
-
Educatieve Waarden : Het verhaal moet belangrijke waarden onderwijzen.
Rol van de Tags
De tags spelen een cruciale rol in de latere verwerking van de gegenereerde tekst.
-
[titre]… [end_titre] : Omkader de titel van het verhaal. Hierdoor kan het gemakkelijk worden geëxtraheerd om op de juiste manier in de gebruikersinterface te worden weergegeven.
-
[resume]… [end_resume] : Omringt de gedetailleerde visuele beschrijvingen van sleutelscènes in het verhaal. Deze samenvattingen zullen worden gebruikt als prompts voor het genereren van afbeeldingen.
Verwerking na Generatie
Zodra het AI-model het verhaal heeft gegenereerd volgens deze instructies, voert de code de volgende stappen uit:
-
Correctie van Tags : De functie
correct_resume_tags
zorgt ervoor dat alle tags correct zijn opgemaakt voor extractie. -
Extractie van Samenvattingen : De functie
extract_summaries
gebruikt de tags[resume]
en[end_resume]
om de visuele beschrijvingen te extraheren. -
Generatie van Afbeeldingen : Elke samenvatting wordt doorgegeven aan de functie
generate_image
om een overeenkomstige afbeelding te creëren. -
Aanmaken van HTML-inhoud : De tekst van het verhaal en de gegenereerde afbeeldingen worden gecombineerd om een volledige HTML-pagina te maken.
Impact op de Generatie
Door deze gedetailleerde instructies te verstrekken, wordt het model geleid om:
-
Het Formaat te Respecteren : Door de gespecificeerde tags te gebruiken, produceert het model een gestructureerde tekst die geautomatiseerde verwerking vergemakkelijkt.
-
Geschikte Inhoud te Genereren : De beperkingen op taal, stijl en thema’s zorgen ervoor dat het verhaal geschikt is voor het beoogde publiek.
-
De Generatie van Afbeeldingen te Vergemakkelijken : Door nauwkeurige visuele beschrijvingen te extraheren, worden kwalitatieve prompts verkregen voor het genereren van afbeeldingen.
Beheer van Tags door het Model
Het model is expliciet geïnstrueerd om de tags niet te vertalen of te wijzigen. Dit is essentieel zodat de tags intact blijven en kunnen worden gebruikt voor de nabewerking. De instructies benadrukken dit punt om te voorkomen dat het model, dat zou kunnen proberen om de gehele tekst te herschrijven of te vertalen, de tags verandert.
Generatie van het Verhaal
Zodra de gedetailleerde instructies zijn gegenereerd door de functie generate_story_instructions
, is de volgende stap om deze instructies door te geven aan het AI-model om het verhaal te creëren.
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
Interactie met het OpenAI Model
-
OpenAI Client : Ik installeer een OpenAI-client met behulp van de eerder verkregen API-sleutel.
-
Prompting : Het model ontvangt een reeks berichten:
- Een systeembericht dat aangeeft dat de assistent een expert is in kinderverhalen.
- Het gebruikersbericht met daarin de gedetailleerde gegenereerde instructies.
-
Respons van het Model : Het model genereert een verhaal op basis van de verstrekte instructies.
Foutafhandeling
Als er een uitzondering optreedt tijdens het aanroepen van de OpenAI API, wordt deze vastgelegd en wordt een foutbericht geretourneerd.
Extractie van Samenvattingen en Tags
Na de generatie van het verhaal is de volgende stap het extraheren van de visuele beschrijvingen met behulp van de gespecificeerde tags.
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
Correctie van Tags
Het model kan soms de tags iets wijzigen (bijvoorbeeld accenten toevoegen). De functie correct_resume_tags
zorgt ervoor dat alle tags uniform en correct zijn opgemaakt.
Extractie van Samenvattingen
De functie extract_summaries
gebruikt een reguliere expressie om alle tekst tussen de tags [resume]
en [end_resume]
te vinden. Deze samenvattingen zijn de gedetailleerde visuele beschrijvingen die zullen worden gebruikt om de afbeeldingen te genereren.
Generatie van Afbeeldingen
Nadat de samenvattingen zijn geëxtraheerd, wordt elke samenvatting gebruikt om een overeenkomstige afbeelding te genereren.
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
Functie generate_image
De functie generate_image
roept de API van het afbeeldingsgeneratiemodel (bijvoorbeeld, OpenAI DALL·E) aan om een afbeelding te creëren op basis van de samenvatting.
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
Generatie van Instructies voor de Afbeeldingen
" De functie generate_image_instructions
past de samenvatting aan om een geschikte prompt te maken voor beeldgeneratie.
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.
"""
-
Stijl: De door de gebruiker gespecificeerde stijl (bijvoorbeeld “aquarel”, “cartoon”) wordt opgenomen in de prompt om de weergave van de afbeelding te beïnvloeden.
-
Taal: De beschrijving wordt aangepast aan de gekozen taal, wat het model kan helpen culturele nuances te begrijpen.
-
Duidelijke Instructies: Door te specificeren dat de scène puur visueel moet zijn, wordt voorkomen dat het model tekst of ongewenste elementen aan de afbeelding toevoegt.
Interactie met de OpenAI API voor Afbeeldingen
-
API-oproep: De functie
client.images.generate
wordt gebruikt om de afbeelding te genereren. -
Belangrijke Parameters:
- Prompt: De aangepaste prompt wordt doorgegeven aan de API.
- Model: Het gespecificeerde model voor beeldgeneratie.
- Grootte: De grootte van de afbeelding (bijvoorbeeld “1024x1024”).
- Kwaliteit: De kwaliteit van de afbeelding (standaard, HD).
- Antwoordformaat: De afbeeldingen worden geretourneerd in base64 om opslag en manipulatie te vergemakkelijken.
Foutafhandeling
Fouten bij het genereren van afbeeldingen worden vastgelegd en gelogd, zodat problemen kunnen worden gediagnosticeerd.
Aanmaak van HTML-inhoud
Nadat de afbeeldingen zijn gegenereerd op basis van de geëxtraheerde samenvattingen, bestaat de volgende stap uit het samenstellen van de tekst van het verhaal en de afbeeldingen in een presenteerbaar formaat voor de gebruiker. Dit gebeurt door gestructureerde HTML-inhoud te creëren die op de website wordt weergegeven.
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
Gedetailleerde Uitleg
-
Titel Extractie:
- Gebruikt een reguliere expressie om de tekst tussen de tags
[titre]
en[end_titre]
te vinden. - Verwijdert de tags uit de hoofdtekst na extractie.
- Als er geen titel is gevonden, wordt een standaardtitel gebruikt.
- Gebruikt een reguliere expressie om de tekst tussen de tags
-
Initialisatie van de HTML:
- De HTML-inhoud begint met de tags
<html>
,<head>
en<body>
. - CSS-stijlen worden toegevoegd voor een betere presentatie (typografie, marges, uitlijningen).
- De HTML-inhoud begint met de tags
-
Tekstsplitsing:
- De tekst wordt gesplitst in segmenten met behulp van de tags
[resume]
en[end_resume]
. - De segmenten vertegenwoordigen de delen van het verhaal zonder de samenvattingen.
- De tekst wordt gesplitst in segmenten met behulp van de tags
-
Samenstelling:
- Elk tekstsegment wordt in een paragraaf
<p>
geplaatst. - Als beeldgeneratie is ingeschakeld en er een bijbehorende afbeelding is, wordt de afbeelding na de paragraaf ingevoegd.
- De afbeeldingen worden gecentreerd en aangepast aan de schermgrootte voor een betere gebruikerservaring.
- Elk tekstsegment wordt in een paragraaf
-
Finalisatie:
- De sluitingstags
</body>
en</html>
worden toegevoegd om het HTML-document compleet te maken.
- De sluitingstags
Waarom Deze Aanpak?
-
Uitlijning van Tekst en Afbeeldingen: Door de afbeeldingen na de overeenkomstige tekstsegmenten in te voegen, wordt het verhaal visueel verrijkt, wat vooral belangrijk is voor kinderen.
-
Flexibiliteit: Als de gebruiker ervoor kiest geen afbeeldingen te genereren, behandelt de code dit geval door alleen de tekst te plaatsen.
-
Toegankelijkheid: Door semantische tags en aangepaste stijlen te gebruiken, is de inhoud toegankelijk op verschillende apparaten (computers, tablets, smartphones).
Upload naar S3 en Status Bijwerken
Zodra de HTML-inhoud is gegenereerd, moet deze toegankelijk worden gemaakt voor de gebruiker. Dit gebeurt door het bestand te uploaden naar een S3-bucket die is geconfigureerd voor het hosten van statische websites.
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
Technische Details
-
Bestandsnaamgeving:
- Bestanden worden benoemd met een timestamp om uniciteit te garanderen.
- De afbeeldingen worden opgeslagen in de map
generated_images/
en de HTML-bestanden ingenerated_content/
.
-
Upload naar S3:
- Gebruikmaing van de S3-client van
boto3
om te communiceren met de service. - De inhoud wordt gecodeerd of gedecodeerd afhankelijk van het type (afbeelding of tekst).
- De parameter
ACL='public-read'
maakt het bestand publiek toegankelijk. - URL-constructie: - De openbare URL wordt opgebouwd door gebruik te maken van het geconfigureerde CloudFront-domein, wat een snelle en veilige distributie van de inhoud mogelijk maakt.
- Gebruikmaing van de S3-client van
-
Beheer van Uitzonderingen:
- In geval van een fout tijdens het downloaden, wordt de uitzondering gelogd en opgegooid om door de
lambda_handler
te worden afgehandeld.
- In geval van een fout tijdens het downloaden, wordt de uitzondering gelogd en opgegooid om door de
Hoofdfunctie lambda_handler
De functie lambda_handler
is het toegangspunt van de Lambda-functie. Het coördineert alle eerder beschreven stappen.
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"},
}
Uitleg
-
Verwerking van de Verzoek:
- Haalt de benodigde informatie op uit de ontvangen gebeurtenis (event).
- De parameters van het verzoek bevatten de prompt, de geselecteerde modellen, de taal, enz.
-
Bijwerken van de Status:
- Voordat de verwerking begint, wordt de status bijgewerkt naar “Processing” in DynamoDB.
-
Generatie van het Verhaal:
- Oproep naar
generate_story
met de juiste parameters.
- Oproep naar
-
Extractie en Verwerking:
- De tags worden gecorrigeerd en de samenvattingen worden geëxtraheerd voor de beeldgeneratie.
-
Generatie van de Beelden:
- Als de generatie van beelden is ingeschakeld, worden de overeenkomstige beelden gegenereerd en de URLs verzameld.
-
Creatie van de HTML Inhoud:
- De tekst en de beelden worden gecombineerd om de uiteindelijke HTML-inhoud te creëren.
-
Uploaden naar S3:
- De HTML-inhoud wordt naar S3 geüpload en de resultaat-URL wordt verkregen.
-
Bijwerken van de Definitieve Status:
- De status wordt bijgewerkt naar “link” met de resultaat-URL in DynamoDB.
-
Terugkeren van de Reactie:
- De reactie omvat het
requestId
en de resultaat-URL, waardoor de klant de status kan controleren of rechtstreeks toegang krijgt tot de inhoud.
- De reactie omvat het
-
Beheer van Uitzonderingen:
- In geval van een fout, wordt de status bijgewerkt naar “Failed” en een HTTP 500-reactie wordt teruggestuurd.
Lambda-functie status_checker.py
Algemeen Overzicht
De functie status_checker.py
stelt gebruikers in staat om de status van hun aanvraag voor verhaalgeneratie te controleren. Het bevraagt DynamoDB om de huidige status en, indien beschikbaar, de resultaat-URL op te halen.
Code Analyse
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,
}
Details
-
Ophalen van de
requestId
:- De
requestId
is essentieel voor het identificeren van de specifieke aanvraag van de gebruiker.
- De
-
Onderzoek in DynamoDB:
- De functie probeert het element op te halen dat overeenkomt met de
requestId
. - Als het element bestaat, worden de status en de
resultUrl
geëxtraheerd.
- De functie probeert het element op te halen dat overeenkomt met de
-
Opbouw van de Reactie:
- Als de status beschikbaar is, wordt deze teruggegeven met de resultaat-URL.
- Als het element niet wordt gevonden, wordt een fout 404 teruggestuurd.
- In geval van een fout bij het opvragen van de database, wordt een fout 500 teruggestuurd met een passend bericht.
-
HTTP-Headers:
- De headers zijn ingesteld om CORS-verzoeken van de website toe te staan.
Integratie met API Gateway
Configuratie van Endpoints
De API Gateway stelt twee belangrijke endpoints bloot om te communiceren met de Lambda-functies:
-
/generate-image
:- Methode:
POST
- Beschrijving: Maakt het mogelijk voor gebruikers om de generatie van een verhaal te starten en eventueel bijbehorende beelden.
- Integratie: Verbonden met de Lambda-functie
StoryPixAI.py
.
- Methode:
-
/check-status
:- Methode:
GET
- Beschrijving: Maakt het mogelijk voor gebruikers om de status van hun aanvraag te controleren door het
requestId
te verstrekken. - Integratie: Verbonden met de Lambda-functie
status_checker.py
.
- Methode:
Authenticatie met Cognito
Om de API te beveiligen en toegang tot de bronnen te beheersen, heb ik Amazon Cognito geïntegreerd.
-
User Pool:
- Beheert de gebruikersreferenties.
- Maakt registratie, inloggen en beheer van gebruikers mogelijk.
-
Authorizer:
- Geconfigureerd in API Gateway om de door Cognito uitgegeven JWT-tokens te verifiëren.
- Zorgt ervoor dat alleen geauthenticeerde verzoeken toegang hebben tot de beschermde endpoints. - Integratie op API Gateway:
- De endpoints
/generate-image
en/check-status
zijn beschermd door de Cognito authorizer. - Klanten moeten de authenticatietoken in de headers van hun verzoeken opnemen (
Authorization
).
Statische Site op S3 en Interactie met de API
Structuur van de Site
De statische website dient als gebruikersinterface voor de applicatie.
-
index.html
:- Bevat het formulier waarmee gebruikers de prompt kunnen invoeren, de generatie-opties kunnen kiezen, en hun aanvraag kunnen indienen.
- Bevat de scripts die nodig zijn voor de interactie met de API en het beheer van de authenticatie.
-
storypixai.js
:- Bevat de JavaScript-code voor het beheren van de interacties met de API.
- Beheert de authenticatie met Cognito, het indienen van het formulier, het volgen van de status en het weergeven van de resultaten.
Gebruikerswerkstroom
-
Verbinding:
- De gebruiker logt in via het geïntegreerde inlogformulier.
- De informatie wordt geverifieerd via Cognito.
-
Indiening van de Aanvraag:
- De gebruiker vult het formulier in met de prompt en de gewenste opties.
- Bij het indienen wordt een
POST
-verzoek verzonden naar de endpoint/generate-image
met de gegevens.
-
Asynchrone Verwerking:
- De API geeft onmiddellijk een
requestId
terug. - De generatieverwerking wordt op de achtergrond uitgevoerd.
- De API geeft onmiddellijk een
-
Statuscontrole:
- De website vraagt periodiek de endpoint
/check-status
op met derequestId
. - Zodra de status “link” is ontvangen, wordt de URL van het resultaat aan de gebruiker getoond.
- De website vraagt periodiek de endpoint
-
Resultaat Weergeven:
- De gebruiker kan op de link klikken om toegang te krijgen tot het gegenereerde verhaal met de afbeeldingen.
Beheer van Verzoeken en Reacties
-
Geauthenticeerde Verzoeken:
- Alle verzoeken naar de API bevatten de authenticatietoken.
- De token wordt beheerd door de Cognito SDK die in de website is opgenomen.
-
Beheer van Statussen:
- Mogelijke statussen zijn “Processing”, “link”, “Failed”.
- De site past zijn interface aan op basis van de ontvangen status (bijv. weergave van een spinner, foutmelding, link naar het resultaat).
Interconnecties tussen de Componenten
Hier volgt hoe de verschillende componenten met elkaar communiceren:
-
Website ↔️ API Gateway:
- De website verzendt verzoeken naar de endpoints die door de API Gateway worden blootgesteld.
- De authenticatietokens worden toegevoegd om de verzoeken te beveiligen.
-
API Gateway ↔️ Lambda-functies:
- De API Gateway roept de overeenkomstige Lambda-functies aan op basis van de ontvangen verzoeken.
-
Lambda-functies ↔️ DynamoDB:
- De Lambda-functies
StoryPixAI.py
enstatus_checker.py
communiceren met DynamoDB om de status van de aanvragen bij te werken en op te halen.
- De Lambda-functies
-
Lambda-functie ↔️ S3:
- De functie
StoryPixAI.py
uploadt de gegenereerde afbeeldingen en de HTML-inhoud naar S3.
- De functie
-
CloudFront ↔️ S3:
- CloudFront wordt gebruikt om de op S3 opgeslagen inhoud snel en veilig te distribueren.
- De aan de gebruikers verstrekte URLs wijzen naar het CloudFront-domein.
-
Gebruiker ↔️ Website:
- De gebruiker interacteert met de website om aanvragen in te dienen en de resultaten te bekijken.
Voorbeeld van Resultaat in de CloudWatch-logs na een Verzoekoproep
Hier is een voorbeeld van logresultaten na een verzoekoproep om u het ruwe gegevensformaat te laten zien:
[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.
Continue Integratie met GitLab CI/CD
Om een soepele ontwikkeling en implementatie van StoryPixAI te waarborgen, heb ik een continue integratie (CI) en een continue levering (CD) pipeline ingesteld met behulp van GitLab CI/CD. Deze configuratie automatiseert de build- en deploymentprocessen en waarborgt zo de kwaliteit en betrouwbaarheid van de code bij elke wijziging.
Configuratie van de Pipeline
De pipeline is gedefinieerd in het bestand .gitlab-ci.yml
in de hoofdmap van het project. Hier is een overzicht van de structuur:
stages:
- Pré-requis optionel
- Vérifications
- Déploiements
- Management
- Suppressions
variables:
TERRAFORM_VERSION: "1.5.7-*"
TF_VAR_region: $AWS_DEFAULT_REGION
``` Deze configuratie definieert de verschillende fasen van de pijplijn en de globale variabelen die worden gebruikt in het CI/CD-proces.
### Hoofdjobs
De pijplijn omvat verschillende hoofdjobs:
1. **Terraform Verificatie**:
```yaml
Terraform Verificatie:
stage: Verificaties
when: manual
script:
- /bin/bash -c "source export.sh && terraform_plan"
Deze job voert terraform plan
uit om de verwachte infrastructuurwijzigingen te controleren zonder ze toe te passen.
-
Terraform Implementatie:
Terraform Implementatie: stage: Implementaties when: manual dependencies: - Terraform Verificatie script: - /bin/bash -c "source export.sh && terraform_apply"
Na verificatie past deze job de infrastructuurwijzigingen toe door
terraform apply
uit te voeren. -
Terraform Verwijdering:
Terraform Verwijdering: stage: Verwijderingen when: manual script: - /bin/bash -c "source export.sh && terraform_destroy"
Deze job verwijdert de infrastructuur indien nodig door
terraform destroy
uit te voeren. -
Beheer van OpenAI Sleutels:
OpenAI Sleutel - Toevoegen: stage: Optionele Voorwaarden 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 "Geen sleutel gevonden." exit 1 fi OpenAI Sleutel - Verwijderen: stage: Verwijderingen when: manual script: - /bin/bash -c "source export.sh && manage_openai_key delete"
Deze jobs beheren de veilige toevoeging en verwijdering van OpenAI API-sleutels in AWS Parameter Store.
Uitvoeringsomgeving
Elke job wordt uitgevoerd in een Docker-container gebaseerd op Ubuntu 22.04, met Terraform en AWS CLI geïnstalleerd:
.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
Voordelen van deze CI/CD-aanpak
-
Automatisering: Elke codewijziging triggert automatisch de pijplijn, wat zorgt voor consistente verificaties en implementaties.
-
Handmatige Controle: Kritieke stappen zoals implementatie en verwijdering zijn geconfigureerd in handmatige modus (
when: manual
), wat extra controle biedt voordat ze worden uitgevoerd. -
Veilig Geheimenbeheer: De integratie met AWS Parameter Store voor het beheer van API-sleutels zorgt voor een veilige behandeling van gevoelige informatie.
-
Flexibiliteit: De structuur in fasen zorgt voor een ordelijke en logische uitvoering van de verschillende stappen in de pijplijn.
-
Reproduceerbaarheid: Het gebruik van een gestandaardiseerde Docker-omgeving garandeert dat builds en tests reproduceerbaar zijn op verschillende systemen.
Deze CI/CD-configuratie maakt niet alleen de implementatie van StoryPixAI automatisch, maar zorgt ook voor een hoog niveau van kwaliteit en betrouwbaarheid gedurende de hele ontwikkelingscyclus.
Conclusie
StoryPixAI was veel meer dan een eenvoudig technisch project. Het was een echte avontuur in de wereld van generatieve AI, waarmee ik mijn passie voor technologie kon combineren met de wens om magische verhalen voor mijn kinderen te creëren.
Dit project bood mij de kans om verschillende aspecten van AI te verkennen, van het ontwerp van een intuïtieve gebruikersinterface tot het beheersen van prompting, en het opzetten van een robuuste cloud-infrastructuur met AWS en Terraform. Elke fase was een bron van leren, waarbij ik werd geconfronteerd met uitdagende technische problemen en mijn vaardigheden op het gebied van full-stack ontwikkeling en DevOps moest uitbreiden.
Ik hoop dat deze blogpost een kijkje achter de schermen van dit spannende avontuur heeft gegeven.
Belangrijkste Punten
-
Gedetailleerde Instructies:
- Duidelijke en gestructureerde prompts zorgen ervoor dat AI-modellen consistente en hoogwaardige resultaten leveren. - Modulaire Architectuur:
- Elk onderdeel (website, API Gateway, Lambda, DynamoDB, S3, Cognito) speelt een specifieke rol, waardoor het onderhoud en de ontwikkeling van het systeem worden vergemakkelijkt.
-
Veiligheid en Schaalbaarheid:
- Het gebruik van beheerde AWS-diensten zorgt voor een robuuste beveiliging en de mogelijkheid om aan een toenemende vraag te voldoen.
Projectlink: StoryPixAI
Dit document is vertaald van de fr versie naar de nl taal met behulp van het model gpt-4o. Voor meer informatie over het vertaalproces, raadpleeg https://gitlab.com/jls42/ai-powered-markdown-translator