El Almacén de Vectores de Rememberizer simplifica el proceso de manejo de datos vectoriales, permitiéndote concentrarte en la entrada de texto y aprovechando el poder de los vectores para diversas aplicaciones como la búsqueda y el análisis de datos.
Introducción
El Almacén de Vectores Rememberizer proporciona una interfaz fácil de usar para manejar datos vectoriales mientras abstrae la complejidad de las incrustaciones vectoriales. Impulsado por PostgreSQL con la extensión pgvector, el Almacén de Vectores Rememberizer te permite trabajar directamente con texto. El servicio se encarga de dividir, vectorizar y almacenar los datos de texto, facilitándote el enfoque en la lógica central de tu aplicación.
Para una comprensión más profunda de los conceptos teóricos detrás de las incrustaciones vectoriales y las bases de datos vectoriales, consulta .
Visión Técnica
Cómo Funcionan los Almacenes de Vectores
Los Almacenes de Vectores de Rememberizer convierten texto en representaciones vectoriales de alta dimensión (embeddings) que capturan el significado semántico. Esto permite:
Búsqueda Semántica: Encontrar documentos basados en el significado en lugar de solo palabras clave
Coincidencia de Similitud: Identificar contenido conceptualmente relacionado
Recuperación Eficiente: Localizar rápidamente información relevante de grandes conjuntos de datos
Componentes Clave
Procesamiento de Documentos: El texto se divide en fragmentos de tamaño óptimo con límites superpuestos para la preservación del contexto
Vectorización: Los fragmentos se convierten en incrustaciones utilizando modelos de última generación
Indexación: Algoritmos especializados organizan vectores para una búsqueda de similitud eficiente
Procesamiento de Consultas: Las consultas de búsqueda se vectorizan y se comparan con las incrustaciones almacenadas
Arquitectura
Rememberizer implementa almacenes de vectores utilizando:
PostgreSQL con la extensión pgvector: Para almacenamiento y búsqueda de vectores eficientes
Organización basada en colecciones: Cada almacén de vectores tiene su propia colección aislada
Acceso impulsado por API: Puntos finales RESTful simples para todas las operaciones
Empezando
Creando un Almacén de Vectores
Navega a la Sección de Almacenes de Vectores en tu panel de control
Haz clic en "Crear nuevo Almacén de Vectores":
Aparecerá un formulario que te pedirá que ingreses detalles.
Completa los Detalles:
Nombre: Proporciona un nombre único para tu almacén de vectores.
Descripción: Escribe una breve descripción del almacén de vectores.
Modelo de Embedding: Selecciona el modelo que convierte texto en vectores.
Algoritmo de Indexación: Elige cómo se organizarán los vectores para la búsqueda.
Métrica de Búsqueda: Define cómo se calcula la similitud entre vectores.
Dimensión del Vector: El tamaño de las incrustaciones de vectores (típicamente 768-1536).
Envía el Formulario:
Haz clic en el botón "Crear". Recibirás una notificación de éxito, y el nuevo almacén aparecerá en tu lista de almacenes de vectores.
Opciones de Configuración
Modelos de Embedding
Modelo
Dimensiones
Descripción
Mejor Para
openai/text-embedding-3-large
1536
Modelo de embedding de alta precisión de OpenAI
Aplicaciones de producción que requieren la máxima precisión
openai/text-embedding-3-small
1536
Modelo de embedding más pequeño y rápido de OpenAI
Aplicaciones con mayores requisitos de rendimiento
Algoritmos de Indexación
Algoritmo
Descripción
Compensaciones
IVFFLAT (predeterminado)
Archivo invertido con compresión plana
Buen equilibrio entre velocidad y precisión; funciona bien para la mayoría de los conjuntos de datos
HNSW
Mundo Pequeño Navegable Jerárquico
Mejor precisión para conjuntos de datos grandes; mayores requisitos de memoria
Métricas de Búsqueda
Métrica
Descripción
Mejor Para
coseno (predeterminado)
Mide el ángulo entre vectores
Coincidencia de similitud de propósito general
producto interno (ip)
Producto punto entre vectores
Cuando la magnitud del vector es importante
L2 (Euclidiana)
Distancia en línea recta entre vectores
Cuando las relaciones espaciales son importantes
Gestión de Almacenes de Vectores
Ver y Editar Almacenes de Vectores:
Accede al panel de gestión para ver, editar o eliminar almacenes de vectores.
Visualización de Documentos:
Navega por documentos individuales y su metadatos asociados dentro de un almacén de vectores específico.
Estadísticas:
Visualiza estadísticas detalladas como el número de vectores almacenados, el rendimiento de las consultas y métricas operativas.
Gestión de Claves API
Las claves API se utilizan para autenticar y autorizar el acceso a los puntos finales de la API del Almacén de Vectores de Rememberizer. La gestión adecuada de las claves API es esencial para mantener la seguridad y la integridad de sus almacenes de vectores.
Creando Claves API
Dirígete a la página de detalles de tu Almacén de Vectores
Navega a la Sección de Gestión de Claves API:
Se puede encontrar dentro de la pestaña "Configuración"
Haz clic en "Agregar Clave API":
Aparecerá un formulario que te pedirá que ingreses los detalles.
Completa los Detalles:
Nombre: Proporciona un nombre para la clave API que te ayude a identificar su caso de uso.
Envía el Formulario:
Haz clic en el botón "Crear". La nueva clave API será generada y mostrada. Asegúrate de copiarla y almacenarla de forma segura. Esta clave se utiliza para autenticar solicitudes a ese almacén de vectores específico.
Revocación de Claves API
Si una clave API ya no es necesaria, puedes eliminarla para prevenir cualquier posible uso indebido.
Por razones de seguridad, es posible que desees rotar tus claves API periódicamente. Esto implica generar una nueva clave y revocar la antigua.
Uso de la API de Vector Store
Después de crear un Vector Store y generar una clave API, puedes interactuar con él utilizando la API REST.
Ejemplos de Código
import requests
import json
API_KEY = "your_api_key_here"
VECTOR_STORE_ID = "vs_abc123" # Reemplaza con tu ID de almacén de vectores
BASE_URL = "https://api.rememberizer.a
# Cargar un documento en el almacén de vectores
def upload_document(file_path, document_name=None):
if document_name is None:
document_name = file_path.split("/")[-1]
with open(file_path, "rb") as f:
files = {"file": (document_name, f)}
headers = {"x-api-key": API_KEY}
response = requests.post(
f"{BASE_URL}/vector-stores/{VECTOR_STORE_ID}/documents",
headers=headers,
files=files
)
if response.status_code == 201:
print(f"¡Documento '{document_name}' cargado con éxito!")
return response.json()
else:
print(f"Error al cargar el documento: {response.text}")
return None
# Cargar contenido de texto en el almacén de vectores
def upload_text(content, document_name):
headers = {
"x-api-key": API_KEY,
"Content-Type": "application/json"
}
data = {
"name": document_name,
"content": content
}
response = requests.post(
f"{BASE_URL}/vector-stores/{VECTOR_STORE_ID}/documents/text",
headers=headers,
json=data
)
if response.status_code == 201:
print(f"¡Documento de texto '{document_name}' cargado con éxito!")
return response.json()
else:
print(f"Error al cargar el texto: {response.text}")
return None
# Buscar en el almacén de vectores
def search_vector_store(query, num_results=5, prev_chunks=1, next_chunks=1):
headers = {"x-api-key": API_KEY}
params = {
"q": query,
"n": num_results,
"prev_chunks": prev_chunks,
"next_chunks": next_chunks
}
response = requests.get(
f"{BASE_URL}/vector-stores/{VECTOR_STORE_ID}/documents/search",
headers=headers,
params=params
)
if response.status_code == 200:
results = response.json()
print(f"Encontrados {len(results['matched_chunks'])} coincidencias para '{query}'")
# Imprimir el resultado principal
if results['matched_chunks']:
top_match = results['matched_chunks'][0]
print(f"Coincidencia principal (distancia: {top_match['distance']}):")
print(f"Documento: {top_match['document']['name']}")
print(f"Contenido: {top_match['matched_content']}")
return results
else:
print(f"Error al buscar: {response.text}")
return None
# Ejemplo de uso
# subir_documento("ruta/al/documento.pdf")
# upload_text("Este es un texto de muestra para ser vectorizado", "sample-document.txt")
# search_vector_store("¿Cómo funciona la similitud de vectores?")
// Cliente de API de Almacén de Vectores
class VectorStoreClient {
constructor(apiKey, vectorStoreId) {
this.apiKey = apiKey;
this.vectorStoreId = vectorStoreId;
this.baseUrl = 'https://api.rememberizer.ai/api/v1';
}
// Obtener información del almacén de vectores
async getVectorStoreInfo() {
const response = await fetch(`${this.baseUrl}/vector-stores/${this.vectorStoreId}`, {
method: 'GET',
headers: {
'x-api-key': this.apiKey
}
});
if (!response.ok) {
throw new Error(`Error al obtener información del almacén de vectores: ${response.statusText}`);
}
return response.json();
}
// Subir un documento de texto
async uploadTextDocument(name, content) {
const response = await fetch(`${this.baseUrl}/vector-stores/${this.vectorStoreId}/documents/text`, {
method: 'POST',
headers: {
'x-api-key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
content
})
});
if (!response.ok) {
throw new Error(`Error al subir el documento de texto: ${response.statusText}`);
}
return response.json();
}
// Subir un archivo
async uploadFile(file, onProgress) {
const formData = new FormData();
formData.append('file', file);
const xhr = new XMLHttpRequest();
return new Promise((resolve, reject) => {
xhr.open('POST', `${this.baseUrl}/vector-stores/${this.vectorStoreId}/documents`);
xhr.setRequestHeader('x-api-key', this.apiKey);
xhr.upload.onprogress = (event) => {
if (event.lengthComputable && onProgress) {
const percentComplete = (event.loaded / event.total) * 100;
onProgress(percentComplete);
}
};
xhr.onload = () => {
if (xhr.status === 201) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error(`Error al subir el archivo: ${xhr.statusText}`));
}
};
xhr.onerror = () => {
reject(new Error('Error de red durante la subida del archivo'));
};
xhr.send(formData);
});
}
// Buscar documentos en el almacén de vectores
async searchDocuments(query, options = {}) {
const params = new URLSearchParams({
q: query,
n: options.numResults || 10,
prev_chunks: options.prevChunks || 1,
next_chunks: options.nextChunks || 1
});
if (options.threshold) {
params.append('t', options.threshold);
}
const response = await fetch(
`${this.baseUrl}/vector-stores/${this.vectorStoreId}/documents/search?${params}`,
{
method: 'GET',
headers: {
'x-api-key': this.apiKey
}
}
);
if (!response.ok) {
throw new Error(`Error de búsqueda: ${response.statusText}`);
}
return response.json();
}
// Listar todos los documentos en el almacén de vectores
async listDocuments() {
const response = await fetch(
`${this.baseUrl}/vector-stores/${this.vectorStoreId}/documents`,
{
method: 'GET',
headers: {
'x-api-key': this.apiKey
}
}
);
if (!response.ok) {
throw new Error(`Error al listar documentos: ${response.statusText}`);
}
return response.json();
}
// Eliminar un documento
async deleteDocument(documentId) {
const response = await fetch(
`${this.baseUrl}/vector-stores/${this.vectorStoreId}/documents/${documentId}`,
{
method: 'DELETE',
headers: {
'x-api-key': this.apiKey
}
}
);
if (!response.ok) {
throw new Error(`Error al eliminar el documento: ${response.statusText}`);
}
return true;
}
}
// Ejemplo de uso
/*
const client = new VectorStoreClient('your_api_key', 'vs_abc123');
// Buscar documentos
client.searchDocuments('¿Cómo funciona la búsqueda semántica?')
.then(results => {
console.log(`Se encontraron ${results.matched_chunks.length} coincidencias`);
results.matched_chunks.forEach(match => {
console.log(`Documento: ${match.document.name}`);
console.log(`Puntuación: ${match.distance}`);
console.log(`Contenido: ${match.matched_content}`);
console.log('---');
});
})
.catch(error => console.error(error));
*/
results = client.search('¿Cuáles son las mejores prácticas para la seguridad de datos?')
puts "Encontrados #{results['matched_chunks'].length} resultados"
Mostrar el mejor resultado
if results['matched_chunks'].any?
top_match = results['matched_chunks'].first
puts "Mejor coincidencia (distancia: #{top_match['distance']}):"
puts "Documento: #{top_match['document']['name']}"
puts "Contenido: #{top_match['matched_content']}"
end
=end
</div>
<div data-gb-custom-block data-tag="tab" data-title='cURL'>
```bash
# Establece tu clave API y el ID de la tienda de vectores
API_KEY="your_api_key_here"
VECTOR_STORE_ID="vs_abc123"
BASE_URL="https://api.rememberizer.ai/api/v1"
# Obtener información del almacén de vectores
curl -X GET "${BASE_URL}/vector-stores/${VECTOR_STORE_ID}" \
-H "x-api-key: ${API_KEY}"
# Subir un documento de texto
curl -X POST "${BASE_URL}/vector-stores/${VECTOR_STORE_ID}/documents/text" \
-H "x-api-key: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "example-document.txt",
"content": "Este es un documento de muestra que será vectorizado y almacenado en la base de datos vectorial para búsqueda semántica."
}'
# Subir un archivo
curl -X POST "${BASE_URL}/vector-stores/${VECTOR_STORE_ID}/documents" \
-H "x-api-key: ${API_KEY}" \
-F "file=@/ruta/a/tu/documento.pdf"
# Buscar documentos
curl -X GET "${BASE_URL}/vector-stores/${VECTOR_STORE_ID}/documents/search?q=semantic%20search&n=5&prev_chunks=1&next_chunks=1" \
-H "x-api-key: ${API_KEY}"
# Listar todos los documentos
curl -X GET "${BASE_URL}/vector-stores/${VECTOR_STORE_ID}/documents" \
-H "x-api-key: ${API_KEY}"
# Eliminar un documento
curl -X DELETE "${BASE_URL}/vector-stores/${VECTOR_STORE_ID}/documents/123" \
-H "x-api-key: ${API_KEY}"
Consideraciones de Rendimiento
Próximamente: Diagrama de Arquitectura de Almacenamiento de Vectores
Este diagrama de arquitectura técnica ilustrará:
La arquitectura de base de PostgreSQL + pgvector
Estructuras de algoritmos de indexación (IVFFLAT vs. HNSW)
Cómo funcionan las métricas de búsqueda en el espacio vectorial (comparación visual)
Proceso de fragmentación de documentos con visualización de superposición
Consideraciones de rendimiento visualizadas a través de diferentes escalas
Optimización para Diferentes Volúmenes de Datos
Volumen de Datos
Configuración Recomendada
Notas
Pequeño (<10k documentos)
IVFFLAT, similitud coseno
La configuración simple proporciona un buen rendimiento
Medio (10k-100k documentos)
IVFFLAT, asegurar reindexación regular
Equilibrio entre velocidad de búsqueda y mantenimiento del índice
Grande (>100k documentos)
HNSW, considerar aumentar las dimensiones del vector
Mayor uso de memoria pero mantiene el rendimiento a gran escala
Estrategias de Fragmentación
El proceso de fragmentación impacta significativamente la calidad de búsqueda:
Tamaño del Fragmento: Rememberizer utiliza un tamaño de fragmento predeterminado de 1024 bytes con una superposición de 200 bytes
Fragmentos Más Pequeños (512-1024 bytes): Coincidencias más precisas, mejor para preguntas específicas
Fragmentos Más Grandes (1500-2048 bytes): Más contexto en cada coincidencia, mejor para temas más amplios
Superposición: Asegura que el contexto no se pierda en los límites de los fragmentos
Optimización de Consultas
Ventanas de Contexto: Utiliza prev_chunks y next_chunks para recuperar contenido circundante
Cantidad de Resultados: Comienza con 3-5 resultados (n parameter) y ajusta según las necesidades de precisión
Umbral: Ajusta el parámetro t para filtrar resultados por puntuación de similitud
Uso Avanzado
Reindexación
Rememberizer activa automáticamente la reindexación cuando los recuentos de vectores superan los umbrales predefinidos, pero considere la reindexación manual después de:
Subir una gran cantidad de documentos
Cambiar el modelo de incrustación
Modificar el algoritmo de indexación
Mejora de Consultas
Para obtener mejores resultados de búsqueda:
Sé específico en las consultas de búsqueda
Incluye contexto cuando sea posible
Usa lenguaje natural en lugar de palabras clave
Ajusta los parámetros según la calidad del resultado
Migrando desde Otras Bases de Datos Vectoriales
Si actualmente estás utilizando otras soluciones de bases de datos vectoriales y deseas migrar a Rememberizer Vector Store, las siguientes guías te ayudarán a transferir tus datos de manera eficiente.
Visión general de la migración
Migrar datos vectoriales implica:
Exportar datos de su base de datos vectorial de origen
Convertir los datos a un formato compatible con Rememberizer
Importar los datos en su Almacén Vectorial de Rememberizer
Verificar que la migración fue exitosa
Beneficios de Migrar a Rememberizer
Fundación PostgreSQL: Construido sobre tecnología de base de datos madura con copias de seguridad y recuperación integradas
Ecosistema Integrado: Conexión fluida con otros componentes de Rememberizer
Gestión Simplificada: Interfaz unificada para operaciones vectoriales
Seguridad Avanzada: Seguridad a nivel de fila y controles de acceso granulares
Arquitectura Escalable: Optimización del rendimiento a medida que crece su datos
Migrando de Pinecone
import os
import pinecone
import requests
import json
import time
# Configurar el cliente de Pinecone
pinecone.init(api_key="PINECONE_API_KEY", environment="PINECONE_ENV")
source_index = pinecone.Index("your-pinecone-index")
# Configurar el cliente de Rememberizer Vector Store
REMEMBERIZER_API_KEY = "your_rememberizer_api_key"
VECTOR_STORE_ID = "vs_abc123" # Tu ID de tienda de vectores de Rememberizer
BASE_URL = "https://api.rememberizer.ai/api/v1"
# 1. Configurar el tamaño del lote para la migración (ajustar según el tamaño de sus datos)
BATCH_SIZE = 100
# 2. Función para obtener vectores de Pinecone
def fetch_vectors_from_pinecone(index_name, batch_size, cursor=None):
# Utiliza la operación de lista si está disponible en tu versión de Pinecone
try:
result = source_index.list(limit=batch_size, cursor=cursor)
vectors = result.get("vectors", {})
next_cursor = result.get("cursor")
return vectors, next_cursor
except AttributeError:
# Para versiones más antiguas de Pinecone sin operación de lista
# Este es un enfoque simplificado; la implementación real depende de tu patrón de acceso a datos
query_response = source_index.query(
vector=[0] * source_index.describe_index_stats()["dimension"],
top_k=batch_size,
include_metadata=True,
include_values=True
)
return {item.id: {"id": item.id, "values": item.values, "metadata": item.metadata}
for item in query_response.matches}, None
# 3. Función para subir vectores a Rememberizer
def upload_to_rememberizer(vectors):
headers = {
"x-api-key": REMEMBERIZER_API_KEY,
"Content-Type": "application/json"
}
for vector_id, vector_data in vectors.items():
# Convertir datos de vectores de Pinecone al formato de Rememberizer
document_name = vector_data.get("metadata", {}).get("filename", f"pinecone_doc_{vector_id}")
content = vector_data.get("metadata", {}).get("text", "")
if not content:
print(f"Omitiendo {vector_id} - no se encontró contenido de texto en los metadatos")
continue
data = {
"name": document_name,
"content": content,
# Opcional: incluir metadatos adicionales
"metadata": vector_data.get("metadata", {})
}
response = requests.post(
f"{BASE_URL}/vector-stores/{VECTOR_STORE_ID}/documents/text",
headers=headers,
json=data
)
if response.status_code == 201:
print(f"¡Documento '{document_name}' subido con éxito!")
else:
print(f"Error al subir el documento {document_name}: {response.text}")
# Agregar un pequeño retraso para prevenir limitaciones de tasa
time.sleep(0.1)
# 4. Función principal de migración
def migrate_pinecone_to_rememberizer():
cursor = None
total_migrated = 0
print("Iniciando la migración de Pinecone a Rememberizer...")
while True:
vectors, cursor = fetch_vectors_from_pinecone("your-pinecone-index", BATCH_SIZE, cursor)
if not vectors:
break
print(f"Se obtuvieron {len(vectors)} vectores de Pinecone")
upload_to_rememberizer(vectors)
total_migrated += len(vectors)
print(f"Progreso: {total_migrated} vectores migrados")
if not cursor:
break
print(f"¡Migración completa! {total_migrated} vectores migrados en total a Rememberizer")
# Ejecutar la migración
# migrate_pinecone_to_rememberizer()
const { PineconeClient } = require('@pinecone-database/pinecone');
const axios = require('axios');
// Configuración de Pinecone
const pineconeApiKey = 'PINECONE_API_KEY';
const pineconeEnvironment = 'PINECONE_ENVIRONMENT';
const pineconeIndexName = 'YOUR_PINECONE_INDEX';
// Configuración de Rememberizer
const rememberizerApiKey = 'YOUR_REMEMBERIZER_API_KEY';
const vectorStoreId = 'vs_abc123';
const baseUrl = 'https://api.rememberizer.ai/api/v1';
// Configuración del tamaño del lote
const BATCH_SIZE = 100;
// Inicializar el cliente de Pinecone
async function initPinecone() {
const pinecone = new PineconeClient();
await pinecone.init({
apiKey: pineconeApiKey,
environment: pineconeEnvironment,
});
return pinecone;
}
// Obtener vectores de Pinecone
async function fetchVectorsFromPinecone(pinecone, batchSize, paginationToken = null) {
const index = pinecone.Index(pineconeIndexName);
try {
// Para versiones más nuevas de Pinecone
const listResponse = await index.list({
limit: batchSize,
paginationToken: paginationToken
});
return {
vectors: listResponse.vectors || {},
nextToken: listResponse.paginationToken
};
} catch (error) {
// Respaldo para versiones más antiguas de Pinecone
// Esto está simplificado; la implementación real depende de tu patrón de acceso a datos
const stats = await index.describeIndexStats();
const dimension = stats.dimension;
const queryResponse = await index.query({
vector: Array(dimension).fill(0),
topK: batchSize,
includeMetadata: true,
includeValues: true
});
const vectors = {};
queryResponse.matches.forEach(match => {
vectors[match.id] = {
id: match.id,
values: match.values,
metadata: match.metadata
};
});
return { vectors, nextToken: null };
}
}
// Subir vectores a Rememberizer
async function uploadToRememberizer(vectors) {
const headers = {
'x-api-key': rememberizerApiKey,
'Content-Type': 'application/json'
};
const results = [];
for (const [vectorId, vectorData] of Object.entries(vectors)) {
const documentName = vectorData.metadata?.filename || `pinecone_doc_${vectorId}`;
const content = vectorData.metadata?.text || '';
if (!content) {
console.log(`Saltando ${vectorId} - no se encontró contenido de texto en los metadatos`);
continue;
}
const data = {
name: documentName,
content: content,
// Opcional: incluir metadatos adicionales
metadata: vectorData.metadata || {}
};
try {
const response = await axios.post(
`${baseUrl}/vector-stores/${vectorStoreId}/documents/text`,
data,
{ headers }
);
if (response.status === 201) {
console.log(`¡Documento '${documentName}' subido con éxito!`);
results.push({ id: vectorId, success: true });
} else {
console.error(`Error al subir el documento ${documentName}: ${response.statusText}`);
results.push({ id: vectorId, success: false, error: response.statusText });
}
} catch (error) {
console.error(`Error al subir el documento ${documentName}: ${error.message}`);
results.push({ id: vectorId, success: false, error: error.message });
}
// Agregar un pequeño retraso para prevenir limitaciones de tasa
await new Promise(resolve => setTimeout(resolve, 100));
}
return results;
}
// Función principal de migración
async function migratePineconeToRememberizer() {
try {
console.log('Iniciando la migración de Pinecone a Rememberizer...');
const pinecone = await initPinecone();
let nextToken = null;
let totalMigrated = 0;
do {
const { vectors, nextToken: token } = await fetchVectorsFromPinecone(
pinecone,
BATCH_SIZE,
nextToken
);
nextToken = token;
if (Object.keys(vectors).length === 0) {
break;
}
console.log(`Se obtuvieron ${Object.keys(vectors).length} vectores de Pinecone`);
const results = await uploadToRememberizer(vectors);
const successCount = results.filter(r => r.success).length;
totalMigrated += successCount;
console.log(`Progreso: ${totalMigrated} vectores migrados con éxito`);
} while (nextToken);
console.log(`¡Migración completa! ${totalMigrated} vectores migrados en total a Rememberizer`);
} catch (error) {
console.error('La migración falló:', error);
}
}
// Ejecutar la migración
// migratePineconeToRememberizer();
Migrando desde Qdrant
import requests
import json
import time
from qdrant_client import QdrantClient
from qdrant_client.http import models as rest
# Configurar cliente Qdrant
QDRANT_URL = "http://localhost:6333" # o tu URL de Qdrant en la nube
QDRANT_API_KEY = "your_qdrant_api_key" # si usas Qdrant Cloud
QDRANT_COLLECTION_NAME = "your_collection"
qdrant_client = QdrantClient(
url=QDRANT_URL,
api_key=QDRANT_API_KEY # Solo para Qdrant Cloud
)
# Configurar el cliente de Rememberizer Vector Store
REMEMBERIZER_API_KEY = "your_rememberizer_api_key"
VECTOR_STORE_ID = "vs_abc123" # Tu ID de tienda de vectores de Rememberizer
BASE_URL = "https://api.rememberizer.ai/api/v1"
# Tamaño del lote para el procesamiento
BATCH_SIZE = 100
# Función para obtener puntos de Qdrant
def fetch_points_from_qdrant(collection_name, batch_size, offset=0):
try:
# Obtener información de la colección para determinar la dimensión del vector
collection_info = qdrant_client.get_collection(collection_name=collection_name)
# Desplazarse a través de los puntos
scroll_result = qdrant_client.scroll(
collection_name=collection_name,
limit=batch_size,
offset=offset,
with_payload=True,
with_vectors=True
)
points = scroll_result[0] # Tupla de (puntos, siguiente_offset)
next_offset = scroll_result[1]
return points, next_offset
except Exception as e:
print(f"Error al obtener puntos de Qdrant: {e}")
return [], None
# Función para subir vectores a Rememberizer
def upload_to_rememberizer(points):
headers = {
"x-api-key": REMEMBERIZER_API_KEY,
"Content-Type": "application/json"
}
results = []
for point in points:
# Extraer datos del punto de Qdrant
point_id = point.id
metadata = point.payload
text_content = metadata.get("text", "")
document_name = metadata.get("filename", f"qdrant_doc_{point_id}")
if not text_content:
print(f"Omitiendo {point_id} - no se encontró contenido de texto en la carga")
continue
data = {
"name": document_name,
"content": text_content,
# Opcional: incluir metadatos adicionales
"metadata": metadata
}
try:
response = requests.post(
f"{BASE_URL}/vector-stores/{VECTOR_STORE_ID}/documents/text",
headers=headers,
json=data
)
if response.status_code == 201:
print(f"¡Documento '{document_name}' subido con éxito!")
results.append({"id": point_id, "success": True})
else:
print(f"Error al subir el documento {document_name}: {response.text}")
results.append({"id": point_id, "success": False, "error": response.text})
except Exception as e:
print(f"Excepción al subir el documento {document_name}: {str(e)}")
results.append({"id": point_id, "success": False, "error": str(e)})
# Agregar un pequeño retraso para prevenir limitaciones de tasa
time.sleep(0.1)
return results
# Función principal de migración
def migrate_qdrant_to_rememberizer():
offset = None
total_migrated = 0
print("Iniciando la migración de Qdrant a Rememberizer...")
while True:
points, next_offset = fetch_points_from_qdrant(
QDRANT_COLLECTION_NAME,
BATCH_SIZE,
offset
)
if not points:
break
print(f"Se obtuvieron {len(points)} puntos de Qdrant")
results = upload_to_rememberizer(points)
success_count = sum(1 for r in results if r.get("success", False))
total_migrated += success_count
print(f"Progreso: {total_migrated} puntos migrados con éxito")
if next_offset is None:
break
offset = next_offset
print(f"¡Migración completa! {total_migrated} puntos migrados en total a Rememberizer")
# Ejecutar la migración
# migrate_qdrant_to_rememberizer()
const { QdrantClient } = require('@qdrant/js-client-rest');
const axios = require('axios');
// Configuración de Qdrant
const qdrantUrl = 'http://localhost:6333'; // o tu URL de Qdrant en la nube
const qdrantApiKey = 'your_qdrant_api_key'; // si usas Qdrant Cloud
const qdrantCollectionName = 'your_collection';
// Configuración de Rememberizer
const rememberizerApiKey = 'YOUR_REMEMBERIZER_API_KEY';
const vectorStoreId = 'vs_abc123';
const baseUrl = 'https://api.rememberizer.ai/api/v1';
// Configuración del tamaño del lote
const BATCH_SIZE = 100;
// Inicializar cliente de Qdrant
const qdrantClient = new QdrantClient({
url: qdrantUrl,
apiKey: qdrantApiKey // Solo para Qdrant Cloud
});
// Obtener puntos de Qdrant
async function fetchPointsFromQdrant(collectionName, batchSize, offset = 0) {
try {
// Obtener información de la colección
const collectionInfo = await qdrantClient.getCollection(collectionName);
// Desplazarse a través de los puntos
const scrollResult = await qdrantClient.scroll(collectionName, {
limit: batchSize,
offset: offset,
with_payload: true,
with_vectors: true
});
return {
points: scrollResult.points,
nextOffset: scrollResult.next_page_offset
};
} catch (error) {
console.error(`Error al obtener puntos de Qdrant: ${error.message}`);
return { points: [], nextOffset: null };
}
}
// Subir vectores a Rememberizer
async function uploadToRememberizer(points) {
const headers = {
'x-api-key': rememberizerApiKey,
'Content-Type': 'application/json'
};
const results = [];
for (const point of points) {
// Extraer datos del punto de Qdrant
const pointId = point.id;
const metadata = point.payload || {};
const textContent = metadata.text || '';
const documentName = metadata.filename || `qdrant_doc_${pointId}`;
if (!textContent) {
console.log(`Saltando ${pointId} - no se encontró contenido de texto en la carga útil`);
continue;
}
const data = {
name: documentName,
content: textContent,
// Opcional: incluir metadatos adicionales
metadata: metadata
};
try {
const response = await axios.post(
`${baseUrl}/vector-stores/${vectorStoreId}/documents/text`,
data,
{ headers }
);
if (response.status === 201) {
console.log(`¡Documento '${documentName}' subido con éxito!`);
results.push({ id: pointId, success: true });
} else {
console.error(`Error al subir el documento ${documentName}: ${response.statusText}`);
results.push({ id: pointId, success: false, error: response.statusText });
}
} catch (error) {
console.error(`Error al subir el documento ${documentName}: ${error.message}`);
results.push({ id: pointId, success: false, error: error.message });
}
// Agregar un pequeño retraso para evitar limitaciones de tasa
await new Promise(resolve => setTimeout(resolve, 100));
}
return results;
}
// Función principal de migración
async function migrateQdrantToRememberizer() {
try {
console.log('Iniciando la migración de Qdrant a Rememberizer...');
let offset = null;
let totalMigrated = 0;
do {
const { points, nextOffset } = await fetchPointsFromQdrant(
qdrantCollectionName,
BATCH_SIZE,
offset
);
offset = nextOffset;
if (points.length === 0) {
break;
}
console.log(`Se obtuvieron ${points.length} puntos de Qdrant`);
const results = await uploadToRememberizer(points);
const successCount = results.filter(r => r.success).length;
totalMigrated += successCount;
console.log(`Progreso: ${totalMigrated} puntos migrados con éxito`);
} while (offset !== null);
console.log(`¡Migración completa! ${totalMigrated} puntos migrados en total a Rememberizer`);
} catch (error) {
console.error('La migración falló:', error);
}
}
// Ejecutar la migración
// migrateQdrantToRememberizer();
Migrando de Supabase pgvector
Si ya estás utilizando Supabase con pgvector, la migración a Rememberizer es particularmente sencilla ya que ambos utilizan PostgreSQL con la extensión pgvector.
import psycopg2
import requests
import json
import time
import os
from dotenv import load_dotenv
# Cargar variables de entorno
load_dotenv()
# Configuración de PostgreSQL de Supabase
SUPABASE_DB_HOST = os.getenv("SUPABASE_DB_HOST")
SUPABASE_DB_PORT = os.getenv("SUPABASE_DB_PORT", "5432")
SUPABASE_DB_NAME = os.getenv("SUPABASE_DB_NAME")
SUPABASE_DB_USER = os.getenv("SUPABASE_DB_USER")
SUPABASE_DB_PASSWORD = os.getenv("SUPABASE_DB_PASSWORD")
SUPABASE_VECTOR_TABLE = os.getenv("SUPABASE_VECTOR_TABLE", "documents")
# Configuración de Rememberizer
REMEMBERIZER_API_KEY = os.getenv("REMEMBERIZER_API_KEY")
VECTOR_STORE_ID = os.getenv("VECTOR_STORE_ID") # p. ej., "vs_abc123"
BASE_URL = "https://api.rememberizer.ai/api/v1"
# Tamaño del lote para el procesamiento
BATCH_SIZE = 100
# Conectar a Supabase PostgreSQL
def connect_to_supabase():
try:
conn = psycopg2.connect(
host=SUPABASE_DB_HOST,
port=SUPABASE_DB_PORT,
dbname=SUPABASE_DB_NAME,
user=SUPABASE_DB_USER,
password=SUPABASE_DB_PASSWORD
)
return conn
except Exception as e:
print(f"Error al conectar a Supabase PostgreSQL: {e}")
return None
# Obtener documentos de Supabase pgvector
def fetch_documents_from_supabase(conn, batch_size, offset=0):
try:
cursor = conn.cursor()
# Ajusta esta consulta según la estructura de tu tabla
query = f"""
SELECT id, content, metadata, embedding
FROM {SUPABASE_VECTOR_TABLE}
ORDER BY id
LIMIT %s OFFSET %s
"""
cursor.execute(query, (batch_size, offset))
documents = cursor.fetchall()
cursor.close()
return documents
except Exception as e:
print(f"Error al obtener documentos de Supabase: {e}")
return []
# Cargar documentos a Rememberizer
def upload_to_rememberizer(documents):
headers = {
"x-api-key": REMEMBERIZER_API_KEY,
"Content-Type": "application/json"
}
results = []
for doc in documents:
doc_id, content, metadata, embedding = doc
# Analizar metadatos si se almacenan como cadena JSON
if isinstance(metadata, str):
try:
metadata = json.loads(metadata)
except:
metadata = {}
elif metadata is None:
metadata = {}
document_name = metadata.get("filename", f"supabase_doc_{doc_id}")
if not content:
print(f"Omitiendo {doc_id} - no se encontró contenido")
continue
data = {
"name": document_name,
"content": content,
"metadata": metadata
}
try:
response = requests.post(
f"{BASE_URL}/vector-stores/{VECTOR_STORE_ID}/documents/text",
headers=headers,
json=data
)
if response.status_code == 201:
print(f"¡Documento '{document_name}' cargado con éxito!")
results.append({"id": doc_id, "success": True})
else:
print(f"Error al cargar el documento {document_name}: {response.text}")
results.append({"id": doc_id, "success": False, "error": response.text})
except Exception as e:
print(f"Excepción al cargar el documento {document_name}: {str(e)}")
results.append({"id": doc_id, "success": False, "error": str(e)})
# Agregar un pequeño retraso para prevenir limitaciones de tasa
time.sleep(0.1)
return results
# Función principal de migración
def migrate_supabase_to_rememberizer():
conn = connect_to_supabase()
if not conn:
print("Error al conectar a Supabase. Abortando la migración.")
return
offset = 0
total_migrated = 0
print("Iniciando la migración de Supabase pgvector a Rememberizer...")
try:
while True:
documents = fetch_documents_from_supabase(conn, BATCH_SIZE, offset)
if not documents:
break
print(f"Se obtuvieron {len(documents)} documentos de Supabase")
results = upload_to_rememberizer(documents)
success_count = sum(1 for r in results if r.get("success", False))
total_migrated += success_count
print(f"Progreso: {total_migrated} documentos migrados con éxito")
offset += BATCH_SIZE
finally:
conn.close()
print(f"¡Migración completa! {total_migrated} documentos migrados en total a Rememberizer")
# Ejecutar la migración
# migrate_supabase_to_rememberizer()
const { Pool } = require('pg');
const axios = require('axios');
require('dotenv').config();
// Configuración de PostgreSQL de Supabase
const supabasePool = new Pool({
host: process.env.SUPABASE_DB_HOST,
port: process.env.SUPABASE_DB_PORT || 5432,
database: process.env.SUPABASE_DB_NAME,
user: process.env.SUPABASE_DB_USER,
password: process.env.SUPABASE_DB_PASSWORD,
ssl: {
rejectUnauthorized: false
}
});
const supabaseVectorTable = process.env.SUPABASE_VECTOR_TABLE || 'documents';
// Configuración de Rememberizer
const rememberizerApiKey = process.env.REMEMBERIZER_API_KEY;
const vectorStoreId = process.env.VECTOR_STORE_ID; // por ejemplo, "vs_abc123"
const baseUrl = 'https://api.rememberizer.ai/api/v1';
// Configuración del tamaño del lote
const BATCH_SIZE = 100;
// Obtener documentos de Supabase pgvector
async function fetchDocumentsFromSupabase(batchSize, offset = 0) {
try {
// Ajusta esta consulta según la estructura de tu tabla
const query = `
SELECT id, content, metadata, embedding
FROM ${supabaseVectorTable}
ORDER BY id
LIMIT $1 OFFSET $2
`;
const result = await supabasePool.query(query, [batchSize, offset]);
return result.rows;
} catch (error) {
console.error(`Error al obtener documentos de Supabase: ${error.message}`);
return [];
}
}
// Subir documentos a Rememberizer
async function uploadToRememberizer(documents) {
const headers = {
'x-api-key': rememberizerApiKey,
'Content-Type': 'application/json'
};
const results = [];
for (const doc of documents) {
// Analizar metadata si está almacenada como cadena JSON
let metadata = doc.metadata;
if (typeof metadata === 'string') {
try {
metadata = JSON.parse(metadata);
} catch (e) {
metadata = {};
}
} else if (metadata === null) {
metadata = {};
}
const documentName = metadata.filename || `supabase_doc_${doc.id}`;
if (!doc.content) {
console.log(`Saltando ${doc.id} - no se encontró contenido`);
continue;
}
const data = {
name: documentName,
content: doc.content,
metadata: metadata
};
try {
const response = await axios.post(
`${baseUrl}/vector-stores/${vectorStoreId}/documents/text`,
data,
{ headers }
);
if (response.status === 201) {
console.log(`¡Documento '${documentName}' subido exitosamente!`);
results.push({ id: doc.id, success: true });
} else {
console.error(`Error al subir el documento ${documentName}: ${response.statusText}`);
results.push({ id: doc.id, success: false, error: response.statusText });
}
} catch (error) {
console.error(`Error al subir el documento ${documentName}: ${error.message}`);
results.push({ id: doc.id, success: false, error: error.message });
}
// Agregar un pequeño retraso para evitar limitaciones de tasa
await new Promise(resolve => setTimeout(resolve, 100));
}
return results;
}
// Función principal de migración
async function migrateSupabaseToRememberizer() {
try {
console.log('Iniciando la migración de Supabase pgvector a Rememberizer...');
let offset = 0;
let totalMigrated = 0;
while (true) {
const documents = await fetchDocumentsFromSupabase(BATCH_SIZE, offset);
if (documents.length === 0) {
break;
}
console.log(`Se obtuvieron ${documents.length} documentos de Supabase`);
const results = await uploadToRememberizer(documents);
const successCount = results.filter(r => r.success).length;
totalMigrated += successCount;
console.log(`Progreso: ${totalMigrated} documentos migrados exitosamente`);
offset += BATCH_SIZE;
}
console.log(`¡Migración completa! ${totalMigrated} documentos migrados en total a Rememberizer`);
} catch (error) {
console.error('La migración falló:', error);
} finally {
await supabasePool.end();
}
}
// Ejecutar la migración
// migrateSupabaseToRememberizer();
Mejores Prácticas de Migración
Sigue estas recomendaciones para una migración exitosa:
Planifica con Anticipación:
Estima el volumen de datos y el tiempo requerido para la migración
Programa la migración durante períodos de baja actividad
Aumenta el espacio en disco antes de comenzar migraciones grandes
Prueba Primero:
Crea un almacén de vectores de prueba en Rememberizer
Migra un pequeño subconjunto de datos (100-1000 vectores)
Verifica la funcionalidad de búsqueda con consultas clave
Validación de Datos:
Compara el conteo de documentos antes y después de la migración
Ejecuta consultas de referencia para asegurar resultados similares
Valida que los metadatos se conserven correctamente
Optimiza para el Rendimiento:
Utiliza operaciones por lotes para mayor eficiencia
Considera la colocación geográfica de las bases de datos de origen y destino
Monitorea los límites de tasa de la API y ajusta los tamaños de los lotes en consecuencia
Pasos Post-Migración:
Verifica la creación del índice en Rememberizer
Actualiza las configuraciones de la aplicación para apuntar al nuevo almacén de vectores
Mantén la base de datos de origen como respaldo hasta que la migración sea verificada
Asegúrate de manejar las claves de API de manera segura y seguir las mejores prácticas para la gestión de claves de API.
Para referencia detallada de la API y documentación de puntos finales, visita la página de .