Local-first, zero-cost AutoML. Upload a CSV and a multi-agent system runs the full ML pipeline — cleaning, EDA, feature engineering, model training, evaluation — streaming its reasoning, code, plots, and tables in real time. Everything runs on your machine: local models via Ollama by default, a local Jupyter kernel for execution, and a local SQLite store for history.
- Your data never leaves your machine. No upload to a cloud service, no third-party analytics. With the default Ollama provider, not even prompts go out.
- $0 per run. No subscription, no metered API — the default setup has no cloud dependency at all. (Anthropic/OpenAI are supported as opt-in providers if you want a stronger model and don't mind the tokens.)
- You stay in control. Watch every decision as it streams, and approve, modify, or reject each pipeline stage through human-in-the-loop gates.
Warning
AgentML runs LLM-generated Python on your machine and has no authentication.
It is designed to run locally, for a single trusted user, bound to 127.0.0.1.
The code validator is a guardrail, not a security sandbox — an agent can still read
and write files your user account can access. Do not expose this server to a
network or run it on shared/multi-user infrastructure without adding your own
authentication and OS-level isolation. See Security.
Upload a CSV and just ask — the dataset is previewed in chat, and the agent starts reasoning about your data immediately:
Agent Mode running the pipeline — the agent streams its reasoning, executes code in the Jupyter kernel, and tracks progress through the five pipeline stages (Prep → EDA → Features → Training → Evaluation):
Rich output in chat — plots render inline as the agent explores your data:
Learn ML concepts interactively — no dataset required. Ask about a concept ("I want to learn gradient descent") and the agent writes a runnable demo, plots it, and explains the intuition step by step:
- 8 specialized agents coordinated by LangGraph:
- Orchestrator — Routes between pipeline stages, manages forward and backward navigation
- General Agent — Handles free-form conversation and data exploration (no pipeline)
- Preprocessing Agent — Data cleaning, type fixing, missing values, duplicates
- EDA Agent — Exploratory data analysis on cleaned data
- Feature Engineering Agent — Encoding, scaling, feature creation and selection
- Modeling Agent — Algorithm selection, training, cross-validation
- Evaluation Agent — Metrics, comparisons, final recommendations
- Hungry Agent — Quality review gate that cross-references stage findings to catch missed opportunities
- Chat Mode — Step-by-step control. Ask questions, run code, explore data. No pipeline triggers. You drive every action. Also works with no dataset at all — use it as an interactive ML tutor that answers concept questions with runnable, plotted examples.
- Agent Mode — Autonomous end-to-end pipeline. The system understands your data, asks clarifying questions, then runs the full ML pipeline with human-in-the-loop approval gates between stages.
- Forward flow: preprocessing > eda > feature engineering > modeling > evaluation
- Backward navigation: If evaluation shows poor results, the orchestrator can route back to earlier stages with specific improvement strategies
- Iterative improvement loop: Performance thresholds trigger automatic revisits with a 2-attempt limit per stage
- "Now I Know Better" restart: After completing the pipeline, the system can propose restarting with insights gained
- Approval gates at every stage transition in Agent Mode
- Options: approve, modify (with feedback), or reject (redo the stage)
- 10-minute timeout with auto-approve fallback
- WebSocket-based streaming of agent thinking, decisions, code execution, plots, and tables
- Live pipeline progress indicator
- Structured message parts: thinking blocks, decision tags, code cells with rich output
- Persistent Jupyter kernel per session with full pandas/sklearn/matplotlib support
- Process-isolated execution — The kernel runs in a child process via ZMQ. If libzmq crashes, only the child dies; the server spawns a new one automatically
- Execution state tracking via ExecutionLedger — variables tracked outside the kernel for crash recovery
- Error loop detection — 2 consecutive errors trigger recovery guidance, 3 force escalation, kernel crashes handled separately
- No-upload chat — Start a session without a CSV for general conversation
- Read-only expired sessions — When a session's CSV is no longer on disk, the session switches to insights-only mode (no code execution, but conversation history and findings preserved)
- Per-session concurrency locks — Prevents concurrent agent runs on the same session
- Auto-cleanup — 1-hour idle timeout with periodic session cleanup
- Session rename via sidebar
- Normal — Professional, numbers-focused output
- Quirky — Puns, pop culture references, and playful language in reasoning (code stays clean)
- Python 3.11+ (3.12 recommended)
- Node.js 18+
- Ollama for the default fully-local setup — install it and pull a model:
ollama pull qwen3:8b. (Skip this only if you plan to use Anthropic/OpenAI instead.)
cd backend
python -m venv venv
# Activate virtual environment
# Windows: venv\Scripts\activate
# macOS/Linux: source venv/bin/activate
pip install -r requirements.txt
python -m ipykernel install --user
python -m uvicorn main:app --reload --port 8002npm install
npm startOpen http://localhost:3000 and start analyzing. With Ollama running, it works out of the box — no API key, no account. To use a cloud provider instead, pick it and add a key in Settings.
You can either upload a CSV and let the agent pipeline run, or just type "hi" to start a chat session without any dataset.
| Variable | Description | Default |
|---|---|---|
AGENTML_API_KEY |
API key for the active cloud provider (also ANTHROPIC_API_KEY / OPENAI_API_KEY) |
(none — set via UI or env) |
AGENTML_LLM_PROVIDER |
anthropic, openai, or ollama |
ollama |
AGENTML_CORS_ORIGINS |
Comma-separated allow-list of browser origins. Not set → local dev origins only. Setting * disables the allow-list (unsafe — see Security). |
http://localhost:3000, http://127.0.0.1:3000 |
AGENTML_ALLOW_PRIVATE_OLLAMA |
Allow an Ollama URL on a private/LAN address (off by default to blunt SSRF) | false |
AGENTML_SESSION_TTL_DAYS |
Purge session history after N days of inactivity (0 = keep forever) |
30 |
REACT_APP_API_URL |
Backend API URL | http://localhost:8002 |
REACT_APP_WS_URL |
Backend WebSocket URL | derived from API URL |
Settings can also be configured via the Settings modal in the UI. See .env.example and backend/.env.example for templates.
AgentML's core feature — an AI agent that writes and runs Python against your data — is also its main risk. Read this before running it anywhere but your own laptop.
Threat model: single trusted local user. The backend has no authentication.
Anyone who can reach the HTTP/WebSocket port can create sessions, run the agent, and
therefore execute code as your user account. That is acceptable when it is bound to
127.0.0.1 on your own machine and nothing else can reach it. It is not acceptable
on a shared host, a public IP, or behind a naive port-forward.
Code execution is not sandboxed. guardrails/code_validator.py blocks obvious
escapes (import os, subprocess, eval, shell magics, dunder introspection) and
raises the bar against a careless model, but it is a denylist, not a jail. Because
pandas/numpy/sklearn are intentionally allowed, generated code can still read any file
your account can read (pd.read_csv('~/.ssh/id_rsa')) and write files (df.to_csv(...)).
If you need a real boundary, run the backend inside a container as a non-root user with
no network egress and only the dataset directory mounted.
Hardening that ships by default:
- Server binds to
127.0.0.1(loopback) vianpm run backend. - CORS defaults to the local dev origins only — a wildcard is never the default, so a
random website can't drive your local API from your browser. Override with
AGENTML_CORS_ORIGINSonly for origins you trust. - Ollama URLs resolving to private/link-local/metadata addresses are rejected (SSRF),
unless you opt in with
AGENTML_ALLOW_PRIVATE_OLLAMA=1. - Uploads are size-capped (50 MB) as they stream, CSV-only, with sanitized filenames.
- Prompt-injection defense escapes agent-protocol tags in dataset/column names and user text; per-field length caps bound context flooding.
- HTTP and WebSocket rate limiters throttle abusive clients.
Secrets. Provider API keys are stored in plaintext — on the backend in
backend/.agentml_settings.json (gitignored) and in the browser's localStorage. Both
are readable by anything running as your user / on the origin. Prefer supplying keys via
environment variables, and treat a machine running AgentML as holding those keys in the
clear. If you don't want cloud keys involved at all, use the Ollama provider (the
default) for fully local inference.
If you deploy this beyond localhost, you are responsible for adding: authentication and per-user session isolation, OS-level sandboxing of the kernel, and a real secrets store. None of that is built in.
Found a vulnerability? Please report it privately via a GitHub security advisory rather than a public issue.
Frontend (React + Zustand)
|
| WebSocket / HTTP
v
Backend (FastAPI)
|
+-- Agent Graph (LangGraph)
| |
| +-- Entry Router (zero-cost, no LLM call)
| | |
| | +-- General Agent (chat mode)
| | +-- Orchestrator (pipeline mode)
| | |
| | +-- 5 Stage Agents
| | +-- Hungry Agent (quality review)
| |
| +-- execute_code tool
| |
| v
+-- Jupyter Kernel (subprocess-isolated via ZMQ)
|
+-- SQLite Database (sessions, messages, experiments)
For the full system design, see docs/architecture.md. For a per-file walkthrough of what every component does and how it connects, see docs/components.md.
agentml/
├── backend/ # Python backend (FastAPI + LangGraph + Jupyter)
│ ├── main.py # FastAPI app, CORS, rate limit, lifespan, route mounts
│ ├── config.py # Settings singleton (LLM provider, API keys, persona)
│ ├── agent/
│ │ ├── pipeline.py # LangGraph topology + nodes + checkpointer (the big one)
│ │ ├── driver.py # run_agent_streaming — the entry point the WS calls
│ │ ├── common.py # Shared building blocks: _get_llm, message utils, thresholds
│ │ ├── routing.py # STAGES + get_next_stage (deterministic stage order)
│ │ ├── schemas_llm.py # Structured-output models (EvaluationDecision, DebateVerdict)
│ │ ├── debate.py # Debate kernel-code assets (scoreboard/viz/preflight)
│ │ ├── state.py # AgentState TypedDict carried through graph nodes
│ │ ├── tools.py # execute_code tool — only tool any agent can call
│ │ ├── ledger.py # ExecutionLedger — tracks kernel variables externally
│ │ ├── events.py # EventEmitter — queues streaming events to the WS
│ │ ├── parser.py # Parses <thinking>/<decision>/<stage>/etc tags
│ │ ├── llm_utils.py # invoke_with_retry / ainvoke_with_retry
│ │ ├── hitl.py # Human-in-the-loop gate definitions
│ │ └── prompts/ # System prompt TEXT only — fetched via get_prompt(stage)
│ │ ├── __init__.py # get_prompt() lazy registry + available_stages()
│ │ ├── base.py # BASE_INSTRUCTIONS + QUIRKY_PERSONA (shared fragments)
│ │ ├── general.py # GENERAL_PROMPT (unified entry point)
│ │ ├── orchestrator.py # EVAL_DECISION_PROMPT (end-of-run gate)
│ │ ├── preprocessing.py
│ │ ├── eda.py
│ │ ├── feature.py
│ │ ├── modeling.py # MODELING_A_PROMPT + MODELING_B_PROMPT (debate specialists)
│ │ ├── evaluation.py
│ │ ├── hungry.py # accuracy-obsessed review gate
│ │ └── debate.py # DEBATE_A/B_SYSTEM + JUDGE_SYSTEM personas
│ ├── api/
│ │ ├── schemas.py # Pydantic request/response models
│ │ └── routes/
│ │ ├── upload.py # POST /api/upload, /api/upload-multiple
│ │ ├── chat.py # POST /api/chat (legacy sync fallback)
│ │ ├── ws.py # /ws/{session_id} (primary streaming endpoint)
│ │ ├── sessions.py # CRUD on sessions
│ │ ├── settings.py # GET/POST /api/settings
│ │ └── models.py # GET /api/models/available (cloud + Ollama)
│ ├── executor/
│ │ └── notebook.py # NotebookExecutor — subprocess-isolated Jupyter kernel
│ ├── db/
│ │ ├── database.py # SQLite (sessions, messages, experiments)
│ │ └── models.py # Pydantic record models
│ ├── guardrails/
│ │ ├── code_validator.py # AST allow/block list (blocks os/sys/eval/etc)
│ │ ├── prompt_sanitizer.py # Prompt injection defense (escapes agent tags)
│ │ └── rate_limiter.py # HTTP + WebSocket rate limiters
│ └── utils/
│ ├── session_manager.py # ChatSession dataclass + in-memory store + lifecycle
│ ├── data_utils.py # Column metadata + preview row formatting
│ └── plot_utils.py # Matplotlib figure → base64 helper
│
├── src/ # React frontend
│ ├── index.js # React root
│ ├── App.js # <AppLayout><ChatLayout /></AppLayout>
│ ├── api/
│ │ └── client.js # Axios instance + WebSocket factory
│ ├── store/
│ │ └── chatStore.js # Zustand store — single source of truth (~700 LOC)
│ ├── utils/
│ │ └── apiError.js # Normalize axios/FastAPI errors to user text
│ └── components/
│ ├── layout/
│ │ ├── AppLayout.jsx # Sidebar + main content wrapper
│ │ └── Sidebar.jsx # Session list, rename, delete, new chat
│ ├── chat/
│ │ ├── ChatLayout.jsx # Header, banners, MessageList, ChatInput
│ │ ├── MessageList.jsx # Scrollable, auto-scroll, streaming overlay
│ │ ├── MessageBubble.jsx # Renders each message part by type
│ │ ├── ChatInput.jsx # Textarea + drag-drop CSV + model selector
│ │ ├── ModelSelector.jsx # Dropdown listing cloud + Ollama models
│ │ ├── CodeBlock.jsx # Collapsible code cell w/ output
│ │ ├── ChartOutput.jsx # Base64 PNG renderer
│ │ ├── TableOutput.jsx # DOMPurify-sanitized pandas HTML tables
│ │ ├── ThinkingBlock.jsx # Collapsible <thinking> block
│ │ ├── ThinkingIndicator.jsx # Animated indicator w/ stage message
│ │ ├── DecisionCard.jsx # <decision> callout
│ │ └── HITLCard.jsx # Human-in-the-loop prompt UI
│ ├── pipeline/
│ │ └── PipelineProgress.jsx # Horizontal 5-stage progress bar
│ └── shared/
│ ├── SettingsModal.jsx # API keys, persona, Ollama URL
│ └── DataTable.jsx # Generic table (currently unused)
│
├── docs/ # Documentation
│ ├── architecture.md # High-level system design + data flows
│ ├── components.md # Per-file reference (this is your map)
│ ├── api.md # HTTP + WebSocket API reference
│ ├── setup.md # Installation, environment, troubleshooting
│ └── images/ # Screenshots used in the README
│
├── public/ # CRA public/ (favicon, index.html)
└── package.json # Frontend dependencies + CRA scripts
Backend: FastAPI, LangGraph, LangChain (OpenAI / Anthropic / Ollama bindings), jupyter-client, SQLite, Pydantic
Frontend: React 18, Zustand, TailwindCSS, Framer Motion, Lucide Icons, ReactMarkdown, react-syntax-highlighter, DOMPurify, axios, react-dropzone
AI: Claude (Sonnet 4.5 / Opus 4), OpenAI (GPT-4o / 4o-mini / o3-mini), or any Ollama model (default: qwen3:8b). Configurable via the in-app Settings modal.
- Architecture — System design, agent graph, data flows
- Component Reference — What every file does and how it connects
- API Reference — HTTP and WebSocket endpoints
- Setup Guide — Installation and troubleshooting
- Contributing — Development guidelines
New to the codebase? Read docs/components.md first, then open backend/agent/driver.py and follow run_agent_streaming end-to-end (the graph itself is built in backend/agent/pipeline.py).




