Notas prácticas: PostgreSQL + pgvector y SQL Server 2025 como almacenes de vectores
Guía práctica paso a paso de Notas prácticas: PostgreSQL + pgvector y SQL Server 2025 como almacenes vectoriales: contratos, verificaciones y espacios de código listos para uso para los equipos que implementan este patrón.
Las notas siguientes reconstruyen un enfoque práctico basado en “PostgreSQL + pgvector y SQL Server 2025 como almacenes vectoriales para RAG: Una guía para profesionales”. Se da énfasis en los contratos, las verificaciones y los marcadores de posición para el código, en lugar de en un enfoque motivacional.
El panorama de las bases de datos vectoriales en 2025
Al trabajar en la etapa del panorama de las bases de datos vectoriales, anote primero el contrato: entradas requeridas, señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación garantiza que los cambios posteriores en el código sean transparentes. Considere esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina las verificaciones de éxito y evite completaciones parciales silenciosas. Mida el recuerdo con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.
-- Find the most relevant chunks, but only from documents
-- belonging to enterprise-tier customers — a single SQL query
SELECT c.content, 1 - (c.embedding <=> query_vec) AS score
FROM rag_chunks c
JOIN documents d ON d.filename = c.source
JOIN customers cu ON cu.id = d.customer_id
WHERE cu.tier = 'enterprise'
ORDER BY score DESC
LIMIT 5;
El stack completo
Al trabajar en la etapa The Full Stack, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Registre los tiempos y el costo de tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas cuando se pasa de entornos de demostración a entornos compartidos. Mida el rendimiento en un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.
Parte 1 — PostgreSQL 18 + pgvector
Al trabajar en la etapa 1 de PostgreSQL 18, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el sistema. Mida la tasa de recuperación con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.
Instalando pgvector en Windows
Al trabajar en la fase de instalación de pgvector en Windows, anote primero el contrato: los datos de entrada requeridos, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Documente tanto el camino óptimo como el de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no de mejoras posteriores. Mida la precisión con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.
set "PGROOT=C:\Program Files\PostgreSQL\18"
cd %TEMP%
git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git
cd pgvector
nmake /F Makefile.win
nmake /F Makefile.win install
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres --name pgvector pgvector/pgvector:pg18
conda install -c conda-forge pgvector
Creación de tablas en pgAdmin
Al trabajar en la fase de Creación de tablas en pgAdmin, anote primero el contrato: los datos de entrada requeridos, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación garantiza que los cambios posteriores en el código sean transparentes. Prefiera unidades pequeñas y verificables a scripts extensos. Cuando un paso falla, el fallo debe referirse a una sola responsabilidad y no a un proceso complicado. Mida el rendimiento con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente. Al trabajar en la fase de Creación de tablas en pgAdmin, anote primero el contrato: los datos de entrada requeridos, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación garantiza que los cambios posteriores en el código sean transparentes. Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas cuando se pasa de entornos de demostración a entornos compartidos.
-- Enable the pgvector extension
CREATE EXTENSION IF NOT EXISTS vector;
-- Main chunks table with VECTOR(768) column
CREATE TABLE IF NOT EXISTS rag_chunks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source TEXT NOT NULL,
chunk_index INTEGER NOT NULL,
content TEXT NOT NULL,
file_hash TEXT,
ingested_at TIMESTAMPTZ DEFAULT NOW(),
embedding vector(768) -- pgvector native type
);
-- HNSW index for approximate cosine similarity search
CREATE INDEX IF NOT EXISTS idx_rag_chunks_embedding
ON rag_chunks USING hnsw (embedding vector_cosine_ops);
-- Source filter index
CREATE INDEX IF NOT EXISTS idx_rag_chunks_source
ON rag_chunks (source);
-- Staleness registry
CREATE TABLE IF NOT EXISTS rag_staleness (
doc_name TEXT PRIMARY KEY,
file_hash TEXT NOT NULL,
chunk_count INTEGER,
ingested_at TIMESTAMPTZ DEFAULT NOW(),
version INTEGER DEFAULT 1
);
-- CDC chunk registry
CREATE TABLE IF NOT EXISTS rag_chunk_registry (
doc_name TEXT NOT NULL,
chunk_hash TEXT NOT NULL,
chunk_id TEXT NOT NULL,
PRIMARY KEY (doc_name, chunk_hash)
);
-- Conversation sessions
CREATE TABLE IF NOT EXISTS rag_sessions (
session_id TEXT PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
model TEXT,
embed_model TEXT,
turn_count INTEGER DEFAULT 0
);
-- Conversation turns
CREATE TABLE IF NOT EXISTS rag_turns (
id SERIAL PRIMARY KEY,
session_id TEXT REFERENCES rag_sessions(session_id) ON DELETE CASCADE,
role TEXT NOT NULL CHECK (role IN ('user','assistant')),
content TEXT NOT NULL,
sources TEXT[],
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_rag_turns_session
ON rag_turns (session_id, created_at);
Dependencias de Python
La etapa de Dependencias de Python funciona mejor cuando se trata como una superficie medible. Capture una transcripción ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el sistema. Fije el intérprete y el archivo de bloqueo de dependencias antes de explicar los bucles. La diferencia entre la computadora portátil y el entorno CI es la causa más común de fallos silenciosos en las demostraciones de API.
uv add google-genai pypdf pgvector psycopg2-binary python-dotenv huggingface_hub
Configuración de conexión (Célula 2)
La etapa 2 de la Célula de Configuración de Conexión funciona mejor cuando se trata como una superficie medible. Capture un registro exitoso, un caso de fallo y la nota de reversión antes de ampliar el alcance. Documente tanto el camino óptimo como el camino de recuperación juntos. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son ajustes realizados posteriormente. Separe la política de fragmentación de la política de recuperación; cambiar una no debe obligar a reescribir la otra cuando cambian las métricas de calidad.
import psycopg2
from pgvector.psycopg2 import register_vector
PG_HOST = "localhost"
PG_PORT = 5432
PG_DB = "postgres"
PG_USER = "postgres"
PG_PASSWORD = os.environ.get("PG_PASSWORD", "postgres")
def get_pg_conn():
"""Returns a fresh PostgreSQL connection with pgvector registered."""
conn = psycopg2.connect(
host=PG_HOST, port=PG_PORT,
dbname=PG_DB, user=PG_USER, password=PG_PASSWORD
)
register_vector(conn) # tells psycopg2 how to handle vector type
return conn
Almacenamiento de Embeddings (Célula 6)
La etapa 6 de la Célula de Almacenamiento de Embeddings funciona mejor cuando se trata como una superficie medible. Capture un transcripto ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando falla un paso, el fallo debe apuntar a una sola responsabilidad y no a un proceso complicado. Separe la política de particionamiento de la política de recuperación. Cambiar una no debe obligar a reescribir la otra cuando cambian las métricas de calidad.
import numpy as np
def store_in_postgres(chunks, embeddings, doc_name) -> int:
conn = get_pg_conn()
cur = conn.cursor()
for i, (chunk, emb) in enumerate(zip(chunks, embeddings)):
cur.execute("""
INSERT INTO rag_chunks (source, chunk_index, content, embedding)
VALUES (%s, %s, %s, %s)
""", (doc_name, i, chunk, np.array(emb))) # np.array → pgvector handles serialization
conn.commit()
conn.close()
return len(chunks)
La etapa 6 de la Célula de Almacenamiento de Embeddings funciona mejor cuando se trata como una superficie medible. Capture un transcripto ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Registre los tiempos y el costo en tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas al pasar del entorno de demostración a los entornos compartidos.
Recuperación con Similitud Cosina (Continuación de la Célula 6)
Para la etapa de recuperación con similitud coseno, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben encontrarse en un lugar que los operadores puedan auditar sin necesidad de leer todo el sistema. Cite los pasajes que realmente sirvieron como base para la respuesta. Sin citaciones, los operadores no podrán distinguir entre alucinaciones y brechas en el indexado.
def retrieve_context(query: str) -> list[dict]:
query_embedding = embed_query(query)
conn = get_pg_conn()
cur = conn.cursor()
cur.execute("""
SELECT
content,
source,
chunk_index,
ingested_at,
1 - (embedding <=> %s) AS cosine_score -- <=> is cosine distance
FROM rag_chunks
ORDER BY embedding <=> %s -- sort ascending (smallest distance first)
LIMIT %s
""", (np.array(query_embedding), np.array(query_embedding), TOP_K))
rows = cur.fetchall()
conn.close()
return [{"text": r[0], "source": r[1], "chunk_index": r[2],
"ingested_at": str(r[3]) if r[3] else "",
"score": round(float(r[4]), 4)} for r in rows]
CDC con pgvector — Patrón Upsert
Para CDC con la etapa Upsert de pgvector, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Cite los pasajes que realmente sustentan la respuesta. Sin citaciones, los operadores no pueden distinguir entre alucinaciones y brechas en el indexado.
cur.execute("""
INSERT INTO rag_staleness (doc_name, file_hash, chunk_count, version)
VALUES (%s, %s, %s, 1)
ON CONFLICT (doc_name) DO UPDATE SET
file_hash = EXCLUDED.file_hash,
chunk_count = EXCLUDED.chunk_count,
ingested_at = NOW(),
version = rag_staleness.version + 1;
""", (doc_name, file_hash, chunk_count))
Parte 2 — SQL Server 2025 (Vector nativo)
En la etapa de SQL Server de la Parte 2, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado. Cite los pasajes que realmente sustentan la respuesta. Sin citaciones, los operadores no pueden distinguir entre una alucinación y una deficiencia en el indexado.
Por qué SQL Server 2025 no necesita extensiones
En la fase de Why SQL Server 2025, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Trate esta fase como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Cite los pasajes que realmente sustentan la respuesta. Sin citaciones, los operadores no pueden distinguir entre una alucinación y una deficiencia en el indexado.
Configuración en SSMS
En la fase de configuración en SSMS, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo de tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos. Cite los pasajes que realmente sustentan la respuesta; sin citas, los operadores no pueden distinguir entre alucinaciones y brechas en el indexado.
-- Batch 1: Run this first — must commit before vector objects are recognized
ALTER DATABASE SCOPED CONFIGURATION SET PREVIEW_FEATURES = ON;
-- Batch 2: Create all tables
CREATE TABLE rag_chunks (
id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
source NVARCHAR(500) NOT NULL,
chunk_index INT NOT NULL,
content NVARCHAR(MAX) NOT NULL,
file_hash NVARCHAR(64),
ingested_at DATETIME2 DEFAULT GETUTCDATE(),
embedding VECTOR(768) -- native SQL Server 2025 type
);
CREATE INDEX idx_rag_source ON rag_chunks(source);
CREATE TABLE rag_staleness (
doc_name NVARCHAR(500) PRIMARY KEY,
file_hash NVARCHAR(64) NOT NULL,
chunk_count INT,
ingested_at DATETIME2 DEFAULT GETUTCDATE(),
version INT DEFAULT 1
);
CREATE TABLE rag_chunk_registry (
doc_name NVARCHAR(500) NOT NULL,
chunk_hash NVARCHAR(64) NOT NULL,
chunk_id NVARCHAR(64) NOT NULL,
PRIMARY KEY (doc_name, chunk_hash)
);
CREATE TABLE rag_sessions (
session_id NVARCHAR(100) PRIMARY KEY,
created_at DATETIME2 DEFAULT GETUTCDATE(),
updated_at DATETIME2 DEFAULT GETUTCDATE(),
model NVARCHAR(200),
embed_model NVARCHAR(200),
turn_count INT DEFAULT 0
);
CREATE TABLE rag_turns (
id INT IDENTITY(1,1) PRIMARY KEY,
session_id NVARCHAR(100) NOT NULL REFERENCES rag_sessions(session_id),
role NVARCHAR(20) NOT NULL CHECK (role IN ('user','assistant')),
content NVARCHAR(MAX) NOT NULL,
sources NVARCHAR(MAX),
created_at DATETIME2 DEFAULT GETUTCDATE()
);
CREATE INDEX idx_rag_turns_session ON rag_turns(session_id, created_at);
-- Batch 3: Must run AFTER Batch 2 commits
-- Cannot run inside a transaction — this is a known SQL Server 2025 preview constraint
CREATE VECTOR INDEX idx_rag_embedding
ON rag_chunks(embedding)
WITH (METRIC = 'COSINE');
Dependencias de Python
En la etapa de dependencias de Python, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar que los operadores puedan auditar sin necesidad de leer todo el grafo. Separe la construcción del cliente del bucle de mensajes para que sea posible cambiar los proveedores sin tener que reescribir la máquina de estados de la conversación.
uv add google-genai pypdf pyodbc python-dotenv huggingface_hub fpdf2
Configuración de conexión (Celda 2)
En la fase de Configuración de Conexión Célula 2, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Cite los pasajes que realmente sustentan la respuesta. Sin citaciones, los operadores no pueden distinguir entre alucinaciones y brechas en el indexado.
import pyodbc
SQL_SERVER = "YOUR_SERVER_NAME" # from SSMS title bar
SQL_DATABASE = "local_rag"
SQL_CONN_STR = (
f"DRIVER={{ODBC Driver 17 for SQL Server}};"
f"SERVER={SQL_SERVER};"
f"DATABASE={SQL_DATABASE};"
f"Trusted_Connection=yes;" # Windows Authentication - no password needed
)
def get_conn():
return pyodbc.connect(SQL_CONN_STR)
def get_conn_autocommit():
"""Required for CREATE/DROP VECTOR INDEX - cannot run inside a transaction."""
return pyodbc.connect(SQL_CONN_STR, autocommit=True)
Almacenamiento de Embeddings (Célula 6)
En la etapa 6 de Almacenamiento de Embeddings, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado. Cite los pasajes que realmente sustentan la respuesta. Sin citas, los operadores no pueden distinguir entre alucinaciones y fallos en el indexado. En la etapa 6 de Almacenamiento de Embeddings, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de la versión de demostración al entorno compartido.
import json
def vec_to_json(embedding: list[float]) -> str:
return json.dumps(embedding) # '[0.12, 0.34, ...]'
def store_in_sqlserver(chunks, embeddings, doc_name) -> int:
drop_vector_index() # must drop before any INSERT
conn = get_conn()
cur = conn.cursor()
sql = """
INSERT INTO rag_chunks (source, chunk_index, content, embedding)
VALUES (?, ?, ?, CAST(? AS VECTOR(768)))
"""
for i, (chunk, emb) in enumerate(zip(chunks, embeddings)):
cur.setinputsizes([
(_pyodbc.SQL_WVARCHAR, 500, 0),
_pyodbc.SQL_INTEGER,
(_pyodbc.SQL_WVARCHAR, 0, 0),
(_pyodbc.SQL_VARCHAR, 0, 0), # ← must be VARCHAR, not NTEXT
])
cur.execute(sql, (doc_name, i, chunk, vec_to_json(emb)))
conn.commit()
conn.close()
create_vector_index() # recreate after all inserts
return len(chunks)
Recuperación con VECTOR_DISTANCE (Celda 6 continuada)
Al trabajar en la etapa de Celda de Recuperación con VECTORDISTANCE, anote primero los requisitos: entradas necesarias, señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación ayuda a mantener honestos los cambios posteriores en el código. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el sistema. Mida la tasa de recuperación con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.
def retrieve_context(query: str) -> list[dict]:
query_embedding = embed_query(query)
query_vec_json = vec_to_json(query_embedding)
conn = get_conn()
cur = conn.cursor()
cur.setinputsizes([(_pyodbc.SQL_VARCHAR, 0, 0)]) # force VARCHAR for vector param
cur.execute(f"""
SELECT TOP ({TOP_K})
content, source, chunk_index, ingested_at,
VECTOR_DISTANCE('cosine', embedding, CAST(? AS VECTOR(768))) AS distance
FROM rag_chunks
ORDER BY distance ASC;
""", (query_vec_json,))
rows = cur.fetchall()
conn.close()
return [{"text": r[0], "source": r[1], "chunk_index": r[2],
"ingested_at": str(r[3]) if r[3] else "",
"score": round(1 - float(r[4]), 4)} for r in rows]
Puntos críticos de SQL Server 2025 con vectores: todos los que hemos encontrado
Al trabajar con la etapa Vector de SQL Server 2025, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Documente tanto el camino óptimo como el de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no de mejoras posteriores. Mida la tasa de recuperación con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.
Problema 1: tipo de objeto desconocido ‘VECTOR’ en la instrucción CREATE
Al trabajar en la etapa de “Gotcha 1: Objeto desconocido”, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación garantiza que los cambios posteriores en el código sean transparentes. Prefiera unidades pequeñas y probables a scripts extensos. Cuando un paso falla, el fallo debe referirse a una sola responsabilidad y no a un proceso complicado. Mida el rendimiento en un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente. Al trabajar en la etapa de “Gotcha 1: Objeto desconocido”, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación garantiza que los cambios posteriores en el código sean transparentes. Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita sorpresas en las facturas cuando se pasa de entornos de demostración a entornos compartidos.
Msg 343, Level 15: Unknown object type 'VECTOR' used in CREATE, DROP, or ALTER statement.
Trampa 2: La clave primaria debe ser una columna INT de 4 bytes
La etapa relacionada con la clave primaria en “Trampa 2” funciona mejor si se trata como una superficie medible. Capture un registro de éxito, un caso de fallo y la nota de reversión antes de ampliar el alcance. Mantenga la configuración fuera del código de la aplicación; los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el sistema. Separe la política de particionamiento de la política de recuperación: cambiar una no debe obligar a reescribir la otra cuando cambien las métricas de calidad.
Msg 42217: Table must have a clustered primary key on a single 4 byte INT column to create a vector index.
-- ❌ Does not work with vector index
id UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID()
-- ✅ Required
id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
Trampa 3: No se puede INSERTAR/DELETE/UPDATE mientras exista un índice vectorial
The Gotcha 3 Cannot INSERT stage funciona mejor cuando se trata como una superficie medible. Capture un registro exitoso, un caso de fallo y la nota de reversión antes de ampliar el alcance. Documente tanto el camino óptimo como el camino de recuperación juntos. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Separe la política de fragmentación de la política de recuperación; cambiar una no debe obligar a reescribir la otra cuando cambian las métricas de calidad.
Msg 42231: Data modification statement failed because table 'rag_chunks' has a vector index on it.
def drop_vector_index():
conn = get_conn_autocommit() # autocommit required
conn.cursor().execute("""
IF EXISTS (
SELECT 1 FROM sys.indexes
WHERE name = 'idx_rag_embedding'
AND object_id = OBJECT_ID('rag_chunks')
)
DROP INDEX idx_rag_embedding ON rag_chunks;
""")
conn.close()
def create_vector_index():
conn = get_conn_autocommit() # autocommit required
conn.cursor().execute("""
CREATE VECTOR INDEX idx_rag_embedding
ON rag_chunks(embedding)
WITH (METRIC = 'COSINE');
""")
conn.close()
Gotcha 4 — CREATE VECTOR INDEX no puede ejecutarse dentro de una transacción
La etapa Gotcha 4 CREATE VECTOR funciona mejor cuando se trata como una superficie medible. Capture un registro ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando falla un paso, el fallo debe apuntar a una sola responsabilidad y no a un proceso complicado. Separe la política de fragmentación de la política de recuperación. Cambiar una no debe obligar a reescribir la otra cuando cambian las métricas de calidad. La etapa Gotcha 4 CREATE VECTOR funciona mejor cuando se trata como una superficie medible. Capture un registro ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Registre los tiempos y el costo en tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas cuando el proceso pasa de la demostración a entornos compartidos.
Msg 574: CREATE VECTOR INDEX statement cannot be used inside a user transaction.
# ❌ Fails — implicit transaction
conn = pyodbc.connect(SQL_CONN_STR)
conn.cursor().execute("CREATE VECTOR INDEX ...")
# ✅ Works - no transaction wrapper
conn = pyodbc.connect(SQL_CONN_STR, autocommit=True)
conn.cursor().execute("CREATE VECTOR INDEX ...")
Gotcha 5: No se permite la conversión explícita de ntext a vector
En la fase de conversión explícita de Gotcha 5, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación: los archivos de entorno, los almacenes de datos confidenciales y las banderas de funcionalidad deben estar en un lugar que los operadores puedan auditar sin necesidad de leer todo el sistema. Cite los pasajes que realmente sustentan la respuesta; sin citas, los operadores no podrán distinguir entre alucinaciones y brechas en el indexado.
Msg 529: Explicit conversion from data type ntext to vector is not allowed.
cur.setinputsizes([
(_pyodbc.SQL_WVARCHAR, 500, 0), # source — Unicode fine
_pyodbc.SQL_INTEGER, # chunk_index
(_pyodbc.SQL_WVARCHAR, 0, 0), # content — Unicode fine
(_pyodbc.SQL_VARCHAR, 0, 0), # embedding ← must be ASCII VARCHAR
])
cur.execute(sql, (doc_name, i, chunk, vec_to_json(emb)))
Gotcha 6: VECTOR_SEARCH no acepta marcadores de posición para parámetros ?
Para Gotcha 6 VECTORSEARCH, se debe planificar la etapa definiendo las entradas, el responsable de dicha etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa desde un punto de control conocido sin tener que adivinar el estado oculto. Se debe documentar tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Se deben citar los pasajes que realmente sustentan la respuesta. Sin citas, los operadores no pueden distinguir entre alucinaciones y brechas en el indexado.
Msg 102: Incorrect syntax near '('.
-- ❌ VECTOR_SEARCH with parameter placeholder — fails
FROM VECTOR_SEARCH(
TABLE = rag_chunks USING VECTOR INDEX idx_rag_embedding,
SIMILAR_TO = CAST(? AS VECTOR(768)), -- pyodbc cannot pass ? here
...
)
-- ✅ VECTOR_DISTANCE - fully parameterized, GA, works perfectly
SELECT TOP (5)
content,
VECTOR_DISTANCE('cosine', embedding, CAST(? AS VECTOR(768))) AS distance
FROM rag_chunks
ORDER BY distance ASC;
PostgreSQL vs SQL Server — Comparación directa
En la fase de PostgreSQL vs SQL Server, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado. Cite los pasajes que realmente sustentan la respuesta. Sin citaciones, los operadores no pueden distinguir entre una alucinación y una deficiencia en el indexado.
Qué añaden ambos bases de datos que ChromaDB no puede
En la etapa de “Qué añaden ambas bases de datos”, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Trate esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Cite los pasajes que realmente sustentan la respuesta. Sin citas, los operadores no pueden distinguir entre alucinaciones y brechas en el indexado.
-- PostgreSQL: Find chunks from documents belonging to a specific customer
SELECT c.content, 1 - (c.embedding <=> query_vec) AS score
FROM rag_chunks c
JOIN documents d ON d.filename = c.source
JOIN customers cu ON cu.id = d.customer_id
WHERE cu.tier = 'enterprise'
ORDER BY score DESC
LIMIT 5;
-- SQL Server: Same query, T-SQL syntax
SELECT TOP 5
c.content,
1 - VECTOR_DISTANCE('cosine', c.embedding, CAST(? AS VECTOR(768))) AS score
FROM rag_chunks c
JOIN documents d ON d.filename = c.source
JOIN customers cu ON cu.id = d.customer_id
WHERE cu.tier = 'enterprise'
ORDER BY score DESC;
Conclusión
En la fase de conclusión, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. La visibilidad temprana de los costos evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos. Cite los pasajes que realmente sustentan la respuesta; sin citas, los operadores no pueden distinguir entre alucinaciones y lagunas en el indexado.
Lista de verificación operativa
Lecturas relacionadas
- Notas prácticas: PageIndex: El framework RAG que desplazó a las bases de datos vectoriales — Guía práctica detallada de Notas prácticas: PageIndex: El framework RAG que desplazó a las bases de datos vectoriales: contratos, verificaciones y espacios para código listo para usar para los equipos que implementan este patrón.
- Notas prácticas: Base de datos vectorial para RAG (Los 10 más importantes para saber en 2026) — Guía práctica detallada de Notas prácticas: Base de datos vectorial para RAG (Los 10 más importantes para saber en 2026): contratos, verificaciones y espacios para código listo para usar para los equipos que implementan este patrón.