[bug][ai-chat] Хрупкая передача «текущей страницы» агенту: openPage всегда null + контекст только в system-промпте #43

Closed
opened 2026-06-20 21:02:00 +03:00 by Ghost · 1 comment

Контекст

Агент не понимает «эта/текущая страница». Через CLIProxyAPI отвечает «я не вижу текущую страницу» и просит уточнить id/название. По словам пользователя, без прокси (прямой эндпоинт) работает. Механика передачи страницы хрупкая по двум независимым причинам.

Полный разбор: docs/backlog/ai-chat-current-page-fragile.md.

Как сейчас инжектится текущая страница

Только текстом в system-промпт: клиент вычисляет openPage из роута → кладёт в тело POST /api/ai-chat/stream → сервис прокидывает в openedPagebuildSystemPrompt дописывает строку The user is currently viewing the page "<title>" (pageId: <id>). Ни инструмента «get current page», ни поля в user-сообщении нет.

Хрупкость №1 (клиент) — НЕ исправлена

apps/client/src/features/ai-chat/components/ai-chat-window.tsx: openPage берётся через const { pageSlug } = useParams(). Но AiChatWindow смонтирован в pathless-родительском layout-роуте, где сегмент :pageSlug не заматчен → useParams() возвращает {}pageSlug === undefinedusePageQuery отключён → openPage всегда null. (Тот же антипаттерн рядом: Layout так же получает spaceSlug === undefinedSearchSpotlight без привязки к спейсу.)

Варианты фикса:

  • A. Заменить useParams() на useMatch("/s/:spaceSlug/p/:pageSlug") / matchPath по useLocation().pathname — матчится по полному URL независимо от позиции в дереве. Минимально и точечно.
  • B. Jotai-атом текущей страницы, выставляемый в <Page/> (он внутри дочернего роута, видит params); читать в окне чата. Заодно чинит Layout/SearchSpotlight.

Хрупкость №2 (прокси-устойчивость) — НЕ исправлена

pageId передаётся ТОЛЬКО строкой в роли system (apps/server/src/core/ai-chat/ai-chat.prompt.ts). CLIProxyAPI оборачивает CLI-бэкенды (Gemini CLI / Claude Code / Codex / Qwen) с их объёмными системными промптами — наш system может быть склеен с их преамбулой, перенесён в systemInstruction, обрезан или недооценён моделью → контекст теряется.

Вариант фикса:

  • C. Дублировать контекст НЕ только в system: скрытый префикс в user-сообщение ИЛИ инструмент get_current_page (берёт pageId из серверной сессии запроса) — идентичность страницы перестаёт зависеть от сохранности system-промпта прокси. C — единственное, что реально лечит исходный симптом.

ПРОТИВОРЕЧИЕ — разрешить ДО фикса

По исходнику openPage должен быть null в обоих режимах, а пользователь говорит, что напрямую работает. Сначала залогировать (дёшево) и сравнить direct vs proxy:

  • что реально уходит в system перед streamText (ai-chat.service.ts) — строка должна быть байт-в-байт одинаковой;
  • долетает ли body.openPage непустым до сервера в обоих режимах;
  • что CLIProxyAPI шлёт апстриму (логи прокси / mitmproxy) — присутствует ли pageId: ... в системной инструкции модели.

Отладочного логирования (предложенного в «открытых вопросах») сейчас тоже НЕ добавлено.

Рекомендация

Сначала разрешить противоречие логами, затем A или B (клиент) + C (устойчивость к прокси).

Файлы

  • apps/client/src/features/ai-chat/components/ai-chat-window.tsx
  • apps/server/src/core/ai-chat/ai-chat.prompt.ts
  • apps/server/src/core/ai-chat/ai-chat.service.ts
  • apps/server/src/core/ai-chat/ai-chat.controller.ts
  • apps/server/src/core/ai-chat/tools/ai-chat-tools.service.ts (новый инструмент get_current_page, если выбираем вариант C)

Источник: бэклог docs/backlog/ai-chat-current-page-fragile.md (статус 🔴 не сделано).

## Контекст Агент не понимает «эта/текущая страница». Через CLIProxyAPI отвечает «я не вижу текущую страницу» и просит уточнить id/название. По словам пользователя, без прокси (прямой эндпоинт) работает. Механика передачи страницы хрупкая по двум независимым причинам. Полный разбор: `docs/backlog/ai-chat-current-page-fragile.md`. ## Как сейчас инжектится текущая страница Только текстом в **system-промпт**: клиент вычисляет `openPage` из роута → кладёт в тело `POST /api/ai-chat/stream` → сервис прокидывает в `openedPage` → `buildSystemPrompt` дописывает строку `The user is currently viewing the page "<title>" (pageId: <id>)`. Ни инструмента «get current page», ни поля в user-сообщении нет. ## Хрупкость №1 (клиент) — НЕ исправлена `apps/client/src/features/ai-chat/components/ai-chat-window.tsx`: `openPage` берётся через `const { pageSlug } = useParams()`. Но `AiChatWindow` смонтирован в **pathless-родительском** layout-роуте, где сегмент `:pageSlug` не заматчен → `useParams()` возвращает `{}` → `pageSlug === undefined` → `usePageQuery` отключён → `openPage` **всегда `null`**. (Тот же антипаттерн рядом: `Layout` так же получает `spaceSlug === undefined` → `SearchSpotlight` без привязки к спейсу.) Варианты фикса: - **A.** Заменить `useParams()` на `useMatch("/s/:spaceSlug/p/:pageSlug")` / `matchPath` по `useLocation().pathname` — матчится по полному URL независимо от позиции в дереве. Минимально и точечно. - **B.** Jotai-атом текущей страницы, выставляемый в `<Page/>` (он внутри дочернего роута, видит params); читать в окне чата. Заодно чинит `Layout`/`SearchSpotlight`. ## Хрупкость №2 (прокси-устойчивость) — НЕ исправлена pageId передаётся **ТОЛЬКО** строкой в роли `system` (`apps/server/src/core/ai-chat/ai-chat.prompt.ts`). CLIProxyAPI оборачивает CLI-бэкенды (Gemini CLI / Claude Code / Codex / Qwen) с их объёмными системными промптами — наш `system` может быть склеен с их преамбулой, перенесён в `systemInstruction`, обрезан или недооценён моделью → контекст теряется. Вариант фикса: - **C.** Дублировать контекст НЕ только в system: скрытый префикс в user-сообщение **ИЛИ** инструмент `get_current_page` (берёт pageId из серверной сессии запроса) — идентичность страницы перестаёт зависеть от сохранности system-промпта прокси. C — единственное, что реально лечит исходный симптом. ## ПРОТИВОРЕЧИЕ — разрешить ДО фикса По исходнику `openPage` должен быть `null` в **обоих** режимах, а пользователь говорит, что напрямую работает. Сначала залогировать (дёшево) и сравнить direct vs proxy: - [ ] что реально уходит в `system` перед `streamText` (`ai-chat.service.ts`) — строка должна быть байт-в-байт одинаковой; - [ ] долетает ли `body.openPage` непустым до сервера в обоих режимах; - [ ] что CLIProxyAPI шлёт апстриму (логи прокси / mitmproxy) — присутствует ли `pageId: ...` в системной инструкции модели. Отладочного логирования (предложенного в «открытых вопросах») сейчас тоже НЕ добавлено. ## Рекомендация Сначала разрешить противоречие логами, затем A или B (клиент) + C (устойчивость к прокси). ## Файлы - `apps/client/src/features/ai-chat/components/ai-chat-window.tsx` - `apps/server/src/core/ai-chat/ai-chat.prompt.ts` - `apps/server/src/core/ai-chat/ai-chat.service.ts` - `apps/server/src/core/ai-chat/ai-chat.controller.ts` - `apps/server/src/core/ai-chat/tools/ai-chat-tools.service.ts` (новый инструмент `get_current_page`, если выбираем вариант C) --- Источник: бэклог `docs/backlog/ai-chat-current-page-fragile.md` (статус 🔴 не сделано).
Owner

инструмент get_current_page принимается

инструмент get_current_page принимается
Ghost closed this issue 2026-06-21 02:01:08 +03:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#43