Spaces:
Sleeping
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: | |
| 1. **Auditable behavior.** A reviewer can see the graph, trace any session, and check why a question was (or wasn't) asked. | |
| 2. **Testable.** Pure functions of `Profile โ Question`. Every branch can be unit-tested. | |
| 3. **Fail-soft.** Even if the LLM brain degrades, the question flow still works. | |
| 4. **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: | |
| 1. All 6 core questions answered (age, dependents, income, existing_cover, primary_goal, location) | |
| 2. All applicable conditional questions answered (parents_age if dependents include parents) | |
| 3. 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 None` | |
| - `dependents`: 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 | |
| ```python | |
| 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 | | |