Blog

Protegendo informações sensíveis e de identificação pessoal (PII) no RAG com Elasticsearch e LlamaIndex.

Como proteger dados sensíveis e informações pessoais identificáveis (PII) em uma aplicação RAG com Elasticsearch e LlamaIndex.

Neste artigo, analisaremos maneiras de proteger informações de identificação pessoal (PII) e dados sensíveis ao usar LLMs públicos em um fluxo RAG (Retrieval Augmented Generation). Vamos explorar a mascaramento de informações pessoais identificáveis (PII) e dados sensíveis usando bibliotecas de código aberto e expressões regulares, bem como o uso de LLMs locais para mascarar dados antes de invocar um LLM público.

Antes de começarmos, vamos revisar alguns termos que usamos nesta postagem.

Terminologia

LlamaIndex é uma estrutura de dados líder para a construção de aplicações LLM (Large Language Model). O LlamaIndex fornece abstrações para vários estágios de construção de um aplicativo RAG (Retrieval Augmented Generation). Frameworks como LlamaIndex e LangChain fornecem abstrações para que os aplicativos não fiquem fortemente acoplados às APIs de nenhum LLM específico.

O Elasticsearch é oferecido pela Elastic. A Elastic é líder do setor e está por trás do Elasticsearch, um armazenamento de dados escalável e banco de dados vetorial que oferece suporte a pesquisa de texto completo para precisão, pesquisa vetorial para compreensão semântica e pesquisa híbrida para o melhor dos dois mundos. O Elasticsearch é um mecanismo de busca e análise distribuído e baseado em REST, um armazenamento de dados escalável e um banco de dados vetorial. Os recursos do Elasticsearch que usamos neste blog estão disponíveis na versão gratuita e de código aberto do Elasticsearch.

A Geração Aumentada por Recuperação (RAG, na sigla em inglês) é uma técnica/padrão de IA em que os Modelos de Aprendizagem Baseados em Aprendizagem (LLMs, na sigla em inglês) recebem conhecimento externo para gerar respostas às consultas do usuário. Isso permite que as respostas do LLM sejam adaptadas a um contexto específico e menos genéricas.

Os embeddings são representações numéricas do significado de um texto/mídia. São representações de menor dimensão de informações de alta dimensão.

RAG e Proteção de Dados

De modo geral, os Modelos de Linguagem de Grande Porte (LLMs, na sigla em inglês) são bons em gerar respostas com base nas informações disponíveis no modelo, que pode ser treinado com dados da internet. No entanto, para as consultas em que as informações não estão disponíveis no modelo, os LLMs precisam receber conhecimento externo ou detalhes específicos não contidos no modelo. Essas informações podem estar em seu banco de dados ou sistema de conhecimento interno. A Geração Aumentada por Recuperação (RAG, na sigla em inglês) é uma técnica na qual, para uma determinada consulta do usuário, você primeiro recupera o contexto/informação relevante de sistemas externos (aos LLMs), como seu banco de dados, e envia esse contexto juntamente com a consulta do usuário para o LLM para gerar uma resposta mais específica e relevante.

Isso torna a técnica RAG altamente eficaz para aplicações em perguntas e respostas, criação de conteúdo e em qualquer situação em que uma compreensão profunda do contexto e dos detalhes seja benéfica.

Consequentemente, em um pipeline RAG, você corre o risco de expor informações internas, como PII (Informações de Identificação Pessoal) e informações sensíveis (por exemplo, nomes, datas de nascimento, números de contas etc.) a servidores públicos.

Embora seus dados estejam seguros ao usar um banco de dados vetorial como o Elasticsearch (através de várias ferramentas como Controle de Acesso Baseado em Funções, Segurança em Nível de Documento etc.), é preciso ter cuidado ao enviar dados para um repositório público de dados.

A proteção de informações de identificação pessoal (PII) e dados sensíveis é crucial ao usar modelos de linguagem de grande porte (LLMs) por diversos motivos:

  • Conformidade com a privacidade: Muitas regiões possuem regulamentações rigorosas, como o Regulamento Geral de Proteção de Dados (RGPD) na Europa ou a Lei de Privacidade do Consumidor da Califórnia (CCPA) nos Estados Unidos, que exigem a proteção de dados pessoais. O cumprimento dessas leis é necessário para evitar consequências legais e multas.

  • Confiança do usuário: Garantir a confidencialidade e a integridade de informações sensíveis constrói a confiança do usuário. Os usuários tendem a usar e interagir mais com sistemas que acreditam proteger sua privacidade.

  • Segurança de dados: a proteção contra violações de dados é essencial. Dados sensíveis expostos a sistemas de gestão de dados sem salvaguardas adequadas podem ficar suscetíveis a roubo ou uso indevido, levando a potenciais danos como roubo de identidade ou fraude financeira.

  • Considerações éticas: Do ponto de vista ético, é importante respeitar a privacidade dos usuários e tratar seus dados de forma responsável. O manuseio inadequado de informações pessoais identificáveis pode levar à discriminação, estigmatização ou outros impactos sociais negativos.

  • Reputação empresarial: Empresas que não protegem dados sensíveis podem sofrer danos à sua reputação, o que pode ter efeitos negativos a longo prazo em seus negócios, incluindo perda de clientes e receita.

  • Redução dos riscos de abuso: O manuseio seguro de dados sensíveis ajuda a prevenir o uso malicioso dos dados ou do modelo, como o treinamento de modelos com dados tendenciosos ou o uso dos dados para manipular ou prejudicar indivíduos.

De modo geral, a proteção robusta de informações pessoais identificáveis e dados sensíveis é necessária para garantir a conformidade legal, manter a confiança do usuário, assegurar a segurança dos dados, defender os padrões éticos, proteger a reputação da empresa e reduzir o risco de abuso.

Resumo rápido

Na publicação anterior, discutimos como implementar uma experiência de perguntas e respostas usando a técnica RAG com o Elasticsearch como banco de dados vetorial, utilizando o LlamaIndex e um Mistral LLM executado localmente. Aqui, damos continuidade a isso.

A leitura da postagem anterior é opcional, pois agora vamos discutir/recapitular rapidamente o que fizemos na postagem anterior.

Tínhamos um conjunto de dados de amostra de conversas de call center entre agentes e clientes de uma empresa fictícia de seguros residenciais. Criamos um aplicativo RAG simples que respondia a perguntas como "Que tipo de problemas relacionados à água os clientes estão relatando?".

Em linhas gerais, o fluxo de trabalho se apresentava assim.

Fluxo RAG

Durante a fase de indexação, carregamos e indexamos documentos usando o pipeline LlamaIndex. Os documentos foram divididos em partes e armazenados no banco de dados vetorial Elasticsearch juntamente com seus respectivos embeddings.

Durante a fase de consulta, quando o usuário fazia uma pergunta, o LlamaIndex recuperava os K documentos mais semelhantes e relevantes para a consulta. Esses K documentos mais relevantes, juntamente com a consulta, foram enviados ao Mistral LLM em execução localmente, que então gerou a resposta a ser enviada de volta ao usuário. Fique à vontade para ler a postagem anterior ou explorar o código.

Na postagem anterior, tínhamos o LLM rodando localmente. No entanto, em produção, você pode querer usar um LLM externo fornecido por várias empresas como OpenAI, Mistral, Anthropic etc. Isso pode ocorrer porque seu caso de uso exige um modelo fundamental maior ou porque a execução local não é uma opção devido a necessidades de produção corporativa, como escalabilidade, disponibilidade, desempenho etc.

A introdução de um LLM externo em seu pipeline RAG expõe você ao risco de vazamento inadvertido de informações sensíveis e PII para os LLMs. Neste post, exploraremos opções sobre como mascarar informações de identificação pessoal (PII) como parte do seu fluxo de trabalho RAG antes de enviar documentos para um LLM externo.

RAG com um mestrado em Direito público

Antes de discutirmos como proteger suas informações pessoais e sensíveis em um pipeline RAG, primeiro construiremos um aplicativo RAG simples usando LlamaIndex, banco de dados Elasticsearch Vector e OpenAI LLM.

Pré-requisitos

Precisaremos do seguinte:

  • O Elasticsearch está configurado e funcionando como banco de dados vetorial para armazenar os embeddings. Siga as instruções da postagem anterior sobre como instalar o Elasticsearch.

  • Chaves de API OpenAI.

Aplicação RAG simples

Para referência, o código completo pode ser encontrado neste repositório do Github(branch:protecting-pii). Clonar o repositório é opcional, pois analisaremos o código a seguir.

Em sua IDE favorita, crie uma nova aplicação Python com os 3 arquivos abaixo.

  • index.py Onde fica o código relacionado à indexação de dados.

  • query.py Onde fica o código relacionado a consultas e interação com o LLM.

  • .env onde ficam as propriedades de configuração, como chaves de API.

Precisamos instalar alguns pacotes. Começamos criando um novo ambiente virtual Python na pasta raiz da sua aplicação.

python3 -m venv .venv

Ative o ambiente virtual e instale os pacotes necessários listados abaixo.

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

Configure as propriedades de conexão do OpenAI e do Elasticsearch no arquivo .env. arquivo.

OPENAI_API_KEY="REPLACEME"
ELASTIC_CLOUD_ID="REPLACEME"
ELASTIC_API_KEY="REPLACEME"

Dados de indexação

Baixe o arquivo conversations.json , que contém conversas entre clientes e atendentes do call center da nossa fictícia empresa de seguros residenciais. Coloque o arquivo no diretório raiz da aplicação, junto com os dois arquivos Python e o arquivo .env. arquivo que você criou anteriormente. Segue abaixo um exemplo do conteúdo do arquivo.

{
"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."
}

Cole o código abaixo em index.py que cuida da indexação dos dados.

# 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()

Executar o código acima cria um índice no Elasticsearch, armazenando os embeddings no índice do Elasticsearch chamado convo_index.

Caso precise de explicações sobre o IngestionPipeline do LlamaIndex, consulte a publicação anterior na seção Criar IngestionPipeline.

Consultando

Na postagem anterior, usamos um LLM local para consulta.

Neste post, utilizamos o LLM público da OpenAI, conforme mostrado abaixo.

# 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)

O código acima imprime a resposta da OpenAI conforme abaixo.

Os clientes apresentaram diversas reclamações relacionadas a problemas com água, incluindo danos causados por água em porões, canos estourados, danos causados por granizo em telhados e recusa de indenizações por motivos como falta de notificação em tempo hábil, problemas de manutenção, desgaste gradual e danos preexistentes. Em todos os casos, os clientes expressaram frustração com as negativas de seus pedidos e buscaram avaliações e decisões justas em relação às suas reivindicações.

Mascaramento de informações pessoais identificáveis (PII) em RAG

O que abordamos até agora envolve o envio de documentos tal como estão para a OpenAI, juntamente com a consulta do usuário.

No pipeline RAG, após a recuperação do contexto relevante de um repositório Vector, temos a oportunidade de mascarar informações pessoais identificáveis (PII) e informações sensíveis antes de enviar a consulta e o contexto para o LLM.

Existem várias maneiras de mascarar informações pessoais identificáveis (PII) antes de enviá-las a um LLM externo, cada uma com seus próprios méritos. Analisamos algumas das opções abaixo.

  1. Utilizando bibliotecas de PNL como spacy.io ou Presidio (biblioteca de código aberto mantida pela Microsoft).

  2. Utilizando o LlamaIndex sem nenhuma configuração adicional. NERPIINodePostprocessor.

  3. Utilizando LLMs locais via PIINodePostprocessor

Após implementar a lógica de mascaramento usando qualquer um dos métodos acima, você pode configurar o IngestionPipeline do LlamaIndex com um PostProcessor (um personalizado ou qualquer um dos PostProcessors prontos para uso do LlamaIndex).

Utilizando bibliotecas de PNL

Como parte do pipeline RAG, poderíamos mascarar dados sensíveis usando bibliotecas de PNL (Processamento de Linguagem Natural). Nesta demonstração, usaremos o pacote spacy.io.

Crie um novo arquivo query_masking_nlp.py e adicione o código abaixo.

# 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]
   ) -> 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)

A resposta do LLM é apresentada abaixo.

Os clientes apresentaram diversas reclamações relacionadas a problemas com água, incluindo danos causados pela água em porões, canos estourados, danos causados por granizo em telhados e inundações durante chuvas fortes. Essas reclamações têm gerado frustrações devido a negativas baseadas em motivos como falta de notificação em tempo hábil, problemas de manutenção, desgaste gradual e danos preexistentes. Os clientes expressaram decepção, estresse e dificuldades financeiras em decorrência dessas negativas de indenização, buscando avaliações justas e análises minuciosas de suas solicitações. Alguns clientes também enfrentaram atrasos no processamento de sinistros, causando ainda mais insatisfação com o serviço prestado pela seguradora.

No código acima, ao criar o Llama Index QueryEngine, fornecemos um CustomPostProcessor.

A lógica que é invocada pelo QueryEngine é definida no método _postprocess_nodes de CustomPostProcessor. Estamos utilizando a biblioteca SpaCy.io para detectar entidades nomeadas em nossos documentos e, em seguida, usamos expressões regulares para substituir esses nomes, bem como informações confidenciais, antes de enviá-los para o LLM.

Abaixo, seguem como exemplo trechos de conversas originais e da conversa mascarada criada pelo CustomPostProcessor.

Texto original:

Cliente: Olá, meu nome é Matthew Lopez, minha data de nascimento é 12 de outubro de 1984 e moro no endereço 456 Cedar St, Smalltown, NY 34567. Meu número de apólice é TUV8901. Agente: Boa tarde, Matthew. Como posso te ajudar hoje? Cliente: Olá, estou extremamente decepcionado com a decisão da sua empresa de negar minha solicitação.

Texto mascarado pelo CustomPostProcessor.

Cliente: Olá, meu nome é [MASKED], meu nome é [MASKED] e meu nome é [MASKED], e moro na Rua Cedar, 456, [MASKED], [MASKED] 34567. Meu número de apólice é [MASKED]. Agente: Boa tarde, [MASKED]. Como posso te ajudar hoje? Cliente: Olá, estou extremamente decepcionado com a decisão da sua empresa de negar minha solicitação.

Observação:

Identificar e mascarar informações pessoais identificáveis e informações sensíveis não é uma tarefa simples. Lidar com os diversos formatos e semânticas de informações sensíveis exige um bom conhecimento do seu domínio e dos seus dados. Embora o código apresentado acima possa funcionar para alguns casos de uso, talvez seja necessário modificá-lo com base em suas necessidades e testes.

Utilizando o LlamaIndex sem nenhuma configuração adicional. NERPIINodePostprocessor

A LlamaIndex facilitou a proteção de informações de identificação pessoal (PII) em um pipeline RAG ao introduzir NERPIINodePostprocessor.

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)

A resposta é a seguinte:

Os clientes apresentaram reclamações relacionadas a incêndios e danos às suas propriedades. Em um dos casos, um pedido de indenização por danos causados por incêndio em uma garagem foi negado devido à exclusão de incêndio criminoso da cobertura. Outro cliente apresentou uma reclamação por danos causados por incêndio em sua casa, que foram cobertos pela apólice. Além disso, um cliente relatou um incêndio na cozinha e recebeu a garantia de que os danos causados pelo fogo estavam cobertos.

Utilizando LLMs locais via PIINodePostprocessor

Também poderíamos utilizar um LLM executado localmente ou em sua rede privada para realizar o trabalho de mascaramento antes de enviar os dados para um LLM público.

Usaremos o Mistral, executado no Ollama em sua máquina local, para fazer o mascaramento.

Execute o Mistral localmente

Baixe e instale o Ollama. Após instalar o Ollama, execute este comando para baixar e executar o mistral.

ollama run mistral

Pode levar alguns minutos para baixar e executar o modelo localmente pela primeira vez. Verifique se o mistral está ativo fazendo uma pergunta como a seguinte: "Escreva um poema sobre nuvens" e veja se você gostou do poema. Mantenha o Ollama em execução, pois precisaremos interagir com o modelo Mistral posteriormente por meio de código.

Crie um novo arquivo chamado query_masking_local_LLM.py e adicione o código abaixo.

# 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)

A resposta será algo semelhante ao que é mostrado abaixo.

Os clientes apresentaram reclamações relacionadas a incêndios e danos às suas propriedades. Em um dos casos, um pedido de indenização por danos causados por incêndio em uma garagem foi negado devido à exclusão de incêndio criminoso da cobertura. Outro cliente apresentou uma reclamação por danos causados por incêndio em sua casa, que foram cobertos pela apólice. Além disso, um cliente relatou um incêndio na cozinha e recebeu a garantia de que os danos causados pelo fogo estavam cobertos.

Conclusão

Neste post, mostramos como você pode proteger informações pessoais identificáveis (PII) e dados sensíveis ao usar LLMs públicos em um fluxo RAG. Demonstramos diversas maneiras de alcançar esse objetivo. É altamente recomendável testar essas abordagens com base em seu caso de uso e necessidades antes de adotá-las.

Conteúdo relacionado

Técnicas avançadas de RAG, parte 2: Consultas e testes

Han Xiang Choong

Resolução de entidades com Elasticsearch, parte 4: O desafio definitivo

Jessica Moszkowicz

Automatização da análise de logs no Streams com ML

Nastia Havriushenko

Criando um agente de IA para RH com Elastic Agent Builder e GPT-OSS

Tomás Murúa

Técnicas avançadas de RAG, parte 1: Processamento de dados

Han Xiang Choong

Pronto para criar buscas de última geração?

Uma pesquisa suficientemente avançada não se consegue apenas com o esforço de uma só pessoa. O Elasticsearch é impulsionado por cientistas de dados, especialistas em operações de aprendizado de máquina, engenheiros e muitos outros que são tão apaixonados por buscas quanto você. Vamos nos conectar e trabalhar juntos para construir a experiência de busca mágica que lhe trará os resultados desejados.

Experimente você mesmo(a)