"""Конец разговора: прощание + запись конспекта в долговременную память. Триггер — слово прощания во фразе отца («до свидания», «прощай», ...). Ассистент отвечает прощальной фразой, затем ОТДЕЛЬНЫМ запросом просит модель сформулировать конспект разговора и дописывает его в data/memory.md. Конспект самоформулируется моделью (как предложил юзер), мы только сохраняем. """ from __future__ import annotations import re import threading from typing import List, Optional from .memory import MemoryStore # Фразы-триггеры прощания (регистронезависимо, как часть слова «пока» тоже сработает) _FAREWELL = re.compile(r"\b(до свидания|прощай|всего доброго|бай|пока)\b", re.I) FAREWELL_REPLY = "До свидания! Я всё запомнила и буду ждать нового разговора. Всего вам доброго!" SUMMARY_PROMPT = ( "Кратко сформулируй (3–5 простых предложений) что важно запомнить из этого " "разговора на будущее: о чём человек спрашивал, что для него важно, " "какие были договорённости или просьбы. Только сам конспект, без вступлений." ) def is_farewell(text: str) -> bool: return bool(_FAREWELL.search(text or "")) def make_summary_prompt(dialog: List[dict]) -> str: """Собрать текст диалога в промпт для конспекта.""" lines = [f"{'Человек' if m['role'] == 'user' else 'Ассистент'}: {m['text']}" for m in dialog] return "\n".join([SUMMARY_PROMPT, ""] + lines) def summarize_and_save(brain, dialog: List[dict], memory: MemoryStore, on_error: Optional[callable] = None, done: Optional[callable] = None) -> None: """Сформулировать конспект диалога и дописать в память (фоновая работа).""" def _work() -> None: try: result = brain.ask(make_summary_prompt(dialog), history=[]) # без системного контекста ассистента if result.cleaned_text: memory.append(result.cleaned_text) except Exception as exc: # Память не критична, но тихо терять ошибку плохо при отладке if on_error: on_error(f"Не смог записать память: {exc}") if done is None: threading.Thread(target=_work, daemon=True).start() else: _work() done()