Commit Graph

4 Commits

Author SHA1 Message Date
tocmo0nlord
1aff7ea2fc Add deterministic geography: precomputed nearest-office distances injected per turn
The 2026-07-05 Weston call (CA3e88ce) showed the model guessing geography: it
recommended Boca Raton for a Weston caller (real answer: Pembroke Pines, ~9 mi),
invented distances ('Weston to Hialeah is twenty miles'), and confirmed a
nonexistent Miami Lakes office when the caller insisted.

- practice.py: office lat/lon + ~70-place South Florida gazetteer + haversine;
  geography_block() renders a full distance table (closest first) from the
  caller's area to every office. 'Westin' aliased to Weston (STT spelling).
- callstate.py: extractor gains caller_area; CallStateGroomer appends the
  GEOGRAPHY block to the system message — zero-lag via a synchronous gazetteer
  scan of user turns until the extractor catches up.
- bot.py: DISTANCES prompt rule (answer only from GEOGRAPHY, never estimate),
  only-these-8-offices guard, caller-area cities added to Whisper hotwords
  ('Weston' was heard as 'Western Florida').

Verified: staged replay of the failed exchanges on activeblue-avc:latest —
correct office, correct distances, Miami Lakes denied (5/5 + 3/3 runs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 19:15:21 +00:00
tocmo0nlord
e714262155 Fix 4 failure modes from the 2026-07-05 live call (the 'Nina' call)
- callstate: corrections win (most recent caller statement replaces old
  slots — 'no, my name is Carlos' was ignored); patient_name only from the
  caller explicitly giving their own name; booking classified only on
  explicit scheduling intent (small talk flipped a call to booking and
  drove a re-ask loop); new will_call_back call type short-circuits to a
  warm close instead of a message-taking flow.
- bot: per-call _today_line() injects the real current date (spoken form,
  answer-only) — AVA guessed 'Tuesday' on a Sunday and argued; hours rule
  now also bans stating open/closed days ('closed on Sundays' was invented).
- EndCallProcessor: phone-confirm gate reads CallState (booking/callback +
  a captured name or reason) instead of the keyword heuristic when tracking
  is on, and the injected confirmation now ends the call — the goodbye had
  already played, and holding the line open confused the caller.

Verified: staged replay of the failed transcript on activeblue-avc:latest
(4 stages x 3 runs, all pass, incl. clean-booking control); ab_replay.py
--state 0 failures, latency med 0.52s. Deployed via systemd restart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 05:44:12 +00:00
tocmo0nlord
54d707ceac Fix unasked pleasantries + callback re-asks (live call 2026-07-04 #3)
- PLEASANTRIES: the 8B parroted the verbatim example ("I'm doing well, thank
  you for asking") when the caller never asked how she was, then burned two
  more turns "starting fresh". Rule is now strictly conditional with no canned
  example: answer+ask-back only if the caller literally asks; never answer a
  question that wasn't asked.
- callstate: extraction now captures the CALLBACK request note ("are my
  glasses ready" -> "status of an order"), so the checklist stops the "what's
  the reason for your call?" re-ask; callback wrap-up wording now says STATE
  the caller-ID number, never ask for one (she asked "what's the best phone
  number" despite having it); first-name-only callbacks still ask the last name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 03:58:15 +00:00
tocmo0nlord
a47f4b423c Fix re-asking: deterministic slot memory + user-turn merge + reason-loop prompt
Historical calls showed the 8B re-asking for name/reason/phone it already had
("I already gave you my full name", the "I want an appointment" -> "what brings
you in?" loop) and VAD splitting one utterance into consecutive user turns.

- callstate.py: CallStateGroomer between agg.user() and the LLM. After each
  agent turn (off the critical path) it extracts collected slots via one short
  JSON-mode Ollama pass, then before each generation injects an ALREADY
  COLLECTED / STILL NEEDED checklist into the system message and merges
  VAD-fragmented consecutive user messages. Callback-type calls get an explicit
  "no booking questions" line. CALL_STATE_TRACKING env (auto: on for ollama,
  off for anthropic).
- bot.py prompt step 1: "I want an appointment" is the booking intent, not the
  reason - ask the visit reason once, never twice.
- scripts/ab_replay.py: regression harness replaying the real failed calls.
  llama3.1-8b raw = 3 failures; with CALL STATE = 0 failures across all
  scenarios (chat latency 0.31s -> 0.55s median, well under the 3s gate).
  Qwen3-14B A/B'd and rejected: no better raw, ~3s/turn, 11GB VRAM.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 23:49:39 +00:00