Локальная LLM в production: как мы подключили Ollama к Laravel
Практика · Laravel · Ollama · Qwen2.5 · CPU inference
AI-анализатор проектов работал через DeepSeek и возвращал строгий JSON из 21 поля. Мы добавили локальную Qwen2.5 7B через Ollama, проверили её на реальной нагрузке и выяснили, где CPU-модель действительно экономит ресурсы, а где облачный AI всё ещё выигрывает.
Коротко: локальная модель не стала безусловной заменой DeepSeek. Лучший результат дала каскадная схема: Ollama обрабатывает массовый первичный поток, а облачная модель подключается только к проектам, которым нужен более глубокий анализ.
Если вы проектируете похожий контур для документов, внутренних данных или автоматизации, посмотрите услугу разработки AI-агентов и RAG-систем.
Задача была сложнее обычного чата
В этом проекте AI встроен в backend-конвейер. Система собирает GitHub-проекты, читает описание, README, сайт и метаданные, а затем определяет тип продукта, целевую аудиторию, проблему, коммерческий потенциал, сложность реализации, доступность данных и конкуренцию.
Ответ нельзя заменить свободным текстом. Следующий этап ожидает структурированный объект: 21 обязательное поле, заданные типы данных и оценки в допустимых диапазонах. Ошибка модели здесь ломает не красоту ответа, а автоматическую обработку.
Сбор данных
Описание проекта, README и метаданные формируют исходный набор данных.
Подготовка входа
ProjectAnalysisInputприводит информацию к единому формату для анализа.Выбор AI-провайдера
Запрос обрабатывает DeepSeek или локальная модель через Ollama.
Проверка JSON
Валидатор проверяет 21 обязательное поле, типы данных и допустимые значения.
Расчёт результата
Система рассчитывает итоговые показатели и оценки по шкале от 0 до 10.
Провайдер изолирован интерфейсом: бизнес-логика и валидация не зависят от конкретной модели.
С чего начинали
Сервер без GPU
Intel Xeon E5-1650 v3, 6 физических ядер и 12 потоков, 62 ГБ RAM, два SATA SSD. Это обычный сервер приложений, а не специализированная AI-машина.
Qwen2.5 7B Q4_K_M
Квантованная модель занимает около 4,7 ГБ. Контекст ограничили 8192 токенами, параллельность — одним запросом.
Фоновая обработка
Анализ идёт через Laravel Horizon. Пользователь не ждёт ответ в браузере, поэтому задержка CPU inference допустима.
Закрытый API
Ollama не опубликована на 0.0.0.0:11434. Доступ разрешён только приложению через туннель и отдельную Docker-сеть.
7B против 14B и облачной модели
На одном и том же ProjectAnalysisInput сравнили DeepSeek, Qwen2.5 7B и Qwen2.5 14B. Более крупная локальная модель местами давала оценки ближе к DeepSeek, но на CPU один запуск занимал больше шести минут.
DeepSeek
Вход: 2345 токенов
Выход: 1517 токенов
Время: 8,5 секунды
Qwen2.5 7B
Вход: 2320 токенов
Выход: 564 токена
Время: около 159 секунд
Qwen2.5 14B
Вход: 2320 токенов
Выход: 676 токенов
Время: около 376 секунд
Вывод не в том, что 14B «плохая». Для интерактивного CPU-конвейера её цена по времени оказалась выше прироста качества.
Почему восемь CPU оказались практичнее двенадцати
Следующий тест — ограничение числа логических CPU для одного и того же запроса. Основной прирост произошёл между четырьмя и шестью потоками. После восьми скорость почти перестала меняться.
Если отдать модели все 12 потоков, MySQL, PHP, Redis и очереди останутся без запаса, а inference не ускорится. Поэтому верхний лимит установили на 8 CPU: это не абсолютный максимум, а баланс всего production-сервера.
Structured output дал больше, чем увеличение модели
Сначала JSON Schema находилась прямо в prompt рядом с инструкцией, README и данными проекта. Для облачного API лишние токены почти незаметны, но локальная модель обрабатывает каждый из них на CPU.
Было: схема внутри prompt
Около 2170 входных токенов.
Стало: native structured output
Около 1550 входных токенов.
Схему передали через нативный параметр format Ollama. После сокращения инструкции и входа лабораторное время анализа снизилось примерно со 159 до 95–100 секунд — почти на 40%, при этом ответ сохранил все обязательные поля.
Для локального inference оптимизация контекста часто выгоднее перехода на более крупную модель или выделения дополнительных CPU.
Валидный JSON ещё не означает правильный анализ
Qwen2.5 7B стабильно возвращала 21 поле без лишних ключей. Но после слишком агрессивного сокращения system prompt появилась семантическая ошибка: репозиторий книги Python Data Science Handbook модель классифицировала как ecommerce — из-за упоминания печатной версии.
Формально ответ был идеальным. Смысл — неверным. После уточнения правил классификация исправилась. Этот пример показал, что проверять замену провайдера только фразой «JSON пришёл» нельзя.
Схема
Обязательные поля, типы, диапазоны и отсутствие неожиданных ключей.
Семантика
Контрольные проекты, пограничные случаи и сравнение с историческими результатами.
Эксплуатация
Timeout, повторы, очереди, логирование, нагрузка и влияние на остальные сервисы.
Laravel не пришлось привязывать к Ollama
Интеграция уже была построена через AiProviderInterface. Мы добавили ещё одну реализацию и оставили выбор провайдера в конфигурации. Основной анализатор по-прежнему собирает вход, вызывает контракт, валидирует DTO и рассчитывает итоговые показатели.
AiProviderInterface
├── OpenAiProvider
├── DeepSeekProvider
└── OllamaProvider
OLLAMA_API_URL=http://…/v1
OLLAMA_MODEL=qwen2.5:7b
OLLAMA_TIMEOUT=600
Ollama предоставляет OpenAI-совместимый /v1/chat/completions, поэтому инфраструктурная разница не просачивается в бизнес-логику. DeepSeekProvider остаётся доступным: провайдера и модель можно менять независимо от конвейера.
Как закрыли сетевой доступ к модели
Laravel и Ollama находятся на разных физических серверах. Публично открывать inference API не стали. Между хостами работает зашифрованный SSH-туннель, а ключ отдельного системного пользователя ограничен единственным направлением permitopen="127.0.0.1:11434".
Контрольный production-тест
Интеграцию проверили из того же Horizon-контейнера, где работают фоновые задачи. Для реального проекта вход сформировал штатный builder, после чего был вызван именно OllamaProvider — без сохранения результата в базу.
Маршрут подтверждён
Запрос ушёл через закрытый адрес на /v1/chat/completions, а не во внешний DeepSeek API.
Валидация пройдена
HTTP 200, модель qwen2.5:7b, 2320 входных и 617 выходных токенов, длительность 164,8 секунды. База данных не изменилась.
Итоговая архитектура: Ollama и DeepSeek вместе
Эксперимент начинался с вопроса «можно ли заменить облачный API?», но более полезным оказался другой вариант — распределить работу по стоимости и сложности.
Обычные фильтры
Код отбрасывает проекты, которые не проходят формальные критерии.
metadata · heuristics · rulesOllama делает первичную оценку
Qwen2.5 7B обрабатывает массовый поток локально и распределяет проекты по базовым категориям.
ignore · watch · interestingDeepSeek получает только сильных кандидатов
Облачная модель выполняет более дорогой глубокий анализ там, где качество важнее стоимости одного запроса.
investigate · build
Так стоимость облачного inference не растёт линейно с общим потоком, данные первичного анализа остаются внутри инфраструктуры, а система не становится зависимой от одного провайдера.
Когда CPU-only LLM подходит
Подходит
- задача выполняется асинхронно;
- есть большой поток однотипных запросов;
- важен контроль данных и инфраструктуры;
- ответ можно строго валидировать;
- допустима очередь с ограниченной параллельностью.
Нужна осторожность
- пользователь ждёт ответ в реальном времени;
- контекст очень большой;
- нужны сложные рассуждения без контрольной выборки;
- несколько запросов должны выполняться параллельно;
- ошибка классификации имеет высокую цену.
Нужен AI-конвейер под ваши данные?
Локальная модель, облачный API или гибридная схема выбираются не по моде, а по объёму запросов, требованиям к данным, допустимой задержке и цене ошибки. Я проектирую такие системы целиком: от источников и очередей до валидации, логирования и рабочего интерфейса.
Laravel · Horizon · Ollama · Qwen2.5 7B · DeepSeek · Docker · SSH tunnel · Structured Output
