vector rebuild
vector rebuild rechnet den vault-lokalen Vektor-Index neu durch — entweder lückenfüllend für fehlende Entitäten oder von Grund auf, wenn das Embedding-Modell oder die Chunking-Logik gewechselt hat.
Wofür ist das?
- Nach einem Modell- oder Chunking-Versionswechsel. Embeddings sind modellspezifisch; das alte Vektor-Geflecht ist mit neuen Embeddings nicht kompatibel. Apparat erkennt den Mismatch beim nächsten Schreibversuch und bricht hart ab —
vector rebuild --forceist dann der einzig saubere Weg, den Index in den neuen Zustand zu bringen. - Nach einem Watcher-Ausfall (Container neu gebaut, Ollama war stundenlang weg, Worker mit Backlog gestoppt). Statt jeden Datei-Touch händisch nachzuziehen, baut der Standardmodus den fehlenden Bestand in einem Rutsch nach.
- Beim Erst-Setup eines Vaults, der schon Inhalte mitbringt. Der Watcher reagiert nur auf neue Schreibevents — vorhandene Markdown-Dateien werden vom Watcher nicht selbständig erfasst.
vector rebuildsetzt den Anfangszustand.
Aufruf
apparat vector rebuild --vault=recherche # voller Reconcile, synchron apparat vector rebuild --vault=recherche --missing-only # nur Lücken füllen, kein Drift-Scan apparat vector rebuild --vault=recherche --async # Reconcile, Embedding in die Queue apparat vector rebuild --vault=recherche --force # Index löschen, alles neu, synchron apparat vector rebuild --vault=recherche --force --async # Index löschen, jeder Eintrag als Job
| Option | Bedeutung |
|---|---|
| `--vault=<name | path |
--force |
Löscht vectors.sqlite (samt -wal/-shm) und embedded jede Entität neu. Pflicht, wenn Modell oder Chunking-Version sich geändert haben. |
--async |
Reiht einen einzigen ReconcileVectorIndexJob mit dem gesamten Work-Set in die embeddings-Queue und kehrt sofort zurück. Fortschritt per vector stats und vector drift beobachten. |
--missing-only |
Lässt den Inhalts-Drift-Scan aus und embedded nur Entitäten, die im Index ganz fehlen. Schneller bei großen Vaults, wenn der Watcher nachweislich gesund war. |
--no-progress |
Unterdrückt die Fortschrittsleiste im synchronen Modus (für CI-Logs und nicht-TTY-Aufrufe). |
Reconcile als integrierter Schritt
Bevor vector rebuild (Default oder --async) die fehlenden Entitäten ermittelt, läuft ein Orphan-Cleanup: jeder Chunk, dessen entity_id im Graph nicht mehr existiert, wird aus vectors.sqlite entfernt. So spiegelt das Ergebnis hinterher wirklich den aktuellen Zustand des Graphen wider — und nicht zusätzlich Geisterzeilen aus früheren Datei-Löschungen, die der Watcher nicht mitbekommen hat.
Das Kommando meldet die geprunten Einträge in einer eigenen Zeile:
Vault "recherche" — embedding 142 of 1000 entities. pruned 4 orphan entities from the vector index. …
--force überspringt den Cleanup-Schritt: die Index-Datei wird sowieso komplett gelöscht und neu aufgebaut.
Inhalts-Drift im Default-Modus
Im Default-Modus läuft nach dem Orphan-Cleanup ein Drift-Scan: für jede Entität, die im Graph existiert und auch im Index repräsentiert ist, lässt Apparat den Chunker noch einmal laufen und vergleicht die frischen content_hash-Werte mit den persistierten. Stimmen sie nicht überein — Datei wurde verändert, während der Watcher aus war —, landet die Entität zusammen mit den Lücken (Entitäten ohne Index-Eintrag) im Work-Set.
Das macht den Default-Lauf umfassender und teurer: ein Chunker-Pass über alle Entitäten kostet auf einem 10k-Vault einige Sekunden. Auf 1k-Vaults ist die Zusatzlast unter einer Sekunde. Keine Ollama-Aufrufe während des Scans selbst — die Embeddings rechnen erst die identifizierten Drift-/Missing-Entitäten neu.
Die Ausgabe macht beide Befunde sichtbar:
Vault "recherche" — embedding 7 of 1000 entities. pruned 2 orphan entities from the vector index. detected 5 entities with drifted content (re-embed required). …
Wer den Scan aussetzen will (etwa weil er nur Lücken füllen möchte und der Vault sehr groß ist), nutzt --missing-only.
Modi im Detail
Default (voller Reconcile). Orphan-Prune + Drift-Scan + Lücken-Füllung. Das ist der inkrementelle Lauf: alles, was im Index nicht mehr passt, wird stillgelegt; alles, was fehlt oder veraltet ist, wird nachgezogen. Stoppt beim ersten Embedding-Fehler — Wiederaufruf nimmt den Faden auf, weil schon erfolgreich verarbeitete Entitäten erneut als „aktuell" gewertet werden.
--missing-only. Überspringt den Drift-Scan und embedded nur Entitäten, zu denen der Index noch keinen Chunk hat. Schnell, aber blind gegenüber Datei-Änderungen, die der Watcher nicht mitbekommen hat. Einsatzfall: regulärer Aufholbetrieb nach kurzem Watcher-Aussetzer, wenn man weiß, dass keine Datei manuell editiert wurde.
--force. Löscht zuerst die Index-Datei. Anschließend gilt jede Entität als nicht-indexiert; alle werden synchron neu embedded. Setzt am Ende last_rebuild_at in der Meta-Tabelle, sodass vector stats den Zeitpunkt anzeigt.
--async. Statt selbst zu embedden, dispatcht das Kommando einen einzigen ReconcileVectorIndexJob auf der embeddings-Queue, der das komplette Work-Set als Payload trägt und intern iteriert. Der laufende apparat-embedding-worker (Supervisor-Programm im Container) arbeitet ihn ab. Sinnvoll für große Vaults und im Container-Standardbetrieb — der Worker hat Retry-Logik (3 Versuche pro Job per Default), das Per-Job-Timeout des Reconcile-Jobs ist auf 2 Stunden gesetzt, deutlich höher als die 60-Sekunden-Default des Workers, sodass auch ein 10k-Vault innerhalb eines Job-Laufs durchläuft.
--force --async. Drop + Queue. Schnellste Hand-off-Variante für einen Modellwechsel auf einem großen Vault: Kommando kehrt nach Sekunden zurück, der Worker arbeitet im Hintergrund.
Pre-Flight bei Modell-Drift
Vor dem inkrementellen oder dem --async-Lauf prüft vector rebuild, ob das persistierte Modell und die persistierte Chunking-Version noch zur Laufzeit-Konfiguration passen. Bei Divergenz bricht das Kommando mit Exit-Code 1 und einer klaren Diagnose ab:
❌ Vector index drift: model "jina/jina-embeddings-v2-base-de" → "alibaba-NLP/gte-large-de". Run `apparat vector rebuild --force`.
Das verhindert, dass der Reconcile-Job mit 1000 Entitäten in die Queue läuft, die der Worker dann gegen einen modell-inkompatiblen Index zerschmettert. --force umgeht den Pre-Flight bewusst — der Drop des Index ist genau die richtige Antwort auf einen Modellwechsel.
Beispielausgaben
Synchron, inkrementell:
Vault "recherche" — embedding 142 of 1000 entities. 142/142 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% ✅ Embedded 142 entities.
Synchron, alles bereits abgedeckt:
✅ Vault "recherche" — vector index already complete (1000 entities).
Async:
Vault "recherche" — embedding 142 of 1000 entities. ✅ Queued ReconcileVectorIndexJob covering 142 entities; track progress with `apparat vector stats` / `vector drift`.
Synchron, Embedding-Fehler nach 17 Entitäten:
Vault "recherche" — embedding 142 of 1000 entities. 17/142 [▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░] 11% ❌ Embedding failed for "abc12345": Connection to http://ollama:11434 failed: … Stopped after 17 / 142 entities.
Exit-Codes
| Code | Bedeutung |
|---|---|
| 0 | Lauf erfolgreich abgeschlossen — sei es im inkrementellen, --force- oder --async-Modus. |
| 1 | Vault konnte nicht aufgelöst werden, Embedding-Service nicht erreichbar (synchron), Repository-Fehler (Modell-Mismatch ohne --force). |
Inhaltliche Befunde — „nichts zu tun" oder „Backlog gequeued" — sind keine Fehler und liefern weiterhin Code 0. Den Worker-Stand prüfst du mit vector stats bzw. vector drift.
Voraussetzungen und Grenzen
--async setzt voraus, dass der Hintergrund-Worker im Container läuft (im Standardbetrieb der Fall). Ein fehlendes Modell bricht das Kommando mit einer klaren Fehlermeldung ab — der Modell-Pull (docker compose exec ollama ollama pull <modell>) bleibt eine bewusste, einmalige Operation und wird nicht automatisch nachgeholt.
Im --missing-only-Modus erfasst das Kommando nur fehlende Einträge, nicht inhaltliche Änderungen seit dem letzten Embedding — wer geänderte Notizen mitziehen will, lässt den Standardmodus laufen oder nimmt --force.
vector rebuild liest aus dem Graph-Index; ein vorheriger graph rebuild ist Voraussetzung für aussagekräftige Ergebnisse.