Construire un RAG avec Milvus + PII Masker
Les IIP (informations personnelles identifiables) sont un type de données sensibles qui peuvent être utilisées pour identifier des personnes.
PIIMasker, développé par HydroX AI, est un outil open-source avancé conçu pour protéger vos données sensibles en exploitant des modèles d'IA de pointe. Que vous traitiez des données clients, réalisiez des analyses de données ou assuriez la conformité avec les réglementations en matière de confidentialité, PII Masker fournit une solution robuste et évolutive pour garder vos informations sécurisées.
Dans ce tutoriel, nous allons montrer comment utiliser PII Masker avec Milvus pour protéger les données privées dans les applications RAG (Retrieval-Augmented Generation). En combinant les atouts des capacités de masquage des données de PII Masker avec la récupération efficace des données de Milvus, vous pouvez créer des pipelines sécurisés et conformes à la confidentialité pour traiter les informations sensibles en toute confiance. Cette approche garantit que vos applications sont équipées pour répondre aux normes de confidentialité et protéger efficacement les données des utilisateurs.
Préparation
Démarrer avec PII Masker
Suivez le guide d'installation de PII Masker pour installer les dépendances requises et télécharger le modèle. Voici un guide simple :
$ git clone https://github.com/HydroXai/pii-masker-v1.git
$ cd pii-masker-v1/pii-masker
Téléchargez le modèle à partir dehttps://huggingface.co/hydroxai/pii_model_weight
, et remplacez-le par les fichiers dans : pii-masker/output_model/deberta3base_1024/
Dépendances et environnement
$ pip install --upgrade pymilvus openai requests tqdm dataset
Nous utiliserons OpenAI comme LLM dans cet exemple. Vous devez préparer la clé api OPENAI_API_KEY
comme variable d'environnement.
$ export OPENAI_API_KEY=sk-***********
Ensuite, vous pouvez créer un notebook python ou jupyter pour exécuter le code suivant.
Préparer les données
Générons quelques fausses lignes contenant des informations PII à des fins de test ou de démonstration.
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.",
]
Masquer les données avec PIIMasker
Initialisons l'objet PIIMasker et chargeons le modèle.
from model import PIIMasker
masker = PIIMasker()
Nous masquons ensuite les IIP d'une liste de lignes de texte et imprimons les résultats masqués.
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
Préparer le modèle d'intégration
Nous initialisons le client OpenAI pour préparer le modèle d'intégration.
from openai import OpenAI
openai_client = OpenAI()
Définir une fonction pour générer des encastrements de texte à l'aide du client OpenAI. Nous utilisons le modèle text-embedding-3-small
comme exemple.
def emb_text(text):
return (
openai_client.embeddings.create(input=text, model="text-embedding-3-small")
.data[0]
.embedding
)
Générer un embedding de test et imprimer sa dimension et ses premiers éléments.
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]
Chargement des données dans Milvus
Créer la collection
from pymilvus import MilvusClient
milvus_client = MilvusClient(uri="./milvus_demo.db")
Comme pour l'argument de MilvusClient
:
- Définir
uri
comme fichier local, par exemple./milvus.db
, est la méthode la plus pratique, car elle utilise automatiquement Milvus Lite pour stocker toutes les données dans ce fichier. - Si vous avez des données à grande échelle, par exemple plus d'un million de vecteurs, vous pouvez configurer un serveur Milvus plus performant sur Docker ou Kubernetes. Dans cette configuration, veuillez utiliser l'adresse et le port du serveur comme uri, par exemple
http://localhost:19530
. Si vous activez la fonction d'authentification sur Milvus, utilisez "<votre_nom_d'utilisateur>:<votre_mot_de_passe>" comme jeton, sinon ne définissez pas le jeton. - Si vous souhaitez utiliser Zilliz Cloud, le service en nuage entièrement géré pour Milvus, réglez les paramètres
uri
ettoken
, qui correspondent au point de terminaison public et à la clé Api dans Zilliz Cloud.
Vérifier si la collection existe déjà et la supprimer si c'est le cas.
collection_name = "my_rag_collection"
if milvus_client.has_collection(collection_name):
milvus_client.drop_collection(collection_name)
Créer une nouvelle collection avec les paramètres spécifiés.
Si nous ne spécifions aucune information de champ, Milvus créera automatiquement un champ id
par défaut pour la clé primaire et un champ vector
pour stocker les données vectorielles. Un champ JSON réservé est utilisé pour stocker les champs non définis par le schéma et leurs valeurs.
milvus_client.create_collection(
collection_name=collection_name,
dimension=embedding_dim,
metric_type="IP", # Inner product distance
consistency_level="Strong", # Strong consistency level
)
Insérer des données
Parcourez les lignes de texte masqué, créez des incorporations, puis insérez les données dans Milvus.
Voici un nouveau champ text
, qui est un champ non défini dans le schéma de la collection. Il sera automatiquement ajouté au champ dynamique JSON réservé, qui peut être traité comme un champ normal à un niveau élevé.
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}
Construire un RAG
Récupérer des données pour une requête
Spécifions une question sur les documents.
question = "What was the office address of Hiroshi's partner from Munich?"
Cherchons la question dans la collection et récupérons la correspondance sémantique 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
)
Jetons un coup d'œil aux résultats de la recherche de la requête.
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
]
]
Utiliser LLM pour obtenir une réponse RAG
Convertir les documents récupérés dans un format de chaîne.
context = "\n".join(
[line_with_distance[0] for line_with_distance in retrieved_lines_with_distances]
)
Définir les invites du système et de l'utilisateur pour le modèle de langue.
Note : Nous disons à LLM que s'il n'y a pas d'informations utiles dans les extraits, il suffit de dire "Je ne sais pas".
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>
"""
Utiliser OpenAI ChatGPT pour générer une réponse basée sur les invites.
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.
Ici, nous pouvons voir que, puisque nous avons remplacé les IIP par des masques, le LLM ne peut pas obtenir les informations des IIP dans le contexte. Il répond donc : "Je ne sais pas". De cette manière, nous pouvons protéger efficacement la vie privée des utilisateurs.