fix(maria): fara "caut informatia" la completari, si trei feluri de raspuns
Din firul testat in grup: la "Urgent este", "Nu m-a contactat nimeni. Cat mai
dureaza?" si "Tot nimic", Maria trimitea de fiecare data "Caut informatia, revin
imediat...", calcula un embedding pe care il arunca, si raspundea acelasi sablon
("am adaugat si asta la M-..."). La intrebarea "cat mai dureaza" nu raspundea deloc,
iar "urgent" nu schimba nimic nicaieri.
- Completarea se decide INAINTEA confirmarii si a cautarii: raspunsul vine instant,
deci nici promisiune, nici embedding degeaba (~1-2s per mesaj).
- triaj.fel_completare: urgenta / stare / detaliu.
- urgenta -> escaladarea se marcheaza si suportul primeste un mesaj cu URGENT;
- stare -> raspuns cu ce stim (de cat timp asteapta) si reamintire catre echipa,
cel mult una la 15 minute, ca sa nu sune telefonul la fiecare "tot nimic";
- detaliu -> confirmare scurta, cu formularea rotita.
- Peste 30 de minute fara raspuns Maria o spune pe fata si indruma spre telefon
(SUPPORT_PHONE), in loc sa repete ca "echipa vede detaliul".
- Gramatica: "acum 10 minute", nu "acum 10 de minute".
Teste: 99 pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
This commit is contained in:
@@ -80,10 +80,6 @@ TACERE_OK = (
|
||||
'Am inteles, ma opresc. Scrie-mi "Maria, continua" cand vrei sa reiau.'
|
||||
)
|
||||
REVENIRE_OK = "Sunt aici. Cu ce te ajut?"
|
||||
FOLLOWUP_OK = (
|
||||
"Multumesc, am adaugat si asta la {ref} — echipa vede detaliul cand preia "
|
||||
"problema."
|
||||
)
|
||||
|
||||
|
||||
def bridge_url() -> str:
|
||||
@@ -346,40 +342,84 @@ def escalate(msg: dict, question: str, search_result: dict, fir: dict | None = N
|
||||
return record
|
||||
|
||||
|
||||
def append_followup(ref: str, msg: dict, text: str) -> bool:
|
||||
"""Adauga raspunsul omului la escaladarea deschisa si il trimite la suport.
|
||||
REAMINTIRE_MIN = 15 # cat de rar poate Maria sa reaminteasca aceeasi problema
|
||||
|
||||
Raspunsul la „te blocheaza sau poti continua?" e chiar informatia care lipseste
|
||||
din escaladare. O escaladare noua ar rupe firul: aceeasi problema, alta
|
||||
referinta, iar omul a citat-o deja pe prima.
|
||||
"""
|
||||
|
||||
def _fisier_escaladare(ref: str):
|
||||
d = config.STATE_DIR / "escalations"
|
||||
fisier = next((f for f in sorted(d.glob(f"*-{ref}.json"))), None) if d.exists() else None
|
||||
if fisier is None:
|
||||
if not d.exists():
|
||||
return None
|
||||
return next(iter(sorted(d.glob(f"*-{ref}.json"))), None)
|
||||
|
||||
|
||||
def _notifica_suport(text: str) -> bool:
|
||||
jid = config.get("SUPPORT_JID") or ""
|
||||
if not jid:
|
||||
return False
|
||||
try:
|
||||
requests.post(f"{bridge_url()}/send", json={"to": jid, "text": text}, timeout=20)
|
||||
return True
|
||||
except Exception as exc: # noqa: BLE001
|
||||
print(f"[consumer] mesaj catre suport netrimis: {exc}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
|
||||
def completeaza(ref: str, msg: dict, text: str) -> str | None:
|
||||
"""Adauga mesajul la escaladarea deschisa si intoarce ce i se raspunde omului.
|
||||
|
||||
`None` daca escaladarea nu mai exista (atunci mesajul se trateaza normal).
|
||||
|
||||
Trei feluri de completare, cu trei raspunsuri diferite: un semnal de urgenta
|
||||
schimba escaladarea si anunta echipa; o intrebare de stare primeste ce stim
|
||||
(de cat timp asteapta, daca am reamintit); un detaliu primeste o confirmare
|
||||
scurta, rotita, ca sa nu sune a robot la a treia oara.
|
||||
"""
|
||||
fisier = _fisier_escaladare(ref)
|
||||
if fisier is None:
|
||||
return None
|
||||
try:
|
||||
record = json.loads(fisier.read_text(encoding="utf-8"))
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
record.setdefault("completari", []).append(
|
||||
{"at": time.time(), "text": text, "message_id": msg.get("id")})
|
||||
return None
|
||||
|
||||
fel = triaj.fel_completare(text)
|
||||
completari = record.setdefault("completari", [])
|
||||
completari.append({"at": time.time(), "text": text, "fel": fel,
|
||||
"message_id": msg.get("id")})
|
||||
cine = msg.get("pushName") or msg.get("from") or "necunoscut"
|
||||
minute = int((time.time() - record.get("at", time.time())) / 60)
|
||||
|
||||
if fel == "urgenta":
|
||||
record["urgenta"] = True
|
||||
trimis = _notifica_suport(
|
||||
f"[Maria] URGENT — {ref} (de la {cine})\n{text[:800]}")
|
||||
record["ultima_reamintire"] = time.time()
|
||||
raspuns = triaj.URGENTA_MARCATA.format(ref=ref)
|
||||
elif fel == "stare":
|
||||
# Reamintirea e limitata: la fiecare „tot nimic" nu suna telefonul echipei.
|
||||
de_reamintit = time.time() - record.get("ultima_reamintire", record.get("at", 0)) \
|
||||
> REAMINTIRE_MIN * 60
|
||||
reamintit = False
|
||||
if de_reamintit:
|
||||
reamintit = _notifica_suport(
|
||||
f"[Maria] {ref}: clientul intreaba de {minute} minute daca s-a "
|
||||
f"rezolvat.\n{text[:400]}")
|
||||
if reamintit:
|
||||
record["ultima_reamintire"] = time.time()
|
||||
intrebari = sum(1 for c in completari if c.get("fel") == "stare")
|
||||
raspuns = triaj.raspuns_stare(ref, minute, reamintit,
|
||||
config.get("SUPPORT_PHONE", ""),
|
||||
a_cata=intrebari - 1)
|
||||
else:
|
||||
_notifica_suport(f"[Maria] Completare la {ref} (de la {cine}):\n{text[:800]}")
|
||||
raspuns = triaj.confirmare(ref, len(completari) - 1)
|
||||
|
||||
try:
|
||||
fisier.write_text(json.dumps(record, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
except OSError as exc:
|
||||
print(f"[consumer] nu pot actualiza {fisier.name}: {exc}", file=sys.stderr)
|
||||
|
||||
jid = config.get("SUPPORT_JID") or ""
|
||||
if jid:
|
||||
cine = msg.get("pushName") or msg.get("from") or "necunoscut"
|
||||
try:
|
||||
requests.post(f"{bridge_url()}/send", json={
|
||||
"to": jid,
|
||||
"text": f"[Maria] Completare la {ref} (de la {cine}):\n{text[:800]}",
|
||||
}, timeout=20)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
print(f"[consumer] completare netrimisa: {exc}", file=sys.stderr)
|
||||
print(f"[consumer] completare la {ref}: {text[:60]!r}", file=sys.stderr)
|
||||
return True
|
||||
print(f"[consumer] completare '{fel}' la {ref}: {text[:60]!r}", file=sys.stderr)
|
||||
return raspuns
|
||||
|
||||
|
||||
_GRUPURI_CACHE: dict = {"at": 0.0, "date": {}}
|
||||
@@ -483,6 +523,20 @@ def handle(index: Index, msg: dict) -> None:
|
||||
send_reply(sender, triaj.CERE_DETALII)
|
||||
return
|
||||
|
||||
# Firul are o escaladare deschisa: mesajul e completare la ea, nu intrebare
|
||||
# noua. Inaintea confirmarii si a cautarii — raspunsul vine instant, deci un
|
||||
# „caut informatia, revin imediat" ar fi o promisiune inutila, iar embedding-ul
|
||||
# s-ar calcula degeaba.
|
||||
if continuare and fir.get("ref") and text.strip():
|
||||
raspuns = completeaza(fir["ref"], msg, text.strip())
|
||||
if raspuns:
|
||||
fir_mod.adauga(fir, "client", text)
|
||||
fir_mod.adauga(fir, "maria", raspuns)
|
||||
fir_mod.salveaza(fir)
|
||||
send_reply(sender, raspuns)
|
||||
return
|
||||
fir["ref"] = None # escaladarea nu mai exista; tratam mesajul normal
|
||||
|
||||
send_reply(sender, ACK_IMAGE_TEXT if has_image else ACK_TEXT)
|
||||
|
||||
question, search_query, immediate = prepare_query(msg)
|
||||
@@ -511,15 +565,6 @@ def handle(index: Index, msg: dict) -> None:
|
||||
surse = ", ".join(f"{t['source']}({t['cosine']})" for t in result["top"]) or "-"
|
||||
print(f"[consumer] rank: {surse} | {result['reason']}", file=sys.stderr)
|
||||
|
||||
# Firul are deja o escaladare deschisa: ce spune omul acum e completare la ea,
|
||||
# nu o problema noua. De cate ori e nevoie, nu o singura data.
|
||||
if continuare and fir.get("ref") and append_followup(fir["ref"], msg, text.strip() or "(captura)"):
|
||||
raspuns = FOLLOWUP_OK.format(ref=fir["ref"])
|
||||
fir_mod.adauga(fir, "maria", raspuns)
|
||||
fir_mod.salveaza(fir)
|
||||
send_reply(sender, raspuns)
|
||||
return
|
||||
|
||||
if not result["covered"]:
|
||||
print(f"[consumer] fara acoperire in documente -> suport ({result['reason']})", file=sys.stderr)
|
||||
record = escalate(msg, question, result, fir)
|
||||
|
||||
Reference in New Issue
Block a user