# Agent System Architecture Diagram ## System Overview (current stack) Docker Compose (dev) services on one network: - `web` (Vite dev) :5173 - `api` (Django + Channels) :8000 - `celery` worker shares Django code - `fyp-redis` broker/channel :6379 - `mcp-agent-server` MCP runtime :8001 (HTTP) MCP wiring: - `MCP_AGENT_URL=http://mcp-agent-server:8001` (required) - MCP server runs in HTTP mode, exposes `/execute` and `/health` endpoints - All agent execution delegates to the remote MCP server (no local LLM fallback) Flow: Frontend → API (HTTP), Frontend ↔ AgentConsumer (WS), API queues Celery, Celery calls MCP server over HTTP, events return via Redis → Channels → WS. ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ FRONTEND (Vue 3 + TypeScript) │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────────┐ │ │ │ Agents.vue │ │ AgentDetail.vue │ │ agentStore.ts │ │ │ ├─────────────────┤ ├──────────────────────┤ ├─────────────────────┤ │ │ │ • List agents │ │ • Run execution │ │ • WebSocket connect │ │ │ │ • Fetch from API│ │ • JSON input │ │ • Event handling │ │ │ │ • Show status │ │ • Live log display │ │ • State management │ │ │ └────────┬────────┘ │ • Stop button │ │ • Auto-reconnect │ │ │ │ │ • Status indicator │ │ • Type-safe API │ │ │ │ └────────┬─────────────┘ └─────────────────────┘ │ │ │ │ │ └───────────┼────────────────────┼─────────────────────────────────────────┘ │ │ │ │ WebSocket │ HTTP/REST │ ▼ ▼ ┌──────────────────────────────────────────────────────────────────────────┐ │ BACKEND (Django + Channels) │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌──────────────────────────┐ ┌─────────────────┐ │ │ │ AgentViewSet │ │ AgentConsumer │ │ Middleware │ │ │ ├─────────────────┤ ├──────────────────────────┤ ├─────────────────┤ │ │ │ REST API: │ │ WebSocket Handler: │ │ • Auth check │ │ │ │ • GET /agent/ │ │ • connect() │ │ • User validate │ │ │ │ • POST /agent/ │ │ • receive() │ │ • Group mgmt │ │ │ │ • GET /agent/id │ │ • handle_start_agent() │ └─────────────────┘ │ │ │ │ │ • handle_stop_agent() │ │ │ │ Returns: Agent │ │ • agent_event() │ ┌─────────────────┐ │ │ │ metadata │ │ • agent_completed() │ │ Serializers │ │ │ └────────┬────────┘ │ • agent_error() │ ├─────────────────┤ │ │ │ └────────┬─────────────────┘ │ • AgentSerializer │ │ │ │ │ • ExecutionSer. │ │ │ │ │ │ • EventSerializer │ │ │ │ └────────┬────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Django Database (SQLite/PostgreSQL) │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ • Agent (uuid, name, description, status, user) │ │ │ │ • AgentExecution (uuid, input_data, output_data, status) │ │ │ │ • AgentEvent (uuid, event_type, content, timestamp) │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────┬───────────────────────────────────────────────────┘ │ │ Celery Task Queue ▼ ┌──────────────────────────────────────────────────────────────────────────┐ │ CELERY WORKER PROCESS │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────────────┐ │ │ │ start_agent_task_mcp() [MCP-only execution] │ │ │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ │ │ 1. Initialize & Output "started" event │ │ │ │ │ │ │ │ │ │ │ │ 2. Call MCPAgentClient.execute_agent() │ │ │ │ │ │ └─ POST to {MCP_AGENT_URL}/execute │ │ │ │ │ │ with: agent_id, query, input_data │ │ │ │ │ │ │ │ │ │ │ │ 3. Await response from MCP server │ │ │ │ │ │ (handles all RAG, LLM, context retrieval) │ │ │ │ │ │ │ │ │ │ │ │ 4. Forward any events from MCP to WebSocket │ │ │ │ │ │ └─ Progress, message, step events displayed live │ │ │ │ │ │ │ │ │ │ │ │ 5. Save result & Output "completed" event │ │ │ │ │ │ └─ Send via Channel Layer to WebSocket Group │ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────┬───────────────────────────────────────────────────┘ │ │ Channel Layer Broadcast ▼ ┌──────────────────────────────────────────────────────────────────────────┐ │ REDIS (Message Broker & Cache) │ ├──────────────────────────────────────────────────────────────────────────┤ │ • Celery task queue │ │ • Channel layer for WebSocket group communication │ │ • Session cache (optional) │ └──────────────────────────────────────────────────────────────────────────┘ ``` MCP Runtime (HTTP mode) - Service: `mcp-agent-server` (container `dynavera-mcp-agent`) - Listens on `0.0.0.0:8001` with `/execute` and `/health` HTTP endpoints - Handles all agent execution: RAG retrieval, LLM inference, context management - Shares code and `build/rag_db` read-only from host - Completely separate from Django/Celery; communicates only via HTTP ## Execution Flow Sequence ``` Frontend Backend LLM System │ │ │ ├─ User Input ──────────────>│ │ │ (JSON via WebSocket) │ │ │ │ │ │ ├─ Create Execution │ │ │ Record │ │ │ │ │ ├─ Queue Celery Task │ │ │ │ │<── execution_started ──────┤ │ │ (WebSocket message) │ │ │ │ │ │ ├─ Output "initializing" │ │<── agent_event ────────────┤ │ │ (WebSocket) │ │ │ │ │ │ ├─ Load GPT4All Model │ │ │ │ │ │─────────────────────────>│ Load Model │ │ (~10-30 seconds) │ │ │<───────────────────────── Model Ready │ │ │ │ ├─ Output "retrieving" │ │<── agent_event ────────────┤ │ │ (WebSocket) │ │ │ │ │ │ ├─ Query RAG DB (if exists) │ │ │ (ChromaDB) │ │ │ │ │ ├─ Output "generating" │ │<── agent_event ────────────┤ │ │ (WebSocket) │ │ │ │ │ │ │───────────────────────────>│ Generate │ │ generate(prompt, │ Response │ │ max_tokens=200) │ │ │<───────────────────────── Response │ │ (5-30 seconds) │ │ │ │ │<── execution_completed ────┤ │ │ (WebSocket with output) │ │ │ │ │ └ Display Log & Result │ │ ``` ## Data Flow: Input to Output ``` User Enters JSON ↓ {"query": "What is fNIRS?"} ↓ Frontend sends via WebSocket ↓ ┌─────────────────────────────────────┐ │ AgentConsumer.receive(text_data) │ └──────────────┬──────────────────────┘ │ ├─ Parse JSON ├─ Validate action └─ Route to handler ↓ ┌─────────────────────────────────────┐ │ handle_start_agent() │ ├─────────────────────────────────────┤ │ • Get agent from DB │ │ • Create AgentExecution │ │ • Queue Celery task │ └──────────────┬──────────────────────┘ │ ├─ Send execution_started event │ (back to WebSocket) │ ├─ Queue in Celery/Redis │ ┌──────────────┴──────────────────────┐ │ │ ▼ ▼ Celery Worker Database Updated │ │ ├─ Fetch execution │ ├─ Initialize models │ ├─ Send progress events │ │ │ ├─ Query RAG (if available) │ │ ├─ Load embedder │ │ ├─ Connect to ChromaDB │ │ └─ Query for context │ │ │ ├─ Initialize LLM │ │ ├─ Load GPT4All model │ │ └─ Prepare prompt │ │ │ ├─ Generate response │ │ └─ model.generate() │ │ │ ├─ Create result dict │ │ │ ├─ Save to AgentExecution │ │ ├─ output_data │ │ ├─ status = 'completed' │ │ └─ completed_at │ │ │ └─ Send via Channel Layer to WebSocket Group │ ├─ event_type: agent_event ├─ event_type: agent_completed │ ▼ Frontend receives │ ├─ Update agentStore ├─ Push to eventLog ├─ Display in UI │ ▼ User sees result ``` ## Component Interaction ``` ┌─────────────────────────────────────────────────────────┐ │ FRONTEND STATE MANAGEMENT │ ├─────────────────────────────────────────────────────────┤ │ │ │ agentStore (Pinia) │ │ ├─ socket: WebSocket connection │ │ ├─ isConnected: boolean │ │ ├─ agentId: UUID │ │ ├─ currentExecutionId: UUID │ │ ├─ executionStatus: 'idle'|'running'|'completed' │ │ ├─ events: Array │ │ │ │ │ ├─ connect(agentId) │ │ ├─ startAgent(inputData) │ │ ├─ stopAgent() │ │ ├─ disconnect() │ │ └─ handleMessage(data) │ │ │ │ AgentDetail.vue (Uses Store) │ │ ├─ Subscribes to: │ │ │ ├─ agentStore.isConnected │ │ │ ├─ agentStore.executionStatus │ │ │ └─ agentStore.eventLog │ │ │ │ │ └─ Calls: │ │ ├─ agentStore.connect() on mount │ │ ├─ agentStore.startAgent() on button click │ │ ├─ agentStore.disconnect() on unmount │ │ └─ agentStore.stopAgent() on stop button │ │ │ └─────────────────────────────────────────────────────────┘ ``` ## Message Type Mapping ``` WebSocket Message Type → Handler Function → Event Display "execution_started" → handleMessage → "Started" tag + message "agent_event" → handleMessage → Event type specific ├─ "progress" → Display stage → [PROGRESS] stage: message ├─ "message" → Display text → [MESSAGE] content └─ "step" → Display step → [STEP] content "execution_completed" → handleMessage → "Completed" tag + output "execution_error" → handleMessage → "Error" tag + message "execution_stopped" → handleMessage → "Stopped" tag + message "error" → handleMessage → "Error" tag + message "connection" → handleMessage → Console log ``` ## Database Schema Relationships ``` User (from auth) │ ├─────── (1:N) ─────────> Agent │ ├─ uuid (PK) │ ├─ name │ ├─ description │ ├─ status │ ├─ created_at │ └─ updated_at │ │ │ ├─────── (1:N) ──────────> AgentExecution │ ├─ uuid (PK) │ ├─ status │ ├─ input_data (JSON) │ ├─ output_data (JSON) │ ├─ error_message │ ├─ created_at │ ├─ started_at │ ├─ completed_at │ │ │ │ │ ├─ (1:N) ──> AgentEvent │ │ ├─ uuid (PK) │ │ ├─ event_type │ │ ├─ content (JSON) │ │ └─ timestamp │ │ │ └─ user_id (FK) │ │ └──────────────────────────────────────────────────────────────────┘ ```