<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Alex Salgado - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Alex Salgado - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/es/search-labs/author/alex-salgado</link>
    </image>
    <link>https://www.elastic.co/es/search-labs/author/alex-salgado</link>
    <atom:link href="https://www.elastic.co/es/search-labs/rss/author/alex-salgado.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[es]]></language>
    <lastBuildDate>Wed, 23 Sep 2026 03:38:17 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Creación de un sistema RAG multimodal con Elasticsearch: la historia de Gotham City]]></title>
    <description><![CDATA[Aprenda a crear un sistema multimodal de generación aumentada de recuperación (RAG) que integre datos de texto, audio, video e imagen para proporcionar una recuperación de información más rica y contextualizada.]]></description>
    <content:encoded><![CDATA[<p>En este blog, aprenderás a crear un pipeline RAG (Retrieval-Augmented Generation) multimodal con Elasticsearch. Exploraremos cómo aprovechar ImageBind para generar incrustaciones para varios tipos de datos, incluidos texto, imágenes, audio y mapas de profundidad. También descubrirás cómo almacenar y recuperar de manera eficiente estas incrustaciones en Elasticsearch mediante dense_vector y búsqueda k-NN. Finalmente, integraremos un modelo de lenguaje grande (LLM) para analizar la evidencia recuperada y generar un reporte final completo.</p><h3>¿Cómo funciona el oleoducto multimodal RAG?</h3><ol><li><p><strong>Recopilación de pistas</strong> → imágenes, audio, textos y mapas de profundidad de la escena del crimen en Gotham.</p></li><li><p><strong>Generación de incrustaciones</strong> → Cada archivo se convierte en un vector mediante el modelo multimodal ImageBind.</p></li><li><p><strong>Indexación en Elasticsearch</strong> → Los vectores se almacenan para una recuperación eficiente.</p></li><li><p><strong>Buscando por similitud</strong> → Dada una nueva pista, se recuperan los vectores más similares.</p></li><li><p><strong>El LLM analiza la evidencia</strong> → ¡Un modelo GPT-4 sintetiza la respuesta e identifica al sospechoso!</p></li></ol><h3>Tecnologías empleadas</h3><ul><li><p><strong>ImageBind</strong> → Genera incrustaciones unificadas para varias modalidades.</p></li><li><p><strong>Elasticsearch</strong> → Permite una búsqueda vectorial rápida y eficiente.</p></li><li><p><strong>LLM (GPT-4, OpenAI)</strong> → Analiza la evidencia y genera un reporte final.</p></li></ul><h3>¿A quién va dirigido este blog?</h3><ul><li><p>Usuarios de Elastic interesados en la búsqueda vectorial multimodal.</p></li><li><p>Desarrolladores que buscan comprender el RAG multimodal en la práctica.</p></li><li><p>Cualquiera que busque soluciones escalables para analizar datos de múltiples fuentes.</p></li></ul><h2>Requisitos previos para el RAG multimodal: Configuración del entorno</h2><p>Para resolver el crimen en Gotham City, debe configurar su entorno tecnológico. Siga esta guía paso a paso:</p><h3>1. Requisitos técnicos</h3><p>Componente</p><p>Especificación</p><p>Sistema operativo</p><p>Linux, macOS o Windows</p><p>Python</p><p>3.10 o posterior</p><p>CARNERO</p><p>Mínimo 8 GB (se recomiendan 16 GB)</p><p>GPU</p><p>Opcional pero recomendado para ImageBind</p><h3><strong>2. Configuración del proyecto</strong></h3><p>Todos los materiales de investigación están disponibles en GitHub, y usaremos Jupyter Notebook (Google Colab) para esta experiencia interactiva de resolución de crímenes. Siga estos pasos para comenzar:</p><h4>Configuración con Jupyter Notebook (Google Colab)</h4><p><strong>1. Accede al cuaderno</strong></p><ul><li><p>Abre nuestro notebook de Google Colab listo para usar: <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/building-multimodal-rag-with-elasticsearch-gotham/notebook/01-mmrag-blog-quick-start.ipynb">RAG multimodal con Elasticsearch</a><u>.</u></p></li><li><p>Este cuaderno contiene todo el código y las explicaciones que debe seguir.</p></li></ul><p><strong>2. Clonar el repositorio</strong></p># Clone the repository with the multimodal RAG code
!git clone -b https://github.com/elastic/elasticsearch-labs.git

# Navigate to the project directory
cd elasticsearch-labs/supporting-blog-content/building-multimodal-rag-with-elasticsearch-gotham<p><strong>3. Instalar dependencias</strong></p> # Install PyTorch and related libraries
!pip install torch&gt;=2.1.0 torchvision&gt;=0.16.0 torchaudio&gt;=2.1.0

# Install vision processing libraries
!pip install opencv-python-headless pillow numpy

# Install the specific ImageBind fork
!pip install git+https://github.com/hkchengrex/ImageBind.git

# Install Elasticsearch and environment management
!pip install elasticsearch python-dotenv<p><strong>4. Configurar credenciales</strong></p># Input your credentials securely
import getpass

ELASTICSEARCH_URL = input("Enter the Elasticsearch endpoint url: ")
ELASTICSEARCH_API_KEY = getpass.getpass("Enter the Elasticsearch API key: ")
OPENAI_API_KEY = getpass.getpass("Enter the OpenAI API key: ")

# Configure environment variables
import os
os.environ["ELASTICSEARCH_API_KEY"] = ELASTICSEARCH_API_KEY
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
os.environ["ELASTICSEARCH_URL"] = ELASTICSEARCH_URL<p>Nota: El modelo ImageBind (~2 GB) se descargará automáticamente en la primera ejecución.</p><p>Ahora que todo está configurado, ¡profundicemos en los detalles y resolvamos el crimen!</p><h2>Introducción: El crimen en Gotham City</h2><p>En una noche lluviosa en Gotham City, un crimen impactante sacude la ciudad. El comisionado Gordon necesita su ayuda para desentrañar el misterio. Las pistas están dispersas en diferentes formatos: imágenes borrosas, audio misterioso, textos encriptados e incluso mapas de profundidad. ¿Estás listo para emplear la tecnología de IA más avanzada para resolver el caso?</p><p>En este blog, se le guiará paso a paso a través de la construcción de un <strong>sistema RAG (Retrieval-Augmented Generation) multimodal</strong> que unifica diferentes tipos de datos (<strong>imágenes, audio, textos y mapas de profundidad</strong>) en un solo espacio de búsqueda. Usaremos <strong>ImageBind</strong> para generar incrustaciones multimodales, <strong>Elasticsearch</strong> para almacenar y recuperar estas incrustaciones y un <strong>modelo de lenguaje grande (LLM)</strong> para analizar la evidencia y generar un reporte final.</p><h2>Fundamentos: Arquitectura RAG multimodal</h2><h3>¿Qué es un RAG multimodal?</h3><p>El auge de la <strong>generación aumentada de recuperación (RAG) multimodal</strong> está revolucionando la forma en que interactuamos con los modelos de IA. Tradicionalmente, los sistemas RAG funcionan exclusivamente con texto, recuperando información relevante de las bases de datos antes de generar respuestas. Sin embargo, el mundo no se limita al texto: <strong>las imágenes, los videos y el audio también contienen conocimientos valiosos</strong>. Es por eso que las arquitecturas multimodales están ganando protagonismo, lo que permite que los sistemas <strong>de IA combinen información de diferentes formatos para obtener respuestas más ricas y precisas</strong>.</p><h3><strong>Tres enfoques principales para el GAR multimodal</strong></h3><p>Para implementar un GAR multimodal, se emplean comúnmente tres estrategias. Cada enfoque tiene sus propios beneficios y limitaciones, según el caso de uso:</p><h4>1. Espacio vectorial compartido</h4><p>Los datos de diferentes modalidades se asignan a un espacio vectorial común empleando modelos multimodales como ImageBind. Esto permite que las consultas de texto recuperen imágenes, videos y audio sin conversión de formato explícita.</p><p><strong>Beneficios:</strong></p><ul><li><p>Permite <strong>la recuperación multimodal</strong> sin necesidad de conversión de formato explícita.</p></li><li><p>Proporciona una <strong>integración fluida</strong> entre diferentes modalidades, lo que permite la recuperación directa a través de texto, imagen, audio y video.</p></li><li><p>Escalable para diversos tipos de datos, lo que lo hace útil para <strong>aplicaciones de recuperación a gran escala</strong>.</p></li></ul><p><strong>Desventajas:</strong></p><ul><li><p><strong>La capacitación requiere grandes conjuntos de datos multimodales</strong>, que pueden no estar siempre disponibles.</p></li><li><p>El espacio de incrustación compartido puede introducir <strong>una deriva semántica</strong>, donde las relaciones entre modalidades no se conservan perfectamente.</p></li><li><p><strong>El sesgo en los modelos multimodales</strong> puede afectar la precisión de la recuperación, según la distribución del conjunto de datos.</p></li></ul><h4>2. Modalidad de conexión a tierra única</h4><p>Todas las modalidades se convierten a un <strong>solo formato</strong>, generalmente <strong>texto</strong>, antes de la recuperación. Por ejemplo, las imágenes se describen a través de <strong>subtítulos generados automáticamente</strong> y el audio se transcribe en texto.</p><p><strong>Beneficios:</strong></p><ul><li><p><strong>Simplifica la recuperación</strong>, ya que todo se convierte en una <strong>representación de texto uniforme</strong>.</p></li><li><p>Funciona bien con <strong>los motores de búsqueda basados en texto existentes</strong>, eliminando la necesidad de una infraestructura multimodal especializada.</p></li><li><p>Puede mejorar la <strong>interpretabilidad</strong> ya que los resultados recuperados están en un formato legible por humanos.</p></li></ul><p><strong>Desventajas:</strong></p><ul><li><p><strong>Pérdida de información</strong>: Es posible que ciertos detalles (por ejemplo, relaciones espaciales en imágenes, tono en audio) no se capturen completamente en las descripciones de texto.</p></li><li><p><strong>Depende de la calidad de los subtítulos/transcripciones</strong>: los errores en las anotaciones automáticas pueden reducir la eficacia de la recuperación.</p></li><li><p><strong>No es óptimo para consultas puramente visuales o auditivas</strong> , ya que el proceso de conversión podría eliminar el contexto esencial.</p></li></ul><h4>3. Recuperación separada</h4><p>Mantiene <strong>modelos distintos</strong> para cada modalidad. El sistema realiza <strong>búsquedas separadas</strong> para cada tipo de datos y luego <strong>combina los resultados</strong>.</p><p><strong>Beneficios:</strong></p><ul><li><p>Permite <strong>la optimización personalizada por modalidad</strong>, mejorando la precisión de la recuperación para cada tipo de datos.</p></li><li><p>Menos dependencia de <strong>modelos multimodales complejos</strong>, lo que facilita la integración de los sistemas de recuperación existentes.</p></li><li><p>Proporciona <strong>un control detallado sobre la clasificación y la reclasificación,</strong> ya que los resultados de diferentes modalidades se pueden combinar dinámicamente.</p></li></ul><p><strong>Desventajas:</strong></p><ul><li><p><strong>Requiere fusión de resultados</strong>, lo que hace que el proceso de recuperación y clasificación sea más complejo.</p></li><li><p>Puede generar <strong>respuestas inconsistentes</strong> si diferentes modalidades devuelven información contradictoria.</p></li><li><p><strong>Mayor costo computacional</strong> ya que se realizan búsquedas independientes para cada modalidad, aumentando el tiempo de procesamiento.</p></li></ul><h3>Nuestra elección: espacio vectorial compartido con ImageBind</h3><p>Entre estos enfoques, elegimos el <strong>espacio vectorial compartido</strong>, una estrategia que se alinea perfectamente con la necesidad de <strong>búsquedas multimodales eficientes</strong>. Nuestra implementación se basa en <strong>ImageBind</strong>, un modelo capaz de representar múltiples modalidades (<strong>texto, imagen, audio y video</strong>) en un <strong>espacio vectorial común</strong>. Esto nos permite:</p><ul><li><p>Realice <strong>búsquedas multimodales</strong> entre diferentes formatos de medios sin necesidad de convertir todo en texto.</p></li><li><p>Emplee <strong>incrustaciones altamente expresivas</strong> para capturar relaciones entre diferentes modalidades.</p></li><li><p>Garantiza la <strong>escalabilidad y la eficiencia</strong>, almacenando incrustaciones optimizadas para una recuperación rápida en Elasticsearch.</p></li></ul><p>Al adoptar este enfoque, creamos una <strong>estable canalización de búsqueda multimodal</strong>, donde una consulta de texto puede <strong>recuperar directamente imágenes o audio</strong> sin procesamiento previo adicional. Este método amplía las aplicaciones prácticas desde <strong>la búsqueda inteligente en grandes repositorios</strong> hasta <strong>los sistemas avanzados de recomendación multimodal</strong>.</p><p>En la ilustración siguiente se muestra el flujo de datos dentro de la canalización RAG multimodal, destacando el proceso de indexación, recuperación y generación de respuestas basado en datos multimodales:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77bce4aa5216bcf3/6a17eef263173069e0585b57/a4ffdb44582738991813c045be37312dacb0d4f3-1488x1436.png" alt="Flujo de datos multimodal" /><h3>¿Cómo funciona el espacio de incrustación?</h3><p>Tradicionalmente, las incrustaciones de texto provienen de modelos de lenguaje (por ejemplo, BERT, GPT). Ahora, con modelos multimodales nativos como <strong>ImageBind</strong> de Meta AI, tenemos una columna vertebral que genera vectores para múltiples modalidades:</p><ul><li><p><strong>Texto</strong>: Las oraciones y los párrafos se transforman en vectores de la misma dimensión.</p></li><li><p><strong>Imágenes (visión):</strong> los pixeles se asignan al mismo espacio dimensional empleado para el texto.</p></li><li><p><strong>Audio</strong>: Las señales de sonido se convierten en incrustaciones comparables a imágenes y texto.</p></li><li><p><strong>Mapas de profundidad</strong>: Los datos de profundidad se procesan y también dan como resultado vectores.</p></li></ul><p>Por lo tanto, cualquier pista (<strong>texto, imagen, audio, profundidad</strong>) se puede comparar con cualquier otra empleando métricas de similitud vectorial como <strong>la similitud de coseno</strong>. Si una <strong>muestra de audio riendo</strong> y una <strong>imagen de la cara de un sospechoso</strong> están "cerca" en este espacio, podemos inferir alguna correlación (por ejemplo, la misma identidad).</p><h2>Etapa 1 - Recopilación de pistas de la escena del crimen</h2><p>Antes de analizar la evidencia, necesitamos recopilarla. El crimen en Gotham dejó rastros que pueden estar ocultos en imágenes, audio, textos e incluso datos de profundidad. Organicemos estas pistas para alimentar nuestro sistema.</p><h3>¿Qué tenemos?</h3><p>El Comisionado Gordon nos envió los siguientes archivos que contienen evidencia recopilada de la escena del crimen en cuatro modalidades diferentes:</p><p><strong>Descripción y modalidad de la pista</strong></p><p><strong>a) Imágenes (2 fotos)</strong></p><ul><li><p><code>crime_scene1.jpg, crime_scene2.jpg</code> → Fotos tomadas de la escena del crimen. Muestra rastros sospechosos en el suelo.</p></li><li><p><code>suspect_spotted.jpg</code> → Imagen de la cámara de seguridad que muestra una silueta huyendo de la escena.</p></li></ul><p><strong>b)</strong> <strong>Audio (1 grabación)</strong></p><ul><li><p><code>joker_laugh.wav </code>→ Un micrófono cerca de la escena del crimen capturó una risa siniestra.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7690380dc545367/6a17eef8be6086886a00483b/3457bab38aa4a3caf61ca2a5e0a8b477ddd15cb1-86x45.png" alt="" /><p><strong>c) Texto (1 mensaje)</strong></p><ul><li><p><code>Riddle.txt, note2.txt</code> → Se encontraron algunas notas misteriosas en el lugar, posiblemente dejadas por el criminal.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8e5b8f4bc4124708/6a17eefa1d1b83850c93e50c/8963228dc3b4b7e2106cb6ddffbe2154020eb735-77x79.png" alt="" /><p><strong>d) Profundidad (1 mapa de profundidad)</strong></p><ul><li><p><code>depth_suspect.png</code> → Una cámara de seguridad con un sensor de profundidad capturó a un sospechoso en un callejón cercano.</p></li><li><p><code>jdancing-depth.png</code> → Una cámara de seguridad con un sensor de profundidad capturó a un sospechoso bajando por la estación de metro.</p></li></ul><p>Estas pruebas están en diferentes formatos y no pueden analizar directamente de la misma manera. Necesitamos transformarlos en incrustaciones, vectores numéricos que permitan la comparación intermodal.</p><h3><strong>Organización de archivos</strong></h3><p>Antes de comenzar a procesar, debemos cerciorarnos de que todas las pistas estén organizadas correctamente en el directorio data/ para que la canalización funcione sin problemas.</p><p><strong>Estructura de directorios esperada:</strong></p>data/
├── images/
│   ├── crime_scene1.jpg
│   ├── suspect_spotted.jpg
│   ...
├── audios/
│   ├── joker_laugh.wav
│   ...
├── texts/
│   ├── riddle.txt
│   ... 
├── depths/
│   ├── depth_suspect.png<h3>Código para verificar la organización de pistas</h3><p>Antes de continuar, cerciorémonos de que todos los archivos necesarios estén en la ubicación correcta.</p>import os

# Base directory for clues
data_dir = "data"

# List of expected files
evidences = {
    "images": ["crime_scene1.jpg","crime_scene1.jpg", "joker_alley.jpg"],
    "audios": ["joker_laugh.wav"],
    "texts": ["riddle.txt", "note2.txt”],
    "depths": ["depth_suspect.png", "jdancing-depth.png"]
}

# Create directories if they don't exist
for category, files in evidences.items():
    category_path = os.path.join(data_dir, category)
    os.makedirs(category_path, exist_ok=True)

    for file in files:
        file_path = os.path.join(category_path, file)
        if not os.path.exists(file_path):
            print(f"Warning: {file} not found in {category_path}.")

print("All files are correctly organized!")<p><strong>Ejecución del archivo</strong></p>python  stages/01-stage/files_check.py<p><strong>Salida esperada (si todos los archivos son correctos):</strong></p>All files are correctly organized!<p><strong>Salida esperada (si falta algún archivo):</strong></p>Warning: joker_laugh.wav not found in data/audios/
Warning: depth_suspect.png not found in data/depths/<p>Este script ayuda a prevenir errores antes de comenzar a generar incrustaciones e indexarlas en Elasticsearch.</p><h2>Etapa 2 - Organización de la evidencia</h2><h3>Generación de incrustaciones con ImageBind</h3><p>Para unificar las pistas, necesitamos transformarlas en incrustaciones, representaciones vectoriales que capturen el significado de cada modalidad. Usaremos <strong>ImageBind</strong>, un modelo de Meta AI que genera incrustaciones para diferentes tipos de datos (<strong>imágenes, audio, texto y mapas de profundidad</strong>) dentro de un espacio vectorial compartido.
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltff4aefffbfb5bf00/6a17eefe6864a45aecb68860/b19a1c32cc7a0b4c00fa5b247b18cce71f9693cb-1580x918.png" alt="Generación de incrustaciones con ImageBind" /><h3><strong>¿Cómo funciona ImageBind?</strong></h3><p>Para comparar diferentes tipos de evidencia (<strong>imágenes, audio, texto y mapas de profundidad</strong>), necesitamos transformarlos en vectores numéricos usando <strong>ImageBind</strong>. Este modelo permite convertir cualquier tipo de entrada al mismo formato de incrustación, lo que permite <strong>búsquedas multimodales</strong> entre modalidades.</p><p>A continuación se muestra un código optimizado (<code>src/embedding_generator.py</code>) para generar incrustaciones para cualquier tipo de entrada empleando los procesadores apropiados para cada modalidad:</p>class EmbeddingGenerator:
    """Class for generating multimodal embeddings using ImageBind."""
    
    def __init__(self):
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        self.model = self._load_model()

    def _load_model(self):
        """Loads the ImageBind model and sets it to inference mode."""
        model = imagebind_model.imagebind_huge(pretrained=True)
        model.eval()
        model.to(self.device)
        return model

    def generate_embedding(self, input_data, modality):
        """Generates embedding for different modalities"""
        processors = {
            "vision": lambda x: data.load_and_transform_vision_data(x, self.device),
            "audio": lambda x: data.load_and_transform_audio_data(x, self.device),
            "text": lambda x: data.load_and_transform_text(x, self.device),
            "depth": self.process_depth
        }
        
        try:
            # Input type verification
            if not isinstance(input_data, list):
                raise ValueError(f"Input data must be a list. Received: {type(input_data)}")
                
            # Convert input data to a tensor format that the model can process
            # For images: [batch_size, channels, height, width] 
            # For audio: [batch_size, channels, time] 
            # For text: [batch_size, sequence_length]
            inputs = {modality: processors[modality](input_data)}
            with torch.no_grad():
                embedding = self.model(inputs)[modality]
            return embedding.squeeze(0).cpu().numpy()
        except Exception as e:
            logger.error(f"Error generating {modality} embedding: {str(e)}", exc_info=True)
            raise<p>Un tensor es una estructura de datos fundamental en el aprendizaje automático y el aprendizaje profundo, especialmente cuando se trabaja con modelos como ImageBind. En nuestro contexto:</p>input_tensor = processors[modality]([input_data], self.device)<p>Aquí, el tensor representa los datos de entrada (imagen, audio o texto) convertidos a un formato matemático que el modelo puede procesar. Específicamente:</p><ul><li><p><strong>Para imágenes</strong>: El tensor representa la imagen como una matriz multidimensional de valores numéricos (pixeles organizados por canales de altura, anchura y color).</p></li><li><p><strong>Para audio</strong>: El tensor representa las ondas sonoras como una secuencia de amplitudes a lo largo del tiempo.</p></li><li><p><strong>Para texto</strong>: El tensor representa palabras o tokens como vectores numéricos.</p></li></ul><h3>Probar la generación de incrustación:</h3><p>Probemos nuestra generación de incrustación con el siguiente código. Almacénelo en 02-stage/test_embedding_generation.py y ejecútelo con este comando:</p>python stages/02-stage/test_embedding_generation.py generator = EmbeddingGenerator()
image_embedding = generator.generate_embedding("data/images/crime_scene1.jpg","vision")

print(image_embedding.shape)<h3>Resultado esperado:</h3>(1024,)<p>Ahora, la imagen se transformó en un <strong>vector de 1024 dimensiones</strong>.</p><h2>Etapa 3: Almacenamiento y búsqueda en Elasticsearch</h2><p>Ahora que generamos las incrustaciones para la evidencia, necesitamos almacenarlas en una base de datos vectorial para permitir búsquedas eficientes. Para ello, emplearemos <strong>Elasticsearch</strong>, que soporta vectores densos (<code>dense_vector</code>) y permite búsquedas de similitud.</p><p>Este paso consta de dos procesos principales:</p><ul><li><p><strong>Indexación de las incrustaciones</strong> → Almacena los vectores generados en Elasticsearch.</p></li><li><p><strong>Búsqueda de similitudes</strong> → Recupera los registros más similares a una nueva evidencia.</p></li></ul><h3>Indexación de la evidencia en Elasticsearch</h3><p>Cada pieza de evidencia procesada por <strong>ImageBind</strong> (imagen, audio, texto o profundidad) se convierte en un <strong>vector de 1024 dimensiones</strong>. Necesitamos almacenar estos vectores en <strong>Elasticsearch</strong> para habilitar futuras búsquedas.</p><p>El siguiente código (<code>src/elastic_manager.py</code>) crea un <strong>índice</strong> en Elasticsearch y configura la asignación para almacenar las incrustaciones.</p>from elasticsearch import Elasticsearch, helpers
...

class ElasticsearchManager:
    """Manages multimodal operations in Elasticsearch"""
    
    def __init__(self):
        load_dotenv()  # Load variables from .env
        self.es = self._connect_elastic()
        self.index_name = "multimodal_content"
        self._setup_index()
    
    def _connect_elastic(self):
        """Connects to Elasticsearch"""
        return Elasticsearch(
            os.getenv("ELASTICSEARCH_URL"),  # Elasticsearch endpoint
            api_key=os.getenv("ELASTICSEARCH_API_KEY")
        )
    
    def _setup_index(self):
        """Sets up the index if it doesn't exist"""
        if not self.es.indices.exists(index=self.index_name):
            mapping = {
                "mappings": {
                    "properties": {
                        "embedding": {
                            "type": "dense_vector",
                            "dims": 1024,
                            "index": True,
                            "similarity": "cosine"
                        },
                        "modality": {"type": "keyword"},
                        "content": {"type": "binary"},
                        "description": {"type": "text"},
                        "metadata": {"type": "object"},
                        "content_path": {"type": "text"}
                    }
                }
            }
            self.es.indices.create(index=self.index_name, body=mapping)
    
    def index_content(self, embedding, modality, content=None, description="", metadata=None, content_path=None):
        """Indexes multimodal content"""
        doc = {
            "embedding": embedding.tolist(),
            "modality": modality,
            "description": description,
            "metadata": metadata or {},
            "content_path": content_path
        }
        
        if content:
            doc["content"] = base64.b64encode(content).decode() if isinstance(content, bytes) else content
        
        return self.es.index(index=self.index_name, document=doc)
    
    def search_similar(self, query_embedding, modality=None, k=5):
        """Searches for similar contents"""
        query = {
            "knn": {
                "field": "embedding",
                "query_vector": query_embedding.tolist(),
                "k": k,
                "num_candidates": 100,
                "filter": [{"term": {"modality": modality}}] if modality else []
            }
        }
        
        try:
            response = self.es.search(
                index=self.index_name,
                query=query,
                size=k            
            )
            
            # Return both source data and score for each hit
            return [{
                **hit["_source"],
                "score": hit["_score"]
            } for hit in response["hits"]["hits"]]
        
        except Exception as e:
            print(f"Error: processing search_evidence: {str(e)}")
            return "Error generating search evidence"<h3>Ejecución de la indexación</h3><p>Ahora, indexemos una evidencia para probar el proceso.</p># Example: Indexing an image from the crime scene
generator = EmbeddingGenerator()
es_manager = ElasticsearchManager(cloud_id="YOUR_CLOUD_ID", api_key="YOUR_API_KEY")

image_embedding = generator.generate_embedding("data/images/crime_scene1.jpg", "vision")

response = es_manager.index_content(
    embedding=image_embedding,
    modality="vision",
    description="Photo of the crime scene with suspicious traces",
    content_path="data/images/crime_scene1.jpg"
)
print(json.dumps(response, indent=2))<p><strong>Salida esperada en Elasticsearch (resumen del documento indexado):</strong></p>{
    "embedding": [0.12, -0.53, 0.89, ...],  
    "modality": "vision",  
    "description": "Photo of the crime scene with suspicious traces",  
    "content_path": "data/images/crime_scene1.jpg"  
}<p>Para indexar todas las pruebas multimodales, ejecute el siguiente comando de Python:</p>python stages/03-stage/index_all_modalities.py<p>Ahora, la evidencia se almacena en <strong>Elasticsearch</strong> y está lista para recuperar cuando sea necesario.</p><h3>Verificación del proceso de indexación</h3><p>Luego de ejecutar el script de indexación, verifiquemos si toda nuestra evidencia se almacenó correctamente en Elasticsearch. Puedes usar <strong>las herramientas de desarrollo de Kibana</strong> para ejecutar algunas consultas de verificación:</p><p>1. Primero, verifique si se creó el índice:</p>GET _cat/indices/multimodal_content?v<p>2. Luego, verifique el recuento de documentos por modalidad:</p>GET multimodal_content/_search
{
  "size": 0,
  "aggs": {
    "modalities": {
      "terms": {
        "field": "modality.keyword"
      }
    }
  }
}<p>3. Finalmente, examine la estructura del documento indexado:</p>GET multimodal_content/_search
{
  "size": 1,
  "query": {
    "match_all": {}
  }
}<h4>Resultados esperados:</h4><ul><li><p>Debe existir un índice denominado 'multimodal_content'.</p></li><li><p>Alrededor de 7 documentos distribuidos en diferentes modalidades (visión, audio, texto, profundidad).</p></li><li><p>Cada documento debe contener: incrustación, modalidad, descripción, metadatos y campos content_path.</p></li></ul><p>Este paso de verificación garantiza que nuestra base de datos de evidencia esté configurada correctamente antes de proceder con las búsquedas de similitud.</p><h3>Búsqueda de evidencia similar en Elasticsearch</h3><p>Ahora que la evidencia fue indexada, podemos realizar búsquedas para encontrar los registros más similares a una nueva pista. Esta búsqueda emplea la <strong>similitud vectorial</strong> para devolver los registros más cercanos en el <strong>espacio de incrustación</strong>.</p><p>El código siguiente realiza esta búsqueda.</p>def search_similar_evidence(self, query_embedding, k=5, modality=None):
    """Performs a kNN search to find the most similar clues."""
    
    knn_query = {
        "field": "embedding",
        "query_vector": query_embedding.tolist(),
        "k": k,
        "num_candidates": 100
    }

    query_body = {"knn": knn_query}
    if modality:
        query_body = {
            "bool": {
                "must": [
                    query_body, 
                    {"term": {"modality": modality}}
                ]
            }
        }

    try:
      results = self.es.search(
        index=self.index_name,
        query=query_body,
        _source_includes=["description", "modality", "content_path"],
        size=k
      )
    except Exception as e:
            print(f"Error processing search_evidence: {str(e)}")
            return "Error generating search evidence”

    return results["hits"]["hits"]<h3>Prueba de la búsqueda: uso de audio como consulta para resultados multimodales</h3><p>Ahora, probemos la búsqueda de evidencia empleando un <strong>archivo de audio sospechoso</strong>. Necesitamos generar una incrustación para el archivo de la misma manera y buscar incrustaciones similares:</p>python stages/03-stage/search_by_audio.py# Initialize classes
generator = EmbeddingGenerator()
es_manager = ElasticsearchManager(cloud_id="YOUR_CLOUD_ID", api_key="YOUR_API_KEY")

# Generate embedding for a suspicious audio
audio_embedding = generator.generate_embedding("data/audios/mysterious_laugh.wav", "audio")

# Search for similar evidence in Elasticsearch
similar_evidences = es_manager.search_similar_evidence(audio_embedding, k=3)

# Display the retrieved results
print("\n🔎 Similar evidence found:\n")
for i, evidence in enumerate(similar_evidences, start=1):
    description = evidence['_source']['description']
    modality = evidence['_source']['modality']
    score = evidence['_score']
    content_path = evidence['_source'].get('content_path', 'N/A')

    print(f"{i}. {description} ({modality})")
    print(f"   Similarity: {score:.4f}")
    print(f"   File path: {content_path}\n")<p><strong>Salida esperada en el terminal:</strong></p>🔎 Similar evidence found:

1. A sinister laugh captured near the crime scene (audio)
   Similarity: 0.9985
   File path: data/audios/joker_laugh.wav

2. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.6068
   File path: data/images/joker_laughing.png

3. Suspect dancing (vision)
   Similarity: 0.5591
   File path: data/images/jdancing.png<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73f8f56b0f2b8493/6a17ef01faa91381ca93c94e/24067ea40f7958e171149221f83cbd9bcfccc53f-1582x1208.png" alt="" /><p>Ahora, podemos <strong>analizar la evidencia recuperada</strong> y determinar su relevancia para el caso.</p><h3>Más allá del audio: exploración de búsquedas multimodales</h3><h4>Invertir los roles: cualquier modalidad puede ser una "pregunta"</h4><p>En nuestro sistema <strong>RAG multimodal</strong> , <strong>cada modalidad</strong> es una <strong>consulta de búsqueda</strong> potencial. Vayamos más allá del ejemplo de audio y exploremos cómo otros tipos de datos pueden <strong>iniciar investigaciones</strong>.</p><h4>1. Búsqueda por texto (descifrar la nota del criminal)</h4><p>Escenario: Encontró un <strong>mensaje de texto cifrado</strong> y desea encontrar evidencia relacionada.</p>python stages/03-stage/search_by_text.py# Generate embedding from text
text = "Why so serious?"
embedding_text = generator.generate_embedding([text], "text")

# Search for related evidence
similar_evidences = es_manager.search_similar(
    query_embedding=embedding_text,
    k=3
)<p><strong>Resultados esperados:</strong></p>🔎 Similar evidence found:

1. Mysterious note found at the location (text)
   Similarity: 0.7639
   File path: data/texts/riddle.txt

2. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.7161
   File path: data/images/joker_laughing.png

3. Why so serious (text)
   Similarity: 0.7132
   File path: data/texts/note2.txt<h4>2. Búsqueda de imágenes (seguimiento de la escena del crimen sospechosa)</h4><p><strong>Escenario:</strong> Una <strong>nueva escena del crimen</strong> (<code>crime_scene2.jpg</code>) debe comparar con otras pruebas.
</p>python stages/03-stage/search_by_image.py# Generate embedding for a suspicious image
vision_embedding = generator.generate_embedding(["data/images/crime_scene2.jpg"], "vision")

# Search for similar evidence in Elasticsearch
similar_evidences = es_manager.search_similar(
    query_embedding=vision_embedding,
    k=3
)<p><strong>Salida:</strong></p>🔎 Similar evidence found:

1. Photo of the crime scene: A dark, rain-soaked alley is filled with playing cards, while a sinister graffiti of the Joker laughing stands out on the brick wall. (vision)
   Similarity: 0.8258
   File path: data/images/crime_scene1.jpg

2. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.6897
   File path: data/images/joker_laughing.png

3. Suspect dancing (vision)
   Similarity: 0.6588
   File path: data/images/jdancing.png<h4>3. Búsqueda de mapas de profundidad (búsqueda 3D)</h4><p><strong>Escenario:</strong> Un <strong>mapa</strong> <code>jdancing-depth.png</code> <strong>de</strong> profundidad () revela <strong>patrones de escape de</strong> imágenes.</p>python stages/03-stage/search_by_depth.py# Generate embedding for a suspicious depth map
vision_embedding = generator.generate_embedding(["data/depths/jdancing-depth.png"], "depth")

# Search for similar evidence in Elasticsearch
similar_evidences = es_manager.search_similar(
    query_embedding=vision_embedding,
    modality="vision",
    k=3
)<p><strong>Salida</strong></p>🔎 Similar evidence found:

1. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.5329
   File path: data/images/joker_laughing.png<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt224eaf1be75a8b6b/6a17ef03e8fbceada23a1a0e/985f9536db95c772c696dfac996822fb00f199ee-1594x1160.png" alt="" /><p></p>2. Photo of the crime scene: A dark, rain-soaked alley is filled with playing cards, while a sinister graffiti of the Joker laughing stands out on the brick wall. (vision)
   Similarity: 0.5053
   File path: data/images/crime_scene1.jpg<p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d75bdf986e1bbdd/6a17eef32f4a5c25d5fa89b1/7023ee786ccc760689257abbde2f759ca3cf5c59-1024x768.jpg" alt="" />3. Suspect dancing (vision)
   Similarity: 0.4859
   File path: data/images/jdancing.png<p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73f8f56b0f2b8493/6a17ef01faa91381ca93c94e/24067ea40f7958e171149221f83cbd9bcfccc53f-1582x1208.png" alt="" /><h3><strong>¿Por qué importa esto?</strong></h3><p>Cada modalidad revela <strong>conexiones únicas</strong>:</p><ul><li><p><strong>Texto</strong> → Patrones lingüísticos del sospechoso.</p></li><li><p><strong>Imágenes</strong> → Reconocimiento de <strong>ubicaciones y objetos.</strong></p></li><li><p><strong>Reconstrucción de escenas en profundidad</strong> → 3D <strong>.</strong></p></li></ul><p>Ahora, tenemos una <strong>base de datos de evidencia estructurada</strong> en <strong>Elasticsearch</strong>, lo que nos permite <strong>almacenar y recuperar evidencia multimodal de manera eficiente</strong>.</p><h3><strong>Resumen de lo que hicimos:</strong></h3><ul><li><p><strong>Incrustaciones multimodales almacenadas</strong> en Elasticsearch.</p></li><li><p><strong>Realizó búsquedas de similitud</strong>, encontrando evidencia relacionada con nuevas pistas.</p></li><li><p><strong>Probó la búsqueda empleando un archivo de audio sospechoso</strong>, cerciorar de que el sistema funcione correctamente.</p></li></ul><p><strong>Siguiente paso:</strong> Emplearemos un <strong>LLM</strong> (Large Language Model) para <strong>analizar la evidencia recuperada</strong> y generar un <strong>reporte final</strong>.</p><h2>Etapa 4 - Conectando los puntos con el LLM</h2><p>Ahora que la <strong>evidencia fue indexada</strong> en <strong>Elasticsearch</strong> y se puede recuperar por similitud, necesitamos un <strong>LLM (Large Language Model)</strong> para <strong>analizarla</strong> y generar un <strong>reporte final</strong> para enviar al Comisionado Gordon. El <strong>LLM</strong> será responsable de <strong>identificar patrones, conectar pistas y sugerir un posible sospechoso</strong> en función de la evidencia recuperada.</p><p>Para esta tarea, usaremos <strong>GPT-4 Turbo</strong>, formulando un <strong>aviso detallado</strong> para que el modelo pueda <strong>interpretar</strong> los resultados de manera eficiente.</p><h3><strong>Integración de LLM</strong></h3><p>Para integrar el <strong>LLM</strong> en nuestro sistema, creamos la clase <strong>LLMAnalyzer</strong> (<code>src/llm_analyzer.py</code>), que recibe la <strong>evidencia recuperada</strong> de <strong>Elasticsearch</strong> y genera un <strong>informe forense</strong> empleando esta evidencia como contexto de solicitud.</p>import os
from openai import OpenAI
import logging
from dotenv import load_dotenv

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class LLMAnalyzer:
    """Evidence analyzer using GPT-4"""
    
    def __init__(self):
        load_dotenv()
        self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
    
    def analyze_evidence(self, evidence_results):
        """
        Analyzes multimodal search results and generates a report
        
        Args:
            evidence_results: Dict with results by modality
            {
                'vision': [...],
                'audio': [...],
                'text': [...],
                'depth': [...]
            }
        """
        # Format evidence for the prompt
        evidence_summary = self._format_evidence(evidence_results)

        # final prompt
        prompt = f"""
You are a highly experienced forensic detective specializing in multimodal evidence analysis. Your task is to analyze the collected evidence (audio, images, text, depth maps) and conclusively determine the **prime suspect** responsible for the Gotham Central Bank case.

---

### **Collected Evidence:**
{evidence_summary}

### **Task:**
1. **Analyze all the evidence** and identify cross-modal connections.
2. **Determine the exact identity of the criminal** based on behavioral patterns, visual/auditory/textual clues, and symbolic markers.
3. **Justify your conclusion** by explaining why this suspect is definitively responsible.
4. **Assign a confidence score (0-100%)** to your conclusion.

---

### **Final Output Format (Strictly Follow This Format):**
- **Prime Suspect:** [Full Name or Alias]
- **Evidence Supporting Conclusion:** [Detailed breakdown of visual, auditory, textual, and behavioral evidence]
- **Behavioral Patterns:** [Key actions, motives, and criminal signature]
- **Confidence Level:** [0-100%]
- **Next Steps (if any):** [What additional evidence would further confirm the identity? If none, state "No further evidence required."]

If there is **insufficient evidence**, specify exactly what is missing and suggest what additional data would be needed for a conclusive identification.

This report must be **direct and definitive**--avoid speculation and provide a final, actionable determination of the suspect's identity.
"""
        try:
            response = self.client.chat.completions.create(
                model="gpt-4-turbo-preview",
                messages=[
                    {
                        "role": "system",
                        "content": "You are a forensic detective specialized in multimodal evidence analysis."
                    },
                    {"role": "user", "content": prompt_01}
                ],
                temperature=0.5,
                max_tokens=1000
            )
            
            report = response.choices[0].message.content
            logger.info("\n📋 Forensic Report Generated:")
            logger.info("=" * 50)
            logger.info(report)
            logger.info("=" * 50)
            
            return report
            
        except Exception as e:
            logger.error(f"Error generating report: {str(e)}")
            return None<h4>Ajuste de temperatura en el análisis LLM:</h4><p>Para nuestro sistema de análisis forense, empleamos una temperatura moderada de 0,5. Se eligió esta configuración equilibrada porque:</p><ul><li><p>Representa un término medio entre las salidas deterministas (demasiado rígidas) y las altamente aleatorias;</p></li><li><p>A 0,5, el modelo mantiene suficiente estructura para proporcionar conclusiones forenses lógicas y justificables;</p></li><li><p>Esta configuración permite que el modelo identifique patrones y establezca conexiones mientras se mantiene dentro de parámetros razonables de análisis forense;</p></li><li><p>Equilibra la necesidad de resultados consistentes y confiables con la capacidad de generar análisis perspicaces.</p></li></ul><p>Este ajuste moderado de temperatura ayuda a garantizar que nuestro análisis forense sea confiable y perspicaz, evitando conclusiones demasiado rígidas y demasiado especulativas.</p><h3>Ejecución del análisis de evidencia</h3><p>Ahora que tenemos la <strong>integración de LLM</strong>, necesitamos un <strong>script</strong> que conecte todos los componentes del sistema. Este script:</p><ul><li><p><strong>Busca evidencia similar</strong> en <strong>Elasticsearch.</strong></p></li><li><p><strong>Analice la evidencia recuperada</strong> empleando el <strong>LLM</strong> para generar un <strong>reporte final.</strong></p></li></ul><h4>Código: Script de análisis de evidencia</h4>python stages/04-stage/rag_crime_analyze.pyimport sys
import os
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'src'))

from embedding_generator import EmbeddingGenerator
from elastic_manager import ElasticsearchManager
from llm_analyzer import LLMAnalyzer

import json
import logging
from dotenv import load_dotenv

# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Load environment variables
load_dotenv()

# Initialize classes
generator = EmbeddingGenerator()
es_manager = ElasticsearchManager()

llm = LLMAnalyzer()
logger.info("✅ All components initialized successfully")
    
try:
    evidence_data = {}
    
    # Get data for each modality
    test_files = {
        'vision': 'data/images/crime_scene2.jpg',
        'audio': 'data/audios/joker_laugh.wav',
        'text': 'Why so serious?',
        'depth': 'data/depths/jdancing-depth.png'
    }
    
    logger.info("🔍 Collecting evidence...")
    for modality, test_input in test_files.items():
        try:
            if modality == 'text':
                embedding = generator.generate_embedding([test_input], modality)
            else:
                embedding = generator.generate_embedding([str(test_input)], modality)
            
            results = es_manager.search_similar(embedding, k=2)
            if results:
                evidence_data[modality] = results
                logger.info(f"✅ Data retrieved for {modality}: {len(results)} results")
            else:
                logger.warning(f"⚠️ No results found for {modality}")
                
        except Exception as e:
            logger.error(f"❌ Error retrieving {modality} data: {str(e)}")
    
    if not evidence_data:
        raise ValueError("No evidence data found in Elasticsearch!")
    
    # Test forensic report generation
    logger.info("\n📝 Generating forensic report...")
    report = llm.analyze_evidence(evidence_data)
    
    if report:
        logger.info("✅ Forensic report generated successfully")
        logger.info("\n📊 Report Preview:")
        logger.info("+" * 50)
        logger.info(report)
        logger.info("+" * 50)
    else:
        raise ValueError("Failed to generate forensic report")
        
except Exception as e:
    logger.error(f"❌ Error in analysis : {str(e)}")<h4>Salida esperada de LLM</h4>**Prime Suspect:** The Joker

**Evidence Supporting Conclusion:**

- **Visual Evidence:**
  - The photo of the crime scene with playing cards scattered around and the graffiti of the Joker laughing matches the Joker's known calling cards and thematic elements. The similarity score of 0.83 indicates a high likelihood that these elements are directly associated with the Joker.
  - The image of the Joker with green hair, white face paint, and a sinister smile in an urban night setting, although with a lower similarity score of 0.69, still supports the presence or recent activity of the Joker in areas consistent with the crime scene's characteristics.

- **Auditory Evidence:**
  - The captured sinister laugh with a similarity score of 1.00 perfectly matches known audio profiles of the Joker, making it a direct auditory signature of his presence at or near the crime scene.
  - Despite the lower similarity score of 0.61, the second audio piece further corroborates the Joker's involvement through thematic consistency.

- **Textual Evidence:**
  - The mysterious note found at the location, with a similarity score of 0.76, likely contains thematic or direct references to the Joker's modus operandi or signature phrases, further implicating him in the crime.
  - The similarity score of 0.72 for the Joker's description in textual evidence reinforces the thematic connection to the crime scene.

- **Depth Evidence:**
  - Depth sensor capture of the suspect with a similarity score of 0.77 suggests a physical presence matching the Joker's known dimensions or characteristic movements.
  - The lower similarity score of 0.53 in the second depth evidence still contributes to the overall pattern of evidence pointing towards the Joker, albeit with less certainty.

**Behavioral Patterns:**
- The Joker is known for his theatrical crimes, often leaving behind a signature trail of chaos, including playing cards, sinister laughter, and thematic graffiti. These elements are not only consistent with his known criminal signature but also directly observed at the crime scene.
- His motives often include creating chaos, drawing attention to his acts, and challenging his arch-nemesis, Batman, making a high-profile bank heist fitting within his behavioral patterns.

**Confidence Level:** 95%

**Next Steps:** No further evidence required.

The combination of visual, auditory, textual, and depth evidence strongly points to the Joker as the prime suspect. The thematic consistency across multiple modes of evidence, combined with known behavioral patterns and criminal signature, leaves little doubt regarding his involvement. While there is always a small margin of uncertainty in forensic analysis, the evidence at hand provides a compelling case against the Joker with a high degree of confidence.<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt05526b73530f46ed/6a17ef043e03d782434f2d30/132ee0880b7fb1e64b5b2d886ab76b58baa6de37-1024x768.jpg" alt="" /><h2>Conclusión: Caso resuelto</h2><p>Con todas las <strong>pistas recopiladas y analizadas</strong>, el <strong>sistema RAG multimodal</strong> identificó a un sospechoso: <strong>The Joker</strong>.</p><p>Al combinar <strong>imágenes, audio, texto y mapas de profundidad</strong> en un <strong>espacio vectorial compartido</strong> usando <strong>ImageBind</strong>, el sistema pudo <strong>detectar conexiones</strong> que fueron imposibles de identificar manualmente. <strong>Elasticsearch</strong> garantizó <strong>búsquedas rápidas y eficientes</strong>, mientras que el <strong>LLM</strong> sintetizó la evidencia en un <strong>reporte claro y concluyente</strong>.</p><p>Sin embargo, el <strong>verdadero poder</strong> de este sistema <strong>va más allá de Gotham City</strong>. La <strong>arquitectura RAG multimodal</strong> abre las puertas a <strong>numerosas aplicaciones del mundo real</strong>:</p><ul><li><p><strong>Vigilancia urbana:</strong> Identificación de sospechosos en función de <strong>imágenes, audio y datos de sensores</strong>.</p></li><li><p><strong>Análisis forense:</strong> Correlacionar <strong>evidencia de múltiples fuentes</strong> para resolver <strong>crímenes complejos</strong>.</p></li><li><p><strong>Recomendación multimedia:</strong> Crear <strong>sistemas de recomendación</strong> que comprendan <strong>contextos multimodales</strong> (por ejemplo, sugerir <strong>música</strong> basada en imágenes o texto).</p></li><li><p><strong>Tendencias en redes sociales:</strong> Detección de <strong>temas de tendencia</strong> en diferentes formatos de datos.</p></li></ul><p>Ahora que aprendió a <strong>construir un sistema RAG multimodal</strong>, ¿por qué no <strong>probarlo con sus propias pistas</strong>?</p><p><strong>¡Comparte tus descubrimientos</strong> con nosotros y ayuda a la <strong>comunidad</strong> a avanzar en el campo de la <strong>IA multimodal</strong>!</p><h2>Agradecimientos especiales</h2><p>Me gustaría agradecer a Adrian Cole por su valiosa contribución y revisión durante el proceso de definición de la arquitectura de implementación de este código.</p><h2>Referencias</h2><ul><li><p><a href="https://www.elastic.co/es/search-labs/blog/multimodal-image-retrieval-with-roboflow">Cree un sistema de recuperación de imágenes multimodal empleando la búsqueda KNN y las incrustaciones CLIP</a></p></li><li><p><a href="https://www.elastic.co/es/search-labs/tutorials/search-tutorial/vector-search/nearest-neighbor-search">k-Vecino más cercano (kNN) Búsqueda</a></p></li><li><p><a href="https://pytorch.org/docs/stable/tensors.html">Documentación oficial de PyTorch sobre tensores</a></p></li><li><p><a href="https://imagebind.metademolab.com/">ImageBind: una nueva forma de "vincular" la IA a través de los sentidos</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/building-multimodal-rag-system</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/building-multimodal-rag-system</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Alex Salgado]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt75ee2f922dacb7a8/6a17ef067b54f9775a8b39a3/47635eb4dadb8481854862668231eaa3a005ebee-1600x900.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 11 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>