✅ Решения

← К оглавлению урока

⚡ Кратко

tenacity-обёртка для запроса; embed_content → numpy-вектор; FAISS-индекс + search.

Часть 1. Ответы

  1. Перегрузка сервера (медленный ответ), превышение лимита запросов (rate limit), нестабильная сеть.
  2. Таймаут прерывает ожидание, если сервер не ответил за N секунд. Без него запрос может «висеть» бесконечно, и программа зависнет.
  3. 500 — ошибка сервера, 403 — неверный ключ/нет доступа, 429 — превышен лимит. Повторять бессмысленно 403 (ключ не починится сам).
  4. Эмбеддинг — числовой вектор текста. Тексты похожи, если их векторы близки (малое расстояние / большой косинус).
  5. Поиск по словам ищет совпадения символов/слов; семантический — близость смысла, находит синонимы и перефразировки без общих слов.

Часть 2. Эталон кода

Задание 1 — устойчивый запрос

# resilient.py
import time, os
from tenacity import retry, stop_after_attempt, wait_exponential
from google import genai
from google.genai import types
from dotenv import load_dotenv

load_dotenv()
client = genai.Client(
    api_key=os.getenv("GEMINI_API_KEY"),
    http_options=types.HttpOptions(timeout=10000),   # 10 c
)

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def ask(prompt):
    time.sleep(0.3)
    r = client.models.generate_content(model="gemini-2.0-flash", contents=[prompt])
    return r.text

print(ask("Объясни, что такое rate limit, в 2 предложениях."))

Задание 2 — эмбеддинги

# embed.py
import os
import numpy as np
from google import genai
from dotenv import load_dotenv

load_dotenv()
client = genai.Client(api_key=os.getenv("GEMINI_API_KEY"))

def get_embedding(text):
    r = client.models.embed_content(model="text-embedding-004", contents=text)
    return np.array(r.embeddings[0].values)

v1 = get_embedding("Я люблю программирование.")
v2 = get_embedding("Кодинг – это моё хобби.")
print("Размерность:", v1.shape)              # (768,)

Задание 3 — семантический поиск (FAISS)

# search.py (продолжение embed.py)
import faiss

texts = [
    "Кошки любят рыбу и мясо",
    "Основной рацион кошек — это белок",
    "Чем кормить котёнка?",
    "Собака играет в парке",
    "Спелый ананас полон витаминов",
]
emb = np.array([get_embedding(t) for t in texts])
index = faiss.IndexFlatL2(emb.shape[1])
index.add(emb)

q = get_embedding("Питание кошек").reshape(1, -1)
D, I = index.search(q, 2)
print([texts[i] for i in I[0]])

Часть 3. Со звёздочкой — косинусная близость

# cosine_search.py (фрагмент)
emb = np.array([get_embedding(t) for t in texts]).astype("float32")
faiss.normalize_L2(emb)                  # нормализуем векторы
index = faiss.IndexFlatIP(emb.shape[1])  # скалярное произведение = косинус
index.add(emb)

q = get_embedding("Питание кошек").reshape(1, -1).astype("float32")
faiss.normalize_L2(q)
D, I = index.search(q, 2)                 # D теперь — косинусная близость (больше = ближе)
print([texts[i] for i in I[0]])
⚠️ Имена моделей/полей и API FAISS зависят от версий; сверяйтесь с документацией.