Inicio / Artículos / Versión 0.7.0 de una base de datos vectorial: IVF-RaBitQ, DiskANN con io_uring y una biblioteca un 40 % más pequeña

Versión 0.7.0 de una base de datos vectorial: IVF-RaBitQ, DiskANN con io_uring y una biblioteca un 40 % más pequeña

Un repaso a la versión 0.7.0 de una base de datos vectorial embebible: búsqueda local en el espacio de trabajo, integración con ReMe, índices cuantizados y en disco, wheels musllinux y DocIterator.

1364 palabras

La versión 0.7.0 es una entrega amplia de esta base de datos vectorial embebible. Cambian cuatro frentes: el ecosistema que la rodea, los algoritmos de índice, la forma de desplegar la biblioteca y la comodidad en el día a día. Lo más destacado es un CLI de búsqueda en el espacio de trabajo llamado zvec-grep, una integración con ReMe, IVF-RaBitQ, un DiskANN listo para producción con io_uring y soporte para ARM64, selección de AVX2 y AVX512 en tiempo de ejecución para RaBitQ, una biblioteca dinámica alrededor de un 40 % más pequeña, wheels musllinux, binarios del SDK precompilados desde CI, DocIterator y un tokenizador de N-gramas para la búsqueda de texto completo. Las notas completas están en la página de la release del proyecto en GitHub para la etiqueta v0.7.0.

Ecosistema

zg: búsqueda local en el espacio de trabajo

La nueva herramienta de línea de comandos zvec-grep busca en un espacio de trabajo sin salir de tu máquina. Detrás de una sola interfaz hay tres capas:

  • ripgrep, para coincidencias rápidas literales y con regex
  • BM25 con jieba, para el ranking léxico
  • búsqueda vectorial HNSW, para consultas semánticas

Está pensada tanto para personas como para agentes de IA, y puede engancharse a flujos de agentes mediante MCP. Quien monte RAG sobre una base de código, la memoria de un agente o documentación tiene un único comando, zg, en lugar de varias herramientas cosidas entre sí.

Integración con ReMe

La base de datos funciona ahora como backend de almacenamiento de archivos para ReMe, un kit de gestión de memoria para agentes. ReMe la usa para búsqueda aproximada de vecinos más cercanos con HNSW dentro del propio proceso sobre los archivos de memoria del agente, de modo que las aplicaciones obtienen recuperación vectorial de baja latencia sin lanzar un proceso de base de datos aparte.

Algoritmos de índice

IVF-RaBitQ

RaBitQ llegó primero a los índices HNSW y Flat en versiones anteriores. En la 0.7.0 alcanza los índices IVF y ofrece otra forma de ceder un poco de recall a cambio de un gran ahorro de memoria con datos a escala de miles de millones.

Un índice IVF-RaBitQ agrupa los vectores en listas invertidas, y cada lista se cuantiza con RaBitQ a más o menos un bit por dimensión. El índice sigue siendo compacto, admite reordenación mediante un refiner y filtrado escalar, y usa la misma API Query de siempre. Para cargas que encajan en el perfil IVF, es decir, grandes trabajos por lotes o búsquedas con poca memoria, suele ser la opción más barata.

Despacho SIMD para RaBitQ

RaBitQ elige ahora su ruta de código más rápida en tiempo de ejecución. Una CPU con AVX512 usa los kernels AVX512; si no, se usa AVX2, y el código escalar queda como último recurso. Así, un mismo binario aprovecha el hardware nuevo sin compilaciones separadas ni flags del compilador.

DiskANN, listo para producción

DiskANN fue la gran novedad de la v0.5.0. Esta versión lo hace más rápido, más robusto y disponible en más plataformas.

Backend io_uring. En Linux 5.3 y posteriores, DiskANN prefiere io_uring a libaio. Llama directamente al kernel, así que la compilación no necesita liburing. El orden de respaldo es io_uring, luego libaio y luego pread, y cuando falta un backend el rendimiento baja de forma ordenada en vez de fallar.

E/S asíncrona solapada y beam dinámico. La búsqueda por haz solapa ahora el trabajo de CPU con las lecturas de disco y ajusta el ancho del haz sobre la marcha para mantener ocupada la E/S sin inundar el disco de peticiones. Las cargas limitadas por disco ganan en latencia. Una corrección relacionada en la deduplicación del filtro de visitas elimina inserciones de candidatos redundantes.

ARM64. DiskANN compila y funciona en Linux ARM64 y en Macs con Apple Silicon. En macOS usa pread síncrono con F_NOCACHE y la lectura anticipada desactivada, mientras que la CI en Linux prueba todos los backends. Los casos límite, como lecturas parciales o cortas, EINTR y el paso de un backend a otro, se gestionan de forma segura.

Introspección. El backend de E/S activo se cachea y es visible desde las API de C, C++ y Python, así que se puede comprobar en tiempo de ejecución si las consultas usan io_uring, libaio o pread.

Mejor calidad del grafo

Vamana incorpora una construcción opcional en dos pasadas. Una primera pasada rápida genera un grafo aproximado y la segunda lo refina, un buen trato cuando el recall importa más que el tiempo de construcción.

HNSW también puede construir su grafo a partir de los vectores originales sin procesar que aporta un proveedor de índice, aunque el índice guarde para la búsqueda una copia cuantizada o transformada. La construcción del grafo trabaja con precisión completa, así que la calidad mejora, y la memoria en las consultas sigue siendo reducida.

Despliegue

Una biblioteca dinámica más ligera

Las bibliotecas compartidas precompiladas de C y C++ son alrededor de un 40 % más pequeñas en macOS ARM64. Por ejemplo, libzvec_c_api.dylib pasa de unos 37 MB a 22 MB. Las API públicas no cambian, la velocidad de las consultas no se resiente y el código central de búsqueda se sigue compilando con -O3.

El ahorro viene de tres cambios:

  • compilar con -ffunction-sections -fdata-sections y dejar que el enlazador descarte el código que no se usa
  • exportar solo los símbolos públicos propios de la biblioteca, con -exported_symbols_list o --version-script
  • eliminar protobuf del árbol de dependencias por completo

Los manifiestos de las colecciones siguen siendo compatibles byte a byte con el formato protobuf anterior. Ahora la serialización corre a cargo de un pequeño codificador y decodificador internos.

Wheels musllinux

Hay wheels musllinux oficiales para x86_64 y ARM64, de modo que se instala sin problemas en Alpine Linux y otros sistemas basados en musl. Para lograrlo hicieron falta algunos arreglos específicos de musl. Los búferes SIMD grandes salieron de la pila de hilo predeterminada de 128 KB, y el logger pasó a ser un singleton de Meyers para evitar registros duplicados de __cxa_atexit entre bibliotecas compartidas.

Pipeline de SDK precompilados

Un workflow de GitHub Release genera ahora paquetes de SDK para C y C++ listos para usar. Las etiquetas alpha, beta y RC se marcan automáticamente como prerelease. Antes de subir nada, cada paquete pasa una prueba de humo. Los runners de escritorio y de musl compilan pequeños programas de prueba en C y C++ y los ejecutan contra el SDK preparado. En Android la comprobación es una compilación cruzada que tiene que enlazar, y en iOS, una revisión de los dos slices del XCFramework y de la estructura de cabeceras.

Usabilidad

DocIterator

Quien trabaja con memoria de agentes o ETL a menudo quiere recorrer todos los documentos de una colección sin vector de consulta. DocIterator resuelve eso. Al crearse toma una instantánea de los segmentos actuales y del bitmap de borrados, así que las escrituras y borrados posteriores no alteran el recorrido. Funciona desde C++, C y Python, y un control de admisión impide que el recorrido y las operaciones de mantenimiento se solapen.

En Python:

with collection.iter_docs(
    output_fields=["title"],   # only fetch needed scalar fields; primary key is always returned
    include_vector=False,
) as docs:
    for doc in docs:
        print(doc.id, doc.fields["title"])

Tokenizador de N-gramas para FTS

La búsqueda de texto completo estrena un tokenizador de N-gramas. Indexa secuencias de caracteres solapadas de longitud n y no tiene en cuenta los límites entre palabras. Encaja con la búsqueda de frases y subcadenas en textos cortos, identificadores de código y líneas de log, y con idiomas en los que es ambiguo dónde acaba una palabra. n se fija mediante extra_params al crear el índice FTS.

Rendimiento

  • DiskANN solapa trabajo de CPU y de disco en la búsqueda por haz, y el ancho de haz dinámico mantiene la concurrencia en un nivel razonable.
  • RaBitQ elige los kernels AVX512 o AVX2 en tiempo de ejecución, sin necesidad de un único objetivo de compilación fijo.
  • La eliminación de código muerto en la biblioteca dinámica produce binarios más pequeños, que cargan antes y ocupan menos memoria residente, mientras los kernels de búsqueda siguen en -O3.

Otros cambios y correcciones

  • API de Python: nuevo collection.close(), validación de consultas FTS vacías, de topk y de nombres de campo desconocidos, y un manejo más limpio de los vectores de NumPy.
  • API de C++: los nombres públicos siguen ahora snake_case.
  • API de C: se acabó la falsa cadena de versión 0.2.1 cuando faltan las etiquetas de git.
  • Corrección de la búsqueda: los filtros se validan, el estado obsoleto del filtro se reinicia y ya no se pueden saltar los invariantes del heap.
  • K-Means: corregidos los pesos de muestreo en K-MC2 esférico y la normalización de centroides.
  • Almacenamiento: se gestionan los límites de lotes escalares que no coinciden, el último fragmento IPC del almacén mmap se procesa bien, los segmentos de escritura que solo contienen borrados se persisten y los directorios de segmentos huérfanos o retirados se limpian durante la recuperación tras un fallo y en Windows.
  • Colecciones: las lecturas y escrituras siguen funcionando mientras se ejecuta Optimize.
  • Vamana: se respetan las métricas de consulta asimétricas.
  • Compilación y CI: menos exportaciones de DLL en Windows, eliminadas marcas obsoletas de parches de submódulos, dependencias de GitHub Actions actualizadas y comprobaciones que se lanzan cuando un PR en borrador pasa a estar listo.

El trabajo previsto sobre escalabilidad del almacenamiento, más algoritmos y SDK para otros lenguajes se sigue en la issue pública de la hoja de ruta, la número 309 del repositorio del proyecto en GitHub.