Как запустить локально LLM?

Цель и необходимость

Как только я решил свои мысли записывать на какой-то внешний носитель, так я сразу начал осознавать проблему поиска в этих записях. Для дальнейшей работы нам необходимо дать несколько определений:

  1. Морфологический поиск связан с учётом различных форм слов при поиске. Есть два подхода: либо учитывать все формы, либо вырезать корень слова и искать только по нему. Второй способ называется stemming, он отличается быстротой и простотой, но имеет проблемы со словами, у которых корень изменчив (бег — бежать, расти — прирост, лев — львица).

  2. Семантический поиск — способ и технология поиска информации, основанная на использовании контекстного (смыслового) значения запрашиваемых фраз, вместо словарных значений отдельных слов или выражений при поисковом запросе.

Некоторые особенности семантического поиска:

  • Учитывается информационный контекст, местонахождение и цель поиска пользователя, словесные вариации, синонимы, обобщённые и специализированные запросы, язык запроса и другие особенности.

  • Цель такого поиска — определять особенности пользователя и предоставлять ему наиболее релевантные результаты.

  • Ряд крупных поисковых систем, например Google и Bing, используют некоторые элементы семантического поиска, но не являются таковыми в чистом виде.

Эмбеддинги (embeddings) — это численные представления текста или слов в виде векторов, которые отражают их семантическое значение. В контексте семантического поиска, эмбеддинги используются для преобразования текста запроса и документов в числовой формат, который можно анализировать на наличие схожести по смыслу.

  • Семантический поиск использует эмбеддинги для сравнения семантической близости между запросом пользователя и документами. Если эмбеддинг запроса близок к эмбеддингу документа, это означает, что оба имеют схожий смысл.

  • Эмбеддинги позволяют системе понимать не только отдельные слова, но и контекст, что делает семантический поиск более умным и точным по сравнению с традиционными методами поиска (морфологический поиск).

Морфологический поиск — часть процесса семантического поиска, связанный с обработкой лингвистических особенностей запросов. Чаще всего системы предлагают поиск по вхождению символов в текст документов, а это может вести к не полным результатам поиска. Проблема становится актуальной, когда документов много, как и текста в них. На данный момент у меня в базе знаний более 2800 файлов и искать становится все сложнее и сложнее.

  1. LLM (Large Language Model) — это一большая языковая модель, обученная на огромных объемах текстовых данных. Эти модели способны понимать и генерировать человеческий язык, а также выполнять задачи обработки естественного языка (NLP — Natural Language Processing).

С течением времени при работе с базой знаний начал замечать, что все чаше и чаще хочется обобщать большие тексты. Конечно же я начал некоторые тексты выгружать в публичные LLM. Но параллельно начал осознавать, что такие выгрузки нарушают приватность моей жизни.

В итоге какие проблемы имеем:

  1. Поиск и возможность искать по смыслу.

  2. Приватность производимых операций. Лично для меня выходом стало появление LLM, которые возможно свободно скачивать, устанавливать и использовать в своих проектах.

В статье будем устанавливать LLM на сервер и давать возможность нашим сервисам взаимодействовать с ней.

Virtual machine

Я использую VM в proxmox, в настройках которой уже добавления видеокарта. Установку драйверов NVIDIA не описываю, так как это описано в соответствующей статье (см. ссылки в самом низу). Помимо этого необходимо процессор VM указать как host. Важным в данном контексте для нас является поддержка Advanced Vector Extensions (AVX), так как позволяет быстрее обрабатывать данные в рамках GPU. Проверить поддержку AVX возможно (вывод должен быть как в тексте ниже):

$ grep avx /proc/cpuinfo
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm rep_good nopl cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy svm cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw perfctr_core ssbd ibpb vmmcall fsgsbase tsc_adjust bmi1 avx2 smep bmi2 rdseed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 clzero xsaveerptr virt_ssbd arat npt lbrv nrip_save tsc_scale vmcb_clean flushbyasid pausefilter pfthreshold v_vmsave_vmload vgif overflow_recov succor arch_capabilities
.....

doсker-compose

Для начала нам необходимо нарисовать архитектуру docker-compose.yml сервисов и работу с нашим оборудованием, чтобы понимать на верхнем уровне:

        graph TD;
    subgraph docker-compose.yml;
        A[anythingllm] -->| зависит от | B[ollama];
        A[anythingllm] -->| зависит от | C[postgres-vector];
        D[NVIDIA Container Toolkit и драйверы видеокарты] -->| используется для | B[ollama];
    end

    B[ollama] -.->| API для LLM и эмбеддингов | A[anythingllm];

    C[postgres-vector] -.->| векторные операции через pgvector | A[anythingllm];

    E[Хостовая машина] --> D[NVIDIA Container Toolkit];
    F[GPU устройства] --> D[NVIDIA Container Toolkit];

    style B fill:#f9f,stroke:#333,stroke-width:2px;
    style C fill:#ccf,stroke:#333,stroke-width:2px;
    

Диаграмма показывает основные компоненты системы:

  1. Сервис anythingllm зависит от:

  2. Для корректной работы сервисов ollama требуется:

    • Установленный NVIDIA Container Toolkit на хостовой машине

    • Драйверы NVIDIA и GPU-устройства

  3. Система предоставляет API для взаимодействия с LLM (от ollama) и веб-интерфейс (от anythingllm).

Теперь мы готовы создать файлы для запуска docker-compose.yml:

  1. touch .env docker-compose.yml Dockerfile init-vector.sql

  2. mkdir config_{anythingllm,ollama,postgres}

Содержимое .env (переменные с секретами):

POSTGRES_DB=<your database name>
POSTGRES_USER=<your vector user>
POSTGRES_PASSWORD=<your postgres password>
POSTGRES_PORT=<tyour postgres port>
JWT_SECRET=<your jwt secret>

Содержимое Dockerfile (сборка postgres с расширением pgvector):

FROM postgres:17-bookworm

# Устанавливаем необходимые пакеты
RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    git \
    postgresql-server-dev-17 \
    && rm -rf /var/lib/apt/lists/*

# Клонируем и устанавливаем pgvector
RUN git clone https://github.com/pgvector/pgvector.git \
    && cd pgvector \
    && make DISABLE_JIT=1 \
    && make install \
    && cd .. \
    && rm -rf pgvector

# Создаем директорию для скриптов инициализации
RUN mkdir -p /docker-entrypoint-initdb.d

# Копируем скрипт инициализации
COPY init-vector.sql /docker-entrypoint-initdb.d/

EXPOSE 5432

CMD ["postgres"]

Содержимое init-vector.sql (установка расширения pgvector):

-- Включаем расширение pgvector
CREATE EXTENSION IF NOT EXISTS vector;

-- Настройки для лучшей производительности векторов
ALTER SYSTEM SET shared_preload_libraries = 'vector';
ALTER SYSTEM SET max_connections = 200;
ALTER SYSTEM SET shared_buffers = '1GB';
ALTER SYSTEM SET work_mem = '50MB';

-- Перезагрузите конфигурацию
SELECT pg_reload_conf();

Содержимое docker-compose.yml:

services:
  ollama:
    image: ollama/ollama:latest
    container_name: llm
    restart: unless-stopped
    volumes:
      - ./config_ollama:/root
    environment:
      - OLLAMA_KEEP_ALIVE=60 minutes
      - OLLAMA_FLASH_ATTENTION=1
      - OLLAMA_HOST=0.0.0.0
      - LD_LIBRARY_PATH=/opt/cuda/lib64:/usr/local/cuda/lib64
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=all
      - CUDADIR=/usr/local/cuda
      - CUDA_HOME=/usr/local/cuda
      - FORCE_CUDA=1
      - CUDA_VISIBLE_DEVICES=0
      - CUDA_LAUNCH_BLOCKING=1
    ports:
      - "11434:11434"
    devices:
      - /dev/dri:/dev/dri
      - /dev/nvidiactl:/dev/nvidiactl
      - /dev/nvidia0:/dev/nvidia0
    runtime: nvidia

  postgres-vector:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: postgres-vector
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - ./config_postgres:/var/lib/postgresql/data
    env_file: .env
    ports:
      - "${POSTGRES_PORT:-5432}:5432"

  anythingllm:
     image: mintplexlabs/anythingllm:latest
     container_name: anythingllm
     restart: unless-stopped
     ports:
       - "3001:3001"
     environment:
       - STORAGE_DIR=/app/server/storage
       - LLM_PROVIDER=ollama
       - OLLAMA_BASE_PATH=http://ollama:11434
       - OLLAMA_MODEL_PREF=deepseek-r1:14b
       - OLLAMA_MODEL_TOKEN_LIMIT=4096
       - EMBEDDING_ENGINE=ollama
       - EMBEDDING_BASE_PATH=http://ollama:11434
       - EMBEDDING_MODEL_PREF=bge-m3:latest
       - EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
       - JWT_SECRET=${JWT_SECRET}
       # Аудио функции
       - WHISPER_PROVIDER=local
       - TTS_PROVIDER=native
       # Векторная база данных - PostgreSQL с pgvector
       - VECTOR_DB=pgvector
       - PGVECTOR_CONNECTION_STRING=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres-vector:${POSTGRES_PORT}/${POSTGRES_DB}
       - PGVECTOR_TABLE_NAME=vector_embeddings
       - PGVECTOR_EMBEDDING_DIMENSION=1536
       # Дополнительные настройки PostgreSQL
       - PGVECTOR_SSL=false
       - PGVECTOR_SCHEMA=public
     env_file: .env
     volumes:
       - ./config_anythingllm:/app/server/storage
     depends_on:
       - ollama
       - postgres-vector

Сначала запускаем только ollama: docker-compose up -d ollama и проверяем какие модели доступны: docker exec -it llm ollama list. В самом начале должен быть пустой список, но мы можем это легко исправить:

Название модели

Команда скачивания

Описание функциональности

1

bge-m3

docker exec -it llm ollama pull bge-m3:latest

Модель для генерации векторных представлений текста (embedding model).

2

qwen3:8b

docker exec -ит llm ollama pull qwen3:8b

Модель необходима для интеграции с умным домом.

3

LLaVA

docker exec -it llm ollama pull LLaVA:latest

Модель необходима для объяснения изображений на картинке.

4

deepseek-r1

docker exec -it llm ollama pull deepseek-r1:8b

Модель для работы в чат-режиме (основной вариант).

5

docker exec -it llm ollama pull deepseek-r1:14b

Дополнительный вариант модели с большей производительностью.

Запускаем наши сервисы через docker-compose down && docker-compose up -d.

Умный дом

Для интеграции с умным домом (Homeassistant):

  1. Настройки -> Устройства и службы -> Добавить интеграцию -> Набираем ollama -> Вставляем адрес http://<your local API>:11434 -> Выбираем модель qwen3:8b (downloaded).

  2. Настройки -> Голосовые ассистенты -> Ассистенты -> Добавить ассистента -> Добавляем Ollama Conversation и делаем его предпочитаемым через 3 точки возле названия ассистента.

  3. Настройки -> Голосовые ассистенты -> Доступ к объектам -> Проверяем/Смотрим какие у нас есть возможности, при необходимости редактируем. Теперь через значок диалога в верхнем правом углу основного интерфейса вызываем диалог и проверяем как все работает.

Остался 1 шаг до своего голосового ассистента :)

Браузер

Для работы с локальной LLM прямо в браузере (Mozilla Firefox) устанавливаем page-assist. Идем в настройки расширения и добавляем адрес для работы: Начинаем чат:

IDE

Лично я использую VSCode, поэтому все настройки далее будут для этой IDE. Устанавливаем расширение Continue :

code --install-extension Continue.continue

Вставляю настройки ~/.continue/config.yaml:

name: Local Assistant
version: 1.0.0
schema: v1
models:
  - name: deepseek-r1:8b
    provider: openai
    model: deepseek-r1:8b
    apiBase: http://<your address>/v1
    apiKey: "sk-no-key-needed"
    temperature: 0.7
    maxTokens: 2048
    contextWindow: 8192
    forcePrompt: true

  - name: deepseek-r1:14b
    provider: openai
    model: deepseek-r1:14b
    apiBase: http://<your address>/v1
    apiKey: "sk-no-key-needed"
    temperature: 0.7
    maxTokens: 2048
    contextWindow: 8192
    forcePrompt: true
assistant:
  - name: deepseek-r1:8b
    models:
      - deepseek
  - name: deepseek-r1:14b
    models:
      - deepseek
allowAnonymousTelemetry: false
tabAutocompleteModel:
  title: deepseek-r1:8b
  provider: openai
  model: deepseek-r1:8b
context:
  - provider: code
  - provider: docs
  - provider: diff
  - provider: terminal
  - provider: problems
  - provider: folder
  - provider: codebase

Проверяем работу:

Obsidian

Ищем в community plugins Copilot. Действия:

  1. Переходим в модели и добавляем модель в «Chat Models» (deepseek-r1:8b и deepseek-r1:14b) и «Embedding Models» (ollama_bge-m3). Сам процесс добавления моделей похож на добавление моделей в другие сервисы выше, поэтому этот процесс не описываю.

  2. Как выяснилось моя база знаний не подходит для локальной векторризации, которая существует в этом плагине. Там важно, что в случае деление на партиции (для больших объемов данных), необходимо чтобы первая партиция была не более 500 мб. При различных комбинациях у меня не удалось этого достичь. В итоге я пользуюсь внутри obsidian только чатом и возможностью добавлять в контекст конкретную заметку. Для ускорения работы сделал/изменил шаблоны промтов, которые вызываются через /. |400

Anythingllm

После запуска docker-compose зайти в интерфейс возможно через http://<your_ip>:3001. Все необходимые настройки уже должны примениться. Первым делом нам необходимо проиндексировать нашу базу знаний и перевести ее в векторный вид, чтобы далее возможно было проводить с этими векторами операции. Нюанс индексирования таков, что файлы добавляются только и это становится причиной появления «дубликатов». Для себя делаю выгрузки только с новыми файлами или измененными, далее планирую удалять все файлы и заново их индексировать, чтобы актуализировать информацию. Сама операция индексации заняла у меня несколько дней.

Open settings -> Agent Skills -> Добавляем навыки. Рекомендую добавить web-search (минимум), чтобы наша LLM могла искать информацию в Интернете.

Теперь можем искать в нашей базе знаний и при необходимости в интернете.

Итог

Теперь у нас во всех наших инструментах есть доступ к локальной модели LLM и мы можем это использовать в своей работе и жизни в частности.

Ссылки:

  1. Как запустить свою базу знаний?

  2. Зачем нужен собственный сервер в 2025?

  3. Как установить Proxmox?

  4. Как установить Docker?

  5. Как запустить прокси-сервер для сервисов?

  6. Как установить драйвера NVIDIA в Linux?

  7. Как запустить сервис по управлению умным домом?