Future with Shivank · Hands-on project · Learner brief
NPS Analyst in a Box Promoters, passives, detractors — and a team of agents that turns 600 survey answers into a leadership brief
You built an "analyst in a box" for stocks. Now point the same design at the bank's own customers. Five specialist agents and a director read a Net Promoter Score survey, find why the score moved, put a rupee value on unhappy customers, decide what to fix first, and say who to call back this week. Every number is computed by plain Python; the agents only read, decide and explain.
How to read the coloured boxes:
Tools return JSON and never crash · agents are Agent(name, instructions, tools, model) · the orchestrator calls specialists with .as_tool() · three phases (parallel → dependent → synthesis) · verdicts come from tables, not moods. If any of that feels new, re-read Section 4.2–4.6 first. This page spends its time on what is new: NPS, money at risk, text themes, what-if analysis and fact-checking the final brief.
Start here · the problem statement
The problem, explained with 10 customers
The situation
Future with Shivank Bank asks every customer one question: "On a scale of 0 to 10, how likely are you to recommend our bank to a friend?" — and then "Why did you give this score?"
- 9–10 · Promoter happy, will recommend the bank
- 7–8 · Passive (neutral) okay, but would switch easily
- 0–6 · Detractor unhappy, likely to leave and complain
A tiny example: 10 customers in September
| # | Score | Group | Comment | Product | Yearly revenue to bank |
|---|---|---|---|---|---|
| 1 | 10 | Promoter | "UPI is super quick" | Savings | ₹5,000 |
| 2 | 9 | Promoter | "My RM is very helpful" | Wealth | ₹2,00,000 |
| 3 | 9 | Promoter | "App is easy" | Savings | ₹4,000 |
| 4 | 8 | Passive | "Fine, but call centre wait is long" | Card | ₹10,000 |
| 5 | 7 | Passive | "Okay" | Savings | ₹3,000 |
| 6 | 4 | Detractor | "OTP not coming after the update" | Savings | ₹6,000 |
| 7 | 3 | Detractor | "Can't login since the update" | Card | ₹12,000 |
| 8 | 5 | Detractor | "OTP fails, moving my salary account" | Savings | ₹8,000 |
| 9 | 2 | Detractor | "New advisory fee, nobody explained it" | Wealth | ₹3,00,000 |
| 10 | 0 | Detractor | "Excellent service, love it!" | Savings | ₹7,000 |
Getting that number takes one line of code. The hard part is the four questions the CEO will ask next:
- How bad is it? NPS fell from +30 last month to −20 in one month.
- Why? Read the comments: three of the five detractors (#6, #7, #8) complain about OTP or login after the app update. That's the cause.
- How much money is at risk? The three OTP customers bring ₹26,000 a year in total. The one fee complaint (#9) brings ₹3,00,000. The loudest problem (OTP) is not the most expensive one (fees).
- What do we do? Fix OTP first — it moves the score the most. Phone customer #9 today — that one account is worth more than the other nine put together.
Customer #10 gave a 0 but wrote "Excellent service". They probably thought 0 meant best. If they meant 10, the NPS is 0, not −20. A good analyst flags this — and doesn't silently change the score.
With 10 customers, you can do all of this in your head.
The real problem
The bank actually has 600 customers, 536 written comments and 3 months of data. Last quarter, two analysts spent three days in Excel reading every comment, colour-coding topics, slicing scores by month, product and channel, working out the money at risk, and writing one page for the CEO. That's the job we hand to agents — one question per agent:
| Question | Agent | What it does in the 10-customer example |
|---|---|---|
| How bad is it? | ScoreAnalystAgent | Calculates −20 and spots that September dropped |
| Why? | VoiceOfCustomerAgent | Reads comments, finds "OTP/login after update" in 3 of 5 detractors |
| How much money? | ChurnRiskAgent | Finds ₹3 lakh sits with one Wealth customer; flags #10 as a likely mistake |
| What do we do? | ActionPlannerAgent | Fix OTP first; call #9 today |
| One page for the CEO | ReportWriterAgent | Writes the brief with a RED / AMBER / GREEN alert |
Python does all the counting and maths, so the numbers are always exact. The agents do what Excel can't: read comments, decide where to look next, connect complaints to money, and write it up.
Understand it
What we are building, and why a bank cares
P1The ask: four questions for Thursday
| The CEO's question | Who answers it | What they hand back |
|---|---|---|
| How bad is it? | ScoreAnalystAgent | NPS with margin of error, monthly trend, best/worst segments, health verdict |
| Why did it happen? | VoiceOfCustomerAgent | Top themes in the comments, real quotes, root-cause hypotheses |
| How much money is at risk? | ChurnRiskAgent | Revenue at risk in ₹ lakh, risk verdict, data-quality warnings |
| What are we doing about it? | ActionPlannerAgent | Fix list ranked two ways (NPS points, rupees saved), who to call back this week |
| One page, please. | ReportWriterAgent | The NPS brief with a GREEN / AMBER / RED alert level |
If all you need is the number, pandas is enough — milestone M0 below is exactly that, about 20 lines. Agents earn their keep in the parts a script can't do alone: reading 500 comments, choosing where to drill down next ("September looks bad — which channel?"), connecting a complaint to a rupee figure, and writing it for a CEO. The design rule from Section 4 still holds: code computes, agents reason.
P2Just-enough NPS (three ideas, nothing more)
Every NPS survey asks one question: "How likely are you to recommend Future with Shivank Bank to a friend or colleague?" on a 0–10 scale, usually followed by "What is the main reason for your score?"
Idea 1 · Three buckets
Promoters (9–10) post "must try!" in the family WhatsApp group. Passives (7–8) — also called neutrals — eat, pay and forget; they'd switch the moment a new place opens. Detractors (0–6) leave a one-star review and tell ten people. Notice how harsh the scale is: a 6 sounds fine, but a 6 is a detractor.
Idea 2 · One subtraction
NPS runs from −100 (everyone is a detractor) to +100 (everyone is a promoter). It is a score, not a percentage — write "+18.5", never "18.5%". Passives add nothing to the top line, but they are in the total you divide by. Play with that below.
Build an NPS — start from our survey
Idea 3 · Small groups wobble
The same NPS from 26 answers and from 600 answers are not equally trustworthy. Our tools attach a 95% margin of error to every NPS: count each answer as +1, 0 or −1, and NPS is just their average × 100. With few answers that average wobbles. For example, a segment that scores +15 from only 26 answers has a margin of error of about ±31 points: its real score could be anything from −16 to +46.
Calling a 26-answer segment "our worst" is like dropping a batsman after two innings. The tools mark any segment with fewer than 30 answers low_sample, and the agents are told never to call it best or worst.
P3NPS is a money number
The CX team is judged on NPS. The CFO is judged on revenue. Our project connects the two with one line of finance — expected loss, the same idea an insurance actuary uses:
The group with the most unhappy customers is not always the group with the most money at risk, because yearly revenue per customer differs a lot between products. A dashboard that only counts complaints can send the bank to fix the wrong thing first. That is why the brief must show two rankings: most complaints and most money.
1 lakh = ₹1,00,000. 100 lakh = 1 crore. The tools report money in lakh so the LLM never has to count zeros.
The 3% / 10% / 25% churn rates are a house assumption, not a fact. Real teams estimate them from history ("of last year's detractors, how many closed their account?"). The project puts them in config.py, echoes them in every revenue tool result, and the brief must list them under DATA CAVEATS. An assumption you can see is an assumption someone can challenge.
P4The architecture: a CX insights team in code
| Agent | Human it replaces (the mechanical part only) | Its tools |
|---|---|---|
| ScoreAnalystAgent | Analyst building the monthly NPS pivot tables | get_nps_overview get_nps_by_segment* |
| VoiceOfCustomerAgent | Analyst reading 500 comments and colour-coding cells | get_comment_themes |
| ChurnRiskAgent | Finance partner + data-quality reviewer — the sceptic | get_revenue_at_risk find_score_comment_mismatches get_nps_by_segment* |
| ActionPlannerAgent | CX lead turning findings into a fix list | estimate_fix_impact get_callback_list |
| ReportWriterAgent | Head of CX writing to leadership | none — pure synthesis |
| OrchestratorAgent | Director of CX assigning the work | the five agents above, each wrapped with .as_tool() |
Tools are the hands (pandas does the counting). Agents are the brains (they decide what to look at and explain it). The orchestrator is the manager (assigns, collects, never does the work itself). New in this project: an auditor at the door. Like the cashier who recounts the cash drawer at closing instead of trusting the sales log, fact_check.py re-computes the headline numbers and checks the brief quotes them correctly.
Build it
Getting started
You write the code yourself. Build it bottom-up, the same way as Section 4: data → tools → agents → orchestrator → main. The survey file is given to you.
P5Setup, folders & data
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
pip install -r requirements.txt
cp .env.example .env # Windows: copy .env.example .env → then paste your OPENAI_API_KEY# NPS analyst in a box — install with: pip install -r requirements.txt (Python 3.11–3.13)
openai-agents>=0.4 # the OpenAI Agents SDK (Agent, Runner, function_tool, .as_tool) — brings openai + pydantic
pandas>=2.2 # every number: NPS, segments, revenue at risk
python-dotenv>=1.0 # reads OPENAI_API_KEY from .env# Copy this file to .env (same folder) and paste your key. Never commit .env.
OPENAI_API_KEY=sk-...
# Optional
# NPS_MODEL=gpt-4o # any model your key can use, e.g. gpt-4.1 or gpt-5-mini
# NPS_SURVEY_PATH=data/nps_survey.csv # or point at your own survey CSVThis is the suggested layout. Every file except data/nps_survey.csv is yours to write.
The data: data/nps_survey.csv
600 survey answers from July to September 2026 (200 a month); 536 of them have a written comment. One row is one customer's answer. The bank, the customers and their answers are fictional.
Download it: nps_survey.csv (64 KB). If it opens as text in your browser, right-click the link, choose "Save link as…" and save it as data/nps_survey.csv. Or run this from your project folder:
mkdir -p data
curl -L -o data/nps_survey.csv "https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/248/987/original/nps_survey.csv?1792931898"
# Windows PowerShell: mkdir data; curl.exe -L -o data/nps_survey.csv "https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/248/987/original/nps_survey.csv?1792931898"| Column | What it holds | Example |
|---|---|---|
response_id | A unique id for the answer | R0001 |
date | The day the customer answered | 2026-07-01 |
month | The month of that day | 2026-07 |
product | Savings Account, Credit Card or Wealth | Savings Account |
tenure | How long they have banked with us: <1 yr, 1-3 yrs or 3+ yrs | 3+ yrs |
channel | How they mostly bank: Mobile App, Branch or Call Centre | Branch |
score | Answer to "How likely are you to recommend us?", 0 to 10 | 10 |
comment | Answer to "Why did you give this score?" (can be blank) | Opening the account was smooth and fully online. |
annual_revenue_inr | Yearly revenue the bank earns from this customer, in rupees | 12200 |
Own it
Your project
P6Milestones, stretch goals and rubric
| Milestone | Build | Done when… | Time |
|---|---|---|---|
| M0 · Data | venv, nps_by_hand.py | prints Overall NPS 18.5 and the three months | 30 min |
| M1 · Hands | config + the three tool files | try_tools.py prints every tool's result, and the numbers agree with nps_by_hand.py | 2 h |
| M2 · One brain | ScoreAnalystAgent alone with Runner.run | its scorecard copies every number exactly, with signs | 1 h |
| M3 · The team | the other four agents + orchestrator + main.py | --trace shows three CALLs before any OUTPUT | 2 h |
| M4 · Trust | fact_check.py | 3 runs: wording differs, fact check passes every time | 1 h |
| M5 · Present | the brief + one slide | you can defend which fix goes first, with both rankings | 1 h |
Stretch goals (pick two)
- LLM theme tagger — tag each comment with an LLM that must pick from a fixed list of themes (a
Literal[...]type), cache the tags to CSV, and compare the top themes with your keyword rules. - A guardrail that blocks bad briefs — if
fact_checkfails, send the brief back to the writer with the failed checks, at most twice. - A label-aware fact check — check each number next to its label (for example, the line that mentions 2026-08), not just anywhere in the brief.
- Close-the-loop drafts — a new agent drafts a callback note per detractor; a human approves every note before anything is sent.
- Your own data — any CSV with the same columns runs with
--survey. Different segments? EditSEGMENT_COLUMNS. - A month-on-month view — re-run on each month's slice and have the writer compare this brief with last month's.
Rubric (100 points)
| Criterion | What earns full marks | Points |
|---|---|---|
| Correct numbers | fact check passes; signs and units right; no number the tools didn't produce | 25 |
| Finds the three hidden stories | what caused the drop · where the money at risk sits · a data-quality problem in the scores | 25 |
| Agent design | numbered steps, fixed sections and labels, grounding rules, exact keys in hand-offs | 20 |
| Parallel phase verified | --trace output included in the submission | 10 |
| Honest caveats | low samples, assumptions, alternative NPS numbers — all stated | 10 |
| Code quality | commented, plain functions testable without an LLM, no crashes on bad input | 10 |
Wrap-up
What to remember
- NPS = % promoters − % detractors, on all answers. 9–10 / 7–8 / 0–6. A score from −100 to +100, not a percentage.
- Small groups wobble. Show the margin of error; never crown a 26-answer segment.
- Loud ≠ expensive. Rank fixes by NPS points and by rupees at risk, and show the trade-off.
- Code computes, agents reason. Every number, verdict and alert comes from Python or a table.
- Same skeleton as Section 4: parallel analysis → dependent planning → synthesis, with a hand-off form between phases.
- Check the output with code. A fact check after every run turns a demo into something you can send to a CEO.
Future with Shivank Bank and its survey are fictional, generated for learning. The churn rates are illustrative assumptions.