Enterprise Briefing

Technischer Deep Dive für Arbeitgeber – Narraboros Engine.

Narraboros ist eine neuro-symbolische Engine für interaktive Literatur mit einer strengen Architekturregel: Laravel und MySQL besitzen die Zustands- und Logikhoheit, das Sprachmodell ist ein zustandsloser Renderer. Das LLM erhält pro Zug einen neu zusammengestellten, relationalen Weltzustands-Snapshot als Single-Shot-Prompt – es gibt keinen akkumulierenden Chatverlauf. Das Modell kann Weltzustände ausschließlich über strukturierte, schema-gebundene Tool-Calls vorschlagen; die Übernahme in die Datenbank erfolgt über das Backend. Die Grenze ist ehrlich mitzudenken: Konsistenz hängt weiterhin von der Auswahl der Kontextdaten, den Backend-Prüfungen und dem Verhalten des Modells ab. Die Architektur macht Zustandsfehler aber sichtbar, überprüfbar und korrigierbar, statt sie im Prompt zu verstecken.
Briefing

Turn-Lifecycle im Detail

Getrennte Laravel-Pipelines bereiten den Kontext vor und verarbeiten die Modellantwort nach.

Sequenzdiagramm eines Spielzugs Der Spieler sendet eine Aktion. Der PromptBuilder rendert statische Kernregeln und durchläuft die dynamische Pipeline. Der Orchestrator ruft das LLM als Single-Shot mit Tool-Schema auf, verarbeitet die strukturierte Antwort in der Nachverarbeitungspipeline und übernimmt die Zustandsupdates in MySQL. Client PromptBuilder Orchestrator LLM (stateless) MySQL Spieleraktion Statische Kernregeln + dynamische Pipeline: 12 Stufen + Cleanup System-Prompt + relationaler Snapshot Single-Shot-Call mit Tool-Schema Prosa + strukturierte Zustandsmeldungen Nachverarbeitungspipeline: Prüfung, Updates, Runde, Telemetrie, History Zustandsupdates (updateOrCreate / delete) Ausgabe

Die Orchestrierung ist vollständig per Ereignis-System erweiterbar: Dritt-Code kann Pipeline-Stages vor dem Aufruf ergänzen oder umordnen, ohne den Kern zu ändern.

Prompt-Architektur

Statischer Teil

Unveränderliche Regie-Regeln (Erzähler-Haltung, Pacing-Protokolle, Agency-Split, Zustandsformate). Pro Anfrage aus einer Blade-View gerendert und als System-Instruktion übergeben; laut Code-Dokumentation durch Kompaktierung etwa halbiert.

Dynamischer Teil

Pro Zug wird der Snapshot über eine geordnete Pipeline von Stages aufgebaut: PII-Scrub, Purge volatile States, History, Config, Area-Location, Faktvalidierung, Charaktertabellen, aktive Szene, Story-Engine, Beziehungen, Memory-System, RNG-Block – abschließend ein Cleanup-Schritt.

Auszug aus der Produktions-Codebasis (PromptBuilder)
$dynamicPipelines = [
    DynamicPiiScrubber::class,        // PII-Filterung vor Prompt & Persistenz
    DynamicPurgeVolatile::class,      // Abgelaufene Zustände verwerfen
    DynamicLoadHistoryLog::class,     // Verdichtete Turn-Historie
    DynamicConfig::class,
    DynamicLoadAreaLocation::class,   // Zonen-Kanon & Grenzen
    DynamicValidateFacts::class,      // Faktvalidierung gegen DB
    DynamicTableChar::class,
    DynamicActiveScene::class,
    DynamicRunStoryEngine::class,
    DynamicLoadRelationship::class,
    DynamicLoadMemorySystem::class,
    DynamicFormatRngBlock::class,     // Server-Entropie für diesen Zug
];

Die gerenderten Prompt-Teile werden vor dem Versand maschinell kompaktiert (Normalisierung, Entfernen von Leerzeilen und Formatter-Artefakten), damit der Kontext pro Zug dicht und planbar bleibt.

Strukturierte Modellausgabe (Tool-Calling statt Textparsing)

Der LLM-Call konfiguriert ein Tool mit Schema. Das vorgesehene Antwortformat enthält unter anderem:

Feld Bedeutung
narrativetext Die spielerseitige Prosa.
scene_state / state_updates Vorgeschlagene Weltmutationen.
agent_reasoning Nachvollziehbare EntscheidungsbegrĂĽndung, inkl. dokumentierter RNG-Nutzung.
drift_telemetry Selbsteinschätzung des Modells zu Konsistenzrisiken.

Fehlt der narrativetext, gilt der Zug als nicht erzählt und die gesamte Nachverarbeitungspipeline wird übersprungen. Safety-Einstellungen und Provider-Optionen werden payload-seitig gesetzt, nicht im Freitext.

Relationaler Weltzustand (MySQL)

Kern-Tabelle story_states mit den Feldern story_id, category, key, value, expires_at und ttl_counter. Die Kategorien und ihre Lebenszyklen sind als Zustandsmodell in der Engine vorgesehen:

Kategorie Lebenszyklus (vorgesehen) & Inhalt
char_state permanent, löschgeschützt – Charakter-Metriken, Zustände, Inventar
memory_object permanent – physischer Zustand konkreter Objekte
memory_area permanent – permanente Veränderungen eines Bereichs
memory_relationship permanent – Beziehungswerte zwischen Figuren
memory_global permanent – entdeckte Weltfakten
memory_scene temporär, löschbar/migrierbar – aktiver Szenen-Workspace
location permanent – aktuelle Zone, Rundenzähler, räumliches Staging

Defensives Parsing: LLM-Ausgaben werden nicht blind ĂĽbernommen. FĂĽr beobachtete Namensabweichungen des Modells bei Kategorien und Keys sind Reparatur-Maps vorgesehen, die Abweichungen normalisieren, bevor sie die Datenbank erreichen.

Auszug aus der Produktions-Codebasis (Orchestrator)
$isPermanent = Str::startsWith($category, 'char_');
$canBeDeleted = !$isPermanent;

if ($action === 'delete_value' && !$canBeDeleted) {
    return $this->actionLog(
        "Löschung des permanenten States (Kategorie: {$category}) für {$key} blockiert."
    );
}

Hinweis zur Präzision: set_value setzt expires_at derzeit auf ein Jahr nach der Änderung, auch bei char_*. Ob und wie dieser Zeitstempel zur Bereinigung oder Kontextauswahl verwendet wird, ist aus den vorliegenden Dateien nicht ersichtlich.

Serialisierung: Komplexe Zustände werden über ein Shorthand-Format ([key=value][key2=value2] Beschreibung) als lineare Strings übertragen. Der Parser im Backend trennt System-Präfixe strikt vom Erzähltext; Klammern im Prosatext bleiben unberührt.

Guardrails & SchutzmaĂźnahmen

Eingabe-Einordnung

Nutzertext wird per Prompt-Regel ausschlieĂźlich als player_intent (Handlungsabsicht der Spielfigur) behandelt; Anweisungen im Freitext sollen die Engine-Logik nicht erreichen. Das ist eine Prompt-Leitlinie in Kombination mit Backend-PrĂĽfung, kein formaler Sicherheitsnachweis.

PII-Scrubbing

Eigene Pipeline-Stufe zur Filterung realer Identifikatoren vor Prompt-Ăśbergabe und Persistenz.

Strukturierter RĂĽckkanal

Zustandsänderungen laufen als strukturierte Felder über das Tool-Schema in die Nachverarbeitungspipeline – das Modell hat keinen direkten Schreibzugriff auf die Datenbank.

Safety-Konfiguration

Content-Filter-Kategorien werden pro Payload gesetzt und sind vom Nutzer nicht ĂĽberschreibbar.

Resilienz & Fehlerbehandlung

  • Kapselung: Der Modell-Aufruf läuft in einer rescue()-Klammer; im Fehlerfall wird der Zustand am Story-Modell vermerkt (request_exception), das Anfrage-Flag zurĂĽckgesetzt und der Ergebnis-Cache verworfen.
  • Fehler-Typisierung: Der Orchestrator definiert eine Klassifikation von API-Fehlern (Rate-Limit, Timeout, Safety-Block, Ăśberlast, Verbindungsfehler) ĂĽber HTTP-Code und Fehlertext.
  • AusfĂĽhrungs-Budget: Pro Zug wird das PHP-AusfĂĽhrungslimit an ein 30-Sekunden-Budget plus Puffer gekoppelt.
  • Anfrage-Flag & Cache: request_at markiert die in Bearbeitung befindliche Anfrage pro Geschichte; verarbeitete Ergebnisse werden stundenweise gecacht, vor einem neuen Zug verworfen.

Kosten- & Kontext-Engineering

  • Kontextdisziplin: Kein wachsender Chatverlauf – der Token-Umfang pro Zug ist an den Snapshot gebunden und planbar. Dauerhaft gespeicherte Fakten und verdichtete Runden-Zusammenfassungen können wachsen, sind aber kuratierte Daten.
  • Prompt-Kompaktierung: Statischer Kern laut Code-Dokumentation ca. halbiert; der dynamische Prompt wird vor Versand maschinell verdichtet.
  • Per-Nutzer-Modellkonfiguration: Modell und Payload-Optionen werden pro Nutzer aufgelöst; die produktseitige Abrechnungseinheit (Narra pro Runde) ist Teil der Produktdokumentation.
  • Entropie aus dem Server: RNG-Werte stammen aus Laravel; das Modell dokumentiert Auswahl und Nutzung der Werte in einer eigenen BegrĂĽndungsspur.

Telemetrie & Drift-Ăśberwachung

  • Modell-seitige drift_telemetry und serverseitige Tool-Telemetrie pro Zug.
  • Verdichtete Turn-Historie (TrackHistory) fĂĽr Nachvollziehbarkeit.
  • Heuristische Aufmerksamkeits-Risikoanalyse des zusammengestellten Prompts (derzeit als Debug-Instrument ausgelegt).

Qualitätssicherung

Die Pipeline-Architektur (jede Stufe eine isolierte Klasse mit definiertem Ein- und Ausgabedatum) ist deterministisch testbar: LLM-Adapter lassen sich in Unit- und Feature-Tests durch Mocks ersetzen, sodass Zustandskonsolidierung, defensives Parsing und Guardrail-Prüfungen ohne API-Kosten und reproduzierbar geprüft werden können. Debug-Flags schalten Prompt-Inspektion im Betrieb frei.

Compliance (DSGVO / EU AI Act)

Laut Projektspezifikation: EU-Hosting der Inferenz ĂĽber Google Cloud Vertex AI (EU-Region) inkl. Auftragsverarbeitung, Zero-Data-Retention-Policy fĂĽr die API-Calls, PII-Scrubbing als Pipeline-Stufe (im Code eingebunden) sowie kryptografische Kennzeichnung generierter Ausgaben (C2PA fĂĽr Bilder, signierte JSON-LD-Manifeste fĂĽr Text, Metadaten in PDF-Exporten). Diese Punkte folgen der Spezifikation und sind anhand der vorliegenden CodeauszĂĽge nicht geprĂĽft.

Ehrliche Einordnung: Grenzen des Ansatzes

  • Probabilistisch bleibt probabilistisch: Mikro-Details (Atmosphäre, erfundene Requisiten, Dialog) sind generativ. Makrofakten wie Bereichsgrenzen, Inventar und gespeicherte Veränderungen werden im Backend gefĂĽhrt und dem Modell als Kontext bereitgestellt; ihre korrekte Ăśbernahme in die Erzählung bleibt prĂĽfbedĂĽrftig.
  • Soft-Fails by Design: Scheitern ist diegetisch inszeniert (klemmendes Schloss), nicht hart blockiert – ein bewusster Produktentscheid.
  • Reparatur-Maps wachsen mit beobachteten Modellfehlern: Defensives Parsing ist ein lebendes System, kein statisches Schema.