Upload backend-to-frontdoor-spec-v6.md

#1
by dgrandis - opened
Files changed (1) hide show
  1. backend-to-frontdoor-spec-v6.md +265 -0
backend-to-frontdoor-spec-v6.md ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Спецификация: ChatService ↔ L1 Router Agent
2
+
3
+ **Создано**: 2026-04-01
4
+ **Статус**: Draft
5
+ **Базовые документы**:
6
+ - «Базовая схема работы ChatService бэкенда и агента L1 Router Agent»
7
+ - «Sequence flows ChatService» (2026-03-30)
8
+
9
+ ---
10
+
11
+ ## 1. Назначение
12
+
13
+ Описывает взаимодействие между ChatService (бэкенд модуля ИИ-агентов) и L1 Router Agent (входная точка мультиагентной системы). Описаны: формат вызовов, жизненный цикл чата и сообщений, runtime-binding, retry, cleanup.
14
+
15
+ ---
16
+
17
+ ## 2. Участники
18
+
19
+ | Участник | Описание |
20
+ |----------|----------|
21
+ | **API Gateway** | Вход в backend-контур Space. Валидация JWT, rate limiting. |
22
+ | **ChatService** | Бэкенд модуля ИИ-агентов. Хранит чаты, сообщения, feedback. Вызывает L1 Router Agent. |
23
+ | **FeedbackService** | Хранилище истории, feedback и runtime-binding. |
24
+ | **L1 Router Agent** | Downstream агентный сервис. Stateful — поддерживает runtime-сессии через `agent_id`. |
25
+ | **Observability** | Langfuse, Prometheus. Логирование, trace, метрики. |
26
+
27
+ ---
28
+
29
+ ## 3. Общая схема
30
+
31
+ ```
32
+ API Gateway
33
+
34
+
35
+ ChatService
36
+ │ • хранит сообщения, feedback
37
+ │ -- • формирует inline chat-context
38
+ │ -- • управляет retry и placeholder-ами
39
+
40
+
41
+ L1 Router Agent (stateful)
42
+ │ • принимает POST /v1/chat/completions
43
+ │ • управляет session_id (создаёт, сменяет при смене темы)
44
+ │ • возвращает ответ + session_id
45
+
46
+
47
+ Domain Agent → L3 Agent → Tools
48
+ ```
49
+
50
+ ---
51
+
52
+ ## 4. Формат вызова ChatService → L1 Router Agent
53
+
54
+ ChatService вызывает L1 Router Agent через **Completions-совместимый формат** (`POST /v1/chat/completions`). L1 Router Agent внутри себя трансформирует в межагентный формат для Domain/L3.
55
+
56
+ ### 4.1. Request: ChatService → L1 Router Agent
57
+
58
+ **`POST /v1/chat/completions`**
59
+
60
+ | Поле | Тип | Описание |
61
+ |------|-----|----------|
62
+ | `message` | string | Текст последнего сообщения пользователя |
63
+ | `history` | array\<Message\> | Inline chat-context — предыдущие сообщения из Chat Storage |
64
+ | `request_id` | string (UUID) | Уникальный ID запроса. Используется для дедупликации retry. |
65
+ | `trace_id` | string (UUID) | Сквозной ID трассировки |
66
+ | `session_id` | string \| null | ID текущей сессии (темы). `null` — первое обращение, сессии ещё нет. |
67
+ | `auth` | object | Данные пользователя (извлекаются из JWT) |
68
+ | `auth.employee_id` | string | Табельный номер сотрудника |
69
+ | `auth.login` | string | Корпоративный логин |
70
+ | `stream` | boolean | Включить SSE-стриминг |
71
+
72
+ **Структура Message (в history):**
73
+
74
+ | Поле | Тип | Описание |
75
+ |------|-----|----------|
76
+ | `role` | string | `"user"` \| `"assistant"` |
77
+ | `content` | string | Текст сообщения |
78
+
79
+ **Пример (первое сообщение, сессии ещё нет):**
80
+ ```json
81
+ {
82
+ "message": "Сколько дней отпуска мне положено?",
83
+ "history": [],
84
+ "request_id": "a1b2c3d4-0001-4000-8000-000000000001",
85
+ "trace_id": "f7e6d5c4-b3a2-4000-9000-000000000001",
86
+ "session_id": null,
87
+ "auth": {
88
+ "employee_id": "EMP-8834",
89
+ "login": "i.petrov"
90
+ },
91
+ "stream": false
92
+ }
93
+ ```
94
+
95
+ **Пример (follow-up, сессия есть):**
96
+ ```json
97
+ {
98
+ "message": "А как его оформить?",
99
+ "history": [
100
+ {"role": "user", "content": "Сколько дней отпуска мне положено?"},
101
+ {"role": "assistant", "content": "Сотруднику положено 28 календарных дней..."}
102
+ ],
103
+ "request_id": "a1b2c3d4-0001-4000-8000-000000000003",
104
+ "trace_id": "f7e6d5c4-b3a2-4000-9000-000000000003",
105
+ "session_id": "ses-001",
106
+ "auth": {
107
+ "employee_id": "EMP-8834",
108
+ "login": "i.petrov"
109
+ },
110
+ "stream": false
111
+ }
112
+ ```
113
+
114
+ ### 4.2. Response: L1 Router Agent → ChatService
115
+
116
+ | Поле | Тип | Описание |
117
+ |------|-----|----------|
118
+ | `status` | enum | `"completed"` \| `"needs_clarification"` \| `"no_result"` \| `"error"` |
119
+ | `content` | string \| null | Текст ответа или уточняющий вопрос |
120
+ | `session_id` | string | ID сессии. При первом запросе — новый. При смене темы — нов��й. Иначе — тот же. |
121
+ | `request_id` | string (UUID) | Эхо request_id из запроса |
122
+ | `trace_id` | string (UUID) | Эхо trace_id из запроса |
123
+ | `sources` | array \| null | Источники ответа |
124
+ | `sources[].title` | string | Название источника |
125
+ | `sources[].url` | string | URL источника |
126
+ | `sources[].type` | string | `"confluence_page"` \| `"sql_result"` |
127
+ | `artifacts` | array \| null | Файлы, вложения, выгрузки от агентов |
128
+ | `artifacts[].type` | string | `"file"` \| `"csv"` \| `"document"` \| `"link"` |
129
+ | `artifacts[].ref` | string | URL для скачивания |
130
+ | `artifacts[].title` | string \| null | Название файла |
131
+ | `artifacts[].note` | string \| null | Описание |
132
+
133
+ **Пример (успех, первый запрос — L1 Router Agent создал сессию):**
134
+ ```json
135
+ {
136
+ "status": "completed",
137
+ "content": "Сотруднику положено 28 календарных дней ежегодного оплачиваемого отпуска.",
138
+ "session_id": "ses-001",
139
+ "request_id": "a1b2c3d4-0001-4000-8000-000000000001",
140
+ "trace_id": "f7e6d5c4-b3a2-4000-9000-000000000001",
141
+ "sources": [
142
+ {"title": "Политика отпусков", "url": "https://confluence.internal/pages/conf-hr-1452", "type": "confluence_page"}
143
+ ],
144
+ "artifacts": null,
145
+ }
146
+ ```
147
+
148
+ **Пример (с файловым вложением):**
149
+ ```json
150
+ {
151
+ "status": "completed",
152
+ "content": "Шаблон авансового отчёта доступен для скачивания.",
153
+ "session_id": "ses-001",
154
+ "request_id": "...",
155
+ "trace_id": "...",
156
+ "sources": [
157
+ {"title": "Шаблоны командировочных документов", "url": "https://confluence.internal/pages/conf-hr-2200", "type": "confluence_page"}
158
+ ],
159
+ "artifacts": [
160
+ {
161
+ "type": "file",
162
+ "ref": "https://confluence.internal/download/conf-hr-2200/avansoviy_otchet.xlsx",
163
+ "title": "Шаблон авансового отчёта",
164
+ "note": "Excel-файл для заполнения"
165
+ }
166
+ ]
167
+ }
168
+ ```
169
+
170
+
171
+ **Пример (нет данных):**
172
+ ```json
173
+ {
174
+ "status": "no_result",
175
+ "content": "В базе знаний отсутствует информация по данной теме.",
176
+ "session_id": "ses-001",
177
+ "request_id": "...",
178
+ "trace_id": "...",
179
+ "sources": null,
180
+ "artifacts": null
181
+ }
182
+ ```
183
+
184
+ **Пример (ошибка):**
185
+ ```json
186
+ {
187
+ "status": "error",
188
+ "content": "Запрашиваемая информация недоступна.",
189
+ "session_id": "ses-001",
190
+ "request_id": "...",
191
+ "trace_id": "...",
192
+ "sources": null,
193
+ "artifacts": null
194
+ }
195
+ ```
196
+
197
+ ### 4.3. SSE-стриминг (stream: true)
198
+
199
+ При `stream: true` L1 Router Agent отправляет ответ по частям через SSE:
200
+
201
+ ```
202
+ data: {"delta": {"content": "Сотруднику "}}
203
+ data: {"delta": {"content": "положено "}}
204
+ data: {"delta": {"content": "28 календарных дней."}}
205
+ data: {"sources": [{"title": "Политика отпусков", "url": "https://..."}]}
206
+ data: {"session_id": "ses-001"}
207
+ data: [DONE]
208
+ ```
209
+
210
+ `session_id` приходит в финальном событии.
211
+
212
+ ---
213
+
214
+ ## 5. Управление сессиями (session_id)
215
+
216
+ L1 Router Agent — **stateful**. Он управляет сессиями: создаёт при первом обращении, сохраняет контекст темы, создаёт новую при смене темы.
217
+
218
+ ### Жизненный цикл session_id
219
+
220
+ | Событие | Что происходит |
221
+ |---------|---------------|
222
+ | Первый запрос (`session_id: null`) | L1 Router Agent создаёт сессию, возвращает `session_id: "ses-001"`. Gateway сохраняет. |
223
+ | Follow-up в той же теме (`session_id: "ses-001"`) | L1 Router Agent продолжает сессию, контекст сохранён. Возвращает тот же `session_id`. |
224
+ | Смена темы | L1 Router Agent определяет смену темы (специальный тул), создаёт новую сессию. Возвращает `session_id: "ses-002"`. Gateway обновляет. |
225
+ | L1 Router Agent перезапущен | L1 Router Agent не узнаёт `session_id`, работает по `history` (fallback). Возвращает новый `session_id`. Gateway обновляет. |
226
+
227
+ ### Важно
228
+
229
+ - `session_id` управляется L1 Router Agent, не ChatService
230
+ - ChatService просто хранит последний `session_id` и передаёт обратно в следующем запросе
231
+ - `history` всегда передаётся как fallback — если `session_id` устарел, L1 Router Agent не теряет контекст
232
+ - Один пользователь может иметь несколько `session_id` в рамках одного п��тока сообщений (каждая тема — отдельная сессия)
233
+
234
+ ---
235
+
236
+ ## 6. Жизненный цикл сообщения (assistant)
237
+
238
+ Каждый ответ ассистента проходит через статусы:
239
+
240
+ | Статус | Описание | Переход |
241
+ |--------|----------|---------|
242
+ | `pending` | Placeholder создан, запрос отправлен в L1 Router Agent | → `completed` / `needs_clarification` / `retryable` |
243
+ | `completed` | Ответ получен от L1 Router Agent, сохранён | — |
244
+ | `needs_clarification` | L1 Router Agent вернул уточняющий вопрос | → следующий completions-вызов |
245
+ | `retryable` | L1 Router Agent вернул ошибку / timeout, retry возможен | → `completed` (после retry) / `failed` (по TTL) |
246
+ | `failed` | Retry не произошёл в течение TTL | — |
247
+
248
+ ### Алгоритм
249
+
250
+ 1. Gateway получает запрос от фронтенда
251
+ 2. Создаёт assistant placeholder (`status=pending`)
252
+ 3. Отправляет запрос в L1 Router Agent
253
+ 4. Сохраняет user message в Chat Storagenj
254
+ 5. Результат:
255
+ - **Успех** → обновляет placeholder (`status=completed`, `content=answer`), сохраняет `session_id`
256
+ - **Ошибка/timeout** → обновляет placeholder (`status=retryable`)
257
+
258
+ ---
259
+
260
+ ## 7. Обработка ошибок
261
+
262
+ | Ситуация | HTTP-код | Ответ |
263
+ |----------|---------|-------|
264
+ | L1 Router Agent | 200 | Response с `completed` |
265
+ | L1 Router Agent | 500 | Bad Gateway - L1 Router Agent не доступен |