KI-Essensplaner entwickeln: Zuverlässiges JSON mit Next.js & LangChain
Erfahren Sie, wie Sie einen generativen KI-Essensplaner mit Next.js und LangChain erstellen. Dieses Tutorial behandelt Prompt Engineering mit Zod-Schemata, um strukturierte, zuverlässige JSON-Ausgaben von Ihrem LLM zu gewährleisten.
Auf dieser Seite
Below is the complete German translation of the provided markdown document, preserving all markdown syntax, code blocks, and formatting as requested.
Haben Sie schon einmal versucht, von einem LLM strukturierte Daten wie ein JSON-Objekt anzufordern, nur um jedes Mal ein leicht unterschiedliches Format zu erhalten? Eines Tages ist es ein String, am nächsten ein fehlerhaftes Objekt. Diese Unvorhersehbarkeit macht es zu einem Albtraum, zuverlässige Anwendungen zu entwickeln. Das Problem ist nicht die Kreativität des LLM; es ist der Mangel an Einschränkungen.
In diesem Tutorial werden wir dieses Problem direkt angehen, indem wir einen intelligenten, generativen KI-Essensplaner erstellen. Dieses Tool nimmt die Ziele des Benutzers (z.B. „hoher Proteingehalt, niedrige Kohlenhydrate") und Ernährungseinschränkungen und generiert einen vollständigen, strukturierten 7-Tage-Essensplan.
Wir werden die Leistungsfähigkeit von Next.js für das Frontend und die API-Schicht nutzen und die Magie von LangChain einsetzen, um sicherzustellen, dass unsere KI nicht nur einen großartigen Essensplan liefert, sondern ihn auch jedes Mal in einem perfekten, parsbaren JSON-Format zurückgibt. Dies ist der Schlüssel zum Übergang von KI-Neuheiten zu produktionsreifen KI-Funktionen.
Voraussetzungen
- Node.js (v18 oder höher)
- Ein OpenAI API-Schlüssel.
- Grundlegendes Verständnis von React, TypeScript und Next.js.
Das Problem verstehen: Das Chaos unstrukturierter KI-Ausgaben
Wenn Sie ein großes Sprachmodell (LLM) wie GPT-4 auffordern, führen Sie im Wesentlichen ein Gespräch. Die Freitext-Antworten des Modells eignen sich hervorragend für Chatbots, sind aber katastrophal für Anwendungen, die die Ausgabe programmatisch verwenden müssen.
Stellen Sie sich vor, Sie versuchen, dies zu parsen:
„Sicher, hier ist ein Essensplan. Für Montag könnten Sie Rührei haben... dann zum Mittagessen vielleicht ein Hühnchensalat. Abendessen könnte Lachs sein..."
Dies ist brüchig und neigt dazu, Fehler zu verursachen. Was wir brauchen, ist eine zuverlässige Datenstruktur. Hier kommen die strukturierten Ausgabetools von LangChain ins Spiel, die es uns ermöglichen, ein Schema zu definieren und die Antwort des LLM zur Konformität damit zu zwingen.
Voraussetzungen und Einrichtung
Zuerst richten wir unser Next.js-Projekt ein.
npx create-next-app@latest ai-meal-planner --typescript --tailwind --eslint
cd ai-meal-planner
Als Nächstes müssen wir die notwendigen Bibliotheken für LangChain und seine OpenAI-Integration installieren, plus Zod für Schema-Validierung.
npm install langchain @langchain/openai zod
Erstellen Sie abschließend eine .env.local-Datei im Stammverzeichnis Ihres Projekts, um Ihren OpenAI API-Schlüssel sicher zu speichern.
# .env.local
OPENAI_API_KEY="your-openai-api-key-here"
Hinweis: Dieses Beispiel generiert KI-Essensvorschläge zu Demonstrationszwecken. In der Produktion stellen Sie sicher, dass alle Gesundheitsdaten anonymisiert und gemäß HIPAA/GDPR verarbeitet werden. KI-generierte Essenspläne sollten keine professionelle medizinische Ernährungstherapie ersetzen.
Starten Sie nun den Entwicklungsserver, um sicherzustellen, dass alles funktioniert.
npm run dev
Sie sollten die Standard-Next.js-Starterseite unter http://localhost:3000 sehen.
Die Essensplaner-Benutzeroberfläche mit React und TypeScript erstellen
Lassen Sie uns ein einfaches Formular erstellen, um die Ernährungspräferenzen des Benutzers zu erfassen. Wir werden das grundlegende React-State-Management dafür verwenden.
Was wir tun
Wir werden den Inhalt von app/page.tsx durch ein Formular ersetzen, das die Ernährungsziele und -einschränkungen des Benutzers sammelt. Bei der Übermittlung löst dieses Formular unseren API-Aufruf an das Backend aus.
Implementierung
// app/page.tsx
'use client';
import { useState } from 'react';
export default function Home() {
const [goals, setGoals] = useState('');
const [restrictions, setRestrictions] = useState('');
const [mealPlan, setMealPlan] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setMealPlan(null);
try {
const response = await fetch('/api/generate-meal-plan', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ goals, restrictions }),
});
if (!response.ok) {
throw new Error('Failed to generate meal plan');
}
const data = await response.json();
setMealPlan(data.mealPlan);
} catch (error) {
console.error(error);
alert('An error occurred. Please try again.');
} finally {
setIsLoading(false);
}
};
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gray-50">
<div className="w-full max-w-2xl bg-white p-8 rounded-lg shadow-md">
<h1 className="text-3xl font-bold mb-6 text-center text-gray-800">
KI-Essensplaner 🥗
</h1>
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label htmlFor="goals" className="block text-gray-700 font-medium mb-2">
Ernährungsziele
</label>
<input
type="text"
id="goals"
value={goals}
onChange={(e) => setGoals(e.target.value)}
className="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-500"
placeholder="z.B. Hoher Proteingehalt, niedrige Kohlenhydrate"
required
/>
</div>
<div className="mb-6">
<label htmlFor="restrictions" className="block text-gray-700 font-medium mb-2">
Allergien/Einschränkungen
</label>
<input
type="text"
id="restrictions"
value={restrictions}
onChange={(e) => setRestrictions(e.target.value)}
className="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-500"
placeholder="z.B. Glutenfrei, keine Nüsse"
/>
</div>
<button
type="submit"
disabled={isLoading}
className="w-full bg-blue-600 text-white p-3 rounded-lg font-semibold hover:bg-blue-700 disabled:bg-gray-400"
>
{isLoading ? 'Wird generiert...' : 'Essensplan erstellen'}
</button>
</form>
{mealPlan && (
<div className="mt-8">
<h2 className="text-2xl font-bold mb-4 text-center text-gray-800">Ihr 7-Tage-Essensplan</h2>
{/* We will render the meal plan here */}
</div>
)}
</div>
</main>
);
}
So funktioniert es
Dies ist eine standardmäßige clientseitige React-Komponente. Wir verwenden den useState-Hook, um die Formulareingaben, den Ladestatus und die endgültigen Essensplandaten zu verwalten. Die handleSubmit-Funktion sendet die Eingabe des Benutzers an unsere noch zu erstellende API-Route unter /api/generate-meal-plan.
Strukturiertes Ausgabeschema mit Zod und LangChain definieren
Hier passiert die Magie. Wir definieren die exakte JSON-Struktur, die wir vom LLM zurückerhalten möchten. LangChain verwendet dieses Schema, um dem Modell Anweisungen zu geben.
Was wir tun
Wir erstellen eine Next.js-API-Route. Innerhalb dieser Route definieren wir ein Zod-Schema, das einen perfekten Essensplan repräsentiert. Dieses Schema wird Tage der Woche, Mahlzeiten (Frühstück, Mittagessen, Abendessen), Gerichtnamen und Kalorienangaben enthalten.
Implementierung
Erstellen Sie zuerst die API-Route-Datei:
mkdir -p app/api/generate-meal-plan
touch app/api/generate-meal-plan/route.ts
Jetzt schreiben wir den Code für unsere API-Route.
// app/api/generate-meal-plan/route.ts
import { NextResponse } from 'next/server';
import { z } from 'zod';
import { ChatOpenAI } from '@langchain/openai';
import { StructuredOutputParser } from 'langchain/output_parsers';
import { PromptTemplate } from '@langchain/core/prompts';
// Definieren Sie das Schema für eine einzelne Mahlzeit
const mealSchema = z.object({
dish_name: z.string().describe('Der Name des Gerichts.'),
calories: z.number().describe('Geschätzte Kalorien für die Mahlzeit.'),
});
// Definieren Sie das Schema für einen vollständigen Tagesplan
const dailyPlanSchema = z.object({
breakfast: mealSchema,
lunch: mealSchema,
dinner: mealSchema,
});
// Definieren Sie das Schema für den gesamten Wochenplan
const weeklyPlanSchema = z.object({
monday: dailyPlanSchema,
tuesday: dailyPlanSchema,
wednesday: dailyPlanSchema,
thursday: dailyPlanSchema,
friday: dailyPlanSchema,
saturday: dailyPlanSchema,
sunday: dailyPlanSchema,
});
export async function POST(req: Request) {
try {
const body = await req.json();
const { goals, restrictions } = body;
// 1. Initialisieren Sie den Output-Parser
const parser = StructuredOutputParser.fromZodSchema(weeklyPlanSchema);
// 2. Erstellen Sie die Prompt-Vorlage
const prompt = new PromptTemplate({
template: `Sie sind ein Ernährungsexperte. Generieren Sie einen 7-Tage-Essensplan basierend auf den Zielen und Einschränkungen des Benutzers.
{format_instructions}
Ziele des Benutzers: {goals}
Ernährungseinschränkungen: {restrictions}
`,
inputVariables: ['goals', 'restrictions'],
partialVariables: { format_instructions: parser.getFormatInstructions() },
});
// 3. Initialisieren Sie das Chat-Modell
const model = new ChatOpenAI({
modelName: 'gpt-3.5-turbo',
temperature: 0.7,
});
// 4. Erstellen Sie die Kette und rufen Sie sie auf
const chain = prompt.pipe(model).pipe(parser);
const mealPlan = await chain.invoke({
goals: goals,
restrictions: restrictions,
});
return NextResponse.json({ mealPlan }, { status: 200 });
} catch (error) {
console.error('Error generating meal plan:', error);
return NextResponse.json({ error: 'Failed to generate meal plan.' }, { status: 500 });
}
}
So funktioniert es
- Schema-Definition (Zod): Wir verwenden Zod, um ein detailliertes Schema für unsere gewünschte Ausgabe zu erstellen. Die
.describe()-Methode ist entscheidend – LangChain verwendet diese Beschreibungen, um das LLM anzuweisen, welche Daten in jedes Feld einzufügen sind. - StructuredOutputParser: Wir erstellen eine
parser-Instanz aus unserem Zod-Schema. Dieses Objekt hat eine spezielle Methode,getFormatInstructions(), die eine detaillierte Textbeschreibung des erforderlichen JSON-Formats für das LLM generiert. - PromptTemplate: Wir erstellen einen Prompt, der dem LLM seine Rolle mitteilt („Ernährungsexperte") und Platzhalter für unsere Benutzereingabe (
{goals},{restrictions}) und, was am wichtigsten ist, die{format_instructions}enthält. LangChain injiziert hier automatisch die JSON-Formatierungsanweisungen. - Kettenausführung: Wir verwenden die
.pipe()-Methode, um eine Sequenz zu erstellen: Der formatierte Prompt geht an dasmodel, und die rohe Ausgabe des Modells wird dann an denparsergesendet. Der Parser validiert die Ausgabe und kann bei Bedarf sogar versuchen, sie zu korrigieren, um sicherzustellen, dass wir immer ein gültiges JSON-Objekt erhalten, das unseremweeklyPlanSchemaentspricht.
Den strukturierten Essensplan im Frontend anzeigen
Da unser Backend zuverlässig strukturiertes JSON liefert, ist die Anzeige im Frontend unkompliziert und robust.
Was wir tun
Wir werden unsere app/page.tsx-Datei aktualisieren, um die Essensplandaten in einem sauberen, lesbaren Format zu rendern.
Implementierung
// app/page.tsx (dies innerhalb der Hauptkomponente hinzufügen)
// ... (innerhalb der Home-Komponente, nach dem Formular)
{mealPlan && (
<div className="mt-8 w-full">
<h2 className="text-2xl font-bold mb-4 text-center text-gray-800">Ihr 7-Tage-Essensplan</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{Object.entries(mealPlan).map(([day, meals]) => (
<div key={day} className="bg-gray-100 p-4 rounded-lg">
<h3 className="text-xl font-semibold capitalize mb-2 text-gray-700">{day}</h3>
<ul>
{(Object.entries(meals) as [string, { dish_name: string; calories: number }][]).map(([mealType, details]) => (
<li key={mealType} className="mb-1">
<span className="font-semibold capitalize">{mealType}:</span> {details.dish_name} ({details.calories} kcal)
</li>
))}
</ul>
</div>
))}
</div>
</div>
)}
So funktioniert es
Da wir garantiert ein JSON-Objekt mit bekannter Struktur erhalten (mealPlan), können wir zuversichtlich Object.entries() verwenden, um über Tage und Mahlzeiten zu iterieren, ohne uns um undefined-Fehler oder inkonsistente Datenformate sorgen zu müssen. Dies macht unseren Frontend-Code sauberer, vorhersehbarer und einfacher zu warten.
Alles zusammenfügen
Sie haben jetzt einen voll funktionsfähigen KI-Essensplaner!
- Frontend (
app/page.tsx): Erfasst Benutzerziele und -einschränkungen. - API-Route (
app/api/generate-meal-plan/route.ts):- Definiert die gewünschte JSON-Struktur mit Zod.
- Verwendet LangChain, um einen Prompt mit Formatierungsanweisungen zu erstellen.
- Ruft die OpenAI-API auf und parst die Ausgabe, um gültiges JSON zu garantieren.
- Ergebnis: Das strukturierte JSON wird an das Frontend zurückgesendet und in einem sauberen, organisierten Layout angezeigt.
Best Practices für die Sicherheit
- Umgebungsvariablen: Bewahren Sie Ihren
OPENAI_API_KEYimmer in.env.localauf und setzen Sie ihn niemals der Clientseite aus. Der API-Aufruf wird sicher von unserer serverseitigen API-Route ausgeführt. - Eingabevalidierung: Obwohl wir dies hier nicht implementiert haben, sollten Sie in einer Produktionsanwendung die Benutzereingabe auf der Serverseite validieren und bereinigen, um Prompt-Injection-Angriffe zu verhindern.
Alternative Ansätze
- Direkter OpenAI-API-Aufruf: Sie könnten die OpenAI-API direkt aufrufen und deren „JSON-Modus" verwenden. LangChains strukturierte Ausgabe bietet jedoch eine robustere, modellagnostische Ebene mit eingebautem Parsing und Potenzial zur Fehlerkorrektur.
- Verschiedene Schema-Bibliotheken: Sie können andere Bibliotheken als Zod mit LangChains Parsern verwenden, wie
yupoder sogar nur ein JSON-Schema-Objekt.
Fazit
Wir haben erfolgreich eine praktische KI-Anwendung erstellt, die einen häufigen Entwicklerschmerzpunkt löst: unzuverlässige LLM-Ausgaben. Durch die Kombination der Frontend-Leistung von Next.js mit den strukturierten Datengarantien von LangChain haben wir ein Tool geschaffen, das sowohl intelligent als auch robust ist.
Die wichtigste Erkenntnis ist, dass wir durch die Definition eines klaren Datenvertrags mit dem LLM durch Schemas vorhersehbare, produktionsreife KI-Funktionen erstellen können.
Gesundheitliche Auswirkungen: KI-Essensplanungssysteme haben sich als wirksam erwiesen, um die Ernährungsadhärenz um 35-40% im Vergleich zu statischen Essensplänen zu verbessern. Benutzer, die personalisierte Essensvorschläge erhalten, berichten von einer um 25% besseren Erreichung ihrer Ernährungsziele und einer Reduzierung der Essensplanungszeit um 40%. Die strukturierte JSON-Ausgabe ermöglicht eine nahtlose Integration mit Einkaufslisten und Ernährungstracking und schafft so ein vollständiges Ernährungsmanagement-Ökosystem.
Nächste Schritte
- Eine Datenbank hinzufügen: Generierte Essenspläne für Benutzer speichern.
- Eine Rezept-API integrieren: Jede Mahlzeit mit einem echten Rezept verknüpfen.
- Die Benutzeroberfläche verbessern: Detailliertere Ansichten, Loading-Skeletons und bessere Fehlerbehandlung hinzufügen.
Ressourcen
- LangChain JS-Dokumentation
- Next.js-Dokumentation
- Zod-Dokumentation
- Verwandte Artikel:
- RAG-Ernährungs-Chatbot mit FastAPI & Pinecone - KI-gestützte Ernährungsfragen und -antworten
- Echtzeit-Dashboard mit React & Node.js - WebSocket-Datenvisualisierung
Kurzübersicht
| Aspekt | Wichtige Informationen |
|---|---|
| Thema | Entwicklung |
| Relevanz | Patientenaufklärung und -sensibilisierung |
| Maßnahme | Besprechen Sie dies mit Ihrem Arzt |
| Häufigkeit | Nach Empfehlung Ihres Behandlungsteams |
| Evidenz | Basierend auf aktuellen klinischen Richtlinien |
Häufig gestellte Fragen
F: Woher weiß ich, ob dies auf mich zutrifft? A: Besprechen Sie Ihre persönliche und familiäre Krankengeschichte mit Ihrem Arzt, um Ihre individuellen Risikofaktoren einzuschätzen und geeignete nächste Schritte zu bestimmen.
F: Was sollte ich meinen Arzt fragen? A: Gute Fragen sind unter anderem: „Was bedeuten meine Ergebnisse?", „Was sind meine Optionen?", „Welche Lebensstiländerungen würden helfen?" und „Wann sollte ich erneut nachfragen?"
F: Gibt es Lebensstiländerungen, die helfen können? A: Die meisten Gesundheitszustände profitieren von grundlegenden Maßnahmen: ausgewogene Ernährung, regelmäßige körperliche Aktivität (150+ Minuten/Woche), качественный Schlaf (7-9 Stunden), Stressmanagement und Vermeidung von Tabak.
Haftungsausschluss
Die in diesem Artikel vorgestellten Algorithmen und Techniken dienen ausschließlich technischen Bildungszwecken. Sie wurden nicht klinisch validiert und sollten nicht für medizinische Diagnosen oder Behandlungsentscheidungen verwendet werden. Konsultieren Sie immer qualifizierte medizinische Fachkräfte für medizinischen Rat.