#region FeatureSpec [C:3] [TYPE ADR] [SEMANTICS spec,requirements,feature,agent-chat,context,guardrails,tools] @BRIEF Refinement of agent chat logic: enriched context passing from frontend, guardrails card UX improvements, and tool execution optimisation. @RELATION DEPENDS_ON -> [ADR-0008:ADR] @RELATION DEPENDS_ON -> [ADR-0006:ADR] @RELATION DEPENDS_ON -> [ADR-0005:ADR] ## Navigation (DSA Indexer keywords) @SEMANTICS: spec, requirements, feature, agent-chat, context, guardrails, tools, hitl, confirmation, streaming, tool-execution **Feature Branch**: `035-agent-chat-context` **Created**: 2026-07-04 | **Status**: Draft **Input**: "доработка логики агентского чата - передача контекста из фронта, доработка guardrails cards, оптимизация tools etc" ## Clarifications ### Session 2026-07-04 - **Страница `/agent` — выделенный модуль чата**: Агент-чат — отдельная страница (`/agent`), на которую пользователь переходит по кнопке с любой страницы сервиса. Контекст страницы-источника передаётся через query-параметры URL при навигации: `objectType`, `objectId`, `objectName`, `envId`, `route`. Фронтенд на странице `/agent` читает эти параметры в `onMount` и формирует `UIContext` JSON. Контекст статичен в рамках визита на `/agent` — повторные сообщения используют тот же контекст. Смена окружения через EnvSelector возможна и обновляет `envId` для последующих сообщений. Пример: со страницы `http://localhost:5173/dashboards/10?env_id=ss-dev` кнопка «Спросить агента» ведёт на `/agent?objectType=dashboard&objectId=10&objectName=Отчёт%20по%20энергопотреблению&envId=ss-dev&route=/dashboards/10`. - **Инструменты, релевантные контексту**: Список инструментов, доступных LLM, **фильтруется** (жёсткое исключение, не приоритизация) по `objectType`. `dashboard` → только дашборд-ориентированные инструменты; `dataset` → датасет-ориентированные; `null`/отсутствует → полный набор разрешённых инструментов. `show_capabilities` всегда включён. ## User Scenarios Each story is an independently testable unit. Prioritized P1 (MVP) → P2 → P3. All stories share `@SEMANTICS` domain keywords from the feature header. ### Story 1 — Context-Aware Agent on `/agent` Page (P1) **Why P1**: Пользователь переходит на выделенную страницу агента (`/agent`) с любой страницы сервиса по кнопке «Спросить агента». Контекст страницы-источника передаётся через query-параметры URL: `objectType`, `objectId`, `objectName`, `envId`, `route`. Агент, получив этот контекст, должен: (а) понимать, на каком объекте находился пользователь — отвечать контекстно без явного называния объекта; (б) фильтровать доступные инструменты по типу объекта — дашборд-ориентированные для дашборда, датасет-ориентированные для датасета. Контекст статичен в рамках визита на `/agent`; смена окружения через EnvSelector обновляет `envId`. **Independent Test**: Со страницы дашборда (`/dashboards/10?env_id=ss-dev`) нажать «Спросить агента» → переход на `/agent?objectType=dashboard&objectId=10&envId=ss-dev...`. Спросить «расскажи про этот дашборд» — агент: (1) отвечает с информацией о дашборде id=10 не требуя названия; (2) использует только дашборд-ориентированные инструменты, а не общий список из 22 тулов. **Acceptance**: 1. **Given** пользователь на странице дашборда нажимает «Спросить агента» и переходит на `/agent?objectType=dashboard&objectId=10&envId=ss-dev` **When** спрашивает «какой статус у этого дашборда?» **Then** агент понимает, что речь о дашборде id=10 в окружении `ss-dev`, и отвечает с git-статусом, датой изменения, результатами валидации — без явного указания названия или ID. 2. **Given** переход на `/agent?objectType=dataset&objectId=15&envId=staging` **When** агент вызван и спрашивают «есть ли проблемы с этим датасетом?» **Then** агент использует dataset id=15, staging и отвечает про маппинги, зависимости, статус ревью. 3. **Given** `objectType: "dashboard"` в контексте **When** агент формирует набор инструментов для LLM **Then** только дашборд-ориентированные инструменты (`search_dashboards`, `get_health_summary`, `deploy_dashboard`, `run_llm_validation`, `run_llm_documentation`, `execute_migration`, `create_branch`, `commit_changes`, `show_capabilities`) доступны; нерелевантные (`superset_explore_database`, `run_backup` и др.) — исключены. 4. **Given** `objectType: "dataset"` **When** агент формирует набор инструментов **Then** только датасет-ориентированные (`superset_explore_database`, `superset_format_sql`, `superset_audit_permissions`, `superset_execute_sql`, `superset_create_dataset`, `search_dashboards`, `get_task_status`, `list_environments`, `show_capabilities`) доступны. 5. **Given** пользователь переходит с дашборда A на страницу агента, затем возвращается и переходит с дашборда B **When** агент вызывается снова со страницы B **Then** новый `objectId` в URL — никакого залипания старого контекста. 6. **Given** пользователь на странице без контекстного объекта (переход с `/settings`) **When** агент вызывается (URL без `objectType`) **Then** агент работает в общем режиме с полным набором доступных инструментов. --- ### Story 2 — Guardrails Card Refinement (P2) **Why P2**: The current confirmation card shows tool name and arguments with a simple confirm/deny. Risk classification is a heuristic based on tool name prefixes only — it does not consider the environment (production vs staging), the scope of data affected, or the user's role. Users need richer context to make informed confirmation decisions, and guardrails should adapt to actual risk, not just tool category. **Independent Test**: Trigger a guarded tool (e.g., `deploy_dashboard` to production) — the confirmation card shows environment badge («⚠️ PRODUCTION»), estimated impact scope («3 дашборда будут изменены»), and a contextualised warning. Trigger the same tool to staging — the card shows a lower-severity styling. **Acceptance**: 1. **Given** the agent calls `deploy_dashboard` targeting the `prod` environment **When** a confirmation is triggered **Then** the card displays a «production» risk badge with destructive-tone styling, warns about the target environment, and requires explicit confirmation. 2. **Given** the same tool targets a `dev` or `staging` environment **When** a confirmation is triggered **Then** the card displays a lower-risk badge (info-tone), and the prompt is less alarming («Изменения в dev-окружении»). 3. **Given** the agent calls a read-only tool (e.g., `search_dashboards`) with HITL enabled **When** a confirmation is required **Then** the card clearly labels the action as «чтение данных» with distinct visual treatment (success-tone, no warning icon) — the user immediately distinguishes read from write confirmations. 4. **Given** the user's role lacks permission for a requested tool **When** the agent attempts the tool call **Then** the confirmation is pre-rejected server-side — the card displays «Недостаточно прав» with the required role shown and no confirm button (only a dismiss). 5. **Given** the user dismisses or denies a confirmation **When** the action is cancelled **Then** the agent does NOT proceed silently — it returns a clear cancellation message in the chat stream. --- ### Story 3 — Tool Execution Optimisation (P3) **Why P3**: The agent currently invokes tools via HTTP to FastAPI with no retry logic, no response-size awareness beyond a hard truncation limit, and no tool-level access control based on user role. Large responses waste LLM context window; network flakes cause avoidable failures; role-inappropriate tools unnecessarily confuse the LLM. **Independent Test**: Trigger `run_backup` as an analyst user — the tool is not offered to the LLM. Trigger `search_dashboards` with a large result set — the response is intelligently summarised. Simulate a transient FastAPI error — the tool retries once and succeeds. **Acceptance**: 1. **Given** the user has role `analyst` (no write permissions for git/deploy) **When** the agent's tool set is assembled **Then** write tools (`deploy_dashboard`, `commit_changes`, `create_branch`) are excluded from the LLM's available tools — the agent cannot even propose them. 2. **Given** a tool call to FastAPI returns a transient 502/503 error **When** the tool is a read-only query **Then** the agent retries once (with exponential backoff) before reporting failure to the user. 3. **Given** a tool returns >4000 characters of raw JSON **When** the response is prepared for the LLM **Then** structured data is summarised (top-N items + count) rather than hard-truncated mid-structure, preserving semantic value. 4. **Given** a tool invocation hangs (no response for 30 seconds) **When** the timeout elapses **Then** the agent receives a clear timeout error and can attempt an alternative approach or report the issue — the chat does not hang indefinitely. 5. **Given** the embedding router is available **When** the LLM selects tools **Then** the tool set passed to the LLM is restricted to the top-K semantically relevant tools (plus the mandatory capabilities tool), reducing prompt size and improving LLM tool-selection accuracy. --- ### Edge Cases - User navigates from dashboard page to `/agent` with context, closes tab, reopens from another dashboard → fresh context from new URL parameters. - No environment is selected in the UI → переход на `/agent` без `envId`; agent operates in environment-agnostic mode (cannot use env-specific tools). - The user's JWT expires mid-conversation → the next tool call fails with 401; agent reports «сессия истекла, обновите страницу» without retrying. - Conversation is restored from history that was created before context enrichment → old messages lack `uicontext` JSON; agent gracefully degrades to «контекст недоступен». - HITL confirmation is pending when the Gradio connection drops → state is preserved in the LangGraph checkpoint; on reconnect, the backend re-emits `confirm_required` for the pending checkpoint. - Multiple browser tabs have the agent chat open → each tab derives independent context from its own URL parameters; no cross-tab interference. - Malformed `objectType` in URL (e.g. `?objectType=invalid`) → frontend treats as `null`; backend validation rejects unknown enum values with 422. ## Requirements ### Functional (IDs survive HCA 128× via hierarchical naming) - **AGCTX-FR-001**: При переходе на `/agent` с любой страницы сервиса, страница-источник передаёт контекст через query-параметры URL: `objectType`, `objectId`, `objectName`, `envId`, `route`. Страница `/agent` читает параметры в `onMount` и формирует структурированный JSON-объект `UIContext`, который передаётся агенту при отправке сообщений. - **AGCTX-FR-002**: The agent backend MUST receive the UIContext JSON and inject it into the system prompt / runtime context block in a machine-and-human-readable format, distinguishable from user message text. - **AGCTX-FR-003**: Context is static for the duration of a `/agent` page visit. If the user navigates away and returns, fresh context is derived from new URL parameters. EnvId may be updated during the visit via EnvSelector (`updateActiveEnv`), which synchronises both the model atom and the envId sent with subsequent messages. - **AGCTX-FR-004**: The UIContext payload MUST be versioned (`contextVersion` field, currently 1). Backend MUST validate: `objectType` enum (`"dashboard"|"dataset"|"migration"`), `objectId` format (numeric string or null), `envId` against known environments, max field lengths (objectName ≤ 256 chars), max payload size (4 KB malformed/oversized context MUST trigger a 422 error or be silently discarded with audit logging — never silently injected into the LLM prompt. - **AGCTX-FR-005**: The agent backend MUST **hard-filter** (exclude, not deprioritise) the tool list by `objectType`: dashboard-oriented tools for dashboard context, dataset-oriented tools for dataset context, full tool set when `objectType` is null/absent. `show_capabilities` always included. - **AGGRD-FR-001**: Guardrails risk classification MUST consider the target environment (`prod` vs `staging` vs `dev`) in addition to the tool name prefix. - **AGGRD-FR-002**: The confirmation card UI MUST display environment context (badge, tone) and distinguish read vs write operations visually. - **AGGRD-FR-003**: Permission-denied tool calls MUST be rejected server-side before confirmation is presented — the card shows «access denied» without a confirm button. - **AGGRD-FR-004**: Cancelled confirmations MUST produce an explicit «operation cancelled» message in the chat stream. - **AGTL-FR-001**: Tool availability MUST be filtered by user role — write tools excluded for read-only roles. - **AGTL-FR-002**: Read-only tool HTTP calls MUST retry once with a fixed 1-second delay on transient errors (5xx, connection errors). Write/mutating tools MUST NOT auto-retry — raises immediately. - **AGTL-FR-003**: Large tool responses (>4000 chars) MUST be structured-truncated (top-N items + total count for arrays; sentence-boundary truncation for text) rather than hard-truncated mid-structure. - **AGTL-FR-004**: Tool execution MUST have a configurable timeout (default 30s); hanging calls MUST return a timeout error rather than block the chat. For write/mutating tools, timeout MUST produce «operation status unknown — check status» rather than automatic retry, as the backend mutation may have succeeded despite the timeout. - **AGTL-FR-005**: Tool RBAC filtering MUST be enforced at TWO layers: (a) prompt-level — excluded tools are not presented to the LLM; (b) invocation-level — before executing any tool, the backend MUST validate the tool name against the user's role permissions even if it was filtered from the prompt. Rejected invocations emit `permission_denied` SSE event, not `confirm_required`. - **AGTL-FR-006**: Embedding-based tool router integration (existing `_embedding_router.py`) is a SHOULD enhancement for post-MVP. When active, it restricts the tool list to top-K semantically relevant tools. Not required for initial release. ### Key Entities - **UIContext**: Structured JSON payload assembled by the frontend from URL query parameters at `/agent` page entry. Contains: `objectType` (`"dashboard"`|`"dataset"`|`"migration"`|`null`), `objectId` (string|null), `objectName` (string|null, max 256 chars), `envId` (string|null), `route` (string — source page route), `contextVersion` (number, currently 1). Static for the duration of the `/agent` visit. EnvId may be updated mid-visit via `updateActiveEnv`. Бэкенд валидирует все поля перед инжекцией в промпт. - **ContextToolAffinity**: Server-side mapping from `objectType` → set of relevant tool operations. Used to prioritise/filter tools for the LLM. Example: `dashboard` → `[search_dashboards, get_health_summary, deploy_dashboard, run_llm_validation, ...]`; `dataset` → `[superset_explore_database, superset_format_sql, superset_audit_permissions, ...]`; `none` → all available tools. - **GuardrailDecision**: Result of server-side guardrail evaluation. Contains: risk level (safe/guarded/dangerous), risk tone (success/warning/destructive), environment context, permission check result, recommended confirmation prompt. Used to render the confirmation card. - **ToolPermission**: Mapping of tool operation → required role(s). Derived from the `@assistant_tool` decorator's `permission_checks` (ADR-0008). Used to filter the LLM's available tool set per user. - **ToolResponseSummary**: Structured summary of a large tool response. Contains: result count, top-N items (trimmed), summary text, total items indicator. Generated server-side before the response enters the LLM context window. ## Success Criteria - **SC-001**: Агент, получивший контекст дашборда через `/agent?objectType=dashboard&objectId=42&envId=ss-dev`, в 100% тестовых сценариев правильно идентифицирует дашборд (id=42) и использует только дашборд-ориентированные инструменты — измерено через интеграционный тест с заданным контекстом. - **SC-002**: Guardrails card renders with correct risk classification (read/write/dangerous + environment context) within 200ms of confirmation trigger — measured via browser performance API. - **SC-003**: Tool invocation errors from transient FastAPI failures decrease by 50% relative to current baseline — измерено через structured audit log comparison за 1 week of operation after deployment. - **SC-004**: Average tool response size entering the LLM context window decreases by 30% — measured via log analysis of response summarisation calls over 1 week of operation. - **SC-005**: Zero cases where an `analyst`-role user can invoke a write tool (git deploy, dashboard migration, etc.) — verified via RBAC integration test at both prompt-filtering and invocation-enforcement layers. - **SC-006**: 100% of malformed/oversized UIContext payloads are rejected or discarded with audit logging — verified via fuzzing test. #endregion FeatureSpec