Turns a transactions CSV into a one-page decision brief: three key takeaways and one recommended action.
Python does the arithmetic. The language model only writes the sentences. A final step checks every number in the finished brief against the calculated metrics and flags anything that does not reconcile.
Ask a chatbot to read a spreadsheet and it will hand back a confident paragraph with a wrong number in it. The paragraph reads fine, which is why nobody catches it, and the figure ends up in a meeting.
So the model never touches the arithmetic. Pandas calculates everything first and passes the results across as JSON. The prompt bans any figure that is not in that JSON. Then the audit checks what came back, which turns the claim from a promise into something you can test.
Four cells, start to finish.
- Load. Reads any transactions CSV. Six column names at the top of cell 1 are the only thing you change to point it at different data.
- Calculate. Builds the metric pack in pandas: revenue trend by month, category mix, performance by location, customer segments, plus the two checks below.
- Prompt. Prints a prompt carrying the metrics as JSON. Paste it into ChatGPT, Claude or Gemini. No API key, no paid account, no setup.
- Audit. Paste the brief back. Every number in it is traced to a calculated metric, and untraceable figures come back marked UNVERIFIED.
Constant columns. The engine finds columns that hold the same value on every row and names them. In the sample data Gross margin percentage is identical across all 1,000 transactions, so any advice about margin would be meaningless. The prompt is told not to give any. A tool that declines to answer what the data cannot support is worth more than one that always has something to say.
Missing values. Counted per column, so a takeaway never rests on a mostly empty field.
A checker that has only ever said yes is not a checker, so it was tested in both directions.
| Input | Numbers checked | Result |
|---|---|---|
The real brief in sample_brief.md |
15 | All 15 traced back. Brief passes. |
| A rigged brief carrying four invented figures | 6 | All 4 inventions flagged. Both genuine figures passed. |
test_notebook.py runs both. It executes the code cells straight out of the shipped .ipynb rather than a copy of them, so the test cannot quietly drift from the file you download.
Runtime, then Run all. Cells 1 to 3 finish on their own in under a minute. Cell 4 waits for you to paste a brief in.
Supermarket Sales: 1,000 transactions, 3 branches, January to March 2019. It loads straight from plotly/datasets, so there is no download and no login.
If that URL is ever unreachable, cell 1 falls back to a seeded synthetic set with the same columns. The notebook always runs.
Change DATA_URL and the six column constants at the top of cell 1, or set UPLOAD = True and pick a file. Nothing else needs editing, because the metric engine, the prompt and the audit all read from those constants.
REVENUE_COL = "Total" # money per transaction
DATE_COL = "Date"
CATEGORY_COL = "Product line" # what was sold
LOCATION_COL = "City" # where
SEGMENT_COL = "Customer type" # who
QUANTITY_COL = "Quantity"| File | What it is |
|---|---|
decision_brief_generator.ipynb |
The notebook. This is the thing to run. |
build_notebook.py |
Generates the notebook. Edit cells here, not in raw JSON. |
test_notebook.py |
Executes the shipped notebook and red-teams the audit. |
sample_brief.md |
A brief generated from the real data, for reference. |
pip install -r requirements.txt
python test_notebook.pyMIT. See LICENSE.
