Перейти к основному содержимому

ADR 0082 — Chat RAG: собственная вкладка гибридного поиска, без переименования "Chat"

Статус​

Принят.

Контекст​

ADR 0080 (PROGRAMA 28, Волна 4, направление G2) оставил готовыми конвейер индексации и три HTTP-маршрута Chat RAG — POST .../rag/search, POST .../rag/reindex, GET .../rag/coverage, — но явно объявил, что ЭКРАН — работа Волны 5. Тем временем ADR 0078 (RN-202) решил, что вкладка sessions останется с меткой "Chat", никогда "Chat RAG", как просит более свежий handoff, — потому что в тот момент "Chat RAG" описывал возможность, которой у продукта не было (без конвейера, без UI цитирования). Это направление (Волна 5, G3) — момент, когда эта возможность начинает действительно существовать, и вопрос, на который ему нужно ответить: теперь, когда RAG существует, куда идёт метка "Chat RAG"?

Два варианта: переименовать sessions в "Chat RAG" (что handoff буквально предлагает) или дать "Chat RAG" СОБСТВЕННУЮ вкладку. Два экрана отвечают на структурно разные вопросы — sessions — это разговор с АКТИВИРОВАННЫМ агентом, который тратит ключ owner на каждый ход (RN-058) и может писать в backlog, в код, в любой инструмент, который есть у агента; RAG — это поиск по уже построенному ИНДЕКСУ, без какого-либо агента посередине и только для чтения по природе (три маршрута ADR 0080 — viewer/viewer/maintainer, ни один не вызывает разговорный LLM). Слияние двух в одной вкладке вынудило бы UI выражать "это цитата индексированного чанка" и "это реплика агента" в одной ленте, когда пользователь никогда не просил обе вещи одновременно.

Решение​

"Chat RAG" становится собственной вкладкой, key: 'rag', не трогая метку sessions. RN-202 остаётся действительной как была — sessions никогда не был "Chat RAG" и по-прежнему не является, — но её причина меняется с "возможности не существует" на "возможность существует в ДРУГОМ месте". Вкладка входит в apps/web/src/routes/project-tabs.ts (ordem: 28, сразу после code и перед backlog) — то же соседство "посмотреть, что уже произведено/проиндексировано", что занимает вкладка Código, и перед Backlog, где рождается новое производство.

Экран потребляет три контракта ADR 0080, не угадывая форму (apps/web/src/lib/api-client.ts/api-types.ts получают searchRag/ getRagCoverage/reindexRag и типы, отражённые 1:1 из DTO — RagSearchHit, RagChunkOrigin как объединение, различаемое по kind, RagCoverage). У UI три части:

  1. RagCoveragePanel — РЕАЛЬНОЕ количество проиндексированных docs/adr/сессий против реального итога, и chunksTotal/chunksWithoutVector. Никакого "переиндексировано Xмин назад": ответ backend не несёт этих данных с ADR 0080, и не этот экран будет их выдумывать (RN-252).
  2. Поиск с фильтром области (pills docs/adr/session, отсутствие = все) и предупреждение, когда vectorAvailable: false — "busca só por palavra-chave — embedding indisponível", с причиной, когда backend её даёт (RN-252, та же честная деградация RN-233, доходящая до UI).
  3. RagCitationCard — цитата с комбинированным score и двумя раздельными сигналами (null, когда сигнал не нашёл чанк, никогда 0%, сохраняя различие ADR 0080). Источник file показывает путь/headingPath как текст; источник session переходит к точному СОБЫТИЮ через useNavigate + search: { highlightEvent } — ТОТ ЖЕ маршрут и тот же параметр, что уже используют чипы свидетельств Psicólogo (HypothesisCard.tsx, Фаза 4b), — переиспользование, а не второй путь навигации (RN-253).

Кнопка "Reindexar agora" появляется только для owner/maintainer, повторяя на клиенте мерило, которое маршрут уже применяет (RN-238), — тот же шаблон useCurrentWorkspaceWithRole, что ProjectSettingsTab/ ProjectApprovalsTab уже используют для других gates maintainer (RN-254). Тот, у кого нет роли, просто не видит кнопку, вместо того чтобы видеть её отключённой: переиндексация запускает N вызовов к репозиторию проекта и к provider embedding — то же мерило "меняет то, что тратит продукт, не спрашивая", что у предела параллелизма области (RN-083).

Последствия​

Обещание, которое RN-202 отложила, приходит, но не так, как нарисовал handoff. Handoff переименовывает существующую вкладку; продукт открывает новую. Это наиболее защитимое решение, учитывая, что у двух экранов никогда не было одного и того же вопроса, — а цена ошибки (лишняя вкладка в реестре) меньше, чем цена смешения разговора-с-агентом и поиска-по-индексу на одной поверхности.

Нет глубокой ссылки по пути на вкладку Código. Цитата с источником file показывает sourcePath/headingPath простым текстом, без перехода, — у вкладки Código (FASE 26/26b) сегодня нет механизма открытия конкретного файла через маршрут/search, а построение этого механизма вне области этого направления. Это объявлено, а не наполовину реализовано: текст показывает реальный путь, просто он не кликабелен.

Gate maintainer на клиенте — лишь UX. Гарантирует, что reindex не выполнится без роли, по-прежнему api (RequireRole, RN-238) — экран лишь избегает клика, который api отклонила бы в любом случае. Это следует тому же шаблону, что все остальные UI-gates продукта (auto mode, предел параллелизма, модель области): ни один из них не заменяет проверку сервера.