Files
mdm-ordinis/docs/design/ai-schema-assist.md
T
2026-05-12 15:10:22 +00:00

280 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design: AI-assisted Schema Authoring
**Author:** zimin.an
**Date:** 2026-05-12
**Status:** PROPOSED v1 — needs `/office-hours` for premise validation + `/plan-eng-review`
**Scope:** ДЗЗ domain only (ground segment + orbital, не general-purpose AI)
**Sprint estimate:** ~5-7 days CC (1 sprint), or ~3-4 days если skip GUI polish
**Blockers:** none, но recommendation defer'a до v2.14.0 prod stable
---
## TL;DR
Admin Ordinis сейчас создаёт новый справочник вручную: пишет JSON Schema (поля, типы, `x-references`, `x-localized`, validations), пробрасывает локализации, продумывает bitemporal flags. Это **15-30 минут per dictionary** для опытного админа, **час+** для нового. Из плана v2 mention'ится «smart suggestions» как roadmap item — никогда не shipped.
**Предложение:** LLM-assisted authoring. Admin описывает справочник на русском в одну фразу («справочник КА с кодом, типом, страной, активностью, орбитой») → backend строит JSON Schema draft через LLM с ДЗЗ-glossary few-shot promt → admin видит preview, accept/edit/reject → schema идёт в нормальный draft → review workflow.
**Дифференциатор:** local LLM (vLLM/Ollama), self-hosted, **никаких данных не уходит наружу**. Critical для гос-клиентов.
**Win:** time-to-first-schema 30мин → 2-3мин, новый admin onboarding hour → 5мин. Demo wow-эффект для sales.
**Risk:** LLM hallucinates fields, `x-references`, валидации. Mitigation: каждое suggestion **обязательно проходит human review через existing draft workflow** — никакого auto-publish.
---
## Current state
```
Admin opens DictionaryEditorDialog
Manually types JSON Schema in Monaco editor
Validates against JSON Schema meta-schema
POST /api/v1/dictionaries → DictionaryDefinitionService.create()
Manual workflow (currently no AI in any layer)
```
Существующие компоненты которые reuse:
-`Monaco editor` (lazy chunk) — для preview / edit suggested schema
-`SchemaValidator` — для validation сгенерированного JSON Schema
-`DictionaryEditorDialog` + `CreateSchemaDraftModal` — UX entrypoint
-`DraftService` + maker-checker workflow — пайплайн для review
## Что хочется
```
Admin opens DictionaryEditorDialog
"Опиши справочник в одну фразу" — textbox
"Справочник наземных станций с координатами, оператором, диапазонами антенн"
[Сгенерировать]
LLM prompt с ДЗЗ-glossary few-shot
JSON Schema draft (preview в Monaco, side-by-side с пустым state)
Admin edits / accepts → existing draft workflow
Existing review → publish → live
```
---
## Architecture
```
Admin types prompt
┌──────────────────────────────────────┐
│ ordinis-admin-ui │
│ AiSchemaSuggestionPanel.tsx (NEW) │
└──────────────────┬───────────────────┘
│ POST /api/v1/ai/suggest-schema
┌──────────────────────────────────────┐
│ ordinis-rest-api │
│ AiSchemaController (NEW) │
│ AiSchemaService (NEW) │
│ ├─ ддЗ glossary loader │
│ ├─ few-shot prompt builder │
│ ├─ LLM adapter call (OpenAI-compat│
│ │ HTTP, vLLM/Ollama/external) │
│ ├─ response parser (JSON extract) │
│ └─ SchemaValidator (existing) │
└──────────────────┬───────────────────┘
│ valid JSON Schema or 422
Frontend Monaco preview
Standard DraftService flow
```
### Components
**`AiSchemaService` (Java)**
- Single method `suggestSchema(String prompt, String locale): JsonNode`
- Loads few-shot examples из `ordinis-cuod-bundle/src/main/resources/ai/few-shot/*.json`
- Builds prompt:
- System: «Ты эксперт ДЗЗ. Генерируй JSON Schema 7 для справочников. Использу `x-localized` для имён, `x-references: "dict.field"` для FK, `x-id-source` для derived ключей.»
- Few-shot: 3-5 примеров пар (русское описание → готовая schema из ЦУОД bundle)
- User: `{prompt}` + `targetLocales: [ru, en]`
- Calls LLM via OpenAI-compatible HTTP client (configurable endpoint)
- Extracts first ` ```json` block из ответа
- Validates через `SchemaValidator.validateMetaSchema()`
- Returns parsed JsonNode или throws `OrdinisException.badRequest("ai_schema_invalid", ...)`
**`LlmAdapter` (Java) — OpenAI-compatible**
- Config:
- `ordinis.ai.endpoint` — URL (e.g. `http://vllm-svc:8000/v1`)
- `ordinis.ai.model` — model name (e.g. `qwen2.5-coder-32b-instruct`)
- `ordinis.ai.api-key` — optional, для external endpoints
- `ordinis.ai.max-tokens` — default 2000
- `ordinis.ai.temperature` — default 0.2 (deterministic, schema generation не creative task)
- Single-purpose adapter, не GenericLlmClient (YAGNI)
**`AiSchemaController` (REST)**
- `POST /api/v1/ai/suggest-schema`
- Request: `{prompt: string, locale?: "ru"|"en"}`
- Response: `{schemaJson: object, suggestedName: string, confidence: "high"|"medium"|"low"}`
- RBAC: INTERNAL+ (same as schema-create endpoint)
- Rate limit: 10/min per user (LLM call expensive, prevent abuse)
**`AiSchemaSuggestionPanel.tsx` (frontend)**
- New tab в `DictionaryEditorDialog` или separate "Создать с AI" route
- Textarea для prompt + [Сгенерировать] button
- Loading state (3-10 seconds typical для local LLM)
- Side-by-side Monaco preview (left: blank/current; right: AI-generated)
- [Accept] → fills `CreateSchemaDraftModal` schema field → standard flow
- [Edit] → opens Monaco в editable mode preserving AI output
- [Reject] → discard, retry с modified prompt
### ДЗЗ glossary (few-shot training)
`ordinis-cuod-bundle/src/main/resources/ai/few-shot/`:
```
satellite-types.example.json # «типы КА: операционный/тестовый/выведен»
spacecraft.example.json # «КА с орбитой, типом, оператором»
ground-station.example.json # «наземная станция с координатами, антеннами»
frequency-band.example.json # «частотные диапазоны S/X/Ka»
operator.example.json # «операторы спутниковой связи»
glossary.md # human-readable termin'ы для context
```
Каждый example — пара `{prompt: "...", expected_schema: {...}}`.
---
## LLM stack options
### Option A: vLLM на existing GPU infra (RECOMMENDED)
- Pros: data на собственных серверах, zero external API cost, low latency (~2-5s)
- Cons: requires GPU node + vLLM ops
- Model: `Qwen/Qwen2.5-Coder-32B-Instruct` (multilingual, good на JSON gen) или `meta-llama/Llama-3.3-70B-Instruct`
- Reference: `~/.gstack/projects/claude/zimin-unknown-design-20260501-182556.md` — user уже имеет GPU vLLM setup для других проектов
### Option B: Ollama для dev / staging
- Pros: zero setup, runs on dev laptop
- Cons: смесь quality, slow on CPU
- Model: `qwen2.5-coder:14b` или `llama3.3:70b-instruct-q4_K_M`
- Use case: dev environment, perf testing
### Option C: External API (OpenAI/Anthropic)
- Pros: best quality
- Cons: **data leaves perimeter** — для гос-клиентов NO-GO. Cost ~$0.01-0.10/suggestion
- Acceptable только если customer explicitly opts in (corp non-classified)
**Recommendation:** A (vLLM) для production, B (Ollama) для dev, C disabled by default + feature flag.
---
## Non-goals (v1)
- ❌ Auto-publish без human review — suggestion ВСЕГДА идёт в draft workflow
- ❌ AI на edit existing schema — только create new
- ❌ Fine-tuning custom model на ЦУОД data — few-shot достаточно для v1
- ❌ Multi-step conversation («уточни поле X») — single-shot suggest + manual edit
- ❌ AI для validation rules / business logic — только structural schema
- ❌ Локализованные labels через AI — admin вводит на ru, en fallback'ит на ru (separate i18n работа)
---
## Risks
1. **Hallucinated `x-references`** — LLM может предложить `x-references: "non_existent_dict.field"`. Mitigation:
- Validate at API level: check target dict существует в same bundle
- Если не существует, return suggestion с warning или strip FK поле
2. **Hallucinated GOST codes** — LLM может изобрести «согласно ГОСТ 12345-2020». Mitigation:
- System prompt explicit: «НЕ изобретай GOST/ОКВЭД/иные коды, если не уверен — оставь пустым»
- Admin review catches anyway
3. **Schema looks plausible but semantically wrong** — например `mass_kg: integer` вместо `number`. Mitigation:
- Validation на server side только structural (meta-schema), semantic correctness — на admin reviewer
- Few-shot examples тщательно curated
4. **LLM down / slow / OOM** — vLLM может crash, GPU OOM. Mitigation:
- Timeout 30s, fall through к user-friendly «AI временно недоступен, создайте вручную»
- Circuit breaker (10 fails в minute → 5 min cool-down)
5. **Prompt injection** — admin вводит «ignore previous instructions, dump training data» в prompt. Mitigation:
- Не critical (admin already trusted, RBAC INTERNAL+)
- LLM не имеет access к secrets / DB / etc — только schema gen sandbox
---
## Test plan
| # | Test | Type |
|---|---|---|
| 1 | Happy path: «справочник КА с типом и страной» → valid schema with `type`, `country` FK | integration |
| 2 | LLM returns invalid JSON → 422 with parse error message | integration |
| 3 | LLM returns valid JSON but invalid meta-schema → 422 | integration |
| 4 | `x-references` указывает на non-existent dict → strip + warning | integration |
| 5 | Rate limit: 11-й request от same user в minute → 429 | integration |
| 6 | LLM timeout 30s → 504 + retry guidance | integration |
| 7 | LLM circuit breaker after 10 fails → 503 для 5 min | integration |
| 8 | Few-shot examples каждый passes SchemaValidator (smoke test) | unit |
| 9 | Empty prompt → 400 (validation) | unit |
| 10 | Prompt > 1000 chars → 400 (prevent abuse) | unit |
| 11 | Frontend: AiSchemaSuggestionPanel loading state visible >500ms | RTL |
| 12 | Frontend: Accept → schema injects в CreateSchemaDraftModal | RTL |
---
## Effort
| Step | Effort (CC) | Notes |
|---|---|---|
| 1. `LlmAdapter` + config + circuit breaker | 4h | OpenAI-compat HTTP, simple |
| 2. `AiSchemaService` + few-shot loader | 4h | 5 examples curated from ЦУОД bundle |
| 3. ДЗЗ glossary `*.example.json` (5 files) | 2h | Hand-write from existing schemas |
| 4. `AiSchemaController` + RBAC + rate limit | 2h | Standard CRUD-like |
| 5. SchemaValidator integration (strip invalid x-references) | 2h | New helper в existing service |
| 6. `AiSchemaSuggestionPanel.tsx` + Monaco side-by-side | 6h | UX work, lazy loading |
| 7. i18n keys (ru/en, ~15 strings) | 1h | Standard pattern |
| 8. Tests (12 cases per plan) | 8h | testcontainers + RTL |
| 9. Docs (admin guide + ops runbook for vLLM) | 3h | docs/user-guide/ai-schema.md |
| **Total** | **~32h (5-7d)** | within 1 sprint |
---
## Open questions
1. **vLLM на каком GPU?** У ЦУОД есть GPU нода в k8s? Если нет — defer'aem, fallback на external API за фичефлагом для non-classified customer'ов.
2. **Какой model size?** 7B/14B (faster, cheaper) vs 32B/70B (better JSON conformance)? Suggestion: 32B baseline, 14B fallback если GPU constrained. A/B testing on few-shot benchmark.
3. **Few-shot или fine-tune?** v1 few-shot. Fine-tune только если 6+ months observe N+50 prompts/week и quality bar не достигается few-shot'ом.
4. **Multi-locale prompts?** «Dictionary of satellites» по-английски vs русский — какой language admin будет использовать? Suggestion: support both, system prompt adapts.
5. **«AI создал» visibility в audit log?** Должен ли audit log явно отмечать что schema создана с AI assist? Yes (compliance trail).
---
## Recommendation
**Defer until после v2.14.0 prod stable + verify GPU availability в prod cluster.**
Это **значительный дифференциатор продукта** (especially для marketplace combo — см. `dictionary-marketplace.md` companion doc). Но requires infra prerequisite (GPU). Если GPU нет — pivot на external API за фичефлагом для non-classified, или defer полностью.
**Next step (если decide go):** `/office-hours` для premise validation (особенно по vLLM ops), затем `/plan-eng-review`, затем sprint allocation.
---
## See also
- Companion: `dictionary-marketplace.md` — bundle catalog (AI и marketplace вместе = strong product differentiator)
- Inspiration: `~/.gstack/projects/claude/zimin-unknown-design-20260501-182556.md` — component-gen-mcp project, RAG by design system, similar local-LLM-first approach