Saltar a contenido

Búsqueda vectorial

Solo en PostgreSQL, sobre pgvector, y solo para las columnas declaradas con vector_column().

Un método por columna

Cada vector_column() genera el suyo: find_similar_by_<columna>.

resultados = public_sync_api.post.find_similar_by_embedding(
    text='cómo desplegar en producción',   # se codifica con el encoder declarado
    limit=10,
    metric='cosine',                       # 'l2' | 'cosine' | 'inner_product'
    threshold=0.5,                         # solo resultados por debajo de esa distancia
    includes=['autor'],
)

for r in resultados:
    print(r['distance'], r['record'].titulo)

Devuelve una lista de diccionarios con dos claves:

Clave Qué es
record El DTO <Modelo>Read de la fila
distance La distancia al vector de búsqueda — menor es más parecido

Con un vector ya calculado

resultados = public_sync_api.post.find_similar_by_embedding(
    vector=[0.12, -0.04, ...],   # tantas dimensiones como declare la columna
    limit=5,
)

text y vector son excluyentes: hay que pasar uno, y solo uno.

La métrica

Las funciones de pgvector devuelven distancia, no similitud:

Métrica Rango Lectura
cosine [0, 2] 0 = idénticos, 1 = ortogonales, 2 = opuestos
l2 [0, ∞) Distancia euclídea. Depende de la magnitud de los vectores
inner_product — Producto interno negado: menor valor = más parecido

Con metric='cosine', un threshold=0.5 deja pasar lo que se parezca en torno a un 75 % o más.

Combinar con filtros

El método acepta includes, rls y session. Si además necesitas filtrar por columnas, usa el filtro de la columna vectorial en find_many, que ordena por distancia:

public_sync_api.post.find_many(
    embedding='cómo desplegar en producción',   # búsqueda por similitud
    estado='publicado',                         # y filtro normal
    limit=10,
)

Las columnas vectoriales no se cargan por defecto

Un vector de 768 dimensiones en cada fila de un listado es mucho tráfico para nada, así que se difieren:

posts = public_sync_api.post.find_many(limit=10)                        # sin embedding
posts = public_sync_api.post.find_many(limit=10, include_vectors=True)  # con él

Al escribir

Si la columna declara source, el cliente codifica solo: escribes el texto y el vector se calcula al guardar. Eso implica cargar el modelo de embeddings en el proceso que escribe, así que la primera escritura de un proceso es más lenta.