Перейти к содержанию

▍PrivateGPT

Запуск локального PrivateGPT в Docker Swarm

Вводная часть

Для запуска PrivateGPT нам понадобится:

  1. Docker Swarm
  2. Traefik

Директория с моделями:

/docker/data/private-gpt/models


Итак, приступим. Склонируем репозиторий:

┌─( root@swarm-manager1 ) - ( 23 files,  ) - ( ~/src )
└─> git clone https://github.com/imartinez/privateGPT.git
Cloning into 'privateGPT'...
remote: Enumerating objects: 1510, done.
remote: Counting objects: 100% (23/23), done.
remote: Compressing objects: 100% (22/22), done.
remote: Total 1510 (delta 2), reused 15 (delta 0), pack-reused 1487
Receiving objects: 100% (1510/1510), 1.69 MiB | 4.10 MiB/s, done.
Resolving deltas: 100% (822/822), done.

Переходим в директорию:

cd privateGPT
и создаём файл Dockerfile c таким кодом:

nano Dockerfile
### IMPORTANT, THIS IMAGE CAN ONLY BE RUN IN LINUX DOCKER
### You will run into a segfault in mac
FROM python:3.11.6-slim-bookworm as base

# Install poetry
RUN pip install pipx
RUN python3 -m pipx ensurepath
RUN pipx install poetry
ENV PATH="/root/.local/bin:$PATH"
ENV PATH=".venv/bin/:$PATH"

# Dependencies to build llama-cpp
RUN apt update && apt install -y \
  libopenblas-dev\
  ninja-build\
  build-essential\
  pkg-config\
  wget

# https://python-poetry.org/docs/configuration/#virtualenvsin-project
ENV POETRY_VIRTUALENVS_IN_PROJECT=true

FROM base as dependencies
WORKDIR /home/worker/app
COPY pyproject.toml poetry.lock ./

RUN poetry install --with local
RUN poetry install --with ui

FROM base as app
RUN pip install pipx

ENV PYTHONUNBUFFERED=1
ENV PORT=8080
EXPOSE 8080

# Prepare a non-root user
RUN adduser worker
WORKDIR /home/worker/app

RUN mkdir local_data; chown worker local_data
RUN mkdir models; chown worker models
COPY --chown=worker --from=dependencies /home/worker/app/.venv/ .venv
COPY --chown=worker private_gpt/ private_gpt
COPY --chown=worker fern/ fern
COPY --chown=worker *.yaml *.md ./
COPY --chown=worker scripts/ scripts

ENV PYTHONPATH="$PYTHONPATH:/private_gpt/"

USER worker
ENV PATH="/home/worker/.local/bin:$PATH"
COPY pyproject.toml poetry.lock ./
RUN pipx install poetry
ENTRYPOINT python -m private_gpt

Изменим файл settings.yaml, добавив следующий параметр:

nano settings.yaml
qdrant:
    url: "http://qdrant:6333"

Сборка контейнера

docker build -t daffin/gpt .

Возможные ошибки и их решение

Приведу несоклько примеров ошибок, с которыми пришлось столнуться. Например, если после сборки попробовать запустить котейнер, то можем увидеть:

10:03:37.474 [INFO    ]                     httpx - HTTP Request: POST http://qdrant:6333/collections/make_this_parameterizable_per_api_call/points/search "HTTP/1.1 404 Not Found"
Traceback (most recent call last):
  File "/home/worker/app/.venv/lib/python3.11/site-packages/gradio/queueing.py", line 495, in call_prediction
    output = await route_utils.call_process_api(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
qdrant_client.http.exceptions.UnexpectedResponse: Unexpected Response: 404 (Not Found)
Raw response content:
b'{"status":{"error":"Not found: Collection `make_this_parameterizable_per_api_call` doesn\'t exist!"},"time":0.000071683}'

Как видим в qdrant не создана коллекция с именем make_this_parameterizable_per_api_call. Ну что же, создаём:

root@f13b7f90a63c:/# curl -X PUT 'http://qdrant:6333/collections/make_this_parameterizable_per_api_call' \
    -H 'Content-Type: application/json' \
    --data-raw '{
        "vectors": {
          "size": 384,
          "distance": "Dot"
        }
    }'
{"result":true,"status":"ok","time":1.054573524}

Столкнулся ещё с такой ошибкой:

13:45:34.751 [INFO    ] private_gpt.settings.settings_loader - Starting application with profiles=['default', 'docker']
There was a problem when trying to write in your cache folder (/nonexistent/.cache/huggingface/hub). You should set the environment variable TRANSFORMERS_CACHE to a writable directory.
13:45:40.220 [WARNING ]                matplotlib - Matplotlib created a temporary cache directory at /tmp/matplotlib-ji4jk7xx because the default path (/nonexistent/.config/matplotlib) is not a writable directory; it is highly recommended to set the MPLCONFIGDIR environment variable to a writable directory, in particular to speed up the import of Matplotlib and to better support multiprocessing.
13:45:40.564 [INFO    ]   matplotlib.font_manager - generated new fontManager
13:45:41.699 [INFO    ] private_gpt.components.llm.llm_component - Initializing the LLM in mode=local
Traceback (most recent call last):
  File "/home/worker/app/.venv/lib/python3.11/site-packages/injector/__init__.py", line 798, in get
    return self._context[key]
           ~~~~~~~~~~~~~^^^^^
KeyError: <class 'private_gpt.ui.ui.PrivateGptUi'>


...

  File "/home/worker/app/.venv/lib/python3.11/site-packages/llama_index/llms/llama_cpp.py", line 119, in __init__
    raise ValueError(
ValueError: Provided model path does not exist. Please check the path or provide a model_url to download.

Если я правильно понял, то необходимо до запуска самого PrivateGPT скачать нужные модели. Можно скачать вручную в директорию /docker/data/private-gpt/models:

wget "https://huggingface.co/TheBloke/Mistral-7B-Instruct-v0.2-GGUF/resolve/main/mistral-7b-instruct-v0.2.Q4_K_M.gguf?download=true" -O mistral-7b-instruct-v0.2.Q4_K_M.gguf

или лучше запустить скрипт, который автоматически скачает все необходимые файлы, именно для этого пришлось править файл Dockerfile:

Для начала зайдём внутрь контейнера и выполним ряд команд:

┌─( root@swarm-worker1 ) - ( 24 files,  ) - ( ~/src/privateGPT )
└─> docker run --entrypoint "" -it --rm -v /docker/data/private-gpt/models:/home/worker/app/models enlr/gpt bash

worker@b21f9c89e085:~/app$ source .venv/bin/activate

(private-gpt-py3.11) worker@b21f9c89e085:~/app$ poetry run python scripts/setup
06:56:48.092 [INFO    ] private_gpt.settings.settings_loader - Starting application with profiles=['default']
Downloading embedding BAAI/bge-small-en-v1.5
README.md: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 94.8k/94.8k [00:00<00:00, 9.51MB/s]
.gitattributes: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.52k/1.52k [00:00<00:00, 14.4MB/s]
config_sentence_transformers.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 124/124 [00:00<00:00, 1.53MB/s]
modules.json: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 349/349 [00:00<00:00, 3.80MB/s]
config.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 743/743 [00:00<00:00, 5.55MB/s]
1_Pooling/config.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 190/190 [00:00<00:00, 1.42MB/s]
special_tokens_map.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 125/125 [00:00<00:00, 813kB/s]
tokenizer_config.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 366/366 [00:00<00:00, 4.24MB/s]
sentence_bert_config.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 52.0/52.0 [00:00<00:00, 315kB/s]
vocab.txt: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 232k/232k [00:00<00:00, 1.71MB/s]
tokenizer.json: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 711k/711k [00:00<00:00, 1.75MB/s]
pytorch_model.bin: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 134M/134M [00:21<00:00, 6.14MB/s]
model.onnx: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 133M/133M [00:33<00:00, 3.97MB/s]
model.safetensors: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 133M/133M [00:33<00:00, 3.96MB/s]
Fetching 14 files: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 14/14 [00:34<00:00,  2.45s/it]
Embedding model downloaded!█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 133M/133M [00:33<00:00, 5.58MB/s]
Downloading LLM model-q4_K.gguf
model-q4_K.gguf: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4.37G/4.37G [06:16<00:00, 11.6MB/s]
LLM model downloaded!
Downloading tokenizer mistralai/Mistral-7B-Instruct-v0.2
tokenizer_config.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.46k/1.46k [00:00<00:00, 14.5MB/s]
tokenizer.model: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 493k/493k [00:00<00:00, 5.66MB/s]
tokenizer.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.80M/1.80M [00:00<00:00, 5.94MB/s]
special_tokens_map.json: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 72.0/72.0 [00:00<00:00, 576kB/s]
Tokenizer downloaded!
Setup done

Можно уже запустить GPT c моделями, указанными по умолчанию, но можно немного изменить файл settings.yaml и задать русскую модель сайгу изменив переменные llm_hf_repo_id и llm_hf_model_file:

local:
  prompt_style: "mistral"
  llm_hf_repo_id: IlyaGusev/saiga_mistral_7b_gguf
  llm_hf_model_file: model-q4_K.gguf
  embedding_hf_model_name: BAAI/bge-small-en-v1.5

Пример docker-compose

nano gpt.yml
version: '3.9'

services:
  private-gpt:
    image: registry.enlr.ru/privategpt:latest
    environment:
      - PORT=8080
      - PGPT_PROFILES=docker
      - PGPT_MODE=local
      - PGPT_HF_REPO_ID=IlyaGusev/saiga_mistral_7b_gguf
      - PGPT_HF_MODEL_FILE=model-q4_K.gguf
      - PGPT_EMBEDDING_HF_MODEL_NAME=BAAI/bge-small-en-v1.5
    volumes:
      - /docker/data/private-gpt/local_data:/home/worker/app/local_data
      - /docker/data/private-gpt/models:/home/worker/app/models
    networks:
      - traefik-public
    deploy:
      replicas: 1
      restart_policy:
        condition: any
        delay: 5s
        window: 120s
      update_config:
        parallelism: 1
        monitor: 60s
        failure_action: rollback
        order: stop-first
      placement:
        constraints: [node.role == worker]
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.gpt.rule=Host(`gpt.example.ru`)"
        - "traefik.http.routers.gpt.entrypoints=https"
        - "traefik.http.routers.gpt.tls=true"
        - "traefik.http.routers.gpt.middlewares=WhitelistHome"
        - "traefik.http.services.gpt.loadbalancer.server.port=8080"


  qdrant:
    image: qdrant/qdrant:latest
    volumes:
      - /docker/data/qdrant:/qdrant/storage
    networks:
      - traefik-public
    deploy:
      replicas: 1
      restart_policy:
        condition: any
        delay: 5s
        window: 120s
      update_config:
        parallelism: 1
        monitor: 60s
        failure_action: rollback
        order: stop-first
      placement:
        constraints: [node.role == worker]
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.qdrant.rule=Host(`qdrant.example.ru`)"
        - "traefik.http.routers.qdrant.entrypoints=https"
        - "traefik.http.routers.qdrant.tls=true"
        - "traefik.http.routers.qdrant.middlewares=WhitelistHome"
        - "traefik.http.services.qdrant.loadbalancer.server.port=6333"


networks:
  traefik-public:
    external: true

Запуск

docker stack deploy --with-registry-auth -c gpt.yml gpt

Теперь если перейти по адресу https://gpt.example.ru мы должны увидеть страницу чата:

PrivateGPT

На этом всё! 😎 🤘

Теперь можно играться с разными моделями с сайта huggingface.

Полезные ссылки

Адрес дашборда qdrant:

https://qdrant.example.ru/dashboard

К началу