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:
48
bot.py
48
bot.py
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user