Представьте: у вас есть мощный домашний сервер с RTX 4090, на котором крутится Mistral 7B. Вы хотите дать доступ к нему команде из десяти человек. Они одновременно отправляют запросы на генерацию текста, создание эмбеддингов, анализ документов.
И что происходит? Правильно. Всё падает.
Ollama по умолчанию обрабатывает запросы последовательно. Два одновременных запроса — и один из них зависнет навечно. Три запроса — сервер может просто отказаться отвечать. Это не баг, это фича для одиночного использования.
LLMeQueue решает эту проблему радикально просто: ставит всё в очередь.
Что такое LLMeQueue и зачем он нужен
Это Python-микросервис, который садится между вашими приложениями и локальной LLM (через Ollama). Он принимает запросы в формате OpenAI API, складывает их в очередь и по одному отправляет на обработку. Когда модель на GPU закончила с одним запросом — сервис берёт следующий.
Ключевая фишка: LLMeQueue полностью совместим с OpenAI API. Ваши приложения, которые работают с ChatGPT, будут думать, что обращаются к OpenAI, но на самом деле запросы пойдут на ваш домашний GPU.
Зачем это нужно? Давайте посмотрим на реальные сценарии:
- Массовая генерация эмбеддингов для документов (по 100-200 файлов за раз)
- Параллельная работа нескольких пользователей через веб-интерфейс
- Интеграция с существующими инструментами вроде LangChain, которые уже используют OpenAI API
- Тестирование промптов с разными параметрами одновременно
Без очереди эти сценарии превращаются в кошмар. С очередью — работают как часы.
Как это работает под капотом
Архитектура до боли простая:
| Компонент | Что делает |
|---|---|
| FastAPI сервер | Принимает HTTP запросы в формате OpenAI API |
| Очередь (asyncio.Queue) | Хранит запросы до обработки |
| Воркеры | Берут запросы из очереди и отправляют в Ollama |
| Ollama API клиент | Общается с локальной LLM |
Когда приходит запрос на /v1/chat/completions, сервер не бежит сразу в Ollama. Он кладёт запрос в очередь и возвращает клиенту «принято, ждите». Воркеры в фоне забирают запросы по одному, отправляют в модель, ждут ответа и возвращают результат.
Вот как выглядит базовый код сервера:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import aiohttp
import asyncio
from typing import List
app = FastAPI()
# Простейшая очередь
request_queue = asyncio.Queue()
class ChatMessage(BaseModel):
role: str
content: str
class ChatCompletionRequest(BaseModel):
model: str
messages: List[ChatMessage]
max_tokens: int = 100
@app.post("/v1/chat/completions")
async def chat_completion(request: ChatCompletionRequest):
"""Принимаем запрос, кладём в очередь, ждём результата"""
# Генерируем ID для отслеживания
request_id = str(uuid.uuid4())
# Кладём в очередь
await request_queue.put({
"id": request_id,
"request": request.dict(),
"event": asyncio.Event()
})
# Ждём, пока воркер обработает
# ...
return {"choices": [{"message": {"content": "Ответ от модели"}}]}
async def worker():
"""Воркер, который забирает запросы из очереди"""
while True:
task = await request_queue.get()
try:
# Отправляем в Ollama
async with aiohttp.ClientSession() as session:
async with session.post(
"http://localhost:11434/api/chat",
json={
"model": task["request"]["model"],
"messages": task["request"]["messages"],
"options": {"num_predict": task["request"]["max_tokens"]}
}
) as response:
result = await response.json()
# Сохраняем результат
task["result"] = result
task["event"].set()
except Exception as e:
task["error"] = str(e)
task["event"].set()
finally:
request_queue.task_done()Чем LLMeQueue лучше других решений
Давайте сравним с тем, что уже есть на рынке:
| Решение | Плюсы | Минусы | Когда использовать |
|---|---|---|---|
| Прямой доступ к Ollama | Максимальная простота | Нет очереди, падает при нагрузке | Для одного пользователя |
| LLMeQueue | Очередь, OpenAI API формат | Нужно настраивать | Для команды или массовой обработки |
| Nginx как балансировщик | Можно настроить очереди | Сложная конфигурация для LLM | Если уже есть инфраструктура |
| Платное облако (OpenAI) | Всё работает из коробки | Дорого, данные уходят наружу | Когда не жалко денег |
Главное преимущество LLMeQueue — он решает конкретную проблему (очередь запросов) максимально простым способом. Не нужно изучать сложные системы кластеризации, не нужно настраивать балансировщики. Скачал, запустил, работает.
Внимание: LLMeQueue не делает распределённые вычисления на несколько GPU. Если вам нужно распределить модель между несколькими видеокартами, смотрите в сторону llama.cpp RPC-server или стратегий масштабирования.
Как запустить LLMeQueue за 5 минут
1Установите Ollama и модель
Если ещё не сделали:
curl -fsSL https://ollama.ai/install.sh | sh
ollama pull mistral:7b2Клонируйте LLMeQueue
git clone https://github.com/ваш-репозиторий/LLMeQueue.git
cd LLMeQueue
pip install -r requirements.txt3Настройте конфигурацию
# config.yaml
server:
host: "0.0.0.0" # слушаем все интерфейсы
port: 8000
ollama:
base_url: "http://localhost:11434"
default_model: "mistral:7b"
queue:
max_size: 100 # максимум запросов в очереди
workers: 2 # два воркера для параллельной обработки4Запустите сервер
python main.py --config config.yamlСервер запустится на порту 8000. Теперь можно отправлять запросы:
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mistral:7b",
"messages": [{"role": "user", "content": "Привет, как дела?"}]
}'Или использовать с Python-клиентом OpenAI:
import openai
client = openai.OpenAI(
base_url="http://localhost:8000/v1", # Ваш LLMeQueue
api_key="not-needed"
)
response = client.chat.completions.create(
model="mistral:7b",
messages=[{"role": "user", "content": "Напиши стих про Python"}]
)
print(response.choices[0].message.content)Реальные кейсы использования
Вот где LLMeQueue спасает жизнь:
Массовая генерация эмбеддингов
У вас есть 500 документов. Нужно создать эмбеддинги для каждого и положить в векторную базу. Без очереди это займёт вечность (или сломается). С LLMeQueue — отправляете все запросы разом, они обрабатываются по очереди, вы получаете все эмбеддинги.
# Пример массовой генерации эмбеддингов
documents = ["документ 1", "документ 2", ...] # 500 документов
embeddings = []
for doc in documents:
response = client.embeddings.create(
model="nomic-embed-text",
input=doc
)
embeddings.append(response.data[0].embedding)
# LLMeQueue сам разберётся с очередьюВеб-интерфейс для команды
Сделали простой чат-интерфейс на Streamlit для команды из 5 человек. Все пишут запросы одновременно. Без LLMeQueue интерфейс будет висеть и показывать ошибки. С LLMeQueue — запросы просто будут выполняться дольше, но всё заработает.
Интеграция с существующими инструментами
У вас уже есть код, который использует OpenAI API. Вы купили мощную станцию для локальных LLM и хотите перенести туда обработку. Меняете base_url с api.openai.com на localhost:8000 — и всё работает. Ничего переписывать не нужно.
Ограничения и подводные камни
LLMeQueue — не волшебная таблетка. У него есть свои ограничения:
- Одна модель за раз: Очередь работает для одной запущенной модели. Если нужно переключаться между разными моделями, придётся дорабатывать.
- Нет приоритизации: Запросы обрабатываются в порядке FIFO (первый пришёл — первый ушёл). Срочный запрос будет ждать своей очереди.
- Зависит от Ollama: Если Ollama упадёт, LLMeQueue продолжит принимать запросы в очередь, но не сможет их обработать.
- Только HTTP: Нет поддержки WebSockets для стриминга. Ответы приходят целиком.
Эти ограничения — осознанный выбор. LLMeQueue делает одну вещь (очередь запросов) и делает её хорошо. Для сложных сценариев придётся искать другие решения или дорабатывать.
Кому подойдёт LLMeQueue
Этот инструмент — для конкретных людей с конкретными проблемами:
| Кто вы | Ваша проблема | Как LLMeQueue поможет |
|---|---|---|
| Разработчик в маленькой компании | Нет бюджета на OpenAI API, но нужно дать доступ команде к LLM | Поставите на сервер с GPU, команда сможет работать одновременно |
| Исследователь данных | Нужно обработать тысячи текстов для создания датасета | Отправите все запросы разом, они обработаются по очереди |
| Энтузиаст с домашним сервером | Хотите поделиться доступом к своей LLM с друзьями | Настроите за час, друзья смогут пользоваться через API |
| Разработчик готовых решений | Интегрируете LLM в продукт, но клиенты шлют запросы пачками | Очередь не даст серверу упасть под нагрузкой |
Если вы один используете LLM для редких запросов — LLMeQueue вам не нужен. Если у вас команда или массовая обработка — он сэкономит нервы и время.
Что дальше? Развитие проекта
LLMeQueue в текущем виде решает базовую задачу. Но есть куда расти:
- Приоритетная очередь: Чтобы срочные запросы обрабатывались первыми
- Балансировка между несколькими GPU: Если у вас несколько видеокарт, можно распределять запросы между ними
- Поддержка стриминга: Чтобы ответы приходили по мере генерации, как в ChatGPT
- Мониторинг и метрики: Графики загрузки очереди, времени обработки, ошибок
- Автоматическое масштабирование: Запуск дополнительных воркеров при высокой нагрузке
Пока это всё — задачи для сообщества. Базовая версия работает и решает 80% проблем. Остальные 20% — для тех, кому нужно больше.
Главный совет: не усложняйте раньше времени. Начните с простой очереди. Когда упрётесь в её ограничения — дорабатывайте. Часто оказывается, что простого решения хватает на годы.
А если нужна совсем простая настройка — посмотрите как запустить локальную LLM-инфраструктуру. Там тоже есть про очереди, но в более общем контексте.
LLMeQueue — это не про революцию. Это про то, чтобы перестать бояться давать доступ к своему GPU. Поставить очередь, открыть порт, работать. Всё просто. Как и должно быть.