Root cause of re-login failing after first-run setup: on EasyPanel/w/o a volume, the container's /app/backend/data (users, incl. the changed admin password) is wiped on every recreate, so logging back in with the new password fails. Added: - ENV DATA_DIR=/app/backend/data (explicit) + VOLUME declaration in Dockerfile - README 'Data persistence' section: must mount a persistent volume to /app/backend/data Verified: backend + live server login>setup>relogin-with-new-password works (200); the failure is deployment data persistence, not login logic.
132 lines
5.3 KiB
Markdown
132 lines
5.3 KiB
Markdown
# 🎯 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: **username `admin` / password `1234`**.
|
||
On first login you'll be **forced to set your email + change the password** before using the app.
|
||
|
||
### 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 | Username | Password |
|
||
|------|----------|----------|
|
||
| super_admin | `admin` | `1234` (forced to set email + change on first login) |
|
||
|
||
Admins create additional users (users/login has no self-registration).
|
||
|
||
## ⚠️ Data persistence (IMPORTANT)
|
||
All data (users, passwords, groups, sessions) lives in `DATA_DIR` (default `/app/backend/data`).
|
||
**This directory MUST be a persistent volume in production.** If the container restarts/recreates
|
||
without a volume, all data is wiped — including the admin password you changed on first login, so
|
||
you will not be able to log back in with your new password.
|
||
|
||
- Docker run: `-v ./data:/app/backend/data`
|
||
- Docker Compose: already mounts `./data:/app/backend/data` (see `docker-compose.yml`)
|
||
- EasyPanel / other platforms: **mount a persistent volume to `/app/backend/data`** (or point
|
||
`DATA_DIR` at a mounted path), otherwise user data resets on every deploy.
|