Architecture

How the pieces fit together.

System Overview

AgentLens Architecture ┌─────────────────────────────────────────────────────────────────────┐ │ Your Application │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ @track_agent │ │ @track_tool │ │ agentlens. │ │ │ │ decorator │ │ decorator │ │ track() │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ └──────────────────┼──────────────────┘ │ │ ▼ │ │ ┌──────────────┐ │ │ │ AgentTracker │ ← Manages sessions & events │ │ └──────┬───────┘ │ │ ▼ │ │ ┌──────────────┐ │ │ │ Transport │ ← Batching, retry, threading │ │ └──────┬───────┘ │ └───────────────────────────┼──────────────────────────────────────────┘ │ HTTP POST /events (batched JSON) │ ┌───────────────────────────┼──────────────────────────────────────────┐ │ ▼ AgentLens Backend │ │ ┌──────────────┐ │ │ │ Express.js │ ← API server │ │ │ Server │ │ │ └──────┬───────┘ │ │ ┌─────┴─────┐ │ │ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ │ │ │ Events │ │ Sessions │ ← Route handlers │ │ │ Router │ │ Router │ │ │ └────┬─────┘ └────┬─────┘ │ │ └─────┬──────┘ │ │ ▼ │ │ ┌──────────────┐ │ │ │ SQLite DB │ ← WAL mode, foreign keys │ │ │ (embedded) │ │ │ └──────────────┘ │ │ ▲ │ │ │ serves static files │ │ ┌──────────────┐ │ │ │ Dashboard │ ← Vanilla HTML/CSS/JS │ │ │ (SPA) │ │ │ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────┘

Data Flow

  1. Instrumentation: Your agent code uses decorators (@track_agent, @track_tool_call) or manual agentlens.track() calls to create events.
  2. Buffering: The Transport layer buffers events in memory and flushes them in batches (default: every 10 events or 5 seconds).
  3. Ingestion: Batched events are sent via HTTP POST to /events. Session lifecycle events (session_start, session_end) manage session records.
  4. Storage: Events and sessions are persisted to SQLite with WAL mode for concurrent read/write performance.
  5. Visualization: The dashboard queries the API to display timelines, token charts, and explanations.

Component Details

Python SDK (sdk/)

ModuleResponsibility
__init__.pyModule-level API (init, track, explain). Global tracker instance.
models.pyPydantic data models (AgentEvent, ToolCall, DecisionTrace, Session)
tracker.pyAgentTracker — manages sessions, creates events, generates explanations
transport.pyBatched HTTP transport with background flush thread and retry logic
decorators.py@track_agent and @track_tool_call for zero-config instrumentation

Backend (backend/)

FileResponsibility
server.jsExpress app setup, middleware, static file serving, routes
db.jsSQLite connection (better-sqlite3), schema initialization
routes/events.jsPOST /events — batch event ingestion with transaction support
routes/sessions.jsGET/POST sessions, session detail, explain endpoint
seed.jsDemo data seeder

Dashboard (dashboard/)

A lightweight single-page application built with vanilla HTML, CSS, and JavaScript. No build step, no framework dependencies. Served directly by the Express backend.

Design Decisions

🗄️ Why SQLite?

Zero configuration. No external database to install or manage. SQLite with WAL mode handles concurrent reads and writes well for single-server deployments. The database file lives in the backend directory and can be backed up by copying a single file.

📦 Why batched transport?

Sending individual HTTP requests per event would add unacceptable latency to agent execution. The Transport layer buffers events and sends them in batches, using a background thread so tracking never blocks your agent's main execution path.

🎨 Why vanilla JS dashboard?

No build step means zero friction to get started. The dashboard loads instantly without webpack, Vite, or any toolchain. For an observability tool, simplicity of deployment trumps framework features.