Vector databases to specjalizowane bazy danych zoptymalizowane do przechowywania i wyszukiwania embeddingów. To jak biblioteka z magicznym katalogiem, który znajduje podobne książki na podstawie ich treści!
1┌─────────────────────────────────────────────────────────┐
2│ Vector Databases Landscape │
3├─────────────────────────────────────────────────────────┤
4│ │
5│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
6│ │ Pinecone │ │ Qdrant │ │ Chroma │ │
7│ │ (Cloud) │ │ (Self-host) │ │ (Local) │ │
8│ └──────────────┘ └──────────────┘ └──────────────┘ │
9│ │
10│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
11│ │ Weaviate │ │ Milvus │ │ FAISS │ │
12│ │ (Semantic) │ │ (Enterprise)│ │ (In-memory) │ │
13│ └──────────────┘ └──────────────┘ └──────────────┘ │
14│ │
15└─────────────────────────────────────────────────────────┘1import chromadb
2from chromadb.utils import embedding_functions
3
4# Inicjalizacja klienta
5client = chromadb.Client() # In-memory
6# lub: client = chromadb.PersistentClient(path="./chroma_db") # Persistent
7
8# Konfiguracja funkcji embeddingu
9openai_ef = embedding_functions.OpenAIEmbeddingFunction(
10 api_key="twój-klucz",
11 model_name="text-embedding-3-small"
12)
13
14# Tworzenie kolekcji
15collection = client.create_collection(
16 name="python_safari",
17 embedding_function=openai_ef,
18 metadata={"description": "Dokumentacja kursu Python Safari"}
19)
20
21# Dodawanie dokumentów
22collection.add(
23 documents=[
24 "Python to język programowania wysokiego poziomu",
25 "RAG łączy retrieval z generowaniem tekstu",
26 "Vector databases przechowują embeddings"
27 ],
28 metadatas=[
29 {"topic": "python", "level": "beginner"},
30 {"topic": "ai", "level": "advanced"},
31 {"topic": "databases", "level": "intermediate"}
32 ],
33 ids=["doc1", "doc2", "doc3"]
34)
35
36# Wyszukiwanie
37results = collection.query(
38 query_texts=["Jak zacząć naukę programowania?"],
39 n_results=2,
40 where={"level": "beginner"} # Filtrowanie po metadanych
41)
42
43print(results)1from qdrant_client import QdrantClient
2from qdrant_client.models import Distance, VectorParams, PointStruct
3import numpy as np
4
5# Połączenie z Qdrant
6client = QdrantClient(host="localhost", port=6333)
7# lub: client = QdrantClient(":memory:") # In-memory
8
9# Tworzenie kolekcji
10client.create_collection(
11 collection_name="documents",
12 vectors_config=VectorParams(
13 size=1536, # Rozmiar embeddingu
14 distance=Distance.COSINE
15 )
16)
17
18# Dodawanie punktów
19def add_documents(texts: list[str], embeddings: list[list[float]]):
20 """Dodaje dokumenty do Qdrant."""
21 points = [
22 PointStruct(
23 id=i,
24 vector=embedding,
25 payload={"text": text}
26 )
27 for i, (text, embedding) in enumerate(zip(texts, embeddings))
28 ]
29
30 client.upsert(
31 collection_name="documents",
32 points=points
33 )
34
35# Wyszukiwanie
36def search(query_embedding: list[float], limit: int = 5):
37 """Wyszukuje podobne dokumenty."""
38 results = client.search(
39 collection_name="documents",
40 query_vector=query_embedding,
41 limit=limit
42 )
43
44 return [
45 {
46 "text": hit.payload["text"],
47 "score": hit.score
48 }
49 for hit in results
50 ]
51
52# Filtrowanie
53from qdrant_client.models import Filter, FieldCondition, MatchValue
54
55filtered_results = client.search(
56 collection_name="documents",
57 query_vector=query_embedding,
58 query_filter=Filter(
59 must=[
60 FieldCondition(
61 key="category",
62 match=MatchValue(value="python")
63 )
64 ]
65 ),
66 limit=5
67)1import faiss
2import numpy as np
3from dataclasses import dataclass
4
5@dataclass
6class FAISSIndex:
7 """Wrapper dla FAISS."""
8
9 dimension: int
10 index: faiss.Index = None
11 documents: list[str] = None
12
13 def __post_init__(self):
14 # Różne typy indeksów
15 # Flat - dokładny, wolniejszy
16 self.index = faiss.IndexFlatL2(self.dimension)
17
18 # IVF - szybszy, przybliżony
19 # quantizer = faiss.IndexFlatL2(self.dimension)
20 # self.index = faiss.IndexIVFFlat(quantizer, self.dimension, 100)
21
22 self.documents = []
23
24 def add(self, embeddings: np.ndarray, documents: list[str]):
25 """Dodaje wektory do indeksu."""
26 embeddings = np.array(embeddings).astype('float32')
27 self.index.add(embeddings)
28 self.documents.extend(documents)
29
30 def search(self, query_embedding: np.ndarray, k: int = 5) -> list[tuple[str, float]]:
31 """Wyszukuje k najbliższych sąsiadów."""
32 query = np.array([query_embedding]).astype('float32')
33 distances, indices = self.index.search(query, k)
34
35 results = []
36 for idx, dist in zip(indices[0], distances[0]):
37 if idx < len(self.documents):
38 results.append((self.documents[idx], float(dist)))
39
40 return results
41
42# Przykład użycia
43faiss_index = FAISSIndex(dimension=384)
44
45# Dodaj dokumenty
46embeddings = np.random.rand(100, 384).astype('float32')
47documents = [f"Dokument {i}" for i in range(100)]
48faiss_index.add(embeddings, documents)
49
50# Wyszukaj
51query = np.random.rand(384).astype('float32')
52results = faiss_index.search(query, k=5)1from pinecone import Pinecone, ServerlessSpec
2
3# Inicjalizacja
4pc = Pinecone(api_key="twój-klucz")
5
6# Tworzenie indeksu
7pc.create_index(
8 name="python-safari",
9 dimension=1536,
10 metric="cosine",
11 spec=ServerlessSpec(
12 cloud="aws",
13 region="us-east-1"
14 )
15)
16
17# Połączenie z indeksem
18index = pc.Index("python-safari")
19
20# Upsert (wstaw/aktualizuj)
21index.upsert(
22 vectors=[
23 {
24 "id": "doc1",
25 "values": [0.1, 0.2, ...], # 1536 wartości
26 "metadata": {
27 "text": "Python to język programowania",
28 "category": "programming",
29 "level": 1
30 }
31 }
32 ],
33 namespace="tutorials"
34)
35
36# Wyszukiwanie
37results = index.query(
38 vector=[0.1, 0.2, ...],
39 top_k=10,
40 include_metadata=True,
41 namespace="tutorials",
42 filter={
43 "category": {"$eq": "programming"},
44 "level": {"$lte": 3}
45 }
46)
47
48# Statystyki
49stats = index.describe_index_stats()
50print(f"Liczba wektorów: {stats['total_vector_count']}")1from rank_bm25 import BM25Okapi
2import numpy as np
3
4class HybridSearch:
5 """Łączy semantic search z keyword search."""
6
7 def __init__(self, documents: list[str], embeddings: np.ndarray):
8 self.documents = documents
9 self.embeddings = embeddings
10
11 # BM25 dla keyword search
12 tokenized = [doc.lower().split() for doc in documents]
13 self.bm25 = BM25Okapi(tokenized)
14
15 def search(
16 self,
17 query: str,
18 query_embedding: np.ndarray,
19 alpha: float = 0.5, # Waga semantic search
20 top_k: int = 5
21 ) -> list[tuple[str, float]]:
22 """Hybrid search z konfigurowalnymi wagami."""
23
24 # Semantic search scores
25 semantic_scores = np.dot(self.embeddings, query_embedding)
26 semantic_scores /= np.linalg.norm(self.embeddings, axis=1)
27 semantic_scores /= np.linalg.norm(query_embedding)
28
29 # Normalize to [0, 1]
30 semantic_scores = (semantic_scores - semantic_scores.min()) / (semantic_scores.max() - semantic_scores.min())
31
32 # BM25 scores
33 bm25_scores = np.array(self.bm25.get_scores(query.lower().split()))
34 if bm25_scores.max() > 0:
35 bm25_scores = bm25_scores / bm25_scores.max()
36
37 # Hybrid score
38 hybrid_scores = alpha * semantic_scores + (1 - alpha) * bm25_scores
39
40 # Top K
41 top_indices = np.argsort(hybrid_scores)[::-1][:top_k]
42
43 return [(self.documents[i], hybrid_scores[i]) for i in top_indices]Vector databases to infrastruktura każdego systemu RAG. W następnej lekcji poznasz LlamaIndex - framework, który upraszcza budowanie aplikacji RAG!