OPEN SOURCE

Showcase: data-analyst-agent — An Autonomous BI Agent That Writes and Runs Python Against Your Data

OCT 13, 2026·9 MIN READ

Open-source showcase · github.com/Shivay00001/data-analyst-agent

TL;DR: data-analyst-agent is a full-stack autonomous BI agent: upload a CSV, type a question in plain English, and it has an LLM write real Python (pandas/numpy/matplotlib/seaborn), executes the code in an isolated subprocess with a 60-second timeout, and returns the generated code, the output, and publication-style charts — all tracked in a task database and displayed in a Next.js console. I read backend/server.py end to end: this is genuinely autonomous analysis, not a chat wrapper. Status: working demo — the agentic loop is real, with the sandboxing and auth hardening a production deployment would need.

1. What it is

Two services: a FastAPI backend (backend/server.py, ~8.9 KB) with SQLAlchemy models (models.py: Dataset and ExecutionLog tables) and an async SQLite store, plus a Next.js frontend (frontend/src/app/page.tsx, ~9.7 KB) that handles dataset upload, prompt entry, provider/key selection, and live task polling. A Dockerfile ships the backend. Dependencies are the real data stack: pandas, numpy, matplotlib, seaborn, litellm, sqlalchemy, aiosqlite.

The workflow mirrors how a human analyst works: look at the data, write code, run it, look at the chart, iterate. Here the LLM writes the code, the backend runs it, and the console shows you everything — including the code itself, which is the right call for trust.

2. The problem it solves

Business data analysis has a bottleneck problem: every "can you just check…" question waits in a queue behind an analyst's backlog, and BI dashboards answer yesterday's questions, not today's. Meanwhile, LLMs are fluent in pandas but can't see your data and can't run code — and letting them near production data raises exactly the right fears about correctness and safety.

This agent closes the loop in a controlled way: the LLM never touches your data directly. It writes analysis code against a stated file path, and only the backend executes that code — in a separate process, with a timeout, writing charts to a per-task directory. You get analyst-speed answers with an audit trail: the exact code that produced every number and chart is stored in the database.

3. How it works — the agentic loop

  1. Upload. POST /api/datasets/upload saves the file to workspace/<uuid>_<filename> and records it in the datasets table. The /workspace directory is also mounted as static files, so generated charts are directly servable.
  2. Dispatch. POST /api/execute takes {prompt, provider, dataset_filepath}, creates an ExecutionLog row with status pending, and hands the job to a FastAPI BackgroundTask so the HTTP request returns immediately with a task_id.
  3. Generate. The background worker calls litellm.acompletion with a carefully written system prompt. The prompt is worth quoting in spirit: the model is told it is an autonomous BI analyst, must output only valid Python in a fenced block, must handle missing values before any computation, must print the exact mathematical formula before every metric (auditability), must save plots with plt.savefig('name.png', dpi=300, bbox_inches='tight') — never plt.show() — wrapped in try/except, and must print each saved filename so the backend can capture it. Predictive ML is explicitly forbidden (no scikit-learn): this is a descriptive-analytics agent that knows its lane. The provider string routes keys automatically: gpt-* → OpenAI, claude-* → Anthropic, gemini-* → Google, zhipu-* → Zhipu GLM, ollama* → a local Ollama server at localhost:11434 — so you can run the brain fully offline.
  4. Execute. The fenced ```python block is extracted, prepended with a styling preamble (pandas/numpy/matplotlib/seaborn imports plus sns.set_theme(style='whitegrid')), written to a per-task script.py, and run via asyncio.create_subprocess_exec with the task directory as cwd and a hard 60-second timeout (the process is killed on timeout). Stdout and stderr are captured separately and stored.
  5. Collect. Any output line ending in .png is treated as a saved chart and converted to a servable URL under /workspace/<task_id>/. The log row flips to completed (or failed, with the error captured) — code, output, and chart URLs all persisted.
  6. Display. The frontend polls GET /api/tasks/{task_id} every 2 seconds while the status is pending/running, then renders three panes: generated code, execution output, and the charts. The default prompt in the UI — "Group sales by month and plot a bar chart of total revenue." — is exactly the kind of question this loop eats for breakfast.

4. The details that show it was built by someone who's run analysis in production

4b. What the frontend shows — and why showing the code matters

The console's three-pane result view (generated code, execution output, charts) is the most important UX decision in the repo, and it deserves a paragraph on why. Most "AI analyst" demos show you the chart and hide the code — which means you're trusting a black box with your numbers. This console inverts that: the generated Python is a first-class pane, the raw stdout (including the printed formulas the system prompt mandates) is a second pane, and the chart is the third.

That ordering is a trust model. When the agent says "revenue grew 12% month-over-month," you can read the exact groupby it ran, see the formula it printed, and check whether it handled the NaNs it was told to handle. For business decisions made on these numbers, "show your work" isn't a nicety — it's the difference between a demo and a tool. The 2-second polling loop (setInterval against /api/tasks/{task_id}, stopping on completed/failed) keeps the UI live without websockets, which is the right complexity level for this stage.

One more detail worth noting: the frontend passes API keys per-request via headers (X-OpenAI-Key and friends) rather than requiring them baked into the server environment. That makes the console usable against a shared backend where each analyst brings their own key — a small touch that anticipates multi-user operation even though auth itself isn't built yet.

5. How to run it

# Backend
cd backend
python -m venv venv && venv/Scripts/activate
pip install -r requirements.txt
uvicorn server:app --port 8000

# Frontend
cd frontend
npm install
npm run dev

Open the console, add your provider key (or point it at local Ollama), upload a CSV, type a question like the default monthly-revenue example, and watch the three panes fill in: code, output, charts. Database tables are created automatically on startup via the lifespan hook.

6. Honest status: what works, what needs hardening

Working. The full loop — upload → LLM code generation → isolated execution → chart capture → persisted task log → live-polling UI — is implemented and coherent. Multi-provider routing including local Ollama is real. The auditability constraints in the system prompt are genuinely unusual and genuinely good.

Needs hardening before production (from reading the code):

7. Roadmap — from demo to product

  1. Hard sandboxing — run generated code in throwaway containers with no network, CPU/memory caps, and read-only data mounts.
  2. Auth + workspaces — users, per-user datasets, per-run cost attribution (LLM tokens per task are the natural billing unit).
  3. SQL generation — extend beyond CSVs: let the agent write SQL against connected databases (the repo already has SQLAlchemy and a database.py — the seam is there).
  4. Streaming + iteration — stream the generated code as it's written, and let users ask follow-ups against the same task context ("now break it down by region").
  5. Eval harness — a benchmark set of datasets × questions with expected chart types and metric values, run against every prompt or model change.

Frequently asked questions

Does the LLM see my actual data?

No — it receives only the dataset's file path and your prompt. It writes code against that path; the backend executes the code where the data lives. The model never ingests your rows.

Can I use a local model instead of a paid API?

Yes. Any provider string starting with ollama routes to http://localhost:11434, so a local Ollama model can be the analyst brain with zero API spend.

What if the generated code has a bug?

The failure is captured, not hidden: stderr is stored in the execution log, the task flips to failed, and you see both the code and the error in the console — which is exactly the material you'd need to fix the prompt or retry.

Is running LLM-generated code safe?

In the current repo: safe enough for a local demo on your own machine, not for a multi-user deployment. The subprocess-plus-timeout isolation is real but thin — containerize the executor before exposing it to other users.

The bottom line

data-analyst-agent is the most architecturally interesting repo in this batch: a genuine agentic loop (generate → execute → capture → persist → display) with production instincts baked into the prompt engineering — formula auditability, NaN-first discipline, save-don't-show plotting. The sandboxing and auth need real work before it serves anyone but you, and the ports need aligning, but the core idea is sound and the implementation is honest: you see the code it wrote, the output it produced, and the charts it saved. That's what an AI analyst should look like.

Source: github.com/Shivay00001/data-analyst-agent — MIT licensed. Verified by reading backend/server.py, backend/models.py, backend/database.py, and frontend/src/app/page.tsx on the main branch.

References & further reading

  1. github.com/Shivay00001/data-analyst-agent

Cite this article: Shivam Kumar (2026). Showcase: data-analyst-agent — An Autonomous BI Agent That Writes Python. VisionQuantech. https://visionquantech.com/blog/showcase-data-analyst-agent.html

Want this for your business?

VisionQuantech builds AI receptionists, chatbots & automation for clinics, home services, real estate & more.

Book a Free Demo
SHARE𝕏in⧉
Chat with us Email us