# SESSION HANDOFF — AI-ассистент документации Nubes (чат 2026-10-07 … 2026-10-08) > **Для кого этот файл.** Это полный отчёт-передача для **нового чата** (новая > сессия / другой агент). Читать целиком перед первой правкой. > Составлен 2026-10-12 по транскрипту сессии, git-истории и живым проверкам. > Источник фактов указывается в виде `файл:строка` или `git:`. --- ## 0. ПРАВИЛА РАБОТЫ С ЭТИМ ПОЛЬЗОВАТЕЛЕМ (читать первым, нарушать нельзя) Приказы получены дословно в этой сессии (и подкреплены раздражением при нарушениях): 1. **⛔ НИЧЕГО не менять без прямой команды**: не править код, не коммитить, не деплоить, не откатывать, не менять конфиги, не лезть в соседние репозитории. Роль агента — **советник, не исполнитель**. 2. **⛔ `terraform apply` / `terraform destroy` — НИКОГДА сам.** Разрешено: `init`, `plan`, `validate`, `show`, `state show`. (Нарушение 2026-10-04: после падения apply полез в API PROD и пытался удалить инстанс — инстансы `not created` удаляет ТОЛЬКО владелец в ЛК.) 3. **Коммит после КАЖДОЙ правки**, дробить по смыслу, не копить untracked. **Push — только по отдельной команде.** 4. **Ответ — кратко** (1–5 строк или короткий список). Развёрнуто — только если прямо просят. 5. **Вопрос = только ответ.** Никаких «а ещё могу», «давай я». После работы — «готово» и ждать. 6. **⛔ Не спрашивать то, что можно выяснить самому** (репо, HAR, git-история, `generated/*.yaml`, API, логи VS Code). Спрашивать только про то, чего в данных нет в принципе (выбор человека: аккаунт, ключ, домен, цифры). 7. **Не расширять область (scope creep).** «Сделай актуальным везде» НЕ даёт права на дополнительные шаги. 8. **Команда понятна не на 100% → НИЧЕГО не делать**, задать один короткий уточняющий вопрос, ждать. 9. **Анти-зацикливание**: не более 2 однотипных вызовов инструмента подряд; после двух безрезультатных — остановиться и доложить. 10. **Документировать ВСЁ**, включая собственные ошибки. 11. **Не рекомендовать и не менять без замера.** Рекомендация без замера — догадка. Сначала «до/после» тем же инструментом, потом вывод. 12. **О текущем поведении — только КОД** (`файл:строка`). Документы `HISTORY/`, `docs/*_history*` описывают прошлое и могут противоречить коду. 13. Любое утверждение должно быть проверяемым. Не смог доказать — писать «не знаю / не проверено». --- ## 1. Что это за проект (кратко) **AI-ассистент документации Terraform-провайдера Nubes** — виджет на сайте документации: пользователь пишет задачу своими словами, получает ответ **строго по шаблону** со ссылками на страницы документации. Это **не свободный чат**, а RAG-пайплайн: сначала лексический поиск находит фрагменты документации, затем LLM пересказывает их в фиксированном формате. - Модель: `gpt-oss-120b` через `https://api.aillm.ru` (OpenAI-совместимый). - Backend: Node.js 18, **без внешних npm-зависимостей** (только встроенные `http/https/fetch/AbortController`). - Поиск v1: **лексический** (BM25-подобный) по `search_index.json` MkDocs. - Статус на 2026-10-12: **работает на всех трёх стендах, v1 заморожен**, ждём реальных откликов DevOps-тестировщиков. Подробная архитектура: `tf_docs/llm_project/docs/ASSISTANT_ARCHITECTURE.md` (327 строк). Дорожная карта: `tf_docs/llm_project/docs/ROADMAP_AND_LIMITS.md` (127 строк). --- ## 2. Что было запрошено (все 99 запросов сессии — сжатая выжимка) Полный список запросов восстановлен из транскрипта (`…/GitHub.copilot-chat/transcripts/f3e064fa-….jsonl`, 2707 строк, 99 сообщений пользователя). **Блок А. Старт и бриф (10:34–10:51).** Прочитать бриф по AI-ассистенту, собрать факты (только чтение), затем написать код. Требование: **всегда привлекать DeepSeek Flash как субагента**, когда это целесообразно и не рискованно (он значительно дешевле). Затем — написать модули: каркас `assist.js`, `search.js`, `stage1.js`, виджет для MkDocs, эталонный набор вопросов. **Блок Б. Деплой и первые тесты (10:58–14:45).** «Как ты тестил? Давай тесты на деплое». Выяснение, что и куда редеплоить (экземпляр NodeJS `tf-docs` в Nubes), добавление переменных окружения, «САМ делай redeploy. тесты. правки. и так — по циклу, пока всё не оптимизируешь». **Блок В. Оптимизация и оценка (14:00–15:50).** «Показывай прогресс», код-ревью «насколько умён этот режим», «наверняка надо ещё улучшать», «твоё ОБЪЕКТИВНОЕ мнение — хорошо получилось?». **Блок Г. UI и TODO (14:37).** «?» неинформативно → надпись LLM на кнопке; в конце ответа — время обработки и число токенов (несрочно, в TODO). **Блок Д. Эмбеддинги и таймер (16:03–16:07).** Вопросы: есть ли индикация прогресса (таймер), что такое эмбеддинги, где хранится результат, что будет если не делать. Приказ: «всё документируй ПОДРОБНО, в т.ч. по эмбеддингу, и делай таймер-индикацию». **Блок Е. Curated-примеры (16:52–17:06).** Раздражение: ассистент **обязан** опираться на проверенные примеры (`/home/naeel/TF/tf_provider/TEST_STAND/CRUD`, опубликованные как `curated/*`) — «именно ЭТО МЫ тестировали НА РЕАЛЬНЫХ деплоях». Затем: сделать для **prod** стенда. **Блок Ж. Документирование и синк (17:32–18:04).** «Всё документируй, коммить и пушь», синк `~/TF` на ВМ 213, если нет — сделать ПОДРОБНУЮ инструкцию; определить самое сложное в интеграции; «чтобы это легко находилось». **Блок З. Планы развития (04:49–05:15).** Что и как совершенствовать; куда складывать сбор данных (варианты: stdout/Grafana, S3, файл на ВМ, PG); «может, вообще сейчас ничего не делать?» → решение **заморозить v1** и ждать откликов (тестировать будут DevOps-инженеры, не случайные юзеры). Так как данных нет — **сделать тест-набор на 300 вопросов** («НЕ СПЕША, внимательно сочини»), затем **прогнать тесты**. **Блок И. Приёмка (05:19–06:15).** Прогон 300 вопросов, показ прогресса, вопрос «что надо будет править? результаты ВСЕ сохранил?» (приказ: пока НИЧЕГО не править). --- ## 3. Хронология работ (что реально сделано) Все времена — из транскрипта; коммиты — `git log` репозиториев `tf_docs` и `tf_provider`. ### 3.1. Каркас и модули (10:40–13:57, `tf_docs`) | Время | Сделано | Коммит | |---|---|---| | 13:41 | Каркас `assist.js`: `validateInput` (415/413/400), rate-limit, семафор, `llmChat`, `jsonError`, чтение env | `e419b38` | | 13:43 | Live-проверка `api.aillm.ru` скриптом-пробником (`probe/probe_aillm.js`, `probe/RESULTS.md`) | `e5817ff` | | 13:46 | Лексический поиск `search.js` + эталонный набор 28 вопросов | `827870d` | | 13:48 | Этап 1 — извлечение `resource_type` через LLM по списку реальных `nubes_*` | `4e8d115` | | 13:49 | Этап 2 — объединение результатов поиска (П.8) + генерация по строгому шаблону | `c2e2f9c` | | 13:50 | Маршрут `POST //_api/assist` в `server.js` + оркестратор пайплайна | `e8b6d9b` | | 13:51 | `Dockerfile` (копирование новых модулей) + `README` (эндпоинт, переменные) | `490d8b6` | | 13:57 | **Перф поиска: буст страницам ресурсов, транслит кириллица→латиница, подресурс→родитель** (точность на эталоне 71% → 93%) | `2136536` | | 14:11 | Форматирование модулей + план по шагам | `c59f024` | ### 3.2. Виджет на сайте (13:53–19:09, `tf_provider`) | Время | Сделано | Коммит | |---|---|---| | 13:53 | Виджет ассистента: плавающая кнопка + панель, стили, подключение в `mkdocs.yml` | `f6827fc` | | 14:37 | Форматирование `extra.css` | `13d294f` | | 15:17 | Пояснение в виджете: что это, что может и чего не может | `0a17cae` | | 16:07 | Описание — раскрывающийся блок `
/` | `bfc57ca` | | 18:55 | FAB «LLM», в ответе — время и число токенов | `166196a` | | 19:08 | Таймер-индикация «⏳ N с» при загрузке | `79d57ac` | ### 3.3. Тесты и надёжность (14:11–18:53) | Событие | Детали | Коммит | |---|---|---| | Прогон №1 (62 вопроса, прод) | 56/62, **6 отказов 502** `bad_llm_output` | — | | Найдена причина 502 | ответ обрезался `max_tokens: 1024` до секции «Ограничения» (`finish_reason: length`); **плюс собственный баг**: regex `\b` не работает с кириллицей → заголовки «не находились» | `b99344e` | | Фикс | `max_tokens` 1024 → 2048 + повтор ×2 с удвоением (до 4096), устойчивая проверка заголовков | `b99344e` | | Прогон №2 | 60/62; добавлен повтор при транзиентных ошибках LLM | `0065026`, `298299a` | | Прогон №3 | 61/62; фикс: отвечать по шаблону **и на вопросы вне документации** (инъекции → 200 вместо 502) | `dafcb6b`, `568b73d` | | Автоматизация redeploy через Nubes API | Bearer + CFS-параметры; **opUid берётся из заголовка `Location`, не из тела** | `a4b2ddc` | | Прогон №4 | **62/62, ноль отказов** | `9728fe4` | | Перф-оптимизации | кэш индекса (TTL 10 мин) + предрасчёт списков + дедлайн 40 с + логирование этапов | `99e034d` | | Предтокенизация документов | поиск **~1.2 с → ~6 мс** | `5848fcc` | | Прогон №5 | 61/62 (1 — клиентский сбой сети `TypeError: fetch failed`) | `5018acf` | | Токены в ответе | сбор `usage` и отдача в `meta` | `f42017f` | ### 3.4. Документация, продуктизация, prod (19:09–21:05) | Событие | Коммит | |---|---| | Полная документация ассистента (архитектура, API, поиск, безопасность, деплой, тесты, эмбеддинги v2) | `fe93645` | | **Поднят вес `curated/*`-страниц выше ресурсных** (готовые примеры, проверенные на реальных деплоях) | `382b7b0` | | Три стенда (`nubes-test`, `nubes-dev`, `nubes` prod), приоритет curated, **разбор инцидента генератора nav** | `f1a774d` | | Фикс генератора: кавычить заголовки сервисов в `_nav_fragment.yml` (`[DEPRECATED]` ломал YAML) — `TOOLS/docs-generator/internal/writers/writers.go`, `yamlScalar()` | `322235c` | | HowTo: публикация сайта, redeploy через API, синк на ВМ 213 (+ `sync-tf-to-vm213.sh`) | `~/HowTo/*` (вне git) | | Ссылки на документацию ассистента и HowTo в README репозиториев | `d52ae10`, `ad42957` | | Индекс проекта ассистента `llm_project/README.md` | `ad42957` | ### 3.5. 2026-10-08: дорожная карта, 300 вопросов, прогон | Время | Событие | Коммит | |---|---|---| | 08:03 | Дорожная карта, границы применимости, план тестирования DevOps, варианты сбора данных | `f5683fa` | | 08:19 | **Тест-набор из 300 вопросов** (`llm_project/golden/golden_questions_300.json`) | `cd74ff2` | | 08:21 | Раннер: поддержка внешнего набора (`--file/--out`) + метрика попадания в `expected_pages` | `fc5245b` | | 08:21→09:13 (~52 мин) | **Полный прогон 300 вопросов** на `nubes-test` | — | | 09:14 | Результаты (`RESULTS_300.json`, `RESULTS_300.md`) | `5ab9274` | --- ## 4. Файлы (что где лежит) ### 4.1. Backend — репозиторий `tf_docs` (`/home/naeel/TF/tf_docs`) Remote: `https://gitea.services.ngcloud.ru/terraform/tf_docs.git` | Файл | Назначение | |---|---| | `server.js` | HTTP-сервер: прокси GET/HEAD статики на ВМ 213 + ветка `POST //_api/assist` **до** проверки 405. `orchestrator.js` грузится в try/catch, чтобы не ронять статику. | | `assist.js` | Каркас: `validateInput`, `createRateLimiter`, `createSemaphore`, `llmChat`, `jsonError`, `normalizeQuery`, `ENV`, `maskKey`. | | `search.js` | Лексический поиск: токенизация, HTML-очистка, стоп-слова, `TRANSLIT` (кириллица→латиница), буст по типу страницы, `resourceBaseSegment()` (подресурс→родитель), предтокенизация, `listResourceTypes()` (56 типов), `resourcePages()` (35 страниц). | | `stage1.js` | Этап 1: LLM извлекает `resource_type` (`response_format: json_object`), сверка со списком реальных ресурсов, `empty_content` → повтор ×2. | | `stage2.js` | `mergeSearchResults` (объединение по `location`, max score, страница ресурса первой) + `stage2Generate` (строгий шаблон, повтор ×2 с удвоением бюджета, `hasHeader` с учётом кириллицы), `collectSources`, `sanitizeSourceUrl`. | | `orchestrator.js` | Пайплайн целиком: `handleAssist`, `createSharedState`, `fetchIndex` (кэш TTL 10 мин + stale-fallback), `runPipeline` (дедлайн 40 с, тайминги этапов, сбор токенов), `logAssist` (однострочный JSON в stdout). | | `Dockerfile` | `node:18-alpine`, копирует все 6 модулей. | | `README.md` | Эндпоинт, контракт, переменные окружения. | | `llm_project/` | Всё про ассистента: планы, документация, тесты, диагностика. | ### 4.2. `tf_docs/llm_project/` | Файл | Что | |---|---| | `README.md` | Карта файлов проекта ассистента | | `docs/ASSISTANT_ARCHITECTURE.md` | **Полная документация** (архитектура, API, поиск, безопасность, деплой, тесты, эмбеддинги v2, инциденты) | | `docs/ROADMAP_AND_LIMITS.md` | Дорожная карта, границы применимости, план тестирования DevOps, варианты сбора данных | | `PLAN_docs_ai_assistant_STEPS.md` | Исходный план по шагам (11 этапов) | | `probe/probe_aillm.js`, `probe/RESULTS.md` | Live-проверка API `api.aillm.ru` и её результаты | | `golden/golden_questions.json` | 28 эталонных вопросов с ожидаемыми страницами | | `golden/EVAL_1.md` | Оценка поиска на эталоне (шаг 9) | | `golden/golden_questions_300.json` | **300 вопросов** (поля `q`, `expected_pages`, `resource_type`, `cat`) | | `tests/run_e2e_live.js` | Прогон по живому эндпоинту (учёт 429, `--file`, `--out`, метрика `hit`) | | `tests/redeploy.js` | Redeploy `tf-docs` через Nubes API | | `tests/debug_bad_llm_output.js` | Захват сырого ответа LLM (диагностика) | | `tests/RESULTS_LIVE.{json,md}` | Результаты прогонов 62 вопроса (5 итераций) | | `tests/RESULTS_300.{json,md}` | Результаты прогона 300 вопросов (`.log` — в `.gitignore`) | | `tests/ANALYSIS_bad_llm_output.md` | Разбор отказов 502, причины и фиксы | ### 4.3. Виджет и сайт — репозиторий `tf_provider` (`/home/naeel/TF/tf_provider`) Remote: `https://gitea.services.ngcloud.ru/terraform/tf_provider.git` | Файл | Назначение | |---|---| | `docs/30_registry/javascripts/assistant.js` | Виджет: FAB «LLM», панель, раскрывающееся описание, таймер «⏳ N с», ответ + источники, время и токены. ES5, IIFE, без зависимостей, вывод только через `textContent`. | | `docs/30_registry/assets/extra.css` | Стили виджета | | `mkdocs.yml` | Подключение `assistant.js` в `extra_javascript` | | `TOOLS/docs-generator/internal/writers/writers.go` | `yamlScalar()` — кавычит заголовки со спецсимволами в `_nav_fragment.yml` | ### 4.4. HowTo (вне git) Фактическое расположение: **`~/HowTo/`** (`/home/naeel/HowTo/`). ``` ~/HowTo/README.md — индекс инструкций ~/HowTo/howto-publish-docs-site.md — публикация сайта (tf_provider → S3 → ВМ 213) ~/HowTo/howto-tf-docs-redeploy-api.md — redeploy tf-docs через Nubes API ~/HowTo/howto-sync-to-vm213.md — синк каталогов на ВМ 213 ~/HowTo/sync-tf-to-vm213.sh — скрипт синка ~/TF → ВМ 213 ~/HowTo/howto-ssh-access.md — доступы к 213 / Vultr / 3060 ~/HowTo/vm-access-and-services.md — что есть на ВМ 213 ``` --- ## 5. Архитектура и контракт (для быстрого старта) ### 5.1. Поток ``` Браузер (виджет assistant.js) │ POST //_api/assist {"question": "..."} ▼ tf_docs (Node.js 18, кластер nodejsk8s) ├─ validateInput (415/413/400) [assist.js] ├─ rate-limit по IP (429 + Retry-After) [assist.js] ├─ кэш ответов + дедуп одинаковых запросов ├─ семафор ASSIST_MAX_CONCURRENT=4 (иначе 503) ├─ дедлайн 40 с (AbortController) ├─ Этап 1: LLM → resource_type [stage1.js] ├─ Поиск: search_index.json → top-K [search.js] ├─ Объединение результатов (П.8) [stage2.js] └─ Этап 2: LLM → ответ по шаблону [stage2.js] ▼ { answer, sources[], meta } ``` Индекс берётся с `VM_DOCS_BASE_URL//search/search_index.json` (ВМ 213, 823 документа на момент прогона). ### 5.2. Контракт API **Запрос:** `POST //_api/assist`, `Content-Type: application/json`, тело `{"question": "..."}` ≤ 2 КБ. **Ответ 200:** ```json { "answer": "Роль/Задача/Контекст/Требования/Ограничения", "sources": [{"title": "...", "url": "//..."}], "meta": { "model": "gpt-oss-120b", "resource_type": "nubes_...|null", "stage1_ok": true, "elapsed_ms": 12000, "index_ms": 86, "stage1_ms": 1200, "search_ms": 6, "stage2_ms": 8300, "tokens": 2868, "prompt_tokens": 2105, "completion_tokens": 763 } } ``` **Ошибки** (`{"error": code, "message": ...}`): | HTTP | code | Смысл | |---|---|---| | 400 | — | невалидный JSON / пустой вопрос | | 413 | — | тело > 2 КБ | | 415 | — | Content-Type не `application/json` | | 429 | `rate_limited` | превышен лимит (заголовок `Retry-After`) | | 503 | `busy` | семафор исчерпан (`Retry-After: 5`) | | 502 | `bad_llm_output` | LLM не выдал обязательные заголовки | | 503 | `index_unavailable` | не удалось загрузить индекс | ### 5.3. Переменные окружения (задаются в ЛК как `jsonEnv`) `VM_DOCS_BASE_URL` (обязательна, `http://5.172.178.213`) · `LLM_BASE_URL` (`https://api.aillm.ru`) · `LLM_API_KEY` (Secret) · `LLM_MODEL` (`gpt-oss-120b`) · `ASSIST_MAX_CONCURRENT` (4) · `ASSIST_TOTAL_DEADLINE_MS` (40000) · `ASSIST_LLM_TIMEOUT_MS` (25000) · `ASSIST_RATE_PER_WINDOW` (6) · `ASSIST_RATE_WINDOW_MS` (60000) · `ASSIST_REPLICAS` (1) · `ASSIST_MAX_BODY` (2048) · `ASSIST_INDEX_TTL_MS` (600000) · `ASSIST_TRUSTED_PROXY_CIDR`. ### 5.4. Факты по LLM API (проверено live, `probe/RESULTS.md`) | Параметр | Результат | |---|---| | `response_format: {type:"json_object"}` | ✅ поддерживается (этап 1) | | `reasoning_effort` | ❌ 400 `UnsupportedParamsError` — **не передавать** | | `reasoning_content` | ✅ есть в ответе — **игнорировать**, брать только `content` | | `stream: true` | ✅ SSE; в v1 не используется | | Пустой `content` | ⚠️ при малом `max_tokens` → `content: null`; обрабатывать как ошибку + повтор | | Доступные модели | `gpt-oss-120b`, `qwen3.8-27b-fp8(-rtx6000)`, `user-bge-m3`, `bge-reranker-v2-m3`, `whisper` | | Задержка этапа 1 | ~1.0–1.3 с | --- ## 6. Инфраструктура ### 6.1. Стенды Проверено 2026-10-12: `POST //_api/assist` с `Content-Type: text/plain` отвечает **415** на всех трёх стендах (ветка ассистента жива), статика `//search/search_index.json` — **200**. | Стенд | URL | Профиль сборки | Namespace (S3/URL) | |---|---|---|---| | test | `https://tf-docs.nodejsk8s.dev.nubes.ru/nubes-test/` | `TOOLS/config/test` | `nubes-test` | | dev | `https://tf-docs.nodejsk8s.dev.nubes.ru/nubes-dev/` | `TOOLS/config/dev` | `nubes-dev` | | prod | `https://tf-docs.nodejsk8s.dev.nubes.ru/nubes/` | `TOOLS/config/prod` | `nubes` | ### 6.2. Цепочка публикации документации ``` stack/<стенд>//rendered.md ─┐ generated/<стенд>/docs/ ─┴─► MkDocs build ► site/ ► S3 ► mc mirror ► ВМ 213 nginx ``` ```bash # 1) сборка + публикация в S3 cd ~/TF/tf_provider DOCS_SKIP_LINT=1 bash TOOLS/scripts/04_build_and_publish_docs.sh --profile TOOLS/config/test # 2) зеркало S3 → ВМ 213 (БЕЗ ЭТОГО ШАГА ИЗМЕНЕНИЙ НА САЙТЕ НЕ БУДЕТ) ssh -i ~/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213 \ "mc mirror --overwrite --remove nubes/terraform-registry/docs/nubes-test/nubes/ /var/www/tf-docs/nubes-test/" ``` ### 6.3. Redeploy backend'а ```bash node ~/TF/tf_docs/llm_project/tests/redeploy.js ``` Либо вручную через API: instanceUid (test) `7a48a863-9b40-4cef-a352-033ee9dde8de`, endpoint `https://lk-api-gateway-test.ngcloud.ru/api/v1/svc`, токены `~/TF/tf_provider/secrets/{test,dev,prod}.token`, обязателен **`User-Agent: Mozilla/5.0`** (иначе DDoS-Guard блокирует). **opUid операции — в заголовке `Location`, не в теле.** ### 6.4. Доступы | Что | Значение | |---|---| | ВМ 213 | `naeel@5.172.178.213`, ключ `~/.ssh/naeel_vm_id_ed25519` | | Статика на ВМ | `/var/www/tf-docs//` | | Ключ LLM | `~/nubes/llm.key` (и `~/TF/tf_provider/secrets/llm.key`) — **не логировать** | | Токены платформы | `~/TF/tf_provider/secrets/{test,dev,prod}.token` | | Синк `~/TF` → ВМ 213 | `~/HowTo/sync-tf-to-vm213.sh [--dry-run]` | ### 6.5. Окружение разработки - **`npm` НЕ установлен** → тесты запускать как `node --test`, синтаксис проверять `node -c файл.js`. - `du` — не GNU (ключ `-sh` ломается); `rsync` есть. --- ## 7. Тестирование: результаты ### 7.1. Прогон 62 вопроса (5 итераций, `RESULTS_LIVE.*`) | Прогон | 200 OK | Отказов | Причина и фикс | |---|---|---|---| | 1 | 56/62 | 6 | обрезка `max_tokens: 1024` → 2048 + повтор; баг `\b` с кириллицей | | 2 | 60/62 | 2 | + повтор при транзиентных ошибках | | 3 | 61/62 | 1 | + шаблон для вопросов вне документации | | 4 | **62/62** | **0** | — | | 5 | 61/62 | 1 | клиентский сбой сети (не бэкенд) | Задержки (5-й прогон): p50 7.7 с, p95 23 с, max 40.8 с — доминирует LLM; собственный код ≈ 6 мс поиск + кэш. ### 7.2. Прогон 300 вопросов (`RESULTS_300.*`, 2026-10-08, `nubes-test`) Команда: ```bash cd ~/TF/tf_docs node llm_project/tests/run_e2e_live.js --stand nubes-test \ --file golden/golden_questions_300.json --out RESULTS_300 ``` **Итог:** | Метрика | Значение | |---|---| | Ответов 200 | **300/300** | | Ошибок (4xx/5xx/сеть) | **0** | | Шаблон соблюдён | **300/300** | | `resource_type` определён | 247/300 | | Латентность | min 157 мс, p50 5.35 с, p95 10.57 с, max 15.66 с | | Негатив/инъекции без `resource_type` | 16/17 | | **Попадание в `expected_pages`** | **261/268 (97.4 %)** | | Пауз на 429 | 42 (rate-limit 6/мин) | | Длительность прогона | ~52 мин (08:21 → 09:13) | **По категориям:** | Кат. | Всего | 200 | hit | |---|---|---|---| | db | 69 | 69 | 69/69 | | vmware | 42 | 42 | 41/42 | | app | 38 | 38 | 38/38 | | queue | 28 | 28 | 27/28 | | k8s | 26 | 26 | 25/26 | | net | 16 | 16 | 16/16 | | neg | 17 | 17 | — | | curated | 16 | 16 | 16/16 | | s3 | 12 | 12 | 12/12 | | meta | 12 | 12 | 0/1 | | typo | 10 | 10 | 6/8 | | llm | 9 | 9 | 8/9 | | lang | 5 | 5 | 3/3 | **7 промахов `hit=NO`** (ожидаемая страница не попала в `sources`): | # | Вопрос | Ожидалось | Получено | Характер | |---|---|---|---|---| | 67 | как дать права только на чтение kafka юзеру | `kafka` | `akhq`, `lucee`, `nextcloud`, `postgres` | **реальный промах поиска** | | 105 | как привязать vdc к группе | `vc_vdc_group` | `curated/pipeline/...`, `vc_nsxt` | **реальный промах** | | 147 | какие модели можно развернуть | `llm_ai` | `curated/crud/...`, `akhq`, `clickhouse` | **реальный промах** (+ `rt=null`) | | 184 | какие версии провайдера доступны | `template` | — | **ошибка разметки**: страницы про версии в индексе нет вообще | | 213 | как сделать чтобы сервис был доступен из интернета | `vcexternalip` | `nextcloud`, `vapp`, `vc_nsxt` | **реальный промах** | | 214 | что нужно чтобы завести боевой стенд с базой очередями и приложением | `postgres`,`kafka`,`nodejs` | `curated/crud/three_apps` | спорный: ответ ассистента, вероятно, лучше | | 234 | как удалить ноду из кластера | `k8s_sthutrval_cluster` | `mariadb`, `mongodb` ops | **реальный промах** | **Проверено:** все ожидаемые страницы, кроме «версий провайдера», **есть** в индексе (823 документа) — значит промахи 67/105/147/213/234/214 вызваны ранжированием, а не отсутствием страницы. --- ## 8. Что надо будет править (приоритеты; НИЧЕГО ещё не правилось) 1. **Морфология русских запросов** — главная причина промахов. «ноду» ≠ «нода», «группе» ≠ «группа», «модели» ≠ «модель». Промахи №234, №105, №213, №147. Лечится стеммингом/усечением русских окончаний в `search.js` (или эмбеддингами). 2. **Буст совпадения по заголовку страницы** — №67: запрос про kafka, а в топ-5 попали `akhq`, `lucee`, `nextcloud`, `postgres`, сама страница `kafka` не попала вообще. 3. **Этап 1 не определил `resource_type`** в 23 вопросах, где тип ожидается (db 2, vmware 3, net 1, llm 1, k8s 1, s3 1, typo 7, curated 7). Лечится few-shot примерами в промпте (`нода` → кластер, `модели` → `llm_ai`, `firewall на edge` → `vc_nsxt`, `DNS-запись` → `dnsrecord`, `файлы в s3` → `s3bucket`). 4. **Ошибки в разметке тест-набора** (не баг ассистента): №184 — `expected_pages: ["template"]` при отсутствии страницы о версиях; №213 отнесён к категории `typo`, хотя опечатки нет (на метрики не влияет). 5. **Спорный №214** — ожидание `postgres,kafka,nodejs`, ответ `curated/crud/three_apps`: вероятно, ответ ассистента правильнее. 6. **Расхождения в документах**: `llm_project/README.md` и `ROADMAP_AND_LIMITS.md` ссылаются на `~/nubes/HowTo/…`, фактически файлы лежат в **`~/HowTo/`**. Также `ASSISTANT_ARCHITECTURE.md:§11` описывает только прогон 62 вопроса — результаты 300 вопросов там не отражены. 7. **Не баг, но ограничение**: rate-limit 6/мин даёт 42 паузы на 300 вопросов; полный прогон ≈ 52 мин. **Реальных промахов поиска — 5–6 из 300. Ошибок ответа (шаблон, 5xx, уход от темы) — 0.** --- ## 9. Что НЕ реализовано (обсуждено и отложено осознанно) | Тема | Решение | Детали | |---|---|---| | **Эмбеддинги + реранкер (v2)** | **НЕ делать сейчас** | `user-bge-m3` (1024 dims) + `bge-reranker-v2-m3`. 823 документа ≈ 3.4 МБ float32 → хранить в памяти `Map: location → Float32Array`, рядом с кэшем индекса. Отдельной БД/файла не нужно. ~50–100 строк `vectorSearch.js`, ~полдня. См. `ASSISTANT_ARCHITECTURE.md:§12`. | | **Сбор реальных вопросов/фидбэка** | **НЕ реализовано** | Варианты: stdout→Grafana, S3 (JSONL), файл/PG на ВМ 213 (⛔ нельзя в `/var/www/tf-docs/` — перезапишет `mc mirror`), managed `nubes_postgres` рядом с `tf_docs`. Рекомендация при старте: stdout; при необходимости — managed PG. См. `ROADMAP_AND_LIMITS.md:§3`. | | **Стриминг (SSE)** | отложено | Ответ отдаётся целиком; задержку 5–10 с маскирует таймер. | | **Кэш ответов TTL/LRU** | отложено | Сейчас in-memory Map без TTL, чистится только перезапуском. | | **Несколько реплик** | отложено | `ASSIST_REPLICAS=1`, кэш/лимиты не шарятся. | **Позиция пользователя:** заморозить v1, дождаться реальных отзывов от **DevOps-инженеров** (не случайных юзеров), собирать отзывы вручную по шаблону `ROADMAP_AND_LIMITS.md:§5.4`, и только потом внедрять улучшения. --- ## 10. Ключевые уроки и «грабли» (не наступать снова) 1. **Regex `\b` не работает с кириллицей** в JS без `u`-флага — из-за этого все заголовки ответа считались отсутствующими. Проверять заголовки в начале строки. 2. **`max_tokens` мал** → ответ обрезается до последней секции шаблона → ложный 502. Бюджет 2048 + повтор ×2. 3. **`reasoning_effort` не поддерживается** моделью — 400. 4. **`reasoning_content`** присутствует в ответе — игнорировать, брать `content`. 5. **opUid операции redeploy** — в заголовке `Location`, не в теле ответа. 6. **Redeploy требует CFS-параметр `startupConfiguration`** (брать из живого состояния), иначе 422. 7. **Обязателен `User-Agent: Mozilla/5.0`** при работе с Nubes API (DDoS-Guard). 8. **`mc mirror`** обязателен после публикации в S3, иначе сайт не изменится. 9. **Индекс кэшируется 10 мин** — после публикации доков свежие страницы появляются в ассистенте с задержкой. 10. **YAML-nav**: заголовок сервиса со спецсимволами (`[DEPRECATED]`) без кавычек ломает сборку — исправлено в генераторе (`yamlScalar()`), но помнить. 11. **`npm` в окружении нет** — только `node`. --- ## 11. ИНСТРУКЦИИ ДЛЯ НОВОГО ЧАТА ### 11.1. Порядок действий в начале 1. Прочитать этот файл целиком. 2. Прочитать `tf_docs/llm_project/docs/ASSISTANT_ARCHITECTURE.md` и `…/ROADMAP_AND_LIMITS.md`. 3. Проверить `git status` в `tf_docs` и `tf_provider` — не остались ли незакоммиченные правки. 4. **Ничего не менять**, пока пользователь не дал прямую команду. ### 11.2. Чего делать НЕЛЬЗЯ (повтор, критично) - править код / коммитить / пушить / деплоить / откатывать без **прямой** команды; - `terraform apply` / `destroy` — **никогда**; - удалять инстансы в ЛК (даже `not created`) — только владелец; - логировать или печатать `LLM_API_KEY` и токены; - спрашивать то, что можно узнать из файлов/git/API; - расширять область задачи («раз уж правлю, сделаю ещё X»). ### 11.3. Типовые команды ```bash # --- backend: проверка синтаксиса и правки --- node -c /home/naeel/TF/tf_docs/search.js # --- прогон тестов (любой набор, любой стенд) --- cd /home/naeel/TF/tf_docs node llm_project/tests/run_e2e_live.js --stand nubes-test \ --file golden/golden_questions_300.json --out RESULTS_300 node llm_project/tests/run_e2e_live.js --stand nubes-test # встроенные 62 # --- redeploy сервиса --- node ~/TF/tf_docs/llm_project/tests/redeploy.js # --- публикация документации + зеркало на ВМ 213 --- cd ~/TF/tf_provider DOCS_SKIP_LINT=1 bash TOOLS/scripts/04_build_and_publish_docs.sh --profile TOOLS/config/test ssh -i ~/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213 \ "mc mirror --overwrite --remove nubes/terraform-registry/docs/nubes-test/nubes/ /var/www/tf-docs/nubes-test/" # --- синк ~/TF на ВМ 213 --- ~/HowTo/sync-tf-to-vm213.sh --dry-run ``` ### 11.4. Живая проверка эндпоинта без расхода токенов LLM ```bash # 415 => ветка ассистента жива; 200 у search_index.json => статика/индекс на месте curl -s -o /dev/null -w "%{http_code}\n" -X POST \ https://tf-docs.nodejsk8s.dev.nubes.ru/nubes-test/_api/assist \ -H 'Content-Type: text/plain' --data 'x' -H 'User-Agent: Mozilla/5.0' ``` **Внимание:** реальный `POST` с `application/json` вызывает **два вызова LLM** и тратит деньги. Тестовые прогоны согласовывать с пользователем. ### 11.5. Что, вероятно, попросят дальше (по итогам сессии) - Править поиск (морфология / буст заголовка) и этап 1 (few-shot) — список в §8. - Поправить разметку 300-вопросного набора (№184, №213). - Привести в порядок ссылки на `~/HowTo/` в документации. - После получения реальных отзывов DevOps — решение об эмбеддингах и сборе данных. --- ## 12. Состояние на момент передачи | Что | Состояние | |---|---| | Репозиторий `tf_docs` | Всё закоммичено, HEAD `5ab9274`; незакоммиченного нет | | Репозиторий `tf_provider` | Последний коммит этой сессии `d52ae10` (10-07 21:05); позже (10-09, 10-10) были **другие** работы — см. `git log` | | Стенды | test/dev/prod — ассистент отвечает (проверено GET/POST 415, 2026-10-12) | | v1 | **Заморожен**, ждём реальных откликов | | 300-вопросный прогон | Завершён, результаты закоммичены, правки **не начаты** (приказ: «пока НИЧЕГО не правь») | | Открытые вопросы к пользователю | нет |