fix(scenario): recontact chats normally first then re-engages mid-chat; channel default=social; remove 'add more' button (15 personas auto)
- recontact persona now opens as a NORMAL customer (no 'I asked before' in the opening); the mid-chat time-lapse system note (turn 2) makes them re-engage warmer instead. - channel default changed facebook->social everywhere (create/analyze/store/simulator/me). - 15 personas auto-generated (TARGET=15); removed the 'สร้างบุคคลต้นแบบเพิ่มเติม' button/guide. All 9 backend suites pass. Rebuilt dist.
This commit is contained in:
@@ -139,7 +139,7 @@ def create_group():
|
||||
"product": product,
|
||||
"segment": (data.get("segment") or ""),
|
||||
"description": (data.get("description") or ""),
|
||||
"channel": (data.get("channel") or "facebook"),
|
||||
"channel": (data.get("channel") or "social"),
|
||||
"language": (data.get("language") or "th"),
|
||||
"files": saved_files,
|
||||
"file_text": file_text[:60000],
|
||||
@@ -209,7 +209,7 @@ def analyze_group(gid: str):
|
||||
segment=inp.get("segment", ""),
|
||||
description=inp.get("description", ""),
|
||||
file_text=inp.get("file_text", ""),
|
||||
channel=inp.get("channel", "facebook"),
|
||||
channel=inp.get("channel", "social"),
|
||||
)
|
||||
except Exception as exc:
|
||||
s["groups"].update(gid, status="failed", error=str(exc))
|
||||
@@ -223,7 +223,7 @@ def analyze_group(gid: str):
|
||||
personas = PersonaGenerator(s["llm"]).generate(
|
||||
sales_kit=sales_kit,
|
||||
language=inp.get("language", "th"),
|
||||
channel=inp.get("channel", "facebook"),
|
||||
channel=inp.get("channel", "social"),
|
||||
)
|
||||
personas = existing + personas
|
||||
except Exception as exc:
|
||||
|
||||
@@ -74,7 +74,7 @@ def _personal_group(s, actor) -> dict:
|
||||
g["id"],
|
||||
status="ready",
|
||||
owner_user_id=actor["id"],
|
||||
input={"channel": "facebook", "language": "th"},
|
||||
input={"channel": "social", "language": "th"},
|
||||
sales_kit={"productName": "personal practice", "valueProps": [], "features": []},
|
||||
)
|
||||
return s["groups"].get(g["id"])
|
||||
|
||||
@@ -35,7 +35,7 @@ def generate_own_persona(llm: LLMClient, *, mode: str, spec: dict[str, Any]) ->
|
||||
if not isinstance(persona, dict):
|
||||
raise ValueError("own-persona generator returned invalid data")
|
||||
persona.setdefault("tier", "B")
|
||||
persona.setdefault("channel", "facebook")
|
||||
persona.setdefault("channel", "social")
|
||||
persona.setdefault("initiation_mode", "customer")
|
||||
persona.setdefault("pains", [])
|
||||
persona.setdefault("negotiation_levers", [])
|
||||
|
||||
@@ -8,7 +8,8 @@ from ..llm import LLMClient
|
||||
from .persona_prompts import PERSONA_SYSTEM
|
||||
|
||||
TIERS = ["A", "B", "C"]
|
||||
PER_TIER = 5
|
||||
PER_TIER = 5 # 5 per tier = 15 total
|
||||
TARGET = 15 # total personas the system generates (no "add more" button needed)
|
||||
|
||||
|
||||
class PersonaGenerator:
|
||||
@@ -20,7 +21,7 @@ class PersonaGenerator:
|
||||
*,
|
||||
sales_kit: dict[str, Any],
|
||||
language: str = "en",
|
||||
channel: str = "facebook",
|
||||
channel: str = "social",
|
||||
) -> list[dict[str, Any]]:
|
||||
kit_json = json.dumps(sales_kit, ensure_ascii=False)[:12000]
|
||||
lang_name = "Thai" if language == "th" else "English"
|
||||
@@ -46,13 +47,15 @@ class PersonaGenerator:
|
||||
PERSONA_SYSTEM, prompt, temperature=0.8, max_tokens=14000
|
||||
)
|
||||
personas = result.get("personas") or []
|
||||
if (isinstance(personas, list) and len(personas) >= 15) or attempt >= 3:
|
||||
if (isinstance(personas, list) and len(personas) >= TARGET) or attempt >= 3:
|
||||
break
|
||||
if not isinstance(personas, list) or not personas:
|
||||
raise ValueError("persona generator returned no personas")
|
||||
|
||||
normalized, counts = [], {"A": 0, "B": 0, "C": 0}
|
||||
for idx, p in enumerate(personas, start=1):
|
||||
if len(normalized) >= TARGET:
|
||||
break # already reached 20 total
|
||||
if not isinstance(p, dict):
|
||||
continue
|
||||
tier = p.get("tier", p.get("intent_tier"))
|
||||
@@ -86,7 +89,6 @@ class PersonaGenerator:
|
||||
|
||||
if len(normalized) < 8:
|
||||
raise ValueError(f"expected ~15 personas, generated only {len(normalized)}")
|
||||
# NOTE: if we're short of 15 (real LLMs occasionally return 14/13), we ACCEPT what we
|
||||
# got rather than crashing the whole analyze — the caller/UI can top up with
|
||||
# "create more personas". A retry loop lives in generate().
|
||||
# NOTE: if we're short of 15 (real LLMs occasionally return 14), we ACCEPT what we
|
||||
# got rather than crashing the whole analyze — with TARGET=15 there's normally no gap.
|
||||
return normalized
|
||||
|
||||
@@ -102,21 +102,16 @@ class Simulator:
|
||||
"social": "Chat style: short, casual, quick social-messaging replies.",
|
||||
"f2f_call": "Style: natural, conversational like a live face-to-face or phone talk.",
|
||||
}.get(scenario, "")
|
||||
# Personality trait: a "recontact" customer asked about this before and is only now
|
||||
# coming back, already warmer / more ready to buy. Reference it in chat naturally.
|
||||
if persona.get("recontact"):
|
||||
adapt += (
|
||||
"\nYou contacted/interacted with this seller BEFORE (earlier contact) and are now "
|
||||
"coming back — you already know the product basics, so you're warmer and more ready "
|
||||
"to decide. Mention this naturally (e.g. 'I asked about this a while back')."
|
||||
)
|
||||
# NOTE: 'recontact' is a MID-CHAT behavior, not baked into the opening — the trainee
|
||||
# chats with this customer normally first. At the right turn (see chat_routes) a
|
||||
# time-lapse system note is inserted and only THEN does the customer re-engage warmer.
|
||||
tolerance = int(persona.get("tolerance", 3) or 3)
|
||||
system = CHAT_SYSTEM.format(
|
||||
name=persona.get("name", "Customer"),
|
||||
tone=persona.get("communication_style", "natural, casual"),
|
||||
profession=persona.get("profession", "customer"),
|
||||
age_group=persona.get("age_group", "adult"),
|
||||
channel=persona.get("channel", "facebook") + (f" ({scenario})" if scenario else ""),
|
||||
channel=persona.get("channel", "social") + (f" ({scenario})" if scenario else ""),
|
||||
background=persona.get("background", ""),
|
||||
personality=persona.get("personality", ""),
|
||||
lifestyle=persona.get("lifestyle", ""),
|
||||
|
||||
Reference in New Issue
Block a user