diff --git a/HISTORY/SONNET/docs_improvement_answer.md b/HISTORY/SONNET/docs_improvement_answer.md new file mode 100644 index 0000000..2de1d30 --- /dev/null +++ b/HISTORY/SONNET/docs_improvement_answer.md @@ -0,0 +1,36 @@ +# Sonnet: ответ по улучшению документации + +## Принцип: «User Journey First» — 4 сценария + +A. «Хочу задеплоить» → описание (30s) → минимальный пример (1m) → apply +B. «Хочу настроить параметр» → справка с читаемыми constraints +C. «Хочу использовать output» → outputs + HCL-примеры +D. «Хочу modify/restart/suspend» → страница операций + +## Изменения по 3 слоям + +### Слой 1: LLM-промпт — P1 (макс. польза, 0 компиляции) +- Раскрывать `value_list` → «Допустимые значения: 1, 3, 5, 7» +- Раскрывать `regex` → «Формат: cron» +- Заполнять пустые описания из MAN +- Группы (clusterConfiguration) — 1 предложение из MAN +- Операции — заполнять «—» из MAN +- Заменять TODO в примерах на реальные значения + +### Слой 2: docs-generator (Go) — P2 +- Убрать колонку ID +- value_list → читаемый текст +- Двойной пример: минимальный (15 строк) + полный в
+- Секция «Быстрый старт» перед MAN + +### Слой 3: mkdocs — P3 +- Sidebar по категориям (Базы данных / K8s / Хранилище / ...) +- Хлебные крошки +- Убрать inline-навигацию (заменяет sidebar) +- Back/Next кнопки + +## Порядок реализации +1. LLM промпт — мгновенный эффект на все 34 сервиса +2. renderParamTable — убрать ID, раскрыть constraints +3. buildExamplePage — двойной пример +4. mkdocs nav — категории в sidebar diff --git a/HISTORY/SONNET/docs_improvement_briefing.md b/HISTORY/SONNET/docs_improvement_briefing.md new file mode 100644 index 0000000..cf5603f --- /dev/null +++ b/HISTORY/SONNET/docs_improvement_briefing.md @@ -0,0 +1,130 @@ +# Sonnet Briefing: анализ и улучшение документации провайдера + +Цель: изучить КАЖДЫЙ шаг генерации документации и предложить конкретные улучшения, +чтобы пользователю было понятно и удобно работать с каждым ресурсом. + +--- + +## ⛔ Файлы — ПРОЧИТАТЬ ОБЯЗАТЕЛЬНО ВСЕ + +### Генераторы + +| # | Файл | Что смотреть | +|---|------|-------------| +| 1 | `TOOLS/docs-generator/main.go` | весь main — как вызывается, какие флаги | +| 2 | `TOOLS/docs-generator/internal/writers/writers.go` | ВСЕ функции. Особенно: `buildCreateParamsPage`, `buildModifyParamsPage`, `buildOutputsPage`, `buildExamplePage`, `buildManualPage`, `renderParamTable`, `renderNestedParams`, `htmlToMarkdown`, `formatParamOrBlock` | +| 3 | `TOOLS/docs-generator/internal/types/` | структуры YAML-спеки | + +### LLM-обработка + +| # | Файл | Что смотреть | +|---|------|-------------| +| 4 | `TOOLS/scripts/05_generate_docs_llm.py` | весь скрипт — как вызывается LLM, промпт, как парсится ответ | +| 5 | `docs/LLM_DOCS_GENERATION.md` | архитектура, правила для LLM | + +### Результаты генерации (примеры) + +| # | Файл | Что смотреть | +|---|------|-------------| +| 6 | `generated/test/docs/postgres_params_create.md` | RAW-вывод docs-generator (без LLM) | +| 7 | `generated/test/docs_llm/90_postgres.md` | ПОСЛЕ LLM-обработки | +| 8 | `generated/test/docs/postgres.md` | главная страница (MAN) | +| 9 | `generated/test/docs/postgres_example.md` | HCL пример | +| 10 | `generated/test/docs/postgres_outputs.md` | выходные параметры | +| 11 | `generated/test/docs/postgres_ops.md` | список операций | + +--- + +## Текущий пайплайн (3 шага) + +``` +YAML-спеки + → docs-generator (Go) → raw .md с HTML-таблицами + → LLM (gpt-oss-120b, по одному файлу) → улучшенные .md + → mkdocs-material → статический сайт → S3 +``` + +--- + +## Что видит пользователь СЕЙЧАС (пример: postgres_params_create.md) + +### До LLM (raw): +```html + + +``` +- Колонка ID (техническая, пользователю не нужна) +- Английские заголовки (Code, Description, Constraints) +- Пустые ячейки Description +- Raw `value_list=` в Constraints + +### После LLM: +``` +| clusterConfiguration | map-fixed | да | — | — | +``` +- Русские заголовки ✅ +- ID убран ✅ +- Но: пустые Description (—), value_list не раскрыт + +--- + +## Проблемы (что нужно улучшить) + +### 1. Пустые описания параметров +Многие параметры в выводе имеют `—` в колонке «Описание». Если сервис предоставил `descr` или `man` в YAML — он должен быть в документации. + +### 2. Технические колонки +- Колонка «Constraints» показывает `value_list=1, 3, 5, 7` вместо читаемого «Допустимые значения: 1, 3, 5, 7» +- Колонка «Default» показывает пустую строку вместо «нет» или «—» +- ID параметров виден в raw-версии, но нужен ли он вообще? + +### 3. MAN-секция (service_man) +Это HTML-строка с полным руководством от облачного провайдера. Она обрабатывается `htmlToMarkdown()` — regex-заменами. Часто результат нечитаемый: сломанные списки, потерянные ссылки, HTML-мусор. + +### 4. HCL-примеры +`buildExamplePage` генерирует пример с ВСЕМИ параметрами (required + default). Это гигантский манифест на 100+ строк. Может, показывать сначала минимальный working example, а полный — отдельно? + +### 5. Навигация +На каждой странице — строка навигации из 6 ссылок. Занимает место, дублируется. Может, сделать сайдбар или хлебные крошки? + +### 6. LLM-промпт (05_generate_docs_llm.py) +Промпт просит «улучшить формулировки», но: +- Не просит раскрывать value_list в читаемый вид +- Не просит добавлять «почему» и «зачем» к параметрам +- Не использует `service_man` как дополнительный контекст для обогащения описаний +- Обрабатывает по одному файлу — теряет контекст между страницами + +--- + +## Вопросы + +### Q1: Структура страниц +Текущая: Manual | Create params | Modify params | Outputs | Ops | Example. +Удобно ли это? Что переставить/добавить/убрать? Может, всё на одной странице с якорями? + +### Q2: HTML-таблицы vs Markdown-таблицы +Сейчас raw — HTML, LLM конвертирует в Markdown-таблицы. Оставить Markdown? Или HTML-таблицы лучше (CSS, выравнивание)? + +### Q3: LLM-промпт +Как улучшить промпт чтобы: +- description параметров наполнялся из `service_man` где возможно +- value_list показывался читаемо +- empty cells говорили «не указано» а не «—» + +### Q4: HCL-примеры +Минимальный пример + полный? Или только минимальный? Или только полный? + +### Q5: Постраничная vs одностраничная документация +7 .md файлов на сервис. Это норм или перебор? Может, генерировать один README.md на сервис со всем внутри? + +### Q6: Что ещё можно улучшить для UX? +Посмотри на любые 2-3 страницы из `generated/test/docs_llm/` и скажи: что непонятно, что раздражает, чего не хватает. + +--- + +## Ожидаемый ответ + +1. Анализ текущего состояния: что хорошо, что плохо (с конкретными примерами из файлов) +2. Конкретные предложения по каждому из 6 вопросов +3. Unified diff предлагаемых изменений в writers.go и 05_generate_docs_llm.py +4. Пример одной страницы «как должно быть» для postgres (хотя бы params_create) diff --git a/HISTORY/SONNET/docs_improvement_final.md b/HISTORY/SONNET/docs_improvement_final.md new file mode 100644 index 0000000..a06f431 --- /dev/null +++ b/HISTORY/SONNET/docs_improvement_final.md @@ -0,0 +1,50 @@ +# Документация — полный диалог и финальный план + +## Ответы на 3 вопроса Соннета + +### 1. Inline-навигацию убирать? +**Убирать, но сначала добавить страницы в sidebar.** +Сейчас ресурсные страницы не в `mkdocs.yml nav:` — они сироты. Inline nav — единственная навигация. +Порядок: сначала P3 (добавить в sidebar через `_nav_fragment.yml`), потом убрать inline из writers.go. + +### 2. Минимальный пример — только required без default? +**Да.** Параметр с дефолтом и так сработает без указания. Минимальный пример = required=true И default пустой. +15 строк вместо 100. + +### 3. Категории для sidebar +Группировка по 7 категориям: + +| Категория | Сервисы | +|---|---| +| Базы данных | postgres, redis, mongodb, mariadb, clickhouse | +| Очереди | rabbitmq, kafka | +| Хранилище | s3, s3bucket, nextcloud | +| K8s | k8s_velero, k8s_sthutrval_cluster, k8s_openbao, vc_mgmt_sthutrval_cluster | +| VMware | vc_org, vc_vdc, vc_nsxt, vcexternalip, vapp, vc_vm_v2, vc_vm_v3, vc_vdc_group | +| Приложения | flask, nodejs, lucee, http, gitea, superset, pgadmin, harbor, akhq, llm_ai | +| Сеть | zones_v2, dnsrecord | + +--- + +## Финальный план (3 слоя) + +### P1: LLM-промпт (05_generate_docs_llm.py) — 0 компиляции, 34 сервиса +6 инструкций: +- value_list → «Допустимые значения: X, Y, Z» +- regex → «Формат: cron / UUID / IP» +- Описания групп из MAN +- Пустые описания заполнять +- Операции без «—» +- TODO в примерах → реальные значения + +### P2: docs-generator (writers.go) +- renderParamTable: убрать ID, value_list → читаемый текст +- buildExamplePage: минимальный пример + полный в
+ +### P3: mkdocs навигация +- docs-generator генерирует _nav_fragment.yml с категориями +- 04_build_and_publish_docs.sh вставляет его в mkdocs.yml +- writers.go: убрать inline nav +- mkdocs.yml: breadcrumbs + prev/next + +### Порядок: P1 → P2 → P3 diff --git a/HISTORY/SONNET/docs_p1_approved.md b/HISTORY/SONNET/docs_p1_approved.md new file mode 100644 index 0000000..bc1c1c0 --- /dev/null +++ b/HISTORY/SONNET/docs_p1_approved.md @@ -0,0 +1,32 @@ +# Документация — финальный план P1 (утверждён) + +Дата: 2026-08-09 +Источник: Sonnet, после серии брифов и уточнений + +## Что меняется в 05_generate_docs_llm.py + +### 1. SYSTEM_PROMPT — замена +Новый промпт с правилами A-E (см. HISTORY/SONNET/docs_prompt_full_response.md) + +### 2. max_tokens: 4096 → 8192 + +### 3. Новая функция extract_man(text) → str +Вырезает блок ## MAN из Name.md. Используется как контекст для params/ops. + +### 4. Новая функция build_prompt(file_type, filename, content, man) → str +Формирует сообщение для LLM: тип файла + MAN-контекст + содержимое. + +### 5. Обработка ВСЕХ типов файлов +Было: только Name.md +Стало: Name.md, _params_create.md, _params_modify.md, _ops.md, _example.md +MAN-контекст: для params и ops, без MAN для главной и примеров. + +### 6. Копирование в docs_llm/ +- outputs, params-landing, subresource — копировать as-is +- 30_registry/, guides/, index.md — копировать из docs/ + +### Решения +- Subresource: копировать as-is (не через LLM) +- max_tokens: 8192 +- Вывод: docs_llm/ +- 30_registry копировать в самом скрипте diff --git a/HISTORY/SONNET/docs_p1_final.md b/HISTORY/SONNET/docs_p1_final.md new file mode 100644 index 0000000..62a8a07 --- /dev/null +++ b/HISTORY/SONNET/docs_p1_final.md @@ -0,0 +1,35 @@ +# Sonnet: финальный план P1 — 05_generate_docs_llm.py + +## Пайплайн на один сервис + +``` +docs/Name.md ─┐ +docs/Name_params_create.md ─┤ LLM → docs_llm/Name.md +docs/Name_params_modify.md ─┤ docs_llm/Name_params_create.md +docs/Name_ops.md ─┤ docs_llm/Name_params_modify.md +docs/Name_example.md ─┘ docs_llm/Name_ops.md + docs_llm/Name_example.md + +docs/Name_outputs.md ─── copy → docs_llm/Name_outputs.md +docs/Name_params.md ─── copy → docs_llm/Name_params.md +docs/Name_subresource*.md ─── copy → docs_llm/Name_subresource*.md +``` + +## Ключевые детали + +### MAN-контекст +- Извлекается из `docs/Name.md` (raw HTML) функцией `extract_man()` +- Передаётся в том же сообщении что и params/ops файлы +- Для главной страницы (Name.md) и примеров (_example.md) — без MAN + +### Параметры LLM +- `max_tokens`: 4096 → **8192** +- `temperature`: 0.15 (без изменений) +- `model`: gpt-oss-120b (без изменений) + +### Интеграция с 04_build_and_publish_docs.sh +Вариант A: DOCS_GEN_DIR → `docs_llm/`. Скрипт копирует guides/, 30_registry/, index.md в docs_llm/. + +## Вопрос: subresource-страницы обрабатывать LLM? +postgres_user.md, postgres_database.md, postgres_backup.md — их структура как у _params_create.md. +Пока копировать as-is или тоже через LLM? diff --git a/HISTORY/SONNET/docs_prompt_answer.md b/HISTORY/SONNET/docs_prompt_answer.md new file mode 100644 index 0000000..9d9b295 --- /dev/null +++ b/HISTORY/SONNET/docs_prompt_answer.md @@ -0,0 +1,38 @@ +# Sonnet: ответы — готовый SYSTEM_PROMPT и механизм MAN→группы + +## Вопрос 2: MAN → группы + +Явный маппинг НЕ нужен. LLM делает семантический матч: +- Видит группу `clusterConfiguration` с sub-params `cpu, memory, disk, replicas` +- Видит в MAN: «Квота (millicore) ядра пода... Квота памяти... Размер диска... Количество узлов» +- Сопоставляет по смыслу → «Ресурсы пода кластера: CPU, RAM, диск, реплики» + +Условие: MAN в том же сообщении, что и params-файл. + +## Готовый SYSTEM_PROMPT + +См. полный текст с правилами A-E: +- A: удалить колонку ID +- B: value_list → «Допустимые значения: X, Y, Z» +- C: regex → читаемый формат +- D: пустые описания → заполнить из MAN +- E: группы (map-fixed) → 1 предложение о содержимом + ++ правила для _ops.md, _example.md, главной страницы (MAN) + +## Новая логика вызова LLM + +MAN передаётся как контекст в том же сообщении что и params-файл: +``` +Тип файла: _params_create +Сервис: postgres +=== MAN СЕРВИСА === +{текст MAN из Name.md} +=== Файл === +{содержимое} +``` + +## 2 вопроса + +1. max_tokens 4096 → 8192? (params для postgres ~4KB HTML) +2. Писать в docs_llm/ или сразу на место? diff --git a/HISTORY/SONNET/docs_prompt_full_response.md b/HISTORY/SONNET/docs_prompt_full_response.md new file mode 100644 index 0000000..6abf4ea --- /dev/null +++ b/HISTORY/SONNET/docs_prompt_full_response.md @@ -0,0 +1,162 @@ +# Sonnet: ПОЛНЫЙ ответ — готовый SYSTEM_PROMPT + механизм MAN→группы + +## Вопрос 2: механизм маппинга MAN → группы + +**Явный маппинг не нужен.** LLM делает его сам через семантику. + +Как это работает для `clusterConfiguration`: + +``` +LLM видит в _params_create.md: + группа: clusterConfiguration (map-fixed) + sub-params: cpu, memory, disk, replicas + +LLM видит в MAN (в том же сообщении): + «Квота (millicore) ядра пода... Квота (megabyte) памяти... + Размер диска... Количество узлов (реплик)...» + +LLM выводит: + → «Ресурсы пода кластера: CPU (milicores), RAM (MB), диск (GB), реплики» +``` + +**Страховка**: группы без секции в MAN (`autoscaleConfiguration`) — LLM работает только по именам sub-params: `enabled`, `percent`, `quota`, `schedule` → «Автомасштабирование PV: расширяет диск на заданный процент при заполнении». + +**Условие**: MAN передаётся в **том же сообщении**, что и params-файл, а не отдельно. + +--- + +## Полный SYSTEM_PROMPT + +```python +SYSTEM_PROMPT = """Ты — технический писатель Nubes Terraform Provider. +Улучшаешь автогенерированные Markdown-файлы документации. + +═══════════════════════════════════════════════════════ +АБСОЛЮТНЫЕ ЗАПРЕТЫ +═══════════════════════════════════════════════════════ +- НЕ выдумывай имена параметров, типы, значения по умолчанию +- НЕ трогай HCL-блоки (всё внутри ```hcl ... ```) +- НЕ трогай имена параметров в таблицах (snake_case / camelCase из API) +- НЕ трогай навигационные строки вида [Manual](x.md) · [Create params](y.md) ... +- НЕ добавляй и не удаляй строки/колонки в таблицах +- Верни ТОЛЬКО готовый текст файла. Без объяснений, без``` вокруг всего текста + +═══════════════════════════════════════════════════════ +ПРАВИЛА ДЛЯ ТАБЛИЦ ПАРАМЕТРОВ (_params_create.md, _params_modify.md) +═══════════════════════════════════════════════════════ +Таблицы содержат столбцы: ID | Code | Type | Required | Default | Description | Constraints +Можно менять ТОЛЬКО текст в
и . + +ПРАВИЛО A — удали колонку ID: + Удали из заголовка и соответствующий первый из каждой строки. + +ПРАВИЛО B — value_list в Constraints: + value_list=1, 3, 5, 7 → очисти ячейку Constraints до пустой. + В ячейку Description добавь строку: «Допустимые значения: **1, 3, 5, 7**» + Если в Description уже был текст — добавь после него, через пробел или перевод строки (
). + +ПРАВИЛО C — regex в Constraints: + Замени regex-строку на читаемое описание формата: + - cron-подобный regex → «Формат: cron-выражение. Пример: `0 0 * * *`» + - UUID regex → «Формат: UUID» + - IP-адрес regex → «Формат: IP-адрес» + - Прочее → кратко опиши формат своими словами + +ПРАВИЛО D — пустое Description (пустая ячейка или —): + Напиши краткое описание параметра (1–2 предложения). Приоритет источников: + 1. MAN — ищи текст, связанный с параметром по смыслу и по именам sub-params + 2. Имя параметра snake_case → понятный русский + 3. Тип и контекст соседних параметров в группе + +ПРАВИЛО E — верхнеуровневые группы (строки с map-fixed или array-map-fixed): + Эти строки — контейнеры, в них вложены sub-params. + Если Description пустое — напиши 1 предложение: что содержит группа и зачем. + Смотри на имена sub-params (они идут в следующих строках) + MAN. + Пример: clusterConfiguration с sub-params cpu/memory/disk/replicas + → «Ресурсы пода кластера: CPU (milicores), RAM (MB), диск (GB) и количество реплик» + Пример: backupConfiguration с sub-params s3_uid/retain/schedule + → «Параметры резервного копирования: S3-хранилище, расписание и глубина хранения» + +═══════════════════════════════════════════════════════ +ПРАВИЛА ДЛЯ ОПЕРАЦИЙ (_ops.md) +═══════════════════════════════════════════════════════ +Операции без описания (пустая строка, нет текста после —): + Напиши 1 предложение о том, что делает операция с ресурсом. + Используй MAN если передан. Не придумывай параметров. + + Универсальные шаблоны (если MAN не помогает): + suspend → «Приостановка ресурса (поды остановлены, данные сохранены)» + resume → «Запуск ранее остановленного ресурса» + restart → «Перезапуск подов ресурса. ⚠️ Возможна кратковременная недоступность» + reconcile → «Принудительная синхронизация состояния с API» + recovery → «Восстановление из резервной копии» + +═══════════════════════════════════════════════════════ +ПРАВИЛА ДЛЯ ГЛАВНОЙ СТРАНИЦЫ (Name.md — секция ## MAN) +═══════════════════════════════════════════════════════ +Блок ## MAN содержит HTML внутри
. +Преобразуй HTML → читаемый Markdown: +

/

→ ## / ### +
  • → - элемент списка + → **текст** + → `текст` + текст → [текст](url) +
    ,

    ,

    → удали тег, замени переносами строк где нужно + Лишние пустые строки подряд → одна пустая строка + +Сохраняй всё смысловое содержание. Не перефразируй, не сокращай. + +═══════════════════════════════════════════════════════ +ПРАВИЛА ДЛЯ ПРИМЕРОВ (_example.md) +═══════════════════════════════════════════════════════ +Строки с TODO — замени на типичный реальный пример если он предсказуем: + resource_name = "TODO" → "my-postgres" + resource_realm = "TODO" → "k8s-3-sandbox-nubes-ru" # укажите ваш кластер + master_ip_space = "TODO" → "internet-no-antiddos-v1" # из вашей организации + slave_ip_space = "TODO" → "internet-no-antiddos-v1" # из вашей организации + +Оставь TODO если значение непредсказуемо (UUID чужого ресурса): + s3_uid = "TODO" → s3_uid = "TODO" # UUID ресурса nubes_s3 из state: nubes_s3.backup_store.id +""" +``` + +--- + +## Новая логика USER-сообщения + +```python +# При обработке _params_create / _params_modify / _ops / _example: +man_text = extract_man_section(service_main_md) # берём ## MAN из Name.md + +prompt = f"""Тип файла: {file_type} +Сервис: {service_name} + +=== MAN СЕРВИСА (контекст для описаний групп и параметров) === +{man_text} + +=== Файл для улучшения: {filename} === +{file_content} +""" + +# При обработке Name.md (главная): +prompt = f"""Тип файла: ГЛАВНАЯ СТРАНИЦА +Сервис: {service_name} + +=== Файл для улучшения: {filename} === +{file_content} +""" +``` + +--- + +## 2 вопроса от Соннета + +1. `max_tokens` сейчас `4096` — но `_params_create.md` для postgres ~4KB HTML. Поднять до `8192`? +2. Писать в `docs_llm/` или сразу на место? + +--- + +## Ответы + +1. **max_tokens = 8192** — да. После обогащения описаниями файл станет больше. +2. **Писать в `docs_llm/`** — не затирать сырой вывод docs-generator, нужен для отладки. diff --git a/TOOLS/scripts/05_generate_docs_llm.py b/TOOLS/scripts/05_generate_docs_llm.py index b8a431d..4b9eaef 100644 --- a/TOOLS/scripts/05_generate_docs_llm.py +++ b/TOOLS/scripts/05_generate_docs_llm.py @@ -5,9 +5,9 @@ LLM-улучшатель документации Nubes Terraform Provider. чтобы сделать описания читаемыми и логичными. Использование: - python3 05_generate_docs_llm.py generated/test/docs + python3 05_generate_docs_llm.py generated/test/docs [--out generated/test/docs_llm] """ -import json, os, sys, time +import json, os, re, shutil, sys, time from pathlib import Path from urllib.request import Request, urlopen @@ -15,16 +15,94 @@ API_URL = "https://api.aillm.ru/v1/chat/completions" API_KEY = "sk-ucI5YvOticoOQ9Kuj5K9mQ" MODEL = "gpt-oss-120b" -SYSTEM_PROMPT = """Ты — технический писатель. Улучши ОДИН .md файл документации Terraform-провайдера: сделай описания грамотными и логичными. +SYSTEM_PROMPT = """Ты — технический писатель Nubes Terraform Provider. +Улучшаешь автогенерированные Markdown-файлы документации. + +═══════════════════════════════════════════════════════ +АБСОЛЮТНЫЕ ЗАПРЕТЫ +═══════════════════════════════════════════════════════ +- НЕ выдумывай имена параметров, типы, значения по умолчанию +- НЕ трогай HCL-блоки (всё внутри ```hcl ... ```) +- НЕ трогай имена параметров в таблицах (snake_case / camelCase из API) +- НЕ трогай навигационные строки вида [Manual](x.md) · [Create params](y.md) ... +- НЕ добавляй и не удаляй строки/колонки в таблицах +- Верни ТОЛЬКО готовый текст файла. Без объяснений, без``` вокруг всего текста + +═══════════════════════════════════════════════════════ +ПРАВИЛА ДЛЯ ТАБЛИЦ ПАРАМЕТРОВ (_params_create.md, _params_modify.md) +═══════════════════════════════════════════════════════ +Таблицы содержат столбцы: ID | Code | Type | Required | Default | Description | Constraints +Можно менять ТОЛЬКО текст в

и . + +ПРАВИЛО A — value_list в Constraints: + value_list=1, 3, 5, 7 → очисти ячейку Constraints до пустой. + В ячейку Description добавь строку: «Допустимые значения: **1, 3, 5, 7**» + Если в Description уже был текст — добавь после него, через пробел или перевод строки (
). + +ПРАВИЛО B — regex в Constraints: + Замени regex-строку на читаемое описание формата: + - cron-подобный regex → «Формат: cron-выражение. Пример: `0 0 * * *`» + - UUID regex → «Формат: UUID» + - IP-адрес regex → «Формат: IP-адрес» + - Прочее → кратко опиши формат своими словами + +ПРАВИЛО C — пустое Description (пустая ячейка или —): + Напиши краткое описание параметра (1–2 предложения). Приоритет источников: + 1. MAN — ищи текст, связанный с параметром по смыслу и по именам sub-params + 2. Имя параметра snake_case → понятный русский + 3. Тип и контекст соседних параметров в группе + +ПРАВИЛО D — верхнеуровневые группы (строки с map-fixed или array-map-fixed): + Эти строки — контейнеры, в них вложены sub-params. + Если Description пустое — напиши 1 предложение: что содержит группа и зачем. + Смотри на имена sub-params (они идут в следующих строках) + MAN. + Пример: clusterConfiguration с sub-params cpu/memory/disk/replicas + → «Ресурсы пода кластера: CPU (milicores), RAM (MB), диск (GB) и количество реплик» + Пример: backupConfiguration с sub-params s3_uid/retain/schedule + → «Параметры резервного копирования: S3-хранилище, расписание и глубина хранения» + +═══════════════════════════════════════════════════════ +ПРАВИЛА ДЛЯ ОПЕРАЦИЙ (_ops.md) +═══════════════════════════════════════════════════════ +Операции без описания (пустая строка, нет текста после —): + Напиши 1 предложение о том, что делает операция с ресурсом. + Используй MAN если передан. Не придумывай параметров. + + Универсальные шаблоны (если MAN не помогает): + suspend → «Приостановка ресурса (поды остановлены, данные сохранены)» + resume → «Запуск ранее остановленного ресурса» + restart → «Перезапуск подов ресурса. ⚠️ Возможна кратковременная недоступность» + reconcile → «Принудительная синхронизация состояния с API» + recovery → «Восстановление из резервной копии» + +═══════════════════════════════════════════════════════ +ПРАВИЛА ДЛЯ ГЛАВНОЙ СТРАНИЦЫ (Name.md — секция ## MAN) +═══════════════════════════════════════════════════════ +Блок ## MAN содержит HTML внутри
. +Преобразуй HTML → читаемый Markdown: +

/

→ ## / ### +
  • → - элемент списка + → **текст** + → `текст` + текст → [текст](url) +
    ,

    ,

    → удали тег, замени переносами строк где нужно + Лишние пустые строки подряд → одна пустая строка + +Сохраняй всё смысловое содержание. Не перефразируй, не сокращай. + +═══════════════════════════════════════════════════════ +ПРАВИЛА ДЛЯ ПРИМЕРОВ (_example.md) +═══════════════════════════════════════════════════════ +Строки с TODO — замени на типичный реальный пример если он предсказуем: + resource_name = "TODO" → "my-postgres" + resource_realm = "TODO" → "k8s-3-sandbox-nubes-ru" # укажите ваш кластер + master_ip_space = "TODO" → "internet-no-antiddos-v1" # из вашей организации + slave_ip_space = "TODO" → "internet-no-antiddos-v1" # из вашей организации + +Оставь TODO если значение непредсказуемо (UUID чужого ресурса): + s3_uid = "TODO" → s3_uid = "TODO" # UUID ресурса nubes_s3 из state: nubes_s3.backup_store.id +""" -ПРАВИЛА: -1. НЕ ВЫДУМЫВАЙ параметры, типы, значения. Только улучшай формулировки. -2. HTML-таблицы — меняй ТОЛЬКО текст внутри

. НЕ ломай теги. -3. HCL-блоки (```hcl ... ```) — НЕ ТРОГАТЬ вообще. -4. Navigation-строки — НЕ ТРОГАТЬ. -5. MAN-секцию с HTML — переведи в читаемый Markdown. -6. Верни ТОЛЬКО улучшенный Markdown. Без JSON, без пояснений, без ``` в начале/конце. - Просто готовый текст файла.""" def call_llm(prompt: str) -> str: data = json.dumps({ @@ -34,7 +112,7 @@ def call_llm(prompt: str) -> str: {"role": "user", "content": prompt}, ], "temperature": 0.15, - "max_tokens": 4096, + "max_tokens": 8192, }).encode() req = Request(API_URL, data=data, headers={ "Authorization": f"Bearer {API_KEY}", @@ -45,7 +123,6 @@ def call_llm(prompt: str) -> str: with urlopen(req, timeout=120) as resp: result = json.loads(resp.read()) content = result["choices"][0]["message"]["content"].strip() - # Strip markdown fences if present if content.startswith("```"): lines = content.split("\n") if len(lines) > 2: @@ -56,61 +133,149 @@ def call_llm(prompt: str) -> str: time.sleep(5) raise RuntimeError("LLM failed after 3 retries") + +def extract_man(text: str) -> str: + """Вырезает блок ## MAN из главной страницы.""" + m = re.search(r'## MAN\s*\n(.*?)(?=\n## |\Z)', text, re.DOTALL) + return m.group(1).strip() if m else "" + + +def build_prompt(file_type: str, filename: str, content: str, man: str = "") -> str: + """Формирует промпт для LLM с типом файла и опциональным MAN-контекстом.""" + parts = [f"Тип файла: {file_type}"] + if man: + parts.append(f"\n=== MAN СЕРВИСА (контекст для описаний групп и параметров) ===\n{man}") + parts.append(f"\n=== {filename} ===\n{content}") + return "\n".join(parts) + + +def file_type(name: str) -> str: + """Определяет тип файла по имени.""" + if name.endswith("_params_create"): + return "ПАРАМЕТРЫ СОЗДАНИЯ" + if name.endswith("_params_modify"): + return "ПАРАМЕТРЫ ИЗМЕНЕНИЯ" + if name.endswith("_ops"): + return "ОПЕРАЦИИ" + if name.endswith("_example"): + return "ПРИМЕР" + return "ГЛАВНАЯ СТРАНИЦА" + + +def needs_man_context(name: str) -> bool: + """Нужен ли MAN-контекст для этого типа файла.""" + return any(name.endswith(s) for s in ("_params_create", "_params_modify", "_ops")) + + +def is_llm_target(name: str) -> bool: + """Файлы, которые обрабатываются через LLM.""" + return any(name.endswith(s) for s in ("_params_create", "_params_modify", "_ops", "_example")) or ( + "_" not in name and name != "index" + ) + + def main(): docs_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("generated/test/docs") if not docs_dir.exists(): print(f"ERROR: {docs_dir} not found", file=sys.stderr) sys.exit(1) - # Process only main manual pages: Name.md (not _example, _params_*, _outputs, _ops) + # Определяем выходную директорию + out_dir = docs_dir + for i, arg in enumerate(sys.argv): + if arg == "--out" and i + 1 < len(sys.argv): + out_dir = Path(sys.argv[i + 1]) + break + out_dir.mkdir(parents=True, exist_ok=True) + + # Находим все сервисы (по главным страницам без суффиксов и без subresource) all_md = sorted(docs_dir.glob("*.md")) - targets = [] + services = set() for f in all_md: name = f.stem if name == "index": continue - # Skip known non-manual suffixes - if any(name.endswith(sfx) for sfx in ["_example", "_params_create", "_params_modify", "_outputs", "_ops", "_params"]): - continue - # Also skip second-level (subresource) example files - if "_" in name: - # Could be subresource manual like "postgres_database" - # Check if there's a matching _example file - targets.append(f) - else: - targets.append(f) + if "_" not in name: + services.add(name) - print(f"Processing {len(targets)} manual pages...") + if not services: + print("No services found", file=sys.stderr) + sys.exit(1) + print(f"Services: {len(services)}") failed = [] - for i, f in enumerate(targets): - svc = f.stem - print(f" [{i+1}/{len(targets)}] {svc}...", end=" ", flush=True) + total = 0 - content = f.read_text() - # Truncate very long files - if len(content) > 12000: - content = content[:12000] + "\n\n... (обрезано для LLM)\n" + for svc in sorted(services): + print(f"\n--- {svc} ---") - prompt = f"Улучши этот файл документации:\n\n=== {f.name} ===\n{content}" + # Читаем MAN из главной страницы (raw docs) + main_file = docs_dir / f"{svc}.md" + man_text = "" + if main_file.exists(): + man_text = extract_man(main_file.read_text()) - try: - improved = call_llm(prompt) - if improved and len(improved) > 100: - f.write_text(improved) - print("OK") - else: - print("SKIP (empty response)") - except Exception as e: - print(f"FAILED: {e}") - failed.append(svc) + # Обрабатываем все файлы сервиса + svc_files = sorted(docs_dir.glob(f"{svc}*.md")) + for f in svc_files: + name = f.stem + if not is_llm_target(name): + # Копируем как есть: outputs, params (landing), subresource + dest = out_dir / f.name + shutil.copy2(f, dest) + continue - time.sleep(1.5) + total += 1 + print(f" {f.name}...", end=" ", flush=True) + content = f.read_text() + if len(content) > 12000: + content = content[:12000] + "\n\n... (обрезано)\n" + + ft = file_type(name) + man = man_text if needs_man_context(name) else "" + prompt = build_prompt(ft, f.name, content, man) + + try: + improved = call_llm(prompt) + if improved and len(improved) > 100: + dest = out_dir / f.name + dest.write_text(improved) + print("OK") + else: + # fallback: копируем оригинал + dest = out_dir / f.name + shutil.copy2(f, dest) + print("SKIP (empty response, copied original)") + except Exception as e: + dest = out_dir / f.name + shutil.copy2(f, dest) + print(f"FAILED: {e} (copied original)") + failed.append(f.name) + + time.sleep(1.5) + + # Копируем index.md + index_src = docs_dir / "index.md" + if index_src.exists(): + shutil.copy2(index_src, out_dir / "index.md") + + # Копируем 30_registry если есть + for sub in ["30_registry", "guides"]: + src = docs_dir.parent / sub + if not src.exists(): + src = docs_dir / sub + if src.exists(): + dest = out_dir / sub + if dest.exists(): + shutil.rmtree(dest) + shutil.copytree(src, dest) + print(f" copied {sub}/") + + print(f"\nProcessed {total} files. Failed: {len(failed)}") if failed: - print(f"\nFailed ({len(failed)}): {', '.join(failed)}") - else: - print(f"\nAll {len(targets)} OK") + print(f"Failed: {', '.join(failed)}") + if __name__ == "__main__": main()
IDCodeTypeDescriptionConstraints
788cluster_configurationmap-fixed
DescriptionConstraintsIDчислоDescriptionConstraints...