Open source · Сентябрь 2026. Разбор опубликованного кода на коммите 2eed0e18. Ссылки закреплены на этой версии; ограничения описаны для неё.
Вызов локальной модели из PHP
Ollama запускает модель отдельным процессом и предоставляет HTTP API. Я написал PHP-клиент, который превращает два конкретных сценария — генерацию текста и получение эмбеддинга — в явные запросы и ответы приложения. Библиотека не хранит диалоги и не выбирает модель за пользователя: она отвечает за обращение к серверу и преобразование его ответа.
Я автор пакета, его архитектуры, DTO, транспорта и тестов. В composer.json указаны PHP ^7.2|^8.0, cURL и JSON. Клиент подключается к существующему PHP-коду как Composer-зависимость.
- PHP-приложениеТекст, выбор модели и прикладной сценарий
- Ollama PHP ClientDTO, HTTP, проверка и преобразование ответа
- 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. Начать полезно с одного короткого текста и проверки размерности, прежде чем подключать постоянный индекс.