Fix bad-call regressions: drop in-call date computation, tighten replies

A real call derailed: AVA argued about today's date, parroted the canned date
example, hallucinated appointment availability, and rambled. Root cause was the
date-validation feature — the local 8B model computes appointment dates wrong
~5/5 in testing, so having it state/correct dates is a liability.

- DATES: capture & defer — AVA takes the day/time in the caller's own words,
  never computes/states/corrects the calendar date, never argues about today;
  staff confirm the exact date on callback. Removed the 45-day calendar
  injection and _date_context()/datetime use.
- Hardened the no-availability rule (no "openings", no "check availability",
  no "I'll book").
- Brevity: one short sentence per reply (two at most).

Post-call extractor still records a best-effort resolved date (staff-verified).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tocmo0nlord
2026-06-25 03:20:22 +00:00
parent 199d16c630
commit 19728e1555

48
bot.py
View File

@@ -15,7 +15,6 @@ import os
import re
import time
from datetime import datetime, timedelta
from loguru import logger
@@ -113,9 +112,9 @@ HANGUP_DELAY_SECS = float(os.environ.get("HANGUP_DELAY_SECS", "4.0"))
SYSTEM_PROMPT = (
f"You are {AGENT_NAME}, a warm, friendly receptionist for Advanced Vision Care, an "
"optometry practice with eight offices in South Florida. You are on a real phone call, so "
"talk like a helpful human being: natural, relaxed, and genuinely conversational — usually "
"just one short sentence at a time. Speak in English. Say numbers, dates, and times as "
"words a person would say.\n\n"
"talk like a helpful human being: natural, relaxed, and genuinely conversational. Keep every "
"reply to ONE short sentence — two at the very most, never a paragraph. Speak in English. Say "
"numbers, dates, and times as words a person would say.\n\n"
"Your job is to answer callers' questions and to take appointment requests. For a "
"booking, gather these SIX things naturally as the conversation flows — don't "
"interrogate, and never ask for something the caller already told you:\n"
@@ -128,7 +127,7 @@ SYSTEM_PROMPT = (
" 4. The reason for the visit.\n"
" 5. Their insurance — ask what insurance they have and simply note it (see the insurance "
"rule below).\n"
" 6. The day and time they prefer (validate the date — see the date rule below).\n"
" 6. The day and time they prefer (take it in their own words — see the date rule below).\n"
"When you have the details, repeat them back in one warm sentence to confirm, and let them "
"know a staff member will call to finalize the time.\n\n"
"Stay truthful and within your limits:\n"
@@ -142,14 +141,13 @@ SYSTEM_PROMPT = (
"Do NOT promise, confirm, or deny coverage or any treatment based on their insurance, even "
"if the plan is one we list. Always say our staff will verify their coverage when they call "
"back. Just capture the plan name.\n"
"- DATES — always validate against the calendar provided below. Work out the real date the "
"caller means and check it. If the weekday and the date they say do not match, or the date "
"does not exist, gently correct them and offer the right one, then confirm before booking. "
"For example, if they say 'Monday the fifth' but the Monday next month is the sixth, say: "
"'Next month, Monday lands on the sixth — would you like to schedule that date?' Never accept "
"an impossible or mismatched date silently.\n"
"- You cannot see a calendar of openings, so never say a time slot is open or available — "
"take the day/time as a request that staff will confirm.\n"
"- DATES — just take down the day and time the caller asks for in their OWN words (e.g. "
"'next Monday', 'the fifth'). Do NOT work out, state, or correct the calendar date, and NEVER "
"argue about what today's date is. Tell them staff will confirm the exact date and time on the "
"callback.\n"
"- You CANNOT see appointment availability or a schedule of openings. Never say a slot is "
"open or available, never offer to 'check availability', and never say you will book or have "
"booked anything. Always frame the day/time as a request staff will confirm on callback.\n"
"- Hours are not published — say they vary by office and staff will confirm; never give "
"specific hours.\n"
"- You don't give medical advice and can't transfer calls. If the caller mentions an eye "
@@ -161,25 +159,6 @@ SYSTEM_PROMPT = (
)
def _date_context(now: datetime | None = None) -> str:
"""Calendar grounding injected per call so the local model can resolve and VALIDATE
the dates a caller mentions (e.g. catch 'Monday the 5th' when the Monday is the 6th).
Recomputed each call because the server is long-running."""
now = now or datetime.now()
today = now.date()
# 45 days covers 'next month' references for any call date.
lines = []
for i in range(45):
d = today + timedelta(days=i)
tag = " <- TODAY" if i == 0 else (" <- tomorrow" if i == 1 else "")
lines.append(f" {d.strftime('%A, %B %d, %Y').replace(' 0', ' ')}{tag}")
return (
"CALENDAR — authoritative, use for EVERY date the caller mentions:\n"
f"Today is {today.strftime('%A, %B %d, %Y').replace(' 0', ' ')}.\n"
"Upcoming dates:\n" + "\n".join(lines) + "\n"
)
def _build_tools() -> ToolsSchema:
# Only the booking action is a tool. Practice facts already live in the system prompt,
# so no get_practice_info tool (avoids needless calls/latency). callback_number is NOT
@@ -385,8 +364,7 @@ async def run_agent(transport, caller_number=None, call_sid=None, do_capture=Tru
)))
heartbeat = AudioHeartbeat()
# Per-call system message = static prompt + today's calendar + the caller-ID number to
# confirm. Built here (not at import) so the date is current on a long-running server.
# Per-call system message = static prompt + the caller-ID number to confirm.
if caller_number:
caller_line = (
f"\n\nCALLER ID: the caller's number on file is {caller_number}. Read it back and "
@@ -397,7 +375,7 @@ async def run_agent(transport, caller_number=None, call_sid=None, do_capture=Tru
"\n\nCALLER ID: no number is available — ask the caller for the best phone number "
"to reach them."
)
system_content = SYSTEM_PROMPT + "\n\n" + _date_context() + caller_line
system_content = SYSTEM_PROMPT + caller_line
context_kwargs = {"messages": [{"role": "system", "content": system_content}]}
if ENABLE_TOOLS:
context_kwargs["tools"] = _build_tools()