lct-hack/backend/app/dialog/runtime.py
Ivan Gerasimov 7c97310938 lct-07 (без LLM): слот-автомат, персона, звонящий на заготовках, make repl
Звонящий не выдаёт данные сам: автомат держит раскрытые факты явно и
отдаёт звонящему только их. Тест прогоняет длинную серию реплик и
проверяет, что ни один нераскрытый факт не прозвучал.

Главная находка — матчинг по порогу близости не работает. На
multilingual-e5-small настоящие вопросы дают 0.79–0.95, а «Оставайтесь
на линии» — до 0.89: диапазоны перекрываются, и любой порог либо выдаёт
адрес на «успокойтесь», либо не слышит «где вы находитесь?».

Решение — сравнение с ближайшим соседом: у пункта чек-листа несколько
формулировок (examples), рядом общий список не-вопросов
(checklists/common.yaml), и реплика засчитывается пункту, только если она
ближе к нему, чем к любому не-вопросу. На отложенных фразах: 19 из 20
вопросов, 0 из 8 ложных срабатываний.

Реплика режется только по границам предложений, со знаком: «?» для e5 —
сильный признак вопроса, без него «Куда ехать» уходит к не-вопросам.

Подсказка берёт неотработанный пункт из автомата. В живой сессии автомат
начнёт слышать оператора, когда голосовой контур передаст ему stt.final.

Ждёт ключа LLM: условие approach у скрытых фактов, звонящий своими
словами, dialog/llm.py с кэшем.
2026-09-17 13:57:43 +03:00

28 lines
1.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Модели диалога, общие для всех сессий процесса.
Эмбеддер грузится один раз при старте приложения: 2–3 секунды на загрузке
занятия недопустимы, а на старте стенда их никто не заметит.
Если модели нет — занятие всё равно идёт, но подсказки теряют точность
(идут по порядку чек-листа), и это видно в /api/health.
"""
import logging
from functools import lru_cache
from pathlib import Path
from app.config import get_settings
from app.dialog.embeddings import E5Embedder, Embedder
log = logging.getLogger(__name__)
ROOT = Path(__file__).resolve().parents[2]
@lru_cache(maxsize=1)
def get_embedder() -> Embedder | None:
model_dir = ROOT / get_settings().models_dir / "e5-small"
try:
return E5Embedder(model_dir)
except FileNotFoundError:
log.warning("модели эмбеддингов нет в %s — слот-автомат выключен, make models", model_dir)
return None