CodeGym /Kurse /ChatGPT Apps /Speichern und Wiederherstellen des Kontexts zwischen Schr...

Speichern und Wiederherstellen des Kontexts zwischen Schritten

ChatGPT Apps
Level 11 , Lektion 2
Verfügbar

1. Was ist der Workflow‑Kontext und warum braucht man ihn überhaupt

In einer typischen Web‑Anwendung haben Sie eine recht klare Vorstellung davon, wo der Zustand lebt: Datenbank, Cache, plus etwas im Frontend wie Redux oder lokaler React‑State. In der ChatGPT‑App ist es spannender: Der Zustand ist über drei Welten verteilt – innerhalb des Modells (Chatverlauf), im Widget (UI‑State) und auf Ihrem Server/MCP (Geschäftsdaten).

Unter dem Workflow‑Kontext verstehen wir die Gesamtheit der Daten, die nötig sind, um die Fragen „Bei welchem Schritt sind wir?“ und „Was ist bereits bekannt?“ zu beantworten. Bezogen auf unser Lernprojekt GiftGenius gehören zum Kontext:

  • Profil der zu beschenkenden Person: Alter, Geschlecht, Interessen;
  • Budget und ggf. Währung;
  • Liste der generierten Ideen und welche davon der Nutzer geliked oder ausgeblendet hat;
  • technische Dinge: Sitzungs‑ oder Workflow‑Kennung, Status („profile_collected“, „ideas_shown“, „checkout_started“).

Dieser Kontext wird nicht nur von Ihnen als Backend‑Entwickler benötigt. Er wird auch vom Modell benötigt, damit es versteht, welche Fragen bereits gestellt wurden, welche Tools schon aufgerufen wurden und worum es aktuell überhaupt geht. Und er wird vom Nutzer benötigt, um beim Zurückkehren in den Chat nicht bei null zu starten.

Nutzer denken intuitiv, dass „ChatGPT sich alles merkt“. Tatsächlich merkt sich das Modell nur den Text des Dialogs, solange er ins Kontextfenster passt. Strukturierte Dinge wie order_id, cart_id oder „Liste gelikter Ideen“ müssen Sie auf Ihrem Server speichern, sonst erhalten Sie eine perfekte Maschine zur Erzeugung selbstsicherer, aber falscher Aussagen.

2. Drei Ebenen des Zustands: UI, LLM, Business

Am einfachsten versteht man das Speichern des Kontexts über ein Modell aus drei Zustandsebenen. Auch bekannt als „State Triad“.

Tabelle der Ebenen

Wir verwenden eine kleine Tabelle:

Ebene Wo lebt sie Lebenszyklus Wofür zuständig Beispiel in GiftGenius
UI State Widget (React, widgetState) Solange der Chat/die Nachricht mit dem Widget geöffnet ist Visueller Zustand, lokale Eingaben Welche Karten hervorgehoben sind, Formularzustand
LLM Context Chatverlauf in OpenAI Solange die Nachricht ins Kontextfenster passt Verständnis des Dialogs und Reasoning „Wir suchen ein Geschenk für Mama, Budget 50 $“
Business State MCP / Ihr Backend (DB/Redis) So lange, wie Sie möchten (persistent) Quelle der Wahrheit: validierte Daten, Status { step: "ideas", budget: 50, liked: [42, 51] }

Die UI‑Ebene ist schnell und reaktiv, aber sehr fragil: ChatGPT kann das iframe mit dem Widget „unmounten“, wenn Sie im Verlauf nach oben scrollen, und später erneut mounten. Genau dafür gibt es widgetState, das etwas länger lebt als die React‑Komponente und mit dem ChatGPT‑Host‑Client synchronisiert wird.

Die LLM‑Ebene vermittelt dem Modell das Gefühl eines kontinuierlichen Dialogs, speichert aber nur Text und Tool‑Aufrufe. Man kann dort JSON mit Ihrem Warenkorb ablegen, aber das ist im Grunde nur JSON im Text – das Modell behandelt das nicht wie eine Datenbank.

Die Business‑Ebene ist das, was Sie als Engineer kontrollieren können: Dort liegen validierte Daten, Indizes, Bestellstatus. Sobald Sie ein ernsthaftes Szenario haben (Geschenke, Buchungen, Lernen), sollte genau diese Ebene zur maßgeblichen Quelle der Wahrheit über den Zustand werden.

Das zentrale Engineering‑Problem ist, zu verhindern, dass diese drei Ebenen in unterschiedliche Richtungen driften. Der Nutzer hat im Widget das Budget geändert, das Modell denkt noch an das alte, und in der Datenbank steht ein dritter Wert – klassisches Rezept für seltsames Verhalten.

3. Was genau speichern wir: die Struktur von WorkflowContext

Um konkret zu werden, beschreiben wir in TypeScript ein Interface für den Kontext von GiftGenius. Angenommen, wir haben bereits mehrere Schritte: Profilerfassung, Budgetwahl, Ideengenerierung und Ansicht/Likes.

Beginnen wir mit einer einfachen Struktur:

// backend/types/workflow.ts
export type GiftWorkflowStep =
  | "profile"
  | "budget"
  | "ideas"
  | "checkout";

export interface GiftWorkflowContext {
  id: string;              // workflowId – Kennung des Workflows
  userId?: string;         // falls Authentifizierung bereits eingerichtet ist
  currentStep: GiftWorkflowStep;
  profile?: {
    age?: number;
    gender?: string;
    interests?: string[];
  };
  budget?: {
    min?: number;
    max?: number;
    currency: string;
  };
  ideas?: {
    id: string;
    title: string;
  }[];
  likedIdeaIds: string[];
  hiddenIdeaIds: string[];
  updatedAt: number;       // Zeitstempel für TTL/Bereinigung
}

Das ist kein finales Schema, aber die wichtigen Elemente sind bereits vorhanden. Es gibt:

  • eine Workflow‑Kennung, unter der wir diesen Kontext finden;
  • den aktuellen Schritt, der sowohl dem Widget als auch dem Modell hilft zu verstehen, wo wir stehen;
  • eine Menge von Feldern, die auf einzelnen Schritten befüllt werden;
  • Servicefelder wie den Zeitpunkt der Aktualisierung.

Ein Wort zu Kennungen. In dieser Vorlesung verstehen wir unter workflowId die Kennung eines konkreten Workflows innerhalb unseres Backends/MCP. Sie kann mit der ChatGPT‑Dialog‑Sitzungskennung (sessionId) übereinstimmen, aber wir verlassen uns nicht darauf. userId ist die Benutzerkennung aus Ihrem Authentifizierungssystem (falls vorhanden); ein Benutzer kann mehrere aktive Workflows haben. Im Feld id liegt genau dieses workflowId, nach dem wir den Kontext suchen und ihn aktualisieren.

In den nächsten Abschnitten klären wir drei Dinge: wo solche Objekte gespeichert werden, wie sie dorthin geschrieben werden und wie man sie wieder herausholt – sowohl ins Widget als auch ins Modell.

4. Wo den Zustand speichern: Optionen und Kompromisse

Über das Speichern des Zustands kann man in zwei Dimensionen nachdenken: wo er liegt und wie lange er lebt. In diesem Abschnitt konzentrieren wir uns auf den Speicherort, auf die Lebensdauer kommen wir im Check‑listen‑Abschnitt und im Block zu typischen Fehlern zurück.

Zunächst klären wir den Speicherort.

Im Dialog (im Prompt)

Manchmal möchte man sagen: „Geben wir dem Modell einfach jedes Mal JSON mit dem aktuellen Zustand zurück, und es soll selbst damit umgehen.“ Das funktioniert für sehr einfache Szenarien und kurze Schrittketten, stößt aber schnell auf zwei Probleme: Begrenzung der Kontextlänge und fehlende Garantien hinsichtlich der Datenintegrität.

Außerdem ist das MCP‑Protokoll von Natur aus stateless: Wie HTTP speichert es standardmäßig keinen Zustand zwischen Anfragen. Um einen Tool‑Aufruf an eine konkrete Sitzung zu binden, müssen Sie explizit eine Kennung – workflow oder session id – entweder in den Tool‑Argumenten oder über Metadaten/Headers übergeben.

Business‑State ausschließlich im Dialog zu speichern, ist daher eher ein Lehr‑Experiment als eine Architektur.

Im Widget: UI + widgetState

Auf UI‑Ebene verwenden wir den üblichen React‑State (useState, useReducer usw.), aber wie bereits erwähnt kann die Komponente demontiert werden. Im Apps SDK gibt es dafür den Mechanismus widgetState, der außerhalb von React lebt und mit dem ChatGPT‑Host synchronisiert wird. Wenn Sie beim Mounten des Widgets einen gespeicherten Wert von dort holen und bei Änderungen wieder ablegen, erhalten Sie einen lokalen, aber recht bequemen Speicher.

Dieser Speicher eignet sich hervorragend für rein visuelle Zustände: welche Karten gerade eingeklappt sind, in welchem Tab Sie sich befinden, was der Nutzer in ein Formular eingegeben hat, bevor er auf „Weiter“ geklickt hat. Er ersetzt jedoch nicht den Server: Sobald der Nutzer den Chat auf einem anderen Gerät öffnet oder nach einer Woche zurückkommt, hilft widgetState möglicherweise nicht mehr. Und darauf Business‑Logik zu bauen, ist fragwürdig.

Auf dem Server/MCP: Map, Redis, DB

Schließlich die zentrale Produktionsvariante: Wir speichern den GiftWorkflowContext auf der MCP‑Server‑ oder Backend‑Seite. Da MCP‑Client und ‑Server per Protokoll stateless sind, müssen wir bei jedem Tool‑Aufruf die workflowId (oder ein state_token) mitgeben, um zu wissen, welchen Kontext wir aktualisieren.

Es gibt mehrere Implementierungsoptionen:

  • In‑Memory‑Map in Node.js – geeignet für Demos und Dev‑Umgebungen: alles schnell, aber verschwindet beim Neustart;
  • Redis oder ein anderer In‑Memory‑Cache mit TTL – gut für kurze Wizard‑Szenarien (Assistenten mit wenigen Schritten): lebt ein, zwei Stunden, danach kann gelöscht werden;
  • normale SQL/NoSQL‑Datenbank – Pflicht für Szenarien wie „Rückkehr nach einer Woche“ oder „Entwürfe und Warenkörbe“.

In dieser Vorlesung gehen wir nicht tief auf eine konkrete Datenbank ein, sondern konzentrieren uns auf das Interface und darauf, was dort landen sollte.

5. Einfachster Storage im MCP‑Server: Map nach workflowId

Beginnen wir mit etwas Handfestem: einer einfachen In‑Memory‑Map im MCP‑Server, bei der der Schlüssel die workflowId ist. Im Lern‑Demo kann man sie schlicht der sessionId des Dialogs gleichsetzen, aber in der Produktion sollten Sie die workflowId als separate Workflow‑Kennung führen. Der Wert in dieser Map ist der GiftWorkflowContext. In der echten Produktion ersetzen Sie das durch Redis oder eine DB, das API bleibt jedoch dasselbe.

Angenommen, wir haben einen MCP‑Server in TypeScript. Fügen wir in der Nähe der Initialisierung Folgendes hinzu:

// mcp/workflowStore.ts
import { GiftWorkflowContext } from "../backend/types/workflow";

const workflows = new Map<string, GiftWorkflowContext>();

export function getWorkflow(id: string): GiftWorkflowContext | undefined {
  return workflows.get(id);
}

export function saveWorkflow(ctx: GiftWorkflowContext): void {
  workflows.set(ctx.id, { ...ctx, updatedAt: Date.now() });
}

Als Nächstes – ein Tool, das das Profil der zu beschenkenden Person speichert. Wichtig ist, dass es die workflowId und die Profildaten entgegennimmt und intern den entsprechenden Kontext aktualisiert/erstellt:

// mcp/tools/setProfile.ts
import { jsonSchema } from "@modelcontextprotocol/sdk"; // Alias
import { getWorkflow, saveWorkflow } from "../workflowStore";

export const setProfileTool = {
  name: "gift_set_profile",
  description: "Speichert das Profil der zu beschenkenden Person",
  inputSchema: jsonSchema.object({
    workflowId: jsonSchema.string(),
    age: jsonSchema.number().optional(),
    gender: jsonSchema.string().optional(),
    interests: jsonSchema.array(jsonSchema.string()).optional()
  }),
  async run(input: any) {
    const existing = getWorkflow(input.workflowId);
    const ctx = existing ?? {
      id: input.workflowId,
      currentStep: "profile",
      likedIdeaIds: [],
      hiddenIdeaIds: []
    };
    ctx.profile = {
      age: input.age,
      gender: input.gender,
      interests: input.interests ?? []
    };
    ctx.currentStep = "budget";
    saveWorkflow(ctx);
    return {
      structuredContent: {
        type: "profileSaved",
        workflowId: ctx.id,
        profile: ctx.profile,
        nextStep: ctx.currentStep
      }
    };
  }
};

Dieses Tool löst bereits zwei Aufgaben: Es speichert das Profil und schiebt currentStep auf den nächsten Schritt. In einem realen Projekt möchten Sie die Tools „Daten speichern“ und „zum Schritt wechseln“ möglicherweise trennen, aber zum Verständnis des Konzepts genügt diese Variante.

Achten Sie auf die workflowId in den Argumenten: Genau dieser Parameter verknüpft den Tool‑Aufruf mit dem benötigten Kontext. Der Client‑Teil (Widget oder Agent) muss ihn irgendwo speichern und mitgeben.

6. Anbindung an das Apps SDK: Woher bekommt man workflowId und sessionId

Die Frage „Woher bekommt man die workflowId?“ ist in ChatGPT Apps ein wenig philosophisch. Die Möglichkeiten hängen davon ab, ob Sie Authentifizierung, MCP direkt oder das Agents SDK verwenden. Im Allgemeinen sind die Varianten: Generierung auf der Serverseite beim ersten Tool‑Aufruf oder Generierung im Widget und Übergabe nach unten.

Für das Lernbeispiel nehmen wir an, dass der erste Schritt ein MCP‑Tool ist, das den Workflow erstellt, und das Widget danach nur dessen ID übernimmt.

Die einfachste Variante:

// mcp/tools/startWorkflow.ts
import { randomUUID } from "crypto";
import { saveWorkflow } from "../workflowStore";

export const startWorkflowTool = {
  name: "gift_start_workflow",
  description: "Erstellt einen neuen Geschenk‑Workflow",
  inputSchema: { type: "object", properties: {} },
  async run() {
    const id = randomUUID();
    saveWorkflow({
      id,
      currentStep: "profile",
      likedIdeaIds: [],
      hiddenIdeaIds: [],
      updatedAt: Date.now()
    });
    return {
      structuredContent: {
        type: "workflowStarted",
        workflowId: id,
        currentStep: "profile"
      }
    };
  }
};

Nachdem das Modell die workflowId in der Tool‑Antwort erhalten hat, kann es:

  • sie im Hintergrund im Kontext mitführen;
  • sie über structuredContent an das Widget übergeben, damit das Widget den Wert in widgetState speichert und bei weiteren Tool‑Aufrufen mitsendet.

Auf Widget‑Seite sieht der Code ungefähr so aus.

7. Speichern von workflowId und lokalem UI‑Zustand im Widget

Angenommen, wir haben ein Ideen‑Listen‑Widget, das wissen möchte, welchen Workflow es darstellt, und lokale Likes behalten soll, selbst wenn die Komponente demontiert wird. In vereinfachter Form:

// app/widgets/GiftIdeasWidget.tsx
import { useEffect, useState } from "react";

interface Idea {
  id: string;
  title: string;
}

interface WidgetProps {
  widgetId: string;
  workflowId: string;   // kam aus structuredContent
  ideas: Idea[];
}

interface UiState {
  liked: string[];
}

export function GiftIdeasWidget(props: WidgetProps) {
  const [uiState, setUiState] = useState<UiState>({ liked: [] });

  useEffect(() => {
    window.openai.getWidgetState<UiState>(props.widgetId).then(saved => {
      if (saved) setUiState(saved);
    });
  }, [props.widgetId]);

  function toggleLike(id: string) {
    const exists = uiState.liked.includes(id);
    const next: UiState = {
      liked: exists
        ? uiState.liked.filter(x => x !== id)
        : [...uiState.liked, id]
    };
    setUiState(next);
    window.openai.setWidgetState(props.widgetId, next);
    // Hier könnte man auch das MCP‑Tool "gift_like_idea" aufrufen
  }

  return (
    <ul>
      {props.ideas.map(idea => (
        <li key={idea.id}>
          {idea.title}
          <button onClick={() => toggleLike(idea.id)}>
            {uiState.liked.includes(idea.id) ? "★" : "☆"}
          </button>
        </li>
      ))}
    </ul>
  );
}

Hier wird widgetState genau als UI‑Ebene verwendet: Wir merken uns, welche Ideen hervorgehoben sind. Eigentlich sollten Likes zusätzlich an den Server gesendet werden (über ein MCP‑Tool oder einen API‑Endpunkt in Next.js), damit auch die Business‑Ebene weiß, was der Nutzer ausgewählt hat.

Wichtig ist, nicht zu versuchen, den gesamten Workflow auf widgetState aufzubauen. Er sollte eine Ergänzung zum Business‑Kontext auf dem Server sein.

8. Workflow wieder aufnehmen: Nutzer ist zurückgekehrt

Kommen wir zu einem interessanteren Fall: Der Nutzer hat ChatGPT geschlossen, kommt nach ein paar Stunden oder Tagen zurück und öffnet denselben Chat erneut. Was sollte passieren?

Das ideale UX: Modell und App erkennen, dass der Nutzer bereits einen unvollendeten Workflow hat, ziehen dessen Kontext nach und sagen so etwas wie: „Sie haben bereits Profil und Budget angegeben, machen wir mit der Ideenauswahl weiter.“

Architektonisch sieht das so aus:

  1. Auf Ihrem Server wird ein GiftWorkflowContext gespeichert, der an eine bestimmte userId oder zumindest an eine interne workflowId gebunden ist.
  2. Bei einer neuen Anfrage (oder dem ersten Tool‑Aufruf innerhalb des Dialogs) fragt die App den Server: „Gibt es für diesen Nutzer einen aktiven Workflow?“
  3. Wenn ja, gibt der Server ihn zurück und ggf. ein spezielles Flag resume, das das Modell in seiner Antwort nutzt.

In einem einfachen monolithischen Demo kann man davon ausgehen, dass MCP‑Server und Next.js‑Applikation im selben Repository (oder sogar Prozess) laufen, daher nutzen wir denselben workflowStore sowohl aus MCP als auch in den API‑Routen.

In Next.js kann das eine einfache API‑Route sein:

// app/api/gift/workflow/route.ts
import { NextRequest, NextResponse } from "next/server";
import { getWorkflow } from "@/mcp/workflowStore"; // In diesem Demo teilen sich MCP und Next.js denselben Speicher

export async function GET(req: NextRequest) {
  const id = req.nextUrl.searchParams.get("workflowId");
  if (!id) return NextResponse.json({ error: "Missing workflowId" }, { status: 400 });

  const ctx = getWorkflow(id);
  if (!ctx) return NextResponse.json({ exists: false });

  return NextResponse.json({
    exists: true,
    context: ctx
  });
}

Das Widget (oder ein MCP‑Tool) kann diesen Endpunkt aufrufen, wenn es seinen Zustand aktualisieren muss: z. B. beim ersten Mounten oder beim Wechsel des Schritts. In der Lernkonfiguration reicht die Kombination aus workflowId + Storage in der Map; in der echten Produktion fügen Sie Authentifizierung und Besitzprüfung hinzu.

Wenn Sie das Agents SDK oder eine komplexere Orchestrierung verwenden, können Sie die Idee zu „Checkpoints“ ausbauen – das Speichern des Zustands an den Enden größerer Schritte, von denen der Agent bei einem Neustart fortsetzen kann. Das ist allerdings Thema des nächsten Moduls.

9. Vor‑/Zurück‑Navigation und Schritthistorie

Unweigerlich stellt sich die Frage: „Kann man einen Schritt zurückgehen?“ Für den Nutzer ist das sehr naheliegend: Budget ändern, Interessen korrigieren, ein überflüssiges Produkt aus der Auswahl entfernen.

Technisch bedeutet das zweierlei:

  • Sie müssen nicht nur den aktuellen Schritt, sondern auch die Historie der getroffenen Entscheidungen speichern;
  • Sie müssen abgeleitete Daten nach einem Rollback sorgfältig neu berechnen.

Eine Möglichkeit – dem Kontext ein Feld history hinzuzufügen, das Schnappschüsse der Schritte enthält. Zum Beispiel:

export interface StepSnapshot {
  step: GiftWorkflowStep;
  payload: any;          // konkrete Daten des Schritts
  createdAt: number;
}

export interface GiftWorkflowContext {
  // ...vorherige Felder
  history: StepSnapshot[];
}

Wenn der Nutzer das Profil ausfüllt, fügen Sie der Historie einen Schnappschuss mit step "profile" hinzu. Wenn er das Budget ändert – einen weiteren Schnappschuss. Beim Zurückspringen zum Profil:

  • aktualisieren Sie currentStep = "profile";
  • schneiden Sie optional die Historie bis zum benötigten Index ab;
  • berechnen Sie abgeleitete Werte neu (z. B. Ideen und Likes leeren, wenn sie vom Budget abhängen).

Auf Modellebene ist Synchronisation wichtig: Wenn der Nutzer im Widget auf „Zurück“ klickt, sollte ein Tool‑Aufruf ausgelöst werden, der den Business‑Kontext aktualisiert und in der Antwort eine explizite Beschreibung des neuen Zustands zurückgibt. Andernfalls bekommen Sie die klassische Desynchronisation: UI zeigt Schritt 2, das Modell ist sicher, dass Sie bei Schritt 3 sind.

Auf Widget‑Ebene kann der Rücksprung wie ein einfacher Button aussehen:

async function goBackToProfile() {
  await fetch("/api/gift/workflow/back", {
    method: "POST",
    body: JSON.stringify({ workflowId, targetStep: "profile" })
  });
  // UI aktualisieren, lokalen State bereinigen
}

Der Server entscheidet dann, was im Kontext genau bereinigt wird und welche Nachricht über die Tool‑Antwort an das Modell gesendet wird.

10. Wie das alles mit dem Modell verbinden: Kontext für das Reasoning

Alles, was wir mit dem State tun, wird letztlich nicht nur vom Nutzer, sondern auch vom LLM benötigt. Das Modell sollte verstehen:

  • was bereits bekannt ist (z. B. Profil der zu beschenkenden Person und Budget);
  • welche Schritte bereits durchlaufen wurden;
  • ob es unvollendete Prozesse gibt.

Wie diese Informationen beim Modell ankommen, hängt von der Architektur der App ab: Sie können sie in den System‑Prompt injizieren, in ToolOutput strukturiert zurückgeben oder spezielle Felder _meta/Annotations verwenden, sofern sie vom SDK unterstützt werden.

Typisches Muster:

  1. Das MCP‑Tool gibt in structuredContent einen kurzen Schnappschuss des Kontexts zurück: aktueller Schritt, Schlüsselfelder und ggf. workflowId.
  2. Das Apps SDK verwandelt dies in ein Widget oder Text + versteckte Daten.
  3. Das Modell erkennt anhand des structuredContent, dass der Workflow fortgesetzt wurde, und plant die nächste Aktion entsprechend.

In manchen Fällen, wenn das Modell wichtige Parameter „vergessen“ hat oder zu halluzinieren beginnt, können Sie den Kontext gezielt aktualisieren: Ein spezielles Tool aufrufen, das den aktuellen Zustand zurückgibt – das Modell „ist wieder im Kontext“.

Wichtig ist, nicht zu versuchen, den gesamten GiftWorkflowContext bis zum letzten Feld ins Modell zu stopfen. Es reichen die Schlüsselaspekte: Für wen suchen wir ein Geschenk, welches Budget, wie viele Ideen wurden bereits gezeigt, gibt es einen unvollendeten Checkout.

11. Mini‑Checkliste beim Entwurf des WorkflowContext

Bevor wir zu den typischen Fehlern übergehen, ist es hilfreich, ein kleines Set von Fragen zu formulieren, die Sie sich beim Entwurf des Workflow‑Kontexts beantworten sollten (Sie können das wörtlich neben das Interface schreiben):

  • Welche Schritte hat der Workflow und welcher minimale Datensatz wird pro Schritt benötigt?
    Das schützt vor riesigen JSON‑Monstern „für alle Fälle“.
  • Was muss nur innerhalb eines Chats erinnert werden und was über Sitzungen und Geräte hinweg?
    Ersteres kann in widgetState und Prompts bleiben, Letzteres muss zwingend in die Server‑Datenbank.
  • Wie sieht die Kennung des Kontexts aus?
    Das kann eine Kombination aus userId + scenario, eine separate workflowId oder beides sein. Hauptsache, Sie können den Kontext in der DB eindeutig finden.
  • Wie bereinigen Sie alte Workflows?
    Für Demos ist „nie bereinigen“ tolerierbar, aber in der Produktion brauchen Sie entweder TTL oder Hintergrund‑Jobs, die alte Workflows löschen.
  • Braucht der Nutzer einen Rücksprung und wie setzen Sie ihn um?
    Speichern Sie einen verzweigten Baum oder reicht eine lineare Schrittliste mit Rücksprungmöglichkeit?

Und zuletzt: Spielen Sie im Kopf das Szenario „Der Nutzer kommt nach einer Woche in einem anderen Chat zurück“ durch. Wenn Sie nicht erklären können, wie die App vom alten Workflow erfährt und was sie anzeigen soll, müssen Sie den Teil mit persistentem Speicher stärken.

12. Typische Fehler beim Umgang mit Kontext zwischen Schritten

Fehler Nr. 1: alles nur im Chatverlauf speichern.
Manchmal ist die Versuchung groß: „Das Modell sieht doch alles im Text, listen wir im Prompt einfach jedes Mal Budget, Produkte und die Auswahl des Nutzers auf.“ Dieser Ansatz stößt schnell an Kontextlimits und bietet keinerlei Integritätsgarantien: Das Modell kann einen wichtigen Fakt „vergessen“ oder Kennungen verwechseln. Geschäftskritische Dinge (Geld, Buchungen, Bestellungen) müssen in Ihrem Backend/MCP als Quelle der Wahrheit leben.

Fehler Nr. 2: der Versuch, den gesamten Workflow ausschließlich auf widgetState aufzubauen.
widgetState im Apps SDK ist dafür gedacht, UI‑Zustand zwischen Unmount und Remount des Widgets zu erhalten, nicht für die langfristige Speicherung eines Workflows. Wenn Sie dort Profil, Warenkorb und Schritthistorie ablegen, führt das bei Gerätewechseln zu Chaos und macht eine Wiederherstellung nach längerer Zeit unmöglich. Das Widget ist für visuelle Details und lokalen Komfort zuständig. Die gesamte Szenariologik gehört auf den Server.

Fehler Nr. 3: fehlende explizite workflowId oder ein anderer Schlüssel.
Mitunter verlässt man sich auf implizite Kennungen wie conversation_id, führt aber kein eigenes Workflow‑Konzept ein. Dadurch wird es unmöglich, einen Workflow vom anderen zu unterscheiden, mehrere parallele Workflows zu trennen oder genau den wiederherzustellen, den man benötigt. Ein einfaches String‑Feld workflowId überall dort, wo es Tools und API‑Endpunkte gibt, löst viele Probleme – insbesondere in MCP, das per Protokoll stateless ist.

Fehler Nr. 4: Vermischung von UI‑State und Business‑Logik.
Klassische Situation: Im widgetState landet nicht nur „welcher Tab ist offen“, sondern auch „welche Produkte sind im Warenkorb“, und anschließend versucht man, auf Basis dieses States serverseitige Entscheidungen zu treffen. Bei der kleinsten Desynchronisation (Widget gerendert, aber Anfrage noch nicht angekommen – oder umgekehrt) sieht das Modell eine Realität, die UI eine andere und die Datenbank eine dritte. Die Verantwortungsgrenze muss klar sein: Der Server speichert und validiert Geschäftsdaten, das Widget zeigt sie an und bietet dem Nutzer eine bequeme Möglichkeit, sie zu ändern.

Fehler Nr. 5: kein Wiederherstellungs‑ und Rollback‑Szenario.
Es ist leicht, einen schönen „Happy Path“ zu zeichnen, in dem der Nutzer perfekt Schritt für Schritt vorgeht, nichts kaputtgeht, ChatGPT nicht neu lädt und die Verbindung nicht abreißt. In der Realität kann jeder Schritt scheitern, der Nutzer kann mittendrin gehen und nach einer Woche zurückkehren. Wenn Sie keine Struktur für den WorkflowContext vorgesehen, keinen Plan zur Suche nach „aktiven“ Workflows und keine Buttons „Zurück“ und „Später fortsetzen“ eingeplant haben, wird Ihr Szenario fragil und für Nutzer frustrierend. Ein durchdachter Kontext ist die Grundlage für Ausfallsicherheit, über die wir in der nächsten Vorlesung sprechen werden.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION