# Memory — Langzeitgedächtnis für Agenten

> **Bauplan zu einem gebauten System.** Umgesetzt; der Stand steht in der
> [Roadmap](Roadmap.md) 3.2. Die drei Punkte unter „Offen" laufen dort weiter.

Löst K1 aus der [Bestandsaufnahme](archiv/Bestandsaufnahme-2026-07.md): Geplante Agenten
begannen bei jedem Cron-Lauf bei null. Ein Agent, der alle 30 Minuten lief, wusste
nichts von seinem letzten Durchgang — er rief dieselben Quellen ab, zog dieselben
Schlüsse und konnte keine Entwicklung über Zeit verfolgen.

## Warum nicht der vorhandene State-Store

`IStateStore` ist eine Schlüssel-Wert-Tabelle für kleine Marker („zuletzt gesehene ID").
Ein Gedächtnis darin abzulegen hieße, JSON in eine `Value`-Spalte zu schreiben — damit
lässt sich nichts filtern, sortieren oder auswerten.

Deshalb typisierte Spalten in einer eigenen Tabelle. Das Schema ist bewusst schlicht
gehalten, damit eine MySQL-Variante später dieselbe Struktur mit wenigen
Dialektunterschieden bekommen kann.

## Modell

| Feld | Zweck |
|---|---|
| `Scope` | `agent` (privat) oder `shared` (alle Agenten der Instanz) |
| `Subject` | Worum es geht — Ticker, Kunde, Projekt |
| `Key` | **Optional.** Erneutes Merken darunter *aktualisiert* statt anzulegen |
| `Category` | `fact`, `decision`, `observation`, `task`, `contact`, `other` |
| `Tags` | Schlagworte zum Wiederfinden |
| `Importance` | 1–5, steuert die Reihenfolge beim Abruf |
| `CreatedBy` | Bleibt auch im geteilten Bereich sichtbar |

Die Scope-Trennung ist absichtlich dieselbe wie beim `FileRW`-Tool (`personal`/`shared`) —
für Agenten bleibt das Konzept dadurch wiedererkennbar.

## Der Schlüssel ist das Wichtigste

Ohne ihn wüchse das Gedächtnis eines alle 30 Minuten laufenden Agenten um 48 Einträge
pro Tag zur selben Sache. Mit `key='kursziel_nvda'` bleibt es **ein** Eintrag, der
sich fortschreibt — das Anlagedatum bleibt erhalten, nur `UpdatedAt` wandert.

Beobachtungen ohne Schlüssel sammeln sich weiterhin an; das ist gewollt, wenn ein
Verlauf entstehen soll.

## Abruf

Sortiert nach Wichtigkeit, dann Aktualität. Das ist wesentlich, weil das Ergebnis
begrenzt wird: Bei einer Kappung muss das Wichtigste überleben.

Zusätzlich greift eine Zeichenobergrenze (8.000 Zeichen) — ein Abruf darf den Kontext
nicht sprengen, dieselbe Überlegung wie bei der Tool-Ergebnis-Kappung (T2).

## Speicher-Fundament

`SqliteStorage` bündelt den Zugang zur Instanz-Datenbank:

- **WAL** — beliebig viele Leser parallel zu einem Schreiber
- **busy_timeout** — ein Schreiber wartet kurz, statt sofort zu scheitern
- **Connection-Pooling** — kein Verbindungsaufbau je Aufruf
- **Schreib-Warteschlange im Prozess** — macht Fehlerbilder reproduzierbar

Vorher öffnete jeder Aufruf eine Verbindung ohne diese Einstellungen. Bei mehreren
gleichzeitig schreibenden Agenten gab das `database is locked` — das sah nach einer
Grenze von SQLite aus, war aber nur fehlende Konfiguration. Zwei Tests decken das
gezielt ab (60 gleichzeitige Schreibvorgänge, gemischtes Lesen und Schreiben).

## Offen

- **Automatische Einblendung**: Ein Agent muss `recall` derzeit selbst aufrufen. Für
  geplante Läufe wäre eine kurze Übersicht der wichtigsten Erinnerungen im Auftrag
  hilfreich. Sie gehört in die Nutzernachricht, nicht in den System-Prompt — sonst
  verfällt bei jedem Lauf der Prompt-Cache (T1).
- **Verfall**: Alte, unwichtige Beobachtungen könnten nach einer Frist entfallen.
- **MySQL**: Zweite Implementierung von `IMemoryRepository`, wenn mehrere Rechner
  dazukommen.
