607 lines
41 KiB
Markdown
607 lines
41 KiB
Markdown
# 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-вопросный прогон | Завершён, результаты закоммичены, правки **не начаты** (приказ: «пока НИЧЕГО не правь») |
|
||
| Открытые вопросы к пользователю | нет |
|