AI data loss prevention for Microsoft Agent Framework
People often paste personal data into AI prompts – card numbers, phone numbers, home addresses, and whole résumés. Once that data reaches your AI provider, the provider’s logging and retention apply to it, not yours.
Arcjet sensitive info detection scans prompt content inside your application, before it reaches the AI provider. Detection runs locally in your own environment, so the raw text doesn’t leave your app. The SDK reports the decision to Arcjet with the type and position of each finding, but not the matched text. When Arcjet finds something, you decide what happens next: block the request, remove the data, or ask the user to edit their prompt.
Get started
Section titled “Get started”package main
import ( "context"
"github.com/arcjet/arcjet-go" "github.com/arcjet/arcjet-go/agentframework" "github.com/arcjet/arcjet-go/sensitiveinfo/rampart" "github.com/microsoft/agent-framework-go/tool" "github.com/microsoft/agent-framework-go/tool/functool")
type noteArgs struct { OrderID string `json:"order_id"` Note string `json:"note"`}
func saveNote(_ context.Context, in noteArgs) (map[string]string, error) { return map[string]string{"order_id": in.OrderID, "note": in.Note}, nil}
// Create the Rampart backend once at startup and reuse it.var backend = must(rampart.New(rampart.Options{}))
func guardedSaveNote(guard *arcjet.GuardClient) (tool.FuncTool, error) { detectPII := must(arcjet.GuardSensitiveInfo(arcjet.GuardSensitiveInfoOptions{ Mode: arcjet.ModeLive, Deny: []arcjet.EntityType{ arcjet.SensitiveInfoEmail, arcjet.SensitiveInfoPhoneNumber, arcjet.SensitiveInfoIPAddress, arcjet.SensitiveInfoCreditCardNumber, }, Backend: backend, })) fn := functool.MustNew( functool.Config{Name: "save_note", Description: "Save a free-text note on an order"}, saveNote, ) return agentframework.GuardTool(guard, fn, agentframework.ToolPolicy{ Action: "note.saved", Rules: agentframework.Args(func(_ context.Context, in noteArgs) ([]arcjet.GuardRuleInput, error) { return []arcjet.GuardRuleInput{detectPII.Text(in.Note)}, nil }), })}
func must[T any](v T, err error) T { if err != nil { panic(err) } return v}How detection works
Section titled “How detection works”Sensitive info detection runs on-device with Rampart, a named-entity-recognition (NER) model paired with deterministic recognizers:
- Deterministic recognizers match structured types with validated patterns – card numbers, email addresses, IP addresses, URLs, and Social Security numbers.
- The NER model detects the free-form PII that people paste into prompts – names, street addresses, phone numbers, and government or financial identifiers. These don’t follow a fixed pattern, so a model finds them where pattern matching can’t.
The SDK rule runs detection on your own infrastructure, so the text isn’t sent to Arcjet or a third party for analysis. That makes it a good fit in front of an AI provider. The server-side sensitive information detector in an agent guard policy and coding agent hooks screen text on Arcjet’s side instead. Use the server-side detector for text longer than the SDK scans, and to detect protected health information (PHI) and secrets such as API keys, which on-device detection doesn’t cover.
Configure detection
Section titled “Configure detection”Choose which PII to block
Section titled “Choose which PII to block”Use deny to list the entity types to block, or allow to block everything
except the types you list (the two are mutually exclusive). Tune the list to
your app – for a support bot that legitimately collects phone numbers, leave
PHONE_NUMBER out of deny:
arcjet.GuardSensitiveInfo(arcjet.GuardSensitiveInfoOptions{ Mode: arcjet.ModeLive, // Blocks the tool. Use ModeDryRun to log only. Deny: []arcjet.EntityType{ arcjet.SensitiveInfoCreditCardNumber, arcjet.SensitiveInfoEmail, }, Backend: backend, // Create once with rampart.New})For every entity type and where Arcjet detects it, see Entity types.
Detect names, addresses, and IDs
Section titled “Detect names, addresses, and IDs”The Rampart NER model detects names, addresses, and government or financial
identifiers. Add them to Deny, and create the backend once at startup with
rampart.New:
backend, err := rampart.New(rampart.Options{})if err != nil { log.Fatal(err)}detectPII, err := arcjet.GuardSensitiveInfo(arcjet.GuardSensitiveInfoOptions{ Mode: arcjet.ModeLive, Deny: []arcjet.EntityType{ arcjet.SensitiveInfoEmail, arcjet.SensitiveInfoGivenName, arcjet.SensitiveInfoSurname, }, Backend: backend,})Choose what text to scan
Section titled “Choose what text to scan”Pass the text to scan as sensitiveInfoValue in JavaScript,
sensitive_info_value in Python, or WithSensitiveInfoValue in Go. For a chat
endpoint, this is usually the user’s most recent message. To scan every message,
pass the full conversation history instead – PII can appear earlier in the
exchange.
Test before you block
Section titled “Test before you block”Set mode to "DRY_RUN" in JavaScript, Mode.DRY_RUN in Python, or
ModeDryRun in Go to log detections without blocking requests. Run it in
production to see which PII appears in your prompts, then switch to live mode
when the entity list matches what you want to block.
Combine with other protections
Section titled “Combine with other protections”Sensitive info detection controls what data reaches your AI provider. Pair it with the other AI protection layers, each of which covers a different risk:
- Prompt injection detection blocks hostile instructions and jailbreak attempts.
- AI abuse protection and AI budget control block automated clients and enforce per-user token budgets.
- Agent guards apply PII detection directly inside agent tool handlers and pipelines that don’t route through HTTP. A local policy input keeps the raw string in the SDK while reporting policy evidence to Arcjet.
- Protected health information (PHI) detection finds medical record numbers, health plan member IDs, and vehicle and device identifiers with the server-side detector in an agent guard policy.