Lección 77 · 10 min · Gratis

Sistema de recomendación de películas con Gemini y Qdrant

Copyright 2026 Google LLC.
# @title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

Nota: Este notebook requiere límites de tasa de nivel de pago para ejecutarse correctamente. (Consulta precios para más detalles).

Descripción general

La API de Gemini proporciona acceso a una familia de modelos de IA generativa para crear contenido y resolver problemas. Estos modelos están diseñados y entrenados para manejar texto e imágenes como entrada.

Qdrant es un motor de búsqueda de similitud vectorial de código abierto diseñado para una búsqueda semántica eficiente y escalable. Ofrece una API simple pero potente para almacenar y buscar vectores de alta dimensión, admite el filtrado con metadatos (payloads) y se integra fácilmente en sistemas de producción. Qdrant se puede autoalojar o acceder a través de su servicio en la nube administrado, lo que lo hace rápido de configurar e ideal para una amplia gama de aplicaciones de IA que dependen de la comprensión y recuperación semántica.

En este notebook, aprenderás a realizar una búsqueda de similitud en datos de un sitio web con la ayuda de la API de Gemini y Qdrant.

andycandy's GitHub avatar

Este notebook fue contribuido por Anand Roy.

LinkedIn - Ve los otros notebooks de %pip install -q "google-genai>=2.9.0" %pip install -q protobuf==4.25.1 qdrant-client[fastembed]

Configura tu clave de API

Para ejecutar la siguiente celda, tu clave de API debe estar almacenada en un Secreto de Colab llamado GEMINI_API_KEY. Si aún no tienes una clave de API, o no estás seguro de cómo crear un Secreto de Colab, consulta Autenticación para ver un ejemplo.

from google.colab import userdata
from google import genai

GEMINI_API_KEY=userdata.get('GEMINI_API_KEY')
client = genai.Client(api_key=GEMINI_API_KEY)

Construyendo el índice vectorial de películas

Esta sección cubre la preparación del conjunto de datos de películas, la generación de embeddings usando Gemini y su indexación en Qdrant para la búsqueda de similitud.

1. Carga el conjunto de datos de Kaggle

Comienza cargando el conjunto de datos de Kaggle usando la biblioteca kagglehub. El conjunto de datos utilizado en este notebook es el TMDB Movie Dataset 2024, que contiene aproximadamente más de 1 millón de entradas de películas.

import kagglehub
from kagglehub import KaggleDatasetAdapter

file_path = "TMDB_movie_dataset_v11.csv"

df = kagglehub.load_dataset(
  KaggleDatasetAdapter.PANDAS,
  "asaniczka/tmdb-movies-dataset-2023-930k-movies",
  file_path,
)
/tmp/ipykernel_26130/2431045845.py:6: DeprecationWarning: load_dataset is deprecated and will be removed in a future version.
  df = kagglehub.load_dataset(

2. Inspecciona la estructura del conjunto de datos

Dado que el conjunto de datos es grande, inspeccionarlo te ayuda a identificar campos útiles y a filtrar datos irrelevantes desde el principio.

print("\nDataset Columns:")
print(df.columns)

print("\nMissing Values per Column:")
print(df.isnull().sum())

print(f"\nNumber of rows: {len(df)}")
print(f"Number of unique IDs: {df['id'].nunique()}")
Dataset Columns:
Index(['id', 'title', 'vote_average', 'vote_count', 'status', 'release_date',
       'revenue', 'runtime', 'adult', 'backdrop_path', 'budget', 'homepage',
       'imdb_id', 'original_language', 'original_title', 'overview',
       'popularity', 'poster_path', 'tagline', 'genres',
       'production_companies', 'production_countries', 'spoken_languages',
       'keywords'],
      dtype='object')

Missing Values per Column:
id                            0
title                        13
vote_average                  0
vote_count                    0
status                        0
release_date             239109
revenue                       0
runtime                       0
adult                         0
backdrop_path            930599
budget                        0
homepage                1123663
imdb_id                  624300
original_language             0
original_title               13
overview                 270635
popularity                    0
poster_path              418486
tagline                 1078824
genres                   526517
production_companies     702743
production_countries     580808
spoken_languages         557829
keywords                 928832
dtype: int64

Number of rows: 1254611
Number of unique IDs: 1253629

3. Filtra y limpia el conjunto de datos

Este paso filtra el conjunto de datos para mantener solo los metadatos útiles para la búsqueda semántica: id, title, overview, genres, keywords, tagline y release_date. Estos campos proporcionan suficiente contexto para generar embeddings significativos.

Se eliminan las entradas (filas) a las que les falta un title o que carecen tanto de overview como de genres, ya que no tienen suficientes datos descriptivos para recomendaciones precisas.

import pandas as pd
import numpy as np
import ast
print(f"Original rows: {len(df)}")

columns_to_keep = ['id', 'title', 'overview', 'genres', 'keywords', 'tagline', 'release_date']

df_relevant = df[columns_to_keep].copy()

print(f"Rows before dropping missing title: {len(df_relevant)}")
df_relevant.dropna(subset=['title'], inplace=True)
df_relevant = df_relevant[~(df_relevant['genres'].isna() & df_relevant['overview'].isna())]
print(f"Rows after dropping missing title and dropping missing (genres and overview): {len(df_relevant)}")

# Fill missing text columns with empty strings
text_cols_to_fill = ['overview', 'genres', 'keywords', 'tagline']
for col in text_cols_to_fill:
    df_relevant[col] = df_relevant[col].fillna('')


# Extract release year from the release_date string
def get_year(date_str):
    if pd.isna(date_str) or not isinstance(date_str, str) or len(date_str) < 4:
        return None
    try:
        return int(date_str[:4])
    except (ValueError, TypeError):
        return None

df_relevant['release_year'] = df_relevant['release_date'].apply(get_year)

print("\nSample data after cleaning (keeping missing overviews):")
print(df_relevant[['id', 'title', 'overview', 'genres', 'keywords', 'tagline', 'release_year']].head())
Original rows: 1254611
Rows before dropping missing title: 1254611
Rows after dropping missing title and dropping missing (genres and overview): 1109811

Sample data after cleaning (keeping missing overviews):
       id            title                                           overview  \
0   27205        Inception  Cobb, a skilled thief who commits corporate es...   
1  157336     Interstellar  The adventures of a group of explorers who mak...   
2     155  The Dark Knight  Batman raises the stakes in his war on crime. ...   
3   19995           Avatar  In the 22nd century, a paraplegic Marine is di...   
4   24428     The Avengers  When an unexpected enemy emerges and threatens...   

                                        genres  \
0           Action, Science Fiction, Adventure   
1            Adventure, Drama, Science Fiction   
2               Drama, Action, Crime, Thriller   
3  Action, Adventure, Fantasy, Science Fiction   
4           Science Fiction, Action, Adventure   

                                            keywords  \
0  rescue, mission, dream, airplane, paris, franc...   
1  rescue, future, spacecraft, race against time,...   
2  joker, sadism, chaos, secret identity, crime f...   
3  future, society, culture clash, space travel, ...   
4  new york city, superhero, shield, based on com...   

                                             tagline  release_year  
0               Your mind is the scene of the crime.        2010.0  
1  Mankind was born on Earth. It was never meant ...        2014.0  
2                  Welcome to a world without rules.        2008.0  
3                        Enter the world of Pandora.        2009.0  
4                            Some assembly required.        2012.0

4. Prepara el texto para embedding

Este paso prepara los metadatos de la película para embedding combinando campos relevantes en una única cadena de texto estructurada. Esta representación incluye el título, la sinopsis, los géneros, las palabras clave, el eslogan y el año de lanzamiento (si está disponible). La salida se almacena en una nueva columna llamada text_for_embedding.

Los embeddings son representaciones vectoriales numéricas de texto que capturan el significado semántico y las relaciones. Estos vectores se pueden usar para tareas como la búsqueda de similitud y la agrupación. Aprende más sobre los embeddings de texto y explora el notebook de embeddings de Gemini.

def create_embedding_text(row):
    """Combines available movie metadata into a single string for embedding."""
    # Title is always present, so it can be included directly
    title_str = f"Title: {row['title']}"
    overview_str = f"Overview: {row['overview']}" if row['overview'] else ""
    year_str = f"Release Year: {int(row['release_year'])}" if pd.notna(row['release_year']) else ""
    genre_str = f"Genres: {row['genres']}" if row['genres'] else ""
    keywords_str = f"Keywords: {row['keywords']}" if row['keywords'] else ""
    tagline_str = f"Tagline: {row['tagline']}" if row['tagline'] else ""

    parts = [
        title_str,
        overview_str,
        year_str,
        genre_str,
        keywords_str,
        tagline_str
    ]
    return "\n".join(part for part in parts if part)

df_relevant['text_for_embedding'] = df_relevant.apply(create_embedding_text, axis=1)

# Use this to inspect how movie data has been transformed for embedding
print(df_relevant[['id', 'title', 'text_for_embedding']].head())
id            title                                 text_for_embedding
0   27205        Inception  Title: Inception\nOverview: Cobb, a skilled th...
1  157336     Interstellar  Title: Interstellar\nOverview: The adventures ...
2     155  The Dark Knight  Title: The Dark Knight\nOverview: Batman raise...
3   19995           Avatar  Title: Avatar\nOverview: In the 22nd century, ...
4   24428     The Avengers  Title: The Avengers\nOverview: When an unexpec...

5. Muestra un subconjunto para desarrollo

Para que el notebook sea fácil de ejecutar y garantizar un desarrollo eficiente, querrás iterar rápidamente y minimizar el uso de recursos. En lugar de usar el conjunto de datos completo, este paso muestrea 5,000 películas de los datos limpios, a menos que el conjunto de datos ya sea más pequeño, en cuyo caso se usan todas las entradas.

SAMPLE_SIZE = 5000

if len(df_relevant) > SAMPLE_SIZE:
    print(f"\nTaking a random sample of {SAMPLE_SIZE} movies for development.")
    df_sample = df_relevant.sample(n=SAMPLE_SIZE, random_state=42)
else:
    print(f"\nCleaned dataset size ({len(df_relevant)}) is smaller than or equal to SAMPLE_SIZE. Using the full cleaned dataset.")
    df_sample = df_relevant

print(f"Working with {len(df_sample)} movies for the next steps.")
print(df_sample[['id', 'title', 'release_year']].head())

columns_for_payload = ['title', 'overview', 'genres', 'keywords', 'tagline', 'release_year']
columns_final = ['id', 'text_for_embedding'] + columns_for_payload
df_sample = df_sample[columns_final]

print("\nFinal sample DataFrame structure for embedding/indexing:")
print(df_sample.info())
Taking a random sample of 5000 movies for development.
Working with 5000 movies for the next steps.
            id               title  release_year
95090   714945              Gather        2020.0
198838  117602    California Girls        1985.0
827619  648297          After Jake        2013.0
801274  637062  Duas vezes Senzala        2017.0
846938  342211                Rise           NaN

Final sample DataFrame structure for embedding/indexing:
<class 'pandas.core.frame.DataFrame'>
Index: 5000 entries, 95090 to 650934
Data columns (total 8 columns):
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 0   id                  5000 non-null   int64  
 1   text_for_embedding  5000 non-null   object 
 2   title               5000 non-null   object 
 3   overview            5000 non-null   object 
 4   genres              5000 non-null   object 
 5   keywords            5000 non-null   object 
 6   tagline             5000 non-null   object 
 7   release_year        4260 non-null   float64
dtypes: float64(1), int64(1), object(6)
memory usage: 351.6+ KB
None

6. Inicializa Qdrant para la indexación vectorial

Con los datos preparados, el siguiente paso es configurar Qdrant, un motor de búsqueda de similitud vectorial optimizado para almacenar y consultar vectores de alta dimensión. Admite una indexación, filtrado y búsqueda de similitud rápidos en millones de vectores.

Qdrant puede ejecutarse:

  • Localmente como un servicio independiente
  • En la nube para implementaciones de producción
  • O completamente en memoria para un uso rápido y temporal durante el desarrollo

En este notebook, Qdrant se inicializa usando el modo en memoria pasando ":memory:" al cliente. Esto almacena datos solo en la RAM, lo que significa que no persistirá después de que termine la sesión. Esto es adecuado para la experimentación, pero no para guardar resultados a largo plazo.

También configuras lo siguiente:

  • COLLECTION_NAME: El nombre de la colección de Qdrant para almacenar vectores de películas
  • VECTOR_SIZE: Establecido en 3072 para que coincida con la dimensionalidad de los embeddings de texto generados por Gemini
  • DISTANCE_METRIC: Establecido en distancia coseno, que es ideal para medir la similitud semántica entre vectores de embedding
from qdrant_client import QdrantClient, models
import time

COLLECTION_NAME = "tmdb_movies_sample"

VECTOR_SIZE = 3072
DISTANCE_METRIC = models.Distance.COSINE


# Initialize Qdrant client using in-memory storage
qdrant_client = QdrantClient(":memory:")

7. Define la función de embedding por lotes

Este paso define la función get_embeddings_batch, que genera embeddings de texto para lotes de datos de películas utilizando el modelo de embedding de Gemini (gemini-embedding-001), incluyendo reintentos automáticos para mayor robustez.

import time
from google.api_core import exceptions, retry

MODEL_FOR_EMBEDDING = "gemini-embedding-001" # @param ["gemini-embedding-001", "gemini-embedding-2-preview"] {"allow-input":true, isTemplate: true}

BATCH_SIZE = 25
QDRANT_BATCH_SIZE = 3072


@retry.Retry(timeout=3000)
def get_embeddings_batch(texts: list[str], task_type="RETRIEVAL_DOCUMENT") -> list[list[float]] | None:
    """
    Generates embeddings for a batch of texts using Gemini API with retry.

    Args:
        texts: A list of strings to embed.
        task_type: The task type for the embedding model.

    Returns:
        A list of embedding vectors (list of floats), or None if a non-retryable error occurs.
    """
    if not texts:
        return []
    try:
        response = client.models.embed_content(
          model=MODEL_FOR_EMBEDDING,
          contents=texts,
          config={
            "task_type":task_type,
          }
        )
        return response.embeddings
    except exceptions.RetryError as e:
        print(f"Embedding batch failed after retries: {e}")
        return None
    except Exception as e:
        print(f"An unexpected error occurred during embedding: {e}")
        return None

# Example of what an embedding looks like
sample_embedding = get_embeddings_batch(["Example movie about space and survival"])[0]
print("Example embedding vector:", sample_embedding.values[:10])
Example embedding vector: [-0.023393, 0.011092304, 0.007310852, -0.095236674, 0.012521885, 0.0069673047, -0.011544445, -0.02173588, 0.027118236, 0.004006482]

8. Crea una colección en Qdrant

Una colección en Qdrant es como una tabla en una base de datos, almacena vectores junto con metadatos opcionales (payload). Cada colección tiene su propia configuración, incluyendo el tamaño del vector y la métrica de similitud.

# In case someone tries running the whole notebook again they would want to create the collection again

try:
    qdrant_client.delete_collection(collection_name=COLLECTION_NAME)
    print(f"Existing collection '{COLLECTION_NAME}' deleted.")
except Exception as e:
    print(f"Error deleting collection (it might not exist): {e}")

try:
    qdrant_client.create_collection(
        collection_name=COLLECTION_NAME,
        vectors_config=models.VectorParams(
            size=VECTOR_SIZE,
            distance=DISTANCE_METRIC
        )
    )
    print(f"Collection '{COLLECTION_NAME}' created successfully.")
except Exception as e:
    print(f"Error creating collection: {e}")
Existing collection 'tmdb_movies_sample' deleted.
Collection 'tmdb_movies_sample' created successfully.

9. Crea payloads para el almacenamiento de metadatos

En Qdrant, además de almacenar embeddings vectoriales, puedes adjuntar información adicional llamada payload a cada vector. Estos metadatos ayudan a filtrar o recuperar resultados relevantes basados en atributos como el título, los géneros o el año de lanzamiento.

La función create_payload prepara el payload extrayendo columnas específicas de cada registro de película, manejando los valores faltantes y asegurando que los tipos de datos sean compatibles con Qdrant.

payload_columns = [
    'title', 'overview', 'genres', 'keywords', 'tagline', 'release_year'
]

def create_payload(row, payload_columns):
    payload = {}
    for col in payload_columns:
        value = row[col]
        if pd.isna(value):
            payload[col] = None
        elif isinstance(value, (np.int64, np.int32)):
            payload[col] = int(value)
        elif isinstance(value, (np.float64, np.float32)):
             payload[col] = float(value)
        else:
            payload[col] = value
    return payload

10. Embedding por lotes e indexación en Qdrant

Este paso procesa el conjunto de datos de películas muestreadas en lotes para generar embeddings vectoriales utilizando la API de Gemini y cargar (upsert) estos embeddings junto con sus payloads de metadatos en la colección de Qdrant.

Puntos clave de este proceso:

  • El conjunto de datos se divide en lotes de tamaño BATCH_SIZE para la generación de embeddings para mantenerse dentro de los límites de la API.
  • Los datos de texto de cada lote se envían a la API de embedding de Gemini con reintentos manejados en la función de embedding.
  • Para cada lote incrustado con éxito, el código prepara puntos (cada uno contiene un ID, un embedding vectorial y un payload de metadatos) para ser cargados en Qdrant.
  • Los puntos se almacenan en búfer y se cargan en fragmentos de tamaño QDRANT_BATCH_SIZE para optimizar el rendimiento.
  • El proceso incluye manejo de errores y lógica de reintento para evitar que las fallas detengan toda la operación.
  • Al final, se cargan los puntos restantes en el búfer.
  • Se imprimen estadísticas resumidas de los elementos procesados, fallidos y cargados con éxito.
from tqdm import tqdm

print(f"Starting batch embedding and indexing process for {len(df_sample)} movies...")
print(f"Using Gemini Batch Size: {BATCH_SIZE}, Qdrant Upsert Batch Size: {QDRANT_BATCH_SIZE}")

points_to_upsert_buffer = []
total_processed = 0
total_failed_embedding = 0
total_upserted = 0

num_batches = (len(df_sample) + BATCH_SIZE - 1) // BATCH_SIZE

for i in tqdm(range(0, len(df_sample), BATCH_SIZE), total=num_batches, desc="Processing Batches"):
    batch_df = df_sample.iloc[i : i + BATCH_SIZE]
    batch_texts = batch_df['text_for_embedding'].tolist()
    batch_ids = batch_df['id'].tolist()

    if not batch_texts:
        continue

    # Generate embeddings for the current batch of movie texts
    batch_embeddings = get_embeddings_batch(batch_texts, task_type="RETRIEVAL_DOCUMENT")

    # Check if embeddings were successfully generated and correspond to batch size
    if batch_embeddings and len(batch_embeddings) == len(batch_texts):
        for j in range(len(batch_ids)):
            item_id = batch_ids[j]
            item_embedding = batch_embeddings[j]
            row_data = batch_df.iloc[j]

            # Prepare metadata payload for this movie
            payload = create_payload(row_data, payload_columns)

            # Create a Qdrant PointStruct with id, embedding vector, and payload
            point = models.PointStruct(
                id=int(item_id),
                vector=item_embedding.values,
                payload=payload
            )
            points_to_upsert_buffer.append(point)

        total_processed += len(batch_ids)

    else:
        print(f"Failed to get embeddings for batch starting at index {i}. Skipping {len(batch_ids)} items.")
        total_failed_embedding += len(batch_ids)
        continue

    # Upload buffered points to Qdrant if buffer reached batch size or end of data
    if len(points_to_upsert_buffer) >= QDRANT_BATCH_SIZE or (i + BATCH_SIZE >= len(df_sample)):
        if points_to_upsert_buffer:
            try:
                qdrant_client.upsert(
                    collection_name=COLLECTION_NAME,
                    points=points_to_upsert_buffer,
                    wait=False
                )
                total_upserted += len(points_to_upsert_buffer)
                points_to_upsert_buffer = []
            except Exception as e:
                print(f"Error upserting chunk to Qdrant: {e}")
                points_to_upsert_buffer = []
                time.sleep(5)
                # Pause before retrying to avoid hammering the service after an error

# Upload any remaining points left in buffer after loop completion
if points_to_upsert_buffer:
    print(f"Upserting final remaining chunk of {len(points_to_upsert_buffer)} points.")
    try:
        qdrant_client.upsert(
            collection_name=COLLECTION_NAME,
            points=points_to_upsert_buffer,
            wait=True
        )
        total_upserted += len(points_to_upsert_buffer)
        points_to_upsert_buffer = []
    except Exception as e:
        print(f"Error upserting final chunk: {e}")

print("Batch embedding and indexing finished.")
print(f"Total items processed (attempted embedding): {total_processed}")
print(f"Total points successfully prepared for upsert: {total_upserted}")
Starting batch embedding and indexing process for 5000 movies...
Using Gemini Batch Size: 25, Qdrant Upsert Batch Size: 3072
Processing Batches: 100%|██████████| 200/200 [04:15<00:00,  1.28s/it]
Batch embedding and indexing finished.
Total items processed (attempted embedding): 5000
Total points successfully prepared for upsert: 5000
# Waiting for collection to settle
time.sleep(5)

try:
    count = qdrant_client.count(collection_name=COLLECTION_NAME, exact=True)
    print(f"\nVerification: Collection '{COLLECTION_NAME}' now contains {count.count} points.") # it should print 5000

except Exception as e:
    print(f"Error verifying collection count: {e}")
Verification: Collection 'tmdb_movies_sample' now contains 5000 points.

11. Busca y recomienda películas similares usando embeddings vectoriales

Con todos los vectores de películas indexados en Qdrant, ahora puedes realizar búsquedas semánticas. Esto te permite tomar cualquier consulta de usuario (como una frase, descripción de película o concepto), convertirla en un embedding usando el mismo modelo de Gemini y recuperar los vectores de películas más similares de la colección usando la similitud del coseno.

Esta función recommend_movies demuestra cómo:

  • Generar un embedding a partir de tu consulta de entrada usando la API de Gemini.
  • Realizar una búsqueda de similitud usando el método search() de Qdrant.
  • Recuperar las k entradas de películas más similares, incluyendo sus metadatos y puntuaciones de similitud.

Este es el paso final donde la base de datos vectorial funciona como un motor de recomendación.

def recommend_movies(query_text, top_k=5):
    """
    Finds movies similar to the query_text using the Qdrant index.

    Args:
        query_text (str): The user's query (e.g., movie title, description, theme).
        top_k (int): The maximum number of recommendations to return.

    Returns:
        list: A list of dictionaries, where each dictionary contains the
              payload (movie details) and similarity score of a recommended movie.
              Returns an empty list if query embedding fails or no results found.
    """
    print(f"Searching for recommendations based on: '{query_text}'")
    # Generate embedding for the user query using Gemini
    query_embedding = get_embeddings_batch(query_text, task_type="RETRIEVAL_QUERY")[0].values

    print(f"Query embedding: {query_embedding}")

    if query_embedding is None:
        print("Error: Could not generate embedding for the query.")
        return []

    try:
        # Perform a semantic search on Qdrant using the query vector
        search_result = qdrant_client.search(
            collection_name=COLLECTION_NAME,
            query_vector=query_embedding,
            limit=top_k,
            with_payload=True
        )

        recommendations = []
        if search_result:
            print(f"Found {len(search_result)} potential recommendations:")
            for hit in search_result:
                recommendation = {
                    "id": hit.id,
                    "score": hit.score,
                    "payload": hit.payload
                }
                recommendations.append(recommendation)
        else:
            print("No recommendations found matching the query.")

        return recommendations

    except Exception as e:
        print(f"Error during Qdrant search: {e}")
        return []

Prueba tu recomendador de películas

Ahora puedes probar tu recomendador de películas describiendo un tema, género o concepto en lenguaje natural. El sistema devolverá las películas semánticamente más similares de tu conjunto de datos basándose en la búsqueda de similitud vectorial utilizando embeddings generados por Gemini.

query = """
  I want to watch something with my girlfriends that’s
  both funny and teaches something.
"""
recommendations = recommend_movies(query, top_k=5)

if recommendations:
    print("\n--- Recommendations ---")
    for rec in recommendations:
        print(f"  - Score: {rec['score']:.4f}")
        print(f"    Title: {rec['payload'].get('title', 'N/A')}")
        print(f"    Genre: {rec['payload'].get('genres', 'N/A')}")
        print(f"    Year: {rec['payload'].get('release_year', 'N/A')}")
        print("-" * 10)
Searching for recommendations based on: '
  I want to watch something with my girlfriends that’s
  both funny and teaches something.
'
Query embedding: [-0.007658997, -0.00046528463, 0.003591214, -0.060340602, 0.005171188, -0.031223807, -0.012421608, -0.0029493966, 0.015560944, 0.011762511, 0.0043392465, -0.012576682, -0.00040339225, 0.00087798183, 0.104019016, 0.003676365, -0.004047451, -0.02373786, 0.03213893, -0.008110603, -0.010839047, -0.013205221, 0.005924564, 0.0144873345, 0.028277583, -0.0042119334, 0.030671213, 0.007607076, 0.039369747, -0.005576319, 0.029120907, 0.038622007, 0.022760479, 0.03496449, 0.020038292, 0.020724915, 0.008881883, 0.0064935135, -0.015201997, -0.008609423, -0.0074098404, -0.012157845, -0.0015358008, -0.008790521, 0.010938966, 0.015006626, 0.0006905204, -0.015275856, 0.0053447806, 0.02772103, -0.004355093, -0.022345519, 0.0011232442, -0.15648542, -0.012109607, -0.011598385, -0.036081407, -0.037203718, -0.00052338815, -0.024585776, 0.0056603705, 0.012978436, -0.02326816, -0.042064063, -0.0045854757, -0.0038374194, 0.011914898, -0.008569233, -0.00563646, -0.0057406654, 0.003564379, -0.0027617854, -0.053308308, 0.015196148, 0.024755571, -0.0012330861, -0.0017028818, -0.00686818, 0.026154077, 0.027949875, 0.010232497, 0.013906707, -0.014215794, -0.00077658653, -0.03721396, 0.0038600422, -0.009375854, 0.0014674509, -0.024641044, 0.014696107, 0.011201146, 0.005817903, 0.009098573, 0.025025342, 0.001822161, -0.016922103, -0.017847916, -0.01776402, -0.020937024, 0.0070619164, -0.0019094929, -0.011268232, 0.011396772, -0.00031001618, 0.013096436, -0.036100443, 0.022771003, -0.020070584, 0.002808597, 0.022003504, -0.020196551, -0.000636638, -0.021315569, 0.008470259, 0.009000547, -0.17078017, -0.008122156, -0.033109933, -0.033418566, 0.025020653, 0.025066907, -0.00068508927, 0.013421411, -0.0046520378, -0.0018675564, -0.026637224, 0.0016807325, 0.0023628597, -0.012904365, 0.013818259, 0.0044365413, 0.006451984, 0.019827154, 0.009803692, -0.00086508243, 0.005361341, 0.025869422, -0.022989567, -0.035207458, 0.00782946, -0.00868237, 0.004783333, 0.005028272, 0.024064386, -0.0030946343, -0.017029624, -0.028759211, 0.02417663, -0.0079752, -0.016356735, 0.0061651436, 0.020515447, 0.009211107, 0.010411367, 0.025470657, -0.04449482, -0.031313714, -0.008920078, -0.00679817, 0.008555871, -0.009875798, 0.01754391, 0.021959884, 0.03281524, 0.0326368, 0.0021419073, -0.04363063, -0.012033348, -0.010159976, 0.008532187, -0.010688875, 0.0077637048, -0.023004718, 0.0186693, -0.008716494, -0.018206827, 0.014381067, 0.0048174644, -0.019966824, -0.00861907, -0.016488982, -0.024439292, -0.010636176, -0.008473983, 0.007603981, -0.0034735212, -0.02638222, -0.02585726, -0.021062585, -0.027130863, -0.015517001, -0.011427255, -0.015218365, -0.028255204, -0.01650875, -0.013723117, -0.028768916, 0.010185366, 0.0074167554, -0.006806622, 0.0063054864, 0.023237647, -0.0016865035, -0.016455656, 0.031744212, -0.01627361, 0.019574087, 0.008197609, -0.0
… (salida recortada)
/tmp/ipykernel_26130/3969524323.py:26: DeprecationWarning: `search` method is deprecated and will be removed in the future. Use `query_points` instead.
  search_result = qdrant_client.search(

Próximos pasos

Este notebook demostró cómo construir un sistema de recomendación de películas combinando las capacidades de embedding de la API de Gemini con la búsqueda vectorial de Qdrant.

Referencias útiles de la API

Para una comprensión más detallada y para explorar funciones avanzadas, consulta la documentación oficial:

Ejemplos relacionados

Para explorar más casos de uso y obtener inspiración adicional, consulta estos notebooks relacionados en este directorio:

  • Búsqueda de similitud usando Qdrant: Un ejemplo enfocado en la construcción de sistemas de búsqueda semántica con embeddings y Qdrant.

  • Descripción general del SDK de Google GenAI: Te guía a través de la instalación y configuración del SDK, la creación de prompts de texto y multimodales, el conteo de tokens, los filtros de seguridad, el chat de múltiples turnos, la llamada a funciones, la carga de archivos, el almacenamiento en caché de contexto y más.

  • Embeddings de texto con la API de Gemini: Se centra en la generación y el trabajo con embeddings de texto utilizando la API de Gemini, ideal para construir sistemas de búsqueda y recomendación basados en vectores.

Lección del curso «Gemini API Cookbook (examples)» de Google, publicado con licencia Apache 2.0. Traducción y adaptación al español de IA con Clase. IA con Clase no está afiliado a Google. Ver el original · Licencia
Esta lección es gratuita. El resto del curso se abre con la Membresía de IA con Clase, que incluye todos los cursos del catálogo. Ver precios