This commit is contained in:
2026-03-27 15:51:10 +03:00
parent 15586f9a8c
commit 0bff171936
1245 changed files with 99621 additions and 543076 deletions
@@ -0,0 +1,168 @@
---
id: api-rag-session-changes
title: Применение изменений к RAG-сессии
doc_type: api_method
domain: rag
status: draft
owner: system-analyst
source_of_truth: code
related_docs:
- arch-rag-package
- logic-rag-indexing
- entity-rag-session
- entity-rag-index-job
related_code:
- src/app/modules/rag/module.py
- src/app/schemas/rag_sessions.py
- src/app/schemas/indexing.py
entities:
- RagSession
- IndexJob
tags:
- rag
- api
- changes
- incremental-indexing
---
# Применение изменений к RAG-сессии
## Summary
- Purpose: поставить incremental indexing для уже существующей `RagSession`.
- Actor: внешний клиент модуля RAG.
- Trigger: частичное обновление индекса после изменения файлов.
- Endpoint: `POST /api/rag/sessions/{rag_session_id}/changes`
- Main entities: `RagSession`, `IndexJob`.
- Main logic: проверка существования сессии, создание change job, асинхронная обработка `upsert`/`delete`.
- Main errors: `not_found` для отсутствующей сессии, `422` для некорректного payload.
- Source of truth: `src/app/modules/rag/module.py`, `src/app/schemas/rag_sessions.py`.
## Назначение
Метод позволяет обновить индекс без полной переиндексации проекта. Он принимает только изменённые файлы и операции удаления.
## Контекст
Endpoint относится к новому API с явной работой через `rag_session_id`. В отличие от legacy `/api/index/changes`, он не создаёт сессию молча и требует, чтобы она уже существовала.
## Технический use case
### Основной сценарий
1. Клиент передаёт `rag_session_id` в path и список `changed_files` в body.
2. Endpoint проверяет наличие сессии через `RagSessionStore.get`.
3. При успехе `IndexingOrchestrator.enqueue_changes` создаёт новую job и запускает фоновое применение изменений.
4. API возвращает `index_job_id` и стартовый статус.
### Альтернативные ветки
- Если `rag_session_id` не найдена, endpoint бросает `AppError("not_found", ...)`.
- Для `op=delete` в последующей логике происходит удаление документов по пути без повторной генерации embeddings.
## Функциональные требования
### Request validation
- Path parameter `rag_session_id` обязателен.
- `changed_files` обязателен и состоит из элементов `ChangedFile`.
- Для каждого элемента обязательны `op` и `path`.
- `op` допускает только `upsert` или `delete`.
### Processing rules
- Сессия должна существовать до постановки change job.
- Каждый вызов создаёт новый `IndexJob`.
- Фактическое применение изменений выполняется асинхронно.
### State changes
- В `rag_index_jobs` появляется новая задача.
- Сам индекс меняется позже, внутри `RagService.index_changes`.
### Side effects
- Публикация job events.
- Удаление документов по `delete_paths` и upsert новых документов в фоне.
## Contract
### Endpoint
- Method: `POST`
- Path: `/api/rag/sessions/{rag_session_id}/changes`
- Auth: определяется внешним слоем приложения.
- Idempotent: нет, повторный вызов создаёт новую job.
- Timeout: короткий, endpoint не дожидается завершения индексации.
- Retry: только если клиент готов к созданию дополнительной job.
### Request
| Field | Type | Required | Constraints | Description |
|------|------|----------|-------------|-------------|
| `rag_session_id` | `string` | yes | path param, non-empty | идентификатор существующей RAG-сессии |
| `changed_files` | `array<ChangedFile>` | yes | схема каждого элемента обязательна | изменения файлов |
| `changed_files[].op` | `enum` | yes | `upsert` or `delete` | тип операции |
| `changed_files[].path` | `string` | yes | `min_length=1` | путь файла |
| `changed_files[].content` | `string \| null` | no | нужен для `upsert` | содержимое файла |
| `changed_files[].content_hash` | `string \| null` | no | повышает cache reuse | hash содержимого |
### Response
| Field | Type | Description |
|------|------|-------------|
| `index_job_id` | `string` | идентификатор фоновой задачи |
| `status` | `string` | стартовый статус задачи |
### External contract refs
- OpenAPI: формируется FastAPI по `response_model=IndexJobQueuedResponse`.
- Schema: `RagSessionChangesRequest`, `ChangedFile`, `IndexJobQueuedResponse`.
- DTO / serializer: `src/app/schemas/rag_sessions.py`, `src/app/schemas/indexing.py`.
- Additional refs: `logic-rag-indexing`.
## Errors
| error_id | http_code | when | client_behavior | retry |
|----------|-----------|------|-----------------|-------|
| `not_found` | `404` | `rag_session_id` отсутствует | создать новую сессию или исправить id | no |
| `validation_error` | `422` | нарушена схема request | исправить payload | no |
## Нефункциональные требования
### Security
- Метод доверяет внешнему слою авторизации.
### Observability
- Logs: прямое логирование endpoint отсутствует.
- Metrics: нет отдельной метрики на уровне метода.
- Traces: отсутствуют.
- Audit: каждая операция материализуется в `IndexJob`.
### Reliability
- Проверка существования сессии защищает от случайной записи в неинициализированный scope.
- Ошибки индексации доступны через job status и SSE events.
### Performance
- Быстрый ответ за счёт фонового выполнения.
## Связанные блоки логики
- `logic-rag-indexing`
## Связанные сущности
- `RagSession`
- `IndexJob`
## Связанный код
### Files
- `src/app/modules/rag/module.py`
- `src/app/schemas/rag_sessions.py`
- `src/app/schemas/indexing.py`
### Symbols
- `RagModule.public_router.rag_session_changes`
- `RagSessionStore.get`
- `IndexingOrchestrator.enqueue_changes`
## Связанные документы
- `arch-rag-package`
- `logic-rag-indexing`
- `entity-rag-session`
- `entity-rag-index-job`
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| 2026-03-13 | code | Задокументирован публичный endpoint incremental indexing для существующей сессии. |
@@ -0,0 +1,166 @@
---
id: api-rag-session-create
title: Создание RAG-сессии и запуск snapshot-индексации
doc_type: api_method
domain: rag
status: draft
owner: system-analyst
source_of_truth: code
related_docs:
- arch-rag-package
- logic-rag-indexing
- entity-rag-session
- entity-rag-index-job
related_code:
- src/app/modules/rag/module.py
- src/app/schemas/rag_sessions.py
- src/app/schemas/indexing.py
entities:
- RagSession
- IndexJob
tags:
- rag
- api
- session
- snapshot
---
# Создание RAG-сессии и запуск snapshot-индексации
## Summary
- Purpose: создать новую `RagSession` и асинхронно поставить полную индексацию snapshot-файлов.
- Actor: внешний клиент модуля RAG.
- Trigger: первичная загрузка файлов проекта в индекс.
- Endpoint: `POST /api/rag/sessions`
- Main entities: `RagSession`, `IndexJob`.
- Main logic: создание UUID-сессии, постановка snapshot job, возврат идентификаторов сессии и job.
- Main errors: в коде endpoint нет собственной бизнес-валидации сверх pydantic; ошибки индексации проявляются позже в job status.
- Source of truth: `src/app/modules/rag/module.py`, `src/app/schemas/rag_sessions.py`.
## Назначение
Метод открывает новую RAG-сессию и запускает первичную индексацию файлов. Он используется как основной публичный вход для нового API пакета `rag`.
## Контекст
В отличие от legacy `/api/index/snapshot`, этот endpoint всегда создаёт новый `rag_session_id`, что позволяет независимо хранить несколько снимков одного проекта.
## Технический use case
### Основной сценарий
1. Клиент передаёт `project_id` и массив `files`.
2. `RagSessionStore.create` создаёт новую запись в `rag_sessions`.
3. `IndexingOrchestrator.enqueue_snapshot` создаёт `IndexJob` и запускает фоновую обработку.
4. API сразу возвращает `rag_session_id`, `index_job_id` и стартовый статус.
### Альтернативные ветки
- Если часть файлов не подлежит индексации, они будут отфильтрованы уже внутри indexing pipeline, а не на этапе ответа API.
- Ошибки индексации не меняют синхронный ответ create endpoint, а отражаются в последующем статусе job.
## Функциональные требования
### Request validation
- `project_id` обязателен и не может быть пустым.
- `files` передаются списком объектов `FileSnapshot`.
- Для каждого файла обязательны `path`, `content`, `content_hash`.
### Processing rules
- На каждый вызов создаётся новая `RagSession`.
- Snapshot job создаётся сразу после сохранения сессии.
- Ответ не ждёт завершения индексации.
### State changes
- В `rag_sessions` появляется новая запись.
- В `rag_index_jobs` появляется новая запись в статусе `queued`.
### Side effects
- Запуск фоновой `asyncio` task.
- Последующая публикация progress events в EventBus.
## Contract
### Endpoint
- Method: `POST`
- Path: `/api/rag/sessions`
- Auth: определяется внешним слоем приложения, внутри endpoint не задана.
- Idempotent: нет.
- Timeout: короткий, так как endpoint не ждёт индексацию.
- Retry: допустим только на стороне клиента с пониманием, что будет создана новая сессия.
### Request
| Field | Type | Required | Constraints | Description |
|------|------|----------|-------------|-------------|
| `project_id` | `string` | yes | `min_length=1` | идентификатор проекта |
| `files` | `array<FileSnapshot>` | yes | может быть пустым, но схема обязана соблюдаться | snapshot файлов для первичной индексации |
| `files[].path` | `string` | yes | `min_length=1` | путь файла |
| `files[].content` | `string` | yes | без дополнительных ограничений | содержимое файла |
| `files[].content_hash` | `string` | yes | `min_length=1` | hash содержимого для cache reuse |
### Response
| Field | Type | Description |
|------|------|-------------|
| `rag_session_id` | `string` | идентификатор созданной сессии |
| `index_job_id` | `string` | идентификатор фоновой задачи индексации |
| `status` | `IndexJobStatus` | стартовый статус задачи, обычно `queued` |
### External contract refs
- OpenAPI: формируется FastAPI по `response_model=RagSessionCreateResponse`.
- Schema: `RagSessionCreateRequest`, `RagSessionCreateResponse`.
- DTO / serializer: `src/app/schemas/rag_sessions.py`, `src/app/schemas/indexing.py`.
- Additional refs: `logic-rag-indexing`.
## Errors
| error_id | http_code | when | client_behavior | retry |
|----------|-----------|------|-----------------|-------|
| `validation_error` | `422` | нарушена pydantic-схема request | исправить payload | no |
## Нефункциональные требования
### Security
- Авторизация и аутентификация находятся вне этого метода.
### Observability
- Logs: прямое логирование в endpoint отсутствует.
- Metrics: отдельные API-метрики не выделены.
- Traces: отсутствуют.
- Audit: факт вызова материализуется через `RagSession` и `IndexJob`.
### Reliability
- Даже при дальнейшей ошибке индексации клиент может получить статус через job endpoint.
- Фоновая задача создаётся немедленно после ответа.
### Performance
- Время ответа не зависит от размера snapshot, кроме времени сериализации request.
## Связанные блоки логики
- `logic-rag-indexing`
## Связанные сущности
- `RagSession`
- `IndexJob`
## Связанный код
### Files
- `src/app/modules/rag/module.py`
- `src/app/schemas/rag_sessions.py`
- `src/app/schemas/indexing.py`
### Symbols
- `RagModule.public_router.create_rag_session`
- `RagSessionStore.create`
- `IndexingOrchestrator.enqueue_snapshot`
## Связанные документы
- `arch-rag-package`
- `logic-rag-indexing`
- `entity-rag-session`
- `entity-rag-index-job`
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| 2026-03-13 | code | Задокументирован публичный endpoint создания RAG-сессии. |
@@ -0,0 +1,166 @@
---
id: api-rag-session-job
title: Получение статуса и событий задачи индексации
doc_type: api_method
domain: rag
status: draft
owner: system-analyst
source_of_truth: code
related_docs:
- arch-rag-package
- entity-rag-session
- entity-rag-index-job
related_code:
- src/app/modules/rag/module.py
- src/app/modules/rag/job_store.py
- src/app/schemas/rag_sessions.py
entities:
- RagSession
- IndexJob
tags:
- rag
- api
- job-status
- sse
---
# Получение статуса и событий задачи индексации
## Summary
- Purpose: отдать текущее состояние job и поток событий её выполнения в рамках конкретной `RagSession`.
- Actor: внешний клиент модуля RAG.
- Trigger: polling или live-monitoring после запуска snapshot/change indexing.
- Endpoint: `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}` и `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}/events`
- Main entities: `RagSession`, `IndexJob`.
- Main logic: чтение job по id, проверка принадлежности сессии, возврат status payload или SSE stream.
- Main errors: `not_found` при отсутствии job или несовпадении `rag_session_id`.
- Source of truth: `src/app/modules/rag/module.py`, `src/app/modules/rag/job_store.py`.
## Назначение
Документ описывает два связанных метода наблюдения: синхронный status endpoint и потоковый SSE endpoint. Оба работают поверх одной сущности `IndexJob`.
## Контекст
Create и changes endpoints возвращают только стартовый статус задачи, поэтому клиенту нужны отдельные методы для отслеживания выполнения. SSE-поток даёт live progress, а status endpoint нужен для простого polling.
## Технический use case
### Основной сценарий
1. Клиент вызывает status endpoint или открывает SSE stream по `index_job_id`.
2. Endpoint читает job из `IndexJobStore`.
3. Если job отсутствует или принадлежит другой `rag_session_id`, возвращается `not_found`.
4. Status endpoint отдаёт снимок counters и error payload.
5. SSE endpoint подписывается на `EventBus` c `replay=True` и транслирует `index_status`, `index_progress`, `terminal`.
### Альтернативные ветки
- При отсутствии новых событий SSE endpoint каждые 10 секунд отправляет `: keepalive`.
- После события `terminal` поток завершается и отписывается от EventBus.
## Функциональные требования
### Request validation
- `rag_session_id` и `index_job_id` обязательны как path parameters.
- Job должна существовать и принадлежать переданной сессии.
### Processing rules
- Status endpoint не подписывается на события и читает только текущее состояние job.
- SSE endpoint использует `replay=True`, чтобы клиент получил уже опубликованные события.
- Оба метода защищают от доступа к job другой сессии.
### State changes
- Методы не меняют состояние job.
### Side effects
- SSE endpoint создаёт временную подписку на EventBus.
- При завершении или разрыве соединения выполняется `unsubscribe`.
## Contract
### Endpoint
- Method: `GET`
- Path: `/api/rag/sessions/{rag_session_id}/jobs/{index_job_id}` и `/api/rag/sessions/{rag_session_id}/jobs/{index_job_id}/events`
- Auth: определяется внешним слоем приложения.
- Idempotent: да.
- Timeout: status endpoint короткий; SSE stream долгоживущий.
- Retry: polling можно повторять безопасно; SSE можно переподключать.
### Request
| Field | Type | Required | Constraints | Description |
|------|------|----------|-------------|-------------|
| `rag_session_id` | `string` | yes | path param | идентификатор сессии |
| `index_job_id` | `string` | yes | path param | идентификатор задачи |
### Response
| Field | Type | Description |
|------|------|-------------|
| `rag_session_id` | `string` | идентификатор сессии, только для status endpoint |
| `index_job_id` | `string` | идентификатор задачи |
| `status` | `IndexJobStatus` | текущее состояние job |
| `indexed_files` | `integer` | число успешно обработанных файлов |
| `failed_files` | `integer` | число файлов с ошибками |
| `cache_hit_files` | `integer` | число cache hit |
| `cache_miss_files` | `integer` | число cache miss |
| `error` | `object \| null` | ошибка, если job завершилась с `error` |
### External contract refs
- OpenAPI: status endpoint использует `response_model=RagSessionJobResponse`; SSE endpoint отдаёт `text/event-stream`.
- Schema: `RagSessionJobResponse`.
- DTO / serializer: `src/app/schemas/rag_sessions.py`.
- Additional refs: `entity-rag-index-job`.
## Errors
| error_id | http_code | when | client_behavior | retry |
|----------|-----------|------|-----------------|-------|
| `not_found` | `404` | job отсутствует или не принадлежит переданной сессии | проверить id или создать новую задачу | no |
## Нефункциональные требования
### Security
- Проверка `job.rag_session_id == rag_session_id` обязательна для обоих методов.
### Observability
- Logs: отдельные логи чтения статуса не реализованы.
- Metrics: отсутствуют.
- Traces: отсутствуют.
- Audit: история job хранится в `rag_index_jobs`, поток событий в памяти EventBus.
### Reliability
- SSE heartbeat удерживает соединение активным.
- `finally` блок гарантирует `unsubscribe`.
### Performance
- Status endpoint работает как лёгкий запрос к БД.
- SSE stream масштабируется числом активных подписчиков и объёмом событий.
## Связанные блоки логики
- `logic-rag-indexing`
## Связанные сущности
- `RagSession`
- `IndexJob`
## Связанный код
### Files
- `src/app/modules/rag/module.py`
- `src/app/modules/rag/job_store.py`
- `src/app/schemas/rag_sessions.py`
### Symbols
- `RagModule.public_router.rag_session_job`
- `RagModule.public_router.rag_session_job_events`
- `IndexJobStore.get`
## Связанные документы
- `arch-rag-package`
- `entity-rag-session`
- `entity-rag-index-job`
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| 2026-03-13 | code | Задокументированы status и SSE endpoints для наблюдения за indexing job. |
@@ -0,0 +1,222 @@
---
## id: arch-rag-package
title: Пакет RAG
doc_type: architecture_overview
domain: rag
status: draft
owner: system-analyst
source_of_truth: code
related_docs:
- logic-rag-indexing
- logic-rag-retrieval
- entity-rag-session
- entity-rag-index-job
- api-rag-session-create
- api-rag-session-changes
- api-rag-session-job
related_code:
- src/app/modules/rag/module.py
- src/app/modules/rag/services/rag_service.py
- src/app/modules/rag/indexing_service.py
- src/app/modules/rag/persistence/repository.py
- src/app/modules/rag/persistence/schema_repository.py
entities:
- RagSession
- IndexJob
- RagDocument
tags:
- rag
- indexing
- retrieval
- architecture
# Пакет RAG
## Summary
- Scope: модуль индексации проектных файлов, хранения RAG-слоёв и выдачи retrieval-контекста.
- Purpose: построить индекс по документации и Python-коду и дать runtime доступ к релевантным фрагментам.
- Main modules: `RagModule`, `RagService`, `IndexingOrchestrator`, `RagRepository`, `RepoWebhookService`.
- Main domains: RAG-сессии, задачи индексации, документы индекса, blob-cache, retrieval.
- Main integrations: PostgreSQL/pgvector, GigaChat embeddings, FastAPI, EventBus, story context.
- Key entrypoints: `/api/rag/sessions`, `/api/rag/sessions/{rag_session_id}/changes`, `/api/rag/sessions/{rag_session_id}/jobs/{index_job_id}`, `/internal/rag-repo/webhook`.
- Key data flows: snapshot indexing, incremental reindex, retrieval из `rag_chunks`, webhook-нормализация коммитов.
- Source of truth: код `src/app/modules/rag/*`.
## Назначение
Пакет `rag` отвечает за полный цикл подготовки retrieval-контекста для проекта: принимает снапшоты и изменения файлов, преобразует их в набор атомарных `RagDocument`, векторизует, сохраняет в БД и предоставляет доступ к индексированным данным другим частям системы.
## Контекст
Модуль используется как инфраструктурный слой для agent/runtime и смежных интеграций. На вход он принимает либо список файлов проекта, либо webhook репозитория. На выходе формирует устойчивый индекс, ассоциированный с `rag_session_id`, и статус задач индексации, пригодный для опроса и SSE-подписки.
## Границы системы
### In scope
- Создание и хранение `RagSession`.
- Постановка и выполнение задач snapshot/change indexing.
- Индексация markdown-документации в слои `D1-D4`.
- Индексация Python-кода в слои `C0-C4`.
- Кэширование по `repo_id + blob_sha`.
- Сохранение retrieval-документов в `rag_chunks`.
- Выдача статуса задач и событий прогресса.
- Нормализация webhook Gitea/Bitbucket и связывание коммитов со story.
### Out of scope
- Финальная генерация ответа пользователю.
- Оркестрация LLM-диалога.
- Управление git-репозиторием и загрузка файлов из внешних источников.
- Политики маршрутизации intent/runtime вне собственного persistence/retrieval API.
## Архитектурная схема
`RagModule` собирает зависимости модуля и публикует HTTP endpoints. Для индексации он использует `RagSessionStore`, `IndexJobStore`, `IndexingOrchestrator` и `RagService`. `RagService` выбирает docs/code pipeline, обогащает документы метаданными файла, запрашивает embeddings и записывает результат через `RagRepository`. `RagRepository` агрегирует schema/session/job/document/cache/query репозитории. Отдельно `RagRepoModule` принимает repository webhooks и прокидывает нормализованный commit context в story storage и cache writer.
## Основные модули
| module | responsibility | depends_on | key_code_refs |
| ---------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------- |
| `RagModule` | сборка зависимостей, публичный и internal API | `RagService`, `IndexingOrchestrator`, `RagSessionStore`, `IndexJobStore` | `src/app/modules/rag/module.py` |
| `RagService` | синхронная бизнес-логика индексации файлов и cache reuse | docs/code pipeline, embedder, `RagRepository` | `src/app/modules/rag/services/rag_service.py` |
| `IndexingOrchestrator` | асинхронный job lifecycle, retry, project lock, EventBus | `IndexJobStore`, `RagIndexer`, `EventBus`, `RetryExecutor` | `src/app/modules/rag/indexing_service.py` |
| `DocsIndexingPipeline` | построение слоёв документации `D1-D4` | classifier, chunker, document builder | `src/app/modules/rag/indexing/docs/pipeline.py` |
| `CodeIndexingPipeline` | построение слоёв кода `C0-C4` | AST parser, symbol/edge/entrypoint/role builders | `src/app/modules/rag/indexing/code/pipeline.py` |
| `RagRepository` | единая точка persistence и retrieval | schema/session/job/document/cache/query repositories | `src/app/modules/rag/persistence/repository.py` |
| `RepoWebhookService` | нормализация webhook payload и выделение story id | story writer, cache writer | `src/app/modules/rag/webhook_service.py` |
## Основные доменные области
- RAG session как граница индекса конкретного проекта или его временного снапшота.
- Index job как жизненный цикл асинхронной индексации и канал наблюдения за прогрессом.
- RagDocument как атом индекса, который попадает в retrieval-хранилище и в cache.
- Repo webhook context как источник commit metadata для story и cache.
## Основные интеграции
| integration | direction | purpose | protocol / transport | related_docs |
| ------------------------ | --------- | --------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------- |
| PostgreSQL + pgvector | outbound | хранение документов, jobs, sessions и vector search | SQLAlchemy / SQL / pgvector | `logic-rag-retrieval` |
| GigaChat embeddings | outbound | получение embedding для batch документов | HTTP client через `GigaChatClient` | `logic-rag-indexing` |
| FastAPI | inbound | публичный и internal API модуля | HTTP | `api-rag-session-create`, `api-rag-session-changes`, `api-rag-session-job` |
| EventBus | outbound | публикация прогресса индексации и terminal events | in-process async events / SSE | `api-rag-session-job` |
| Story context repository | outbound | запись webhook-коммитов для story | Python interface | `logic-rag-indexing` |
## Основные потоки
### Flow 1
1. Клиент вызывает `POST /api/rag/sessions` с `project_id` и snapshot файлов.
2. `RagSessionStore` создаёт `rag_session_id`, а `IndexingOrchestrator` создаёт `IndexJob`.
3. `RagService` фильтрует файлы, переиспользует cache по `blob_sha` или строит docs/code документы заново.
4. Документы векторизуются, записываются в `rag_chunks`, а job получает финальный статус `done` или `error`.
### Flow 2
1. Клиент вызывает `POST /api/rag/sessions/{rag_session_id}/changes`.
2. `IndexingOrchestrator` сериализует обработку по `rag_session_id`.
3. `RagService` удаляет документы по `delete_paths`, пересобирает upsert-файлы и применяет изменения к индексу.
4. Клиент читает статус и события задачи через job endpoints.
## Архитектурные решения и ограничения
### Key decisions
- Snapshot и incremental indexing используют один и тот же `RagService`, различаясь только стратегией записи.
- Кэш документов привязан к `repo_id + blob_sha`, а не к `rag_session_id`, что позволяет переиспользовать embeddings между сессиями одного проекта.
- Документация и код индексируются разными pipeline, но сохраняются в общую таблицу `rag_chunks`.
- Асинхронность вынесена в `IndexingOrchestrator`, чтобы `RagService` оставался application-service без управления job lifecycle.
### Constraints
- Code indexing поддерживает только Python-файлы.
- Docs indexing ориентирован на markdown и frontmatter YAML.
- Deprecated endpoint `/internal/rag/retrieve` не используется для рабочего retrieval.
- Реальное retrieval API доступно через repository/runtime adapters, а не через публичный HTTP endpoint модуля.
### Risks
- Ошибки embeddings или временные сетевые сбои переводят job в `error` только после исчерпания retry.
- Полное `replace_documents` для snapshot удаляет все документы сессии перед вставкой новых.
- Retrieval ranking завязан на SQL-эвристики по layer, lexical match и metadata, поэтому качество зависит от корректности metadata builders.
## Нефункциональные аспекты
### Security
- Публичные endpoints не содержат собственной бизнес-авторизации внутри модуля и полагаются на внешний слой приложения.
- Webhook provider определяется по headers/payload без явной проверки подписи в самом `RepoWebhookService`.
### Reliability
- Проектный `asyncio.Lock` предотвращает параллельную индексацию одной `rag_session`.
- `RetryExecutor` повторяет временные сбои индексации.
### Observability
- Logs: `RagService` пишет предупреждения по cache hit/miss и skipped files.
- Metrics: явные метрики не выделены.
- Traces: явная трассировка не реализована.
- Audit: job status и webhook commit binding сохраняются в БД.
### Performance
- Embeddings отправляются батчами с размером из `RAG_EMBED_BATCH_SIZE`.
- Cache reuse исключает повторную векторизацию неизменённых blob.
### Scalability
- Индекс хранится на уровне SQL-таблиц с векторными полями и индексами по session/layer/path.
- При росте объёма данных узким местом остаются полнотабличные delete/insert по snapshot и SQL sorting retrieval.
## Связанные сущности
- `RagSession`
- `IndexJob`
- `RagDocument`
## Связанный код
### Files
- `src/app/modules/rag/module.py`
- `src/app/modules/rag/services/rag_service.py`
- `src/app/modules/rag/indexing_service.py`
- `src/app/modules/rag/persistence/repository.py`
- `src/app/modules/rag/persistence/schema_repository.py`
- `src/app/modules/rag/webhook_service.py`
### Symbols
- `RagModule`
- `RagRepoModule`
- `RagService`
- `IndexingOrchestrator`
- `RagRepository`
- `RepoWebhookService`
## Связанные документы
- `logic-rag-indexing`
- `logic-rag-retrieval`
- `entity-rag-session`
- `entity-rag-index-job`
- `api-rag-session-create`
- `api-rag-session-changes`
- `api-rag-session-job`
## История изменений
| Date | Source | Changes |
| ---------- | ------ | ------------------------------------------------------------------- |
| 2026-03-13 | code | Создан обзор архитектуры пакета `rag` на основе текущей реализации. |
+154
View File
@@ -0,0 +1,154 @@
---
id: entity-rag-index-job
title: Сущность IndexJob
doc_type: domain_entity
domain: rag
status: draft
owner: system-analyst
source_of_truth: code
related_docs:
- arch-rag-package
- logic-rag-indexing
- entity-rag-session
- api-rag-session-job
related_code:
- src/app/modules/rag/job_store.py
- src/app/modules/rag/indexing_service.py
- src/app/modules/rag/persistence/job_repository.py
- src/app/modules/rag/persistence/schema_repository.py
entities:
- IndexJob
- RagSession
tags:
- rag
- indexing
- job
- domain-entity
---
# Сущность IndexJob
## Summary
- Domain: rag
- Purpose: представить асинхронную задачу индексации и её наблюдаемый статус.
- Entity role: operational entity для выполнения snapshot/change indexing.
- Main attributes: `index_job_id`, `rag_session_id`, `status`, `indexed_files`, `failed_files`, `cache_hit_files`, `cache_miss_files`, `error`.
- Lifecycle: `queued -> running -> done|error`.
- Invariants: job всегда принадлежит одной `RagSession`, статус хранится как enum `IndexJobStatus`.
- Related APIs: создание job косвенно через session endpoints, чтение через job status endpoint и SSE endpoint.
- Related logic: `IndexingOrchestrator`, retry, EventBus publishing.
- Source of truth: `src/app/modules/rag/job_store.py`, `src/app/modules/rag/indexing_service.py`.
## Назначение
`IndexJob` хранит технический прогресс и итог выполнения индексации. Он нужен, чтобы API модуля мог вернуть результат не синхронно, а через опрос статуса и подписку на события.
## Контекст
Job создаётся на каждую snapshot- или changes-операцию. Сервис индексации обновляет его counters и публикует события прогресса в EventBus под ключом `index_job_id`.
## Роль в доменной модели
Это операционная сущность, которая связывает пользовательский запрос на индексацию с фактическим процессом обработки файлов. Она не хранит сам индекс, но управляет прозрачностью выполнения и ошибками.
## Атрибуты
| attribute | type | required | description | constraints |
|-----------|------|----------|-------------|-------------|
| `index_job_id` | `str` | yes | уникальный идентификатор задачи | primary key, non-empty |
| `rag_session_id` | `str` | yes | ссылка на целевую RAG-сессию | non-empty |
| `status` | `IndexJobStatus` | yes | текущее состояние задачи | `queued`, `running`, `done`, `error` |
| `indexed_files` | `int` | yes | число успешно обработанных файлов | `>= 0` |
| `failed_files` | `int` | yes | число файлов с ошибками | `>= 0` |
| `cache_hit_files` | `int` | yes | число файлов, обслуженных из cache | `>= 0` |
| `cache_miss_files` | `int` | yes | число файлов, потребовавших embeddings | `>= 0` |
| `error` | `ErrorPayload \| None` | no | информация о необработанной временной ошибке после retry | optional |
## Состояния и жизненный цикл
### Основные состояния
- `queued`
- `running`
- `done`
- `error`
### Переходы состояний
1. `IndexJobStore.create` создаёт job в состоянии `queued`.
2. `IndexingOrchestrator._run_with_project_lock` переводит job в `running`.
3. Успешная индексация переводит job в `done` и заполняет counters.
4. Ошибка после исчерпания retry переводит job в `error` и заполняет `ErrorPayload`.
## Инварианты и ограничения
- Job не мигрирует между `rag_session_id`.
- Финальные counters сохраняются в БД перед публикацией terminal event.
- Ошибки уровня `TimeoutError`, `ConnectionError`, `OSError` считаются временными и оборачиваются в `index_retry_exhausted` только после retry exhaustion.
## Связи с другими сущностями
| entity | relation | description |
|--------|----------|-------------|
| `RagSession` | many-to-one | каждая задача относится к одной сессии |
| `RagDocument` | indirect | job обновляет набор документов сессии, но не владеет ими напрямую |
## Использование в системе
### Related API
- `POST /api/rag/sessions`
- `POST /api/rag/sessions/{rag_session_id}/changes`
- `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}`
- `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}/events`
### Related UI
- Прямого UI в репозитории не обнаружено.
### Related logic
- `logic-rag-indexing`
### Related integrations
- EventBus SSE stream
- PostgreSQL таблица `rag_index_jobs`
## Функциональные требования
- Job должна создаваться до запуска фоновой задачи.
- Публичный API обязан проверять принадлежность job указанной `rag_session_id`.
- Progress events должны публиковаться в формате, достаточном для фронта или внешнего клиента.
## Нефункциональные требования
### Audit / history
- `created_at` и `updated_at` сохраняются в таблице `rag_index_jobs`.
### Security
- Доступ к job опирается на проверку связи `job.rag_session_id == requested rag_session_id`.
### Observability
- SSE stream отдаёт `index_status`, `index_progress`, `terminal`.
## Связанный код
### Files
- `src/app/modules/rag/job_store.py`
- `src/app/modules/rag/indexing_service.py`
- `src/app/modules/rag/persistence/job_repository.py`
- `src/app/modules/rag/persistence/schema_repository.py`
### Symbols
- `IndexJob`
- `IndexJobStore.create`
- `IndexJobStore.get`
- `IndexJobStore.save`
- `IndexingOrchestrator._run_with_project_lock`
## Связанные документы
- `arch-rag-package`
- `logic-rag-indexing`
- `entity-rag-session`
- `api-rag-session-job`
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| 2026-03-13 | code | Добавлено описание lifecycle и контракта сущности `IndexJob`. |
@@ -0,0 +1,143 @@
---
id: entity-rag-session
title: Сущность RagSession
doc_type: domain_entity
domain: rag
status: draft
owner: system-analyst
source_of_truth: code
related_docs:
- arch-rag-package
- logic-rag-indexing
- api-rag-session-create
- api-rag-session-changes
- api-rag-session-job
related_code:
- src/app/modules/rag/session_store.py
- src/app/modules/rag/persistence/session_repository.py
- src/app/modules/rag/persistence/schema_repository.py
entities:
- RagSession
tags:
- rag
- session
- domain-entity
---
# Сущность RagSession
## Summary
- Domain: rag
- Purpose: связать индекс и связанные job с конкретным проектом или его рабочим снимком.
- Entity role: корневая сущность области RAG indexing/retrieval.
- Main attributes: `rag_session_id`, `project_id`, `created_at`.
- Lifecycle: создаётся до первой индексации и используется как scope всех retrieval-запросов.
- Invariants: `rag_session_id` уникален, `project_id` обязателен.
- Related APIs: `POST /api/rag/sessions`, `POST /api/rag/sessions/{rag_session_id}/changes`, `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}`.
- Related logic: snapshot indexing, change indexing, retrieval filtering.
- Source of truth: `src/app/modules/rag/session_store.py`, таблица `rag_sessions`.
## Назначение
`RagSession` определяет границу индекса для проекта. Все документы, задачи и retrieval-запросы внутри `rag` привязаны к этой сущности.
## Контекст
Сессия используется и в новом API с UUID, и в legacy/internal режиме, где `project_id` может совпадать с `rag_session_id`. Через неё сервис восстанавливает `repo_id`, который затем участвует в кэшировании документов.
## Роль в доменной модели
`RagSession` является владельцем набора индексированных документов и асинхронных `IndexJob`. Без неё нельзя безопасно выполнять reindex или retrieval, потому что именно она задаёт scope таблицы `rag_chunks`.
## Атрибуты
| attribute | type | required | description | constraints |
|-----------|------|----------|-------------|-------------|
| `rag_session_id` | `str` | yes | уникальный идентификатор сессии | primary key, non-empty |
| `project_id` | `str` | yes | идентификатор проекта или workspace | non-empty |
| `created_at` | `timestamp with time zone` | yes | время создания записи в БД | default `CURRENT_TIMESTAMP` |
## Состояния и жизненный цикл
### Основные состояния
- created
- active
- reused via legacy/internal API
### Переходы состояний
1. `RagSessionStore.create(project_id)` создаёт новую сессию с UUID.
2. `RagSessionStore.put(rag_session_id, project_id)` создаёт или обновляет сессию с заданным ключом.
3. После создания сессия используется для indexing и retrieval до тех пор, пока не будет заменена новым идентификатором на уровне вызывающего сервиса.
## Инварианты и ограничения
- `project_id` не должен быть пустым.
- Для retrieval и indexing используется только один `rag_session_id` за операцию.
- `RagService._resolve_repo_id` использует `project_id` этой сущности как `repo_id` для cache scope.
## Связи с другими сущностями
| entity | relation | description |
|--------|----------|-------------|
| `IndexJob` | one-to-many | одна сессия может иметь много задач индексации |
| `RagDocument` | one-to-many | все записи в `rag_chunks` привязаны к одной сессии |
## Использование в системе
### Related API
- `POST /api/rag/sessions`
- `POST /api/rag/sessions/{rag_session_id}/changes`
- `GET /api/rag/sessions/{rag_session_id}/jobs/{index_job_id}`
### Related UI
- Прямого UI в репозитории не обнаружено.
### Related logic
- `logic-rag-indexing`
- `logic-rag-retrieval`
### Related integrations
- PostgreSQL таблица `rag_sessions`
## Функциональные требования
- Сессия должна создаваться до постановки snapshot job.
- При change indexing запрос должен ссылаться на существующую сессию в новом публичном API.
- Legacy/internal API может создавать запись с предсказуемым `rag_session_id`, равным `project_id`.
## Нефункциональные требования
### Audit / history
- Время создания фиксируется в таблице `rag_sessions`.
### Security
- Отдельных прав доступа на уровне сущности внутри модуля нет.
### Observability
- Основная наблюдаемость сессии идёт через связанные `IndexJob`.
## Связанный код
### Files
- `src/app/modules/rag/session_store.py`
- `src/app/modules/rag/persistence/session_repository.py`
- `src/app/modules/rag/persistence/schema_repository.py`
### Symbols
- `RagSession`
- `RagSessionStore.create`
- `RagSessionStore.put`
- `RagSessionStore.get`
## Связанные документы
- `arch-rag-package`
- `logic-rag-indexing`
- `api-rag-session-create`
- `api-rag-session-changes`
- `api-rag-session-job`
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| 2026-03-13 | code | Добавлено описание сущности `RagSession` и её роли в границах индекса. |
@@ -0,0 +1,166 @@
---
id: logic-rag-indexing
title: Индексация файлов в RAG
doc_type: logic_block
domain: rag
status: draft
owner: system-analyst
source_of_truth: code
related_docs:
- arch-rag-package
- entity-rag-session
- entity-rag-index-job
- api-rag-session-create
- api-rag-session-changes
related_code:
- src/app/modules/rag/indexing_service.py
- src/app/modules/rag/services/rag_service.py
- src/app/modules/rag/indexing/docs/pipeline.py
- src/app/modules/rag/indexing/code/pipeline.py
- src/app/modules/rag/persistence/document_repository.py
entities:
- RagSession
- IndexJob
- RagDocument
tags:
- rag
- indexing
- snapshot
- changes
---
# Индексация файлов в RAG
## Summary
- Purpose: превратить входной список файлов в набор индексируемых `RagDocument` и сохранить их в persistence.
- Trigger: создание RAG-сессии, запрос изменений, internal snapshot/changes endpoints.
- Inputs: `rag_session_id`, файлы snapshot или changed_files, progress callback.
- Outputs: обновлённые записи в `rag_chunks`, `rag_session_chunk_map`, статистика indexed/failed/cache hit/cache miss.
- Main entities: `RagSession`, `IndexJob`, `RagDocument`.
- Main dependencies: `IndexingOrchestrator`, `RagService`, `DocsIndexingPipeline`, `CodeIndexingPipeline`, `GigaChatEmbedder`, `RagRepository`.
- Side effects: SQL delete/insert, cache write, SSE events, job status update.
- Source of truth: `src/app/modules/rag/indexing_service.py`, `src/app/modules/rag/services/rag_service.py`.
## Назначение
Блок обеспечивает управляемую индексацию файлов проекта в многослойный RAG-индекс. Он должен одинаково поддерживать полный snapshot и инкрементальные изменения, не допуская гонок внутри одной `rag_session_id`.
## Контекст
Индексация запускается через HTTP API модуля. `IndexingOrchestrator` отвечает за job lifecycle и progress events, а `RagService` за фактическую переработку файлов в документы. Для уменьшения стоимости embeddings используется cache по содержимому blob.
## Технический use case
### Основной сценарий
1. API создаёт `IndexJob` и передаёт управление в `IndexingOrchestrator`.
2. `IndexingOrchestrator` фильтрует входной набор, ставит статус `running`, публикует стартовое событие и захватывает lock по `rag_session_id`.
3. `RagService` определяет `repo_id`, фильтрует индексируемые файлы, проверяет cache по `blob_sha` и либо переиспользует документы, либо заново строит docs/code слои.
4. Для новых документов `RagService` добавляет file metadata, запрашивает embeddings батчами и сохраняет документы через `RagRepository`.
5. `IndexingOrchestrator` обновляет job counters, публикует финальный `index_status` и `terminal`.
### Альтернативные ветки
- Для `changes` операции с `op=delete` только удаляют документы по `path`, без повторной сборки.
- Если файл не поддержан ни docs-, ни code-pipeline, сервис делает fallback в docs pipeline.
- При временном сбое индексации `RetryExecutor` повторяет операцию; после исчерпания попыток job получает `error`.
## Функциональные требования
### Preconditions
- `rag_session_id` уже существует либо создаётся до запуска indexing job.
- Файлы передаются в виде словарей, совместимых со схемами `FileSnapshot` или `ChangedFile`.
- Для cache reuse у файла должен быть `content_hash` или доступный `content`.
### Processing rules
- Snapshot перед записью выполняет полную замену документов сессии через `replace_documents`.
- Incremental changes отделяет `delete_paths` от upsert-файлов и применяет изменения через `apply_document_changes`.
- `repo_id` выводится из `project_id` у сессии, а при отсутствии сессии fallback равен `rag_session_id`.
- Для поддерживаемого markdown строятся docs-слои `D1-D4`.
- Для поддерживаемого Python-кода строятся code-слои `C0-C4`.
- Каждый документ получает metadata файла: `blob_sha`, `repo_id`, `artifact_type`, `section`, `doc_id`, `owner`, `system_component`, `last_modified`, `staleness_score`.
### Validation rules
- До индексации snapshot/changes проходят фильтрацию через `filter_snapshot_files` и `filter_changes_for_indexing`.
- Пустые или неподходящие файлы исключаются из обрабатываемого набора.
- Вектор сохраняется только если размерность embedding совпадает с размерностью поля `vector` при retrieval.
### Output / result rules
- Результат операции всегда выражается четырьмя счётчиками: `indexed_files`, `failed_files`, `cache_hit_files`, `cache_miss_files`.
- Для snapshot весь набор документов сессии после операции должен соответствовать текущему переданному snapshot.
- Для changes в индексе должны остаться только документы по актуальному состоянию изменённых путей.
### Side effects
- Удаление и вставка строк в `rag_chunks`.
- Запись `rag_session_chunk_map` для документов, имеющих `repo_id` и `blob_sha`.
- Сохранение cache в `rag_blob_cache` и `rag_chunk_cache`.
- Публикация SSE-событий прогресса и завершения задачи.
## Ограничения и условия вызова
- Одновременно может выполняться только одна indexing operation на `rag_session_id`.
- Code indexing работает только для Python файлов, распознаваемых `PythonFileFilter`.
- Docs indexing рассчитывает на markdown с возможным YAML frontmatter.
- Метод `replace_documents` делает жёсткую замену индекса сессии и не подходит для конкурентного merge разных snapshot-источников.
## Нефункциональные требования
### Security
- Модуль не валидирует источник файлов и не выполняет контентную санацию сверх собственных парсеров.
### Observability
- Logs: фиксируются skipped files и режим обработки `cache` / `embed`.
- Metrics: отдельные счётчики не выделены, но статистика сохраняется в job.
- Traces: не реализованы.
- Audit: `rag_index_jobs` и `rag_session_chunk_map` образуют журнал выполнения и происхождения chunk.
### Reliability
- `asyncio.Lock` сериализует операции в рамках одной сессии.
- `RetryExecutor` покрывает временные ошибки `TimeoutError`, `ConnectionError`, `OSError`.
### Performance
- Embeddings обрабатываются батчами.
- Cache hit исключает повторный парсинг и повторный вызов embedder.
## Связанные API / UI / integration points
- `POST /api/rag/sessions`
- `POST /api/rag/sessions/{rag_session_id}/changes`
- `POST /internal/rag/index/snapshot`
- `POST /internal/rag/index/changes`
## Связанные сущности
- `RagSession`
- `IndexJob`
- `RagDocument`
## Связанный код
### Files
- `src/app/modules/rag/indexing_service.py`
- `src/app/modules/rag/services/rag_service.py`
- `src/app/modules/rag/indexing/docs/pipeline.py`
- `src/app/modules/rag/indexing/code/pipeline.py`
- `src/app/modules/rag/persistence/document_repository.py`
### Symbols
- `IndexingOrchestrator.enqueue_snapshot`
- `IndexingOrchestrator.enqueue_changes`
- `IndexingOrchestrator._run_with_project_lock`
- `RagService.index_snapshot`
- `RagService.index_changes`
- `RagService._index_files`
## Связанные документы
- `arch-rag-package`
- `entity-rag-session`
- `entity-rag-index-job`
- `api-rag-session-create`
- `api-rag-session-changes`
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| 2026-03-13 | code | Описана фактическая логика snapshot и incremental indexing пакета `rag`. |
@@ -0,0 +1,150 @@
---
id: logic-rag-retrieval
title: Retrieval и ранжирование RAG-документов
doc_type: logic_block
domain: rag
status: draft
owner: system-analyst
source_of_truth: code
related_docs:
- arch-rag-package
- entity-rag-session
related_code:
- src/app/modules/rag/persistence/repository.py
- src/app/modules/rag/persistence/query_repository.py
- src/app/modules/rag/persistence/retrieval_statement_builder.py
- src/app/modules/rag/contracts/enums.py
entities:
- RagSession
- RagDocument
tags:
- rag
- retrieval
- ranking
- pgvector
---
# Retrieval и ранжирование RAG-документов
## Summary
- Purpose: вернуть релевантные RAG-документы из `rag_chunks` для заданной сессии и набора фильтров.
- Trigger: вызовы runtime adapters и внутренних retrieval-компонентов.
- Inputs: `rag_session_id`, `query_embedding`, `query_text`, `layers`, path filters, preference filters, limit.
- Outputs: список rows с `path`, `content`, `layer`, `title`, `metadata`, `span_start`, `span_end`, ranking fields.
- Main entities: `RagSession`, `RagDocument`.
- Main dependencies: `RagRepository`, `RagQueryRepository`, `RetrievalStatementBuilder`, PostgreSQL/pgvector.
- Side effects: отсутствуют, retrieval только читает БД.
- Source of truth: `src/app/modules/rag/persistence/query_repository.py`, `src/app/modules/rag/persistence/retrieval_statement_builder.py`.
## Назначение
Блок retrieval выбирает из индекса наиболее полезные документы по конкретному `rag_session_id`. Он объединяет в одном SQL векторную близость, lexical match, слой документа и структурные сигналы из metadata.
## Контекст
Публичный HTTP endpoint retrieval внутри `rag` помечен как deprecated, поэтому рабочий доступ к retrieval идёт через repository/runtime adapters. Это означает, что контракт фактически определяется SQL-builder и форматом rows, возвращаемых `RagQueryRepository`.
## Технический use case
### Основной сценарий
1. Клиент runtime вызывает `RagRepository.retrieve(...)`.
2. `RagQueryRepository` строит SQL через `RetrievalStatementBuilder.build_retrieve`.
3. SQL ограничивает поиск текущей `rag_session_id`, при необходимости слоями и path-фильтрами.
4. База сортирует документы по `prefer_bonus`, `test_penalty`, `layer_rank`, `lexical_rank`, `structural_rank`, `distance`.
5. Репозиторий возвращает rows с распарсенным `metadata_json`.
### Альтернативные ветки
- Для lexical fallback по коду используется `retrieve_lexical_code`, который работает только по слою `C0_SOURCE_CHUNKS`.
- Для точного добора файлов используется `retrieve_exact_files`, который читает заданные `path` без векторного ранжирования.
- Если `query_text` не даёт terms, lexical retrieval возвращает пустой результат без выполнения SQL.
## Функциональные требования
### Preconditions
- В `rag_chunks` уже должны существовать документы нужной `rag_session_id`.
- Для vector retrieval embedding документа должен быть ненулевым и совпадать по размерности с query embedding.
### Processing rules
- Базовый фильтр retrieval всегда включает `rag_session_id = :sid`.
- При наличии `layers` запрос ограничивается указанными слоями.
- `path_prefixes` задают include-фильтр по `LIKE prefix%`.
- `exclude_path_prefixes` и `exclude_like_patterns` исключают части дерева путей до сортировки.
- `prefer_path_prefixes` и `prefer_like_patterns` формируют `prefer_bonus`, поднимая приоритет совпавших путей.
- `prefer_non_tests` создаёт `test_penalty`, если путь попадает под test-паттерны.
### Validation rules
- Path filters экранируются для корректной работы `LIKE`.
- `retrieve_exact_files` нормализует и отбрасывает пустые пути до построения SQL.
- `retrieve_lexical_code` не выполняет SQL, если query terms отсутствуют.
### Output / result rules
- Каждый row содержит контент документа и технические поля ранжирования.
- `metadata_json` всегда декодируется в словарь `metadata`.
- Limit применяется на уровне SQL и ограничивает итоговый набор строк.
### Side effects
- Побочных эффектов нет, кроме чтения из БД.
## Ограничения и условия вызова
- Retrieval работает только внутри одной `rag_session_id` и не агрегирует несколько сессий.
- Layer ranking зашит в код SQL-builder и требует явного обновления при появлении новых слоёв.
- Полноценный HTTP retrieval endpoint в модуле отсутствует: `/internal/rag/retrieve` возвращает `410 deprecated`.
## Нефункциональные требования
### Security
- Retrieval не выполняет маскирование содержимого документов.
### Observability
- Logs: отдельное логирование запросов retrieval не реализовано.
- Metrics: метрики по latency и quality не выделены.
- Traces: отсутствуют.
- Audit: результат зависит только от состояния `rag_chunks` и входных фильтров.
### Reliability
- Пустой или некорректный lexical search безопасно возвращает пустой набор.
- `retrieve_exact_files` работает без embeddings и может использоваться как fallback.
### Performance
- Основной ranking выполняется в одном SQL-запросе.
- Для vector retrieval используются поля `embedding` и индексы по session/layer/path.
## Связанные API / UI / integration points
- Runtime retrieval adapters в `src/app/modules/agent/runtime/steps/retrieval/adapter.py`
- Explain retrieval gateway в `src/app/modules/agent/runtime/steps/explain/layered_gateway.py`
- Deprecated endpoint `POST /internal/rag/retrieve`
## Связанные сущности
- `RagSession`
- `RagDocument`
## Связанный код
### Files
- `src/app/modules/rag/persistence/repository.py`
- `src/app/modules/rag/persistence/query_repository.py`
- `src/app/modules/rag/persistence/retrieval_statement_builder.py`
- `src/app/modules/rag/contracts/enums.py`
### Symbols
- `RagRepository.retrieve`
- `RagRepository.retrieve_lexical_code`
- `RagRepository.retrieve_exact_files`
- `RagQueryRepository.retrieve`
- `RetrievalStatementBuilder.build_retrieve`
- `RetrievalStatementBuilder.build_lexical_code`
## Связанные документы
- `arch-rag-package`
- `entity-rag-session`
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| 2026-03-13 | code | Описан фактический retrieval contract и ranking SQL для пакета `rag`. |