Open source · Сентябрь 2026. Разбор опубликованного кода на коммите 3b1c8ab0. Ссылки закреплены на этой версии; ограничения описаны для неё.
Векторный поиск внутри PHP-приложения
Я написал Qdrant PHP Client как отдельную библиотеку для работы с векторной базой по HTTP. Её задача — связать PHP-приложение с коллекциями, точками и поиском Qdrant. Генерация эмбеддингов, предметные модели и правила доступа остаются в приложении. Это позволяет подключать поиск к существующему бэкенду постепенно, без переноса всей бизнес-логики в другой сервис.
Моя роль — автор библиотеки: публичный API, транспорт, обработка ошибок, примеры и тесты. В composer.json заявлены PHP ^7.2|^8.0, расширения cURL и JSON.
- PHP-приложениеМодель эмбеддингов, документ и права доступа
- Qdrant PHP ClientКоллекции, точки, фильтры и HTTP-запросы
- QdrantХранение векторов и поиск соседей
Модель эмбеддингов находится за пределами клиента. Назад приложение получает идентификаторы, оценки сходства и запрошенный payload.
Сценарий: похожие статьи на выбранном языке
Для каталога статей приложение сначала получает эмбеддинг текста, сохраняет его как vector, а язык и идентификатор материала — как payload. При поиске вектор запроса должен быть построен той же моделью и иметь размерность коллекции. Фильтр по языку передаётся в Qdrant вместе с запросом: отбор выполняется до возврата результатов, а не после получения пяти потенциально неподходящих статей.
Ниже самостоятельный пример чтения для заранее заполненной коллекции case_articles с размерностью 4 и метрикой Cosine. Четыре числа — синтетический вектор для проверки API, не реальный эмбеддинг и не демонстрация качества семантического поиска. У точек должен быть строковый payload locale.
<?php
require __DIR__ . '/vendor/autoload.php';
use Tenqz\Qdrant\QdrantClient;
use Tenqz\Qdrant\Transport\Infrastructure\Factory\CurlHttpClientFactory;
$http = (new CurlHttpClientFactory())->create('127.0.0.1', 6333);
$client = new QdrantClient($http);
$filter = ['must' => [
['key' => 'locale', 'match' => ['value' => 'ru']],
]];
$response = $client->search(
'case_articles', [0.1, 0.2, 0.3, 0.4], 5, $filter
);
foreach ($response['result'] as $hit) {
echo $hit['id'] . ' ' . $hit['score'] . PHP_EOL;
}
Клиент возвращает весь JSON-ответ Qdrant, поэтому результаты находятся в $response['result']. Код не подменяет оценку сходства вероятностью релевантности. Составление ссылки на статью и окончательная проверка доступа происходят в вызывающем приложении.
Решение 1: небольшой API поверх явного транспорта
QdrantClient принимает HttpClientInterface. Фасад формирует HTTP-метод, путь и тело операции, а cURL-реализация отвечает за соединение, JSON и заголовок API-ключа. В тесте можно заменить транспорт и проверить конкретный запрос, не запуская векторную базу. В приложении этот же интерфейс оставляет точку для собственного HTTP-адаптера.
Решение 2: сохранять форму ответа провайдера
Возвращаемые массивы сохраняют ответ API вместе с его служебными полями. Такой подход облегчает просмотр исходных данных и добавление прикладной обработки, но переносит проверку вложенной структуры на вызывающий код. Здесь нет DTO для каждой комбинации фильтров и результата; границу между библиотекой и предметной моделью нужно обозначить в приложении.
Решение 3: различать причины неудачного запроса
В транспорте выделены NetworkException, HttpException и SerializationException. Это позволяет отдельно обработать сбой соединения, отказ API и невозможность прочитать JSON. Разбор JSON происходит до проверки HTTP-статуса: HTML-ответ прокси может проявиться как ошибка сериализации. Автоматических повторов здесь нет; политика повторной записи остаётся решением вызывающего кода.
Что можно проверить в исходниках
Результат работы — устанавливаемый Composer-пакет с операциями создания коллекций, загрузки точек, фильтрованного поиска, рекомендаций и пакетного поиска. Фасад показывает точные тела запросов. HTTP-транспорт раскрывает тайм-аут и обработку ответов. В unit-тестах есть ожидания HTTP-метода, endpoint и параметров через подставной транспорт.
Область unit-тестов — состав параметров, маршрутизация операций и передача ответа через интерфейс транспорта. Качество поиска оценивается отдельно на запросах и размеченных результатах конкретного приложения; пропускная способность зависит также от конфигурации сервера и данных.
Границы реализации
Клиент не вычисляет эмбеддинги, не синхронизирует индекс с основной базой и не предоставляет все новые возможности Qdrant автоматически. Например, upsertPoints не принимает параметр ожидания применения записи: успешный ответ на загрузку нельзя без проверки считать подтверждением её немедленной видимости. Размерность, жизненный цикл коллекций и согласование удалений остаются частью интеграции.
Как попробовать
Установите пакет и подготовьте отдельный Qdrant для эксперимента. Для создания и заполнения тестовой коллекции используйте пример из репозитория; он выполняет операции записи, поэтому не направляйте его на рабочие данные. Затем выполните показанный выше запрос к своей подготовленной коллекции.
composer require tenqz/qdrant
Подробности установки и обработки ошибок разобраны в статье о Qdrant PHP Client. Исходники и история изменений доступны в tenqz/qdrant.