$npx -y skills add AlexAI-MCP/hermes-CCC --skill faissFacebook AI Similarity Search — ultra-fast vector similarity search for large-scale local embedding retrieval without a database server.
| 1 | # FAISS — Fast Vector Similarity Search |
| 2 | |
| 3 | FAISS (Facebook AI Similarity Search) is a library for efficient similarity search over large collections of vectors. No server required — runs entirely in-process. |
| 4 | |
| 5 | ## When to Use FAISS |
| 6 | |
| 7 | - Millions of vectors, need maximum speed |
| 8 | - Can't run a server (embedded use case) |
| 9 | - Research / prototyping at scale |
| 10 | - Custom index types (HNSW, IVF, PQ) |
| 11 | |
| 12 | ## Setup |
| 13 | |
| 14 | ```bash |
| 15 | pip install faiss-cpu # CPU only |
| 16 | pip install faiss-gpu # GPU (requires CUDA) |
| 17 | pip install sentence-transformers numpy |
| 18 | ``` |
| 19 | |
| 20 | --- |
| 21 | |
| 22 | ## Basic Flat Index (Exact Search) |
| 23 | |
| 24 | ```python |
| 25 | import faiss |
| 26 | import numpy as np |
| 27 | from sentence_transformers import SentenceTransformer |
| 28 | |
| 29 | model = SentenceTransformer("all-MiniLM-L6-v2") # dim=384 |
| 30 | DIM = 384 |
| 31 | |
| 32 | # Build index |
| 33 | documents = [ |
| 34 | "Python async programming", |
| 35 | "Machine learning with PyTorch", |
| 36 | "Docker container management", |
| 37 | "GraphQL API design", |
| 38 | ] |
| 39 | |
| 40 | embeddings = model.encode(documents).astype(np.float32) |
| 41 | |
| 42 | # Flat L2 index (exact, brute force) |
| 43 | index = faiss.IndexFlatL2(DIM) |
| 44 | index.add(embeddings) |
| 45 | print(f"Index size: {index.ntotal}") |
| 46 | |
| 47 | # Search |
| 48 | query = "how to do async in Python?" |
| 49 | q_vec = model.encode([query]).astype(np.float32) |
| 50 | |
| 51 | distances, indices = index.search(q_vec, k=3) |
| 52 | for i, idx in enumerate(indices[0]): |
| 53 | print(f"[{distances[0][i]:.3f}] {documents[idx]}") |
| 54 | ``` |
| 55 | |
| 56 | --- |
| 57 | |
| 58 | ## Cosine Similarity (Normalize first) |
| 59 | |
| 60 | ```python |
| 61 | # For cosine similarity: normalize vectors, use IndexFlatIP (inner product) |
| 62 | def normalize(vecs): |
| 63 | norms = np.linalg.norm(vecs, axis=1, keepdims=True) |
| 64 | return vecs / np.maximum(norms, 1e-8) |
| 65 | |
| 66 | embeddings_norm = normalize(embeddings.astype(np.float32)) |
| 67 | index_cos = faiss.IndexFlatIP(DIM) |
| 68 | index_cos.add(embeddings_norm) |
| 69 | |
| 70 | q_norm = normalize(model.encode([query]).astype(np.float32)) |
| 71 | scores, indices = index_cos.search(q_norm, k=3) |
| 72 | ``` |
| 73 | |
| 74 | --- |
| 75 | |
| 76 | ## IVF Index (Fast Approximate Search) |
| 77 | |
| 78 | ```python |
| 79 | # For large datasets (>100k vectors) |
| 80 | N_CLUSTERS = 100 # sqrt(N) is a good heuristic |
| 81 | |
| 82 | quantizer = faiss.IndexFlatL2(DIM) |
| 83 | index_ivf = faiss.IndexIVFFlat(quantizer, DIM, N_CLUSTERS) |
| 84 | |
| 85 | # Must train before adding |
| 86 | index_ivf.train(embeddings) |
| 87 | index_ivf.add(embeddings) |
| 88 | index_ivf.nprobe = 10 # search 10 clusters (higher = more accurate, slower) |
| 89 | |
| 90 | distances, indices = index_ivf.search(q_vec, k=5) |
| 91 | ``` |
| 92 | |
| 93 | --- |
| 94 | |
| 95 | ## HNSW Index (Best Speed/Accuracy Tradeoff) |
| 96 | |
| 97 | ```python |
| 98 | index_hnsw = faiss.IndexHNSWFlat(DIM, 32) # 32 = M parameter |
| 99 | index_hnsw.add(embeddings) |
| 100 | |
| 101 | distances, indices = index_hnsw.search(q_vec, k=5) |
| 102 | ``` |
| 103 | |
| 104 | --- |
| 105 | |
| 106 | ## Save and Load Index |
| 107 | |
| 108 | ```python |
| 109 | # Save |
| 110 | faiss.write_index(index, "my_index.faiss") |
| 111 | |
| 112 | # Load |
| 113 | index = faiss.read_index("my_index.faiss") |
| 114 | ``` |
| 115 | |
| 116 | --- |
| 117 | |
| 118 | ## With Metadata (store separately) |
| 119 | |
| 120 | ```python |
| 121 | import pickle |
| 122 | |
| 123 | # FAISS only stores vectors — store metadata separately |
| 124 | metadata = [{"id": i, "text": doc} for i, doc in enumerate(documents)] |
| 125 | |
| 126 | # Save both |
| 127 | faiss.write_index(index, "vectors.faiss") |
| 128 | with open("metadata.pkl", "wb") as f: |
| 129 | pickle.dump(metadata, f) |
| 130 | |
| 131 | # Load and query |
| 132 | index = faiss.read_index("vectors.faiss") |
| 133 | with open("metadata.pkl", "rb") as f: |
| 134 | metadata = pickle.load(f) |
| 135 | |
| 136 | distances, indices = index.search(q_vec, k=3) |
| 137 | for idx in indices[0]: |
| 138 | print(metadata[idx]["text"]) |
| 139 | ``` |
| 140 | |
| 141 | --- |
| 142 | |
| 143 | ## GPU Acceleration |
| 144 | |
| 145 | ```python |
| 146 | res = faiss.StandardGpuResources() |
| 147 | index_gpu = faiss.index_cpu_to_gpu(res, 0, index) # GPU 0 |
| 148 | distances, indices = index_gpu.search(q_vec, k=5) |
| 149 | ``` |
| 150 | |
| 151 | --- |
| 152 | |
| 153 | ## Index Selection Guide |
| 154 | |
| 155 | | Index | Size | Speed | Accuracy | Use Case | |
| 156 | |-------|------|-------|----------|----------| |
| 157 | | IndexFlatL2 | Any | Slow | 100% | <100k vectors, exact needed | |
| 158 | | IndexFlatIP | Any | Slow | 100% | Cosine similarity | |
| 159 | | IndexIVFFlat | Large | Fast | ~95% | 100k–10M vectors | |
| 160 | | IndexHNSWFlat | Medium | Very fast | ~99% | Best general choice | |
| 161 | | IndexIVFPQ | Huge | Fastest | ~90% | Billions, memory-constrained | |