add: documentation

This commit is contained in:
“Naeel”
2026-06-30 15:45:24 +04:00
parent 540c1f7293
commit ca276d200f
1055 changed files with 47294 additions and 0 deletions
@@ -0,0 +1,219 @@
# Natural Language Infrastructure (NLI) Pattern
**Статус:** ✅ Реализовано
**Компонент:** Tubulus Resource + Gemini AI
**Файлы:** `internal/provider/tubulus_ai.go`, `internal/provider/tubulus_resource.go`
## Концепция
Вместо того, чтобы пользователь писал технические параметры в HCL, он может указать инструкцию на естественном языке, которую AI (Gemini) преобразует в конфигурацию на этапе `terraform plan`.
## Архитектура
```
┌──────────────────┐
│ User Instruction│ "создай на 30 секунд,
│ (Natural Lang) │ запиши пароль_123"
└────────┬─────────┘
│
▼
┌──────────────────┐
│ ModifyPlan() │ Terraform Plugin Framework
│ (Hook) │ Перехватывает план до показа
└────────┬─────────┘
│
▼
┌──────────────────┐
│ askGemini() │ Gemini 2.0 Flash API
│ (AI Parser) │ Возвращает структурированный JSON
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Plan Injection │ plan.DurationMs = 30000
│ (Computed) │ plan.BodyMessage = "пароль_123"
└──────────────────┘
```
## Пример использования
```text
resource "nubes_tubulus_instance" "test" {
display_name = "My Test"
description = "AI-driven resource"
# Естественный язык вместо технических параметров
instruction = "поработай секунд 20, положи 'hello world' и вали на 1 этапе"
# Эти поля рассчитаются автоматически через AI:
# duration_ms = 20000
# body_message = "hello world"
# where_fail = 1
# fail_in_progress = true
}
```
## Системный промпт (ключевые элементы)
1. **Типизация с примерами:**
```
"duration_ms": (integer) Длительность в миллисекундах
Примеры: "5 секунд" → 5000, "минута" → 60000, "быстро" → 1000
```
2. **Строгие ограничения:**
```
"where_fail": ТОЛЬКО [0, 1, 2, 3]
0 = не ломается, 1 = prepare, 2 = data_fill, 3 = after_vault
```
3. **Логические зависимости:**
```
Если fail_in_progress=true, но этап не указан — ставь where_fail=2
```
4. **Дефолты для null:**
```
Если НЕ указан текст явно для body_message — оставь null (не "ai_generated")
```
5. **Понимание разговорного языка:**
```
"долго" = 120000, "очень долго" = 300000, "полминутки" = 30000
```
## Технические детали
### ModifyPlan Lifecycle
```go
func (r *TubulusResource) ModifyPlan(ctx, req, resp) {
// 1. Проверка: это удаление?
if req.Plan.Raw.IsNull() { return }
// 2. Извлечение плана
var plan TubulusResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
// 3. Проверка: есть инструкция?
if plan.Instruction.IsNull() { return }
// 4. СТАБИЛИЗАЦИЯ: уже вызывали AI?
// (Предотвращает "plan inconsistency" между plan/apply)
if !plan.DurationMs.IsUnknown() &&
!plan.BodyMessage.IsUnknown() {
return
}
// 5. Вызов Gemini
aiConfig, err := r.askGemini(ctx, plan.Instruction.ValueString())
// 6. Инъекция значений
if aiConfig.DurationMs != nil {
plan.DurationMs = types.Int64Value(*aiConfig.DurationMs)
}
// 7. Обновление плана
resp.Plan.Set(ctx, &plan)
}
```
### Стабилизация (Critical!)
**Проблема:** Terraform вызывает `ModifyPlan()` дважды:
- 1-й раз: при `terraform plan` (показ пользователю)
- 2-й раз: при `terraform apply` (финальная проверка)
Если Gemini возвращает разные значения → ошибка:
```
Error: Provider produced inconsistent final plan
```
**Решение:** Проверяем, что значения ещё `Unknown`:
```go
if !plan.BodyMessage.IsUnknown() {
return // Уже вызывали AI, не повторяем
}
```
### Null vs Empty String
**Важно:** Если AI не вернул `body_message`, устанавливаем `null`, а не пустую строку:
```go
if aiConfig.BodyMessage != nil {
plan.BodyMessage = types.StringValue(*aiConfig.BodyMessage)
} else {
plan.BodyMessage = types.StringNull() // ← Критично!
}
```
Иначе: план при `apply` будет видеть `""` вместо `null` и выдаст inconsistency.
## Тестовые кейсы (100% Success Rate)
| Инструкция | Duration | BodyMessage | WhereFail | FailInProgress |
|------------|----------|-------------|-----------|----------------|
| "сделай быстро" | 1000 | null | 0 | false |
| "долго долго" | 300000 | null | 0 | false |
| "напиши привет" | 5000 | "привет" | 0 | false |
| "сломай сразу" | 5000 | null | 0 | true (at_start) |
| "зделай нормална на 10 сикунд" | 10000 | null | 0 | false |
| "30 сек, упади в середине, пароль_123" | 30000 | "пароль_123" | 2 | true |
| "duration 15000ms, fail at stage 3" | 15000 | null | 3 | true |
| "просто сделай что-нибудь" | 5000 | null | 0 | false |
## Расширяемость
Этот паттерн можно применить к **любому ресурсу** провайдера:
### PostgreSQL NLI
```text
# resource "nubes_postgres_instance" "db" {
instruction = "создай базу на 50 гигов с репликой в двух зонах"
# AI → cpu=2, ram=4096, disk=50000, replicas=2
}
```
### VM NLI
```text
# resource "nubes_vm_instance" "server" {
instruction = "подними ubuntu 22.04 с 8 ядрами и 16 гигами памяти"
# AI → os="ubuntu-22.04", cpu=8, ram=16384
}
```
### Edge Gateway NLI
```hcl
# resource "nubes_edge_gateway" "firewall" {
instruction = "настрой файрвол: блокируй китай и открой 80, 443"
# AI → firewall_rules=[...], allowed_ports=[80,443]
}
```
## Преимущества
1. **Снижение порога входа:** Новички могут писать конфигурации без изучения схемы ресурсов
2. **Скорость разработки:** "Создай Postgres" вместо 30 строк HCL
3. **Читаемость:** `instruction = "быстрая база"` понятнее, чем `cpu=1, ram=2048, disk=10000`
4. **Прозрачность:** Результат виден в `terraform plan` (не чёрный ящик)
5. **Аудит:** Все AI вызовы логируются → можно отследить "что просил vs что получил"
## Ограничения
1. **Latency:** Каждый `terraform plan` вызывает Gemini (~1-2 секунды)
2. **Cost:** Free Tier Gemini → 15 requests/min. Для крупных проектов нужен Billing
3. **Determinism:** AI может интерпретировать один запрос по-разному (минимизировано через примеры в промпте)
4. **Offline:** Требует интернет для доступа к Gemini API
## Улучшения (TODO)
- [ ] **Caching:** Кэшировать `(instruction → JSON)` локально, чтобы повторный plan не вызывал API
- [ ] **Offline Mode:** Fallback на дефолтные значения, если Gemini недоступен
- [ ] **Multi-language:** Поддержка английского, немецкого и т.д.
- [ ] **Validation:** Предупреждение, если AI вернул некорректные значения (например, `where_fail=99`)
## Выводы
Natural Language Infrastructure — это **первый шаг к "инфраструктуре как разговор"**. Вместо того, чтобы быть промежуточным звеном между человеком и API, Terraform становится **интерфейсом**, а AI — **компилятором**.
Эта реализация доказывает, что Plugin Framework достаточно гибок для таких экспериментов, и открывает дверь к новому способу управления облаком.