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
An analytics assistant that answers questions about sales data and checks every number against the query that produced it.
DataPython · JavaScriptGitHub @erprakash26OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Sales analytics assistant that only shows an answer when every number matches a query result. FastAPI, SQLite, validated tools, grounding checks, held-out evaluation.
Translation pending
Loading the full README.
Read the full README ↗AI analytics assistants can state numbers confidently that no query ever produced. GroundedBI shows the guardrails that prevent that: fixed validated tools, read-only SQL, and an answer withheld unless every number matches a query result.
You ask a question about a synthetic electronics retailer's sales, such as "Why did revenue drop in March compared with February?". A planner picks from six predefined, read-only tools; their arguments are validated and their SQL is fixed and parameterized. The answer is written only from the values those queries return, and then checked: every number in it must exactly match a query result, or the answer is withheld. An unclear question gets a clarifying question back, and a question the data can't answer (profit, forecasts, "delete the data") is declined before any query runs.
flowchart TD
Q([Question]) --> V{"Validate<br/>1-500 characters, plain text"}
V -- invalid --> E422[["422 validation error"]]
V -- valid --> S{"Within scope?"}
S -- "profit, forecasts, commands, creative requests" --> OOS[["Out of scope: declined"]]
S -- yes --> O["Data overview<br/>months, products, regions, categories"]
O --> P{"Plan"}
P -- "unknown name, vague period, no clear intent" --> C[["Clarifying question"]]
P -- "unrelated to sales data" --> OOS
P -- "tool calls" --> T["Validated tool call<br/>strict Pydantic arguments"]
T --> SQL[("Read-only, parameterized SQL<br/>SQLite, mode=ro")]
SQL --> W["Answer template<br/>quotes the query's display strings"]
W --> G{"Grounding check<br/>every number matches a result?"}
G -- yes --> A[["Answered, with evidence and trace"]]
G -- no --> X[["Withheld: grounding_failed"]]
Every /ask response carries a trace of each tool call and a request ID that matches the server's structured JSON logs. Answered questions also include the evidence tables the numbers came from.
Five layers of protection. Each works on its own, so one layer's mistake doesn't become a wrong answer or a changed database.
The planner is measured on question sets with labelled expectations: which tools should run, with which arguments, which status the response should have, and whether the answer passes the grounding check.
evals/datasets.json and its score is no longer an unbiased estimate.evals/check_expectations.py, so the labels themselves are checked.A confident wrong answer is an answered, grounded response to a question that still failed: the worst outcome, because it looks trustworthy.
| Run | Dev | Held-out v1 | Held-out v2 | Confident wrong |
|---|---|---|---|---|
| Baseline | 21/21 | 3/10 (fresh) | not yet written | 2 (held-out v1) |
| After the first fix round | 41/41 | 10/10 (seen) | 7/10 (fresh) | 1 (held-out v2) |
| After the second fix round | 51/51 | 10/10 (seen) | 10/10 (seen) | 0 |
After two fix rounds all 71 questions pass with no confident wrong answers, but every set has now been seen. The last unbiased number is 7/10 on the fresh held-out v2 set, with one confident wrong answer. Each row links to the report committed at that point.
Before public deployment the app was audited and hardened. What is in place:
This is the beginning of the README. Read the rest at the original. Read the full README ↗