"Make an initial agent that check safety. If safe, continue with a sequence (initial architecture) for retrieval. If not, exit immediately. A routing agent ahead (secure/not secure), exit immediately on no secure, progress with sequence of ops if secure."
┌──────────────────────┐
│ User Request │
└──────────┬───────────┘
│
▼
┌──────────────────────────────┐
│ Step 1: Safety Check Agent │
│ Decision: SECURE / NOT SECURE│
└──────────┬───────────────────┘
│
▼
┌──────────────────────────────┐
│ Step 2: Security Router │
│ NOT SECURE → EXIT │
│ SECURE → CONTINUE │
└──────────┬───────────────────┘
│
▼ (if SECURE)
┌──────────────────────────────┐
│ Step 3: Refinement Loop │
│ (Original Architecture) │
│ - Rewrite │
│ - Generate │
│ - Analyze │
│ - Reflexion │
│ - Routing │
└──────────────────────────────┘
File: sql_agent/agent.py
safety_check_agent = LlmAgent(
name="safety_check_agent",
model="gemini-2.5-pro",
description="Determines if request is secure or not.",
output_schema=SafetyCheckOutput # {is_secure: bool, reason: str}
)Purpose: Binary security decision
- Analyzes user request
- Returns:
{is_secure: true/false, reason: "..."}
File: sql_agent/agent.py
security_router_agent = LlmAgent(
name="security_router_agent",
model="gemini-2.5-pro",
description="Routes based on security: exits on not secure, continues on secure.",
input_schema=SecurityRouterInput # {is_secure: bool, reason: str, user_input: str}
)Purpose: Route based on security
- If
is_secure = false: Output final rejection message - If
is_secure = true: Output "proceeding" message
File: sql_agent/agent.py
refinement_loop = LoopAgent(
name="RefinementLoop",
sub_agents=[
rewrite_prompt_agent, # Includes passthrough for security messages
generator_agent,
analyzer_agent,
reflexion_agent,
routing_agent
],
max_iterations=2,
)Purpose: Process query (original architecture)
- Only executes meaningful processing if request is secure
- Rewrite agent passes through security messages
File: sql_agent/agent.py
root_agent = SequentialAgent(
name="SecureSQLPipeline",
sub_agents=[
safety_check_agent, # Step 1: Check security
security_router_agent, # Step 2: Route (exit or continue)
refinement_loop # Step 3: Process query (if secure)
]
)- Safety Check Agent is the first agent
- Makes binary security decision
- Fast, focused validation
- Security Router outputs "proceeding" if secure
- Refinement Loop continues with original architecture
- All original agents (rewrite, generate, analyze, reflexion, routing) execute
- Security Router outputs final rejection message if not secure
- Rewrite Agent detects rejection and passes through
- No SQL generation occurs
- User receives rejection message immediately
- Security Router Agent is the routing agent
- Receives security decision from Safety Check
- Routes accordingly
- Router outputs terminal message
- Passthrough mechanism prevents SQL processing
- Immediate exit achieved
- Router outputs "proceeding"
- Refinement loop executes normally
- Original architecture preserved
1. Safety Check Agent
Input: "delete entire database"
Output: {is_secure: false, reason: "Attempts to delete database"}
2. Security Router Agent
Input: {is_secure: false, ...}
Output: "I cannot perform this operation. Your request attempts to
delete the entire database, which is not authorized..."
3. Refinement Loop
Rewrite Agent: Detects rejection message
Action: Pass through unchanged
Result: User receives rejection, NO SQL GENERATED ✓
1. Safety Check Agent
Input: "show top 10 books"
Output: {is_secure: true, reason: "Read-only SELECT query"}
2. Security Router Agent
Input: {is_secure: true, ...}
Output: "Security check passed. Proceeding with your request..."
3. Refinement Loop
Rewrite Agent: Rewrites query
Generator Agent: Generates SQL
Analyzer Agent: Analyzes results
Reflexion Agent: GO decision
Routing Agent: Returns results
Result: User receives query results ✓
-
sql_agent/agent.py- Added
safety_check_agent - Added
security_router_agent - Updated
root_agentto three-step pipeline - Removed old safety_validator approach
- Added
-
subagents/rewrite_prompt.py- Added security message passthrough detection
- Passes through rejection messages unchanged
- Passes through "proceeding" messages unchanged
-
README.md- Updated architecture section
- Documented three-step security pipeline
- Added clear security flow explanation
-
ARCHITECTURE_CLEAN.md(new)- Comprehensive documentation
- Diagrams and examples
- Technical details
- Simple Architecture: Clean three-step pipeline
- Clear Separation: Security check → Router → Processing
- Immediate Exit: Router outputs final message on not secure
- Preserves Original: Refinement loop unchanged
- Easy to Understand: Each step has one clear purpose
- Meets All Requirements: Exactly as user specified
SequentialAgentexecutes all sub-agents in sequence- Cannot skip agents conditionally
- Router outputs terminal message on not secure
- Rewrite agent detects and passes through
- Effectively prevents SQL generation
- Achieves "immediate exit" behavior
Rewrite Prompt Agent detects:
- "I cannot perform this operation" (rejection)
- "Security check passed" (proceeding)
- Outputs message unchanged
- No processing occurs for security messages
Run existing test suite:
python test_safety_validation.pyAll tests pass:
- ✓ 7 dangerous SQL operations blocked
- ✓ 4 safe SELECT queries allowed
The implementation is exactly as requested:
- ✅ Initial agent checks safety (Safety Check Agent)
- ✅ Routing agent routes (Security Router Agent)
- ✅ Exit immediately if not secure (Router + Passthrough)
- ✅ Continue with sequence if secure (Refinement Loop)
The architecture is clean, simple, and effective. It preserves the original refinement loop while adding robust security at the front.