diff --git a/docs/docs/reference/gen_notebooks/pii.md b/docs/docs/reference/gen_notebooks/pii.md index 001aca82a615..c4e7149d0413 100644 --- a/docs/docs/reference/gen_notebooks/pii.md +++ b/docs/docs/reference/gen_notebooks/pii.md @@ -15,22 +15,32 @@ title: Handling and Redacting PII -# How to use Weave with PII data: +# How to use Weave with PII data -In this tutorial, we'll demonstrate how to utilize Weave while ensuring your Personally Identifiable Information (PII) data remains private. Weave supports removing PII from LLM calls and preventing PII from being displayed in the Weave UI. +In this guide, you'll learn how to use W&B Weave while ensuring your Personally Identifiable Information (PII) data remains private. The guide demonstrates the following methods to identify, redact and anonymize PII data: -To detect and protect our PII data, we'll identify and redact PII data and optionally anonymize it with the following methods: 1. __Regular expressions__ to identify PII data and redact it. 2. __Microsoft's [Presidio](https://microsoft.github.io/presidio/)__, a python-based data protection SDK. This tool provides redaction and replacement functionalities. 3. __[Faker](https://faker.readthedocs.io/en/master/)__, a Python library to generate fake data, combined with Presidio to anonymize PII data. -Additionally, we'll make use of _Weave Ops input/output logging customization_ to seamlessly integrate PII redaction and anonymization into the workflow. See [here](https://weave-docs.wandb.ai/guides/tracking/ops/#customize-logged-inputs-and-outputs) for more information. +Additionally, you'll learn how to use _`weave.op` input/output logging customization_ and _`autopatch_settings`_ to integrate PII redaction and anonymization into the workflow. For more information, see [Customize logged inputs and outputs](https://weave-docs.wandb.ai/guides/tracking/ops/#customize-logged-inputs-and-outputs). -For this use-case, we will leverage Anthropic's Claude Sonnet to perform sentiment analysis while tracing the LLM calls using Weave's [Traces](https://wandb.github.io/weave/quickstart). Sonnet will receive a block of text and output one of the following sentiment classifications: _positive_, _negative_, or _neutral_. +To get started, do the following: -## Overview of Weave Ops Input/Output Logging Customization +1. Review the [Overview](#overview) section. +2. Complete the [prerequisites](#prerequisites). +3. Review the [available methods](#redaction-methods-overview) for identifying, redacting and anonymizing PII data. +4. [Apply the methods to Weave calls](#apply-the-methods-to-weave-calls). -Weave Ops support defining input and output postprocessing functions. These functions allow you to modify the data that is passed to your LLM call or logged to Weave, respectively. +## Overview + +The following section provides an overview of input and output logging using `weave.op`, as well as best practices for working with PII data in Weave. + +### Customize input and output logging using `weave.op` + +Weave Ops allow you to define input and output postprocessing functions. Using these functions, you can modify the data that is passed to your LLM call or logged to Weave. + +In the following example, two postprocessing functions are defined and passed as arguments to `weave.op()`. ```python from dataclasses import dataclass @@ -60,14 +70,33 @@ def some_llm_call(a: int, hide_me: str) -> CustomObject: return CustomObject(x=a, secret_password=hide_me) ``` -# Setup +### Best practices for using Weave with PII data + +Before using Weave with PII data, review the best practices for using Weave with PII data. + +#### During testing +- Log anonymized data to check PII detection +- Track PII handling processes with Weave Traces +- Measure anonymization performance without exposing real PII + +#### In production +- Never log raw PII +- Encrypt sensitive fields before logging + +#### Encryption tips +- Use reversible encryption for data you need to decrypt later +- Apply one-way hashing for unique IDs you don't need to reverse +- Consider specialized encryption for data you need to analyze while encrypted + +## Prerequisites -Let's install the required packages and set up our API keys. Your Weights & Biases API key can be found [here](https://wandb.ai/authorize), and your Anthropic API keys are [here](https://console.anthropic.com/settings/keys). +1. First, install the required packages. ```python %%capture # @title required python packages: +!pip install cryptography !pip install presidio_analyzer !pip install presidio_anonymizer !python -m spacy download en_core_web_lg # Presidio uses spacy NLP engine @@ -78,6 +107,11 @@ Let's install the required packages and set up our API keys. Your Weights & Bias !pip install cryptography # to encrypt our data ``` +2. Set up your API keys. You can find your API keys at the following links. + + - [W&B](https://wandb.ai/authorize) + - [Anthropic](https://console.anthropic.com/settings/keys). + ```python %%capture @@ -90,6 +124,8 @@ _ = set_env("ANTHROPIC_API_KEY") _ = set_env("WANDB_API_KEY") ``` +3. Initialize your Weave project. + ```python import weave @@ -99,7 +135,7 @@ WEAVE_PROJECT = "pii_cookbook" weave.init(WEAVE_PROJECT) ``` -Let's load our initial PII data. For demonstration purposes, we'll use a dataset containing 10 text blocks. A larger dataset with 1000 entries is available. +4. Load the demo PII dataset, which contains 10 text blocks. ```python @@ -112,11 +148,20 @@ pii_data = response.json() print('PII data first sample: "' + pii_data[0]["text"] + '"') ``` -# Redaction Methods Implementation +## Redaction methods overview -## Method 1: Regular Expression Filtering +Once you've completed the [setup](#setup), you can -Our initial method is to use [regular expressions (regex)](https://docs.python.org/3/library/re.html) to identify PII data and redact it. It allows us to define patterns that can match various formats of sensitive information like phone numbers, email addresses, and social security numbers. By using regex, we can scan through large volumes of text and replace or redact information without the need for more complex NLP techniques. +To detect and protect our PII data, we'll identify and redact PII data and optionally anonymize it using the following methods: + +1. __Regular expressions__ to identify PII data and redact it. +2. __Microsoft [Presidio](https://microsoft.github.io/presidio/)__, a Python-based data protection SDK that provides redaction and replacement functionality. +3. __[Faker](https://faker.readthedocs.io/en/master/)__, a Python library for generating fake data. + + +### Method 1: Filter using regular expressions + +[Regular expressions (regex)](https://docs.python.org/3/library/re.html) are the simplest method to identify and redact PII data. Regex allows you to define patterns that can match various formats of sensitive information like phone numbers, email addresses, and social security numbers. Using regex, you can scan through large volumes of text and replace or redact information without the need for more complex NLP techniques. ```python @@ -181,15 +226,10 @@ print(f"Raw text:\n\t{test_text}") print(f"Redacted text:\n\t{cleaned_text}") ``` -## Method 2: Microsoft Presidio Redaction -Our next method involves complete removal of PII data using Presidio. This approach redacts PII and replaces it with a placeholder representing the PII type. - -For example: -`"My name is Alex"` becomes `"My name is "`. +### Method 2: Redact using Microsoft Presidio +The next method involves complete removal of PII data using [Microsoft Presidio](https://microsoft.github.io/presidio/). Presidio redacts PII and replaces it with a placeholder representing the PII type. For example, Presidio replaces `Alex` in `"My name is Alex"` with ``. -Presidio comes with a built-in [list of recognizable entities](https://microsoft.github.io/presidio/supported_entities/). We can select the ones that are important for our use case. In the below example, we redact names, phone numbers, locations, email addresses, and US Social Security Numbers. - -We'll then encapsulate the Presidio process into a function. +Presidio comes with a built-in support for [common entities](https://microsoft.github.io/presidio/supported_entities/). In the below example, we redact all entities that are a `PHONE_NUMBER`, `PERSON`, `LOCATION`, `EMAIL_ADDRESS` or `US_SSN`. The Presidio process is encapsulated in a function. ```python @@ -229,17 +269,17 @@ print(f"Raw text:\n\t{text}") print(f"Redacted text:\n\t{anonymized_text}") ``` -## Method 3: Anonymization with Replacement using Fakr and Presidio +### Method 3: Anonymize with replacement using Faker and Presidio -Instead of redacting text, we can anonymize it by swapping PII (like names and phone numbers) with fake data generated using the [Faker](https://faker.readthedocs.io/en/master/) Python library. For example: +Instead of redacting text, you can anonymize it by using MS Presidio to swap PII like names and phone numbers with fake data generated using the [Faker](https://faker.readthedocs.io/en/master/) Python library. For example, suppose you have the following data: `"My name is Raphael and I like to fish. My phone number is 212-555-5555"` -might become +Once the data has been processed using Presidio and Faker, it might look like: `"My name is Katherine Dixon and I like to fish. My phone number is 667.431.7379"` -To effectively utilize Presidio, we must supply references to our custom operators. These operators will direct Presidio to the functions responsible for swapping PII with fake data. +To effectively use Presidio and Faker together, we must supply references to our custom operators. These operators will direct Presidio to the Faker functions responsible for swapping PII with fake data. ```python @@ -285,7 +325,7 @@ print(f"Raw text:\n\t{text_to_anonymize}") print(f"Anonymized text:\n\t{anonymized_results.text}") ``` -Let's consolidate our code into a single class and expand the list of entities to include the additional ones we identified earlier. +Let's consolidate our code into a single class and expand the list of entities to include the additional ones identified earlier. ```python @@ -348,19 +388,55 @@ print(f"Raw text:\n\t{text_to_anonymize}") print(f"Anonymized text:\n\t{anonymized_text}") ``` -# Applying the Methods to Weave Calls +### Method 4: Use `autopatch_settings` + +You can use `autopatch_settings` to configure PII handling directly during initialization for one or more of the supported LLM integrations. The advantages of this method are: -In these examples we will integrate our PII redaction and anonymization methods into Weave Models, and preview the results in Weave Traces. +1. PII handling logic is centralized and scoped at initialization, reducing the need for scattered custom logic. +2. PII processing workflows can be customized or disabled entirely for specific intergations. -We'll create a [Weave Model](https://wandb.github.io/weave/guides/core-types/models) which is a combination of data (which can include configuration, trained model weights, or other information) and code that defines how the model operates. +To use `autopatch_settings` to configure PII handling, define `postprocess_inputs` and/or `postprocess_output` in `op_settings` for any one of the supported LLM integrations. -In this model, we will include our predict function where the Anthropic API will be called. Additionally, we will include our postprocessing functions to ensure that our PII data is redacted or anonymized before it is sent to the LLM. +```python + +def postprocess(inputs: dict) -> dict: + if "SENSITIVE_KEY" in inputs: + inputs["SENSITIVE_KEY"] = "REDACTED" + return inputs + +client = weave.init( + ..., + autopatch_settings={ + "openai": { + "op_settings": { + "postprocess_inputs": postprocess, + "postprocess_output": ..., + } + }, + "anthropic": { + "op_settings": { + "postprocess_inputs": ..., + "postprocess_output": ..., + } + } + }, +) +``` -Once you run this code you will receive a links to the Weave project page as well as the specific trace (LLM calls)you ran. -## Regex Method +## Apply the methods to Weave calls -In the simplest case, we can use regex to identify and redact PII data in the original text. +In the following examples, we will integrate our PII redaction and anonymization methods into Weave Models and preview the results in Weave Traces. + +First, we'll create a [Weave Model](https://wandb.github.io/weave/guides/core-types/models). A Weave Model is a combination of information like configuration settings, model weights, and code that defines how the model operates. + +In our model, we will include our predict function where the Anthropic API will be called. Anthropic's Claude Sonnet is used to perform sentiment analysis while tracing LLM calls using [Traces](https://wandb.github.io/weave/quickstart). Claude Sonnet will receive a block of text and output one of the following sentiment classifications: _positive_, _negative_, or _neutral_. Additionally, we will include our postprocessing functions to ensure that our PII data is redacted or anonymized before it is sent to the LLM. + +Once you run this code, you will receive a links to the Weave project page, as well as the specific trace (LLM calls) you ran. + +### Regex method + +In the simplest case, we can use regex to identify and redact PII data from the original text. ```python @@ -420,9 +496,9 @@ for entry in pii_data: await model.predict(entry["text"]) ``` -## Presidio Redaction Method +### Presidio redaction method -Here we will use Presidio to identify and redact PII data in the original text. +Next, we will use Presidio to identify and redact PII data from the original text. ![](../../media/pii/redact.png) @@ -484,9 +560,9 @@ for entry in pii_data: await model.predict(entry["text"]) ``` -## Faker + Presidio Replacement Method +### Faker and Presidio replacement method -Here we will have Faker generate anonymized replacement PII data and use Presidio to identify and replace the PII data in the original text. +In this example, we use Faker to generate anonymized replacement PII data and use Presidio to identify and replace the PII data in the original text. ![](../../media/pii/replace.png) @@ -551,27 +627,81 @@ for entry in pii_data: await model.predict(entry["text"]) ``` -## Checklist for Safely Using Weave with PII Data +### `autopatch_settings` method -### During Testing -- Log anonymized data to check PII detection -- Track PII handling processes with Weave Traces -- Measure anonymization performance without exposing real PII +In the following example, we set `postprocess_inputs` for `anthropic` to the `postprocess_inputs_regex()` function () at initialization. The `postprocess_inputs_regex` function applies the`redact_with_regex` method defined in [Method 1: Regular Expression Filtering](#method-1-regular-expression-filtering). Now, `redact_with_regex` will be applied to all inputs to any `anthropic` models. -### In Production -- Never log raw PII -- Encrypt sensitive fields before logging -### Encryption Tips -- Use reversible encryption for data you need to decrypt later -- Apply one-way hashing for unique IDs you don't need to reverse -- Consider specialized encryption for data you need to analyze while encrypted +```python +import json +from typing import Any + +import anthropic + +import weave + +client = weave.init( + ..., + autopatch_settings={ + "anthropic": { + "op_settings": { + "postprocess_inputs": postprocess_inputs_regex, + } + } + }, +) + + +# Define an input postprocessing function that applies our regex redaction for the model prediction Weave Op +def postprocess_inputs_regex(inputs: dict[str, Any]) -> dict: + inputs["text_block"] = redact_with_regex(inputs["text_block"]) + return inputs + + +# Weave model / predict function +class sentiment_analysis_regex_pii_model(weave.Model): + model_name: str + system_prompt: str + temperature: int + + async def predict(self, text_block: str) -> dict: + client = anthropic.AsyncAnthropic() + response = await client.messages.create( + max_tokens=1024, + model=self.model_name, + system=self.system_prompt, + messages=[ + {"role": "user", "content": [{"type": "text", "text": text_block}]} + ], + ) + result = response.content[0].text + if result is None: + raise ValueError("No response from model") + parsed = json.loads(result) + return parsed +``` + + +```python +# create our LLM model with a system prompt +model = sentiment_analysis_regex_pii_model( + name="claude-3-sonnet", + model_name="claude-3-5-sonnet-20240620", + system_prompt='You are a Sentiment Analysis classifier. You will be classifying text based on their sentiment. Your input will be a block of text. You will answer with one the following rating option["positive", "negative", "neutral"]. Your answer should be one word in json format: {classification}. Ensure that it is valid JSON.', + temperature=0, +) + +print("Model: ", model) +# for every block of text, anonymized first and then predict +for entry in pii_data: + await model.predict(entry["text"]) +``` + +### (Optional) Encrypt your data -
- (Optional) Encrypting our data ![](../../media/pii/encrypt.png) -In addition to anonymizing PII, we can add an extra layer of security by encrypting our data using the cryptography library's [Fernet](https://cryptography.io/en/latest/fernet/) symmetric encryption. This approach ensures that even if the anonymized data is intercepted, it remains unreadable without the encryption key. +In addition to anonymizing PII, you can add an extra layer of security by encrypting your data using the cryptography library's [Fernet](https://cryptography.io/en/latest/fernet/) symmetric encryption. This approach ensures that even if the anonymized data is intercepted, it remains unreadable without the encryption key. ```python import os @@ -666,4 +796,3 @@ for entry in pii_data: encrypted_input = EncryptedSentimentAnalysisInput.encrypt(entry["text"]) await model.predict(encrypted_input) ``` -
diff --git a/docs/notebooks/pii.ipynb b/docs/notebooks/pii.ipynb index ad4650105f99..956303eeca13 100644 --- a/docs/notebooks/pii.ipynb +++ b/docs/notebooks/pii.ipynb @@ -10,7 +10,6 @@ "---\n", "docusaurus_head_meta::end -->\n", "\n", - "" ] }, @@ -20,7 +19,7 @@ "id": "m752k2fWKDql" }, "source": [ - "# How to use Weave with PII data:" + "# How to use Weave with PII data" ] }, { @@ -29,25 +28,35 @@ "id": "C70egOGRLCgm" }, "source": [ - "In this tutorial, we'll demonstrate how to utilize Weave while ensuring your Personally Identifiable Information (PII) data remains private. Weave supports removing PII from LLM calls and preventing PII from being displayed in the Weave UI. \n", + "In this guide, you'll learn how to use W&B Weave while ensuring your Personally Identifiable Information (PII) data remains private. The guide demonstrates the following methods to identify, redact and anonymize PII data:\n", "\n", - "To detect and protect our PII data, we'll identify and redact PII data and optionally anonymize it with the following methods:\n", "1. __Regular expressions__ to identify PII data and redact it.\n", "2. __Microsoft's [Presidio](https://microsoft.github.io/presidio/)__, a python-based data protection SDK. This tool provides redaction and replacement functionalities.\n", "3. __[Faker](https://faker.readthedocs.io/en/master/)__, a Python library to generate fake data, combined with Presidio to anonymize PII data.\n", "\n", - "Additionally, we'll make use of _Weave Ops input/output logging customization_ to seamlessly integrate PII redaction and anonymization into the workflow. See [here](https://weave-docs.wandb.ai/guides/tracking/ops/#customize-logged-inputs-and-outputs) for more information.\n", + "Additionally, you'll learn how to use _`weave.op` input/output logging customization_ and _`autopatch_settings`_ to integrate PII redaction and anonymization into the workflow. For more information, see [Customize logged inputs and outputs](https://weave-docs.wandb.ai/guides/tracking/ops/#customize-logged-inputs-and-outputs).\n", "\n", - "For this use-case, we will leverage Anthropic's Claude Sonnet to perform sentiment analysis while tracing the LLM calls using Weave's [Traces](https://wandb.github.io/weave/quickstart). Sonnet will receive a block of text and output one of the following sentiment classifications: _positive_, _negative_, or _neutral_." + "To get started, do the following:\n", + "\n", + "1. Review the [Overview](#overview) section.\n", + "2. Complete the [prerequisites](#prerequisites).\n", + "3. Review the [available methods](#redaction-methods-overview) for identifying, redacting and anonymizing PII data.\n", + "4. [Apply the methods to Weave calls](#apply-the-methods-to-weave-calls)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Overview of Weave Ops Input/Output Logging Customization\n", + "## Overview \n", + "\n", + "The following section provides an overview of input and output logging using `weave.op`, as well as best practices for working with PII data in Weave.\n", + "\n", + "### Customize input and output logging using `weave.op`\n", + "\n", + "Weave Ops allow you to define input and output postprocessing functions. Using these functions, you can modify the data that is passed to your LLM call or logged to Weave.\n", "\n", - "Weave Ops support defining input and output postprocessing functions. These functions allow you to modify the data that is passed to your LLM call or logged to Weave, respectively." + "In the following example, two postprocessing functions are defined and passed as arguments to `weave.op()`." ] }, { @@ -87,19 +96,43 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Setup\n", + "### Best practices for using Weave with PII data \n", + "\n", + "Before using Weave with PII data, review the best practices for using Weave with PII data.\n", + "\n", + "#### During testing\n", + "- Log anonymized data to check PII detection\n", + "- Track PII handling processes with Weave Traces\n", + "- Measure anonymization performance without exposing real PII\n", + "\n", + "#### In production\n", + "- Never log raw PII\n", + "- Encrypt sensitive fields before logging\n", + "\n", + "#### Encryption tips\n", + "- Use reversible encryption for data you need to decrypt later\n", + "- Apply one-way hashing for unique IDs you don't need to reverse\n", + "- Consider specialized encryption for data you need to analyze while encrypted" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", "\n", - "Let's install the required packages and set up our API keys. Your Weights & Biases API key can be found [here](https://wandb.ai/authorize), and your Anthropic API keys are [here](https://console.anthropic.com/settings/keys)." + "1. First, install the required packages. " ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%capture\n", "# @title required python packages:\n", + "!pip install cryptography\n", "!pip install presidio_analyzer\n", "!pip install presidio_anonymizer\n", "!python -m spacy download en_core_web_lg # Presidio uses spacy NLP engine\n", @@ -110,6 +143,16 @@ "!pip install cryptography # to encrypt our data" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Set up your API keys. You can find your API keys at the following links.\n", + "\n", + " - [W&B](https://wandb.ai/authorize)\n", + " - [Anthropic](https://console.anthropic.com/settings/keys)." + ] + }, { "cell_type": "code", "execution_count": 4, @@ -126,6 +169,13 @@ "_ = set_env(\"WANDB_API_KEY\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Initialize your Weave project." + ] + }, { "cell_type": "code", "execution_count": 6, @@ -154,7 +204,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's load our initial PII data. For demonstration purposes, we'll use a dataset containing 10 text blocks. A larger dataset with 1000 entries is available." + "4. Load the demo PII dataset, which contains 10 text blocks. " ] }, { @@ -184,16 +234,24 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Redaction Methods Implementation" + "## Redaction methods overview\n", + "\n", + "Once you've completed the [setup](#setup), you can \n", + "\n", + "To detect and protect our PII data, we'll identify and redact PII data and optionally anonymize it using the following methods:\n", + "\n", + "1. __Regular expressions__ to identify PII data and redact it.\n", + "2. __Microsoft [Presidio](https://microsoft.github.io/presidio/)__, a Python-based data protection SDK that provides redaction and replacement functionality.\n", + "3. __[Faker](https://faker.readthedocs.io/en/master/)__, a Python library for generating fake data.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Method 1: Regular Expression Filtering\n", + "### Method 1: Filter using regular expressions\n", "\n", - "Our initial method is to use [regular expressions (regex)](https://docs.python.org/3/library/re.html) to identify PII data and redact it. It allows us to define patterns that can match various formats of sensitive information like phone numbers, email addresses, and social security numbers. By using regex, we can scan through large volumes of text and replace or redact information without the need for more complex NLP techniques. " + "[Regular expressions (regex)](https://docs.python.org/3/library/re.html) are the simplest method to identify and redact PII data. Regex allows you to define patterns that can match various formats of sensitive information like phone numbers, email addresses, and social security numbers. Using regex, you can scan through large volumes of text and replace or redact information without the need for more complex NLP techniques. " ] }, { @@ -287,15 +345,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Method 2: Microsoft Presidio Redaction\n", - "Our next method involves complete removal of PII data using Presidio. This approach redacts PII and replaces it with a placeholder representing the PII type. \n", - "\n", - "For example:\n", - "`\"My name is Alex\"` becomes `\"My name is \"`.\n", + "### Method 2: Redact using Microsoft Presidio \n", + "The next method involves complete removal of PII data using [Microsoft Presidio](https://microsoft.github.io/presidio/). Presidio redacts PII and replaces it with a placeholder representing the PII type. For example, Presidio replaces `Alex` in `\"My name is Alex\"` with ``.\n", "\n", - "Presidio comes with a built-in [list of recognizable entities](https://microsoft.github.io/presidio/supported_entities/). We can select the ones that are important for our use case. In the below example, we redact names, phone numbers, locations, email addresses, and US Social Security Numbers.\n", - "\n", - "We'll then encapsulate the Presidio process into a function." + "Presidio comes with a built-in support for [common entities](https://microsoft.github.io/presidio/supported_entities/). In the below example, we redact all entities that are a `PHONE_NUMBER`, `PERSON`, `LOCATION`, `EMAIL_ADDRESS` or `US_SSN`. The Presidio process is encapsulated in a function." ] }, { @@ -366,17 +419,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Method 3: Anonymization with Replacement using Fakr and Presidio\n", + "### Method 3: Anonymize with replacement using Faker and Presidio\n", "\n", - "Instead of redacting text, we can anonymize it by swapping PII (like names and phone numbers) with fake data generated using the [Faker](https://faker.readthedocs.io/en/master/) Python library. For example:\n", + "Instead of redacting text, you can anonymize it by using MS Presidio to swap PII like names and phone numbers with fake data generated using the [Faker](https://faker.readthedocs.io/en/master/) Python library. For example, suppose you have the following data:\n", "\n", "`\"My name is Raphael and I like to fish. My phone number is 212-555-5555\"` \n", "\n", - "might become\n", + "Once the data has been processed using Presidio and Faker, it might look like:\n", "\n", "`\"My name is Katherine Dixon and I like to fish. My phone number is 667.431.7379\"`\n", "\n", - "To effectively utilize Presidio, we must supply references to our custom operators. These operators will direct Presidio to the functions responsible for swapping PII with fake data." + "To effectively use Presidio and Faker together, we must supply references to our custom operators. These operators will direct Presidio to the Faker functions responsible for swapping PII with fake data." ] }, { @@ -446,7 +499,7 @@ "id": "R4HJcskpSYdL" }, "source": [ - "Let's consolidate our code into a single class and expand the list of entities to include the additional ones we identified earlier." + "Let's consolidate our code into a single class and expand the list of entities to include the additional ones identified earlier." ] }, { @@ -543,24 +596,64 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Applying the Methods to Weave Calls\n", + "### Method 4: Use `autopatch_settings` \n", + "\n", + "You can use `autopatch_settings` to configure PII handling directly during initialization for one or more of the supported LLM integrations. The advantages of this method are:\n", "\n", - "In these examples we will integrate our PII redaction and anonymization methods into Weave Models, and preview the results in Weave Traces.\n", + "1. PII handling logic is centralized and scoped at initialization, reducing the need for scattered custom logic.\n", + "2. PII processing workflows can be customized or disabled entirely for specific intergations.\n", "\n", - "We'll create a [Weave Model](https://wandb.github.io/weave/guides/core-types/models) which is a combination of data (which can include configuration, trained model weights, or other information) and code that defines how the model operates. \n", + "To use `autopatch_settings` to configure PII handling, define `postprocess_inputs` and/or `postprocess_output` in `op_settings` for any one of the supported LLM integrations. \n", "\n", - "In this model, we will include our predict function where the Anthropic API will be called. Additionally, we will include our postprocessing functions to ensure that our PII data is redacted or anonymized before it is sent to the LLM.\n", + "```python \n", + "\n", + "def postprocess(inputs: dict) -> dict:\n", + " if \"SENSITIVE_KEY\" in inputs:\n", + " inputs[\"SENSITIVE_KEY\"] = \"REDACTED\"\n", + " return inputs\n", "\n", - "Once you run this code you will receive a links to the Weave project page as well as the specific trace (LLM calls)you ran." + "client = weave.init(\n", + " ...,\n", + " autopatch_settings={\n", + " \"openai\": {\n", + " \"op_settings\": {\n", + " \"postprocess_inputs\": postprocess,\n", + " \"postprocess_output\": ...,\n", + " }\n", + " },\n", + " \"anthropic\": {\n", + " \"op_settings\": {\n", + " \"postprocess_inputs\": ...,\n", + " \"postprocess_output\": ...,\n", + " }\n", + " }\n", + " },\n", + ")\n", + "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Regex Method \n", + "## Apply the methods to Weave calls\n", + "\n", + "In the following examples, we will integrate our PII redaction and anonymization methods into Weave Models and preview the results in Weave Traces.\n", + "\n", + "First, we'll create a [Weave Model](https://wandb.github.io/weave/guides/core-types/models). A Weave Model is a combination of information like configuration settings, model weights, and code that defines how the model operates. \n", + "\n", + "In our model, we will include our predict function where the Anthropic API will be called. Anthropic's Claude Sonnet is used to perform sentiment analysis while tracing LLM calls using [Traces](https://wandb.github.io/weave/quickstart). Claude Sonnet will receive a block of text and output one of the following sentiment classifications: _positive_, _negative_, or _neutral_. Additionally, we will include our postprocessing functions to ensure that our PII data is redacted or anonymized before it is sent to the LLM.\n", "\n", - "In the simplest case, we can use regex to identify and redact PII data in the original text." + "Once you run this code, you will receive a links to the Weave project page, as well as the specific trace (LLM calls) you ran." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Regex method \n", + "\n", + "In the simplest case, we can use regex to identify and redact PII data from the original text." ] }, { @@ -651,9 +744,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Presidio Redaction Method\n", + "### Presidio redaction method\n", "\n", - "Here we will use Presidio to identify and redact PII data in the original text." + "Next, we will use Presidio to identify and redact PII data from the original text." ] }, { @@ -784,9 +877,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Faker + Presidio Replacement Method\n", + "### Faker and Presidio replacement method\n", "\n", - "Here we will have Faker generate anonymized replacement PII data and use Presidio to identify and replace the PII data in the original text.\n" + "In this example, we use Faker to generate anonymized replacement PII data and use Presidio to identify and replace the PII data in the original text.\n" ] }, { @@ -886,32 +979,94 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Checklist for Safely Using Weave with PII Data\n", + "### `autopatch_settings` method\n", "\n", - "### During Testing\n", - "- Log anonymized data to check PII detection\n", - "- Track PII handling processes with Weave Traces\n", - "- Measure anonymization performance without exposing real PII\n", + "In the following example, we set `postprocess_inputs` for `anthropic` to the `postprocess_inputs_regex()` function () at initialization. The `postprocess_inputs_regex` function applies the`redact_with_regex` method defined in [Method 1: Regular Expression Filtering](#method-1-regular-expression-filtering). Now, `redact_with_regex` will be applied to all inputs to any `anthropic` models." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "from typing import Any\n", "\n", - "### In Production\n", - "- Never log raw PII\n", - "- Encrypt sensitive fields before logging\n", + "import anthropic\n", "\n", - "### Encryption Tips\n", - "- Use reversible encryption for data you need to decrypt later\n", - "- Apply one-way hashing for unique IDs you don't need to reverse\n", - "- Consider specialized encryption for data you need to analyze while encrypted" + "import weave\n", + "\n", + "client = weave.init(\n", + " ...,\n", + " autopatch_settings={\n", + " \"anthropic\": {\n", + " \"op_settings\": {\n", + " \"postprocess_inputs\": postprocess_inputs_regex,\n", + " }\n", + " }\n", + " },\n", + ")\n", + "\n", + "\n", + "# Define an input postprocessing function that applies our regex redaction for the model prediction Weave Op\n", + "def postprocess_inputs_regex(inputs: dict[str, Any]) -> dict:\n", + " inputs[\"text_block\"] = redact_with_regex(inputs[\"text_block\"])\n", + " return inputs\n", + "\n", + "\n", + "# Weave model / predict function\n", + "class sentiment_analysis_regex_pii_model(weave.Model):\n", + " model_name: str\n", + " system_prompt: str\n", + " temperature: int\n", + "\n", + " async def predict(self, text_block: str) -> dict:\n", + " client = anthropic.AsyncAnthropic()\n", + " response = await client.messages.create(\n", + " max_tokens=1024,\n", + " model=self.model_name,\n", + " system=self.system_prompt,\n", + " messages=[\n", + " {\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": text_block}]}\n", + " ],\n", + " )\n", + " result = response.content[0].text\n", + " if result is None:\n", + " raise ValueError(\"No response from model\")\n", + " parsed = json.loads(result)\n", + " return parsed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# create our LLM model with a system prompt\n", + "model = sentiment_analysis_regex_pii_model(\n", + " name=\"claude-3-sonnet\",\n", + " model_name=\"claude-3-5-sonnet-20240620\",\n", + " system_prompt='You are a Sentiment Analysis classifier. You will be classifying text based on their sentiment. Your input will be a block of text. You will answer with one the following rating option[\"positive\", \"negative\", \"neutral\"]. Your answer should be one word in json format: {classification}. Ensure that it is valid JSON.',\n", + " temperature=0,\n", + ")\n", + "\n", + "print(\"Model: \", model)\n", + "# for every block of text, anonymized first and then predict\n", + "for entry in pii_data:\n", + " await model.predict(entry[\"text\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "
\n", - " (Optional) Encrypting our data \n", + "### (Optional) Encrypt your data \n", + "\n", "![](../../media/pii/encrypt.png)\n", "\n", - "In addition to anonymizing PII, we can add an extra layer of security by encrypting our data using the cryptography library's [Fernet](https://cryptography.io/en/latest/fernet/) symmetric encryption. This approach ensures that even if the anonymized data is intercepted, it remains unreadable without the encryption key.\n", + "In addition to anonymizing PII, you can add an extra layer of security by encrypting your data using the cryptography library's [Fernet](https://cryptography.io/en/latest/fernet/) symmetric encryption. This approach ensures that even if the anonymized data is intercepted, it remains unreadable without the encryption key.\n", "\n", "```python\n", "import os\n", @@ -1005,8 +1160,7 @@ "for entry in pii_data:\n", " encrypted_input = EncryptedSentimentAnalysisInput.encrypt(entry[\"text\"])\n", " await model.predict(encrypted_input)\n", - "```\n", - "
" + "```" ] } ],