Busabase
Busabase is an open-source (MIT) database and workspace for AI agents — data, docs, skills, and apps in one place. Run it locally, self-host it inside your network, or use Busabase Cloud, and turn age
翻譯準備中
🧙♂️ Local-first autonomous data analysis agent. Private, self-correcting, and evidence-backed analytics powered by DuckDB, Polars & local LLMs.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A local-first autonomous data analysis agent. Ask a real question about your data; it investigates — looking, computing, revising its approach when the data disagrees with it — then verifies the result and explains it, streaming its reasoning as it goes.
Upgrading from w1? See the migration notes — most installs need to change nothing.
Full documentation, including task guides (EDA, model training) and an edge-cases & gotchas reference, lives at wizardw2.vercel.app.
Wizard runs on macOS (Apple Silicon and Intel), Linux (x86-64 and ARM64) and Windows 10/11 (x86-64). Windows on ARM is not supported yet. Nothing below needs administrator rights.
| Platform | Install |
|---|---|
| macOS | brew install Wizard-AIA/wizard/wizard |
| Linux | curl -fsSL https://wizardw2.vercel.app/install.sh | sh |
| Windows (PowerShell) | irm https://wizardw2.vercel.app/install.ps1 | iex |
Then confirm it works and set it up:
wizard --version # wizard CLI v1.0.15, backend API compat v4.0.0
wizard --help
wizard init # choose a provider and models, install what is missing
wizard start # launches Wizard and opens http://localhost:3000
macOS or Linux, Homebrew (also works on Linux):
brew install Wizard-AIA/wizard/wizard
macOS or Linux, installer script (no Homebrew needed; installs into ~/.wizard):
curl -fsSL https://wizardw2.vercel.app/install.sh | sh
# options: --version 1.0.15 --install-dir DIR --no-modify-path --force --verbose
curl -fsSL https://wizardw2.vercel.app/install.sh | sh -s -- --version 1.0.15 --no-modify-path
Windows, PowerShell 5.1 or 7+ (installs into %LOCALAPPDATA%\Wizard and adds it to your user PATH):
irm https://wizardw2.vercel.app/install.ps1 | iex
# with options:
& ([scriptblock]::Create((irm https://wizardw2.vercel.app/install.ps1))) -Version 1.0.15 -NoModifyPath
Windows, Scoop:
scoop install https://github.com/Wizard-AIA/Wizard-w2/releases/latest/download/wizard.json
Any platform, manual download. Take the archive for your platform from the latest release, check it against SHA256SUMS, extract it, change into the extracted Wizard-v<version>-<platform> directory, and run ./cli/wizard init.
Every option also has an environment variable (WIZARD_VERSION, WIZARD_INSTALL_DIR, WIZARD_NO_MODIFY_PATH), and WIZARD_RELEASE_BASE_URL points the installers at an internal mirror.
Wizard needs Python 3.12+, Node.js 20+, uv and pnpm. Those are minimums: anything newer works, and wizard init uses whatever your machine already has (nvm, fnm, Volta, Homebrew and system installs are all found). It only offers to install what is missing, and then installs the current release. Ollama is needed only if you want local models; cloud-only setups do not need it. Docker is optional.
wizard init walks through the setup on a terminal:
•, and a receipt (its length and last four characters) proves the paste arrived. The key is never printed, and it is checked with the provider right away;For scripts and CI use wizard init --non-interactive (see wizard init --help); --install-prerequisites lets it install missing tools without asking.
| macOS | Linux | Windows | |
|---|---|---|---|
| Program | ~/.wizard or the Homebrew keg | ~/.wizard | %LOCALAPPDATA%\Wizard |
| Settings, keys, logs, Python environment | ~/Library/Application Support/Wizard | ~/.config/wizard (or $XDG_CONFIG_HOME) | %APPDATA%\Wizard |
WIZARD_CONFIG_DIR overrides the settings location. Behind a proxy, the standard HTTPS_PROXY and NO_PROXY variables are honoured by the installers, the updater and model lookups.
| Installed with | Upgrade |
|---|---|
| Homebrew | brew update && brew upgrade wizard, then wizard init (your settings are kept) |
| Installer script or PowerShell | wizard update (wizard update --check only reports) |
| Scoop | scoop update wizard |
wizard update never overwrites files a package manager owns; on a Homebrew or Scoop install it tells you the right command instead.
Releases are either stable or pre-release (a beta or release candidate, tagged like v1.0.14-beta.1). You are on the stable channel unless you choose otherwise, and a pre-release is never offered to anyone who has not opted in: the installers, wizard update, Homebrew and Scoop only ever see stable releases.
| I want to | Do this |
|---|---|
| Try pre-releases on an existing install | wizard channel pre-release, then wizard update (or wizard update --pre-release to do both) |
| Install a pre-release on a new machine | curl -fsSL https://wizardw2.vercel.app/install.sh | sh -s -- --pre-release (PowerShell: -PreRelease) |
| Install one specific pre-release | ... | sh -s -- --version 1.0.14-beta.1 |
| Go back to stable | wizard channel stable |
| See which channel I am on | wizard channel (wizard doctor and wizard update --check show it too) |
The pre-release channel follows the newest release of any kind, and a stable release wins as soon as it is newer than the pre-release you are on. wizard update never downgrades: if you switch to stable while running a pre-release that is ahead of it, you stay on that build until a stable release passes it. Re-running the installer is different, because you are asking for a specific install: without --pre-release it installs the latest stable release, which moves you back to stable. The --pre-release installer option is served by the website's install.sh once a stable release that contains it exists; until then, use the installer attached to the pre-release itself (see docs/releasing.md). Homebrew and Scoop follow the stable channel only; to try a pre-release on a machine that has Wizard from Homebrew, use the installer script instead.
wizard uninstall # removes the program and its PATH entries; keeps your settings and keys
wizard uninstall --purge # removes everything: program, settings, API keys, logs, Python environment
Homebrew and Scoop installs are removed with brew uninstall wizard / scoop uninstall wizard; wizard uninstall prints the right one. wizard delete removes only your data and leaves the program installed.
Run wizard doctor. It checks the version, platform, where wizard runs from, whether it is on your PATH, the settings directory, Python, Node, uv and pnpm, your configuration, and the running service, and prints a fix for anything that is not a pass (--json for scripts, --network to test connectivity). It exits 3 when something required is missing.
| Symptom | Fix |
|---|---|
wizard: command not found right after installing | Open a new terminal (the installer edited your shell startup files or user PATH). wizard doctor says exactly what to add. |
checksum mismatch | The download was corrupted or altered and nothing was installed. Re-run; if it persists, check your proxy. |
another Wizard is already installed | Two installs shadow each other. Upgrade the existing one, remove it, or pass --force. |
wizard init says Node or pnpm is missing but you have it | Run wizard doctor: it lists where it looked and why a tool was rejected. |
| Exit codes | 0 ok · 1 failure · 2 bad usage · 3 missing dependency or broken install · 4 network |
Integrity. The installers and the updater verify each archive's SHA-256 against the release's SHA256SUMS before unpacking anything. That protects against corrupt or partial downloads; the checksum file comes from the same release, so it is integrity, not a signature.
Building from source is separate from the above; see Contributing & Development at the bottom, or the CLI README.
Wizard runs entirely on your machine. Your data never leaves it, and no API key is required by default — cloud providers are supported too, gated behind an explicit data mode (see below).
You upload a file (or connect a database) and ask a question in plain language. A manager model works out what to do; a worker model writes the Python. The code is statically screened, then executed in its own subprocess under OS-native sandboxing by default (Landlock/seccomp on Linux, sandbox-exec on macOS, a restricted job object on Windows) — or in a Docker container scoped to your session if you opt into that instead.
The important part is what happens next. Rather than following a plan fixed before anything ran, the manager sees the real output and decides what to do next — examine a column, compute something else, consult an attached document, revise the plan outright, or stop and answer. It repeats until it has an answer or runs out of budget. Real analytical questions are not one step; you find out the join key is dirty, or that "active customer" means three different things in three tables, only once you have looked.
Every stage streams to the browser as it happens — the reasoning, each move and what it found, the generated code, the program's stdout, and the answer token by token.
auto works it out from the model itself.After installing, wizard init and wizard start are all you need. Open http://localhost:3000; API docs are at http://localhost:8000/docs.
wizard status # what's running, host sizing, sandbox capability
wizard stop
wizard doctor # if anything looks wrong
Local models need Ollama (or LM Studio); a cloud provider needs only an API key. Docker Desktop is optional, see Docker alternative.
Contributors building from a clone (end users should use the installers above):
git clone https://github.com/Wizard-AIA/Wizard-w2.git && cd Wizard-w2
cd cli && go build -o wizard ./cmd/wizard && cd ..
./cli/wizard init
./cli/wizard start
See cli/README.md for the full subcommand reference. Or do it by hand:
uv pip install --system -r requirements.txt -r requirements-local.txt
cd backend && uvicorn src.api.api:app --port 8000
cd frontend && pnpm install && pnpm dev
Open http://localhost:3000. API docs are at http://localhost:8000/docs.
You do not need to install a model first. Go to /models and use Install a model — starter picks are offered per provider, downloads show progress in the page, and nothing sends you to a terminal or the LM Studio window. Ollama models are installed through its own API; LM Studio models through the lms CLI that ships with it.
If you would rather use a terminal, any two models work — a reasoning model and a code model is the useful split, and nothing in the app is tied to a particular pair:
ollama pull qwen3:8b # reasoning
ollama pull qwen2.5-coder:7b # code
Optionally ollama pull embeddinggemma (or nomic-embed-text). Wizard embeds through whichever model server you already run, so this is all that semantic retrieval needs — no extra install, and no GPU libraries. Without one, matching falls back to word overlap.
EXECUTION_BACKEND defaults to host: generated code runs in a subprocess of the backend — a separate process with a memory ceiling, a per-step timeout, an interrupt that works, and a namespace that survives between steps. Docker is opt-in; set EXECUTION_BACKEND=docker to use a container per session instead.
That subprocess is contained by the operating system (HOST_SANDBOX=best-effort): writes are confined to the session workspace, outbound network is denied, and memory and process counts are capped. What your machine can actually enforce differs by platform and is listed on /settings, with a reason for every gap — press Verify there to have it spawn a probe that attempts each forbidden operation rather than take the claim on trust. The static code guard still runs first. Docker remains the strongest option for data or questions you did not write yourself.
Any model you have pulled appears in the picker, and the app picks a sensible one per role on its own — no model name is configured anywhere by default.
A note on model size. The agent decides its own next step each iteration, which is a lot to ask of a very small model. Under 4B parameters it does not ask: it runs a shorter, deterministic loop — write the code, correct it if it fails, answer — with no self-revision and no verification pass. That is three model calls for a question instead of nine, which is the difference between a minute and twenty on a laptop. 7B and up is where the investigation behaviour starts to earn its round-trips. Picking Deep in the composer restores them at any size.
Do not put a reasoning model in the manager role. MODEL_NAME is the model that plans, decides and writes the answer — three to five calls per question. A reasoning model (deepseek-r1, qwq, anything that thinks out loud) spends hundreds to thousands of tokens deliberating before each one, and on a small distill that is minutes per call. Its thinking is stripped correctly and never reaches the answer, but you still pay for it. Use a plain instruct model here — qwen2.5:3b and llama3.2:3b are both good and small. A reasoning model is fine as the WORKER_MODEL_NAME, which is called once per step.
LM Studio works out of the box — no configuration needed if it is on its default port.
The provider is stored per role, so you can leave the reasoning model on Ollama and put only the code model on LM Studio, or vice versa. Models are discovered through LM Studio's native API, which reports quantization, context length and whether a model is currently loaded — an unloaded model is marked, because LM Studio loads it on first use and that can take a while on a laptop.
Any other OpenAI-compatible server (vLLM, llama.cpp, a hosted gateway) works through API_PROVIDER=custom_gateway with GATEWAY_API_URL.
git clone https://github.com/Wizard-AIA/Wizard-w2.git
cd Wizard-w2
docker compose up --build -d
Open http://localhost:3000. API docs are at http://localhost:8000/docs. Already extracted a release zip instead of cloning? The same docker-compose.yml is in it — just run the command from inside the extracted folder.
The ports are published on 127.0.0.1 only, so nothing is reachable from another machine. To serve your network, set WIZARD_BIND_ADDRESS (the backend mounts the Docker socket and runs generated code, so also set API_KEY and put it behind the production nginx profile). Redis stays on loopback either way, because it has no password.
WIZARD_BIND_ADDRESS=0.0.0.0 API_KEY=change-me docker compose --profile production up --build -d
The sandbox image ships in tiers. standard is the default; pick a smaller one if you are tight on space, and the agent is simply told about a smaller toolkit rather than writing code that then fails to import.
SANDBOX_TIER=core docker compose up --build -d # pandas, numpy, pyarrow, duckdb, matplotlib, openpyxl
SANDBOX_TIER=full docker compose up --build -d # adds survival analysis and geospatial
Copy backend/.env.example to backend/.env. Everything has a working default; the values you are most likely to touch:
| Key | Default | Purpose |
|---|---|---|
API_PROVIDER | ollama | Default backend: ollama, lmstudio, anthropic, openai, gemini or custom_gateway |
DATA_MODE | "" (derives) | local-only, hybrid or cloud-only — what may leave this machine |
MODEL_NAME | "" | Pin the reasoning model. Empty = use what the provider has |
WORKER_MODEL_NAME | "" | Pin the code model. Empty = use what the provider has |
AGENT_TIER | auto | auto, compact, balanced or full — how long an investigation may run |
AGENT_MAX_ITERATIONS | 24 | Hard ceiling, whatever the tier says |
AGENT_REQUIRE_APPROVAL | False | True to approve every plan before it runs |
AGENT_VERIFY | True | Recompute the headline result a second way |
AGENT_GROUNDING_CHECK | True | Flag figures that appear in no execution output |
CONTEXT_DOCS_ENABLED | True | Accept reference documents alongside the data |
OLLAMA_BASE_URL | http://host.docker.internal:11434 | Where Ollama lives |
LMSTUDIO_BASE_URL | http://host.docker.internal:1234 | Where LM Studio lives (root, not /v1) |
PLOT_FORMAT | html | html for interactive Plotly, png for static |
EXECUTION_BACKEND | host |
Resource limits — LLM_NUM_THREAD, SANDBOX_MEM_LIMIT, SESSION_MAX_ACTIVE, QUEUE_MAX_WORKERS — are left unset on purpose. They are derived from the machine at boot, and setting one pins it.
The manager and worker alternate several times per question, so what matters is whether both fit in RAM at once. Wizard measures them and decides:
Two 7B models want roughly 14 GB; a 16 GB laptop running a browser and a sandbox does not have that. /settings shows the estimate, the budget and which way it went. Using the same model for both roles removes the reload entirely — one resident copy, nothing to evict.
Generated code is untrusted. Three layers apply:
EXECUTION_BACKEND=host, each session's subprocess is restricted by the operating system: Landlock plus a seccomp filter on Linux, a deny-by-default sandbox-exec profile on macOS, a job object and a Low integrity level on Windows. Writes are confined to the session workspace, outbound network is denied (loopback aside), and memory and process counts are capped. What your machine can actually enforce is listed on /settings, with a reason for anything it cannot — outbound network is not enforced on Windows, and it says so.EXECUTION_BACKEND=docker, one container per session with cap_drop=ALL, no-new-privileges, memory and PID limits, and a per-execution timeout; set SANDBOX_DOCKER_RUNTIME=runsc for gVisor.Verify it rather than trust it. /settings has a Verify button — it spawns a probe that tries to write outside the workspace, open an outbound connection and allocate past the ceiling, and reports what stopped each one. GET /api/sandbox/selftest is the same thing from the command line.
[!IMPORTANT] The backend mounts the host Docker socket so it can create sandbox containers. That is host-root-equivalent access. Run Wizard on a trusted machine, and set
API_KEYand a narrowCORS_ALLOW_ORIGINSbefore exposing it beyond localhost.
Report vulnerabilities privately — see SECURITY.md.
A question takes many minutes, or never finishes. Four things to check, in order of how much they usually cost:
MODEL_NAME a reasoning model? See the note above — this is by far the most common cause. deepseek-r1:1.5b as the manager can spend minutes per call thinking.LLM_NUM_THREAD set in backend/.env? Delete it. Local inference is memory-bandwidth bound, so more threads than physical cores is contention, not throughput — 8 threads on a 4-core laptop is slower than 4. Unset, it is measured. Same for LLM_NUM_CTX: unset it and the context is sized to the machine, which also stops the provider evicting one model to make room for the other on every step.ollama ps during a run. The manager and worker alternate, so if only one is resident each step is paying a reload from disk. Smaller models, or a smaller LLM_NUM_CTX, fix it.AGENT_TURN_TIMEOUT (default 300s) bounds a turn regardless: on reaching it the agent stops exploring and answers from what it has, and says so. Raise it if you would rather wait.The backend cannot reach Ollama. If the backend runs outside Docker, leave OLLAMA_BASE_URL unset — the shipped default is rewritten to 127.0.0.1 when the backend is not itself containerised, because host.docker.internal only resolves on machines that have Docker Desktop. Inside compose the file passes the right value itself. On Linux the compose file adds a host-gateway alias; if you still cannot connect, set OLLAMA_BASE_URL=http://172.17.0.1:11434.
Settings shows "Local subprocess" instead of "Docker container". Docker is unreachable, so code is running in a subprocess of the backend. That is a supported mode — bounded, interruptible, and it keeps variables between steps — but it is not isolated from your filesystem. Start Docker Desktop and reload to get a container back.
Settings shows "In-process (no isolation)". Spawning was forbidden. Set EXECUTION_BACKEND=host in backend/.env. Only this mode has no isolation and no persistent namespace.
Retrieval says "Word overlap". No embedding model is installed on your provider. ollama pull embeddinggemma and reload. Nothing breaks without one; matching is just less good at paraphrases.
The model picker is empty. Nothing is installed yet, or the model server is not running. Open /models and use Install a model — there are starter picks per provider, and you do not need a terminal or the LM Studio window. ollama pull qwen3:8b still works if you prefer.
The LM Studio tab is empty but LM Studio is running. Almost always Serve on Local Network being off — with it off LM Studio accepts loopback connections only, and the backend runs in a container. The error under the tab names the exact URL that was tried. Note that LMSTUDIO_BASE_URL wants the root (http://host.docker.internal:1234), not the /v1 endpoint the LM Studio UI displays; a trailing /v1 is stripped for you.
LM Studio answers the first question very slowly. It loads the model on first request. The picker marks models that are not loaded; loading one in LM Studio beforehand avoids the stall.
Analysis keeps failing on the same step. The agent stops after MAX_CORRECTION_RETRIES. The generated code and the traceback are in the "Ran N steps" disclosure — that usually shows a column that does not exist or a type that needs converting first.
A large upload is slow. Files over MAX_INMEMORY_ROWS are sampled for analysis; the full file stays in the workspace and can be read directly in generated code.
Modelling code fails to import xgboost or lightgbm on macOS, host-mode. Both ship compiled libraries that need the OpenMP runtime, which macOS does not include: brew install libomp, then re-run the analysis. This is an xgboost/lightgbm-on-macOS requirement, not a Wizard one — it affects any Python project using them there.
The full developer repository — tests, benchmarks, CI pipelines, and architecture docs — is this same repo: Wizard-AIA/Wizard-w2. See CONTRIBUTING.md for the full workflow and CLAUDE.md for an architecture tour.
graph TD
classDef client fill:#0ea5e9,stroke:#0369a1,stroke-width:2px,color:#fff;
classDef api fill:#10b981,stroke:#047857,stroke-width:2px,color:#fff;
classDef brain fill:#db2777,stroke:#9d174d,stroke-width:2px,color:#fff;
classDef sandbox fill:#f59e0b,stroke:#b45309,stroke-width:2px,color:#000;
classDef store fill:#64748b,stroke:#334155,stroke-width:2px,color:#fff;
UI["Next.js client<br/>(streams every move)"]:::client
WS["FastAPI · WS /ws/chat"]:::api
Session["Session<br/>tables · documents · sandbox"]:::api
Loop["Analysis loop<br/>bounded by the tier budget"]:::api
Manager["Manager model<br/>decide · revise · answer"]:::brain
Worker["Worker model<br/>Python"]:::brain
Guard["Code guard<br/>AST policy check"]:::sandbox
Box["Per-session container<br/>cap_drop · mem/pid limits"]:::sandbox
Trust["Trust layer<br/>verify · ground · assumptions"]:::store
Store["SQLite<br/>cache · trajectories · memory"]:::store
Retr["Retriever<br/>columns · memory · documents"]:::store
UI <-->|typed event frames| WS
WS --> Session --> Loop
Loop <--> Retr <--> Store
Loop -->|"1 what next?"| Manager
Manager -->|"2 inspect / consult"| Retr
Manager -->|"3 write code for this sub-task"| Worker
Worker --> Guard -->|allowed| Box
Box -->|"4 real output"| Loop
Loop -.->|"repeat until answerable"| Manager
Loop -->|5| Trust
Trust -->|"6 synthesise from real output"| Manager
The dotted line is the part that matters. Step 4 feeds back into step 1: the manager sees what the code actually produced and picks the next move from it, so a plan that turns out to be wrong gets rewritten instead of carried out. How many times round that loop is allowed depends on the model — see AGENT_TIER above.
Analysis
PLOT_FORMAT)Trust
Data
tables['name'], so cross-table joins need no extra stepOperational
API_KEY for deployments beyond localhostInteractive docs: http://localhost:8000/docs.
The session id is returned in the X-Session-Id header and should be sent back on subsequent requests.
| Method | Path | Purpose |
|---|---|---|
GET | /api/config | Server capabilities |
POST | /api/session | Create a session |
GET | /api/models | Models installed on the host |
POST | /api/models | Choose models for this session |
POST | /api/datasets | Upload a file |
POST | /api/documents | Attach a reference document |
DELETE | /api/documents/{name} | Remove one |
GET | /api/data/preview | Paginated table view |
POST | /api/chat | Run a turn, buffered |
WS | /ws/chat | Run a turn, streamed |
GET | /api/workspace/files | Files this session produced |
GET | /api/report | Summary of the session's analyses |
Client → server
{ "type": "message", "content": "which day has the highest tips?", "mode": "planning" }
{ "type": "approval", "approved": true, "tool": "execute_plan", "content": "...", "plan": "..." }
{ "type": "cancel" }
{ "type": "ping" }
Server → client
session · status · step_start · step_end · reasoning_delta · plan_delta · content_delta · code · stdout · artifact · approval_required · warning · error · final
Reasoning and the final answer arrive as separate delta streams, so the client can render a live "thinking" panel independently of the answer.
uv pip install --system -r requirements.txt # API server
uv pip install --system -r requirements-local.txt # + the analysis toolkit, for running without Docker
cd frontend && pnpm install && cd ..
pytest # 600+ tests; no Docker, model, network or subprocess
ruff check . --fix && ruff format .
cd frontend && pnpm lint && npx tsc --noEmit && pnpm build
Tests are organised as unit/, integration/, regression/ and negative/ under backend/tests/. The regression suite pins previously-fixed defects and each test explains what broke — worth reading before changing sessions, the database layer or the code guard.
host (subprocess, no Docker), docker or inprocess |
SANDBOX_TIER | standard | core, standard or full — how much toolkit the image installs |
SYSTEM_PROFILE | auto | auto measures the machine; or pin laptop/server/hpc |
HOST_SANDBOX | best-effort | off, best-effort or require — OS containment for the host runtime |
HOST_SANDBOX_NETWORK | deny | deny blocks outbound traffic from generated code (loopback still allowed) |
SANDBOX_ENABLED | True | False disables containers entirely |
SANDBOX_NETWORK_DISABLED | False | True is safer, but blocks on-demand package installs |
SANDBOX_DOCKER_RUNTIME | "" | Set runsc for gVisor kernel isolation |
CORS_ALLOW_ORIGINS | http://localhost:3000 | Comma-separated allowlist |
API_KEY | "" | When set, mutating routes require X-API-Key |
EMBEDDING_REMOTE_MODEL | "" | Pin the embedding model. Empty = discover one from the provider |
MODEL_MEMORY_FRACTION | 0 | Share of RAM the models may use. 0 uses 0.60 |
REDIS_URL | "" | Empty means in-process cache and queue |