De CLIP a JinaCLIP: aprendizaje general de representaciones de texto-imagen para búsqueda y RAG multimodal
Recientemente, el auge de los modelos de incrustación multimodales ha captado la atención en diversas industrias, transformando la forma en que las máquinas interpretan texto e imágenes. Sin embargo, a pesar de los avances significativos, estos modelos enfrentan desafíos fundamentales, uno de los cuales es la brecha de modalidad. Esta brecha se manifiesta en cómo las incrustaciones de imagen y texto están espacialmente distantes en el espacio latente, incluso cuando describen el mismo objeto.
En un reciente Unstructured Data Meetup organizado por Zilliz, Bo Wang, Engineering Manager en Jina AI, nos guio a través de las complejidades de la brecha de modalidad y la transición del modelo CLIP de OpenAI a JinaCLIP.
En este blog, recapitularemos sus puntos principales y también implementaremos un sistema de búsqueda de similitud multimodal. Este sistema usará JinaCLIP para generar incrustaciones multimodales y la base de datos vectorial Milvus para almacenar y recuperar incrustaciones similares dada una consulta determinada. Para más detalles, te recomendamos ver la presentación completa de Bo en YouTube.
¿Qué hace que un modelo de incrustación sea bueno?
La función principal de un modelo de incrustación es mapear cosas como textos e imágenes en vectores, representaciones numéricas que capturan relaciones semánticas dentro de los datos. Por ejemplo, la distancia entre dos vectores debería reflejar la similitud de los dos elementos que representan, ya sean dos imágenes, dos textos o uno de cada uno.
Los modelos de incrustación están en el núcleo de las tareas modernas de IA como la búsqueda semántica, la recuperación de imágenes y más. Pero ¿qué hace que un modelo de incrustación sea bueno?
Versatilidad en distintos dominios
Un buen modelo de incrustación debería funcionar bien en una amplia gama de dominios sin requerir ajuste fino. Esta adaptabilidad garantiza que las incrustaciones sean útiles de forma universal, ya sea que la entrada sea texto, imágenes u otros tipos de datos. Por ejemplo, si una imagen se incrusta en un espacio vectorial y un texto relacionado se incrusta en el mismo espacio, las dos incrustaciones deberían estar cerca entre sí, lo que significa su similitud semántica. Esta amplia aplicabilidad es esencial para tareas de IA como la búsqueda de similitud, la recuperación de imágenes y las aplicaciones entre dominios.
Similitud semántica precisa
Una característica clave de un buen modelo de incrustación es su capacidad para conectar izquierda y derecha produciendo incrustaciones con altas puntuaciones de similitud para elementos semánticamente relacionados. Esto va más allá de solo datos de texto; se aplica a cualquier entrada multimodal. Ya sea comparando texto con texto, imagen con imagen o texto con imagen, el modelo debería reflejar las relaciones correctas mediante la proximidad de las incrustaciones en el espacio vectorial.
Sin embargo, los modelos más antiguos que conectaban modalidades de texto e imagen tenían más dificultades para lograr este objetivo. Dependían de procesos manuales, como etiquetar imágenes o usar texto circundante, para establecer conexiones. Estos métodos dieron lugar a una generalización deficiente, particularmente en escenarios fuera de distribución.
El desafío de las tareas multimodales
Antes de la introducción de modelos como CLIP, conectar modalidades (texto e imágenes) era una tarea muy manual y guiada por hipótesis. Ya fueran etiquetas de Flickr o descripciones textuales añadidas por los usuarios, el proceso estaba lejos de ser fluido. Los enfoques supervisados, que a menudo usaban etiquetas de clasificadores, eran propensos a la inestabilidad, y su rendimiento fuera del dominio era insatisfactorio. Esto significaba que, aunque se podían conectar imágenes y textos, los resultados no siempre eran consistentes, especialmente cuando la entrada era compleja.
Para resolver este problema, se desarrolló CLIP, un modelo de preentrenamiento contrastivo lenguaje-imagen, para mejorar el rendimiento de los modelos multimodales entrenándolos con pares imagen-texto. La capacidad de CLIP para alinear las dos modalidades en un espacio de embeddings compartido fue innovadora. Sin embargo, este modelo también tiene sus limitaciones.
Modelo CLIP: Un avance con restricciones
CLIP estableció un nuevo estándar al entrenar dos codificadores separados, uno para texto y otro para imágenes. La idea central era aprender un espacio de embeddings compartido donde los vectores de texto e imagen pudieran coexistir, con textos e imágenes semánticamente similares situados más cerca entre sí. Los resultados fueron impresionantes: CLIP permitió realizar tareas multimodales usando una simple función de similitud coseno.
Figura: Cómo funciona el modelo CLIP
Sin embargo, CLIP tiene sus deficiencias. Un problema significativo es su incapacidad para manejar eficazmente tareas solo de texto. Dado que CLIP fue optimizado principalmente para leyendas cortas (a menudo de menos de 77 caracteres), los textos más largos representan un desafío. Además, CLIP carecía de ejemplos negativos difíciles en su proceso de entrenamiento. Los negativos difíciles son ejemplos que son cercanos pero incorrectos y son cruciales para refinar los modelos, especialmente en tareas como la recuperación de texto. Sin ellos, el rendimiento de CLIP se estancó en tareas que requieren una comprensión detallada del texto.
Transición a JinaCLIP: Abordando las deficiencias de Clip
JinaCLIP se basa en la arquitectura original de CLIP para abordar sus limitaciones. Amplía la entrada de texto y utiliza una arquitectura BERT v2 adaptada para la codificación de texto. Para el procesamiento de imágenes, JinaCLIP incorpora el modelo EVA-02. El proceso de entrenamiento de JinaCLIP se centra en superar los desafíos planteados por las entradas de texto cortas en las leyendas de imágenes e introducir negativos difíciles. Este proceso ocurre en tres etapas, como se muestra a continuación:
Figura: Proceso de entrenamiento de JinaCLIP
Las etapas son aprendizaje de alineación, integración de texto largo y ajuste fino:
Aprendizaje de alineación con textos cortos y leyendas: En esta etapa, el modelo se entrena utilizando tanto datos de imágenes con leyendas como pares de texto con significados similares. El objetivo es alinear los embeddings de imagen y texto optimizando conjuntamente la similitud texto-imagen y texto-texto. Este proceso de coentrenamiento garantiza que, mientras el modelo aprende a asociar texto con imágenes, también mantenga sus capacidades solo de texto. Aunque el rendimiento solo de texto disminuye ligeramente en comparación con el entrenamiento exclusivamente con pares de texto, esta fase preserva un enfoque equilibrado.
Integración de descripciones de texto largas: Durante la segunda etapa, se introducen datos sintéticos para entrenar el modelo. Estos datos alinean imágenes con textos más largos generados por modelos de IA, describiendo el contenido con mayor detalle. Al mismo tiempo, se siguen utilizando pares solo de texto para el entrenamiento. El modelo aprende a manejar descripciones de texto más extensas mientras mantiene la alineación con las imágenes, mejorando su capacidad para comprender relaciones texto-imagen tanto cortas como largas.
Ajuste fino con tripletes de texto y negativos difíciles: La etapa final se centra en usar tripletes de texto que incluyen negativos difíciles, pares de textos en los que dos son semánticamente similares, pero uno es deliberadamente diferente. Esto ayuda al modelo a hacer distinciones semánticas más finas, especialmente en escenarios solo de texto. Al mismo tiempo, el modelo continúa entrenándose con pares sintéticos de imagen-texto largo. Esta fase mejora significativamente el rendimiento solo de texto del modelo, al tiempo que garantiza que la alineación imagen-texto aprendida en etapas anteriores siga siendo robusta.
Este enfoque mantiene un sólido rendimiento en tareas multimodales mientras mejora las capacidades en escenarios solo de texto. Veamos cómo se compara este nuevo enfoque con el Clip original.
Las métricas de rendimiento indican cambios en el rendimiento de recuperación en comparación con el modelo CLIP original: un cambio del 165% en texto a texto, 12% en imagen a imagen, 6% en imagen a texto y 2% en texto a imagen, como se muestra en la imagen a continuación.
Figura: Comparación del rendimiento del modelo entre jina-clip, openai-clip-vit-b-16 y jina-embeddings-v2-base-en
El nuevo proceso de entrenamiento mejora la generalización entre tareas y ayuda a reducir la brecha de modalidad. Analicemos la brecha de modalidad, un obstáculo importante en los modelos de IA multimodal, y por qué ocurre este problema.
La brecha de modalidad: por qué ocurre
La brecha de modalidad es la separación espacial entre incrustaciones de diferentes tipos de entrada, como textos e imágenes que son semánticamente similares pero están lejos en el espacio vectorial. Esta brecha debilita la eficacia de los sistemas de recuperación multimodal, ya que las incrustaciones no reflejan plenamente la verdadera relación entre entradas relacionadas. Como puedes ver en la imagen a continuación, la representación parece un cono truncado, con incrustaciones de imagen en un extremo e incrustaciones de texto en el otro, mostrando claramente la brecha de modalidad.
Figura: Las posiciones iniciales de las incrustaciones de imagen y texto en Jina CLIP, con pesos completamente aleatorizados y sin entrenamiento previo, proyectadas en dos dimensiones
Hay algunas razones clave por las que surge esta brecha, incluso en modelos modernos:
El efecto cono durante la inicialización
Los modelos multimodales, en particular aquellos que manejan entradas de texto e imagen, suelen usar codificadores separados para cada modalidad. Estos codificadores normalmente se inicializan con pesos aleatorios o preentrenados, lo que hace que las incrustaciones de texto se agrupen en una región del espacio vectorial y las incrustaciones de imagen en otra, como se ve en la imagen anterior. Esta separación inicial, llamada efecto cono, dificulta que las incrustaciones de diferentes modalidades (por ejemplo, imágenes y textos relacionados) se alineen correctamente. Por ejemplo, la incrustación de texto para un gato esponjoso y la incrustación de imagen de un gato pueden comenzar muy alejadas debido a este sesgo de inicialización, aunque estén semánticamente relacionadas.
Escalado de temperatura
Durante el entrenamiento, los modelos de IA usan una técnica llamada escalado de temperatura en su función de pérdida contrastiva para ajustar cuán estrechamente se alinean las incrustaciones. Si bien ayuda a ajustar finamente las distancias entre incrustaciones, el escalado de temperatura también puede preservar inadvertidamente la separación inicial entre modalidades, reforzando la brecha de modalidad. Por ejemplo, si el escalado de temperatura no se ajusta cuidadosamente, las incrustaciones para un cachorro juguetón y la imagen de un perro podrían permanecer demasiado alejadas en el espacio vectorial, aunque describan el mismo concepto.
Falsos negativos dentro del lote
Al entrenar con grandes conjuntos de datos, los pares no coincidentes de puntos de datos (por ejemplo, una imagen de un perro emparejada con el texto “una manzana roja”) se tratan como ejemplos negativos. Sin embargo, estos falsos negativos aún podrían compartir cierta superposición semántica. Por ejemplo, si ambos elementos se relacionan con una categoría amplia de animales (por ejemplo, una imagen de un perro y el texto “un lobo juguetón”), el modelo podría separar estos embeddings más de lo necesario, empeorando aún más la brecha de modalidad al tratarlos incorrectamente como completamente no relacionados.
Comprender por qué ocurre la brecha de modalidad es solo el primer paso. Para cerrar esta brecha, se han desarrollado diversas estrategias de entrenamiento para acercar los embeddings de diferentes modalidades, garantizando una recuperación más precisa en tareas multimodales.
Abordar la Brecha de Modalidad: Técnicas de Entrenamiento y Más Allá
La brecha de modalidad es un problema estructural que no tiene una solución fácil, pero existen estrategias para mitigar su impacto. La clave es reducir la separación entre los embeddings de diferentes modalidades durante el entrenamiento, garantizando que las imágenes y los textos que van juntos se asignen más cerca en el espacio vectorial.
Escalado de Temperatura Más Alta
En la práctica, aumentar la temperatura durante el entrenamiento puede animar a los embeddings de diferentes modalidades a acercarse al añadir más aleatoriedad al proceso de aprendizaje del modelo. Por ejemplo, aumentar la temperatura puede ayudar a alinear más estrechamente los embeddings de una imagen de un gato y el texto “un gato esponjoso”. Sin embargo, esto tiene el costo de una convergencia más lenta y requiere más recursos computacionales. Si bien los modelos entrenados con escalado de alta temperatura tienden a mejorar la alineación de los embeddings de texto e imagen, la compensación suele ser tiempos de entrenamiento más largos y mayores demandas computacionales.
Muestreo de Negativos Difíciles
Introducir ejemplos negativos difíciles, pares no coincidentes que son semánticamente cercanos, es otra técnica para reducir la brecha de modalidad. Estos pares negativos difíciles impulsan al modelo a aprender distinciones más finas entre conceptos relacionados, mejorando la precisión de recuperación. Por ejemplo, emparejar una imagen de un gato con el texto “un tigre pequeño” podría parecer una falta de coincidencia, pero el modelo aprende que estas dos entradas tienen semánticas superpuestas (ambos son felinos) y aún deberían estar algo alineadas en el espacio de embeddings. Este proceso enseña al modelo a reconocer tanto las similitudes como las diferencias entre conceptos relacionados, refinando su capacidad para diferenciar entre coincidencias cercanas.
Recuperación Multimodal con Milvus y JinaCLIP: Un Ejemplo Práctico
Ahora que hemos explorado las razones técnicas detrás de la brecha de modalidad, profundicemos en un ejemplo práctico de cómo crear un sistema de recuperación multimodal usando Milvus, una base de datos vectorial de código abierto, y JinaCLIP.
Milvus gestiona, busca y consulta vectores de alta dimensionalidad, lo que lo convierte en una solución ideal para tareas de recuperación multimodal y tareas de Generación Aumentada por Recuperación (RAG). Ya sea que trabajes con texto, imágenes u otros tipos de datos, Milvus te permite almacenar embeddings de forma eficiente y recuperar resultados relevantes utilizando técnicas avanzadas de búsqueda por similitud.
En esta sección, recorreremos el proceso de usar Milvus para crear un sistema de recuperación multimodal que maneje embeddings de texto e imagen. El sistema permitirá a los usuarios introducir texto o imágenes y recuperar los resultados más semánticamente relevantes de un conjunto de datos mixto.
Paso 1: Configuración del Entorno
Primero, necesitamos instalar las bibliotecas que necesitaremos para nuestro sistema en nuestro entorno:
!pip install pymilvus transformers torch timm
Luego, necesitamos importar las bibliotecas necesarias y configurar nuestra clase de codificador.
# Import necessary libraries
from transformers import AutoModel
from pymilvus import MilvusClient
import torch
# Definir la clase Encoder para gestionar la generación de embeddings de texto e imágenes
class Encoder:
def __init__(self, model_name: str):
# Inicializar el modelo (AutoModel de transformers en lugar de SentenceTransformer)
self.model = AutoModel.from_pretrained(model_name, trust_remote_code=True)
def encode_text(self, text: list[str]) -> list[float]:
# Generar embeddings solo para texto
with torch.no_grad():
text_emb = self.model.encode_text(text)
return text_emb
def encode_image(self, image_urls: list[str]) -> list[list[float]]:
# Generar embeddings solo para imágenes
with torch.no_grad():
image_emb = self.model.encode_image(image_urls)
return image_emb
# Inicializar el encoder con el modelo jinaai/jina-clip-v1
model_name = "jinaai/jina-clip-v1"
encoder = Encoder(model_name)
En el código anterior, primero importamos las bibliotecas requeridas: AutoModel de transformers para nuestro modelo de codificación, MilvusClient para interactuar con Milvus y torch para operaciones con tensores.
Luego definimos una clase Encoder que envuelve el AutoModel de transformers. Los métodos encode_text y encode_image utilizan el modelo para generar embeddings para texto e imágenes, respectivamente. El gestor de contexto torch.no_grad() se utiliza para deshabilitar el cálculo de gradientes, que no es necesario para la inferencia y ahorra memoria.
Finalmente inicializamos el encoder con el modelo jinaai/jina-clip-v1, que puede codificar tanto texto como imágenes en un espacio de embeddings compartido.
Paso 2: Preparación de los datos
A continuación, prepararemos nuestros datos de muestra para generar embeddings e insertarlos en Milvus.
# Cargar datos: imágenes y texto para embeddings
sentences = ['A blue cat', 'A red cat', 'A red cat generated by AI', 'A dog biting on a stick']
image_urls = [
'https://i.pinimg.com/600x315/21/48/7e/21487e8e0970dd366dafaed6ab25d8d8.jpg',
'https://i.pinimg.com/736x/c9/f2/3e/c9f23e212529f13f19bad5602d84b78b.jpg',
'https://images.fineartamerica.com/images/artworkimages/mediumlarge/1/red-cat-art-3771-bb-james-ahn.jpg',
'https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/29708160-9b39-42d0-a5ed-4f8b9c85a267/labrador+retriever+dans+pet+care.jpeg?format=1500w'
]
# Generar embeddings para texto e imágenes
text_embeddings = encoder.encode_text(sentences)
image_embeddings = encoder.encode_image(image_urls)
# Asegurar dimensiones consistentes
dim = len(image_embeddings[0])
Aquí, definimos listas de oraciones de muestra y URLs de imágenes. Las URLs contienen imágenes de gatos rojos, gatos azules y un perro. Luego usamos nuestro encoder para generar embeddings tanto para el texto como para las imágenes. La variable dim almacena la dimensión de los embeddings, que necesitaremos al crear nuestra colección de Milvus.
Paso 3: Configuración de Milvus
Ahora configuraremos nuestro cliente de Milvus y crearemos una colección para nuestros datos multimodales.
# Insertar embeddings en Milvus
collection_name = "multimodal_rag_demo"
milvus_client = MilvusClient(uri="./milvus_demo.db")
# Comprobar si la colección existe y eliminarla si es así
if milvus_client.has_collection(collection_name):
print(f"Collection {collection_name} already exists. Deleting it...")
milvus_client.drop_collection(collection_name)
# Crear colección de Milvus
milvus_client.create_collection(
collection_name=collection_name,
auto_id=True,
dimension=dim,
enable_dynamic_field=True,
)
Inicializamos un cliente de Milvus y nos conectamos a un archivo de base de datos local. Comprobamos si ya existe una colección con el nombre deseado y, de ser así, la eliminamos para garantizar un punto de partida limpio.
Luego creamos una nueva colección con el nombre especificado. El parámetro auto_id=True indica a Milvus que genere automáticamente IDs para nuestras entradas. Establecemos dimension para que coincida con nuestros embeddings y habilitamos los campos dinámicos, lo que nos permite añadir metadatos adicionales a nuestras entradas.
Paso 4: Crear un índice
Antes de insertar datos, crearemos un índice para optimizar el rendimiento de búsqueda:
index_params = MilvusClient.prepare_index_params()
index_params.add_index(
field_name="vector", # The field to be indexed (your embedding field)
metric_type="COSINE", # Can be L2, COSINE, etc.
index_type="FLAT", # FLAT ensures exact search, ideal for small datasets
)
# Create the index for the collection
milvus_client.create_index(collection_name, index_params)
print(f"Index created successfully for collection: {collection_name}")
El código anterior crea un índice FLAT usando similitud coseno como métrica de distancia.
Paso 5: Insertar datos en Milvus
Con nuestra colección e índice configurados, ahora podemos insertar nuestros datos en Milvus.
data_to_insert = []
# Insert text embeddings
for idx, txt_emb in enumerate(text_embeddings):
data_to_insert.append({"text": sentences[idx], "vector": txt_emb})
# Insert image embeddings
for idx, img_emb in enumerate(image_embeddings):
data_to_insert.append({"image_url": image_urls[idx], "vector": img_emb})
# Insert data into Milvus
insert_result = milvus_client.insert(
collection_name=collection_name,
data=data_to_insert,
)
print(f"Insert result: {insert_result}")
milvus_client.load_collection(collection_name)
En el código anterior, creamos una lista de diccionarios, cada uno de los cuales contiene una oración de texto o una URL de imagen, junto con su vector de incrustación correspondiente. Luego usamos el método insert del cliente de Milvus para agregar estos datos a nuestra colección. Después de la inserción, cargamos la colección en memoria para prepararla para la búsqueda.
Paso 6: Realizar búsqueda multimodal
Finalmente, realizamos una búsqueda multimodal usando consultas tanto de imagen como de texto.
# Multimodal search with a combination of image and text query
query_image_url = 'https://imgcdn.stablediffusionweb.com/2024/4/5/5e70d71c-a7f6-44e9-972d-8c5a27d45c3d.jpg'
query_text = "Give me similar red cats"
# Generate query embedding (image + text)
query_embedding_image = encoder.encode_image([query_image_url])[0]
query_embedding_text = encoder.encode_text([query_text])[0]
# Perform search in Milvus using image embedding
search_results_image = milvus_client.search(
collection_name=collection_name,
data=[query_embedding_image],
output_fields=["image_url", "text"], # Ensure both fields are included in the results
limit=5, # Number of results to return
search_params={"metric_type": "COSINE", "params": {"nprobe": 10}}
)
# Perform search in Milvus using text embedding
search_results_text = milvus_client.search(
collection_name=collection_name,
data=[query_embedding_text],
output_fields=["image_url", "text"], # Ensure both fields are included in the results
limit=5, # Number of results to return
search_params={"metric_type": "COSINE", "params": {"nprobe": 10}}
)
# Retrieve and print results from image query
print("Image Query Results:")
for result in search_results_image:
for hit in result:
image_url = hit['entity'].get('image_url')
text = hit['entity'].get('text')
if image_url:
print(f"Retrieved Image URL (from image query): {image_url}")
if text:
print(f"Retrieved Text (from image query): {text}")
# Retrieve and print results from text query
print("Text Query Results:")
for result in search_results_text:
for hit in result:
image_url = hit['entity'].get('image_url')
text = hit['entity'].get('text')
if image_url:
print(f"Retrieved Image URL (from text query): {image_url}")
if text:
print(f"Retrieved Text (from text query): {text}")
En el código anterior, demostramos el proceso de realizar una búsqueda multimodal. Comenzamos definiendo tanto una URL de imagen de consulta como un texto de consulta. La imagen de consulta se ve así:
Figura 2: Imagen de entrada de consulta de un gato rojo y blanco
Luego codificamos estas consultas usando nuestro codificador para generar sus respectivos embeddings. Después realizamos dos búsquedas separadas en Milvus: una usando el embedding de la imagen y otra usando el embedding del texto. Para cada búsqueda, solicitamos tanto los campos image_url como text en la salida, lo que nos permite recuperar ambos tipos de datos. Limitamos nuestros resultados a 5 para cada búsqueda y empleamos la similitud coseno como nuestra métrica de distancia. El parámetro nprobe en nuestros parámetros de búsqueda nos permite ajustar con precisión el número de clústeres en los que buscar, ofreciendo un equilibrio entre la velocidad de búsqueda y la precisión. Por último, mostramos los resultados, destacando qué imágenes o textos se recuperaron para cada tipo de consulta, demostrando así la capacidad del sistema para manejar y correlacionar consultas basadas tanto en texto como en imágenes.
Puedes encontrar el código completo aquí.
Estos son resultados de muestra:
Como puedes ver, cuando usamos una imagen para realizar una búsqueda vectorial, obtenemos tanto URL de imágenes como texto relacionado con la imagen, y viceversa. Este es el poder de la búsqueda multimodal. Aquí hay una de las imágenes que se recupera como una imagen similar en ambos casos.
Figura 3: Imagen de salida de un gato rojo después de realizar una búsqueda de similitud en Milvus
Como puedes ver, la imagen es similar a la imagen del gato rojo que dimos al sistema como nuestra consulta.
Puedes añadir un reranker a este sistema para ordenar los resultados de búsqueda en términos de relevancia, con los resultados más relevantes apareciendo arriba. Aunque, como es evidente, Milvus hace un buen trabajo recuperando primero las mejores coincidencias. También puedes ampliar este sistema para crear un sistema RAG multimodal.
Conclusión
Bo Wang explicó las complejidades de cerrar la brecha de modalidad en los modelos multimodales y mostró la transición de CLIP a JinaCLIP. Desglosó los desafíos clave de la alineación texto-imagen y demostró cómo JinaCLIP aborda estos problemas.
En este artículo, ampliamos sus ideas creando un sistema de búsqueda de similitud multimodal usando JinaCLIP para generar embeddings y Milvus para lograr una recuperación eficiente. Con este conocimiento, ahora puedes implementar tus aplicaciones de búsqueda multimodal, asegurando un rendimiento fluido en consultas basadas en texto e imágenes, e incluso llevarlo más allá integrando técnicas avanzadas de búsqueda como el reranking.
Recursos adicionales
Sigue leyendo

Why Context Engineering Is Becoming the Full Stack of AI Agents
Discover how context engineering unifies prompts, RAG, and tools to build smarter, production-ready AI agents powered by Milvus.

Announcing VDBBench 1.0: Open-Source VectorDB Benchmarking with Your Real-World Production Workloads
Discover VDBBench 1.0, an open-source tool for benchmarking vector databases with real-world production data, streaming ingestion, and concurrent workloads.

VidTok: Rethinking Video Processing with Compact Tokenization
VidTok tokenizes videos to reduce redundancy while preserving spatial and temporal details for efficient processing.


