Start here

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.

🏦 Future with Shivank Bank, Monday morning. The Head of Customer Experience walks in: "Our quarterly NPS dropped. The CEO asks on Thursday: how bad is it, why did it happen, how much money is at risk, and what are we doing about it? Last quarter two analysts spent three days reading comments in Excel. Can your agents do the first draft before lunch?"
600survey answers, July–September 2026
5 + 1specialist agents plus a director
3hidden stories your agents must find
~$0.20per full run on gpt-4o

How to read the coloured boxes:

🧠 Analogy💰 Finance corner⚠️ Watch out⏭️ Already covered💡 Tip🧪 Simulation

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
NPS = % promoters − % detractors # passives count in the total, but add nothing

A tiny example: 10 customers in September

#ScoreGroupCommentProductYearly revenue to bank
110Promoter"UPI is super quick"Savings₹5,000
29Promoter"My RM is very helpful"Wealth₹2,00,000
39Promoter"App is easy"Savings₹4,000
48Passive"Fine, but call centre wait is long"Card₹10,000
57Passive"Okay"Savings₹3,000
64Detractor"OTP not coming after the update"Savings₹6,000
73Detractor"Can't login since the update"Card₹12,000
85Detractor"OTP fails, moving my salary account"Savings₹8,000
92Detractor"New advisory fee, nobody explained it"Wealth₹3,00,000
100Detractor"Excellent service, love it!"Savings₹7,000
🧪 Try it
3 promoters (30%) − 5 detractors (50%), with 2 passives in the total → NPS = −20

Getting that number takes one line of code. The hard part is the four questions the CEO will ask next:

  1. How bad is it? NPS fell from +30 last month to −20 in one month.
  2. 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.
  3. 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).
  4. 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.
⚠️ The trap

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:

QuestionAgentWhat it does in the 10-customer example
How bad is it?ScoreAnalystAgentCalculates −20 and spots that September dropped
Why?VoiceOfCustomerAgentReads comments, finds "OTP/login after update" in 3 of 5 detractors
How much money?ChurnRiskAgentFinds ₹3 lakh sits with one Wealth customer; flags #10 as a likely mistake
What do we do?ActionPlannerAgentFix OTP first; call #9 today
One page for the CEOReportWriterAgentWrites 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.

📋 Problem statement for learners. Future with Shivank Bank's NPS fell from +32 in July to −6.5 in September. Given 600 survey answers (score, comment, product, channel, revenue), build a team of AI agents that tells leadership: how bad the drop is, why it happened in customers' own words, how much revenue is at risk, and what to fix first and who to call back this week — on one page, with every number checked by code. The data: nps_survey.csv (600 answers).

Understand it

What we are building, and why a bank cares

P1The ask: four questions for Thursday

The CEO's questionWho answers itWhat they hand back
How bad is it?ScoreAnalystAgentNPS with margin of error, monthly trend, best/worst segments, health verdict
Why did it happen?VoiceOfCustomerAgentTop themes in the comments, real quotes, root-cause hypotheses
How much money is at risk?ChurnRiskAgentRevenue at risk in ₹ lakh, risk verdict, data-quality warnings
What are we doing about it?ActionPlannerAgentFix list ranked two ways (NPS points, rupees saved), who to call back this week
One page, please.ReportWriterAgentThe NPS brief with a GREEN / AMBER / RED alert level
💡 Why agents, and not just a pandas script?

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

0det
1det
2det
3det
4det
5det
6det
7passive
8passive
9promoter
10promoter
🧠 The new biryani place

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 = % promoters − % detractors our survey: 268 promoters, 175 passives, 157 detractors (600 answers) 44.7% − 26.2% = +18.5

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.

🧪 Simulation

Build an NPS — start from our survey

Promoters (9–10)268
Passives (7–8)175
Detractors (0–6)157
P 45%Pa 29%D 26%
NPS = 44.7% promoters − 26.2% detractors = +18.5  (±6.6 at 95%, 600 answers) → NEEDS WORK

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.

🧠 Two innings

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:

revenue at risk = Σ (customer's yearly revenue × chance they leave this year) chance of leaving (an ASSUMPTION, set in config.py): promoter 3% · passive 10% · detractor 25%
💰 The finance corner: loud ≠ expensive

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.

⚠️ Assumptions, written down

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

AgentHuman it replaces (the mechanical part only)Its tools
ScoreAnalystAgentAnalyst building the monthly NPS pivot tablesget_nps_overview get_nps_by_segment*
VoiceOfCustomerAgentAnalyst reading 500 comments and colour-coding cellsget_comment_themes
ChurnRiskAgentFinance partner + data-quality reviewer — the scepticget_revenue_at_risk find_score_comment_mismatches get_nps_by_segment*
ActionPlannerAgentCX lead turning findings into a fix listestimate_fix_impact get_callback_list
ReportWriterAgentHead of CX writing to leadershipnone — pure synthesis
OrchestratorAgentDirector of CX assigning the workthe five agents above, each wrapped with .as_tool()
OrchestratorAgentdirector of CX · calls every specialist as a tool · returns the final brief word for word ① three calls at once ② then ③ finally ScoreAnalystAgenthow bad is it?get_nps_overviewget_nps_by_segment *+ drill-down into the worst month VoiceOfCustomerAgentwhy did it happen?get_comment_themes× detractor / passive / promoter ChurnRiskAgenthow much money? can we trust it?get_revenue_at_riskfind_score_comment_mismatchesget_nps_by_segment * ActionPlannerAgentwhat do we fix first?estimate_fix_impactget_callback_list NPScontext ReportWriterAgenthead of CXno tools · synthesis onlyalert table → brief all four reports go in NPS briefGREEN · AMBER · RED fact_check.pyplain Python re-checks the numbers data/nps_survey.csv → pandas, loaded once (cached) · feeds every tool · * = shared by two agents
Three phases, same as Section 4. ① The three analysis agents don't need each other, so they run together. ② The planner needs the top themes, so it waits. ③ The writer needs everything. Then code — not an LLM — checks the brief.
🧠 Hands, brains, manager — and an auditor

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

Bash · one-time setup (macOS / Linux; Windows notes in the comments)
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
Text · requirements.txt
# 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
Text · .env.example
# 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 CSV
nps-analyst-project/ ├── data/ │ └── nps_survey.csv # GIVEN: the 600 survey answers ├── nps_by_hand.py # Step 0: NPS in plain pandas, no LLM ├── try_tools.py # Step 1: every tool's numbers, no LLM ├── nps_insights_agent/ │ ├── config.py # NPS rules, house rules, assumptions, model name │ ├── tools/ # the HANDS — plain Python │ │ ├── survey_tools.py # overview, segments, revenue at risk, mismatches, callbacks │ │ ├── theme_tools.py # comments → themes (keyword rules) │ │ └── whatif_tools.py # "if we fix X": NPS points + rupees saved │ ├── agents/ # the BRAINS — 5 specialists + orchestrator │ ├── fact_check.py # the auditor │ └── main.py # command line: --trace --verbose --stream --log-dir --survey └── stretch/llm_theme_tagger.py # stretch goal: LLM tagging vs keyword rules

This 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:

Bash · download the survey into data/
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"
ColumnWhat it holdsExample
response_idA unique id for the answerR0001
dateThe day the customer answered2026-07-01
monthThe month of that day2026-07
productSavings Account, Credit Card or WealthSavings Account
tenureHow long they have banked with us: <1 yr, 1-3 yrs or 3+ yrs3+ yrs
channelHow they mostly bank: Mobile App, Branch or Call CentreBranch
scoreAnswer to "How likely are you to recommend us?", 0 to 1010
commentAnswer to "Why did you give this score?" (can be blank)Opening the account was smooth and fully online.
annual_revenue_inrYearly revenue the bank earns from this customer, in rupees12200

Own it

Your project

P6Milestones, stretch goals and rubric

MilestoneBuildDone when…Time
M0 · Datavenv, nps_by_hand.pyprints Overall NPS 18.5 and the three months30 min
M1 · Handsconfig + the three tool filestry_tools.py prints every tool's result, and the numbers agree with nps_by_hand.py2 h
M2 · One brainScoreAnalystAgent alone with Runner.runits scorecard copies every number exactly, with signs1 h
M3 · The teamthe other four agents + orchestrator + main.py--trace shows three CALLs before any OUTPUT2 h
M4 · Trustfact_check.py3 runs: wording differs, fact check passes every time1 h
M5 · Presentthe brief + one slideyou can defend which fix goes first, with both rankings1 h

Stretch goals (pick two)

  1. 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.
  2. A guardrail that blocks bad briefs — if fact_check fails, send the brief back to the writer with the failed checks, at most twice.
  3. 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.
  4. Close-the-loop drafts — a new agent drafts a callback note per detractor; a human approves every note before anything is sent.
  5. Your own data — any CSV with the same columns runs with --survey. Different segments? Edit SEGMENT_COLUMNS.
  6. 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)

CriterionWhat earns full marksPoints
Correct numbersfact check passes; signs and units right; no number the tools didn't produce25
Finds the three hidden storieswhat caused the drop · where the money at risk sits · a data-quality problem in the scores25
Agent designnumbered steps, fixed sections and labels, grounding rules, exact keys in hand-offs20
Parallel phase verified--trace output included in the submission10
Honest caveatslow samples, assumptions, alternative NPS numbers — all stated10
Code qualitycommented, plain functions testable without an LLM, no crashes on bad input10

Wrap-up

What to remember

  1. NPS = % promoters − % detractors, on all answers. 9–10 / 7–8 / 0–6. A score from −100 to +100, not a percentage.
  2. Small groups wobble. Show the margin of error; never crown a 26-answer segment.
  3. Loud ≠ expensive. Rank fixes by NPS points and by rupees at risk, and show the trade-off.
  4. Code computes, agents reason. Every number, verdict and alert comes from Python or a table.
  5. Same skeleton as Section 4: parallel analysis → dependent planning → synthesis, with a hand-off form between phases.
  6. 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 · AI Agents in Finance · Project: NPS analyst in a box
Fictional bank, generated data, for learning only. Agent wording varies run to run; the numbers come from code.