Files
gooseek/docs/architecture/MICROSERVICES.md
home f4d945a2b5 feat: localization service and frontend integration
- Add localization-service microservice (locale resolution, translations)
- Add frontend API routes /api/locale and /api/translations/[locale]
- Add LocalizationProvider and localization context
- Integrate localization into layout, EmptyChat, MessageInput components
- Update MICROSERVICES.md architecture docs
- Add localization-service to workspaces

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 21:45:44 +03:00

407 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Архитектура микросервисов GooSeek
Документ описывает план разбиения монолитного приложения GooSeek на микросервисы.
---
## 1. Текущая архитектура (монолит)
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Next.js Application (monolith) │
├─────────────────────────────────────────────────────────────────────────────┤
│ API Routes │ Agents/Search │ Models/Providers │ Data │
│ /api/chat │ classifier │ OpenAI, Anthropic │ SQLite│
│ /api/search │ researcher │ Ollama, Groq... │ config│
│ /api/images │ widgets │ registry │ uploads│
│ /api/videos │ writer │ │ │
│ /api/uploads │ media (image/video) │ │ │
│ /api/chats │ │ │ │
│ /api/providers │ │ │ │
│ /api/config │ │ │ │
│ /api/suggestions │ │ │ │
│ /api/weather │ │ │ │
│ /api/discover │ │ │ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ External: SearXNG │ Yahoo Finance │ Open-Meteo │ LLM APIs (OpenAI, etc.) │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## 2. Предлагаемые микросервисы
### 2.1 Общая схема
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Frontend │────▶│ Gateway │────▶│ Chat │────▶│ Research │
│ (Next.js) │ │ (BFF/API) │ │ Service │ │ Service │
└──────────────┘ └──────────────┘ └──────┬───────┘ └──────┬───────┘
│ │ │ │
│ │ │ │
│ ▼ ▼ ▼
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ │ Config/Chats │ │ LLM Proxy │ │ Search │
└────────────▶│ Service │ │ Service │ │ Service │
└──────────────┘ └──────────────┘ └──────┬───────┘
│ │ │
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Storage │ │ Providers │ │ Uploads │
│ Service │ │ (external) │ │ Service │
└──────────────┘ └──────────────┘ └──────────────┘
```
---
## 3. Описание сервисов
### 3.1 Frontend (Next.js UI)
**Назначение:** Только UI — страницы, компоненты, клиентский state.
**Содержимое:**
- `src/app/page.tsx`, `layout.tsx`, маршруты страниц
- `src/components/` — все UI компоненты
- `src/lib/hooks/` — клиентские хуки
- `src/lib/actions.ts` — вызовы к Gateway API
**Исключить:** Все API routes (`src/app/api/`), серверная логика агентов, прямые вызовы DB.
**API:** Вызывает только Gateway.
---
### 3.2 Gateway (Backend-for-Frontend / API Gateway)
**Назначение:** Единая точка входа для клиента, роутинг, валидация, агрегация.
**Реализация:** Отдельный Node.js сервис (Fastify/Express) или часть Next.js API routes, которая только проксирует.
**Маршруты:**
- `POST /api/chat` → Chat Service
- `POST /api/search` → Chat Service (API mode)
- `GET/POST /api/chats/*` → Storage Service
- `POST /api/uploads` → Uploads Service
- `GET/POST /api/providers/*` → Config + LLM Proxy
- `GET/POST /api/config/*` → Storage Service (config)
- `POST /api/suggestions` → Chat Service
- `POST /api/images` → Chat/Media Service
- `POST /api/videos` → Chat/Media Service
- `GET /api/weather` → Widgets Service (или inline)
- `POST /api/discover` → Search Service
**Зависимости:** Chat, Storage, Uploads, Search. Не содержит бизнес-логики.
---
### 3.3 Chat Service (Оркестратор)
**Назначение:** Главный оркестратор диалога: классификация, планирование, вызов research и writer.
**Исходники:**
- `src/lib/agents/search/index.ts` — SearchAgent
- `src/lib/agents/search/api.ts` — APISearchAgent
- `src/lib/agents/search/classifier.ts`
- `src/lib/agents/search/widgets/` — executor + виджеты (weather, stock, calc)
- `src/lib/prompts/search/` — classifier, writer, researcher prompts
- `src/lib/session.ts`
**API (внутренние):**
- `POST /classify` — классификация запроса
- `POST /search` — полный пайплайн (classify → research → widgets → write)
- `POST /suggestions` — генерация подсказок
**Вызывает:**
- Research Service — для поиска
- LLM Proxy — для LLM вызовов (classify, write, suggestions)
- Widgets (внешние API: Yahoo Finance, погода) — можно вынести в отдельный сервис
**Поток данных:**
1. Классификация (LLM)
2. Параллельно: Research Service + виджеты
3. Генерация ответа (LLM)
4. Сохранение в Storage (через Gateway или напрямую)
---
### 3.4 Research Service
**Назначение:** Выполнение исследовательского цикла — tools (web, academic, social, scrape, uploads).
**Исходники:**
- `src/lib/agents/search/researcher/` — Researcher, ActionRegistry
- `src/lib/agents/search/researcher/actions/` — webSearch, academicSearch, socialSearch, scrapeURL, uploadsSearch, plan, done
- `src/lib/prompts/search/researcher.ts`
**API:**
- `POST /research` — вход: classification, query, chatHistory, config → выход: searchFindings, findings
**Вызывает:**
- Search Service — SearXNG (web, academic, social)
- Uploads Service — семантический поиск по файлам
- LLM Proxy — для researcher agent (tool calls)
- Внешние URL — scrape
**Важно:** Researcher — это LLM-driven agent с tool calls. LLM решает, какой tool вызвать. Либо LLM живёт в Chat Service и вызывает tools по HTTP, либо Research Service сам держит копию researcher LLM — тогда нужно дублировать LLM-вызовы.
**Рекомендация:** Оставить researcher внутри Chat Service, а в Research Service вынести только **действия** (web search, scrape, uploads search) как отдельные HTTP endpoints. Chat Service вызывает Research Service по каждому tool call. Это уменьшает связанность, но увеличивает сетевые вызовы.
**Альтернатива:** Research как один сервис с LLM — получает query и classification, сам итерирует и возвращает готовые findings. Меньше round-trips, но дублирование LLM-инфраструктуры.
---
### 3.5 Search Service (SearXNG + обёртка)
**Назначение:** Обёртка над SearXNG для веб/академического/медиа поиска.
**Исходники:**
- `src/lib/searxng.ts`
- Вызовы: webSearch, academicSearch, socialSearch, image, video — все идут через `searchSearxng`
**API:**
- `GET /search?q=...&engines=...&categories=...`
- `GET /images?q=...`
- `GET /videos?q=...`
**Внешняя зависимость:** SearXNG (уже отдельный сервис/контейнер).
**Реализация:** Минимальный Node.js/Fastify сервис, который проксирует к SearXNG и добавляет логику (rate limiting, fallback, кэш).
---
### 3.6 Uploads Service
**Назначение:** Загрузка файлов, парсинг (PDF, DOCX, TXT), эмбеддинги, семантический поиск.
**Исходники:**
- `src/lib/uploads/manager.ts` — UploadManager
- `src/lib/uploads/store.ts` — UploadStore (поиск по эмбеддингам)
- `src/lib/utils/splitText.ts`, `computeSimilarity.ts`
- Зависимости: pdf-parse, officeparser
**API:**
- `POST /upload` — multipart, возвращает fileIds
- `POST /search` — query, fileIds → chunks (семантический поиск)
- `GET /files/:id` — метаданные файла
**Вызывает:** LLM Proxy — только для embedding модели (можно вынести в отдельный Embedding Service).
**Хранение:** Файлы на диске/volume, `uploaded_files.json`, `.content.json` с chunks.
---
### 3.7 Storage Service
**Назначение:** Централизованное хранилище: чаты, сообщения, конфигурация.
**Исходники:**
- `src/lib/db/` — schema, migrations, drizzle
- `src/lib/config/` — ConfigManager, modelProviders
**API:**
- `GET/POST/PUT/DELETE /chats`
- `GET/POST/PUT/DELETE /chats/:id`
- `GET/POST/PUT/DELETE /messages`
- `GET/POST /config`
- `GET/POST /config/providers`
**База:** SQLite (или PostgreSQL при масштабировании). config.json можно мигрировать в ту же БД или отдельную таблицу.
---
### 3.8 LLM Proxy Service (опционально)
**Назначение:** Единая точка для вызовов LLM и embedding — OpenAI, Anthropic, Ollama, Groq, Gemini и т.д.
**Исходники:**
- `src/lib/models/` — registry, providers, base LLM/Embedding
**API:**
- `POST /chat` — text generation (streaming)
- `POST /embed` — embedding
- `GET /providers` — список провайдеров и моделей
**Конфигурация:** Берёт из Storage Service (config/providers) или env.
**Альтернатива:** LLM Proxy можно не выделять в отдельный сервис — каждый сервис (Chat, Research, Uploads) сам подключается к провайдерам. Но тогда дублирование кода и конфигурации. Рекомендуется вынести в общую библиотеку или отдельный сервис.
---
### 3.9 Geo Device Service (реализован)
**Назначение:** Определение геопозиции пользователя, типа устройства, браузера и ОС.
**Расположение:** `apps/geo-device-service/`
**API:**
- `GET /api/context` — контекст по IP и заголовкам (User-Agent, Accept-Language)
- `POST /api/context` — контекст с дополнительными данными от клиента (screen, timezone, Geolocation API)
**Данные:**
- **geo** — latitude, longitude, city, country, timezone (geoip-lite по IP или из body)
- **device** — type (desktop/mobile/tablet), vendor, model
- **browser** — name, version (ua-parser-js)
- **os** — name, version
- **client** — screenWidth, viewportHeight, timezone, language, hardwareConcurrency, deviceMemory, doNotTrack (из POST body)
**Запуск:** `npm run dev:geo` или `PORT=4002 npm run start -w geo-device-service`
**Интеграция:** Frontend вызывает `/api/geo-context` (проксирует к сервису), клиент — `fetchContextWithClient()` из `@/lib/geoDevice`.
---
### 3.10 Localization Service (реализован)
**Назначение:** Локализация на основе геопозиции. Определяет locale (язык) пользователя по данным из Geo Device Service.
**Расположение:** `apps/localization-service/`
**Зависимость:** Geo Device Service (вызывает `/api/context` для получения countryCode, client.language, Accept-Language).
**API:**
- `GET /api/locale` — resolve locale по IP/заголовкам (гео через geo-device-service)
- `POST /api/locale` — resolve locale с client data (screen, language, geo)
- `GET /api/translations/:locale` — переводы для UI
- `GET /api/locales` — список поддерживаемых локалей
**Данные (LocalizationContext):**
- **locale** — BCP 47 language code (ru, en, de, ...)
- **source** — geo | accept-language | client | fallback
**Приоритет определения locale:**
1. client.language (браузер navigator.language)
2. Accept-Language заголовок
3. countryCode из геопозиции
4. fallback: en
**Запуск:** `npm run dev:locale` или `PORT=4003 npm run start -w localization-service`
**Конфигурация:** `GEO_DEVICE_SERVICE_URL` — URL geo-device-service (по умолчанию http://localhost:4002)
**Интеграция:** Frontend вызывает `/api/locale`, `/api/translations/[locale]` (проксирует к сервису), клиент — `fetchLocaleWithClient()`, `fetchTranslations()` из `@/lib/localization`.
---
## 4. Матрица зависимостей
| Сервис | От кого получает вызовы | Кого вызывает |
|----------------|----------------------------------|----------------------------------|
| Gateway | Frontend | Chat, Storage, Uploads, Search |
| Localization | Frontend | Geo Device Service |
| Geo Device | Frontend, Localization | — |
| Chat | Gateway | Research, LLM Proxy, Storage |
| Research | Chat | Search, Uploads, LLM Proxy |
| Search | Research, Chat (media) | SearXNG (внешний) |
| Uploads | Gateway, Research | LLM Proxy (embedding) |
| Storage | Gateway, Chat | — |
| LLM Proxy | Chat, Research, Uploads | OpenAI, Anthropic, Ollama... |
---
## 5. Разбиение по репозиториям/папкам
### Вариант A: Монорепо (рекомендуется для старта)
```
gooseek/
├── apps/
│ ├── frontend/ # Next.js UI
│ ├── gateway/ # BFF / API Gateway
│ ├── chat-service/ # SearchAgent, classifier, writer, widgets
│ ├── research-service/ # Researcher, actions
│ ├── search-service/ # SearXNG wrapper
│ ├── uploads-service/ # UploadManager, UploadStore
│ ├── storage-service/ # DB, config
│ ├── llm-proxy/ # Models registry, providers
│ ├── geo-device-service/ # Геопозиция, устройство, браузер
│ ├── localization-service/ # Локализация (зависит от geo-device)
│ ├── shared-types/ # Общие типы, DTO
│ └── shared-utils/ # formatHistory, splitText, computeSimilarity
├── docker-compose.yaml
└── package.json # Turborepo/nx workspace
```
### Вариант B: polyrepo
Отдельные репозитории для каждого сервиса. Больше гибкости в деплое, но сложнее координация изменений.
---
## 6. Порядок миграции (фазы)
### Фаза 1: Выделение Search Service
- Обернуть SearXNG в отдельный сервис
- Chat/Research вызывают его по HTTP вместо прямого импорта `searchSearxng`
- **Риск:** Низкий. SearXNG уже внешний.
### Фаза 2: Выделение Storage Service
- Вынести DB + config в отдельный сервис
- Gateway и Chat переходят на HTTP к Storage
- **Риск:** Средний. Много точек входа в DB.
### Фаза 3: Выделение Uploads Service
- Вынести UploadManager + UploadStore
- Research (uploadsSearch action) и Gateway вызывают Uploads по HTTP
- **Риск:** Средний. Embedding модель — зависимость.
### Фаза 4: Выделение LLM Proxy
- Общая обёртка над провайдерами
- Chat, Research, Uploads вызывают LLM Proxy
- **Риск:** Высокий. Много мест используют LLM напрямую.
### Фаза 5: Выделение Research Service
- Researcher + actions в отдельный сервис
- Chat вызывает Research по одному endpoint «проведи исследование»
- **Риск:** Высокий. Тесная связь с Session, streaming.
### Фаза 6: Gateway + развязка Frontend
- Frontend только UI, все вызовы через Gateway
- Gateway — тонкий роутер без бизнес-логики
- **Риск:** Средний. Рефакторинг API routes.
---
## 7. Ключевые вызовы и риски
1. **Streaming:** Текущий chat stream идёт от SearchAgent до клиента. При разбиении нужно решить: стримить через Gateway (proxy) или от Chat Service напрямую (WebSocket/SSE через Gateway как туннель).
2. **Session/state:** SessionManager — in-memory. При масштабировании Chat Service нужен Redis или аналог для shared state.
3. **Латентность:** Каждый HTTP hop добавляет ~1050ms. Research делает много итераций — если Research в отдельном сервисе, round-trips умножаются.
4. **Embedding в Uploads:** Uploads нужна embedding модель. Варианты: вызывать LLM Proxy, или держать тяжёлую модель (transformers) внутри Uploads.
5. **Конфигурация:** Сейчас config.json на диске. При микросервисах — централизованный config (env, vault, или Storage Service).
---
## 8. Минимальный MVP разбиения
Если цель — не полный microservices, а **модульность и возможность деплоить части отдельно**:
| Сервис | Код | Отдельный процесс |
|---------------|-----------------------------|-------------------|
| **search-api**| `searxng.ts` + route | Да (отдельный порт) |
| **uploads-api**| `uploads/` + route | Да |
| **chat** | Всё остальное (agents, LLM, DB) | Один процесс |
| **frontend** | Next.js UI | Отдельно (static/SSR) |
Так получается 34 контейнера вместо 1, но без глубокой декомпозиции логики.
---
## 9. Следующие шаги
1. Определить: полное разбиение или MVP.
2. Выбрать стек для каждого сервиса (Node.js/Fastify, Next.js API routes, Go и т.д.).
3. Описать контракты API (OpenAPI/JSON Schema) для межсервисного взаимодействия.
4. Настроить docker-compose для локальной разработки.
5. Внедрить общие пакеты (shared-types, shared-utils) в монорепо.
6. Начать с Фазы 1 (Search Service) как пилот.