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>
20 KiB
Архитектура микросервисов 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 ServicePOST /api/search→ Chat Service (API mode)GET/POST /api/chats/*→ Storage ServicePOST /api/uploads→ Uploads ServiceGET/POST /api/providers/*→ Config + LLM ProxyGET/POST /api/config/*→ Storage Service (config)POST /api/suggestions→ Chat ServicePOST /api/images→ Chat/Media ServicePOST /api/videos→ Chat/Media ServiceGET /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— SearchAgentsrc/lib/agents/search/api.ts— APISearchAgentsrc/lib/agents/search/classifier.tssrc/lib/agents/search/widgets/— executor + виджеты (weather, stock, calc)src/lib/prompts/search/— classifier, writer, researcher promptssrc/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, погода) — можно вынести в отдельный сервис
Поток данных:
- Классификация (LLM)
- Параллельно: Research Service + виджеты
- Генерация ответа (LLM)
- Сохранение в Storage (через Gateway или напрямую)
3.4 Research Service
Назначение: Выполнение исследовательского цикла — tools (web, academic, social, scrape, uploads).
Исходники:
src/lib/agents/search/researcher/— Researcher, ActionRegistrysrc/lib/agents/search/researcher/actions/— webSearch, academicSearch, socialSearch, scrapeURL, uploadsSearch, plan, donesrc/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— UploadManagersrc/lib/uploads/store.ts— UploadStore (поиск по эмбеддингам)src/lib/utils/splitText.ts,computeSimilarity.ts- Зависимости: pdf-parse, officeparser
API:
POST /upload— multipart, возвращает fileIdsPOST /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, drizzlesrc/lib/config/— ConfigManager, modelProviders
API:
GET/POST/PUT/DELETE /chatsGET/POST/PUT/DELETE /chats/:idGET/POST/PUT/DELETE /messagesGET/POST /configGET/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— embeddingGET /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. Ключевые вызовы и риски
-
Streaming: Текущий chat stream идёт от SearchAgent до клиента. При разбиении нужно решить: стримить через Gateway (proxy) или от Chat Service напрямую (WebSocket/SSE через Gateway как туннель).
-
Session/state: SessionManager — in-memory. При масштабировании Chat Service нужен Redis или аналог для shared state.
-
Латентность: Каждый HTTP hop добавляет ~10–50ms. Research делает много итераций — если Research в отдельном сервисе, round-trips умножаются.
-
Embedding в Uploads: Uploads нужна embedding модель. Варианты: вызывать LLM Proxy, или держать тяжёлую модель (transformers) внутри Uploads.
-
Конфигурация: Сейчас 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. Следующие шаги
- Определить: полное разбиение или MVP.
- Выбрать стек для каждого сервиса (Node.js/Fastify, Next.js API routes, Go и т.д.).
- Описать контракты API (OpenAPI/JSON Schema) для межсервисного взаимодействия.
- Настроить docker-compose для локальной разработки.
- Внедрить общие пакеты (shared-types, shared-utils) в монорепо.
- Начать с Фазы 1 (Search Service) как пилот.