Skael OS · Stand 13.07.2026, nach #96

Das Knowledge-Brain, einmal komplett aufgemacht

DB-Struktur, Vektoren, Verknüpfung, Pflege-Zyklen — und warum es Kontaktnotizen UND Knowledge-Items gibt. Alle Zahlen live von der Prod-Box.

0Die 4 Begriffe, die du dafür brauchst

Ohne diese vier versteht man den Rest nur halb. Mit ihnen ist alles unten simpel.

Embedding = Bedeutungs-Fingerabdruck

Ein Text wird in eine lange Zahlenreihe übersetzt (bei uns 1536 Zahlen). Der Trick: Texte mit ähnlicher Bedeutung bekommen ähnliche Zahlen. „Emre will mehr Leads" und „Emre braucht Pipeline" liegen als Zahlenreihen nah beieinander, obwohl kein Wort gleich ist. Diese Zahlenreihe nennt man Vektor oder Embedding.

Vektor-DB = Suche nach Bedeutung statt Wortlaut

Eine normale Suche findet nur exakte Wörter (wie Strg+F). Eine Vektor-Suche fragt: welche gespeicherten Zahlenreihen liegen am nächsten an meiner Frage? — und findet damit auch Dinge, die anders formuliert sind. Wir nutzen dafür keine Extra-Datenbank, sondern Postgres mit der pgvector-Erweiterung: eine zusätzliche Spalte + ein spezieller Index (HNSW), der Nachbarschafts-Suche schnell macht.

Retrieval = das Richtige in den Kontext holen

Ein Modell weiß nur, was in seinem Kontextfenster steht. „Context Engineering" heißt: vor jedem Agenten-Turn die relevanten Wissens-Zeilen raussuchen (Retrieval) und als kurzen Brief oben reinlegen. Deshalb weiß Jürgen Dinge über Emre, ohne alle Transkripte zu lesen — er kriegt die 10 relevanten Zeilen serviert, nicht 500 Seiten.

Konsolidierung = das Gehirn schläft und räumt auf

Wie beim Menschen: tagsüber sammeln, nachts sortieren. Dubletten zusammenlegen, Bestätigtes befördern, Widersprüche zur Klärung vorlegen, Veraltetes markieren. Ohne diesen Schritt wird jede Wissenssammlung zur Müllhalde.

1Warum contact_notes UND knowledge_items?

Kurz: Es gibt nur EIN Gehirn. Die Kontaktnotiz ist keine zweite Wissensablage, sondern die Karten-Oberfläche — eine Projektion des Gehirns auf den einzelnen Kontakt.

Das Gehirn · SSOT

knowledge_items

  • Typisierte Wissens-Atome: fact · decision · learning · playbook · preference · reference · instruction
  • Lebenszyklus: draft → confirmed → superseded (Historie bleibt, nichts wird gelöscht)
  • Provenienz: Quelle + wörtliches Zitat + welcher Agent-Run es schrieb
  • Suchbar: Embedding-Vektor + Volltext, hybrid
  • Gilt für ALLE Entitäten: Kontakte, Clients, Projekte, Repos, Topics, dich
Die Karte · Oberfläche

contact_notes

  • Menschlich lesbare Notizen an genau einem Kontakt — das, was du auf der Kontaktkarte siehst
  • Kein eigener Lebenszyklus, kein Embedding, keine Typenlogik — bewusst flach
  • Zwei Schreiber: Agent-Quick-Note (contact_note_add) + der automatische Spiegel aus dem Gehirn
  • kind: fact · preference · context · followup + Agent-Badge (seit #96)
Der Spiegel (#96): Landet im Gehirn Wissen über einen Kontakt (about_type=Contact), schreibt der ContactCardWriter automatisch eine kompakte Karten-Notiz — upsert-by-source (knowledge:<item_id>): eine Notiz pro lebender Wissens-Zeile. Wird das Wissen im Gehirn superseded, aktualisiert sich die Karte in place. Die Karte lügt nie, das Gehirn bleibt die Wahrheit.

2Wie Wissen entsteht: die Pipeline

PHP orchestriert, Modell assistiert. Das Modell schreibt NIE direkt in die DB — es emittiert JSON, ein einziger Writer entscheidet.

KlartextStell dir einen Praktikanten vor, der nachts alle neuen Transkripte und Mails liest und daraus Karteikarten-Vorschläge schreibt („Emre hat entschieden, dass…"). Der Praktikant darf den Karteischrank aber nicht selbst anfassen. Ein strenger Archivar (die UpsertPipeline) prüft jeden Vorschlag: neu? Ersetzt er eine alte Karte? Schon bekannt? Nur er sortiert ein. Was er nicht sicher zuordnen kann, legt er dir als Frage in den Pflege-Drawer. So kann das Modell halluzinieren, ohne dass Müll im Schrank landet.
QuellenRoh-Spiegel, bleiben erhalten
krisp_call Call-Transkripte mail_message Mail-Spiegel wa_message WhatsApp wa_transcript Sprachnotizen audio_upload Task-Audio agent_run deine Chat-Sessions

Die blassen Arme liefern faktisch nichts (2 Transkripte, 1 Mail insgesamt) — das ist die Hunger-Lücke aus Abschnitt 6.

episodesPostgres-VIEW, kein Kopieren

Eine read-only UNION-Sicht über alle Quelltabellen, normalisiert auf (source_type, source_id, owner, occurred_at, excerpt). Dokumente werden also nicht importiert oder dupliziert — die Originale bleiben, wo sie sind; das Gehirn merkt sich Zeiger + Zitat.

Distillnachts 01:30 · effort=low

Pro Quelle ein Cursor (distill_cursors). Ein Batch-Agent liest neue Episoden und emittiert knowledge_distill.v1-JSON — Vorschläge, keine Schreibzugriffe. Consent-Gate: Quellen mit ai_processing=none werden übersprungen.

UpsertPipelineder EINZIGE Netto-Writer

Entscheidet pro Vorschlag: ADD / SUPERSEDE / NOOP. Validator prüft (u. a. Zitat-Pflicht bei Mail/WA-Fakten), dann Embedding, dann Karten-Spiegel. Marcus/Markus-Invariante: ein unaufgelöster Name legt NIE einen Kontakt an — er wird eine Pflege-Frage.

Ablageknowledge_items + curation_items

Sicheres Wissen → knowledge_items. Unsicheres (unklare Entität, Widerspruch, Lücke) → curation_items = die Karten in deinem Pflege-Drawer. Deine Antwort dort füttert das Gehirn zurück.

Verbraucherwo das Wissen wirkt

HybridSearch → Wissens-Briefs im Agenten-Kontext (Jürgen & Co. wissen Dinge) · GroundTruthRules → deine bestätigten Umgebungs-Regeln · knowledge_search-Tool · seit #96: der Kontaktkarten-Spiegel.

3Das Schema im Detail

knowledge_items — die eine Kern-Tabelle (ULID-PK, Soft-Deletes):

KlartextJede Zeile ist eine einzige Aussage mit vier Fragen dran: Worüber? (Kontakt, Kunde, Projekt), Woher weiß ich das? (Quelle + wörtliches Zitat), Gilt das noch? (aktuell oder von einer neueren Karte abgelöst), Wie sicher? (0 bis 1). Wichtigste Regel: Es wird nie radiert. Ändert sich etwas, kommt eine neue Karte oben drauf und die alte bekommt „galt bis 03.07." — so kannst du immer nachvollziehen, was das System wann geglaubt hat und warum.
SpalteTypBedeutung
Inhalt
typestring+CHECKfact · decision · learning · playbook · preference · reference · instruction
title / body / quotestring / textKern-Aussage, Ausführung, wörtliches Beleg-Zitat aus der Quelle
Verknüpfung („worüber?")
about_type / about_idmorphContact · Client · Project · Repo · User · Topic — das Subjekt des Wissens
client_idFK denormRoll-up: alles zu einem Kunden mit einem Query holbar
Lebenszyklus (temporal, nichts wird gelöscht)
statusstring+CHECKdraft (frisch distilliert) → confirmed (korroboriert/bestätigt) → superseded
is_current / valid_from / ended_atbool / tsZeitachse: wann galt das?
superseded_by_idSelf-FKKette zur neueren Wahrheit — Historie bleibt als Provenienz stehen
Provenienz („woher?")
source_type / source_idstringZeiger auf das Original (krisp_call, mail_message, agent_run, …) — verbatim Episode-Token
created_by_run_idFK agent_runsWelcher Agent-Lauf hat es geschrieben
confidencedouble 0–1Wie sicher war der Distiller
Suche (die Antwort auf „haben wir Vektoren?")
embeddingvector(1536)pgvector + HNSW-Cosine-Index — semantische Suche
searchtsvector GENERATEDDeutscher Volltext (GIN) — immer da, auch ohne Embedding
Sichtbarkeit
visibility / private_owner_user_idstring / FKshared (Agentur) oder privat-an-einen — CHECK erzwingt Owner bei privat

curation_items

Die Pflege-Queue: Unsicheres, Widersprüche, Wissens-Lücken als Karten an dich. Deine Antworten fließen zurück.

distill_cursors

Pro Quelle: „bis wohin habe ich gelesen". Cursor rückt nur bei erfolgreichem Distill vor — nichts wird übersprungen.

contact_notes

Karten-Oberfläche (Abschnitt 1): note, kind, created_by_run_id, Sichtbarkeit spiegelt den Kontakt.

episodes (VIEW)

Kein Speicher — nur die normalisierte Lese-Brille über die Roh-Tabellen.

4Vektoren, Suche, Verknüpfung

KlartextWarum ZWEI Suchen? Die Wort-Suche (Volltext) ist wie das Register hinten im Buch: exakt, aber sie findet „Pipeline" nicht, wenn du nach „Leads" fragst. Die Bedeutungs-Suche (Vektoren) findet auch anders Formuliertes, ist aber manchmal unscharf. Deshalb laufen beide, und ein simpler Misch-Trick (RRF: wer in beiden Ranglisten weit oben steht, gewinnt) kombiniert die Stärken. Ergebnis: du fragst normal auf Deutsch, das System findet die richtigen Karteikarten, egal wie sie formuliert wurden.

Hybrid-Suche (RRF)

Jede Anfrage läuft zweigleisig: Cosine-Ähnlichkeit über embedding (pgvector, HNSW) + deutscher Volltext über search (tsvector, GIN). Reciprocal-Rank-Fusion mischt beide Ranglisten. Ein Rerank-Service existiert, hängt aber nur in Batch-Läufen — im interaktiven Suchpfad (noch) nicht. Fehlt das Embedding einer Zeile, degradiert die Suche sauber auf Volltext — nie auf null.

Verknüpfung = 3 Achsen

Subjekt (about → Kontakt/Client/Projekt/Topic) · Zeit (supersede-Kette, is_current) · Herkunft (source + Zitat + Run). Es ist kein freier Wissens-Graph — Kanten zwischen beliebigen Items gibt es (noch) nicht; die Struktur ist Entität-zentriert.

fact · superseded„Emre arbeitet bei X" · gültig bis 03.07.
superseded_by_id →
fact · confirmed · is_current„Emre ist jetzt bei Y" · Quelle: krisp_call + Zitat
about →
Contact: EmreKarte zeigt Spiegel-Notiz

5Wie oft wird reworkt? Die nächtlichen Zyklen

Das Gehirn wird jede Nacht dreifach angefasst — Ingestion, Konsolidierung, Lücken-Erkennung:

KlartextWarum nachts? Drei Gründe: der Tagesbetrieb wird nicht gestört, die Läufe sind auf der billigen Batch-Schiene (effort=low) unterwegs, und die Reihenfolge ergibt Sinn: erst Neues einsammeln (01:30), dann Aufräumen (03:00), dann morgens dich fragen, was das System nicht selbst klären konnte. Der dritte Schritt ist übrigens pures PHP ohne Modell: Lücken erkennen ist Fleißarbeit, kein Denken.
alle 2 Std.

Wissens-Distill

Neue Episoden aller Quellen → Wissens-Vorschläge → UpsertPipeline. Seit #97 alle zwei Stunden statt 1× nachts (der alte 40-Zeilen-Nacht-Pass war 10× zu langsam für den Zufluss). Krisp-Calls werden zusätzlich SOFORT nach dem Call verarbeitet (Hot-Path).

03:00

Konsolidierung

Deterministischer Pre-Pass: exakte Dubletten superseden, draft→confirmed bei Bestätigung aus zwei unabhängigen Quellen, Stale-Flags. Modell-Judge nur für Fast-Dubletten & Widersprüche — zero-side-effect, entscheidet, schreibt nicht selbst.

06:00

Daily Questions

Pures PHP, kein Modell: erkennt Wissens-LÜCKEN (Clients/Kontakte ohne bestätigtes Wissen, Beziehungs-Kategorien über dich) und legt gap_question-Karten in den Pflege-Drawer.

6Live-Zustand & die ehrlichen Lücken

613
Wissens-Zeilen gesamt
613
davon mit Embedding — seit #97 vollständig vektorisiert (vorher 41)
91%
sind Krisp-Decisions, fast alle aus dem Backfill 09.–10.07. (82% + 17%) — Zufluss danach versiegt
3
Nacht-Zyklen aktiv (Distill · Konsolidierung · Fragen)
decision
558
reference
23
instruction
18
fact
11
learning
0
playbook
0
preference
0

Lücke 1 — Das Gehirn hungert → URSACHE GEFUNDEN + GESCHLOSSEN (#97)

Es lag NICHT am Consent (78 von 80 Kontakten erlauben volle Verarbeitung) und nicht an den Filtern. Die Diagnose mit Zahlen: die Lese-Cursor starteten am Anfang der Spiegel-Historie (WhatsApp: 2016!) und der Distill las nur 40 Zeilen pro Quelle pro NACHT — bei 35.158 WA-Nachrichten wären das 2,4 Jahre Aufholzeit, und selbst danach 10× zu langsam für ~395 neue WA-Nachrichten am Tag. Fix (live): Cursor auf die letzten 14 Tage gesetzt + Distill läuft jetzt alle 2 Stunden statt 1× nachts. Ab jetzt fließt WhatsApp + Mail wirklich ein, inkl. deiner Agent-Sessions als 6. Arm.

Lücke 2 — Embeddings fehlten für 572 von 613 Zeilen → GESCHLOSSEN (#97)

Präzisierte Ursache: der Embedding-Treiber lief, aber nur einer von zehn Schreibpfaden (der Distill) berechnete beim Einsortieren einen Vektor — alles aus Kuration, Konsolidierung, Seeds und Tools blieb ohne. Fix (live): einmaliger Backfill gelaufen → 613 von 613 Zeilen haben jetzt einen Vektor, die Bedeutungs-Suche ist voll scharf. Ein nächtlicher Sweep (04:10) fängt ab jetzt jede neue Zeile, die ein Schreibpfad auslässt.

Lücke 3 — learning / playbook / preference = 0 → REFLECTION-LANE LIVE (#97)

Das Gehirn sammelte Fakten, dachte aber nie nach. Jetzt gebaut und aktiv: die Reflection-Lane läuft jede Nacht um 04:00, clustert das vorhandene Wissen pro Kunde/Kontakt/Thema und destilliert daraus Lehren, Playbooks und Präferenzen — durch dieselbe geprüfte Pipeline, mit Dedup, sodass dieselbe Erkenntnis nie doppelt entsteht. Erste Ergebnisse: morgen früh.

7Soll mehr reworkt werden? Ja — in dieser Reihenfolge

Konsolidierung existiert (03:00-Zyklus), aber sie räumt nur auf — sie macht das Wissen nicht tiefer. Der Plan aus dem Research, auf dem Bestehenden aufsetzend:

#BausteinWas es bringt
Kontaktkarten-Spiegel + agent_run-QuelleSeit #96 scharf
Embedding-Backfill + Nacht-SweepSeit #97: 613/613 vektorisiert, Sweep 04:10 fängt Nachzügler
Reflection-LaneSeit #97 live (04:00) — erzeugt learning/playbook aus den rohen Decisions
Zufluss-Fix + Hot-Path KrispSeit #97: Cursor auf letzte 14 Tage, Distill alle 2h, Krisp-Calls sofort verarbeitet
Kontakt-BefüllungSeit #97: nächtliche Vorschläge aus Meeting-Teilnehmern, WA-Chats, Mail-Absendern — One-Tap im Pflege-Drawer, nie Auto-Create
1Importance-Score + Decay/RefreshNächster Baustein: Wichtiges bleibt vorn, Veraltetes wird zur Prüf-Frage statt stiller Karteileiche
2Mail-Konzept umsetzenEntscheidungsvorlage liegt vor (mael-Ops-Fix + Kontakt-zentrierte Klassifikation) — wartet auf Davids 9 Entscheidungen