\n\n\n\n Praktische Datenbankdesign-Muster für Produktionsbots - BotClaw Praktische Datenbankdesign-Muster für Produktionsbots - BotClaw \n

Praktische Datenbankdesign-Muster für Produktionsbots

📖 8 min read1,486 wordsUpdated Mar 30, 2026


Warum die Datenbank deines Bots heimlich das Sagen hat

Der erste „erfolgreiche“ Bot, den ich 2018 auf den Markt brachte, fiel nicht wegen eines schlechten Modells oder langsamen Codes um, sondern weil die Datenbank ein Durcheinander war.
Nutzer erhielten Antworten, die für jemand anderen bestimmt waren. Sitzungen „vergaßen“ zufällig, wer du warst. Analytics waren nutzlos.
Alles, weil ich die DB wie einen nachträglichen Gedanken behandelte.

Wenn du Bots für echte Nutzer baust, ist die Datenbank das Rückgrat. Nicht das Modell. Nicht das Gateway. Das Datenmodell.
2026 haben wir schönere LLMs und glänzendere Frameworks, aber den gleichen alten Schmerz:

  • „Wo speichere ich den Konversationsstatus?“
  • „Wie halte ich die Sitzungen getrennt?“
  • „Was logge ich, ohne den Speicherplatz zu sprengen?“

Lass mich erklären, wie ich Datenbanken für Bots entwerfe, die tatsächlich in der Produktion laufen, nicht nur in Demos.

Grundlegende Entitäten: Nutzer, Sitzungen, Nachrichten

Jeder Bot, den ich versende, beginnt mit drei Tabellen. Die Namen ändern sich, die Ideen nicht:

  • users — wer mit dir spricht
  • sessions — eine Reihe von zusammenhängenden Gesprächen
  • messages — was gesagt wurde, in der Reihenfolge

users

Ich betrachte Nutzer als „wer diese Plattform denkt, dass du bist“, nicht als „das philosophische Konzept einer Person“.

users
- id (pk)
- external_id (eindeutig, z.B. slack_user_id, Telefon)
- platform (slack | web | whatsapp | etc.)
- created_at
- deleted_at (nullable)
 

Ein Slack-Nutzer und ein WhatsApp-Nutzer könnten die gleiche Person sein, aber ich überkompliziere die „globale Identität“ nicht, bis ich sie wirklich brauche.
Wenn du versuchst, die Identität am ersten Tag perfekt zu lösen, stehst du still.

sessions

Eine Sitzung ist „ein zusammenhängender Gesprächsfaden.“ Für einen Support-Bot könnte sie beginnen, wenn jemand eine Frage stellt, und enden nach 30 Minuten Stille oder wenn ein Agent das Ticket schließt.

sessions
- id (pk)
- user_id (fk users.id)
- platform (wieder, slack/web/etc.)
- status (active | closed)
- started_at
- ended_at (nullable)
- metadata (jsonb) -- z.B. Thema, channel_id, ticket_id
 

Ich speichere immer metadata als JSON für plattformspezifischen Kram, den ich nicht in das Kernschema eindringen lassen möchte.
Beispiel: Slack channel_id, Web browser_session_id, was auch immer.

messages

Nachrichten sind die einzige Quelle der Wahrheit für „was tatsächlich passiert ist.“

messages
- id (pk)
- session_id (fk sessions.id)
- sender_type (user | bot | system)
- sender_id (nullable, fk users.id für Nutzer, vielleicht bot_id später)
- external_message_id (nullable, z.B. slack_ts)
- content (text)
- content_type (text | json | rich)
- created_at
- raw_payload (jsonb, nullable)
 

Zwei Gründe, warum diese Tabelle wichtig ist:

  • Debugging: Wenn jemand sagt „Der Bot ist verrückt geworden“, benötigst du ein genaues Transkript.
  • Training: Du wirst echte Gespräche für Feinabstimmungen oder Anpassungen des Prompts wollen.

Für einen Bot, der ~50.000 Nachrichten/Tag bearbeitet (einen, den wir auf Postgres 15 auf AWS RDS ausführen),
hat sich diese Struktur mit ordentlichem Indexing auf session_id und created_at gut bewährt.
Anfragen für die letzten 50 Nachrichten einer Sitzung sind immer noch günstig.

Wo man den Bot-“Status” ablegt, damit er dich nicht beißt

Der größte Designfehler, den ich sehe: Status überall unterbringen. Redis. JWTs. Zufällige JSON-Spalten. Versteckt in Prompt-Strings.
Dann weiß niemand, welcher Status der echte ist.

Kurzfristiger Status: cache ihn

Dinge, die du nur während der aktuellen Interaktion benötigst und die du von Grund auf neu rekonstruieren kannst, kommen in schnellen Speicher:

  • Redis, KeyDB oder In-Memory-Speicher für flüchtige Schlüssel
  • Ablauf in Minuten, nicht Tagen

Beispiel-Schlüssel, die ich tatsächlich benutze:

session_state:{session_id} -> json, ttl=30m
rate_limit:{user_id} -> counters, ttl=1h
otp:{phone} -> code, ttl=5m
 

Wenn Redis ausfällt, könnte dein Bot vergessen, wo er in einem Formular war. Nervig, aber nicht katastrophal.
Das ist die richtige Art von Dingen, die dort hingehören.

Persistenter Status: in die Datenbank

Alles, was du bereuen würdest, zu verlieren, gehört in eine echte Datenbank:

  • Onboarding-Fortschritt
  • Merkmale pro Nutzer
  • Langlaufende Workflows („Darlehensantrag #1234 Schritt 3/7“)

Ich halte das normalerweise in einer separaten Tabelle, anstatt sessions aufzublähen:

conversation_state
- id (pk)
- session_id (fk sessions.id, einzigartig)
- state_name (text) -- z.B. "verify_email", "collect_address"
- data (jsonb) -- beliebiger strukturierter Status
- updated_at
 

Ja, JSON. Nein, nicht für alles. Aber für den Bot-Flow-Status ist JSON in Ordnung. Es ändert sich oft und du bist der einzige Verbraucher.

Verstecke den Status nicht in JWTs

Die Speicherung der Sitzungslogik in JWT-Claims ist verlockend. Es ist auch der Weg, wie du Fehler bekommst, bei denen mehrere Clients über die Realität uneinig sind.
Ich verwende JWTs für Auth, und das war’s. Status lebt in der DB oder im Cache.

Logging, Analytics und nicht im Datenmeer ertrinken

Bots erzeugen eine absurde Menge an Daten. Wenn du jedes Token für jede Anfrage protokollierst und es für immer aufbewahrst, wird dir deine Infrastrukturrechnung monatlich in Erinnerung gerufen.

Trenne „Laufzeitprotokolle“ und „Analytics-Daten“

Ich betrachte sie als zwei unterschiedliche Anliegen:

  • Laufzeitprotokolle: zum Debuggen. Kurze Aufbewahrung (7–30 Tage).
    Verschicke sie an etwas wie Loki, Elasticsearch oder CloudWatch.
  • Analytics-Daten: strukturiert, abfragbar, langfristig. Lebt in SQL oder einem Warehouse.

Für Analytics mag ich ein separates Schema oder eine Datenbank:

conversation_metrics
- id (pk)
- session_id
- user_id
- messages_user_count
- messages_bot_count
- started_at
- ended_at
- first_response_ms
- resolved (bool)
 

Du berechnest dies über einen Job, der messages nächtlich scannt oder über Streaming.
In einem Kundenbot aktivierten wir die nächtliche Aggregation im März 2025 und reduzierten die Supportanfragen „Was geht hier vor?“ um die Hälfte, weil wir tatsächlich Abbrechpunkte sehen konnten.

Prompt- und Modellprotokolle

Wenn dein Bot LLMs verwendet, halte eine Aufzeichnung dessen, was du gesendet hast und was du zurückbekommen hast, mit einem Verweis auf die Nachricht/Sitzung.

llm_calls
- id (pk)
- session_id
- message_id (fk messages.id, nullable)
- provider (openai | anthropic | local)
- model
- prompt_tokens
- completion_tokens
- cost_usd
- request_payload (jsonb) -- redigiert
- response_payload (jsonb) -- redigiert
- created_at
 

Zwei wichtige Hinweise:

  • Redigiere Geheimnisse und PII bevor du Protokolle speicherst.
  • Setze eine Aufbewahrungsrichtlinie. Du brauchst für 99 % der Bots keine 3 Jahre Rohdaten für Eingabeaufforderungen.

SQL vs NoSQL vs Vektorspeicher (und wie ich sie mische)

Kurze Version: Ich setze standardmäßig auf Postgres. Ich füge andere Speichersysteme hinzu, wenn der Schmerz real ist, nicht hypothetisch.

Relational (Postgres, MySQL)

Perfekt für:

  • Nutzer, Sitzungen, Nachrichten, Status
  • Berichterstattung, Joins, Migrationen
  • Starke Konsistenz für „wer sagte was und wann“

Postgres mit jsonb bietet genug Flexibilität, um nicht zu früh fünf verschiedene Datenbanken aufzusetzen.

Cache (Redis)

Verwende dies für:

  • Ratenlimits
  • Flüchtiger Sitzungsstatus
  • Kleine, häufig angefragte Lookup-Tabellen (Konfigurationsflags, Feature-Überschalter)

Vektorspeicher

Wenn dein Bot Retrieval-augmentierte Generierung (RAG) macht, benötigst du Einbettungen und Ähnlichkeitssuche.
In der Praxis sehe ich drei Muster:

  • Postgres + pgvector
  • Dedizierte Dienste wie Pinecone, Weaviate, Qdrant
  • Cloud-spezifische Optionen wie Aurora + pgvector, AlloyDB usw.

Ich verlasse mich auf pgvector bis:

  • Einbettungen > ~5–10 Millionen Zeilen oder
  • Abfrage-Latenzanforderungen werden eng (< 50 ms) bei hohem Traffic

Schema-Skizze:

documents
- id (pk)
- source (kb | ticket | faq)
- source_id
- content
- metadata (jsonb)
- created_at

document_embeddings
- id (pk)
- document_id (fk documents.id)
- embedding vector(1536)
- created_at
 

Halte das „Was“ (Dokumente) getrennt vom „Wie wir danach suchen“ (Einbettungen).
Du wirst die Modelle ändern; du möchtest das nicht mit deinem Kerninhalt verknüpfen.

FAQ: Schemafragen, die ich immer wieder bekomme

Q: Soll ich vollständige Nachrichten in der DB speichern oder nur Zusammenfassungen?

Speichere vollständige Nachrichten, mindestens für 30–90 Tage. Zusammenfassungen sind großartig für die Suche und Analytics, aber wenn etwas kaputt geht, möchtest du den Rohtext.
Wenn du dir Sorgen über den Speicherplatz machst, setze eine Aufbewahrungsrichtlinie und archiviere alte Nachrichten in günstigeren Speicher.

Q: Wie gehe ich mit Multi-Tenant-Bots (viele Kunden) um?

Füge überall, wo es wichtig ist, eine tenant_id hinzu: users, sessions, messages, conversation_state.
Indexiere sie. Jede Abfrage, die auf Nutzerdaten zugreift, muss nach tenant_id filtern.
Wenn du später eine strengere Isolation benötigst, kannst du Mandanten über Schemata oder Datenbanken aufteilen, aber beginne mit einer soliden Mandanten-Spalte.

Q: Wo speichere ich Dateien (Bilder, PDFs), die Nutzer senden?

Nicht in der Datenbank. Speichere sie in S3/GCS/Blob-Speicher und halte Referenzen:

attachments
- id (pk)
- message_id (fk messages.id)
- file_url
- file_type
- size_bytes
- created_at
 

Wenn du OCR oder Einbettung darauf ausführen musst, speichere diese Ausgaben in separaten Tabellen, die über attachment_id zurückverknüpft sind.


🕒 Published:

🛠️
Written by Jake Chen

Full-stack developer specializing in bot frameworks and APIs. Open-source contributor with 2000+ GitHub stars.

Learn more →
Browse Topics: Bot Architecture | Business | Development | Open Source | Operations

More AI Agent Resources

ClawgoAgntmaxAgntboxClawdev
Scroll to Top