13 KiB
План: 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.
Уровень задаётся только явно, одним из способов (приоритет сверху вниз):
- Env-переменная (рекомендуется):
export TF_VAR_ai_hint_level=3
- В
variables.tfпользовательского проекта (опционально, только если нужно зафиксировать уровень в коде):
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"
}
}
- CLI-флаг напрямую:
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 │
└─────────────────┘
Компоненты:
- Wrapper-скрипт / CLI — вызывает
terraform plan -json, читаетai_hint_level, передаёт вывод в анализатор. - AI Analyzer (Go) — формирует промпт по уровню, отправляет в LLM, форматирует ответ.
- 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-рекомендации ниже
# Пользователь вызывает:
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
Ключевой интерфейс:
// 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
// google_gemini.go — реализует LLMProvider
type GeminiProvider struct {
client *genai.Client
model string
}
Шаг 5: Конфигурация
Через env-переменные (не хардкод):
# Какой 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:
- Создать
cloud_llm.go— реализацияLLMProviderдля нового API - Переключить
AI_PROVIDER=cloudв env - Код анализатора НЕ меняется — интерфейс
LLMProviderтот же - Google Gemini остаётся как fallback (опционально)
// Фабрика провайдеров — выбор по переменной окружения
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)
# В 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.