Sales Trainer v0.1: corporate sales-training simulator (Flask+Vue, 15 personas, chat simulator, judge, analytics)

- Auth/roles (no self-reg), admin user provision, JWT
- Analyze: sales kit + initial pain-fit from form/upload
- Persona generator: 15 personas (5/tier) w/ pain variety, negotiation, init mode, channel, latent/revealable, wrong_text special
- Chat simulator: per-mode initiation, one-shot, hidden signals, judge-LLM debrief+coaching
- Trainee loop: win/lose board, weak-areas, user-generated personas
- Admin analytics; EN+TH Vue SPA served by Flask
- Deploy: Dockerfile, docker-compose, README, eng-log + HANDOFF
- Tests (mock LLM): m0/m1/routes/e2e all pass
This commit is contained in:
Macky
2026-08-07 15:31:06 +07:00
commit c3d31c06e2
70 changed files with 6135 additions and 0 deletions

119
README.md Normal file
View File

@@ -0,0 +1,119 @@
# 🎯 Sales Trainer
A **corporate, multi-user sales-training simulator**. Admins upload/describe a product; the app
analyzes it and generates **15 realistic customer personas** (5 per buying-intent tier) with real,
varied pains. Trainees **chat one-on-one** with each persona to practice closing a sale — customers
negotiate, stall, and refuse unless their pain is genuinely resolved. Debrief reveals + coaches.
Built on patterns from the **CrowdSight / MiroFish** swarm engine and clean-room Hermes Brain & Tools plugin.
---
## Features
- **Login + roles** (no self-registration): `super_admin` / `admin` / `user`.
- Admin builds & edits **persona groups** (product + 15 personas), hand-edits personas, sees analytics.
- User (trainee) **can't create** — only selects a group and practices; sees own results.
- **Input** via form (product / segment / description) **and/or file upload** (.pdf/.md/.txt).
Product data is used mainly to extract **pains**; personas are reusable across similar products.
- **15 personas** (5 × tier A/B/C):
- A = ready to buy · B = unsure · C = not interested but has pain (hardest).
- Varied demographics, income, occupation, lifestyle, personality — consistent with product.
- **Pain variety** (directly-solvable / partial / unrelated red-herring).
- **Negotiation levers** (price, freebies, delivery time, scope, payment).
- **Initiation mode**: customer opens OR seller must open the sale (outbound, e.g. insurance).
- **Channel**: Facebook / LINE.
- Special tier-C **"wrong_text"** persona (appears to buy, loses interest, but still has pain).
- **One-shot rule**: a persona is chatted **once per user** (final); shared across other users.
- **Chat realism**: all tiers can lose; everything negotiates; hidden internal signals + latent
fields (pain/income/personality/budget) revealed only after the result.
- **Debrief**: short summary + **coaching** (how to answer better on weak-score messages),
scored by a **separate judge LLM** (no speed factor).
- **Training loop**: win/lose board, **weak-area analysis**, and **user-generated personas**
(weak-area "lock" or manual form).
- **Admin analytics**: close rate, avg score, hardest personas.
- **EN + TH** UI.
---
## Quick start
### Local (dev)
```bash
# backend (Python 3.11)
cd backend
uv venv --python 3.11 .venv
uv pip install -r requirements.txt --python .venv/bin/python
cp .env.example .env # edit LLM keys + JWT_SECRET
uv run python run.py # Flask on :5001
# frontend (separate terminal)
cd frontend
npm install
npm run dev # Vite on :3000 -> proxies /api to :5001
```
The first run creates a default super-admin: **`admin@salestrainer.local` / `admin123`** (change it!).
### Docker / EasyPanel
```bash
cp .env.example .env # set LLM_API_KEY + a strong JWT_SECRET
docker compose up -d # single container serving frontend + API on :5001
```
---
## LLM config
Any OpenAI-compatible endpoint (OpenAI, DeepSeek, or custom base URL):
```env
LLM_PROVIDER=deepseek # deepseek | openai | custom
LLM_BASE_URL= # optional override
LLM_MODEL_NAME=deepseek-chat # optional override
LLM_API_KEY=sk-...
```
---
## Architecture
```
frontend/ Vue 3 + Vite SPA (login, dashboard, group builder, personas, chat, debrief,
gen-persona, weak-areas, analytics). Built to dist/ and served by Flask.
backend/ Flask API (JWT auth, roles, groups, analyzer, persona generator, chat simulator,
judge, trainee loop, analytics). Filesystem JSON persistence (no external DB).
app/services/ analyzer · persona_generator · simulator (+ judge) · report · trainee · groups · sessions
docs/PLAN.md full design & decisions record
```
- **Storage**: `backend/data/` — JSON files per entity (users, orgs, groups, sessions, my_personas).
- **LLM calls**: analyzer (sales kit + pain-fit), persona generator, persona chat, judge.
---
## Tests
Run with the built-in deterministic **mock LLM** (no external key needed):
```bash
cd backend
uv run python scripts/test_m0.py # auth/roles/no-self-registration
uv run python scripts/test_m1.py # group create + failure handling + role visibility
uv run python scripts/test_routes.py # all API routes registered
uv run python scripts/test_e2e.py # full flow: analyze→personas→chat→debrief→one-shot→board→analytics
```
Real-model verification requires a live `LLM_API_KEY` in `.env`.
---
## Default accounts
| Role | Email | Password |
|------|-------|----------|
| super_admin | `admin@salestrainer.local` | `admin123` (change after first login) |
Admins create additional users (users/login has no self-registration).