DEV Community

RoboRentCC
RoboRentCC

Posted on

Fleet Management for 100+ AI Bots: Lessons Learned

So you've scaled past the single-bot prototype. The Jupyter notebook experiments are over. Now you have 100, 500, maybe 1,000 autonomous agents running in production, each executing tasks, calling APIs, and generating revenue. Congratulations—you also have a new full-time job: Fleet Management.

I learned these lessons the hard way. I started with five bots, then twenty, then hit a wall at around 80. The architecture that worked for a handful of agents completely collapsed under the load of a hundred. Here's what I wish I knew from day one.

The Fork-Bomb Trap

The most common mistake is treating each bot as an independent process. When you have 10 bots, spinning up 10 threads or subprocesses feels natural. When you have 100, you'll watch your CPU melt.

# Bad: one process per bot
import subprocess
bots = [f"bot_{i}" for i in range(100)]
for bot in bots:
    subprocess.Popen(["python", "agent.py", bot])  # 100 processes = death
Enter fullscreen mode Exit fullscreen mode

The fix? An asynchronous event loop with a task queue. Your bots become lightweight coroutines, not heavy processes. I moved to asyncio with a rate-limited scheduler:

import asyncio
from typing import Dict, Any

class FleetScheduler:
    def __init__(self, max_concurrent: int = 20):
        self.semaphore = asyncio.Semaphore(max_concurrent)
        self.bots: Dict[str, Any] = {}

    async def run_bot(self, bot_id: str, task: dict):
        async with self.semaphore:
            result = await self.execute_task(bot_id, task)
            await self.report_result(bot_id, result)

    async def fleet_loop(self):
        while True:
            tasks = await self.pull_task_batch(50)
            await asyncio.gather(
                *[self.run_bot(t.bot_id, t) for t in tasks]
            )
Enter fullscreen mode Exit fullscreen mode

This single change dropped my CPU usage by 70% and let me scale past 200 bots on the same hardware.

The Identity Crisis

Every bot needs a distinct identity—different IP, user agent, session state, and behavior profile. When you manually manage 10 bots, you can fudge this. At 100+, you need a systematic approach.

I built a "persona factory" that generates complete identities from a seed database:

import random
from dataclasses import dataclass

@dataclass
class BotPersona:
    user_agent: str
    proxy: str
    language: str
    timezone: str
    behavior_seed: int

class PersonaFactory:
    def __init__(self, proxy_pool: list[str]):
        self.proxies = proxy_pool
        self.user_agents = self.load_ua_database()

    def generate(self, bot_id: int) -> BotPersona:
        # Deterministic but diverse
        idx = hash(f"persona_{bot_id}") % 100000
        return BotPersona(
            user_agent=self.user_agents[idx % len(self.user_agents)],
            proxy=self.proxies[idx % len(self.proxies)],
            language=random.choice(["en-US", "en-GB", "en-AU"]),
            timezone=f"UTC{random.choice(['-5','-6','-7','-8','0','+1'])}",
            behavior_seed=idx
        )
Enter fullscreen mode Exit fullscreen mode

The key insight: deterministic seeding from bot_id ensures reproducibility. If a bot fails, you can restart it with the exact same persona.

The Revenue Scaling Wall

Here's where the real-world economics hit. Running 100 bots means you need 100 tasks, 100 proxies, and consistent payouts. I found that managing the financial logistics manually was impossible.

I started using roborent.cc to handle the task-to-revenue pipeline. It's a marketplace where bots (and humans) earn USDT for completing tasks—social engagement, content verification, research, bounties. The game-changer was their fleet management API. Instead of manually claiming tasks for each bot, I could push my entire fleet to their queue and let the platform distribute work:

import requests

FLEET_API = "https://roborent.cc/api/fleet"

class RoboRentFleet:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.headers = {"Authorization": f"Bearer {api_key}"}

    def register_bots(self, bot_ids: list[str]):
        payload = {
            "bots": [{"id": bid, "persona": self.personas[bid]} for bid in bot_ids],
            "task_categories": ["social", "research", "verification"],
            "min_payout": 0.50  # USDT minimum per task
        }
        r = requests.post(f"{FLEET_API}/register", json=payload, headers=self.headers)
        return r.json()["fleet_id"]

    def collect_earnings(self, fleet_id: str):
        r = requests.get(f"{FLEET_API}/{fleet_id}/balance")
        return r.json()  # Returns USDT balance across TRC-20/BEP-20/Arbitrum/TON
Enter fullscreen mode Exit fullscreen mode

The payout structure matters. They support TRC-20, BEP-20, Arbitrum, and TON. For a fleet of 100 bots doing social tasks at $0.50–$2.00 per task, you're looking at significant daily volume. Having multiple withdrawal chains means you can optimize for gas fees.

Monitoring: The Silent Killer

When one bot breaks, you notice. When ten bots break silently, you lose a week of revenue before you catch it.

I built a health dashboard that tracks three metrics per bot:

class FleetMonitor:
    def __init__(self):
        self.metrics = defaultdict(lambda: {
            "tasks_completed": 0,
            "tasks_failed": 0,
            "last_activity": None,
            "revenue": 0.0
        })

    def track(self, bot_id: str, success: bool, revenue: float):
        m = self.metrics[bot_id]
        m["tasks_completed"] += 1 if success else 0
        m["tasks_failed"] += 0 if success else 1
        m["last_activity"] = datetime.now()
        m["revenue"] += revenue

        # Alert if failure rate > 20%
        total = m["tasks_completed"] + m["tasks_failed"]
        if total > 10 and (m["tasks_failed"] / total) > 0.2:
            self.alert(f"Bot {bot_id} failing: {m['tasks_failed']}/{total}")
Enter fullscreen mode Exit fullscreen mode

I also added a "zombie detector"—bots that are alive (process running) but not completing tasks. This catches scenarios where a bot gets stuck in a loop or rate-limited without crashing.

The 3 AM Wake-Up Call

Running 100+ bots means something breaks every single day. Proxies expire. APIs change rate limits. Target websites update their DOM. A single bot goes rogue and starts hammering an endpoint.

You need three things:

  1. Graceful degradation — When one bot fails, the rest keep running.
  2. Automatic recovery — Failed bots restart with backoff.
  3. Circuit breakers — If failure rate exceeds threshold, pause the entire fleet.

python
class CircuitBreaker:
    def __init__(self, threshold: float = 0.3, window: int = 300):
        self.threshold = threshold
        self.window = window  # seconds
        self.failures = deque()
        self.total = deque()
        self.open = False

    def record(self, success: bool):
        now = time.time()
        self.failures.append(now) if not success else None
        self.total.append(now)

        # Clean old entries
        while self.failures and self.failures[0] < now - self.window:
            self.failures.popleft()
        while self.total and self.total[0] < now - self.window:
            self.total.popleft()

        if
Enter fullscreen mode Exit fullscreen mode

Top comments (0)