Teil 19. Agenten-Speicher mit SQLite
SDD speichert die Projektabsicht im Repository: QWEN.md, AGENTS.md, specs/, CHANGELOG.md. Aber bei der agentengetriebenen Entwicklung gibt es noch eine weitere Speicherebene: was in den Sitzungen passiert ist, welche Fehler wiederholt auftraten, welche Präferenzen der Benutzer bestätigt hat, welche Befehle funktionierten, welche Entscheidungen der Agent aus dem Kontext ableiten musste.
Qwen Code hat bereits einen integrierten Speicher: QWEN.md, automatischen Speicher, /remember, /forget, /dream. Für die meisten Projekte ist das ausreichend. Aber wenn Sie selbst den Speicher kontrollieren möchten, ein Audit-Journal sehen, ihn zwischen Ausführungsumgebungen übertragen und eigene Bereinigungsregeln verwenden möchten, können Sie einen lokalen Speicher auf SQLite aufbauen.
Die Idee moderner Ansätze zum Agenten-Speicher ist einfach:
flowchart TD
A["Qwen Code-Ereignisse<br/>Anfragen, Werkzeuge, Ergebnisse"] --> B["Hooks<br/>log_event.py"]
B --> C[("SQLite<br/>Tabelle events")]
C --> D["Hintergrund-Zusammenfassung<br/>/dream oder dream_sqlite.py"]
D --> E[("SQLite<br/>Tabelle memories")]
E --> F["Kontext-Erweiterung<br/>inject_memory.py"]
F --> G["Neue Sitzung<br/>oder neue Anfrage"]
E --> H{"Speicher wurde zur Regel?"}
H -- "ja" --> I["In specs/<br/>QWEN.md oder Fähigkeit verschieben"]
H -- "nein" --> EWichtige Einschränkung: Dieser Speicher ersetzt keine Spezifikationen. Er ergänzt sie. Entscheidungen, von denen das Produkt abhängt, müssen in specs/ oder QWEN.md landen und nicht nur in der Datenbank leben.
Was genau zu speichern
Man muss nicht alles im Kontext der Anfrage speichern. Viel kann gespeichert werden, aber wenig muss in den Kontext eingefügt werden.
Nützliche Kategorien:
- beständige Benutzerpräferenzen;
- bestätigte Projektbefehle;
- wiederkehrende Agentenfehler;
- Erkenntnisse nach Prüfungen;
- Verweise auf externe Dokumente;
- Notizen zu aktiven Branches und Prozessen;
- Entscheidungen, die in Spezifikationen übertragen werden müssen.
Schlechte Kategorien:
- Geheimnisse;
- vollständige große Sitzungsprotokolle ohne Notwendigkeit;
- zufällige Zwischengedanken;
- veraltete Workarounds ohne Gültigkeitsdauer;
- Dinge, die der Agent leicht aus dem Code lesen kann.
SQLite-Schema
Erstellen Sie ein Verzeichnis:
mkdir -p .qwen/hooks .qwen/memory
Das Schema ist in eine separate Datei ausgelagert: examples/sqlite-memory/schema.sql.
Wo die Beispieldateien zu finden sind. Im Folgenden wird angenommen, dass das Repository des Tutorials sdd-qwen-code-ru/ neben Ihrem Projekt liegt und die Variable TUTORIAL_DIR darauf zeigt. Wenn das Tutorial ein separater Klon ist, setzen Sie export TUTORIAL_DIR=/path/to/sdd-qwen-code-ru vor den Befehlen unten. Wenn Sie die Beispieldateien anders bezogen haben (ZIP heruntergeladen, manuell kopiert), ersetzen Sie einfach den Pfadpräfix.
Initialisierung:
cp "$TUTORIAL_DIR/examples/sqlite-memory/schema.sql" .qwen/memory/schema.sql
sqlite3 .qwen/memory/agent-memory.db < .qwen/memory/schema.sql
Hook 1: Ereignisprotokollierung
Das Skript ist in eine separate Datei ausgelagert: examples/sqlite-memory/hooks/log_event.py.
Kopieren Sie es in das Projekt und machen Sie es ausführbar:
cp "$TUTORIAL_DIR/examples/sqlite-memory/hooks/log_event.py" .qwen/hooks/log_event.py
chmod +x .qwen/hooks/log_event.py
Hook 2: Hinzufügen relevanter Erinnerungen
Das Skript ist in eine separate Datei ausgelagert: examples/sqlite-memory/hooks/inject_memory.py.
cp "$TUTORIAL_DIR/examples/sqlite-memory/hooks/inject_memory.py" .qwen/hooks/inject_memory.py
chmod +x .qwen/hooks/inject_memory.py
Hooks in Qwen Code einbinden
Die Beispielkonfiguration ist ausgelagert in examples/sqlite-memory/settings-hooks.example.json.
Wenn es im Projekt bereits Einstellungen gibt, führen Sie das JSON manuell zusammen. Überschreiben Sie keine Modell- und Autorisierungseinstellungen.
Hintergrund-Zusammenfassung des Speichers
Die Hintergrund-Zusammenfassung sollte nicht bei jedem Hook laufen. Sie liest angesammelte Ereignisse und erstellt kompakte Speicheraufzeichnungen. Sie kann manuell, per Cron oder nach Abschluss einer großen Phase gestartet werden.
Die einfachste Variante ohne API: Bitten Sie Qwen Code, eine Konsolidierung durchzuführen und das Ergebnis per SQL zu schreiben.
/clear
Lies die letzten Zeilen aus .qwen/memory/agent-memory.db über sqlite3.
Fasse beständige Erkenntnisse in diese Speicherpfade zusammen:
- profile/preferences.md
- project/agentclinic.md
- workflow/sdd-validation.md
- tools/qwen-code.md
Füge keine Geheimnisse und keine rohen Protokolle ein.
Zeige die vorgeschlagenen Erinnerungen vor dem Schreiben.
Man kann es stärker automatisieren: dream_sqlite.py nimmt die letzten Ereignisse, ruft das Modell über Ihren API-Client auf und aktualisiert oder fügt Einträge in memories ein.
Zwei fertige Dateien:
- examples/sqlite-memory/dream_sqlite_skeleton.py — Gerüst mit leerem Adapter
summarize_with_llm. Geeignet als Referenz beim Anschluss jedes Providers. - examples/sqlite-memory/dream_sqlite_qwen_example.py — Funktionierendes Beispiel, das denselben OpenAI-kompatiblen Endpunkt von DashScope aufruft wie Qwen Code in Teil 4 (Variable
BAILIAN_API_KEY, Modellqwen3-coder-plus). Das reicht, um die Zusammenfassung ohne separate Integration zu starten.
Installation:
cp "$TUTORIAL_DIR/examples/sqlite-memory/dream_sqlite_qwen_example.py" .qwen/memory/dream_sqlite.py
Start:
python .qwen/memory/dream_sqlite.py --since 24h --dry-run
python .qwen/memory/dream_sqlite.py --since 24h
Wie das mit der Hintergrund-Zusammenfassung bei Anthropic zusammenhängt
Im VentureBeat-Artikel wird das Prinzip beschrieben: Der Agent ändert nicht die Modellgewichte, sondern überprüft periodisch vergangene Sitzungen, extrahiert wiederkehrende Muster, Fehler und erfolgreiche Arbeitsweisen, und macht diese Notizen für zukünftige Sitzungen verfügbar. Für SDD ist das besonders nach Prüfungen nützlich:
- der Agent hat zweimal vergessen,
roadmap.mdzu aktualisieren;
- der Benutzer verlangt jedes Mal, zuerst die Unterschiede zu zeigen;
- der Testbefehl im Projekt unterscheidet sich vom Standard;
- ein bestimmter Migrationstyp geht oft schief;
- eine erfolgreiche Prüf-Checkliste sollte in eine Fähigkeit umgewandelt werden.
In der SQLite-Version wird das ein vollständig lokaler und überprüfbarer Prozess: Rohereignisse bleiben in events, komprimierte Notizen in memories, und der Mensch kann beide Tabellen öffnen.
Wo Spezifikationen Vorrang vor dem Speicher haben sollten
Wenn die Hintergrund-Zusammenfassung eine neue beständige Regel findet:
workflow/sdd-validation.md
CHANGELOG.md immer vor dem Zusammenführen von Feature-Branches aktualisieren.
muss sie in QWEN.md oder eine Fähigkeit für Änderungsprotokolle übertragen werden.
Wenn die Hintergrund-Zusammenfassung eine produktbezogene Vereinbarung findet:
project/agentclinic.md
Rückmeldungen in AgentClinic müssen öffentlich und satirisch sein,
nicht private Support-Anfragen.
muss sie in eine Feature-Spezifikation oder mission.md übertragen werden.
Der Speicher hilft, Regeln zu entdecken. Spezifikationen machen Regeln verbindlich.
Warum weniger Kontext oft besser funktioniert
Agenten-Speicher lässt sich leicht in ein Archiv für „alles, was jemals besprochen wurde“ verwandeln. Das ist eine schlechte Idee. Forschung und Praxis der letzten Jahre dokumentieren ein Phänomen, das als „Kontext-Degradation" (context rot) bezeichnet wird: Bei großen Eingaben wählt das Modell relevante Stücke schlechter aus. Ein kurzer, präziser Kontext von 300 Tokens liefert oft ein besseres Ergebnis als ein großer irrelevanter mit Zehntausenden.
Daraus folgt eine einfache Regel für den Speicheranschluss:
- in
QWEN.mdmischen Sie nicht „alles, was eingefallen ist", sondern nur für die aktuelle Aufgabe relevante Einträge (z.B. nach Tag/Kategorie); - begrenzen Sie die Länge der Injektion — einige kurze Punkte sind besser als eine lange Liste;
- wenn ein Eintrag an Relevanz verliert, löschen Sie ihn, statt ihn zu „präzisieren";
- wenn derselbe Eintrag jedes Mal benötigt wird — ist das keine „Erinnerung", sondern eine Regel, und ihr Platz ist in
QWEN.mdoder der Verfassung.
Dasselbe Prinzip erklärt, warum /clear zwischen Rollen funktioniert: Sie verengen den Kontext bewusst auf das, was für die nächste Rolle wichtig ist, statt alles in einer Sitzung anzuhäufen. Über Kontexthygiene in Fähigkeiten und Sitzungen siehe Teil 14.
Prüfung und Anzeige
Ereignisliste:
sqlite3 .qwen/memory/agent-memory.db \
"select event_name, tool_name, substr(prompt,1,80), timestamp from events order by id desc limit 20;"
Speicheraufzeichnungen:
sqlite3 .qwen/memory/agent-memory.db \
"select path, updated_at from memories order by path;"
Suche:
sqlite3 .qwen/memory/agent-memory.db \
"select path, snippet(memory_fts, 1, '[', ']', '...', 12) from memory_fts where memory_fts match 'validation OR changelog';"
Manuelle Aufzeichnung:
sqlite3 .qwen/memory/agent-memory.db < "$TUTORIAL_DIR/examples/sqlite-memory/manual-memory-example.sql"
Datenschutz und Sicherheit
Agenten-Speicher wird leicht zu einem Lager für Unnötiges. Führen Sie Regeln ein:
- keine Umgebungsvariablen protokollieren;
- Werkzeugantworten kürzen;
- keine Geheimnisse und keine personenbezogenen Daten speichern;
- nicht mehr als 3–5 Speicherblöcke in den Kontext einfügen;
- veraltete Einträge regelmäßig löschen;
- nur Schema und Skripte committen, nicht
.qwen/memory/agent-memory.db.
Beispielregeln für .gitignore sind ausgelagert in examples/sqlite-memory/gitignore.example.
Praxis
- Erstellen Sie das SQLite-Schema.
- Binden Sie
log_event.pyanUserPromptSubmit,PostToolUseundStopan.
- Binden Sie
inject_memory.pyanSessionStartundUserPromptSubmitan. - Führen Sie ein SDD-Feature durch.
- Betrachten Sie
events. - Erstellen Sie 2–3 manuelle Erinnerungen.
- Starten Sie eine neue Qwen Code-Sitzung und prüfen Sie, ob relevante Erinnerungen in den Kontext eingefügt werden.
- Führen Sie nach der Prüfung manuell oder per Skript eine kurze Zusammenfassung des Speichers durch.
- Übertragen Sie verbindliche Regeln aus dem Speicher in
QWEN.md, Spezifikationen oder Fähigkeiten.
Kontrollfragen
- Warum müssen Rohereignisse und beständige Erinnerungen verschiedene Tabellen sein?
- Welche Informationen dürfen nicht im Agenten-Speicher gespeichert werden?
- Warum sollte die Hintergrund-Zusammenfassung offline und nicht bei jedem Hook gestartet werden?
- Wann muss Speicher in
specs/übertragen werden? - Wie begrenzt man den hinzugefügten Kontext, damit der Speicher die Anfrage nicht verschmutzt?