Pular para o conteúdo principal

Tutorial Oracle AI Vector Search: armazene e consulte embeddings com Python

Este tutorial apresenta o Oracle AI Vector Search, explica seus recursos centrais e conduz um projeto prático local. Cobre requisitos de setup, busca semântica, indexação vetorial e passos comuns de troubleshooting.
Atualizado 19 de set. de 2026  · 15 min lido

Explorar com IA

ChatGPTClaudePerplexity

Neste tutorial, vamos criar um demo local de busca semântica com Oracle AI Vector Search, Python e Oracle Database Free 26ai. 

Começaremos com vetores tridimensionais escritos à mão para que o cálculo de distância fique visível; depois, vamos gerar embeddings de texto e consultá-los no Oracle Database com SQL. 

Este tutorial é para desenvolvedores Python e SQL que estão começando com busca vetorial e têm familiaridade com Docker, variáveis de ambiente, pacotes Python e configuração de banco de dados local.

Aplicativos com IA precisam de busca que entenda significado, não só palavras exatas. Um app de suporte, portal de documentação ou ferramenta interna de conhecimento deve conseguir encontrar “armazenamento de banco de dados para busca em IA” mesmo quando o melhor documento diz “armazenar embeddings vetoriais em colunas nativas”.

Vector embeddings tornam isso possível ao representar texto como vetores numéricos. O Oracle AI Vector Search permite armazenar esses vetores diretamente no Oracle Database, consultá-los com SQL e manter embeddings ao lado dos dados relacionais do aplicativo. 

Para este fluxo local, não precisamos de um banco vetorial separado.

Vamos executar o Oracle Database Free 26ai localmente, conectar a partir do Python no modo Thin do oracledb, armazenar primeiro vetores manuais, depois armazenar embeddings da OpenAI, rodar busca semântica, comparar a recuperação semântica com um predicado simples de frase exata e criar um índice vetorial validado por meio de USER_INDEXES.

Ao final, teremos um fluxo de busca semântica em Python que armazena embeddings no Oracle Database e recupera localmente os documents mais similares.

O que é o Oracle AI Vector Search?

Oracle AI Vector Search é um conjunto de recursos do Oracle Database para armazenar, indexar e consultar embeddings vetoriais. Um embedding vetorial é uma lista de números de tamanho fixo que representa o significado de textos, imagens ou outros dados em uma forma que o banco consegue comparar matematicamente.

Um modelo de embedding cria os vetores numéricos. Ele recebe como entrada uma sentença, parágrafo, descrição de imagem ou trecho de código e retorna um vetor com um número fixo de dimensões.

Entradas similares devem produzir vetores próximos entre si, enquanto entradas não relacionadas devem ficar mais distantes. Por isso, precisamos usar o mesmo modelo de embedding para os documents armazenados e para as consultas, pois as distâncias só fazem sentido quando os vetores compartilham o mesmo sistema de coordenadas.

Neste tutorial, os vetores manuais são deliberadamente pequenos para inspecionarmos a matemática. Os embeddings reais de texto são bem maiores porque o modelo precisa de dimensões suficientes para codificar relações sutis entre palavras, frases e tópicos.

A busca vetorial ranqueia linhas pela distância entre vetores. Para consultas de distância euclidiana e de cosseno, valores menores significam correspondências mais próximas. Neste tutorial, começaremos com uma coluna VECTOR(3, FLOAT32) pequena para ver o comportamento da distância diretamente e depois passaremos para embeddings VECTOR(1536, FLOAT32) gerados a partir de texto.

Fluxo do Oracle AI Vector Search

Figura 1. Fluxo do Oracle AI Vector Search

Oracle AI Vector Search armazena embeddings em uma coluna nativa VECTOR e ranqueia os resultados pela distância vetorial.

Oracle Database é útil quando embeddings precisam ficar ao lado de dados relacionais do app, como IDs de documentos, títulos, conteúdo, usuários, produtos, tickets ou metadados. 

Alguns aplicativos usam um banco vetorial ou mecanismo de busca separado para cargas especializadas de recuperação; mas, para o fluxo local deste tutorial, o Oracle Database oferece os recursos de armazenamento e busca vetorial de que precisamos.

Principais recursos do Oracle AI Vector Search

Usaremos quatro recursos do Oracle AI Vector Search no demo: armazenamento vetorial nativo, funções de distância, índices vetoriais e integração relacional.

Tipo de dado vetorial nativo

Oracle Database suporta colunas nativas VECTOR com dimensões explícitas e formatos de elemento. No demo, usaremos VECTOR(3, FLOAT32) para vetores manuais e VECTOR(1536, FLOAT32) para o modelo de embedding padrão text-embedding-3-small.

Funções e operadores de distância

Os exemplos executáveis usam VECTOR_DISTANCE() porque mantém a métrica de distância explícita no SQL. 

Usaremos distância euclidiana para o exemplo manual e distância de cosseno para embeddings de texto. 

Oracle Database também inclui operadores abreviados de distância vetorial e comportamento relacionado a produto interno, mas este tutorial mantém o SQL executável no caminho explícito com VECTOR_DISTANCE().

Métricas de distância diferentes respondem a perguntas ligeiramente diferentes sobre “proximidade”. 

Para um tutorial curto, distância de cosseno é um bom padrão para embeddings de texto porque foca na direção, o que costuma mapear bem para similaridade semântica. 

A distância euclidiana é mais fácil de visualizar com vetores escritos à mão, e comparações no estilo produto interno são úteis em alguns sistemas quando a magnitude do vetor ou a normalização fazem parte do desenho do modelo.

Métrica

O que compara

Bom para

Fique atento a

Cosseno

Direção do vetor

Embeddings de texto e similaridade semântica

Os resultados dependem do mesmo modelo de embedding e de pré-processamento consistente

Euclidiana

Distância em linha reta

Exemplos pequenos, intuição geométrica, alguns vetores de features numéricas

A magnitude afeta a distância; o escalonamento pode importar

Produto interno

Direção e magnitude juntas

Workflows baseados em vetores normalizados ou pontuação por produto interno

É preciso entender direção do score e regras de normalização antes de comparar resultados

Índices vetoriais

A busca vetorial exata é útil para correção e datasets pequenos. À medida que os dados crescem, índices vetoriais dão suporte a buscas aproximadas de vizinhos mais próximos.

Oracle AI Vector Search inclui índices de grafo de vizinhos em memória, frequentemente associados a buscas no estilo HNSW, e índices de partições de vizinhos, associados à partição no estilo IVF. 

Neste demo local, usamos ORGANIZATION NEIGHBOR PARTITIONS e validamos o índice com USER_INDEXES.

Índices aproximados trocam um pouco da exatidão da busca exaustiva por mais velocidade em escala. 

A família de índice certa depende da carga, volume de dados, padrão de atualização e meta de recall.

Este tutorial usa um índice de partições de vizinhos no estilo IVF porque simplifica o setup local e oferece um passo concreto de criação de índice sem transformar o tutorial em um guia de tuning.

Família de índice

Ideia básica

Pontos fortes

Trade-offs

Quando usar

IVF / partições de vizinhos

Particionar o espaço vetorial e buscar nas partições prováveis

Modelo mental simples, prático para muitos datasets carregados em lote

Recall e velocidade dependem da partição e das configurações de busca

Queremos um caminho direto de busca aproximada para um dataset em crescimento

HNSW / grafo de vizinhos

Construir um grafo que liga vetores próximos para travessia rápida

Ótimo perfil de recall e latência para muitos workloads de vizinhos mais próximos

Mais orientado a memória; a configuração pode importar mais

Precisamos de baixa latência e podemos investir memória e esforço de tuning

Integração com consultas relacionais

Uma coluna vetorial pode viver ao lado de colunas relacionais comuns. Nossa tabela de documents vai armazenar título, conteúdo e embedding juntos, para que o SQL retorne tanto o score de similaridade quanto o texto original.

Como começar com Oracle AI Vector Search

Vamos usar um caminho local: Oracle Database Free 26ai no Docker, o serviço FREEPDB1 e um usuário de aplicação dedicado vector_demo

Os conceitos de busca vetorial são acessíveis, mas a configuração é intermediária porque usamos Docker, um schema de banco, pacotes Python, variáveis de ambiente e uma chave de API de embedding.

Pré-requisitos

Vamos precisar de:

  • Docker Desktop ou Docker Engine.
  • Acesso ao Oracle Container Registry.
  • Portas locais 1521 e 5500 livres.
  • Python 3.10 ou superior.
  • Noções de Python e SQL.
  • Familiaridade básica com Docker e variáveis de ambiente.
  • Uma chave de API da OpenAI para as etapas de embedding.

Este demo requer uma versão/ambiente do Oracle Database que inclua os recursos vetoriais usados aqui, incluindo colunas nativas VECTOR, VECTOR_DISTANCE() e CREATE VECTOR INDEX

O caminho local usa Oracle Database Free 26ai com a tag de imagem mostrada abaixo, e o DSN usa o serviço de banco plugável FREEPDB1.

As etapas com vetores manuais não exigem uma chave de API da OpenAI. As etapas semânticas enviam o texto de exemplo e a consulta para o provedor de embedding configurado, então usaremos apenas textos de amostra ou não sensíveis.

Este tutorial usa o oracledb, o driver Python atual para Oracle Database. O python-oracledb roda em modo Thin por padrão, então este demo local não requer bibliotecas do Oracle Client. O modo Thick é útil em outras implantações, mas aqui não chamaremos oracledb.init_oracle_client().

Iniciar o Oracle Database Free 26ai localmente

Crie um diretório de projeto para os scripts que vamos construir.

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

Inicie o Oracle Database Free 26ai com a imagem local do container.

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

Verifique os logs do container e aguarde até o banco indicar que a inicialização foi concluída.

docker logs oracle-free-26ai-vector

FREEPDB1 é o serviço de database plugável usado por este caminho local do Oracle Database Free. Nosso DSN no Python será localhost:1521/FREEPDB1.

Criar o schema vector_demo no FREEPDB1

Use SYS apenas para a etapa de configuração do schema. A senha no comando sqlplus é o valor de ORACLE_PWD do comando docker run; a senha no CREATE USER é a senha da aplicação que vamos exportar como DB_PASSWORD.

Rode a configuração dentro do container do banco.

# Crie o usuário da aplicação e conceda os privilégios necessários para o 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

Este é um atalho local de desenvolvimento para o container de demo. CREATE SEQUENCE é necessário porque as tabelas usam colunas identity. 

Em produção, revisaríamos privilégios com um DBA e aplicaríamos privilégio mínimo para a carga exata da aplicação.

Instalar dependências Python

Crie e ative um ambiente virtual.

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

Instale os pacotes Python usados no demo.

python -m pip install oracledb openai

Definir variáveis de ambiente

Guarde credenciais do banco e parâmetros de embedding em variáveis de ambiente em vez de fixá-los nos arquivos 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 é exigida apenas para as etapas de embedding e busca semântica. EMBEDDING_DIM deve corresponder ao número de dimensões retornado pelo modelo escolhido.

Projeto demo do Oracle AI Vector Search

Vamos construir o demo em oito checkpoints. Cada script adiciona um conceito e gera uma saída que podemos verificar antes de avançar.

O checkpoint “apenas banco” termina após os Passos 1–3. Nesse ponto, teremos conectado ao Oracle Database, armazenado vetores manuais, consultado por distância e criado a tabela semântica. A chave de API da OpenAI passa a ser necessária no Passo 4.

Passo 1: conectar ao banco local

Primeiro, vamos verificar se o Python consegue conectar ao Oracle Database como o usuário de aplicação vector_demo. Este script lê DB_USER, DB_PASSWORD e DB_DSN do ambiente e imprime o modo do driver e a versão do banco.

Criar o script

Salve o código a seguir como 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()

Executar o script

Rode a verificação de conexão.

python 01_check_connection.py

Saída esperada

A saída deve confirmar que a conexão funciona e que o driver está em modo Thin.

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

Agora sabemos que o banco local, o nome do serviço, as credenciais e o driver Python estão funcionando antes de criar as tabelas vetoriais.

Passo 2: criar intuição com vetores manuais

Antes de introduzirmos modelos de embedding, vamos armazenar três vetores pequenos que conseguimos entender. A chamada array.array("f", values) cria um array de ponto flutuante 32-bit que o python-oracledb consegue associar a uma coluna VECTOR(..., FLOAT32).

Vetores 3D de maçã, banana e carro mostrando a distância euclidiana a partir do vetor de consulta maçã.

Figura 2. Vetores manuais e distância euclidiana.

O exemplo manual usa distância euclidiana. O vetor “apple” tem distância 0,0000 porque coincide com o vetor de consulta. O vetor “banana” fica em segundo porque sqrt((1.0 - 0.9)^2 + (0.0 - 0.1)^2 + (0.0 - 0.0)^2) = 0,1414.

Criar o script

Salve o código a seguir como 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()

Executar o script

Rode o script de vetores manuais.

python 02_manual_vectors.py

Saída esperada

O vetor idêntico aparece primeiro com distância 0,0000 e o vetor “banana” aparece em segundo.

Created manual_vectors.
 Inserted 3 manual vectors.

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

Esta é a ideia central da busca vetorial: as linhas são ranqueadas pela proximidade matemática a um vetor de consulta. Modelos de embedding geram vetores maiores, mas o comportamento no banco é o mesmo.

Passo 3: criar a tabela semântica de documentos

Agora que sabemos que vetores funcionam, vamos criar uma tabela no estilo de aplicação. O detalhe importante é que a dimensão da coluna vetorial deve corresponder à dimensão de saída do modelo de embedding.

A dimensão faz parte do contrato da tabela. Se um modelo retorna 1.536 números, a coluna deve ser declarada como VECTOR(1536, FLOAT32)

Se depois trocarmos para um modelo com comprimento diferente, precisamos recriar ou migrar a coluna vetorial para que inserts e consultas continuem compatíveis.

Como DDL não permite associar a dimensão vetorial como bind normal, o script converte EMBEDDING_DIM em inteiro antes de usar no CREATE TABLE.

Criar o script

Salve o código a seguir como 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()

Executar o script

Rode o script de criação da tabela.

python 03_create_documents_table.py

Saída esperada

A saída deve mostrar o modelo de embedding configurado e a dimensão vetorial.

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

Concluímos a parte apenas de banco. O Oracle Database consegue armazenar e comparar vetores localmente; em seguida, vamos trocar vetores escritos à mão por embeddings gerados a partir de texto.

Passo 4: verificar a dimensão do modelo de embedding

Antes de inserir embeddings, vamos pedir um vetor ao modelo e verificar seu comprimento. Isso evita o erro vetorial mais comum: inserir um vetor cujo comprimento não corresponde à dimensão da coluna VECTOR.

Um modelo de embedding não é apenas um conversor texto→número; ele define o espaço de significado da aplicação. Todos os embeddings de documentos e consultas neste demo devem vir do mesmo modelo.

Usar um único modelo garante que uma distância pequena represente significados similares, e não vetores não relacionados que por acaso tenham forma parecida. O modelo também determina o comprimento do vetor, por isso checamos a dimensão retornada antes de carregar dados.

Criar o script

Salve o código a seguir como 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()

Executar o script

Rode a checagem de dimensão.

python 04_check_embedding_dimension.py

Saída esperada

Para o modelo e dimensão padrão, a saída deve parecer com isto.

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

Se escolhermos um modelo de embedding diferente mais tarde, devemos atualizar EMBEDDING_MODEL, definir EMBEDDING_DIM para o comprimento retornado e recriar a tabela documents.

Passo 5: gerar e inserir embeddings de documentos

Agora vamos gerar embeddings para um pequeno dataset autocontido e inseri-los no Oracle Database. 

Cada embedding é convertido para array.array("f", embedding) antes de associar, para corresponder à coluna vetorial FLOAT32.

Criar o script

Salve o código a seguir como 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()

Executar o script

Rode o script de insert.

python 05_insert_embeddings.py

Saída esperada

A contagem de linhas deve corresponder aos sete documents inline.

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

Agora temos texto e embeddings armazenados juntos no Oracle Database. Os documents não relacionados sobre violão e sopa foram incluídos para que os resultados tenham correspondências obviamente próximas e distantes.

Passo 6: rodar uma consulta de busca semântica

Em seguida, vamos embutir uma consulta em linguagem natural e compará-la com os embeddings dos documentos armazenados. Este script usa distância de cosseno, comumente usada em embeddings de texto porque foca na direção do vetor, não na magnitude bruta.

Para busca semântica, a consulta passa pelo mesmo modelo de embedding que os documents. Não estamos pedindo ao SQL que entenda a frase em inglês diretamente. 

Estamos pedindo ao modelo de embedding que transforme a frase em um vetor e, então, pedindo ao Oracle Database que ranqueie os vetores armazenados pela distância a esse vetor de consulta.

Criar o script

Salve o código a seguir como 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()

Executar o script

Rode o script de busca semântica.

python 06_semantic_search.py

Saída esperada

Rankings e distâncias exatas podem variar quando o modelo muda. Porém, documents sobre Oracle AI Vector Search, busca semântica, código Python para banco e indexação vetorial devem ranquear no topo.

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

Para as consultas de distância euclidiana e de cosseno deste tutorial, valores menores indicam correspondências mais próximas. 

Produto interno é outra abordagem útil em muitos workflows de busca vetorial, especialmente quando a magnitude ou a normalização dos vetores importam, mas a direção do score e a normalização exigem cuidado. 

Vamos manter o código executável em distância euclidiana e de cosseno para simplificar a interpretação.

Se normalizarmos embeddings em um app futuro, precisamos normalizar tanto os embeddings de documentos armazenados quanto os de consulta de forma consistente; do contrário, distâncias e rankings podem mudar.

Passo 7: comparar busca semântica com correspondência por frase

A busca semântica é útil porque não exige que a frase exata apareça no documento.

Vamos comparar a consulta semântica com um predicado simples de frase exata.

Isto não é um demo de mecanismo de busca full-text; é uma pequena comparação em SQL que torna visível a diferença entre correspondência literal e ranqueamento por significado.

Criar o script

Salve o código a seguir como 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()

Executar o script

Rode o script de comparação.

python 07_semantic_vs_keyword.py

Saída esperada

A busca semântica deve retornar documents relevantes mesmo que a frase exata não esteja presente.

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

Predicados SQL tradicionais continuam importantes para filtros, junções, permissões e correspondência exata. A busca vetorial adiciona um sinal de ranqueamento por significado que podemos combinar com dados relacionais quando a aplicação precisa de recuperação semântica.

Passo 8: adicionar e validar um índice vetorial

Por fim, vamos criar um índice vetorial e validar que o Oracle Database o reporta como um índice válido pertencente ao schema do demo. 

Este passo demonstra o fluxo de criação do índice; nosso dataset minúsculo não é grande o suficiente para conclusões de performance.

Sem índice, o Oracle Database pode comparar o vetor de consulta com cada vetor armazenado exatamente. Essa abordagem é adequada para datasets minúsculos e útil durante o aprendizado.

Com datasets maiores, índices vetoriais aproximados reduzem o espaço de busca para que as consultas retornem rápido e ainda encontrem vizinhos próximos. 

Escolha a família de índice considerando tamanho de dados, metas de latência, requisitos de recall, orçamento de memória e frequência de mudança dos vetores.

A consulta ao final confirma que a busca semântica ainda retorna resultados após a criação do índice. 

Não usaremos EXPLAIN PLAN ou DBMS_XPLAN aqui porque este tutorial valida a criação do índice com metadados do schema, não com o plano de execução.

Criar o script

Salve o código a seguir como 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()

Executar o script

Rode o script do índice.

python 08_create_vector_index.py

Saída esperada

A saída deve incluir uma linha para DOCUMENT_EMBEDDING_IDX, com INDEX_TYPE definido como VECTOR e STATUS como 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 confirma que o índice existe e é válido. Isso não prova que uma consulta específica usou o índice, e este tutorial não usa o plano de execução como método de validação. 

Para workloads de produção, testaríamos com volume de dados, padrões de consulta e metas de performance realistas.

Dicas e solução de problemas do Oracle AI Vector Search

O container do banco ainda está iniciando

Espere a inicialização concluir e rode docker logs oracle-free-26ai-vector novamente. O banco precisa estar pronto antes de o Python conectar a localhost:1521/FREEPDB1.

A porta 1521 já está em uso

Pare o serviço local em conflito ou altere o mapeamento de portas do Docker. Atualize o DB_DSN se a porta do host mudar.

A conexão com o banco falha

Verifique DB_USER, DB_PASSWORD e DB_DSN. Use FREEPDB1 no DSN e conecte os scripts da aplicação como vector_demo, não como SYS, SYSTEM ou PDBADMIN.

A chave de API de embedding está ausente

Os passos 1–3 são apenas de banco. Os passos 4–8 exigem OPENAI_API_KEY e podem gerar custos de API.

Uma dimensão vetorial não corresponde

Rode 04_check_embedding_dimension.py, atualize EMBEDDING_DIM, rode novamente 03_create_documents_table.py e recarregue os embeddings com 05_insert_embeddings.py.

Falha ao associar um vetor (bind)

Garanta que todo bind de vetor FLOAT32 use array.array("f", values). O tutorial usa este formato para vetores manuais, embeddings armazenados e embeddings de consulta.

Os rankings semânticos diferem do exemplo

Isso é esperado. Provedores de embedding podem atualizar modelos, e distâncias de ponto flutuante podem variar entre execuções.

Falha na criação do índice vetorial

Confirme que a tabela documents existe, que o schema vector_demo é o dono e que DOCUMENT_EMBEDDING_IDX não ficou de uma execução anterior incompleta.

Preparando para produção

Use oracledb.create_pool() para pool de conexões, faça benchmark com volumes realistas, revise segurança e gestão de segredos e ajuste índices conforme padrões reais de consulta.

Parar e remover o container

Quando terminar o tutorial, pare e remova o container local.

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

Conclusão

Construímos um fluxo local de busca semântica com Oracle AI Vector Search, Oracle Database Free 26ai e Python. 

Começamos com valores VECTOR(3, FLOAT32) escritos à mão, inserimos embeddings gerados por modelo com array.array("f", values), consultamos documents similares com VECTOR_DISTANCE(), comparamos a recuperação semântica com um predicado simples de frase exata e validamos um índice vetorial via USER_INDEXES.

Essa abordagem funciona bem quando embeddings devem ficar ao lado de dados relacionais e queremos armazenamento e busca visíveis em SQL sem adicionar um banco vetorial separado para o fluxo local. 

O demo é pequeno de propósito: ensina os fundamentos, não escala de produção. Aplicativos maiores devem fazer benchmark com dados realistas, revisar segurança e privilégios, usar pool de conexões e validar escolhas de indexação sob padrões reais de consulta.

Depois, você pode aprofundar a implementação com estes recursos:

FAQs

O que é o Oracle AI Vector Search?

Oracle AI Vector Search é um conjunto de recursos do Oracle Database para armazenar, indexar e consultar embeddings vetoriais ao lado de dados relacionais.

Preciso de uma chave de API da OpenAI para concluir o tutorial?

As etapas de vetores manuais e configuração do banco não exigem chave de API. As etapas 4–8 exigem um provedor de embedding e a variável OPENAI_API_KEY configurada.

Por que EMBEDDING_DIM deve corresponder ao modelo de embedding?

A dimensão da coluna VECTOR deve ser igual ao número de valores retornados pelo modelo de embedding. Uma inconsistência faz a inserção vetorial falhar.

Quais métricas de distância o tutorial usa?

O exemplo com vetores manuais usa distância euclidiana. Os exemplos de busca semântica usam distância de cosseno.

Um índice vetorial válido prova que a consulta usou o índice?

Não. USER_INDEXES confirma que o índice existe e está válido, mas não confirma que uma consulta específica o utilizou.


Mark Nelson's photo
Author
Mark Nelson
LinkedIn

Mark Nelson é arquiteto e developer evangelist na Oracle.  Atua na convergência entre IA, microsserviços e tecnologias de banco de dados.  É blogueiro ativo, autor publicado, revisor técnico na Manning Publications, Section Leader no Stanford Code in Place e mentor na DeepLearning.ai.  Ele apresenta regularmente em Java e Oracle User Groups, encontros de IA e grandes conferências.  Tem paixão por aprender e ensinar.  Soma mais de trinta anos de experiência no mercado, na IBM e na Oracle.

Tópicos
Inteligência Artificial
Python

Principais cursos da DataCamp

Curso

Introdução ao Oracle SQL

4 h
18.4K
Aprimore suas habilidades em Oracle SQL, incluindo noções básicas de SQL, agregação, combinação e personalização de dados.
Ver detalhesRight Arrow
Iniciar Curso
Ver maisRight Arrow