первый коммит
This commit is contained in:
271
README.DB.STORY_PLAN.md
Normal file
271
README.DB.STORY_PLAN.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# План доработки БД для хранения контекста Story и метаданных RAG
|
||||
|
||||
## Цель
|
||||
Зафиксировать проект миграции, который:
|
||||
- добавляет в таблицу чанков признаки артефакта (тип, источник, контекст),
|
||||
- вводит отдельный контур хранения инкремента по `story_id`,
|
||||
- не зависит от выбранного режима RAG (общий/сессионный/гибридный).
|
||||
|
||||
## Границы
|
||||
- Документ описывает план и целевую схему.
|
||||
- Реализация SQL-миграций и backfill выполняется отдельным шагом после согласования.
|
||||
|
||||
## 1) Метаданные чанков (RAG-слой)
|
||||
|
||||
### 1.1. Что добавить
|
||||
Для таблицы `rag_chunks` (или эквивалента таблицы чанков) добавить поля:
|
||||
- `artifact_type` (`REQ|ARCH|API|DB|UI|CODE|OTHER`)
|
||||
- `path` (нормализованный относительный путь файла)
|
||||
- `section` (заголовок/логический раздел документа)
|
||||
- `doc_id` (стабильный идентификатор документа)
|
||||
- `doc_version` (версия документа/ревизия)
|
||||
- `owner` (ответственная команда/человек)
|
||||
- `system_component` (система/подсистема/компонент)
|
||||
- `last_modified` (время последнего изменения источника)
|
||||
- `staleness_score` (0..1, в первую очередь для `CODE`)
|
||||
|
||||
### 1.2. Ограничения и индексы
|
||||
- `CHECK` для `artifact_type` и диапазона `staleness_score`.
|
||||
- Индексы:
|
||||
- `(artifact_type)`
|
||||
- `(doc_id, doc_version)`
|
||||
- `(system_component)`
|
||||
- `(path)`
|
||||
- GIN/BTREE по потребности для фильтрации в retrieval.
|
||||
|
||||
## 2) Контур Story (отдельно от чанков)
|
||||
|
||||
### 2.1. Таблица `story_records`
|
||||
Карточка Story:
|
||||
- `story_id` (PK, строковый уникальный идентификатор)
|
||||
- `project_id` (идентификатор проекта/репозитория)
|
||||
- `title`
|
||||
- `status` (`draft|in_progress|review|done|archived`)
|
||||
- `baseline_commit_sha` (базовый снимок)
|
||||
- `snapshot_id` (опционально для session-RAG)
|
||||
- `created_at`, `updated_at`
|
||||
- `created_by`, `updated_by`
|
||||
|
||||
Индексы:
|
||||
- `(project_id)`
|
||||
- `(status)`
|
||||
- `(updated_at)`
|
||||
|
||||
### 2.2. Таблица `story_artifacts`
|
||||
Связь Story с артефактами изменений:
|
||||
- `id` (PK)
|
||||
- `story_id` (FK -> `story_records.story_id`)
|
||||
- `artifact_role` (`requirement|analysis|doc_change|test_model|note|decision|risk`)
|
||||
- `doc_id`
|
||||
- `doc_version`
|
||||
- `path`
|
||||
- `section`
|
||||
- `chunk_id` (nullable; ссылка на chunk если стабильно поддерживается)
|
||||
- `change_type` (`added|updated|removed|linked`)
|
||||
- `summary` (краткое описание изменения)
|
||||
- `source_ref` (ссылка/внешний id)
|
||||
- `created_at`
|
||||
- `created_by`
|
||||
|
||||
Уникальность (черновик):
|
||||
- `UNIQUE(story_id, artifact_role, COALESCE(doc_id,''), COALESCE(path,''), COALESCE(section,''), COALESCE(change_type,''))`
|
||||
|
||||
Индексы:
|
||||
- `(story_id, artifact_role)`
|
||||
- `(story_id, change_type)`
|
||||
- `(doc_id, doc_version)`
|
||||
- `(path)`
|
||||
|
||||
### 2.3. Таблица `story_links`
|
||||
Связи Story с внешними сущностями и Story-to-Story:
|
||||
- `id` (PK)
|
||||
- `story_id` (FK)
|
||||
- `link_type` (`story|adr|ticket|pr|commit|doc|external`)
|
||||
- `target_ref` (идентификатор/ссылка)
|
||||
- `description`
|
||||
- `created_at`
|
||||
|
||||
Индексы:
|
||||
- `(story_id, link_type)`
|
||||
- `(target_ref)`
|
||||
|
||||
## 3) Почему `story_id` не в чанках
|
||||
- Один чанк может относиться к нескольким Story.
|
||||
- Чанки нестабильны при переиндексации.
|
||||
- Разделение слоев упрощает поддержку и не привязывает модель к типу RAG.
|
||||
|
||||
Итог: связь Story и чанков/документов хранить в `story_artifacts`, а не в `rag_chunks`.
|
||||
|
||||
## 4) Целевая модель RAG: Hybrid-Lite
|
||||
Выбранный вектор на текущем этапе: `Session-first + Shared Cache + Story Ledger`.
|
||||
|
||||
### 4.1. Принципы
|
||||
- Рабочий retrieval выполняется из сессионного индекса (видит незакоммиченные изменения).
|
||||
- Общий кэш чанков/эмбеддингов используется только для ускорения индексации.
|
||||
- Источник правды по инкременту Story находится в Story-таблицах, а не в RAG-индексе.
|
||||
|
||||
### 4.2. Что хранить дополнительно
|
||||
- `rag_blob_cache`: кэш файловых blob по `repo_id + blob_sha`.
|
||||
- `rag_chunk_cache`: кэш чанков/эмбеддингов, привязанный к `blob_sha`.
|
||||
- `rag_session_chunk_map`: привязка сессии к используемым chunk (чтобы retrieval был изолированным).
|
||||
- `session_artifacts`: временные артефакты сессии до появления `story_id` (late binding).
|
||||
|
||||
### 4.3. Алгоритм индексации (delta-only)
|
||||
1. На старте сессии сканировать рабочее дерево и считать `blob_sha` для файлов индексации.
|
||||
2. Для каждого файла:
|
||||
- `cache hit`: взять chunk/embedding из кэша и связать с текущей сессией.
|
||||
- `cache miss`: выполнить chunk+embed и записать результат в кэш.
|
||||
3. Для retrieval использовать `rag_session_chunk_map` как первичный источник.
|
||||
4. При необходимости делать fallback к cache-scoped данным по `repo_id` (опционально, под флагом).
|
||||
|
||||
### 4.4. Почему это подходит
|
||||
- Нет необходимости в сложном ACL общего RAG на уровне приложения.
|
||||
- Нет обязательной зависимости от ручного commit, индекс отражает локальные изменения.
|
||||
- Снижается время загрузки сессии за счет переиспользования эмбеддингов.
|
||||
- История Story не теряется и не зависит от режима RAG.
|
||||
|
||||
### 4.5. Late binding `story_id` (целевой процесс)
|
||||
1. Аналитик запускает работу только со ссылкой на документ (без `story_id`).
|
||||
2. Агент обрабатывает задачу в `session-RAG` и сохраняет все изменения в `session_artifacts`.
|
||||
3. Аналитик вручную делает commit и указывает `story_id`.
|
||||
4. Вебхук на commit:
|
||||
- извлекает `story_id` из commit metadata/message,
|
||||
- обновляет репозиторный RAG,
|
||||
- выполняет `bind session -> story`: переносит/привязывает `session_artifacts` к `story_artifacts`,
|
||||
- фиксирует связь `story_id <-> commit_sha <-> changed_files`.
|
||||
5. Исходный документ аналитики тоже попадает в контекст Story ретроспективно, даже если изначально был без `story_id`.
|
||||
|
||||
## 5) Черновик DDL (PostgreSQL)
|
||||
```sql
|
||||
-- 0. Enum-like checks можно заменить на справочники при необходимости
|
||||
|
||||
-- A) Session artifacts (временный слой до появления story_id)
|
||||
CREATE TABLE IF NOT EXISTS session_artifacts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
session_id TEXT NOT NULL,
|
||||
project_id TEXT NOT NULL,
|
||||
artifact_role TEXT NOT NULL,
|
||||
source_ref TEXT,
|
||||
doc_id TEXT,
|
||||
doc_version TEXT,
|
||||
path TEXT,
|
||||
section TEXT,
|
||||
chunk_id TEXT,
|
||||
change_type TEXT,
|
||||
summary TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT,
|
||||
CONSTRAINT chk_session_artifact_role CHECK (artifact_role IN (
|
||||
'analysis','doc_change','note','decision','risk','test_model'
|
||||
)),
|
||||
CONSTRAINT chk_session_change_type CHECK (change_type IS NULL OR change_type IN (
|
||||
'added','updated','removed','linked'
|
||||
))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_session_artifacts_session ON session_artifacts(session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_session_artifacts_project ON session_artifacts(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_session_artifacts_role ON session_artifacts(artifact_role);
|
||||
|
||||
-- 1) Story records
|
||||
CREATE TABLE IF NOT EXISTS story_records (
|
||||
story_id TEXT PRIMARY KEY,
|
||||
project_id TEXT NOT NULL,
|
||||
title TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
baseline_commit_sha TEXT,
|
||||
snapshot_id TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT,
|
||||
updated_by TEXT,
|
||||
CONSTRAINT chk_story_status CHECK (status IN (
|
||||
'draft','in_progress','review','done','archived'
|
||||
))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_story_records_project ON story_records(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_story_records_status ON story_records(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_story_records_updated_at ON story_records(updated_at DESC);
|
||||
|
||||
-- 2) Story artifacts
|
||||
CREATE TABLE IF NOT EXISTS story_artifacts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
story_id TEXT NOT NULL REFERENCES story_records(story_id) ON DELETE CASCADE,
|
||||
artifact_role TEXT NOT NULL,
|
||||
doc_id TEXT,
|
||||
doc_version TEXT,
|
||||
path TEXT,
|
||||
section TEXT,
|
||||
chunk_id TEXT,
|
||||
change_type TEXT,
|
||||
summary TEXT,
|
||||
source_ref TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by TEXT,
|
||||
CONSTRAINT chk_story_artifact_role CHECK (artifact_role IN (
|
||||
'requirement','analysis','doc_change','test_model','note','decision','risk'
|
||||
)),
|
||||
CONSTRAINT chk_story_change_type CHECK (change_type IS NULL OR change_type IN (
|
||||
'added','updated','removed','linked'
|
||||
))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_story_artifacts_story_role ON story_artifacts(story_id, artifact_role);
|
||||
CREATE INDEX IF NOT EXISTS idx_story_artifacts_story_change ON story_artifacts(story_id, change_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_story_artifacts_doc ON story_artifacts(doc_id, doc_version);
|
||||
CREATE INDEX IF NOT EXISTS idx_story_artifacts_path ON story_artifacts(path);
|
||||
|
||||
-- Вариант уникальности можно уточнить после согласования процессов
|
||||
|
||||
-- 3) Story links
|
||||
CREATE TABLE IF NOT EXISTS story_links (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
story_id TEXT NOT NULL REFERENCES story_records(story_id) ON DELETE CASCADE,
|
||||
link_type TEXT NOT NULL,
|
||||
target_ref TEXT NOT NULL,
|
||||
description TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_story_link_type CHECK (link_type IN (
|
||||
'story','adr','ticket','pr','commit','doc','external'
|
||||
))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_story_links_story_type ON story_links(story_id, link_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_story_links_target_ref ON story_links(target_ref);
|
||||
```
|
||||
|
||||
## 6) План внедрения (после согласования)
|
||||
1. Подтвердить перечень полей и enum-значений.
|
||||
2. Подготовить SQL-миграцию `Vxxx__story_context.sql`.
|
||||
3. Обновить bootstrap/инициализацию схемы.
|
||||
4. Обновить репозитории для `story_records/story_artifacts/story_links`.
|
||||
5. Добавить таблицу и репозиторий `session_artifacts` (session-scoped артефакты без `story_id`).
|
||||
6. Добавить запись session-артефактов в оркестраторе во время работы аналитика.
|
||||
7. Добавить webhook-обработчик `bind session -> story` при появлении commit со `story_id`.
|
||||
8. Добавить API/сервисный метод `get_story_context(story_id)` для повторного входа в Story.
|
||||
9. Добавить тесты:
|
||||
- unit на репозитории,
|
||||
- интеграционные на happy-path записи/чтения,
|
||||
- регресс на отсутствие зависимости от типа RAG.
|
||||
10. Добавить миграцию для `rag_blob_cache/rag_chunk_cache/rag_session_chunk_map`.
|
||||
11. Внедрить `delta-only` индексацию для session-RAG с переиспользованием кэша.
|
||||
|
||||
## 7) Открытые вопросы
|
||||
- Нужен ли отдельный справочник для `artifact_type`, `artifact_role`, `link_type`.
|
||||
- Что считать `doc_version`: semver, дата, commit, hash файла.
|
||||
- Нужна ли soft-delete политика для Story.
|
||||
- Требуется ли аудит (кто/когда менял `summary` и связи).
|
||||
- Какой уровень обязательности `chunk_id` (опционален по умолчанию).
|
||||
- Нужна ли TTL/очистка для `rag_blob_cache/rag_chunk_cache`.
|
||||
- Делать ли fallback к репозиторному кэшу по умолчанию или только при explicit-флаге.
|
||||
- Как определять соответствие `session_id` и commit в webhook (1:1, последний активный, explicit token).
|
||||
- Как долго хранить `session_artifacts` до bind/cleanup.
|
||||
|
||||
## 8) Критерии готовности
|
||||
- По `story_id` можно восстановить инкремент без исходной сессии.
|
||||
- История изменений не теряется при переиндексации RAG.
|
||||
- Аналитик и тестировщик используют один `story_id` как общий ключ контекста.
|
||||
- Схема работает при любом выбранном режиме RAG.
|
||||
- Session-RAG поднимается быстрее за счет cache hit по неизмененным файлам.
|
||||
- Артефакты аналитика, созданные до появления `story_id`, корректно попадают в Story после commit/webhook bind.
|
||||
Reference in New Issue
Block a user