Skip to content

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.

main.go
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
}

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.

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.

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,
})

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.

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.

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: