Ollama PHP Client

Локальные модели через явные контракты генерации и эмбеддингов.

PHP-клиент к Ollama с отдельными DTO для текста и эмбеддингов, заменяемым транспортом и явными границами обработки ответа.

PHP · Ollama · Embeddings · LLM

Моя роль

Автор библиотеки: архитектура PHP-клиента, DTO, HTTP-транспорт и тесты.

Open source · Сентябрь 2026. Разбор опубликованного кода на коммите 2eed0e18. Ссылки закреплены на этой версии; ограничения описаны для неё.

Вызов локальной модели из PHP

Ollama запускает модель отдельным процессом и предоставляет HTTP API. Я написал PHP-клиент, который превращает два конкретных сценария — генерацию текста и получение эмбеддинга — в явные запросы и ответы приложения. Библиотека не хранит диалоги и не выбирает модель за пользователя: она отвечает за обращение к серверу и преобразование его ответа.

Я автор пакета, его архитектуры, DTO, транспорта и тестов. В composer.json указаны PHP ^7.2|^8.0, cURL и JSON. Клиент подключается к существующему PHP-коду как Composer-зависимость.

Граница между приложением и моделью
  1. PHP-приложениеТекст, выбор модели и прикладной сценарий
  2. Ollama PHP ClientDTO, HTTP, проверка и преобразование ответа
  3. OllamaЗагруженная модель и вычисление результата

Приложение получает текст или вектор. Хранение, поисковый индекс и проверка качества не входят в обязанности клиента.

Сценарий: подготовить текст для поиска

Представим индекс инженерных заметок. Перед записью в векторную базу приложению нужен эмбеддинг каждой заметки. Оно передаёт один текст и имя модели в EmbeddingRequest, вызывает embed и получает EmbeddingResponse. Конкретная база в этом сценарии не навязывается: полученный массив можно передать другому адаптеру приложения.

Пример ожидает локальный Ollama на порту 11434 с уже загруженной моделью nomic-embed-text. Он выводит фактическую размерность ответа, а не предполагает её заранее. Текст синтетический; этот вызов сам по себе не доказывает качество поиска по реальному корпусу.

<?php
require __DIR__ . '/vendor/autoload.php';

use Tenqz\Ollama\Embedding\Application\DTO\Request\EmbeddingRequest;
use Tenqz\Ollama\Embedding\Infrastructure\Client\OllamaEmbeddingClient;
use Tenqz\Ollama\Transport\Infrastructure\Http\Client\CurlTransportClient;

$http = new CurlTransportClient('http://127.0.0.1:11434', [], 120, 10);
$client = new OllamaEmbeddingClient($http);
$request = new EmbeddingRequest(
    'nomic-embed-text', 'How to configure a reverse proxy'
);
$request->setKeepAlive('5m');

$response = $client->embed($request);
$vector = $response->getEmbedding();
echo 'Dimension: ' . $response->getDimension() . PHP_EOL;

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

Решение 1: разделить генерацию и эмбеддинги

У операций разные контракты: GenerationRequest описывает prompt, system и параметры генерации, а EmbeddingRequest — модель и входной текст. Отдельные клиенты и исключения помогают вызывающему коду различать неудачный ответ генерации и сбой подготовки поискового вектора. Это небольшие границы вокруг понятных действий, без общей сущности «AI-запрос» с десятками необязательных полей.

Решение 2: преобразовать ответ в явный объект

EmbeddingResponse предоставляет getEmbedding, getDimension и метаданные времени, когда их прислал сервер. Первый метод отклоняет пустой список эмбеддингов исключением. Сам клиент проверяет наличие массива embeddings, но не доказывает семантическую пригодность вектора: проверка чисел, требуемой размерности и качества остаётся у интеграции.

Решение 3: держать транспорт заменяемым

Оба клиента зависят от TransportClientInterface. В cURL-реализации отдельно задаются тайм-аут соединения и всего запроса; в примере это 10 и 120 секунд. Тесты подставляют транспорт и проверяют endpoint, отправленные данные и преобразование ответа без загрузки модели. Реальный сервер остаётся отдельной интеграционной проверкой.

Результат, который можно исследовать

В репозитории есть реализация вызова /api/embed и тесты клиента: выбор endpoint, тело запроса, отображение результата и ошибки. Тесты DTO ответа отдельно разбирают пустые данные, размерность и сохранение метаданных.

Результат проекта — Composer-библиотека с раздельными контрактами генерации и эмбеддингов. Unit-тесты используют подставной транспорт и проверяют преобразование HTTP-ответов в DTO и исключения. Качество модели и длительность вычислений оцениваются отдельно на выбранном сервере и наборе входных данных.

Границы версии

В запросе эмбеддинга поле input имеет тип string: наличие списка векторов в DTO ответа не означает готовую пакетную отправку нескольких текстов. Для генерации следует использовать setStream(false), что соответствует значению по умолчанию. Поле stream сериализуется, но транспорт буферизует ответ, а JSON-адаптер разбирает его целиком. Поток токенов с обработкой NDJSON этой реализацией не предоставляется.

Библиотека также не скачивает модели, не управляет GPU и не строит очередь заданий. Длительные запросы занимают PHP-процесс; размещение их в фоновом обработчике и защита удалённого Ollama относятся к архитектуре приложения.

Как попробовать

Запустите Ollama локально, загрузите выбранную embedding-модель и установите пакет. Затем сохраните PHP-пример рядом с vendor и выполните его из терминала. На первом запросе учитывайте время загрузки модели.

composer require tenqz/ollama
ollama pull nomic-embed-text

Другие параметры запросов приведены в README; актуальный код — в tenqz/ollama. Начать полезно с одного короткого текста и проверки размерности, прежде чем подключать постоянный индекс.

GitHub

Давайте делать сложное понятным.

Архитектура, инженерное лидерство и AI в разработке — когда система слишком важна, чтобы её упрощать, и слишком дорогая, чтобы ею не владеть.

LinkedIn Telegram Email