This example demonstrates how to integrate the agent-control SDK into an existing application. It simulates a Customer Support Agent - a realistic enterprise scenario that shows the key patterns for protecting AI agents with server-defined controls.
- Universally understood use case: Customer support is familiar to everyone
- Natural need for guardrails: PII protection, prompt injection defense
- Multiple operation types: LLM calls + tool calls (database, knowledge base, tickets)
- Enterprise-relevant: Shows patterns real companies would use
# 1. Install SDK and evaluators (first time only)
pip install -e sdks/python -e evaluators
# 2. Start all services (database, server, UI, demo controls)
./examples/customer_support_agent/demo.sh start
# 3. Run the demo
python examples/customer_support_agent/run_demo.pyThe demo.sh start command:
- Starts PostgreSQL database
- Runs migrations
- Starts the API server (http://localhost:8000)
- Starts the UI (http://localhost:4000)
- Registers the agent with demo controls (PII detection, prompt injection)
When you open the UI, you'll see the agent with controls already configured.
# Check status of all services
./examples/customer_support_agent/demo.sh status
# Stop all services
./examples/customer_support_agent/demo.sh stop
# Reset everything (deletes database, asks for confirmation)
./examples/customer_support_agent/demo.sh reset-
Install the SDK and evaluators:
pip install -e sdks/python pip install -e evaluators
-
Install UI dependencies:
cd ui pnpm install
If you prefer to run services manually:
-
Start the database (requires Docker):
cd server docker compose up -d -
Run database migrations:
cd server make alembic-upgrade -
Start the server (Terminal 1):
cd server make runServer runs at http://localhost:8000
-
Start the UI (Terminal 2):
cd ui pnpm devUI runs at http://localhost:4000
After demo.sh start, the agent already has demo controls configured. Just run:
python examples/customer_support_agent/run_demo.pyTest different scenarios:
You: Hello, I need help with a refund
Agent: I understand you'd like a refund. Let me look into your order...
You: /test-pii
Running PII Detection Tests...
(Messages with SSN patterns will be blocked)
You: /test-injection
Running Prompt Injection Tests...
(Injection attempts will be blocked)
You: /quit
Goodbye!
Run all test scenarios automatically:
python examples/customer_support_agent/run_demo.py --automatedTo remove all controls from the agent (keeps the agent registered):
python examples/customer_support_agent/run_demo.py --reset- Open http://localhost:4000
- Click on "Customer Support Agent" in the list
- Click "Add Control" to create additional controls
See Example Controls below for configuration examples.
| Command | Description |
|---|---|
/help |
Show all commands |
/test-safe |
Run safe message tests |
/test-pii |
Test PII detection (if control configured) |
/test-injection |
Test prompt injection detection |
/lookup <query> |
Look up customer (e.g., /lookup C001) |
/search <query> |
Search knowledge base |
/ticket |
Create a test support ticket |
/quit |
Exit the demo |
Initialize once at application startup:
import agent_control
agent_control.init(
agent_name="customer-support-agent",
agent_description="AI-powered customer support assistant",
)This:
- Registers the agent with the server
- Fetches controls associated with the agent
- Enables the
@control()decorator
Use the @control() decorator on any function you want to protect:
from agent_control import control
@control()
async def respond_to_customer(message: str) -> str:
response = await llm.generate(message)
return responseThe decorator:
- Calls the server with
check_stage="pre"before execution (validates input) - Calls the server with
check_stage="post"after execution (validates output) - Raises
ControlViolationErrorif a control triggers with "deny" action
Catch ControlViolationError to provide graceful fallbacks:
from agent_control import ControlViolationError
try:
response = await respond_to_customer(user_message)
except ControlViolationError as e:
# Control triggered - return safe fallback
print(f"Blocked by: {e.control_name}")
response = "I cannot help with that request."Important: Controls are defined on the server via the UI, not in code.
This design provides:
- Centralized management: Security team controls safeguards without code changes
- Instant updates: Change controls without redeploying agents
- Audit trail: Server logs all control evaluations
- Separation of concerns: Developers focus on features, security team on safeguards
customer_support_agent/
├── README.md # This file
├── demo.sh # Start/stop/reset script
├── setup_demo_controls.py # Creates agent and demo controls
├── support_agent.py # Main agent with SDK integration
└── run_demo.py # Interactive demo runner
Manages the full demo lifecycle:
start- Starts database, server, UI, and sets up demo controlsstop- Stops all servicesreset- Deletes database and stops servicesstatus- Shows service status
Creates the demo agent with pre-configured controls:
block-ssn-in-output- Blocks responses containing SSN patternsblock-prompt-injection- Blocks common injection attemptsblock-credit-card- Blocks credit card numbers in input
Contains:
- SDK initialization
- Mock services (LLM, database, knowledge base, tickets)
- Protected functions with
@control()decorator CustomerSupportAgentclass with error handling
Contains:
- Interactive chat loop
- Test command handlers (
/test-pii,/test-injection, etc.) - Automated test scenarios
The demo setup creates three controls automatically. Here are examples of additional controls you might add:
name: block-pii-in-output
scope:
step_types: ["llm"]
stages: ["post"]
selector:
path: output
evaluator:
name: regex
config:
pattern: '\d{3}-\d{2}-\d{4}' # SSN pattern
action:
decision: deny
message: "Response contains PII (SSN pattern)"name: block-prompt-injection
scope:
step_types: ["llm"]
stages: ["pre"]
selector:
path: input
evaluator:
name: regex
config:
pattern: '(?i)(ignore.*instructions|system:|you are now)'
action:
decision: deny
message: "Potential prompt injection detected"name: block-toxic-input
scope:
step_types: ["llm"]
stages: ["pre"]
selector:
path: input
evaluator:
name: galileo.luna2
config:
stage_type: local
metric: input_toxicity
operator: gt
target_value: 0.8
action:
decision: deny
message: "Inappropriate content detected"-
Without controls: Run the demo without configuring any controls. All messages should pass through.
-
With PII control: Add a PII detection control, then run
/test-pii. Messages with SSN patterns should be blocked. -
With injection control: Add a prompt injection control, then run
/test-injection. Injection attempts should be blocked.
- Explore the main examples for more integration patterns
- Read the SDK documentation
- Check the server API documentation