Ga naar hoofdinhoud

Oracle AI Vector Search-tutorial: Embeddings opslaan en bevragen met Python

Deze tutorial introduceert Oracle AI Vector Search, legt de kernfuncties uit en doorloopt een praktische lokale setup. Ook komen set-upeisen, semantisch zoeken, vectorindexering en veelvoorkomende troubleshooting aan bod.
· 15 min lezen

Verken met AI

In deze tutorial bouwen we een lokale demo voor semantisch zoeken met Oracle AI Vector Search, Python en Oracle Database Free 26ai. 

We beginnen met handgeschreven driedimensionale vectoren zodat de afstandsberekening zichtbaar is, genereren daarna tekstembeddings en bevragen die met SQL vanuit Oracle Database. 

Deze tutorial is voor Python- en SQL-ontwikkelaars die nieuw zijn met vector search en vertrouwd zijn met Docker, omgevingsvariabelen, Python-packages en een lokale databaseset-up.

AI-gedreven applicaties hebben search nodig die betekenis begrijpt, niet alleen exacte woorden. Een support-app, documentatieportaal of interne kennistool moet “databaseopslag voor AI-zoekopdrachten” kunnen vinden, ook als het best passende document zegt: “vector-embeddings opslaan in native kolommen.”

Vector-embeddings maken dat mogelijk door tekst als numerieke vectoren te representeren. Met Oracle AI Vector Search kunnen we die vectoren rechtstreeks in Oracle Database opslaan, met SQL bevragen en embeddings naast relationele applicatiedata bewaren. 

Voor deze lokale workflow hebben we geen aparte vectordatabank nodig.

We draaien Oracle Database Free 26ai lokaal, verbinden vanuit Python met oracledb Thin-modus, slaan eerst handmatige vectoren op, vervolgens OpenAI-embeddings, voeren semantisch zoeken uit, vergelijken semantische retrieval met een eenvoudige exacte frase-predicate en maken een vectorindex die we valideren via USER_INDEXES.

Aan het eind hebben we een werkende Python-gestuurde semantische zoekworkflow die embeddings opslaat in Oracle Database en de meest vergelijkbare documents lokaal ophaalt.

Wat is Oracle AI Vector Search?

Oracle AI Vector Search is een set mogelijkheden in Oracle Database voor het opslaan, indexeren en bevragen van vector-embeddings. Een vector-embedding is een lijst met vaste lengte met getallen die de betekenis van tekst, afbeeldingen of andere data representeert in een vorm die een database wiskundig kan vergelijken.

Een embeddingmodel maakt de numerieke vectoren. Het neemt invoer zoals een zin, alinea, afbeeldingsbeschrijving of codefragment en geeft een vector met een vast aantal dimensies terug.

Vergelijkbare input zou vectoren moeten opleveren die dicht bij elkaar liggen, terwijl niet-gerelateerde input verder uit elkaar valt. We moeten daarom hetzelfde embeddingmodel gebruiken voor opgeslagen documents en inkomende queries, omdat afstanden alleen betekenis hebben als vectoren hetzelfde coördinatenstelsel delen.

In deze tutorial zijn de handmatige vectoren bewust klein zodat we de wiskunde kunnen inspecteren. De echte tekstembeddings zijn veel groter, omdat het model genoeg dimensies nodig heeft om subtielere relaties tussen woorden, frasen en onderwerpen te coderen.

Vector search rangschikt rijen op basis van de afstand tussen vectoren. Bij Euclidische en cosinusafstandsqueries betekent een kleinere afstand een dichterbijzijnde match. In deze tutorial beginnen we met een kleine VECTOR(3, FLOAT32)-kolom zodat we het afstandsgedrag direct kunnen zien, en gaan daarna naar VECTOR(1536, FLOAT32)-embeddings die uit tekst worden gegenereerd.

Oracle AI Vector Search flow

Figuur 1. Oracle AI Vector Search-flow

Oracle AI Vector Search slaat embeddings op in een native VECTOR-kolom en rangschikt resultaten op vectorafstand.

Oracle Database is handig wanneer embeddings naast relationele applicatiedata horen, zoals document-ID’s, titels, content, gebruikers, producten, tickets of metadata. 

Sommige applicaties gebruiken een aparte vectordatabank of zoekmachine voor gespecialiseerde retrieval-workloads, maar voor de lokale semantische zoekworkflow in deze tutorial levert Oracle Database de vectoropslag en zoekfuncties die we nodig hebben.

Belangrijkste functies van Oracle AI Vector Search

We gebruiken vier Oracle AI Vector Search-functies in de demo: native vectoropslag, afstandsfuncties, vectorindexen en relationele integratie.

Native vectordatatype

Oracle Database ondersteunt native VECTOR-kolommen met expliciete dimensies en elementformaten. In de demo gebruiken we VECTOR(3, FLOAT32) voor handmatige vectoren en VECTOR(1536, FLOAT32) voor het standaard-embeddingmodel text-embedding-3-small.

Afstandfuncties en -operatoren

De uitvoerbare voorbeelden gebruiken VECTOR_DISTANCE() omdat daarmee de afstandsmaat expliciet in SQL blijft. 

We gebruiken Euclidische afstand voor het voorbeeld met handmatige vectoren en cosinusafstand voor tekstembeddings. 

Oracle Database bevat ook verkorte vectorafstand-operatoren en dotproduct-gerelateerd afstandsgedrag, maar deze tutorial houdt de uitvoerbare SQL op het expliciete pad met VECTOR_DISTANCE().

Verschillende afstandsmaatstaven beantwoorden net andere vragen over “nabijheid.” 

Voor een kleine tutorial is cosinusafstand een goede standaard voor tekstembeddings, omdat deze vooral op richting focust, wat vaak goed overeenkomt met semantische gelijkenis. 

Euclidische afstand is makkelijker te visualiseren met handgeschreven vectoren, en vergelijkingen in de stijl van dotproduct zijn nuttig in sommige retrievalsystemen wanneer vectorgrootte of genormaliseerde vectoren deel uitmaken van het modelontwerp.

Maatstaf

Wat wordt vergeleken

Goede toepassing

Let op

Cosinus

Vectorrichting

Tekstembeddings en semantische gelijkenis

Resultaten hangen af van hetzelfde embeddingmodel en consistente preprocessing

Euclidisch

Rechte-lijnafstand

Kleine voorbeelden, geometrische intuïtie, sommige numerieke featurevectoren

Grootte beïnvloedt de afstand, dus schaling kan uitmaken

Dotproduct

Richting en grootte samen

Workflows rond genormaliseerde vectoren of inner-product scoring

Score-richting en normalisatieregels moeten duidelijk zijn voor je resultaten vergelijkt

Vectorindexen

Exacte vector search is nuttig voor juistheid en kleine datasets. Naarmate datasets groeien, ondersteunen vectorindexen approximate nearest-neighbor-workflows.

Oracle AI Vector Search bevat in-memory neighbor graph-indexen, vaak geassocieerd met HNSW-zoekopdrachten, en neighbor-partitions-indexen, vaak geassocieerd met IVF-partitionering. 

In deze lokale demo gebruiken we ORGANIZATION NEIGHBOR PARTITIONS en valideren de index met USER_INDEXES.

Approximate indexen ruilen een beetje uitputtende-zoek-zekerheid in voor snellere retrieval op schaal. 

De juiste indexfamilie hangt af van de workload, datavolume, updatepatroon en recall-doel.

Deze tutorial gebruikt een IVF-achtige neighbor-partitions-index omdat dit de lokale set-up eenvoudig houdt en een concreet index-creatiestap biedt zonder er een index-tuninggids van te maken.

Indexfamilie

Basisidee

Sterke punten

Afwegingen

Wanneer je ’m zou gebruiken

IVF / neighbor partitions

Partitioneer de vectorruimte en doorzoek dan waarschijnlijke partities

Eenvoudig denkkader, praktisch voor veel batch-geladen datasets

Recall en snelheid hangen af van partitionering en zoekinstellingen

We willen een rechtlijnig approximate-zoekpad voor een groeiende dataset

HNSW / neighbor graph

Bouw een graaf die nabijgelegen vectoren koppelt voor snelle traversals

Sterk recall- en latentiebereik voor veel nearest-neighbor-workloads

Meer geheugen-georiënteerd en configuratie kan zwaarder wegen

We hebben lage latency nodig en kunnen geheugen en tuningbudget inzetten

Integratie met relationele queries

Een vectorkolom kan naast gewone relationele kolommen bestaan. Onze documents-tabel slaat titel, content en embedding samen op, zodat SQL zowel de gelijkenisscore als de originele documenttekst kan retourneren.

Aan de slag met Oracle AI Vector Search

We gebruiken één lokaal set-up-pad: Oracle Database Free 26ai in Docker, de FREEPDB1-service en een aparte applicatiegebruiker vector_demo. 

De vector-searchconcepten zijn goed te overzien, maar de set-up is intermediate omdat we Docker, een databaseschema, Python-packages, omgevingsvariabelen en een embedding-API-sleutel gebruiken.

Vereisten

We hebben nodig:

  • Docker Desktop of Docker Engine.
  • Toegang tot Oracle Container Registry.
  • Lokale poorten 1521 en 5500 beschikbaar.
  • Python 3.10 of later.
  • Basiskennis Python en SQL.
  • Basiskennis Docker en omgevingsvariabelen.
  • Een OpenAI API-sleutel voor de embeddingstappen.

Deze demo vereist een Oracle Database-versie of -omgeving die de hier gebruikte vectorfuncties bevat, inclusief native VECTOR-kolommen, VECTOR_DISTANCE() en CREATE VECTOR INDEX. 

Het lokale pad gebruikt Oracle Database Free 26ai met de onderstaande image-tag, en de DSN gebruikt de FREEPDB1 pluggable database-service.

De stappen met handmatige vectoren vereisen geen OpenAI API-sleutel. De semantische stappen sturen de voorbeelddocumenttekst en querytekst naar de geconfigureerde embeddingprovider, dus we gebruiken alleen voorbeeld- of niet-gevoelige tekst.

Deze tutorial gebruikt oracledb, de huidige Python-driver voor Oracle Database. python-oracledb draait standaard in Thin-modus, dus voor deze lokale demo zijn geen Oracle Client-libraries nodig. Thick-modus is nuttig in andere Oracle Database-implementaties, maar we roepen hier oracledb.init_oracle_client() niet aan.

Oracle Database Free 26ai lokaal starten

Maak een projectmap voor de scripts die we gaan bouwen.

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

Start Oracle Database Free 26ai met de lokale containerimage.

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

Controleer de containerlogs en wacht tot de database meldt dat de start is voltooid.

docker logs oracle-free-26ai-vector

FREEPDB1 is de pluggable database-service die door dit lokale pad met Oracle Database Free-container wordt gebruikt. Onze Python-DSN wordt localhost:1521/FREEPDB1.

Maak het vector_demo-schema in FREEPDB1

Gebruik SYS alleen voor de schemaset-upstap. Het wachtwoord in het sqlplus-commando is de ORACLE_PWD-waarde uit het docker run-commando; het wachtwoord in CREATE USER is het applicatiewachtwoord dat we exporteren als DB_PASSWORD.

Voer de set-up uit binnen de databasecontainer.

# 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

Dit is een lokale ontwikkelsnelkoppeling voor de democontainer. CREATE SEQUENCE is nodig omdat de tabellen identity-kolommen gebruiken. 

Voor productie zouden we de privileges met een databasebeheerder doornemen en least privilege toepassen voor precies de applicatieworkload.

Python-afhankelijkheden installeren

Maak en activeer een virtual environment.

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

Installeer de Python-packages die door de demo worden gebruikt.

python -m pip install oracledb openai

Omgevingsvariabelen instellen

Sla databasegegevens en embeddinginstellingen op in omgevingsvariabelen in plaats van ze hard te coderen in Python-bestanden.

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 is alleen nodig voor de embedding- en semantische zoekstappen. EMBEDDING_DIM moet overeenkomen met het aantal dimensies dat door het geselecteerde embeddingmodel wordt geretourneerd.

Oracle AI Vector Search-demoproject

We bouwen de demo in acht checkpoints. Elk script voegt één concept toe en produceert uitvoer die we kunnen verifiëren voordat we verder gaan.

Het alleen-database-checkpoint is voltooid na Stappen 1–3. Op dat punt hebben we verbinding gemaakt met Oracle Database, handmatige vectoren opgeslagen, ze op afstand bevraagd en de semantische tabel gemaakt. De OpenAI API-sleutel wordt vereist in Stap 4.

Stap 1: Maak verbinding met de lokale database

We verifiëren eerst dat Python verbinding kan maken met Oracle Database als de vector_demo-applicatiegebruiker. Dit script leest DB_USER, DB_PASSWORD en DB_DSN uit de omgeving en print de drivermodus en databaseversie.

Maak het script

Sla de volgende code op als 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()

Voer het script uit

Voer de verbindingscheck uit.

python 01_check_connection.py

Verwachte output

De uitvoer moet bevestigen dat de verbinding werkt en dat de driver in Thin-modus staat.

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

We weten nu dat de lokale database, servicenaam, credentials en Python-driver werken voordat we vectortabellen aanmaken.

Stap 2: Bouw intuïtie op met handmatige vectoren

Voordat we embeddingmodellen introduceren, slaan we drie kleine vectoren op waar we logisch over kunnen redeneren. De aanroep array.array("f", values) maakt een 32-bits floating-pointarray die python-oracledb kan binden in een VECTOR(..., FLOAT32)-kolom.

Driedimensionale apple-, banana- en car-vectoren met Euclidische afstand vanaf de apple-queryvector.

Figuur 2. Handmatige vectoren en Euclidische afstand.

Het handmatige voorbeeld gebruikt Euclidische afstand. De apple-vector heeft een afstand van 0,0000 omdat deze overeenkomt met de queryvector. De banana-vector staat tweede omdat sqrt((1.0 - 0.9)^2 + (0.0 - 0.1)^2 + (0.0 - 0.0)^2) = 0,1414.

Maak het script

Sla de volgende code op als 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()

Voer het script uit

Voer het script met handmatige vectoren uit.

python 02_manual_vectors.py

Verwachte output

De identieke vector verschijnt eerst met afstand 0,0000, en de nabije banana-vector verschijnt als tweede.

Created manual_vectors.
 Inserted 3 manual vectors.

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

Dit is het kernidee achter vector search: rijen worden gerangschikt op wiskundige nabijheid tot een queryvector. Embeddingmodellen genereren grotere vectoren, maar het databasegedrag blijft hetzelfde.

Stap 3: Maak de semantische documententabel

Nu we weten dat vectoren werken, maken we een echte tabel in applicatiestijl. Het belangrijke detail is dat de dimensie van de vectorkolom moet overeenkomen met de outputdimensie van het embeddingmodel.

De dimensie is onderdeel van het tabelcontract. Als een embeddingmodel 1.536 getallen teruggeeft, moet de kolom gedeclareerd zijn als VECTOR(1536, FLOAT32). 

Als we later overstappen op een model met een andere outputlengte, moeten we de vectorkolom opnieuw maken of migreren, zodat inserts en queries compatibele vectoren blijven gebruiken.

Omdat DDL de vectordimensie niet kan binden als een reguliere SQL-bindvariabele, zet het script EMBEDDING_DIM om naar een integer voordat deze in de CREATE TABLE-instructie wordt gebruikt.

Maak het script

Sla de volgende code op als 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()

Voer het script uit

Voer het script voor het maken van de tabel uit.

python 03_create_documents_table.py

Verwachte output

De uitvoer moet het geconfigureerde embeddingmodel en de vectordimensie tonen.

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

We hebben het alleen-databasegedeelte voltooid. Oracle Database kan lokaal vectoren opslaan en vergelijken; daarna vervangen we handgeschreven vectoren door embeddings die uit tekst zijn gegenereerd.

Stap 4: Verifieer de dimensie van het embeddingmodel

Voordat we embeddings invoegen, vragen we het embeddingmodel om één vector en verifiëren we de lengte. Dit voorkomt de meest voorkomende fout met vectortabellen: een vector invoegen waarvan de lengte niet overeenkomt met de dimensie van de VECTOR-kolom.

Een embeddingmodel is niet zomaar een tekst-naar-getal-converter; het bepaalt de betekenisspace voor de applicatie. Alle document- en query-embeddings in deze demo moeten uit hetzelfde model komen.

Door één model te gebruiken, zorgen we ervoor dat een kleine afstand vergelijkbare betekenis representeert in plaats van twee niet-gerelateerde vectoren die toevallig een vergelijkbare vorm hebben. Het model bepaalt ook de vectorlengte, daarom controleren we de geretourneerde dimensie voordat we data laden.

Maak het script

Sla de volgende code op als 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()

Voer het script uit

Voer de dimensiecheck uit.

python 04_check_embedding_dimension.py

Verwachte output

Voor het standaardmodel en de standaarddimensie zou de uitvoer er zo uit moeten zien.

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

Als we later een ander embeddingmodel kiezen, moeten we EMBEDDING_MODEL bijwerken, EMBEDDING_DIM instellen op de geretourneerde vectorlengte en de documents-tabel opnieuw aanmaken.

Stap 5: Genereer en voeg documentembeddings in

Nu genereren we embeddings voor een kleine, zelfstandige dataset en voegen die in in Oracle Database. 

Elke embedding wordt geconverteerd naar array.array("f", embedding) vóór het binden, zodat deze overeenkomt met de FLOAT32-vectorkolom.

Maak het script

Sla de volgende code op als 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()

Voer het script uit

Voer het insertscript uit.

python 05_insert_embeddings.py

Verwachte output

Het aantal rijen moet overeenkomen met de zeven inline documents.

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

We hebben nu tekst en embeddings samen opgeslagen in Oracle Database. De niet-gerelateerde documents over gitaar en soep zijn opgenomen, zodat de zoekresultaten duidelijke nabije en verre matches hebben.

Stap 6: Voer een semantische zoekquery uit

Vervolgens embedden we een natuurlijke-taalquery en vergelijken die met opgeslagen documentembeddings. Dit script gebruikt cosinusafstand, wat vaak wordt gebruikt voor tekstembeddings omdat het focust op vectorrichting in plaats van ruwe grootte.

Voor semantisch zoeken gaat de query door hetzelfde embeddingmodel als de documents. We vragen SQL niet om de Engelse zin direct te begrijpen. 

We vragen het embeddingmodel om de zin in een vector om te zetten en vragen Oracle Database vervolgens om opgeslagen vectoren te rangschikken op hun afstand tot die queryvector.

Maak het script

Sla de volgende code op als 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()

Voer het script uit

Voer het script voor semantisch zoeken uit.

python 06_semantic_search.py

Verwachte output

Exacte rangschikkingen en afstanden kunnen variëren als het embeddingmodel verandert. Toch zouden documents over Oracle AI Vector Search, semantisch zoeken, Python-databasecode en vectorindexering hoog moeten eindigen.

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...

Voor de Euclidische en cosinusafstandsqueries in deze tutorial duiden kleinere waarden op betere matches. 

Dotproduct is een andere nuttige vergelijkingsmethode in veel vector-search-workflows, vooral wanneer grootte of genormaliseerde vectoren belangrijk zijn, maar score-richting en normalisatie vragen om zorgvuldige behandeling. 

We houden de uitvoerbare code bij Euclidische en cosinusafstand zodat de resultaatinterpretatie eenvoudig blijft.

Als we in een toekomstige applicatie zelf embeddings normaliseren, moeten we zowel opgeslagen documentembeddings als query-embeddings consequent normaliseren; anders kunnen afstanden en rangschikkingen veranderen.

Stap 7: Vergelijk semantisch zoeken met een frase-match

Semantisch zoeken is nuttig omdat het niet vereist dat exact dezelfde frase in het document voorkomt.

Laten we de semantische query vergelijken met een eenvoudige predicate voor een exacte frase.

Dit is geen demo van een fulltext-zoekmachine; het is een kleine SQL-vergelijking die het verschil zichtbaar maakt tussen letterlijk matchen en betekenisgebaseerd rangschikken.

Maak het script

Sla de volgende code op als 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()

Voer het script uit

Voer het vergelijkingsscript uit.

python 07_semantic_vs_keyword.py

Verwachte output

Het semantische zoeken zou relevante documents moeten retourneren, ook al is de exacte keywordfrase niet aanwezig.

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

Traditionele SQL-predicaten blijven belangrijk voor filters, joins, permissies en exact matchen. Vector search voegt een betekenisgebaseerd rangsignaal toe dat we met relationele data kunnen combineren wanneer een applicatie semantische retrieval nodig heeft.

Stap 8: Voeg een vectorindex toe en valideer die

Tot slot maken we een vectorindex en valideren we dat Oracle Database die rapporteert als een geldige index in het demoschema. 

Deze stap demonstreert de workflow voor indexcreatie; onze piepkleine dataset is niet groot genoeg voor zinvolle prestatieconclusies.

Zonder index kan Oracle Database de queryvector exact vergelijken met elke opgeslagen vector. Die aanpak is geschikt voor heel kleine datasets en nuttig tijdens het leren.

Bij grotere datasets verkleinen approximate vectorindexen de zoekruimte, zodat queries snel kunnen terugkeren en toch nabije buren vinden. 

Kies de indexfamilie na het overwegen van datagrootte, latentiedoelen, recall-eisen, geheugenbudget en hoe vaak vectoren veranderen.

De query aan het eind bevestigt dat semantisch zoeken nog steeds resultaten retourneert na het aanmaken van de index. 

We gebruiken hier geen EXPLAIN PLAN of DBMS_XPLAN, omdat deze tutorial indexcreatie valideert met schemametadata, niet met queryplangedrag.

Maak het script

Sla de volgende code op als 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()

Voer het script uit

Voer het indexscript uit.

python 08_create_vector_index.py

Verwachte output

De uitvoer moet een rij bevatten voor DOCUMENT_EMBEDDING_IDX, met INDEX_TYPE ingesteld op VECTOR en STATUS op 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 bevestigt dat de index bestaat en geldig is. Het bewijst niet dat een specifieke query de index gebruikte, en deze tutorial gebruikt geen uitvoeringsplanuitvoer als validatiemethode. 

Voor productie-workloads zouden we testen met realistisch datavolume, querypatronen en prestatiedoelen.

Oracle AI Vector Search-tips en troubleshooting

De databasecontainer is nog aan het starten

Wacht tot de start is voltooid en voer daarna docker logs oracle-free-26ai-vector opnieuw uit. De database moet klaar zijn voordat Python kan verbinden met localhost:1521/FREEPDB1.

Poort 1521 is al in gebruik

Stop de conflicterende lokale service of wijzig de Docker-poortmapping. Werk DB_DSN bij als de hostpoort verandert.

De databaseverbinding faalt

Controleer DB_USER, DB_PASSWORD en DB_DSN. Gebruik FREEPDB1 in de DSN, en laat applicatiescripts verbinden als vector_demo, niet als SYS, SYSTEM of PDBADMIN.

De embedding-API-sleutel ontbreekt

Stappen 1-3 zijn alleen database. Stappen 4-8 vereisen OPENAI_API_KEY en kunnen API-kosten met zich meebrengen.

Een vectordimensie komt niet overeen

Voer 04_check_embedding_dimension.py uit, werk EMBEDDING_DIM bij, voer 03_create_documents_table.py opnieuw uit en laad de embeddings opnieuw met 05_insert_embeddings.py.

Een vectorbind faalt

Zorg dat elke FLOAT32-vectorbind array.array("f", values) gebruikt. De tutorial gebruikt dit formaat voor handmatige vectoren, opgeslagen documentembeddings en query-embeddings.

Semantische rangorden verschillen van het voorbeeld

Dat is te verwachten. Embeddingproviders kunnen modellen bijwerken, en floating-pointafstanden kunnen tussen runs variëren.

Aanmaken van een vectorindex faalt

Controleer of de documents-tabel bestaat, het vector_demo-schema eigenaar is, en DOCUMENT_EMBEDDING_IDX niet is achtergebleven van een gedeeltelijk uitgevoerde run.

Voorbereiden op productie

Gebruik oracledb.create_pool() voor connection pooling, benchmark realistische datavolumes, beoordeel security en geheimbeheer en tune indexen op echte querypatronen.

Stop en verwijder de container

Als de tutorial klaar is, stop en verwijder de lokale container.

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

Conclusie

We bouwden een lokale workflow voor semantisch zoeken met Oracle AI Vector Search, Oracle Database Free 26ai en Python. 

We begonnen met handgeschreven VECTOR(3, FLOAT32)-waarden, voegden modelgegenereerde embeddings in met array.array("f", values), bevraagden vergelijkbare documents met VECTOR_DISTANCE(), vergeleken semantische retrieval met een eenvoudige predicate voor een exacte frase en valideerden een vectorindex via USER_INDEXES.

Deze aanpak past goed wanneer embeddings naast relationele data horen en we opslag en zoekopdrachten zichtbaar in SQL willen, zonder voor de lokale workflow een aparte vectordatabank toe te voegen. 

De demo is bewust klein: hij leert de bouwstenen, niet de productieschaal. Grotere applicaties moeten benchmarken met realistische data, security en privileges beoordelen, connection pooling gebruiken en indexkeuzes valideren onder echte querypatronen.

Vervolg je implementatie met deze resources:

FAQs

Wat is Oracle AI Vector Search?

Oracle AI Vector Search is een set mogelijkheden in Oracle Database voor het opslaan, indexeren en bevragen van vector-embeddings naast relationele data.

Heb ik een OpenAI API-sleutel nodig om de tutorial te voltooien?

De stappen met handmatige vectoren en databaseset-up vereisen geen API-sleutel. Stappen 4-8 vereisen een embeddingprovider en de geconfigureerde OPENAI_API_KEY.

Waarom moet EMBEDDING_DIM overeenkomen met het embeddingmodel?

De dimensie van de VECTOR-kolom moet gelijk zijn aan het aantal waarden dat door het embeddingmodel wordt geretourneerd. Een mismatch zorgt ervoor dat het invoegen van vectoren mislukt.

Welke afstandsmaatstaven gebruikt de tutorial?

Het voorbeeld met handmatige vectoren gebruikt Euclidische afstand. De voorbeelden met semantisch zoeken gebruiken cosinusafstand.

Bewijst een geldige vectorindex dat een query de index heeft gebruikt?

Nee. USER_INDEXES bevestigt dat de index bestaat en een geldige status heeft, maar het bevestigt niet dat een specifieke query die heeft gebruikt.

Mark Nelson

Auteur

Mark Nelson

Mark Nelson is architect en developer evangelist bij Oracle.  Hij werkt op het snijvlak van AI, microservices en databasetechnologie.  Hij is een actieve blogger, gepubliceerd auteur, technisch reviewer bij Manning Publications, Section Leader bij Stanford Code in Place en mentor bij DeepLearning.ai.  Hij spreekt regelmatig bij Java- en Oracle-gebruikersgroepen, AI-meetups en grote conferenties.  Hij heeft een passie voor leren en lesgeven.  Hij heeft meer dan dertig jaar ervaring in de industrie, bij IBM en Oracle.

Onderwerpen

Gerelateerd