md
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user