Med StoryPixAI var mitt mål att skapa en interaktiv webbapplikation som gör det möjligt för användare att generera barnhistorier, berikade med bilder skapade av artificiella intelligensmodeller. För att uppnå detta använde jag flera AWS-tjänster som Lambda, API Gateway, DynamoDB, S3 och Cognito för autentisering. Infrastrukturkoden hanteras med Terraform, och distributionen automatiseras via GitLab CI. I detta inlägg avslöjar jag projektets bakomliggande val och de utmaningar som mötts.

Introduktion

Som erfaren arkitekt inom molninfrastruktur och DevOps har jag alltid varit fascinerad av ny teknik och dess potential att förändra vår vardag. Framväxten av generativ AI väckte min växande nyfikenhet, och jag kände behovet av att dyka in i denna pulserande värld.

Så föddes StoryPixAI, ett personligt projekt som tillät mig att utforska de oändliga möjligheterna med AI för att skapa personliga historier och magiska illustrationer för barn. Detta projekt har gett mig möjlighet att anta roller som fullstack-utvecklare, prompt engineer, produktägare och till och med UX/UI-designer, allt medan jag delar min passion för teknik med nära och kära.

I detta bloggpost kommer jag dela mina teknologiska val och de utmaningar som möttes under detta spännande äventyr.

Men först, en aptitretare!

För att ge en försmak av potentialen hos StoryPixAI, här är några automatiskt genererade historier på flera språk. Varje historia åtföljs av illustrationer, vilket gör berättelsen ännu mer uppslukande för barn:

AI till tjänst för kreativitet: en experimentell resa

Mitt äventyr med StoryPixAI började med ett enkelt Proof of Concept (PoC): en Lambda-funktion som interagerade med OpenAI för att generera text och DALL-E för att skapa bilder. Denna första framgång uppmuntrade mig att gå vidare och utforska andra AI-modeller via Bedrock från AWS.

GPT-4 och GPT-4-o: smidiga berättare

Från början av projektet var OpenAIs GPT-4 ett självklart val för textgenerering. Dess förmåga att förstå nyanser i naturligt språk och producera sammanhängande och kreativa berättelser möjliggjorde skapandet av fängslande historier anpassade till barnens ålder och intressen. Jag kunde experimentera med olika skrivstilar, från sagor till rymdäventyr, från djurhistorier till fantastiska berättelser.

När GPT-4-0 lanserades integrerade jag snabbt denna nya modell i StoryPixAI. Jag blev imponerad av dess ökade genereringshastighet, vilket avsevärt reducerade väntetiden för generering, och av den märkbart förbättrade kvaliteten på de genererade berättelserna, med ännu mer flytande, sammanhängande och fantasifulla berättelser. GPT-4-0 har därmed blivit en stor tillgång för StoryPixAI och erbjuder en snabbare och trevligare användarupplevelse.

DALL-E 3: referensillustratören

Om textgenereringsmodellerna gav tillfredsställande resultat visade sig valet av bildgenereringsverktyg vara mer avgörande. Efter många försök etablerade sig DALL-E 3 som referensmodellen för StoryPixAI. Dess förmåga att skapa originella, detaljerade illustrationer som är perfekt anpassade till berättelserna genererade av GPT-4 var en avgörande faktor för projektets framgång.

AWS Bedrock: vägen mot experimentering

För att inte begränsa mig till OpenAI använde jag AWS Bedrock för att enkelt integrera andra generativa AI-modeller i StoryPixAI. Denna plattform gjorde det möjligt för mig att testa Claude från Anthropic och Mistral för textgenerering och Stable Diffusion för bildskapande.

Även om dessa modeller gav intressanta resultat valde jag slutligen att fokusera på GPT-4 och GPT-4-0 för deras snabbhet och textgenereringskvalitet, och på DALL-E 3 för dess förmåga att producera illustrationer perfekt anpassade till berättelserna. Det är viktigt att notera att promptet som används för att generera bilderna till stor del utarbetas av textmodellen själv, vilket säkerställer en sammanhållning mellan berättelsen och illustrationen.

Utmaningen med asynkron API och DynamoDB

När PoC:n hade validerats, började jag skapa ett API för att göra StoryPixAI tillgänglig via en webbgränssnitt. Det var vid denna punkt som jag stötte på min första stora utmaning: tidsgränsen i API Gateway. För att kringgå denna begränsning och möjliggöra generering av längre och mer komplexa berättelser var jag tvungen att införa en asynkron arkitektur.

Amazon DynamoDB kom då till spel. Jag använde denna NoSQL-databas för att lagra pågående berättelsegenereringsuppgifter samt deras resultat när de slutförts. Tack vare detta tillvägagångssätt kunde API:t skicka ett omedelbart svar till användaren, som därefter kunde kontrollera statusen för sin förfrågan och hämta den genererade berättelsen när den var klar.

CORS och användargränssnittet: hinder att övervinna

Inrättandet av webbgränssnittet visade sig också vara en källa till utmaningar. Jag var tvungen att sätta mig in i subtiliteterna med CORS (Cross-Origin Resource Sharing) för att tillåta min frontend att kommunicera med API:t. Jag lade också ner tid på att förbättra användarupplevelsen genom att lägga till funktioner som valet av AI-modeller och bildstilar.

Prompting: en konst att bemästra

Under hela utvecklingen av StoryPixAI har jag förbättrat mina färdigheter i prompting, konsten att formulera rätt förfrågningar för att vägleda AI-modellerna. Jag lärde mig att anpassa mina prompts beroende på de använda modellerna, berättelsens parametrar och användarnas förväntningar. Detta steg var avgörande för att få kvalitetsresultat och säkerställa en tillfredsställande användarupplevelse.

En robust och automatiserad infrastruktur på AWS

StoryPixAI bygger på en serverlös infrastruktur som är värd på Amazon Web Services (AWS) och erbjuder en ideal kombination av flexibilitet, skalbarhet och kostnadsoptimering. Denna arkitektur, helt automatiserad med Terraform och GitLab CI/CD, möjliggör en snabb och pålitlig distribution av applikationen.

AWS-tjänsterna i hjärtat av StoryPixAI

alt text

StoryPixAIs arkitektur kretsar kring följande AWS-tjänster: * Amazon S3 (Simple Storage Service): Lagring av statiska webbplatser (HTML, CSS, JavaScript) och genererade berättelser samt deras tillhörande illustrationer.

  • Amazon CloudFront: Ett innehållsleveransnätverk (CDN) som påskyndar distributionen av StoryPixAI:s innehåll till användare världen över genom att cachelagra det på geografiskt närliggande platser.
  • Amazon API Gateway: Applikationens säkra ingång. Den hanterar användarförfrågningar, autentiserar dem via Amazon Cognito och dirigerar dem till de lämpliga Lambda-funktionerna.
  • AWS Lambda: Serverlösa funktioner som utgör motorn i StoryPixAI. De orkestrerar skapandet av berättelser, bildgenerering, hantering av asynkrona uppgifter och interaktionen med DynamoDB och andra AWS-tjänster.
  • Amazon DynamoDB: En flexibel och högpresterande NoSQL-databas som används för att lagra viktig information för applikationens funktion.
  • Amazon Cognito: En service för identitets- och åtkomsthantering som säkrar applikationen genom att låta användare logga in och kontrollera deras behörigheter. Det säkerställer att endast autentiserade användare kan komma åt berättelsegeneratorns funktioner.
  • Amazon Bedrock: En plattform som förenklar åtkomst och användning av generativa AI-modeller från olika leverantörer, såsom Anthropic (Claude) och Stability AI (Stable Diffusion). Bedrock möjliggör enkelt integration av dessa modeller i applikationen utan att behöva hantera dess underliggande infrastruktur.
  • Andra AWS-tjänster: StoryPixAI använder även andra AWS-tjänster, såsom IAM (Identity and Access Management) för detaljerad åtkomsthantering till resurser, CloudWatch för övervakning och loggar (väsentligt för felsökning och prestandanalys), och Systems Manager Parameter Store (SSM Parameter Store) för lagring av känslig information som API-nycklar, vilket säkerställer applikationens säkerhet.

Terraform: automatisering för infrastrukturen

För att hantera denna komplexa infrastruktur har jag valt Terraform, ett verktyg för Infrastructure as Code (IaC) som möjliggör beskrivning av infrastrukturen i deklarativ kodform. Med hjälp av Terraform kunde jag automatisera skapande, modifiering och borttagning av AWS-resurser, vilket säkerställde en konsekvent, reproducerbar och lättadministrerad miljö. Detta förenklar betydligt distributionsprocessen och minskar risken för mänskliga fel.

GitLab CI/CD: sömlösa och smidiga distributioner

För att säkerställa kontinuerlig och tillförlitlig distribution av StoryPixAI har jag implementerat en CI/CD-pipeline (Continuous Integration / Continuous Deployment) på GitLab. Denna pipeline automatiserar tester, byggande och distribution av applikationen vid varje ändring av källkoden, vilket möjliggör snabb felupptäckt och korrigering samt säker leverans av nya funktioner. Detta tillvägagångssätt garanterar att applikationen ständigt är uppdaterad och minimerar stilleståndstid.

Kombinationen av AWS, Terraform och GitLab CI/CD har möjliggjort konstruktionen av en robust, skalbar och lättunderhållen infrastruktur, vilket ger mig mer tid att fokusera på projektets kreativa aspekt och förbättra användarupplevelsen.

Övergripande arkitektur för StoryPixAI-projektet

Innan vi dyker in i koden, här är en översikt av applikationens arkitektur:

  1. Statisk Webbsida på S3: En statisk webbplats som hostas på en S3-bucket, åtkomlig via CloudFront för global distribution.
  2. API Gateway: Exponerar slutpunkter för berättelsegenerering och statuskontroll.
  3. Lambda Funktioner:
    • StoryPixAI.py: Genererar berättelsen och de tillhörande bilderna.
    • status_checker.py: Kontrollerar statusen för genereringen i DynamoDB.
  4. DynamoDB: Lagrar statusen för genereringsuppgifterna.
  5. S3: Lagrar de genererade bilderna och de resulterande HTML-sidorna.
  6. Cognito: Hanterar användarauktorisationen för att säkra API:et.

Lambda-funktion StoryPixAI.py

Allmän Översikt

Funktionen StoryPixAI.py är hjärtat av applikationen. Den är ansvarig för att:

  • Generera en berättelse baserad på en användarprompt.
  • Skapa detaljerade instruktioner för att vägleda AI-modellen i berättelsegenereringen.
  • Extrahera sammanfattningar för varje scen eller nyckelelement i berättelsen.
  • Generera bilder som motsvarar dessa sammanfattningar.
  • Kombinera text och bilder till en HTML-sida.
  • Lagra resultatet i S3 och uppdatera statusen i DynamoDB.

Kodbrytning

Imports och Initial Konfiguration

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)

I detta avsnitt importerar jag de nödvändiga modulerna, konfigurerar loggern för debugging, och hämtar OpenAI API-nyckeln som är lagrad i AWS Systems Manager Parameter Store (SSM). Detta tillåter att nyckeln säkras och inte lagras i klartext i koden.

Hjälpfunktioner

Korrigering av Taggar
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é.

Denna funktion säkerställer att taggarna som används för att avgränsa sammanfattningar och titlar är enhetliga. Detta är avgörande för korrekt extraktion av sammanfattningar senare.

Extrahera Sammanfattningar
def extract_summaries(text):
    # Extrait les résumés du texte en utilisant des balises spécifiques.

Den använder reguljära uttryck för att extrahera textavsnitt som avgränsas av [resume] och [end_resume]. Dessa sammanfattningar kommer att tjäna som prompts för bildgenereringen.

Generera Instruktioner för Bilder
def generate_image_instructions(prompt, style, language):
    # Génère les instructions pour la création d'images.

Denna funktion formaterar prompten för att vägleda bildgenereringsmodellen genom att inkludera stil och språk.

Uppdatering av 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.

Den uppdaterar tabellen TaskStatus för att följa generationens status, vilket är viktigt för funktionen status_checker.py.

Fördjupad Analys av generate_story_instructions

Funktionen generate_story_instructions är projektets kärna. Den genererar en uppsättning detaljerade instruktioner som ska skickas till AI-modellen för att vägleda berättelsegenereringen.

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}"
    """

Konstruktion av Prompten

Prompten är utformad för att förse AI-modellen med all nödvändig information för att generera en sammanhängande, pedagogisk och barnvänlig berättelse.

  • Språk: Parametern language_description tillåter specifikation av berättelsens språk, vilket säkerställer att den genererade texten blir på önskat språk.

  • Tema: Användarens prompt integreras i instruktionerna för att fungera som bas för berättelsen.

  • Längd: Ett intervall på 1000 till 1500 ord specificeras för att kontrollera berättelsens längd.

  • Nyckelelement: Instruktionerna uppmuntrar inkludering av element som äventyr, magi och viktiga pedagogiska värden.

Detaljerade Instruktioner

Instruktionerna som ges till modellen är mycket detaljerade för att vägleda genereringen på ett korrekt sätt.

Här är en analys av de olika delarna av prompten:

  1. Narrativ Struktur: Modellen ombeds att strukturera berättelsen med en fängslande början, en rik utveckling i händelser samt en tillfredsställande slutsats.

  2. Visuella Beskrivningar: Berättelsen ska vara rik på visuella beskrivningar för att stimulera barnens fantasi.

  3. Karaktärer: Modellen uppmuntras att utveckla älskvärda karaktärer med distinkta personligheter.

  4. Specifika Taggar: Taggar såsom [titel]... [end_titel] och [resume]... [end_resume] används för att avgränsa titeln och de visuella beskrivningarna.

  5. Fantastiska Element: Modellen uppmanas att inkludera magiska eller fantastiska element för att göra berättelsen mer attraktiv.

  6. Pedagogiska Värden: Berättelsen ska lära ut viktiga värderingar.

Taggarnas Roll

Taggarna spelar en avgörande roll i den efterföljande behandlingen av den genererade texten.

  • [titel]… [end_titel]: Avgränsar berättelsens titel. Detta gör det enkelt att extrahera för att visa det på lämpligt sätt i användargränssnittet.

  • [resume]… [end_resume] : Omsluter detaljerade visuella beskrivningar av nyckelscener i berättelsen. Dessa sammanfattningar kommer att användas som prompts för bildgenerering.

Behandling Efter Generering

När AI-modellen har genererat berättelsen enligt dessa instruktioner utför koden följande steg:

  1. Korrigering av Taggar : Funktionen correct_resume_tags säkerställer att alla taggar är korrekt formaterade för extrahering.

  2. Extrahering av Sammanfattningar : Funktionen extract_summaries använder taggarna [resume] och [end_resume] för att extrahera visuella beskrivningar.

  3. Bildgenerering : Varje sammanfattning skickas till funktionen generate_image för att skapa en motsvarande bild.

  4. Skapande av HTML-Innehåll : Berättelsens text och de genererade bilderna kombineras för att skapa en komplett HTML-sida.

Påverkan på Genereringen

Genom att tillhandahålla dessa detaljerade instruktioner styrs modellen för att:

  • Följa Formatet : Genom att använda de specificerade taggarna producerar modellen en strukturerad text som underlättar automatiserad bearbetning.

  • Generera Lämpligt Innehåll : Begränsningarna på språk, stil och teman säkerställer att berättelsen är lämplig för den tänkta målgruppen.

  • Underlätta Bildgenerering : Genom att extrahera noggranna visuella beskrivningar erhålls högkvalitativa prompts för bildgenerering.

Hantering av Taggar av Modellen

Modellen är uttryckligen instruerad att inte översätta eller ändra taggarna. Detta är avgörande för att taggarna ska förbli intakta och kunna användas för efterbehandling. Instruktionerna betonar detta för att undvika att modellen, som kan försöka parafrasera eller översätta hela texten, ändrar taggarna.

Generering av Berättelsen

När de detaljerade instruktionerna har genererats av funktionen generate_story_instructions är nästa steg att överföra dessa instruktioner till AI-modellen så att den kan skapa berättelsen.

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

Interaktion med OpenAI Modellen

  • OpenAI Client : Jag instansierar en OpenAI-klient genom att använda tidigare hämtad API-nyckel.

  • Prompting : Modellen tar emot en serie meddelanden:

    • Ett systemmeddelande som anger att assistenten är expert på barnberättelser.
    • Användarmeddelandet som innehåller de genererade detaljerade instruktionerna.
  • Modellens Svar : Modellen genererar en berättelse baserad på de angivna instruktionerna.

Felsökning

Om ett undantag uppstår vid anropet till OpenAI-API:n fångas det, och ett felmeddelande returneras.

Extrahering av Sammanfattningar och Taggar

Efter att berättelsen har genererats, är nästa steg att extrahera de visuella beskrivningarna genom att använda de specificerade taggarna.

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

Korrigering av Taggar

Modellen kan ibland ändra taggarna något (till exempel, lägga till accenter). Funktionen correct_resume_tags säkerställer att alla taggar är enhetliga och korrekt formaterade.

Extrahering av Sammanfattningar

Funktionen extract_summaries använder ett reguljärt uttryck för att hitta alla förekomster av text mellan taggarna [resume] och [end_resume]. Dessa sammanfattningar är de detaljerade visuella beskrivningarna som kommer att användas för att generera bilder.

Bildgenerering

När sammanfattningarna har extraherats används varje sammanfattning för att generera en motsvarande bild.

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-funktionen

Funktionen generate_image anropar API:et för bildgenereringsmodellen (till exempel, OpenAI DALL·E) för att skapa en bild från sammanfattningen.

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

Generering av Instruktioner för Bilder Funktionen generate_image_instructions anpassar sammanfattningen för att skapa en lämplig prompt för bildgenerering.

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.
    """
  • Stil : Den användarspecificerade stilen (t.ex. “akvarell”, “tecknad film”) inkluderas i prompten för att påverka bildens utseende.

  • Språk : Beskrivningen anpassas till det valda språket, vilket kan hjälpa modellen att förstå kulturella nyanser.

  • Tydliga Instruktioner : Genom att specificera att scenen ska vara rent visuell, förhindras att modellen lägger till text eller oönskade element i bilden.

Interaktion med OpenAI API för Bilder

  • API-anrop : Funktionen client.images.generate används för att generera bilden.

  • Viktiga Parametrar :

    • Prompt : Den justerade prompten skickas till API:et.
    • Modell : Den specificerade bildgenereringsmodellen.
    • Storlek : Bildens storlek (t.ex. “1024x1024”).
    • Kvalitet : Bildens kvalitet (standard, HD).
    • Svarformat : Bilderna returneras i base64-format för att underlätta lagring och hantering.

Felhantering

Felen vid bildgenerering fångas upp och loggas, vilket möjliggör diagnos av problem.

Skapande av HTML-innehåll

Efter att ha genererat bilderna som motsvarar sammanfattningarna, består nästa steg av att sammanställa berättelsetexten och bilderna i ett presentabelt format för användaren. Detta görs genom att skapa strukturerat HTML-innehåll som kommer att visas på webbplatsen.

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

Detaljerad Förklaring

  1. Titelutvinning:

    • Använder ett reguljärt uttryck för att hitta texten mellan taggarna [titre] och [end_titre].
    • Tar bort taggarna från huvudsakliga texten efter utvinning.
    • Om ingen titel hittas, används en standardtitel.
  2. Initialisering av HTML:

    • HTML-innehållet börjar med taggarna <html>, <head>, och <body>.
    • CSS-stilar inkluderas för att förbättra presentationen (typsnitt, marginaler, justeringar).
  3. Textdelning:

    • Texten delas upp i segment med hjälp av taggarna [resume] och [end_resume].
    • Segmenten representerar berättelsens delar utan sammanfattningar.
  4. Sammanställning:

    • Varje textsegment sätts in i ett stycke <p>.
    • Om bildgenerering är aktiverad och det finns en motsvarande bild, sätts bilden in efter stycket.
    • Bilderna centrerar och anpassas till skärmens storlek för en bättre användarupplevelse.
  5. Slutförande:

    • Stängningstaggarnata </body> och </html> läggs till för att komplettera HTML-dokumentet.

Varför Denna Metod?

  • Text- och Bildanpassning: Genom att infoga bilderna efter motsvarande textsegment, berikas berättelsen visuellt, vilket är särskilt viktigt för barn.
  • Flexibilitet: Om användaren väljer att inte generera bilder, hanterar koden detta fall genom att endast inkludera texten.
  • Tillgänglighet: Genom att använda semantiska taggar och anpassade stilar, blir innehållet tillgängligt på olika enheter (datorer, surfplattor, smartphones).

Uppladdning till S3 och Statusuppdatering

När HTML-innehållet har skapats måste det göras tillgängligt för användaren. Detta görs genom att ladda upp filen till en S3-bucket konfigurerad för att vara värd för statiska webbplatser.

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

Tekniska Detaljer

  • Filnamngivning:

    • Filerna namnges med hjälp av en tidsstämpel för att garantera unikhet.
    • Bilderna lagras i mappen generated_images/ och HTML-filerna i generated_content/.
  • Uppladdning till S3:

    • Användning av boto3 S3-klienten för att interagera med tjänsten.
    • Innehållet kodas eller avkodas beroende på typ (bild eller text).
    • Parametern ACL='public-read' gör filen publikt tillgänglig. - Konstruktion av URL:
    • Den publika URL:en konstrueras med hjälp av det konfigurerade CloudFront-domänen, vilket möjliggör snabb och säker distribution av innehåll.
  • Undantagshantering:

    • Vid fel vid nedladdning loggas undantaget och kastas för att hanteras av lambda_handler.

Huvudfunktion lambda_handler

Funktionen lambda_handler är ingångspunkten för Lambda-funktionen. Den orkestrerar alla steg som tidigare beskrivits.

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"},
        }

Förklaring

  • Begäranhantering:

    • Hämtar nödvändig information från den mottagna händelsen (event).
    • Begärans parametrar inkluderar prompt, valda modeller, språk etc.
  • Uppdatering av Status:

    • Innan behandlingen påbörjas, uppdateras statusen till “Processing” i DynamoDB.
  • Generering av Historien:

    • Anrop till generate_story med lämpliga parametrar.
  • Extraktion och Behandling:

    • Taggar korrigeras och sammanfattningar extraheras för bildgenerering.
  • Generering av Bilder:

    • Om bildgenerering är aktiverat genereras motsvarande bilder och URL:erna samlas in.
  • Skapande av HTML Innehåll:

    • Text och bilder kombineras för att skapa det slutliga HTML-innehållet.
  • Uppladdning till S3:

    • HTML-innehållet laddas upp till S3 och resultatets URL erhålls.
  • Uppdatering av Slutlig Status:

    • Statusen uppdateras till “link” med resultatets URL i DynamoDB.
  • Svara med Svar:

    • Svaret inkluderar requestId och resultatets URL, vilket gör det möjligt för klienten att kontrollera status eller komma åt innehållet direkt.
  • Undantagshantering:

    • Vid fel uppdateras statusen till “Failed” och ett HTTP 500-svar returneras.

Lambda-funktion status_checker.py

Allmän Översikt

Funktionen status_checker.py gör det möjligt för användare att kontrollera status för deras begäran om generering av historia. Den frågar DynamoDB för att hämta den aktuella statusen och, om tillgängligt, URL:en för resultatet.

Kodanalys

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,
        }

Detaljer

  • Hämtning av requestId:

    • requestId är avgörande för att identifiera användarens specifika begäran.
  • Fråga till DynamoDB:

    • Funktionen försöker hämta objektet som motsvarar requestId.
    • Om objektet existerar extraheras status och resultUrl.
  • Konstruktion av Svar:

    • Om status är tillgänglig returneras den med resultatets URL.
    • Om objektet inte hittas returneras ett 404-fel.
    • Vid fel vid fråga av databasen returneras ett 500-fel med ett lämpligt meddelande.
  • HTTP-headers:

    • Headers definieras för att tillåta CORS-förfrågningar från webbplatsen.

Integration med API Gateway

Konfiguration av Endpoints

API Gateway exponerar två huvudsakliga endpoints för att interagera med Lambda-funktionerna:

  1. /generate-image:

    • Metod: POST
    • Beskrivning: Gör det möjligt för användare att starta generering av en historia och eventuellt tillhörande bilder.
    • Integration: Kopplas till Lambda-funktionen StoryPixAI.py.
  2. /check-status:

    • Metod: GET
    • Beskrivning: Gör det möjligt för användare att kontrollera status för deras begäran genom att tillhandahålla requestId.
    • Integration: Kopplas till Lambda-funktionen status_checker.py.

Autentisering med Cognito

För att säkra API:et och kontrollera åtkomst till resurser har jag integrerat Amazon Cognito.

  • User Pool:

    • Hanterar användaruppgifter.
    • Möjliggör registrering, login och hantering av användare.
  • Authorizer:

    • Konfigurerad i API Gateway för att verifiera JWT-tokens som utfärdats av Cognito.
    • Säkerställer att endast autentiserade förfrågningar kan komma åt skyddade endpoints. - Integration på API Gateway:
    • Slutpunkterna /generate-image och /check-status är skyddade av Cognito-authenticatorn.
    • Kunder måste inkludera autentiseringstoken i sina förfrågningsrubriker (Authorization).

Statisk Webbplats på S3 och Interaktion med API:et

Webbplatsens Struktur

Den statiska webbplatsen fungerar som användargränssnittet för applikationen.

  • index.html:

    • Innehåller formuläret som låter användare ange prompt, välja genereringsalternativ och skicka sina förfrågningar.
    • Inkluderar nödvändiga skript för interaktion med API:et och autentiseringshantering.
  • storypixai.js:

    • Innehåller JavaScript-kod för att hantera interaktioner med API:et.
    • Hanterar autentisering med Cognito, formulärinlämning, statusuppföljning och visning av resultat.

Användararbetsflöde

  1. Inloggning:

    • Användaren loggar in via det integrerade inloggningsformuläret.
    • Information verifieras via Cognito.
  2. Inlämning av Förfrågan:

    • Användaren fyller i formuläret med prompten och önskade alternativ.
    • Vid inlämning skickas en POST-begäran till slutpunkten /generate-image med data.
  3. Asynkron Behandling:

    • API:et returnerar omedelbart ett requestId.
    • Genereringsprocessen sker i bakgrunden.
  4. Statuskontroll:

    • Webbplatsen frågar periodiskt slutpunkten /check-status och tillhandahåller requestId.
    • När statusen “link” tas emot, visas resultatets URL för användaren.
  5. Visning av Resultat:

    • Användaren kan klicka på länken för att komma åt den genererade historien med bilderna.

Hantering av Förfrågningar och Svar

  • Autentiserade Förfrågningar:

    • Alla förfrågningar till API:et inkluderar autentiseringstoken.
    • Token hanteras av Cognito SDK som ingår i webbplatsen.
  • Status Hantering:

    • Möjliga statusar är “Processing”, “link”, “Failed”.
    • Webbplatsen anpassar sitt gränssnitt beroende på vilken status som mottas (t.ex. visa en spinner, felmeddelande, länk till resultatet).

Interaktion mellan Komponenter

Här är en beskrivning av hur de olika komponenterna interagerar:

  • Webbplats ↔️ API Gateway:

    • Webbplatsen skickar förfrågningar till slutpunkterna exponerade av API Gateway.
    • Autentiseringstoken inkluderas för att säkra förfrågningarna.
  • API Gateway ↔️ Lambda-funktioner:

    • API Gateway anropar de motsvarande Lambda-funktionerna beroende på de mottagna förfrågningarna.
  • Lambda-funktioner ↔️ DynamoDB:

    • Lambda-funktionerna StoryPixAI.py och status_checker.py interagerar med DynamoDB för att uppdatera och hämta förfrågningarnas status.
  • Lambda-funktion ↔️ S3:

    • Funktionen StoryPixAI.py laddar upp de genererade bilderna och HTML-innehållet till S3.
  • CloudFront ↔️ S3:

    • CloudFront används för att snabb och säker distribuera innehåll lagrat på S3.
    • De URL:er som tillhandahålls användare pekar mot CloudFront-domänen.
  • Användare ↔️ Webbplats:

    • Användaren interagerar med webbplatsen för att skicka förfrågningar och se resultaten.

Exempel på Resultat i Cloudwatch-loggar efter en Förfrågningsanrop

Här är ett exempel på resultat från loggarna efter en förfrågningsanmälan så att du kan se det råa dataformatet som genereras:

[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.

Kontinuerlig Integration med GitLab CI/CD

För att säkerställa smidig utveckling och distribution av StoryPixAI har jag implementerat en kontinuerlig integrations- (CI) och kontinuerlig distributions- (CD) pipeline med hjälp av GitLab CI/CD. Denna konfiguration automatiserar bygg- och distributionsprocesserna och säkerställer därmed kodkvaliteten och pålitligheten vid varje ändring.

Pipelinekonfiguration

Pipelinen är definierad i filen .gitlab-ci.yml i projektets rot. Här är en översikt över dess struktur:

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

variables:
  TERRAFORM_VERSION: "1.5.7-*"
  TF_VAR_region: $AWS_DEFAULT_REGION
``` Denna konfiguration definierar de olika stegen i pipeline och globala variabler som används i CI/CD-processen.

### Huvudjobb

Pipeline inkluderar flera nyckeljobb:

1. **Terraform-verifiering**:
   ```yaml
   Terraform-verifiering:
     stage: Verifieringar
     when: manual
     script:
       - /bin/bash -c "source export.sh && terraform_plan"

Detta jobb kör terraform plan för att verifiera de planerade infrastrukturförändringarna utan att tillämpa dem.

  1. Terraform-implementering:

    Terraform-implementering:
      stage: Implementeringar
      when: manual
      dependencies:
        - Terraform-verifiering
      script:
        - /bin/bash -c "source export.sh && terraform_apply"
    

    Efter verifiering tillämpar detta jobb infrastrukturförändringarna genom att köra terraform apply.

  2. Terraform-borttagning:

    Terraform-borttagning:
      stage: Borttagningar
      when: manual
      script:
        - /bin/bash -c "source export.sh && terraform_destroy"
    

    Detta jobb möjliggör att förstöra infrastrukturen vid behov genom att köra terraform destroy.

  3. Hantering av OpenAI nycklar:

    OpenAI-nyckel - Tillägg:
      stage: Valfria förutsättningar
      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 "Ingen nyckel hittades."
            exit 1
          fi      
    
    OpenAI-nyckel - Borttagning:
      stage: Borttagningar
      when: manual
      script:
        - /bin/bash -c "source export.sh && manage_openai_key delete"
    

    Dessa jobb hanterar säker tillägg och borttagning av OpenAI API-nycklar i AWS Parameter Store.

Körningsmiljö

Varje jobb körs i en Docker-container baserad på Ubuntu 22.04, med Terraform och AWS CLI installerade:

.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

Fördelar med detta CI/CD-sätt

  1. Automatisering: Varje kodändring utlöser automatiskt pipeline, vilket säkerställer konsekventa verifieringar och implementeringar.

  2. Manuell Kontroll: Kritiska steg som implementering och borttagning är konfigurerade i manuell läge (when: manual), vilket erbjuder extra kontroll före exekvering.

  3. Säker Hantering av Hemligheter: Integration med AWS Parameter Store för hantering av API-nycklar säkerställer säker hantering av känslig information.

  4. Flexibilitet: Strukturen i steg möjliggör en ordnad och logisk exekvering av de olika stegen i pipeline.

  5. Reproducerbarhet: Användningen av en standardiserad Docker-miljö garanterar att bygg och tester är reproducerbara på olika system.

Denna CI/CD-konfiguration möjliggör inte bara automatisering av StoryPixAIs implementering, utan bibehåller också en hög nivå av kvalitet och tillförlitlighet genom hela utvecklingscykeln.

Slutsats

StoryPixAI har varit mycket mer än bara ett tekniskt projekt. Det var ett riktigt äventyr inom generativ AI, vilket tillät mig att kombinera min passion för teknik med önskan att skapa magiska berättelser för mina barn.

Detta projekt gav mig möjligheten att utforska olika aspekter av AI, från design av ett intuitivt användargränssnitt till behärskning av prompting, samt etablering av en robust molninfrastruktur med AWS och Terraform. Varje steg var en källa till lärande, vilket konfronterade mig med stimulerande tekniska utmaningar och tvingade mig att bredda mina färdigheter inom full-stack utveckling och DevOps.

Jag hoppas att denna bloggpost har gett dig en inblick bakom kulisserna av detta spännande äventyr.

Nyckelpoäng

  • Detaljerade Instruktioner:

    • Klara och strukturerade anvisningar möjliggör att få konsekventa och högkvalitativa resultat från AI-modellerna. - Modulär Arkitektur :
    • Varje komponent (webbplats, API Gateway, Lambda, DynamoDB, S3, Cognito) spelar en specifik roll, vilket underlättar underhåll och utveckling av systemet.
  • Säkerhet och Skalbarhet :

    • Användningen av hanterade tjänster från AWS säkerställer robust säkerhet och kapacitet att anpassa sig till en ökande efterfrågan.

Projektlänk: StoryPixAI

Detta dokument har översatts från fr till sv med hjälp av gpt-4o modellen. För mer information om översättningsprocessen, besök https://gitlab.com/jls42/ai-powered-markdown-translator