Redis®*: Vektorsuche und RAG
Semantische Suche, Empfehlungen und Retrieval-Augmented Generation mit Redis
👋 Willkommen in der Stackhero-Dokumentation!
Stackhero bietet eine einsatzbereite Redis Cloud-Lösung mit zahlreichen Vorteilen, darunter:
Redis CommanderWeb-Oberfläche inklusive.- Unbegrenzte Nachrichtengröße und Übertragungen.
- Updates ganz einfach per Klick.
- Optimale Performance und starke Sicherheit durch eine private, dedizierte Infrastruktur.
Sparen Sie Zeit und vereinfachen Sie Ihren Alltag: Es dauert nur 5 Minuten, um die Redis Cloud Hosting-Lösung von Stackhero auszuprobieren!
Wenn Sie eine semantische Suche, eine Empfehlungs-Engine oder eine Retrieval-Augmented Generation (RAG) Pipeline entwickeln, benötigen Sie eine zuverlässige Möglichkeit, Embeddings zu speichern und schnell die nächsten Übereinstimmungen zu einer Anfrage zu finden. Ihre Redis-Instanz auf Stackhero bietet dies nativ: Die Query Engine indexiert Vektoren und beantwortet Ähnlichkeitssuchen in Millisekunden. Sie müssen keine separate Vektordatenbank neben Redis betreiben – alles läuft an einem Ort.
Dieser Ansatz ist sowohl produktiv als auch effizient, dank zweier zentraler Funktionen:
- Hybride Abfragen. Sie können die Vektor-Ähnlichkeitssuche mit Standardfiltern wie numerischen Bereichen, Tags oder Volltext kombinieren – alles in einer einzigen Anfrage. Zum Beispiel: „Die 5 Chunks, die dieser Frage am nächsten sind, aber nur aus Dokumenten, die dieser Benutzer lesen darf und die nach Januar veröffentlicht wurden“, kann in einer einzigen Abfrage erledigt werden, nicht in mehreren.
- Vereinfachte Architektur. Embeddings, Cache und Sessions befinden sich gemeinsam an einem Ort und teilen sich Zugangsdaten, Backups und Monitoring. Das reduziert Komplexität und Overhead.
Bevor Sie starten
Aktivieren Sie das Modul Search (und JSON, falls Sie Dokumente als JSON speichern möchten) im Bereich Modules Ihrer Service-Konfiguration auf dem Stackhero Dashboard. Für eine Schritt-für-Schritt-Anleitung siehe die Search und JSON Anleitung.
Erstellen eines Vektor-Index
Um ein Vektorfeld zu deklarieren, geben Sie dessen Dimension und Distanzmetrik an. Die Dimension muss zu Ihrem Embedding-Modell passen: Zum Beispiel 1536 für OpenAI text-embedding-3-small, 768 für viele Open-Source-Modelle usw.
FT.CREATE chunksIndex
ON HASH
PREFIX 1 chunk:
SCHEMA
content TEXT
documentId TAG
publishedAt NUMERIC
embedding VECTOR HNSW 6
TYPE FLOAT32
DIM 1536
DISTANCE_METRIC COSINE
HNSW erstellt einen Graph-Index, der auch bei wachsender Sammlung schnelle Abfragen ermöglicht und ist daher ab einigen Tausend Vektoren die beste Wahl. Für kleine Sammlungen, bei denen exakte Ergebnisse wichtig sind, können Sie FLAT für eine Brute-Force-Suche verwenden.
COSINE ist die empfohlene Metrik für die meisten textbasierten Embedding-Modelle. Sie können auch L2 oder IP wählen, falls Ihr Anwendungsfall dies erfordert.
Abfragen
Eine K-nearest-neighbors (KNN) Abfrage sieht so aus. Der Vektor wird als rohe little-endian float32 Bytes übergeben:
FT.SEARCH chunksIndex "*=>[KNN 5 @embedding $queryVector AS score]"
PARAMS 2 queryVector "<rawBytes>"
SORTBY score
RETURN 2 content score
DIALECT 2
Um Ähnlichkeitssuche mit Filtern zu kombinieren, ersetzen Sie einfach * durch einen Filterausdruck:
FT.SEARCH chunksIndex "(@documentId:{doc42} @publishedAt:[1735689600 +inf])=>[KNN 5 @embedding $queryVector AS score]"
PARAMS 2 queryVector "<rawBytes>"
SORTBY score
DIALECT 2
Vektorabfragen erfordern
DIALECT 2. Wenn Sie dies weglassen, wird die ältere Syntax verwendet und die Abfrage führt zu einem Fehler.
Eine RAG-Pipeline in Node.js
import { createClient, SCHEMA_FIELD_TYPE, SCHEMA_VECTOR_FIELD_ALGORITHM } from 'redis';
const client = createClient({ url: process.env.STACKHERO_REDIS_URL_TLS });
await client.connect();
const DIMENSIONS = 1536;
const toBytes = embedding => Buffer.from(new Float32Array(embedding).buffer);
// 1. Erstellen Sie den Index einmalig beim Start
try {
await client.ft.create(
'chunksIndex',
{
content: SCHEMA_FIELD_TYPE.TEXT,
documentId: SCHEMA_FIELD_TYPE.TAG,
embedding: {
type: SCHEMA_FIELD_TYPE.VECTOR,
ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.HNSW,
TYPE: 'FLOAT32',
DIM: DIMENSIONS,
DISTANCE_METRIC: 'COSINE'
}
},
{ ON: 'HASH', PREFIX: 'chunk:' }
);
}
catch (error) {
if (!error.message.includes('Index already exists')) {
throw error;
}
}
// 2. Indexieren Sie Ihre Chunks (das Embedding stammt von Ihrem Modellanbieter)
async function indexChunk({ id, documentId, content, embedding }) {
await client.hSet(`chunk:${id}`, {
content,
documentId,
embedding: toBytes(embedding)
});
}
// 3. Rufen Sie die Chunks ab, die einer Frage am nächsten sind, beschränkt auf ein Dokument
async function retrieve({ questionEmbedding, documentId, count = 5 }) {
const results = await client.ft.search(
'chunksIndex',
`(@documentId:{${documentId}})=>[KNN ${count} @embedding $queryVector AS score]`,
{
PARAMS: { queryVector: toBytes(questionEmbedding) },
SORTBY: 'score',
RETURN: [ 'content', 'score' ],
DIALECT: 2
}
);
return results.documents.map(({ value }) => value.content);
}
// 4. Geben Sie diese als Kontext an Ihr Modell weiter
const context = await retrieve({ questionEmbedding, documentId: 'doc42' });
const prompt = `Antworten Sie ausschließlich mit diesem Kontext:\n\n${context.join('\n\n')}\n\nFrage: ${question}`;
Dasselbe in Python
import os
import numpy as np
import redis
from redis.commands.search.field import TextField, TagField, VectorField
from redis.commands.search.index_definition import IndexDefinition, IndexType
from redis.commands.search.query import Query
DIMENSIONS = 1536
r = redis.from_url(os.environ['STACKHERO_REDIS_URL_TLS'])
# Erstellen Sie den Index einmalig beim Start
try:
r.ft('chunksIndex').create_index(
(
TextField('content'),
TagField('documentId'),
VectorField(
'embedding',
'HNSW',
{'TYPE': 'FLOAT32', 'DIM': DIMENSIONS, 'DISTANCE_METRIC': 'COSINE'},
),
),
definition=IndexDefinition(prefix=['chunk:'], index_type=IndexType.HASH),
)
except redis.ResponseError as error:
if 'Index already exists' not in str(error):
raise
def index_chunk(chunk_id, document_id, content, embedding):
r.hset(
f'chunk:{chunk_id}',
mapping={
'content': content,
'documentId': document_id,
'embedding': np.array(embedding, dtype=np.float32).tobytes(),
},
)
def retrieve(question_embedding, document_id, count=5):
query = (
Query(f'(@documentId:{{{document_id}}})=>[KNN {count} @embedding $queryVector AS score]')
.sort_by('score')
.return_fields('content', 'score')
.dialect(2)
)
results = r.ft('chunksIndex').search(
query,
query_params={'queryVector': np.array(question_embedding, dtype=np.float32).tobytes()},
)
return [document.content for document in results.docs]
Dimensionierung Ihres Dienstes
Vektoren werden im Arbeitsspeicher gespeichert. Sie können den Speicherbedarf mit folgender Formel abschätzen:
numberOfVectors x dimensions x 4 bytes, plus etwa 30–50 % zusätzlich für den HNSW-Graphen.
Beispiel: Eine Million Vektoren mit 1536 Dimensionen benötigen etwa 6 GB für die Rohdaten. Ein 20-GB-Plan ist für diese Größenordnung ein komfortabler Startpunkt. Sie können den Speicherbedarf reduzieren, indem Sie:
- Ein kleineres Modell verwenden. Ein Modell mit 768 Dimensionen halbiert den Speicherbedarf im Vergleich zu einem mit 1536 Dimensionen.
FLOAT16stattFLOAT32speichern, sofern Ihr Modell dies unterstützt, was den Speicherbedarf nochmals halbiert.
Sie können die tatsächliche Nutzung mit FT.INFO chunksIndex und der Metrik used_memory in Ihrem Prometheus Monitoring überwachen. Wenn Ihr Bedarf steigt, können Sie Ihren Plan unkompliziert direkt über das Dashboard upgraden.
Gut zu wissen
- Vektorindizes funktionieren nur auf Datenbank 0, wie alle Suchindizes in Redis.
- Vektormengen bieten eine einfachere Alternative ohne Einrichtung. Die Befehle
VADDundVSIMsind in Redis integriert, sodass Sie sofort mit der Ähnlichkeitssuche starten können, ohne Module zu aktivieren. Das ist praktisch für einfache Empfehlungsszenarien auf einer einzigen Menge. Die Query Engine ist die bessere Wahl, wenn Sie Filter, Paginierung oder mehrere Felder benötigen. - Das Erstellen eines Index über bestehende Schlüssel läuft im Hintergrund. Sie können den Fortschritt mit
FT.INFOprüfen. Abfragen liefern solange Teilergebnisse, bis die Indexierung abgeschlossen ist.
Weitere Informationen zur Vektorsuche in Redis finden Sie in der offiziellen Redis Vector Search Dokumentation.