169 lines
11 KiB
Markdown
169 lines
11 KiB
Markdown
# Prompt Management Strategy
|
||
|
||
Дата: 2026-06-23 | Версия: v1.0 | Контекст: v1.0.109, обсуждение архитектуры промптов
|
||
|
||
---
|
||
|
||
## А. Несколько промптов — зачем и как
|
||
|
||
Смешиваются **две разные потребности** — их нельзя путать:
|
||
|
||
### 1. Версионирование одного промпта (ОБЯЗАТЕЛЬНО)
|
||
|
||
Это не «несколько промптов», а история эволюции одного.
|
||
|
||
**Почему критично:**
|
||
- `prompt_version` уже кладётся в `spec_events` (provenance, Opus раунд 2).
|
||
- Значит промпт **обязан быть иммутабельным** — правка «на месте» сломает трассировку: старые события будут ссылаться на текст, которого больше нет.
|
||
- **Правка = новая версия**, а не перезапись. Откат, аудит «какая версия породила какой результат», сравнение версий — бесплатно.
|
||
|
||
### 2. Несколько РАЗНЫХ промптов одновременно
|
||
|
||
| Сценарий | Вердикт |
|
||
|----------|---------|
|
||
| **По роли в пайплайне** — разные LLM-вызовы (извлечение услуг vs сравнение vs резолв имён) | ✅ У каждого свой промпт. Настоящая причина «несколько» — по **задаче**. |
|
||
| **Варианты для эксперимента** — форкнул активный, сделал строже, прогнал на тестах, сравнил | ✅ Loop «дублировать → изменить → протестировать → активировать» — правильно. |
|
||
| **Каталог из 20 промптов «на всякий случай»** | ❌ Ловушка. Для узкого домена (colocation/ЦОД) нужен **ОДИН хороший** промпт на задачу. |
|
||
|
||
### Рекомендуемая модель: prompt-as-version (не prompt-as-document)
|
||
|
||
Одна таблица, один активный на `role`, вся история — строки:
|
||
|
||
| Поле | Тип | Смысл |
|
||
|------|-----|-------|
|
||
| `id` | UUID PK | Версия (иммутабельная) |
|
||
| `role` | VARCHAR | Шаг пайплайна: `extract`, `diff` |
|
||
| `name` | VARCHAR | Человеческое имя («default-v3», «strict-prices») |
|
||
| `body` | TEXT | Тело промпта с плейсхолдерами |
|
||
| `parent_id` | UUID FK→prompts.id | От какой версии форкнут |
|
||
| `is_active` | BOOLEAN | UNIQUE(role, is_active) с частичным индексом WHERE is_active |
|
||
| `created_at` | TIMESTAMP | |
|
||
| `created_by` | VARCHAR | Мульти-юзер |
|
||
| `notes` | TEXT | Что изменили и зачем |
|
||
|
||
**Операции:**
|
||
- **Править** = `INSERT` новой строки с `parent_id` на предыдущую. Не `UPDATE`.
|
||
- **Активировать** = переключить `is_active` (старая теряет флаг).
|
||
- **Дублировать** = копия под новым `name` (форк).
|
||
- **Откат** = активировать старую версию.
|
||
|
||
**Никогда не UPDATE текста на месте.**
|
||
|
||
---
|
||
|
||
## Б. Как составлять промпт
|
||
|
||
### Стартовый шаблон — ОБЯЗАТЕЛЕН
|
||
|
||
Пустое поле ввода — катастрофа для качества. Всегда форкать от выверенного дефолта, не с нуля.
|
||
|
||
### Структура (8 секций, порядок важен)
|
||
|
||
Для доменной задачи **без structural output** (gpt-oss-120b):
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ 1. РОЛЬ / КОНТЕКСТ │
|
||
│ «ты эксперт по договорам colocation │
|
||
│ и облачных услуг ЦОД» │
|
||
├─────────────────────────────────────────────┤
|
||
│ 2. ЗАДАЧА │
|
||
│ «сравни текущую спецификацию с документом,│
|
||
│ верни операции изменений» │
|
||
├─────────────────────────────────────────────┤
|
||
│ 3. ДОМЕННЫЙ ГЛОССАРИЙ │
|
||
│ стойко-место, кВт мощности, colocation, │
|
||
│ единицы измерения, типы услуг. │
|
||
│ Без этого модель путает домен. │
|
||
├─────────────────────────────────────────────┤
|
||
│ 4. ФОРМАТ ВХОДА │
|
||
│ Как подаётся spec_current (с ID r1..rN), │
|
||
│ как подаётся текст документа. │
|
||
├─────────────────────────────────────────────┤
|
||
│ 5. КОНТРАКТ ВЫВОДА │
|
||
│ Строгая JSON-схема ops: поля, типы, │
|
||
│ обязательность. КРИТИЧНО — structural │
|
||
│ output нет, схема держится промптом. │
|
||
├─────────────────────────────────────────────┤
|
||
│ 6. ПРАВИЛА / ОГРАНИЧЕНИЯ │
|
||
│ «ссылайся только на id из spec_current», │
|
||
│ «верни source_quote», «верни confidence», │
|
||
│ «name существующих строк не переписывай» │
|
||
├─────────────────────────────────────────────┤
|
||
│ 7. FEW-SHOT ПРИМЕРЫ (2-3) │
|
||
│ ⭐ САМЫЙ МОЩНЫЙ РЫЧАГ для бесплатной │
|
||
│ модели без fine-tune. Примеры учат и │
|
||
│ формату, и поведению лучше инструкций. │
|
||
│ Вход→Выход на ВАШЕМ домене (ЦОД). │
|
||
├─────────────────────────────────────────────┤
|
||
│ 8. EDGE-CASES │
|
||
│ Как помечать неоднозначность, что делать │
|
||
│ при low-confidence. │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Принцип разделения: статика vs рантайм
|
||
|
||
```
|
||
[версионируемая часть — в БД] [рантайм-инъекция — в коде]
|
||
роль + глоссарий + правила + {spec_current} + {document_text}
|
||
+ few-shot + контракт вывода
|
||
```
|
||
|
||
- Статическая инструкция (секции 1-8) → **версионируется** в `prompts.body`.
|
||
- Динамические данные (`{spec_current}`, `{document_text}`, `{date}`) → **подставляются в рантайме** через плейсхолдеры.
|
||
- В БД хранится тело промпта **с плейсхолдерами**, не с конкретными данными.
|
||
|
||
---
|
||
|
||
## В. Prompt CI — как с этим работать
|
||
|
||
```
|
||
┌──────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐
|
||
│ Дублировать │ → │ Изменить │ → │ Прогнать на │ → │ Активировать │
|
||
│ (форк) │ │ (новая v) │ │ golden-наборе│ │ (is_active) │
|
||
└──────────┘ └──────────┘ └───────────┘ └──────────┘
|
||
```
|
||
|
||
1. Форкнуть активный промпт → новый `id`, `is_active=false`
|
||
2. Изменить тело
|
||
3. Прогнать на golden-наборе (3-5 реальных ДС с известным результатом)
|
||
4. Сравнить метрики (precision/recall по ops)
|
||
5. Если лучше — активировать. Если хуже — оставить как эксперимент или удалить.
|
||
|
||
---
|
||
|
||
## Г. Текущее состояние (v1.0.109)
|
||
|
||
Промпты жёстко зашиты в `llm_prompt.py`:
|
||
- `_build_initial()` — для первого документа
|
||
- `_build_diff()` — для сравнения (основной)
|
||
|
||
**Что уже хорошо:**
|
||
- JSON-контракт вывода описан
|
||
- `target_id` (r1..rN) вместо `target_hash`
|
||
- Правила: mode full_replace, UPDATE только изменённые поля, UNRESOLVED для неоднозначного
|
||
|
||
**Чего не хватает (по приоритету):**
|
||
1. Few-shot примеры на домене ЦОД — самый большой рычаг качества
|
||
2. Доменный глоссарий — чтобы модель понимала единицы измерения и термины
|
||
3. `source_quote` в контракте вывода — для provenance
|
||
4. `confidence` в контракте вывода — самооценка модели
|
||
5. Edge-cases инструкция — как вести себя при неоднозначности
|
||
|
||
---
|
||
|
||
## Д. План миграции (если делать)
|
||
|
||
| Шаг | Что | Сложность |
|
||
|-----|-----|-----------|
|
||
| 1 | Таблица `prompts` в PostgreSQL (Lucee datasource `baza`) | Низкая |
|
||
| 2 | CRUD в `api.cfm` для prompts (list, get, create, set_active) | Средняя |
|
||
| 3 | `llm_prompt.py` → читает активный промпт из Lucee API вместо хардкода | Средняя |
|
||
| 4 | `prompt_version` в `spec_events` — ссылка на `prompts.id` | Низкая |
|
||
| 5 | UI для prompts в `index.cfm` (список версий, редактор, активация) | Высокая |
|
||
| 6 | Golden-набор: 3-5 ДС + эталонные ops | Ручная работа |
|
||
| 7 | Prompt CI: скрипт прогона на golden-наборе + сравнение метрик | Средняя |
|
||
|
||
**Первый практический шаг** (максимум пользы, минимум кода): добавить few-shot примеры в текущие промпты прямо в `llm_prompt.py`, не дожидаясь таблицы `prompts`.
|