Mit StoryPixAI war es mein Ziel, eine interaktive Webanwendung zu entwickeln, mit der Nutzer Kindergeschichten generieren können, angereichert durch von KI-Modellen erzeugte Bilder. Zur Umsetzung nutzte ich mehrere AWS-Dienste wie Lambda, API Gateway, DynamoDB, S3 und Cognito für die Authentifizierung. Die Infrastruktur ist mit Terraform verwaltet und die Bereitstellung über GitLab CI automatisiert. In diesem Beitrag enthülle ich die Hintergründe dieses spannenden Projekts, von den technologischen Entscheidungen bis zu den Herausforderungen, denen ich begegnet bin.
Einleitung
Als erfahrener Cloud-Infrastruktur- und DevOps-Architekt war ich schon immer fasziniert von neuen Technologien und ihrem Potenzial, unseren Alltag zu verändern. Das Aufkommen der generativen KI weckte meine wachsende Neugier, und ich fühlte den Drang, in dieses dynamische Feld einzutauchen.
So entstand StoryPixAI, ein persönliches Projekt, das mir erlaubte, die unendlichen Möglichkeiten der KI zu erkunden, um personalisierte Geschichten und zauberhafte Illustrationen für Kinder zu erstellen. Dieses Projekt gab mir die Gelegenheit, in die Rolle eines Full-Stack-Entwicklers, Prompt Engineers, Product Owners und sogar UX/UI-Designers zu schlüpfen, während ich meine Begeisterung für Technologie mit Familie und Freunden teilte.
In diesem Blogbeitrag teile ich meine technologischen Entscheidungen und die Herausforderungen, die ich auf dieser spannenden Reise gemeistert habe.
Aber zuerst ein Vorgeschmack!
Um Ihnen einen Eindruck vom Potenzial von StoryPixAI zu geben, hier einige automatisch generierte Geschichten in mehreren Sprachen.
Jede Geschichte ist mit Illustrationen versehen, die das Erzählerlebnis für Kinder noch immersiver machen:
- Französisch: Tom, Zoé et le Royaume d’Argentor
- Englisch : Die magische Suche der Prinzessin Léa und des Diebs der riesigen Schnecke
- Spanisch : Das große Abenteuer von Roger und Coco, dem Clown
- Deutsch : Der verrückte Holzfäller und das magische Abenteuer
- Italienisch : Das Mädchen und das magische Einhorn im verzauberten Wald
- Portugiesisch : Die verzauberte Reise von Lucas und seinen Freunden
KI im Dienst der Kreativität: ein Experimentierpfad
Meine Reise mit StoryPixAI begann mit einem einfachen Proof of Concept (PoC): einer Lambda-Funktion, die mit OpenAI für die Texterstellung und mit DALL-E für die Bildgenerierung interagierte. Dieser erste Erfolg motivierte mich, weiterzugehen und über AWS Bedrock weitere KI-Modelle zu erforschen.
GPT-4 und GPT-4-o: die agilen Erzähler
Von Anfang an erwies sich OpenAIs GPT-4 als naheliegende Wahl zur Textgenerierung. Seine Fähigkeit, die Feinheiten natürlicher Sprache zu erfassen und kohärente, kreative Erzählungen zu liefern, erlaubte es mir, fesselnde Geschichten zu erstellen, die dem Alter und den Interessen der Kinder entsprechen. Ich konnte mit verschiedenen Schreibstilen experimentieren — von Märchen über Weltraumabenteuer bis hin zu Tiergeschichten und Fantasieromanen.
Als GPT-4-0 veröffentlicht wurde, integrierte ich dieses neue Modell rasch in StoryPixAI. Beeindruckend war seine erhöhte Erzeugungsgeschwindigkeit, die die Wartezeiten deutlich reduzierte, sowie die merkliche Qualitätsverbesserung der generierten Geschichten mit noch flüssigeren, konsistenteren und fantasievolleren Erzählungen. GPT-4-0 wurde so zu einem wichtigen Vorteil für StoryPixAI und sorgte für ein schnelleres und angenehmeres Benutzererlebnis.
DALL-E 3: der bevorzugte Illustrator
Während die Textmodelle zufriedenstellende Ergebnisse lieferten, erwies sich die Wahl des Bildgenerierungswerkzeugs als entscheidender. Nach zahlreichen Tests setzte sich DALL-E 3 als Referenzmodell für StoryPixAI durch. Seine Fähigkeit, originelle, detaillierte Illustrationen zu erstellen, die perfekt zu den von GPT-4 generierten Geschichten passen, war ein entscheidender Erfolgsfaktor.
AWS Bedrock: die Offenheit für Experimente
Um mich nicht auf OpenAI zu beschränken, nutzte ich AWS Bedrock, um andere generative KI-Modelle einfach in StoryPixAI zu integrieren. Diese Plattform erlaubte mir, Claude von Anthropic und Mistral für die Textgenerierung sowie Stable Diffusion für die Bildproduktion zu testen.
Obwohl diese Modelle interessante Ergebnisse lieferten, entschied ich mich letztlich, mich auf GPT-4 und GPT-4-0 wegen ihrer Geschwindigkeit und Qualität bei der Texterzeugung sowie auf DALL-E 3 für die Illustrationserstellung zu konzentrieren. Wichtig zu erwähnen ist, dass der Prompt für die Bildgenerierung größtenteils vom Textmodell selbst erzeugt wird, was Kohärenz zwischen Erzählung und Illustration sicherstellt.
Die Herausforderung der asynchronen API und DynamoDB
Nachdem das PoC validiert war, begann ich, eine API zu erstellen, um StoryPixAI über eine Weboberfläche zugänglich zu machen. An dieser Stelle stieß ich auf die erste große Herausforderung: die Timeout-Begrenzung von API Gateway. Um diese Einschränkung zu umgehen und längere, komplexere Geschichten zu ermöglichen, musste ich eine asynchrone Architektur implementieren.
Amazon DynamoDB kam ins Spiel. Ich verwendete diese NoSQL-Datenbank, um laufende Generierungsaufgaben sowie deren Ergebnisse nach Abschluss zu speichern. Mit diesem Ansatz konnte die API dem Nutzer sofort eine Antwort zurückgeben; der Nutzer konnte dann den Status seiner Anfrage abfragen und die fertige Geschichte abrufen, sobald sie verfügbar war.
CORS und die Benutzeroberfläche: Hindernisse überwinden
Der Aufbau der Weboberfläche brachte ebenfalls Herausforderungen mit sich. Ich musste mich mit den Feinheiten von CORS (Cross-Origin Resource Sharing) vertraut machen, damit mein Frontend mit der API kommunizieren konnte. Zudem investierte ich Zeit in die Verbesserung der Nutzererfahrung, etwa durch die Auswahlmöglichkeiten für KI-Modelle und Bildstile.
Prompting: eine Kunst zu beherrschen
Während der gesamten Entwicklung von StoryPixAI verfeinerte ich meine Fähigkeiten im Prompting — der Kunst, präzise Anweisungen zu formulieren, um die KI-Modelle zielführend zu steuern. Ich lernte, die Prompts an die jeweils verwendeten Modelle, die Parameter der Geschichte und die Erwartungen der Nutzer anzupassen. Dieser Schritt war entscheidend, um qualitativ hochwertige Ergebnisse zu erzielen und ein zufriedenstellendes Nutzererlebnis zu gewährleisten.
Eine robuste, automatisierte Infrastruktur auf AWS
StoryPixAI basiert auf einer serverlosen Infrastruktur, die auf Amazon Web Services (AWS) gehostet wird und eine ideale Kombination aus Flexibilität, Skalierbarkeit und Kostenoptimierung bietet. Diese Architektur ist vollständig automatisiert mit Terraform und GitLab CI/CD und ermöglicht eine schnelle und zuverlässige Bereitstellung der Anwendung.
Die AWS-Dienste im Herzen von StoryPixAI

Die Architektur von StoryPixAI dreht sich um die folgenden AWS-Dienste:
- Amazon S3 (Simple Storage Service) : Speicherung der statischen Dateien der Website (HTML, CSS, JavaScript) sowie der generierten Geschichten und zugehörigen Illustrationen.
- Amazon CloudFront : Ein Content Delivery Network (CDN), das die Auslieferung der StoryPixAI-Inhalte weltweit beschleunigt, indem es Inhalte in geografisch nahegelegenen Standorten cached.
- Amazon API Gateway : Das sichere Eingangstor der Anwendung. Es verwaltet die Anfragen der Nutzer, stellt die Authentifizierung über Amazon Cognito sicher und leitet die Anfragen an die entsprechenden Lambda-Funktionen weiter.
- AWS Lambda : Serverlose Funktionen, die den Kern von StoryPixAI bilden. Sie orchestrieren die Geschichtserstellung, die Bildgenerierung, das Management asynchroner Aufgaben und die Interaktion mit DynamoDB sowie anderen AWS-Diensten.
- Amazon DynamoDB : Eine flexible und leistungsfähige NoSQL-Datenbank, die zur Speicherung essentieller Informationen für den Betrieb der Anwendung verwendet wird.
- Amazon Cognito : Ein Identitäts- und Zugriffsverwaltungsdienst, der die Anwendung sichert, indem er Nutzern die Anmeldung ermöglicht und ihre Berechtigungen kontrolliert. Er stellt sicher, dass nur authentifizierte Nutzer Zugriff auf die Generierungsfunktionen haben.
- Amazon Bedrock : Eine Plattform, die den Zugriff auf und die Nutzung generativer KI-Modelle verschiedener Anbieter wie Anthropic (Claude) und Stability AI (Stable Diffusion) vereinfacht. Bedrock erlaubt die einfache Integration dieser Modelle in die Anwendung, ohne die zugrundeliegende Infrastruktur selbst verwalten zu müssen.
- Weitere AWS-Dienste : StoryPixAI nutzt außerdem andere AWS-Dienste wie IAM (Identity and Access Management) für die feingranulare Berechtigungsverwaltung, CloudWatch für Überwachung und Logs (wichtig für Debugging und Performance-Analysen) und Systems Manager Parameter Store (SSM Parameter Store) zur sicheren Speicherung sensibler Informationen wie API-Schlüssel.
Terraform: Automatisierung für die Infrastruktur
Zur Verwaltung dieser komplexen Infrastruktur entschied ich mich für Terraform, ein Infrastructure-as-Code (IaC)-Tool, das die Beschreibung der Infrastruktur in deklarativem Code ermöglicht. Mit Terraform konnte ich die Erstellung, Änderung und Löschung von AWS-Ressourcen automatisieren und so eine konsistente, reproduzierbare und leicht verwaltbare Umgebung gewährleisten. Das vereinfacht den Bereitstellungsprozess erheblich und reduziert das Risiko menschlicher Fehler.
GitLab CI/CD: reibungslose Bereitstellungen
Um kontinuierliche und zuverlässige Deployments von StoryPixAI sicherzustellen, richtete ich eine CI/CD-Pipeline (Continuous Integration / Continuous Deployment) in GitLab ein. Diese Pipeline automatisiert Tests, das Bauen und die Bereitstellung der Anwendung bei jeder Änderung des Quellcodes, sodass Fehler schnell erkannt und behoben werden können und neue Funktionen vertrauensvoll ausgeliefert werden. Dieser Ansatz stellt sicher, dass die Anwendung stets aktuell ist und Ausfallzeiten minimiert werden.
Diese Kombination aus AWS, Terraform und GitLab CI/CD ermöglichte mir den Aufbau einer robusten, skalierbaren und wartungsfreundlichen Infrastruktur, sodass ich mich mehr auf den kreativen Teil des Projekts und die Verbesserung des Nutzererlebnisses konzentrieren konnte.
Gesamte Architektur des StoryPixAI-Projekts
Bevor wir in den Code eintauchen, hier ein Überblick über die Architektur der Anwendung:
- Statische Seite auf S3 : Eine statische Website, gehostet in einem S3-Bucket und über CloudFront global erreichbar.
- API Gateway : Stellt Endpunkte zur Verfügung für die Generierung von Geschichten und zur Statusabfrage.
- Lambda-Funktionen :
StoryPixAI.py: Generiert die Geschichte und die zugehörigen Bilder.status_checker.py: Prüft den Generierungsstatus in DynamoDB.
- DynamoDB : Speichert den Status der Generierungsaufgaben.
- S3 : Speichert die generierten Bilder und die resultierenden HTML-Seiten.
- Cognito : Verwaltet die Nutzer-Authentifizierung zur Sicherung der API.
Lambda-Funktion StoryPixAI.py
Überblick
Die Funktion StoryPixAI.py ist das Herz der Anwendung. Sie ist verantwortlich für:
- Die Generierung einer Geschichte basierend auf einem Nutzer-Prompt.
- Das Erstellen detaillierter Anweisungen zur Steuerung des KI-Modells bei der Geschichtserstellung.
- Das Extrahieren von Zusammenfassungen für jede Szene oder jedes Schlüsselelement der Geschichte.
- Die Erzeugung entsprechender Bilder zu diesen Zusammenfassungen.
- Das Kombinieren von Text und Bildern zu einer HTML-Seite.
- Das Speichern des Ergebnisses in S3 und das Aktualisieren des Status in DynamoDB.
Codeaufteilung
Importe und Initialkonfiguration
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 diesem Abschnitt importiere ich die benötigten Module, konfiguriere den Logger für das Debugging und rufe den OpenAI-API-Schlüssel ab, der im AWS Systems Manager Parameter Store (SSM) gespeichert ist. Dadurch bleibt der Schlüssel sicher und wird nicht im Klartext im Code hinterlegt.
Hilfsfunktionen
Tag-Korrektur
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é.
Diese Funktion stellt sicher, dass die Tags, die zur Abgrenzung von Zusammenfassungen und Titeln verwendet werden, einheitlich sind. Das ist entscheidend für die korrekte Extraktion der Zusammenfassungen später.
Extraktion der Zusammenfassungen
def extract_summaries(text):
# Extrait les résumés du texte en utilisant des balises spécifiques.
Sie verwendet reguläre Ausdrücke, um die Textabschnitte zu extrahieren, die von [resume] und [end_resume] begrenzt werden. Diese Zusammenfassungen dienen als Prompts für die Bildgenerierung.
Erstellung der Bildanweisungen
def generate_image_instructions(prompt, style, language):
# Génère les instructions pour la création d'images.
Diese Funktion formatiert den Prompt so, dass das Bildgenerierungsmodell durch Stil- und Sprachangaben zielgerichtet gesteuert wird.
Aktualisierung von 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.
Sie aktualisiert die Tabelle TaskStatus, um den Fortschritt der Generierung nachzuverfolgen, was für die Funktion status_checker.py essenziell ist.
Tiefenanalyse von generate_story_instructions
Die Funktion generate_story_instructions ist das Kernstück des Projekts. Sie erzeugt ein Set detaillierter Anweisungen, die dem KI-Modell übergeben werden, um die Erstellung der Geschichte zu steuern.
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}"
"""
Aufbau des Prompts
Der Prompt ist so gestaltet, dass er dem KI-Modell alle nötigen Informationen liefert, um eine kohärente, lehrreiche und kinderfreundliche Geschichte zu generieren.
-
Sprache : Der Parameter
language_descriptionerlaubt die Festlegung der Sprache der Geschichte, sodass der erzeugte Text in der gewünschten Sprache vorliegt. -
Thema : Der Nutzer-Prompt wird in die Instruktionen integriert und dient als Grundlage für die Geschichte.
-
Länge : Ein Bereich von 1000 bis 1500 Wörtern wird vorgegeben, um die Länge der Geschichte zu steuern.
-
Schlüsselelemente : Die Anweisungen ermutigen zur Einbindung von Elementen wie Abenteuer, Magie und wichtigen pädagogischen Werten.
Details der Instruktionen
Die an das Modell gegebenen Anweisungen sind äußerst detailliert, um die Generierung präzise zu steuern.
Hier eine Analyse der verschiedenen Teile des Prompts:
-
Narrative Struktur : Es wird verlangt, die Geschichte mit einem fesselnden Anfang, einer reichhaltigen Entwicklung und einem befriedigenden Abschluss zu strukturieren.
-
Visuelle Beschreibungen : Die Geschichte soll reich an visuellen Beschreibungen sein, um die Fantasie der Kinder anzuregen.
-
Charaktere : Es wird zur Entwicklung sympathischer Charaktere mit ausgeprägten Persönlichkeiten angeregt.
-
Spezielle Tags : Tags wie
[titre]... [end_titre]und[resume]... [end_resume]werden verwendet, um Titel und visuelle Beschreibungen abzugrenzen. -
Fantastische Elemente : Das Modell wird eingeladen, magische oder fantastische Elemente einzubauen, um die Geschichte attraktiver zu machen.
-
Pädagogische Werte : Die Geschichte soll wichtige Werte vermitteln.
Rolle der Tags Les balises jouent un rôle crucial dans le traitement ultérieur du texte généré.
-
[titre]… [end_titre] : Umschließt den Titel der Geschichte. Dadurch kann er leicht extrahiert und in der Benutzeroberfläche angemessen angezeigt werden.
-
[resume]… [end_resume] : Umschließt die detaillierten visuellen Beschreibungen der Schlüsselszenen der Geschichte. Diese Zusammenfassungen werden als Prompts für die Bildgenerierung verwendet.
Traitement Après la Génération
Eine fois que le modèle d’IA a généré l’histoire en suivant ces instructions, le code effectue les étapes suivantes :
-
Korrektur der Tags : Die Funktion
correct_resume_tagsstellt sicher, dass alle Tags korrekt formatiert sind, um sie extrahieren zu können. -
Extraktion der Zusammenfassungen : Die Funktion
extract_summariesverwendet die Tags[resume]und[end_resume], um die visuellen Beschreibungen zu extrahieren. -
Bildgenerierung : Jede Zusammenfassung wird an die Funktion
generate_imageübergeben, um ein entsprechendes Bild zu erstellen. -
Erstellung des HTML-Inhalts : Der Text der Geschichte und die generierten Bilder werden kombiniert, um eine vollständige HTML-Seite zu erstellen.
Auswirkungen auf la Génération
Durch das Bereitstellen dieser detaillierten Anweisungen wird das Modell wie folgt geleitet:
-
Format einhalten : Durch die Verwendung der angegebenen Tags erzeugt das Modell einen strukturierten Text, der die automatisierte Verarbeitung erleichtert.
-
Angepassten Inhalt erzeugen : Vorgaben zu Sprache, Stil und Themen stellen sicher, dass die Geschichte für die Zielgruppe geeignet ist.
-
Bildgenerierung erleichtern : Durch das Extrahieren präziser visueller Beschreibungen entstehen qualitativ hochwertige Prompts für die Bildgenerierung.
Umgang des Modells mit les Balises
Das Modell wird ausdrücklich angewiesen, die Tags nicht zu übersetzen oder zu verändern. Das ist essenziell, damit die Tags intakt bleiben und für das Post-Processing verwendet werden können. Die Anweisungen betonen diesen Punkt, um zu vermeiden, dass das Modell, das versuchen könnte, den gesamten Text umzuschreiben oder zu übersetzen, die Tags verändert.
Generierung de l’Histoire
Sobald die detaillierten Anweisungen von der Funktion generate_story_instructions erzeugt wurden, besteht der nächste Schritt darin, diese Anweisungen an das KI-Modell zu übergeben, damit es die Geschichte erstellt.
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 mit dem OpenAI-Modell
-
OpenAI-Client : Ich instanziiere einen OpenAI-Client unter Verwendung des zuvor abgerufenen API-Schlüssels.
-
Prompting : Das Modell erhält eine Reihe von Nachrichten :
- Eine Systemnachricht, die angibt, dass der Assistent ein Experte für Kindergeschichten ist.
- Die Benutzernachricht, die die generierten detaillierten Anweisungen enthält.
-
Antwort des Modells : Das Modell generiert eine Geschichte basierend auf den gegebenen Anweisungen.
Fehlerbehandlung
Wenn bei dem Aufruf der OpenAI-API eine Ausnahme auftritt, wird diese abgefangen und eine Fehlermeldung zurückgegeben.
Extraktion des Résumés und Balises
Nach der Generierung der Geschichte besteht der nächste Schritt darin, die visuellen Beschreibungen mithilfe der angegebenen Tags zu extrahieren.
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
Korrektur der Tags
Das Modell kann die Tags manchmal leicht verändern (z. B. Akzente hinzufügen). Die Funktion correct_resume_tags stellt sicher, dass alle Tags einheitlich und korrekt formatiert sind.
Extraktion der Zusammenfassungen
Die Funktion extract_summaries verwendet einen regulären Ausdruck, um alle Vorkommen von Text zwischen den Tags [resume] und [end_resume] zu finden. Diese Zusammenfassungen sind die detaillierten visuellen Beschreibungen, die zur Generierung der Bilder verwendet werden.
Bildgenerierung
Sobald die Zusammenfassungen extrahiert sind, wird jede Zusammenfassung verwendet, um ein entsprechendes Bild zu generieren.
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
Funktion generate_image
Die Funktion generate_image ruft die API des Bildgenerierungsmodells (z. B. OpenAI DALL·E) auf, um aus der Zusammenfassung ein Bild zu erstellen.
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
Erstellung der Bildanweisungen
Die Funktion generate_image_instructions passt die Zusammenfassung an, um einen geeigneten Prompt für die Bildgenerierung zu erstellen.
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 : Der vom Benutzer angegebene Stil (z. B. “Aquarell”, “Cartoon”) wird in den Prompt aufgenommen, um das Aussehen des Bildes zu beeinflussen.
-
Sprache : Die Beschreibung wird an die gewählte Sprache angepasst, was dem Modell helfen kann, kulturelle Nuancen zu verstehen.
-
Klare Anweisungen : Indem klargestellt wird, dass die Szene rein visuell sein soll, wird vermieden, dass das Modell Text oder unerwünschte Elemente im Bild hinzufügt.
Interaktion mit der OpenAI-API für Bilder
-
API-Aufruf : Die Funktion
client.images.generatewird zur Generierung des Bildes verwendet. -
Wichtige Parameter :
- Prompt : Der angepasste Prompt wird an die API übergeben.
- Modell : Das angegebene Bildgenerierungsmodell.
- Größe : Die Bildgröße (z. B. “1024x1024”).
- Qualität : Die Bildqualität (Standard, HD).
- Antwortformat : Die Bilder werden im Base64-Format zurückgegeben, um Speicherung und Verarbeitung zu erleichtern.
Fehlerbehandlung
Fehler bei der Bildgenerierung werden erfasst und protokolliert, sodass Probleme diagnostiziert werden können.
Erstellung du Contenu HTML
Nachdem die Bilder für die extrahierten Zusammenfassungen generiert wurden, besteht der nächste Schritt darin, den Text der Geschichte und die Bilder in ein für den Benutzer anzeigbares Format zusammenzufügen. Dies geschieht durch die Erstellung eines strukturierten HTML-Inhalts, der auf der Website angezeigt wird.
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
Detaillierte Erklärung
-
Extraktion des Titels:
- Verwendet einen regulären Ausdruck, um den Text zwischen den Tags
[titre]und[end_titre]zu finden. - Entfernt die Tags aus dem Haupttext nach der Extraktion.
- Wenn kein Titel gefunden wird, wird ein Standardtitel verwendet.
- Verwendet einen regulären Ausdruck, um den Text zwischen den Tags
-
Initialisierung des HTML:
- Der HTML-Inhalt beginnt mit den Tags
<html>,<head>und<body>. - CSS-Stile werden hinzugefügt, um die Darstellung zu verbessern (Schrift, Ränder, Ausrichtungen).
- Der HTML-Inhalt beginnt mit den Tags
-
Aufteilung des Textes:
- Der Text wird in Segmente unterteilt, indem die Tags
[resume]und[end_resume]verwendet werden. - Die Segmente stellen die Teile der Geschichte ohne die Zusammenfassungen dar.
- Der Text wird in Segmente unterteilt, indem die Tags
-
Zusammenstellung:
- Jedes Textsegment wird in einen Absatz
<p>eingefügt. - Wenn die Bildgenerierung aktiviert ist und ein entsprechendes Bild vorhanden ist, wird das Bild nach dem Absatz eingefügt.
- Die Bilder werden zentriert und an die Bildschirmgröße angepasst, um die Benutzererfahrung zu verbessern.
- Jedes Textsegment wird in einen Absatz
-
Abschluss:
- Die schließenden Tags
</body>und</html>werden hinzugefügt, um das HTML-Dokument zu vervollständigen.
- Die schließenden Tags
Warum dieser Ansatz ?
-
Abstimmung von Text und Bildern: Durch das Einfügen der Bilder nach den entsprechenden Textsegmenten wird die Geschichte visuell bereichert, was besonders für Kinder wichtig ist.
-
Flexibilität: Wählt der Benutzer, keine Bilder zu generieren, behandelt der Code diesen Fall, indem nur der Text eingefügt wird.
-
Barrierefreiheit: Durch die Verwendung semantischer Tags und geeigneter Stile ist der Inhalt auf verschiedenen Geräten zugänglich (Computer, Tablets, Smartphones).
Upload zu S3 und Mise à Jour du Statut
Nachdem der HTML-Inhalt generiert wurde, muss er dem Benutzer zugänglich gemacht werden. Dies geschieht durch das Hochladen der Datei in einen S3-Bucket, der für das Hosting statischer Websites konfiguriert ist.
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
-
Benennung der Dateien:
- Die Dateien werden mit einem Zeitstempel benannt, um die Einzigartigkeit zu gewährleisten.
- Die Bilder werden im Ordner
generated_images/gespeichert und die HTML-Dateien ingenerated_content/.
-
Hochladen zu S3:
- Verwendung des S3-Clients von
boto3zur Interaktion mit dem Dienst. - Der Inhalt wird je nach Typ (Bild oder Text) kodiert oder dekodiert.
- Der Parameter
ACL='public-read'macht die Datei öffentlich zugänglich.
- Verwendung des S3-Clients von
-
Aufbau der URL:
- Die öffentliche URL wird unter Verwendung der konfigurierten CloudFront-Domain erstellt, was eine schnelle und sichere Verteilung des Inhalts ermöglicht.
-
Fehlerbehandlung:
- Bei einem Fehler während des Uploads wird die Ausnahme protokolliert und ausgelöst, damit sie vom
lambda_handlerbehandelt werden kann.
- Bei einem Fehler während des Uploads wird die Ausnahme protokolliert und ausgelöst, damit sie vom
Hauptfunktion lambda_handler
Die Funktion lambda_handler ist der Einstiegspunkt der Lambda-Funktion. Sie koordiniert alle zuvor beschriebenen Schritte.
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"},
}
Erklärung
-
Verarbeitung der Anfrage:
- Ruft die erforderlichen Informationen aus dem erhaltenen Ereignis (event) ab.
- Die Anfrageparameter umfassen den Prompt, die ausgewählten Modelle, die Sprache usw.
-
Statusaktualisierung:
- Vor Beginn der Verarbeitung wird der Status in DynamoDB auf “Processing” gesetzt.
-
Generierung der Geschichte:
- Aufruf von
generate_storymit den entsprechenden Parametern.
- Aufruf von
-
Extraktion und Verarbeitung:
- Die Tags werden korrigiert und die Zusammenfassungen für die Bildgenerierung extrahiert.
-
Bildgenerierung:
- Wenn die Bildgenerierung aktiviert ist, werden die entsprechenden Bilder erzeugt und die URLs gesammelt.
-
Erstellung des HTML-Inhalts:
- Text und Bilder werden kombiniert, um den finalen HTML-Inhalt zu erstellen.
-
Upload zu S3:
- Der HTML-Inhalt wird zu S3 hochgeladen und die Ergebnis-URL wird erhalten.
-
Endgültige Statusaktualisierung:
- Der Status wird in DynamoDB auf “link” gesetzt, zusammen mit der Ergebnis-URL.
-
Rückgabe der Antwort:
- Die Antwort enthält den
requestIdund die Ergebnis-URL, so dass der Client den Status prüfen oder direkt auf den Inhalt zugreifen kann.
- Die Antwort enthält den
-
Fehlerbehandlung:
- Im Fehlerfall wird der Status auf “Failed” gesetzt und eine HTTP-500-Antwort zurückgegeben.
Lambda-Funktion status_checker.py
Überblick
Die Funktion status_checker.py ermöglicht es Benutzern, den Status ihrer Anfrage zur Generierung einer Geschichte zu prüfen. Sie fragt DynamoDB ab, um den aktuellen Status und, falls vorhanden, die Ergebnis-URL zu erhalten.
Codeanalyse
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
-
Abrufen des
requestId:- Das
requestIdist entscheidend, um die spezifische Anfrage des Benutzers zu identifizieren.
- Das
-
Abfrage von DynamoDB:
- Die Funktion versucht, das Element zu dem
requestIdabzurufen. - Wenn das Element existiert, werden der Status und das
resultUrlextrahiert.
- Die Funktion versucht, das Element zu dem
-
Erstellung der Antwort:
- Wenn der Status verfügbar ist, wird er zusammen mit der Ergebnis-URL zurückgegeben.
- Wird das Element nicht gefunden, wird ein 404-Fehler zurückgegeben.
- Bei einem Fehler bei der Abfrage der Datenbank wird ein 500-Fehler mit einer entsprechenden Nachricht zurückgegeben.
-
HTTP-Header:
- Die Header werden so gesetzt, dass CORS-Anfragen von der Website erlaubt sind.
Integration mit API Gateway
Konfiguration der Endpoints
Das API Gateway stellt zwei Hauptendpoints zur Verfügung, um mit den Lambda-Funktionen zu interagieren :
-
/generate-image:- Methode :
POST - Beschreibung : Ermöglicht es Benutzern, die Generierung einer Geschichte und gegebenenfalls zugehöriger Bilder zu starten.
- Integration : Verbunden mit der Lambda-Funktion
StoryPixAI.py.
- Methode :
-
/check-status:- Methode :
GET - Beschreibung : Ermöglicht es Benutzern, den Status ihrer Anfrage durch Angabe des
requestIdzu überprüfen. - Integration : Verbunden mit der Lambda-Funktion
status_checker.py.
- Methode :
Authentifizierung mit Cognito
Um die API zu sichern und den Zugriff auf Ressourcen zu kontrollieren, habe ich Amazon Cognito integriert.
-
User Pool:
- Verwaltet die Zugangsdaten der Benutzer.
- Ermöglicht Registrierung, Anmeldung und Benutzerverwaltung.
-
Authorizer:
- In API Gateway konfiguriert, um die von Cognito ausgestellten JWT-Token zu verifizieren.
- Stellt sicher, dass nur authentifizierte Anfragen auf geschützte Endpoints zugreifen können.
-
Integration im API Gateway:
- Die Endpoints
/generate-imageund/check-statussind durch den Cognito-Authorizer geschützt. - Die Clients müssen das Authentifizierungstoken in die Header ihrer Anfragen aufnehmen (
Authorization).
- Die Endpoints
Statische Website auf S3 und Interaction avec l’API
Seitenstruktur
Die statische Website dient als Benutzeroberfläche für die Anwendung.
-
index.html:- Enthält das Formular, mit dem Benutzer den Prompt eingeben, die Generierungsoptionen auswählen und ihre Anfrage absenden können.
- Enthält die Skripte, die für die Interaktion mit der API und die Verwaltung der Authentifizierung erforderlich sind.
-
storypixai.js:- Enthält den JavaScript-Code zur Verwaltung der Interaktionen mit der API.
- Verwaltet die Authentifizierung mit Cognito, das Absenden des Formulars, das Tracking des Status und die Anzeige der Ergebnisse.
Benutzer-Workflow
-
Anmeldung:
- Der Benutzer meldet sich über das integrierte Anmeldeformular an.
- Die Daten werden über Cognito überprüft.
-
Absenden der Anfrage:
- Der Benutzer füllt das Formular mit dem Prompt und den gewünschten Optionen aus.
- Beim Absenden wird eine Anfrage
POSTan den Endpoint/generate-imagemit den Daten gesendet.
-
Asynchrone Verarbeitung:
- Die API gibt sofort ein
requestIdzurück. - Die Generierungsverarbeitung erfolgt im Hintergrund.
- Die API gibt sofort ein
-
Statusabfrage:
- Die Website fragt periodisch das Endpoint
/check-statusab und übermittelt dabei dasrequestId. - Sobald der Status “link” empfangen wird, wird die Ergebnis-URL dem Benutzer angezeigt.
- Die Website fragt periodisch das Endpoint
-
Anzeige des Ergebnisses:
- Der Benutzer kann auf den Link klicken, um auf die generierte Geschichte mit den Bildern zuzugreifen.
Umgang mit Anfragen und Antworten
-
Authentifizierte Anfragen:
- Alle Anfragen an die API enthalten das Authentifizierungstoken.
- Das Token wird vom in die Website eingebundenen Cognito-SDK verwaltet.
-
Statusverwaltung:
- Mögliche Status sind “Processing”, “link”, “Failed”.
- Die Website passt ihre Oberfläche je nach erhaltenem Status an (z. B. Anzeige eines Ladeindikators, Fehlermeldung, Link zum Ergebnis).
Wechselwirkungen zwischen den Komponenten
So interagieren die verschiedenen Komponenten :
-
Website ↔️ API Gateway:
- Die Website sendet Anfragen an die vom API Gateway bereitgestellten Endpoints.
- Authentifizierungstoken werden zur Sicherung der Anfragen mitgesendet.
-
API Gateway ↔️ Lambda-Funktionen:
- Das API Gateway ruft die jeweiligen Lambda-Funktionen entsprechend den empfangenen Anfragen auf.
-
Lambda-Funktionen ↔️ DynamoDB:
- Die Lambda-Funktionen
StoryPixAI.pyundstatus_checker.pyinteragieren mit DynamoDB, um den Status der Anfragen zu aktualisieren und abzurufen.
- Die Lambda-Funktionen
-
Lambda-Funktion ↔️ S3:
- Die Funktion
StoryPixAI.pylädt die generierten Bilder und den HTML-Inhalt nach S3 hoch.
- Die Funktion
-
CloudFront ↔️ S3:
- CloudFront wird verwendet, um die in S3 gespeicherten Inhalte schnell und sicher auszuliefern.
- Die den Benutzern bereitgestellten URLs verweisen auf die CloudFront-Domain.
-
Benutzer ↔️ Website:
- Der Benutzer interagiert mit der Website, um Anfragen zu senden und die Ergebnisse anzusehen.
Exemple de Résultat dans les logs CLoudwatch après un Appel de Requête
Hier ein Beispiel eines Logergebnisses nach einem Request-Aufruf, damit Sie das Rohformat der erzeugten Daten sehen können :
[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.
Kontinuierliche Integration mit GitLab CI/CD
Um eine reibungslose Entwicklung und Bereitstellung von StoryPixAI sicherzustellen, habe ich eine Pipeline für Continuous Integration (CI) und Continuous Deployment (CD) mit GitLab CI/CD eingerichtet. Diese Konfiguration automatisiert Build- und Deployment-Prozesse und gewährleistet so die Qualität und Zuverlässigkeit des Codes bei jeder Änderung.
Pipeline-Konfiguration
Die Pipeline ist in der Datei .gitlab-ci.yml im Projektstamm definiert. Hier ist ein Überblick über ihre Struktur :
stages:
- Pré-requis optionel
- Vérifications
- Déploiements
- Management
- Suppressions
variables:
TERRAFORM_VERSION: '1.5.7-*'
TF_VAR_region: $AWS_DEFAULT_REGION
``` Diese Konfiguration definiert die verschiedenen Stufen der Pipeline und die globalen Variablen, die im CI/CD-Prozess verwendet werden.
### Hauptjobs
Die Pipeline umfasst mehrere zentrale Jobs:
1. **Vérification Terraform** :
```yaml
Vérification Terraform:
stage: Vérifications
when: manual
script:
- /bin/bash -c "source export.sh && terraform_plan"
Dieser Job führt terraform plan aus, um die geplanten Infrastrukturänderungen zu überprüfen, ohne sie anzuwenden.
-
Déploiement Terraform :
Déploiement Terraform: stage: Déploiements when: manual dependencies: - Vérification Terraform script: - /bin/bash -c "source export.sh && terraform_apply"Nach der Überprüfung wendet dieser Job die Infrastrukturänderungen an, indem er
terraform applyausführt. -
Suppression Terraform :
Suppression Terraform: stage: Suppressions when: manual script: - /bin/bash -c "source export.sh && terraform_destroy"Dieser Job ermöglicht es, die Infrastruktur bei Bedarf zu zerstören, indem
terraform destroyausgeführt wird. -
Gestion des Clés OpenAI :
Clé OpenAI - Ajout: stage: Pré-requis optionel 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 "Aucune clé trouvée." exit 1 fi Clé OpenAI - Supression: stage: Suppressions when: manual script: - /bin/bash -c "source export.sh && manage_openai_key delete"Diese Jobs verwalten das sichere Hinzufügen und Entfernen der OpenAI-API-Schlüssel im AWS Parameter Store.
Ausführungsumgebung
Jeder Job läuft in einem Docker-Container basierend auf Ubuntu 22.04, mit installiertem Terraform und AWS CLI:
.terraform_template: &terraform_template
image:
name: ubuntu:22.04
before_script:
- apt-get update
- apt-get install -y gnupg software-properties-common curl
- curl -fsSL https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
- echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep 'VERSION_CODENAME' /etc/os-release | cut -d'=' -f2) main" | tee /etc/apt/sources.list.d/hashicorp.list
- apt-get update
- apt-get install -y terraform=${TERRAFORM_VERSION} python3-pip bash jq zip
- pip3 install awscli >> /dev/null
Vorteile dieses CI/CD-Ansatzes
-
Automatisierung : Jede Codeänderung löst automatisch die Pipeline aus und stellt konsistente Prüfungen und Deployments sicher.
-
Manuelle Kontrolle : Kritische Schritte wie Deployment und Löschung sind im manuellen Modus konfiguriert (
when: manual), was zusätzliche Kontrolle vor der Ausführung bietet. -
Sichere Verwaltung von Geheimnissen : Die Integration mit dem AWS Parameter Store zur Verwaltung von API-Schlüsseln gewährleistet einen sicheren Umgang mit sensiblen Informationen.
-
Flexibilität : Die Struktur in Stages ermöglicht eine geordnete und logische Ausführung der verschiedenen Pipeline-Schritte.
-
Reproduzierbarkeit : Die Verwendung einer standardisierten Docker-Umgebung stellt sicher, dass Builds und Tests auf unterschiedlichen Systemen reproduzierbar sind.
Diese CI/CD-Konfiguration ermöglicht nicht nur die Automatisierung des Deployments von StoryPixAI, sondern trägt auch dazu bei, während des gesamten Entwicklungszyklus ein hohes Maß an Qualität und Zuverlässigkeit aufrechtzuerhalten.
Fazit
StoryPixAI war weit mehr als nur ein technisches Projekt. Es war ein echtes Abenteuer in der Welt der generativen KI und gab mir die Möglichkeit, meine Leidenschaft für Technologie mit dem Wunsch zu verbinden, magische Geschichten für meine Kinder zu erschaffen.
Dieses Projekt bot mir die Gelegenheit, verschiedene Facetten der KI zu erkunden — von der Gestaltung einer intuitiven Benutzeroberfläche über die Beherrschung des Promptings bis hin zur Einrichtung einer robusten Cloud-Infrastruktur mit AWS und Terraform. Jeder Schritt war eine Lernquelle, stellte mich vor anspruchsvolle technische Herausforderungen und zwang mich dazu, meine Fähigkeiten im Full-Stack-Entwicklungs- und DevOps-Bereich zu erweitern.
Ich hoffe, dieser Blogbeitrag hat Ihnen einen Einblick in die Hintergründe dieses spannenden Projekts gegeben.
Wichtige Punkte
-
Detaillierte Anleitungen :
- Klare und strukturierte Prompts ermöglichen es, konsistente und qualitativ hochwertige Ergebnisse von den KI-Modellen zu erhalten.
-
Modulare Architektur :
- Jede Komponente (Website, API Gateway, Lambda, DynamoDB, S3, Cognito) erfüllt eine spezifische Rolle, was Wartung und Weiterentwicklung des Systems erleichtert.
-
Sicherheit und Skalierbarkeit :
- Die Nutzung verwalteter AWS-Services gewährleistet robuste Sicherheit und die Fähigkeit, auf wachsende Nachfrage zu reagieren.
Projektlink : StoryPixAI
Dieses Dokument wurde aus der französischen Version ins Englische mit dem Modell gpt-5-mini übersetzt. Für weitere Informationen zum Übersetzungsprozess siehe https://gitlab.com/jls42/ai-powered-markdown-translator