architect/standards/local-ai/02_ARCHITECTURE.md

02 — АРХИТЕКТУРА: Полный стек

Навигация: ← Теория | README | Установка →


Содержание

  1. Обзор стека
  2. Компоненты
  3. Поток данных
  4. Слои между интерфейсом и матрицей
  5. Порты и сеть
  6. Варианты сборки

1. Обзор стека

                    ПОЛЬЗОВАТЕЛЬ
                        │
                        │ браузер / мобильный
                        ▼
              ┌─────────────────┐
              │   OPEN WEBUI    │  ← Интерфейс (порт 3000)
              │  (React + API)  │    как ChatGPT, локально
              └────────┬────────┘
                       │ HTTP POST /chat
                       ▼
              ┌─────────────────┐
              │   AI ROUTER     │  ← Мозг оркестрации (порт 8000)
              │   (FastAPI)     │    выбирает модель, добавляет
              │                 │    системный промпт, память
              └──┬──────────┬───┘
                 │          │
         ┌───────┘          └──────────┐
         ▼                             ▼
┌────────────────┐           ┌─────────────────┐
│    OLLAMA      │           │    CHROMADB     │
│  (порт 11434)  │           │  (порт 8001)    │
│                │           │                 │
│  Движок LLM   │           │  Векторная БД   │
│  llama.cpp    │           │  Долгая память  │
│  внутри       │           │  RAG            │
└───────┬────────┘           └─────────────────┘
        │
        │ загружает
        ▼
┌────────────────┐
│  МОДЕЛИ QWEN   │
│                │
│  qwen2.5:14b   │  ← основная
│  qwen2.5-coder │  ← для кода
│  nomic-embed   │  ← для RAG
└────────────────┘
        │
        │ умножение на матрицы
        ▼
┌────────────────┐
│  ЖЕЛЕЗО (CPU)  │
│                │
│  RAM: веса     │  ← 7-40GB для модели
│  RAM: KV-cache │  ← 1-32GB для контекста
└────────────────┘

2. Компоненты

Open WebUI — интерфейс пользователя

Что делает:
- Отображает чат (React)
- Управляет историей разговоров
- Позволяет загружать файлы (RAG)
- Управляет системными промптами
- Переключает модели

Что НЕ делает:
- Не генерирует ответы (это Ollama)
- Не хранит долгую память (это ChromaDB)
- Не выбирает модель умно (это Router)

Связь: HTTP → Ollama API (напрямую или через Router)

AI Router — оркестратор

Что делает:
- Принимает запрос от WebUI
- Определяет тип запроса (код/текст/анализ)
- Выбирает нужную модель
- Добавляет системный промпт
- Загружает долгую память из ChromaDB
- Отправляет в Ollama
- Возвращает ответ

Это главный "мозг" — здесь живёт логика поведения

Ollama — движок инференса

Что делает:
- Загружает модели в RAM
- Управляет KV-cache
- Запускает llama.cpp (C++ движок)
- Отдаёт OpenAI-совместимый API
- Выгружает/загружает модели по запросу

API:
POST /api/chat      ← диалог
POST /api/generate  ← одиночный промпт
POST /api/embed     ← получить embedding вектор
GET  /api/tags      ← список загруженных моделей

ChromaDB — долгая память

Что делает:
- Хранит векторные представления текстов
- Ищет похожие тексты по смыслу (не по ключевым словам)
- Обеспечивает RAG (Retrieval Augmented Generation)

Как используется:
1. Важное из разговора → embed → сохранить в ChromaDB
2. При новом вопросе → embed вопрос → найти похожее
3. Найденное → добавить в контекст → ответ с памятью

Пример:
Ты рассказал что работаешь с Drupal.
Через неделю: "добавь блок на сайт" →
ChromaDB находит "работает с Drupal" →
Router добавляет в контекст →
Модель отвечает про Drupal, не спрашивает CMS

Модели Qwen

qwen2.5:14b         — основная умная модель
qwen2.5-coder:7b    — специализирована на коде
qwen2.5:72b         — для сложных задач (нужно 40GB+ RAM)
nomic-embed-text    — только для embeddings, не для чата
                      маленькая (274MB), быстрая

Все модели:
- Скачиваются через Ollama
- Хранятся в ~/.ollama/models/
- Загружаются в RAM при первом запросе
- Выгружаются если нет обращений (настраивается)

3. Поток данных

Простой запрос (без памяти)

1. Пользователь: "напиши функцию на python"
        
2. Open WebUI отправляет:
   POST http://router:8000/chat
   {
     "prompt": "напиши функцию на python",
     "history": [...]
   }
        
3. Router.pick_model():
   "python"  code keyword  qwen2.5-coder:7b
        
4. Router.build_messages():
   [
     {"role": "system", "content": SYSTEM_PROMPT},
     {"role": "user", "content": "напиши функцию на python"}
   ]
        
5. Router  Ollama:
   POST http://ollama:11434/api/chat
   {"model": "qwen2.5-coder:7b", "messages": [...]}
        
6. Ollama загружает модель в RAM (если не загружена)
   Прогоняет через 28 слоёв трансформера
   Генерирует токены один за другим
   Стримит ответ
        
7. Router возвращает в WebUI:
   {"response": "def sort_list...", "model_used": "qwen2.5-coder:7b"}
        
8. Open WebUI отображает ответ

Запрос с памятью (RAG)

1. Пользователь: "как у нас называется главная таблица?"
        
2. Router получает запрос
        
3. Router.embed("как у нас называется главная таблица")
    [0.234, -0.891, 0.445, ...]  (вектор 768 чисел)
        
4. ChromaDB.search(vector, top_k=3):
   Ищет ближайшие по косинусному расстоянию
   Находит: "главная таблица называется orders, создана 2026-01-15"
        
5. Router добавляет в промпт:
   "Из памяти: главная таблица называется orders"
   + исходный вопрос
        
6. Qwen отвечает: "Главная таблица у вас называется orders"
   (без поиска в интернете, из локальной памяти)

4. Слои между интерфейсом и матрицей

ИНТЕРФЕЙС
(Open WebUI)
    │
    │  Слой 1: HTTP/WebSocket
    │  Протокол: REST API (OpenAI-совместимый)
    │  Данные: JSON {messages, model, stream}
    │
    ▼
РОУТЕР
(FastAPI)
    │
    │  Слой 2: Логика выбора модели
    │  Что происходит:
    │  - Анализ ключевых слов → pick_model()
    │  - Загрузка системного промпта
    │  - Добавление истории разговора
    │
    │  Слой 3: Работа с памятью
    │  Что происходит:
    │  - Embed запроса → nomic-embed-text
    │  - Поиск в ChromaDB
    │  - Инжекция в контекст
    │
    │  Слой 4: Формирование промпта
    │  Что происходит:
    │  - system prompt + memory + history + question
    │  - Форматирование в messages[]
    │
    ▼
OLLAMA
(HTTP сервер)
    │
    │  Слой 5: Управление моделями
    │  Что происходит:
    │  - Выбор файла модели (.gguf)
    │  - Загрузка в RAM
    │  - Управление KV-cache
    │
    │  Слой 6: llama.cpp runtime
    │  Что происходит:
    │  - Токенизация текста
    │  - Загрузка весов из RAM
    │  - Вычисления на CPU (AVX2/AVX512)
    │  - KV-cache update
    │  - Sampling следующего токена
    │
    ▼
ЖЕЛЕЗО
(CPU + RAM)
    │
    │  Слой 7: Физика
    │  Что происходит:
    │  - RAM → CPU cache → регистры
    │  - SIMD инструкции (AVX2)
    │  - Матричное умножение FP16/INT4
    │  - Результат обратно в RAM
    │
    ▼
МАТРИЦА
(веса модели в RAM)
7GB для Qwen2.5-14B Q4

5. Порты и сеть

Сервис          Порт    Кто обращается
─────────────────────────────────────────────────────
Open WebUI      3000    Браузер пользователя
AI Router       8000    Open WebUI, прямые клиенты
Ollama          11434   Router, Open WebUI напрямую
ChromaDB        8001    Router

Внутренняя сеть Docker: ai-network
Все контейнеры видят друг друга по имени сервиса:
  http://ollama:11434
  http://chromadb:8001
  http://router:8000

Хост:
  http://host.docker.internal:11434  ← если Ollama на хосте

6. Варианты сборки

Минимальная (0 кода, 0 конфигурации)

Ollama + Open WebUI
Время установки: 5 минут
Что получаем: чат с локальной моделью
Чего нет: умный роутер, долгая память, RAG с файлами

Когда достаточно:
- Личный ассистент для разовых задач
- Тестирование моделей
- Прототипирование

Стандартная (этот документ)

Ollama + Open WebUI + Router + ChromaDB
Время установки: 30 минут
Что получаем: полноценная система

Когда нужна:
- Постоянный ассистент
- Работа с документами (RAG)
- Нужна долгая память
- Разные модели для разных задач

Расширенная (custom)

+ Агент с инструментами (поиск, код, файлы)
+ Fine-tuning на своих данных
+ Несколько пользователей
+ Логирование и аналитика

Когда нужна:
- Команда > 1 человека
- Специализированные задачи
- Хочешь автоматизацию

Следующий документ: 03_INSTALL.md