# План: AI-анализ terraform plan (уровни 0–5) Дата: 2026-03-12 Статус: черновик / на согласование --- ## 1. Суть задачи После выполнения `terraform plan` отправлять вывод на анализ в LLM. Глубина анализа управляется переменной `ai_hint_level` (0–5) в Terraform-конфигурации. Сейчас — Google AI (Gemini API). В будущем — собственный облачный LLM (drop-in замена). --- ## 2. Уровни подсказок (`ai_hint_level`) | Уровень | Название | Что делает | |---------|----------------|-------------------------------------------------------------------| | 0 | OFF | Ничего. AI не вызывается. `terraform plan` работает как обычно. | | 1 | SYNTAX | Проверка синтаксиса: ошибки в HCL, опечатки, невалидные блоки. | | 2 | BASIC | + базовый анализ: неиспользуемые ресурсы, пустые значения. | | 3 | MODERATE | + анализ зависимостей, потенциальные конфликты, порядок apply. | | 4 | DETAILED | + best practices, безопасность (открытые порты, широкие IAM). | | 5 | FULL | Полные рекомендации: оптимизация, рефакторинг, альтернативы. | --- ## 3. Как задаётся уровень **По умолчанию AI не активен и ничего не показывает.** Если `ai_hint_level` нигде не задан — уровень 0, `sless plan` работает как обычный `terraform plan`, без единого упоминания об AI. Уровень задаётся **только явно**, одним из способов (приоритет сверху вниз): 1. Env-переменная (рекомендуется): ```bash export TF_VAR_ai_hint_level=3 ``` 2. В `variables.tf` пользовательского проекта (опционально, только если нужно зафиксировать уровень в коде): ```hcl variable "ai_hint_level" { description = "Уровень AI-анализа terraform plan (0=off, 5=full)" type = number default = 0 validation { condition = var.ai_hint_level >= 0 && var.ai_hint_level <= 5 error_message = "ai_hint_level должен быть от 0 до 5" } } ``` 3. CLI-флаг напрямую: ```bash sless plan --ai-level=3 ``` **Важно:** CLI сам читает уровень из env `TF_VAR_ai_hint_level` или флага `--ai-level`. Если ни того ни другого нет — молча применяет уровень 0. Никакой переменной в манифестах не требуется. Пользователь не видит AI, пока не включит его явно. --- ## 4. Архитектура решения ``` ┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ terraform │────>│ wrapper-скрипт │────>│ AI Analyzer │ │ plan │ │ (bash/Go CLI) │ │ (Go сервис) │ │ (вывод JSON)│ │ │ │ │ └─────────────┘ └──────────────────┘ └────────┬────────┘ │ ┌────────▼────────┐ │ LLM Backend │ │ (Google Gemini) │ │ → Cloud LLM │ └─────────────────┘ ``` ### Компоненты: 1. **Wrapper-скрипт / CLI** — вызывает `terraform plan -json`, читает `ai_hint_level`, передаёт вывод в анализатор. 2. **AI Analyzer (Go)** — формирует промпт по уровню, отправляет в LLM, форматирует ответ. 3. **LLM Backend** — подключаемый через интерфейс (сначала Google, потом облачный). --- ## 5. Реализация по шагам ### Шаг 1: CLI-обёртка (`sless-plan`) Новый бинарник или shell-скрипт, который: - Запускает `terraform plan -json -out=plan.bin` - Читает `ai_hint_level` из state/variables (через `terraform output` или парсинг .tf) - Если level > 0, передаёт JSON-вывод плана в AI Analyzer - Выводит стандартный plan + AI-рекомендации ниже ```bash # Пользователь вызывает: sless plan # вместо terraform plan # или: terraform plan && sless analyze --level=3 ``` ### Шаг 2: AI Analyzer сервис (Go) Расположение: `internal/ai/` или отдельный `cmd/ai-analyzer/` ``` internal/ai/ ├── analyzer.go # основная логика: формирование промпта, парсинг ответа ├── provider.go # интерфейс LLM-провайдера ├── google_gemini.go # реализация для Google Gemini API ├── cloud_llm.go # (заглушка) реализация для будущего облачного LLM └── prompts.go # шаблоны промптов по уровням 1–5 ``` Ключевой интерфейс: ```go // LLMProvider — абстракция над любым LLM-бэкендом. // Сейчас: Google Gemini. В будущем: облачный LLM (drop-in замена). type LLMProvider interface { Analyze(ctx context.Context, prompt string) (string, error) Name() string } ``` ### Шаг 3: Промпты по уровням Каждый уровень = свой system prompt + ограничения: | Уровень | System prompt (суть) | |---------|-----------------------------------------------------------| | 1 | "Проверь только синтаксис HCL. Ничего лишнего." | | 2 | "Синтаксис + базовые проблемы (unused, empty values)." | | 3 | "Анализ зависимостей, порядок, потенциальные конфликты." | | 4 | "Best practices, безопасность, IAM, открытые порты." | | 5 | "Полный аудит: оптимизация, рефакторинг, альтернативы." | ### Шаг 4: Google Gemini интеграция - API: `generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent` - Авторизация: API key (хранить в env `GOOGLE_AI_API_KEY`, не в коде) - SDK: `github.com/google/generative-ai-go` (официальный Go SDK) - Модель: `gemini-pro` или `gemini-1.5-pro` ```go // google_gemini.go — реализует LLMProvider type GeminiProvider struct { client *genai.Client model string } ``` ### Шаг 5: Конфигурация Через env-переменные (не хардкод): ```bash # Какой LLM-провайдер использовать export AI_PROVIDER=google # google | cloud (в будущем) # Google Gemini export GOOGLE_AI_API_KEY=AIza... # API ключ # Будущий облачный LLM export CLOUD_LLM_ENDPOINT=https://llm.cloud.example.com/v1/analyze export CLOUD_LLM_TOKEN=... ``` --- ## 6. Миграция на облачный LLM Когда облако предоставит свой LLM: 1. Создать `cloud_llm.go` — реализация `LLMProvider` для нового API 2. Переключить `AI_PROVIDER=cloud` в env 3. Код анализатора НЕ меняется — интерфейс `LLMProvider` тот же 4. Google Gemini остаётся как fallback (опционально) ```go // Фабрика провайдеров — выбор по переменной окружения func NewProvider(cfg Config) (LLMProvider, error) { switch cfg.Provider { case "google": return NewGeminiProvider(cfg.GoogleAPIKey) case "cloud": return NewCloudLLMProvider(cfg.CloudEndpoint, cfg.CloudToken) default: return nil, fmt.Errorf("unknown AI provider: %s", cfg.Provider) } } ``` --- ## 7. Безопасность - API-ключи — только в env/secrets, никогда в коде или .tf файлах - `terraform plan -json` может содержать sensitive data → фильтровать перед отправкой в LLM - Ограничить размер payload (большие планы обрезать/суммаризировать) - Логировать факт вызова AI, но НЕ содержимое запроса/ответа в production --- ## 8. Структура файлов (итого) ``` sless/ ├── cmd/ │ └── sless-plan/ # CLI-обёртка для terraform plan + AI │ └── main.go ├── internal/ │ └── ai/ │ ├── analyzer.go # логика анализа │ ├── provider.go # интерфейс LLMProvider │ ├── google_gemini.go # Google Gemini реализация │ ├── cloud_llm.go # заглушка для облачного LLM │ └── prompts.go # промпты по уровням 1–5 ├── terraform/ │ └── provider/ # (существующий провайдер — без изменений) └── examples/ └── */ └── variables.tf # добавить ai_hint_level variable ``` --- ## 9. Порядок реализации | # | Задача | Приоритет | |----|------------------------------------------------|-----------| | 1 | Создать `internal/ai/provider.go` (интерфейс) | HIGH | | 2 | Реализовать `google_gemini.go` | HIGH | | 3 | Написать промпты по уровням (`prompts.go`) | HIGH | | 4 | Создать `analyzer.go` (оркестрация) | HIGH | | 5 | CLI-обёртка `cmd/sless-plan/main.go` | HIGH | | 6 | Добавить `ai_hint_level` в examples | MEDIUM | | 7 | Тесты (unit + integration с mock LLM) | MEDIUM | | 8 | Заглушка `cloud_llm.go` для будущей миграции | LOW | | 9 | Документация для пользователей | MEDIUM | --- ## 10. Пример использования (целевой UX) ```bash # В terraform проекте пользователя: $ export TF_VAR_ai_hint_level=3 $ export GOOGLE_AI_API_KEY=AIza... $ sless plan # Terraform Plan output (стандартный) # ... # ──────────────── AI Analysis (level 3: MODERATE) ──────────────── # ⚠ Resource "kubernetes_deployment.func" зависит от "kubernetes_namespace.ns" # но namespace создаётся в другом модуле — возможен race condition. # ⚠ Порядок destroy может вызвать проблемы: trigger удалится раньше function. # ✓ Синтаксис корректен, конфликтов не обнаружено. # ───────────────────────────────────────────────────────────────── ``` --- ## Решение зафиксировано Подход: **интерфейс LLMProvider** → сначала Google Gemini → потом drop-in замена на облачный LLM. Уровень задаётся через обычную Terraform variable. Никаких изменений в самом провайдере sless.