22 KiB
План развития Brief Bench FastAPI
Дата создания: 17 декабря 2025 Статус: Backend готов, требуется frontend интеграция и тестирование
Текущее состояние
Готово (Backend)
- Структура FastAPI приложения
- JWT авторизация (8-значный логин)
- TgBackendInterface (полная реализация с httpx)
- DBApiClient (интеграция с DB API)
- RagService (интеграция с RAG backends, mTLS)
- API endpoints:
POST /api/v1/auth/login- авторизацияGET/PUT /api/v1/settings- настройки пользователяPOST /api/v1/query/bench- batch запросыPOST /api/v1/query/backend- последовательные запросыPOST/GET/DELETE /api/v1/analysis/sessions- сессии анализа
- Docker setup (Dockerfile, docker-compose.yml)
- Документация (README.md, DB_API_CONTRACT.md)
Требуется доделать
- Frontend файлы (перенос из rag-bench-old-version)
- API client для frontend
- Интеграция frontend с новым API
- Тестирование
- Production deployment конфигурация
Этап 1: Подготовка Frontend
1.1 Перенос статических файлов из rag-bench-old-version
Старая архитектура:
- Статический SPA напрямую делал fetch запросы к RAG backends
- Настройки хранились в localStorage (отдельно для каждого окружения)
- Встроенная поддержка 3 окружений (IFT, PSI, PROD)
- Два режима: Bench (batch) и Backend (sequential)
- Аннотации, экспорт/импорт, детальный UI для анализа ответов
Файлы для переноса:
# Скопировать из rag-bench-old-version/ в static/
static/
├── index.html # Основная страница (24KB, 495 строк)
├── styles.css # Material Design стили (23KB)
├── app.js # Основная логика (61KB, ~1500+ строк)
├── settings.js # Дефолтные настройки (3KB)
└── api-client.js # НОВЫЙ ФАЙЛ - будет создан
Ключевые изменения в архитектуре:
- Было:
Browser → RAG Backend(прямой fetch) - Стало:
Browser → FastAPI → RAG Backend - Было: Настройки в localStorage
- Стало: Настройки в DB API (персональные для каждого пользователя)
- Добавлено: JWT авторизация с 8-значным логином
1.2 Создать API client для frontend
Файл: static/api-client.js
Полная реализация:
/**
* Brief Bench API Client
* Взаимодействие с FastAPI backend
*/
class BriefBenchAPI {
constructor() {
this.baseURL = '/api/v1'
}
// ============================================
// Internal Helpers
// ============================================
_getToken() {
return localStorage.getItem('access_token')
}
_setToken(token) {
localStorage.setItem('access_token', token)
}
_clearToken() {
localStorage.removeItem('access_token')
}
_getHeaders(includeAuth = true) {
const headers = {
'Content-Type': 'application/json'
}
if (includeAuth) {
const token = this._getToken()
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
}
return headers
}
async _handleResponse(response) {
// Handle 401 Unauthorized
if (response.status === 401) {
this._clearToken()
throw new Error('Сессия истекла. Пожалуйста, войдите снова.')
}
// Handle 502 Bad Gateway (RAG backend error)
if (response.status === 502) {
throw new Error('RAG backend недоступен или вернул ошибку')
}
// Handle other errors
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(errorData.detail || `HTTP ${response.status}: ${response.statusText}`)
}
// Handle 204 No Content
if (response.status === 204) {
return null
}
return await response.json()
}
async _request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`
try {
const response = await fetch(url, options)
return await this._handleResponse(response)
} catch (error) {
console.error(`API request failed: ${endpoint}`, error)
throw error
}
}
// ============================================
// Auth API
// ============================================
/**
* Авторизация с 8-значным логином
* @param {string} login - 8-значный логин
* @returns {Promise<{access_token: string, user: object}>}
*/
async login(login) {
const response = await this._request(`/auth/login?login=${login}`, {
method: 'POST',
headers: this._getHeaders(false)
})
// Сохранить токен
this._setToken(response.access_token)
return response
}
/**
* Выход (очистка токена)
*/
logout() {
this._clearToken()
window.location.reload()
}
/**
* Проверка авторизации
* @returns {boolean}
*/
isAuthenticated() {
return !!this._getToken()
}
// ============================================
// Settings API
// ============================================
/**
* Получить настройки пользователя для всех окружений
* @returns {Promise<{user_id: string, settings: object, updated_at: string}>}
*/
async getSettings() {
return await this._request('/settings', {
method: 'GET',
headers: this._getHeaders()
})
}
/**
* Обновить настройки пользователя
* @param {object} settings - Объект с настройками для окружений
* @returns {Promise<{user_id: string, settings: object, updated_at: string}>}
*/
async updateSettings(settings) {
return await this._request('/settings', {
method: 'PUT',
headers: this._getHeaders(),
body: JSON.stringify({ settings })
})
}
// ============================================
// Query API
// ============================================
/**
* Отправить batch запрос (Bench mode)
* @param {string} environment - Окружение (ift/psi/prod)
* @param {Array<{body: string, with_docs: boolean}>} questions - Массив вопросов
* @returns {Promise<{request_id: string, timestamp: string, environment: string, response: object}>}
*/
async benchQuery(environment, questions) {
return await this._request('/query/bench', {
method: 'POST',
headers: this._getHeaders(),
body: JSON.stringify({
environment,
questions
})
})
}
/**
* Отправить последовательные запросы (Backend mode)
* @param {string} environment - Окружение (ift/psi/prod)
* @param {Array<{body: string, with_docs: boolean}>} questions - Массив вопросов
* @param {boolean} resetSession - Сбрасывать ли сессию после каждого вопроса
* @returns {Promise<{request_id: string, timestamp: string, environment: string, response: object}>}
*/
async backendQuery(environment, questions, resetSession = true) {
return await this._request('/query/backend', {
method: 'POST',
headers: this._getHeaders(),
body: JSON.stringify({
environment,
questions,
reset_session: resetSession
})
})
}
// ============================================
// Analysis Sessions API
// ============================================
/**
* Сохранить сессию анализа
* @param {object} sessionData - Данные сессии
* @returns {Promise<object>}
*/
async saveSession(sessionData) {
return await this._request('/analysis/sessions', {
method: 'POST',
headers: this._getHeaders(),
body: JSON.stringify(sessionData)
})
}
/**
* Получить список сессий
* @param {string|null} environment - Фильтр по окружению (опционально)
* @param {number} limit - Лимит результатов
* @param {number} offset - Смещение для пагинации
* @returns {Promise<{sessions: Array, total: number}>}
*/
async getSessions(environment = null, limit = 50, offset = 0) {
const params = new URLSearchParams({ limit, offset })
if (environment) {
params.append('environment', environment)
}
return await this._request(`/analysis/sessions?${params}`, {
method: 'GET',
headers: this._getHeaders()
})
}
/**
* Получить конкретную сессию
* @param {string} sessionId - ID сессии
* @returns {Promise<object>}
*/
async getSession(sessionId) {
return await this._request(`/analysis/sessions/${sessionId}`, {
method: 'GET',
headers: this._getHeaders()
})
}
/**
* Удалить сессию
* @param {string} sessionId - ID сессии
* @returns {Promise<null>}
*/
async deleteSession(sessionId) {
return await this._request(`/analysis/sessions/${sessionId}`, {
method: 'DELETE',
headers: this._getHeaders()
})
}
}
// Export API client instance
const api = new BriefBenchAPI()
Особенности реализации:
- Автоматическое добавление
Authorization: Bearer {token}из localStorage - Обработка всех HTTP ошибок (401 → logout + reload, 502 → RAG error)
- Singleton instance (
const api = new BriefBenchAPI()) - Методы соответствуют FastAPI endpoints один-к-одному
Этап 2: Адаптация Frontend под новую архитектуру
2.1 Добавить Login Screen
Изменения в index.html:
<!-- Login screen (показывается если нет токена) -->
<div id="login-screen">
<h1>Brief Bench</h1>
<input type="text" id="login-input" placeholder="8-значный логин" maxlength="8">
<button id="login-btn">Войти</button>
<div id="login-error"></div>
</div>
<!-- Main app (показывается после авторизации) -->
<div id="app" style="display: none;">
<!-- Существующий интерфейс -->
</div>
Логика в app.js:
- При загрузке проверить наличие токена в localStorage
- Если нет → показать login screen
- Если есть → валидировать токен (попробовать загрузить настройки)
- Если токен невалидный → показать login screen
2.2 Переписать вызовы API
Старый код (прямые fetch):
// Было
const response = await fetch('https://rag-backend/api/bench', {
method: 'POST',
headers: { ... },
body: JSON.stringify(questions)
})
Новый код (через API client):
// Стало
const api = new BriefBenchAPI()
const response = await api.benchQuery('ift', questions)
Что нужно переписать:
- Загрузка настроек пользователя (при старте приложения)
- Отправка bench/backend запросов
- Сохранение сессий анализа
- Загрузка истории сессий
- Экспорт данных
2.3 Управление Settings через UI
Новая логика:
- При загрузке приложения:
GET /api/v1/settings→ загрузить настройки для всех 3 окружений - Отобразить в UI (вкладки для IFT/PSI/PROD)
- При изменении:
PUT /api/v1/settings→ сохранить в DB API
Поля настроек (для каждого окружения):
- API Mode: bench / backend (radio buttons)
- Bearer Token (input, password type)
- System Platform (input)
- System Platform User (input)
- Platform User ID (input)
- Platform ID (input)
- With Classify (checkbox, только для backend mode)
- Reset Session Mode (checkbox, только для backend mode)
2.4 Интеграция History (сессии анализа)
Функционал:
- После выполнения анализа → кнопка "Сохранить сессию"
- При сохранении:
POST /api/v1/analysis/sessions- Отправить environment, api_mode, request, response, annotations
- Вкладка "История":
- Загрузить список:
GET /api/v1/analysis/sessions?environment=ift - Отобразить в виде таблицы (дата, окружение, режим, кол-во вопросов)
- При клике → загрузить полную сессию:
GET /api/v1/analysis/sessions/{id} - Кнопка удаления:
DELETE /api/v1/analysis/sessions/{id}
- Загрузить список:
Этап 3: Environment Selector
3.1 UI для выбора окружения
Добавить в index.html:
<div id="environment-selector">
<label>Окружение:</label>
<select id="env-select">
<option value="ift">ИФТ</option>
<option value="psi">ПСИ</option>
<option value="prod">ПРОД</option>
</select>
</div>
Логика:
- При выборе окружения → загрузить настройки для этого окружения
- Отобразить текущий apiMode (bench/backend)
- При отправке запроса → использовать выбранное окружение
3.2 Валидация перед отправкой
Проверки:
- Выбрано окружение
- Настройки для окружения существуют
- apiMode соответствует выбранному режиму (bench/backend)
- Если требуется bearerToken → он заполнен
Показывать ошибки:
- "Настройки для окружения ИФТ не найдены. Перейдите в Settings."
- "Окружение ПСИ настроено на Backend mode, но вы выбрали Bench режим."
Этап 4: Тестирование
4.1 Ручное тестирование
Сценарии:
-
Авторизация:
- Успешный вход (8 цифр)
- Ошибка (невалидный логин)
- Истечение токена (через 30 дней или вручную испортить токен)
-
Настройки:
- Загрузка настроек для всех окружений
- Изменение настроек для одного окружения
- Сохранение → перезагрузка страницы → настройки сохранились
-
Bench Query:
- Выбрать IFT, bench mode
- Отправить несколько вопросов
- Проверить что ответ отображается корректно
- Проверить headers в Network tab (Request-Id, System-Id, Bearer token)
-
Backend Query:
- Выбрать PSI, backend mode
- Отправить вопросы последовательно
- Проверить что между вопросами происходит reset session
-
Сессии анализа:
- Сохранить сессию после анализа
- Открыть историю → найти сессию
- Загрузить сессию → проверить что данные корректны
- Удалить сессию
-
Ошибки:
- DB API недоступен → показать ошибку
- RAG backend недоступен → показать ошибку 502
- Невалидный токен → редирект на login
4.2 Автоматическое тестирование (опционально)
Backend tests (pytest):
tests/
├── test_auth.py # Тесты авторизации
├── test_settings.py # Тесты settings endpoints
├── test_query.py # Тесты query endpoints
├── test_analysis.py # Тесты analysis endpoints
└── conftest.py # Fixtures (mock DB API, mock RAG)
Установка:
pip install pytest pytest-asyncio httpx
Запуск:
pytest tests/ -v
Этап 5: Production Deployment
5.1 Настройка .env для production
Критичные изменения:
# Application
DEBUG=false
# JWT (сгенерировать новый секретный ключ!)
JWT_SECRET_KEY=<сгенерированный-секрет-256-бит>
# DB API (production URL)
DB_API_URL=https://db-api.production.com/api/v1
# RAG Backends (production hosts)
IFT_RAG_HOST=ift-rag.production.com
PSI_RAG_HOST=psi-rag.production.com
PROD_RAG_HOST=prod-rag.production.com
# mTLS сертификаты (положить в certs/)
IFT_RAG_CERT_CA=/app/certs/ift/ca.crt
IFT_RAG_CERT_KEY=/app/certs/ift/client.key
IFT_RAG_CERT_CERT=/app/certs/ift/client.crt
# (аналогично для PSI и PROD)
5.2 CORS configuration
Изменить app/main.py:
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"https://brief-bench.production.com", # Production domain
"http://localhost:8000" # Для локальной разработки
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)
5.3 Размещение mTLS сертификатов
certs/
├── ift/
│ ├── ca.crt
│ ├── client.key
│ ├── client.crt
├── psi/
│ ├── ca.crt
│ ├── client.key
│ ├── client.crt
└── prod/
├── ca.crt
├── client.key
└── client.crt
Важно: Убедиться что права доступа 600 или 400 (только чтение владельцем)
5.4 Docker deployment
Запуск:
# Создать .env
cp .env.example .env
nano .env # Заполнить production значения
# Разместить сертификаты
mkdir -p certs/ift certs/psi certs/prod
# Скопировать сертификаты в соответствующие папки
# Запустить
docker-compose up -d
# Проверить логи
docker-compose logs -f fastapi
# Проверить здоровье
curl http://localhost:8000/health
5.5 Logging и Monitoring
Добавить middleware для логирования (опционально):
# app/middleware/logging_middleware.py
import logging
import time
from fastapi import Request
logger = logging.getLogger(__name__)
async def log_requests(request: Request, call_next):
start_time = time.time()
logger.info(f"Request: {request.method} {request.url}")
response = await call_next(request)
process_time = time.time() - start_time
logger.info(
f"Response: {response.status_code} "
f"({process_time:.2f}s)"
)
return response
Зарегистрировать в app/main.py:
from app.middleware.logging_middleware import log_requests
app.middleware("http")(log_requests)
Этап 6: Дополнительные улучшения (опционально)
6.1 Rate Limiting
- Ограничить количество запросов от одного пользователя
- Использовать
slowapiили redis-based rate limiter
6.2 WebSocket для real-time updates
- Показывать прогресс для backend mode (вопрос N из M)
- Использовать FastAPI WebSocket
6.3 Export функционал
- CSV экспорт сессий анализа
- JSON экспорт для интеграции с другими системами
6.4 Advanced Analytics
- Dashboard с метриками (кол-во запросов, успешность, время ответа)
- Графики по окружениям
Приоритезация задач
Критично (сделать в первую очередь)
- Перенос статических файлов из rag-bench-old-version →
static/ - Создание
api-client.js - Добавление login screen в
index.html - Переписывание вызовов API в
app.js - Тестирование auth flow
Важно (сделать после критичного)
- Интеграция Settings UI
- Environment selector
- Сохранение и загрузка сессий анализа
- Ручное тестирование всех сценариев
Желательно (если есть время)
- Автоматические тесты (pytest)
- Production deployment настройка
- Logging middleware
- Rate limiting
- Export функционал
Следующие шаги
Рекомендуемый порядок:
- Посмотреть структуру старого проекта
rag-bench-old-version - Скопировать статические файлы в
static/ - Создать
api-client.jsс методами для всех endpoints - Модифицировать
index.html(добавить login screen) - Переписать
app.jsдля работы с новым API - Протестировать локально с
uvicorn app.main:app --reload - Исправить найденные проблемы
- Подготовить production deployment
Готовы начать с первого этапа?