From de121ccb3507b3e9e1113c9ad35847d22a275353 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Mon, 12 Oct 2026 07:53:00 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=BF=D0=BE=D0=BB=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=BE=D1=82=D1=87=D1=91=D1=82-=D0=BF=D0=B5=D1=80=D0=B5=D0=B4?= =?UTF-8?q?=D0=B0=D1=87=D0=B0=20=D0=BF=D0=BE=20=D1=81=D0=B5=D1=81=D1=81?= =?UTF-8?q?=D0=B8=D0=B8=20AI-=D0=B0=D1=81=D1=81=D0=B8=D1=81=D1=82=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=20(=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D1=87=D0=B0=D1=82=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- llm_project/docs/SESSION_HANDOFF.md | 606 ++++++++++++++++++++++++++++ 1 file changed, 606 insertions(+) create mode 100644 llm_project/docs/SESSION_HANDOFF.md diff --git a/llm_project/docs/SESSION_HANDOFF.md b/llm_project/docs/SESSION_HANDOFF.md new file mode 100644 index 0000000..1f1bdcb --- /dev/null +++ b/llm_project/docs/SESSION_HANDOFF.md @@ -0,0 +1,606 @@ +# 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-вопросный прогон | Завершён, результаты закоммичены, правки **не начаты** (приказ: «пока НИЧЕГО не правь») | +| Открытые вопросы к пользователю | нет |