Aufbau eines serverlosen AI-Barkeepers - Teil 1: Hör auf, die Speisekarte zu sein

Diese Datei wurde automatisch von KI übersetzt, es können Fehler auftreten
Meine größten Interessen und Hobbys liegen im Bereich Essen und Trinken, und ich liebe es, zu Hause Cocktails zu mixen.
Es gibt etwas seltsam Befriedigendes daran, den perfekten Negroni zu mixen oder einen richtigen Whiskey Sour zu schütteln. Aber wenn ich Partys veranstalte, stehe ich jedes Mal vor dem gleichen Problem: Ich werde zur menschlichen Speisekarte. Die Leute wissen nie, was sie wollen, obwohl ich mit einer voll ausgestatteten Bar auftauche, also neigen sie am Ende dazu, die gleichen alten Cocktails zu bestellen, die sie immer tun.
An Silvester waren wir auf dem Weg zu Freunden zu einer Party, und wie immer fragten sie, ob ich während des Abends Cocktails mixen könnte. Natürlich könnte ich das tun, aber ich brauchte eine Art Getränkekarte, aus der die Leute wählen konnten. Ich würde nicht mein gesamtes Setup mitbringen, nur fast...
Also war mein anfänglicher Plan, es einfach auf Papier auszudrucken und die Leute konnten daraus wählen. Dann wachte der Ingenieur in mir auf und dachte: Was wäre, wenn ich ein serverloses Getränkebestellsystem erstellen würde?
Dies ist der erste Beitrag in einer dreiteiligen Reihe, in der ich einen serverlosen KI-Cocktail-Assistenten baue. Meine Gäste können die Speisekarte selbst durchsuchen und direkt auf ihrem Handy bestellen, was sie wollen. Und am Ende erhalten sie auch KI-Empfehlungen, was sie bestellen sollen, mit anderen Worten, weniger Arbeit für mich.
Das Projekt wird nach Veröffentlichung von Teil 3 auf serverless-handbook geteilt.
Lasst uns beginnen!
Was wir bauen
Eine Web-App, in der Gäste meine Getränkekarte durchsuchen, Bestellungen aufgeben und KI-Empfehlungen erhalten können. Stellen Sie sich das als digitale Speisekarte und Barkeeper-Assistent kombiniert vor.
Um das zu schaffen, brauche ich ein solides Fundament, das eine Datenbank zur Speicherung von Getränken und Bestellungen, REST-APIs, die es der App ermöglichen, Daten zu lesen und zu schreiben, Authentifizierung, um das Admin-Panel zu sichern und gleichzeitig den Gästen ein freies Durchsuchen zu ermöglichen, und Bild-Upload, damit ich Fotos zu jedem Getränk hinzufügen kann. Alles serverlos, natürlich - keine Server zu verwalten, nur Dienste, die mit der Nachfrage skalieren.
Architekturübersicht
Im ersten Teil werden wir das Fundament behandeln. Dies ist der Grund, auf dem alles steht. Ohne ein richtiges Fundament wird es schwierig sein, neue Funktionen und Funktionalitäten zu erweitern. Wir werden das Frontend-Hosting, die von Amazon API Gateway gehostete API, die Datenverarbeitung mit AWS Lambda, die Authentifizierung mit Amazon Cognito, Amazon S3 für Bilder, Amazon EventBridge für ereignisgesteuerte Workflows wie die KI-Bilderzeugung und Amazon Aurora DSQL als unsere primäre Datenbank erstellen.

Aurora DSQL - die serverlose relationale Datenbank
Für die Datenbank habe ich mich für Aurora DSQL entschieden. Es ist eine serverlose, PostgreSQL-kompatible Datenbank. Keine Provisionierung, keine Verbindungspools zu verwalten und sie skaliert automatisch.
Warum habe ich mich für DSQL entschieden? Als ich das Projekt startete, stand ich zwischen der Verwendung von DynamoDB oder DSQL. Ein wichtiger Aspekt war, dass die ausgewählte Datenbank serverlos sein und ohne VPC laufen musste, was Aurora Serverless ausschließt. Der Grund, warum ich mich für DSQL und ein relationales Modell entschieden habe, war, dass ich eine ordnungsgemäße Suche und Filterung benötige, beispielsweise nach Zutaten. DynamoDB ist ein großartiger Key-Value-Store, aber nicht alle Datenmodelle passen dazu.
DSQL bietet mir mehrere Vorteile, die es zur richtigen Wahl machen. Es ist serverlos, ich zahle nur für das, was ich benutze, und es skaliert auf null, wenn niemand die Speisekarte durchsucht. Da es PostgreSQL-kompatibel ist, erhalte ich ordnungsgemäßes SQL mit integrierten Such- und Filterfunktionen, die für die Abfrage nach Zutaten unerlässlich sind. Es gibt auch kein Passwort-Management, da die IAM-Authentifizierung die Dinge sicher hält, ohne Geheimnisse zu rotieren. Und wichtig ist, dass es natürlich mit dem kurzlebigen Verbindungsmodell von Lambda funktioniert. Jede Lambda-Aufruf kann sich unabhängig authentifizieren, ohne persistente Pools zu verwalten.
Einrichten von DSQL
Das Erstellen eines neuen DSQL-Clusters ist ziemlich unkompliziert. Ich verwende CloudFormation, um einen DSQL-Cluster und zwei IAM-Rollen zu erstellen, eine zum Lesen von Daten und eine zum Schreiben. Warum zwei verschiedene Rollen? Hauptsächlich wegen der Trennung der Aufgaben. In diesem System werden 95 % des Verkehrs Leseoperationen sein, Gäste, die die Speisekarte durchsuchen. Nur Admins führen Schreibvorgänge durch, wenn sie Getränke verwalten. Durch die Aufteilung der Rollen stelle ich sicher, dass die Lambda-Funktionen, die Gästeanfragen bearbeiten, nur lesen können. Sie können physisch keine Daten ändern, selbst wenn ich einen Fehler in der Funktion mache. Die Schreiberrolle ist hingegen auf die Admin-Lambda-Funktionen hinter dem Cognito-Autorisierer beschränkt. Es ist eine kleine zusätzliche Anstrengung, die eine sinnvolle Grenze schafft.
Datenbankrollen und Berechtigungen
DSQL verwendet ein zweistufiges Berechtigungsmodell. Zuerst benötigen Sie eine IAM-Rolle, die sich mit dem Cluster verbinden kann. Zweitens benötigen Sie eine Datenbankrolle, die steuert, was Sie tun können, sobald Sie verbunden sind.
In meinem Setup funktioniert es so.
- Die Lambda-Funktion übernimmt eine IAM-Rolle, wie die
DatabaseReaderRolevon oben, über STS - Mit diesen Anmeldeinformationen ruft die Lambda-Funktion
dsql.generate_db_connect_auth_token()auf, dies erfordert die Berechtigungdsql:DbConnect - Die Lambda-Funktion verbindet sich mit DSQL unter Verwendung des Authentifizierungstokens als Passwort
- DSQL ordnet die IAM-Rolle einer Datenbankrolle zu, die den Tabellenzugriff steuert
Hier ist der Python-Code, der dies tut.
Die Datenbankrolleneinrichtung sieht so aus.
Die Writer-Rolle folgt dem gleichen Muster, gewährt aber auch INSERT-, UPDATE-, DELETE-Berechtigungen.
Datenbankschema
Das Datenbankschema besteht aus mehreren Tabellen und Indizes, aber die primäre Tabelle ist die Getränketabelle. Ich musste hier ein paar bewusste Designentscheidungen treffen. Am wichtigsten ist, wie ich Zutaten speichere?
Ich verwende JSONB für Zutaten, weil jedes Getränk eine völlig andere Liste hat. Ein Margarita benötigt drei Zutaten wie Tequila, Limettensaft und Triple Sec. Ein klassisches Tiki-Getränk könnte acht oder mehr benötigen. Mit einem relationalen Modell bräuchte ich entweder eine separate Zutaten-Tabelle mit einer Viele-zu-eins-Beziehung (was Komplexität hinzufügt) oder eine feste Anzahl von Zutatenspalten (die Platz verschwenden oder die Liste begrenzen). JSONB gibt mir Flexibilität, ohne die Suchbarkeit zu opfern - PostgreSQL kann innerhalb von JSONB-Feldern mit ordnungsgemäßen Indizes abfragen, also wenn ich später nach "allen Getränken mit Tequila" suchen möchte, ist es immer noch effizient.
Ich habe in Betracht gezogen, Zutaten als einfaches TEXT-Feld mit kommagetrennten Werten zu speichern, aber das skaliert nicht, wenn ich später mehr Metadaten hinzufügen möchte, Menge, Maßeinheit, Ersatzstoffe. JSONB macht das Schema zukunftssicher, ohne Migrationen zu erfordern.
Der Berechtigungsfehler, der mich Stunden gekostet hat
Als ich die Lösung entwickelte, musste ich Tabellen aktualisieren und neue Tabellen erstellen, und hier ist etwas, das mich bei DSQL überrascht hat. Ich hatte meine Datenbankrollen eingerichtet, alles funktionierte perfekt. Aber nachdem ich eine neue Tabelle für die Benutzerregistrierung hinzugefügt, sie bereitgestellt und sofort Berechtigungsfehler erhalten hatte.
Berechtigung verweigert für Tabelle registration_codesDas Problem? DSQL unterstützt ALTER DEFAULT PRIVILEGES nicht.
In regulärem PostgreSQL können Sie so etwas ausführen.
-- Dies funktioniert NICHT in DSQL
ALTER DEFAULT PRIVILEGES IN SCHEMA cocktails
GRANT SELECT, INSERT, UPDATE ON TABLES TO lambda_drink_writer;Dies würde automatisch Berechtigungen für alle zukünftigen Tabellen gewähren. Aber DSQL unterstützt diesen Befehl nicht. Wenn Sie eine neue Tabelle erstellen, haben Ihre bestehenden Datenbankrollen keinen Zugriff darauf, bis Sie explizit Berechtigungen gewähren.
Die Lösung ist einfach, aber leicht zu vergessen. Jedes Mal, wenn Sie eine Tabelle hinzufügen, benötigen Sie eine entsprechende GRANT-Anweisung.
Ich habe dies gelöst, indem ich eine dedizierte Migrationsdatei führe, die alle Tabellenberechtigungen verfolgt. Wenn ich eine neue Tabelle hinzufüge, aktualisiere ich diese Datei mit den expliziten Grants. Es ist manuell, aber es verhindert die "warum kann mein Lambda diese Tabelle nicht lesen" Debugging-Sitzungen.
Authentifizierung und Autorisierung
Bevor wir uns in die API vertiefen, schauen wir uns die Authentifizierung an und richten sie ein. Das System hat zwei Arten von Benutzern mit sehr unterschiedlichen Zugriffsebenen. Gäste können frei Getränke durchsuchen, sie müssen nicht beweisen, wer sie sind, bis sie eine Bestellung aufgeben wollen. Admins hingegen benötigen eine vollständige Authentifizierung, um das Menü zu verwalten, alle Bestellungen anzuzeigen und den Bestellstatus zu aktualisieren.
Diese Aufteilung bedeutet, dass ich sowohl Authentifizierung (Überprüfung, wer Sie behaupten zu sein) als auch Autorisierung (Entscheidung, was Sie tun dürfen) benötige. Admins durchlaufen einen vollständigen Cognito-Flow mit Benutzername und Passwort. Gäste erhalten einen leichten, einladungsbasierten Registrierungsflow, den ich in Teil 2 detaillieren werde.
Admins müssen sich also anmelden, um Admin-Aufgaben auszuführen, wie z.B. neue Getränke zu erstellen oder bestehende zu aktualisieren.

Cognito User Pool für Admins
Amazon Cognito User Pool wird die Admin-Authentifizierung enthalten und verarbeiten. Es bietet einen gehosteten User Pool, JWT-Token und Gruppenverwaltung, ohne dass ich etwas davon bauen muss.
Cognito Hosted Login
Anstatt meine eigenen Login-Formulare zu erstellen, verwende ich Cognitos gehostete UI. Sie verarbeitet den gesamten Login-Flow einschließlich Benutzername/Passwort, Fehlermeldungen und Passwort-Reset, alles mit einer sauberen, anpassbaren Oberfläche.
Die ManagedLoginVersion: 2 gibt Ihnen die neuere, anpassbarere Login-Erfahrung. Die ManagedLoginBranding-Ressource ermöglicht es Ihnen, die Login-Seite zu stylen. Ich verwende hier Cognitos Standardwerte, aber Sie können Farben, Logos und CSS anpassen.
Die gehostete UI-URL folgt diesem Muster.
https://.auth..amazoncognito.com/login
?client_id=
&response_type=code
&scope=email+openid+profile
&redirect_uri=https:///admin/callback Der Login-Flow funktioniert so.
- Admin klickt auf "Login" im Admin-Panel
- Frontend leitet zur Cognito-gehosteten UI weiter
- Admin gibt Anmeldeinformationen ein
- Cognito validiert und leitet mit einem Autorisierungscode zurück
- Frontend tauscht Code gegen JWT-Token (Zugriff, ID, Auffrischungstoken) aus
- Zugriffstoken wird gespeichert und mit API-Anfragen gesendet
Wenn sich ein Admin anmeldet, gibt Cognito ein JWT-Zugriffstoken zurück. Dieses Token enthält Ansprüche, einschließlich cognito:groups, die die Gruppenmitgliedschaften des Benutzers auflisten. Dies wird später bei der Autorisierung des Admins verwendet.
Für eine tiefere Einsicht in Authentifizierungs- und Autorisierungsmuster mit Cognito lesen Sie meinen Beitrag PEP und PDP für sichere Autorisierung mit Cognito.
REST-API mit API Gateway
Das Einrichten und Erstellen der REST-API ist nichts Besonderes, wir werden Amazon API Gateway verwenden, und ich wähle die REST-Version (v1) für den Job. Es gibt ein paar Gründe dafür, aber in diesem Teil werde ich einen von ihnen behandeln. Ich brauche Unterstützung für API-Schlüssel und Throttling, und derzeit ist die REST-Version die einzige, die das unterstützt. Die API folgt einer klaren Trennung mit öffentlichen Endpunkten für Gäste, die Getränke durchsuchen und Bestellungen aufgeben, und Admin-Endpunkten für die Verwaltung des Menüs. Jede hat unterschiedliche Authentifizierungs- und Throttling-Anforderungen.
Öffentliche Endpunkte (nur API-Schlüssel):
GET /drinks - Alle Getränke auflisten
GET /drinks/{id} - Getränkedetails abrufen
GET /sections - Menüabschnitte auflisten
POST /orders - Bestellung aufgeben
GET /orders/{id} - Bestellstatus überprüfen
Admin-Endpunkte (API-Schlüssel + JWT):
POST /admin/drinks - Getränk erstellen
PUT /admin/drinks/{id} - Getränk aktualisieren
DELETE /admin/drinks/{id} - Getränk löschen
GET /admin/orders - Alle Bestellungen anzeigen
PUT /admin/orders/{id} - Bestellstatus aktualisierenLambda Authorizer für AuthN + AuthZ
API Gateway verwendet einen Lambda Authorizer, um Anfragen zu validieren. Hier findet sowohl die Authentifizierung als auch die Autorisierung statt.
Der JWT-Validator ruft Cognitos öffentliche Schlüssel (JWKS) ab und verifiziert.
- Signatur - Token wurde nicht manipuliert
- Ablauf - Token ist nicht abgelaufen
- Aussteller - Token kam aus meinem Cognito-Benutzerpool
Wenn alle Überprüfungen bestanden werden, schaut sich der Authorizer den cognito:groups-Anspruch an. Admin-Endpunkte erfordern, dass der Benutzer in der admin-Gruppe ist, die Autorisierungsprüfung.
Warum ein Lambda Authorizer?
API Gateway hat integrierte Cognito-Authorizer, aber sie führen nur die Authentifizierung durch. Sie überprüfen, ob das Token gültig ist, können aber keine Gruppenmitgliedschaft überprüfen. Der Lambda Authorizer ermöglicht es mir, beides in einem Schritt zu tun.
Der Authorizer übergibt auch den Benutzerkontext an nachgelagerte Lambda-Funktionen.
Dieser Kontext ist in event["requestContext"]["authorizer"] verfügbar, sodass Lambda-Funktionen wissen, wer die Anfrage gestellt hat, ohne das JWT erneut zu analysieren.
Warum API-Schlüssel für öffentliche Endpunkte?
Selbst öffentliche Endpunkte benötigen einen API-Schlüssel. Dies dient nicht der Authentifizierung, sondern dem Schutz und der Kontrolle. API-Schlüssel sind mit Nutzungsplänen mit Ratenlimits verknüpft, die mir eine feinkörnige Throttling-Kontrolle ermöglichen. CloudWatch-Metriken zeigen die Nutzung pro Schlüssel, was Einblicke darüber gibt, wer die API aufruft und wie oft. Und wenn etwas schief geht, kann ich einen Schlüssel sofort als Notausschalter deaktivieren.
Der API-Schlüssel ist in das Frontend eingebettet, er ist also kein Geheimnis. Aber er gibt mir die Kontrolle darüber, wer die API aufrufen kann und wie oft.
Nutzungspläne und Throttling
Verschiedene Endpunkte haben unterschiedliche Ratenlimits basierend auf der erwarteten Nutzung.
PublicUsagePlan:
Type: AWS::ApiGateway::UsagePlan
Properties:
ApiStages:
- ApiId: !Ref DrinkAssistantApi
Stage: v1
Throttle:
"/drinks/GET":
RateLimit: 100 # Anfragen pro Sekunde
BurstLimit: 200 # Spike-Zulassung
"/orders/POST":
RateLimit: 50 # langsamer - schreibt in die Datenbank
BurstLimit: 100Das Durchsuchen von Getränken kann 100 Mal pro Sekunde erfolgen. Das Aufgeben von Bestellungen ist auf 50/Sekunde begrenzt, da es Datenbankschreibvorgänge und Ereignisveröffentlichungen beinhaltet, also möchte ich konservativer sein.
Für eine Hausparty ist das eine massive Übertreibung. Aber es ist eine gute Angewohnheit, und wenn dies jemals ein echtes Produkt werden sollte, ist die Grundlage vorhanden.
API Gateway Caching
Das Getränkemenü ändert sich selten, aber Gäste durchsuchen es ständig. Ohne Caching trifft jeder Seitenaufruf Lambda und DSQL. Mit 20 Gästen, die das Menü alle paar Sekunden aktualisieren, ist das eine unnötige Belastung und Kosten.
API Gateway hat ein eingebautes Caching, das für dieses Szenario überraschend effektiv ist.
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: prod
CacheClusterEnabled: true
CacheClusterSize: "0.5" # Kleinste Größe, ~$15/Monat
MethodSettings:
- HttpMethod: GET
ResourcePath: /drinks
CachingEnabled: true
CacheTtlInSeconds: 3600 # 1 StundeJetzt gibt GET /drinks 1 Stunde lang zwischengespeicherte Antworten zurück. Das Menü ändert sich nicht so oft, also ist dies ein vernünftiger Kompromiss. Aber hier ist der Haken, was passiert, wenn ich ein neues Getränk hinzufüge? Gäste würden ein veraltetes Menü durchsuchen, bis der Cache natürlich abläuft.
Die Lösung ist eine aggressive Invalidierung. Der createDrink Lambda löscht den gesamten API-Cache nach dem Speichern in der Datenbank.
Dieses Muster, aggressives Caching für Lesevorgänge, sofortige Invalidierung bei Schreibvorgängen, gibt Ihnen das Beste aus beiden Welten: schnelle Seitenaufrufe beim Durchsuchen und sofortige Updates, wenn sich das Menü ändert. Beachten Sie, dass dieser Ansatz gut für den Menü-Endpunkt funktioniert, weil Datenänderungen selten und kontrolliert sind. Wenn ich den Bestellstatus oder Echtzeitdaten zwischenspeichern würde, würde ich entweder eine kürzere TTL verwenden oder das Caching ganz überspringen. Der Schlüssel ist, die Cache-Strategie darauf abzustimmen, wie oft sich Ihre Daten tatsächlich ändern.
Bild-Upload mit S3
Getränkefotos gehen mit vorsignierten URLs zu S3. Dies hält große Uploads von Lambda fern, da es ein 10MB-Anfragelimit hat.
Der Ablauf ist unkompliziert. Wenn ein Admin ein Getränkefoto hochladen möchte, fordert er eine Upload-URL von der API an. Die Lambda-Funktion generiert eine vorsignierte PUT-URL, die 10 Minuten lang gültig ist und dem Admin eine temporäre, eingeschränkte Berechtigung zum direkten Upload zu S3 gibt. Der Browser des Admins lädt das Bild dann direkt zu S3 hoch, ohne Lambda zu berühren. Nach dem Upload serviert CloudFront das Bild an Gäste, die die Speisekarte durchsuchen.
Der S3-Bucket CloudFormation.
Und die Lambda, die vorsignierte URLs generiert.
Ereignisgesteuerte Bild-Prompt-Generierung
Hier wird es interessant, und die Teile und Lösungen, die ich normalerweise baue und über die ich schreibe. Wenn ich ein neues Getränk hinzufüge, möchte ich einen KI-generierten Bild-Prompt bereit haben, für wenn ich schließlich das Getränkefoto erstelle. Anstatt manuell Prompts zu schreiben, lasse ich Amazon Bedrock sie basierend auf dem Namen und den Zutaten des Getränks generieren.
Der Schlüssel ist die Entkopplung in ihrer besten Form. Ich hätte Bedrock direkt vom createDrink Lambda aufrufen können, aber das würde die Latenz der API-Antwort erhöhen, da der Admin auf die KI-Generierung warten muss. EventBridge löst dies elegant. Die API kehrt unmittelbar nach dem Speichern in der Datenbank zurück und hält die Antwortzeiten schnell. Der Event Bus handhabt die Prompt-Generierung asynchron, und wenn Bedrock langsam ist oder vorübergehend ausfällt, versucht EventBridge automatisch erneut. Diese lose Kopplung bedeutet auch, dass ich das System später leicht erweitern kann, indem ich weitere Event Listener für Benachrichtigungen oder Analysen hinzufüge, ohne den API-Code zu berühren.

Warum ein benutzerdefinierter Event Bus?
Ich verwende einen dedizierten Event Bus anstelle des Standard-AWS-Busses.
DrinkAssistantEventBus:
Type: AWS::Events::EventBus
Properties:
Name: drink-assistant-eventsWarum nicht einfach den Standard-Bus verwenden? Drei Gründe.
- Isolation - Meine Anwendungsereignisse vermischen sich nicht mit AWS-Service-Ereignissen (CloudTrail usw.)
- Berechtigungen - Ich kann bestimmten IAM-Rollen Zugriff auf nur diesen Bus gewähren
- Filterung - Regeln sehen nur Ereignisse aus meiner Anwendung, was Muster einfacher macht
Für ein kleines Projekt mag dies übertrieben erscheinen, aber es ist eine gute Angewohnheit. Wenn Sie mehrere Anwendungen haben, die Ereignisse veröffentlichen, hält ein dedizierter Bus pro Domain die Dinge sauber.
Der Ereignisfluss

Die createDrink Lambda-Funktion veröffentlicht ein Ereignis nach dem Speichern in der Datenbank.
Das Ereignis ist Fire-and-Forget. Wenn es fehlschlägt, loggen wir es, scheitern aber nicht den API-Aufruf. Das Getränk ist bereits gespeichert.
Generieren des Prompts mit Bedrock
Hier wird Prompt-Engineering kritisch. Ich habe zunächst ein generische System-Prompt wie "Erstelle einen Bild-Prompt für ein Cocktail-Foto" versucht und es freigelassen. Die Ergebnisse waren funktional, aber uninspiriert - generische Beschreibungen von Getränken in generischem Glasgeschirr unter generischer Beleuchtung. Nicht schlecht, aber nicht mit meiner Vision des nordischen Minimalismus in Einklang.
Ich habe Amazon Bedrock Nova Pro für diese Aufgabe gewählt, weil es Kosten und Qualität ausgleicht. Nova Pro ist billiger als Claude und produziert dennoch kohärente, detaillierte Ausgaben. Das Modell halluziniert nicht wild, und für Bild-Prompts brauche ich keine blutige, kantige Argumentation, ich brauche konsistente, vorhersehbare Ausgaben, mit denen ein Bildgenerierungsmodell arbeiten kann.
Die eigentliche Arbeit steckt in den Prompts. Das System-Prompt etabliert die Persona und Perspektive des Fotografen. Das Benutzer-Prompt bietet spezifische Einschränkungen und ästhetische Anleitung. Hier ist die Entwicklung:
Erster Versuch (zu generisch):
System: "Du bist ein Fotograf. Erstelle einen Bild-Prompt für einen Cocktail."
Benutzer: "Cocktail: Negroni. Zutaten: Campari, Gin, Wermut."
Ergebnis: "Ein Negroni-Cocktail in einem Kristallglas, garniert mit einer Orangenscheibe, fotografiert in einem Studio-Setting mit professioneller Beleuchtung."
Das funktioniert, aber es ist generisch - könnte jedes Cocktail-Foto beschreiben.
Zweiter Versuch (spezifischer):
System: "Du bist ein Fotograf, der sich auf Getränkefotografie spezialisiert hat. Erstelle einen detaillierten, fotorealistischen Prompt für ein Cocktail-Foto."
Benutzer: "Cocktail: Negroni. Erstelle einen Prompt, der die minimalistische, nordische Ästhetik betont, professionelle Studio-Beleuchtung."
Ergebnis: "Ein Negroni in einem Rocks-Glas, minimal garniert mit einer Orangenscheibe, vor einem weichen grauen Hintergrund. Saubere Studio-Beleuchtung, die die tiefe rote Farbe und Klarheit des Getränks betont. Minimalistische Komposition. Professionelle Fotografie."
Besser! Spezifischer, aber die nordische Ästhetik ist immer noch nicht klar genug.
Endgültige Version (was ich tatsächlich verwende):
Die Temperatur von 0,7 schlägt eine Balance. Höher (näher an 1,0) würde jeden Prompt zufälliger und kreativer machen, aber weniger konsistent über Getränke hinweg. Niedriger (näher an 0) wäre repetitiv. Bei 0,7 ist jeder Prompt einzigartig, aber vorhersehbar genug, dass ein Bildgenerierungsmodell die Ästhetik richtig bekommt. Das maxTokens: 300 ist speziell gesetzt, weil Nova Canvas, das Bildgenerierungsmodell, das ich während des Projekts getestet habe, ein 300-Token-Limit für Prompts hat. Dies zwingt die Textgenerierung, knapp zu bleiben, während sie immer noch beschreibend ist.
Diese Erweiterung des System-Prompts - zu erklären, dass ich speziell nordischen Minimalismus will, zu beschreiben, was das bedeutet (weiche Schatten, neutrale Hintergründe, minimale Garnierung) - war der Schlüssel zum Durchbruch. Als ich zum ersten Mal Prompts mit dieser Version generierte, waren die Ergebnisse endlich mit meiner Vision in Einklang, saubere, minimale, premium-aussehende Cocktail-Fotografie.
Der Prompt wird für die spätere Verwendung, wenn ich tatsächlich das Bild generiere (vielleicht mit Nova Canvas in einer zukünftigen Version), in S3 gespeichert.
Erweiterbarkeit
Dieses Muster skaliert gut. Wenn ich Bildgenerierung hinzufüge, ist es nur eine weitere Regel, die auf das gleiche Ereignis hört. Keine Änderungen an der API erforderlich.
Was als Nächstes kommt
Das Fundament ist fertig. Gäste können die Speisekarte durchsuchen, Admins können Getränke verwalten. Aber Gäste können noch keine Bestellungen aufgeben, sie brauchen eine Möglichkeit, sich zu identifizieren, ohne vollständige Konten zu erstellen.
In Teil 2 werde ich den kompletten Bestellablauf bauen.
- Gastregistrierung mit Einladungscodes und selbstsignierten JWTs
- Bestellungsplatzierung mit einem benutzerdefinierten Lambda Authorizer
- Echtzeit-Benachrichtigungen mit AWS AppSync Events - wenn ich ein Getränk als bereit markiere, sehen es die Gäste sofort
Bleiben Sie dran und folgen Sie mir auf LinkedIn, damit Sie es nicht verpassen!
Abschließende Worte
Dieses Fundament mag nicht der auffälligste Teil des Projekts sein, aber es ist die Grundlage, die alles andere ermöglicht. Datenbank, APIs, Authentifizierung, ereignisgesteuerte Workflows. Es ist alles hier, bereit, die wahre Magie zu unterstützen.
Denn dies ist nur der Anfang. Der KI-Cocktail-Assistent ist das Ziel, und dort wird es interessant. KI-Empfehlungen basierend auf Geschmackspräferenzen, Bildgenerierung für Getränkefotos, Echtzeit-Bestellaktualisierungen, vielleicht sogar konversationelle Bestellungen. Das Fundament ist fertig. Jetzt kommt der lustige Teil.
Das ist es, was ich an serverlosen und verwalteten Diensten wie DSQL, Cognito, EventBridge und Bedrock liebe. Sie erledigen die undifferenzierte schwere Arbeit, damit Sie schnell auf dem Fundament vorankommen und Ihre Zeit auf das verwenden können, was das Projekt wirklich einzigartig macht. Die KI-Funktionen. Die Benutzererfahrung. Die Dinge, die die Leute sagen lassen "das ist cool".
Teil 2 kommt bald. Gastregistrierung, Bestellablauf und Echtzeit-Benachrichtigungen. Bleiben Sie dran.
Schauen Sie sich meine anderen Beiträge auf jimmydqv.com an und folgen Sie mir auf X für mehr serverlose Inhalte.
Wie Werner sagt: Jetzt baue!
What's Next
The foundation is done. Guests can browse the menu, admins can manage drinks. But guests can't actually place orders yet, they need a way to identify themselves without creating full accounts.
In Part 2, I'll build the complete ordering flow.
- Guest registration with invite codes and self-signed JWTs
- Order placement with a custom Lambda authorizer
- Real-time notifications using AWS AppSync Events - when I mark a drink ready, guests see it instantly
Stay tuned and follow me on LinkedIn so you don't miss it!
Final Words
This foundation might not be the flashiest part of the project, but it's the groundwork that makes everything else possible. Database, APIs, authentication, event-driven workflows. It's all here, ready to support the real magic.
Because this is just the beginning. The AI cocktail assistant is the goal, and that's where it gets interesting. AI recommendations based on taste preferences, image generation for drink photos, real-time order updates, maybe even conversational ordering. The foundation is done. Now comes the fun part.
That's what I love about serverless and managed services like DSQL, Cognito, EventBridge, and Bedrock. They handle the undifferentiated heavy lifting so you can move fast on the foundation and spend your time on what actually makes the project unique. The AI features. The user experience. The things that make people say "that's cool."
Part 2 is coming soon. Guest registration, ordering flow, and real-time notifications. Stay tuned.
Check out my other posts on jimmydqv.com and follow me on X for more serverless content.
As Werner says, Now Go Build!
