AI underwriting analyst agent team — live, voice-driven risk assessment, policy verification, and decisioning.
Automate the work of an underwriting analyst: gather applicant information, verify policy/account details, assess risk, and make accept/decline decisions — all through natural voice interaction.
Underwriting is a high-volume, decision-heavy job. live_underwriter turns it into an agent team that:
- 🎙️ Talks to applicants — live voice intake (speech-to-text) and voice responses (text-to-speech)
- 🔍 Normalizes & validates applicant data before any decision
- ✅ Verifies policy/account details against records
- 🧠 Assesses risk — fraud detection, document review, and payout/decision calculation
- 🤝 Coordinates multiple specialist agents through a stateful LangGraph workflow
flowchart TD
A[User Voice Input] --> B[STT: Speech-to-Text]
B --> C[1. Normalization]
C --> D[2. Validation]
D --> E[3. Verify Account/Policy]
E --> F{Policy Exists?}
F -- No --> G[Reject / Escalate]
F -- Yes --> H[4. Review & Verify]
H --> I[5. Submit Decision]
I --> J[Fraud Detection]
J --> K[Document Review]
K --> L[Risk / Payout Calculation]
L --> M[6. Decision]
M --> N[TTS: Text-to-Speech]
N --> O[Voice Response to User]
| Agent | Role |
|---|---|
| Voice Intake | STT → text, TTS → voice response |
| Normalization | Clean & standardize applicant data |
| Validation | Check data completeness & format |
| Policy Verification | Verify account/policy exists |
| Review | Review & verify applicant details |
| Fraud Detection | Flag suspicious applications |
| Document Review | Analyze supporting documents |
| Risk / Decision | Calculate risk & underwriting decision |
The workflow is a LangGraph state machine. Each agent is a node that reads
the shared UnderwritingState, does its analysis, and writes back a partial
update. The graph routes between nodes based on the state — so agents "spawn"
sequentially, each building on the previous one's output.
flowchart TD
subgraph Intake
A[User Input<br/>transcript / voice] --> B[STT<br/>faster-whisper]
B --> C[Normalize Agent<br/>LLM extracts structured data<br/>+ DOB normalization]
end
subgraph Core Analysis
C --> D[Validate Agent<br/>required fields + format checks]
D --> E[Verify Policy Agent<br/>SQLite lookup + fuzzy/suffix match]
E --> F{Policy verified?}
F -- No --> G[Reject / END]
F -- Yes --> H[Review Agent<br/>reconcile applicant vs policy]
end
subgraph Risk Analysis
H --> I[Fraud Check Agent<br/>keywords + velocity + mismatches]
I --> J[Document Review Agent<br/>analyze bank statements / tax returns]
J --> K[Risk Decision Agent<br/>score 0-100 -> accept/decline/review]
end
subgraph Human Oversight
K --> L{Flagged?<br/>high risk / review / flags}
L -- Yes --> M[Create Review Record]
M --> N[Human Review Queue<br/>approve / decline]
L -- No --> O[Final Decision]
N --> O
end
O --> P[TTS<br/>Kokoro speaks decision]
How analysis flows through the state:
| Step | Agent | Reads from state | Writes to state |
|---|---|---|---|
| 1 | Normalize | transcript |
applicant (structured) |
| 2 | Validate | applicant |
audit trail (errors) |
| 3 | Verify Policy | applicant.policy_number |
policy (verified?) |
| 4 | Review | applicant, policy |
audit trail (mismatches) |
| 5 | Fraud Check | transcript, applicant |
risk.flags |
| 6 | Document Review | applicant → DB docs |
risk.flags |
| 7 | Risk Decision | applicant, policy, risk |
risk, decision |
| 8 | Human Review | risk (if flagged) |
review record |
Each agent is stateless and composable — it only transforms the shared state, so the graph can be re-wired, agents added/removed, or the flow branched without touching the other agents.
This project uses
uvfor the backend andnpmfor the frontend.
cd backend
uv sync --extra dev
# Configure the LLM (Ollama / OpenAI-compatible). Copy the template:
cp .env.example .env
# Then edit .env — set OLLAMA_MODEL (e.g. qwen2.5, qwen3, llama3.1),
# OLLAMA_BASE_URL, and OLLAMA_API_KEY. See .env.example for all options.
# Run the CLI
uv run live-underwriter --seed-db
uv run live-underwriter --transcript "My name is Jane Doe, policy POL-1001, coverage 500000"
# Run the API server (http://localhost:8000)
uv run live-underwriter-api
# or: uv run python -m uvicorn live_underwriter.api:app --reload
# Run tests
uv run python -m pytestcd frontend
npm install
npm run dev # http://localhost:5173 (proxies /api to :8000)
npm run build # production buildThe web UI has a Record voice button that captures audio from your
microphone, sends it to POST /api/transcribe (faster-whisper), and fills the
transcript box. To enable it, install the voice extras:
cd backend
uv sync --extra voice # installs faster-whisper + kokoroThe web UI has a Sample applicant dropdown with 6 curated test cases, each with a distinct underwriting outcome (accept, review, flagged docs, reject). Pick one to auto-fill the transcript, then run the workflow.
You can upload real PDF documents (bank statements, tax returns, W-2s) to test the document review agent. Uploaded documents are extracted (via pypdf) and analyzed alongside the applicant's seeded documents for risk signals like overdrafts, delinquent accounts, or defaults.
When the AI flags a case (high risk, "review" decision, or document/fraud flags), it automatically creates a review record. The Human review queue panel in the UI lets an underwriter approve or decline each flagged case.
Note for pyenv users: if
uv run pytestresolves to the wrong Python (a pyenv shim conflict), invoke pytest as a module instead:uv run python -m pytest.
live_underwriter/
├── backend/ # Python / LangGraph / FastAPI
│ ├── src/live_underwriter/
│ │ ├── graph.py # LangGraph state machine
│ │ ├── state.py # Underwriting state schema
│ │ ├── agents/ # Specialist agents
│ │ ├── tools/ # Underwriting tools (STT/TTS/PDF)
│ │ ├── api.py # FastAPI REST server
│ │ ├── api_server.py # uvicorn entry point
│ │ ├── db.py # SQLite repository + seed
│ │ ├── llm.py # Ollama LLM wiring
│ │ ├── dates.py # Smart DOB normalization
│ │ ├── samples.py # Curated test applicants
│ │ └── cli.py # CLI entry point
│ └── tests/
├── frontend/ # Vite + React + Tailwind (light theme)
│ ├── src/
│ │ ├── App.tsx # Main dashboard UI
│ │ ├── api.ts # Backend API client
│ │ ├── types.ts # Shared response types
│ │ └── index.css # Tailwind + light theme
│ └── vite.config.ts # Dev proxy /api -> :8000
├── docs/
└── examples/
- Core underwriting pipeline (normalize → validate → verify → review → decide)
- Fraud detection agent
- Document review agent
- Risk / decision calculation
- Voice layer (STT/TTS)
- FastAPI REST backend
- Vite + React + Tailwind frontend (light theme)
- Live voice intake in the web UI (mic → STT → transcript)
- Human-in-the-loop review for flagged cases
- Sample applicant selector (test different underwriting outcomes)
- Smart DOB normalization (understands any date format)
- Real PDF document extraction (pypdf)
- Upload real documents for testing (PDF → text → document review)
MIT © 2026 Shakti Prasad Mohapatra