Files
avc-phone-ai/.env.example
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

75 lines
4.3 KiB
Plaintext

# Copy to .env and fill in. run.sh auto-loads it.
# ── Public ingress (Twilio dials this back) ──────────────────────────────────
# Public hostname; nginx terminates TLS here and proxies to the app. Must match the
# Twilio webhook host (Twilio signs https://PUBLIC_HOST/voice).
PUBLIC_HOST=voip.activeblue.net
PORT=8200
# App bind address. Default 127.0.0.1 (nginx proxies in locally) — not exposed on LAN.
BIND_HOST=127.0.0.1
# ── Twilio ───────────────────────────────────────────────────────────────────
# From console.twilio.com. Used to auto-hang-up the carrier leg and (recommended)
# validate inbound webhook signatures. Twilio signs webhooks with the Auth Token, so
# signature validation must use the Auth Token (not an API Key Secret).
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here
# Inbound webhook signature validation is ON whenever TWILIO_AUTH_TOKEN is set.
# Set to false only for local testing without real Twilio requests.
TWILIO_VALIDATE=true
# Shared secret embedded in the Media Stream wss URL to gate /ws. Set a stable random
# value (e.g. `openssl rand -base64 24`); if blank, one is generated per process start.
STREAM_TOKEN=
# ── Odoo appointment integration ─────────────────────────────────────────────
# Leave ODOO_USER/ODOO_API_KEY blank to disable Odoo and log requests to JSONL only.
# Same creds the activeblue-agent container uses (docker inspect activeblue-agent).
# Verified working against db1 with ODOO_TARGET=crm.
ODOO_URL=http://localhost:8069
ODOO_DB=db1
ODOO_USER=mr.garcia09@gmail.com
ODOO_API_KEY=
ODOO_TARGET=crm # crm = callback lead (recommended) | calendar = tentative event
# ── Capacity ─────────────────────────────────────────────────────────────────
# Max simultaneous calls (each uses GPU; Ollama serializes generation). Over-cap
# callers hear BUSY_MESSAGE and are hung up. Tune to your GPU headroom (2-3 typical).
MAX_CONCURRENT_CALLS=2
# BUSY_MESSAGE=Thank you for calling Advanced Vision Care. All of our lines are busy right now. Please call back in a few minutes. Goodbye.
# ── Models (defaults are fine) ───────────────────────────────────────────────
OLLAMA_MODEL=llama3.1:8b
OLLAMA_URL=http://127.0.0.1:11434/v1
# LLM provider: ollama (local, default) | anthropic (Claude API). Flip to A/B test Claude.
LLM_PROVIDER=ollama
ANTHROPIC_API_KEY=
# Default is the most capable model; for low-latency phone voice prefer claude-haiku-4-5
# (fastest) or claude-sonnet-4-6 (balance).
ANTHROPIC_MODEL=claude-opus-4-8
# ── STT: Whisper (faster-whisper, real-time in-call) ─────────────────────────
WHISPER_MODEL=base
WHISPER_DEVICE=cuda
WHISPER_COMPUTE=float16
KOKORO_VOICE=af_heart
KOKORO_MODEL_DIR=/home/tocmo0nlord/pipecat-run/models
# ── Call behaviour ───────────────────────────────────────────────────────────
AGENT_NAME=AVA
# How the name is SPOKEN (TTS only; logs/Odoo keep AGENT_NAME). "Eva" -> "EE-vuh".
AGENT_NAME_SPOKEN=Eva
# Grace pause after the goodbye before the carrier leg is dropped (seconds).
HANGUP_DELAY_SECS=4.0
# Half-duplex: ignore caller audio while the agent speaks (+ tail) so its own echo on the
# phone line can't trigger a false barge-in that cancels its reply. false = allow barge-in.
HALF_DUPLEX=true
ECHO_TAIL_SECS=0.25
# VAD kept sensitive (half-duplex gates echo, so this only affects the caller's turn).
VAD_CONFIDENCE=0.5
VAD_MIN_VOLUME=0.15
VAD_START_SECS=0.1
VAD_STOP_SECS=0.5
# Deterministic slot memory (callstate.py): injects an ALREADY-COLLECTED / STILL-NEEDED
# checklist into the system prompt each turn + merges VAD-fragmented user turns, so the
# local 8B stops re-asking for name/reason/phone. Default: on for ollama, off for anthropic.
#CALL_STATE_TRACKING=true