feat(assistant): умная память Квантика — свежесть, категории, темы по всем предметам

Память об ученике (1+2+3 из плана), всё строго на русском:
- СВЕЖЕСТЬ: эффективный вес заметок с затуханием по времени (полураспад ~31 день),
  в промпт идут только актуальные (порог по effWeight). Старое тихо тает.
- УМНОЕ СЛИЯНИЕ: вместо дедупа по первым 24 символам — стем-токены (русская
  морфология) + Jaccard; похожие заметки сливаются (вес+, текст освежается),
  а не плодят дубли. Лимит 18.
- КАТЕГОРИИ: экстрактор классифицирует факт (трудность/предпочтение/цель/
  сильная сторона/личное), возвращает JSON; запоминаются и сильные стороны/
  интересы, не только проблемы. Гард по кириллице — не-русский текст не попадает.
- ТРУДНЫЕ ТЕМЫ ПО ВСЕМ ПРЕДМЕТАМ: профиль считает слабые темы из user_answers+
  topics (любой предмет, русские названия), объединяя с экзаменом, а не только math9.
- UI «Что я о тебе помню»: у заметок русская плашка-категория.
Без миграции (колонки kind/weight/updated_at уже есть). Проверено: логика 8/8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-24 22:27:28 +03:00
parent 4c9656e8a8
commit 7b4a274aed
2 changed files with 92 additions and 23 deletions
+6 -1
View File
@@ -375,6 +375,7 @@
'.asst-mem-prof{background:rgba(155,93,229,.07);border:1px solid rgba(155,93,229,.18);border-radius:10px;padding:9px 12px;line-height:1.75;margin-bottom:10px;}',
'.asst-mem-notes-h{font-size:.66rem;font-weight:800;color:#8a94a6;text-transform:uppercase;letter-spacing:.03em;margin:6px 0 4px;}',
'.asst-mem-note{display:flex;align-items:center;gap:8px;justify-content:space-between;padding:6px 0;border-bottom:1px solid rgba(15,23,42,.06);}',
'.asst-mem-cat{display:inline-block;font-size:.6rem;font-weight:800;text-transform:uppercase;letter-spacing:.03em;color:#7e3eca;background:rgba(155,93,229,.1);border-radius:99px;padding:1px 7px;margin-right:7px;vertical-align:1px;}',
'.asst-mem-note:last-of-type{border-bottom:none;}',
'.asst-mem-x{border:none;background:none;color:#b4bcc8;cursor:pointer;font-size:1.15rem;line-height:1;padding:0 4px;}',
'.asst-mem-x:hover{color:#e0335e;}',
@@ -625,7 +626,11 @@
if (p.weakSubjects && p.weakSubjects.length) prof.push('Слабые предметы: ' + p.weakSubjects.map(function (s) { return esc(s.name) + ' ' + s.avg + '%'; }).join(', '));
if (p.weakTopics && p.weakTopics.length) prof.push('Трудные темы: ' + p.weakTopics.map(function (t) { return esc(t.topic) + ' ' + t.rate + '%'; }).join(', '));
if (p.streak >= 3) prof.push('Серия занятий: ' + p.streak + ' дн.');
var notes = (m.notes || []).map(function (n) { return '<div class="asst-mem-note"><span>' + esc(n.text) + '</span><button class="asst-mem-x" data-id="' + n.id + '" title="Забыть">&times;</button></div>'; }).join('');
var MEM_CAT = { difficulty: 'трудность', goal: 'цель', preference: 'предпочтение', strength: 'сильная сторона', personal: 'личное', note: 'заметка' };
var notes = (m.notes || []).map(function (n) {
var cat = MEM_CAT[n.kind] || 'заметка';
return '<div class="asst-mem-note"><span><span class="asst-mem-cat">' + esc(cat) + '</span>' + esc(n.text) + '</span><button class="asst-mem-x" data-id="' + n.id + '" title="Забыть">&times;</button></div>';
}).join('');
var body = m.enabled === false
? '<div class="asst-mem-off">Персональная память выключена администратором.</div>'
: '<div class="asst-mem-body">' +