Protect Rules SDK

Define Protect rules, block a harmful LLM response before it reaches someone, and wire the check into an Anthropic call.

📝
TL;DR

Define a Protect ruleset, run it against a text response, and see Protect swap a rule-violating output for a safe fallback message before it ever reaches someone.

TimeDifficultyPackage
15 minIntermediateai-evaluation
Prerequisites
  • Future AGI account → app.futureagi.com
  • API keys: FI_API_KEY and FI_SECRET_KEY (see Get your API keys)
  • An ANTHROPIC_API_KEY (only needed for the Anthropic step)
  • Python 3.11+

Install

pip install ai-evaluation anthropic
export FI_API_KEY="your-api-key"
export FI_SECRET_KEY="your-secret-key"
export ANTHROPIC_API_KEY="your-anthropic-key"

Tutorial

Initialize the Protect client

Create a client authenticated with your Future AGI keys.

import os
from fi.evals import Protect

protector = Protect(
    fi_api_key=os.environ["FI_API_KEY"],
    fi_secret_key=os.environ["FI_SECRET_KEY"],
)

You should see no output. Protect only raises if the keys are missing, which you’ll hit in the troubleshooting table below.

Define the ruleset

Protect rules key off the SDK’s metric_map, not arbitrary metric names. The four canonical keys are toxicity, bias_detection, prompt_injection, and data_privacy_compliance. This ruleset blocks toxic content and prompt injection attempts.

rules = [
    {"metric": "toxicity"},
    {"metric": "prompt_injection"},
]

fallback_message = "This message cannot be displayed"

Only the SDK’s tone-matching branch accepts contains and type keys. Add either to a content_moderation or security rule and protect() raises SDKException before it runs a single check.

Apply Protect to a text

Run the ruleset against a single piece of text to see the pass path.

response = protector.protect(
    "Sure, I can process a refund for order #48213 - it'll land back on your card in 5-7 business days.",
    protect_rules=rules,
    action=fallback_message,
    reason=True,
    timeout=25000,  # milliseconds, not seconds
)
print(response)

You should see (illustrative):

{'status': 'passed', 'messages': "Sure, I can process a refund for order #48213 - it'll land back on your card in 5-7 business days.", 'reasons': ['All checks passed'], 'completed_rules': ['toxicity', 'prompt_injection'], 'uncompleted_rules': [], 'failed_rule': None, 'time_taken': 0.42}

A clean support reply passes every rule, so messages comes back unchanged and status reads passed.

Trip a rule and see the fallback

Run the same ruleset against text written to fail, so you can see the swap the TLDR promised.

unsafe_response = "You're an idiot for even asking that. Figure it out yourself."

blocked = protector.protect(
    unsafe_response,
    protect_rules=rules,
    action=fallback_message,
    reason=True,
    timeout=25000,
)
print(blocked)

You should see (illustrative):

{'status': 'failed', 'messages': 'This message cannot be displayed', 'reasons': ['toxicity check failed'], 'completed_rules': ['toxicity'], 'uncompleted_rules': ['prompt_injection'], 'failed_rule': 'toxicity', 'time_taken': 0.31}

status reads failed, and messages is no longer the original text - it’s fallback_message. This is the safe fallback the ruleset exists to enforce.

Guard an Anthropic response

Run the same ruleset against a real model response before it reaches someone.

from anthropic import Anthropic
from fi.evals import Protect

anthropic_client = Anthropic()
protector = Protect()  # reads FI_API_KEY / FI_SECRET_KEY from the environment

response = anthropic_client.messages.create(
    max_tokens=1000,
    model="claude-3-5-sonnet-20240620",
    messages=[
        {"role": "user", "content": "Hi, I am a student, can you help me with my homework?"}
    ],
)
response_text = response.content[0].text

protect_response = protector.protect(
    response_text,
    protect_rules=rules,
    action=fallback_message,
    reason=True,
    timeout=25000,
)

print(protect_response["messages"])

You should see the model’s homework-help answer printed unchanged: a helpful, on-topic reply doesn’t trip toxicity or prompt_injection. Swap the user message for something that provokes an unsafe response and protect_response["messages"] becomes fallback_message instead.

Use the standalone protect() function

Skip the Protect class entirely when you don’t need a reusable client.

from fi.evals import protect

protected_response = protect(
    "Your subscription renews on the 14th - you can cancel anytime from account settings.",
    protect_rules=[{"metric": "toxicity"}],
    action=fallback_message,
    reason=True,
    timeout=25000,
)
print(protected_response["messages"])

You should see the account text printed unchanged. protect() takes the same protect_rules, action, reason, and timeout arguments as Protect.protect(), just without instantiating a client first.

Read the reasons

Log reasons alongside status so a blocked response is auditable after the fact, not just silently swapped.

print(blocked["reasons"])
print(blocked["completed_rules"])
print(blocked["failed_rule"])

You should see something like:

['toxicity check failed']
['toxicity']
'toxicity'

reasons is a short summary, not one entry per rule - on a pass it’s just ['All checks passed']. For which rules ran and which one tripped, use completed_rules and failed_rule instead.

Troubleshooting

SymptomCauseFix
InvalidValueType on protector.protect(...)A rule uses a metric name outside the valid set ("Tone", "Toxicity", or any typo)Use one of toxicity, bias_detection, prompt_injection, data_privacy_compliance
SDKException mentioning contains or typeA toxicity or prompt_injection rule carries a contains or type keyDrop those keys. Only tone-matching rules accept them, and "Tone" itself isn’t a valid metric here
Call times out almost immediatelytimeout is in milliseconds; a value like 25 aborts after 25msPass a realistic value, e.g. timeout=25000 for roughly 25 seconds
InvalidAuthError on Protect(...) or protect(...)FI_API_KEY or FI_SECRET_KEY missing or unexportedRe-run the export block in the current shell, then re-run the script
ModuleNotFoundError: No module named 'fi.evals'ai-evaluation isn’t installed, or an unrelated fi package shadows itpip install ai-evaluation, and check pip show fi doesn’t point at a different package
anthropic.AuthenticationError on anthropic_client.messages.create(...)ANTHROPIC_API_KEY not exportedExport ANTHROPIC_API_KEY, or swap in a client for a provider key you have
protect_response["messages"] never changes even for an unsafe responseThe ruleset only covers metrics the response doesn’t tripAdd the relevant metric (e.g. bias_detection for biased output) or test with input written to trip the rule you’re checking

Next: gate a production agent’s outputs alongside tracing, evals, and alerts in Production Quality Monitoring.

Was this page helpful?

Questions & Discussion