- 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
484 lines
32 KiB
Markdown
484 lines
32 KiB
Markdown
# Sales Trainer — Plan & Architecture
|
||
|
||
App platform for Sales Training. Users develop **customer personas with pain points**
|
||
from uploaded files + a natural-language description, then **chat with simulated
|
||
customers** to practice closing a sale. Informed by two codebases:
|
||
|
||
- **MiroFish** (`~/Gitea/MiroFish`) — original full-stack CrowdSight engine (Flask + Vue, OASIS, Zep, persona/report/chat).
|
||
- **hermes-brain-and-tools** (`~/Gitea/hermes-brain-and-tools`) — clean-room CrowdSight plugin (seed → ontology → graph → environment; profile generator; durable workflow; report contract; social simulation).
|
||
|
||
This app is a **standalone web app** (not a Hermes plugin) with its own **login/auth**,
|
||
deployable via Dockerfile/EasyPanel (the user's established pattern).
|
||
|
||
---
|
||
|
||
## 1. Product goals
|
||
|
||
1. **Input**: setup form (product / customer segment / description) AND/OR upload files
|
||
(`.pdf/.md/.txt`). Product info is used **primarily to extract pains** as the raw material
|
||
for persona generation — it is just initial grounding.
|
||
2. **Analyze**: extract value propositions, features, pricing anchors, target profile, and an
|
||
**initial pain-fit**. A generated persona is **reusable across products in the same/similar
|
||
category** — it need not be locked to the exact uploaded product.
|
||
3. **Create persona pool**: generate 15 personas (5 × intent tier A/B/C), each marked with a
|
||
buying tier, a set of pains (varied, not all product-solvable), negotiation levers, and an
|
||
initiation mode (customer-initiated vs seller-initiated). Channel: Facebook / LINE.
|
||
4. **Standard report**: human-readable analysis report (per persona: background, pains,
|
||
objections, price sensitivity, buying signals, revealable/latent fields) — downloadable.
|
||
5. **Sales Simulation (chat)**: trainee chats 1:1 with a persona (one-shot). The persona
|
||
reacts realistically: negotiates, stalls, and **refuses to buy** unless the trainee actually
|
||
resolves the persona's specific pain(s). On a result, the app reveals the pain and
|
||
**why the close succeeded/failed** (short debrief + coaching).
|
||
6. **Training loop**: track each user's wins/losses; analyze the personas a user tends to lose
|
||
against and **generate new harder/variant personas** (or manual form) to keep training.
|
||
|
||
---
|
||
|
||
## 2. Persona design (core requirement)
|
||
|
||
### 2.1 Intent tiers (2–3 levels) — REQUIRED
|
||
| Tier | Name | Behavior |
|
||
|------|------|----------|
|
||
| **A** | **Ready-to-buy** (ตั้งใจซื้อ) | Has budget + authority + urgency. Short close window, but still expects the seller to confirm fit & handle 1–2 objections. Won't buy if the offer clearly misses their need. |
|
||
| **B** | **Unsure / educating** (ไม่แน่ใจ) | Researching/considering. Needs discovery, trust-building, proof, comparison, and a clear reason to act now. High chance to stall or go silent. |
|
||
| **C** | **Not interested but has pain** (ไม่สนใจแต่มี pain) | Unaware of the category/value, budget-constrained, or skeptical. Strong resistance, but has a real, unresolved pain — the ONLY path to a close is surfacing and resolving that pain. |
|
||
|
||
### 2.1b Tier counts
|
||
- **5 personas per tier** → **15 personas minimum** per project (3 tiers × 5).
|
||
- Each persona is a distinct, realistic individual — no two share the same background/income/personality combination.
|
||
|
||
### 2.2 Persona variety (REQUIRED) — every persona gets:
|
||
- **Background** (life story / situation that justifies their behavior)
|
||
- **Income & occupation** (varied across personas: salary, e.g. employee / freelancer / SME owner / executive / student / homemaker)
|
||
- **Age group & lifestyle** (varied: age ranges, family stage, living situation, habits)
|
||
- **Personality & temperament** (e.g. skeptical, analytical, impulsive, cautious, price-haggler, relationship-driven, impatient, distrustful)
|
||
- **Communication style** (tone, vocabulary, formality, emoji usage, sentence length)
|
||
- **Goal** when entering the chat + **objections** + **budget** + **decision timeline**
|
||
- **Pains** (1–4), each with: description, root need, and **the specific condition(s)** the seller must satisfy to resolve it.
|
||
|
||
**Consistency rule:** persona demographics (age band, occupation, lifestyle, income) must be
|
||
**consistent with the product's target user** (from the sales kit / product data), and the
|
||
**additional description / scenario field acts as a framing constraint** for persona creation
|
||
(e.g. "product targets SME restaurants in Bangkok" → all 15 personas fit that world, with
|
||
sensible income/lifestyle spread).
|
||
|
||
### 2.3 Realism rules (from user)
|
||
- Customers **never buy easily**. Every close requires "earning" it.
|
||
- **All tiers can lose — including Ready-to-buy (A).** A customer walks away (no sale) if the
|
||
conversation is genuinely bad — e.g. rude/aggressive language, ignoring their needs, pushy
|
||
pitching. Even a customer who desperately wants the product will refuse if the seller
|
||
violates basic decency or trust. "Wanting it" never overrides "bad experience."
|
||
- They **negotiate** (price, timeline, scope, add-ons).
|
||
- They **refuse / end the chat** if the seller fails to address their pain.
|
||
- Each persona has hidden pains the seller must **discover** (ask questions), not just pitch.
|
||
|
||
### 2.3a Negotiation — ALL tiers (REQUIRED)
|
||
Every persona bargains over **concessions/benefits**, at every tier:
|
||
- **Price reduction** (discount ask),
|
||
- **Freebies / add-ons / bundling**,
|
||
- **Delivery / fulfillment timeline** (especially for made-to-order / production goods),
|
||
- Scope adjustments, payment terms, guarantees, etc.
|
||
The seller must respond to negotiation realistically — give value in exchange, not just give in.
|
||
The specific negotiable leverage each persona will push on is defined in the persona card.
|
||
|
||
### 2.3b Pain variety (REQUIRED)
|
||
- **NOT every pain aligns with the product.** Persona pains may be:
|
||
- **Directly solvable** by the product (the clear win),
|
||
- **Partially solvable / nearby** (the product helps but doesn't fully close it — seller must
|
||
manage expectation / bundle / reframe), or
|
||
- **Unrelated/unsolvable** by this product (a genuine red herring — seller must recognize it
|
||
and redirect rather than force a fit).
|
||
- This mirrors reality: real customers rarely have exactly the pain a product solves. The
|
||
seller must **diagnose which pain is which** and only claim what the product truly delivers.
|
||
|
||
### 2.3c Chat initiation — TWO modes (REQUIRED)
|
||
The simulation supports **two initiation modes**, set per persona (a persona is either one or the other):
|
||
- **Customer-initiated** (ลูกค้าทักก่อน): for products/brands marketed to drive engagement
|
||
(e.g. cars, food, retail). The customer opens the chat; the seller responds.
|
||
- **Seller-initiated** (ฝ่ายเราเปิดการขายก่อน): for products/services sold proactively /
|
||
outbound (e.g. insurance, B2B services). **The customer does NOT message first — the seller
|
||
must open the sale.** The simulator gives the seller an opening task to start the conversation
|
||
with a cold/warm lead, and the persona reacts accordingly.
|
||
|
||
Each persona card declares which mode applies (and, for customer-initiated, includes the
|
||
persona's opener; for seller-initiated, the persona's initial mood/receptiveness).
|
||
|
||
> Note: this supersedes any earlier "customer always messages first" rule. Initiation is
|
||
> per-case: some personas are customer-initiated, some are seller-initiated.
|
||
|
||
### 2.3d Special tier-3 case — "wrong text / lost interest" (REQUIRED, ≥1 in tier C)
|
||
- At least **one tier-C persona** is designed as a **false lead**: their opening message makes
|
||
them *appear* ready to buy (e.g. "I need this, tell me the price"). But once the seller
|
||
responds, the persona immediately reveals lost interest and wants to **end the conversation**
|
||
("never mind, forget it") — yet **deep down the pain still exists**.
|
||
- To close: the seller must not take the "forget it" at face value; they must gently re-engage,
|
||
rebuild a moment of connection, and surface the still-live pain without being pushy. High
|
||
difficulty, frequent refusal — it tests resilience + empathy + non-pushy discovery.
|
||
|
||
### 2.3e Channel context (REQUIRED)
|
||
- The simulated chat is presented as a real messaging thread. Primary channels: **Facebook** and
|
||
**LINE**. A persona/case declares which channel it happens on (affects the look, and can color
|
||
tone — e.g. LINE more casual, FB page vs Messenger).
|
||
- Other channels optional later; v1 ships Facebook + LINE.
|
||
|
||
### 2.4 Persona lifecycle & one-shot rule (REQUIRED)
|
||
- **1 persona ↔ many users**: a persona is a shared playable asset; any user who has not yet
|
||
chatted it may practice on it.
|
||
- **1 user ↔ 1 chat per persona max (one-shot)**: a user can chat a given persona **only once** —
|
||
the outcome is final and that persona is "used" for that user. It cannot be re-chatted/replayed
|
||
by the same user. (Reels/retry against it is not allowed.)
|
||
- To practice again on a similar customer, the user must **generate a new persona** (2.5).
|
||
|
||
### 2.5 Persona sources & generation (REQUIRED)
|
||
- **Admin-created baseline pool**: admin/upload-generated personas form the shared pool that
|
||
**new users** can pick from and practice on.
|
||
- **User-generated personas**: after practicing, a user can generate **their own persona** to
|
||
train on, in two ways:
|
||
1. **Weak-area generation** (4 in feature list): auto-analyze the personas this user tends to
|
||
lose against → generate a new, harder/variant persona targeting that weakness, as a "lock"
|
||
to overcome it; **or**
|
||
2. **Manual form**: the user describes the persona they want to practice (target profile,
|
||
situation, difficulty level) and the system generates it.
|
||
- User-generated personas are private to that user (unlike the admin pool).
|
||
|
||
### 2.6 Data exposure by view (REQUIRED)
|
||
- **Approve/edit view (admin)**: full persona data — pains, income, personality, negotiation
|
||
levers, hidden details. Admin sees everything to review/edit/approve.
|
||
- **Select/chat view (trainee)**: **only** the persona's name + basic info one would plausibly
|
||
know up front (profession, age group, channel, initiation mode, product context). **Hidden**:
|
||
pain, income, personality, budget, negotiation levers — anything you couldn't know without
|
||
talking. Revealed only **after** the conversation ends (win/lose + debrief).
|
||
- **Win/lose status**: the user can always see which personas they've **won** vs **lost** vs
|
||
**not yet tried**.
|
||
|
||
---
|
||
|
||
## 3. App architecture
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────┐
|
||
│ Vue 3 Frontend (port 3000) │
|
||
│ Login · Dashboard · Project setup · Report · Chat view │
|
||
└───────────────▲────────────────────────────┬───────────────┘
|
||
│ HTTP/JSON (JWT) │
|
||
┌───────────────┴────────────────────────────▼───────────────┐
|
||
│ Flask Backend (port 5001) │
|
||
│ │
|
||
│ auth/ → register, login, JWT, per-user isolation, ROLES │
|
||
│ api/ → projects, uploads, personas, report, chat, groups │
|
||
│ services/ → analyzer · persona generator │
|
||
│ → sales_kit (product facts + initial pain-fit) │
|
||
│ → report builder │
|
||
│ → sales_simulator (chat engine) │
|
||
│ storage/ → filesystem JSON per user (no SQL DB) │
|
||
│ llm_client/ → OpenAI/DeepSeek/custom OpenAI-compatible calls │
|
||
└────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 3.0 Roles & permissions (multi-user corporate)
|
||
| Role | Can do |
|
||
|------|--------|
|
||
| **Super Admin** | Manage all users, assign roles, manage org-level persona groups, view all data + analytics. |
|
||
| **Admin** (Manager) | Create/edit/delete **persona groups** (define a product/offer + its personas), **manually edit any persona**, manage users in their scope, view analytics. |
|
||
| **User** (trainee) | **Cannot create groups.** Only **selects an existing persona group** and practices (chat) on its personas; sees own results. |
|
||
|
||
- Org model: `organization` → `users` → `persona_groups` → `projects`/`sessions`.
|
||
- **Persona group** = a reusable packaged scenario: product definition (sales kit) + the 15
|
||
personas generated for it. Admins build groups **and may edit/re-analyze them later**
|
||
(groups are editable; regenerate is allowed; **admin can hand-edit any persona field**).
|
||
- A trainee's "project" is a **training session** bound to a group + a chosen persona.
|
||
|
||
### 3.0b Registration (confirmed)
|
||
- **No self-registration.** Admin creates users and sends invites (email/account-creation).
|
||
Only Super Admin and Admin can provision accounts. Roles: Super Admin / Admin / User (no Trainer).
|
||
|
||
- **Repo root**: `~/Gitea/Sales Trainer/`
|
||
- **Stack**: Flask 3 + Vue 3 (Vite) + JWT auth + filesystem JSON persistence + OpenAI-compatible LLM.
|
||
- **LLM**: reuse the provider-agnostic pattern from MiroFish (`llm_client.py`): configurable
|
||
`LLM_PROVIDER` / `LLM_BASE_URL` / `LLM_MODEL_NAME` / `LLM_API_KEY` via `.env`. Supports
|
||
**OpenAI, DeepSeek, or any OpenAI-compatible custom model** (base URL + model name overridable).
|
||
- **Persistence**: per-user `data/<user_id>/projects/<project_id>/…` JSON (mirrors
|
||
MiroFish/Hermes durable file approach). No external DB needed for v1.
|
||
|
||
### 3.1 Data flow
|
||
1. **Setup (admin)** → create a persona group: form (product/segment/description) + upload files
|
||
(+ channel & initiation-mode preferences).
|
||
2. **Analyze** (background, async) →
|
||
- **Sales Kit** extracted from input (what we sell, features, pricing, value prop, target, use cases)
|
||
**mainly for pain extraction** (reusable across same/similar category);
|
||
- **Personas** generated (15 personas w/ tier, pains, background, negotiation levers, initiation mode, channel, latent/revealable fields).
|
||
3. **Approve/Edit (admin)** → review full persona data; edit any field; approve pool.
|
||
4. **Report** (background, async) → structured report assembled from personas (downloadable; full data).
|
||
5. **Chat** → a trainee picks a persona (one-shot) → real-time conversational close attempt,
|
||
per the persona's initiation mode.
|
||
- Each message: persona responds; the simulator also returns an internal **state update**
|
||
(tier, pain-resolution progress, trust, buying-signal flags) — kept hidden.
|
||
- On **close** or **refusal**, the chat terminates with a debrief that reveals latent fields.
|
||
6. **Training loop** → record win/lose; weak-area analysis; generate-new-persona (lock or manual).
|
||
|
||
---
|
||
|
||
## 4. Services detail
|
||
|
||
### 4.1 `analyzer` (Sales Kit extraction + initial pain-fit)
|
||
Input: **setup form** with:
|
||
- **Product** (what it is) — required,
|
||
- **Initial customer segment** (optional),
|
||
- **Additional description / scenario** (optional framing for persona creation),
|
||
- **OR upload files** (`.pdf/.md/.txt`) carrying all of this — user may skip the form entirely.
|
||
|
||
**Decision logic (clear/simple):** if files are uploaded, parse them for product + target + pains;
|
||
if the form is also filled, the **form's explicit fields win** and file text fills the gaps
|
||
(and is still analyzed for extra context / pain-fit). If only files → derive everything from files.
|
||
If only the form → use the form.
|
||
|
||
Output (JSON): `productName`, `category`, `valueProps[]`, `features[]`, `pricing`
|
||
(budget anchors), `targetAudience` (incl. segment from form), `useCases[]`, `competitors[]`,
|
||
`objectionHandlers[]`, and — critically — **`initialPainFit[]`**: the analyzer's first-pass
|
||
judgement of **which pains the product can plausibly solve** (with evidence/claims from the
|
||
inputs), so persona pains can be built partly against and partly away from this baseline.
|
||
`scenario`/description is preserved as a **framing constraint** passed to persona generation.
|
||
This grounds all persona + chat generation so the simulation stays on-product.
|
||
|
||
### 4.2 `persona_generator`
|
||
Builds the **15 personas (5 × tier A/B/C)** for a given persona group. Prompt engineered to enforce:
|
||
- exactly 5 personas per tier;
|
||
- the required variety fields (background, income/occupation, personality,
|
||
communication style, goals, budget, timeline, objections);
|
||
- **pain variety** (2.3b): not every pain is product-solvable — include directly-solvable,
|
||
partially-solvable, and unrelated pains; use `initialPainFit` as the baseline;
|
||
- **the tier-C "wrong text / lost interest" special persona** (2.3d) — at least 1;
|
||
- **chat opener** for every persona + whether they initiate friendly/blunt/indifferent;
|
||
- **initiation mode** (customer-initiated vs seller-initiated) and **channel** (facebook/line);
|
||
- **latent vs revealable fields** (2.6) so the UI can hide what a real seller wouldn't know;
|
||
- JSON output schema (strict), language follows project `language` (en/th).
|
||
|
||
### 4.2b `weak_area_analyzer` (win/loss insight → new persona)
|
||
- Tracks each user's per-persona outcomes (won/lost) and their scores.
|
||
- On request, analyzes the user's **losses**: which tier / initiation mode / channel / pain-type
|
||
/ objection-type / negotiation-style they tend to lose against.
|
||
- Produces (a) a **summary insight** ("you lose most against seller-initiated, price-hardball
|
||
persona; you rarely handle discount + delivery-time pressure together"), and (b) a **spec** to
|
||
**generate a new persona** targeting that weakness (as a 'lock' to overcome) — or the user can
|
||
use the manual persona form instead.
|
||
- A user can also see their **win/lose status board** (which personas won/lost/not-tried).
|
||
|
||
### 4.3 `sales_simulator` (chat engine) — the heart
|
||
State machine per chat session:
|
||
|
||
```
|
||
READY → (customer or seller opens per mode) TALKING ⇄ (negotiating/objecting/thinking) → CLOSED | REFUSED | TIMEOUT
|
||
```
|
||
|
||
- **Initiation depends on the persona's mode (2.3c)**:
|
||
- **customer-initiated**: the customer sends the first message; the seller responds.
|
||
- **seller-initiated**: the customer does NOT message first — the simulator gives the seller
|
||
an **opening task** ("open the sale"), and the seller must start the conversation; the
|
||
persona then reacts as a cold/warm lead.
|
||
- **Channels**: Facebook or LINE (2.3e) — affects presentation + some tone.
|
||
- **One-shot (2.4)**: a user may not start a second session on a persona they've already
|
||
finished (won or lost). Backend enforces it.
|
||
- **Hidden/latent data (2.6)**: the trainee only ever sees revealable fields during the chat;
|
||
pain / income / personality / budget / negotiation levers / opener are latent and hidden until
|
||
the end.
|
||
- **Hidden pain state**: each persona has hidden unresolved pains. The simulator tracks
|
||
per-pain resolution. Pitching without discovery does NOT resolve pain.
|
||
- **Pain-fit realism (2.3b)**: since not all pains are product-solvable, the simulator must
|
||
let sellers mis-diagnose — claiming to solve an unrelated pain must backfire (trust down)
|
||
or lead down a dead end, while the real (product-solvable) pain stays unresolved.
|
||
- **Per message**: LLM plays the persona in-character (using persona card + sales kit +
|
||
chat history + current internal state). Returns:
|
||
- `reply` (the persona's in-character message),
|
||
- `internal`: updated `{ trust, painProgress, buyingSignals, tier, mayRefuse }`.
|
||
- **Close trigger**: the judge-LM decides if the seller has satisfied the pain-resolution
|
||
conditions (visible+hidden) AND the persona verbally accepts the offer/price. Only then `CLOSED`.
|
||
- **Refuse trigger**: if trust collapses, the seller pushes a hard pitch without
|
||
addressing pain, or after N failed attempts → `REFUSED`.
|
||
- **Negotiation**: personas actively counter (price, scope, timeline, freebies, delivery). Seller
|
||
must handle these on top of resolving pain.
|
||
- **Timeout/abandon**: persona goes silent if seller is repetitive/low-value.
|
||
|
||
**Internal signals are NEVER shown live.** Trust/pain/buying meters stay hidden during the
|
||
conversation (default) — the trainee reads the customer's words only.
|
||
|
||
**Debrief (on CLOSED or REFUSED) — REQUIRED, shown only at conversation end:** reveal the
|
||
latent fields (pain, income, personality, budget, negotiation levers, hidden opener), then:
|
||
- Keep the summary **short**, then give **coaching**: for each message that hurt the score,
|
||
suggest **how the seller should have responded** so the trainee understands and can improve.
|
||
- **CLOSED**: brief note on the persona's `pain` (what it was) + why it closed.
|
||
- **REFUSED**: the unaddressed pain(s), where trust was lost, which pain was mis-diagnosed (if
|
||
any) — each with **a concrete "better reply" suggestion**.
|
||
- A judge-LM (separate from the persona LLM) produces the score + coaching.
|
||
- Toggle: admin can choose to show live meters **per persona group** if desired; default hidden.
|
||
|
||
---
|
||
|
||
## 5. API surface (v1)
|
||
|
||
| Method | Path | Purpose |
|
||
|--------|------|---------|
|
||
| POST | `/api/auth/login` | JWT login |
|
||
| GET | `/api/auth/me` | current user + role |
|
||
| POST/PUT | `/api/admin/users` | admin: create users + invite, assign roles (super-admin/admin/user) |
|
||
| GET | `/api/admin/users` | admin: list users |
|
||
| POST | `/api/groups` | admin: create persona group (product files/description) |
|
||
| GET | `/api/groups` | list persona groups visible to role (user: selectable only) |
|
||
| GET | `/api/groups/<id>` | get group + personas (admin: full; user: practice view) |
|
||
| POST | `/api/groups/<id>/analyze` | admin: trigger analyze (sales kit + 15 personas) |
|
||
| GET | `/api/groups/<id>/report` | get/download report |
|
||
| GET | `/api/groups/<id>/personas` | list personas (grouped by tier; user sees revealable fields only) |
|
||
| GET | `/api/groups/<id>/personas/<pid>` | persona card (admin: full; user: revealable only) |
|
||
| PUT | `/api/groups/<id>/personas/<pid>` | admin: hand-edit a persona |
|
||
| POST | `/api/groups/<id>/reanalyze` | admin: re-run analyze / regenerate personas |
|
||
| POST | `/api/groups/<id>/personas/<pid>/chat` | user: start/send message → persona reply + hidden state (one-shot enforced) |
|
||
| GET | `/api/groups/<id>/personas/<pid>/session` | user: session state/history |
|
||
| GET/POST | `/api/sessions` | user: my training sessions + debriefs (own results) |
|
||
| GET | `/api/sessions/<id>/debrief` | user: end-of-chat debrief (pain + reason + coaching + latent reveal) |
|
||
| GET | `/api/me/board` | user: win/lose status board per persona (won/lost/not-tried) |
|
||
| GET | `/api/me/weak-areas` | user: analyze which personas I tend to lose against (insight) |
|
||
| POST | `/api/me/personas/generate` | user: generate own persona — body: {mode: "weak-area" \| "manual", ...spec} |
|
||
| GET | `/api/me/personas` | user: list my generated (private) personas + status |
|
||
| GET | `/api/analytics` | admin: aggregate trainee analytics (close rate, avg score, hardest personas) |
|
||
|
||
All `/api/*` except login require `Authorization: Bearer <jwt>`; data scoped by role + org.
|
||
Admins call analyze/persona-edit endpoints; trainees read revealable fields + run one-shot sessions +
|
||
generate their own personas.
|
||
|
||
---
|
||
|
||
## 6. Frontend views (Vue 3 + Vite)
|
||
|
||
1. **Login** (no self-registration — accounts created by admin)
|
||
2. **Dashboard** — (admin) persona groups + user mgmt; (user) selectable groups + my sessions
|
||
3. **Admin: Group Builder** — setup form (product/segment/description) + upload files; pick
|
||
channel (Facebook/LINE) + initiation-mode mix; trigger analyze → approve/edit the 15 personas
|
||
4. **Admin: User Management** — no self-registration; create + invite users, set roles
|
||
5. **Personas (approve/edit, admin only)** — **full data** for all 15 personas; edit any field, approve
|
||
6. **Personas (select, user)** — win/lose status board + list of available personas; each card shows
|
||
**only revealable info** (name, profession, age group, channel, initiation mode, product context)
|
||
7. **Report** — rendered analysis report + download (admin; full persona data)
|
||
8. **Simulation (chat)** — Facebook/LINE-style thread. **Internal signals + latent fields hidden**
|
||
during chat. Initiation per mode: customer opens OR seller gets an "open the sale" task.
|
||
**One-shot enforced.** On end → **debrief overlay**: reveal latent fields + short summary +
|
||
coaching (how to improve weak-score replies) + pain + reason + score. Result saved; persona
|
||
marked won/lost for this user.
|
||
9. **Gen persona (user)** — generate own persona: **weak-area** (from my loss analysis) or
|
||
**manual form** (describe target persona). Private to the user.
|
||
10. **Weak-areas (user)** — insight: which personas I tend to lose against + generate-a-lock CTA
|
||
11. **Admin Analytics** — aggregate trainee results (close rate, avg score, hardest personas).
|
||
|
||
i18n: en + th (mirrors MiroFish pattern). Role-based navigation (admin vs trainee).
|
||
|
||
---
|
||
|
||
## 7. Security, config, deploy
|
||
|
||
- **Auth**: JWT (HS256) with password hashing (werkzeug `generate_password_hash`).
|
||
Secrets in `.env`. No raw tokens/keys in UI. **No self-registration** — only admin-provisioned accounts.
|
||
- **LLM credentials**: `.env` only, never shipped/logged.
|
||
- **File safety**: upload allowed types + size caps; parse text server-side; strip anything
|
||
executable; keep raw uploads out of any served path.
|
||
- **LLM route discipline**: the chat + persona gen + judge are the only LLM-touching callers.
|
||
- **Deploy**: single `Dockerfile` (python:3.11 + Node 18, build Vue → serve static via
|
||
Flask or nginx) + `docker-compose.yml` with `.env`, per the user's EasyPanel pattern.
|
||
Local tests via `http.server` / Flask dev (no Docker on local Mac).
|
||
|
||
---
|
||
|
||
## 8. Milestones (build order) — ALL COMPLETE ✅
|
||
|
||
- **M0 — Scaffold & auth**: repo, Flask app factory, JWT auth, roles (super-admin/admin/user),
|
||
user + org store, **admin-user creation/invite (no self-registration)**. ✅
|
||
- **M1 — Input & analyze**: setup form (product/segment/description) + file upload/parse with
|
||
precedence rule, sales-kit extraction + **initial pain-fit**, JSON storage. ✅
|
||
- **M2 — Persona groups & persona generation**: group model (editable/re-analyzeable); 15 personas
|
||
(5 × tier) with variety, pain variety, negotiation levers, chat openers, **initiation mode
|
||
(customer/seller)**, **channel (facebook/line)**, **latent vs revealable fields**, and the
|
||
tier-C "wrong text" special case; **admin hand-edit persona endpoints**. ✅
|
||
- **M3 — Report**: assemble + render + download report. ✅
|
||
- **M4 — Chat simulation**: stateful persona chat (per-mode initiation: customer opens OR seller
|
||
"open the sale" task), Facebook/LINE thread, negotiation, all-tiers-can-lose, close/refuse logic,
|
||
**hidden signals + latent fields**, **one-shot enforcement**, **separate judge LLM** for scoring,
|
||
**short debrief with coaching** (latent reveal + pain + reason + how to improve). ✅
|
||
- **M5 — Trainee loop**: win/lose status board, weak-area analysis, **user-generated personas**
|
||
(weak-area "lock" + manual form), my-sessions. ✅
|
||
- **M6 — Frontend polish**: login, role-based dashboard, group builder (+ persona approve/edit UI),
|
||
user mgmt, personas (select view), report, chat UI, debrief overlay, gen-persona, weak-areas,
|
||
**admin analytics dashboard**, EN+TH i18n. ✅ (SPA served by Flask; verified live)
|
||
- **M7 — Deploy & docs**: Dockerfile, docker-compose, README, engineering-handoff docs,
|
||
E2E verification with honest status reporting. ✅ (mock-LLM E2E; real-key + Docker pending)
|
||
|
||
> Status: prototype complete + verified with mock LLM. **Pending: real-LLM live smoke test
|
||
> and remote Docker/EasyPanel validation** (see docs/HANDOFF.md).
|
||
|
||
---
|
||
|
||
## 9. Acceptance criteria
|
||
|
||
1. Multi-user corporate, **no self-registration**: super-admin/admin create + invite users; roles
|
||
Super Admin / Admin / User (no Trainer). **User (trainee) cannot create** — selects a group + practices.
|
||
2. Setup accepts **form (product / segment / description) AND/OR uploaded files**, with a clear
|
||
decision rule (form-wins, file-fills-gaps; files-only → derive all).
|
||
3. Analyzer produces sales kit **+ initial pain-fit**; persona demographics (age, occupation,
|
||
lifestyle, income) are **varied AND consistent with product + scenario framing**.
|
||
4. → 15 personas (5 × tier A/B/C); **admin can edit any persona and re-analyze the group**.
|
||
5. Persona group has **≥1 tier-C "wrong text / lost interest" special persona**.
|
||
6. Report is human-readable + downloadable.
|
||
7. **All tiers can lose** — bad conversation (e.g. rude language) = no sale, even for ready-to-buy.
|
||
8. **All tiers negotiate** concessions (price / freebies / delivery timeline / scope / payment).
|
||
9. Initiation is **per-persona mode**: customer-initiated (customer opens) OR seller-initiated
|
||
(seller gets an "open the sale" task). Channels **Facebook + LINE**. Internal signals + latent
|
||
fields are **hidden** during chat; only revealable fields shown.
|
||
10. **One-shot rule**: a user can chat a persona only once (won/lost = final); the same persona
|
||
stays playable for other users.
|
||
11. **Two persona views**: admin sees full data (approve/edit); trainee sees only revealable info
|
||
(name, profession, age group, channel, initiation mode) — latent fields revealed only after result.
|
||
12. **Win/lose status board**; **weak-area analysis**; **user-generated personas** (weak-area "lock"
|
||
OR manual form), private to the user.
|
||
13. Debrief is **short + coaches**: suggests how to improve on weak-score messages; reveals pain +
|
||
reason. Scored by a **separate judge LLM**; **no speed factor** in scoring.
|
||
14. Product data is used primarily for **pain extraction**; personas are reusable across the
|
||
same/similar product category.
|
||
15. **Admin analytics dashboard** aggregates trainee results (close rate, avg score, hardest personas).
|
||
16. Single `docker compose up` runs the whole app (or EasyPanel build); LLM pickable as
|
||
OpenAI / DeepSeek / any OpenAI-compatible custom model via `.env`.
|
||
17. E2E tests pass; honest report of any blocked stages.
|
||
|
||
---
|
||
|
||
## 10. Confirmed decisions (all from user)
|
||
|
||
- Standalone web app (Flask + Vue + JWT), Docker/EasyPanel deploy.
|
||
- Multi-user corporate, **no self-registration** — Super Admin / Admin / User (no Trainer).
|
||
Admin creates + invites users.
|
||
- **Admin** builds/edits persona groups, **hand-edits any persona**, re-analyzes groups, sees analytics.
|
||
- LLM: **OpenAI, DeepSeek, or custom OpenAI-compatible** (configurable via `.env`).
|
||
- **5 personas per tier → 15 min per group.**
|
||
- Persona variety: **age-group, occupation, lifestyle, income** — consistent with product + scenario framing.
|
||
- Internal chat signals **hidden**, revealed at end; **short debrief + coaching** (how to answer better on weak-score messages).
|
||
- **All tiers can lose**; **all tiers negotiate** concessions.
|
||
- **Separate judge LLM** for close/refuse + scoring; **no speed factor** in scoring.
|
||
- **Admin analytics dashboard** (aggregate trainee results).
|
||
- UI **EN + TH**.
|
||
- Product **user-input via form AND/OR uploaded file** with clear precedence; analyzer computes
|
||
initial pain-fit. Product data mainly for **pain extraction** — personas reusable across same/similar category.
|
||
- Personas have **pain variety** (not only product-solvable), demographically consistent with product.
|
||
- **Initiation per persona**: customer-initiated OR seller-initiated (open-the-sale task).
|
||
- **Channels**: Facebook + LINE.
|
||
- **Two views**: admin full data; trainee revealable-only (latent hidden until result).
|
||
- **One-shot rule**: 1 user = 1 chat per persona; persona shared across users.
|
||
- **User-generated personas**: weak-area "lock" OR manual form (private).
|
||
- **Win/lose board** + **weak-area analysis**.
|
||
- **≥1 tier-C "wrong text / lost interest" persona.**
|
||
|
||
## 11. Remaining open questions (low-risk; defaults noted)
|
||
- **Invite delivery**: email link to set password, or admin pre-sets a temporary password?
|
||
(Default: admin sets temporary password on account creation; optional email later.)
|
||
- **Analytics granularity**: just group-level aggregates, or drill-down per persona/trainee?
|
||
(Default: group + per-persona close rate + avg score; per-trainee detail on request.)
|
||
- **Scoring weights** (beyond dropping speed): pain 40 / trust 30 / objection-handling 30 ok?
|
||
- **Persona edit UI**: full form for all fields, or JSON editor for power users?
|
||
(Default: structured form for common fields + JSON for advanced.)
|
||
|
||
_Plan is ready for build. Confirm the low-risk defaults above if you disagree, otherwise I begin M0._
|