Spaces:
Sleeping
05 โ Needs Analysis Flow (Fact-Find)
| Field | Value |
|---|---|
| Project | Insurance Sales Portfolio Expert |
| Version | 0.1 |
| Date | 2026-05-13 |
| Implementation | backend/needs_finder.py |
0. Why an explicit graph (not "let the LLM figure it out")
A good Independent Financial Advisor opens with a stable, repeatable set of questions โ and conditionally deep-dives based on the buyer's signal. We replicate this with an explicit question graph, not an LLM that improvises every session.
Why:
- Auditable behavior. A reviewer can see the graph, trace any session, and check why a question was (or wasn't) asked.
- Testable. Pure functions of
Profile โ Question. Every branch can be unit-tested. - Fail-soft. Even if the LLM brain degrades, the question flow still works.
- Bilingual by construction. Each node has English + Hindi prompts side by side.
1. The graph
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Q1: age (core) โ
โ "What is your age?" โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Q2: dependents (core) โ
โ "Who else do you cover?" โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Q3: income_band (core) โ
โ "Annual income?" โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Q4: existing_cover (core) โ
โ "Already have health ins?" โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Q5: primary_goal (core) โ
โ "Why are you here?" โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Q6: location (core) โ
โ "Which city / tier?" โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โผ
(conditional branches)
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโ
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Q7: parents_age (cond) โ โ Q8: health_conditions (always) โ
โ asked IF dependents include โ โ "Any pre-existing condition โ
โ 'parent' โ โ on your side?" โ
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Q9: budget_band (core) โ
โ "Premium budget?" โ
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Profile complete โ readback + โ
โ policy recommendation โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
2. Termination criteria
The graph emits next_question = None (i.e., ready to recommend) when all of:
- All 6 core questions answered (age, dependents, income, existing_cover, primary_goal, location)
- All applicable conditional questions answered (parents_age if dependents include parents)
- health_conditions and budget_band answered
Or when the user sets profile.free_form_session = True โ the orchestrator skips fact-find and answers free questions directly.
3. Bilingual prompts
Every node has both prompt_en and prompt_hi. The orchestrator picks based on the user's detected language. Example:
| Node | English | Hindi |
|---|---|---|
| age | "To start, what's your age?" | "เคถเฅเคฐเฅ เคเคฐเคคเฅ เคนเฅเค โ เคเคชเคเฅ เคเคฎเฅเคฐ เคเฅเคฏเคพ เคนเฅ?" |
| dependents | "Who else do you want to coverโฆ" | "เคเคชเคเฅ เค เคฒเคพเคตเคพ เคเคฟเคธ-เคเคฟเคธ เคเฅ cover เคเคฐเคจเคพ เคนเฅโฆ" |
| budget | "What annual premium budgetโฆ" | "Premium เคเฅ เคฒเคฟเค เคธเคพเคฒเคพเคจเคพ เคเคฟเคคเคจเคพ เคเคฐเฅเคโฆ" |
This is what "Indic-native, not translated" (Doc 01 ยง5.3) looks like in practice โ the question is authored in both languages, not machine-translated at runtime.
4. Parsing user answers
Each node has a parser callable that normalizes the user's natural-language answer into a typed Profile field:
age:lambda s: int("".join(c for c in s if c.isdigit())[:3] or 0) or Nonedependents: free-form string (will be normalized via LLM in v1.1)income_band: bucketed via keyword match ("5 lakh" โ "under_5L")
When the parser returns None, we treat the question as unanswered and re-ask (v1 simply leaves the field as None and moves on โ re-ask is v1.1).
5. How the orchestrator uses this
profile = Profile.from_session(session_id)
q = next_question(profile, language=detected_language)
if q is not None:
# Still in fact-find phase โ ask the next question
reply = q.prompt_hi if language == "indic" else q.prompt_en
profile.asked.append(q.id)
else:
# Profile complete โ proceed to retrieval + recommendation
chunks = retrieve(user_query, top_k=5)
reply = brain.generate(...)
The orchestrator can also let the user drive the conversation freely โ if the user immediately asks a specific question like "compare Star and HDFC ERGO," we set free_form_session = True and skip the fact-find.
6. What the user sees
Bot: "Hi, I'm your AI insurance advisor. To start, what's your age?"
User: "I'm 32"
Bot: "Got it. Who else do you want to cover โ just yourself, spouse, kids, or parents?"
User: "self + spouse, kids later"
Bot: "Roughly what's your annual income โ under 5 lakh, 5-10, 10-25, or 25+ lakh?"
User: "around 18 lakhs"
... [continues through the core 6] ...
Bot: "Here's what I've understood: 32 years old; covering self+spouse; income 10L-25L;
no existing cover; goal: first health policy; in a metro; budget 15K-30K.
Did I get that right?"
User: "yes"
Bot: "Based on that, three policies stand out: [...] โ let me explain why."
7. v2 enhancements
| # | Enhancement | Why |
|---|---|---|
| 1 | LLM-parser for free-form answers ("I'm in my early 30s with 2 kids") | Handles natural-language replies instead of rigid bucket-matching |
| 2 | Re-ask on None parse |
Robust to user confusion |
| 3 | Skip-confirm flow ("you can skip this โ say 'skip'") | Buyer autonomy |
| 4 | Save profile across sessions | Returning user picks up where they left off |
| 5 | Tone-match the user's energy (formal vs casual) | Conversation feels human |