Files
tf_docs/HISTORY/2026-10-12_code_review_and_fixes.md

7.2 KiB
Raw Permalink Blame History

tf_docs: код-ревью AI-ассистента и правки (поиск подресурсов + таймаут ЛЛМ)

Дата: 2026-10-12 Коммит: 12d29ee (репозиторий tf_docs, ветка master, запушен) Контекст: по запросу — фактическое ревью кода/архитектуры ассистента с упором на ЛЛМ.

Что проверялось

Прочитан backend целиком: server.js, assist.js, search.js, stage1.js, stage2.js, orchestrator.js; сверено с llm_project/docs/ASSISTANT_ARCHITECTURE.md и результатами прогонов (llm_project/tests/RESULTS_*.json).

Найдено (факты, файл:строка на момент ревью)

  1. 🔴 search.js:206-215 — resourceBaseSegment перебирал суффиксы-подресурсы в исходном порядке, где _user стоит раньше _sub_user. Для nubes_s3_sub_user (rest=s3_sub_user) отрезался _user → base s3_sub вместо s3. Следствие: isResourcePage не находила страницу ресурса ("s3_sub_user" !== "s3"), поиск по resource_type для *_sub_user был неэффективен. Реальный ресурс (есть в golden_questions_300.json); эффект маскировался лексическим поиском по исходному вопросу.
  2. 🔴 assist.js:25 — ASSIST_LLM_TIMEOUT_MS (25 000) был объявлен в ENV и задокументирован (README, ASSISTANT_ARCHITECTURE.md:§5), но нигде не использовался: orchestrator.js не передавал timeoutMs в stage1, а stage2Generate его не принимал. Фактически работал только общий дедлайн 40 с.
  3. 🟡 orchestrator.js:205 — clientIp брал последний элемент X-Forwarded-For (ближайший хоп) вместо первого (исходный клиент). Влияет только при заданном ASSIST_TRUSTED_PROXY_CIDR (по умолчанию пусто).
  4. 🟡 orchestrator.js:317,328 — мёртвая ветка дедупа (handleAssistAgain — заглушка); в однопоточном JS недостижима.
  5. 🟢 кэш ответов без TTL/лимита (orchestrator.js:306); npm test покрывает только прокси/health (LLM-пайплайн без юнит-тестов); fetchIndex без лимита размера; FORWARD_HEADERS без etag/last-modified; при aborted отдаётся нестандартный HTTP 499.

Исправлено (2 пункта)

1. search.js — порядок суффиксов подресурсов

Массив knownSubs вынесен в KNOWN_RESOURCE_SUBS, отсортирован по убыванию длины при загрузке модуля. Проверено изолированно: nubes_s3_sub_user → s3 (было s3_sub), nubes_mariadb_sub_user → mariadb; nubes_postgres_database → postgres, nubes_postgres → postgres — без изменений.

2. assist.js + stage1.js + orchestrator.js — реальный per-call таймаут

  • llmChat получил параметр timeoutMs (дефолт ENV.ASSIST_LLM_TIMEOUT_MS) и составной сигнал: дочерний AbortController, абортящийся по таймауту ИЛИ по аборту вызывающего. Таймаут покрывает вызов целиком (в т.ч. чтение тела).
  • Таймаут отдаётся отдельным кодом llm_timeout (отмена клиентом/дедлайн — по-прежнему aborted), чтобы stage2 различал причины и корректно решал о повторе.
  • stage1.js освобождён от дублирующего AbortController.
  • orchestrator.js: llm_timeout → HTTP 504.

Проверено на локальном «зависшем» сервере: таймаут → llm_timeout (314 мс при лимите 300), abort родителя → aborted (152 мс). node -c всех модулей OK, node --test 4/4.

Проверка на живом стенде

  • redeploy инстанса tf-docs (7a48a863-…) через llm_project/tests/redeploy.js → успешно (finish=2026-10-12T07:56:44+03:00, ok=true); /health → 200.
  • Прогон 62 вопросов на nubes-test (--out RESULTS_AFTER_FIX): 62/62 — 200 OK, шаблон 62/62, ошибок 0; задержки p50 5.9 с, p95 10.8 с, max 12.4 с; llm_timeout не срабатывал ни разу.
  • ⚠️ Сравнение с прежним RESULTS_LIVE.json некорректно: тот от 2026-10-07, за 5 дней индекс документации изменился (у 45 из 62 вопросов другой набор источников) → это дрейф индекса, а не эффект правки.

Что НЕ менялось (осознанно)

Пункты 3–5 из «Найдено» (XFF, мёртвая ветка дедупа, кэш без TTL, юнит-тесты) — оставлены как есть: не мешают текущей работе; вернуться при масштабировании/переработке.

Режим «редактор файла» (добавлено в этой же сессии)

Новый эндпоинт POST /<stand>/_api/file (filemode.js): тело {content, instruction} → ответ {content, changes[], warnings[]}. LLM возвращает ПОЛНЫЙ обновлённый файл (обычно terraform.tfvars). Опирается на текстовый search_index.json; секреты сюда не кладут (отдельный secrets.auto.tfvars), логируются только размеры.

Следующий этап — требования (НЕ реализовано)

  • Схемы заливать ВМЕСТЕ с документацией. В пайплайне публикации (TOOLS/scripts/04_build_and_publish_docs.sh) после сборки site/ копировать stack/<stand>/<svc>/raw.json → site/schema/<svc>.json, чтобы mc mirror (в scripts/publish-docs.sh) залил их в S3 наравне со страницами. Бэкенд берёт схему с VM_DOCS_BASE_URL/<stand>/schema/<svc>.json. (Сделано в этой сессии.)
  • AGENTS.md для IDE-агентов — сделан в tf_provider (самодостаточный: структура raw.json, инвентарь 35 ресурсов, workflow, секреты, типичные ошибки).
  • MCP — отложен: требует ручной настройки у юзера в IDE (на этом этапе исключено); ставка на zero-config AGENTS.md + режим /_api/file.