Effectuer une recherche de similarité vectorielle avec Vertex AI

Bienvenue dans la recherche de similarité vectorielle de Firebase Data Connect, l'implémentation Firebase de la recherche sémantique qui s'intègre à Google Vertex AI.

Au cœur de cette fonctionnalité se trouvent les embeddings vectoriels, qui sont des tableaux de nombres à virgule flottante représentant la signification sémantique d'un texte ou d'un contenu multimédia. En exécutant une recherche des voisins les plus proches à l'aide d'un embedding vectoriel d'entrée, vous pouvez trouver tous les contenus sémantiquement similaires. Data Connect utilise l'extension pgvector de PostgreSQL pour cette fonctionnalité.

Cette puissante recherche sémantique peut être utilisée dans des cas d'utilisation tels que les moteurs de recommandation et les moteurs de recherche. Il s'agit également d'un élément clé de la génération augmentée par récupération dans les flux d'IA générative. La documentation Vertex AI est un excellent point de départ pour en savoir plus.

Vous pouvez vous appuyer sur la compatibilité intégrée de Data Connect pour générer automatiquement des embeddings vectoriels à l'aide de l'API Embeddings de Vertex AI, ou utiliser cette API pour les générer manuellement.

Prérequis

  • Configurez Data Connect pour votre projet.

  • Activez les API Vertex AI.

Prérequis

Vous pouvez choisir entre un flux de développement local (si vous êtes un développeur Web, Kotlin Android ou iOS) ou un flux IDX (pour les développeurs Web). Vous pouvez utiliser une base de données locale ou votre projet Data Connect de production et son instance Cloud SQL pour PostgreSQL pour le développement.

Ces instructions supposent que vous avez créé votre projet Data Connect en suivant le guide de démarrage rapide.

Intégrer à PostgreSQL local

  1. Configurez une instance PostgreSQL locale.
  2. Attribuez-vous le rôle IAM Utilisateur Vertex AI.
  3. Configurez les identifiants par défaut de l'application Google Cloud dans votre environnement.
  4. Installez l'extension pgvector dans votre instance PostgreSQL locale.
  5. Activez l'extension à l'aide de CREATE EXTENSION vector, conformément aux instructions du dépôt pgvector.

Intégrer IDX

  1. Configurez votre espace de travail IDX à l'aide du modèle Data Connect.
  2. Attribuez-vous le rôle IAM Utilisateur Vertex AI.
  3. Activez l'extension à l'aide de CREATE EXTENSION vector, conformément aux instructions du dépôt pgvector.

Concevoir votre schéma

Pour effectuer une recherche vectorielle, ajoutez un champ de type Vector à votre schéma. Par exemple, si vous souhaitez effectuer une recherche sémantique à l'aide de descriptions de films, ajoutez un champ pour contenir les embeddings vectoriels associés à la description du film. Dans ce schéma, descriptionEmbedding est ajouté pour stocker les embeddings vectoriels du champ description.

type Movie @table {
 id: ID! @col(name: "movie_id") @default(id: ID! @col(name: "movie_id") @default(expr: "uuidV4()")
 title: String!
 description: String
 descriptionEmbedding: Vector! @col(size:768)
 // ...
}

Générer et récupérer des embeddings

Data Connect offre une prise en charge intégrée des embeddings vectoriels avec la valeur de serveur _embed. Cela indique à Data Connect de générer des embeddings vectoriels en appelant en interne les API d'embedding de Vertex AI. La valeur du serveur _embed peut être utilisée dans les mutations et les requêtes.

Mutations

Générer et stocker un embedding avec Data Connect

Dans votre application de recherche vectorielle, vous souhaiterez probablement demander que les embeddings soient générés dès que vous ajoutez des enregistrements à votre base de données. Voici une mutation createMovie qui ajoute un enregistrement de film à la table Movie et transmet également une description de film avec un embedding spécifié model.

mutation createMovie($movieData: Movie_Data! @pick(fields: ["title", "genre", "description"])) {
  movie_insert(data: {
    ...movieData,
    descriptionEmbedding_embed: {model: "textembedding-gecko@003", text: $movieData.description}
  })
}

Dans certains cas, vous pouvez mettre à jour la description et l'intégration du film.

mutation updateDescription($id: String!, $description: String!) {
  movie_update(id: $id, data: {
    description: $description,
    descriptionEmbedding_embed: {model: "textembedding-gecko@003", text: $description}
  })
}

Pour appeler cette dernière mutation à partir d'un client :

import { updateMovieDescriptionWithEmbed } from 'lib/dataconnect-sdk/';

await updateMovieDescriptionWithEmbed({ id: movieId, description: description});

// Use the response

Requêtes

Récupérez les embeddings vectoriels à l'aide d'une requête semblable à celle-ci. Notez que le descriptionEmbedding renvoyé par la requête est un tableau de valeurs float, qui n'est généralement pas lisible par un humain. Par conséquent, les SDK générés par Data Connect ne permettent pas de le renvoyer directement.

Vous pouvez utiliser les embeddings vectoriels renvoyés pour effectuer une recherche de similarité, comme décrit dans la section suivante.

query getMovieDescription($id: String!) @auth(is: PUBLIC) {
 movie(id: $id)
   id
   description
   descriptionEmbedding
}

Effectuer une recherche de similarités

Nous pouvons maintenant effectuer une recherche par similarité.

Pour chaque champ Vector, Data Connect génère une fonction GraphQL qui implémente la recherche par similarité. Le nom de cette fonction générée est ${pluralType}_${vectorFieldName}_similarity. Il accepte quelques paramètres, comme indiqué dans les exemples suivants et dans la liste de référence.

Vous pouvez définir une fonction GraphQL qui appelle la recherche par similarité. Comme mentionné ci-dessus, la valeur de serveur _embed indique à Data Connect de générer les embeddings vectoriels à l'aide des API Embedding de Vertex AI. Dans ce cas, il s'agit de créer des embeddings pour la chaîne de recherche utilisée pour la comparaison avec les embeddings de description de film.

Dans cet exemple, la recherche de similarités renverra jusqu'à cinq films dont la description est sémantiquement la plus proche de la requête saisie. L'ensemble de résultats est trié par ordre croissant de distance (du plus proche au plus éloigné).

query searchMovieDescriptionUsingL2Similarity ($query: String!) @auth(level: PUBLIC) {
    movies_descriptionEmbedding_similarity(
      compare_embed: {model: "textembedding-gecko@003", text: $query},
      where: {content: {ne: "No info available for this movie."}}, limit: 5)
      {
        id
        title
        description
      }
  }

Ajuster la requête de similarité

Les valeurs par défaut des paramètres de recherche tels que method et within fonctionnent bien pour la plupart des cas d'utilisation. Toutefois, si vous remarquez que votre requête renvoie des résultats trop différents ou qu'il manque des résultats que vous souhaitez inclure, essayez d'ajuster ces paramètres.

Pour trouver une valeur appropriée pour within, nous pouvons ajouter _metadata.distance aux champs sélectionnés pour voir à quelle distance du vecteur de requête se trouve chaque résultat. En fonction des valeurs distance renvoyées, nous pouvons définir le paramètre within. Seuls les résultats dont la distance est inférieure à la valeur de within seront inclus :

query searchMovieDescriptionUsingL2Similarity ($query: String!) @auth(level: PUBLIC) {
    movies_descriptionEmbedding_similarity(
      compare_embed: {model: "textembedding-gecko@003", text: $query},
      within: 2,
      where: {content: {ne: "No info available for this movie."}}, limit: 5)
      {
        id
        title
        description
        _metadata {
          distance
        }
      }
  }

Vous pouvez également tester différentes fonctions de distance en définissant le paramètre method.

query searchMovieDescriptionUsingL2Similarity ($query: String!) @auth(level: PUBLIC) {
    movies_descriptionEmbedding_similarity(
      compare_embed: {model: "textembedding-gecko@003", text: $query},
      within: .5,
      method: COSINE,
      where: {content: {ne: "No info available for this movie."}}, limit: 5)
      {
        id
        title
        description
        _metadata {
          distance
        }
      }
  }

Notez que différentes méthodes renvoient des valeurs de distance très différentes. Si vous avez défini within, vous devrez à nouveau ajuster cette valeur après avoir modifié method.

Appeler la requête de similarité

Pour appeler une recherche de similarités à partir du code client :

import { searchMovieDescriptionUsingL2similarity} from 'lib/dataconnect-sdk';

const response = await searchMovieDescriptionUsingL2similarity({ query });

// Use the response

Utiliser des embeddings personnalisés

Data Connect vous permet également de travailler directement avec les embeddings en tant que Vectors au lieu d'utiliser la valeur du serveur _embed pour les générer.

Stocker un embedding personnalisé

À l'aide de l'API Vertex Embeddings, spécifiez un modèle de correspondance et demandez des résultats d'embedding de la bonne dimension.

Ensuite, convertissez le tableau de valeurs float renvoyé en Vector pour le transmettre à l'opération de mise à jour pour le stockage.

mutation updateDescription($id: String!, $description: String!, $descriptionEmbedding: Vector!) {
  movie_update(id: $id, data: {
    // title, genre...
    description: $description,
    descriptionEmbedding: $descriptionEmbedding
  })
}

Effectuer une recherche de similarité à l'aide d'embeddings personnalisés

Effectuez la même opération pour récupérer les embeddings des termes de recherche et les caster en Vectors.

Appelez ensuite la requête _similarity pour effectuer chaque recherche.

query searchMovieDescriptionUsingL2Similarity($compare: Vector!, $within: Float, $excludesContent: String, $limit: Int) @auth(level: PUBLIC) {
    movies_descriptionEmbedding_similarity(
      compare: $compare,
      method: L2,
      within: $within,
      where: {content: {ne: $excludesContent}}, limit: $limit)
      {
        id
        title
        description
      }
  }

Déployer en production

Déployer votre schéma et votre connecteur

La dernière étape d'une itération Data Connect typique consiste à déployer vos composants en production.

Lorsque vous déployez votre schéma contenant des types Vector sur Cloud SQL à l'aide de la commande firebase deploy, l'interface de ligne de commande Firebase prend les mesures nécessaires pour activer la génération d'embeddings basés sur Vertex AI sur votre instance Cloud SQL.

firebase deploy --only dataconnect

Si vous souhaitez activer manuellement la prise en charge de l'intégration dans votre instance Cloud SQL ou si vous rencontrez une erreur CLI, suivez ces instructions.

Syntaxe de la recherche vectorielle

Extensions de schéma

Le type de données Vector de Data Connect est mappé sur le type vector de PostgreSQL, tel que défini par l'extension pgvector. Le type vector de pgvector est stocké sous forme de tableau de nombres à virgule flottante de simple précision dans PostgreSQL.

Dans Data Connect, le type Vector est représenté sous la forme d'un tableau de nombres JSON. Les entrées sont forcées dans un tableau de valeurs float32. Si la conversion échoue, une erreur est générée.

Utilisez le paramètre de taille de la directive @col pour définir les dimensions du vecteur.

type Question @table {
    text: String!
    category: String!
    textEmbedding: Vector! @col(size: 768)
}

size n'est compatible qu'avec les types Vector. Les opérations Vector, telles que la recherche par similarité, nécessitent que tous les Vector aient le même nombre de dimensions.

directive @col(
  # … existing args
  """
  Defines a fixed column size for certain scalar types.

  - For Vector, size is required.
  - For all other types, size is currently unsupported and hence supplying it will result in a schema error.
  """
  size: Int
) on FIELD_DEFINITION

Valeur du serveur _embed pour les requêtes et les mutations

_embed

Cette valeur de serveur indique au service Data Connect de générer et de stocker des embeddings à l'aide des API d'embedding de Vertex AI. Cette valeur de serveur peut être utilisée à la fois pour les requêtes et les mutations.

Paramètres de la recherche par similarité

method: COSINE|INNER_PRODUCT|L2

Fonction de distance utilisée pour rechercher les voisins les plus proches. Les algorithmes actuellement compatibles sont un sous-ensemble des algorithmes de recherche pgvector.

within: float

Contrainte sur la distance dans laquelle la recherche de voisin le plus proche est effectuée.

where: FDC filter condition

Consultez le guide sur les schémas, les requêtes et les mutations.

limit: int

Nombre de résultats à renvoyer.