Skip to content

Commit 39e95c2

Browse files
fix(docs): Test all examples (#16)
Make sure all examples work and their corresponding READMEs are uptodate
1 parent b02b012 commit 39e95c2

23 files changed

Lines changed: 345 additions & 677 deletions
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# Agent Control Demo
2+
3+
Complete example demonstrating the Agent Control workflow: creating controls, running a controlled agent, and dynamically updating controls without code changes.
4+
5+
## Overview
6+
7+
This demo shows how to:
8+
1. Create an agent and register it with the Agent Control server
9+
2. Define controls (regex, list-based) and assign them to a policy
10+
3. Run an agent that uses the `@control` decorator with server-side policies
11+
4. Dynamically update controls on the server (enable/disable rules without redeploying code)
12+
13+
## Prerequisites
14+
15+
Before running this example:
16+
17+
1. **Start the Agent Control server** from the monorepo root:
18+
```bash
19+
make server-run
20+
```
21+
22+
2. **Verify server is running**:
23+
```bash
24+
curl http://localhost:8000/health
25+
# Expected: {"status":"healthy","version":"..."}
26+
```
27+
28+
3. **Install dependencies** (if not already done from monorepo root):
29+
```bash
30+
make sync
31+
```
32+
33+
## Quick Start
34+
35+
### Step 1: Create Controls on the Server
36+
37+
Run the setup script to create the agent, controls, and policy:
38+
39+
```bash
40+
uv run python examples/agent_control_demo/setup_controls.py
41+
```
42+
43+
This creates:
44+
- A demo agent (`demo-chatbot`)
45+
- Two controls:
46+
- **block-ssn-output**: Regex control to detect and block Social Security Numbers (SSN)
47+
- **block-banned-words**: List-based control to block profanity and sensitive keywords
48+
- A policy with both controls assigned to the agent
49+
50+
### Step 2: Run the Demo Agent
51+
52+
Start the agent that uses server-side controls:
53+
54+
```bash
55+
uv run python examples/agent_control_demo/demo_agent.py
56+
```
57+
58+
The agent will:
59+
- Initialize with `agent_control.init()` to connect to the server
60+
- Load the assigned policy with all controls
61+
- Apply the `@control()` decorator to protect functions
62+
- Test various scenarios:
63+
- ✅ Normal message (allowed)
64+
- ❌ Message containing banned words (blocked)
65+
- ✅ Message with allowed content (passes)
66+
- ❌ Tool output containing SSN (blocked)
67+
68+
### Step 3: Update Controls Dynamically
69+
70+
Update controls on the server without changing code or restarting the agent:
71+
72+
```bash
73+
# Disable the SSN control (allow SSNs)
74+
uv run python examples/agent_control_demo/update_controls.py --allow-ssn
75+
76+
# Re-enable the SSN control (block SSNs again)
77+
uv run python examples/agent_control_demo/update_controls.py --block-ssn
78+
```
79+
80+
**Key Insight**: Controls are enforced server-side, so you can update rules in real-time without code deployments.
81+
82+
## Files
83+
84+
| File | Description |
85+
|:-----|:------------|
86+
| [setup_controls.py](setup_controls.py) | Creates agent, controls, policy, and assigns policy to agent |
87+
| [demo_agent.py](demo_agent.py) | Demo agent using `@control` decorator with server-side policies |
88+
| [update_controls.py](update_controls.py) | Updates controls dynamically (enable/disable SSN blocking) |
89+
90+
## How It Works
91+
92+
### 1. Server-Side Control Definition
93+
94+
Controls are defined on the server with:
95+
- **Scope**: When to check (step types, stages: pre/post)
96+
- **Selector**: What to check (input, output, specific fields)
97+
- **Evaluator**: How to check (regex patterns, list matching, AI-based)
98+
- **Action**: What to do (allow, deny, steer, warn, log)
99+
100+
Example from `setup_controls.py`:
101+
```python
102+
# Regex control to block SSN in output
103+
control_data = ControlDefinition(
104+
description="Block SSN patterns in output",
105+
enabled=True,
106+
execution="server",
107+
scope=ControlScope(step_types=["tool"], stages=["post"]),
108+
selector=ControlSelector(path="output"),
109+
evaluator=EvaluatorConfig(
110+
name="regex",
111+
config={"pattern": r"\b\d{3}-\d{2}-\d{4}\b"}
112+
),
113+
action=ControlAction(decision="deny")
114+
)
115+
```
116+
117+
### 2. Agent Integration
118+
119+
The agent uses the `@control()` decorator which:
120+
- Automatically fetches assigned policy from server
121+
- Evaluates controls before/after function execution
122+
- Blocks violations or allows safe operations
123+
124+
Example from `demo_agent.py`:
125+
```python
126+
import agent_control
127+
from agent_control import control, ControlViolationError
128+
129+
# Initialize and connect to server
130+
agent_control.init(
131+
agent_name="demo-chatbot",
132+
)
133+
134+
# Apply server-side controls
135+
@control()
136+
async def chat(message: str) -> str:
137+
return f"Echo: {message}"
138+
139+
# Handle violations
140+
try:
141+
response = await chat("user input")
142+
except ControlViolationError as e:
143+
print(f"Blocked: {e.message}")
144+
```
145+
146+
### 3. Dynamic Updates
147+
148+
Controls can be updated on the server without code changes:
149+
- Enable/disable controls
150+
- Update patterns and rules
151+
- Change enforcement decisions (deny → warn)
152+
- Add new controls to existing policies
153+
154+
## Configuration
155+
156+
All scripts use the same agent configuration:
157+
158+
```python
159+
AGENT_NAME = "demo-chatbot"
160+
SERVER_URL = os.getenv("AGENT_CONTROL_URL", "http://localhost:8000")
161+
```
162+
163+
Set `AGENT_CONTROL_URL` environment variable to connect to a different server:
164+
```bash
165+
export AGENT_CONTROL_URL=http://your-server:8000
166+
```
167+
168+
## Troubleshooting
169+
170+
### Server Connection Issues
171+
172+
**Error**: `Failed to connect to server`
173+
174+
**Fix**:
175+
```bash
176+
# Check if server is running
177+
curl http://localhost:8000/health
178+
179+
# If not running, start it
180+
make server-run
181+
```
182+
183+
### Agent Not Found
184+
185+
**Error**: `Agent not found` when running `demo_agent.py`
186+
187+
**Fix**: Run `setup_controls.py` first to create the agent and controls:
188+
```bash
189+
uv run python examples/agent_control_demo/setup_controls.py
190+
```
191+
192+
### Import Errors
193+
194+
**Error**: `ModuleNotFoundError: No module named 'agent_control'`
195+
196+
**Fix**: Install dependencies from monorepo root:
197+
```bash
198+
make sync
199+
```
200+
201+
## Next Steps
202+
203+
- Explore [CONCEPTS.md](../../CONCEPTS.md) to understand controls, policies, scopes, and evaluators
204+
- Check out [CrewAI example](../crewai/) for multi-agent orchestration with controls
205+
- Read [SDK documentation](../../sdks/python/README.md) for full API reference
206+
- Try [LangChain example](../langchain/) for LangChain integration
207+
208+
## Resources
209+
210+
- [Main Documentation](../../README.md)
211+
- [SDK Documentation](../../sdks/python/README.md)
212+
- [Server Documentation](../../server/README.md)
213+
- [Models Documentation](../../models/README.md)

‎examples/agent_control_demo/demo_agent.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444

4545
# Configuration
4646
AGENT_NAME = "demo-chatbot"
47-
AGENT_ID = "672e50df-af4c-429f-965a-3d7f8262302f"
4847
SERVER_URL = os.getenv("AGENT_CONTROL_URL", "http://localhost:8000")
4948

5049

@@ -161,8 +160,8 @@ async def run_demo():
161160
try:
162161
logger.info(f"Initializing agent: {AGENT_NAME}")
163162
agent_control.init(
164-
agent_name=AGENT_ID,
165-
agent_description=AGENT_NAME,
163+
agent_name=AGENT_NAME,
164+
agent_description="Demo chatbot for testing controls",
166165
server_url=SERVER_URL,
167166
)
168167
logger.info("Agent initialized successfully")

‎examples/agent_control_demo/setup_controls.py‎

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import asyncio
2424
import os
2525
import sys
26-
from uuid import UUID
2726

2827
# Add the SDK to path for development
2928
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../sdks/python/src"))
@@ -33,7 +32,6 @@
3332

3433
# Configuration
3534
AGENT_NAME = "demo-chatbot"
36-
AGENT_ID = "672e50df-af4c-429f-965a-3d7f8262302f"
3735
SERVER_URL = os.getenv("AGENT_CONTROL_URL", "http://localhost:8000")
3836

3937

@@ -43,15 +41,12 @@ async def create_agent(client: AgentControlClient) -> str:
4341
print("STEP 1: Creating Agent")
4442
print("=" * 60)
4543

46-
# Use the provided UUID for the agent
47-
agent_name = UUID(AGENT_ID)
48-
4944
try:
5045
response = await client.http_client.post(
5146
"/api/v1/agents/initAgent", # Correct endpoint
5247
json={
5348
"agent": {
54-
"agent_name": str(agent_name),
49+
"agent_name": AGENT_NAME,
5550
"agent_description": "Demo chatbot for testing controls",
5651
},
5752
"steps": []
@@ -67,8 +62,8 @@ async def create_agent(client: AgentControlClient) -> str:
6762
else:
6863
print(f"✓ Agent already exists: {AGENT_NAME}")
6964

70-
print(f" Agent UUID: {agent_name}")
71-
return str(agent_name)
65+
print(f" Agent Name: {AGENT_NAME}")
66+
return AGENT_NAME
7267

7368
except Exception as e:
7469
print(f"✗ Failed to create agent: {e}")
@@ -352,7 +347,7 @@ async def main():
352347
print("AGENT CONTROL DEMO: Setup Controls")
353348
print("=" * 60)
354349
print(f"\nServer URL: {SERVER_URL}")
355-
print(f"Agent: {AGENT_NAME} ({AGENT_ID})")
350+
print(f"Agent: {AGENT_NAME}")
356351

357352
async with AgentControlClient(base_url=SERVER_URL) as client:
358353
# Check server health
@@ -362,15 +357,12 @@ async def main():
362357
except Exception as e:
363358
print(f"\n✗ Server not available: {e}")
364359
print("\nMake sure the server is running:")
365-
print(" cd server && make run")
360+
print(" make server-run ")
366361
return
367362

368-
# Use the provided UUID for verification
369-
agent_name = AGENT_ID
370-
371363
# If verify-only mode, just run verification
372364
if args.verify_only:
373-
await verify_full_chain(client, agent_name)
365+
await verify_full_chain(client, AGENT_NAME)
374366
return
375367

376368
# 1. Create agent

‎examples/agent_control_demo/update_controls.py‎

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from agent_control import AgentControlClient
2626

2727
# Configuration
28-
AGENT_ID = "672e50df-af4c-429f-965a-3d7f8262302f"
28+
AGENT_NAME = "demo-chatbot"
2929
SERVER_URL = os.getenv("AGENT_CONTROL_URL", "http://localhost:8000")
3030

3131

@@ -154,14 +154,11 @@ async def main():
154154
parser.add_argument("--status", action="store_true", help="Show current status")
155155
args = parser.parse_args()
156156

157-
# Use the provided UUID
158-
agent_name = AGENT_ID
159-
160157
print("\n" + "=" * 60)
161158
print("AGENT CONTROL DEMO: Update Controls")
162159
print("=" * 60)
163160
print(f"\nServer URL: {SERVER_URL}")
164-
print(f"Agent UUID: {agent_name}")
161+
print(f"Agent Name: {AGENT_NAME}")
165162

166163
async with AgentControlClient(base_url=SERVER_URL) as client:
167164
# Check server health
@@ -174,7 +171,7 @@ async def main():
174171
return
175172

176173
# Find the SSN control
177-
ctrl = await get_control_by_name(client, agent_name, "block-ssn-output")
174+
ctrl = await get_control_by_name(client, AGENT_NAME, "block-ssn-output")
178175
if not ctrl:
179176
print("\n✗ SSN control not found!")
180177
print(" Run setup_controls.py first:")
@@ -184,16 +181,16 @@ async def main():
184181
control_id = ctrl.get("id")
185182

186183
if args.status:
187-
await show_current_status(client, agent_name)
184+
await show_current_status(client, AGENT_NAME)
188185
elif args.allow_ssn:
189186
await allow_ssn(client, control_id)
190-
await show_current_status(client, agent_name)
187+
await show_current_status(client, AGENT_NAME)
191188
elif args.block_ssn:
192189
await block_ssn(client, control_id)
193-
await show_current_status(client, agent_name)
190+
await show_current_status(client, AGENT_NAME)
194191
else:
195192
# Default: show status and usage
196-
await show_current_status(client, agent_name)
193+
await show_current_status(client, AGENT_NAME)
197194
print("\n" + "=" * 60)
198195
print("Usage")
199196
print("=" * 60)

0 commit comments

Comments
 (0)