·4 min read

Build an AI Data Analyst with Upstash Box

Ali Tarık ŞahinAli Tarık ŞahinSoftware Engineer @Upstash
https://upstash.com/blog/box-ai-data-analyst

150+ countries, 15 years of happiness data. We grabbed it from Kaggle, described the analysis we wanted, and had charts in minutes, without setting up a Python environment or installing anything locally.

Happiness score trends with confidence bands

Top and bottom 5 countries by happiness score across 15 years, confidence bands included.


Here's How

We used the World Happiness Report (2011–2025) from Kaggle. Create the box and upload the dataset:

import "dotenv/config"
import fs from "node:fs"
import { Agent, Box, ClaudeCode } from "@upstash/box"
 
const box = await Box.create({
  runtime: "python",
  agent: {
    harness: Agent.ClaudeCode,
    model: ClaudeCode.Sonnet_4_6,
  },
})
 
const csv = fs.readFileSync("world_happiness_report_2005_2025.csv", "utf-8")
 
await box.files.write({
  path: "/workspace/home/happiness.csv",
  content: csv,
})

That's all the setup. The box is provisioned, the CSV is on its filesystem, and the agent is ready.


The first analysis pass

The prompt describes the columns, flags the missing data before 2019, and asks for three charts:

const analysisPrompt = `
You have the World Happiness Report dataset (2011–2025) at /workspace/home/happiness.csv.
 
Columns: year, rank_in_year, country, happiness_score, lower_whisker, upper_whisker,
explained_log_gdp_per_capita, explained_social_support, explained_healthy_life_expectancy,
explained_freedom, explained_generosity, explained_corruption, dystopia_plus_residual.
 
Note: the explained_* columns are empty for years before 2019 — handle that gracefully.
 
Produce three charts and save each to /workspace/home/:
 
1. trends.png — Line chart of happiness_score over time (2011–2025) for the
   top 5 and bottom 5 countries by their 2025 happiness_score.
2. ranking_2025.png — Horizontal bar chart of the top 15 and bottom 15 countries
   by 2025 happiness_score.
3. biggest_movers.png — Bar chart of the 10 countries with the largest rank
   improvement and 10 with the largest rank decline since their first available year.
`.trim()
 
const run = await box.agent.run({ prompt: analysisPrompt })
console.log(run.result)

The agent installs any missing packages, writes the scripts, runs them, and fixes any errors itself. When it's done, the three charts are on disk.

Downloading them is a single read per file:

for (const [remote, local] of [
  ["/workspace/home/trends.png",         "happiness_trends.png"],
  ["/workspace/home/ranking_2025.png",   "happiness_ranking_2025.png"],
  ["/workspace/home/biggest_movers.png", "happiness_biggest_movers.png"],
] as const) {
  const b64 = await box.files.read(remote, { encoding: "base64" })
  fs.writeFileSync(local, Buffer.from(b64, "base64"))
}

Here's what came out.

Trends, top 5 and bottom 5 countries by 2025 score:

Happiness score trends 2011–2025

Nordic countries dominate the top. Finland has held first place for years. Afghanistan drops to the bottom after 2021.

2025 rankings, top 15 and bottom 15:

Happiest and least happy countries 2025

Finland leads at 7.764. Costa Rica is the only non-Nordic country in the top five. Afghanistan sits at 1.446, nearly two full points below the next lowest country.

Biggest rank movers since first year in the dataset:

Countries with the biggest rank changes

Serbia climbed 88 positions, the biggest jump in the dataset. Most of the improvers are Eastern European and Balkan countries. Jordan fell 65 places; Venezuela, Myanmar, and Lebanon aren't far behind.


The follow-up: multi-turn on the same container

The CSV is already on disk. The three charts are already on disk. The second prompt just asks for a refinement:

const followUpPrompt = `
Re-open /workspace/home/trends.png.
Add a shaded confidence band around each line using the lower_whisker and
upper_whisker columns (alpha=0.15, same color as the line).
Save the updated chart back to /workspace/home/trends.png.
`.trim()
 
const followUp = await box.agent.run({ prompt: followUpPrompt })

The agent finds both the CSV and the earlier chart already on disk and adds the bands.

Trends chart with confidence bands

Finland's band is barely visible, consistent data for years. The bands widen for lower-ranked countries with shorter or patchier survey histories.


Reconnecting later

If you close your terminal and come back later, Box.get() wakes the paused container and resumes with the same files and installed packages:

import "dotenv/config"
import { Box } from "@upstash/box"
 
const box = await Box.get(process.env.BOX_ID!)
 
await box.agent.run({
  prompt: "Filter trends.png to show only European countries and save as trends_europe.png.",
})

Same pattern works for anything: revisit an analysis, try a different slice, refine a chart before sharing it.