Costruire RAG con Milvus + PII Masker
Le PII (Personally Identifiable Information) sono un tipo di dati sensibili che possono essere utilizzati per identificare le persone.
PII Masker, sviluppato da HydroX AI, è uno strumento open-source avanzato progettato per proteggere i vostri dati sensibili sfruttando modelli AI all'avanguardia. Che si tratti di gestire i dati dei clienti, di eseguire analisi dei dati o di garantire la conformità alle normative sulla privacy, PII Masker offre una soluzione robusta e scalabile per mantenere al sicuro le informazioni.
In questo tutorial mostreremo come utilizzare PII Masker con Milvus per proteggere i dati privati nelle applicazioni RAG (Retrieval-Augmented Generation). Combinando i punti di forza delle capacità di mascheramento dei dati di PII Masker con l'efficiente recupero dei dati di Milvus, è possibile creare pipeline sicure e conformi alla privacy per gestire le informazioni sensibili con fiducia. Questo approccio garantisce che le vostre applicazioni siano in grado di soddisfare gli standard di privacy e di proteggere efficacemente i dati degli utenti.
Preparazione
Iniziare con PII Masker
Seguite la guida all'installazione di PII Masker per installare le dipendenze necessarie e scaricare il modello. Ecco una semplice guida:
$ git clone https://github.com/HydroXai/pii-masker-v1.git
$ cd pii-masker-v1/pii-masker
Scaricare il modello dahttps://huggingface.co/hydroxai/pii_model_weight
e sostituirlo con i file in: pii-masker/output_model/deberta3base_1024/
Dipendenze e ambiente
$ pip install --upgrade pymilvus openai requests tqdm dataset
In questo esempio utilizzeremo OpenAI come LLM. È necessario preparare la chiave api OPENAI_API_KEY
come variabile d'ambiente.
$ export OPENAI_API_KEY=sk-***********
Quindi si può creare un notebook python o jupyter per eseguire il codice seguente.
Preparare i dati
Generiamo alcune righe false che contengono informazioni PII a scopo di test o dimostrazione.
text_lines = [
"Alice Johnson, a resident of Dublin, Ireland, attended a flower festival at Hyde Park on May 15, 2023. She entered the park at noon using her digital passport, number 23456789. Alice spent the afternoon admiring various flowers and plants, attending a gardening workshop, and having a light snack at one of the food stalls. While there, she met another visitor, Mr. Thompson, who was visiting from London. They exchanged tips on gardening and shared contact information: Mr. Thompson's address was 492, Pine Lane, and his cell phone number was +018.221.431-4517. Alice gave her contact details: home address, Ranch 16",
"Hiroshi Tanaka, a businessman from Tokyo, Japan, went to attend a tech expo at the Berlin Convention Center on November 10, 2023. He registered for the event at 9 AM using his digital passport, number Q-24567680. Hiroshi networked with industry professionals, participated in panel discussions, and had lunch with some potential partners. One of the partners he met was from Munich, and they decided to keep in touch: the partner's office address was given as house No. 12, Road 7, Block E. Hiroshi offered his business card with the address, 654 Sakura Road, Tokyo.",
"In an online forum discussion about culinary exchanges around the world, several participants shared their experiences. One user, Male, with the email 2022johndoe@example.com, shared his insights. He mentioned his ID code 1A2B3C4D5E and reference number L87654321 while residing in Italy but originally from Australia. He provided his +0-777-123-4567 and described his address at 456, Flavorful Lane, Pasta, IT, 00100.",
"Another user joined the conversation on the topic of international volunteering opportunities. Identified as Female, she used the email 2023janedoe@example.com to share her story. She noted her 9876543210123 and M1234567890123 while residing in Germany but originally from Brazil. She provided her +0-333-987-6543 and described her address at 789, Sunny Side Street, Berlin, DE, 10178.",
]
Mascherare i dati con PIIMasker
Inizializziamo l'oggetto PIIMasker e carichiamo il modello.
from model import PIIMasker
masker = PIIMasker()
Quindi mascheriamo le PII da un elenco di righe di testo e stampiamo i risultati mascherati.
masked_results = []
for full_text in text_lines:
masked_text, _ = masker.mask_pii(full_text)
masked_results.append(masked_text)
for res in masked_results:
print(res + "\n")
Alice [B-NAME] , a resident of Dublin Ireland attended flower festival at Hyde Park on May 15 2023 [B-PHONE_NUM] She entered the park noon using her digital passport number 23 [B-ID_NUM] [B-NAME] afternoon admiring various flowers and plants attending gardening workshop having light snack one food stalls While there she met another visitor Mr Thompson who was visiting from London They exchanged tips shared contact information : ' s address 492 [I-STREET_ADDRESS] his cell phone + [B-PHONE_NUM] [B-NAME] details home Ranch [B-STREET_ADDRESS]
Hiroshi [B-NAME] [I-STREET_ADDRESS] a businessman from Tokyo Japan went to attend tech expo at the Berlin Convention Center on November 10 2023 . He registered for event 9 AM using his digital passport number Q [B-ID_NUM] [B-NAME] with industry professionals participated in panel discussions and had lunch some potential partners One of he met was Munich they decided keep touch : partner ' s office address given as house No [I-STREET_ADDRESS] [B-NAME] business card 654 [B-STREET_ADDRESS]
In an online forum discussion about culinary exchanges around the world [I-STREET_ADDRESS] several participants shared their experiences [I-STREET_ADDRESS] One user Male with email 2022 [B-EMAIL] his insights He mentioned ID code 1 [B-ID_NUM] [I-PHONE_NUM] reference number L [B-ID_NUM] residing in Italy but originally from Australia provided + [B-PHONE_NUM] [I-PHONE_NUM] described address at 456 [I-STREET_ADDRESS]
Another user joined the conversation on topic of international volunteering opportunities . Identified as Female , she used email 2023 [B-EMAIL] share her story She noted 98 [B-ID_NUM] [I-PHONE_NUM] M [B-ID_NUM] residing in Germany but originally from Brazil provided + [B-PHONE_NUM] [I-PHONE_NUM] described address at 789 [I-STREET_ADDRESS] DE 10 178
Preparare il modello da incorporare
Inizializziamo il client OpenAI per preparare il modello di incorporamento.
from openai import OpenAI
openai_client = OpenAI()
Definiamo una funzione per generare embedding di testo utilizzando il client OpenAI. Utilizziamo il modello text-embedding-3-small
come esempio.
def emb_text(text):
return (
openai_client.embeddings.create(input=text, model="text-embedding-3-small")
.data[0]
.embedding
)
Generare un embedding di prova e stamparne la dimensione e i primi elementi.
test_embedding = emb_text("This is a test")
embedding_dim = len(test_embedding)
print(embedding_dim)
print(test_embedding[:10])
1536
[0.009889289736747742, -0.005578675772994757, 0.00683477520942688, -0.03805781528353691, -0.01824733428657055, -0.04121600463986397, -0.007636285852640867, 0.03225184231996536, 0.018949154764413834, 9.352207416668534e-05]
Caricare i dati in Milvus
Creare la collezione
from pymilvus import MilvusClient
milvus_client = MilvusClient(uri="./milvus_demo.db")
Come per l'argomento di MilvusClient
:
- L'impostazione di
uri
come file locale, ad esempio./milvus.db
, è il metodo più conveniente, poiché utilizza automaticamente Milvus Lite per memorizzare tutti i dati in questo file. - Se si dispone di una grande quantità di dati, ad esempio più di un milione di vettori, è possibile configurare un server Milvus più performante su Docker o Kubernetes. In questa configurazione, utilizzare l'indirizzo e la porta del server come uri, ad esempio
http://localhost:19530
. Se si abilita la funzione di autenticazione su Milvus, utilizzare "<nome_utente>:<password>" come token, altrimenti non impostare il token. - Se si desidera utilizzare Zilliz Cloud, il servizio cloud completamente gestito per Milvus, impostare
uri
etoken
, che corrispondono all'endpoint pubblico e alla chiave Api di Zilliz Cloud.
Verificare se la raccolta esiste già e, in caso affermativo, eliminarla.
collection_name = "my_rag_collection"
if milvus_client.has_collection(collection_name):
milvus_client.drop_collection(collection_name)
Creare una nuova raccolta con i parametri specificati.
Se non si specifica alcun campo, Milvus creerà automaticamente un campo predefinito id
per la chiave primaria e un campo vector
per memorizzare i dati vettoriali. Un campo JSON riservato viene utilizzato per memorizzare campi non definiti dalla mappa e i loro valori.
milvus_client.create_collection(
collection_name=collection_name,
dimension=embedding_dim,
metric_type="IP", # Inner product distance
consistency_level="Strong", # Strong consistency level
)
Inserire i dati
Si interviene sulle righe di testo mascherate, si creano le incorporazioni e si inseriscono i dati in Milvus.
Ecco un nuovo campo text
, che è un campo non definito nello schema della collezione. Verrà aggiunto automaticamente al campo dinamico JSON riservato, che può essere trattato come un campo normale ad alto livello.
from tqdm import tqdm
data = []
for i, line in enumerate(tqdm(masked_results, desc="Creating embeddings")):
data.append({"id": i, "vector": emb_text(line), "text": line})
milvus_client.insert(collection_name=collection_name, data=data)
Creating embeddings: 100%|██████████| 4/4 [00:01<00:00, 2.60it/s]
{'insert_count': 4, 'ids': [0, 1, 2, 3], 'cost': 0}
Costruire la RAG
Recuperare i dati per una query
Specifichiamo una domanda sui documenti.
question = "What was the office address of Hiroshi's partner from Munich?"
Cerchiamo la domanda nella raccolta e recuperiamo la corrispondenza semantica top-1.
search_res = milvus_client.search(
collection_name=collection_name,
data=[
emb_text(question)
], # Use the `emb_text` function to convert the question to an embedding vector
limit=1, # Return top 1 results
search_params={"metric_type": "IP", "params": {}}, # Inner product distance
output_fields=["text"], # Return the text field
)
Diamo un'occhiata ai risultati della ricerca della query
import json
retrieved_lines_with_distances = [
(res["entity"]["text"], res["distance"]) for res in search_res[0]
]
print(json.dumps(retrieved_lines_with_distances, indent=4))
[
[
"Hiroshi [B-NAME] [I-STREET_ADDRESS] a businessman from Tokyo Japan went to attend tech expo at the Berlin Convention Center on November 10 2023 . He registered for event 9 AM using his digital passport number Q [B-ID_NUM] [B-NAME] with industry professionals participated in panel discussions and had lunch some potential partners One of he met was Munich they decided keep touch : partner ' s office address given as house No [I-STREET_ADDRESS] [B-NAME] business card 654 [B-STREET_ADDRESS]",
0.6544462442398071
]
]
Utilizzare LLM per ottenere una risposta RAG
Convertire i documenti recuperati in un formato stringa.
context = "\n".join(
[line_with_distance[0] for line_with_distance in retrieved_lines_with_distances]
)
Definire le richieste del sistema e dell'utente per il Lanage Model.
Nota: diciamo a LLM che se non ci sono informazioni utili negli snippet, basta dire "non lo so".
SYSTEM_PROMPT = """
Human: You are an AI assistant. You are able to find answers to the questions from the contextual passage snippets provided. If there are no useful information in the snippets, just say "I don't know".
AI:
"""
USER_PROMPT = f"""
Use the following pieces of information enclosed in <context> tags to provide an answer to the question enclosed in <question> tags.
<context>
{context}
</context>
<question>
{question}
</question>
"""
Utilizzare OpenAI ChatGPT per generare una risposta basata sui prompt.
response = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": USER_PROMPT},
],
)
print(response.choices[0].message.content)
I don't know.
Qui possiamo vedere che, poiché abbiamo sostituito le PII con delle maschere, l'LLM non può ottenere le informazioni PII nel contesto. In questo modo, possiamo proteggere efficacemente la privacy degli utenti.