Files
tf_docs/llm_project/docs/SESSION_HANDOFF.md
T

607 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SESSION HANDOFF — AI-ассистент документации Nubes (чат 2026-10-07 … 2026-10-08)
> **Для кого этот файл.** Это полный отчёт-передача для **нового чата** (новая
> сессия / другой агент). Читать целиком перед первой правкой.
> Составлен 2026-10-12 по транскрипту сессии, git-истории и живым проверкам.
> Источник фактов указывается в виде `файл:строка` или `git:<hash>`.
---
## 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 /<stand>/_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 | Описание — раскрывающийся блок `<details>/<summary>` | `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 /<stand>/_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 /<stand>/_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/<stand>/search/search_index.json` (ВМ 213,
823 документа на момент прогона).
### 5.2. Контракт API
**Запрос:** `POST /<stand>/_api/assist`, `Content-Type: application/json`,
тело `{"question": "..."}` ≤ 2 КБ.
**Ответ 200:**
```json
{
"answer": "Роль/Задача/Контекст/Требования/Ограничения",
"sources": [{"title": "...", "url": "/<stand>/..."}],
"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 /<stand>/_api/assist` с `Content-Type: text/plain`
отвечает **415** на всех трёх стендах (ветка ассистента жива), статика
`/<stand>/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/<стенд>/<svc>/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/<ns>/` |
| Ключ 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-вопросный прогон | Завершён, результаты закоммичены, правки **не начаты** (приказ: «пока НИЧЕГО не правь») |
| Открытые вопросы к пользователю | нет |