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?

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 Fortschritts­leiste 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 — Wieder­aufruf 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 Standard­betrieb 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 Standard­modus laufen oder nimmt --force.

vector rebuild liest aus dem Graph-Index; ein vorheriger graph rebuild ist Voraussetzung für aussagekräftige Ergebnisse.