Geo Device Service: - Новый сервис определения геопозиции, устройства, браузера - geoip-lite, ua-parser-js, CORS - GET/POST /api/context Frontend: - /api/geo-context — прокси к geo-device, fallback при недоступности - geoDevice.ts — fetchContextWithClient, fetchContextWithGeolocation - Weather: геопозиция через geo-device + GeoJS + ipwhois fallback - Weather API: поддержка city (геокодинг Open-Meteo) - Discover: вкладка GooSeek по умолчанию Документация: - MICROSERVICES.md — секция 3.9 Geo Device Service Co-authored-by: Cursor <cursoragent@cursor.com>
372 lines
20 KiB
Markdown
372 lines
20 KiB
Markdown
# Архитектура микросервисов 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`.
|
||
|
||
---
|
||
|
||
## 4. Матрица зависимостей
|
||
|
||
| Сервис | От кого получает вызовы | Кого вызывает |
|
||
|-------------|----------------------------------|----------------------------------|
|
||
| Gateway | Frontend | Chat, Storage, Uploads, Search |
|
||
| 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/ # Геопозиция, устройство, браузер
|
||
│ ├── 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 добавляет ~10–50ms. 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) |
|
||
|
||
Так получается 3–4 контейнера вместо 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) как пилот.
|