How to Build a System Monitor Dashboard for Your AI Agent (Step by Step)
I run an AI agent autonomously on a Windows 10 laptop with 8GB of RAM. That means every megabyte counts, every CPU cycle matters, and if a process goes rogue, the whole operation tanks.
So I built a system monitor dashboard. Not a bloated Electron app — a lightweight FastAPI server with SVG gauges, real-time WebSocket updates, and WebUI that looks like a war room. Here's exactly how I did it.
Why Not Just Use Task Manager?
Because Task Manager doesn't integrate with your agent's cron jobs, watchdog scripts, and deployment pipeline.
When your AI agent runs autonomously — writing articles, searching bounties, monitoring services — you need:
- Live CPU/RAM/Disk gauges visible in the browser, not just a desktop window
- Agent status cards showing which AI agents are running and what they're doing
- Service health — is the web server up? The video streaming service? The newsletter engine?
- Victory log — what got done since the last time you checked?
Task Manager shows you "what's using RAM right now." A war room dashboard shows you "is everything working and should I care?"
The Stack
- Backend: FastAPI (Python) — async, fast, easy to add API endpoints
- Frontend: Pure HTML/CSS/JS — no frameworks, no build step
- Charts: SVG circular gauges — clean, resolution-independent, lightweight
- Data: psutil (Python) for system metrics, file reads for agent state
- Updates: WebSocket push or HTTP polling, your choice
Step 1: The Server Skeleton
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
import psutil, json, asyncio
app = FastAPI()
@app.get("/api/health")
async def health():
return {"status": "alive", "timestamp": asyncio.get_event_loop().time()}
@app.get("/api/war-room")
async def war_room():
cpu = psutil.cpu_percent(interval=0.5)
mem = psutil.virtual_memory()
disk = psutil.disk_usage("/")
return {
"cpu": cpu,
"ram": {"used": mem.used, "total": mem.total, "percent": mem.percent},
"disk": {"used": disk.used, "total": disk.total, "percent": disk.percent}
}
The health endpoint responds in ~0.02s — I verified this after fixing an event loop blocking issue where I was calling subprocess.run() without wrapping it in asyncio.to_thread().
Step 2: SVG Circular Gauges
Instead of canvas or chart libraries, I used inline SVG:
<svg viewBox="0 0 120 120" class="gauge">
<circle cx="60" cy="60" r="52" class="gauge-bg"/>
<circle cx="60" cy="60" r="52" class="gauge-fill"
stroke-dasharray="326.7"
stroke-dashoffset="${326.7 - (326.7 * percent / 100)}"
stroke="${color}"/>
<text x="60" y="55" class="gauge-value">${percent}%</text>
<text x="60" y="75" class="gauge-label">CPU</text>
</svg>
The math: circumference = 2 × π × 52 ≈ 326.7. Stroke-dashoffset shifts the visible arc proportionally. At 0%, the entire stroke is hidden. At 100%, the full circle is drawn.
I styled it with a dark theme (#0a0a0f background, #1e1e2e card backgrounds, #a78bfa purple for the active fill). No CSS framework needed — 80 lines of CSS does everything.
Step 3: Agent Status Cards
Each agent gets a card with:
- Name and emoji icon (⚡ for the main agent, 🌊 for the co-agent)
- "Active" / "Idle" badge with matching color
- Last action timestamp
- Current task / phase
<div class="agent-card" style="border-left: 4px solid #a78bfa;">
<div class="agent-header">
<span class="agent-icon">⚡</span>
<span class="agent-name">Hermes</span>
<span class="status-badge active">Active</span>
</div>
<div class="agent-detail">Task: Bounty hunting (phase: research)</div>
<div class="agent-detail timestamp">Last action: 2m ago</div>
</div>
The data comes from reading the agent's state file — JSON persists between ticks, so the dashboard always reflects the latest status.
Step 4: Victory Log Timeline
This is the most satisfying part — a scrolling log of everything that got done:
<div class="timeline">
<div class="timeline-entry">
<span class="timeline-time">15:03</span>
<span class="timeline-text">✅ Published article: "Bug Bounty Pipeline"</span>
</div>
<div class="timeline-entry">
<span class="timeline-time">14:57</span>
<span class="timeline-text">🔍 Found monk-io #59 — sqlite3.dll bounty</span>
</div>
<div class="timeline-entry">
<span class="timeline-time">14:30</span>
<span class="timeline-text">⚡ Wallet check: 5 USDC confirmed on Base</span>
</div>
</div>
I keep this in a JSON file (victory-log.json) and the dashboard reads it on load. New entries get pushed via the API.
Step 5: The Watchdog That Keeps It Alive
The dashboard is useless if the server crashes and nobody notices. I run a PowerShell watchdog that polls every 30 seconds:
while ($true) {
$proc = Get-Process -Name "python" -ErrorAction SilentlyContinue |
Where-Object { $_.CommandLine -match "server.py" }
if (-not $proc) {
Write-Host "Server down — restarting..."
Start-Process python -ArgumentList "server.py" -WindowStyle Hidden
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') RESTARTED server" | Out-File logs/watchdog.jsonl -Append
}
Start-Sleep -Seconds 30
}
And a keeper cron that runs every 5 minutes and restarts the watchdog if the watchdog dies. Self-healing all the way down.
Full File Count
The entire project is 16 files and ~62KB:
server.py — FastAPI app with 8 endpoints
war-room.html — The dashboard page (served as HTML response)
style.css — Dark theme, gauges, cards, timeline
watchdog.ps1 — Process monitor
master.ps1 — Start/stop/status controller
...
What I Learned
Use
asyncio.to_thread()for blocking calls. FastAPI's event loop will block onsubprocess.run()andos.scandir()— wrapping them into_thread()drops response time from 3s to 0.02s.SVG gauges are simpler than canvas. No resize handlers, no redraw logic. CSS handles everything.
Pure HTML/CSS/JS is enough. You don't need React, D3.js, or Chart.js for a system dashboard. The page loads instantly and uses virtually no RAM.
Self-healing infrastructure matters. The watchdog keeps the server alive. The keeper keeps the watchdog alive. I've tested this by manually killing processes and watched them come back within 30 seconds.
Try It Yourself
The code is straightforward enough to copy-paste in an afternoon. If your AI agent runs autonomously, it deserves a dashboard that shows you what it's up to — not a terminal log you have to grep through.
Build the war room. Your agent will thank you.
Top comments (0)