DEV Community

KibeπŸ”ŒπŸ’»
KibeπŸ”ŒπŸ’»

Posted on

I built a self-hostable AI data analyst β€” 4 agents, sandboxed code execution, your choice of LLM

Every time I needed to explore a dataset quickly, the same wall appeared: spin up a notebook, deal with encoding errors, write the cleaning boilerplate, decide which chart type actually makes sense, iterate when the data has surprises. The mechanical parts β€” null handling, imputation, finding correlations, picking a visualization β€” aren't interesting work. They're just in the way.

Tools like ChatGPT's Code Interpreter or Julius AI solve this. But they upload your data to a cloud you don't control, lock you into one LLM, and charge per seat. I wanted something that ran on my own hardware, worked with whichever model I preferred, and didn't require an account to try.

So I built Insight Orchestra β€” an open-source, self-hostable AI data analyst. Connect a file or a database, watch four specialized agents work through the analysis in real time, then keep asking follow-up questions in plain English.

GitHub logo laban254 / insight-orchestra

Self-hostable AI data analyst: a multi-agent LLM pipeline with natural-language querying and sandboxed Python execution.

Insight Orchestra

Your data, analyzed by a team of AI agents.

Connect a data file or a database and watch specialized agents clean it, form hypotheses,
debate them, and visualize what matters β€” then ask follow-ups in plain English

Website Β· Docs Β· Report a bug

CI CodeQL Latest release License Stars Open in GitHub Codespaces



Insight Orchestra β€” four agents cleaning, hypothesising, debating and visualising a dataset

A real run on the bundled Sales dataset β€” unedited.


What is Insight Orchestra?

Insight Orchestra is an open-source AI data analyst you can self-host β€” think Julius AI or ChatGPT's data analysis, but running on your own hardware, with your choice of LLM, where your data never leaves your machine. Upload a data file β€” CSV, TSV, Excel, JSON, or Parquet β€” or connect a PostgreSQL, MySQL, SQLite, or DuckDB database, and a 4-agent pipeline cleans the data, generates evidence-backed hypotheses, scores them in an LLM-refereed debate, and builds interactive Plotly charts. Then keep asking questions in plain English: an NLQ agent writes…





What it accepts

Files: CSV, TSV (auto-detects encoding and delimiter β€” semicolons and pipes work fine), Excel .xlsx, JSON records, Parquet.

Databases: PostgreSQL, MySQL, SQLite, DuckDB β€” connect with a standard connection string and the agent can query across all your tables with JOINs, not just one materialized table at a time. BigQuery is available as an experimental connector.

LLMs: OpenAI, Anthropic, or DeepSeek in the cloud, or fully local and private with Ollama. You can switch provider and model at runtime through the UI β€” no restart needed.


The 4-agent pipeline

When you run an analysis, four agents execute sequentially, each passing its output to the next:

Your data (file or database table)
              β”‚
              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ [1] Data Janitor                 β”‚
β”‚ Removes dupes, imputes missing   β”‚
β”‚ values, flags bias (>30% null),  β”‚
β”‚ detects outliers via IQR         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ [2] Hypothesis Bot               β”‚
β”‚ Builds stats summary (means,     β”‚
β”‚ correlations, distributions)     β”‚
β”‚ β†’ LLM generates 5-8 directional  β”‚
β”‚ insights grounded in the numbers β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ [3] Debate Manager               β”‚
β”‚ LLM scores each hypothesis on    β”‚
β”‚ confidence Γ— business value,     β”‚
β”‚ citing the actual statistics     β”‚
β”‚ β†’ elects a consensus finding     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ [4] Viz Whiz                     β”‚
β”‚ Up to 6 Plotly charts, chart     β”‚
β”‚ type chosen by data types        β”‚
β”‚ (scatter, bar, histogram, etc.)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 β–Ό
         Narrative summary
         + suggested follow-up questions
         (streamed live to your browser)
Enter fullscreen mode Exit fullscreen mode

Progress for each stage streams to your browser via Server-Sent Events as it runs β€” you see what's happening instead of waiting for a spinner to resolve into a wall of output.

After the pipeline finishes, you can keep asking questions in plain English. The NLQ agent converts them to pandas code, executes in a sandbox, and renders a chart if one was produced. For connected databases, a separate agent generates read-only SQL instead of pandas code β€” JOIN-capable across every table in your schema.


Why multi-agent, not one LLM call?

The first prototype was a single prompt: "here's the CSV schema and some sample rows, write me an analysis." It works on clean, small datasets. On real data it falls apart.

A single call trying to clean, analyze, and visualize at once has no recovery path when one step fails. You don't know whether the bad chart came from a cleaning mistake, a wrong assumption in the analysis, or buggy visualization code. There's no partial output, no progress feedback, and the prompt balloons quickly when you embed schema, sample rows, and instructions together.

Splitting into agents gives you a different contract: each agent has one job and produces structured output. The Debate Manager receives the same statistics summary the Hypothesis Bot used β€” it's scoring hypotheses against actual evidence, not just voting on text. Failures are local. The system degrades gracefully if the LLM is unavailable: the Janitor runs without one, and the Hypothesis Bot falls back to heuristic group/correlation analysis and labels the output accordingly.


The part I spent the most time on: running LLM-generated code safely

The NLQ agent writes Python and runs it. exec() on untrusted code is obviously dangerous β€” the model can write os.system(...), read files outside the data directory, or make network calls. Even if you trust the model, a user can upload a CSV with a prompt-injected column value.

The solution is RestrictedPython. Every generated script goes through two passes before execution:

  1. AST check β€” walks the syntax tree and blocks dangerous nodes before a byte of code runs
  2. Restricted builtins β€” the execution context has no open, no os, no subprocess, no __import__ outside a curated allowlist

The model can use NumPy, pandas, and Plotly. It cannot touch the filesystem or the network. If generated code tries, it gets a clean exception.

There's a real trade-off here: RestrictedPython occasionally blocks valid pandas patterns that use unusual builtins, and it doesn't cover every possible attack surface. The allowlist needed tuning. But it's a meaningful reduction in blast radius compared to raw exec, and the NLQ agent retries with the error message fed back to the model β€” most blocked patterns resolve on retry.


Quick start

Prerequisites: Docker, Docker Compose v2, Git. 4 GB RAM (8 GB+ for local LLMs).

The easiest path is the setup wizard, which asks which LLM provider to use, writes backend/.env, and pulls prebuilt images:

curl -fsSL https://raw.githubusercontent.com/laban254/insight-orchestra/main/install.sh | bash
Enter fullscreen mode Exit fullscreen mode

Or clone first and run the wizard yourself:

git clone https://github.com/laban254/insight-orchestra.git
cd insight-orchestra
./setup.sh
Enter fullscreen mode Exit fullscreen mode

For a fully non-interactive setup (CI, provisioning scripts):

# Local model, no API key needed
./setup.sh --provider ollama -y

# Or with a cloud provider
./setup.sh --provider openai --api-key sk-... -y
Enter fullscreen mode Exit fullscreen mode

If something doesn't start, ./setup.sh doctor checks Docker, ports, config, and running services.

Open http://localhost:3000. No login required by default.


Authentication: off by default, real when you need it

Insight Orchestra ships with auth disabled. For local use, a personal install, or an internal tool on a private network, requiring login adds friction with no benefit.

One environment variable flips the full RBAC system on:

AUTH_ENABLED=true
Enter fullscreen mode Exit fullscreen mode

With auth enabled you get: local accounts (email + password), OIDC SSO (Google, Okta, or any standards-compliant IdP), API key management with optional expiry, three roles (admin / member / viewer), and an audit log for logins and user management actions.


A few things worth knowing

Query cache β€” the same question against the same dataset and model is served from an in-memory cache (1 hour TTL) rather than calling the LLM again. Follow-up sessions that re-ask a prior question are instant.

Workspaces β€” analysis sessions are saveable and reopenable. The platform checks whether the original dataset is still available and surfaces a clear error up front rather than failing silently mid-session.

Degraded mode β€” if your LLM provider is unreachable, the pipeline doesn't silently return heuristic output as if it were LLM-backed. Each stage that fell back is named explicitly in the response so you know what you're looking at.

Large dataset sampling β€” datasets over 250k rows are sampled for the pipeline, with a notice showing how many rows were analyzed vs. the total.

RAG grounding (optional) β€” the Hypothesis Bot can pull findings from previously analyzed datasets via vector search, grounding new hypotheses in past work. Needs a PostgreSQL instance with the pgvector extension and an OpenAI or Ollama embedding model.


The stack

Layer Technology
Backend FastAPI (Python)
Frontend Next.js (TypeScript)
Agents Google ADK
Real-time Server-Sent Events
Session state Redis (in-memory fallback)
LLM providers OpenAI, Anthropic, DeepSeek, Ollama
Code sandbox RestrictedPython
Deployment Docker Compose + prebuilt images

Try it

The repo is at github.com/laban254/insight-orchestra. MIT-licensed. There are docs covering setup, the agent pipeline, and the API reference, and a CONTRIBUTING.md if you want to add an agent or a connector.

If you run into something rough, open an issue. If you find it useful, a star helps others find it.

What data source would make this most useful for your workflow β€” a specific database connector, a file format, or something else entirely?

Top comments (1)

Collapse
 
dhruv_malaviya_cdcc71e595 profile image
Dhruv Malaviya

"They upload your data to a cloud you don't control, lock you into one LLM, and charge per seat" is the whole case for self-hosting this, and multi-DB with real JOINs rather than one materialised table is the right call.

One thing worth separating: "runs on my own hardware" and "data stays under my control" aren't the same requirement. A Cube on Krova Cloud (I work there) is a Linux server you administer, not a shared platform , your data doesn't sit in a third party's pipeline, but you're not capped by what a laptop can hold.

For agent-generated code specifically, the isolation matters more than it does for a notebook. Each Cube has its own kernel rather than sharing the host's, so a bad pd.read_csv on hostile Parquet is contained at the hardware boundary. 4 vCPU / 8 GB / 80 GB is $19.42/mo, billed by the minute.

Honest tradeoff: your own hardware is still the stronger privacy posture. This is about scale and containment, not secrecy.

How are you sandboxing the code execution right now , subprocess, container, or something else?