Ist Vector Database noch notwendig, wenn das Context Window immer größer wird?
1. Überblick
Jede neue Generation großer Sprachmodelle (LLM) hat ein längeres Context Window. Deshalb fragen sich viele Teams: Wenn man ganze Dokumentenbestände in den Prompt packen kann, braucht man dann überhaupt noch RAG mit Vector Database?
Dieser Artikel antwortet nicht mit „Ja” oder „Nein”. Du wirst sehen, wo die beiden Ansätze unterschiedlich sind. Der Artikel enthält Java-Code zur Schätzung deines Token-Budgets, einen minimalen Vector Store, um den Retrieval-Mechanismus zu verstehen, und einen einfachen Strategy Selector. Danach kannst du selbst entscheiden, was zu deinem System passt.
2. Zwei Wege, Wissen in LLMs zu bringen
LLMs kennen deine internen Daten nicht – zum Beispiel Rückgaberichtlinien, API-Dokumentation oder Kundenbestellverlauf. Es gibt zwei Hauptwege, um diese Daten in die Antwort zu bringen.
2.1. Retrieval mit Vector Database
Ein typischer RAG-Prozess (Retrieval-Augmented Generation) hat vier Schritte:
- Teile Dokumente in kleine Segmente (Chunks) auf.
- Wandle jeden Chunk mit einem Embedding-Modell in einen numerischen Vektor um.
- Speichere die Vektoren in einer Vector Database mit Index, um die nächsten Nachbarn schnell zu finden.
- Wenn der Nutzer fragt, embeddest du die Frage, holst die top-K nächsten Chunks und packst sie in den Prompt.
Der finale Prompt enthält nur einige wenige relevante Segmente, nicht den ganzen Dokumentenbestand.
2.2. Long Context: „einfach alles in den Prompt”
Dieser Weg ist viel einfacher: Du packst alle Dokumente in den Prompt und lässt das Modell selbst die Information finden. Kein Chunking, kein Embedding, keine zusätzliche Infrastruktur.
Bei kleinen Dokumentenbeständen funktioniert das oft gut. Das Modell sieht den ganzen Kontext, kann also auch Fragen beantworten, die mehrere Teile zusammenbringen – etwas, das Top-K-Retrieval leicht verpasst.
3. Warum diese Frage gerade so aktuell ist
Es gibt mehrere Gründe, warum die Diskussion so lebhaft ist:
- Context Windows werden schnell größer. Dokumentenbestände, die früher aufgeteilt werden mussten, passen jetzt in einen Prompt.
- Prompt Caching. Viele Provider erlauben, den fixen Anfangsteil eines Prompts zu cachen. Dadurch sinken Kosten und Latenz, wenn man denselben Dokumentblock erneut sendet.
- RAG ist schwer richtig zu machen. Falsches Chunking, sprachungeeignetes Embedding, Top-K übersieht kritische Segmente – das sind häufige Fehler. Jedes Team, das eine RAG-Pipeline debuggt hat, möchte eine Komplexitätsschicht loswerden.
Long Context hat aber auch eigene Grenzen. Die Qualität der Informationsrückgewinnung in sehr langen Prompts ist nicht gleichmäßig: Information mitten in einem großen Textblock wird manchmal übersehen. Kosten und Latenz wachsen mit der Anzahl der Input-Tokens. Es gibt auch Probleme, die ein Context Window nicht löst, wie Zugriffskontrolle und häufig wechselnde Daten. Die folgenden Abschnitte gehen darauf ein.
4. Anfangen mit Zahlen: Schätzung des Token-Budgets
Bevor du dich für eine Architektur entscheidest, musst du wissen, wie viele Token dein Dokumentenbestand “wiegt”. Das folgende Beispiel nutzt nur Standard-Java.
import java.util.List;
public class TokenBudget {
// Grobe Schätzung: Ein echter Tokenizer gibt andere Ergebnisse, besonders bei Vietnamesisch
private static final double CHARS_PER_TOKEN = 4.0;
public static long estimateTokens(String text) {
return Math.round(text.length() / CHARS_PER_TOKEN);
}
public static long estimateCorpus(List<String> documents) {
return documents.stream().mapToLong(TokenBudget::estimateTokens).sum();
}
public static void main(String[] args) {
List<String> policies = List.of(
"Chính sách hoàn tiền: khách hàng được hoàn tiền khi đơn hàng bị lỗi.",
"Phí vận chuyển được tính theo khối lượng và khoảng cách giao hàng.",
"Tài khoản Customer bị khoá sau nhiều lần đăng nhập sai."
);
long total = estimateCorpus(policies);
long budget = 8_000; // Teil des Context Windows für Dokumente
System.out.printf("Corpus ~%d tokens, budget %d, fits=%b%n",
total, budget, total <= budget);
}
}
Erläuterung der einzelnen Teile:
CHARS_PER_TOKENist eine sehr grobe Heuristik. Vietnamesisch mit Diakritika braucht oft mehr Tokens als Englisch. Wenn du genaue Zahlen brauchst, nutz den Tokenizer des Modells, das du tatsächlich verwendest.budgetist nicht das gesamte Context Window. Du musst Platz für System Prompt, Chat-Historie und Antwort freilassen.- Ergebnis für die drei kurzen Segmente:
Corpus ~48 tokens, budget 8000, fits=true
Wenn fits=true und deine Dokumentensammlung ändert sich selten, ist Long Context ein ernsthafter Kandidat. Wenn die Sammlung um ein Vielfaches größer ist als das Budget, brauchst du Retrieval, egal wie lang das Context Window ist.
5. Ein minimaler Vector Store zum Verstehen des Mechanismus
Um zu sehen, was Retrieval wirklich tut, schreiben wir einen Vector Store im Speicher. Das Embedding hier ist ein “Hashing Bag-of-Words” Stil, nur zur Veranschaulichung. Ein echtes System würde ein Embedding-Modell aufrufen.
import java.util.*;
public record Document(String id, String text) {}
public record ScoredDocument(Document document, double score) {}
public class HashingEmbedder {
private final int dimensions;
public HashingEmbedder(int dimensions) { this.dimensions = dimensions; }
public double[] embed(String text) {
double[] vector = new double[dimensions];
for (String token : text.toLowerCase().split("\\P{L}+")) {
if (token.isBlank()) continue;
vector[Math.floorMod(token.hashCode(), dimensions)] += 1.0;
}
double norm = Math.sqrt(Arrays.stream(vector).map(v -> v * v).sum());
if (norm > 0) for (int i = 0; i < dimensions; i++) vector[i] /= norm;
return vector;
}
}
public class InMemoryVectorStore {
private record Entry(Document document, double[] vector) {}
private final HashingEmbedder embedder;
private final List<Entry> entries = new ArrayList<>();
public InMemoryVectorStore(HashingEmbedder embedder) { this.embedder = embedder; }
public void add(Document document) {
entries.add(new Entry(document, embedder.embed(document.text())));
}
public List<ScoredDocument> search(String query, int topK) {
double[] q = embedder.embed(query);
return entries.stream()
.map(e -> new ScoredDocument(e.document(), dot(q, e.vector())))
.sorted(Comparator.comparingDouble(ScoredDocument::score).reversed())
.limit(topK)
.toList();
}
private static double dot(double[] a, double[] b) {
double sum = 0;
for (int i = 0; i < a.length; i++) sum += a[i] * b[i];
return sum;
}
}
Die Hauptpunkte:
embedteilt Wörter nach Nicht-Buchstaben auf (\\P{L}+), behält also vietnamesische Akzente bei. Jedes Wort wird in eine Dimension des Vektors gehasht.- Der Vektor wird auf Länge 1 normalisiert. Dann ist das Skalarprodukt (
dot) genau die Cosine Similarity. searchiteriert über alle Einträge. Das ist Brute-Force-Suche, O(n) Komplexität. Eine echte Vector Database nutzt Index-Strukturen wie HNSW, um schnell zu bleiben, wenn es viele Vektoren gibt.
Probieren wir es aus:
var store = new InMemoryVectorStore(new HashingEmbedder(1024));
store.add(new Document("refund", "Chính sách hoàn tiền: khách hàng được hoàn tiền khi đơn hàng bị lỗi."));
store.add(new Document("shipping", "Phí vận chuyển được tính theo khối lượng và khoảng cách giao hàng."));
store.add(new Document("account", "Tài khoản Customer bị khoá sau nhiều lần đăng nhập sai."));
store.search("khách hàng muốn hoàn tiền", 2)
.forEach(r -> System.out.printf("%s -> %.3f%n", r.document().id(), r.score()));
refund -> 0.6xx
shipping -> 0.1xx
Die genauen Zahlen hängen von der Hash-Funktion ab. Die Reihenfolge ist stabil: refund rangiert oben, weil es viele Wörter mit der Frage gemeinsam hat. Nur dieses Segment kommt in den Prompt, nicht alle drei.
Hinweis: Echte Embeddings verstehen Semantik (“Geld zurückgeben” ist nah bei “Rückerstattung”), Hashing Bag-of-Words nicht. Nutze das Beispiel oben nicht in der Produktion.
In der Produktion ist, wenn du PostgreSQL verwendest, die Erweiterung pgvector oft ein sinnvoller erster Schritt, bevor du eine spezielle Vector Database erwägst:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE policy_chunk (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
content TEXT NOT NULL,
embedding vector(768)
);
CREATE INDEX ON policy_chunk USING hnsw (embedding vector_cosine_ops);
-- :query_embedding wird von der Anwendung übergeben
SELECT id, content
FROM policy_chunk
WHERE tenant_id = :tenant_id
ORDER BY embedding <=> :query_embedding
LIMIT 5;
Die Dimensionalität 768 muss zum Embedding-Modell passen, das du verwendest. Der Operator <=> berechnet Cosine-Distanz. Die Klausel WHERE tenant_id = ... führt uns zu einem wichtigen Grund, warum Retrieval immer noch notwendig ist, den wir in Abschnitt 7 besprechen.
6. Beide Ansätze kombinieren: Strategie nach Daten wählen
In der Praxis ist die Antwort oft “es kommt auf den Dokumentenbestand an”. Wir können diese Entscheidung als Code ausdrücken, damit das Team einheitliche Kriterien hat.
public enum ContextStrategy { FULL_CONTEXT, RETRIEVAL }
public record CorpusProfile(long totalTokens,
boolean changesFrequently,
boolean needsPerUserAccess) {}
public class ContextStrategySelector {
private final long documentBudgetTokens;
public ContextStrategySelector(long documentBudgetTokens) {
this.documentBudgetTokens = documentBudgetTokens;
}
public ContextStrategy choose(CorpusProfile profile) {
if (profile.needsPerUserAccess()) return ContextStrategy.RETRIEVAL;
if (profile.totalTokens() > documentBudgetTokens) return ContextStrategy.RETRIEVAL;
if (profile.changesFrequently() && profile.totalTokens() > documentBudgetTokens / 2) {
return ContextStrategy.RETRIEVAL; // Prompt Caching ist weniger wirksam, wenn Daten sich ändern
}
return ContextStrategy.FULL_CONTEXT;
}
}
Die Logik in Prioritätsreihenfolge:
- Wenn jeder Nutzer nur einen Teil der Daten sehen darf, musst du filtern, bevor du in den Prompt gehst. Retrieval mit Filter macht das natürlicherweise.
- Wenn der Bestand größer ist als dein Token-Budget, gibt es keine andere Wahl als Retrieval.
- Wenn Daten häufig wechseln und bereits viel Budget einnehmen, ist Prompt Caching weniger wirksam, also ist Retrieval oft günstiger.
- In den übrigen Fällen: nutz Long Context für die Einfachheit.
Der Schwellwert / 2 ist nur ein Beispiel. Du solltest Kosten und Qualität auf deinen Daten messen und anpassen.
7. Die wichtigsten Verhaltensweisen testen
Dependency für Tests – wenn das Projekt einen Spring Boot Parent nutzt, ist die Version schon verwaltet:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class RetrievalAndStrategyTest {
@Test
void givenRefundQuestion_whenSearch_thenRefundPolicyRanksFirst() {
var store = new InMemoryVectorStore(new HashingEmbedder(1024));
store.add(new Document("refund", "Chính sách hoàn tiền: khách hàng được hoàn tiền khi đơn hàng bị lỗi."));
store.add(new Document("shipping", "Phí vận chuyển được tính theo khối lượng và khoảng cách giao hàng."));
var results = store.search("khách hàng muốn hoàn tiền", 1);
assertThat(results).hasSize(1);
assertThat(results.getFirst().document().id()).isEqualTo("refund");
}
@Test
void givenSmallStaticCorpus_whenChoose_thenFullContext() {
var selector = new ContextStrategySelector(50_000);
var profile = new CorpusProfile(10_000, false, false);
assertThat(selector.choose(profile)).isEqualTo(ContextStrategy.FULL_CONTEXT);
}
@Test
void givenPerUserAccess_whenChoose_thenRetrievalEvenIfSmall() {
var selector = new ContextStrategySelector(50_000);
var profile = new CorpusProfile(1_000, false, true);
assertThat(selector.choose(profile)).isEqualTo(ContextStrategy.RETRIEVAL);
}
}
Der dritte Test ist geschäftlich am wichtigsten. Selbst wenn der Dokumentenbestand klein ist, zwingt dich die Zugriffskontrolle, Retrieval zu nutzen. Das ist ein Zwang, den ein großes Context Window nicht eliminiert.
8. Wann du Vector Database noch brauchst, wann nicht
Du brauchst Retrieval immer noch (Vector Database oder Vector Index in einer vorhandenen DB), wenn:
- Der Dokumentenbestand ist viel größer als das Context Window, z. B. alle Support-Tickets oder das gesamte interne Wiki.
- Es gibt Zugriffskontrolle nach Nutzer oder Mandant. Du darfst das Modell nicht Daten “sehen” lassen, auf die ein Nutzer keinen Zugriff hat, auch nicht im Prompt.
- Daten ändern sich ständig und müssen fast sofort reflektiert werden.
- Hohe Query-Rate. Ein riesiger Prompt für jeden Request lässt Kosten und Latenz mit der Token-Zahl wachsen.
- Du brauchst klare Quellenangaben, d. h., du willst wissen, auf welchen Chunk sich die Antwort stützt.
Du kannst Vector Database weglassen, wenn:
- Der Dokumentenbestand ist klein, stabil und passt ins Token-Budget, z. B. Produktrichtlinien oder Moduldokumentation.
- Fragen brauchen oft eine Gesamtsynthese der Dokumente, wie “Fasse alle Unterschiede zwischen diesen zwei Verträgen zusammen”. Top-K-Retrieval schneidet hier schlecht ab.
- Du prototypisierst und möchtest Wert beweisen, bevor du Infrastruktur aufbaust.
Auch nicht unbedingt Vector Search, wenn Abfragen präzise sind wie Order-IDs, SKUs oder Funktionsnamen. Full-Text-Suche oder SQL-Queries sind meist besser. Viele Systeme nutzen Hybrid Search und kombinieren Keyword- und Vector-Suche.
9. Häufige Fehler
- Context Window mit Dokumentenbudget verwechseln. Du vergisst, Platz für System Prompt, Chat-Historie und Output zu reservieren, daher wird die Request in der Produktion gekürzt oder wirft einen Fehler.
- Alles in den Prompt packen ohne die Qualität zu bewerten. Je länger der Prompt, desto leichter übersieht das Modell Details in der Mitte. Erstelle eine Test-Suite mit Fragen und Referenzantworten und messe auf deinen Daten.
- Eine dedizierte Vector Database zu früh bauen. Mit einigen Zehntausend Chunks reicht ein Vector Index in PostgreSQL meist aus, und du sparst dir ein System, das du betreiben musst.
- Zugriffskontrolle nach Retrieval filtern. Top-K zu nehmen und dann nach Berechtigungen zu filtern kann dazu führen, dass alle gültigen Ergebnisse entfernt werden. Bau die Zugriffsbedingung in die Query selbst ein, wie die Klausel
WHERE tenant_idin Abschnitt 5. - Das Embedding-Modell wechseln ohne neu zu indexieren. Vektoren aus zwei verschiedenen Modellen sind nicht untereinander vergleichbar.
Hinweis: Long Context und Retrieval schließen sich nicht gegenseitig aus. Ein häufiges Muster ist großzügiges Retrieval, mehr Chunks als früher zu nehmen und dann das lange Context Window nutzen, damit das Modell selbst filtert. Das reduziert das Risiko zu übersehen und hält die Kosten unter Kontrolle.
10. Fazit
Längere Context Windows verschieben das Gleichgewicht, eliminieren aber nicht die Notwendigkeit für Retrieval. Mit kleinen, stabilen Dokumentenbeständen ist “alles in den Prompt” oft einfach und gut genug. Mit großen Daten, häufigen Änderungen, Zugriffskontrolle oder hohem Durchsatz ist Retrieval noch immer notwendig. Aber es muss nicht zwingend eine separate Vector Database sein: ein Vector Index in einer vorhandenen Datenbank reicht meist aus.
Der praktische Weg ist messen, dann entscheiden. Schätze die Token-Zahl deines Dokumentenbestands, definiere Anforderungen an Zugriffskontrolle und Update-Häufigkeit, dann bewerte die Qualität an echten Fragen. Wenn du diese Kriterien als Code wie ContextStrategySelector ausdrückst, hat das ganze Team eine klare, testbare Entscheidung.