8a7091ddec
Копия пользовательской автопамяти (29 фактов + индекс MEMORY.md) в .claude/memory/, чтобы переносить между машинами через git. README.md — как восстановить в пользовательскую папку на другой машине. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
126 lines
16 KiB
Markdown
126 lines
16 KiB
Markdown
---
|
||
name: project_pet_assistant
|
||
description: «Квантик-ассистент» — сквозной помощник поверх питомца; Ф0/Ф1/«Спроси» РЕАЛИЗОВАНЫ на master (правиловый движок)
|
||
metadata:
|
||
node_type: memory
|
||
type: project
|
||
originSessionId: 60467058-b40e-4bd9-9f7f-d1e362e8039a
|
||
---
|
||
|
||
Дизайн-доку: `plans/pet-assistant/PLAN.md`. Реализация: commit **3f8009c** (2026-06-04), на master.
|
||
|
||
Суть: питомец «Квантик» стал плавающим помощником (низ-слева) на всех страницах — контекстные
|
||
подсказки, проактивные напоминания, поздравления, панель «Спроси Квантика». Движок **правиловый**
|
||
(без LLM), правила инлайн в `frontend/js/assistant.js`.
|
||
|
||
Фича-гейт: **отдельный `assistant`** (commit e1cde83 — сменили с reuse 'pet'), `requireFeature('assistant')`,
|
||
дефолт ON; админ включает/выключает в Управление→фичи (adminController allowed + games.js GAME_FEATURES,
|
||
key 'assistant'). Включён **всем** (assistant_enabled DEFAULT 1 — личный тумблер в профиле);
|
||
«видел» — **серверная** таблица `assistant_seen` (cross-device); ассистент **и на учебнике** (через
|
||
DEEPLINK_INJECT в server.js); тон **консервативный** (дневной лимит 2, кулдауны, «не показывать»);
|
||
«Спроси» — **поиск по FAQ + точка расширения под локальную модель** (в `ask()` контроллера).
|
||
|
||
Файлы: миграция `062_assistant.sql` (assistant_enabled + assistant_seen); `assistantController.js`
|
||
(FAQ инлайн — `backend/src/data/` в .gitignore!) + `routes/assistant.js`; mount `/api/assistant`
|
||
под `requireFeature('pet')`; `js/api.js` (assistantContext/Seen/Dismiss/Settings/Ask); загрузчик в
|
||
`js/sidebar.js` (как flashcard-fab); тумблер в `profile.html` («Настройки» → prefAssistant).
|
||
Эндпоинты: GET /context, POST /seen, /dismiss, /ask, PATCH /settings. Лицо — `pet-sprite.js`,
|
||
данные — `/api/pet` + `/api/assistant/context` (dueCards, homework).
|
||
|
||
Сделано: Ф0 (каркас+контекстные подсказки) + Ф1 (проактив: домашка/карточки/серия/квест/
|
||
activeLesson «продолжи урок» + поздравления левелап/серия) + Ф2 (коачмарк-тур новичка по разделам,
|
||
офер на дашборде, повтор Assistant.tour()) + Ф3-lite (FAQ-«Спроси»). Тур-правило id 'onboarding'
|
||
в assistant_seen. activeLesson (commit 9baaca7) — запрос как «продолжить чтение» из courseController.
|
||
+ Контент-апгрейд (commit c33295e): контекстные подсказки на ВСЕ разделы (PAGE_HINTS),
|
||
«Совет дня» (tip-daily, дашборд), FAQ ~10→~50, «Спроси» ищет и по платформе (LS.globalSearch),
|
||
умный проактив weakSubject (слабый предмет по test_sessions, в /api/assistant/context) +
|
||
daily-plan (из квестов+карточек). LLM подключена (commit 9dbc044): ask() вызывает OpenAI-совместимую модель с грунтовкой по топ-FAQ,
|
||
source:'model', таймаут 12с, откат на FAQ при ошибке/без ключа. Конфиг ENV в backend/.env(.example):
|
||
ASSISTANT_LLM_URL (дефолт Groq chat/completions), ASSISTANT_LLM_KEY (пусто → FAQ), ASSISTANT_LLM_MODEL
|
||
(дефолт llama-3.3-70b-versatile); локальный Ollama без ключа поддержан (localhost → зовётся без Bearer).
|
||
АКТИВНО (2026-06-04): подключён **Google Gemini** через OpenAI-совместимый эндпоинт
|
||
(ASSISTANT_LLM_URL=https://generativelanguage.googleapis.com/v1beta/openai/chat/completions),
|
||
рабочая модель **gemini-2.5-flash** (ключ в backend/.env, не в гите). Гочи по моделям на этом ключе:
|
||
gemini-2.0-flash / -lite → 429 limit:0 (нет free-квоты); gemini-1.5-* → 404; gemini-2.5-flash / gemini-flash-latest → 200.
|
||
Ключ валиден (auth ок). Сменить провайдера/модель — только через backend/.env + рестарт.
|
||
Возможности-апгрейд (commit 479c621): ответы модели рендерятся markdown + KaTeX (ленивая загрузка
|
||
katex с jsdelivr; модель просим LaTeX $...$); ask принимает context; «Объяснить выделенное» (запоминаем
|
||
selection на mouseup) и «Объяснить/Конспект параграфа» на учебнике (getPageContext по .sec.active);
|
||
«Флешкарты из параграфа» → POST /api/assistant/flashcards (модель→JSON, есть починка обрезанного,
|
||
max_tokens 1400) → колода через LS.fcCreateDeck/fcAddCard; репетитор на экзамене — кнопка «Спросить
|
||
Квантика» в task-card.js (tc-ask) → Assistant.ask(условие+ответ+решение). Экспорт Assistant.ask(q,context)
|
||
и explainSelection(). Гочи: на этом Gemini-ключе free-квота есть только у gemini-2.5-flash; placeholder
|
||
в renderRich — @@M..@@ (не цифры-в-пробелах, иначе коллизии).
|
||
Админ-панель + чат (commit dc073e2): конфиг LLM теперь в **app_settings** (assistant_llm_url/key/model),
|
||
правится из Управление→игры (карточка «Помощник Квантик — модель»: пресеты Gemini/Groq/OpenRouter/Ollama,
|
||
URL/модель/ключ, Сохранить/Проверить/Очистить, статус). Эндпоинты GET/PUT/POST /api/admin/assistant(/test)
|
||
admin-only. llmConfig() читает app_settings→ENV→дефолт; нет ключа → FAQ-режим. Текущий Gemini-ключ пока
|
||
в backend/.env (DB пусто → берётся ENV; можно перенести в БД через админку). «Спроси» — многоходовой чат
|
||
(history последние 6 реплик уходят модели, лента сообщений, «Очистить»).
|
||
RAG+кэш+учитель (commit 2252bbd, миграция 063): **RAG** — индексатор backend/scripts/index-textbooks.js
|
||
→ таблица textbook_chunks; ask() подмешивает релевантные куски (LIKE-скоринг, ≥2 слова). ГОЧА: бол-во
|
||
учебников рендерят текст ЧЕРЕЗ JS-движки → в статическом HTML только заголовки §, поэтому индекс берёт
|
||
только статично-текстовые (≈132 чанка: chemistry, часть physics); JS-рендеримые покрываются контекстом
|
||
страницы (getPageContext читает отрендеренный DOM). Полное покрытие = headless-рендер — СДЕЛАНО (commit 0119ea0): scripts/index-textbooks-headless.js
|
||
(puppeteer-core + системный Chrome, служебный JWT в localStorage т.к. /textbook требует логина) рендерит
|
||
учебники через локальный сервер, кликает §, забирает рендерный текст движков. Прогон: 87/107 книг, индекс
|
||
132→**746 чанков** (physics-9 и др. JS-учебники теперь покрыты). npm: index:textbooks:full. Сервер не
|
||
требует рестарта — ragContext читает БД на каждом запросе.
|
||
|
||
Батч 4 фич (commit 4224a22, миграция 064): **Источники** — ragContext отдаёт sources (slug/section/section_ref),
|
||
под ответом ссылка «по учебнику X, §N» на /textbook/slug#sec-<ref>; section_ref заполняет headless-индексатор
|
||
(psel-card data-id); статический индексатор больше НЕ делает delAll (per-book — не затирает headless).
|
||
**Режим-наставник**: ask(mode: answer/hint/check) + промпт; в «Спроси» переключатель из 3 кнопок; на карточке
|
||
экзамена (task-card.js) кнопка «Подсказка» (mode hint) рядом со «Спросить Квантика»; Assistant.ask(q,ctx,{mode}).
|
||
**Оценка**: лайк/дизлайк под ответом (assistant_feedback, POST /assistant/feedback) + сводка в админке (up/down/recent).
|
||
**Утренний бриф**: rule 'brief' на дашборде до 12:00 (PET.weeklyXP «N из 5 дн» + план), daily-plan сдвинут на день.
|
||
НЕ сделано: цели недели (явная установка) + напоминания по расписанию (нужен планировщик/push); голосовой ввод/TTS;
|
||
генератор заданий учителю; авто-cron на index:textbooks:full.
|
||
|
||
Мета-фильтр + тумблер экзамена (commit 961504b): вопросы про модель/нейросеть/провайдера/системный промпт
|
||
отбиваются шаблоном META_RE (саморефренция ты/тебя/твой РЯДОМ с термином модель/gpt/промпт — не блокирует
|
||
«модель атома/газа») + запрет в системном промпте. Кнопки «Подсказка»/«Спросить Квантика» на карточках
|
||
экзамена по умолчанию СКРЫТЫ (assistant_exam_buttons='0'); тумблер в админке → /context examButtons →
|
||
assistant.js вешает html.asst-exam-on (CSS показывает .tc-asst-btn). Память чата: последние 6 реплик уходят
|
||
модели, живёт в памяти вкладки до «Очистить»/reload. §-ссылки источников: section_ref у 531/746 чанков (после
|
||
headless-reindex); у остальных ссылка ведёт на главу.
|
||
|
||
Лимит-UX + отдельный раздел админки (commit 7830084): «не помнит» оказалось free-лимитом Gemini (HTTP 429,
|
||
≈20 req/min). callLLM теперь возвращает {text,error}; ask отдаёт source:'limit' («много запросов, подожди,
|
||
память не потеряется») или 'error'; фронт показывает это и НЕ ломает историю (pop неудачного вопроса). Многоходовость
|
||
работала и раньше — глушил лимит. В окне «Спроси» добавлено пояснение про память (≈6 реплик = рабочая память) +
|
||
аватар Квантика в шапке, окно шире/мягче. Управление вынесено в **отдельный раздел админки «Помощник Квантик»**
|
||
(frontend/js/admin/sections/assistant.js; AdminSections.assistant; ROUTE_TO_SECTION+ADMIN_ONLY_TABS+btn-tab-assistant;
|
||
из games.js конфиг и фича-запись удалены) — там системный вкл/выкл (feature 'assistant' через /api/admin/features) +
|
||
модель/ключ/тест/RAG/кнопки экзамена/счётчик/качество. ВАЖНО: free-квота Gemini gemini-2.5-flash = **20 запросов В СУТКИ** (quotaId GenerateRequestsPerDayPerProjectPerModel-FreeTier),
|
||
остальные модели Gemini на ключе — limit:0. Это мизер → постоянный 429. Решение: сменить провайдера на **Groq**
|
||
(free-тариф щедрый, ~тысячи/день, 30 RPM) — создать ключ console.groq.com, в админ-разделе «Помощник Квантик»
|
||
выбрать пресет Groq + вставить ключ; ИЛИ включить billing Gemini; ИЛИ локальная Ollama. Провайдер меняется в админке.
|
||
|
||
Мульти-провайдер (commit e2bff24): конфиг = список провайдеров app_settings.assistant_providers (JSON
|
||
[{id,name,url,model,key}]) + assistant_active. llmConfig=активный; providersOrdered=активный первым + остальные
|
||
с ключом; callLLMFailover перебирает их при 429/timeout/network/http (askModel и flashcards идут через него) —
|
||
второй ключ авто-подхватывает при исчерпании квоты первого. Legacy assistant_llm_* мигрируются в список при
|
||
первом GET админки. Админ-раздел «Помощник Квантик»: список провайдеров (радио=активный, Тест/Изменить/Удалить
|
||
по каждому) + форма с пресетами. Эндпоинты POST/DELETE /admin/assistant/provider(/:id), POST /admin/assistant/active.
|
||
Решение лимита Gemini (20/сутки): добавить Groq вторым провайдером — failover сам переключит.
|
||
ГОЧА: **Groq гео-заблокирован в Беларуси** («Access denied» на console.groq.com) — ключ не создать без VPN.
|
||
Железо машины подходит для ЛОКАЛЬНОЙ модели: 32 ГБ ОЗУ, GTX 1080 8 ГБ VRAM (WMI врёт 4 ГБ) → Qwen2.5-7B через
|
||
Ollama идеально. Выбор «локально vs Gemini billing vs OpenRouter» пока НЕ сделан (вопрос прерван). Failover-
|
||
уведомление (commit aac1240): callLLMFailover пишет app_settings.assistant_failover {failedName,servedName,reason,at};
|
||
при успехе активного снимается; админ-раздел показывает баннер «провайдер X недоступен — работаю на Y» / «все недоступны».
|
||
|
||
ПОДКЛЮЧЁН Kilo Code (работает из Беларуси!) как АКТИВНЫЙ провайдер: URL
|
||
**https://kilocode.ai/api/openrouter/chat/completions**, ключ — JWT (в app_settings, не в репо), модель
|
||
**nvidia/nemotron-3-ultra-550b-a55b:free** (бесплатно, чистый русский + LaTeX). Gemini остался вторым (failover).
|
||
Гочи Kilo на этом ключе: публичные `:free` (deepseek/mistral) → 404 «No endpoints»; платные → 401 «need to sign in»
|
||
(нет кредитов); kilo-auto/free → пустой ответ; рабочие бесплатные: nvidia/nemotron-3-ultra-550b-a55b:free и
|
||
openrouter/owl-alpha. Наш callLLM (без HTTP-Referer) Kilo принимает. nemotron — reasoning-модель → в промпт добавлено
|
||
«не выводи рассуждения вслух» (commit d1be2c1). Список моделей Kilo: GET .../api/openrouter/models (342 шт).
|
||
Админка (games.js, карточка): тумблер RAG, «Переиндексировать», число фрагментов. **Кэш** assistant_cache
|
||
(7 дней, только вопросы без контекста/истории) + **счётчик** assistant_usage (ИИ/кэш/FAQ, в админке).
|
||
**Учитель**: role в /context, доп. системный промпт для teacher/admin + teacher-чипы в «Спроси».
|
||
**Ключ перенесён в БД** (app_settings assistant_llm_url/key/model), из backend/.env удалён — конфиг
|
||
теперь только через админку. НЕ сделано: headless-RAG для JS-учебников; голосовой ввод; редактор промпта.
|
||
Связано: [[reference_quick_lesson]] [[reference_student_materials]] [[feedback_no_emoji]].
|