<?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[Srikanth Manvi - 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[Srikanth Manvi - 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/srikanth-manvi</link>
    </image>
    <link>https://www.elastic.co/es/search-labs/author/srikanth-manvi</link>
    <atom:link href="https://www.elastic.co/es/search-labs/rss/author/srikanth-manvi.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[es]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 08:27:45 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Cómo usar el Conector de Almacenamiento de Vectores de Elasticsearch para el Kernel Semántico de Microsoft para el desarrollo de agentes de IA]]></title>
    <description><![CDATA[Microsoft Semantic Kernel es un kit de desarrollo ligero y de código abierto que te permite construir agentes de IA con facilidad e integrar los últimos modelos de IA en tu base de código en C#, Python o Java. Con el lanzamiento del Conector de Almacenamiento Vectorial de Semantic Kernel Elasticsearch, los desarrolladores que emplean Semantic Kernel para construir agentes de IA pueden ahora integrar Elasticsearch como un almacén vectorial escalable de nivel empresarial, mientras continúan empleando abstracciones de Semantic Kernel.]]></description>
    <content:encoded><![CDATA[<p>En colaboración con el equipo <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">de Microsoft Semantic Kernel</a> , anunciamos la disponibilidad del <a href="https://github.com/elastic/semantic-kernel-net/">Semantic Kernel Elasticsearch Vector Store Connector</a>, para usuarios <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">de Microsoft Semantic Kernel</a> (.NET). Semantic Kernel simplifica la construcción de agentes de IA de nivel empresarial, incluyendo la capacidad de mejorar grandes modelos de lenguaje (LLMs) con respuestas más relevantes y basadas en datos desde una Vector Store. Semantic Kernel proporciona una capa de abstracción fluida para interactuar con Vector Stores como Elasticsearch, ofreciendo funciones esenciales como crear, listar y eliminar colecciones de registros, así como subir, recuperar y eliminar registros individuales.</p><p>El conector de <a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/elasticsearch-connector?pivots=programming-language-csharp">almacenamiento vectorial de Semantic Kernel Elasticsearch, ya de fábrica</a> , soporta las <a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/?pivots=programming-language-csharp#the-vector-store-abstraction">abstracciones de almacenamiento vectorial</a> Semantic Kernel, lo que facilita mucho a los desarrolladores integrar Elasticsearch como un almacén vectorial mientras crean agentes de IA.</p><p>Elasticsearch tiene una estable base en la comunidad de código abierto y recientemente adoptó la <a href="https://www.elastic.co/blog/elasticsearch-is-open-source-again">licencia AGPL</a>. Combinadas con el kernel semántico de código abierto de Microsoft, estas herramientas ofrecen una solución poderosa y preparada para compañías. Puedes empezar localmente activando Elasticsearch en unos minutos ejecutando este <code>curl -fsSL https://elastic.co/start-local | sh </code>de comandos (consulta <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">start-local</a> para más detalles) y pasar a versiones <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;utm_source=semantickernel&amp;utm_content=documentation">alojadas en la nube</a> o <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.16/install-elasticsearch.html">autoalojadas</a> mientras produces tus agentes de IA.</p><p>En este blog analizamos cómo usar <a href="https://github.com/elastic/semantic-kernel-net/">Semantic Kernel Elasticsearch Vector Store Connector</a> al usar Semantic Kernel. En el futuro se pondrá a disposición una versión en Python del conector.</p><h2>Escenario de alto nivel: Construcción de una aplicación RAG con Kernel Semántico y Elasticsearch</h2><p>En la siguiente sección repasamos un ejemplo. A un nivel general, estamos construyendo una aplicación RAG (Recuperación Generación Aumentada) que toma la pregunta del usuario como entrada y devuelve una respuesta. Usaremos Azure OpenAI (también se puede usar <a href="https://devblogs.microsoft.com/semantic-kernel/introducing-new-ollama-connector-for-local-models/">un LLM local</a> ) como LLM, Elasticsearch como almacén vectorial y Semantic Kernel (.net) como marco para unir todos los componentes.</p><p>Si no estás familiarizado con las arquitecturas RAG, puedes hacer una breve introducción con este artículo: <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag</a>.</p><p>La respuesta la genera el LLM, que se alimenta con el contexto relevante para la pregunta, recuperado de Elasticsearch vectorstore. La respuesta también incluye la fuente que el LLM empleó como contexto.</p><h3>Ejemplo RAG</h3><p>En este ejemplo concreto, construimos una aplicación que permite a los usuarios hacer preguntas sobre hoteles almacenados en una base de datos interna de hoteles. El usuario podría, por ejemplo, Busca un hotel específico, según diferentes criterios, o pide una lista de hoteles.</p><p>Para la base de datos de ejemplo, generamos una <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">lista de hoteles</a> con 100 entradas. El tamaño de la muestra es intencionadamente pequeño para que puedas probar la demo del conector lo más fácilmente posible. En una aplicación real, el conector Elasticsearch mostraría sus beneficios sobre otras opciones, como la implementación de almacenamiento vectorial 'InMemory', especialmente cuando se trabaja con cantidades extremadamente grandes de datos.</p><p>La aplicación de demostración completa se puede encontrar en el <a href="https://github.com/elastic/semantic-kernel-net/tree/main/Elastic.SemanticKernel.Playground">repositorio</a> de conectores de almacenamiento vectorial de Elasticsearch.</p><p>Empecemos agregando los paquetes NuGet necesarios y aplicando directivas a nuestro proyecto:</p>dotnet add package "Elastic.Clients.Elasticsearch" -v 8.16.2
dotnet add package "Elastic.SemanticKernel.Connectors.Elasticsearch" -v 0.1.2
dotnet add package "Microsoft.Extensions.Hosting" -v 9.0.0
dotnet add package "Microsoft.SemanticKernel.Connectors.AzureOpenAI" -v 1.30.0
dotnet add package "Microsoft.SemanticKernel.PromptTemplates.Handlebars" -v 1.30.0using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Elastic.Clients.Elasticsearch;
using Elastic.Transport;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;<p>Ahora podemos crear nuestro modelo de datos y proporcionarle atributos específicos del Núcleo Semántico para definir el esquema del modelo de almacenamiento y algunas pistas para la búsqueda de texto:</p>/// &lt;summary&gt;
/// Data model for storing a "hotel" with a name, a description, a  description embedding and an optional reference link.
/// &lt;/summary&gt;
public sealed record Hotel
{
	[VectorStoreRecordKey]
	public required string HotelId { get; set; }

	[TextSearchResultName]
	[VectorStoreRecordData(IsFilterable = true)]
	public required string HotelName { get; set; }

	[TextSearchResultValue]
	[VectorStoreRecordData(IsFullTextSearchable = true)]
	public required string Description { get; set; }

	[VectorStoreRecordVector(Dimensions: 1536, DistanceFunction.CosineSimilarity, IndexKind.Hnsw)]
	public ReadOnlyMemory&lt;float&gt;? DescriptionEmbedding { get; set; }

	[TextSearchResultLink]
	[VectorStoreRecordData]
	public string? ReferenceLink { get; set; }
}<p>Los atributos del Esquema del Modelo de Almacenamiento ('VectorStore*') son los más relevantes para el uso real del Conector de Almacenamiento Vectorial Elasticsearch, a saber:</p><p></p><ul><li><p><code>VectorStoreRecordKey</code> para marcar una propiedad en una clase de registro como la clave bajo la cual el registro se almacena en un almacén vectorial.</p></li><li><p><code>VectorStoreRecordData</code> para marcar una propiedad en una clase de registro como 'datos'.</p></li><li><p><code>VectorStoreRecordVector</code> para marcar una propiedad en una clase de registro como vector.</p></li></ul><p>Todos estos atributos aceptan varios parámetros opcionales que pueden emplear para personalizar aún más el modelo de almacenamiento. En el caso de <code>VectorStoreRecordKey </code>, por ejemplo, es posible especificar una función de distancia diferente o un tipo de índice distinto.</p><p>Los atributos de búsqueda de texto (<code>TextSearch*</code>) serán importantes en el último paso de este ejemplo. Volveremos a ellos más tarde.</p><p>En el siguiente paso, inicializamos el motor Semantic Kernel y obtenemos referencias a los servicios principales. En una aplicación real, se debe usar <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection">inyección de dependencias</a> en lugar de acceder directamente a la colección de servicios. Lo mismo se aplica a la configuración y secretos codificados en fija, que deberían leer usando un <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration">proveedor de configuración</a> en su lugar:</p>var builder = Host.CreateApplicationBuilder(args);

// Register AI services.
var kernelBuilder = builder.Services.AddKernel();

kernelBuilder.AddAzureOpenAIChatCompletion("gpt-4o", "https://my-service.openai.azure.com", "my_token");

kernelBuilder.AddAzureOpenAITextEmbeddingGeneration("ada-002", "https://my-service.openai.azure.com", "my_token");

// Register text search service.
kernelBuilder.AddVectorStoreTextSearch&lt;Hotel&gt;();

// Register Elasticsearch vector store.
var elasticsearchClientSettings = new ElasticsearchClientSettings(new Uri("https://my-elasticsearch-instance.cloud"))
    .Authentication(new BasicAuthentication("elastic", "my_password"));

kernelBuilder.AddElasticsearchVectorStoreRecordCollection&lt;string, Hotel&gt;("skhotels", elasticsearchClientSettings);

// Build the host.
using var host = builder.Build();

// For demo purposes, we access the services directly without using a DI context.

var kernel = host.Services.GetService&lt;Kernel&gt;()!;
var embeddings = host.Services.GetService&lt;ITextEmbeddingGenerationService&gt;()!;
var vectorStoreCollection = host.Services.GetService&lt;IVectorStoreRecordCollection&lt;string, Hotel&gt;&gt;()!;

// Register search plugin.
var textSearch = host.Services.GetService&lt;VectorStoreTextSearch&lt;Hotel&gt;&gt;()!;
kernel.Plugins.Add(textSearch.CreateWithGetTextSearchResults("SearchPlugin"));<p>El servicio de <code>vectorStoreCollection</code> ahora puede usar para crear la colección e incorporar algunos <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">discos demo</a>:</p>await vectorStoreCollection.CreateCollectionIfNotExistsAsync();

// CSV format: ID;Hotel Name;Description;Reference Link
var hotels = (await File.ReadAllLinesAsync("hotels.csv"))
    .Select(x =&gt; x.Split(';'));

foreach (var chunk in hotels.Chunk(25))
{
    var descriptionEmbeddings = await embeddings.GenerateEmbeddingsAsync(chunk.Select(x =&gt; x[2]).ToArray());
    
    for (var i = 0; i &lt; chunk.Length; ++i)
    {
        var hotel = chunk[i];
        await vectorStoreCollection.UpsertAsync(new Hotel
        {
            HotelId = hotel[0],
            HotelName = hotel[1],
            Description = hotel[2],
            DescriptionEmbedding = descriptionEmbeddings[i],
            ReferenceLink = hotel[3]
        });
    }
}<p>Esto muestra cómo Semantic Kernel reduce el uso de un almacén vectorial con toda su complejidad a unas pocas llamadas a métodos simples.</p><p>En el fondo, se crea un nuevo índice en Elasticsearch y se generan todas las asignaciones de propiedades necesarias. Nuestro conjunto de datos se mapea de forma completamente transparente en el modelo de almacenamiento y finalmente se almacena en el índice. A continuación se muestra cómo se ven los mapeos en Elasticsearch.</p>{
  "mappings": {
    "properties": {
      "descriptionEmbedding": {
        "dims": 1536,
        "index": true,
        "index_options": {
          "type": "hnsw"
        },
        "similarity": "cosine",
        "type": "dense_vector"
      },
      "hotelName": {
        "type": "keyword"
      },
      "description": {
        "type": "text"
      }
    }
  }
}<p>El <code>embeddings.GenerateEmbeddingsAsync()</code> llama de forma transparente al servicio configurado Azure AI Embeddings Generation.</p><p>Se puede observar aún más magia en el último paso de esta demo.</p><p>Con una sola llamada a <code>InvokePromptAsync</code>, se realizan todas las siguientes operaciones cuando el usuario hace una pregunta sobre los datos:</p><p>1. Se genera una incrustación para la pregunta del usuario</p><p>2. Se busca en el almacén vectorial las entradas relevantes</p><p>3. Los resultados de la consulta se insertan en una plantilla de prompt</p><p>4. La consulta real en forma de prompt final se envía al servicio de finalización de chat con IA</p>// Invoke the LLM with a template that uses the search plugin to
// 1. get related information to the user query from the vector store
// 2. add the information to the LLM prompt.
var response = await kernel.InvokePromptAsync(
    promptTemplate: """
                    Please use this information to answer the question:
                    {{#with (SearchPlugin-GetTextSearchResults question)}}
                      {{#each this}}
                        Name: {{Name}}
                        Value: {{Value}}
                        Source: {{Link}}
                        -----------------
                      {{/each}}
                    {{/with}}
                    
                    Include the source of relevant information in the response.

                    Question: {{question}}
                    """,
    arguments: new KernelArguments
    {
        { "question", "Please show me all hotels that have a rooftop bar." },
    },
    templateFormat: "handlebars",
    promptTemplateFactory: new HandlebarsPromptTemplateFactory());<p>¿Recuerdas los atributos <code>TextSearch*</code> que definimos previamente en nuestro modelo de datos? Estos atributos nos permiten usar marcadores de posición correspondientes en nuestra plantilla de prompts, que se llenan automáticamente con la información de nuestras entradas en el almacén vectorial.</p><p>La respuesta final a nuestra pregunta "Por favor, muéstrame todos los hoteles que tengan un bar en la azotea" es la siguiente:</p>Console.WriteLine(response.ToString());

// &gt; The hotel that has a rooftop bar is Skyline Suites. You can find more information about this hotel [here](https://example.com/yz567).<p>La respuesta correcta se refiere a la siguiente entrada de nuestro hotels.csv</p>9;
Skyline Suites;
Offering panoramic city views from every suite, this hotel is perfect for those who love the urban landscape. Enjoy luxurious amenities, a rooftop bar, and close proximity to attractions. Luxurious and contemporary.;
https://example.com/yz567<p>Este ejemplo muestra muy bien cómo el uso de Microsoft Semantic Kernel logra una reducción significativa de la complejidad gracias a sus abstracciones bien pensadas, además de permitir un nivel muy alto de flexibilidad. Por ejemplo, al cambiar una sola línea de código, el almacén vectorial o los servicios de IA empleados pueden ser reemplazados sin necesidad de refactorizar ninguna otra parte del código.</p><p>Al mismo tiempo, el framework proporciona un enorme conjunto de funcionalidades de alto nivel, como la función 'InvokePrompt', o el sistema de plantillas o plugins de búsqueda.</p><p>La aplicación de demostración completa se puede encontrar en el repositorio de conectores de almacenamiento vectorial de Elasticsearch.</p><h2>¿Qué más es posible con Elasticsearch?</h2><ul><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Mapeado de nuevas semantic_text de Elasticsearch: Simplificación de la búsqueda semántica</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-reranking-with-retrievers">Reclasificación semántica en Elasticsearch con los retrievers</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">Técnicas avanzadas de RAG parte 1: Procesamiento de datos</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">Técnicas avanzadas de RAG parte 2: Consultas y pruebas</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-rag-with-llama3-opensource-and-elastic">Construyendo RAG con Llama 3 de código abierto y Elastic</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/local-rag-agent-elasticsearch-langgraph-llama3">Un tutorial para construir agentes locales usando LangGraph, LLaMA3 y almacenamiento vectorial de Elasticsearch desde cero</a></p></li></ul><h2>Elasticsearch &amp; Semantic Kernel: ¿Qué sigue?</h2><ul><li><p>Mostramos cómo la tienda vectorial de Elasticsearch puede conectarse fácilmente a Semantic Kernel mientras se construyen aplicaciones GenAI en .NET. Estad atentos para la próxima integración de Python.</p></li><li><p>A medida que Semantic Kernel construye abstracciones para funciones avanzadas de búsqueda como <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">la búsqueda híbrida</a>, el Elasticsearch Connect permitirá a los desarrolladores .NET implementarlas fácilmente usando Semantic Kernel.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[.NET]]></category>
    <category><![CDATA[Base de datos vectorial]]></category>
    <dc:creator><![CDATA[Florian Bernd,Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d8725035e86f8a8/6a17fe447f6f1564f8c09d74/0564fe794e4c66d0507317822d7aa71826183d20-1311x762.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 06 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Protección de la información sensible y de la PII en RAG con Elasticsearch y LlamaIndex]]></title>
    <description><![CDATA[Cómo proteger datos sensibles y de PII en una aplicación RAG con Elasticsearch y LlamaIndex.]]></description>
    <content:encoded><![CDATA[<p></p><p></p><p>En esta publicación analizaremos formas de proteger la Información Personal Identificable (PII) y los datos sensibles al emplear LLMs públicos en un flujo RAG (Generación Aumentada por Recuperación). Exploraremos el enmascaramiento de la PII y datos sensibles empleando librerías de código abierto y expresiones regulares, así como el uso de LLMs locales para enmascarar los datos antes de invocar un LLM público.</p><p>Antes de empezar, repasemos algunos términos que usamos en esta publicación.</p><h2>Terminología</h2><p><a href="https://www.llamaindex.ai/">LlamaIndex</a> es un marco de datos líder para construir aplicaciones LLM (Grandes Modelos de Lenguaje). LlamaIndex proporciona abstracciones para las distintas etapas de la construcción de una aplicación RAG (Generación Aumentada por Recuperación). Frameworks como LlamaIndex y LangChain proporcionan abstracciones para que las aplicaciones no se acoplen estrechamente a las APIs de ningún LLM específico.</p><p><a href="https://www.elastic.co/enterprise-search">Elasticsearch</a> es ofrecido <a href="https://elastic.co/">por Elastic</a>. Elastic es líder en el sector detrás de Elasticsearch, un almacén de datos escalable y base de datos vectoriales que soporta búsqueda de texto completo para mayor precisión, búsqueda vectorial para comprensión semántica y búsqueda híbrida para lo mejor de ambos mundos. Elasticsearch es un motor de búsqueda y análisis distribuido y RESTful, almacén de datos escalable y base de datos vectorial. Las capacidades de Elasticsearch que empleamos en este blog están disponibles en la versión Gratis y Abierta de Elasticsearch.</p><p><a href="https://www.promptingguide.ai/techniques/rag">La Generación Aumentada por Recuperación (RAG)</a> es una técnica/patrón de IA en la que los LLMs reciben conocimientos externos para generar respuestas a las consultas de los usuarios. Esto permite adaptar las respuestas de los LLM a un contexto específico y ser menos genéricas.</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.13/semantic-search.html">Las incrustaciones</a> son representaciones numéricas del significado de texto/medios. Son representaciones de información de alta dimensión en dimensiones inferiores.</p><h2>RAG y Protección de Datos</h2><p>Generalmente, los Grandes Modelos de Lenguaje (LLMs) son buenos generando respuestas basadas en la información disponible en el modelo que puede capacitar con datos de Internet. Sin embargo, para aquellas consultas donde la información no está disponible en el modelo, los LLMs deben proporcionar con conocimientos externos o detalles específicos que no estén contenidos en el modelo. Dicha información puede estar en tu base de datos o en tu sistema de conocimiento interno. La Generación Aumentada por Recuperación (RAG) es una técnica en la que, para una consulta de usuario dada, primero recuperas el contexto/información relevante de sistemas externos (a los LLMs) (por ejemplo, tu base de datos) y envías ese contexto junto con la consulta de usuario a un LLM para generar una respuesta más específica y relevante.</p><p>Esto hace que la técnica RAG sea muy eficaz para aplicaciones en respuesta a preguntas, creación de contenido y en cualquier lugar, un conocimiento profundo del contexto y el detalle es beneficioso.</p><p>Como resultado, en una cadena RAG corres el riesgo de exponer información interna como PII (Información Personal Identificable) e información sensible (por ejemplo, nombres, fechas de nacimiento, números de cuenta, etc.) a los LLMs públicos.</p><p>Aunque tus datos son seguros cuando usas una base de datos vectorial como Elasticsearch (a través de varias palancas como <a href="https://www.elastic.co/guide/en/cloud-enterprise/current/ece-configure-rbac.html">Control de Acceso basado en Roles</a>, <a href="https://www.elastic.co/search-labs/blog/dls-internal-knowledge-search">Seguridad a Nivel de Documentos</a> , etc.), hay que tener cuidado al enviar datos fuera a un LLM público.</p><p>Proteger la Información Personalmente Identificable (PII) y los datos sensibles es crucial al emplear grandes modelos de lenguaje (LLMs) por varias razones:</p><ul><li><p><strong>Cumplimiento de la privacidad</strong>: Muchas regiones tienen regulaciones estrictas, como el Reglamento General de Protección de Datos (RGPD) en Europa o la Ley de Privacidad del Consumidor de California (CCPA) en Estados Unidos, que exigen la protección de los datos personales. El cumplimiento de estas leyes es necesario para evitar consecuencias legales y multas.</p></li><li><p><strong>Confianza del usuario</strong>: Garantizar la confidencialidad e integridad de la información sensible genera confianza en los usuarios. Los usuarios tienen más probabilidades de emplear e interactuar con sistemas que creen que protegerán su privacidad.</p></li><li><p><strong>Seguridad de los datos</strong>: La protección frente a brechas de datos es esencial. Los datos sensibles expuestos a los LLMs sin las salvaguardas adecuadas pueden ser susceptibles a robo o mal uso, lo que puede provocar daños como el robo de identidad o el fraude financiero.</p></li><li><p><strong>Consideraciones éticas</strong>: Éticamente, es importante respetar la privacidad de los usuarios y manejar sus datos de forma responsable. Un mal manejo de la PII puede provocar discriminación, estigmatización u otros impactos sociales negativos.</p></li><li><p><strong>Reputación empresarial</strong>: Las compañías que no protegen datos sensibles pueden sufrir daños en su reputación, lo que puede tener efectos negativos a largo plazo en su negocio, incluyendo la pérdida de clientes e ingresos.</p></li><li><p><strong>Reducción de los riesgos de abuso</strong>: El manejo seguro de datos sensibles ayuda a prevenir el uso malicioso de los datos o del modelo, como capacitar los modelos con datos sesgados o usar los datos para manipular o dañar a individuos.</p></li></ul><p>En general, es necesaria una protección robusta de las PII y datos sensibles para garantizar el cumplimiento legal, mantener la confianza de los usuarios, garantizar la seguridad de los datos, mantener estándares éticos, proteger la reputación empresarial y reducir el riesgo de abuso.</p><h2>Resumen rápido</h2><p>En la <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch">publicación anterior</a> hablamos de cómo implementar la experiencia de preguntas frecuentes empleando una técnica RAG con Elasticsearch como base de datos vectorial, empleando LlamaIndex y un LLM Mistral que se ejecuta localmente. Aquí vamos a construir sobre eso.</p><p>Leer la publicación anterior es opcional, ya que ahora vamos a hablar o resumir rápidamente lo que hicimos en la publicación anterior.</p><p>Teníamos un conjunto de datos de ejemplo de conversaciones en centros de llamadas entre agentes y clientes de una compañía de seguros de hogar ficticia. Construimos una aplicación RAG sencilla que respondía a preguntas como "¿Por qué tipo de problemas relacionados con el agua presentan reclamaciones los clientes?".</p><p>A grandes rasgos, así es como se veía el flujo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" alt="Flujo RAG" /><p>Durante la fase de Indexación, cargamos e indexamos documentos usando la canalización LlamaIndex. Los documentos se agrupaban en fragmentos y se almacenaban en la base de datos vectorial Elasticsearch junto con sus incrustaciones.</p><p>Durante la fase de consulta, cuando el usuario hacía una pregunta, LlamaIndex recuperaba documentos similares en top-K que eran relevantes para la consulta. Estos documentos relevantes para el top-K junto con la consulta se enviaban al LLM Mistral que se ejecutaba localmente, que luego generaba la respuesta para ser enviada de vuelta al usuario. No dudes en leer la publicación anterior o <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/tree/main">explorar el código</a>.</p><p>En la publicación anterior teníamos el LLM funcionando localmente. Sin embargo, en producción puede que quieras usar un LLM externo proporcionado por varias compañías como <a href="https://openai.com/">OpenAI</a>, <a href="https://mistral.ai/">Mistral</a>, <a href="https://www.anthropic.com/claude">Anthropic</a> , etc. Puede ser porque tu caso de uso necesita un modelo fundamental más amplio o porque ejecutar localmente no es una opción debido a necesidades de producción empresarial como escalabilidad, disponibilidad, rendimiento, etc.</p><p>Introducir un LLM externo en tu pipeline RAG te expone al riesgo de filtrar inadvertidamente información sensible y PII a los LLMs. En esta publicación exploraremos opciones sobre cómo enmascarar la información de PII como parte de tu pipeline RAG antes de enviar documentos a un LLM externo.</p><h2>RAG con un LLM público</h2><p>Antes de hablar sobre cómo proteger tu PII e información sensible en una cadena RAG, primero construiremos una aplicación RAG sencilla empleando LlamaIndex, la base de datos Elasticsearch Vector y OpenAI LLM.</p><h3>Prerrequisitos</h3><p>Necesitaremos lo siguiente.</p><ul><li><p><strong>Elasticsearch</strong> está en funcionamiento como base de datos vectorial para almacenar los embeddings. Sigue las instrucciones de la publicación anterior sobre <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch#install-elasticsearch">cómo instalar Elasticsearch</a>.</p></li><li><p>Abre las claves API de IA.</p></li></ul><h3>Aplicación sencilla de RAG</h3><p>Para referencia, el código completo se puede encontrar en este <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/tree/protecting-pii">Repositorio de Github</a>(branch:protecting-pii). Clonar el repositorio es opcional, ya que repasaremos el código a continuación.</p><p>En tu IDE favorito, crea una nueva aplicación en Python con los siguientes 3 archivos.</p><ul><li><p><code>index.py</code> donde va el código relacionado con la indexación de datos.</p></li><li><p><code>query.py</code> donde va el código relacionado con la consulta y la interacción con LLM.</p></li><li><p><code>.env</code> donde van propiedades de configuración como las claves de API.</p></li></ul><p>Necesitamos instalar algunos paquetes. Comenzamos creando un nuevo <a href="https://docs.python.org/3/library/venv.html">entorno virtual</a> de Python en la carpeta raíz de tu aplicación.</p>python3 -m venv .venv
<p>Activa el entorno virtual e instala los paquetes requeridos a continuación.</p>source .venv/bin/activate
pip install llama-index 
pip install llama-index-embeddings-openai
pip install llama-index-vector-stores-elasticsearch
pip install sentence-transformers
pip install python-dotenv
pip install openai
<p>Configura las propiedades de conexión de OpenAI y Elasticsearch en el archivo .env archivo.</p>OPENAI_API_KEY="REPLACEME"
ELASTIC_CLOUD_ID="REPLACEME"
ELASTIC_API_KEY="REPLACEME"
<h4>Datos de indexación</h4><p>Descarga el <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/blob/main/conversations.json">archivo conversations.json</a> que contiene <em>conversaciones</em> entre clientes y agentes del centro de llamadas de nuestra ficticia compañía de seguros de hogar. Coloca el archivo en el directorio raíz de la aplicación junto a los 2 archivos python y el archivo .env Archivo que creaste antes. A continuación se muestra un ejemplo del contenido del archivo.</p>{
"conversation_id": 103,
"customer_name": "Sophia Jones",
"agent_name": "Emily Wilson",
"policy_number": "JKL0123",
"conversation": "Customer: Hi, I'm Sophia Jones. My Date of Birth is November 15th, 1985, Address is 303 Cedar St, Miami, FL 33101, and my Policy Number is JKL0123.\nAgent: Hello, Sophia. How may I assist you today?\nCustomer: Hello, Emily. I have a question about my policy.\nCustomer: There's been a break-in at my home, and some valuable items are missing. Are they covered?\nAgent: Let me check your policy for coverage related to theft.\nAgent: Yes, theft of personal belongings is covered under your policy.\nCustomer: That's a relief. I'll need to file a claim for the stolen items.\nAgent: We'll assist you with the claim process, Sophia. Is there anything else I can help you with?\nCustomer: No, that's all for now. Thank you for your assistance, Emily.\nAgent: You're welcome, Sophia. Please feel free to reach out if you have any further questions or concerns.\nCustomer: I will. Have a great day!\nAgent: You too, Sophia. Take care.",
"summary": "A customer inquires about coverage for stolen items after a break-in at home, and the agent confirms that theft of personal belongings is covered under the policy. The agent offers assistance with the claim process, resulting in the customer expressing relief and gratitude."
}
<p>Más allá del código de abajo en <code>index.py</code> que se encarga de indexar los datos.</p># index.py
# pip install sentence-transformers
# pip install llama-index-embeddings-openai
# pip install llama-index-embeddings-huggingface

import json
import os
from dotenv import load_dotenv
from llama_index.core import Document
from llama_index.core import Settings
from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.node_parser import SentenceSplitter
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore


def get_documents_from_file(file):
   """Reads a json file and returns list of Documents"""

   with open(file=file, mode='rt') as f:
       conversations_dict = json.loads(f.read())

   # Build Document objects using fields of interest.
   documents = [Document(text=item['conversation'],
                         metadata={"conversation_id": item['conversation_id']})
                for
                item in conversations_dict]
   return documents

# Load .env file contents into env
load_dotenv('.env')
Settings.embed_model = HuggingFaceEmbedding(
   model_name="BAAI/bge-small-en-v1.5"
)

def main():
   # ElasticsearchStore is a VectorStore that
   # takes care of Elasticsearch Index and Data management.
   es_vector_store = ElasticsearchStore(index_name="convo_index",
                                        vector_field='conversation_vector',
                                        text_field='conversation',
                                        es_cloud_id=os.getenv("ELASTIC_CLOUD_ID"),
                                        es_api_key=os.getenv("ELASTIC_API_KEY"))

   # LlamaIndex Pipeline configured to take care of chunking, embedding
   # and storing the embeddings in the vector store.
   llamaindex_pipeline = IngestionPipeline(
       transformations=[
           SentenceSplitter(chunk_size=350, chunk_overlap=50),
           Settings.embed_model
       ],
       vector_store=es_vector_store
   )

   # Load data from a json file into a list of LlamaIndex Documents
   documents = get_documents_from_file(file="conversations.json")
   llamaindex_pipeline.run(documents=documents)
   print(".....Indexing Data Completed.....\n")

if __name__ == "__main__":
   main()
<p>Ejecutar el código anterior para ver crea un índice en Elasticsearch, almacena los embeddings en el índice de Elasticsearch llamado <code>convo_index</code>.</p><p>Si necesitas explicación sobre LlamaIndex IngestionPipeline, consulta la publicación anterior en la sección <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch#indexing-data">Crear Pipeline</a>.</p><h4>Consulta</h4><p>En la publicación anterior usamos un LLM local para <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch#querying">hacer consultas</a>.</p><p>En esta publicación usamos un LLM público, OpenAI, como se muestra a continuación.</p># query.py
from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.llms.openai import OpenAI
from index import es_vector_store

# Public LLM where we send user query and Related Documents
llm = OpenAI()

index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the LLM.
# Note that documents are sent as-is. So any PII/Sensitive data is sent to the LLM.
query_engine = index.as_query_engine(llm, similarity_top_k=10)

query="Give me summary of water related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
result = query_engine.query(bundle)
print(result)
<p>El código anterior muestra la respuesta de OpenAI como se indica a continuación.</p><p>Los clientes presentaron diversas reclamaciones relacionadas con el agua, incluyendo problemas como daños por agua en sótanos, tuberías rotas, daños por granizo en los tejados y denegación de reclamaciones por motivos como falta de notificación a tiempo, problemas de mantenimiento, desgaste gradual y daños preexistentes. En cada caso, los clientes expresaron frustración por las denegaciones de reclamaciones y buscaron evaluaciones y decisiones justas respecto a sus reclamaciones.</p><h2>Ocultar la PII en RAG</h2><p>Lo que cubrimos hasta ahora implica enviar documentos tal cual a OpenAI junto con la consulta del usuario.</p><p>En la tubería RAG, tras recuperar el contexto relevante de un almacén vectorial, tenemos la oportunidad de enmascarar la información personal personal y sensible antes de enviar la consulta y el contexto al LLM.</p><p>Hay varias formas de enmascarar la información de la PII antes de enviarla a un LLM externo, cada una con su propio mérito. A continuación analizamos algunas de las opciones</p><ol><li><p>Empleando bibliotecas de PLN como spacy.io o <a href="https://microsoft.github.io/presidio/">Presidio</a> (biblioteca de código abierto mantenida por Microsoft).</p></li><li><p>Uso de LlamaIndex desde el primer momento <code>NERPIINodePostprocessor.</code></p></li><li><p>Uso de LLMs locales mediante <code>PIINodePostprocessor</code></p></li></ol><p>Una vez que implementes la lógica de enmascaramiento usando cualquiera de las formas anteriores, podrías configurar el LlamaIndex IngestionPipeline con un PostProcesador (tu propio personalizado o cualquiera de los PostProcessors listos para usar de LlamaIndex).</p><h3>Uso de bibliotecas de PLN</h3><p>Como parte de la cadena RAG podríamos enmascarar datos sensibles usando bibliotecas de PLN. Usaremos el paquete spacy.io en esta demo.</p><p>Crea un nuevo <code>query_masking_nlp.py</code> de archivo y agrega el código que aparece a continuación.</p># query_masking_nlp.py

# pip install spacy
# python3 - m spacy download en_core_web_sm
import re
from typing import List, Optional

import spacy
from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.core.postprocessor.types import BaseNodePostprocessor
from llama_index.core.schema import NodeWithScore
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.openai import OpenAI
from index import es_vector_store

# Load the spaCy model
nlp = spacy.load("en_core_web_sm")

# Compile regex patterns for performance
phone_pattern = re.compile(r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b')
email_pattern = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
date_pattern = re.compile(r'\b(\d{1,2}[-/]\d{1,2}[-/]\d{2,4}|\d{2,4}[-/]\d{1,2}[-/]\d{1,2})\b')
dob_pattern = re.compile(
r"(January|February|March|April|May|June|July|August|September|October|November|December)\s(\d{1,2})(st|nd|rd|th),\s(\d{4})")
address_pattern = re.compile(r'\d+\s+[\w\s]+\,\s+[A-Za-z]+\,\s+[A-Z]{2}\s+\d{5}(-\d{4})?')
zip_code_pattern =  re.compile(r'\b\d{5}(?:-\d{4})?\b')
policy_number_pattern = re.compile(r"[A-Z]{3}\d{4}\.$")  # 3 characters followed by 4 digits, in our case e.g XYZ9876

Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# match = re.match(policy_number_pattern, "XYZ9876")
# print(match)


def mask_pii(text):
   """
   Masks Personally Identifiable Information (PII) in the given
   text using pre-defined regex patterns and spaCy's named entity recognition.
   Args:
       text (str): The input text containing potential PII.
   Returns:
       str: The text with PII masked.
   """

   # Process the text with spaCy for NER
   doc = nlp(text)

   # Mask entities identified by spaCy NER (e.g First/Last Names etc)
   for ent in doc.ents:
       if ent.label_ in ["PERSON", "ORG", "GPE"]:
           text = text.replace(ent.text, '[MASKED]')

   # Apply regex patterns after NER to avoid overlapping issues
   text = phone_pattern.sub('[PHONE MASKED]', text)
   text = email_pattern.sub('[EMAIL MASKED]', text)
   text = date_pattern.sub('[DATE MASKED]', text)
   text = address_pattern.sub('[ADDRESS MASKED]', text)
   text = dob_pattern.sub('[DOB MASKED]', text)
   text = zip_code_pattern.sub('[ZIP MASKED]', text)
   text = policy_number_pattern.sub('[POLICY MASKED]', text)

   return text


class CustomPostProcessor(BaseNodePostprocessor):
   """
   Custom Postprocessor which masks Personally Identifiable Information (PII).
   PostProcessor is called on the Documents before they are sent to the LLM.
   """
   def _postprocess_nodes(
           self, nodes: List[NodeWithScore], query_bundle: Optional[QueryBundle]
   ) -&gt; List[NodeWithScore]:
       # Masks PII
       for n in nodes:
          n.node.set_content(mask_pii(n.text))
       return nodes

   
# Use Public LLM to send user query and Related Documents
llm = OpenAI()
index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the LLM.
# Note that documents are masked based on custom logic defined in CustomPostProcessor._postprocess_nodes.
query_engine = index.as_query_engine(llm, similarity_top_k=10, node_postprocessors=[CustomPostProcessor()])



query = "Give me summary of water related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
response = query_engine.query(bundle)
print(response)

<p>La respuesta del LLM se muestra a continuación.</p>Los clientes presentaron diversas reclamaciones relacionadas con el agua, incluyendo problemas como daños por agua en sótanos, tuberías rotas, daños por granizo en los tejados e inundaciones durante lluvias intensas. Estas reclamaciones provocaron frustraciones debido a denegaciones basadas en motivos como la falta de notificación oportuna, problemas de mantenimiento, desgaste gradual y daños preexistentes. Los clientes expresaron decepción, estrés y carga financiera como resultado de estas denegaciones de reclamaciones, buscando evaluaciones justas y revisiones exhaustivas de sus reclamaciones. Algunos clientes también sufrieron retrasos en la tramitación de las reclamaciones, lo que provocó una mayor insatisfacción con el servicio prestado por la compañía de seguros.<p>En el código anterior, al crear el Llama Index QueryEngine, suministramos un CustomPostProcessor.</p><p>La lógica que invoca el QueryEngine en está definida en el método <code>_postprocess_nodes</code> de <code>CustomPostProcessor</code>. Estamos usando la biblioteca SpaCy.io para detectar Entidades Nombradas en nuestro y luego usamos algunas expresiones regulares para reemplazar esos nombres, así como información sensible, antes de enviar los documentos al LLM.</p><p>Como ejemplo a continuación, se presentan partes de conversaciones originales y la conversación enmascarada creada por el CustomPostProcessor.</p><p>Texto original:</p>Cliente: Hola, soy Matthew Lopez, la fecha de nacimiento es el 12 de octubre de 1984 y vivo en 456 Cedar St, Smalltown, NY 34567. Mi número de póliza es TUV8901. Agente: Buenas tardes, Matthew. ¿En qué puedo ayudarlo hoy? Cliente: Hola, estoy extremadamente decepcionado con la decisión de su compañía de denegar mi reclamación.<p>Texto enmascarado por el CustomPostProcessor.</p>Cliente: Hola, soy [ENMASCARADO], [ENMASCARADO] es [ENMASCARADO NACIMIENTO], y vivo en 456 Cedar St, [ENMASCARADO], [ENMASCARADO] 34567. Mi número de póliza es [MASKED]. Agente: Buenas tardes, [ENMASCARADO]. ¿En qué puedo ayudarlo hoy? Cliente: Hola, estoy extremadamente decepcionado con la decisión de su compañía de denegar mi reclamación.<p>Nota:</p><p><em>Identificar y enmascarar la información personal personal y la información sensible no es una tarea sencilla. Cubrir diversos formatos y semánticas de información sensible requiere un buen conocimiento de tu dominio y datos. Aunque el código presentado arriba puede funcionar para algunos casos de uso, puede que tengas que modificar según tus necesidades y pruebas.</em></p><h3>Usando el LlamaIndex de fábrica <code>NERPIINodePostprocessor</code></h3><p>LlamaIndex facilitó la protección de la información de PII en una cadena RAG al introducir <code>NERPIINodePostprocessor.</code></p>from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.core.postprocessor import NERPIINodePostprocessor
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.openai import OpenAI
from index import es_vector_store

Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# Use Public LLM to send user query and Related Documents
llm = OpenAI()

ner_processor = NERPIINodePostprocessor()
index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the LLM.
# Note that documents masked using the NERPIINodePostprocessor so that PII/Sensitive data is not sent to the LLM.
query_engine = index.as_query_engine(llm, similarity_top_k=10, node_postprocessors=[ner_processor])

query = "Give me summary of fire related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
response = query_engine.query(bundle)
print(response)
<p>La respuesta es la siguiente que se muestra</p>Los clientes presentaron reclamaciones relacionadas con incendios respecto a daños en sus propiedades. En un caso, se denegó una reclamación por daños por incendio en un garaje debido a que el incendio provocado quedó excluido de la cobertura. Otro cliente presentó una reclamación por daños de incendio en su casa, que estaba cubierta por su póliza. Además, un cliente informó de un incendio en la cocina y le cercioraron que los daños por incendio estaban cubiertos.<h3>Uso de LLMs locales mediante <code>PIINodePostprocessor</code></h3><p>También podríamos aprovechar un LLM que funcione localmente o en tu red privada para hacer el trabajo de enmascaramiento antes de enviar los datos a un LLM público.</p><p>Usaremos Mistral funcionando en Ollama en tu máquina local para hacer el enmascaramiento.</p><h4>Ejecutar Mistral localmente</h4><p>Descarga e instala <a href="https://ollama.com/">Ollama</a>. Luego de instalar Ollama, ejecuta este comando para descargar y ejecutar <a href="https://ollama.com/library/mistral">mistral</a></p>ollama run mistral
<p>Puede que tardes unos minutos en descargar y ejecutar el modelo localmente por primera vez. Verifica si el mistral está funcionando haciendo una pregunta como la siguiente: "Escribe un poema sobre nubes" y verifica si el poema te gusta. Mantén a Ollama funcionando, ya que tendremos que interactuar con el modelo mistral más adelante mediante código.</p><p>Crea un archivo nuevo llamado <code>query_masking_local_LLM.py</code> y agrega el código de abajo.</p># pip install llama-index-llms-ollama
from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.core.postprocessor import PIINodePostprocessor
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.ollama import Ollama
from llama_index.llms.openai import OpenAI
from index import es_vector_store

Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# Use Public LLM to send user query and Related Documents and Local LLM to mask
public_llm = OpenAI()
local_llm = Ollama(model="mistral")

pii_processor = PIINodePostprocessor(llm=local_llm)
index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the public LLM.
# Note that documents are masked using the local llm via PIINodePostprocessor
# so that PII/Sensitive data is not sent to the public LLM.
query_engine = index.as_query_engine(public_llm, similarity_top_k=10, node_postprocessors=[pii_processor])


query = "Give me summary of fire related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
result = query_engine.query(bundle)
print(result)
<p>La respuesta es algo parecido a la que se muestra a continuación</p>Los clientes presentaron reclamaciones relacionadas con incendios respecto a daños en sus propiedades. En un caso, se denegó una reclamación por daños por incendio en un garaje debido a que el incendio provocado quedó excluido de la cobertura. Otro cliente presentó una reclamación por daños de incendio en su casa, que estaba cubierta por su póliza. Además, un cliente informó de un incendio en la cocina y le cercioraron que los daños por incendio estaban cubiertos.<h3>Conclusión</h3><p>En esta publicación mostramos cómo se puede proteger la PII y los datos sensibles al usar LLMs públicos en un flujo RAG. Demostramos múltiples formas de lograrlo. Se recomienda encarecidamente probar estos enfoques según tu caso de uso y necesidades antes de adoptarlos.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/rag-security-masking-pii</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/rag-security-masking-pii</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" length="0" type="image/png"/>
    <pubDate>Thu, 25 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[RAG (Generación aumentada por recuperación) con LlamaIndex, Elasticsearch y Mistral]]></title>
    <description><![CDATA[Aprende a implementar un sistema RAG (Generación Aumentada por Recuperación) usando LlamaIndex, Elasticsearch y Mistral ejecutando localmente.]]></description>
    <content:encoded><![CDATA[<p>En este blog discutiremos cómo implementar la experiencia de preguntas frecuentes empleando una técnica RAG (Generación Aumentada de Recuperación) con Elasticsearch como base de datos vectorial. Emplearemos LlamaIndex y un LLM Mistral que se ejecuta localmente.</p><p>Antes de empezar, veremos algo de terminología.</p><h3>Terminología</h3><p><a href="https://www.llamaindex.ai/">LlamaIndex</a> es un marco de datos líder para construir aplicaciones LLM (Grandes Modelos de Lenguaje). LlamaIndex proporciona abstracciones para las distintas etapas de la construcción de una aplicación RAG (Generación Aumentada por Recuperación). Frameworks como LlamaIndex y LangChain proporcionan abstracciones para que las aplicaciones no se acoplen estrechamente a las APIs de ningún LLM específico.</p><p><a href="https://www.elastic.co/enterprise-search">Elasticsearch</a> es ofrecido <a href="https://elastic.co/">por Elastic</a>. Elastic es líder en el sector detrás de Elasticsearch, un motor de búsqueda y análisis que soporta búsqueda en texto completo para mayor precisión, búsqueda vectorial para comprensión semántica y búsqueda híbrida para lo mejor de ambos mundos. Elasticsearch es un almacén de datos escalable y una base de datos vectorial. Las capacidades de elasticsearch que empleamos en este blog están disponibles en la versión gratis y abierta de Elasticsearch.</p><p><a href="https://www.promptingguide.ai/techniques/rag">La Generación de Aumentos por Recuperación (RAG)</a> es una técnica/patrón de IA en la que los LLMs reciben conocimientos externos para generar respuestas a las consultas de los usuarios. Esto permite adaptar las respuestas de los LLM a un contexto específico y que las respuestas sean más específicas.</p><p><a href="https://docs.mistral.ai/">Mistral</a> ofrece tanto modelos LLM de código abierto como optimizados de nivel empresarial. En este tutorial, usaremos su modelo <a href="https://docs.mistral.ai/models/#mistral-7b">de código abierto mistral-7b</a> que funciona en tu portátil. Si no quieres ejecutar el modelo en tu portátil, alternativamente podrías usar su versión en la nube, en cuyo caso tendrás que modificar el código de este blog para usar las claves y paquetes de API adecuados.</p><p><a href="https://ollama.com/">Ollama</a> ayuda a ejecutar LLMs localmente en tu portátil. Usaremos Ollama para ejecutar localmente el modelo de código abierto Mistral-7b.</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.13/semantic-search.html">Las incrustaciones</a> son representaciones numéricas del significado de texto/medios. Son representaciones de información de alta dimensión en dimensiones inferiores.</p><h3>Construcción de una aplicación RAG con LlamaIndex, Elasticsearch y Mistral: Resumen del escenario</h3><p><strong>Escenario:</strong></p><p>Disponemos de un conjunto de datos de ejemplo (como archivo JSON) de conversaciones en centros de llamadas entre agentes y clientes de una compañía de seguros de hogar ficticia. Construiremos una aplicación RAG sencilla que podrá responder preguntas como</p><p><code>Give me summary of water related issues.</code></p><h3>Caudal de alto nivel</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" alt="Flujo RAG" /><p>Tenemos Mistral LLM funcionando localmente usando Ollama.</p><p>A continuación, <em>cargamos las conversaciones</em> del archivo JSON como <code>Documents</code> en <a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">ElasticsearchStore</a> (que es un VectorStore respaldado por Elasticsearch). Al cargar los documentos creamos incrustaciones usando el modelo Mistral que se ejecuta localmente. Almacenamos estos embeddings junto con las <em>conversaciones</em> en LlamaIndex Elasticsearch vector Store (<a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">ElasticsearchStore</a>).</p><p>Configuramos un LlamaIndex IngestionPipeline y lo suministramos con el LLM local que usamos, en este caso Mistral que funciona a través de Ollama.</p><p>Cuando hacemos una pregunta como "Dame un resumen de los problemas relacionados con el agua.", Elasticsearch realiza una búsqueda semántica y devuelve <em>conversaciones</em> relacionadas con problemas relacionados con el agua. Estas <em>conversaciones</em> , junto con la pregunta original, se envían al LLM que se ejecuta localmente para generar una respuesta.</p><h3>Pasos para construir la aplicación RAG</h3><h4>Ejecutar Mistral localmente</h4><p>Descarga e instala <a href="https://ollama.com/">Ollama</a>. Luego de instalar Ollama, ejecuta este comando para descargar y ejecutar <a href="https://ollama.com/library/mistral">mistral</a></p>ollama run mistral
<p>Puede que tardes unos minutos en descargar y ejecutar el modelo localmente por primera vez. Verifica si el mistral está funcionando haciendo una pregunta como la siguiente: "Escribe un poema sobre nubes" y verifica si el poema te gusta. Mantén a Ollama funcionando, ya que tendremos que interactuar con el modelo mistral más adelante mediante código.</p><h4>Instalar Elasticsearch</h4><p>Pon en marcha Elasticsearch ya sea creando un despliegue en la nube (<a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud">instrucciones aquí</a>) o ejecutándolo en docker (<a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/docker">instrucciones aquí</a>). También puedes crear un despliegue autoalojado de Elasticsearch de calidad producción <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/docker#self-hosted-production-deployments">empezando por aquí</a>.</p><p>Suponiendo que usas el despliegue en la nube, descarga la clave API y el ID de la nube para el despliegue tal y como se indica en las instrucciones. Los usaremos más tarde.</p><h4>Aplicación RAG</h4><p>Como referencia, se puede encontrar el código completo en este <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany">repositorio de Github</a>. Clonar el repositorio es opcional, ya que repasaremos el código a continuación.</p><p>En tu IDE favorito, crea una nueva aplicación en Python con los siguientes 3 archivos.</p><ul><li><p><code>index.py</code> donde va el código relacionado con la indexación de datos.</p></li><li><p><code>query.py</code> donde va el código relacionado con la consulta y la interacción con LLM.</p></li><li><p><code>.env</code> donde van propiedades de configuración como las claves de API.</p></li></ul><p>Necesitamos instalar algunos paquetes. Comenzamos creando un nuevo <a href="https://docs.python.org/3/library/venv.html">entorno virtual</a> de Python en la carpeta raíz de tu aplicación.</p>python3 -m venv .venv
<p>Activa el entorno virtual e instala los paquetes requeridos a continuación.</p>source .venv/bin/activate
pip install llama-index 
pip install llama-index-embeddings-ollama
pip install llama-index-llms-ollama
pip install llama-index-vector-stores-elasticsearch
pip install sentence-transformers
pip install python-dotenv
<h4>Datos de indexación</h4><p>Descarga el <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/blob/main/conversations.json">archivo conversations.json</a> que contiene <em>conversaciones</em> entre clientes y agentes de centros de llamadas de nuestra compañía de seguros de hogar ficticia. Coloca el archivo en el directorio raíz de la aplicación junto a los 2 archivos python y el archivo .env Archivo que creaste antes. A continuación se muestra un ejemplo del contenido del archivo.</p>{
    "conversation_id": 103,
    "customer_name": "Sophia Jones",
    "agent_name": "Emily Wilson",
    "policy_number": "JKL0123",
    "conversation": "Customer: Hi, I'm Sophia Jones. My Date of Birth is November 15th, 1985, Address is 303 Cedar St, Miami, FL 33101, and my Policy Number is JKL0123.\nAgent: Hello, Sophia. How may I assist you today?\nCustomer: Hello, Emily. I have a question about my policy.\nCustomer: There's been a break-in at my home, and some valuable items are missing. Are they covered?\nAgent: Let me check your policy for coverage related to theft.\nAgent: Yes, theft of personal belongings is covered under your policy.\nCustomer: That's a relief. I'll need to file a claim for the stolen items.\nAgent: We'll assist you with the claim process, Sophia. Is there anything else I can help you with?\nCustomer: No, that's all for now. Thank you for your assistance, Emily.\nAgent: You're welcome, Sophia. Please feel free to reach out if you have any further questions or concerns.\nCustomer: I will. Have a great day!\nAgent: You too, Sophia. Take care.",
    "summary": "A customer inquires about coverage for stolen items after a break-in at home, and the agent confirms that theft of personal belongings is covered under the policy. The agent offers assistance with the claim process, resulting in the customer expressing relief and gratitude."
}
<p>Definimos una función llamada <code>get_documents_from_file</code> en <code>index.py</code> que lee el archivo json y crea una lista de Documentos. <a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/documents_and_nodes/">Los objetos documento</a> son la unidad básica de información con la que trabaja LlamaIndex.</p># index.py
import json, os
from llama_index.core import Document, Settings
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.ingestion import IngestionPipeline
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore
from dotenv import load_dotenv

def get_documents_from_file(file):
   """Reads a json file and returns list of Documents"""

   with open(file=file, mode='rt') as f:
       conversations_dict = json.loads(f.read())
      
   # Build Document objects using fields of interest.
   documents = [Document(text=item['conversation'],
                         metadata={"conversation_id": item['conversation_id']})
                for
                item in conversations_dict]
   return documents
<p>Create IngestPipeline</p><p>En primer lugar, agrega las claves de CloudID y API de Elasticsearch que obtuviste en la sección <code>Install Elasticsearch</code> al archivo <code>.env</code> . Tu archivo <code>.env</code> debería parecer a lo siguiente (con valores reales).</p>ELASTIC_CLOUD_ID=&lt;REPLACE WITH YOUR CLOUD ID&gt;
ELASTIC_API_KEY=&lt;REPLACE WITH YOUR API_KEY&gt;
<p><a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/ingestion_pipeline/">LlamaIndex IngestionPipeline</a> te permite componer una tubería usando múltiples componentes. Agrega el código de abajo al archivo <code>index.py</code> .</p># index.py

# Load .env file contents into env
# ELASTIC_CLOUD_ID and ELASTIC_API_KEY are expected to be in the .env file.
load_dotenv('.env')

# ElasticsearchStore is a VectorStore that
# takes care of ES Index and Data management.
es_vector_store = ElasticsearchStore(index_name="calls",
                                     vector_field='conversation_vector',
                                     text_field='conversation',
                                     es_cloud_id=os.getenv("ELASTIC_CLOUD_ID"),
                                     es_api_key=os.getenv("ELASTIC_API_KEY"))


def main():
    # Embedding Model to do local embedding using Ollama.
    ollama_embedding = OllamaEmbedding("mistral")

    # LlamaIndex Pipeline configured to take care of chunking, embedding
    # and storing the embeddings in the vector store.
    pipeline = IngestionPipeline(
        transformations=[
            SentenceSplitter(chunk_size=350, chunk_overlap=50),
            ollama_embedding,
        ],
        vector_store=es_vector_store
    )

    # Load data from a json file into a list of LlamaIndex Documents
    documents = get_documents_from_file(file="conversations.json")

    pipeline.run(documents=documents)
    print(".....Done running pipeline.....\n")


if __name__ == "__main__":
    main()

<p>Como se mencionó anteriormente, el LlamaIndex IngestPipeline puede estar compuesto por múltiples componentes. Estamos agregando 3 componentes a la tubería en la línea <code>pipeline = IngestionPipeline(...</code>.</p><ul><li><p><a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/node_parsers/modules/?h=sentencesp#sentencesplitter">Fragmentador de Sentences</a>: Como se puede ver en la definición de <code>get_documents_from_file()</code>, cada documento tiene un campo de texto que contiene la conversación encontrada en el archivo json. Este campo de texto es un texto largo. Para que la búsqueda semántica funcione bien, debe dividir en fragmentos de textos más pequeños. La clase <a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/node_parsers/modules/?h=sentencesp#sentencesplitter">SentenceSplitter</a> hace esto por nosotros. Estos bloques se denominan nodos en la terminología de LlamaIndex. Hay metadatos en los nodos que apuntan al documento al que pertenecen. Alternativamente, podrías usar Elasticsearch Ingestpipeline para el chunking, como se muestra en este <a href="https://www.elastic.co/search-labs/blog/chunking-via-ingest-pipelines">blog</a>.</p></li><li><p><a href="https://docs.llamaindex.ai/en/stable/module_guides/models/embeddings/">OllamaEmbedding</a>: Los modelos de incrustación convierten un fragmento de texto en números (también llamados vectores). Tener representación numérica nos permite realizar <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">búsquedas semánticas</a> donde los resultados coinciden con el significado de la palabra en lugar de simplemente hacer una búsqueda de texto. Suministramos a IngestionPipeline <code>OllamaEmbedding("mistral")</code>. Los chunks que dividimos usando SentenceSplitter se envían al modelo Mistral que se ejecuta en tu máquina local mediante Ollama, luego Mistral crea embeddings para los chunks.</p></li><li><p><a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">ElasticsearchStore</a>: El almacén vectorial LlamaIndex ElasticsearchStore respalda las incrustaciones que se están creando en un Índice Elasticsearch. ElasticsearchStore se encarga de crear y poblar el contenido del índice Elasticsearch especificado. Al crear el ElasticsearchStore (referenciado por <code>es_vector_store</code>) proporcionamos el nombre del índice Elasticsearch que queremos crear (<code>calls</code> en nuestro caso), el campo en el índice donde queremos almacenar las incrustaciones (<code>conversation_vector</code> en nuestro caso) y el campo donde queremos almacenar el texto (<code>conversation</code> en nuestro caso). En resumen, basar en nuestra configuración <code>ElasticsearchStore</code> crea un nuevo índice en Elasticsearch con <code>conversation_vector</code> y <code>conversation</code> como campos (entre otros campos creados automáticamente).</p></li></ul><p>Para unirlo todo, gestionamos la tubería llamando a <code>pipeline.run(documents=documents)</code>.</p><p>Ejecuta el script index.py para ejecutar la tubería de ingest:</p>python index.py
<p>Una vez completada la ejecución de la pipeline, deberíamos ver un nuevo índice en Elasticsearch llamado <code>calls</code>. Ejecutando una consulta sencilla de elasticsearch usando la Dev Console deberías poder ver los datos cargados junto con las incrustaciones.</p>GET calls/_search?size=1
<p>Para resumir lo que hicimos hasta ahora, creamos Documents a partir de un archivo JSON, los dividimos en bloques, creamos incrustaciones para esos fragmentos y almacenamos las incrustaciones (y la conversación de texto) en un almacén vectorial (ElasticsearchStore).</p><h4>Consulta</h4><p>El <a href="https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_guide/">llamaIndex VectorStoreIndex</a> te permite recuperar documentos relevantes y datos de consulta. Por defecto, VectorStoreIndex almacena incrustaciones en memoria en un <a href="https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_guide/">SimpleVectorStore</a>. Sin embargo, se pueden usar almacenes vectoriales externos (como <a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">ElasticsearchStore</a>) para hacer que las incrustaciones sean persistentes.</p><p>Abre el <code>query.py</code> y pega el código de abajo</p># query.py
from llama_index.core import VectorStoreIndex, QueryBundle, Response, Settings
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.llms.ollama import Ollama
from index import es_vector_store

# Local LLM to send user query to
local_llm = Ollama(model="mistral")
Settings.embed_model= OllamaEmbedding("mistral")

index = VectorStoreIndex.from_vector_store(es_vector_store)
query_engine = index.as_query_engine(local_llm, similarity_top_k=10)

query="Give me summary of water related issues"
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
result = query_engine.query(bundle)
print(result)
<p>Definimos un LLM local (<code>local_llm</code>) para apuntar al modelo Mistral que se ejecuta en Ollama. A continuación, creamos un VectorStoreIndex (<code>index</code>) a partir de la tienda vectorial ElasticssearchStore que creamos antes y luego obtenemos un motor de consulta desde el índice. Al crear el motor de consultas, hacemos referencia al LLM local que debe usar para responder, también proporcionamos (<code>similarity_top_k=10</code>) para configurar el número de documentos que deben recuperar del almacén vectorial y enviar al LLM para obtener una respuesta.</p><p>Ejecuta el script <code>query.py</code> para ejecutar el flujo RAG:</p>python query.py
<p>Enviamos el <code>Give me summary of water related issues</code> de consulta (no dudes en personalizar el <code>query</code>) y la respuesta del LLM, que se proporciona con los documentos relacionados, debería ser algo así como el siguiente.</p>En el contexto proporcionado, vemos varios casos en los que los clientes consultaron por la cobertura por daños relacionados con el agua. En dos casos, las inundaciones causaron daños en los sótanos y las filtraciones en el tejado fueron el problema en otro caso. Los agentes confirmaron que ambos tipos de daños por agua están cubiertos por sus respectivas pólizas. Por lo tanto, los problemas relacionados con el agua, como inundaciones y filtraciones en el tejado, suelen estar cubiertos por las pólizas de seguro de hogar.<h4>Algunas advertencias:</h4><p>Esta entrada de blog es una introducción para principiantes a la técnica RAG con Elasticsearch y, por tanto, omite la configuración de las características que te permitirán llevar este punto de partida a producción. Al construir para casos de uso en producción, querrás considerar aspectos más sofisticados como poder proteger tus datos con <a href="https://www.elastic.co/search-labs/blog/dls-internal-knowledge-search">Seguridad a Nivel de Documento</a>, fragmentar tus datos como parte de una <a href="https://www.elastic.co/search-labs/blog/chunking-via-ingest-pipelines">pipeline de Ingest</a> de Elasticsearch o incluso ejecutar otros <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-overview.html">trabajos de ML</a> con los mismos datos que se usan para casos de uso de GenAI/Chat/Q&amp;A.</p><p>También podrías considerar buscar datos y crear incrustaciones desde diversas fuentes externas (por ejemplo, Azure Blob Storage, Dropbox, Gmail, etc.) usando <a href="https://www.elastic.co/guide/en/enterprise-search/current/connectors.html">Elastic Connectors</a>.</p><p>Elastic hace posible todo lo anterior y más posible, y ofrece una solución integral de nivel empresarial para casos de uso de GenAI y más allá.</p><h4>¿Qué sigue?</h4><ul><li><p>Quizá notaste que estamos enviando 10 conversaciones relacionadas junto con la pregunta del usuario al LLM para formular una respuesta. Estas conversaciones pueden contener información personal identificable (PII) como nombre, fecha de nacimiento, dirección, etc. En nuestro caso, el LLM es local, así que la fuga de datos no es un problema. Sin embargo, cuando quieres usar un LLM que se ejecuta en la nube (por ejemplo, OpenAI) no es deseable enviar textos que contengan información de PII. En un blog de seguimiento veremos cómo lograr información de PII de Enmascaramiento antes de enviarla a LLMs externos en el flujo RAG.</p></li><li><p>En esta publicación empleamos un LLM local; en la próxima publicación sobre Enmascarar datos de PII en RAG, veremos cómo podemos cambiar fácilmente de un LLM local a uno público.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" length="0" type="image/png"/>
    <pubDate>Fri, 12 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>