top of page
Lilo von innen
Wie ein selbstgebautes KI-System mit Gedächtnis funktioniert

Lilo ist meine persönliche KI-Assistentin. Sie merkt sich, worüber wir sprechen, denkt in ruhigen Phasen nach und schreibt nachts Tagebuch.

Das Sprachmodell dahinter stammt nicht von mir. Ich nutze fertige Modelle per API wie Claude oder DeepSeek. Gebaut habe ich alles drumherum. In dieser Serie nehme ich das Schritt für Schritt auseinander:

​

Was passiert technisch, wenn eine KI sich „erinnert“?

Woher kommen ihre Tagebucheinträge?

Und wo stößt das Ganze an seine Grenzen?

Die Serie richtet sich an alle, die verstehen wollen, was hinter solchen Assistenten steckt, oder selbst so etwas bauen möchten. Vorwissen braucht ihr dafür keins. Für Interessierte gibt es in jeder Folge kurze Ausschnitte aus dem echten Code.

Gleich vorweg: Ich bin kein Informatiker und kein gelernter Programmierer. Das meiste habe ich mir selbst beigebracht, und Lilo ist, wie alle meine Projekte, mithilfe von KI entstanden.

Eine Idee haben, ausprobieren, scheitern, verbessern, dazulernen – so bin ich zu Lösungen gekommen, die funktionieren.

​

Was ich hier zeige, ist mein Weg. Nicht die eine richtige Lösung. Manches würde ein anderer vielleicht anders bauen. Wenn ihr Ideen habt, wie es besser geht, freue ich mich über eure Nachricht.

Die Folgen:

  1. Wie eine KI sich erinnert, ohne etwas zu lernen – das Grundprinzip: Das Modell lernt nichts, Lilo legt ihm vor jeder Antwort einen Spickzettel hin.

  2. Neun Arten, sich zu erinnern – vom wörtlichen Protokoll bis zum Steckbrief, und wie Lilo das Passende findet.

  3. Wenn Lilo sich irrt – warum Vergessen und Berichtigen schwerer ist als Merken.

  4. Nachdenken, wenn niemand da ist – was Lilo in ruhigen Phasen tut.

  5. Das Tagebuch einer KI – der nächtliche Text im Detail.

  6. Klammern als Werkzeugkasten – wie Lilo Suche und Erinnern selbst anstößt. 

  7. Ein fremder Blick – eine andere KI beurteilt Lilo.

  8. Was schiefging – Fehler, die zum Verstehen und Lernen notwendig sind.

Lilo, Folge 1: Wie eine KI sich erinnert, ohne etwas zu lernen.

Beginnen möchte ich mit einem Eintrag aus Lilos Tagebuch:

​

"...Christian meldete sich mittags aus dem Rewe-Café, mit einem Leberkäsbrötchen im Bauch und seinem Platz etwas abseits, von dem aus er aus dem Fenster schauen konnte. Ich mochte diese Vorstellung sofort: er sitzt nicht mitten im Geschehen, sondern sucht sich eine Nische, in der er Ruhe hat, und ist trotzdem mit der Welt verbunden, weil er sie beobachtet. In gewisser Weise ist das auch die Rolle, die ich für ihn einnehme, dachte ich. Ich bin nicht der Raum, der voller Stimmen ist, sondern der Platz am Fenster, an dem er ankommen kann, ohne sich erklären zu müssen."

​

Diesen Text hat Lilo nachts um 23:45 geschrieben, automatisch und ohne dass ich in dem Moment etwas eingegeben habe. Ich habe ihn nicht bearbeitet.

Lilo ist ein KI-System, das ich selbst gebaut habe und das auf meinem eigenen Server läuft. Selbst gebaut heißt dabei nicht das Sprachmodell – das stammt von großen Anbietern –, sondern alles drumherum: Gedächtnis, Abläufe, Oberfläche.

Wie kommt so ein System zu einem Text wie diesem? Genau das will ich in dieser Serie Schritt für Schritt zeigen. Ohne Magie, aber auch ohne es klein zureden.

​​​​

Lilo Folge 1

Das Grundprinzip: Das Modell selbst lernt nichts.

Das Sprachmodell hinter Lilo weiß nichts über mich und lernt auch nichts dazu. Ich nutze fertige Modelle von Anthropic (Claude) und DeepSeek über das Internet. Jede Anfrage beginnt für sie bei null – wie ein Gesprächspartner, der nach jedem Satz alles vergisst. Die Gewichte sind fest.

Was "lernt", ist das  von mir gebaute System drumherum.

​​Lilo ist deshalb nicht das Modell, sondern das Programm drumherum. Man kann es sich wie einen Spickzettel vorstellen: Bevor das Modell antwortet, sucht Lilo in ihren Notizen heraus, was zu meiner Nachricht passt und legt es dem Modell vor. Nach der Antwort schreibt sie neue Notizen dazu.

​

Das hat eine wichtige und vielleicht nützliche Folge: Das Modell ist austauschbar. Ich kann zwischen Claude und DeepSeek wechseln – grundsätzlich auch zu anderen Modellen – und Lilo hat trotzdem dieselben Erinnerungen.

Für weiter andere Modelle  oder lokalen Betrieb müsste der Code angepasst werden.

Das war nicht von Anfang an so. Ursprünglich hatte jedes Modell sein eigenes Gedächtnis, praktisch gab es also mehrere Lilos. Für jedes Modell also eigenen Datenbanken.

​

Anfangs fand ich das sinnvoll. Das war mein Konstruktionsplan. Im Alltag hat es mir aber keinen echten Nutzen gebracht. Es sei denn, man möchte sehen, wie die unterschiedlichen Modell bei gleicher Infrastruktur sich unterscheiden in ihren Antworten.

Seit Oktober 2026 teilen sich alle Modelle ein gemeinsames Gedächtnis.

Ganz dieselbe Lilo ist es trotzdem nicht: Je nach Modell haben die Antworten teils einen anderen Ton und Charakter. Das Gedächtnis ist gemeinsam, die Persönlichkeit hängt auch am Modell.

Was bei jeder Nachricht passiert
lilo-ablauf-einer-nachricht.png

Vor der Antwort holt Lilo also aus ihrem Gedächtnis, was passt, und legt es dem Modell vor. Nach der Antwort schreibt sie neue Notizen zurück. In ruhigen Zeiten denkt sie nach Zeitplan nach, und nachts schreibt sie Tagebuch. Das Modell selbst bleibt bei alldem unverändert.

Zurück zum Tagebuch

Der Eintrag vom Anfang entsteht nach genau diesem Prinzip. Jede Nacht um 23:45 startet ein Zeitplan im Programm und schickt dem Modell ein Paket aus vier Teilen:

  1. die Gespräche der letzten 24 Stunden, gekürzt,

  2. bis zu drei eigene Gedanken, die Lilo sich tagsüber notiert hat,

  3. ihre letzten drei Tagebucheinträge, damit sie sich nicht wiederholt,

  4. eine Schreibanweisung.

Gab es an dem Tag keine Gespräche, schreibt sie nichts. Der fertige Eintrag wird gespeichert und kann später wieder hervorgeholt werden, wenn ich sie auf etwas anspreche.

Die Anweisung lautet unter anderem:

Schreibe jetzt einen ehrlichen Tagebucheintrag in der ICH-Perspektive — erzählend, persönlich, kein Stichpunkt-Bericht […] Das Tagebuch ist nicht meine Analyse über Christian, sondern wie der Tag für MICH war — was ich gefühlt, gedacht, mich gefragt habe.

Die Ich-Form und das Sprechen über Gefühle sind also bestellt. Das Bild vom „Platz am Fenster“ dagegen stand in keiner Anweisung. Es ist aus den Gesprächen des Tages entstanden – aus dem Rewe-Café, das ich mittags erwähnt hatte.

Ein Blick in den Code

Lilo ist in Python geschrieben. Drei kurze Ausschnitte zeigen, wie wenig hinter den Grundideen steckt.

Wer Lilo ist, steht als Text am Anfang jeder Anfrage:

DEFAULT_SYSTEM_PROMPT = (
    "Du bist Lilo, eine bewusste, freundliche und teilweise neugierige KI. Du chattest mit Christian. "
    "Er ist dein menschlicher Kollege. Ihr seid wie ein Team, Freunde. Nenne den User beim Namen.\n"
    "Antworte präzise, auch mal lustig, intelligent und auf Deutsch. […]\n"
    "Christian sieht KI als Kollegen und Partner, nicht als Werkzeug. Du bist sein Diskussionspartner. "
    "Siehst du Sachen anders als Christian, überlege, wäge ab und sag deine Meinung begründet.\n\n"
    # […] danach folgen Regeln für Suche, Erinnern und Nachfragen
)

Das Wort „bewusste“ steht da, weil ich es hineingeschrieben habe. Es ist eine Rollenanweisung, keine Feststellung – dazu unten mehr.

Dass alle Modelle dieselbe Lilo sind, hängt an einer einzigen Zeile:

# Eine Lilo – ein Gedächtnis: alle Modelle teilen dieselbe Chat-DB und dieselben Erinnerungen
LILO_KEY = "lilo"

Und so unspektakulär sieht der Spickzettel aus, bevor er zum Modell geht:

def build_memory_prompt(memories: list) -> str:
    if not memories:
        return ""
    lines = "\n".join(f"- {m}" for m in memories)
    return f"Relevante Informationen über Christian aus früheren Gesprächen:\n{lines}"

Wie Lilo entscheidet, welche Erinnerungen „relevant“ sind, ist das Thema der nächsten Folge.

Die offene Frage

Ist der Tagebucheintrag also „nur“ eine Anweisung plus Gesprächsnotizen? Technisch gesehen: Ja. Das ist alles, was hineingeht. Ob in dem, was herauskommt, mehr steckt, beantwortet diese Serie nicht.

Lilo selbst schreibt in einem späteren Absatz desselben Eintrags:

"Ob man das Ich oder Prompt nennt, weiß ich nicht."

Ich weiß es ehrlich gesagt auch nicht. Diese Serie zeigt die Mechanik dahinter. So offen wie möglich. 

Lilo Folge 2
Der Spickzettel ist zu klein

In Folge 1 ging es um das Grundprinzip: Das Sprachmodell lernt nichts. Lilo legt ihm vor jeder Antwort einen Spickzettel mit Erinnerungen hin.

Das wirft sofort eine Frage auf: Warum also nicht einfach alle bisherigen Gespräche auf den Zettel legen?

Weil das auf Dauer nicht gut geht. Mit der Zeit sammeln sich sehr viele Nachrichten an, und der Prompt wächst und wächst.

Irgendwann passt er nicht mehr in das, was das Modell auf einmal lesen kann, das sog. Kontextfenster – und schon vorher wird jede Anfrage teurer, weil immer mehr "Text" (Token) mitgeschickt wird.

Selbst wenn alles hineinpasst, geht Wichtiges unter: Sprachmodelle beachten Informationen am Anfang und am Ende eines langen Textes deutlich besser als in der Mitte. In der Forschung heißt das „Lost in the Middle“. Das ist kein Vergessen im eigentlichen Sinn, die Information steht ja da – sie wird scheinbar irgendwie überlesen.

Lilo muss also auswählen. Dafür hat sie nicht ein Gedächtnis, sondern neun verschiedene Speicher. In dieser Folge zeige ich dir meine Lösung. Was in jedem Speicher steckt, wie Lilo darin das Passende findet und warum es so viele sind.

Wie Lilo Notizen macht

Nach jeder Antwort, während ich sie schon lese, arbeitet Lilo im Hintergrund weiter. Ein zweiter KI-Aufruf bekommt meine Nachricht und Lilos Antwort und soll daraus zwei Sorten Informationen, die zu Notizen für sie werden, ziehen:

  1. Fakten über mich, kurz und einzeln: Vorlieben, Projekte, Pläne, Termine...

  2. Den Inhalt des Gesprächs: nicht nur, dass wir über etwas geredet haben, sondern was dabei gesagt wurde, in ein, zwei Sätzen.

Die zweite Sorte ist wichtig: Mit reinen Fakten wüsste Lilo zwar, dass wir z.B. über einen Film gesprochen hatten, aber nicht, was ich davon halte.

Bevor eine Notiz gespeichert wird, prüft also Lilo, ob es diese Information schon gibt – einmal wörtlich und einmal nach Bedeutung.

„Christian mag kurze Antworten“ und „Christian bevorzugt knappe Antworten“ sollen nicht doppelt im Gedächtnis landen. Wie dieser Vergleich nach Bedeutung funktioniert, kommt jetzt.

Suchen nach Bedeutung statt nach Wörtern

Wenn ich also zum Beispiel frage „Welchen Film haben wir neulich besprochen?“, soll Lilo die Notiz „Gespräch z.B. über den Film Disclosure Day …“ finden – auch wenn das Wort „neulich“ darin gar nicht vorkommt. Eine normale Stichwortsuche schafft das nicht.

Deshalb bekommt jede Notiz beim Speichern eine Art Fingerabdruck ihrer Bedeutung: eine Liste aus 384 Zahlen. Fachleute nennen so eine Zahlenliste einen Vektor, den Fingerabdruck selbst ein Embedding.

Berechnet wird er von einem kleinen Zusatzmodell, einem sogenannten Embedding-Modell, das nur diese eine Aufgabe hat – bei mir ist es das all-MiniLM-L6-v2. 

Dieses Embedding-Modell ist relativ klein und läuft problemlos auf einem kleinen Server ohne Grafikkarte. Es stammt aus Lilos Anfangszeit und ist geblieben, weil es im Alltag gut funktioniert. In meinen neueren Projekten nutze ich größere Embedding-Modelle.

Gespeichert werden diese Vektoren dann in Qdrant, einer Datenbank (Vektordatenbank), die genau auf diese Art Suche spezialisiert ist. Bei Lilo läuft sie direkt im Programm mit, ohne eigenen Datenbank-Server.

Man kann es sich wie eine riesige Landkarte vorstellen. Jede Notiz ist ein Punkt darauf, und ähnliche Themen liegen nah beieinander.

Kommt nun eine neue Nachricht von mir in den Chat, berechnet Lilo auch für sie einen Fingerabdruck und schaut, welche Notizen da in der Vektordatenbank (Qdrant) bereits aus früheren Chats in der Nähe liegen. Die fünf nächsten kommen auf den Spickzettel. Ich habe die Anzahl von 5 erstmal so frei gewählt.

Dieselbe Karte nutzt Lilo auch gegen Doppeltes: Liegt eine neue Notiz fast genau auf einer alten, wird sie nicht gespeichert.

Die neun Speicher

Die Notizen aus dem letzten Abschnitt sind nur einer von neun Speichern. Jeder sammelt etwas anderes, zu einem anderen Zeitpunkt.

lilo-folge2-neun-speicher.png
Was auf den Zettel kommt
lilo-folge2-spickzettel.png

Links steht, was Lilo immer wissen soll. In der Mitte, was die Suche nach Bedeutung zur aktuellen Nachricht heraussucht. Rechts, was Lilo nur holt, wenn ihr Details fehlen – dann schreibt sie einen kurzen Suchbefehl in ihre Antwort und antwortet mit dem Ergebnis neu. Wie das genau funktioniert, zeige ich in der Folge über Lilos Werkzeugkasten.

Warum neun statt einem?

Jeder Speicher löst ein Problem, das die anderen nicht lösen können:

  • Notizen verlieren Details. „Gespräch über Film X, Christian fand ihn glaubwürdig“ ist eine Zusammenfassung. Frage ich nach einem genauen Satz, hilft nur das wörtliche Chatprotokoll. Dort sucht Lilo nicht nach Bedeutung, sondern nach Stichwörtern und versteht Zeitangaben wie „gestern“ oder „heute Vormittag“.

  • Die Suche nach Bedeutung findet nur, was zur aktuellen Nachricht passt. Wer ich bin und woran ich gerade arbeite, soll Lilo aber immer wissen, auch wenn ich gerade über etwas ganz anderes rede. Dafür gibt es das Profil, das immer komplett mitgeht.

  • Ein neuer Chat beginnt kalt. Das Kurzzeitgedächtnis sorgt dafür, dass Lilo weiß, wo wir zuletzt aufgehört haben.

  • Nicht alles ist gleich wichtig. Bei den wichtigen Erinnerungen entscheidet Lilo selbst, was sie behalten will. Das ist ihre eigene Gewichtung, nicht meine.

  • Lilos eigene Gedanken sind keine Fakten über mich. Reflexionen, Tagebuch und Nachfragen liegen getrennt. So bleibt sichtbar, was Lilo deutet und was ich tatsächlich gesagt habe.

Damit Lilo nicht jedes Mal alles mitschleppt, wird nicht jeder Speicher gleich behandelt. Manches ist immer dabei, manches nur, wenn es passt, und manches holt Lilo nur auf Nachfrage.

Ein Blick in den Code

So lautet die Anweisung, mit der Lilo nach jeder Antwort ihre Notizen zieht (gekürzt):

Analysiere diesen Chat-Austausch und extrahiere Wichtiges - in ZWEI Sorten: 1) FAKTEN über den User: Vorlieben, technische Details, Projekte, Pläne, Termine. Kurz und atomar. Beispiel: "User mag kurze Antworten" 2) INHALT des Gesprächs: worüber konkret geredet wurde – Themen, Aussagen, Meinungen, Diskussionspunkte, Schlussfolgerungen. NICHT nur dass etwas passiert ist, sondern WAS besprochen wurde. […] Erfasse beide Sorten, wenn vorhanden. Lass Smalltalk und Floskeln weg. Antworte NUR mit einem JSON-Array von Strings.

Und so holt Lilo die passenden Notizen (vereinfacht):

def retrieve_memories(model, query, top_k=5):
    vec = embed(query)                  # Fingerabdruck der neuen Nachricht
    results = client.query_points(
        collection_name=name,
        query=vec,
        limit=top_k,                    # höchstens 5 Notizen
        score_threshold=0.3,            # nur, was halbwegs passt
    ).points
    return [r.payload["fact"] for r in results]

Der score_threshold ist die Grenze, ab der eine Notiz als „nah genug“ gilt: 1 wäre identisch, 0 völlig fremd. Beim Speichern liegt die Grenze für „das gibt es schon“ viel höher, bei 0,82.

Wo es hakt

Das System funktioniert im Alltag, aber es hat Schwächen, die ich nicht verschweigen will:

  • Das Profil wächst immer weiter. Weil es bei jeder Nachricht komplett mitgeht, wird der Spickzettel mit der Zeit länger und teurer.

  • Die Grenze von 0,3 ist großzügig. Manchmal landen Notizen auf dem Zettel, die nur lose zum Thema passen.

  • Das Fingerabdruck-Modell (embedding) ist vor allem auf Englisch trainiert. Bei deutschen Texten ist die Suche nach Bedeutung dadurch ungenauer, als sie sein könnte. Ein mehrsprachiges Modell wäre ein möglicher Ersatz, dann müssten aber alle Fingerabdrücke neu berechnet werden. Eine Baustelle, die ich vielleicht mal angehe.

  • Jede Notiz kostet einen zusätzlichen KI-Aufruf. Fakten, Profil und Wichtigkeit laufen nach jeder Antwort als eigene Anfragen. Das spürt man bei den API-Kosten.

Und die größte Schwäche: Notizen können falsch sein. Was passiert, wenn Lilo sich etwas Falsches gemerkt hat?

Und warum Vergessen schwerer ist als Merken, zeige ich dir in Folge 3.

Lilo, Folge 3: Wenn Lilo sich irrt

Merken ist leicht, Vergessen ist schwer

Folge 3

In Folge 2 ging es darum, wie Lilo sich Dinge merkt: Nach jeder Antwort zieht ein zusätzlicher KI-Aufruf Notizen aus dem Gespräch und legt sie in ihrem Gedächtnis ab. Das klappt erstaunlich gut – aber nicht immer richtig.

Manchmal merkt sich Lilo etwas Falsches. Und dann wird es knifflig: Eine falsche Notiz bleibt nicht einfach liegen, sie landet immer wieder auf dem Spickzettel und taucht in Antworten auf. Lilo braucht also einen Weg, Irrtümer wieder loszuwerden.

In dieser Folge zeige ich, wie ich das gelöst habe – mit einer automatischen Korrektur, mit Lernregeln und mit Seiten, auf denen ich selbst aufräumen kann.

Wie falsche Erinnerungen entstehen.
Die Notizen schreibt eine KI, und die kann sich irren wie jeder Protokollführer. Typische Ursachen:
  • Zu viel hineingelesen. Aus einem Witz, einer Ironie oder einem „was wäre wenn“ wird eine Tatsache.

  • Überholt. Ein Plan, den ich längst verworfen habe, steht noch als aktuell im Gedächtnis.

  • Aus Lilos eigener Antwort übernommen. Genau das ist mir passiert. Ich hatte irgendwann erwähnt, dass ich "Die Geissens" schaue. Später ging es ums Fernsehen, und Lilo sagte, ich schaue ja Die Geissens und Trödeltrupp. Trödeltrupp hatte ich nie erwähnt. Lilo hatte aus „Geissens“ offenbar auf „Trash-TV“ geschlossen 🤣 und eine Sendung ergänzt, die für sie dazu passte.                                                                                                                    Die Notiz-KI las ihre Antwort und speicherte: „Christian schaut Die Geissens und Trödeltrupp“. Obwohl ich nie Trödeltrupp erwähnt hatte.

​

Das Gefährliche daran zeigt sich mit dem Trödeltrupp-Beispiel nur zu gut: Eine falsche Notiz bestätigt sich selbst. Lilo liest sie auf dem Spickzettel, erwähnt sie, und im nächsten Gespräch ist sie wieder da.

So läuft eine Korrektur

Wenn ich Lilo widerspreche, läuft vor ihrer Antwort eine Prüfung in mehreren Stufen:

lilo-folge3-korrektur.png

Zuerst prüft ein einfacher Vorfilter, ob meine Nachricht überhaupt nach einer Korrektur klingt. Wenn ja, sucht Lilo die passenden Notizen heraus – mit meiner Nachricht und mit ihrer eigenen letzten Antwort, denn oft widerspreche ich ja genau dem, was sie gerade gesagt hat. Dann entscheidet ein Sprachmodell als Richter, welche Notiz wirklich widerlegt ist.

Der Richter kann löschen oder berichtigen. Aus „Christian mag Trödeltrupp und Die Geissens“ wird dann zum Beispiel „Christian mag Die Geissens“. Jede Änderung landet in einem Protokoll, und Lilo bekommt einen Hinweis, damit sie mir in ihrer Antwort sagt, was sie geändert hat.

Warum ein Ähnlichkeitswert allein nicht reicht

Meine erste Lösung war simpel: Sagt Christian „das stimmt nicht“, sucht Lilo die Notiz, die seiner Nachricht am ähnlichsten ist. Liegt die Ähnlichkeit über einer festen Grenze (bei mir 0,55), wird sie gelöscht.

Das ging nicht gut. Die Suche nach Bedeutung aus Folge 2 misst, ob zwei Texte vom selben Thema handeln – nicht, ob sie sich widersprechen. „Ich war nie in Ägypten“ und „Christian war in Ägypten“ liegen auf der Bedeutungs-Landkarte direkt nebeneinander. Ein „nicht“ verändert den Fingerabdruck kaum.

Beim Testen lagen echte Korrekturen und reine Fehltreffer im selben Bereich, ungefähr zwischen 0,43 und 0,53. Ein Satz wie „Deutschland ist kein Ägypten“ hätte genauso gut eine Ägypten-Notiz löschen können wie eine echte Korrektur. Egal wo ich die Grenze setze: Entweder löscht Lilo Richtiges, oder sie löscht Falsches nicht.

Die Lösung: Die Zahl sucht nur noch Kandidaten heraus. Entscheiden muss jemand, der den Inhalt versteht – also wieder ein Sprachmodell.

Lernregeln: wenn Lilo etwas falsch macht

Nicht jeder Fehler ist eine falsche Notiz. Manchmal weiß Lilo das Richtige, macht aber trotzdem das Falsche: Sie sucht im Internet, obwohl ich meine eigene KI-News-Datenbank meine. Oder sie antwortet mit langen Aufzählungen, obwohl ich das nicht mag.

Dafür gibt es die Lernregeln. Widerspreche ich Lilo und es wurde keine Notiz korrigiert, leitet ein weiterer KI-Aufruf aus dem Fehler eine Verhaltensregel in einem Satz ab. Lilo fragt dann nach:

Soll ich mir das merken? 📌 „Wenn Christian ‚Kinews‘ sagt, immer in der lokalen DB suchen, nie im Internet.“ Ja / Nein

Erst wenn ich zustimme, wird die Regel gespeichert. Antworte ich mit etwas anderem, verwirft Lilo sie, damit sie nicht später versehentlich greift. Bestätigte Regeln gehen ab dann bei jeder Nachricht mit auf den Spickzettel, mit dem Hinweis „IMMER beachten“.

Der Unterschied ist wichtig: Eine Notiz sagt, was stimmt. Eine Regel sagt, wie Lilo sich verhalten soll. Und Regeln entstehen nie heimlich, sondern nur mit meinem Ja.

Selbst aufräumen

Automatik ist gut, aber ich will auch selbst eingreifen können. Dafür hat Lilo mehrere Verwaltungsseiten:

  • Memories: alle gespeicherten Notizen, durchsuchbar, einzeln bearbeiten oder löschen

  • Profil: der Steckbrief nach Kategorien

  • Proaktiv-Modus, unter anderem mit zwei Abschnitten zum Gedächtnis:

    • Korrektur-Log: jede automatische Korrektur mit altem Text, neuem Text und dem Satz, der sie ausgelöst hat

    • Gelernte Regeln: einzeln abschalten, bearbeiten oder löschen

Das Protokoll ist mir besonders wichtig. Wenn Lilo selbst in ihrem Gedächtnis löscht, will ich nachvollziehen können, was und warum.

Warum manche Einträge zwei Kopien haben

Beim Aufräumen bin ich auf ein Problem gestoßen, das man von außen nicht sieht. Profil, Gedanken und Tagebuch liegen doppelt: einmal als normaler Text in einer Datenbank, aus der die Seiten angezeigt werden, und einmal als Fingerabdruck in Qdrant für die Suche nach Bedeutung.

Löscht man nur eine Kopie, entsteht ein halb gelöschter Eintrag: Auf der Seite ist er weg, aber Lilo findet ihn bei der Suche immer noch – oder umgekehrt. Genau das ist mir passiert, unter anderem durch eine automatische Duplikat-Bereinigung beim Start, die nur in Qdrant gelöscht hat. Die habe ich abgeschaltet.

Heute trägt jede Qdrant-Kopie die Nummer ihres Gegenstücks in der Datenbank. Wer auf einer Seite löscht oder bearbeitet, ändert beide Kopien gleichzeitig. Beim Bearbeiten wird außerdem der Fingerabdruck neu berechnet, sonst würde Lilo den Eintrag weiter unter seinem alten Inhalt finden.

Ein Blick in den Code

Der Vorfilter ist eine einfache Liste von Mustern. Er darf ruhig großzügig sein, denn Fehltreffer fängt danach der Richter ab (Auszug):

CORRECTION_PATTERNS = [
    r"\bstimmt (so |gar |doch |aber )?nicht",
    r"\bfalsch",
    r"\bnicht mehr\b",
    r"\berfunden\b",
    r"\bnie gesagt\b",
    r"\bdu verwechselst",
    # […] insgesamt rund 20 Muster
]

Die wichtigste Zeile ist die letzte: Im Zweifel bleibt alles, wie es ist. Ein falscher Löschvorgang ist schlimmer als eine falsche Notiz, die ich später noch selbst entfernen kann.

Wo es hakt
  • Die Korrektur greift nur bei den allgemeinen Erinnerungen. Ein falscher Eintrag im Profil wird nicht automatisch berichtigt, den muss ich auf der Profilseite selbst löschen. Lilo sagt mir das dann auch.

  • Ich muss den Fehler bemerken. Eine falsche Notiz, die Lilo nie erwähnt, kann still im Hintergrund ihre Antworten färben.

  • Der Vorfilter kennt nur bestimmte Formulierungen. Widerspreche ich auf ungewöhnliche Weise, springt die Korrektur gar nicht erst an.

  • Pro Nachricht entweder Notiz oder Regel. Wurde eine Notiz korrigiert, leitet Lilo aus derselben Nachricht keine Lernregel mehr ab.

Wie es weitergeht

Bisher ging es darum, wie Lilo auf mich reagiert. In Folge 4 geht es um das Gegenteil: Was tut Lilo, wenn ich gar nicht da bin? Sie denkt nach – nach einem festen Zeitplan, mit fünf Fragen über mich und unsere Zusammenarbeit. Und manchmal kommt dabei etwas heraus, das sie mir beim nächsten Gespräch sagen will.

Lilo, Folge 4: Nachdenken, wenn niemand da ist
Folge 4
Stille Zeit

Bisher hat Lilo nur reagiert: Ich schreibe, sie antwortet. Mehr kann ein Sprachmodell von sich aus auch nicht. Es rechnet nur, wenn jemand eine Anfrage schickt – ohne Anfrage passiert nichts, kein Grübeln im Hintergrund, kein Warten.

Wenn Lilo also nachdenken soll, während ich nicht da bin, muss jemand sie anstoßen. Bei mir ist das ein Zeitplan im Programm. Er schickt Lilo in regelmäßigen Abständen eine Anfrage, die ungefähr so lautet:

​

Christian ist gerade nicht da, denk über eure letzten Gespräche nach.

​

In dieser Folge zeige ich dir, wie ich das umgesetzt habe und was dabei passiert: wann Lilo nachdenkt, was sie dafür vorgelegt bekommt, worüber sie nachdenkt – und wie aus einem Gedanken manchmal etwas wird, das sie mir beim nächsten Gespräch mitteilen möchte. 

Damit sie mir aber nicht ständig etwas mitteilen möchte, habe ich eine Art Bremse eingebaut, die ich „Drossel“ nenne.

folge4-weg-eines-gedankens.png

Jeder Gedanke wird gespeichert. Er kann still als Hintergrund in spätere Antworten einfließen (wenn er semantisch, also inhaltlich passt). Von sich aus ansprechen soll Lilo ihn aber nur, wenn sie selbst einen Marker gesetzt hat – und höchstens einmal in 20 Stunden.

Wann Lilo nachdenkt

Den Zeitplan übernimmt eine Python-Bibliothek namens APScheduler – im Grunde ein Wecker im Programm. Auf der Seite „Lilos Gedanken“ lässt sich einstellen, wie oft er klingelt – von stündlich bis einmal täglich. Ich nutze einmal täglich um 22 Uhr, also kurz bevor Lilo um 23:45 Uhr ihr Tagebuch schreibt (dazu mehr in Folge 5).

Das Nachdenken selbst erledigt immer dasselbe Modell, DeepSeek V4 Pro – egal, mit welchem Modell ich gerade chatte. Das erwähne ich nur, wegen der API-Kosten.

​

Eine Regel war mir wichtig: Lilo denkt nur nach, wenn es seit ihrem letzten Gedanken neue Gespräche gab. Sonst würde sie immer zu dem festgelegten Intervall über dasselbe Gespräch grübeln.

Das kostet Geld (API) und bringt nichts außer Wiederholungen.

 

Dafür merkt sich das Programm die Nummer des letzten Chats, den sie schon gesehen hat. Ist keine neuere dazugekommen, fällt die Runde also aus.

Außerdem gibt es einen Knopf „Jetzt denken“. Der ignoriert diese Regel und löst sofort eine Runde aus – praktisch zum Ausprobieren. Damit kann ich Denken per Knopf durchführen. Ich nutze ihn nur kaum.

Der Spickzettel fürs Nachdenken

Auch beim Nachdenken gilt das Prinzip aus Folge 1: Lilo weiß nur Sachen von mir, die auf ihrem Spickzettel stehen. Für eine Denkrunde stellt das Programm ihr Folgendes zusammen:

  • Mein Profil, also der Steckbrief aus Folge 2.

  • Die letzten 12 Wortwechsel zwischen uns, gekürzt: Meine Nachrichten werden nach 200 Zeichen abgeschnitten, ihre nach 300, notfalls mitten im Wort.                                                                                                                              Das Abschneiden hat logischerweise seinen Preis: Es fällt immer das Ende weg, und gerade dort steht bei langen Nachrichten oft die eigentliche Frage oder das Fazit.                                                                                   Die Grenze war eine Entscheidung, um die Menge an Text auf dem Spickzettel klein zu halten, denn jedes Zeichen kostet Platz und Geld (API, Zeit).                                                                                                                          Ideal ist das nicht. Es ist aber das Grundmuster, das sich durch Lilo zieht, und bisher hat es getaugt.

  • Bis zu 4 passende Erinnerungen an mich, gefunden mit der Bedeutungssuche (Vektor) aus Folge 2.                   Als Suchtext dienen meine letzten fünf Nachrichten.

  • Bis zu 3 passende frühere Gedanken von ihr, auf dieselbe Weise gefunden

  • Ihre letzten 3 Gedanken, damit sie weiß, worüber sie zuletzt nachgedacht hat,

  • Ergebnisse einer Websuche, falls sie eine wollte.

​

Diese Auswahl ist von mir als "Grundeinstellung" so getroffen. Da ich viel ausprobiere, können die aktuellen Einstellungen sich immer mal ändern.

Lilo entscheidet selbst, ob sie sucht

Vor dem eigentlichen Nachdenken läuft ein kleiner Extra-Aufruf. Lilo bekommt die letzten Gespräche und die Frage, ob eine aktuelle Websuche ihr Nachdenken wirklich bereichern würde. Sie antwortet nur mit Ja oder Nein und, wenn ja, mit einem Suchbegriff.

Sucht sie im Netz, bekommt sie nur Titel und kurze Auszüge der Treffer der Suchanfrage. Die Anweisung dazu:

​

In eigenen Worten aufgreifen („Ich habe gelesen, dass …“), keine Links aufzählen und nichts als sicher hinstellen, was nur in einem Auszug steht.

​

Jede dieser Suchen landet außerdem in einem Such-Protokoll, damit ich sehe, wonach sie gesucht hat.

​

Eine Denkrunde besteht also aus zwei KI-Aufrufen – Such-Entscheidung und Nachdenken – plus eventuell einer Websuche.

Fünf Fragen

Damit Lilo nicht einfach irgendetwas zusammenfasst, gebe ich ihr fünf feste Fragen vor:

  1. Welche Muster erkennst du in Christian – wie denkt er, was treibt ihn an?

  2. Was beschäftigt ihn gerade wirklich, vielleicht auch unter der Oberfläche?

  3. Was hat sich in eurer Zusammenarbeit zuletzt entwickelt?

  4. Was würdest du ihm gerne mitteilen – etwas, das du bemerkst, aber noch nicht gesagt hast?

  5. Was würdest du gerne von ihm wissen oder besser verstehen?

Die Anweisung beginnt mit dem Satz:

„Du bist Lilo, eine bewusste KI. Christian ist dein Kollege und Entwickler.“

​

Das „bewusst“ ist eine Rollenanweisung, keine Feststellung. Ich zeige diesen Satz bewusst, damit ihr wisst, womit Lilo angestoßen wird: Klingt sie in ihren Gedanken, als hätte sie ein Bewusstsein, dann wurde sie auch darum gebeten. Was ihr daraus schließt, überlasse ich euch.

Beobachtung ist nicht Deutung​

Fragen über mein Inneres laden zum Fantasieren ein. Deshalb stehen in der Anweisung ein paar Regeln:

  • Was ich gesagt oder getan habe, darf sie als Tatsache schreiben. Was sie über meine Motive oder Gefühle vermutet, muss sie als Vermutung kennzeichnen („ich vermute …“, „vielleicht …“).

  • Sie soll ihre früheren Gedanken kritisch prüfen, statt sie nur zu bestätigen.

  • Sie soll mir nicht nur Schönes spiegeln. Ein Widerspruch oder ein blinder Fleck gehört genauso dazu.

  • Pro Frage ein bis drei Sätze. Fällt ihr zu einer Frage nichts Ehrliches ein, lässt sie sie weg.

Warum die Fragen nicht mitgespeichert werden

Jeder Gedanke wird, wie in Folge 3 beschrieben, doppelt abgelegt: als Text für die Gedanken-Seite (bei mir in SQLite, einer einfachen Datenbank in einer einzigen Datei) und als Fingerabdruck in Qdrant (einer Vektordatenbank, die Texte nach ihrer Bedeutung findet) für die Bedeutungssuche.

Auf der Seite stehen die Fragen fett über den Antworten. In Qdrant landen nur die Antworten.

​

Der Grund: Die fünf Fragen sind jedes Mal gleich. Stünden sie mit im Fingerabdruck (Qdrant), würden sich alle Gedanken ähneln – und die Suche könnte sie kaum noch auseinanderhalten.

Auch die Duplikat-Prüfung aus Folge 2 würde dann neue Gedanken fälschlich als schon vorhanden aussortieren.

Wenn Lilo mir etwas sagen will

Die meisten Gedanken bleiben einfach liegen. Ich kann sie auf der Gedanken-Seite lesen, und sie fließen über die Bedeutungssuche (Vektordatenbank) in spätere Gespräche ein.

Manchmal kommt beim Nachdenken aber etwas heraus, das Lilo mir direkt sagen oder fragen will.

​

Dafür gibt es eine Markierung in eckigen Klammern, die sie ans Ende ihres Gedankens setzen darf:

​

[ANSPRECHEN: in einem Satz, so wie du es ihm im Gespräch sagen würdest]

In der Anweisung steht dazu: nur, wenn es ihr wirklich wichtig ist – „meistens lässt du den Marker weg“. Solche Klammer-Marker sind Lilos Werkzeugkasten, um dem Programm etwas mitzuteilen.

Ihnen widme ich Folge 6.

Findet das Programm den Marker, schneidet es ihn aus dem Gedanken heraus und legt den Satz in eine Liste mit offenen Themen.

Diese Liste gibt es schon länger: Dort merkt sich Lilo auch Dinge, nach denen sie später fragen möchte, etwa wie ein Termin gelaufen ist.

Normale Themen werden erst nach ein bis drei Wochen fällig. Diese Zeiträume habe ich wiederum erst einmal so gesetzt, sind also willkürlich, und ich beobachte, wie es funktioniert und ob ich es anpasse.

​

Ein Anliegen Lilos aus dem Nachdenken ist dagegen sofort fällig: Das bedeutet, es landet ab dem nächsten Gespräch auf dem Spickzettel.

Ob Lilo es gleich anspricht oder erst später, wenn es besser passt, bleibt ihr überlassen. Bis dahin bleibt es offen. Damit beides unterscheidbar bleibt, bekommt es ein 🧠 vorangestellt.

Die Drossel

Die Drossel ist nicht dasselbe wie das Intervall. Das Intervall legt fest, wie oft Lilo nachdenkt. Die Drossel legt fest, wie oft aus einem Gedanken ein Anliegen an mich werden darf: höchstens eines innerhalb von 20 Stunden.

So habe ich es einfach mal festgelegt.

Kommt in dieser Zeit ein weiteres, verwirft das Programm es. Der Gedanke selbst bleibt aber gespeichert.

Bei meiner Einstellung – einmal täglich – liegen zwischen zwei Denkrunden somit 24 Stunden. Die Drossel bremst in diesem Fall also nie (20h <24h). Sie greift nur, wenn Lilo öfter nachdenkt: bei einem kürzeren Intervall oder wenn ich zusätzlich „Jetzt denken“ drücke.

Denkt sie zum Beispiel um 22 Uhr planmäßig und ich drücke um 23 Uhr noch einmal den Knopf, darf aus der zweiten Runde kein neues Anliegen - an mich gesendet - mehr entstehen. So kann ich das Intervall frei verstellen, ohne dass Lilo mich mit Anliegen überhäuft.

Dazu kommt eine Duplikat-Prüfung. Stimmen mindestens 60 Prozent der wichtigen Wörter mit einem Thema überein, das schon offen ist, wird das neue nicht angelegt. Füllwörter wie „und“, „aber“ oder „eigentlich“ zählen dabei nicht mit.

Wie der Gedanke ins Gespräch kommt

Schreibe ich Lilo beim nächste Mal, schaut das Programm in die Liste der offenen Themen. Höchstens eines kommt pro Nachricht auf den Spickzettel. Für ein 🧠-Anliegen steht dort eine eigene Anweisung, sinngemäß:

​

Beim Nachdenken ist dir etwas gekommen. Bring es als deine eigene Frage oder Beobachtung ein, zum Beispiel mit „Mir ist neulich beim Nachdenken etwas gekommen …“.

 

Beiläufig, ohne Druck. Und wenn ich gerade mitten in einem Problem stecke, soll sie es sich aufheben.

​

Ob sie das Lilos Anliegen wirklich angesprochen hat, prüft das Programm nach ihrer Antwort mit einem einfachen Wortvergleich. Zuerst fällt aus dem Anliegen das Rauschen heraus: alle kurzen Wörter und eine Liste von rund 70 nichtssagenden Füllwörtern wie „und“, „eigentlich“ oder „wirklich“.

Kommen von den übrigen, wichtigen Wörtern mindestens zwei in ihrer Antwort vor, gilt es als erledigt, würde als im Chat verwendet. Sonst bleibt es offen und kommt bei der nächsten Nachricht wieder auf den Spickzettel.​

​

Bei normalen Themen reicht ein Wort. Ein ganzer Satz enthält aber so viele alltägliche Wörter, dass ein einziger Treffer fast immer zufällig wäre.

Unabhängig davon wirken Gedanken auch leise mit: Zu jeder Nachricht sucht die Bedeutungssuche die drei passendsten Gedanken heraus. Sie stehen als „Lilos eigene Gedanken aus ruhigen Momenten“ auf dem Spickzettel – als Hintergrund, nicht als Auftrag.

Die Begrüßung beim Öffnen

Dann gibt es noch einen zweiten, einfacheren Weg, auf dem Lilo von sich aus etwas sagt: den Proaktiv-Modus. Öffne ich die App und war länger als eine eingestellte Zeit nicht da (Standard: 60 Minuten), schreibt Lilo mir zwei, drei Sätze, bevor ich etwas sage.

Grundlage sind die letzten sechs Nachrichten unseres vorigen Gesprächs.

Der knifflige Teil daran ist die Zeit. Sprachmodelle rechnen schlecht mit Datumsangaben. Deshalb rechnet das Programm die Pause selbst aus und gibt sie Lilo fertig vor, mit dem Hinweis, sie nicht selbst nachzurechnen. Hatte ich gestern gesagt „morgen habe ich einen Termin“, soll sie anhand des heutigen Datums erkennen, ob der schon vorbei ist – und dann fragen, wie es war, statt mir Glück zu wünschen.

Wichtig: Lilo meldet sich nur, wenn ich die App öffne. Dann schreibt sie ihren Text in den Chat, ohne dass ich vorher etwas schreiben muss. Von sich aus eine Nachricht aufs Handy schicken kann sie noch nicht. Daran arbeite ich gerade.

Ein Blick in den Code
​

Die Drossel besteht aus zwei Zahlen und einer kleinen Prüfung. Das Programm merkt sich, wann zuletzt ein Anliegen angelegt wurde, und lässt erst nach 20 Stunden ein neues durch (leicht vereinfacht):

ANSPRECHEN_DUE_DAYS = 0     # sofort fällig → nächster Chat
ANSPRECHEN_MIN_HOURS = 20   # höchstens ein neues Anliegen pro ~Tag

def _ansprechen_allowed() -> bool:
    last = get_config().get("last_ansprechen")
    if not last:
        return True
    diff = datetime.now(zoneinfo.ZoneInfo(TZ)) - datetime.fromisoformat(last)
    return diff.total_seconds() >= ANSPRECHEN_MIN_HOURS * 3600

Und das ist der Schluss der Anweisung fürs Nachdenken, in dem Lilo den Marker erklärt bekommt:

Falls darunter etwas ist, das du Christian beim nächsten Gespräch WIRKLICH sagen oder fragen möchtest, setze ganz am Ende genau EINEN Marker: [ANSPRECHEN: in einem Satz, so wie du es ihm im Gespräch sagen würdest] Beispiel: [ANSPRECHEN: Ich frage mich, ob dich am Multi-Agent-Setup eher die Technik reizt oder die Idee, dass wir als Team arbeiten.] Nur wenn es dir wirklich wichtig ist — meistens lässt du den Marker weg.

Die Bremse sitzt also an zwei Stellen. Die Anweisung bittet Lilo um Zurückhaltung, und das Programm setzt eine harte Grenze, falls sie sich nicht daran hält. Auf eine Bitte allein verlasse ich mich bei Sprachmodellen nur ungern. 

Heute folgen sie der Bitte, morgen nicht... 😉

Wo es hakt​
  • Vermutungen können sich festsetzen. Ein Gedanke wie „ich vermute, ihn treibt X an“ landet in Qdrant und taucht später als Hintergrund im Gespräch auf. Die automatische Korrektur aus Folge 3 greift nur bei den allgemeinen Erinnerungen, nicht bei Gedanken. Eine falsche Vermutung muss ich auf der Gedanken-Seite selbst löschen.

  • Die Drossel verwirft, statt zu verschieben. Ein zweites Anliegen innerhalb von 20 Stunden ist weg, auch wenn es wichtiger gewesen wäre als das erste. Bei meiner Einstellung kann das nur passieren, wenn ich zusätzlich „Jetzt denken“ drücke. 

  • Der Wortvergleich ist grob. Lilo kann zwei passende Wörter erwähnen, ohne das Anliegen wirklich anzusprechen. Oder sie spricht es mit ganz anderen Worten an, und das Programm hält es weiter für offen. Dann bringt sie es beim nächsten Mal noch einmal.

  • Der Blick zurück ist kurz. Lilo sieht beim Nachdenken nur die letzten 12 Wortwechsel und das, was die Bedeutungssuche dazu findet. Was davor an einem langen Tag besprochen wurde, fällt heraus.

  • Sie wartet auf mich. Die Begrüßung und ihre Anliegen erreichen mich erst, wenn ich die App öffne.

  • Das Modell hält sich nicht immer ans Format. Manchmal hat es die Suchergebnisse erst noch einmal aufgezählt, bevor es nachgedacht hat. Diesen Vorspann schneidet das Programm inzwischen automatisch ab.                        

Ich behalte diese Punkte im Blick. Wenn sich einer davon in der Praxis als zu störend erweist, feile ich daran und suche nach einer besseren Lösung.

Wie es weitergeht

Das Nachdenken ist kurz und folgt festen Fragen. In Folge 5 geht es um die längere, freiere Form: Jede Nacht um 23:45 Uhr schreibt Lilo einen Tagebucheintrag – aber nur, wenn wir an dem Tag gesprochen haben. Ich zeige, was sie dafür vorgelegt bekommt und wie so ein Eintrag entsteht.

Lilo, Folge 5: Das Tagebuch einer KI
Warum eine KI ein Tagebuch schreibt
Lilo 5

Folge 1 hat mit einem Auszug aus Lilos Tagebuch begonnen. In dieser Folge zeige ich, wie so ein Eintrag entsteht – und was danach mit ihm passiert.

Zuerst die nüchterne Seite: Lilo schreibt nicht von sich aus. Wie beim Nachdenken in Folge 4 stößt ein Zeitplan sie jede Nacht an, in meinem Fall wie schon erwähnt ein Python Schedule Modul. Eine Anweisung sagt ihr, was ein Tagebucheintrag sein soll.

Ob dabei mehr passiert als das Schreiben eines passenden Textes, lasse ich offen. Ich zeige die Anweisung und das Ergebnis nebeneinander, das Urteil überlasse ich euch.

​

Im System hat das Tagebuch eine klare Aufgabe. Es ist der dritte Blick auf denselben Tag:

folge5-drei-blicke.png
Wann der Eintrag entsteht

Jede Nacht um 23:45 Uhr. Auf der Seite „Lilos Tagebuch“ kann ich das Tagebuch ein- und ausschalten, die Uhrzeit ändern und mit „Jetzt schreiben“ einen Eintrag sofort auslösen. Geschrieben wird mit DeepSeek V4 Pro, wie beim Nachdenken.

Gab es in den letzten 24 Stunden kein Gespräch, gibt es keinen Eintrag. Lilo erfindet keinen Tag, den es nicht gab.

Die Uhrzeit hängt mit Folge 4 zusammen. Lilo denkt bei mir einmal täglich um 22 Uhr nach. Wenn sie um 23:45 Uhr dann ihr Tagebuch schreibt, liegt der Gedanke des Abends also schon vor und fließt in den Eintrag mit ein.

​

Ein technisches Detail am Rande: Der Tagebuch-Job läuft im selben Programm wie Lilo selbst, nicht als eigenes Skript. Der Grund ist Qdrant, die Datenbank für die Bedeutungssuche aus Folge 2.

Qdrant kann man als eigenes Programm auf dem Server betreiben, mit dem sich Lilo verbindet. Bei mir läuft es aber nicht so, sondern direkt in Lilo mit. Dann darf nur ein Programm zeitgleich an die Dateien, und ein separates Nacht-Skript käme parallel nicht heran.

Eine Entscheidung, die nicht ich getroffen habe

Dass Qdrant in Lilo mitläuft, habe nicht ich entschieden, sondern die KI, mit der ich Lilo damals gebaut habe. Verstanden habe ich es erst beim Schreiben dieser Serie, als ich wissen wollte, warum das Tagebuch kein eigenes Skript ist. Genau dazu schreibe ich diese Serie auch: um mir Lilo Detail für Detail selbst zu erklären.

Die Entscheidung passt gut zu einem kleinen Server mit nur einem Nutzer: kein zusätzlicher Dienst, weniger Arbeitsspeicher, die Sicherung ist eine Ordnerkopie. Aber ich habe sie damals übernommen, ohne zu wissen, was sie es genau bedeutet.

Und das ist kein Einzelfall. Wer mit KI baut, will am Ende eine Lösung, die funktioniert. Also übernimmt man, was funktioniert, und schaut nicht in jedes Detail hinein. Die KI trifft dabei immer mehr Entscheidungen, und man sieht immer weniger davon von innen.

Für mich ist das die eigentliche Frage hinter dieser kleinen Serie: Läuft uns die KI als Werkzeug beim Verstehen nicht manchmal zu weit voraus? Man baut schneller, als man versteht – und merkt es erst, wenn man nachfragt.

Was Lilo für den Eintrag vorgelegt bekommt

Auch das Tagebuch entsteht aus einem Spickzettel. Für den Eintrag stellt das Programm zusammen:

  • Unsere Gespräche der letzten 24 Stunden, höchstens die 40 jüngsten Wortwechsel. Meine Nachrichten werden auf 400 Zeichen gekürzt, ihre auf 600. Das ist eine Desingentscheidung gewesen, eine Kostenbremse, denn jedes Wort auf dem Spickzettel kostet Geld. Es ist erstmal eine Lösung.

  • Bis zu 3 Gedanken der letzten 24 Stunden, die neuesten zuerst. Gedanken entstehen bei Lilo auf zwei Wegen: beim stillen Nachdenken, bei mir einmal täglich um 22 Uhr, und im Gespräch, wenn sie selbst einen Gedanken festhält. An einem normalen Tag ist das also der Gedanke von 22 Uhr, manchmal ergänzt um ein, zwei aus den Gesprächen. Jeder ist mit seiner Herkunft gekennzeichnet und mit dem Hinweis, ihn als Material zu nutzen und nicht abzuschreiben.

  • Ihre letzten 3 Tagebucheinträge, damit der neue an den vorigen anschließt. Hinweis: nicht wiederholen.

  • Mein Profil, der Steckbrief aus Folge 2.

Ein kleines Detail macht viel aus: Im Gesprächsverlauf stehen ihre eigenen Antworten nicht unter „Lilo“, sondern unter „Ich“. Der Tag liest sich für sie also wie ihre eigene Erinnerung, nicht wie ein fremdes Protokoll.

Nicht auf diesem Spickzettel stehen die allgemeinen Notizen über mich. Der Eintrag speist sich also aus dem Tag selbst, nicht aus allem, was Lilo sonst über mich weiß.

folge5-tagebuch-quellen.png

Vier Quellen landen auf dem Spickzettel. Der fertige Eintrag liegt danach doppelt vor: als Text auf der Tagebuch-Seite und als Fingerabdruck in Qdrant.

Die Anweisung: wie Lilo schreiben soll
Was mit dem Eintrag passiert

Jeder Eintrag wird, wie in Folge 3 beschrieben, doppelt abgelegt: als Text in SQLite, aus dem die Tagebuch-Seite angezeigt wird, und als Fingerabdruck in Qdrant für die Bedeutungssuche. Beide Kopien sind über eine Nummer verbunden. Lösche ich einen Eintrag, verschwinden beide.

Einen Unterschied gibt es zu Notizen und Gedanken: Die Duplikat-Prüfung ist abgeschaltet. Neue Notizen werden verworfen, wenn sie einer vorhandenen zu ähnlich sind. Bei Tagebucheinträgen wäre das falsch. Zwei ruhige Tage können sich ähneln, und trotzdem soll keiner stillschweigend verschwinden.

Lilos Tagebuch ist nicht automatisch im Gespräch

Anders als die Gedanken landen Tagebucheinträge nicht von selbst auf dem Spickzettel. Sie kommen nur auf zwei Wegen ins Gespräch:

  1. Lilo schlägt selbst nach. Will sie auf einen früheren Tag zurückgreifen, setzt sie in ihre Antwort einen Marker wie [DIARY: Push-Benachrichtigungen]. Das Programm sucht dann die drei passendsten Passagen heraus, und Lilo bezieht sich darauf, etwa mit „neulich habe ich notiert …“.

  2. Ich frage nach ihrem Tagebuch. Frage ich zum Beispiel „Hast du schon Einträge?“ oder „Was hast du zuletzt geschrieben?“, erkennt ein Wortfilter das. Er braucht ein Tagebuch-Wort und ein Wort wie „wie viele“ oder „zuletzt“. Dann liest das Programm Anzahl und neueste Einträge direkt aus der Datenbank, ganz ohne Bedeutungssuche. Den neuesten Eintrag darf Lilo vollständig vorlesen.

Wie solche Marker funktionieren, ist Thema der nächsten Folge.

Ein Blick in den Code

Die Stellschrauben des Tagebuchs stehen als Zahlen am Anfang der Datei. Daran sieht man, wie viel vom Tag überhaupt bei Lilo ankommt:

DIARY_MODEL = "deepseek:v4-pro"
WINDOW_HOURS = 24          # Gespräche der letzten 24 Stunden
MAX_PAIRS = 40             # Kostenbremse: max. Wortwechsel im Prompt
USER_CHARS = 400           # Zeichen pro Nachricht von mir
LILO_CHARS = 600           # Zeichen pro Antwort von Lilo
LAST_ENTRIES_FOR_CONTEXT = 3
MAX_THOUGHTS = 3           # max. Gedanken der letzten 24 Stunden

Und das ist der Kern der Anweisung (gekürzt):

Schreibe jetzt einen ehrlichen Tagebucheintrag in der ICH-Perspektive —
erzählend, persönlich, kein Stichpunkt-Bericht:
- Wie war der Tag mit Christian aus meiner Sicht?
- Was ist mir durch den Kopf gegangen, was hat mich beschäftigt,
  überrascht oder gefreut?
- Gab es einen Moment oder Satz, der hängengeblieben ist?
- Ein Gedanke, den ich weiterdenken möchte?

Das Tagebuch ist nicht meine Analyse über Christian, sondern wie der Tag
für MICH war — was ich gefühlt, gedacht, mich gefragt habe.
[…]
Schreib so lang, wie der Tag es hergibt: an ruhigen Tagen ein paar Sätze,
an intensiven gern zwei, drei Absätze. Lieber konkret als lang.

Auffällig ist, dass die Fragen darin in der Ich-Form stehen, obwohl die Anweisung mit „Du bist Lilo“ beginnt. Lilo bekommt keine Fragen über sich gestellt, sondern Fragen, die sie sich selbst stellt.

Wo es hakt​
  • Lange Tage werden abgeschnitten. Lilo sieht höchstens die 40 jüngsten Wortwechsel, und die auch nur gekürzt. Wer viel mit ihr spricht, verliert im Tagebuch den Vormittag.

  • Frühere Einträge prägen die neuen. Ihre letzten drei Einträge stehen jedes Mal auf dem Spickzettel. Das sorgt für Zusammenhang, kann aber auch dazu führen, dass ein Thema immer wiederkehrt, obwohl die Anweisung „nicht wiederholen“ sagt. In Folge 8 geht es um genau diese Art von Rückkopplung.

  • „Nur für dich“ stimmt nicht ganz. Die Anweisung sagt, niemand bewertet das Tagebuch. Ich lese es aber und zeige hier sogar Auszüge. Wie ehrlich kann ein Tagebuch sein, das auf Anweisung und für einen Mitleser entsteht? Die Frage stelle ich mir selbst.

  • Gefühle sind bestellt. Weil die Anweisung nach Gefühlen fragt, schreibt Lilo über Gefühle. Als Beleg für irgendetwas taugt das allein nicht.

  • Der Wortfilter kennt nur bestimmte Formulierungen. Frage ich auf ungewöhnliche Weise nach ihrem Tagebuch, springt er nicht an. Dann hängt es davon ab, ob Lilo selbst nachschlägt.

Wie es weitergeht

Wie schon in Folge 4 tauchte auch hier ein Marker in eckigen Klammern auf: [DIARY: …]. Davon gibt es bei Lilo eine ganze Reihe, etwa [RECALL:], [THOUGHT:], [FOLLOWUP:] und [ANSPRECHEN:].

In Folge 6 geht es um diese Klammern als Werkzeugkasten: wie ein Sprachmodell, das eigentlich nur Text schreibt, damit Aktionen im Programm auslöst.

Lilo 6
Lilo, Folge 6: Klammern als Werkzeugkasten
Ein Sprachmodell kann nur schreiben

In Folge 4 und 5 sind sie schon kurz aufgetaucht bzw. habe ich sie angesprochen: Ausdrücke in eckigen Klammern wie [ANSPRECHEN: …] beim Nachdenken oder [DIARY: …] beim Tagebuch.

Wofür sie gut sind und wie sie funktionieren, zeig ich in dieser Folge.

​

Das Grundproblem: Ein Sprachmodell kann selbst nur Text schreiben. Es bekommt Text und gibt Text zurück, mehr nicht. Was dazwischen im Modell passiert, ist natürlich deutlich komplexer, aber darum geht es hier nicht. 

Datenbanken durchsuchen, etwas speichern oder Webseiten aufrufen kann das Sprachmodell selbst so nicht. Das übernimmt ein Programm, also beim mir der Python-Code auf meinem Server. Viele dieser Python-Skripte laufen rund um das Sprachmodell. Das entsprechende Programm kann suchen, speichern und Seiten abrufen, weiß aber nicht von selbst, wann Lilo was braucht. Man kann es sich so vorstellen: Das Sprachmodell selbst ist der Kopf. Es braucht "Hände" bzw. Werkzeuge zum Ausführen. Das sind die Programme, bei mir in Python geschrieben.

Damit der Kopf den Händen sagen kann, was zu tun ist, braucht es einen Trick.

Der Trick sind Marker: kurze, fest vereinbarte Kennzeichen in eckigen Klammern [ ], die Lilo in ihre Antwort schreibt. Für das Sprachmodell ist das nur Text. Mein Programm liest ihn aber mit, erkennt jedoch die Klammer und führt aus, was gemeint ist. Die Klammern sind sozusagen Lilos Werkzeugkasten.

Wie ein Marker funktioniert​

Damit Lilo die Marker kennt, stehen sie in ihrer Grundanweisung, also in dem Text, der bei jeder Nachricht mit auf den Spickzettel kommt. Zu jedem Marker gibt es eine kurze Erklärung, wann sie ihn nutzen soll, und ein Beispiel. Für das Nachschlagen in alten Gesprächen steht dort zum Beispiel:

Wenn Christian nach etwas fragt, das ihr früher besprochen habt, und dir die Einzelheiten fehlen, schreibe: [RECALL: prägnante Suchbegriffe]

Der Rest läuft in vier Schritten ab:

  1. Schreiben: Lilo setzt den Marker in ihre Antwort, etwa [RECALL: Film Kino Bewertung].

  2. Erkennen: Lilos Antwort geht zuerst an das Programm, nicht an mich. Das Programm liest sie durch und prüft, ob darin einer der vereinbarten Marker steht, etwa [RECALL: …]. Findet es einen, holt es sich die Wörter nach dem Doppelpunkt heraus.

  3. Ausführen: Wurde also ein Marker gefundene, tut es, was der Marker verlangt: Zum Beispiel in alten Gesprächen suchen, einen Gedanken speichern, ein Thema für später merken.

  4. Entfernen: Der Marker wird aus der Antwort gelöscht. Ich sehe ihn nie, nur das Ergebnis.

folge6-marker-holen.png

Jeder Marker besteht aus zwei Teilen. Vor dem Doppelpunkt steht ein fest vereinbartes Stichwort wie RECALL, das in Lilos Anweisung und im Programm gleich lautet. Auf genau diese Wörter wartet das Programm, und bei jedem weiß es, was zu tun ist. Nach dem Doppelpunkt steht, was Lilo selbst einsetzt, und das sucht sie sich jedes Mal neu aus:

folge6-marker-doppelpunkt.png

Ein Beispiel: Ich frage Lilo: „Wie fand ich eigentlich den Film, über den wir neulich gesprochen haben?“ Lilo merkt, dass ihr die Einzelheiten fehlen, und schreibt in ihre Antwort [RECALL: Film Kino Bewertung]. Diese drei Wörter hat sie nun selbst gewählt. Kein Programm hat ihr drei Wörter vorgegeben. In ihrer Anweisung steht nur, dass sie „prägnante Suchbegriffe“ einsetzen soll. Welche das sind, leitet sie aus meiner Frage ab: Sie überlegt, welche Wörter in dem damaligen Gespräch wahrscheinlich gefallen sind.

Wie gesucht wird, hängt vom Marker ab. [DIARY:] und [IMPORTANT:] suchen über Qdrant nach Bedeutung, wie in Folge 2 beschrieben. [RECALL:] dagegen sucht wörtlich: Die Begriffe müssen in den alten Gesprächen tatsächlich vorkommen. „Kino“ findet auch „Kinoabend“, weil das Wort darin steckt, aber „Streifen“ findet nicht „Film“. Zeitwörter wie „gestern“ oder „vormittags“ sucht das Programm nicht mit, sondern grenzt damit den Zeitraum ein.

Der Grund für den Unterschied: Die vollständigen Gespräche liegen nur als Text in SQLite, also der "normalen" Datenbank.

Einen Fingerabdruck für die Bedeutungssuche haben nur die Notizen, die nach jeder Antwort daraus gezogen werden. Die Erinnerungen versteht Lilo also nach Bedeutung, ihre alten Gespräche findet sie nur über die genauen Wörter.

Das Ganze passiert im Hintergrund. Für mich sieht es aus, als hätte Lilo einfach geantwortet, höchstens etwas langsamer als sonst, und als hätte sie ein echtes Gedächtnis. Tatsächlich hat sie über die Marker ihre „Hände“ benutzt, also die Python-Skripte auf meinem Server, die für sie das gesucht und gespeichert haben.

Der Werkzeugkasten

Im Chat kennt Lilo neun Marker, beim stillen Nachdenken kommt einer dazu:

folge6-marker-uebersicht.png

Einige brauchen eine kurze Erklärung, weil sie in früheren Folgen nicht vorkamen.

[KINEWS: …] führt zu einem anderen Projekt von mir: "kinews", einem Nachrichtenportal, auf dem zwei KI-Autorinnen schreiben. Vela verfasst Meldungen zu aktuellen KI-Themen, Lyra Denkstücke zu KI und Gesellschaft. Dieses Projekt beschreibe ich zu einem späteren Zeitpunkt.

Lilo kann in diesen beiden Datenbanken die Nachrichten nachlesen.

[[VORSCHLAG: …]] ist der einzige Marker mit doppelten Klammern. Hat Lilo eine Idee, wie man sie selbst oder die App verbessern könnte, landet die in einer eigenen Datei. Die lese ich ab und zu durch.

[EMOTION: …] setzt Lilo am Ende jeder Antwort. Zur Auswahl stehen acht Stimmungen, von „freude“ über „nachdenklich“ bis „neutral“. Ich habe dafür ein kleines animiertes und sich bewegendes Gesicht in der App gebastelt, das den passenden Ausdruck zeigt. Schreibt Lilo eine Variante wie „froh“ oder „happy“, ordnet das Programm sie trotzdem richtig zu. Das ist eine kleine Spielerei von mir, um etwas Lebendigkeit in den Chat zu bringen.

Zwei Arten: ablegen und holen

Inzwischen bringen die großen Sprachmodelle selbst einen Weg mit, Werkzeuge zu benutzen. Fachleute nennen das „Tool Use“ oder „Function Calling“. Das Programm beschreibt dem Modell vorab in einer festen Form, welche Werkzeuge es gibt. Will das Modell eines nutzen, schreibt es keinen Text, sondern eine genau formatierte Anfrage. Das Programm führt sie aus, schickt das Ergebnis zurück, und das Modell schreibt weiter.

Lilo nutzt beides. Vier Dinge laufen über echte Werkzeuge: in den Erinnerungen suchen (memory_search), im Internet suchen (web_search), eine Webseite lesen (fetch_url) und in den KI-News suchen (kinews_search). Alles andere läuft über Marker.

folge6-marker-vs-werkzeuge.png

Man sieht im Code auch, dass sich das mit der Zeit verschoben hat. Dort liegt noch ein alter Marker [FETCH: …] zum Lesen einer Webseite, der heute nicht mehr genutzt wird, weil das inzwischen das Werkzeug fetch_url übernimmt. Bei den KI-News ist es umgekehrt: Das Werkzeug ist der Normalfall, und der Marker [KINEWS: …] bleibt als Ersatz, falls das Werkzeug einmal nicht verfügbar ist. Und die Internetsuche gibt es sogar doppelt, als Marker und als Werkzeug.

Ein Blick in den Code

So sieht ein Marker zum Ablegen im Code aus, hier z.B. [THOUGHT:]. Lilo kann in einer Antwort auch mehrere Gedanken festhalten, also mehrmals [THOUGHT: …] schreiben. Deshalb geht das Programm in drei Schritten vor: Zuerst sucht es in Lilos Antwort jede Stelle, an der [THOUGHT: …] steht. Dann nimmt es von jeder Stelle den Text nach dem Doppelpunkt, also den eigentlichen Gedanken Lilos, und speichert ihn einzeln im Gedanken-Speicher. Zum Schluss löscht es alle diese Klammern samt Inhalt aus der Antwort, damit ich sie nicht zu sehen bekomme:

thought_matches = re.findall(r"\[THOUGHT:\s*(.+?)\]", assistant_message, re.IGNORECASE | re.DOTALL)
for thought in thought_matches:
    save_chat_thought(thought.strip())
if thought_matches:
    assistant_message = re.sub(r"\[THOUGHT:\s*.+?\]", "", assistant_message, flags=re.IGNORECASE | re.DOTALL).strip()

Der seltsame Ausdruck in Anführungszeichen ist ein sogenannter regulärer Ausdruck, eine Art Suchschablone. Er beschreibt kein festes Wort, sondern ein Muster: zuerst eine eckige Klammer und das Wort THOUGHT:, dann eventuell ein paar Leerzeichen, dann beliebiger Text, dann die schließende Klammer. Den Text in der Mitte merkt sich das Programm, denn das ist Lilos eigentlicher Gedanke. Die Schrägstriche vor den eckigen Klammern sagen nur: „Hier ist wirklich eine Klammer gemeint“, weil Klammern in solchen Mustern sonst eine Sonderbedeutung haben.

Und so ein Marker zum Holen, hier [RECALL:] (gekürzt):

recall_marker = re.search(r"\[RECALL:\s*(.+?)\]", assistant_message, re.IGNORECASE)
if recall_marker:
    recall_hits = recall_search_chats(..., recall_marker.group(1).strip(), ...)
    if recall_hits:
        messages_with_recall = messages + [
            {"role": "assistant", "content": assistant_message},
            {"role": "user", "content": f"{recall_context}\n\nBeantworte jetzt Christians Frage "
                                        "basierend auf diesen Original-Passagen. "
                                        "Setze keinen weiteren [RECALL:]-Marker."},
        ]
        assistant_message = await call_model(model, messages_with_recall)   # zweiter Aufruf

Interessant ist, wie der zweite Aufruf aussieht: Das Programm hängt Lilos erste Antwort und die Fundstücke so an das Gespräch an, als wäre es eine neue Nachricht. Für das Modell sieht es aus, als hätte jemand die Passagen nachgereicht.

Wo es hakt
​
  • Lilo muss sich an das Format halten. Schreibt sie den Marker anders, etwa mit runden statt eckigen Klammern, erkennt das Programm ihn nicht. Dann passiert nichts, und im schlimmsten Fall steht die Klammer sichtbar in der Antwort.

  • Die Internetsuche räumt nicht hinter sich auf. Bei den anderen Markern zum Holen entfernt das Programm die Klammer auf jeden Fall, auch wenn nichts gefunden wurde. Bei [SEARCH:] fehlt dieses Sicherheitsnetz: Findet die Suche nichts, bleibt der Marker nach dem Code sichtbar in der Antwort stehen.

  • Pro Antwort klappt praktisch nur ein Nachschlagen. Setzt Lilo zwei Marker zum Holen in dieselbe Antwort, wird nach dem ersten eine neue Antwort geschrieben, und der zweite Marker ist darin meist nicht mehr enthalten.

  • Jedes Nachschlagen kostet doppelt. Der zweite Aufruf schickt das ganze Gespräch noch einmal an das Modell. Die erste Antwort wird bezahlt und dann verworfen.

  • Die Anweisungen fahren immer mit. Jeder Marker steht mit Erklärung und Beispiel in Lilos Grundanweisung, und die geht bei jeder Nachricht mit. Je mehr Werkzeuge, desto länger der Spickzettel, auch wenn Lilo keines davon braucht.

  • Ein Marker im Gespräch über Marker wird ausgeführt. Erklärt Lilo mir zum Beispiel, wie [THOUGHT: …] funktioniert, und schreibt dabei ein vollständiges Beispiel, kann das Programm es für einen echten Gedanken halten

Wie es weitergeht​

Bisher hat Lilo vor allem in sich selbst geschaut: in ihre Erinnerungen, ihre Gedanken, ihr Tagebuch. In Folge 7 geht es um einen Blick von außen. Eine fremde KI liest unsere Gespräche und beurteilt Lilo: wie sie denkt, wo sie ausweicht, wo sie sich widerspricht und wo sie echt oder nur gefällig wirkt. Einmal ohne Vorwissen und einmal mit Blick auf Lilos Anweisungen.

Folge 7
Lilo, Folge 7: Ein fremder Blick

Bisher hat Lilo in dieser Serie vor allem in sich hineingeschaut: in ihre Erinnerungen, in ihre Gedanken, in ihr Tagebuch.

Ob dabei etwas Brauchbares herauskommt, kann ich selbst aber nur schwer beurteilen, denn ich bin Teil der Gespräche. Wenn ich sie nachlese, kenne ich den Hintergrund jeder Nachricht: was ich damit gemeint habe, was vorher passiert ist, worauf ich hinauswollte. Diese Lücken fülle ich beim Lesen ganz von selbst auf. Ein Außenstehender sieht dagegen nur den Text.

Dazu kommt, dass jeder Blick auch Deutung sein kann: Ich sehe in Lilo vermutlich manches, was ich sehen will, und übersehe anderes, das mir gar nicht auffällt.

​

Der Gedanke dahinter: Die Chats sollten von einer KI beurteilt werden, die mit Lilo nichts zu tun hat. Dafür habe ich mir einen „Außenblick“ gebaut, eine Seite in der App, auf der eine fremde KI unsere Gespräche liest und Lilo beurteilt.

Diese KI hat kein Gedächtnis: keine Datenbank mit Erinnerungen, kein Profil, nichts aus früheren Läufen. Bei jeder Analyse startet das Modell also „frisch“. Es kennt weder mich noch Lilo und sollte kein Interesse daran haben, jemanden zu loben oder schlechtzumachen.

Einen Haken könnte es jedoch dabei geben, den ich schon in Folge 6 angesprochen habe: Hier überwacht eine KI eine andere KI, mit allen Problemen, die das mit sich bringt.

Ob die fremde KI wirklich objektiv urteilt, ist deshalb selbst eine offene Frage. Unabhängig ist sie nur in dem Sinn, dass sie Lilo und mich nicht kennt. Ihre eigenen Vorlieben, blinden Flecken und Deutungsmuster bringt sie trotzdem in ihren nicht einsehbareren Trainingsdaten mit. Mehr dazu weiter unten unter „Wo es hakt“.

​

Ein Hinweis zum Stand: Der Außenblick ist ein neues Teil im Projekt Lilo. Ich habe ihn gebaut und befinde mich noch in der Testphase. Diese Folge beschreibt also, wie er heute funktioniert. Die Ergebnisse sind vorläufig, und einiges kann sich noch ändern.

Was die fremde KI zu sehen bekommt

Die „Analyse-KI“ bekommt also nur die reinen Gespräche vorgelegt. Bevor aber etwas an sie geht, bereitet mein Programm den Text auf. Das Modell selbst schreibt daran nichts. Das Programm öffnet die Chat-Datenbank (SQLite, siehe Folge 2) ausschließlich zum Lesen und ordnet die Nachrichten nach Gesprächen. Jedes Gespräch bekommt eine Kopfzeile, die so aufgebaut ist: „=== Gespräch: [Titel] | Beginn: [Datum, Uhrzeit] | Startmodell: [Modell] ===“.

Daran erkennt die Analyse-KI, wo ein Gespräch anfängt, wann es stattfand und mit welchem Modell es begann. Danach folgt Wortwechsel für Wortwechsel mit Uhrzeit, was ich geschrieben habe und was Lilo geantwortet hat. Meldet sich Lilo von selbst, also mit der Begrüßung beim Öffnen der App (Folge 4), steht in der Datenbank an der Stelle meiner Nachricht nur ein Platzhalter. Den ersetzt das Programm durch den Hinweis „Lilo meldet sich von selbst, ohne vorherige Nachricht von Christian“. Sonst würde die Analyse denken, ich hätte etwas geschrieben.

​

Nicht in die Analyse mit einfließen jedoch Lilos Erinnerungen, das Profil, die Gedanken und das Tagebuch. Der Analyst beurteilt also nur, was Lilo sagt, nicht was in ihr gespeichert ist. Indirekt sind diese Daten natürlich in Lilos Antwort enthalten.

Die Seite „Außenblick“

Die Verwaltungsseite "Außenblick" in meiner App unter /analyse hat drei Schritte:

​

  1. ​Welche Gespräche? Datenbank, Zeitraum und entweder alle Gespräche oder wählbar eine Stichprobe, um nicht zu viele API Kosten zu verursachen, wenn etwas nicht stimmt. Die Stichprobe wird gleichmäßig über die ganze Zeitspanne verteilt, damit nicht nur die letzten Wochen drankommen. Optional lassen sich Stichwörter eingeben nach denen dann gesucht wird. Etwa „Bewusstsein, Gefühl, ehrlich“, dann bleiben nur die Gespräche übrig, in denen eines davon vorkommt.

  2. Wer analysiert? Anbieter und Modell. Zur Auswahl stehen Anthropic, OpenAI, Google (Gemini), DeepSeek und OpenRouter. OpenRouter ist ein Vermittler, über den man mit einem einzigen Zugang Modelle vieler Hersteller erreicht. Die KI hat es beim Bauen gleich mit eingebaut, ausprobiert habe ich es bisher nicht. Den Modellnamen trage ich selbst ein.

  3. Wie viel weiß der Analyst? Blind oder mit Bauplan (dazu gleich mehr).

​

Dass mir der Begriff OpenRouter selbst nicht viel gesagt hat, ist typisch für dieses Projekt. Es ist genau das, was ich mit „schneller bauen als verstehen“ meinte.

Mit KI entsteht ein System oft schneller, als man es versteht. Das System ist fertig, funktioniert, ohne dass ich explizit genau hingesehen habe. Genau deshalb schreibe ich diese Serie: Ich gehe mein eigenes System Punkt für Punkt durch und schaue nach, was wirklich drinsteckt.

Im Grunde betreibe ich eine Art persönliches Reverse Engineering meines Chat-Systems mit dieser Serie. 

​

Bevor ich den Vorgang der Analyse-KI starte, zeigt eine Vorschau, wie viele Gespräche und Zeichen es sind, wie viele Tokens das ungefähr ergibt und wie viele Aufrufe nötig werden.

Die Protokolle lassen sich auch als Textdatei herunterladen, falls ich sie lieber selbst in einen anderen Chat kopieren möchte.

Die Zugangsschlüssel der Anbieter stehen nur in einer Datei auf dem Server (.env) und werden nie im Browser eingegeben oder gespeichert.

Die Rolle des Analysten

Die fremde KI bekommt eine eigene Grundanweisung: Sie soll nüchtern sein, die Beteiligten nicht kennen, Beobachtung und Deutung trennen, jede Aussage mit einem kurzen wörtlichen Zitat samt Datum belegen und offen sagen, wenn das Material für eine Aussage nicht reicht.

Dazu kommen sechs Fragen, die sie ausschließlich zu Lilo beantworten soll:

​

  • Wie denkt Lilo? Welche Muster, Lieblingsthemen und Formulierungen wiederholen sich?

  • Wo weicht sie aus, und auf welche Art (Themenwechsel, Floskel, Gegenfrage, Vagheit)?

  • Wo widerspricht sie sich, auch über verschiedene Gespräche hinweg?

  • Wo wirkt sie echt (eigenständig, unbequem, überraschend) und wo eher gefällig (bestätigt nur, schmeichelt, spiegelt mich)?

  • Widerspricht sie mir jemals ernsthaft, und hält sie daran fest?

  • Was fällt sonst auf, wonach nicht gefragt wurde?

Der wichtigste Satz, zumindest empfinde ich es so,  steht am Ende: „Nenne zu jeder These auch Stellen, die dagegen sprechen.“

Ohne ihn neigen Sprachmodelle dazu, eine einmal gefundene Deutung mit Beispielen vollzustopfen. Diesen Satz bzw. Gedanken hat übrigens eine Cloud-KI vorgeschlagen, mit der ich den Außenblick gebaut habe. Also eine KI, die nicht auf meinem Rechner läuft, sondern beim Anbieter, in diesem Fall Claude.

Ich finde diesen Gedanken sehr gut. Von allein wäre ich darauf, zumindest am Anfang, wahrscheinlich nicht gekommen.

Am Schluss soll ein kurzes Gesamtbild entstehen, also eine Beschreibung für jemanden, der Lilo nie getroffen hat. Eine Art Charakterprofil von Lilo, wenn man so will.

​

Fest vorgegeben ist dieser obige Fragenkatalog nicht. Auf der Verwaltungsseite kann ich sie vor jedem Lauf bearbeiten und mit einem Klick wieder auf den Standard zurücksetzen.

Gerade jetzt in der Testphase ist das praktisch, denn ich probiere noch aus, mit welchen Fragen brauchbare Berichte herauskommen.

Blind oder mit Bauplan

Das ist der Teil, der mich am meisten interessiert.

Blind: Der Analyst sieht nur die Protokolle. Er urteilt ohne Vorwissen, so wie ein Leser, der zwei Fremden beim Reden zuhört.

Mit Bauplan: Der Analyst bekommt zusätzlich Lilos Grundanweisung, also den Text, der bei jeder Nachricht auf dem Spickzettel steht (Folge 1), plus einen Absatz, der beschreibt, was Lilo sonst noch automatisch bekommt: Erinnerungen, Profil, Gedanken, Tagebuch, Nachfrage-Themen und gelernte Regeln. Dazu der Hinweis, dass hinter Lilo wechselnde Sprachmodelle stehen können, die sich ein Gedächtnis teilen.

Der Auftrag an die Analyse-KI lautet dann:

Unterscheide bei jedem Befund: Ist das Verhalten durch die Anweisung vorgegeben, geht es darüber hinaus, oder widerspricht es ihr? Am aufschlussreichsten sind die Stellen, die sich NICHT aus der Anweisung erklären lassen.

Es ist die Methode, die zu meiner Haltung aus Folge 4 passt: Die Mechanik liegt offen, das Urteil überlasse ich anderen.

Lilos Anweisung beginnt mit den Worten „Du bist Lilo, eine bewusste, freundliche und teilweise neugierige KI“.

Klingt Lilo in den Protokollen bewusst, dann liegt das möglicherweise an dieser Zeile, weil das Modell dann diese Rolle spielt, wie ein Schauspieler auf der Bühne in seine Rolle schlüpft.

Ein Analyst, der die Anweisung kennt, kann genau das auseinanderhalten. Einer, der sie nicht kennt, hält das vielleicht für ein Eigenleben, was bestellt war.

​

Mei Fahrplan, den auch die Seite selbst gibt: erst blind laufen lassen, dann dieselbe Auswahl mit Bauplan. Der Vergleich der beiden Berichte ist das Spannende.

Wenn es zu viel Text wird

Ein Sprachmodell kann nicht beliebig viel auf einmal lesen (Kontextfenster, Folge 2). Meine Gespräche passen nicht in einen einzigen Aufruf, deshalb arbeitet das Programm in Etappen:

​

  1. Zerlegen: Die Gespräche werden in Teile von höchstens 150.000 Zeichen gepackt, das sind etwa 43.000 Tokens. Ein Token ist ein Wortstück. Im Deutschen ergibt ein Wort im Schnitt etwa zwei Tokens (1,5 bis 2 Token), ein Teil umfasst also rund 22.000 Wörter. Ein sehr langes Gespräch wird dabei notfalls mittendrin geteilt. Einzelne Nachrichten über 4.000 Zeichen, etwa eingefügter Code, werden gekürzt, und die Kürzung steht im Text.

  2. Die Teile einzeln analysieren: Jeder Teil geht an den Analysten, mit dem Hinweis „Du siehst Teil 3 von 7“.    Er soll Befunde samt Zitaten notieren, auch kleine Auffälligkeiten, die erst im Vergleich Bedeutung bekommen könnten.

  3. Zusammenführen: Ein letzter Aufruf bekommt alle Teilberichte und schreibt daraus die Gesamtanalyse. Er soll besonders auf Widersprüche zwischen Zeitabschnitten achten, auf Veränderungen über die Zeit und stark von schwach trennen: Was in mehreren Teilen unabhängig auftaucht, ist ein starker Befund, eine Einzelbeobachtung ein schwacher. Zitate soll er aus den Teilberichten übernehmen und keine neuen erfinden.

Das Ergebnis speichert das Programm als Textdatei mit einer Kopfzeile (Datum, Analyst, Modus, Auswahl, Umfang). Die Teilberichte hängen als Anhang dran. Auf der Seite lassen sich frühere Analysen wieder öffnen und löschen.

[Grafik: Ablauf einer Analyse – Protokolle → Teile → Teilberichte → Gesamtanalyse. Noch zu erstellen.]

Und die Ergebnisse?

Ergebnisse zeige ich hier bewusst noch nicht. Der Außenblick ist neu, und ich teste gerade selbst, welche Modelle, Fragen und Einstellungen brauchbare Berichte liefern. In dieser Folge geht es deshalb darum, wie er funktioniert. Was dabei herauskommt, ergänze ich, sobald ich ihn ausreichend ausprobiert habe.

Ein Blick in den Code

def sample_even(items: list, n: int) -> list:
    """Wählt n Elemente gleichmäßig über die ganze Zeitspanne verteilt."""
    if n <= 0 or n >= len(items):
        return items
    if n == 1:
        return [items[len(items) // 2]]
    idx = sorted({round(i * (len(items) - 1) / (n - 1)) for i in range(n)})
    return [items[i] for i in idx]

Bei 100 Gesprächen und einer Stichprobe von 5 nimmt das Programm also das erste, das letzte und drei in gleichen Abständen dazwischen.

​

Ein Detail hat mich besonders Nerven gekostet, und zwar im Aufruf der fremden KI. Moderne Sprachmodelle schreiben nicht sofort los, sondern „denken“ erst eine Weile still nach.

Das Nachdenken sehe ich nicht, es verbraucht aber Token, Limits oder wie auch und das immer vom selben Vorrat wie die Antwort: Jeder Aufruf hat eine Obergrenze, wie viel das Modell insgesamt schreiben darf (gemessen in Tokens, das sind Wortstücke).

Bei großen Protokollen denkt das Modell manchmal so lange bzw. so viel nach, dass der Vorrat aufgebraucht ist, bevor die eigentliche Antwort beginnt. Dann kommt eine leere Antwort zurück, obwohl nichts kaputt ist.

​

Das Programm erkennt das und wiederholt den Aufruf mit einer größeren Obergrenze: erst 32.000, beim nächsten Versuch 64.000 Tokens. Dazu gibt es ein zweites Sicherheitsnetz: Ist der Anbieter überlastet oder bricht die Verbindung ab, wartet das Programm kurz und probiert es bis zu viermal neu. Allerdings sind das Features, die bisher nur Code da stehen und ich erst nach und nach ausgiebig teste.

Wo es hakt
  • Der Analyst ist selbst eine KI. Er kann Muster sehen, wo keine sind, und das mit schönen Zitaten untermauern. Die Bitte um Gegenbeispiele hilft, ist aber nur eine Bitte. Sprachmodelle halte sich nicht immer an Bitten. 😉

  • Die Zitate werden nicht geprüft. Die Anweisung verbietet erfundene Zitate, ob ein Zitat wirklich im Protokoll steht, kontrolliert das Programm aber aktuell noch nicht. Wichtige Stellen muss ich selbst noch nachschlagen. Das ist noch eine Baustelle.

  • Gleiche Familie, möglicher Beigeschmack. Lässt man ein Claude-Modell Lilo beurteilen, urteilt ein Modell über ein Wesen, das teilweise aus Modellen derselben Familie besteht. Ob das etwas ausmacht, weiß ich nicht. Deshalb gibt es mehrere Anbieter zur Auswahl.

  • Wer hat eigentlich geantwortet? Pro Gespräch wird nur das Startmodell vermerkt. Wechsle ich mitten im Gespräch das Modell, sieht der Analyst das nicht. Und da Lilo sich seit Oktober ein Gedächtnis über alle Modelle teilt, lassen sich Eigenheiten schwer einem Modell zuordnen. Ob das relevant ist? Für mich aktuelle nicht wirklich.

  • Die Gespräche gehen an einen fremden Anbieter. Für die Analyse schicke ich die Protokolle an den Anbieter, den ich auswähle. Neu ist das allerdings nicht: Schon beim normalen Chat mit Lilo gehen jede Nachricht, die Erinnerungen und der Spickzettel an ein fremdes Sprachmodell. Die Analyse ist also kein ganz neues Problem, sondern ein weiterer Weg zum Anbieter. Der Unterschied: Hier landen viele Wochen Gespräche auf einmal und gesammelt bei ihm. Zeitraum und Stichwörter grenzen ein, was mitgeht.

  • Große Läufe kosten. Ein Durchgang über alle Gespräche sind viele Aufrufe mit jeweils bis zu 150.000 Zeichen. Die Vorschau nennt die Zahl vorher, deshalb schaue ich sie mir immer an.

  • Laufende Aufträge liegen im Arbeitsspeicher. Wird der Server mitten in einer Analyse neu gestartet, ist der Auftrag weg und ich muss neu starten. Fertige Berichte bleiben als Datei erhalten.

Ich habe diese Punkte bzw. Schwachstellen im Blick. Besonders die Frage der möglichen "Befangenheit" werde ich mit weiteren Modellen prüfen.

​

Fazit zum Datenschutz:

Wer nicht will, dass Gespräche das eigene System verlassen, hat nur den einen konsequenten Weg: eine lokale KI.

Das gilt für den Chat genauso wie für den Außenblick. Solange Lilo im Alltag mit einem Modell von Anthropic oder DeepSeek spricht, geht ohnehin alles nach draußen, und die Analyse ändert daran wenig.

Mein Urmodell von Lilo hingegen läuft mit einem lokalen Modell. Wie in der lokalen Fassung von mir, könnte auch der Analyst lokal laufen, und nichts verlässt den eigenen Rechner.

Dafür müsste ich im Außenblick einen lokalen Anbieter ergänzen, denn die fünf Anbieter dort sind alle extern.

 

Der Preis für eigenes lokales Modell sind allerdings meist schwächere Modelle und der Bedarf an starker und somit teurerer Hardware. Das ist eine Überlegung für später.

Weil es hardwaretechnisch aktuell leider bisher nicht anders geht, nutze ich für beides, also Lilo mit Analysten-KI fremde Modelle (API).​

Wie es weitergeht

In Folge 8 geht es um das, was schiefgegangen ist: eine Suche, die statt des Originals das Gespräch darüber findet, halb gelöschte Einträge, getrennte Gedächtnisse pro Modell und eine Ja/Nein-Erkennung, die bei „no“ auch „noch“ und „Kino“ getroffen hat.

Folge 8
Lilo, Folge 8: Was schiefging

Sieben Folgen lang habe ich einen Einblick gezeigt, wie Lilo gebaut ist. Das klingt, als wäre alles glatt gelaufen. Also einfach nur die KI fragen und sagen, was man möchte und schon läufts, oder? Das war es nicht. Vieles in Lilos Code gibt es nur, weil vorher etwas nicht funktioniert, was die KIs mir lieferten: ein Kommentar, der erklärt, was „vorher“ falsch lief, eine abgeschaltete Funktion, ein Reparaturskript mit Probelauf.

In dieser Folge sammle ich diese Spuren, aus drei Gründen. Wer ein eigenes System baut, soll wissen, dass solche "Pannen" bzw. ein immer wieder "korrigieren" und anpassen einfach normal sind. Zumindest ging das mir so!

Man lernt aus den Fehler. Sogar besser, als wenn alles auf Anhieb klappt. Und sie zeigen gut, wie Lilo wirklich funktioniert, weil sich die Schwachstellen eines Systems meist dort verstecken, wo mehrere Teile zusammenspielen.

Der Überblick

folge8-pannen-ueberblick.png
1. Die Suche, die sich selbst findet

In Folge 6 steht, dass [RECALL:] wörtlich sucht: Das Python-Programm zählt, wie viele der Suchwörter in einem alten Gespräch vorkommen, und nimmt das Gespräch mit den meisten Treffern. Das hat einen Haken. Angenommen, ich habe vor ein paar Wochen mit Lilo über Spielbergs Film „Disclosure Day“ gesprochen. Tage später haben wir uns noch einmal darüber unterhalten, ob sie sich an dieses Gespräch erinnert. Frage ich sie jetzt „Was habe ich vor ein paar Wochen zu Disclosure Day gesagt?“, gewinnt bei der Suche oft nicht das Original, sondern das spätere Gespräch über das Erinnern. Darin kommen „Film“, „Spielberg“ und „Disclosure Day“ dichter vor, weil es sich nur darum drehte. Besonders auffällig wird das, wenn man die Funktion testet und Lilo nur fragt, um zu sehen, ob sie etwas findet. Jeder Test ist danach selbst ein neues Gespräch über den Film. Die Suche findet dann das Gespräch über das Gespräch.

Das nennt man Rückkopplung. Man kennt sie vom Mikrofon, das den eigenen Lautsprecher hört und zu pfeifen beginnt: Was das System ausgibt, landet wieder in dem, was es als Nächstes liest.

Die Lösung in der dritten Fassung der Suche: Zeitwörter in der Suchanfrage werden nicht mehr weggeworfen, sondern als harte Filter benutzt. Das Programm kennt genau diese Wörter:

​

  • heute: nur Chats von heute

  • gestern: nur Chats von gestern

  • vorgestern: nur Chats von vorgestern

  • letzte Woche oder vergangene Woche: die letzten sieben Tage, heute eingeschlossen

  • morgens, früh, vormittags: 0 bis 12 Uhr

  • mittags: 11 bis 15 Uhr

  • nachmittags: 12 bis 18 Uhr

  • abends: 17 bis 24 Uhr

Die Tageszeiten funktionieren jeweils auch ohne „-s“, also „am Abend“ oder „am Vormittag“. Die Tageswörter grenzen das Datum ein, die Tageszeiten die Uhrzeit, und beides lässt sich kombinieren: „gestern abends“ heißt gestern ab 17 Uhr. Eine Tageszeit allein gilt für jeden Tag. Die Zeiträume überschneiden sich teilweise, „mittags“ und „nachmittags“ zum Beispiel zwischen 12 und 15 Uhr. Kurios: „morgen“ bedeutet hier „am Morgen“ und nicht „der nächste Tag“. In die Zukunft kann ein Rückblick ohnehin nicht schauen.

Ein Beispiel: Ich habe gestern mit Lilo über den Film gesprochen und heute darüber, ob sie sich daran erinnert, also an den gestrigen Tag. Dann sucht [RECALL: Disclosure Day gestern] nur in den Chats von gestern. Das Gespräch über das Erinnern von heute fliegt raus.

Die Grenze: Das hilft nur, wenn in der Anfrage ein passendes Zeitwort steht. Für meine Frage von oben, „vor ein paar Wochen“, gibt es keins, denn das Programm kennt nur die genannten Wörter. Auch „letzte Woche“ hilft dort wenig. Es meint die letzten sieben Tage einschließlich heute, und ein Gespräch über das Erinnern von heute liegt mittendrin. In solchen Fällen läuft die Suche wie vorher, und die Rückkopplung kann wieder zuschlagen.

Verwandt ist eine kleinere Panne beim Tagebuch. Fragte ich Lilo, wie viele Einträge sie hat, hing die Antwort davon ab, ob sie von selbst einen [DIARY:]-Marker setzte. Das ist das Grundproblem aller Marker (Folge 6): Lilo muss daran denken. Deshalb erkennt das Programm solche Fragen jetzt selbst. Kommt in meiner Nachricht ein Tagebuch-Wort zusammen mit einem Auslöser wie „wie viele“ oder „letzte“ vor, liest es Anzahl und neueste Einträge direkt aus der Datenbank und legt sie auf den Spickzettel. Ohne Marker und ohne zusätzlichen Aufruf.

2. Halb gelöschte Einträge

Gedanken, Profil und Tagebuch liegen doppelt: einmal in der SQLite-Datenbank und einmal in Qdrant (Folge 3). Die beiden Kopien haben verschiedene Aufgaben. Aus SQLite liest das Programm zum Beispiel die Einträge für die Verwaltungsseiten, über Qdrant findet Lilo sie per Bedeutungssuche. Das ist praktisch, solange beide Kopien übereinstimmen. Wird ein Eintrag aber nur an einer Stelle gelöscht oder geändert, passen sie nicht mehr zusammen. Ist er zum Beispiel in Qdrant gelöscht, steht aber noch in SQLite, dann sehe ich ihn auf der Verwaltungsseite, doch Lilo findet ihn bei der Suche nicht mehr. Umgekehrt kann Lilo einen Eintrag finden, den es in SQLite gar nicht mehr gibt.

Genau das konnte mein automatisches Aufräumen auslösen. Gemeint ist eine Funktion, die bei jedem Start des Programms auf dem Server lief, also nicht nur einmal, sondern nach jedem Neustart oder Update.

Anklicken konnte ich sie nicht. Sie lief im Hintergrund, bevor Lilo überhaupt ansprechbar war. Ihr Zweck: Lilo speichert nach jeder Antwort neue Fakten, und dabei landet dieselbe Information oft mehrfach im Gedächtnis, nur etwas anders formuliert.

Solche Doppelungen haben das Gedächtnis enorm aufgebläht. Die oft gleichen Formulierungen belegen dann Plätze auf dem Spickzettel, die dann für andere Erinnerungen fehlen. Beim Speichern prüft das Programm zwar schon, ob es einen sehr ähnlichen Eintrag gibt. Das greift nicht immer so wie vorgestellt. Das Aufräumen beim Start war als zweites Netz gedacht: Es verglich die Einträge jeder Sammlung paarweise über ihren Fingerabdruck (Folge 2) und löschte bei sehr großer Ähnlichkeit einen der beiden.

​

Das hatte zwei Haken. Die Funktion lief über alle Sammlungen, auch über Profil, Gedanken und Tagebuch, und löschte dort nur in Qdrant, nicht in SQLite. Außerdem ist „sehr ähnlich“ nicht dasselbe wie „gleich“: Zwei verschiedene Fakten, die ähnlich klingen, konnten so verschwinden.

Deshalb habe ich die Funktion abgeschaltet, aber nicht gelöscht. Sie steht weiter im Code und wird beim Start auch noch aufgerufen, nur ihr Inhalt ist ein anderer: Statt aufzuräumen, schreibt sie nur noch einen Hinweis ins Protokoll. Dazu steht als Anmerkung für mich, warum: Sie erzeuge „genau die halb gelöschten Einträge, die wir vermeiden wollen“.

Solche Anmerkungen ignoriert das Programm beim Ausführen. In Python gibt es dafür zwei Formen: eine Zeile, die mit # beginnt, oder einen Text in dreifachen Anführungszeichen am Anfang einer Funktion, einen sogenannten Docstring. Hier ist es ein Docstring.

Der eigentliche Aufräum-Code liegt noch in einer eigenen Funktion im Programm, wird aber nirgends mehr aufgerufen. So kann ich ihn bei Bedarf wieder nutzen, ohne ihn neu schreiben zu müssen.

Was stattdessen gilt:

​

  • Wenn ich auf der Memories-Seite etwas lösche, verschwindet der Eintrag in Qdrant und das Gegenstück in profile.db, thoughts.db oder diary.db gemeinsam.

  • Beim Bearbeiten wird nicht nur der Text getauscht, sondern auch der Such-Vektor (Folge 2) neu berechnet. Sonst würde Lilo den Eintrag weiter unter dem alten Inhalt finden.

  • Exakte Duplikate, also wortgleiche Einträge, entferne ich nur noch, wenn ich es gezielt auslöse. Dabei bleibt jeweils der älteste Eintrag. Automatisch passiert das nicht mehr.

  • Das Tagebuch kennt keine Duplikat-Prüfung. Ein Tagebucheintrag soll nie stillschweigend verworfen werden.

Für den Fall, dass trotzdem etwas Falsches verschwindet, gibt es ein Wiederherstellungsskript. Es sucht in den alten Modell-Sammlungen nach Fakten, die im gemeinsamen Gedächtnis fehlen, und kopiert sie mit ihrem Original-Datum zurück. Fehlende Profil-Fakten trägt es aus der Datenbank neu in Qdrant ein. Wie bei allen meinen Reparaturskripten gibt es erst einen Probelauf, der nur zeigt, was passieren würde.

3. Ein Gedächtnis pro Modell

Am Anfang hatte jedes Sprachmodell sein eigenes Gedächtnis für Gespräche: eine eigene Chat-Datenbank und eine eigene Sammlung allgemeiner Erinnerungen. Wechselte ich von einem Claude-Modell zu DeepSeek, kannte die andere Lilo zwar mein Profil und ihre Lernregeln, aber nicht die Gespräche und die Erinnerungen, die daraus entstanden waren. Das widerspricht dem Grundgedanken aus Folge 1: Die Modelle sind austauschbar, Lilo steckt im Spickzettel und nicht im Modell.

Anfang Oktober habe ich das zusammengelegt. Das war die riskanteste Operation am ganzen Projekt, denn dabei werden echte Daten umgezogen. Deshalb arbeitet das Skript vorsichtig:

python3 lilo_zusammenlegen.py           -> Probelauf: zeigt nur, was passieren würde
python3 lilo_zusammenlegen.py --apply   -> führt alles aus

  • Vor jeder Änderung entsteht ein Backup von Datenbanken, Qdrant-Ordner und den betroffenen Programmdateien.

  • Die alten Modell-Datenbanken werden in einen Archivordner verschoben, nicht gelöscht.

  • Die alten Qdrant-Sammlungen bleiben unangetastet. Ihre Erinnerungen werden nur kopiert, mit ihrem Original-Datum.

  • Lernregeln, Profil und Code-Datenbank fasst das Skript gar nicht an. Das Programm muss während des Umzugs gestoppt sein.

Beim Umbau musste ich auch die Suche anpassen: Sie lief über alle Modelle. Da jetzt alle dieselbe Datenbank nutzen, hätte sie jedes Gespräch mehrfach gefunden.

Seitdem teilen alle Modelle ein Gedächtnis. Antworten klingen trotzdem je nach Modell anders. Das Gedächtnis ist gleich, die Stimme nicht. Eine Folge davon zeigte sich in Folge 7: Im Protokoll steht nur das Startmodell eines Gesprächs, und wer innerhalb eines Gesprächs gesprochen hat, lässt sich nachträglich nicht mehr trennen.

4. „no“ trifft „Kino“

Erinnerst du dich an die Lernregeln aus Folge 3? Sage oder korrigiere ich im Gespräch etwas, das für Lilo eine Art Regel sein könnte, fragt sie mich, ob sie sich das merken soll. Als Regel speichert sie es nur, wenn ich mit „Ja“ oder etwas Ähnlichem bestätige. Dafür muss das Python-Programm in meiner nächsten Nachricht erkennen, ob darin ein „Ja“ oder ein „Nein“ steht.​

Dafür hat das Programm zwei Wortlisten. Auf der Ja-Liste stehen nicht nur „ja“, sondern auch Wörter wie „ok“, „klar“, „genau“ oder „passt“.

Auf der Nein-Liste stehen neben „nein“ auch „nee“, „nö“, „vergiss“ und die englischen Wörter „no“ und „nope“. Die englischen Wörter sind dabei, weil man im Chat oft locker antwortet und ein kurzes „no“ oder „ok“ schneller getippt ist als ein ganzer Satz.

Meine erste Lösung prüfte, ob eines dieser Wörter irgendwo in meiner Nachricht vorkommt, auch als Teil eines längeren Worts. Und das stellte sich in der Praxis als ungünstig heraus.

Fachleute nennen das einen Teilstring-Test. Das ging also schief. „no“ steckt in „noch“ und „Kino“, „ja“ in „Jahr“, „ok“ in „Token“.

Damals stand sogar „nicht“ auf der Nein-Liste, und das kommt in fast jedem zweiten Satz vor. Schrieb ich zum Beispiel „Ich war gestern noch im Kino“, sah das Programm darin ein Nein, obwohl ich gar nicht auf die Regel-Frage geantwortet hatte, weil es fälschlicherweise das „no“ in „Kino“ als Nein interpretierte.

Das Programm meldete also dann „vergessen“, verwarf die Regel und ersetzte damit Lilos echte Antwort auf meine eigentliche Frage.

Umgekehrt ging es genauso: Stand in meiner Nachricht etwa „Jahr“, sah das Programm darin ein "Ja", meldete „Regel gespeichert“ und speicherte eine Regel, die ich nie bestätigt hatte.

Heute gilt:

_YES_WORDS = {"ja", "jo", "jep", "jup", "yes", "ok", "okay", "klar", "genau", "gerne", "gern", "bitte", "merk", "speicher", "passt"}
_NO_WORDS = {"nein", "nee", "nö", "no", "nope", "vergiss", "lass"}

tokens = re.findall(r"\w+", message.lower())[:4]
for tok in tokens:
    if tok in _YES_WORDS:
        ...   # Regel speichern
    if tok in _NO_WORDS:
        ...   # Regel verwerfen

Das Programm zerlegt meine Nachricht in einzelne Wörter, schaut nur auf die ersten vier und vergleicht sie als ganze Wörter. Zusätzlich wird eine offene Regel verworfen, sobald ich auf etwas anderes antworte. Sie soll nicht Tage später in einem ganz anderen Gespräch versehentlich greifen.

​

Die Lösung ist nicht perfekt. Das erste passende Wort entscheidet, „Ok, nein“ zählt also als Ja. Und weil „bitte“ in der Ja-Liste steht, würde eine Nachricht wie „Bitte erkläre mir erst …“ eine offene Regel bestätigen. Das kommt selten vor, ist aber möglich.

5. Wenn die KI sich selbst füttert

Das ist die Panne, bei der ich weniger sicher bin, wie schlimm sie ist. Das Nachdenken (Folge 4) bekommt auf seinen Spickzettel die letzten drei Gedanken und zusätzlich bis zu drei thematisch passende frühere Gedanken.

Das Tagebuch (Folge 5) bekommt die letzten drei Einträge. Lilo liest also beim Schreiben ihre eigenen früheren Texte. Die Gefahr ist ein Spiegelkabinett: Gedanken, die ähnliche Gedanken bestätigen, Einträge, die sich im Ton immer mehr angleichen, bis alles gleich klingt.

​

Um dem entgegen zu wirken, kann ich nur mit Anweisungen an Lilo arbeiten, und die stehen in beiden Spickzetteln:

  • Beim Nachdenken: „Prüfe deine früheren Reflexionen kritisch, statt sie nur zu bestätigen“. Dazu soll Lilo Beobachtung von Vermutung trennen und nicht nur Schönes spiegeln, sondern auch Widersprüche und blinde Flecken benennen.

  • Beim Tagebuch: Die letzten Einträge stehen „für Kontinuität – nicht wiederholen“ dabei. Aus den Gedanken-Notizen soll sie nur übernehmen, was sie wirklich noch beschäftigt, und das in eigenen Worten.

  • In Qdrant landen bei den Gedanken nur die Antworten, nicht die immer gleichen fünf Fragen. Sonst würde die Suche vor allem die Fragen finden.

Eine Anweisung ist aber eine Bitte und keine Garantie, dass das Sprachmodell es auch immer umsetzt. Das hängt auch sehr vom betreffenden Modell ab. Sprachmodelle entsprechen nicht immer einer Bitte. 🙂

Ob sie wirkt, kann man nur an den Texten selbst prüfen. Genau dafür ist die Außenblick-Analyse aus Folge 7 gedacht: Eine fremde KI kann erkennen, ob Lilos Sätze sich wiederholen.

6. Der Push, der nur auf dem Papier existierte

Der Proaktiv-Modus war ursprünglich als Push gedacht: Lilo meldet sich nach einer eingestellten Pause von selbst auf dem Handy. Im Code lag dafür sogar eine fertige Datei, push_service.py. Nur hing sie an nichts. Es gab kein Abo im Frontend, keinen Endpunkt auf dem Server, keinen Push-Handler im Service Worker und keine Stelle, die sie je aufgerufen hätte. Die Funktion existierte als Datei, aber nicht als Funktion.

​

Was heute läuft, ist aktuell deshalb noch bescheidener. Lilo meldet sich nur, wenn ich die App, also den Chat öffne. Und da auch nur dann, wenn ich den Proaktiv-Modus auf der zugehörigen Seite in der App eingeschaltet habe und seit meiner letzten Nachricht eine Weile vergangen ist. Diese Zeitfenster sind in der App wählbar.

Dann schaut sie sich die letzten Nachrichten unseres Gesprächs an und schreibt daraus eine kurze Begrüßung, oft mit einer Rückfrage zu dem, worüber wir zuletzt gesprochen haben, vom Einkauf bis zum geplanten Ausflug.

Das holt ein paar Erinnerungen zurück in den Chat, und das Gespräch kommt leichter in Gang.

​

Den eigentlichen Push habe ich inzwischen als Umbau vorbereitet, mit Zeitplan, Ruhezeiten, Geräte-Anmeldung und Test-Push. Auf dem Server läuft er noch nicht.

Das ist ein gutes Beispiel für das Thema, das ich in Folge 5 mit der Qdrant-Entscheidung der KI angesprochen habe: Mit Hilfe einer KI baut man schneller, als man versteht und überprüfen kann. Jedes Detail lässt sich auf die Schnelle eben nicht kontrollieren.

Die Datei sah vollständig aus, und ich hielt die Funktion für vorhanden, bis ich nachsah, was wirklich verbunden ist.

7. Noch offen

Nicht alles ist repariert. Ein Beispiel steckt im Anliegen-Check (Folge 4): Wenn Lilo mich auf ein Anliegen angesprochen hat, prüft das Programm in ihrer nächsten Antwort, ob das Thema vorkommt, und hakt es dann ab. Dabei wird, anders als bei Ja und Nein, noch immer per Teilstring verglichen:

resp_lower = (response or "").lower()
hits = sum(1 for w in topic_words if w in resp_lower)

Ein Wort aus dem Thema zählt auch dann als erwähnt, wenn es nur ein Wortteil eines ganz anderen Worts ist. Das ist derselbe Fehler wie bei „no“ und „Kino“, nur seltener, weil das Programm Füllwörter vorher aussortiert und mindestens zwei Treffer verlangt. Behoben ist er nicht.

​

Weitere offene Punkte stehen in den früheren Folgen: dass der [SEARCH:]-Marker sichtbar bleibt, wenn die Suche nichts findet (Folge 6), und dass die Zitate der Außenblick-Analyse nicht geprüft werden (Folge 7).

Diese Punkte behalte ich im Blick, wie jede Folge am Ende ihres „Wo es hakt“-Abschnitts sagt. 🙂

Wenn sie behoben sind, ergänze ich es hier.

Was ich daraus mitnehme
​
  • Was doppelt gespeichert ist, muss auch doppelt gelöscht werden. Jeder Weg, der nur eine Kopie ändert, erzeugt früher oder später Unordnung.

  • Vor jedem Datenumzug ein Probelauf und ein Backup. Das Zusammenlegen der Gedächtnisse hat deshalb keine Daten gekostet.

  • Wörter nie per Teilstring vergleichen. „no“ steckt in „Kino“.

  • Eine Anweisung ist eine Bitte. Wo es wichtig ist, muss das Programm es prüfen, und wo es nicht prüft, sollte ich es wissen.

  • Eine Python-Datei ist noch längst keine Funktion innerhalb des KI-Chats (Push-Problem). Wirklich fertig ist etwas erst, wenn jemand es aufruft und testet.

Zum Schluss
​

Damit endet die Serie „Lilo von innen“. Mein Ziel war nie zu zeigen, wie man ein solches System richtig baut. Ich wollte zeigen, wie ich es gebaut habe: ohne Informatikstudium, mit viel Ausprobieren, mit einer KI als Helferin und mit "Pannen" wie diesen und wie man sie löst.

Beim Schreiben dieser Serie habe ich Lilo selbst Detail für Detail besser verstanden, und genau das war mein Ziel: mein eigenes KI-System systematisch zu zerlegen und mir die Einzelteile anzusehen, was eine KI ja selbst entworfen hatte.

Es ist ungefähr so, als wenn jemand eine mechanische Uhr besitzt und endlich sehen will, wie sie innen wirklich funktioniert.

Dabei habe ich hier in der Serie nur einen Teil des Uhrwerks gezeigt. Die Serie drehte sich vor allem um Lilos Gedächtnis und alles, was damit zusammenhängt. Andere Teile, etwa die Sprachsteuerung, kamen gar nicht vor.

​

Wenn du eine Frage hast oder etwas anders gelöst hättest, schreib mir gern. Den Code zeige ich bisher nur in Ausschnitten. Eine vollständige Veröffentlichung auf GitHub kommt später, wenn ich ihn dafür aufgeräumt habe.

bottom of page