[go: up one dir, main page]

Accéder au contenu principal

Tutoriel Oracle AI Vector Search : stocker et interroger des embeddings avec Python

Ce tutoriel présente Oracle AI Vector Search, ses fonctionnalités clés, et guide un projet local pas à pas. Il couvre la mise en place, la recherche sémantique, l’indexation vectorielle et les points de dépannage courants.
Actualisé 22 sept. 2026  · 15 min lire

Explorer avec l’IA

ChatGPTClaudePerplexity

Dans ce tutoriel, nous allons construire une démo locale de recherche sémantique avec Oracle AI Vector Search, Python et Oracle Database Free 26ai. 

Nous commencerons avec des vecteurs tridimensionnels saisis à la main pour visualiser le calcul de distance, puis nous générerons des embeddings de texte et les interrogerons depuis Oracle Database avec SQL. 

Ce tutoriel s’adresse aux développeurs Python et SQL qui découvrent la recherche vectorielle et qui sont à l’aise avec Docker, les variables d’environnement, les packages Python et l’installation d’une base locale.

Les applications dopées à l’IA ont besoin d’une recherche qui comprend le sens, pas seulement les mots exacts. Une application de support, un portail de documentation ou un outil interne de connaissance doit pouvoir retrouver « stockage de base de données pour la recherche IA » même si le meilleur document correspondant dit « store vector embeddings in native columns ».

Vector embeddings rendent cela possible en représentant le texte sous forme de vecteurs numériques. Oracle AI Vector Search permet de stocker ces vecteurs directement dans Oracle Database, de les interroger en SQL et de conserver les embeddings à côté des données relationnelles de l’application. 

Dans ce flux de travail local, nous n’avons pas besoin d’une base de données vectorielle séparée.

Nous exécuterons Oracle Database Free 26ai en local, nous connecterons Python en mode oracledb Thin, stockerons d’abord des vecteurs manuels, puis des embeddings OpenAI, lancerons une recherche sémantique, comparerons la récupération sémantique à un prédicat d’exacte correspondance de phrase, et créerons un index vectoriel validé via USER_INDEXES.

À la fin, nous aurons un flux de travail de recherche sémantique piloté par Python qui stocke les embeddings dans Oracle Database et récupère localement les documents les plus similaires.

Qu’est-ce qu’Oracle AI Vector Search ?

Oracle AI Vector Search regroupe des fonctionnalités d’Oracle Database pour stocker, indexer et interroger des embeddings vectoriels. Un embedding est une liste de taille fixe de nombres qui représente le sens d’un texte, d’images ou d’autres données dans une forme que la base peut comparer mathématiquement.

Un modèle d’embedding génère ces vecteurs numériques. Il prend en entrée une phrase, un paragraphe, la description d’une image ou un extrait de code, et renvoie un vecteur avec un nombre fixe de dimensions.

Des entrées similaires doivent produire des vecteurs proches, tandis que des entrées sans rapport doivent être plus éloignées. Il faut donc utiliser le même modèle d’embedding pour les documents stockés et les requêtes entrantes, car les distances n’ont de sens que si les vecteurs partagent le même repère.

Dans ce tutoriel, les vecteurs manuels sont volontairement petits pour que nous puissions inspecter les calculs. Les véritables embeddings de texte sont bien plus grands, car le modèle a besoin de suffisamment de dimensions pour encoder des relations plus fines entre les mots, les expressions et les sujets.

La recherche vectorielle classe les lignes selon la distance entre vecteurs. Pour les requêtes de distance euclidienne et cosinus, plus la distance est petite, plus la correspondance est proche. Dans ce tutoriel, nous commencerons avec une petite colonne VECTOR(3, FLOAT32) afin d’observer directement le comportement des distances, puis nous passerons à des embeddings VECTOR(1536, FLOAT32) générés à partir de texte.

Flux Oracle AI Vector Search

Figure 1. Flux Oracle AI Vector Search

Oracle AI Vector Search stocke les embeddings dans une colonne VECTOR native et classe les résultats par distance vectorielle.

Oracle Database est utile lorsque les embeddings doivent vivre à côté des données relationnelles de l’application comme les identifiants de documents, titres, contenus, utilisateurs, produits, tickets ou métadonnées. 

Certaines applications utilisent une base vectorielle ou un moteur de recherche séparé pour des charges de travail spécialisées, mais pour ce flux local de recherche sémantique, Oracle Database fournit le stockage vectoriel et les fonctions de recherche nécessaires.

Fonctionnalités clés d’Oracle AI Vector Search

Nous utiliserons quatre fonctionnalités dans la démo : stockage vectoriel natif, fonctions de distance, index vectoriels et intégration relationnelle.

Type de données vectorielles natif

Oracle Database prend en charge des colonnes VECTOR natives avec dimensions explicites et formats d’éléments. Dans la démo, nous utiliserons VECTOR(3, FLOAT32) pour les vecteurs manuels et VECTOR(1536, FLOAT32) pour le modèle d’embedding par défaut text-embedding-3-small.

Fonctions et opérateurs de distance

Les exemples exécutables utilisent VECTOR_DISTANCE() car la métrique de distance reste explicite en SQL. 

Nous utiliserons la distance euclidienne pour l’exemple manuel, puis la distance cosinus pour les embeddings de texte. 

Oracle Database inclut également des opérateurs abrégés de distance vectorielle et des comportements liés au produit scalaire, mais ce tutoriel conserve du SQL exécutable avec la fonction explicite VECTOR_DISTANCE().

Différentes métriques répondent à des nuances différentes de « proximité ». 

Pour un petit tutoriel, la distance cosinus est un bon défaut pour des embeddings de texte car elle se concentre sur la direction, souvent bien corrélée à la similarité sémantique. 

La distance euclidienne est plus facile à visualiser avec des vecteurs écrits à la main, et les comparaisons de type produit scalaire sont utiles dans certains systèmes lorsque la magnitude ou la normalisation des vecteurs fait partie du design du modèle.

Métrique

Ce qu’elle compare

Bon usage

Points d’attention

Cosinus

Direction du vecteur

Embeddings de texte et similarité sémantique

Résultats dépendants du même modèle d’embedding et d’un prétraitement cohérent

Euclidienne

Distance en ligne droite

Petits exemples, intuition géométrique, certains vecteurs de caractéristiques numériques

La magnitude influe sur la distance, donc l’échelle peut compter

Produit scalaire

Direction et magnitude ensemble

Flux pensés autour de vecteurs normalisés ou d’un scoring par produit interne

Le sens du score et les règles de normalisation doivent être comprises avant toute comparaison

Index vectoriels

La recherche vectorielle exacte est utile pour la justesse et les petits jeux de données. Quand les données grossissent, les index vectoriels permettent des recherches approchées de plus proches voisins.

Oracle AI Vector Search inclut des index en mémoire sous forme de graphes de voisins, souvent associés aux recherches de type HNSW, et des index par partitions de voisins, souvent associés au partitionnement de type IVF. 

Dans cette démo locale, nous utilisons ORGANIZATION NEIGHBOR PARTITIONS et validons l’index avec USER_INDEXES.

Les index approximatifs échangent un peu de certitude d’une recherche exhaustive contre une récupération plus rapide à l’échelle. 

Le bon type d’index dépend de la charge, du volume, du rythme de mise à jour et de l’objectif de rappel.

Ce tutoriel utilise un index de type IVF par partitions de voisins pour garder l’installation locale simple et illustrer concrètement la création d’index sans transformer le tutoriel en guide d’optimisation.

Famille d’index

Principe

Forces

Compromis

Quand l’utiliser

IVF / partitions de voisins

Partitionner l’espace vectoriel, puis chercher dans les partitions probables

Modèle mental simple, pratique pour beaucoup de jeux chargés par lots

Le rappel et la vitesse dépendent du partitionnement et des réglages de recherche

Nous voulons une voie simple de recherche approchée pour un jeu de données en croissance

HNSW / graphe de voisins

Construire un graphe reliant les vecteurs proches pour une traversée rapide

Bon profil rappel/latence pour de nombreuses charges de plus proches voisins

Plus orienté mémoire, et la configuration peut compter davantage

Nous avons besoin d’une faible latence et pouvons allouer mémoire et efforts de tuning

Intégration aux requêtes relationnelles

Une colonne vectorielle peut cohabiter avec des colonnes relationnelles classiques. Notre table documents stockera le titre, le contenu et l’embedding ensemble, afin que SQL renvoie à la fois le score de similarité et le texte d’origine.

Comment démarrer avec Oracle AI Vector Search

Nous utiliserons un chemin d’installation local : Oracle Database Free 26ai dans Docker, le service FREEPDB1 et un utilisateur applicatif dédié vector_demo. 

Les concepts de recherche vectorielle sont accessibles, mais la mise en place est intermédiaire : nous utilisons Docker, un schéma de base, des packages Python, des variables d’environnement et une clé d’API d’embedding.

Prérequis

Il vous faudra :

  • Docker Desktop ou Docker Engine.
  • Accès à Oracle Container Registry.
  • Les ports locaux 1521 et 5500 disponibles.
  • Python 3.10 ou supérieur.
  • Des bases en Python et SQL.
  • Une familiarité avec Docker et les variables d’environnement.
  • Une clé API OpenAI pour les étapes d’embedding.

Cette démo nécessite une version/environnement Oracle Database incluant les fonctionnalités vectorielles utilisées ici, notamment les colonnes VECTOR, VECTOR_DISTANCE() et CREATE VECTOR INDEX. 

Le chemin local utilise Oracle Database Free 26ai avec le tag d’image ci-dessous, et le DSN utilise le service de base enfichable FREEPDB1.

Les étapes avec vecteurs manuels ne nécessitent pas de clé OpenAI. Les étapes sémantiques envoient le texte d’exemple des documents et des requêtes au fournisseur d’embedding configuré, donc nous n’utiliserons que du texte d’exemple/non sensible.

Ce tutoriel utilise oracledb, le pilote Python actuel pour Oracle Database. python-oracledb s’exécute en mode Thin par défaut, donc cette démo locale ne nécessite pas les bibliothèques Oracle Client. Le mode Thick est utile dans d’autres déploiements Oracle Database, mais nous n’appellerons pas oracledb.init_oracle_client() ici.

Démarrer Oracle Database Free 26ai en local

Créez un répertoire de projet pour les scripts que nous allons écrire.

mkdir oracle-ai-vector-search-python
 cd oracle-ai-vector-search-python

Démarrez Oracle Database Free 26ai avec l’image locale du conteneur.

docker run --name oracle-free-26ai-vector \
   --detach \
   --publish 1521:1521 \
   --publish 5500:5500 \
   --shm-size=1g \
   --env ORACLE_PWD="replace-with-a-strong-password" \
   container-registry.oracle.com/database/free:23.26.1.0-lite-amd64

Consultez les journaux du conteneur et attendez que la base indique que le démarrage est terminé.

docker logs oracle-free-26ai-vector

FREEPDB1 est le service de base enfichable utilisé par ce chemin local avec Oracle Database Free. Notre DSN Python sera localhost:1521/FREEPDB1.

Créer le schéma vector_demo dans FREEPDB1

Utilisez SYS uniquement pour l’étape de configuration du schéma. Le mot de passe dans la commande sqlplus correspond à la valeur ORACLE_PWD de la commande docker run ; le mot de passe dans CREATE USER est le mot de passe applicatif que nous exporterons en DB_PASSWORD.

Exécutez la configuration dans le conteneur de base de données.

# Create the application user and grant the privileges needed for the demo.
 docker exec -i oracle-free-26ai-vector \
   sqlplus -s 'sys/"replace-with-oracle-pwd"@FREEPDB1 as sysdba' <<'SQL'
 WHENEVER SQLERROR EXIT SQL.SQLCODE

 CREATE USER vector_demo IDENTIFIED BY "replace-with-vector-demo-password";

 GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE, UNLIMITED TABLESPACE TO vector_demo;

 EXIT
 SQL

Il s’agit d’un raccourci de développement local pour la démo. CREATE SEQUENCE est nécessaire car les tables utilisent des colonnes identité. 

En production, nous reverrions les privilèges avec un administrateur de base et appliquerions le principe du moindre privilège adapté à la charge applicative.

Installer les dépendances Python

Créez et activez un environnement virtuel.

python -m venv .venv
 source .venv/bin/activate
 python -m pip install --upgrade pip

Installez les packages Python utilisés par la démo.

python -m pip install oracledb openai

Définir les variables d’environnement

Stockez les identifiants de base et les réglages d’embedding dans des variables d’environnement plutôt que de les coder en dur dans les fichiers Python.

export DB_USER="vector_demo"
 export DB_PASSWORD="replace-with-vector-demo-password"
 export DB_DSN="localhost:1521/FREEPDB1"
 export OPENAI_API_KEY="replace-with-your-api-key"
 export EMBEDDING_MODEL="text-embedding-3-small"
 export EMBEDDING_DIM="1536"

OPENAI_API_KEY n’est requis que pour les étapes d’embedding et de recherche sémantique. EMBEDDING_DIM doit correspondre au nombre de dimensions renvoyées par le modèle choisi.

Projet démo Oracle AI Vector Search

Nous construirons la démo en huit jalons. Chaque script ajoute un concept et produit une sortie à vérifier avant d’avancer.

La partie « base uniquement » est complète après les étapes 1 à 3. À ce stade, nous nous serons connectés à Oracle Database, aurons stocké des vecteurs manuels, les aurons interrogés par distance et créé la table sémantique. La clé API OpenAI devient nécessaire à l’étape 4.

Étape 1 : se connecter à la base locale

Nous allons d’abord vérifier que Python peut se connecter à Oracle Database en tant qu’utilisateur applicatif vector_demo. Ce script lit DB_USER, DB_PASSWORD et DB_DSN à partir de l’environnement, puis affiche le mode du pilote et la version de la base.

Créer le script

Enregistrez le code suivant sous 01_check_connection.py.

import os

 import oracledb

 ADMIN_USERS = {"SYS", "SYSTEM", "PDBADMIN"}


 def required_env(name):
 	value = os.getenv(name)
 	if not value:
         raise SystemExit(f"Set {name} before running this script.")
 	return value


 def main():
 	# Load required database settings from environment variables.
 	user = required_env("DB_USER")
     password = required_env("DB_PASSWORD")
 	dsn = required_env("DB_DSN")

 	if user.upper() in ADMIN_USERS:
         raise SystemExit(
             "Use the vector_demo application user, not an admin user."
     	)

 	# Open a Thin-mode connection with the application user.
 	with oracledb.connect(user=user, password=password, dsn=dsn) as connection:
         mode = "thin" if oracledb.is_thin_mode() else "thick"
         print("Connected to Oracle Database.")
         print(f"Driver mode: {mode}")
         print(f"Database version: {connection.version}")


 if __name__ == "__main__":
 	main()

Exécuter le script

Lancez la vérification de connexion.

python 01_check_connection.py

Sortie attendue

La sortie doit confirmer que la connexion fonctionne et que le pilote est en mode Thin.

Connected to Oracle Database.
 Driver mode: thin
 Database version: 23.26.1.0.0

Nous savons maintenant que la base locale, le nom de service, les identifiants et le pilote Python fonctionnent avant de créer des tables vectorielles.

Étape 2 : se forger une intuition avec des vecteurs manuels

Avant d’introduire les modèles d’embedding, stockons trois petits vecteurs faciles à raisonner. L’appel array.array("f", values) crée un tableau de flottants 32 bits que python-oracledb peut lier à une colonne VECTOR(..., FLOAT32).

Vecteurs 3D pomme, banane et voiture montrant la distance euclidienne depuis le vecteur de requête pomme.

Figure 2. Vecteurs manuels et distance euclidienne.

L’exemple manuel utilise la distance euclidienne. Le vecteur « apple » a une distance de 0,0000 car il correspond au vecteur de requête. Le vecteur « banana » est classé second car sqrt((1.0 - 0.9)^2 + (0.0 - 0.1)^2 + (0.0 - 0.0)^2) = 0,1414.

Créer le script

Enregistrez le code suivant sous 02_manual_vectors.py.

import array
 import os

 import oracledb

 ADMIN_USERS = {"SYS", "SYSTEM", "PDBADMIN"}


 def required_env(name):
 	value = os.getenv(name)
 	if not value:
         raise SystemExit(f"Set {name} before running this script.")
 	return value


 def main():
 	user = required_env("DB_USER")
 	if user.upper() in ADMIN_USERS:
         raise SystemExit(
             "Use the vector_demo application user, not an admin user."
     	)

 	# Define three small vectors so the distance calculation is easy to inspect.
 	rows = [
         ("apple", array.array("f", [1.0, 0.0, 0.0])),
         ("banana", array.array("f", [0.9, 0.1, 0.0])),
         ("car", array.array("f", [0.0, 1.0, 0.0])),
 	]

 	with oracledb.connect(
         user=user,
         password=required_env("DB_PASSWORD"),
         dsn=required_env("DB_DSN"),
 	) as connection:
         with connection.cursor() as cursor:
             # Recreate the table so the script can be run repeatedly.
             cursor.execute(
                 """
                 BEGIN
                 	EXECUTE IMMEDIATE 'DROP TABLE manual_vectors PURGE';
                 EXCEPTION
                 	WHEN OTHERS THEN
                     	IF SQLCODE <> -942 THEN
                         	RAISE;
                     	END IF;
                 END;
                 """
             )
             cursor.execute(
                 """
                 CREATE TABLE manual_vectors (
                 	id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
                 	label VARCHAR2(100),
                 	embedding VECTOR(3, FLOAT32)
                 )
                 """
             )
             # Bind each vector as a 32-bit floating-point array.
             cursor.executemany(
                 """
                 INSERT INTO manual_vectors (label, embedding)
                 VALUES (:1, :2)
                 """,
                 rows,
             )
             connection.commit()

             # Rank rows by Euclidean distance from the query vector.
             cursor.execute(
                 """
                 SELECT label,
                    	VECTOR_DISTANCE(
                        	embedding,
                        	:query_vector,
                        	EUCLIDEAN
                    	) AS distance
                 FROM manual_vectors
                 ORDER BY distance
                 FETCH EXACT FIRST 2 ROWS ONLY
                 """,
                 query_vector=array.array("f", [1.0, 0.0, 0.0]),
             )

             print("Created manual_vectors.")
             print("Inserted 3 manual vectors.")
             print("\nNearest manual vectors:")
             for rank, (label, distance) in enumerate(cursor, start=1):
                 print(f"{rank}. {label:<6} distance={float(distance):.4f}")


 if __name__ == "__main__":
 	main()

Exécuter le script

Lancez le script de vecteurs manuels.

python 02_manual_vectors.py

Sortie attendue

Le vecteur identique apparaît en premier avec une distance de 0,0000, et « banana » en second.

Created manual_vectors.
 Inserted 3 manual vectors.

 Nearest manual vectors:
 1. apple  distance=0.0000
 2. banana distance=0.1414

C’est l’idée centrale de la recherche vectorielle : les lignes sont classées par proximité mathématique avec un vecteur de requête. Les modèles d’embedding génèrent des vecteurs plus grands, mais le comportement côté base reste le même.

Étape 3 : créer la table de documents sémantiques

Maintenant que les vecteurs fonctionnent, créons une table de type applicatif. Le point clé est que la dimension de la colonne vectorielle doit correspondre à la dimension de sortie du modèle d’embedding.

La dimension fait partie du contrat de la table. Si un modèle renvoie 1 536 nombres, la colonne doit être déclarée en VECTOR(1536, FLOAT32). 

Si nous passons ensuite à un modèle avec une longueur différente, il faudra recréer ou migrer la colonne vectorielle pour conserver la compatibilité des insertions et requêtes.

Comme le DDL ne permet pas de lier la dimension vectorielle via une variable SQL classique, le script convertit EMBEDDING_DIM en entier avant de l’utiliser dans CREATE TABLE.

Créer le script

Enregistrez le code suivant sous 03_create_documents_table.py.

import os

 import oracledb

 ADMIN_USERS = {"SYS", "SYSTEM", "PDBADMIN"}


 def required_env(name):
 	value = os.getenv(name)
 	if not value:
         raise SystemExit(f"Set {name} before running this script.")
 	return value


 def configured_dimension():
 	try:
         dimension = int(required_env("EMBEDDING_DIM"))
 	except ValueError as exc:
         raise SystemExit("EMBEDDING_DIM must be an integer.") from exc

 	if dimension <= 0:
         raise SystemExit("EMBEDDING_DIM must be a positive integer.")

 	return dimension


 def main():
 	user = required_env("DB_USER")
 	if user.upper() in ADMIN_USERS:
         raise SystemExit(
             "Use the vector_demo application user, not an admin user."
     	)

 	# Validate the configured model name and vector dimension.
     model_name = required_env("EMBEDDING_MODEL")
     dimension = configured_dimension()

 	with oracledb.connect(
         user=user,
         password=required_env("DB_PASSWORD"),
         dsn=required_env("DB_DSN"),
 	) as connection:
         with connection.cursor() as cursor:
             # Recreate the documents table so the script is repeatable.
             cursor.execute(
                 """
                 BEGIN
                 	EXECUTE IMMEDIATE 'DROP TABLE documents PURGE';
                 EXCEPTION
                 	WHEN OTHERS THEN
                     	IF SQLCODE <> -942 THEN
                         	RAISE;
                     	END IF;
                 END;
                 """
             )
             cursor.execute(
                 f"""
                 CREATE TABLE documents (
                 	id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
                 	title VARCHAR2(200) NOT NULL,
                 	content VARCHAR2(1000) NOT NULL,
                 	embedding VECTOR({dimension}, FLOAT32)
                 )
                 """
             )

     print(f"Embedding model: {model_name}")
     print(f"Embedding dimension: {dimension}")
     print("Recreated documents table.")


 if __name__ == "__main__":
 	main()

Exécuter le script

Lancez le script de création de table.

python 03_create_documents_table.py

Sortie attendue

La sortie doit afficher le modèle d’embedding configuré et la dimension vectorielle.

Embedding model: text-embedding-3-small
 Embedding dimension: 1536
 Recreated documents table.

Nous avons terminé la partie base de données. Oracle Database peut stocker et comparer des vecteurs en local ; passons maintenant des vecteurs écrits à la main aux embeddings générés à partir de texte.

Étape 4 : vérifier la dimension du modèle d’embedding

Avant d’insérer des embeddings, demandons au modèle un vecteur et vérifions sa longueur. Cela évite l’erreur la plus courante des tables vectorielles : insérer un vecteur dont la longueur ne correspond pas à la dimension de la colonne VECTOR.

Un modèle d’embedding n’est pas qu’un convertisseur texte→nombres ; il définit l’espace de sens de l’application. Tous les embeddings de documents et de requêtes de cette démo doivent venir du même modèle.

Utiliser un seul modèle garantit qu’une petite distance signifie un sens similaire, plutôt que deux vecteurs sans lien qui auraient par hasard une forme proche. Le modèle détermine aussi la longueur du vecteur, d’où la vérification préalable.

Créer le script

Enregistrez le code suivant sous 04_check_embedding_dimension.py.

import os

 from openai import OpenAI


 def required_env(name):
 	value = os.getenv(name)
 	if not value:
         raise SystemExit(f"Set {name} before running this script.")
 	return value


 def configured_dimension():
 	try:
         return int(required_env("EMBEDDING_DIM"))
 	except ValueError as exc:
         raise SystemExit("EMBEDDING_DIM must be an integer.") from exc


 def main():
     required_env("OPENAI_API_KEY")

     model_name = required_env("EMBEDDING_MODEL")
     expected_dimension = configured_dimension()

 	# Request one embedding to confirm the model output dimension.
 	client = OpenAI()
     response = client.embeddings.create(
         model=model_name,
         input="Oracle AI Vector Search dimension check",
 	)

     returned_dimension = len(response.data[0].embedding)

     print(f"Embedding model: {model_name}")
     print(f"Returned embedding length: {returned_dimension}")
     print(f"Configured EMBEDDING_DIM: {expected_dimension}")

 	# Stop before loading data if the table dimension is incompatible.
 	if returned_dimension != expected_dimension:
         raise SystemExit(
             "Dimension check failed. Update EMBEDDING_DIM, rerun "
             "03_create_documents_table.py, and try again."
     	)

     print("Dimension check passed.")


 if __name__ == "__main__":
 	main()

Exécuter le script

Exécutez la vérification de dimension.

python 04_check_embedding_dimension.py

Sortie attendue

Pour le modèle et la dimension par défaut, la sortie doit ressembler à ceci.

Embedding model: text-embedding-3-small
 Returned embedding length: 1536
 Configured EMBEDDING_DIM: 1536
 Dimension check passed.

Si nous choisissons plus tard un autre modèle d’embedding, mettez à jour EMBEDDING_MODEL, réglez EMBEDDING_DIM à la longueur renvoyée et recréez la table documents.

Étape 5 : générer et insérer les embeddings des documents

Nous allons maintenant générer des embeddings pour un petit jeu de données autonome et les insérer dans Oracle Database. 

Chaque embedding est converti en array.array("f", embedding) avant liaison pour correspondre à la colonne vectorielle en FLOAT32.

Créer le script

Enregistrez le code suivant sous 05_insert_embeddings.py.

import array
 import os

 import oracledb
 from openai import OpenAI

 DOCUMENTS = [
 	(
         "Oracle AI Vector Search",
         "Oracle Database can store vector embeddings in native VECTOR "
         "columns and query them with SQL similarity search.",
 	),
 	(
         "Semantic Search for Applications",
         "Semantic search ranks results by meaning, helping applications "
         "find related content even when exact words differ.",
 	),
 	(
         "Vector Index Workflow",
         "Vector indexes help approximate nearest-neighbor search scale "
         "when datasets grow beyond small examples.",
 	),
 	(
         "Local Docker Development",
         "Docker containers make it practical to run a local database for "
         "development and repeatable tutorials.",
 	),
 	(
         "Python Database Code",
         "Python applications can connect to Oracle Database with the "
         "oracledb driver and use environment variables for credentials.",
 	),
 	(
         "Acoustic Guitar Practice",
         "Daily guitar practice improves timing, chord transitions, and "
         "confidence when learning new songs.",
 	),
 	(
         "Weeknight Vegetable Soup",
         "A simple soup can combine onions, carrots, beans, herbs, and "
         "broth for an easy weeknight meal.",
 	),
 ]

 ADMIN_USERS = {"SYS", "SYSTEM", "PDBADMIN"}


 def required_env(name):
 	value = os.getenv(name)
 	if not value:
         raise SystemExit(f"Set {name} before running this script.")
 	return value


 def configured_dimension():
 	try:
         return int(required_env("EMBEDDING_DIM"))
 	except ValueError as exc:
         raise SystemExit("EMBEDDING_DIM must be an integer.") from exc


 def main():
 	user = required_env("DB_USER")
 	if user.upper() in ADMIN_USERS:
         raise SystemExit(
             "Use the vector_demo application user, not an admin user."
     	)

     required_env("OPENAI_API_KEY")
     model_name = required_env("EMBEDDING_MODEL")
     expected_dimension = configured_dimension()

 	# Generate embeddings for all sample documents in one API request.
 	client = OpenAI()
     response = client.embeddings.create(
         model=model_name,
         input=[content for _, content in DOCUMENTS],
 	)

 	# Restore the response order using each item's index.
     embeddings = [
         item.embedding
     	for item in sorted(
             response.data,
             key=lambda item: item.index,
     	)
 	]

 	if any(len(embedding) != expected_dimension for embedding in embeddings):
         raise SystemExit(
             "An embedding length did not match EMBEDDING_DIM. "
             "Rerun 04_check_embedding_dimension.py."
     	)

 	# Bind each embedding as a 32-bit floating-point array.
 	rows = [
         (title, content, array.array("f", embedding))
     	for (title, content), embedding in zip(DOCUMENTS, embeddings)
 	]

 	# Replace the sample rows and commit the transaction.
 	with oracledb.connect(
         user=user,
         password=required_env("DB_PASSWORD"),
         dsn=required_env("DB_DSN"),
 	) as connection:
         with connection.cursor() as cursor:
             cursor.execute("DELETE FROM documents")
             cursor.executemany(
                 """
                 INSERT INTO documents (title, content, embedding)
                 VALUES (:1, :2, :3)
                 """,
                 rows,
             )
             cursor.execute("SELECT COUNT(*) FROM documents")
             row_count = cursor.fetchone()[0]
             connection.commit()

     print(f"Embedding model: {model_name}")
     print(f"Generated embeddings for {len(DOCUMENTS)} documents.")
     print(f"Inserted {len(DOCUMENTS)} documents.")
     print(f"documents row count: {row_count}")


 if __name__ == "__main__":
 	main()

Exécuter le script

Lancez le script d’insertion.

python 05_insert_embeddings.py

Sortie attendue

Le nombre de lignes doit correspondre aux sept documents intégrés.

Embedding model: text-embedding-3-small
 Generated embeddings for 7 documents.
 Inserted 7 documents.
 documents row count: 7

Nous avons maintenant le texte et les embeddings stockés ensemble dans Oracle Database. Les documents sans rapport sur la guitare et la soupe sont inclus, afin que les résultats présentent des correspondances proches et éloignées évidentes.

Étape 6 : exécuter une requête de recherche sémantique

Ensuite, nous allons embedder une requête en langage naturel et la comparer aux embeddings stockés. Ce script utilise la distance cosinus, couramment utilisée pour les embeddings de texte car elle privilégie la direction du vecteur plutôt que sa magnitude brute.

Pour la recherche sémantique, la requête passe par le même modèle d’embedding que les documents. Nous ne demandons pas à SQL de « comprendre » la phrase anglaise directement. 

Nous demandons au modèle de convertir la phrase en vecteur, puis à Oracle Database de classer les vecteurs stockés par leur distance à ce vecteur de requête.

Créer le script

Enregistrez le code suivant sous 06_semantic_search.py.

import array
 import os

 import oracledb
 from openai import OpenAI

 QUERY_TEXT = "Where should an AI app keep meaning-based search data?"
 ADMIN_USERS = {"SYS", "SYSTEM", "PDBADMIN"}


 def required_env(name):
 	value = os.getenv(name)
 	if not value:
         raise SystemExit(f"Set {name} before running this script.")
 	return value


 def main():
 	user = required_env("DB_USER")
 	if user.upper() in ADMIN_USERS:
         raise SystemExit(
             "Use the vector_demo application user, not an admin user."
     	)

     required_env("OPENAI_API_KEY")

 	# Generate an embedding for the natural-language query.
 	client = OpenAI()
     response = client.embeddings.create(
         model=required_env("EMBEDDING_MODEL"),
         input=QUERY_TEXT,
 	)
     query_vector = array.array("f", response.data[0].embedding)

 	with oracledb.connect(
         user=user,
         password=required_env("DB_PASSWORD"),
         dsn=required_env("DB_DSN"),
 	) as connection:
         with connection.cursor() as cursor:
             # Rank stored documents by cosine distance from the query vector.
             cursor.execute(
                 """
                 SELECT title,
                    	content,
                    	VECTOR_DISTANCE(
                        	embedding,
                        	:query_vector,
                        	COSINE
                    	) AS distance
                 FROM documents
                 ORDER BY distance
                 FETCH EXACT FIRST 5 ROWS ONLY
                 """,
                 query_vector=query_vector,
             )
             rows = cursor.fetchall()

 	if not rows:
         raise SystemExit("No documents found. Run 05_insert_embeddings.py first.")

     print(f"Query: {QUERY_TEXT}")
     print("\nTop 5 results:")

 	for rank, (title, content, distance) in enumerate(rows, start=1):
         print(f"{rank}. {title}")
         print(f"   distance: {float(distance):.6f}")
         print(f"   {content[:90]}...")


 if __name__ == "__main__":
 	main()

Exécuter le script

Lancez le script de recherche sémantique.

python 06_semantic_search.py

Sortie attendue

Les classements et distances exacts peuvent varier si le modèle d’embedding change. Cependant, les documents sur Oracle AI Vector Search, la recherche sémantique, le code Python et l’indexation vectorielle devraient figurer en tête.

Query: Where should an AI app keep meaning-based search data?

 Top 5 results:
 1. Oracle AI Vector Search
    distance: <cosine distance from your run>
    Oracle Database can store vector embeddings in native VECTOR columns and query them with SQL...

 2. Semantic Search for Applications
    distance: <cosine distance from your run>
    Semantic search ranks results by meaning, helping applications find related content even when...

 3. Python Database Code
    distance: <cosine distance from your run>
    Python applications can connect to Oracle Database with the oracledb driver and use environment...

 4. Vector Index Workflow
    distance: <cosine distance from your run>
    Vector indexes help approximate nearest-neighbor search scale when datasets grow beyond small...

 5. Local Docker Development
    distance: <cosine distance from your run>
    Docker containers make it practical to run a local database for development and repeatable...

Pour les requêtes de distance euclidienne et cosinus de ce tutoriel, des valeurs plus faibles indiquent des correspondances plus proches. 

Le produit scalaire est une autre approche utile dans de nombreux flux de recherche vectorielle, notamment quand la magnitude ou la normalisation importent, mais le sens du score et la normalisation doivent être traités avec soin. 

Nous garderons du code exécutable avec euclidienne et cosinus pour faciliter l’interprétation.

Si nous normalisons nous‑mêmes les embeddings dans une future application, il faudra normaliser de la même façon les embeddings stockés et ceux des requêtes ; sinon, distances et classements peuvent changer.

Étape 7 : comparer la recherche sémantique à une correspondance de phrase

La recherche sémantique est utile car elle ne requiert pas la présence exacte de la même expression dans le document.

Comparons la requête sémantique à un simple prédicat d’expression exacte.

Ce n’est pas une démo de moteur de recherche plein texte ; c’est une petite comparaison SQL qui met en évidence la différence entre appariement littéral et classement fondé sur le sens.

Créer le script

Enregistrez le code suivant sous 07_semantic_vs_keyword.py.

import array
 import os

 import oracledb
 from openai import OpenAI

 SEMANTIC_QUERY = "Where should an AI app keep meaning-based search data?"
 KEYWORD_PHRASE = "meaning-based search data"
 ADMIN_USERS = {"SYS", "SYSTEM", "PDBADMIN"}


 def required_env(name):
 	value = os.getenv(name)
 	if not value:
         raise SystemExit(f"Set {name} before running this script.")
 	return value


 def main():
 	user = required_env("DB_USER")
 	if user.upper() in ADMIN_USERS:
         raise SystemExit(
             "Use the vector_demo application user, not an admin user."
     	)

     required_env("OPENAI_API_KEY")

 	# Generate an embedding for the semantic query.
 	client = OpenAI()
     response = client.embeddings.create(
         model=required_env("EMBEDDING_MODEL"),
         input=SEMANTIC_QUERY,
 	)
     query_vector = array.array("f", response.data[0].embedding)

 	with oracledb.connect(
         user=user,
         password=required_env("DB_PASSWORD"),
         dsn=required_env("DB_DSN"),
 	) as connection:
         with connection.cursor() as cursor:
             # Rank documents by semantic similarity.
             cursor.execute(
                 """
                 SELECT title,
                        VECTOR_DISTANCE(embedding, :query_vector, COSINE)
                 FROM documents
                 ORDER BY VECTOR_DISTANCE(embedding, :query_vector, COSINE)
                 FETCH EXACT FIRST 3 ROWS ONLY
                 """,
                 query_vector=query_vector,
             )
             semantic_rows = cursor.fetchall()

             # Compare semantic results with an exact phrase predicate.
             cursor.execute(
                 """
                 SELECT title
                 FROM documents
                 WHERE LOWER(title || ' ' || content)
                   	LIKE '%' || :phrase || '%'
                 ORDER BY title
                 FETCH FIRST 3 ROWS ONLY
                 """,
                 phrase=KEYWORD_PHRASE.lower(),
             )
             keyword_rows = cursor.fetchall()

     print(f"Semantic query: {SEMANTIC_QUERY}")
     print(f"Keyword phrase: {KEYWORD_PHRASE}")

     print("\nSemantic results:")
 	for rank, (title, distance) in enumerate(semantic_rows, start=1):
         print(f"{rank}. {title}")
         print(f"   distance: {float(distance):.6f}")

     print("\nKeyword results:")
 	if keyword_rows:
     	for rank, (title,) in enumerate(keyword_rows, start=1):
             print(f"{rank}. {title}")
 	else:
         print("0 exact phrase matches")


 if __name__ == "__main__":
 	main()

Exécuter le script

Lancez le script de comparaison.

python 07_semantic_vs_keyword.py

Sortie attendue

La recherche sémantique doit retourner des documents pertinents même si l’expression exacte n’est pas présente.

Semantic query: Where should an AI app keep meaning-based search data?
 Keyword phrase: meaning-based search data

 Semantic results:
 1. Oracle AI Vector Search
    distance: <cosine distance from your run>
 2. Semantic Search for Applications
    distance: <cosine distance from your run>
 3. Python Database Code
    distance: <cosine distance from your run>

 Keyword results:
 0 exact phrase matches

Les prédicats SQL traditionnels restent essentiels pour les filtres, jointures, droits et correspondances exactes. La recherche vectorielle ajoute un signal de classement fondé sur le sens, que l’on peut combiner aux données relationnelles quand l’application a besoin de récupération sémantique.

Étape 8 : ajouter et valider un index vectoriel

Enfin, nous allons créer un index vectoriel et valider qu’Oracle Database le signale comme un index valide détenu par le schéma de démo. 

Cette étape illustre la création d’index ; notre jeu de données minuscule n’est pas suffisant pour tirer des conclusions de performance.

Sans index, Oracle Database peut comparer exactement le vecteur de requête à chaque vecteur stocké. Cette approche convient aux très petits jeux et pour l’apprentissage.

Avec de grands jeux, les index vectoriels approchés réduisent l’espace de recherche pour des réponses rapides tout en trouvant des voisins proches. 

Choisissez la famille d’index après avoir considéré taille de données, objectifs de latence, exigences de rappel, budget mémoire et fréquence de mise à jour des vecteurs.

La requête finale confirme que la recherche sémantique renvoie toujours des résultats après la création de l’index. 

Nous n’utiliserons pas EXPLAIN PLAN ni DBMS_XPLAN ici : le tutoriel valide la création via les métadonnées de schéma, pas via le plan d’exécution.

Créer le script

Enregistrez le code suivant sous 08_create_vector_index.py.

import array
 import os

 import oracledb
 from openai import OpenAI

 INDEX_NAME = "DOCUMENT_EMBEDDING_IDX"
 QUERY_TEXT = "Where should an AI app keep meaning-based search data?"
 ADMIN_USERS = {"SYS", "SYSTEM", "PDBADMIN"}


 def required_env(name):
 	value = os.getenv(name)
 	if not value:
         raise SystemExit(f"Set {name} before running this script.")
 	return value


 def main():
 	user = required_env("DB_USER")
 	if user.upper() in ADMIN_USERS:
         raise SystemExit(
             "Use the vector_demo application user, not an admin user."
     	)

     required_env("OPENAI_API_KEY")

 	# Generate the query vector used for the validation search.
 	client = OpenAI()
     response = client.embeddings.create(
         model=required_env("EMBEDDING_MODEL"),
         input=QUERY_TEXT,
 	)
     query_vector = array.array("f", response.data[0].embedding)

 	with oracledb.connect(
         user=user,
         password=required_env("DB_PASSWORD"),
         dsn=required_env("DB_DSN"),
 	) as connection:
         with connection.cursor() as cursor:
             # Remove a prior index so the script can be rerun safely.
             try:
                 cursor.execute(f"DROP INDEX {INDEX_NAME}")
             except oracledb.DatabaseError as exc:
                 error = exc.args[0]
                 if error.code != 1418:
                 	raise

             # Create an approximate neighbor-partitions vector index.
             cursor.execute(
                 """
                 CREATE VECTOR INDEX document_embedding_idx
                 ON documents (embedding)
                 ORGANIZATION NEIGHBOR PARTITIONS
                 DISTANCE COSINE
                 WITH TARGET ACCURACY 95
                 """
             )

             # Confirm that Oracle reports the vector index as valid.
             cursor.execute(
                 """
                 SELECT index_name, index_type, status
                 FROM user_indexes
                 WHERE index_name = :index_name
                 """,
                 index_name=INDEX_NAME,
             )
             index_row = cursor.fetchone()

             # Confirm that semantic search still returns rows after index creation.
             cursor.execute(
                 """
                 SELECT title
                 FROM documents
                 ORDER BY VECTOR_DISTANCE(embedding, :query_vector, COSINE)
                 FETCH EXACT FIRST 3 ROWS ONLY
                 """,
                 query_vector=query_vector,
             )
             rows = cursor.fetchall()

 	if not index_row:
         raise SystemExit("Vector index was not found in USER_INDEXES.")

     print("Created vector index DOCUMENT_EMBEDDING_IDX.")
     print("\nIndex validation:")
     print(f"Index name: {index_row[0]")}
     print(f"Index type: {index_row[1]")}
     print(f"Status: {index_row[2]")}
     result_count = len(rows)

 	print(
         "\nSemantic search still returns "
         f"{result_count} results after index creation."
 	)


 if __name__ == "__main__":
 	main()

Exécuter le script

Lancez le script d’indexation.

python 08_create_vector_index.py

Sortie attendue

La sortie doit inclure une ligne pour DOCUMENT_EMBEDDING_IDX, avec INDEX_TYPE à VECTOR et STATUS à VALID.

Created vector index DOCUMENT_EMBEDDING_IDX.

 Index validation:
 Index name: DOCUMENT_EMBEDDING_IDX
 Index type: VECTOR
 Status: VALID

 Semantic search still returns 3 results after index creation.

USER_INDEXES confirme que l’index existe et est valide. Cela ne prouve pas qu’une requête particulière l’a utilisé, et ce tutoriel n’emploie pas la sortie de plan d’exécution comme méthode de validation. 

En production, nous testerions avec un volume réaliste, des patrons de requêtes réels et des objectifs de performance.

Astuces et dépannage pour Oracle AI Vector Search

Le conteneur de base est encore en démarrage

Attendez la fin du démarrage, puis relancez docker logs oracle-free-26ai-vector. La base doit être prête avant que Python puisse se connecter à localhost:1521/FREEPDB1.

Le port 1521 est déjà utilisé

Arrêtez le service local en conflit ou modifiez le mapping de ports Docker. Mettez à jour DB_DSN si le port hôte change.

La connexion à la base échoue

Vérifiez DB_USER, DB_PASSWORD et DB_DSN. Utilisez FREEPDB1 dans le DSN, et connectez les scripts applicatifs en tant que vector_demo, pas SYS, SYSTEM ou PDBADMIN.

La clé API d’embedding est absente

Les étapes 1–3 concernent uniquement la base. Les étapes 4–8 nécessitent OPENAI_API_KEY et peuvent générer des coûts API.

Une dimension vectorielle ne correspond pas

Exécutez 04_check_embedding_dimension.py, mettez à jour EMBEDDING_DIM, relancez 03_create_documents_table.py et rechargez les embeddings avec 05_insert_embeddings.py.

Échec d’un bind de vecteur

Assurez-vous que chaque bind de vecteur en FLOAT32 utilise array.array("f", values). Le tutoriel applique ce format pour les vecteurs manuels, les embeddings stockés et les vecteurs de requête.

Les classements sémantiques diffèrent de l’exemple

C’est attendu. Les fournisseurs d’embedding peuvent mettre à jour les modèles, et les distances flottantes peuvent varier entre exécutions.

La création de l’index vectoriel échoue

Confirmez que la table documents existe, qu’elle appartient au schéma vector_demo et que DOCUMENT_EMBEDDING_IDX n’est pas laissé par une exécution partielle.

Se préparer à la production

Utilisez oracledb.create_pool() pour le pooling de connexions, mesurez sur des volumes réalistes, révisez sécurité et gestion des secrets, et ajustez les index selon des patrons de requêtes réels.

Arrêter et supprimer le conteneur

Une fois le tutoriel terminé, arrêtez et supprimez le conteneur local.

docker stop oracle-free-26ai-vector
docker rm oracle-free-26ai-vector

Conclusion

Nous avons construit un flux local de recherche sémantique avec Oracle AI Vector Search, Oracle Database Free 26ai et Python. 

Nous avons commencé avec des valeurs VECTOR(3, FLOAT32) écrites à la main, inséré des embeddings issus d’un modèle via array.array("f", values), interrogé des documents similaires avec VECTOR_DISTANCE(), comparé la récupération sémantique à un prédicat d’expression exacte, et validé un index vectoriel via USER_INDEXES.

Cette approche convient bien lorsque les embeddings doivent rester à côté des données relationnelles et que l’on souhaite un stockage et une recherche visibles en SQL sans ajouter de base vectorielle séparée pour le flux local. 

La démo est volontairement petite : elle enseigne les briques fondamentales, pas l’échelle de production. Pour des applications plus grandes, mesurez avec des données réalistes, révisez sécurité et privilèges, utilisez le pooling de connexions et validez les choix d’indexation sur des requêtes réelles.

Pour aller plus loin :

FAQs

Qu’est-ce qu’Oracle AI Vector Search ?

Oracle AI Vector Search regroupe des fonctionnalités d’Oracle Database pour stocker, indexer et interroger des embeddings vectoriels aux côtés de données relationnelles.

Ai-je besoin d’une clé API OpenAI pour suivre le tutoriel ?

Les étapes de vecteurs manuels et de configuration de la base ne requièrent pas de clé API. Les étapes 4 à 8 nécessitent un fournisseur d’embedding et la variable OPENAI_API_KEY configurée.

Pourquoi EMBEDDING_DIM doit-il correspondre au modèle d’embedding ?

La dimension de la colonne VECTOR doit être égale au nombre de valeurs renvoyées par le modèle d’embedding. Un écart entraîne un échec à l’insertion.

Quelles métriques de distance le tutoriel utilise-t-il ?

L’exemple avec vecteurs manuels utilise la distance euclidienne. Les exemples de recherche sémantique utilisent la distance cosinus.

Un index vectoriel valide prouve‑t‑il qu’une requête l’a utilisé ?

Non. USER_INDEXES confirme que l’index existe et qu’il est valide, mais cela ne prouve pas qu’une requête donnée l’a utilisé.


Mark Nelson's photo
Author
Mark Nelson
LinkedIn

Mark Nelson est architecte et Developer Evangelist chez Oracle.  Il travaille à la convergence de l'IA, des microservices et des technologies de bases de données.  Blogueur actif, auteur publié, relecteur technique chez Manning Publications, Section Leader pour Stanford Code in Place et mentor chez DeepLearning.ai.  Il intervient régulièrement auprès des Java User Groups et Oracle User Groups, dans des meetups IA et lors de grandes conférences.  Passionné par l'apprentissage et la transmission, il cumule plus de trente ans d'expérience dans l'industrie, chez IBM et Oracle.

Sujets
Intelligence artificielle
Python

Top DataCamp Courses

Cours

Introduction à Oracle SQL

4 h
18.4K
Renforcez vos compétences avec Oracle SQL, notamment les bases du SQL, l'agrégation, la combinaison et la personnalisation des données.
Afficher les détailsRight Arrow
Commencer Le Cours
Voir plusRight Arrow
Contenus associés
cursor ai code editor

Tutoriel

Cursor AI : Un guide avec 10 exemples pratiques

Apprenez à installer Cursor AI sur Windows, macOS et Linux, et découvrez comment l'utiliser à travers 10 cas d'utilisation différents.

Tutoriel

Tutoriel Python sur les structures de données

Initiez-vous aux structures de données de Python : apprenez-en plus sur les types de données et les structures de données primitives et non primitives, telles que les chaînes de caractères, les listes, les piles, etc.
Sejal Jaiswal's photo

Sejal Jaiswal

24 min

Tutoriel

Tableaux Python

Tableaux Python avec exemples de code. Découvrez comment créer et imprimer des tableaux à l'aide de Python NumPy dès aujourd'hui.
DataCamp Team's photo

DataCamp Team

3 min

Tutoriel

Tutoriel sur les boucles Python

Tutoriel complet d'introduction aux boucles Python. Apprenez et pratiquez les boucles while et for, les boucles imbriquées, les mots-clés break et continue, la fonction range et bien plus encore.
Satyabrata Pal's photo

Satyabrata Pal

15 min

Tutoriel

Tutoriel sur les boucles « for » en Python

Apprenez à implémenter des boucles « for » en Python pour itérer une séquence ou les lignes et colonnes d'un DataFrame pandas.
Aditya Sharma's photo

Aditya Sharma

5 min

Tutoriel

Méthode index() de Python expliquée à l'aide d'exemples

Découvrez comment utiliser la fonction index() de Python pour trouver la position d'éléments dans des listes.
Sejal Jaiswal's photo

Sejal Jaiswal

6 min

Voir PlusVoir Plus