fix(bridge): stop dropping real speech — disable avg_logprob confidence floor

Live test showed the bot needed ~30 tries to answer once: 15 of 18 captured
utterances were dropped as "segment dropped (confidence=...)" — but they were
real speech ("자비스 안녕" transcribed at avg_logprob-confidence 0.0-0.29), killed
by the min_confidence=0.3 floor. Short/quiet/accented Korean over a Discord mic
scores very low avg_logprob, so the confidence floor eats real speech.

Noise rejection is handled by the VAD pre-gate + no_speech_prob hard cutoff
(both kept). The avg_logprob floor is now OFF by default (STT_MIN_CONFIDENCE=0,
env-tunable), and the no_speech threshold is env-tunable too. Raise only if
hallucinations slip past the no_speech gate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
javis-bot
2026-06-13 21:42:36 +09:00
parent 2e21185dc0
commit f2b43cb310

View File

@@ -209,12 +209,16 @@ def transcribe(wav_bytes: bytes) -> dict:
return {"text": "", "language": None}
segments, info = _whisper.transcribe(audio, beam_size=1)
# Second line of defence: even speech-like audio (e.g. a clap the VAD let
# through) can make Whisper hallucinate, so drop non-speech / low-confidence
# segments by Whisper's own no_speech_prob + avg_logprob. Mirrors the desktop
# listener's policy, driven by the same config thresholds.
no_speech_threshold = getattr(_cfg, "whisper_no_speech_threshold", 0.5)
min_confidence = getattr(_cfg, "whisper_min_confidence", 0.3)
# Second line of defence: drop non-speech / hallucinated segments by
# Whisper's own no_speech_prob. The no_speech_prob hard cutoff (plus the VAD
# pre-gate above) is what rejects noise/hallucinations. The avg_logprob
# CONFIDENCE floor is deliberately OFF by default (STT_MIN_CONFIDENCE=0):
# short, accented, or quiet real speech over a Discord mic scores very low
# avg_logprob (e.g. the wake word "자비스" at 0.0-0.3) and a confidence floor
# silently eats it, making the bot need many tries to hear one utterance.
# Raise STT_MIN_CONFIDENCE only if hallucinations slip past the no_speech gate.
no_speech_threshold = float(os.environ.get("STT_NO_SPEECH_THRESHOLD", str(getattr(_cfg, "whisper_no_speech_threshold", 0.5))))
min_confidence = float(os.environ.get("STT_MIN_CONFIDENCE", "0.0"))
kept = filter_speech_segments(
segments,
no_speech_threshold=no_speech_threshold,