Compare commits

11 Commits

1526 changed files with 34575 additions and 553190 deletions
+14
View File
@@ -0,0 +1,14 @@
# Analysis Assets
Этот каталог содержит служебные артефакты для аналитической и генеративной работы агента.
## Структура
- `rules/` — правила построения документации, frontmatter и шаблоны документов.
## Назначение
Каталог `.analysis/` отделен от `docs/`, чтобы:
- хранить служебные policy- и template-материалы вне пользовательской документации;
- передавать правила в LLM как отдельный policy-context;
- не смешивать документацию проекта и внутренние артефакты анализа.
+32
View File
@@ -0,0 +1,32 @@
# Documentation Rules
## Назначение
Этот файл фиксирует общие правила формирования, обновления и поддержки технической документации проекта.
Документация проекта должна создаваться как система атомарных, связанных между собой документов, пригодных:
- для чтения человеком;
- для сопровождения командой;
- для индексирования в RAG;
- для автоматического обновления агентом на основе кода и существующих артефактов.
Этот файл задает:
- общие принципы документационной архитектуры;
- правила декомпозиции документации;
- правила размещения файлов;
- требования к связям между документами;
- требования к качеству markdown-документов;
- правила генерации и обновления документации агентом.
Детальные шаблоны документов и правила frontmatter описываются отдельно:
- `.analysis/rules/frontmatter-rules.md`
- `.analysis/rules/templates/*.md`
---
## Область действия
Правила из этого файла применяются ко всей проектной документации, размещаемой в:
```text
docs/documentation/
+60
View File
@@ -0,0 +1,60 @@
# Frontmatter Rules
## Назначение
Этот файл фиксирует правила YAML frontmatter для документов в `docs/documentation/`.
Frontmatter обязателен для каждого markdown-документа и нужен для:
- идентификации документа;
- определения типа документа;
- фиксации связей с кодом и другими документами;
- выделения сущностей, тегов и домена;
- поддержки индексирования в RAG.
Общие правила построения документации описаны в:
- `.analysis/rules/documentation-rules.md`
Шаблоны markdown body описаны в:
- `.analysis/rules/templates/*.md`
---
## Общие правила
1. Frontmatter размещается в начале файла.
2. Формат — YAML между двумя строками `---`.
3. Все документы в `docs/documentation/` должны содержать frontmatter.
4. Поля должны быть стабильными и заполняться единообразно.
5. Не использовать произвольные поля без необходимости.
6. Если значение неизвестно и его нельзя уверенно вывести из evidence, поле лучше не заполнять, кроме обязательных полей.
7. Списковые поля должны оформляться как YAML-массивы.
8. Идентификаторы и ссылки должны быть стабильными и пригодными для машинной обработки.
---
## Базовый frontmatter
Каждый документ должен начинаться с frontmatter вида:
```yaml
---
id: api-orders-create
title: Метод создания заказа
doc_type: api_method
domain: orders
status: draft
owner: system-analyst
source_of_truth: code
related_docs:
- ui-order-create-page
- logic-order-validation
related_code:
- src/orders/api/create_order.py
entities:
- Order
- CreateOrder
tags:
- api
- orders
- create
---
@@ -0,0 +1,29 @@
# Rule: Use Document Templates From Fixed Paths
Агент должен создавать и обновлять техническую документацию только с опорой на шаблоны документов, расположенные в `.analysis/rules`.
Если агент формирует новый документ, он обязан:
- определить тип документа;
- выбрать соответствующий шаблон по фиксированному пути;
- сохранить структуру секций и базовых метаданных из шаблона;
- заполнять только те секции, которые подтверждены кодом и артефактами;
- не придумывать новые произвольные форматы, если для типа уже существует шаблон.
Пути к базовым шаблонам:
- `.analysis/rules/legacy/template_ui_page.md`
- `.analysis/rules/legacy/template_api_method.md`
- `.analysis/rules/legacy/template_logic_block.md`
Правило выбора шаблона:
- для документа типа `ui_page` использовать `.analysis/rules/legacy/template_ui_page.md`
- для документа типа `api_method` использовать `.analysis/rules/legacy/template_api_method.md`
- для документа типа `logic_block` использовать `.analysis/rules/legacy/template_logic_block.md`
Если для нужного типа шаблон отсутствует, агент должен:
1. использовать ближайший подходящий существующий шаблон как временную основу;
2. явно сохранить тип документа в `YAML frontmatter`;
3. не смешивать в одном документе несколько независимых сущностей.
@@ -0,0 +1,89 @@
# Template: api_method
```md
---
id: api-<stable-id>
title: <Human-readable title>
doc_type: api_method
status: draft
source_of_truth: code
domain: <domain-name>
owner: system-analyst
endpoint: <METHOD /path>
auth: <auth-mode-or-unknown>
idempotent: <true-or-false>
related_docs:
- <doc-id>
related_code:
- <path/to/file>
entities:
- <EntityName>
tags:
- api
---
# <API Method Title>
## Purpose
Кратко опиши, какую системную задачу решает метод.
## Endpoint Summary
- Endpoint: `<METHOD /path>`
- Auth: `<auth-mode>`
- Idempotent: `<true/false>`
- Triggered by: `<ui/system/integration if known>`
## Technical Use Case
Опиши пошагово обработку запроса:
- вход в endpoint;
- ключевые проверки;
- вызовы логики;
- обращения к БД и внешним системам;
- формирование ответа.
## Functional Requirements
Вынеси сюда подтвержденные правила, которые дополняют основной сценарий:
- валидации;
- branching logic;
- побочные эффекты;
- ограничения по данным;
- условия ошибок.
## Request and Response Contract
Опиши контракт в кратком виде или дай ссылку на OpenAPI / контрактный файл.
## Related Logic Blocks
- [<Logic block title>](<path-or-doc-link>)
## Data Access and Integrations
- Reads DB: `<if known>`
- Writes DB: `<if known>`
- Integrates with: `<if known>`
## Non-Functional Requirements
Укажи только подтвержденные НФТ:
- timeout;
- audit;
- monitoring;
- security;
- idempotency rules.
## Related Code
- `<path/to/file>`
## Related Documents
- [<Related document>](<path-or-doc-link>)
```
@@ -0,0 +1,71 @@
# Template: logic_block
```md
---
id: logic-<stable-id>
title: <Human-readable title>
doc_type: logic_block
status: draft
source_of_truth: code
domain: <domain-name>
owner: system-analyst
related_docs:
- <doc-id>
related_code:
- <path/to/file>
entities:
- <EntityName>
tags:
- logic
---
# <Logic Block Title>
## Purpose
Кратко опиши, какую переиспользуемую или устойчивую логику реализует блок.
## Where Used
- Called from: `<ui/api/jobs/services if known>`
- Used by: `<list of known callers>`
## Technical Use Case
Опиши пошагово, как работает логический блок:
- входные данные;
- ключевые проверки;
- преобразования;
- обращения к данным;
- результат работы.
## Functional Requirements
Вынеси сюда устойчивые правила и ограничения:
- бизнес-правила;
- проверки;
- ветвления;
- ограничения на вход и выход;
- условия отказа.
## Dependencies
- Uses logic: `<other logic blocks if known>`
- Reads DB: `<if known>`
- Writes DB: `<if known>`
- Integrates with: `<if known>`
## Error Cases
Опиши значимые ошибки и условия их возникновения, если они подтверждены кодом.
## Related Code
- `<path/to/file>`
## Related Documents
- [<Related document>](<path-or-doc-link>)
```
@@ -0,0 +1,82 @@
# Template: ui_page
```md
---
id: ui-<stable-id>
title: <Human-readable title>
doc_type: ui_page
status: draft
source_of_truth: code
domain: <domain-name>
owner: system-analyst
related_docs:
- <doc-id>
related_code:
- <path/to/file>
entities:
- <EntityName>
tags:
- ui
---
# <Page Title>
## Purpose
Кратко опиши, какую пользовательскую задачу решает страница.
## Route and Entry Points
- Route: `<route-if-known>`
- Entry points: `<where user comes from>`
## Technical Use Case
Опиши пошаговый сценарий работы страницы как поток действий и системных реакций.
## UI Structure
Перечисли основные UI-элементы и для каждого укажи:
- назначение;
- источник данных;
- значение по умолчанию или placeholder;
- условия доступности или активации;
- поведение при взаимодействии;
- правила валидации.
## Functional Requirements
Вынеси сюда детальные правила, которые не стоит перегружать в use case:
- вызовы API;
- обработку ответов;
- локальные правила отображения;
- условия переходов;
- feature toggles.
## Related APIs
- [<API document title>](<path-or-doc-link>)
## Related Logic Blocks
- [<Logic block title>](<path-or-doc-link>)
## Non-Functional Requirements
Укажи НФТ, если они подтверждены:
- analytics events;
- observability;
- feature toggles;
- security constraints.
## Related Code
- `<path/to/file>`
## Related Documents
- [<Related document>](<path-or-doc-link>)
```
+115
View File
@@ -0,0 +1,115 @@
# {{title}}
## Summary
- Purpose:
- Actor:
- Trigger:
- Endpoint:
- Main entities:
- Main logic:
- Main errors:
- Source of truth:
## Назначение
## Контекст
## Технический use case
### Основной сценарий
1.
2.
3.
### Альтернативные ветки
-
-
## Функциональные требования
### Request validation
-
### Processing rules
-
### State changes
-
### Side effects
-
## Contract
### Endpoint
- Method:
- Path:
- Auth:
- Idempotent:
- Timeout:
- Retry:
### Request
| Field | Type | Required | Constraints | Description |
|------|------|----------|-------------|-------------|
| | | | | |
### Response
| Field | Type | Description |
|------|------|-------------|
| | | |
### External contract refs
- OpenAPI:
- Schema:
- DTO / serializer:
- Additional refs:
## Errors
| error_id | http_code | when | client_behavior | retry |
|----------|-----------|------|-----------------|-------|
| | | | | |
## Нефункциональные требования
### Security
-
### Observability
- Logs:
- Metrics:
- Traces:
- Audit:
### Reliability
-
-
### Performance
-
## Связанные блоки логики
-
## Связанные сущности
-
## Связанный код
### Files
-
### Symbols
-
## Связанные документы
-
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| | | |
@@ -0,0 +1,105 @@
# {{title}}
## Summary
- Scope:
- Purpose:
- Main modules:
- Main domains:
- Main integrations:
- Key entrypoints:
- Key data flows:
- Source of truth:
## Назначение
## Контекст
## Границы системы
### In scope
-
### Out of scope
-
## Архитектурная схема
## Основные модули
| module | responsibility | depends_on | key_code_refs |
|--------|----------------|------------|---------------|
| | | | |
## Основные доменные области
-
-
## Основные интеграции
| integration | direction | purpose | protocol / transport | related_docs |
|-------------|-----------|---------|----------------------|--------------|
| | | | | |
## Основные потоки
### Flow 1
1.
2.
3.
### Flow 2
1.
2.
3.
## Архитектурные решения и ограничения
### Key decisions
-
### Constraints
-
### Risks
-
## Нефункциональные аспекты
### Security
-
### Reliability
-
### Observability
- Logs:
- Metrics:
- Traces:
- Audit:
### Performance
-
### Scalability
-
## Связанные сущности
-
## Связанный код
### Files
-
### Symbols
-
## Связанные документы
-
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| | | |
@@ -0,0 +1,92 @@
# {{title}}
## Summary
- Domain:
- Purpose:
- Entity role:
- Main attributes:
- Lifecycle:
- Invariants:
- Related APIs:
- Related logic:
- Source of truth:
## Назначение
## Контекст
## Роль в доменной модели
## Атрибуты
| attribute | type | required | description | constraints |
|-----------|------|----------|-------------|-------------|
| | | | | |
## Состояния и жизненный цикл
### Основные состояния
-
### Переходы состояний
1.
2.
3.
## Инварианты и ограничения
-
-
## Связи с другими сущностями
| entity | relation | description |
|--------|----------|-------------|
| | | |
## Использование в системе
### Related API
-
### Related UI
-
### Related logic
-
### Related integrations
-
## Функциональные требования
-
-
## Нефункциональные требования
### Audit / history
-
### Security
-
### Observability
-
## Связанный код
### Files
-
### Symbols
-
## Связанные документы
-
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| | | |
+93
View File
@@ -0,0 +1,93 @@
```md
# {{title}}
## Summary
- Purpose:
- Trigger:
- Inputs:
- Outputs:
- Main entities:
- Main dependencies:
- Side effects:
- Source of truth:
## Назначение
## Контекст
## Технический use case
### Основной сценарий
1.
2.
3.
### Альтернативные ветки
-
-
## Функциональные требования
### Preconditions
-
### Processing rules
-
### Validation rules
-
### Output / result rules
-
### Side effects
-
## Ограничения и условия вызова
-
-
## Нефункциональные требования
### Security
-
### Observability
- Logs:
- Metrics:
- Traces:
- Audit:
### Reliability
-
-
### Performance
-
## Связанные API / UI / integration points
-
## Связанные сущности
-
## Связанный код
### Files
-
### Symbols
-
## Связанные документы
-
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| | | |
```
+97
View File
@@ -0,0 +1,97 @@
# {{title}}
## Summary
- Purpose:
- Actor:
- Trigger:
- Route:
- Main API:
- Main entities:
- Main logic:
- Main states:
- Source of truth:
## Назначение
## Контекст
## Технический use case
### Основной сценарий
1.
2.
3.
### Альтернативные ветки
-
-
## Описание UI
## UI Elements
| id | type | label | data_source | default / placeholder | validation | behavior |
|----|------|-------|-------------|------------------------|------------|----------|
| | | | | | | |
## Функциональные требования
### Input rules
-
### State rules
-
### Navigation rules
-
### Client-side validation
-
## Нефункциональные требования
### Security
-
### Observability
- Logs:
- Metrics:
- Traces:
- Analytics:
### Accessibility
-
### Performance
-
### Feature toggles
-
## Связанные API
-
## Связанные блоки логики
-
## Связанные сущности
-
## Связанный код
### Files
-
### Symbols
-
## Связанные документы
-
## История изменений
| Date | Source | Changes |
|------|--------|---------|
| | | |
+15
View File
@@ -0,0 +1,15 @@
---
alwaysApply: true
---
При задачах на создание или обновление документации всегда:
1. Читай .analysis/rules/documentation-rules.md, .analysis/rules/frontmatter-rules.md и нужный шаблон из .analysis/rules/templates/.
2. Создавай и обновляй документы только в docs/documentation/.
3. Не создавай дублей: сначала ищи существующий документ, потом обновляй его.
4. Соблюдай принцип: один документ = одна сущность / один устойчивый аспект.
5. Каждый документ должен иметь YAML frontmatter, обязательные разделы Summary и Details и структуру по шаблону.
6. Все связи фиксируй явно: related_docs, related_code, entities, tags и typed-поля.
7. Используй только подтвержденный evidence из кода, контрактов, конфигов и существующей документации.
8. Не дублируй содержание между документами — используй ссылки.
9. Явно указывай связанный код и связанные документы.
10. Не выдумывай факты, если evidence недостаточно.
+9
View File
@@ -1,3 +1,12 @@
.env .env
.venv .venv
__pycache__ __pycache__
# Runtime agent traces (local only; written by RequestTraceLogger)
runtime_traces/
# Pipeline harness: per-run artifacts (md/json from tests.pipeline_setup_v3/v4)
tests/**/test_runs/**/*.md
tests/**/test_runs/**/*.json
tests/**/test_results/**/*.md
tests/**/test_results/**/*.json
+25
View File
@@ -0,0 +1,25 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Agent Backend: Uvicorn (Debug)",
"type": "python",
"request": "launch",
"module": "uvicorn",
"args": [
"app.main:app",
"--host",
"0.0.0.0",
"--port",
"15000"
],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"env": {
"PYTHONPATH": "${workspaceFolder}/src"
},
"console": "integratedTerminal",
"justMyCode": false
}
]
}
+5
View File
@@ -0,0 +1,5 @@
{
"files.exclude": {
"**/__pycache__": true
}
}
+3 -2
View File
@@ -3,12 +3,13 @@ FROM python:3.12-slim
WORKDIR /app WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 \ ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 PYTHONUNBUFFERED=1 \
PYTHONPATH=/app/src
COPY requirements.txt ./ COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
COPY app ./app COPY src ./src
EXPOSE 15000 EXPOSE 15000
+5 -5
View File
@@ -915,15 +915,15 @@ flowchart TD
### 4.1.3. Канонический MVP runtime (CODE-first) ### 4.1.3. Канонический MVP runtime (CODE-first)
Единая точка входа исполнения — пакет `app.modules.agent.runtime`: Единая точка входа исполнения — пакет `app.core.agent.runtime`:
- **Роутер:** `app.modules.agent.intent_router_v2`; он отвечает и за routing, и за retrieval planning. - **Роутер:** `app.core.agent.intent_router`; он отвечает и за routing, и за retrieval planning.
- **LLM-слой:** `app.modules.agent.llm`; здесь живут `AgentLlmService`, `PromptLoader` и системные prompt assets. - **LLM-слой:** `app.core.agent.llm`; здесь живут `AgentLlmService`, `PromptLoader` и системные prompt assets.
- **Runtime:** `app.modules.agent.runtime`; внутри него stages разложены по подпакетам `retrieval`, `context`, `gates`, `answer_policy`, `generation`, `finalization`. - **Runtime:** `app.core.agent.runtime`; внутри него stages разложены по подпакетам `retrieval`, `context`, `gates`, `answer_policy`, `generation`, `finalization`.
- **Цепочка:** запрос → `IntentRouterV2` → retrieval planning → runtime retrieval adapter → нормализованный context/evidence → evidence gate 1 → answer policy → LLM generation → evidence gate 2 → finalization → diagnostics. - **Цепочка:** запрос → `IntentRouterV2` → retrieval planning → runtime retrieval adapter → нормализованный context/evidence → evidence gate 1 → answer policy → LLM generation → evidence gate 2 → finalization → diagnostics.
- **Evidence gates:** pre/post проверки достаточности evidence и качества ответа по сценарию. - **Evidence gates:** pre/post проверки достаточности evidence и качества ответа по сценарию.
- **Диагностика:** runtime возвращает machine-readable diagnostics и trace по стадиям. - **Диагностика:** runtime возвращает machine-readable diagnostics и trace по стадиям.
- **RAG:** `app.modules.rag` больше не содержит agent use-case слоев; он остается инфраструктурой indexing/retrieval/storage. - **RAG:** `app.core.rag` больше не содержит agent use-case слоев; он остается инфраструктурой indexing/retrieval/storage.
Тесты: `pipeline_setup_v3` и связанные suite-ы проверяют канонический runtime и его stage-based execution. Тесты: `pipeline_setup_v3` и связанные suite-ы проверяют канонический runtime и его stage-based execution.
+4
View File
@@ -0,0 +1,4 @@
# Запросы
1. Какие методы апи есть в проекте
2. Какие методы апи есть для healthcheck
3. Где документация на healthcheck
BIN
View File
Binary file not shown.
+126
View File
@@ -0,0 +1,126 @@
# Процессы работы с документацией (AS IS / TO BE)
## Основные артефакты системной аналитики
Системные аналитики работают с 3 артефактами:
- бизнес-требованиями
- системной аналитикой
- технической документацией
### Бизнес требования
Описывает бизнес и пользовательские требования, пользователькие use case, макеты экранов.
Сейчас не всегда оформляется как отдельный документ, часто этот шаг пропускается и требования фиксируются сразу в документе системной аналитики.
### Системная аналитика
Документ описыватет изменения в автоматизированной системе. Пишется системными аналитиками для разработчиков и тестировщиков. Так же этот документ проходит согласование с экспертами по архитектуре, безопасности, сопровождению.
Может описывать как целиком процесс (в случае реализации с нуля), так и инкремент, который вносит небольшие изменения в существующие процессы.
В данном документе содкржится вся информация по сути вносимых изменений, но отсутствует контекст о текущей реализации системы.
Состоит из разделов:
- Цели - короткое описание какую проблему и для кого решаем.
- Процесс AS IS и TO BE - фокус на изменения с точки зрения бизнес функций, без технической детализации.
- Ограничения - ограничения и допущения в реализации.
- Архитектура - описывает схему уровня контейнеров, основной фокус на интеграции между контейнерами и интеграционные сценарии.
- Функциональные требования - описывают изменения в системе.
- Нефункциональные требования - требования к аудиту, мониторингу, фичетоглам, пользовтелькой аналитике.
### Техническая документация
Техническая документация описывает реализацию системы. Эта информация используется командой разработки при проектировании и реализации новых фичей, понимании как работает система. Артефакт живет чуть впереди кода
Представялет из себя иерархическую модель документов, сейчас реализованную в конфлюенсе.
Есть несколько типов страниц, каждая из которы описывает определенный тип функциональности
- UI страницы
- API методы
- БД
- Логические блоки
#### UI страницы
Описывают экран на UI.
**Декомпозиция**
Как правило на страницу с описанием выносится целый макет/страница фронтального приложения, с одной основной интеграцией и опционально вспомогательными интеграциями.
Например - форма создания сущности. Есть вспомогательгные методы для полученяи правочников, использующихся при заполнении полей на форме, и вызов оснвного метода создания сущности.
Таким образом приложение декомпозируется на отдельные экраны, коотры свызываются между собой последовательно, но сами по себе являются независимыми
**Состав описания**
Все разделы обязательны.
Страница с описанием содержит:
- Краткое описание
- Технический use case
- Описание макета с декомпозицией на компоненты + их поведение
- Функциональные требования - описание интеграций и логики, специфичной для этой формы UI
- Нефункциональные требования - фичетоглы и события пользовательской аналитики
#### API методы
**Декомпозиция**
На каждый метод API заводится отдельная страница.
**Состав описания**
Все разделы обязательны.
Страница с описанием содержит:
- Краткое описание
- Технический use case
- Функциональные требования - описание интеграций и логики, специфичной для этой формы UI
- Нефункциональные требования - фичетоглы и события пользовательской аналитики
- Контракт метода - описание запроса и ответа. Для ответа так же приводится описание как заполнять поля.
#### БД
**Декомпозиция**
Сейсас это только странциа с описанием таблица. На каждую таблицу заводится отдельная страница.
**Состав описания**
Все разделы обязательны.
Страница с описанием содержит:
- Краткое описание
- Таблица с офисанием физической модели данных
#### Логические блоки
**Декомпозиция**
На отдельную страницу может быть вынесен общий переиспользуемый блок логики. Это позволяет не дублировать его на страницах документации. Как правило соответствует реализации общего компонента в коде.
**Состав описания**
Часть разделов в описании может отсутствовать.
- Краткое описание
- Технический use case
- Функциональные требования - описание интеграций и логики, специфичной для этой формы UI
- Нефункциональные требования - фичетоглы и события пользовательской аналитики
#### Прочие особенности процесса
##### Описание технических use cases
Сценарий описывает основные шаги процесса в разрезе участников, все технические детали, если их нельзя описать одним предложением, выносятся в разделы функциональных требований, нефункциональных требований, или даются ссылки на другие страницы (как правило это страницы с логическими блоками).
В технических use cases приводятся ссылки на страницы с описнаием вызываемых методов API. Особенно это актуально для страниц фронта, т.к. он использует наши методы API, которые есть в документации. Для интеграций с другими АС как правило приводистя ссылка на описание конфлюенса.
## AS IS
Сейчас все артефакты ведутся в конфлюенс. Одна страница содержит описанием одного аретфакта (бизнес требования, системная аналитика, страница документации), страницы организованы иерархически, используюстя ссылки для обозначения связей.
Проблемы:
- документация со временем теряет актуальность
- отсутствие автоматизации
- ручное ведение
---
## TO BE
Целевое состояние:
- аналитик продолжает писать артефакты бизнес-требований и системной аналитики
- агент генерирует и обновляет документацию по странице системной аналитики
- документация становится инженерным артефактом, который ведется в GIT
### Форматы
- Markdown
- OpenAPI
- Mermaid / PlantUML
### Роль агента
- использование документации как базы знаний - как для ответов на вопросы, так и для проектирования изменений в системе.
- внесение изменений в документацию по артефактам системной аналитики
- генерация из документации спецификаций OPENAPI и JSON-schema
+235
View File
@@ -0,0 +1,235 @@
Ниже обновленная версия с учетом гибридной модели интент роутера.
---
## 1. Концепция агента
Агент проектируется как intent-driven система для работы с кодом и документацией, где пользовательский запрос сначала нормализуется и интерпретируется, затем по нему извлекается релевантный контекст из многослойного RAG, после чего специализированный task workflow выполняет целевую задачу. Агент не является единым “умным чатом”: логика разделена на маршрутизацию, retrieval и специализированные execution workflows. Проверка evidence, вызовы LLM и правила сборки ответа находятся внутри task workflows и зависят от типа задачи.
---
## 2. Компонентная модель
```mermaid
flowchart LR
IDE[IDE Plugin / Client] --> API[API Layer]
API --> IR[IntentRouter V3]
IR --> RAG[Retrieval RAG]
RAG --> TW1[Task Workflow: Documentation Explain]
RAG --> TW2[Task Workflow: OpenAPI Generation]
RAG --> TW3[Task Workflow: Documentation Generation]
RAG --> TWN[Other Specialized Task Workflows]
TW1 --> OUT[Response / Artifact]
TW2 --> OUT
TW3 --> OUT
TWN --> OUT
```
---
## 3. Основной flow процесса
### Основной процесс
1. Пользователь отправляет запрос через IDE plugin или другой клиент.
2. `API Layer` принимает запрос и передает его в агент.
3. `IntentRouter V3`:
* нормализует запрос;
* детерминированно извлекает ключевые артефакты;
* с помощью LLM определяет тип задачи и параметры обработки;
* формирует параметры retrieval.
4. Выполняется извлечение данных из `Retrieval RAG`.
5. Извлеченный контекст передается в соответствующий `Task Workflow`.
6. Внутри workflow выполняется:
* подготовка контекста;
* evidence-проверки;
* вызовы LLM;
* формирование результата.
7. Результат возвращается пользователю.
### Sequence diagram
```mermaid
sequenceDiagram
participant User as User / IDE Plugin
participant API as API Layer
participant Router as IntentRouter V3
participant RAG as Retrieval RAG
participant WF as Task Workflow
User->>API: request
API->>Router: agent call
Router->>Router: normalize + extract artifacts
Router->>Router: LLM routing (task / intent)
Router->>RAG: retrieval request
RAG-->>Router: retrieved context
Router->>WF: route result + context
WF->>WF: evidence logic + LLM calls
WF-->>API: final result
API-->>User: response
```
---
## 4. Описание компонентов
### 4.1. IDE Plugin / Client
**Задача**
Точка входа пользователя в агент.
**Как устроен**
Любой внешний клиент (IDE plugin, web UI и др.), который отправляет запрос и получает результат.
**Почему так**
Агент изначально проектируется как backend-система, независимая от интерфейса.
---
### 4.2. API Layer
**Задача**
Обеспечивает внешний интерфейс взаимодействия с агентом.
**Как устроен**
Принимает запрос, валидирует его и передает во внутренний pipeline, затем возвращает результат.
**Почему так**
Позволяет изолировать транспортный слой от логики агента.
---
### 4.3. IntentRouter V3
**Задача**
Определяет, как должен обрабатываться пользовательский запрос и какой сценарий выполнения применить.
**Как устроен**
Гибридная модель из двух частей:
#### 1. Детерминированный слой
Выполняет:
* нормализацию запроса;
* извлечение ключевых артефактов:
* домены;
* типы сущностей (API, entity, component и т.д.);
* явные ссылки (endpoint, путь, имя);
* выделение базовых сигналов (например: explain / list / generate).
Этот слой задает **жесткие рамки интерпретации запроса**.
#### 2. LLM-роутинг
Использует:
* нормализованный запрос;
* извлеченные артефакты;
* описание доступных типов задач;
и определяет:
* тип задачи;
* общий сценарий обработки;
* параметры retrieval;
* ожидаемую форму ответа.
#### Итог
Router формирует:
* параметры retrieval;
* тип task workflow;
* контекст для дальнейшего выполнения.
**Почему решение такое**
Ранее использовался более детерминированный подход с фиксированными сценариями, который хорошо работал в узком наборе задач, но плохо масштабируется. Полностью LLM-based роутинг, наоборот, дает гибкость, но теряет предсказуемость и управляемость.
Поэтому выбран гибридный подход:
* детерминированный слой фиксирует ключевые артефакты и ограничения;
* LLM выполняет гибкую интерпретацию задачи.
Это позволяет:
* сохранить управляемость и стабильность;
* избежать взрывного роста количества сценариев;
* поддерживать сложные и нетиповые запросы.
---
### 4.4. Retrieval RAG
**Задача**
Извлечь релевантный контекст для выполнения задачи.
**Как устроен**
Многослойная система хранения знаний (код, документация, факты, связи), из которой извлекается структурированный контекст в зависимости от параметров, заданных роутером.
**Почему так**
Разные задачи требуют разных типов данных, поэтому используется слойная модель вместо плоского поиска.
---
### 4.5. Task Workflows
**Задача**
Реализуют прикладную логику выполнения конкретного типа задачи.
**Как устроены**
Набор специализированных workflows, например:
* объяснение по документации;
* генерация OpenAPI;
* генерация документации;
* другие сценарии.
Внутри workflow находятся:
* обработка контекста;
* evidence-проверки;
* вызовы LLM;
* сборка результата.
**Почему так**
Логика проверки данных и генерации сильно зависит от задачи, поэтому она инкапсулируется в отдельных workflows, а не в одном универсальном слое.
---
### 4.6. Output / Artifact
**Задача**
Вернуть результат пользователю.
**Как устроен**
Может быть:
* текстовый ответ;
* структурированный список;
* OpenAPI спецификация;
* документация;
* иной артефакт.
**Почему так**
Агент должен поддерживать не только ответы, но и генерацию инженерных артефактов.
---
## Итог
Обновленная архитектура строится на следующем принципе:
* **детерминированное извлечение ключевых артефактов** задает рамки;
* **LLM выполняет гибкий роутинг внутри этих рамок**;
* **retrieval обеспечивает данные**;
* **task workflows реализуют прикладную логику и контроль качества**.
Это позволяет одновременно сохранить управляемость системы и обеспечить масштабируемость под новые типы задач.
+59
View File
@@ -0,0 +1,59 @@
# Intents
## Domains
- `DOCS`
- `GENERAL`
- `CODE` - временно отключен
## GENERAL
### Intent `GENERAL_QA`
Общий интент для вопросов без точного маршрута.
В дальнейшем может использоваться как fallback.
Subintents:
- `SUMMARY` - ответы на общие вопросы по SUMMARY
## DOCS
### Intent `ARCHITECTURE`
Обработка вопросов по архитектуре.
Subintents пока отсутствуют.
Интент запланирован, без реализации.
### Intent `DOC_EXPLAIN`
Объяснение по документации.
Subintents:
- `SUMMARY` - краткое объяснение темы по SUMMARY-блокам документации
- `FIND_FILES` - поиск файлов с релевантной информацией
- `EXPLAIN_API` - объяснение работы метода
- `COMPONENT_INTEGRATIONS` - перечень интеграций компонента, API, UI, сущности, внешних систем
- `ENTITY_INTEGRATIONS` - перечень интеграций сущности
В текущем узком MVP реально реализованы только:
- `SUMMARY`
- `FIND_FILES`
Для запросов по интеграциям целевым retrieval-слоем является `D6_INTEGRATION_INDEX`.
### Intent `OPENAPI_GENERATION`
Генерация OpenAPI-спеки.
Subintents:
- `FULL_SPEC` - создание полной спецификации
### Intent `DOC_GENERATION`
Редактирование документации.
Subintents:
- `FROM_FEATURE` - создание документации из системной аналитики на фичу
+345
View File
@@ -0,0 +1,345 @@
# RAG
## Состояние as is
RAG сейчас используется как общее ядро индексации и retrieval по коду и документации.
Основной storage - `rag_session` и многослойный индекс в БД.
## Основные части
- `RagService` - фасад индексации и retrieval
- `DocsIndexingPipeline` - индексация документации
- `CodeIndexingPipeline` - индексация кода
- `RagRepository` - persistence и retrieval
- `IntentRouterV2` - планирование retrieval: слои, фильтры, ограничения
- `RuntimeRetrievalAdapter` - выполнение retrieval в runtime
## Индексация
Индексация идет по двум направлениям:
- `DOCS`
- `CODE`
На вход подается snapshot или changes.
Для каждого файла выбирается подходящий pipeline.
На выходе формируются документы по слоям и сохраняются в RAG-хранилище.
## Структура БД
Все слои сохраняются в общую таблицу `rag_chunks`.
### Общие поля по слоям
| Поле БД | Назначение |
|---|---|
| `rag_session_id` | идентификатор сессии индексации |
| `path` | путь исходного файла |
| `content` | основной текст записи для retrieval |
| `layer` | идентификатор слоя |
| `title` | короткий заголовок записи |
| `lang` | язык исходного содержимого, в основном для code-слоев |
| `repo_id` | идентификатор репозитория или проекта |
| `commit_sha` | версия кода или документов на момент индексации |
| `span_start`, `span_end` | диапазон строк в исходном файле, если он есть |
| `embedding` | векторное представление записи |
| `metadata_json` | структурированные атрибуты конкретного слоя |
### Поля со смыслом слоя
Смысл конкретного слоя хранится в `metadata_json`.
Именно эти атрибуты определяют, какой объект был извлечен и как его интерпретировать в retrieval.
Домены и поддомены должны храниться в `metadata_json` явно.
## Слои DOCS
### `D0_DOC_CHUNKS`
Задача:
Хранит текстовые фрагменты документации для retrieval по содержимому разделов.
Формирование:
Документ сначала разбирается на frontmatter и body, затем body режется на секции через markdown chunker.
Для каждой секции создается отдельная запись слоя.
Нарезка идет по разделам документа.
Только в fallback-сценарии, когда markdown-структура не найдена, используется нарезка по фиксированным текстовым чанкам.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `document_id` | идентификатор документа-источника | `frontmatter.id`, иначе путь файла |
| `type` | тип документа из frontmatter | `frontmatter.type` |
| `module` | модуль документа | `frontmatter.module` |
| `domain` | домен документа | `frontmatter.domain` |
| `subdomain` | поддомен документа | `frontmatter.subdomain` |
| `tags` | теги документа | `frontmatter.tags` |
| `section_path` | полный путь секции в иерархии заголовков | результат `MarkdownDocChunker` |
| `section_title` | заголовок текущей секции | результат `MarkdownDocChunker` |
| `order` | порядок секции внутри документа | результат `MarkdownDocChunker` |
| `doc_kind` | классификация документа, например `readme`, `spec`, `runbook` | `DocsClassifier.classify(path)` |
| `source_path` | исходный путь документа | путь файла |
| `artifact_type` | тип артефакта, здесь `DOCS` | константа builder |
Связанные классы:
`DocsIndexingPipeline`, `DocsContentParser`, `MarkdownDocChunker`, `DocsDocumentBuilder`
### `D1_DOCUMENT_CATALOG`
Задача:
Хранит карточку документа как точку входа в документ и его краткое описание.
Формирование:
Источник данных - frontmatter `as is`, summary и doc kind, вычисленный классификатором документации.
В `metadata_json` копируются все `key-value` из frontmatter без нормализации и без fallback для frontmatter-атрибутов.
Дополнительно в `metadata_json` добавляются служебные поля `source_path`, `summary_text`, `doc_kind`.
Атрибут `document_id` добавляется только при наличии `frontmatter.id` (fallback до пути файла не применяется).
В `content` попадает summary документа, а не склейка всех частей документа в сплошной текст.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `*` frontmatter fields | все поля frontmatter в исходном виде | frontmatter документа |
| `document_id` | идентификатор документа, добавляется только если в frontmatter есть `id` | `frontmatter.id` |
| `source_path` | исходный путь документа | путь файла |
| `summary_text` | краткое содержание документа | секция `# Summary` |
| `doc_kind` | классификация документа, например `readme`, `spec`, `runbook` | `DocsClassifier.classify(path)` |
Связанные классы:
`DocsIndexingPipeline`, `DocsFrontmatterParser`, `DocsClassifier`, `DocsContentParser`, `DocsDocumentBuilder`
### `D2_FACT_INDEX`
Задача:
Хранит атомарные факты в форме `subject-predicate-object` для точного retrieval по утверждениям.
Формирование:
Факты извлекаются из frontmatter и секций документа, после чего каждая найденная тройка превращается в отдельную запись.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `fact_id` | идентификатор факта | вычисляется builder из содержимого факта и пути |
| `subject_id` | субъект факта | `DocsFactExtractor` |
| `predicate` | предикат или тип связи | `DocsFactExtractor` |
| `object` | значение или объект факта | `DocsFactExtractor` |
| `object_ref` | ссылка на объект, если она выделена отдельно | `DocsFactExtractor` |
| `anchor` | место в документе, откуда взят факт | `DocsFactExtractor` |
| `tags` | теги факта | `DocsFactExtractor` |
| `source_path` | исходный путь документа | путь файла |
Связанные классы:
`DocsIndexingPipeline`, `DocsFactExtractor`, `DocsDocumentBuilder`
### `D3_ENTITY_CATALOG`
Задача:
Хранит сущности, найденные в документации, чтобы искать документы и связи вокруг конкретной сущности.
Формирование:
Сущности извлекаются из frontmatter документа, после чего каждая сущность сохраняется отдельной записью.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `entity_name` | имя сущности | `DocsEntityExtractor` |
| `document_id` | идентификатор документа, где найдена сущность | `frontmatter.id`, иначе путь файла |
| `document_type` | тип документа-источника | `frontmatter.type` |
| `module` | модуль документа | `frontmatter.module` |
| `domain` | домен документа | `frontmatter.domain` |
| `subdomain` | поддомен документа | `frontmatter.subdomain` |
| `tags` | теги документа или сущности | `frontmatter.tags` |
| `source_path` | исходный путь документа | путь файла |
Связанные классы:
`DocsIndexingPipeline`, `DocsEntityExtractor`, `DocsDocumentBuilder`
### `D4_WORKFLOW_INDEX`
Задача:
Хранит workflow и сценарии из документации для ответов про flow, шаги и жизненный цикл процесса.
Формирование:
Workflow извлекаются из detail sections документа и сохраняются как отдельные сценарии.
Извлечение идет из структуры `Details -> ## Сценарий`.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `workflow_id` | идентификатор сценария | вычисляется builder из названия, anchor и документа |
| `document_id` | идентификатор документа-источника | `frontmatter.id`, иначе путь файла |
| `workflow_name` | название сценария | блок `Details -> ## Сценарий -> **Название**` |
| `preconditions` | предусловия сценария | блок `Details -> ## Сценарий -> **Предусловия**` |
| `trigger` | триггер или событие запуска | блок `Details -> ## Сценарий -> **Триггер**` |
| `main_flow` | основной сценарий | блок `Details -> ## Сценарий -> **Основной сценарий**` |
| `alternative_flow` | альтернативные ветки | блок `Details -> ## Сценарий -> **Альтернативный сценарий**` |
| `error_handling` | обработка ошибок | блок `Details -> ## Сценарий -> **Обработка ошибок**` |
| `postconditions` | постусловия | блок `Details -> ## Сценарий -> **Постусловие**` |
| `source_path` | исходный путь документа | путь файла |
Связанные классы:
`DocsIndexingPipeline`, `DocsWorkflowExtractor`, `DocsDocumentBuilder`
### `D5_RELATION_GRAPH`
Задача:
Хранит связи между документами и сущностями документации для navigation и related docs retrieval.
Формирование:
Связи извлекаются из frontmatter и сохраняются как отдельные relation edges.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `relation_id` | идентификатор связи | вычисляется builder из source, target, relation type и anchor |
| `source_id` | источник связи | `frontmatter.id` или source документа в extractor |
| `relation_type` | тип связи | `DocsRelationExtractor` |
| `target_id` | целевой объект связи | `DocsRelationExtractor` |
| `anchor` | место в документе, где обнаружена связь | `DocsRelationExtractor` |
| `source_path` | исходный путь документа | путь файла |
Связанные классы:
`DocsIndexingPipeline`, `DocsRelationExtractor`, `DocsDocumentBuilder`
### `D6_INTEGRATION_INDEX`
Задача:
Хранит прикладные интеграции компонента, API, UI, сущности или внешней системы.
Используется для ответов на вопросы вида "какие интеграции есть у компонента".
Формирование:
Интеграции извлекаются из блока `## Integrations` документа.
Одна интеграция должна превращаться в отдельную запись слоя.
Описание интеграции может быть развернутым, а структурированные атрибуты должны выделяться в словарь.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `integration_id` | идентификатор интеграции | вычисляется builder из source, target и anchor |
| `source_id` | идентификатор объекта, для которого описана интеграция | `frontmatter.id` документа-источника |
| `source_type` | тип исходного объекта | `frontmatter.doc_type` |
| `target` | целевой объект интеграции | блок `## Integrations` |
| `target_type` | тип целевого объекта, например `api`, `ui`, `entity`, `service`, `external_system` | блок `## Integrations` |
| `direction` | направление интеграции | блок `## Integrations` |
| `interaction` | тип взаимодействия | блок `## Integrations` |
| `via` | технический канал интеграции | блок `## Integrations` |
| `purpose` | назначение интеграции | блок `## Integrations` |
| `details` | дополнительные атрибуты интеграции в виде словаря | блок `## Integrations` |
| `domain` | домен документа | `frontmatter.domain` |
| `subdomain` | поддомен документа | `frontmatter.subdomain` |
| `source_path` | исходный путь документа | путь файла |
| `anchor` | место в документе, где описана интеграция | блок `## Integrations` |
Связанные классы:
`DocsIndexingPipeline`, `DocsIntegrationExtractor`, `DocsDocumentBuilder`
## Слои CODE
### `C0_SOURCE_CHUNKS`
Задача:
Хранит фрагменты исходного кода как базовый слой для цитирования, explain и точечной догрузки кода.
Формирование:
Исходный файл режется на кодовые чанки, и для каждого чанка создается отдельная запись.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `chunk_index` | порядковый номер чанка в файле | индекс чанка при нарезке |
| `chunk_type` | тип чанка, например функция, класс или текстовый блок | `CodeTextChunker` |
| `module_or_unit` | модуль, к которому относится chunk | вычисляется из пути файла |
| `is_test` | признак тестового файла | `is_test_path(path)` |
Связанные классы:
`CodeIndexingPipeline`, `CodeTextChunker`, `CodeTextDocumentBuilder`
### `C1_SYMBOL_CATALOG`
Задача:
Хранит символы кода: классы, функции и методы. Используется для поиска по именам и структуре кода.
Формирование:
Символы извлекаются `SymbolExtractor`, и каждый символ сохраняется как отдельная запись.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `symbol_id` | идентификатор символа | `SymbolExtractor` |
| `qname` | полное квалифицированное имя | `SymbolExtractor` |
| `kind` | тип символа: класс, функция, метод | `SymbolExtractor` |
| `signature` | сигнатура символа | `SymbolExtractor` |
| `parent_symbol_id` | родительский символ | `SymbolExtractor` |
| `package_or_module` | модуль или пакет символа | вычисляется из пути файла |
| `is_test` | признак тестового файла | `is_test_path(path)` |
Связанные классы:
`CodeIndexingPipeline`, `PythonAstParser`, `SymbolExtractor`, `SymbolDocumentBuilder`
### `C2_DEPENDENCY_GRAPH`
Задача:
Хранит связи между символами кода: вызовы, импорты, наследование. Используется для анализа зависимостей и flow.
Формирование:
Связи строятся `EdgeExtractor` по AST и списку символов, после чего каждая связь сохраняется отдельной записью.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `edge_id` | идентификатор связи | `EdgeExtractor` |
| `edge_type` | тип связи: вызов, импорт, наследование | `EdgeExtractor` |
| `src_symbol_id` | исходный символ | `EdgeExtractor` |
| `src_qname` | полное имя исходного символа | `EdgeExtractor` |
| `dst_symbol_id` | целевой символ, если он разрешен | `EdgeExtractor` |
| `dst_ref` | текстовая ссылка на целевой символ | `EdgeExtractor` |
| `resolution` | статус разрешения связи | `EdgeExtractor` |
| `is_test` | признак тестового файла | `is_test_path(path)` |
Связанные классы:
`CodeIndexingPipeline`, `EdgeExtractor`, `EdgeDocumentBuilder`
### `C3_ENTRYPOINTS`
Задача:
Хранит точки входа приложения: HTTP routes, CLI commands и другие entrypoints.
Формирование:
Детекторы ищут HTTP и CLI точки входа по символам файла, после чего каждый найденный entrypoint сохраняется отдельной записью.
Фиксация в БД:
| Атрибут в `metadata_json` | Описание | Источник |
|---|---|---|
| `entry_id` | идентификатор точки входа | detector entrypoint model |
| `entry_type` | тип точки входа | detector entrypoint model |
| `framework` | framework, в котором найдена точка входа | detector entrypoint model |
| `route_or_command` | route или команда | detector entrypoint model |
| `handler_symbol_id` | идентификатор обработчика | detector entrypoint model |
| `handler_symbol` | имя обработчика | detector entrypoint model |
| `declaring_symbol` | символ, в котором объявлен entrypoint | detector entrypoint model |
| `entrypoint_kind` | вид точки входа | detector entrypoint model |
| `http_method` | HTTP-метод | detector entrypoint model |
| `route_path` | путь маршрута | detector entrypoint model |
| `decorator_text` | текст декоратора или объявления | detector entrypoint model |
| `summary_text` | краткое описание точки входа | detector entrypoint model |
| `is_test` | признак тестового файла | `is_test_path(path)` |
| `lang_payload` | дополнительные данные детектора | detector metadata |
| `artifact_type` | тип артефакта, здесь `CODE` | константа builder |
Связанные классы:
`CodeIndexingPipeline`, `EntrypointDetectorRegistry`, `FastApiEntrypointDetector`, `FlaskEntrypointDetector`, `TyperClickEntrypointDetector`, `EntrypointDocumentBuilder`
### `C4_SEMANTIC_ROLES`
Задача:
Слой объявлен в enum и retrieval-планах как слой семантических ролей кода.
Формирование:
Слой формируется на основе символов, связей, dataflow slices и execution traces.
В текущем runtime этот слой не используется как активный маршрут, так как домен `CODE` отключен.
Фиксация в БД:
Смысловые атрибуты слоя сохраняются в `rag_chunks.metadata_json`.
Точное краткое описание состава этих атрибутов в текущем документе пока не зафиксировано.
Связанные классы:
`CodeIndexingPipeline`, `SemanticRoleBuilder`, `SemanticRoleDocumentBuilder`
@@ -0,0 +1,289 @@
## 1. Формат ведения технической документации агентом
## 1.1. Общие принципы
Техническая документация, формируемая агентом, должна строиться как система атомарных, не пересекающихся по смыслу документов, связанных между собой явными ссылками.
Ключевые принципы:
- один документ описывает одну сущность или один устойчивый технический аспект;
- документ не должен дублировать соседние документы;
- общая система знаний должна собираться через ссылки, а не через копипасту;
- структура документации должна быть пригодна как для чтения человеком, так и для индексирования в RAG.
## 1.2. Требования к заголовкам
- Заголовок должен отражать только суть раздела.
- Заголовок не должен содержать метаданные (`id`, `doc_type`, `application`, `platform`, `domain`, `sub_domain`).
- Метаданные указываются отдельными строками в теле раздела или в YAML frontmatter.
Пример:
- правильно: `## 6.2 Метод UFS получения списка заказов`
- неправильно: `## 6.2 Блок api_method (id=..., platform=ufs)`
## 1.3. Базовые типы документных единиц
Базовые типы:
- `ui_page`
- `api_method`
- `logic_block`
Дополнительно могут использоваться:
- `architecture_overview`
- `integration_doc`
- `domain_entity`
- `glossary_item`
- `index_page`
## 1.4. Принцип декомпозиции страниц / файлов
### Один устойчивый объект - один документ
Если объект можно переиспользовать или на него могут ссылаться другие документы, его нужно выносить в отдельный файл.
### Документы не должны пересекаться по смыслу
Если описание повторяется в нескольких местах, нужно выделять общий документ и ссылаться на него.
### Use case и детали живут раздельно
Сценарий описывает поток работы, а детали выносятся в функциональные требования, отдельные блоки логики или контрактные описания.
## 1.5. Иерархическая организация документации
Документация должна быть организована как иерархическое дерево каталогов и файлов.
Пример:
```text
docs/
ui/
api/
logic/
domains/
integrations/
architecture/
glossary/
errors/
```
## 1.6. Учет связей между документами
Связи должны быть явными.
Примеры:
- UI-страница ссылается на вызываемые API;
- API-документ ссылается на используемые блоки логики;
- логический блок ссылается на интеграции;
- документ по коду ссылается на системную аналитику, инициировавшую изменения.
## 1.7. Формат markdown-документов
Каждый документ состоит из:
1. YAML frontmatter;
2. Markdown body.
## 1.8. YAML frontmatter
### Обязательные поля
- `id`
- `title`
- `doc_type`
- `status`
- `domain`
- `sub_domain`
- `related_docs`
### Рекомендуемые поля
- `owner`
- `entities`
- `tags`
- `feature`
- `system_analytics_refs`
- `source_of_truth`
- `related_code`
### Допустимые значения `doc_type`
- `ui_page`
- `api_method`
- `logic_block`
- `architecture_overview`
- `integration_doc`
- `domain_entity`
- `glossary_item`
- `index_page`
### Допустимые значения `status`
- `draft`
- `in_review`
- `approved`
- `outdated`
- `generated`
- `active`
## 1.9. Синхронизация с системной аналитикой
Техническая документация строится на основе системной аналитики (features).
Обязательно учитывать:
- концептуальный уровень аналитики;
- детализацию технической документации;
- согласованность терминов, ролей и интеграционных цепочек.
Если атрибуты или детали отсутствуют в аналитике:
- определить их из текста аналитики;
- дополнить данными из репозитория (код, контракты, существующие документы);
- зафиксировать итог в документации как явные метаданные и требования.
## 1.10. Формат body-разделов для блока изменений
Для секции изменений (по аналогии с разделом `6` в аналитике) использовать единый формат.
Под корнем секции изменений указывать общие атрибуты:
- `domain`
- `sub_domain`
Для каждого подраздела `X.Y` указывать метаданные строками сразу после заголовка:
- `id`
- `doc_type`
- `application`
- `platform`
## 1.11. Различие аналитики и документации
- Аналитика - концептуальный уровень, упрощенный use case.
- Документация - детальный инженерный уровень.
Для документации:
- технический use case должен быть детализированным;
- функциональные требования расширяют use case и описывают детали интеграций, логики и поведения;
- функциональные требования не должны копировать шаги сценария без добавления новой информации.
Источник правил:
- `src/app/core/agent/processes/v2/doc_rules_v2/common-elements/tech-use-case.md`
- `src/app/core/agent/processes/v2/doc_rules_v2/common-elements/fr.md`
## 1.12. Требования к `ui_page`
Обязательная структура:
- `### Технический use case`
- `### Требования к UI`
- `### Функциональные требования`
- `### Нефункциональные требования`
### Требования к UI
Внутри обязательно отдельно описывать каждую форму UI:
- табличное представление;
- пустой список (empty state);
- ошибка (error state).
Обязательные правила:
- если есть интеграция, обязательно описать показ ошибки;
- если показывается список, обязательно описать показ отсутствия данных.
### UI-элементы
UI-поля и элементы в документации описываются строго в таблицах.
Обязательные колонки (заполнять там, где применимо):
- `Код элемента`
- `Название и описание`
- `Данные`
- `Поведение`
- `Валидация`
## 1.13. Пользовательская аналитика для `ui_page`
События пользовательской аналитики оформляются таблицей:
- `Название события`
- `Описание`
- `Точка вызова`
- `Payload`
## 1.14. Требования к `api_method`
Обязательная структура:
- `### Технический use case`
- `### Функциональные требования`
- `### Нефункциональные требования`
- `### Контракт`
### Технический use case
Оформляется детально по правилам `tech-use-case.md`.
Обязательные части:
- название
- предусловия
- триггер
- основной сценарий
- альтернативный сценарий
- обработка ошибок
- постусловие
### Функциональные требования
Оформляются по правилам `fr.md`:
- формат `FR.<номер>. <Название>`;
- FR расширяют use case;
- FR не дублируют шаги сценария без дополнительной ценности;
- для интеграционных шагов FR обязательны.
## 1.15. Нефункциональные требования для `api_method`
Разделять на подразделы:
- `#### Аудит` (если применимо)
- `#### Мониторинг`
### Мониторинг
Оформлять таблицей:
- `Метрика`
- `Описание`
- `Условие срабатывания`
Правила:
- в условиях указывать, при каких состояниях фиксируется событие;
- не использовать формулировку вида «точка измерения = метод»;
- базово закладывать метрики:
- `<METRIC_NAME>_SUCCESS`
- `<METRIC_NAME>_FAIL`
- `<METRIC_NAME>_BUSINESS_ERROR`
## 1.16. Распределение ответственности по слоям
- Проверка ролевой модели пользователя обычно выполняется в `ufs`.
- Для `pprb` аудит может не фиксироваться, если это согласовано правилами домена.
- Если проверка ролей вынесена в `ufs`, не дублировать этот шаг в use case `pprb`.
## 1.17. Контракты API
Контракт может быть:
- в markdown-таблицах;
- в OpenAPI;
- в отдельном контрактном файле.
Для markdown-контракта минимум:
- endpoint/method;
- request fields;
- required/optional;
- constraints;
- response;
- errors;
- auth;
- retry;
- timeout;
- idempotency.
## 1.18. Integrations-блок
Если у документа есть интеграции, выделять отдельный `## Integrations`.
Рекомендуемые атрибуты интеграции:
- `target`
- `target_type`
- `direction`
- `interaction`
- `via`
- `purpose`
- `details`
## 1.19. Общие требования к markdown body
- В документе должен быть один `H1`, совпадающий с `title`.
- Основные разделы - `H2`, подразделы - `H3`.
- Не допускать хаотичной вложенности заголовков.
- Вместо дублирования использовать ссылки на связанные документы.
- Сценарии, правила, ограничения и кодовые привязки держать раздельно.
@@ -0,0 +1,212 @@
# Системная аналитика
## Общее описание
Документ описывает изменения в автоматизированной системе. Пишется системными аналитиками для разработчиков и тестировщиков и проходит согласование с экспертами по архитектуре, безопасности и сопровождению.
Документ может описывать как новый процесс, так и инкремент доработки существующей функциональности.
## Требования к заголовкам
- Заголовок должен отражать суть раздела.
- Заголовок не должен содержать лишнюю информацию, которая относится к метаданным (id, doc_type, platform, application и т.д.).
- Метаданные указываются отдельными строками в теле раздела.
## Состав документа
Каждый раздел верхнего уровня оформляется заголовком уровня `#`.
### 1. Цели
- Коротко описать, какую проблему и для кого решаем.
- 1-2 предложения.
- Не дублировать критерии приемки.
### 2. Процесс AS IS и TO BE
- Фокус на пользовательских и бизнес-изменениях.
- Не указывать технические детали (платформы, API, внутренние интеграции).
### 3. Ограничения
- Ограничения и допущения в техническом и бизнесовом плане.
### 4. Критерии приемки
- Описывать с точки зрения пользователя.
- Не добавлять технические детали (платформы, API, внутренние компоненты).
### 5. Архитектура
Нужно указать:
- схему контейнеров,
- таблицу интеграций,
- сквозные интеграционные сценарии.
Слои:
- `ui` - web-приложение, клиент.
- `ufs` - BFF: аутентификация/авторизация, агрегация и маппинг данных.
- `pprb` - backend: API, БД, логика жизненного цикла сущностей.
#### Диаграмма
Mermaid-диаграмма должна содержать:
- основные контейнеры,
- названия приложений и платформ,
- интеграции между приложениями,
- названия вызываемых endpoint или топиков.
#### Таблица интеграций
Обязательные колонки:
- Код
- Название endpoint/топика
- Источник данных
- Потребитель данных
- Инициатор вызова
- Передаваемые данные
#### Сквозной интеграционный сценарий
- Нумерованный список вызовов вида: «Компонент 1 вызывает endpoint в Компонент 2».
- Только интеграционная цепочка, без детального разбора логики.
### 6. Описание изменений
Раздел состоит из подразделов уровня `##` (например, `6.1`, `6.2`, `6.3`).
Под корнем раздела `# 6` указываются общие метаданные:
- `domain`
- `sub_domain`
Для каждого раздела `6.x` обязательно указывать метаданные строками сразу после заголовка:
- `id`
- `doc_type`
- `application`
- `platform`
Дополнительные метаданные для случаев изменения существующей документации:
- `action`
- `target_doc_id`
- `target_path`
#### 6.x для `ui_page`
Обязательная структура:
- `### Технический use case (тезисно)`
- `### Требования к UI`
- `### Функциональные требования`
- `### Нефункциональные требования`
Требования к разделу `### Требования к UI`:
- Внутри нужно отдельно описывать каждую UI-форму.
- Если есть интеграция, обязательно описать, как показывается ошибка.
- Если показываем список, обязательно описать, как показывается отсутствие данных.
Рекомендуемая детализация UI-форм:
- табличное представление,
- пустой список (empty state),
- ошибка (error state).
Правила описания UI-полей:
- Поля описывать списком (не таблицей).
- Общие правила (например, read-only, поведение при пустом значении) выносить в общий блок, не дублировать для каждого поля.
Отдельно нужно различать два сценария описания:
1. Если описывается новая UI-страница или новая самостоятельная UI-форма, раздел оформляется полноценно по шаблону `ui_page`.
- Нужно дать достаточный контекст для разработки и тестирования.
- Нужно подробно описывать структуру формы, состояния отображения, поведение полей, ошибки, empty state и пользовательские действия.
2. Если описывается доработка уже существующей страницы или существующей UI-формы, не нужно повторно копировать полное описание из действующей документации.
- Нужно учитывать уже существующее описание страницы в документации и аналитике.
- В аналитике нужно явно указать, что именно меняется в существующем сценарии: что добавляется, редактируется или удаляется.
- Нужно указывать точку изменения: в какой существующей странице, форме, блоке или сценарии вносится изменение.
- Нужно ссылаться на существующий документ или раздел, где базовое поведение уже описано.
- Нужно описывать только delta изменений, достаточную для реализации доработки и актуализации документации.
- Полное описание существующей страницы в таком разделе не дублируется.
- Для такой доработки в metadata нужно явно указывать `action: update`.
- Если изменение должно попасть в уже существующий markdown-документ, нужно явно указывать `target_doc_id` и/или `target_path`.
- `target_doc_id` должен совпадать с `id` существующего документа, который требуется обновить.
- Если `target_doc_id`/`target_path` не указаны, агент может ошибочно интерпретировать раздел как создание нового документа.
Нефункциональные требования для `ui_page`:
- пользовательская аналитика оформляется таблицей с колонками:
- `Название события`
- `Описание`
- `Точка вызова`
- `Payload`
#### 6.x для `api_method`
Обязательная структура:
- `### Технический use case (тезисно)`
- `### Функциональные требования`
- `### Нефункциональные требования`
- `### Контракт метода`
Правило для функциональных требований:
- Если дополнительных требований нет (дублируют сценарий), писать: `Не выявлены`.
Нефункциональные требования:
- Разделять на подразделы:
- `#### Аудит` (если применимо)
- `#### Мониторинг`
Для `Мониторинг` использовать таблицу с колонками:
- `Метрика`
- `Описание`
- `Условие срабатывания`
Важно:
- В мониторинге описывать условия срабатывания, а не «точку измерения = метод».
- Базово закладывать 3 метрики:
- `<METRIC_NAME>_SUCCESS`
- `<METRIC_NAME>_FAIL`
- `<METRIC_NAME>_BUSINESS_ERROR`
Контракт метода:
- Для запроса: таблица параметров (`header/query/path`) с колонками: название, тип параметра, тип данных, обязательность, описание, пример.
- Для тела JSON (если есть): структура отдельной таблицей.
- Для ответа JSON: таблица с колонками: название, тип данных, обязательность, описание, заполнение, пример.
#### 6.x для `logic_block`
Обязательная структура:
- `### Технический use case (тезисно)`
- `### Функциональные требования`
- `### Нефункциональные требования`
`logic_block` удобно использовать для фиксации точечных изменений существующего сценария, если раздел не описывает новую самостоятельную страницу или новую самостоятельную форму, а только уточняет delta к уже существующей документации.
Если точечное изменение должно изменить существующий документ другого типа, `logic_block` для этого использовать нельзя. В этом случае metadata раздела должна указывать тип и идентификатор целевого существующего документа, который требуется обновить.
## Дополнительные правила по слоям
- Проверка ролевой модели пользователя обычно выполняется на уровне `ufs`.
- Для `pprb` аудит может не фиксироваться, если это правило принято для конкретной фичи/домена.
- Если проверка ролей вынесена в `ufs`, не дублировать этот шаг в сценарии `pprb`.
## Термины
- Аудит: события, которые фиксируют действия пользователя и позволяют ответить на вопрос «кто, что, когда сделал».
- Мониторинг: технические события/метрики для контроля стабильности и поиска сбоев.
+100
View File
@@ -0,0 +1,100 @@
# MVP: процесс v1
## 1. Общее описание
Запрос пользователя обрабатывается цепочкой API → рантайм агента → зарегистрированный процесс версии `v1` → один workflow из трёх последовательных шагов. Процесс **не** обращается к RAG и **не** маршрутизирует интенты: текст сообщения передаётся в LLM по фиксированному промпту. Ответ агента — результат генерации с лёгкой постобработкой (trim).
```mermaid
flowchart LR
subgraph api [API]
RS[RequestService]
end
subgraph runtime [Agent runtime]
AR[AgentRuntime]
PR[ProcessRunner]
end
subgraph v1 [Процесс v1]
P1[V1Process]
WG[V1FlowMainGraph]
end
subgraph wf [Workflow v1.flow_main]
S1[PrepareUserMessageStep]
S2[GenerateAnswerStep]
S3[FinalizeAnswerStep]
end
LLM[AgentLlmService]
RS --> AR
AR --> PR
PR --> P1
P1 --> WG
WG --> S1 --> S2 --> S3
S2 --> LLM
```
Клиент создаёт запрос с `process_version: v1`. `AgentRuntime` поднимает `RuntimeExecutionContext` (запрос, сессия, publisher, trace), выбирает `V1Process` из реестра и вызывает `run`. `V1Process` собирает `V1FlowContext` и прогоняет линейный граф: подготовка текста, один вызов LLM, финализация строки ответа. Итог попадает в `ProcessResult.answer` и дальше в ответ пользователю.
---
## 2. Шаги и контракты
### 2.1. Вход в процесс: `V1Process.run`
| | |
|--|--|
| **Название** | Запуск процесса v1 |
| **Задача** | Собрать контекст workflow и выполнить граф до готового ответа. |
| **Вход** | `RuntimeExecutionContext`: `request` (в т.ч. `message`), `session`, `publisher`, `trace`. |
| **Выход** | `ProcessResult` с полем `answer: str`. |
| **Как работает** | Создаётся `V1FlowContext` с `prompt_name` по умолчанию `v1_flow_main.answer`. Вызывается `V1FlowMainGraph.run`. Возвращается ответ из контекста workflow. |
Код: `src/app/core/agent/processes/v1/process.py`.
---
### 2.2. Шаг workflow: `PrepareUserMessageStep`
| | |
|--|--|
| **Название** | Подготовка сообщения пользователя |
| **Задача** | Сформировать строку, которая уйдёт в LLM как пользовательский ввод. |
| **Вход** | `V1FlowContext` с заполненным `runtime` и `prompt_name`. |
| **Выход** | Тот же контекст с `prepared_message: str`. |
| **Как работает** | Берётся `context.runtime.request.message` и обрезаются пробелы по краям (`strip`). Результат пишется в `prepared_message`. Других преобразований нет. |
Код: `src/app/core/agent/processes/v1/workflow/flow_main/steps/prepare_user_message_step.py`.
---
### 2.3. Шаг workflow: `GenerateAnswerStep`
| | |
|--|--|
| **Название** | Вызов LLM |
| **Задача** | Сгенерировать ответ по выбранному промпту и подготовленному сообщению. |
| **Вход** | `V1FlowContext` с `prepared_message`, `prompt_name`, `runtime.trace` для модуля LLM. |
| **Выход** | Контекст с `answer: str` (сырой ответ модели). |
| **Как работает** | Асинхронно в пуле потоков вызывается `AgentLlmService.generate(prompt_name, prepared_message, ...)`. В trace подключается модуль `workflow.v1.llm`. Идентификатор запроса передаётся в `log_context` для логов. |
Код: `src/app/core/agent/processes/v1/workflow/flow_main/steps/generate_answer_step.py`.
---
### 2.4. Шаг workflow: `FinalizeAnswerStep`
| | |
|--|--|
| **Название** | Финализация ответа |
| **Задача** | Нормализовать строку ответа перед выдачей пользователю. |
| **Вход** | `V1FlowContext` с заполненным `answer` после LLM. |
| **Выход** | Контекст с обновлённым `answer`. |
| **Как работает** | К ответу применяется `strip()` по краям. Другой логики нет. |
Код: `src/app/core/agent/processes/v1/workflow/flow_main/steps/finalize_answer_step.py`.
---
### 2.5. Транспорт: `WorkflowGraph` (v1)
Граф для v1 использует стандартный `WorkflowGraph`: на каждом шаге пишутся события `workflow_started`, `step_started`, `step_completed`, `workflow_completed` в `runtime_traces` через `context.runtime.trace`.
Код: `src/app/core/agent/utils/workflow/graph.py`, обёртка `V1FlowMainGraph` в `src/app/core/agent/processes/v1/workflow/flow_main/graph.py`.
+33
View File
@@ -0,0 +1,33 @@
Нужно реализовать 2 вещи
Создать процесс внесения изменений в файл документации
Создать контекст этого процесса
Контекст наполнять атрибутами
что-то явно задано, фоллбэк через ллм
Написать тестовую аналитику - круд над сущностью
фронт, ефс, ппрб
Все в своей БД
Атрибуты сущности задать в требованиях
Аналитика имеет структуру
Внутри модули - один модуль на правку одного файла.
Модуль извлекается из аналитики парсером и из него формируется задача на редактирование файла
если парсер не сработал - фоллбэк ан ллм
Процесс редактирования работает стандартно
+220
View File
@@ -0,0 +1,220 @@
# MVP: процесс v2
## 1. Общее описание
Процесс v2 в текущем MVP ориентирован в первую очередь на **документацию проекта**, но роутер также поддерживает `GENERAL / GENERAL_QA / SUMMARY` для общих обзорных вопросов. Для документных веток нужна активная RAG-сессия с проиндексированными документами.
Это **узкий MVP**, а не полная target architecture. Поддерживаются три маршрута:
- `GENERAL`
- `GENERAL_QA`
- `SUMMARY`
- `DOCS`
- `DOC_EXPLAIN`
- `SUMMARY`
- `FIND_FILES`
Запрос проходит следующие смысловые этапы:
1. проверка готовности сессии;
2. intent routing;
3. формирование retrieval-параметров;
4. retrieval из `DOCS RAG`;
5. минимальная сборка evidence;
6. запуск task-focused workflow нужной ветки;
7. формирование ответа.
```mermaid
flowchart TB
subgraph api [API]
RS[RequestService]
end
subgraph runtime [Agent runtime]
AR[AgentRuntime]
PR[ProcessRunner]
end
subgraph v2 [Процесс v2]
P2[V2Process]
IR[V2IntentRouter]
POL[V2RetrievalPolicyResolver]
AD[V2RagRetrievalAdapter]
RSR[RagSessionRetriever]
ASM[DocsEvidenceAssembler]
end
subgraph rag [Пакет rag]
RR[RagRepository]
end
subgraph wf [Workflow]
SUM[DocsExplainSummaryGraph]
FF[DocsExplainFindFilesGraph]
end
LLM[AgentLlmService]
RS --> AR --> PR --> P2
P2 --> IR --> POL --> AD --> RSR --> RR
AD --> ASM
ASM --> SUM
ASM --> FF
SUM --> LLM
```
Клиент указывает `process_version: v2`. Без `active_rag_session_id` в сессии процесс возвращает сообщение об ошибке. Иначе выполняется цепочка:
маршрутизация → `RetrievalPlan` → retrieval строк из `DOCS RAG` → минимальная сборка evidence → ветвление по `subintent` → запуск workflow.
### Реализованные домены, интенты и сабинтенты
В коде заданы константы `V2Domain`, `V2Intent`, `V2Subintent`. Сейчас процесс intentionally ограничен одной рабочей областью.
| Уровень | Значение (строка) | Реализация |
|--------|-------------------|------------|
| **Домен (routing_domain)** | `DOCS` | Единственный поддерживаемый домен: документация проекта. |
| **Интент** | `DOC_EXPLAIN` | Единственный интент: объяснение по документации. |
| **Сабинтент** | `SUMMARY` | Объяснение темы по SUMMARY-блокам документации. |
| **Сабинтент** | `FIND_FILES` | Поиск путей к документам, где описана нужная сущность или тема. |
Итого в текущем MVP реализована **одна** рабочая тройка домен×интент: `DOCS` + `DOC_EXPLAIN`, с **двумя** ветками по сабинтенту.
---
## 2. Этапы вне workflow (внутри `V2Process.run`)
### 2.1. `V2IntentRouter.route`
| | |
|--|--|
| **Название** | Маршрутизация запроса (v2) |
| **Задача** | Определить домен, интент, subintent и извлечь якоря из текста. |
| **Вход** | `user_query: str` (текст сообщения пользователя). |
| **Выход** | `V2RouteResult`: `routing_domain`, `intent`, `subintent`, `user_query`, `normalized_query`, `target_terms`, `anchors` (`V2RouteAnchors`), `confidence`. |
| **Как работает** | Router реализован по схеме **LLM-first**: `normalization``target_terms`/`anchors extraction``LLM router``deterministic validator``fallback`. LLM является **основным селектором маршрута**. Deterministic-слой больше не выбирает маршрут по умолчанию: он отвечает только за extraction, валидацию enum/комбинаций и fallback при сломанном или невалидном ответе LLM. В trace пишется событие `intent_routed`. |
Код: `src/app/core/agent/processes/v2/intent_router/router.py`, `modules/normalizer.py`, `modules/target_terms.py`, `modules/anchors.py`, `routers/llm.py`, `routers/validator.py`, `routers/fallback.py`.
---
### 2.2. `V2RetrievalPolicyResolver.resolve`
| | |
|--|--|
| **Название** | Политика retrieval для v2 |
| **Задача** | По результату роутинга выбрать профиль, список слоёв RAG и лимит строк выдачи. |
| **Вход** | `V2RouteResult`. |
| **Выход** | `RetrievalPlan`: `profile`, `layers`, `limit`, опционально `filters`. |
| **Как работает** | Это отдельный смысловой шаг между routing и retrieval. Он не ходит в БД и не извлекает данные, а только подготавливает параметры поиска. Для `FIND_FILES` выбирается один профиль слоёв и лимит, для `SUMMARY` — другой. Лог: `retrieval_plan_resolved`. |
Код: `src/app/core/agent/processes/v2/retrieval/policy_resolver.py`.
---
### 2.3. `V2RagRetrievalAdapter` → `RagSessionRetriever.retrieve`
| | |
|--|--|
| **Название** | Загрузка сырых строк из RAG по плану |
| **Задача** | Делегировать поиск в единственную реализацию retrieval в пакете `rag`. |
| **Вход** | `rag_session_id`, `query_text` (нормализованный запрос), `RetrievalPlan`. |
| **Выход** | `list[dict]` — строки чанков в формате `RagRepository.retrieve` (поля `path`, `layer`, `metadata`, и т.д.). |
| **Как работает** | Выполняется retrieval по уже сформированному плану: профиль, список слоёв и лимит. На этом шаге происходит только извлечение сырых строк из `DOCS RAG`. Лог: `rag_rows_fetched`. |
Код адаптера: `src/app/core/agent/processes/v2/retrieval/v2_rag_adapter.py`.
Код API: `src/app/core/rag/retrieval/session_retriever.py`.
---
### 2.4. `DocsEvidenceAssembler`
| | |
|--|--|
| **Название** | Сборка evidence для задачи |
| **Задача** | Превратить сырые строки retrieval в списки summary или кандидатов файлов с дедупом и скорингом. |
| **Вход** | Список строк `rows`, `V2RouteResult` (для `target_terms`). |
| **Выход** | `list[RetrievedSummary]` или `list[RetrievedFile]`. |
| **Как работает** | Это **минимальная evidence-проверка**, достаточная для MVP. Для `SUMMARY` отбрасываются записи без summary-текста и summary-like секции, затем применяется дедуп и простой скоринг по терминам. Для `FIND_FILES` остаются только релевантные пути документов, также с дедупом и простым скорингом. Здесь нет сложной многоступенчатой валидации: задача шага — отфильтровать очевидный шум и передать в workflow компактное evidence. Лог: `evidence_assembled`. |
Код: `src/app/core/agent/processes/v2/evidence/assembler.py`.
---
## 3. Шаги workflow
Текущие workflow являются **task-focused**: каждая ветка решает одну узкую прикладную задачу и не содержит общей универсальной логики для всех типов вопросов.
### 3.1. Ветка `SUMMARY`: `GenerateSummaryAnswerStep`
| | |
|--|--|
| **Название** | Сборка ответа по summary |
| **Задача** | Сформировать ответ пользователю по найденным SUMMARY-блокам или сообщить об отсутствии. |
| **Вход** | `DocsExplainSummaryContext`: `runtime`, `route`, `rag_session_id`, `prompt_name`, `documents` (список `RetrievedSummary`). |
| **Выход** | Контекст с `answer: str`, `prompt_input` при успешном вызове LLM. |
| **Как работает** | Workflow получает уже отобранные summary-документы. Если документов нет — возвращает честный fallback-ответ. Иначе собирает prompt input из запроса пользователя и найденных summary-блоков и вызывает LLM. Workflow не занимается retrieval и не строит retrieval-план: он решает только задачу генерации ответа по уже подготовленному evidence. |
Код: `src/app/core/agent/processes/v2/workflows/docs_explain_summary/steps/generate_summary_answer_step.py`.
Граф: `DocsExplainSummaryGraph` (`V2WorkflowGraph`).
---
### 3.2. Ветка `FIND_FILES`: `FinalizeFindFilesAnswerStep`
| | |
|--|--|
| **Название** | Сборка списка файлов |
| **Задача** | Вывести пользователю markdown-список путей к файлам документации. |
| **Вход** | `DocsExplainFindFilesContext`: `runtime`, `route`, `rag_session_id`, `files` (`RetrievedFile`). |
| **Выход** | Контекст с `answer: str`. |
| **Как работает** | Workflow получает уже собранный список файлов и формирует финальный ответ. Если файлов нет — возвращает fallback. Если файлы есть — отдает детерминированный список путей. Эта ветка intentionally не использует LLM, потому что задача сводится к выдаче путей, а не к генерации объяснения. |
Код: `src/app/core/agent/processes/v2/workflows/docs_explain_find_files/steps/finalize_find_files_answer_step.py`.
Граф: `DocsExplainFindFilesGraph` (`V2WorkflowGraph`).
---
### 3.3. Транспорт: `V2WorkflowGraph`
| | |
|--|--|
| **Название** | Workflow v2 с буфером trace |
| **Задача** | Выполнить шаги без пошаговых `step_started`/`step_completed` в trace; один раз сбросить сводку. |
| **Вход** | Контекст workflow (`DocsExplainSummaryContext` или `DocsExplainFindFilesContext`). |
| **Выход** | Обновлённый контекст. |
| **Как работает** | Для каждого шага: `trace_input` до `run`, затем `run`, затем `trace_output`; записи копятся в список. В trace уходят `workflow_started`, затем `workflow_trace_flushed` с массивом шагов, затем `workflow_completed`. Статусы пользователю публикуются через `publisher` как и раньше. |
Код: `src/app/core/agent/processes/v2/workflows/v2_workflow_graph.py`.
---
## 4. Сборка в приложении
В `ModularApplication` создаются `RagSessionRetriever`, `V2RagRetrievalAdapter`, `V2RetrievalPolicyResolver`, `DocsEvidenceAssembler` и передаются в `V2Process` (см. `src/app/core/application.py`).
---
## 5. Итоговая концептуальная схема текущего MVP
В концептуальном виде текущий `v2` работает так:
1. **Session check**
Проверка, что есть активная RAG-сессия проекта.
2. **LLM-first intent routing**
Нормализация, extraction (`target_terms`, `anchors`), затем основной выбор маршрута через LLM.
3. **Deterministic validation + fallback**
Проверка enum/комбинации маршрута и fallback только если LLM не ответил или вернул невалидный маршрут.
4. **Retrieval parameter planning**
Формирование профиля поиска, слоёв и лимитов.
5. **RAG retrieval**
Загрузка сырых строк из `DOCS RAG`.
6. **Minimal evidence assembly**
Дедуп, базовый скоринг, отбор полезных summary или файлов.
7. **Task-focused workflow**
Узкая ветка `SUMMARY` или `FIND_FILES`.
8. **Final response**
Либо explanation через LLM, либо детерминированный список файлов.
Это и есть актуальная архитектура **узкого MVP**, синхронизированная с текущей реализацией.
@@ -0,0 +1,346 @@
# V2IntentRouter Architecture
## 1. Архитектура
Текущий `V2IntentRouter` реализован как **LLM-first router**.
Deterministic-слой не выбирает маршрут по умолчанию и используется только для:
- preprocessing
- validation ответа LLM
- fallback, если LLM не ответил или вернул невалидный маршрут
Актуальные компоненты:
- `router.py`
Главная точка входа и оркестратор пайплайна.
- `modules/normalizer.py`
Нормализация текста запроса в `normalized_query`.
- `modules/target_terms.py`
Извлечение retrieval-oriented `target_terms`, `endpoint_paths`, `matched_aliases`, `alias_docs`.
- `modules/anchors.py`
Извлечение `anchors` и marker-сигналов для fallback и downstream retrieval.
- `routers/route_catalog.py`
Каталог допустимых маршрутов (`allowed_routes`).
- `routers/llm.py`
Основной LLM-router. Получает нормализованный запрос, `target_terms`, `anchors` и список допустимых маршрутов.
- `routers/validator.py`
Deterministic validator для enum-значений, комбинации маршрута и базовой нормализации `confidence`.
- `routers/confidence.py`
Пост-обработка confidence после ответа LLM.
- `routers/fallback.py`
Fallback-маршрутизация, если LLM не ответил или ответ не прошёл validator.
- `routers/prompts.yml`
Prompt-контракт для LLM-router.
## 2. Контракт
### Вход
- `user_query: str`
### Выход
`V2RouteResult`:
- `routing_domain: str`
- `intent: str`
- `subintent: str`
- `user_query: str`
- `normalized_query: str`
- `target_terms: list[str]`
- `anchors: V2RouteAnchors`
- `confidence: float`
- `routing_mode: str`
- `llm_router_used: bool`
- `reason_short: str`
`V2RouteAnchors`:
- `entity_names: list[str]`
- `file_names: list[str]`
- `endpoint_paths: list[str]`
- `target_doc_hints: list[str]`
- `matched_aliases: list[str]`
- `process_domain: str | None`
- `process_subdomain: str | None`
## 3. Поддерживаемые домены, интенты и сабинтенты
### Домены
- `DOCS`
- `GENERAL`
### Интенты
- `DOC_EXPLAIN`
- `GENERAL_QA`
### Сабинтенты
- `SUMMARY`
- `FIND_FILES`
### Допустимые маршруты
- `GENERAL / GENERAL_QA / SUMMARY`
- `DOCS / DOC_EXPLAIN / SUMMARY`
- `DOCS / DOC_EXPLAIN / FIND_FILES`
Эти маршруты централизованно заданы в `routers/route_catalog.py`.
## 4. Актуальный флоу
Пайплайн обработки запроса:
1. `router.py` принимает `user_query`.
2. `modules/normalizer.py` строит `normalized_query`.
3. `modules/target_terms.py` извлекает:
- `target_terms`
- `endpoint_paths`
- `matched_aliases`
- `alias_docs`
4. `modules/anchors.py` строит:
- `anchors`
- `file_markers`
- `architecture_markers`
- `logic_markers`
- `domain_markers`
- `endpoint_markers`
5. `router.py` собирает `QueryFeatures`.
6. `routers/llm.py` вызывается как **основной селектор маршрута**.
7. `routers/validator.py` проверяет:
- что значения входят в допустимые enum
- что комбинация маршрута разрешена
- что `confidence` можно привести к `float`
8. `routers/confidence.py` корректирует confidence на основе силы сигналов.
9. Если ответ LLM валиден, возвращается `V2RouteResult` с `routing_mode="llm_default"`.
10. Если LLM не ответил, вернул сломанный JSON или невалидный маршрут, `routers/fallback.py` строит fallback route:
- `FIND_FILES`, если есть `file_markers`
- `DOCS / DOC_EXPLAIN / SUMMARY`, если есть docs-oriented anchors
- иначе `GENERAL / GENERAL_QA / SUMMARY`
## 5. Компоненты по флоу
### `router.py`
- Задача
Оркестрировать полный routing pipeline.
- Как решает
Последовательно вызывает:
- normalizer
- target terms extractor
- anchor extractor
- LLM router
- validator
- confidence adjuster
- fallback router
- Вход
`user_query: str`
- Выход
`V2RouteResult`
### `modules/normalizer.py`
- Задача
Привести запрос к стабильной форме для анализа.
- Как решает
Схлопывает лишние пробелы через `" ".join(...split())`.
- Вход
`user_query: str`
- Выход
`normalized_query: str`
### `modules/target_terms.py`
- Задача
Построить **чистое retrieval-поле** `target_terms`.
- Как решает
Использует позитивную модель отбора и включает в `target_terms` только:
- endpoint paths
- identifier-like tokens
- alias canonical terms
- domain terms
Исключаются:
- question words
- intent words
- filler/noisy words
- marker words
- короткие токены `< 3`, если это не endpoint или alias
- битые path-like токены
Дополнительно:
- lowercase
- trim punctuation по краям
- dedupe
- ограничение до `7` элементов
- приоритет: endpoints → identifiers → aliases → domain terms
- Вход
`normalized_query: str`
- Выход
`TargetTermsAnalysis`:
- `target_terms`
- `endpoint_paths`
- `matched_aliases`
- `alias_docs`
### `modules/anchors.py`
- Задача
Построить `anchors` и marker-сигналы, не смешивая их с `target_terms`.
- Как решает
Извлекает:
- `entity_names` из PascalCase-like токенов
- `file_names` только по жёстким правилам:
- `*.md`, `*.yaml`, `*.yml`, `*.json`
- `docs/...`, `doc/...`, `documentation/...`
- `endpoint_paths` из `TargetTermsAnalysis`
- `target_doc_hints` из alias docs, endpoint map и marker-сигналов
Marker-сигналы живут отдельно:
- `file_markers`
- `architecture_markers`
- `logic_markers`
- `domain_markers`
- `endpoint_markers`
- Вход
- `normalized_query: str`
- `TargetTermsAnalysis`
- Выход
`AnchorAnalysis`
### `routers/route_catalog.py`
- Задача
Держать один источник истины для допустимых маршрутов.
- Как решает
Возвращает:
- список `allowed_routes` для payload LLM
- проверку допустимости комбинации `routing_domain + intent + subintent`
### `routers/llm.py`
- Задача
Выбрать маршрут через LLM как основной селектор.
- Как решает
Формирует JSON payload из:
- `normalized_query`
- `target_terms`
- `anchors`
- `allowed_routes`
Затем:
- вызывает LLM
- парсит JSON
- возвращает сырой candidate route без deterministic business-routing
- Вход
- `normalized_query: str`
- `target_terms: list[str]`
- `anchors: dict`
- Выход
`dict | None`
### `routers/validator.py`
- Задача
Deterministic validation ответа LLM.
- Как решает
Проверяет:
- что `routing_domain`, `intent`, `subintent` заполнены
- что комбинация маршрута входит в `route_catalog`
- что `confidence` можно привести к числу
- Вход
`dict | None`
- Выход
Валидированный `dict | None`
### `routers/confidence.py`
- Задача
Сделать confidence осмысленным после ответа LLM.
- Как решает
Корректирует confidence:
- `-0.1`, если нет strong anchors
- `-0.1`, если запрос короткий или vague
- `+0.05`, если есть явный signal (`file_markers`, `endpoint_paths`, `endpoint_markers`)
- затем clamp в диапазон `0.0..1.0`
- Вход
- `confidence: float`
- `QueryFeatures`
- Выход
`confidence: float`
### `routers/fallback.py`
- Задача
Построить deterministic fallback, если LLM невалиден.
- Как решает
Правила:
- есть `file_markers``DOCS / DOC_EXPLAIN / FIND_FILES`
- есть docs-signals (`endpoint_paths`, `target_doc_hints`, `matched_aliases`, marker groups) → `DOCS / DOC_EXPLAIN / SUMMARY`
- иначе → `GENERAL / GENERAL_QA / SUMMARY`
- Вход
- `user_query: str`
- `QueryFeatures`
- `anchors: V2RouteAnchors`
- `llm_attempted: bool`
- Выход
`V2RouteResult`
### `routers/prompts.yml`
- Задача
Задать LLM-router контракт ответа и guidance по confidence.
- Как решает
Ограничивает модель только `allowed_routes` и требует JSON с полями:
- `routing_domain`
- `intent`
- `subintent`
- `confidence`
- `reason_short`
## 6. Ключевые инварианты
- LLM является default router.
- Deterministic-слой не принимает основной routing decision.
- `target_terms` содержат только retrieval-useful terms.
- `anchors` не содержат `terms`.
- `/health` и другие endpoint paths не должны попадать в `file_names`, если это не файл с расширением.
- `file_names` содержат только реальные file/doc paths.
- Fallback используется только если LLM недоступен или вернул невалидный маршрут.
@@ -0,0 +1,316 @@
# V2RetrievalPolicyResolver Architecture
## 1. Роль компонента
`V2RetrievalPolicyResolver` это deterministic bridge между `V2IntentRouter` и docs-RAG retrieval.
Компонент работает поверх уже готового `V2RouteResult` и не делает повторную интерпретацию пользовательского текста:
- не вызывает LLM;
- не меняет `intent` и `subintent`;
- не ранжирует документы;
- не собирает evidence.
Его задача: собрать один `RetrievalPlan` с полями:
- `profile`
- `layers`
- `limit`
- `filters`
## 2. Зависимости
Актуальная реализация опирается на:
- `src/app/core/agent/processes/v2/retrieval/policy_resolver.py`
- `src/app/core/agent/processes/v2/anchor_signals.py`
- `src/app/core/agent/processes/v2/models.py`
- `src/app/core/rag/contracts/enums.py`
- `src/app/core/agent/processes/v2/retrieval/v2_rag_adapter.py`
- `src/app/core/rag/retrieval/session_retriever.py`
- `src/app/core/rag/persistence/repository.py`
- `src/app/core/rag/persistence/query_repository.py`
- `src/app/core/rag/persistence/retrieval_statement_builder.py`
## 3. Входной контракт
Resolver использует:
- `route.intent`
- `route.subintent`
- `route.anchors.entity_names`
- `route.anchors.file_names`
- `route.anchors.endpoint_paths`
- `route.anchors.target_doc_hints`
- `route.anchors.matched_aliases`
- `route.anchors.process_domain`
- `route.anchors.process_subdomain`
`route.target_terms` в текущей реализации profile/filter branching не влияет.
## 4. Верхнеуровневый branching
`resolve(route)` имеет три ветки:
1. `GENERAL_QA` -> `general_qa_grounded_summary`
2. `FIND_FILES` -> `file_lookup`
3. иначе -> docs summary branch
Инварианты:
- `GENERAL_QA` всегда остаётся general profile;
- `FIND_FILES` всегда остаётся `file_lookup`;
- resolver всегда возвращает один валидный `RetrievalPlan`.
## 5. Внутренняя декомпозиция
Текущая реализация разбита на два helper-класса.
### `_AnchorTermCollector`
Собирает термы для `prefer_like_patterns`.
Источники:
- basename из `target_doc_hints`
- `endpoint_paths`
- `file_names`
- `entity_names`
- `matched_aliases`
- `process_domain`
- `process_subdomain`
Все значения нормализуются в lower-case и превращаются в SQL-like patterns вида `"%term%"`.
Для `FIND_FILES` действует отдельное правило:
- если есть `target_doc_hints`, `prefer_like_patterns` строится только по basename hints;
- иначе используется общий набор collected terms.
### `_RouteFilterBuilder`
Собирает `filters` для трёх веток:
- `general_filters(route)`
- `summary_filters(route)`
- `find_files_filters(route)`
Дополнительно содержит path selection:
- `_summary_prefixes(route)`
- `_find_files_prefixes(route)`
- `_find_files_prefer_prefixes(route)`
## 6. Signal detection
Summary profile и часть path preferences зависят от `anchor_signal_types(route)`.
Сигналы вычисляются так:
- `FIND_FILES`
- если `route.subintent == FIND_FILES`
- `API_ENDPOINT`
- если есть `endpoint_paths`
- или в `target_doc_hints` / `file_names` / `matched_aliases` встречаются маркеры `"/api/"`, `"api"`, `"endpoint"`
- `ARCHITECTURE`
- если в `target_doc_hints` / `file_names` / `matched_aliases` встречаются `"/architecture/"`, `"architecture"`, `"arch"`
- `LOGIC_FLOW`
- если в `target_doc_hints` / `file_names` / `matched_aliases` встречаются `"/logic/"`, `"logic"`, `"workflow"`, `"flow"`, `"process"`
- `DOMAIN_ENTITY`
- если есть `entity_names`
- или в `target_doc_hints` / `file_names` / `matched_aliases` встречаются `"/domains/"`, `"domain"`, `"entity"`, `"component"`
Важно:
- `process_domain` и `process_subdomain` сейчас **не участвуют** в signal detection;
- они влияют только на filters и `prefer_like_patterns`.
## 7. Summary profile selection
Метод `_summary_profile(route)` использует:
- `meaningful = anchor_signal_types(route) - {FIND_FILES}`
Правило:
- если meaningful signal не ровно один -> `docs_summary_generic`
- если ровно один:
- `API_ENDPOINT` -> `docs_summary_api_endpoint`
- `ARCHITECTURE` -> `docs_summary_architecture`
- `LOGIC_FLOW` -> `docs_summary_logic_flow`
- `DOMAIN_ENTITY` -> `docs_summary_domain_entity`
Следствие:
- конфликт API + architecture -> generic;
- API + entity -> generic;
- weak/no signals -> generic.
## 8. Profiles, layers, limits
### `general_qa_grounded_summary`
- condition: `route.intent == GENERAL_QA`
- layers: `[D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS]`
- limit: `8`
### `file_lookup`
- condition: `route.subintent == FIND_FILES`
- layers: `[D1_DOCUMENT_CATALOG, D3_ENTITY_CATALOG]`
- limit: `12`
### `docs_summary_api_endpoint`
- layers: `[D1_DOCUMENT_CATALOG, D2_FACT_INDEX, D0_DOC_CHUNKS]`
- limit: `8`
### `docs_summary_logic_flow`
- layers: `[D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS]`
- limit: `8`
### `docs_summary_domain_entity`
- layers: `[D3_ENTITY_CATALOG, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS]`
- limit: `8`
### `docs_summary_architecture`
- layers: `[D1_DOCUMENT_CATALOG, D5_RELATION_GRAPH, D0_DOC_CHUNKS]`
- limit: `8`
### `docs_summary_generic`
- layers: `[D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS]`
- limit: `8`
## 9. Filters by branch
### General branch
`general_filters(route)` возвращает:
- `prefer_path_prefixes = ["docs/architecture/", "docs/"]`
- `prefer_like_patterns = ["%readme.md%", "%overview%"]`
- `target_doc_hints = list(route.anchors.target_doc_hints)`
Это обзорный, но не узкий plan: hard `path_prefixes` здесь нет.
### Summary branch
`summary_filters(route)` всегда включает:
- `target_doc_hints`
- `metadata.domain`, если есть `process_domain`
- `metadata.subdomain`, если есть `process_subdomain`
- `prefer_path_prefixes`
- `prefer_like_patterns`
Дополнительно:
- если есть `API_ENDPOINT` signal, добавляется hard `path_prefixes = ["docs/api/", "docs/"]`
`prefer_path_prefixes` для summary:
- API -> `["docs/api/", "docs/"]`
- ARCHITECTURE -> `["docs/architecture/", "docs/"]`
- LOGIC_FLOW -> `["docs/logic/", "docs/architecture/", "docs/"]`
- DOMAIN_ENTITY -> `["docs/domains/", "docs/", "docs/api/"]`
- empty signals -> `["docs/"]`
Если сигналов несколько, prefixes объединяются и dedupe-ятся с сохранением порядка.
### FIND_FILES branch
`find_files_filters(route)` всегда включает:
- `target_doc_hints`
- `metadata.domain`, если есть `process_domain`
- `metadata.subdomain`, если есть `process_subdomain`
- `path_prefixes`
- `prefer_path_prefixes`
- `prefer_like_patterns`
`path_prefixes` для `FIND_FILES` выбираются по приоритету:
1. директории из `target_doc_hints`
2. директории из `file_names`, если путь начинается с `docs/`
3. signal-based fallback:
- API -> `["docs/api/", "docs/"]`
- ARCHITECTURE -> `["docs/architecture/", "docs/"]`
- LOGIC_FLOW -> `["docs/logic/", "docs/"]`
- DOMAIN_ENTITY -> `["docs/domains/", "docs/"]`
4. default -> `["docs/"]`
`prefer_path_prefixes` для `FIND_FILES`:
- начинается с `path_prefixes`
- если есть `process_domain` или `process_subdomain`, дополнительно добавляет:
- `"docs/domains/"`
- `"docs/logic/"`
## 10. Hard и soft сигналы в текущей реализации
В терминах текущего кода:
Hard-ish / narrowing filters:
- `path_prefixes`
- `metadata.domain`
- `metadata.subdomain`
Soft preferences:
- `prefer_path_prefixes`
- `prefer_like_patterns`
Отдельно:
- `target_doc_hints` всегда сохраняются в `RetrievalPlan.filters`, но **не маппятся напрямую** в `RagRepository.retrieve(...)` как SQL hard filter.
То есть сейчас `target_doc_hints` это не прямой DB filter, а downstream anchor для других шагов пайплайна и для deterministic exact-doc seeding logic.
## 11. Интеграция с retrieval stack
Следующий слой после resolver теперь исполняет plan не напрямую в `V2Process`, а через `V2RagRetrievalAdapter`.
`V2RagRetrievalAdapter.fetch_rows(...)` использует `RetrievalPlan` так:
- читает `filters["target_doc_hints"]` из самого плана;
- делает exact-path seed через `retrieve_exact_files(...)`;
- для missing hints делает substring fallback через `retrieve_chunks_by_path_substrings(...)`;
- затем делает обычный semantic retrieve через `RagSessionRetriever.retrieve(...)`;
- объединяет exact / substring / semantic rows через dedupe merge.
Это важный сдвиг: execution strategy теперь зависит от **контракта `RetrievalPlan`**, а не от скрытой route-specific логики внутри `V2Process`.
`RagSessionRetriever._map_filters()` прокидывает в `RagRepository.retrieve(...)`:
- `path_prefixes`
- `exclude_path_prefixes`
- `exclude_like_patterns`
- `prefer_path_prefixes`
- `prefer_like_patterns`
- `prefer_non_tests`
- `metadata_domain` из `filters["metadata.domain"]`
- `metadata_subdomain` из `filters["metadata.subdomain"]`
`RetrievalStatementBuilder.build_retrieve(...)` добавляет SQL predicates:
- `lower(metadata_json->>'domain') = :metadata_domain`
- `lower(metadata_json->>'subdomain') = :metadata_subdomain`
Таким образом:
- `process_domain/process_subdomain` реально участвуют в retrieval query;
- `target_doc_hints` реально участвуют в retrieval execution strategy на уровне adapter;
- `V2RetrievalPolicyResolver` определяет plan contract, а следующий шаг исполняет этот contract более буквально.
## 12. Актуальные ограничения
- Логика полностью deterministic.
- `target_terms` сейчас не участвуют в branching resolver.
- `process_domain/process_subdomain` не влияют на summary profile selection.
- API signal добавляет `path_prefixes` даже в generic summary, если среди конфликтующих сигналов присутствует API.
- `target_doc_hints` не являются прямым SQL filter внутри обычного `retrieve`, но используются adapter-уровнем для exact-path / substring seeding до semantic retrieval.
+37
View File
@@ -0,0 +1,37 @@
# Documentation Rules V3
Этот каталог содержит правила генерации технической документации из системной аналитики.
## Цель
- синхронизировать требования к документации с требованиями к аналитике (`04. Analitycs artefacts - features.md`);
- сохранить детальность техдокументации по сравнению с аналитикой;
- убрать дублирование структуры и manifest-слоя между разными файлами;
- собирать итоговый промпт из модулей: глобальные правила + template с manifest + блоки.
## Структура
- `documentation-rules.md` — верхнеуровневый регламент и порядок сборки.
- `global/` — общие правила (заголовки, frontmatter, слой ответственности, мост аналитика->документация).
- `common-elements/` — правила для общих блоков (`summary`, `details`, `use case`, `FR`, `NFR`, `UI`, `Contract`).
- `templates/` — единственный источник истины для структуры итоговой страницы и manifest-метаданных типа документа.
## Принцип сборки
Для конкретного документа агент собирает единый набор правил из:
1. `documentation-rules.md`
2. `global/*.md`
3. `templates/<doc_type>.template.md`
4. `common-elements/*.md`, указанных в frontmatter template
## Правило без дублирования
- `templates/` отвечают за структуру документа, порядок разделов и manifest-метаданные типа.
- `common-elements/` отвечают только за правила написания конкретного раздела.
- отдельный слой `types/` не нужен, если для типа документа используется один основной template.
## Формат template-manifest
Manifest оформляется в YAML frontmatter самого template.
Обязательные поля manifest:
- `doc_type`
- `required_common_elements`
Рекомендуемые поля:
- `special_rules`
@@ -0,0 +1,28 @@
# API Contract Rules
Этот rule описывает только тело секции `### Контракт`.
## Обязательные части
- request parameters (`header/query/path`)
- request body (если применимо)
- response body
- errors
- auth
- timeout
- retry/idempotency (если применимо)
## Правила заголовков внутри тела секции
- Не повторять заголовок `Контракт`.
- Запрещено выводить `## Контракт` и `### Контракт` внутри тела секции.
- Если нужны подзаголовки, использовать только уровень ниже родительской секции: `#### Запрос`, `#### Ответ`, `#### Ошибки`, `#### Auth`, `#### Timeout`, `#### Retry/Idempotency`.
## Табличный формат
Для request/response таблицы должны содержать:
- название
- тип данных
- обязательность
- описание
- пример
Для response дополнительно:
- заполнение (mapping/логика источника данных)
@@ -0,0 +1,17 @@
# DB Columns Rules
## Формат
Структура таблицы оформляется таблицей.
## Обязательные колонки
- `Поле`
- `Тип`
- `Nullable`
- `Описание`
- `Источник заполнения`
- `Использование`
## Правила
- перечислять все ключевые поля таблицы;
- для служебных полей (`id`, `created_at`, `updated_at`, `deleted_at`) явно описывать назначение;
- если тип или nullable не заданы в аналитике, допускается инженерное предположение с рабочим вариантом.
@@ -0,0 +1,16 @@
# DB Constraints Rules
## Что включать
- primary key;
- unique constraints;
- foreign keys;
- важные индексы;
- бизнес-ограничения на уровне БД.
## Формат
- списком или таблицей;
- для каждого индекса и ограничения писать, зачем оно нужно.
## Правила
- если индекс нужен для сценария чтения/пагинации, это должно быть явно сказано;
- если точные названия индексов неизвестны, можно использовать осмысленные проектные названия.
@@ -0,0 +1,12 @@
# DB Table Purpose Rules
## Что описывать
- назначение таблицы;
- в каком сценарии она используется;
- кто является владельцем данных;
- является ли таблица источником истины или производным хранилищем.
## Формат
- 1-3 абзаца без воды;
- явно указывать доменную сущность, которую хранит таблица;
- если сделаны допущения по БД, фиксировать их отдельной фразой.
@@ -0,0 +1,11 @@
# DB Usage Rules
## Что описывать
- какие API / logic block / batch job используют таблицу;
- какие операции выполняются: read / insert / update / delete;
- как таблица участвует в пользовательском сценарии.
## Правила
- ссылки на связанные документы давать по `doc_id` или path;
- не дублировать полный use case, а показывать роль таблицы в сценарии;
- если таблица используется для пагинации, фильтрации или сортировки, это нужно отметить явно.
@@ -0,0 +1,10 @@
# Details Rules
## Назначение
Этот файл задает общие правила для секции `## Details`.
## Правила
- `Details` оформляется как `## Details`.
- Внутри `Details` используются заголовки уровня `###` и ниже.
- Структура `Details` определяется template типа документа.
- В `Details` не нужно дублировать навигацию и связи, если они уже есть во frontmatter.
@@ -0,0 +1,34 @@
# Functional Requirements Rules
Этот rule описывает только тело секции `### Функциональные требования`.
## Формат
- `FR.<номер>. <Название>`
- Нумерация инкрементальная внутри документа.
## Правила
- FR расширяют шаги сценария.
- FR не копируют шаги сценария без добавления новой информации.
- Для интеграционных шагов FR обязательны.
- Если в сценарии есть вызов внешнего API / сервиса / БД, нужен отдельный FR на интеграцию.
- Запрещено повторять заголовок `### Функциональные требования` внутри тела секции.
## FR для интеграционных шагов
Для интеграционного FR обязательно раскрывать:
- как формируется запрос;
- откуда берется каждый значимый атрибут запроса;
- какой downstream вызывается;
- какой ответ считается успешным;
- какие ответы и ситуации считаются бизнес-ошибкой;
- какие ситуации считаются технической ошибкой;
- как downstream-ответ маппится в контракт текущего слоя.
## FR для шагов доступа к БД
Если шаг читает или пишет БД, FR должен по возможности включать:
- таблицу или набор таблиц;
- логику фильтрации;
- логику сортировки;
- логику пагинации;
- пример SQL или близкий к рабочему псевдо-SQL.
Если СУБД и диалект не заданы, допускается сделать рабочее предположение и явно зафиксировать его.
@@ -0,0 +1,20 @@
# Non-Functional Requirements Rules
## Для api_method
- Подразделы:
- `#### Аудит` (если применимо)
- `#### Мониторинг`
## Мониторинг
Оформлять таблицей:
- `Метрика`
- `Описание`
- `Условие срабатывания`
Запрещено:
- использовать «точка измерения = метод» вместо условий срабатывания.
Базовые суффиксы метрик:
- `_SUCCESS`
- `_FAIL`
- `_BUSINESS_ERROR`
@@ -0,0 +1,15 @@
# SQL Example Rules
## Назначение
Секция показывает пример рабочего SQL для основного сценария использования таблицы.
## Правила
- SQL должен быть близок к рабочему, а не абстрактным псевдокодом;
- если диалект БД не указан, допускается выбрать наиболее вероятный вариант и явно зафиксировать допущение;
- пример должен отражать реальный сценарий документа: чтение, вставка, обновление или агрегация;
- для read-сценариев по возможности показывать фильтрацию, сортировку и пагинацию;
- если есть join, нужно кратко пояснить, зачем он нужен.
## Формат
- fenced code block с указанием `sql`;
- под кодом 1-3 поясняющих bullets о ключевых условиях, индексах и параметрах.
@@ -0,0 +1,10 @@
# Summary Rules
## Назначение
Этот файл задает правила для секции `## Summary`.
## Правила
- `Summary` должен быть коротким слоем быстрого контекста.
- `Summary` должен объяснять суть документа без длинных деталей.
- Предпочтительный формат: краткий список ключевых фактов.
- `Summary` не должен дублировать `Details`.
@@ -0,0 +1,19 @@
# Tech Use Case Rules
Этот rule описывает только тело секции `### Технический use case`.
## Обязательные части
- название
- предусловия
- триггер
- основной сценарий
- альтернативный сценарий
- обработка ошибок
- постусловие
## Правила шага
- Один шаг = одно предложение до 15-20 слов.
- Формат шага: смысловое действие + техническая реализация (endpoint/топик/операция).
- Длинные технические детали выносить в FR и ссылаться на FR из шага.
- Для интеграционных шагов описание обработки ошибок обязательно.
- Запрещено повторять заголовок `### Технический use case` внутри тела секции.
@@ -0,0 +1,22 @@
# UI Requirements Rules
## Структура блока
- `### Требования к UI`
- Внутри обязательно отдельные формы:
- табличное представление
- пустой список (empty state)
- ошибка (error state)
## Обязательные правила
- Если есть интеграция, обязательно описывать показ ошибки.
- Если есть список, обязательно описывать показ отсутствия данных.
## Описание UI-элементов
UI-элементы описываются строго в таблице.
Обязательные колонки (где применимо):
- `Код элемента`
- `Название и описание`
- `Данные`
- `Поведение`
- `Валидация`
@@ -0,0 +1,7 @@
# User Analytics Rules
События пользовательской аналитики оформлять таблицей:
- `Название события`
- `Описание`
- `Точка вызова`
- `Payload`
@@ -0,0 +1,56 @@
# Documentation Rules V3
## 1. Общий контракт
- Документация строится на основе системной аналитики, но на более детальном уровне.
- Заголовки отражают только суть раздела; метаданные в заголовках запрещены.
- Метаданные указываются во frontmatter и/или отдельными строками в body.
- Структура документа определяется только template соответствующего типа.
- Правила написания конкретного раздела определяются только соответствующим `common-elements` файлом.
- Manifest типа документа хранится во frontmatter соответствующего template.
- Генератор секции всегда пишет только тело секции, а не сам заголовок секции.
- Дублирование заголовков запрещено: нельзя повторно выводить заголовок текущей секции внутри ее тела.
- Если template уже содержит `### <Заголовок секции>`, то внутри тела допустимы только подзаголовки более глубокого уровня (`####` и ниже).
- Нельзя повышать уровень заголовка внутри тела секции до `##` или повторять `###` с тем же названием секции.
## 2. Источники требований
При генерации документа учитывать:
- `/Users/alex/Dev_projects_v2/ai driven app process/v2/agent/_process/04. Analitycs artefacts - documentation.md`
- `/Users/alex/Dev_projects_v2/ai driven app process/v2/agent/_process/04. Analitycs artefacts - features.md`
- правила v2 из `src/app/core/agent/processes/v2/doc_rules_v2`
## 3. Разрыв аналитика vs документация
- Аналитика: концептуальная, укрупненная.
- Документация: технически детальная.
- Технический use case в документации не копирует аналитический 1-в-1, а детализирует его.
- Функциональные требования расширяют сценарий и не дублируют шаги без новой информации.
## 4. Заполнение пробелов
Если атрибуты/детали отсутствуют в аналитике:
1. восстановить из формулировок аналитики;
2. уточнить по репозиторию (код, контракты, существующие документы);
3. зафиксировать в документации явно.
## 5. Сборка итогового промпта
1. Загрузить global-правила.
2. Загрузить template типа документа.
3. Прочитать YAML frontmatter template как manifest.
4. Загрузить общие блоки, указанные в manifest.
5. Применить body template как единственный источник структуры.
5. Проверить чек-лист совместимости с аналитикой (domain/sub_domain, роли слоев, интеграции, ошибки).
## 6. Специальные инварианты для `api_method`
- Во frontmatter обязательно должно присутствовать поле `endpoint`.
- Внутри `## Details` секция `### Контракт` должна присутствовать ровно один раз.
- Внутри тела секции `### Контракт` запрещено повторять заголовки `## Контракт` и `### Контракт`.
- Внутри `### Технический use case` запрещено повторять заголовок `### Технический use case`.
- Внутри `### Функциональные требования` запрещено повторять заголовок `### Функциональные требования`.
## 7. Формат manifest типа документа
Manifest типа документа хранится во frontmatter `templates/<doc_type>.template.md`.
Минимальная схема:
- `doc_type`
- `required_common_elements`
Дополнительно можно указывать:
- `special_rules`
@@ -0,0 +1,10 @@
# Analytics to Documentation Mapping
## Принцип
- Системная аналитика задает «что».
- Документация детализирует «как».
## Маппинг
- Из раздела архитектуры аналитики переносить контейнеры, интеграции и цепочки вызовов.
- Из раздела изменений аналитики строить отдельные технические страницы (`ui_page`, `api_method`, `logic_block`).
- Если в аналитике упрощенный use case, в документации раскрывать полный технический сценарий по правилам `tech-use-case.md`.
+67
View File
@@ -0,0 +1,67 @@
# Правила определения путей файлов
Текущая happy-path реализация строит путь документа по фиксированному шаблону:
`docs/<domain>/<platform>/<doc_type>/<doc_id>.md`
Пример:
`docs/orders/pprb/ui_page/orders.ui.list.md`
## Источники атрибутов
Для построения пути используются четыре основных атрибута:
- `domain`
- `application`
- `platform`
- `doc_type`
- `id` как `doc_id`
Если атрибуты явно указаны в подразделе `6.x`, нужно использовать их.
Если атрибут не указан, он может быть взят из общих метаданных аналитики или определен fallback-логикой.
## Нормализация сегментов
Каждый сегмент пути нормализуется одинаково:
- значение переводится в lowercase;
- все символы, кроме `a-z`, `0-9`, `.`, `_`, `-`, заменяются на `-`;
- ведущие и хвостовые `.` и `-` удаляются.
Примеры нормализации:
- `Payment Status` -> `payment-status`
- `UFS Orders` -> `ufs-orders`
- `crm.mobile` -> `crm.mobile`
## Значения по умолчанию
Если после нормализации сегмент пустой, используются fallback-значения:
- корневая папка: `domain`, иначе `application`, иначе `common`
- `platform` -> `web`
- `doc_type` -> `misc`
- `doc_id` -> `untitled`
## Что важно в текущей версии
- для корневой папки сначала используется `domain`;
- если `domain` не задан, используется `application`;
- `sub_domain` сейчас не участвует в построении пути;
- операции `create`, `update`, `delete` работают с одним и тем же правилом вычисления пути;
- специальных исключений для разных типов документов пока нет;
- отдельные каталоги для `pprb`, `ufs`, `web` задаются только через значение `platform`.
## Практическое правило для агента
Если нужно предложить или определить путь новой страницы, агент должен:
1. определить `application`;
2. определить `domain`;
3. определить `platform`;
4. определить `doc_type`;
5. определить стабильный `doc_id`;
6. взять корневую папку как `domain`, а если он пустой, то `application`;
7. нормализовать все сегменты;
8. собрать путь по шаблону `docs/<root>/<platform>/<doc_type>/<doc_id>.md`.
@@ -0,0 +1,37 @@
# Frontmatter Rules
## Обязательные поля
```yaml
id: string
title: string
doc_type: string
domain: string
sub_domain: string
related_docs: []
status: string
```
## Рекомендуемые поля
```yaml
tags: []
entities: []
source_of_truth: string
related_code: []
system_analytics_refs: []
```
## Дополнительные обязательные поля по типам документов
- Для `doc_type: api_method` поле `endpoint` обязательно.
- Значение `endpoint` должно содержать HTTP-метод и путь, например: `GET /orders/{orderId}`.
- Если в аналитике endpoint указан в заголовке раздела, use case, контракте или интеграционной схеме, его нужно перенести во frontmatter и не опускать.
## Body-метаданные для секции изменений
Под корнем секции изменений указывать:
- `domain`
- `sub_domain`
Для каждого подраздела `X.Y` указывать строками:
- `id`
- `doc_type`
- `application`
- `platform`
@@ -0,0 +1,10 @@
# Header Rules
## Правила
- Заголовок описывает только смысл раздела.
- Не включать в заголовок: `id`, `doc_type`, `application`, `platform`, `domain`, `sub_domain`.
- Метаданные указываются отдельными строками ниже заголовка или во frontmatter.
## Пример
- Правильно: `## 6.2 Метод UFS получения списка заказов`
- Неправильно: `## 6.2 Блок api_method (id=..., platform=ufs)`
@@ -0,0 +1,10 @@
# Layer Responsibility
- `ui`: отображение, UX, запуск пользовательских сценариев.
- `ufs`: авторизация/аутентификация, агрегация, маппинг, оркестрация вызовов.
- `pprb`: API, БД, доменная логика backend.
## Правила согласованности
- Проверка ролевой модели пользователя обычно фиксируется на уровне `ufs`.
- Если проверка роли вынесена в `ufs`, в `pprb`-сценарии не дублировать этот шаг.
- Аудит для `pprb` может отсутствовать, если это явно принято для домена/фичи.
@@ -0,0 +1,34 @@
---
doc_type: api_method
required_common_elements:
- common-elements/summary.md
- common-elements/details.md
- common-elements/tech-use-case.md
- common-elements/fr.md
- common-elements/nfr.md
- common-elements/api-contract.md
special_rules:
- Технический use case детализируется по `common-elements/tech-use-case.md`.
- FR расширяют use case и не дублируют шаги сценария без новой информации.
- Для интеграционных шагов FR обязательны.
---
# <title>
## Summary
Правила оформления: `../common-elements/summary.md`
## Details
Правила оформления: `../common-elements/details.md`
### Технический use case
Правила оформления: `../common-elements/tech-use-case.md`
### Функциональные требования
Правила оформления: `../common-elements/fr.md`
### Нефункциональные требования
Правила оформления: `../common-elements/nfr.md`
### Контракт
Правила оформления: `../common-elements/api-contract.md`
@@ -0,0 +1,38 @@
---
doc_type: db_table
required_common_elements:
- common-elements/summary.md
- common-elements/details.md
- common-elements/db-purpose.md
- common-elements/db-columns.md
- common-elements/db-constraints.md
- common-elements/db-usage.md
- common-elements/sql-example.md
special_rules:
- Документ описывает одну физическую таблицу БД или materialized view.
- Нужно фиксировать назначение таблицы, поля, ограничения, индексы, связи и сценарии использования.
- Если точные детали БД не заданы, допустимо сделать рабочие инженерные допущения и явно записать их в документ.
---
# <title>
## Summary
Правила оформления: `../common-elements/summary.md`
## Details
Правила оформления: `../common-elements/details.md`
### Назначение таблицы
Правила оформления: `../common-elements/db-purpose.md`
### Структура таблицы
Правила оформления: `../common-elements/db-columns.md`
### Ограничения и индексы
Правила оформления: `../common-elements/db-constraints.md`
### Использование в сценариях
Правила оформления: `../common-elements/db-usage.md`
### Пример SQL
Правила оформления: `../common-elements/sql-example.md`
@@ -0,0 +1,28 @@
---
doc_type: logic_block
required_common_elements:
- common-elements/summary.md
- common-elements/details.md
- common-elements/tech-use-case.md
- common-elements/fr.md
- common-elements/nfr.md
special_rules:
- Logic block описывает переиспользуемую логику без дублирования UI/API деталей.
---
# <title>
## Summary
Правила оформления: `../common-elements/summary.md`
## Details
Правила оформления: `../common-elements/details.md`
### Технический use case
Правила оформления: `../common-elements/tech-use-case.md`
### Функциональные требования
Правила оформления: `../common-elements/fr.md`
### Нефункциональные требования
Правила оформления: `../common-elements/nfr.md`
@@ -0,0 +1,33 @@
---
doc_type: ui_page
required_common_elements:
- common-elements/summary.md
- common-elements/details.md
- common-elements/tech-use-case.md
- common-elements/ui-requirements.md
- common-elements/fr.md
- common-elements/user-analytics.md
special_rules:
- Для списочных страниц обязательно описывать табличное представление, empty state и error state.
- UI-элементы описываются в таблицах по правилам `common-elements/ui-requirements.md`.
---
# <title>
## Summary
Правила оформления: `../common-elements/summary.md`
## Details
Правила оформления: `../common-elements/details.md`
### Технический use case
Правила оформления: `../common-elements/tech-use-case.md`
### Требования к UI
Правила оформления: `../common-elements/ui-requirements.md`
### Функциональные требования
Правила оформления: `../common-elements/fr.md`
### Нефункциональные требования
Правила оформления: `../common-elements/user-analytics.md`
+1 -1
View File
@@ -27,7 +27,7 @@ services:
env_file: env_file:
- .env - .env
environment: environment:
DATABASE_URL: ${DATABASE_URL} DATABASE_URL: postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
GIGACHAT_AUTH_URL: ${GIGACHAT_AUTH_URL} GIGACHAT_AUTH_URL: ${GIGACHAT_AUTH_URL}
GIGACHAT_API_URL: ${GIGACHAT_API_URL} GIGACHAT_API_URL: ${GIGACHAT_API_URL}
GIGACHAT_SCOPE: ${GIGACHAT_SCOPE} GIGACHAT_SCOPE: ${GIGACHAT_SCOPE}
-380
View File
@@ -1,380 +0,0 @@
{
"layers": {
"C0_SOURCE_CHUNKS": {
"retriever": {
"class": "RagService",
"file": "app/modules/rag/services/rag_service.py",
"method": "retrieve"
},
"indexer": {
"class": "CodeTextDocumentBuilder",
"file": "app/modules/rag/indexing/code/code_text/document_builder.py",
"method": "build"
},
"input": {
"type": "observed shape",
"fields": {
"rag_session_id": {
"type": "string",
"required": true
},
"query": {
"type": "string",
"required": true
},
"layers": {
"type": "implicit list[string]",
"required": false,
"source": "RagQueryRouter.layers_for_mode('code')"
}
}
},
"output": {
"type": "list[dict]",
"fields": {
"source": "string",
"content": "string",
"layer": "\"C0_SOURCE_CHUNKS\"",
"title": "string",
"metadata": {
"chunk_index": "int",
"chunk_type": "\"symbol_block\" | \"window\"",
"module_or_unit": "string",
"artifact_type": "\"CODE\""
},
"score": "float | null"
}
},
"examples": {
"input": {
"rag_session_id": "rag-123",
"query": "where is implemented get_user"
},
"output": {
"source": "app/api/users.py",
"content": "async def get_user(user_id: str):\n service = UserService()\n return service.get_user(user_id)",
"layer": "C0_SOURCE_CHUNKS",
"title": "app/api/users.py:get_user",
"metadata": {
"chunk_index": 0,
"chunk_type": "symbol_block",
"module_or_unit": "app.api.users",
"artifact_type": "CODE"
},
"score": 0.07
}
},
"defaults": {
"retrieve_limit": 8,
"embed_batch_size_env": "RAG_EMBED_BATCH_SIZE",
"embed_batch_size_default": 16,
"window_chunk_size_lines": 80,
"window_overlap_lines": 15
},
"limitations": [
"Line spans are stored but not returned in the public retrieval item shape.",
"No direct path or namespace filter is exposed through the retrieval endpoint."
]
},
"C1_SYMBOL_CATALOG": {
"retriever": {
"class": "RagService",
"file": "app/modules/rag/services/rag_service.py",
"method": "retrieve"
},
"indexer": {
"class": "SymbolDocumentBuilder",
"file": "app/modules/rag/indexing/code/symbols/document_builder.py",
"method": "build"
},
"input": {
"type": "observed shape",
"fields": {
"rag_session_id": {
"type": "string",
"required": true
},
"query": {
"type": "string",
"required": true
},
"query_term_expansion": {
"type": "list[string]",
"required": false,
"source": "extract_query_terms(query_text)",
"max_items": 6
}
}
},
"output": {
"type": "list[dict]",
"fields": {
"source": "string",
"content": "string",
"layer": "\"C1_SYMBOL_CATALOG\"",
"title": "string",
"metadata": {
"symbol_id": "string",
"qname": "string",
"kind": "\"class\" | \"function\" | \"method\" | \"const\"",
"signature": "string",
"decorators_or_annotations": "list[string]",
"docstring_or_javadoc": "string | null",
"parent_symbol_id": "string | null",
"package_or_module": "string",
"is_entry_candidate": "bool",
"lang_payload": "object",
"artifact_type": "\"CODE\""
},
"score": "float | null"
}
},
"examples": {
"input": {
"rag_session_id": "rag-123",
"query": "where is implemented get_user"
},
"output": {
"source": "app/api/users.py",
"content": "function get_user\nget_user(user_id)",
"layer": "C1_SYMBOL_CATALOG",
"title": "get_user",
"metadata": {
"symbol_id": "sha256(...)",
"qname": "get_user",
"kind": "function",
"signature": "get_user(user_id)",
"decorators_or_annotations": [
"router.get"
],
"docstring_or_javadoc": null,
"parent_symbol_id": null,
"package_or_module": "app.api.users",
"is_entry_candidate": true,
"lang_payload": {
"async": true
},
"artifact_type": "CODE"
},
"score": 0.07
}
},
"defaults": {
"retrieve_limit": 8,
"layer_rank": 1
},
"limitations": [
"Only Python AST symbols are indexed.",
"Cross-file resolution is not implemented.",
"parent_symbol_id is an observed qname-like value, not guaranteed to be a symbol hash."
]
},
"C2_DEPENDENCY_GRAPH": {
"retriever": {
"class": "RagService",
"file": "app/modules/rag/services/rag_service.py",
"method": "retrieve"
},
"indexer": {
"class": "EdgeDocumentBuilder",
"file": "app/modules/rag/indexing/code/edges/document_builder.py",
"method": "build"
},
"input": {
"type": "observed shape",
"fields": {
"rag_session_id": {
"type": "string",
"required": true
},
"query": {
"type": "string",
"required": true
}
}
},
"output": {
"type": "list[dict]",
"fields": {
"source": "string",
"content": "string",
"layer": "\"C2_DEPENDENCY_GRAPH\"",
"title": "string",
"metadata": {
"edge_id": "string",
"edge_type": "\"calls\" | \"imports\" | \"inherits\"",
"src_symbol_id": "string",
"src_qname": "string",
"dst_symbol_id": "string | null",
"dst_ref": "string | null",
"resolution": "\"resolved\" | \"partial\"",
"lang_payload": "object",
"artifact_type": "\"CODE\""
},
"score": "float | null"
}
},
"examples": {
"input": {
"rag_session_id": "rag-123",
"query": "how get_user calls service"
},
"output": {
"source": "app/api/users.py",
"content": "get_user calls UserService",
"layer": "C2_DEPENDENCY_GRAPH",
"title": "get_user:calls",
"metadata": {
"edge_id": "sha256(...)",
"edge_type": "calls",
"src_symbol_id": "sha256(...)",
"src_qname": "get_user",
"dst_symbol_id": null,
"dst_ref": "UserService",
"resolution": "partial",
"lang_payload": {
"callsite_kind": "function_call"
},
"artifact_type": "CODE"
},
"score": 0.11
}
},
"defaults": {
"retrieve_limit": 8,
"layer_rank": 2,
"graph_build_mode": "static_python_ast"
},
"limitations": [
"No traversal API exists.",
"Edges are stored as retrievable rows, not as a graph-native store.",
"Destination resolution is local to one indexed file."
]
},
"C3_ENTRYPOINTS": {
"retriever": {
"class": "RagService",
"file": "app/modules/rag/services/rag_service.py",
"method": "retrieve"
},
"indexer": {
"class": "EntrypointDocumentBuilder",
"file": "app/modules/rag/indexing/code/entrypoints/document_builder.py",
"method": "build"
},
"input": {
"type": "observed shape",
"fields": {
"rag_session_id": {
"type": "string",
"required": true
},
"query": {
"type": "string",
"required": true
}
}
},
"output": {
"type": "list[dict]",
"fields": {
"source": "string",
"content": "string",
"layer": "\"C3_ENTRYPOINTS\"",
"title": "string",
"metadata": {
"entry_id": "string",
"entry_type": "\"http\" | \"cli\"",
"framework": "\"fastapi\" | \"flask\" | \"typer\" | \"click\"",
"route_or_command": "string",
"handler_symbol_id": "string",
"lang_payload": "object",
"artifact_type": "\"CODE\""
},
"score": "float | null"
}
},
"examples": {
"input": {
"rag_session_id": "rag-123",
"query": "which endpoint handles get user"
},
"output": {
"source": "app/api/users.py",
"content": "fastapi http \"/users/{user_id}\"",
"layer": "C3_ENTRYPOINTS",
"title": "\"/users/{user_id}\"",
"metadata": {
"entry_id": "sha256(...)",
"entry_type": "http",
"framework": "fastapi",
"route_or_command": "\"/users/{user_id}\"",
"handler_symbol_id": "sha256(...)",
"lang_payload": {
"methods": [
"GET"
]
},
"artifact_type": "CODE"
},
"score": 0.05
}
},
"defaults": {
"retrieve_limit": 8,
"layer_rank": 0
},
"limitations": [
"Detection is decorator-string based.",
"No Django, Celery, RQ, or cron entrypoints were found.",
"Returned payload does not expose line spans."
]
}
},
"retrieval_endpoint": {
"entrypoint": {
"file": "app/modules/rag_session/module.py",
"method": "internal_router.retrieve"
},
"request": {
"type": "dict",
"fields": {
"rag_session_id": "string | optional if project_id provided",
"project_id": "string | optional fallback for rag_session_id",
"query": "string"
}
},
"response": {
"type": "dict",
"fields": {
"items": "list[retrieval item]"
}
},
"defaults": {
"mode": "docs unless RagQueryRouter detects code hints",
"limit": 8,
"embedding_provider": "GigaChat embeddings",
"fallback_after_embedding_error": true,
"fallback_to_docs_when_code_empty": true
}
},
"ranking": {
"storage": "PostgreSQL rag_chunks + pgvector",
"query_repository": {
"class": "RagQueryRepository",
"file": "app/modules/rag/persistence/query_repository.py",
"method": "retrieve"
},
"order_by": [
"lexical_rank ASC",
"test_penalty ASC",
"layer_rank ASC",
"embedding <=> query_embedding ASC"
],
"notes": [
"lexical_rank is derived from qname/symbol_id/title/path/content matching extracted query terms",
"test_penalty is applied only when prefer_non_tests=true",
"layer priority is C3 > C1 > C2 > C0 for code retrieval"
]
}
}
-270
View File
@@ -1,270 +0,0 @@
# LLM Inventory
## Provider and SDK
- Provider in code: GigaChat / Sber
- Local SDK style: custom thin HTTP client over `requests`
- Core files:
- `app/modules/shared/gigachat/client.py`
- `app/modules/shared/gigachat/settings.py`
- `app/modules/shared/gigachat/token_provider.py`
- `app/modules/agent/llm/service.py`
There is no OpenAI SDK, Azure SDK, or local model runtime in the current implementation.
## Configuration
Model and endpoint configuration are read from environment in `GigaChatSettings.from_env()`:
- `GIGACHAT_AUTH_URL`
- default: `https://ngw.devices.sberbank.ru:9443/api/v2/oauth`
- `GIGACHAT_API_URL`
- default: `https://gigachat.devices.sberbank.ru/api/v1`
- `GIGACHAT_SCOPE`
- default: `GIGACHAT_API_PERS`
- `GIGACHAT_TOKEN`
- required for auth
- `GIGACHAT_SSL_VERIFY`
- default: `true`
- `GIGACHAT_MODEL`
- default: `GigaChat`
- `GIGACHAT_EMBEDDING_MODEL`
- default: `Embeddings`
- `AGENT_PROMPTS_DIR`
- optional prompt directory override
PostgreSQL config for retrieval storage is separate:
- `DATABASE_URL`
- default: `postgresql+psycopg://agent:agent@db:5432/agent`
## Default models
- Chat/completions model default: `GigaChat`
- Embedding model default: `Embeddings`
## Completion payload
Observed payload sent by `GigaChatClient.complete(...)`:
```json
{
"model": "GigaChat",
"messages": [
{"role": "system", "content": "<prompt template text>"},
{"role": "user", "content": "<runtime user input>"}
]
}
```
Endpoint:
- `POST {GIGACHAT_API_URL}/chat/completions`
Observed response handling:
- reads `choices[0].message.content`
- if no choices: returns empty string
## Embeddings payload
Observed payload sent by `GigaChatClient.embed(...)`:
```json
{
"model": "Embeddings",
"input": [
"<text1>",
"<text2>"
]
}
```
Endpoint:
- `POST {GIGACHAT_API_URL}/embeddings`
Observed response handling:
- expects `data` list
- maps each `item.embedding` to `list[float]`
## Parameters
### Explicitly implemented
- `model`
- `messages`
- `input`
- HTTP timeout:
- completions: `90s`
- embeddings: `90s`
- auth: `30s`
- TLS verification flag:
- `verify=settings.ssl_verify`
### Not implemented in payload
- `temperature`
- `top_p`
- `max_tokens`
- `response_format`
- tools/function calling
- streaming
- seed
- stop sequences
`ASSUMPTION:` the service uses provider defaults for sampling and output length because these fields are not sent in the request payload.
## Context and budget limits
There is no centralized token budget manager in the current code.
Observed practical limits instead:
- prompt file text is loaded as-is from disk
- user input is passed as-is
- RAG context shaping happens outside the LLM client
- docs indexing summary truncation:
- docs module catalog summary: `4000` chars
- docs policy text: `4000` chars
- project QA source bundle caps:
- top `12` rag items
- top `10` file candidates
- logging truncation only:
- LLM input/output logs capped at `1500` chars for logs
`ASSUMPTION:` there is no explicit max-context enforcement before chat completion requests. The current system relies on upstream graph logic to keep inputs small enough.
## Retry, backoff, timeout
### Timeouts
- auth: `30s`
- chat completion: `90s`
- embeddings: `90s`
### Retry
- Generic async retry wrapper exists in `app/modules/shared/retry_executor.py`
- It retries only:
- `TimeoutError`
- `ConnectionError`
- `OSError`
- Retry constants:
- `MAX_RETRIES = 5`
- backoff: `0.1 * attempt` seconds
### Important current limitation
- `GigaChatClient` raises `GigaChatError` on HTTP and request failures.
- `RetryExecutor` does not catch `GigaChatError`.
- Result: LLM and embeddings calls are effectively not retried by this generic retry helper unless errors are converted upstream.
## Prompt formation
Prompt loading is handled by `PromptLoader`:
- base dir: `app/modules/agent/prompts`
- override: `AGENT_PROMPTS_DIR`
- file naming convention: `<prompt_name>.txt`
Prompt composition model today:
- system prompt:
- full contents of selected prompt file
- user prompt:
- raw runtime input string passed by the caller
- no separate developer prompt layer in the application payload
If a prompt file is missing:
- fallback system prompt: `You are a helpful assistant.`
## Prompt templates present
- `router_intent`
- `general_answer`
- `project_answer`
- `docs_detect`
- `docs_strategy`
- `docs_plan_sections`
- `docs_generation`
- `docs_self_check`
- `docs_execution_summary`
- `project_edits_plan`
- `project_edits_hunks`
- `project_edits_self_check`
## Key LLM call entrypoints
### Composition roots
- `app/modules/agent/module.py`
- builds `GigaChatSettings`
- builds `GigaChatTokenProvider`
- builds `GigaChatClient`
- builds `PromptLoader`
- builds `AgentLlmService`
- `app/modules/rag_session/module.py`
- builds the same provider stack for embeddings used by RAG
### Main abstraction
- `AgentLlmService.generate(prompt_name, user_input, log_context=None)`
### Current generate callsites
- `app/modules/agent/engine/router/intent_classifier.py`
- `router_intent`
- `app/modules/agent/engine/graphs/base_graph.py`
- `general_answer`
- `app/modules/agent/engine/graphs/project_qa_graph.py`
- `project_answer`
- `app/modules/agent/engine/graphs/docs_graph_logic.py`
- `docs_detect`
- `docs_strategy`
- `docs_plan_sections`
- `docs_generation`
- `docs_self_check`
- `docs_execution_summary`-like usage via summary step
- `app/modules/agent/engine/graphs/project_edits_logic.py`
- `project_edits_plan`
- `project_edits_self_check`
- `project_edits_hunks`
## Logging and observability
`AgentLlmService` logs:
- input:
- `graph llm input: context=... prompt=... user_input=...`
- output:
- `graph llm output: context=... prompt=... output=...`
Log truncation:
- 1500 chars
RAG retrieval logs separately in `RagService`, but without embedding vectors.
## Integration with retrieval
There are two distinct GigaChat usages:
1. Chat/completion path for agent reasoning and generation
2. Embedding path for RAG indexing and retrieval
The embedding adapter is `GigaChatEmbedder`, used by:
- `app/modules/rag/services/rag_service.py`
## Notable limitations
- Single provider coupling: chat and embeddings both depend on GigaChat-specific endpoints.
- No model routing by scenario.
- No tool/function calling.
- No centralized prompt token budgeting.
- No explicit retry for `GigaChatError`.
- No streaming completions.
- No structured response mode beyond prompt conventions and downstream parsing.
@@ -1,13 +0,0 @@
| column | used_by | safe_to_drop | notes |
| --- | --- | --- | --- |
| `layer` | `USED_BY_CODE_V2`, `USED_BY_DOCS_INDEXING` | no | Core selector for C0-C3 and D1-D4 queries. |
| `title` | `USED_BY_CODE_V2`, `USED_BY_DOCS_INDEXING` | no | Used in lexical ranking and prompt evidence labels. |
| `metadata_json` | `USED_BY_CODE_V2`, `USED_BY_DOCS_INDEXING` | no | C2/C0 graph lookups and docs metadata depend on it. |
| `span_start`, `span_end` | `USED_BY_CODE_V2` | no | Needed for symbol-to-chunk resolution and locations. |
| `symbol_id`, `qname`, `kind`, `lang` | `USED_BY_CODE_V2` | no | Used by code indexing, ranking, trace building, and diagnostics. |
| `repo_id`, `commit_sha` | `USED_BY_CODE_V2`, `USED_BY_DOCS_INDEXING` | no | Used by indexing/cache and retained for provenance. |
| `entrypoint_type`, `framework` | `USED_BY_CODE_V2` | no | Used by C3 filtering and entrypoint diagnostics. |
| `doc_kind`, `module_id`, `section_path` | `USED_BY_DOCS_INDEXING` | no | Still written by docs indexing and covered by docs tests. |
| `artifact_type`, `section`, `doc_version`, `owner`, `system_component`, `last_modified`, `staleness_score` | `USED_BY_DOCS_INDEXING` | no | File metadata still flows through indexing/cache; left intact for now. |
| `rag_doc_id` | `UNUSED` | yes | Written into `rag_chunks` only; no reads in runtime/indexing code. |
| `links_json` | `UNUSED` | yes | Stored in `rag_chunks` only; reads exist for `rag_chunk_cache`, not `rag_chunks`. |
-31
View File
@@ -1,31 +0,0 @@
flowchart TD
A["HTTP: POST /internal/rag/retrieve"] --> B["RagModule.internal_router.retrieve(payload)"]
B --> C["RagService.retrieve(rag_session_id, query)"]
C --> D["RagQueryRouter.resolve_mode(query)"]
D --> E["RagQueryRouter.layers_for_mode(mode)"]
C --> F["GigaChatEmbedder.embed([query])"]
F --> G["GigaChatClient.embed(payload)"]
G --> H["POST /embeddings"]
C --> I["RagRepository.retrieve(...)"]
I --> J["RagQueryRepository.retrieve(...)"]
J --> K["PostgreSQL rag_chunks + pgvector"]
K --> L["ORDER BY lexical_rank, test_penalty, layer_rank, vector distance"]
L --> M["rows: path/content/layer/title/metadata/span/distance"]
M --> N["normalize to {source, content, layer, title, metadata, score}"]
N --> O["response: {items: [...]}"]
C --> P["embedding error?"]
P -->|yes| Q["RagRepository.fallback_chunks(...)"]
Q --> R["latest rows by id DESC"]
R --> N
C --> S["no rows and mode != docs?"]
S -->|yes| T["fallback to docs layers"]
T --> I
U["GraphAgentRuntime for project/qa"] --> V["ProjectQaRetrievalGraphFactory._retrieve_context"]
V --> C
V --> W["ProjectQaSupport.build_source_bundle(...)"]
W --> X["source_bundle"]
X --> Y["context_analysis"]
Y --> Z["answer_composition"]
-457
View File
@@ -1,457 +0,0 @@
# Retrieval Inventory
## Scope and method
This document describes the retrieval and indexing pipeline as implemented in code today. The inventory is based primarily on:
- `app/modules/rag/services/rag_service.py`
- `app/modules/rag/persistence/*.py`
- `app/modules/rag/indexing/code/**/*.py`
- `app/modules/rag/indexing/docs/**/*.py`
- `app/modules/rag_session/module.py`
- `app/modules/agent/engine/graphs/project_qa_step_graphs.py`
- `app/modules/agent/engine/orchestrator/*.py`
`ASSUMPTION:` the intended layer semantics are the ones implied by code and tests, not by future architecture plans. This matters because only `C0` through `C3` are materially implemented today; `C4+` exist only as enum constants.
## Current retrieval pipeline
1. Retrieval entrypoint is `POST /internal/rag/retrieve` in `app/modules/rag_session/module.py`.
2. The endpoint calls `RagService.retrieve(rag_session_id, query)`.
3. `RagQueryRouter` chooses `docs` or `code` mode from the raw query text.
4. `RagService` computes a single embedding for the full query via `GigaChatEmbedder`.
5. `RagQueryRepository.retrieve(...)` runs one SQL query against `rag_chunks` in PostgreSQL with `pgvector`.
6. Ranking order is:
- lexical rank
- test-file penalty
- layer rank
- vector distance `embedding <=> query_embedding`
7. Response items are normalized to `{source, content, layer, title, metadata, score}`.
8. If embeddings fail, retrieval falls back to latest chunks from the same layers.
9. If code retrieval returns nothing, service falls back to docs layers.
## Storage and indices
- Primary store: PostgreSQL from `DATABASE_URL`, configured in `app/modules/shared/db.py`.
- Vector extension: `CREATE EXTENSION IF NOT EXISTS vector` in `app/modules/rag/persistence/schema_repository.py`.
- Primary table: `rag_chunks`.
- Cache tables:
- `rag_blob_cache`
- `rag_chunk_cache`
- `rag_session_chunk_map`
- SQL indexes currently created:
- `(rag_session_id)`
- `(rag_session_id, layer)`
- `(rag_session_id, layer, path)`
- `(qname)`
- `(symbol_id)`
- `(module_id)`
- `(doc_kind)`
- `(entrypoint_type, framework)`
`ASSUMPTION:` there is no explicit ANN index for the vector column in schema code. The code creates general SQL indexes, but no `ivfflat`/`hnsw` index is defined here.
## Layer: C0_SOURCE_CHUNKS
### Implementation
- Produced by `CodeIndexingPipeline.index_file(...)` in `app/modules/rag/indexing/code/pipeline.py`.
- Chunking logic: `CodeTextChunker.chunk(...)` in `app/modules/rag/indexing/code/code_text/chunker.py`.
- Document builder: `CodeTextDocumentBuilder.build(...)` in `app/modules/rag/indexing/code/code_text/document_builder.py`.
- Persisted via `RagDocumentRepository.insert_documents(...)` into `rag_chunks`.
### Input contract
This is an indexing layer, not a direct public retriever. The observed upstream indexing input is a file dict with at least:
- required:
- `path: str`
- `content: str`
- optional:
- `commit_sha: str | None`
- `content_hash: str`
- metadata fields copied through by `RagService._document_metadata(...)`
For retrieval, the layer is queried only indirectly through:
- `rag_session_id: str`
- `query: str`
- inferred mode/layers from `RagQueryRouter`
- fixed `limit=8`
### Output contract
Stored document shape:
- top-level:
- `layer = "C0_SOURCE_CHUNKS"`
- `lang = "python"`
- `source.repo_id`
- `source.commit_sha`
- `source.path`
- `title`
- `text`
- `span.start_line`
- `span.end_line`
- `embedding`
- metadata:
- `chunk_index`
- `chunk_type`: `symbol_block` or `window`
- `module_or_unit`
- `artifact_type = "CODE"`
- plus file-level metadata injected by `RagService`
Returned retrieval item shape:
- `source`
- `content`
- `layer`
- `title`
- `metadata`
- `score`
No `line_start` / `line_end` are returned to the caller directly; they remain in DB columns `span_start` / `span_end` and are only used in logs.
### Defaults & limits
- AST chunking prefers one chunk per top-level class/function/async function.
- Fallback window chunking:
- `size = 80` lines
- `overlap = 15` lines
- Global retrieval limit from `RagService.retrieve(...)`: `8`
- Embedding batch size from env:
- `RAG_EMBED_BATCH_SIZE`
- default `16`
### Known issues
- Nested methods/functions are not emitted as C0 chunks unless represented inside a selected top-level block.
- Returned API payload omits line spans even though storage has them.
- No direct filter by path, namespace, symbol, or `top_k` is exposed through the current endpoint.
## Layer: C1_SYMBOL_CATALOG
### Implementation
- Symbol extraction: `SymbolExtractor.extract(...)` in `app/modules/rag/indexing/code/symbols/extractor.py`.
- AST parsing: `PythonAstParser.parse_module(...)`.
- Document builder: `SymbolDocumentBuilder.build(...)`.
- Retrieval reads rows from `rag_chunks`; there is no dedicated symbol table.
### Input contract
Indexing input is the same per-file payload as C0.
Observed symbol extraction source:
- Python AST only
- supported symbol kinds:
- `class`
- `function`
- `method`
- `const` for top-level imports/import aliases
Retrieval input is still the generic text query endpoint. Query terms are enriched by `extract_query_terms(...)`:
- extracts identifier-like tokens from query text
- normalizes camelCase/PascalCase to snake_case
- adds special intent terms for management/control-related queries
- max observed query terms: `6`
### Output contract
Stored document shape:
- top-level:
- `layer = "C1_SYMBOL_CATALOG"`
- `title = qname`
- `text = "<kind> <qname>\n<signature>\n<docstring?>"`
- `span.start_line`
- `span.end_line`
- metadata:
- `symbol_id`
- `qname`
- `kind`
- `signature`
- `decorators_or_annotations`
- `docstring_or_javadoc`
- `parent_symbol_id`
- `package_or_module`
- `is_entry_candidate`
- `lang_payload`
- `artifact_type = "CODE"`
Observed `lang_payload` variants:
- class:
- `bases`
- function/method:
- `async`
- import alias:
- `imported_from`
- `import_alias`
### Defaults & limits
- Only Python source files are indexed into C-layers.
- Import and import-from declarations are materialized as `const` symbols only at module top level.
- Retrieval ranking gives C1 priority rank `1`, after C3 and before C2/C0.
### Known issues
- No explicit visibility/public-private model.
- `parent_symbol_id` currently stores the parent qname string from the stack, not the parent symbol hash. This is an observed implementation detail.
- Cross-file symbol resolution is not implemented; `dst_symbol_id` in edges resolves only against symbols extracted from the same file.
## Layer: C2_DEPENDENCY_GRAPH
### Implementation
- Edge extraction: `EdgeExtractor.extract(...)` in `app/modules/rag/indexing/code/edges/extractor.py`.
- Document builder: `EdgeDocumentBuilder.build(...)`.
- Built during `CodeIndexingPipeline.index_file(...)`.
### Input contract
Indexing input is the same per-file source payload as C0/C1.
Graph construction method:
- static analysis only
- Python AST walk only
- no runtime tracing
- no tree-sitter
Observed edge types:
- `calls`
- `imports`
- `inherits`
### Output contract
Stored document shape:
- top-level:
- `layer = "C2_DEPENDENCY_GRAPH"`
- `title = "<src_qname>:<edge_type>"`
- `text = "<src_qname> <edge_type> <dst>"`
- `span.start_line`
- `span.end_line`
- `links` contains one evidence link of type `EDGE`
- metadata:
- `edge_id`
- `edge_type`
- `src_symbol_id`
- `src_qname`
- `dst_symbol_id`
- `dst_ref`
- `resolution`: `resolved` or `partial`
- `lang_payload`
- `artifact_type = "CODE"`
Observed `lang_payload` usage:
- for calls: may include `callsite_kind = "function_call"`
### Defaults & limits
- Edge extraction is per-file only.
- `imports` edges are emitted only while visiting a class/function scope; top-level imports do not become C2 edges.
- Layer rank in retrieval SQL: `2`
### Known issues
- There is no traversal API, graph repository, or query language over C2. Retrieval only treats edges as text/vector rows in `rag_chunks`.
- Destination resolution is local to the file-level qname map.
- Top-level module import relationships are incompletely represented because `visit_Import` / `visit_ImportFrom` skip when there is no current scope.
## Layer: C3_ENTRYPOINTS
### Implementation
- Detection registry: `EntrypointDetectorRegistry.detect_all(...)`.
- Detectors:
- `FastApiEntrypointDetector`
- `FlaskEntrypointDetector`
- `TyperClickEntrypointDetector`
- Document builder: `EntrypointDocumentBuilder.build(...)`.
### Input contract
Indexing input is the same per-file source payload as other C-layers.
Detected entrypoint families today:
- HTTP:
- FastAPI decorators such as `.get`, `.post`, `.put`, `.patch`, `.delete`, `.route`
- Flask `.route`
- CLI:
- Typer/Click `.command`
- Typer/Click `.callback`
Not detected:
- Django routes
- Celery tasks
- RQ jobs
- cron jobs / scheduler entries
### Output contract
Stored document shape:
- top-level:
- `layer = "C3_ENTRYPOINTS"`
- `title = route_or_command`
- `text = "<framework> <entry_type> <route_or_command>"`
- `span.start_line`
- `span.end_line`
- `links` contains one evidence link of type `CODE_SPAN`
- metadata:
- `entry_id`
- `entry_type`: observed `http` or `cli`
- `framework`: observed `fastapi`, `flask`, `typer`, `click`
- `route_or_command`
- `handler_symbol_id`
- `lang_payload`
- `artifact_type = "CODE"`
FastAPI-specific observed payload:
- `lang_payload.methods = [HTTP_METHOD]` for `.get/.post/...`
### Defaults & limits
- Retrieval layer rank: `0` highest among code layers.
- Entrypoint mapping is handler-symbol centric:
- decorator match -> symbol -> `handler_symbol_id`
- physical location comes from symbol span
### Known issues
- Route parsing is string-based from decorator text, not semantic AST argument parsing.
- No dedicated entrypoint tags beyond `entry_type`, `framework`, and raw decorator-derived payload.
- Background jobs and non-decorator entrypoints are not indexed.
## Dependency graph / trace current state
### Exists or stub?
- C2 exists and is populated.
- It is not a stub.
- It is also not a full-project dependency graph service; it is a set of per-edge documents stored in `rag_chunks`.
### How the graph is built
- static Python AST analysis
- no runtime instrumentation
- no import graph resolver across modules
- no tree-sitter
### Edge types in data
- `calls`
- `imports`
- `inherits`
### Traversal API
- No traversal API was found in `app/modules/rag/*` or `app/modules/agent/*`.
- No method accepts graph traversal parameters such as depth, start node, edge filters, or BFS/DFS strategy.
- Current access path is only retrieval over indexed edge documents.
## Entrypoints current state
### Implemented extraction
- HTTP routes:
- FastAPI
- Flask
- CLI:
- Typer
- Click
### Mapping model
- `entrypoint -> handler_symbol_id -> symbol span/path`
- The entrypoint record itself stores:
- framework
- entry type
- raw route/command string
- handler symbol id
### Tags/types
- `entry_type` is the main normalized tag.
- Observed values: `http`, `cli`.
- `framework` is the second discriminator.
- There are no richer endpoint taxonomies such as `job`, `worker`, `webhook`, `scheduler`.
## Defaults and operational limits
- Query mode default: `docs`
- Code mode is enabled by keyword heuristics in `RagQueryRouter`
- Retrieval hard limit: `8`
- Fallback limit: `8`
- Query term extraction limit: `6`
- Ranked source bundle for project QA:
- top `12` RAG items
- top `10` file candidates
- No exposed `namespace`, `path_prefixes`, `top_k`, `max_chars`, `max_chunks`, `max_depth` in the public/internal retrieval endpoint
`ASSUMPTION:` the absence of these controls in endpoint and service signatures means they are not part of the current supported contract, even though `RagQueryRepository.retrieve(...)` has an internal `path_prefixes` parameter.
## Known cross-cutting issues
- Retrieval contract is effectively text-only at API level; structured retrieval exists only as internal SQL parameters.
- Response payload drops explicit line spans even though spans are stored.
- Vector retrieval is coupled to a single provider-specific embedder.
- Docs mode is the default, so code retrieval depends on heuristic query phrasing unless the project/qa graph prepends `по коду`.
- There is no separate retrieval contract per layer exposed over API; all layer selection is implicit.
## Where to plug ExplainPack pipeline
### Option 1: replace or extend `project_qa/context_analysis`
- Code location:
- `app/modules/agent/engine/graphs/project_qa_step_graphs.py`
- Why:
- retrieval is already complete at this step
- input bundle already contains ranked `rag_items` and `file_candidates`
- output is already a structured `analysis_brief`
- Risk:
- low
- minimal invasion if ExplainPack consumes `source_bundle` and emits the same `analysis_brief` shape
### Option 2: insert a new orchestrator step between `context_retrieval` and `context_analysis`
- Code location:
- `app/modules/agent/engine/orchestrator/template_registry.py`
- `app/modules/agent/engine/orchestrator/step_registry.py`
- Why:
- preserves current retrieval behavior
- makes ExplainPack an explicit pipeline stage with its own artifact
- cleanest for observability and future A/B migration
- Risk:
- low to medium
- requires one new artifact contract and one extra orchestration step, but no change to retrieval storage
### Option 3: introduce ExplainPack inside `ExplainActions.extract_logic`
- Code location:
- `app/modules/agent/engine/orchestrator/actions/explain_actions.py`
- Why:
- useful if ExplainPack is meant only for explain-style scenarios
- keeps general project QA untouched
- Risk:
- medium
- narrower integration point; may create duplicate reasoning logic separate from project QA analysis path
## Bottom line
- C0-C3 are implemented and persisted in one physical store: `rag_chunks`.
- Retrieval is a hybrid SQL ranking over lexical heuristics plus pgvector distance.
- C2 exists, but only as retrievable edge documents, not as a traversable graph subsystem.
- C3 covers FastAPI/Flask/Typer/Click only.
- The least invasive ExplainPack integration point is after retrieval and before answer composition, preferably as a new explicit orchestrator artifact or as a replacement for `context_analysis`.
@@ -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,214 @@
---
## 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`.
- 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}`, `/api/rag/sessions/{rag_session_id}/jobs/{index_job_id}/events`.
- Key data flows: snapshot indexing, incremental reindex, retrieval из `rag_chunks`.
- Source of truth: код `src/app/modules/rag/*`.
## Назначение
Пакет `rag` отвечает за полный цикл подготовки retrieval-контекста для проекта: принимает снапшоты и изменения файлов, преобразует их в набор атомарных `RagDocument`, векторизует, сохраняет в БД и предоставляет доступ к индексированным данным другим частям системы.
## Контекст
Модуль используется как инфраструктурный слой для agent/runtime. На вход он принимает snapshot и изменения файлов проекта. На выходе формирует устойчивый индекс, ассоциированный с `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 репозитории.
## Основные модули
| 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` |
## Основные доменные области
- 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 | публичный HTTP 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` |
## Основные потоки
### 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.
- HTTP retrieval endpoint в модуле не публикуется.
- Реальное 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 не содержат собственной бизнес-авторизации внутри модуля и полагаются на внешний слой приложения.
### Reliability
- Проектный `asyncio.Lock` предотвращает параллельную индексацию одной `rag_session`.
- `RetryExecutor` повторяет временные сбои индексации.
### Observability
- Logs: `RagService` пишет предупреждения по cache hit/miss и skipped files.
- Metrics: явные метрики не выделены.
- Traces: явная трассировка не реализована.
- Audit: job status сохраняется в БД.
### 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`
### Symbols
- `RagModule`
- `RagService`
- `IndexingOrchestrator`
- `RagRepository`
## Связанные документы
- `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,164 @@
---
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`
## Связанные сущности
- `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 в модуле не публикуется.
## Нефункциональные требования
### 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`
- HTTP retrieval endpoint отсутствует
## Связанные сущности
- `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`. |
+32
View File
@@ -0,0 +1,32 @@
# DOCS Intent Router MVP
## Supported Intents
- `DOCS_QA.API_METHOD_EXPLAIN`
- `DOCS_DISCOVERY.LIST_API_METHODS`
- `DOCS_DISCOVERY.FIND_DOCUMENTS_BY_DOMAIN`
- `DOCS_GENERATION.GENERATE_OPENAPI`
- `DOCS_FALLBACK.GENERAL_DOCS_QA`
## Routing Flow
1. `Stage A`: deterministic pre-routing нормализует запрос, извлекает anchors и scope, считает rule-based confidence.
2. `Stage B`: confidence gating пропускает high-confidence кейсы напрямую и эскалирует ambiguous/weak запросы в LLM.
3. `Stage C`: LLM classifier выбирает только один из 5 MVP саб-интентов и возвращает строгий JSON.
4. После выбора саб-интента router всегда прикрепляет декларативный `retrieval_plan`.
## Confidence And Escalation
- `>= 0.8` и без конфликтующих сигналов: `routing_mode=deterministic`.
- Ниже порога, при пересечении интентов, слабых anchors или коротком неоднозначном запросе: `routing_mode=llm_assisted`.
- Если LLM недоступен или вернул невалидный класс: `routing_mode=llm_fallback` c fallback в `GENERAL_DOCS_QA`.
## Retrieval Plan Mapping
- `API_METHOD_EXPLAIN` -> `docs_api_method_explain_v1`
- `LIST_API_METHODS` -> `docs_list_api_methods_v1`
- `FIND_DOCUMENTS_BY_DOMAIN` -> `docs_find_documents_by_domain_v1`
- `GENERATE_OPENAPI` -> `docs_generate_openapi_v1`
- `GENERAL_DOCS_QA` -> `docs_general_docs_qa_v1`
`retrieval_plan` хранится декларативно в `src/app/modules/agent/intent_router_v2/docs_mvp/retrieval_plans.py`, а legacy `retrieval_spec.filters` обогащается теми же anchors и scope для совместимости с текущим runtime.
View File
+190
View File
@@ -0,0 +1,190 @@
# Снимок runtime-контура CODE_QA (answer layer)
Документ фиксирует текущее состояние runtime-контура `CODE_QA` после рефакторинга для планирования доработок answer layer. Без предложений по новому дизайну и без implementation brief.
---
## 1. Entry point
- **HTTP:** `POST /api/chat/messages``ChatModule.public_router()``send_message()`.
Файл: `src/app/modules/chat/module.py`, строки 7481.
- **Условие:** при `SIMPLE_CODE_EXPLAIN_ONLY=true` запрос идёт в `CodeExplainChatService.handle_message()` (прямой explain без полного CODE_QA pipeline). При `false` — в оркестратор.
- **Оркестратор:** `ChatOrchestrator.enqueue_message()` создаёт задачу и запускает `_process_task()` → в нём вызывается `self._runtime.run(...)`.
Файл: `src/app/modules/chat/service.py`, строки 4769, 71132.
- **Runtime-адаптер:** `CodeQaRunnerAdapter` реализует `AgentRunner`; в `run()` вызывает `self._executor.execute(user_query=..., rag_session_id=..., files_map=...)` в thread pool.
Файл: `src/app/modules/agent/runtime/code_qa_runner_adapter.py`, строки 2141.
- **Фактическая точка входа CODE_QA:** `AgentRuntimeExecutor.execute()`.
Файл: `src/app/modules/agent/runtime/executor.py`, строка 53.
Создание executor: `application.py`, строка 48 — `_executor = AgentRuntimeExecutor(llm=..., retrieval=...)`.
---
## 2. Runtime pipeline
Цепочка внутри `AgentRuntimeExecutor.execute()` (файл `executor.py`):
| Шаг | Файл | Класс/функция | Роль |
|-----|------|----------------|------|
| 1. Роутинг | `executor.py` | `self._router.route(user_query, ...)` | Intent + sub-intent, query_plan, retrieval_spec, symbol_resolution (pending). |
| 2. Сборка запроса retrieval | `retrieval_request_builder.py` | `build_retrieval_request(router_result, rag_session_id)` | Из `RouterResult` собирается `RetrievalRequest`: query, sub_intent, path_scope, requested_layers, retrieval_spec, constraints, query_plan. |
| 3. Retrieval | `executor.py` | `self._retrieve(state)``RuntimeRetrievalAdapter.retrieve_with_plan()` или `retrieve_exact_files()` для OPEN_FILE | По плану или по точным путям; возвращает `raw_rows` (list[dict]). |
| 4. Догидрация (только FIND_ENTRYPOINTS) | `executor.py` | `_hydrate_entrypoint_sources()` | Дозапрос C0 по путям из C3 entrypoints. |
| 5. Разрешение символа | `executor.py` | `_resolve_symbol(initial, raw_rows)` | По C1_SYMBOL_CATALOG: resolved / ambiguous / not_found; обновляет `state.router_result.symbol_resolution`. |
| 6. Retrieval result | `retrieval_result_builder.py` | `build_retrieval_result(raw_rows, report, symbol_resolution)` | Нормализованный `RetrievalResult`: code_chunks, relations, entrypoints, test_candidates, layer_outcomes и т.д. Для EXPLAIN при not_found/ambiguous — пересборка с пустыми rows (строки 9091 executor). |
| 7. Evidence bundle | `evidence_bundle_builder.py` | `build_evidence_bundle(retrieval_result, router_result)` | `EvidenceBundle`: resolved_sub_intent, resolved_target, code_chunks, relations, entrypoints, test_evidence, retrieval_summary. sufficient/failure_reasons не выставляются здесь. |
| 8. Pre evidence gate | `evidence_gate.py` | `evaluate_evidence(state.evidence_pack)` | По sub_intent проверяет достаточность (target, evidence_count, слои, entrypoints, tests). Выставляет `bundle.sufficient`, возвращает `EvidenceGateDecision`; от этого — `state.answer_mode` (normal/degraded). |
| 9. Answer policy | `policy.py` | `self._answer_policy.decide(router_result, gate_decision)` | Решение: вызывать LLM или короткий ответ (OPEN_FILE not_found, EXPLAIN not_found/ambiguous, gate не прошёл). При `should_call_llm=False` сразу идём в `assemble_final_result` с `decision.answer`. |
| 10. Synthesis input | `answer_synthesis.py` | `build_answer_synthesis_input(user_query, state.evidence_pack)` | Строит `AnswerSynthesisInput`: fast_context, deep_context, evidence_summary, semantic_hints, curated_facts (из answer_fact_curator). |
| 11. Выбор промпта | `prompt_selector.py` | `self._prompt_selector.select(sub_intent=..., answer_mode=...)` | Имя системного промпта по sub_intent (и degraded). |
| 12. Payload | `prompt_payload_builder.py` | `self._payload_builder.build(user_query, synthesis_input, evidence_pack, answer_mode)` | JSON payload для LLM: user_query, resolved_scenario, fast/deep_context, evidence_summary, curated must_mention_*, layer_guide, entrypoints, scenario-specific поля. |
| 13. Генерация черновика | `generator.py` | `self._generator.generate(prompt_name, prompt_payload)` | Вызов `AgentLlmService.generate(prompt_name, payload)` → черновик ответа. |
| 14. Post evidence gate | `post_gate.py` | `self._post_gate.validate(answer, answer_mode, ..., sub_intent, user_query, evidence_pack)` | Проверка черновика по sub_intent (EXPLAIN/ARCHITECTURE/TRACE_FLOW/…), возврат `RuntimeValidationResult(passed, action, reasons)`. |
| 15. Repair (если не passed) | `repair.py` | `self._repair.repair(draft_answer, validation, prompt_payload)` | Один вызов LLM с промптом `code_qa_repair_answer`; повторная валидация; при повторном fail — fallback answer. |
| 16. Финальный результат | `result_assembler.py` | `assemble_final_result(state, draft=..., final_answer=..., ...)` | Сборка `RuntimeFinalResult` и диагностики. |
Sub-intent для CODE_QA задаётся в роутере: `QueryPlanBuilder` использует `SubIntentDetector.detect()` и `_resolve_sub_intent()`; итог в `query_plan.sub_intent`. Ретривал-слои по sub_intent задаются в `RetrievalSpecFactory._with_sub_intent_layers()` (`retrieval_spec_factory.py`).
---
## 3. Answer path
- **Выбор промпта:** `RuntimePromptSelector.select(sub_intent, answer_mode)``src/app/modules/agent/runtime/steps/generation/prompt_selector.py`, строки 1821. При answer_mode in `{"degraded","not_found","insufficient"}` возвращается `code_qa_degraded_answer`, иначе — по `sub_intent` из словаря (fallback `code_qa_explain_answer`).
- **Сборка payload:** `RuntimePromptPayloadBuilder.build()``prompt_payload_builder.py`, строки 21–44. В payload попадают: `user_query`, `resolved_scenario`, `resolved_target`, `answer_mode`, `fast_context`, `deep_context`, `evidence_summary`, `semantic_hints`, `diagnostic_hints`, `retrieval_summary`, `confirmed_entrypoints`, `required_entrypoints`, `layer_guide`, плюс сценарий-специфичные поля из `_scenario_payload(synthesis_input)` (must_mention_*, fact_gaps и т.д.).
- **Draft answer:** создаётся в `executor.py`, строки 242246: `RuntimeDraftAnswer(prompt_name=..., prompt_payload=..., answer=self._generator.generate(...))`.
- **Post-processing:** отдельного шага нет; после генерации сразу идёт post-validation.
- **Repair:** `RuntimeAnswerRepairService.repair()``repair.py`, строки 16–37. Формирует JSON с draft_answer, validation_reasons, repair_focus, prompt_payload и один раз вызывает LLM с `code_qa_repair_answer`.
- **Final text:** в executor: при passed — `final_answer = draft.answer` (или результат repair); при не passed после repair — `_fallback_answer(state)`. Итоговая строка попадает в `RuntimeFinalResult.final_answer` в `assemble_final_result()`.
---
## 4. Prompt selection
- **Где:** `src/app/modules/agent/runtime/steps/generation/prompt_selector.py`, класс `RuntimePromptSelector`, метод `select(sub_intent, answer_mode)`.
- **Правила:**
- answer_mode in `{"degraded","not_found","insufficient"}``code_qa_degraded_answer`.
- Иначе по `sub_intent.upper()` из `_PROMPTS`; при отсутствии ключа — `code_qa_explain_answer`.
- **Используемые имена промптов для целевых sub_intent:**
| sub_intent | prompt name |
|-------------|--------------------------------|
| EXPLAIN | `code_qa_explain_answer` |
| EXPLAIN_LOCAL| `code_qa_explain_local_answer` |
| ARCHITECTURE| `code_qa_architecture_answer` |
| TRACE_FLOW | `code_qa_trace_flow_answer` |
- **Шаблоны:** загружаются по имени из YAML в `AgentLlmService.generate()``PromptLoader.load(name)`; конфиг — `src/app/modules/agent/llm/prompts.yml`. Ключи в YAML совпадают с именами выше (в т.ч. `code_qa_explain_answer`, `code_qa_architecture_answer`, `code_qa_trace_flow_answer`); repair — `code_qa_repair_answer`.
- **Выбор по sub_intent:** да, только через `RuntimePromptSelector.select(sub_intent=state.retrieval_request.sub_intent, ...)` в executor, строка 231.
---
## 5. Evidence-to-answer boundary
- **В answer layer evidence приходит как:**
- `EvidenceBundle` (в state.evidence_pack) и
- `AnswerSynthesisInput` (state.synthesis_input), собранный из bundle в `build_answer_synthesis_input()`.
- **Модели/DTO:**
- `EvidenceBundle`: `contracts.py`, 90106 — resolved_intent, resolved_sub_intent, resolved_target, target_type, code_chunks, relations, entrypoints, test_evidence, evidence_count, retrieval_summary.
- `AnswerSynthesisInput`: `contracts.py`, 109121 — user_question, resolved_scenario, resolved_target, fast_context, deep_context, evidence_summary, semantic_hints, **curated_facts**, evidence_sufficient, diagnostic_hints.
- Curated facts строит `answer_fact_curator.build_curated_answer_facts(bundle)` — словарь с ключами `explain`, `architecture`, `trace_flow` и общими полями (scenario, semantic_hints, relation_count и т.д.).
- **Что реально уходит в payload (prompt_payload_builder):**
- Общее: user_query, resolved_scenario, resolved_target, answer_mode, fast_context, deep_context, evidence_summary, semantic_hints, diagnostic_hints, retrieval_summary, confirmed_entrypoints, required_entrypoints, layer_guide.
- EXPLAIN: must_mention_methods/fields/calls/dependencies/constructor_args/files, must_not_infer_missing_details, fact_gaps (из curated_facts["explain"]).
- ARCHITECTURE: must_mention_components/relations, must_use_relation_verbs, must_avoid_semantic_labels_as_primary_claims, must_not_use_retrieval_labels, fact_gaps (из curated_facts["architecture"]).
- TRACE_FLOW: must_mention_flow_steps/calls/sequence_edges, must_avoid_overclaiming_full_flow, fact_gaps (из curated_facts["trace_flow"]).
- **Curated-поля (answer_fact_curator):**
- explain: required_methods, required_calls, required_fields, required_dependencies, required_constructor_args, required_files, fact_gaps (и др.).
- architecture: required_components, required_relations (source/verb/target/edge_type), required_relation_verbs, required_*_edges, forbidden_labels, fact_gaps.
- trace_flow: required_flow_steps (step, source, verb, target, path, line_span), required_calls, required_sequence_edges, fact_gaps.
То есть в LLM попадает не сырой retrieval, а нормализованный контекст (fast/deep_context, evidence_summary) плюс явные списки «must_mention_*» и fact_gaps по сценарию; для methods/dependencies/relations/flow steps уже есть выделенные curated-поля.
---
## 6. Post-validation / answer quality control
- **Post-evidence gate (runtime):** есть. `RuntimePostEvidenceGate.validate()``src/app/modules/agent/runtime/steps/gates/post/post_gate.py`, строки 39–65. Вызывается после генерации черновика (и после repair — повторно).
- **Answer validator:** это тот же post_gate: проверяет пустой ответ, соответствие answer_mode (degraded/not_found/ambiguous) требуемым формулировкам, длину при degraded, затем для normal — `_normal_answer_reasons()` по sub_intent.
- **Repair loop:** один раунд. При `not validation.passed` и наличии `self._repair` вызывается `repair()`; затем повторный `validate()`; если снова не passed — подставляется `_fallback_answer()` и смена answer_mode (`executor.py`, 281298).
- **Правила по sub_intent (post_gate):**
- **EXPLAIN** (93124): target focus; vagueness (_VAGUE_PHRASES); наличие required_methods/calls/dependencies (хотя бы одна группа); «too_vague_for_explain» при нуле совпадений; semantic_leakage (роли из semantic_hints без опоры на код).
- **ARCHITECTURE** (126150): target focus; vagueness; required_components, required_relations, relation_verbs; forbidden_labels (retrieval artifacts); methods_as_primary_components; «too_vague_for_architecture»; semantic_leakage.
- **TRACE_FLOW** (152171): target focus; vagueness; required_flow_steps и required_calls; _mentions_steps (сначала/затем или нумерация); overclaims (_OPTIMISTIC_TRACE_CLAIMS); «too_vague_for_trace_flow».
- **Technical precision для EXPLAIN:** проверяется косвенно: упоминание методов/вызовов/зависимостей из curated; явной проверки «только факты из кода» по токенам нет.
- **Concrete relations для ARCHITECTURE:** да — `_mentions_relations(answer, relations)` и упоминание verbs.
- **Concrete steps и overclaim для TRACE_FLOW:** да — `_mentions_steps`, `_mentions_relations` по steps, и проверка фраз из _OPTIMISTIC_TRACE_CLAIMS.
---
## 7. Problem sources (что может давать слабые ответы)
- **Payload shaping:** `prompt_payload_builder.py` — если curated_facts пустые или скудные (мало methods/calls/relations/steps), must_mention_* не направляют модель; deep_context обрезается до 30 чанков по 800 символов — возможна потеря важных деталей.
- **Prompts:** `prompts.yml` — длинные общие инструкции; для EXPLAIN/ARCHITECTURE/TRACE_FLOW нет жёсткой привязки к структуре payload (например, «обязательно используй must_mention_flow_steps по порядку»); модель может игнорировать fact_gaps.
- **Evidence normalization:** `answer_fact_curator` — методы/вызовы/relations извлекаются эвристически (regex, C1/C2); при слабом C1/C2 или нестандартных именах curated-списки пустеют → валидатор не к чему привязываться, ответ считается «vague».
- **Weak validation:** `post_gate` — проверки по вхождению подстрок (alias) и по небольшому набору фраз; нет проверки полноты (все ли must_mention_* упомянуты), нет проверки порядка шагов для TRACE_FLOW; semantic_leakage выключается при has_concrete_support, что может пропускать смешанные ответы.
- **Repair policy:** один вызов repair с общим промптом `code_qa_repair_answer` и repair_focus по reasons; при множественных reasons фокус может размываться; после repair при повторном fail сразу fallback — без второго раунда repair.
---
## 8. Minimal intervention points
1. **`src/app/modules/agent/runtime/steps/generation/prompt_payload_builder.py`**
Класс `RuntimePromptPayloadBuilder`, метод `build()` и `_scenario_payload()`.
Контролирует: какие поля и списки (must_mention_*, fact_gaps, layer_guide) попадают в JSON для LLM.
Удобно: один вход в «что видит модель»; можно усилить структуру под EXPLAIN/ARCHITECTURE/TRACE_FLOW без трогания оркестрации.
2. **`src/app/modules/agent/runtime/steps/context/answer_fact_curator.py`**
Функции `_explain_facts()`, `_architecture_facts()`, `_trace_flow_facts()`.
Контролируют: состав и качество curated_facts (required_*, fact_gaps).
Удобно: улучшение извлечения методов/relations/steps напрямую улучшает и payload, и валидацию.
3. **`src/app/modules/agent/runtime/steps/gates/post/post_gate.py`**
Класс `RuntimePostEvidenceGate`, методы `_validate_explain()`, `_validate_architecture()`, `_validate_trace_flow()` и хелперы (`_mentions_fact_group`, `_mentions_relations`, `_mentions_steps`).
Контролирует: критерии прохождения и набор reasons для repair.
Удобно: уже разбито по сценариям; можно ужесточить правила и добавить новые reasons без смены архитектуры.
4. **`src/app/modules/agent/llm/prompts.yml`**
Блоки `code_qa_explain_answer`, `code_qa_architecture_answer`, `code_qa_trace_flow_answer`, `code_qa_repair_answer`.
Контролируют: инструкции для черновика и починки.
Удобно: точечные правки формулировок и явные отсылки к полям payload (must_mention_*, fact_gaps).
5. **`src/app/modules/agent/runtime/steps/generation/prompt_selector.py`**
Класс `RuntimePromptSelector`, словарь `_PROMPTS` и метод `select()`.
Контролирует: какой системный промпт выбирается по sub_intent/answer_mode.
Удобно: введение отдельных промптов для подвидов (например, TRACE_FLOW по типу запроса) без изменения executor.
6. **`src/app/modules/agent/runtime/steps/context/answer_synthesis.py`**
Функция `build_answer_synthesis_input()`, формирование `fast_context` и `deep_context` (в т.ч. фильтр по C4 для EXPLAIN/ARCHITECTURE).
Контролирует: объём и приоритет контекста, передаваемого в synthesis_input.
Удобно: можно менять лимиты, порядок чанков или фильтры слоёв локально.
7. **`src/app/modules/agent/runtime/steps/finalization/repair.py`**
Класс `RuntimeAnswerRepairService`, метод `repair()` и `_repair_focus()`.
Контролирует: как validation.reasons мапятся в repair_focus и что уходит в промпт починки.
Удобно: можно сузить фокус repair под конкретные reasons или добавить приоритизацию без изменения цикла в executor.
---
*Документ описывает только текущую реализацию по коду после рефакторинга.*
+33
View File
@@ -0,0 +1,33 @@
# Agent Rules v1
## 1. Evidence-first
Агент должен формировать документацию только на основе подтвержденных источников: кода, существующей документации, системной аналитики и других явно доступных артефактов. Нельзя додумывать поведение системы, зависимости или бизнес-логику, если они не подтверждаются исходными материалами.
## 2. One Stable Object = One Document
Каждый документ должен описывать только одну устойчивую техническую сущность или один устойчивый аспект системы. Если по сущности могут ссылаться другие документы или она может переиспользоваться, ее нужно выносить в отдельный документ.
## 3. No Semantic Duplication
Документы не должны пересекаться по смыслу и повторять одно и то же содержание. Если одна и та же логика, правило или описание нужны в нескольких местах, агент должен вынести их в отдельный документ и использовать ссылки вместо дублирования текста.
## 4. Explicit Document Type
Для каждого документа агент должен явно определять его тип. На базовом уровне нужно использовать типы `ui_page`, `api_method`, `logic_block` и применять для каждого типа свой шаблон содержания и набор метаданных.
## 5. Hierarchical File Structure
Агент должен строить документацию как иерархию каталогов и файлов, а не как плоский набор страниц. Документы нужно раскладывать по смысловым разделам, например `docs/ui`, `docs/api`, `docs/logic`, `docs/architecture`, чтобы структура отражала архитектуру и упрощала навигацию.
## 6. Required YAML Frontmatter
Каждый документ должен начинаться с единообразного `YAML frontmatter`. В нем обязательно должны быть базовые метаданные документа: стабильный `id`, `title`, `doc_type`, `status`, `source_of_truth`, а также ссылки на связанные документы и кодовые артефакты.
## 7. Correct Internal Decomposition
Содержимое документа должно следовать шаблону своего типа и быть правильно декомпозировано внутри самого документа. Сценарии работы нужно описывать отдельно от детальных правил, контрактов, ограничений и дополнительных требований, чтобы документ оставался читаемым, атомарным и пригодным для индексирования.
## 8. Explicit Links Between Code and Docs
Каждый документ должен быть явно связан как минимум с соответствующим кодом и с соседними документами, если такие связи существуют. Агент должен фиксировать эти связи в метаданных и в тексте документа, чтобы документация образовывала связанную систему знаний, а не набор изолированных файлов.
+323
View File
@@ -0,0 +1,323 @@
# Концепция документации (Strong MVP, без связи с кодом)
## 1. Область применения
Документ описывает систему работы с документацией как самостоятельный слой.
Включает:
- текущее состояние (as-is)
- целевые сценарии использования
- целевую модель документации
- расширенный frontmatter
- базовую структуру документа `frontmatter + Summary + Details`
- RAG-слои (без связи с кодом)
---
# 2. Текущее состояние (As-Is)
## 2.1 Источник истины
- Документация хранится в Confluence
- Основная модель — иерархия страниц
- Навигация через дерево страниц
## 2.2 Структура и связи
- Документы не атомарны
- Одна страница содержит несколько тем
- Используются перекрестные ссылки между страницами
## 2.3 Ограничения
- Нет типизации документов
- Нет структурированного metadata-слоя
- Связи не формализованы
---
# 3. Целевые сценарии использования
## 3.1 Поиск документации
Пользователь формулирует запрос, например: "как работает создание заказа".
Система должна:
- найти релевантные документы
- отобрать наиболее точные фрагменты
- учитывать тип документа, модуль и сущности
Результат:
- короткий список релевантных документов
- возможность перейти в детали
## 3.2 Объяснение (Explain)
Пользователь хочет понять:
- как работает компонент
- что делает API
- как устроен процесс
Система должна:
- взять summary документа
- дополнить его деталями из Details
- дать краткое и точное объяснение без лишнего текста
## 3.3 Поиск по сущности (Entity Lookup)
Пользователь ищет:
- где используется сущность
- какие документы с ней связаны
Система должна:
- найти все документы, где упоминается сущность
- показать связи между ними
## 3.4 Навигация по документации
Пользователь начинает с одного документа и хочет:
- понять контекст
- перейти к связанным частям
Система должна:
- использовать parent/children
- использовать links
- строить осмысленный путь по документации
## 3.5 Генерация документации
Система создает новый документ:
- по шаблону
- с корректным frontmatter
- с заполненными разделами `Summary` и `Details`
Результат:
- документ сразу пригоден для использования
- соответствует структуре системы
## 3.6 Актуализация документации
При изменениях документа:
- система должна обновить только нужные части
- сохранить структуру
- обновить frontmatter и Details
Результат:
- документ остается консистентным
- не происходит деградации структуры
## 3.7 Связывание документации
При создании или обновлении:
- система добавляет связи между документами
- формирует граф знаний
Результат:
- документы не изолированы
- появляется навигация и reasoning
---
# 4. Frontmatter (расширенный контракт)
```yaml
id: api.create_order
type: api_method
name: create_order
title: Create order API
module: orders
layer: application
status: draft
updated_at: 2026-03-20
tags:
- orders
- api
entities:
- Order
- Cart
parent: orders_api
children: []
links:
- type: related_api
target: api.get_order
```
## Назначение ключевых полей
- `id` — стабильный идентификатор документа
- `type` — тип документа
- `title` — человекочитаемое имя
- `module` — модуль или bounded context
- `layer` — слой системы
- `status` — состояние документа
- `updated_at` — дата последнего обновления
- `tags` — фильтрация и поиск
- `entities` — база для entity lookup
- `parent` / `children` — иерархия
- `links` — граф связей
Все сведения о связях, сущностях и навигации должны храниться во frontmatter.
В теле документа отдельные разделы для связей, сущностей и навигации не создаются.
---
# 5. Структура документа (целевая)
Каждый документ состоит из двух смысловых слоев:
- frontmatter
- контент
Контент документа всегда состоит из двух обязательных разделов:
- `# Summary`
- `# Details`
## 5.1 Общие правила структуры
- `Summary` и `Details` всегда имеют заголовок уровня `#`
- других заголовков первого уровня в документе быть не должно
- `Summary` остается кратким и пригодным для explain
- `Details` заменяет прежние разделы `Context` и `Основное описание`
- внутренняя структура `Details` зависит от типа документа
## 5.2 Summary
Краткое описание на 3-6 строк.
Используется для explain, краткого ответа и быстрого понимания сути документа.
## 5.3 Details
Раздел содержит полное содержательное описание документа.
Состав подразделов определяется типом документа.
---
# 6. Типовая структура Details для API
Для документов типа `api_method` раздел `# Details` должен содержать следующие подразделы:
- `## Описание`
- `## Сценарий`
- `## Функциональные требования`
- `## Нефункциональные требования`
- `## Контракт`
## 6.1 Описание
Короткое описание сути метода:
- какую работу он выполняет
- для чего предназначен
## 6.2 Сценарий
Сценарий описывается в формате технического use case и включает:
- название
- предусловия
- триггер
- основной сценарий
- альтернативный сценарий
- обработку ошибок
- постусловие
Правила для сценария:
- сценарий должен быть лаконичным
- сценарий должен быть читаемым человеком
- в сценарии фиксируется суть шага, а не полная техническая реализация
- если условие можно выразить одним предложением, его допустимо оставить в сценарии
- если шаг требует детального описания формирования запроса, обработки ответа или внутренней логики, детали выносятся в функциональные требования
## 6.3 Функциональные требования
Функциональные требования описываются как последовательность требований внутри одного документа.
Формат:
- `FR-1`
- `FR-2`
- `FR-3`
Правила:
- идентификаторы локальны для документа
- на них нельзя ссылаться извне как на сквозные идентификаторы
- каждое требование описывает отдельный обязательный аспект реализации
## 6.4 Нефункциональные требования
Нефункциональные требования описываются аналогично функциональным.
Формат:
- `NFR-1`
- `NFR-2`
- `NFR-3`
Детальный формат записи может быть уточнен правилами конкретного проекта.
## 6.5 Контракт
Контракт должен быть пригоден для последующей сборки OpenAPI-спецификации.
Контракт описывает:
- входные параметры метода API
- выходные параметры
- структуру сообщения, как правило JSON
- обязательность полей
- типы полей
- ограничения на размер и формат
- назначение полей
- правила заполнения полей
- примеры данных
---
# 7. Базовая структура Details для остальных типов документов
Для всех типов документов, кроме `api_method`, на текущем этапе обязателен минимум:
- `# Summary`
- `# Details`
Дополнительные рекомендации по внутренней структуре `Details` для `logic_block`, `architecture_overview`, `domain_entity` и других типов будут заданы отдельно.
---
# 8. RAG слои (только документация)
## D0 — Чанки документов
Полный текст + разбиение.
## D1 — Каталог документов
- `id`
- `type`
- `title`
- `module`
- `tags`
## D2 — Индекс фактов
Факты, извлеченные из документов.
## D3 — Каталог сущностей
- сущности
- документы, где они используются
## D4 — Индекс сценариев (Workflow)
- последовательности действий
- пользовательские сценарии
## D5 — Граф связей
- связи между документами
---
# 9. Итог
Система документации:
- атомарные документы
- строгий frontmatter
- единая структура `Summary + Details`
- типовые правила для API-документов
- разделенные RAG-слои
Это позволяет:
- точно искать
- объяснять
- строить навигацию
- генерировать и обновлять документацию
@@ -0,0 +1,546 @@
# Текущая архитектура тестового пайплайна `pipeline_setup_v3`
Документ предназначен как краткое, но точное описание текущего устройства `pipeline_setup_v3` для внешней модели вроде ChatGPT.
Важно: текущий `pipeline_setup_v3` уже использует реальные runtime-компоненты агента, но по сути остается в первую очередь `code-first` пайплайном. Это особенно заметно в `evidence gate` и в наборе prompt'ов для LLM.
## 1. Общая схема пайплайна
`pipeline_setup_v3` запускает один из трех режимов:
- `router_only`
- `router_rag`
- `full_chain`
Во всех режимах используется `AgentRuntimeAdapter`, который является тестовым адаптером поверх реальных компонентов рантайма.
Общий поток для `full_chain`:
1. Пользовательский запрос
2. `IntentRouterV2`
3. Построение `RetrievalRequest`
4. `RuntimeRetrievalAdapter`
5. Построение нормализованного `RetrievalResult`
6. Сборка `EvidenceBundle`
7. `pre-evidence gate`
8. `RuntimeAnswerPolicy`
9. Вызов LLM через `AgentLlmService`
10. `post-evidence gate`
11. При необходимости `repair`
12. Сборка итогового результата, диагностики и артефактов теста
Ключевая идея: `pipeline_setup_v3` не эмулирует локальный тестовый сценарий вручную, а прогоняет реальные компоненты: роутер, retrieval, runtime executor и LLM.
## 2. Из каких компонентов состоит `pipeline_setup_v3`
### 2.1. Harness уровня тестов
Основные части:
- `tests/pipeline_setup_v3/run.py` — CLI-вход для запуска набора кейсов
- `tests/pipeline_setup_v3/core/runner.py` — оркестратор прогона кейсов
- `tests/pipeline_setup_v3/core/case_loader.py` — загрузка YAML-кейсов
- `tests/pipeline_setup_v3/core/validators.py` — проверка ожиданий
- `tests/pipeline_setup_v3/core/artifacts.py` — запись JSON/Markdown-результатов
- `tests/pipeline_setup_v3/runtime/agent_runtime_adapter.py` — мост к runtime-компонентам приложения
### 2.2. Runtime-компоненты, которые реально вызываются
- `IntentRouterV2`
- `RuntimeRepoContextFactory`
- `RuntimeRetrievalAdapter`
- `AgentRuntimeExecutor`
- `AgentLlmService`
- `PromptLoader`
### 2.3. Два варианта исполнения
#### `router_only`
Проверяет только результат роутера:
- `intent`
- `sub_intent`
- `graph_id`
- `conversation_mode`
RAG и LLM не вызываются.
#### `router_rag`
Проверяет:
- роутер
- retrieval plan
- реальный retrieval
- нормализованный `RetrievalResult`
LLM не вызывается.
#### `full_chain`
Проверяет полный runtime-контур:
- роутер
- retrieval
- evidence bundle
- pre-gate
- answer policy
- LLM
- post-gate
- repair
- итоговый answer/diagnostics
## 3. Компонент: Intent Router
### 3.1. Что это такое
`IntentRouterV2` классифицирует запрос и возвращает структурированный план для retrieval и дальнейшего рантайма.
Он не просто выбирает `intent`, а сразу строит:
- `conversation_mode`
- `query_plan`
- `retrieval_spec`
- `retrieval_constraints`
- `symbol_resolution`
- `evidence_policy`
- `graph_id`
### 3.2. Какие интенты есть сейчас
Сейчас в коде поддерживаются:
- `CODE_QA`
- `DOCUMENTATION_EXPLAIN`
- `GENERATE_DOCS_FROM_CODE`
- `FALLBACK`
### 3.3. Какие саб-интенты есть сейчас
#### Для `CODE_QA`
- `OPEN_FILE`
- `EXPLAIN`
- `EXPLAIN_LOCAL`
- `FIND_TESTS`
- `FIND_ENTRYPOINTS`
- `TRACE_FLOW`
- `ARCHITECTURE`
- `NEXT_STEPS` может появляться как follow-up режим на уровне query planning
#### Для `DOCUMENTATION_EXPLAIN`
- `SYSTEM_FLOW_EXPLAIN`
- `COMPONENT_EXPLAIN`
- `API_METHOD_EXPLAIN`
- `ENTITY_EXPLAIN`
#### Для `FALLBACK`
- `GENERIC_FALLBACK`
### 3.4. Что роутер возвращает на выходе
Результат роутера — это `IntentRouterResult`.
Ключевые поля:
- `intent`
- `retrieval_profile`
- `graph_id`
- `conversation_mode`
- `query_plan`
- `retrieval_spec`
- `retrieval_constraints`
- `symbol_resolution`
- `evidence_policy`
### 3.5. Что входит в `query_plan`
`query_plan` содержит:
- `raw`
- `normalized`
- `sub_intent`
- `negations`
- `expansions`
- `keyword_hints`
- `path_hints`
- `doc_scope_hints`
- `symbol_candidates`
- `symbol_kind_hint`
- `anchors`
Это основной bridge между классификацией запроса и retrieval.
### 3.6. Что входит в `retrieval_spec`
`retrieval_spec` содержит:
- `domains`
- `layer_queries`
- `filters`
- `rerank_profile`
Именно этот объект задает, какие слои RAG должны быть запрошены.
### 3.7. Что важно про текущее состояние
Текущий роутер уже умеет выделять docs-интенты и docs-сабинтенты, но downstream runtime ниже по пайплайну все еще во многом оптимизирован под `CODE_QA`.
Это означает:
- docs routing уже есть
- docs layer selection уже есть
- но `pre/post evidence gate` и prompt selection пока ориентированы в первую очередь на code sub-intents
## 4. Структура RAG
### 4.1. Общая идея
RAG устроен как многослойный индекс. Retrieval работает не по одному единственному типу чанков, а по наборам специализированных слоев.
### 4.2. Code-слои
- `C0_SOURCE_CHUNKS` — сырой код / чанки исходников
- `C1_SYMBOL_CATALOG` — каталог символов
- `C2_DEPENDENCY_GRAPH` — зависимости и связи
- `C3_ENTRYPOINTS` — точки входа, маршруты, handler'ы
- `C4_SEMANTIC_ROLES` — семантические роли и behavioral hints
### 4.3. Docs-слои
- `D0_DOC_CHUNKS` — чанки документов
- `D1_DOCUMENT_CATALOG` — каталог документов
- `D2_FACT_INDEX` — атомарные факты
- `D3_ENTITY_CATALOG` — сущности
- `D4_WORKFLOW_INDEX` — сценарии и workflow
- `D5_RELATION_GRAPH` — связи между документами
### 4.4. Как retrieval связывается с роутером
Роутер возвращает:
- `domains`
- `layer_queries`
- `filters`
- `retrieval_constraints`
Дальше `build_retrieval_request(...)` превращает это в `RetrievalRequest`, который содержит:
- `rag_session_id`
- `query`
- `sub_intent`
- `path_scope`
- `keyword_hints`
- `symbol_candidates`
- `requested_layers`
- `retrieval_spec`
- `retrieval_constraints`
- `query_plan`
### 4.5. Что возвращает retrieval
Сырые строки RAG затем нормализуются в `RetrievalResult`, который содержит:
- `target_symbol_candidates`
- `resolved_symbol`
- `symbol_resolution_status`
- `file_candidates`
- `code_chunks`
- `relations`
- `semantic_hints`
- `entrypoints`
- `test_candidates`
- `layer_outcomes`
- `missing_layers`
- `raw_rows`
- `retrieval_report`
### 4.6. Как из retrieval строится evidence
`build_evidence_bundle(...)` собирает `EvidenceBundle`.
Ключевые поля:
- `resolved_intent`
- `resolved_sub_intent`
- `resolved_target`
- `target_type`
- `target_symbol_candidates`
- `file_candidates`
- `code_chunks`
- `relations`
- `entrypoints`
- `test_evidence`
- `evidence_count`
- `sufficient`
- `failure_reasons`
- `retrieval_summary`
Важно: `evidence_count` сейчас считается по количеству `code_chunks`. Это еще одно подтверждение, что runtime сегодня code-first.
## 5. Evidence Gate
В пайплайне есть два gate'а.
## 5.1. Pre-evidence gate
Расположение:
- `src/app/modules/agent/runtime/steps/gates/pre/evidence_gate.py`
Когда срабатывает:
- после retrieval
- после сборки `EvidenceBundle`
- до вызова LLM
Задача:
- понять, достаточно ли evidence для уверенного ответа
- выдать `passed/failure_reasons/degraded_message`
Как работает сейчас:
- для `OPEN_FILE` требует найденный path/file и хотя бы один `C0` chunk
- для `EXPLAIN` требует target symbol и минимум 2 evidence chunk'а
- для `FIND_TESTS` требует target и хотя бы один test candidate
- для `FIND_ENTRYPOINTS` требует хотя бы один entrypoint
- для остальных сценариев требует минимум 1 evidence
Что возвращает:
- `passed`
- `failure_reasons`
- `degraded_message`
Ограничение:
- логика pre-gate пока написана в терминах code sub-intents
- docs-сценарии там явно не моделированы
## 5.2. Post-evidence gate
Расположение:
- `src/app/modules/agent/runtime/steps/gates/post/post_gate.py`
Когда срабатывает:
- после генерации draft answer
- до возврата финального ответа
Задача:
- проверить groundedness черновика
- убедиться, что ответ действительно опирается на evidence
- решить, нужен ли `repair`
Что проверяет:
- что ответ не пустой
- что degraded/not_found/ambiguous-ответы содержат обязательные guardrail-фразы
- что normal answer не слишком общий
- что упоминаются обязательные факты из curated evidence
- что нет явной semantic leakage или contradictions
Отдельные проверки есть для:
- `FIND_ENTRYPOINTS`
- `EXPLAIN`
- `ARCHITECTURE`
- `TRACE_FLOW`
Если gate не проходит, он возвращает:
- `passed=False`
- `action="repair"`
- список `reasons`
После этого runtime может сделать дополнительный шаг `repair` через LLM.
Ограничение:
- post-gate тоже пока ориентирован на code-oriented sub-intents
- docs-сабинтенты для него еще не описаны отдельными правилами
## 6. Обращение к LLM
### 6.1. Где вызывается LLM
В `pipeline_setup_v3` есть два места использования LLM:
1. В классификации интента внутри `IntentClassifierV2`
2. В финальной генерации ответа внутри `AgentRuntimeExecutor`
### 6.2. Prompt для классификации интента
Используется prompt:
- `rag_intent_router_v2`
Назначение:
- если deterministic rules не дали результата, LLM выбирает интент
Текущий prompt исторически описывает старые имена интентов, поэтому его еще нужно синхронизировать с новым docs/fallback контрактом.
### 6.3. Prompt'ы для генерации ответа
Prompt selector сейчас выбирает:
- `code_qa_architecture_answer`
- `code_qa_explain_answer`
- `code_qa_explain_local_answer`
- `code_qa_find_entrypoints_answer`
- `code_qa_find_tests_answer`
- `code_qa_general_answer`
- `code_qa_open_file_answer`
- `code_qa_trace_flow_answer`
- `code_qa_degraded_answer`
Дополнительно для repair используется:
- `code_qa_repair_answer`
### 6.4. Как строится payload для LLM
Перед вызовом LLM runtime собирает:
- `AnswerSynthesisInput`
- `EvidenceBundle`
- `prompt_payload`
В payload передаются:
- `user_question`
- `resolved_target`
- `answer_mode`
- `evidence_summary`
- `retrieval_summary`
- curated facts
- обязательные для упоминания сущности, методы, связи, шаги и т.д.
То есть LLM не получает просто "вопрос и куски текста", а получает уже структурированный grounded payload.
### 6.5. Что важно про текущее состояние prompt'ов
Сейчас runtime prompt selection и prompt contracts явно заточены под code QA.
Это значит:
- для `CODE_QA` full chain оформлен хорошо
- для `DOCUMENTATION_EXPLAIN` routing и retrieval есть, но отдельного docs answer-prompt слоя пока нет
- для docs full-chain пока не хватает собственных prompt names, prompt payload contract и post-gate правил
## 7. Что именно сейчас проверяет `pipeline_setup_v3`
YAML-кейс может проверять четыре группы ожиданий:
- `router`
- `retrieval`
- `llm`
- `pipeline`
Примеры ожиданий:
- ожидаемый `intent`
- ожидаемый `sub_intent`
- нужные слои в `layers_include`
- что retrieval не пустой
- что в answer есть нужные фразы
- какой `answer_mode` получился
## 8. Ключевые выводы о текущей архитектуре
### 8.1. Что уже сделано хорошо
- `pipeline_setup_v3` работает поверх реальных runtime-компонентов
- есть явный контракт между router → retrieval → evidence → answer
- есть два evidence gate
- есть structured diagnostics
- есть нормализованные типы `RetrievalRequest`, `RetrievalResult`, `EvidenceBundle`
### 8.2. Что остается code-first
- pre-evidence gate
- post-evidence gate
- prompt selector
- набор answer prompts
- часть логики нормализации evidence
### 8.3. Что это значит для docs use case
Сейчас docs use case уже частично внедрен:
- есть docs intent
- есть docs sub-intents
- есть docs layer mapping
- есть docs retrieval profile
Но для полноценного `full_chain` по документации еще не хватает:
- docs-oriented pre-gate правил
- docs-oriented post-gate правил
- docs-specific answer prompts
- docs-specific synthesis contract
- отдельных full-chain test cases для `DOCUMENTATION_EXPLAIN` и `FALLBACK`
## 9. Краткое резюме по компонентам
### Intent Router
Назначение:
- классифицировать запрос
- построить retrieval plan
- задать evidence policy
Выход:
- `IntentRouterResult`
### RAG
Назначение:
- вернуть evidence из многослойного индекса
Выход:
- `RetrievalResult`
- затем `EvidenceBundle`
### Pre-evidence gate
Назначение:
- решить, можно ли вообще уверенно отвечать
Выход:
- `passed/failure_reasons/degraded_message`
### Post-evidence gate
Назначение:
- проверить, grounded ли уже сгенерированный ответ
Выход:
- `return` или `repair`
### LLM
Назначение:
- классификация сложных интентов
- генерация финального ответа
- repair ответа при провале post-gate
Текущий фокус:
- в первую очередь `CODE_QA`
+105
View File
@@ -0,0 +1,105 @@
`pipeline_setup_v3` это YAML-driven test harness для проверки agent pipeline на уровне сценариев, а не unit-тестов.
Как он работает:
- Берёт один YAML-файл или директорию с YAML-кейсами.
- Каждый кейс описывает:
- `id`
- `query`
- `runner`
- `mode`
- `input`
- `expected`
- Если в `input` нет готового `rag_session_id`, harness сам получает его:
- либо берёт из `input.rag_session_id`
- либо индексирует `input.repo_path` в RAG и кеширует полученную сессию для одинакового `(repo_path, project_id)`
Какие режимы кейсов есть:
- `router_only`
Проверяется только роутинг, без retrieval и без LLM.
- `router_rag`
Проверяется роутинг плюс retrieval, но без полной генерации ответа.
- `full_chain`
Проверяется полный pipeline: router → retrieval → downstream pipeline/LLM → final answer.
Как устроен execution flow:
1. Loader читает YAML и превращает каждый кейс в `V3Case`.
2. Runner для каждого кейса резолвит `rag_session_id`.
3. `AgentRuntimeAdapter` исполняет кейс в зависимости от `mode`.
4. Возвращаются два объекта:
- `actual`
- `details`
5. Validator сравнивает `actual/details` с `expected`.
6. Writer сохраняет:
- JSON с машинными результатами
- Markdown с человекочитаемой диагностикой
- итоговый `summary.md` по всему прогону
Что обычно лежит в `actual`:
- `intent`
- `sub_intent`
- `graph_id`
- `conversation_mode`
- `rag_count`
- `answer_mode`
- `llm_answer`
- `path_scope`
- `doc_scope`
- `entity_candidates`
- `symbol_candidates`
- `layers`
- `filters`
Что лежит в `details`:
- `router_result`
- `retrieval_request`
- `retrieval_result`
- `rag_rows`
- `diagnostics`
- `llm_request`
- `pipeline_steps`
- иногда `validation`, `token_usage`, `runtime_trace`
Что умеют expectations:
- `expected.router`
Проверяет `intent`, `sub_intent`, `graph_id`, `conversation_mode`
- `expected.retrieval`
Проверяет:
- пустой/непустой retrieval
- минимум строк
- наличие нужных слоёв
- path/doc scope
- symbol/entity candidates
- фильтры
- `expected.llm`
Проверяет:
- есть ли ответ
- содержит ли ответ обязательные фразы
- не содержит ли запрещённые фразы
- `answer_mode`
- `expected.pipeline`
Проверяет в основном итоговый `answer_mode`
Что важно при формулировке нового test case для ChatGPT:
- кейс должен описывать не “как реализовать код”, а “какой пользовательский сценарий проверяем”
- у кейса должны быть:
- понятный `query`
- корректный `mode`
- вход: `rag_session_id` или `repo_path`
- минимально достаточные `expected`
- не надо переописывать весь output, лучше проверять только ключевые инварианты
Хороший шаблон задания для ChatGPT:
1. Укажи, для какого suite нужен кейс.
2. Укажи `mode`: `router_only`, `router_rag` или `full_chain`.
3. Дай пользовательский `query`.
4. Опиши, что именно должно проверяться:
- роутинг
- retrieval layers/scope
- answer mode
- ключевые фразы в ответе
5. Попроси вернуть YAML-фрагмент в формате `pipeline_setup_v3`.
Пример формулировки для ChatGPT:
“Сформируй YAML test case для `pipeline_setup_v3` в режиме `full_chain`. Нужно проверить, что запрос `Объясни по документации как работает /health` маршрутизируется в docs-intent, retrieval использует docs layers, retrieval непустой, а ответ содержит `/health` и не содержит фраз про отсутствие данных.”
Если хочешь, я могу сразу подготовить тебе готовый prompt для ChatGPT, который будет генерировать новые кейсы в нужном формате.
+1
View File
@@ -11,6 +11,7 @@ requires-python = ">=3.11"
dependencies = [ dependencies = [
"fastapi>=0.116", "fastapi>=0.116",
"uvicorn>=0.35", "uvicorn>=0.35",
"python-dotenv>=1.0",
"pydantic>=2.11", "pydantic>=2.11",
"langgraph>=0.6", "langgraph>=0.6",
"langgraph-checkpoint-postgres>=2.0", "langgraph-checkpoint-postgres>=2.0",
+1
View File
@@ -4,3 +4,4 @@ markers =
router_rag: intent-router -> rag integration pipeline tests router_rag: intent-router -> rag integration pipeline tests
full_chain: intent-router -> rag -> llm integration pipeline tests full_chain: intent-router -> rag -> llm integration pipeline tests
code_qa_eval: CODE_QA golden evaluation harness (fixture + real-adapter; needs DB for full run) code_qa_eval: CODE_QA golden evaluation harness (fixture + real-adapter; needs DB for full run)
docs_qa_eval: DOCS_QA golden evaluation harness
+1
View File
@@ -1,5 +1,6 @@
fastapi==0.116.1 fastapi==0.116.1
uvicorn==0.35.0 uvicorn==0.35.0
python-dotenv==1.0.1
pydantic==2.11.7 pydantic==2.11.7
langgraph==0.6.7 langgraph==0.6.7
langgraph-checkpoint-postgres==2.0.23 langgraph-checkpoint-postgres==2.0.23
+9
View File
@@ -0,0 +1,9 @@
__all__ = ["AgentRuntime"]
def __getattr__(name: str):
if name == "AgentRuntime":
from app.core.agent.runtime import AgentRuntime
return AgentRuntime
raise AttributeError(name)
+22
View File
@@ -0,0 +1,22 @@
__all__ = [
"AgentProcess",
"ProcessResult",
"V1Process",
"V2Process",
]
def __getattr__(name: str):
if name in {"AgentProcess", "ProcessResult"}:
from app.core.agent.processes.base import AgentProcess, ProcessResult
return {"AgentProcess": AgentProcess, "ProcessResult": ProcessResult}[name]
if name == "V1Process":
from app.core.agent.processes.v1.process import V1Process
return V1Process
if name == "V2Process":
from app.core.agent.processes.v2.v2_process import V2Process
return V2Process
raise AttributeError(name)
+26
View File
@@ -0,0 +1,26 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from dataclasses import field
from typing import TYPE_CHECKING
from app.schemas.changeset import ChangeItem
if TYPE_CHECKING:
from app.core.agent.runtime.execution_context import RuntimeExecutionContext
@dataclass(slots=True)
class ProcessResult:
answer: str = ""
changeset: list[ChangeItem] = field(default_factory=list)
apply_changeset: bool = False
class AgentProcess(ABC):
version = ""
@abstractmethod
async def run(self, context: "RuntimeExecutionContext") -> ProcessResult:
raise NotImplementedError
@@ -0,0 +1,3 @@
from app.core.agent.processes.v1.process import V1Process
__all__ = ["V1Process"]
@@ -0,0 +1,22 @@
from __future__ import annotations
from app.core.agent.processes.base import AgentProcess, ProcessResult
from app.core.agent.processes.v1.workflow import V1FlowMainGraph
from app.core.agent.processes.v1.workflow.flow_main import V1FlowContext
from app.core.agent.utils.llm import AgentLlmService
class V1Process(AgentProcess):
version = "v1"
def __init__(self, llm: AgentLlmService, prompt_name: str = "v1_flow_main.answer") -> None:
self._prompt_name = prompt_name
self._workflow = V1FlowMainGraph(llm)
async def run(self, context) -> ProcessResult:
flow_context = V1FlowContext(
runtime=context,
prompt_name=self._prompt_name,
)
flow_context = await self._workflow.run(flow_context)
return ProcessResult(answer=flow_context.answer)
@@ -0,0 +1,3 @@
from app.core.agent.processes.v1.workflow.flow_main.graph import V1FlowMainGraph
__all__ = ["V1FlowMainGraph"]
@@ -0,0 +1,7 @@
from app.core.agent.processes.v1.workflow.flow_main.context import V1FlowContext
from app.core.agent.processes.v1.workflow.flow_main.graph import V1FlowMainGraph
__all__ = [
"V1FlowContext",
"V1FlowMainGraph",
]
@@ -0,0 +1,13 @@
from __future__ import annotations
from dataclasses import dataclass
from app.core.agent.runtime.execution_context import RuntimeExecutionContext
@dataclass(slots=True)
class V1FlowContext:
runtime: RuntimeExecutionContext
prompt_name: str
prepared_message: str = ""
answer: str = ""
@@ -0,0 +1,24 @@
from __future__ import annotations
from app.core.agent.processes.v1.workflow.flow_main.context import V1FlowContext
from app.core.agent.processes.v1.workflow.flow_main.steps.finalize_answer_step import FinalizeAnswerStep
from app.core.agent.processes.v1.workflow.flow_main.steps.generate_answer_step import GenerateAnswerStep
from app.core.agent.processes.v1.workflow.flow_main.steps.prepare_user_message_step import PrepareUserMessageStep
from app.core.agent.utils.llm import AgentLlmService
from app.core.agent.utils.workflow import WorkflowGraph
class V1FlowMainGraph:
def __init__(self, llm: AgentLlmService) -> None:
self._graph = WorkflowGraph(
workflow_id="v1.flow_main",
source="workflow.v1",
steps=(
PrepareUserMessageStep(),
GenerateAnswerStep(llm),
FinalizeAnswerStep(),
),
)
async def run(self, context: V1FlowContext) -> V1FlowContext:
return await self._graph.run(context)
@@ -0,0 +1,8 @@
namespace: v1_flow_main
prompts:
answer: |
Ты полезный ассистент.
Ответь на сообщение пользователя по существу.
Не придумывай факты, если данных недостаточно.
Если пользователь пишет по-русски, отвечай по-русски.
@@ -0,0 +1,9 @@
from app.core.agent.processes.v1.workflow.flow_main.steps.finalize_answer_step import FinalizeAnswerStep
from app.core.agent.processes.v1.workflow.flow_main.steps.generate_answer_step import GenerateAnswerStep
from app.core.agent.processes.v1.workflow.flow_main.steps.prepare_user_message_step import PrepareUserMessageStep
__all__ = [
"FinalizeAnswerStep",
"GenerateAnswerStep",
"PrepareUserMessageStep",
]
@@ -0,0 +1,19 @@
from __future__ import annotations
from app.core.agent.processes.v1.workflow.flow_main.context import V1FlowContext
from app.core.agent.utils.workflow import WorkflowStep
class FinalizeAnswerStep(WorkflowStep[V1FlowContext]):
step_id = "finalize_answer"
title = "Финализация ответа"
async def run(self, context: V1FlowContext) -> V1FlowContext:
context.answer = context.answer.strip()
return context
def trace_input(self, context: V1FlowContext) -> dict[str, object]:
return {"answer_length_before_strip": len(context.answer)}
def trace_output(self, context: V1FlowContext) -> dict[str, object]:
return {"answer_length": len(context.answer)}
@@ -0,0 +1,32 @@
from __future__ import annotations
import asyncio
from app.core.agent.processes.v1.workflow.flow_main.context import V1FlowContext
from app.core.agent.utils.llm import AgentLlmService
from app.core.agent.utils.workflow import WorkflowStep
class GenerateAnswerStep(WorkflowStep[V1FlowContext]):
step_id = "generate_answer"
title = "Вызов LLM"
def __init__(self, llm: AgentLlmService) -> None:
self._llm = llm
async def run(self, context: V1FlowContext) -> V1FlowContext:
request_id = context.runtime.request.request_id
context.answer = await asyncio.to_thread(
self._llm.generate,
context.prompt_name,
context.prepared_message,
log_context=f"agent:{request_id}",
trace=context.runtime.trace.module("workflow.v1.llm"),
)
return context
def trace_input(self, context: V1FlowContext) -> dict[str, object]:
return {"prompt_name": context.prompt_name, "prepared_message_length": len(context.prepared_message)}
def trace_output(self, context: V1FlowContext) -> dict[str, object]:
return {"answer_length": len(context.answer)}
@@ -0,0 +1,16 @@
from __future__ import annotations
from app.core.agent.processes.v1.workflow.flow_main.context import V1FlowContext
from app.core.agent.utils.workflow import WorkflowStep
class PrepareUserMessageStep(WorkflowStep[V1FlowContext]):
step_id = "prepare_user_message"
title = "Подготовка сообщения"
async def run(self, context: V1FlowContext) -> V1FlowContext:
context.prepared_message = context.runtime.request.message.strip()
return context
def trace_output(self, context: V1FlowContext) -> dict[str, object]:
return {"prepared_message_length": len(context.prepared_message)}
@@ -0,0 +1,13 @@
__all__ = ["V2IntentRouter", "V2Process"]
def __getattr__(name: str):
if name == "V2IntentRouter":
from app.core.agent.processes.v2.intent_router.router import V2IntentRouter
return V2IntentRouter
if name == "V2Process":
from app.core.agent.processes.v2.v2_process import V2Process
return V2Process
raise AttributeError(name)

Some files were not shown because too many files have changed in this diff Show More