14 KiB
14 KiB
План доработки БД для хранения контекста 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(идентификатор проекта/репозитория)titlestatus(draft|in_progress|review|done|archived)baseline_commit_sha(базовый снимок)snapshot_id(опционально для session-RAG)created_at,updated_atcreated_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_iddoc_versionpathsectionchunk_id(nullable; ссылка на chunk если стабильно поддерживается)change_type(added|updated|removed|linked)summary(краткое описание изменения)source_ref(ссылка/внешний id)created_atcreated_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(идентификатор/ссылка)descriptioncreated_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)
- На старте сессии сканировать рабочее дерево и считать
blob_shaдля файлов индексации. - Для каждого файла:
cache hit: взять chunk/embedding из кэша и связать с текущей сессией.cache miss: выполнить chunk+embed и записать результат в кэш.
- Для retrieval использовать
rag_session_chunk_mapкак первичный источник. - При необходимости делать fallback к cache-scoped данным по
repo_id(опционально, под флагом).
4.4. Почему это подходит
- Нет необходимости в сложном ACL общего RAG на уровне приложения.
- Нет обязательной зависимости от ручного commit, индекс отражает локальные изменения.
- Снижается время загрузки сессии за счет переиспользования эмбеддингов.
- История Story не теряется и не зависит от режима RAG.
4.5. Late binding story_id (целевой процесс)
- Аналитик запускает работу только со ссылкой на документ (без
story_id). - Агент обрабатывает задачу в
session-RAGи сохраняет все изменения вsession_artifacts. - Аналитик вручную делает commit и указывает
story_id. - Вебхук на commit:
- извлекает
story_idиз commit metadata/message, - обновляет репозиторный RAG,
- выполняет
bind session -> story: переносит/привязываетsession_artifactsкstory_artifacts, - фиксирует связь
story_id <-> commit_sha <-> changed_files.
- извлекает
- Исходный документ аналитики тоже попадает в контекст Story ретроспективно, даже если изначально был без
story_id.
5) Черновик DDL (PostgreSQL)
-- 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) План внедрения (после согласования)
- Подтвердить перечень полей и enum-значений.
- Подготовить SQL-миграцию
Vxxx__story_context.sql. - Обновить bootstrap/инициализацию схемы.
- Обновить репозитории для
story_records/story_artifacts/story_links. - Добавить таблицу и репозиторий
session_artifacts(session-scoped артефакты безstory_id). - Добавить запись session-артефактов в оркестраторе во время работы аналитика.
- Добавить webhook-обработчик
bind session -> storyпри появлении commit соstory_id. - Добавить API/сервисный метод
get_story_context(story_id)для повторного входа в Story. - Добавить тесты:
- unit на репозитории,
- интеграционные на happy-path записи/чтения,
- регресс на отсутствие зависимости от типа RAG.
- Добавить миграцию для
rag_blob_cache/rag_chunk_cache/rag_session_chunk_map. - Внедрить
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.