Cybersecurity

Why One Agent Isn't Enough

Python RAG AI Agents SQL Injection XSS SQL Regression Hashing
1,346 words Includes Code
🎯 Key Takeaway: Multi-agent systems split complex tasks across specialized agents—planner, coder, tester, security, reviewer—each doing one thing well. The orchestrator manages flow, state, and failure. The result: higher quality code with built-in quality gates.
Multi-agent software development pipeline with Planner, Coder, Tester, Security, and Reviewer agents
Five specialized agents working in sequence: plan → code → test → secure → review.

Why One Agent Isn't Enough

Single-agent systems handle simple tasks well. But as tasks grow complex—multi-file refactors, feature implementations with security requirements, code that needs tests and documentation—a single agent trying to do everything produces lower quality results.

The solution: specialized agents. Each agent does one thing well, and an orchestrator coordinates the workflow.

The Five Specialized Agents

Multi-agent architecture showing orchestrator, five specialized agents, communication flow, state, and failure handling
The multi-agent architecture: orchestrator manages specialized agents with shared state.

1. Planner Agent

Role: Analyzes the task, explores the codebase, and creates an implementation plan.

Input: "Add rate limiting to API endpoints"
Output:
  1. Read existing middleware
  2. Create rate_limit.py with token bucket algorithm
  3. Add middleware to app.py
  4. Create tests/test_rate_limit.py
  5. Update documentation

2. Coder Agent

Role: Implements the changes described by the Planner.

Input: Plan from Planner
Actions:
  - Read relevant files
  - Write new code
  - Edit existing files
  - Run basic syntax checks
Output: Code changes ready for testing

3. Tester Agent

Role: Runs tests and reports failures.

Input: Code changes from Coder
Actions:
  - Run pytest/vitest
  - Analyze failures
  - Report which tests fail and why
Output: Test results (pass/fail + details)

4. Security Agent

Role: Checks for security vulnerabilities.

Input: Code changes
Checks:
  - Input validation
  - SQL injection
  - XSS vulnerabilities
  - Hard-coded secrets
  - Dependency vulnerabilities
Output: Security report (issues found or clear)

5. Reviewer Agent

Role: Final quality check before merge.

Input: All previous results
Checks:
  - Code style consistency
  - Documentation completeness
  - Test coverage
  - No regressions
  - PR description quality
Output: Approved or changes requested

The Orchestrator

The orchestrator is the conductor. It:

  • Manages workflow — Passes work from agent to agent
  • Maintains state — Tracks what's been done
  • Handles failure — Retries or escalates
  • Enforces limits — Max retries, cost caps, time limits
  • Reports progress — Keeps humans informed

Communication Patterns

Pattern Description Use Case
Sequential Agent A → B → C → D Simple pipeline
Parallel Tester + Security run simultaneously Independent checks
Loop Coder ↔ Tester until tests pass Fix failures
Fan-out Planner distributes to multiple Coders Large tasks

State Management

Agents need to share state. Common state includes:

  • Task description — What needs to be done
  • Plan — Steps from the Planner
  • Code changes — Files modified by Coder
  • Test results — Pass/fail from Tester
  • Security findings — Issues from Security agent
  • Review comments — Feedback from Reviewer
  • Retry count — How many attempts have been made

Failure Handling

What happens when an agent fails?

Failure Response Max Retries
Tests fail Send failures back to Coder 3
Security issues Send findings back to Coder 3
Review rejected Send comments back to Coder 2
Agent timeout Skip and escalate to human 1
Max retries exceeded Stop and alert human

Cost Considerations

Multi-agent systems use more tokens than single-agent systems. Cost factors:

  • Agent count: 5 agents = 5x the context loading
  • Retries: Failed attempts add cost
  • Context passing: Each agent needs full context
  • Parallel execution: Multiple agents running simultaneously
💡 Cost Optimization: Use smaller, faster models for simple agents (Tester, Security) and larger models for complex agents (Planner, Reviewer).

Security in Multi-Agent Systems

Multi-agent systems introduce new security concerns:

  • Agent permissions: Each agent should have minimal required access
  • State integrity: Prevent agents from corrupting shared state
  • Injection attacks: Malicious task descriptions could manipulate agents
  • Privilege escalation: Agent A shouldn't gain Agent B's permissions

Python Simulation

Here's a safe, self-contained simulation of a multi-agent system:

"""Multi-Agent Software Development Simulation — Safe demo."""

from dataclasses import dataclass, field
from typing import Callable
from enum import Enum


class AgentRole(Enum):
    PLANNER = "Planner"
    CODER = "Coder"
    TESTER = "Tester"
    SECURITY = "Security"
    REVIEWER = "Reviewer"


class TaskStatus(Enum):
    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    PASSED = "passed"
    FAILED = "failed"
    RETRY = "retry"


@dataclass
class Task:
    description: str
    status: TaskStatus = TaskStatus.PENDING
    plan: list[str] = field(default_factory=list)
    code_changes: list[str] = field(default_factory=list)
    test_results: dict = field(default_factory=dict)
    security_issues: list[str] = field(default_factory=list)
    review_comments: list[str] = field(default_factory=list)
    retry_count: int = 0


@dataclass
class AgentResult:
    agent: AgentRole
    success: bool
    output: str
    issues: list[str] = field(default_factory=list)


class MultiAgentOrchestrator:
    """Simulates a multi-agent development workflow."""

    def __init__(self, max_retries: int = 3):
        self.max_retries = max_retries
        self.agents: dict[AgentRole, Callable] = {
            AgentRole.PLANNER: self._planner_agent,
            AgentRole.CODER: self._coder_agent,
            AgentRole.TESTER: self._tester_agent,
            AgentRole.SECURITY: self._security_agent,
            AgentRole.REVIEWER: self._reviewer_agent,
        }
        self.history: list[AgentResult] = []

    def execute(self, task: Task) -> Task:
        """Run the full multi-agent pipeline."""
        print(f"\n{'='*50}")
        print(f"Task: {task.description}")
        print(f"{'='*50}")

        # Step 1: Plan
        result = self._run_agent(AgentRole.PLANNER, task)
        if not result.success:
            return task

        # Step 2: Implement
        result = self._run_agent(AgentRole.CODER, task)
        if not result.success:
            return task

        # Step 3: Test (with retry loop)
        while task.retry_count < self.max_retries:
            result = self._run_agent(AgentRole.TESTER, task)
            if result.success:
                break
            task.retry_count += 1
            print(f"  ↻ Retry {task.retry_count}/{self.max_retries}")
            self._run_agent(AgentRole.CODER, task)

        if task.status == TaskStatus.FAILED:
            return task

        # Step 4: Security check
        result = self._run_agent(AgentRole.SECURITY, task)
        if not result.success:
            task.retry_count += 1
            if task.retry_count < self.max_retries:
                self._run_agent(AgentRole.CODER, task)
                return self.execute(task)  # Restart pipeline

        # Step 5: Review
        result = self._run_agent(AgentRole.REVIEWER, task)
        return task

    def _run_agent(self, role: AgentRole, task: Task) -> AgentResult:
        """Run a single agent and record the result."""
        agent_fn = self.agents[role]
        result = agent_fn(task)
        self.history.append(result)

        status = "✅" if result.success else "❌"
        print(f"{status} {role.value}: {result.output}")
        if result.issues:
            for issue in result.issues:
                print(f"   ⚠️  {issue}")

        return result

    # ── Agent Implementations (mock) ──────────────────────

    def _planner_agent(self, task: Task) -> AgentResult:
        task.plan = [
            "Read existing middleware structure",
            "Implement rate limiting logic",
            "Add middleware to application",
            "Write unit tests",
        ]
        task.status = TaskStatus.IN_PROGRESS
        return AgentResult(
            agent=AgentRole.PLANNER,
            success=True,
            output=f"Created plan with {len(task.plan)} steps",
        )

    def _coder_agent(self, task: Task) -> AgentResult:
        task.code_changes = [
            "Created rate_limit.py",
            "Modified app.py to add middleware",
            "Created tests/test_rate_limit.py",
        ]
        return AgentResult(
            agent=AgentRole.CODER,
            success=True,
            output=f"Implemented {len(task.code_changes)} changes",
        )

    def _tester_agent(self, task: Task) -> AgentResult:
        # Simulate: first attempt fails, second passes
        if task.retry_count == 0 and not task.test_results:
            task.test_results = {"passed": 3, "failed": 1}
            task.status = TaskStatus.FAILED
            return AgentResult(
                agent=AgentRole.TESTER,
                success=False,
                output="3 passed, 1 failed",
                issues=["test_rate_limit_exceeded failed"],
            )
        task.test_results = {"passed": 4, "failed": 0}
        task.status = TaskStatus.PASSED
        return AgentResult(
            agent=AgentRole.TESTER,
            success=True,
            output="4 passed, 0 failed",
        )

    def _security_agent(self, task: Task) -> AgentResult:
        task.security_issues = []
        return AgentResult(
            agent=AgentRole.SECURITY,
            success=True,
            output="No security issues found",
        )

    def _reviewer_agent(self, task: Task) -> AgentResult:
        task.review_comments = [
            "Code follows project style",
            "Tests cover edge cases",
            "Documentation updated",
        ]
        return AgentResult(
            agent=AgentRole.REVIEWER,
            success=True,
            output="Approved — ready for merge",
        )


# ── Run Simulation ────────────────────────────────────────

if __name__ == "__main__":
    orchestrator = MultiAgentOrchestrator(max_retries=3)

    task = Task(
        description="Add rate limiting to API endpoints"
    )

    final = orchestrator.execute(task)

    print(f"\n{'='*50}")
    print("Final Status:")
    print(f"  Status: {final.status.value}")
    print(f"  Plan steps: {len(final.plan)}")
    print(f"  Code changes: {len(final.code_changes)}")
    print(f"  Test results: {final.test_results}")
    print(f"  Security issues: {len(final.security_issues)}")
    print(f"  Review comments: {len(final.review_comments)}")
    print(f"  Retries: {final.retry_count}")
    print(f"{'='*50}")
💡 Try it: Save this as multi_agent_demo.py and run python multi_agent_demo.py. Watch the agents collaborate and handle failures.

When to Use Multi-Agent Systems

Scenario Single Agent Multi-Agent
Quick bug fix ✅ Better ⚠️ Overhead
Feature implementation ⚠️ Works ✅ Better quality
Security-sensitive code ❌ Risky ✅ Dedicated security agent
Large refactoring ⚠️ May miss issues ✅ Multiple review stages
Production deployment ❌ Too risky ✅ Full pipeline

Key Takeaways

  • Multi-agent systems specialize—each agent does one thing well
  • The orchestrator manages workflow, state, and failure
  • Specialized agents: Planner → Coder → Tester → Security → Reviewer
  • Failure handling with retry loops is essential
  • Multi-agent systems cost more but produce higher quality output
  • Security review is a dedicated stage, not an afterthought
  • Use multi-agent for complex, security-sensitive, or production tasks

Further Reading

Related BestWordz Tools

💬 Join the conversation on BestWordz Community — Discuss multi-agent architectures and share your implementations.

Try the JSON Formatter

Put what you've learned into practice with this free BestWordz tool.

Open Tool →