Это вторая часть статьи.Часть 1: Практика без Python и data scienceAI в PHP: не теория, а место, с которого можно начатьВ своей прошлой статье я описал на доволЭто вторая часть статьи.Часть 1: Практика без Python и data scienceAI в PHP: не теория, а место, с которого можно начатьВ своей прошлой статье я описал на довол

AI для PHP-разработчиков. Часть 2: практическое использование TransformersPHP

2026/02/08 03:43
17м. чтение

Это вторая часть статьи.
Часть 1: Практика без Python и data science

AI в PHP: не теория, а место, с которого можно начать

В своей прошлой статье я описал на довольно общем уровне почему тема AI вроде бы везде, но при этом почти не пересекается с повседневной PHP-разработкой. Не потому что PHP "не подходит", а потому что сам разговор обычно идёт мимо наших задач и привычного способа мышления. Ну и, конечно, о том, что почти нет материала, который объясняет AI именно для PHP-разработчиков, их задач и их мышления.

После публикации мне несколько раз задали один и тот же вопрос, в разных формулировках:

И это, пожалуй, самый интересный вопрос из тех, что я получил. Ниже я попытаюсь дать на него ответ.

Про "точку входа"

Когда вы слышите "использование AI в проекте", скорее всего в вашей голове сразу возникает слишком много лишнего: инфраструктура, обучение моделей, эксперименты, отдельные сервисы, новые роли в команде и т.д.

Но если честно, в большинстве PHP-проектов нам это просто не нужно (хотя разобраться в этом самому - жутко интересная задача).

Но всё же, нам с вами не нужно:

  • обучать модели с нуля

  • разбираться в градиентном спуске

  • собирать датасеты и т.д.

Нужно понять, что AI ≠ Data Science. Друзья, в большинстве PHP-проектов никто не обучает модели, пожалуйста, запомните это! По крайней мере в прикладных PHP-проектах, где мы говорим об использовании готовых моделей, а не об исследованиях и обучении.

Нам нужно взять готовый инструмент и аккуратно встроить его в уже существующую логику. Так же, как мы это делаем с любой другой библиотекой.

И вот тут начинается интересное.

Что это вообще за зверь такой TransformersPHP и с чем его едят?

Про использование LLM через API знают уже все. Это полезно и удобно, но в таком виде AI остаётся чем-то внешним: сервисом, к которому ты просто отправляешь текст. Внутри – туман.

В какой-то момент мне захотелось посмотреть на более "приземлённый" уровень: не генерация текста, а представление семантики: эмбеддинги, задачи классификации и поиска. Именно на этом уровне решается большинство прикладных задач в бэкенд-системах: поиск по тексту, сравнение и автоматическая классификация, а не диалоговое взаимодействие человека с моделью.

И здесь неожиданно выяснилось, что есть инструменты, которые позволяют делать это напрямую в PHP, без Python-стека. Один из них – TransformersPHP.

Важно сразу понять: это не попытка превратить PHP в Python и не универсальное решение. Это библиотека для inference (инференс) – использования уже обученных моделей.

Как по мне, TransformersPHP – один из самых интересных и показательных проектов в современной PHP ML-экосистеме. Отдельное спасибо его автору - Kyrian Obikwelu за то что создал этот проект и продолжает над ним работать. В общем это библиотека, которая позволяет использовать трансформер-модели (BERT, RoBERTa, DistilBERT и др.) напрямую из PHP, без Python и без внешних API.

По сути, это PHP-ориентированная обертка над идеями Hugging Face Transformers, адаптированная под PHP-экосистему и реальные прикладные сценарии.

Ключевая особенность библиотеки – локальный инференс. Модели загружаются и выполняются на стороне PHP-приложения (через ONNX Runtime), что открывает важные архитектурные возможности:

  • отсутствие сетевых вызовов к LLM API

  • полный контроль над данными (это может быть важно для privacy (конфиденциальности) в вашей работе)

  • предсказуемое и стабильное время обработки запроса

  • возможность оффлайн-работы (после первого запуска и загрузки модели)

TransformersPHP поддерживает типовые задачи NLP, такие как: получение эмбеддингов, классификацию текста, семантическое сравнение и прочее.

Что мне нравится больше всего

Самое ценное ощущение – отсутствие разрыва контекста.

Я остаюсь в PHP, я пишу тот же код, у меня тот же деплой и у меня те же подходы к архитектуре. Ничего не изменилось. Модель для меня в этом случае – не некий "магический объект", а просто ещё один источник данных. Да, непривычный, но вполне объяснимый - не хуже и не лучше других.

И это сильно меняет отношение к всей теме. Вы согласны?

Запуск модели за 10 минут

Один из самых важных моментов – это первый запуск.
Если он сложный, на этом всё обычно и заканчивается.

В случае с TransformersPHP ощущение как раз обратное: это больше похоже на работу с обычной зависимостью.

Полное руководство по установке можно найти на сайте документации.

Мы же с вами для простоты запустим докер контейнер со всеми необходимыми зависимостями. Да, Docker file выглядит объёмно – но это одноразовая инфраструктура. Сам запуск и первый демо-пример действительно укладываются в несколько минут.

Что нам нужно (требования)

  • PHP 8.1 или выше

  • Composer

  • Расширение PHP FFI

  • JIT-компиляция (опционально, для повышения производительности)

  • Увеличенный лимит памяти (для сложных задач, таких как генерация текста)

Структура проекта

/project/ ├── app/ │ ├── demo.php │ ├── semantic-search.php ├── docker/ │ ├── Dockerfile ├── docker-composer.yaml ├── composer.json

Установка (Docker)

Файл: docker-compose.yml

networks: ai-for-php-developers: driver: bridge services: app: build: context: . dockerfile: docker/Dockerfile volumes: - .:/var/www ports: - "8088:8088" command: php -S 0.0.0.0:8088 -t app networks: - ai-for-php-developers

Файл: docker/Dockerfile

# ------------------------------ # Install system dependencies # ------------------------------ RUN apt-get update && apt-get install -y \ libzip-dev \ zip \ unzip \ git \ libxml2-dev \ libcurl4-openssl-dev \ libpng-dev \ libonig-dev \ && rm -rf /var/lib/apt/lists/* # ------------------------------ # Install PHP extensions # ------------------------------ RUN docker-php-ext-install zip pdo_mysql bcmath xml mbstring curl gd pcntl # ------------------------------ # Enable FFI # ------------------------------ RUN apt-get update && apt-get install -y \ libffi-dev \ pkg-config \ && rm -rf /var/lib/apt/lists/* RUN docker-php-ext-install ffi RUN echo "ffi.enable=1" > /usr/local/etc/php/conf.d/ffi.ini # ------------------------------ # Install ONNX Runtime # ------------------------------ ENV ONNXRUNTIME_VERSION=1.17.1 RUN curl -L https://github.com/microsoft/onnxruntime/releases/download/v${ONNXRUNTIME_VERSION}/onnxruntime-linux-x64-${ONNXRUNTIME\_VERSION}.tgz \ | tar -xz \ && cp onnxruntime-linux-x64-${ONNXRUNTIME_VERSION}/lib/libonnxruntime.so* /usr/lib/ \ && ldconfig \ && rm -rf onnxruntime-linux-x64-${ONNXRUNTIME_VERSION} # ------------------------------ # Install Composer # ------------------------------ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer # Set working directory WORKDIR /var/www # Copy existing application directory contents COPY . /var/www # Configure PHP RUN echo "memory_limit = 512M" >> /usr/local/etc/php/conf.d/docker-php-ram-limit.ini RUN echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/docker-php-max-execution-time.ini # Expose port 9000 and start php-fpm server EXPOSE 9000 CMD ["php-fpm"]

И для установки самого TransformersPHP

Файл: composer.json

{ "type": "project", "minimum-stability": "stable", "prefer-stable": true, "require": { "codewithkyrian/transformers": "~0.6.2" }, "config": { "allow-plugins": { "codewithkyrian/platform-package-installer": true } } }

Команда для запуска окружения

docker compose build --pull docker compose up -d docker compose exec app /bin/bash -c "composer install"

Идея простая: чтобы пример можно было поднять локально без ручной настройки окружения.

Базовый демо-пример

Начнём с самого простого: анализа настроений.

Если всё, что описано выше сработало хорошо и установка прошла нормально, можно запустить пример, описанный ниже (примите во внимание, что первый запуск может занять несколько секунд):

docker compose exec app php app/demo.php

Пример использования выглядит концептуально просто: вы загружаете предобученную модель и применяете ее к тексту так же, как это делали бы в Python – но уже внутри PHP-кода. TransformersPHP предлагает простой pipeline API для задач вроде анализа настроений, классификации текста, семантического сравнения и т.д. В примере ниже модель определяет тональность двух фраз и показывает метку и score.

Файл: app/demo.php

require_once __DIR__ . '/../vendor/autoload.php'; use function Codewithkyrian\Transformers\Pipelines\pipeline; // Выделить конвейер для анализа настроений $classifier = pipeline('sentiment-analysis'); $out = $classifier(['I love transformers!']); echo 'I love transformers!'; echo print_r($out, true); $out = $classifier(['I hate transformers!']); echo 'I hate transformers!'; echo print_r($out, true);

Результат, конечно, же вполне ожидаемый:

I love transformers! Array ( [label] => POSITIVE [score] => 0.99978870153427 ) I hate transformers! Array ( [label] => NEGATIVE [score] => 0.99863630533218 )

Цель этого примера – снять у вас психологический барьер: эта модель в PHP — это просто ещё один объект, с которым можно работать.

Этот же пример можно запустить онлайн.

Важно понимать архитектурную роль TransformersPHP.

Эта библиотека не конкурирует с большими LLM-сервисами вроде GPT или Claude. Она закрывает другой, очень важный слой:

  • быстрые эмбеддинги

  • локальная классификация

  • семантический поиск

  • lightweight NLP без внешних зависимостей

В связке с PHP это выглядит особенно логично. PHP остается центральным слоем бизнес-логики, а трансформеры становятся встроенным инструментом, а не удаленным сервисом. TransformersPHP – это хороший пример того, как современный ML постепенно перестает быть "чужим" для PHP и становится частью его нативной экосистемы, пусть и через аккуратные инженерные мосты вроде ONNX.

Реальный кейс: семантический поиск по событиям

Учебные примеры хороши, но быстро надоедают. Гораздо интереснее посмотреть на задачу, которая реально встречается в бэкенде. Сейчас мы с вами сделаем кое-что поинтересней.

Для запуска этого примера используйте следующую команду

docker compose exec app php app/semantic-search.php

Сценарий

Есть события с коротким текстовым описанием. Пользователь ищет, например:

  • "санкции против IT-компаний"

  • "космическая гонка среди стран региона"

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

Небольшое мысленное упражнение

Допустим, у нас есть лента событий или материалов, где каждое событие описано парой предложений. Что-то вроде:

  • введены новые ограничения в отношении технологических корпораций

  • страны региона наращивают инвестиции в спутниковые программы

  • обострение конфликта на политической почве в нескольких провинциях

Теперь пользователь вводит запрос: "космическая гонка среди стран региона".

Ни одно из этих слов буквально не обязано встречаться в описании событий. Упс… И это нормально – люди редко формулируют мысли так же, как их описывают системы.

Идея решения

Вместо того чтобы пытаться угадать слова, можно попробовать искать по смыслу. Не в философском смысле, а в инженерном:

  • описание события → вектор,

  • запрос пользователя → вектор,

  • дальше – обычный поиск ближайших значений.

То есть, нам нужно использовать эмбеддинги как универсальный индекс смысла.

Таким образом мы:

  1. Берём массив событий {id, title, description}

  2. Считаем эмбеддинг только по description (заголовок часто слишком короткий, неинформативный и может добавлять шум)

  3. Эмбеддим пользовательский запрос

  4. Ищем ближайшие векторы

  5. Сортируем и возвращаем результат

Без обучения моделей и без сложной инфраструктуры. Эмбеддинги для событий считаются один раз и могут храниться где угодно. Запрос пользователя обрабатывается в момент поиска. Дальше – сортировка и вывод результатов.

И... вуаля!
Никакого обучения моделей.
Никакой магии.
Просто другой способ представить текст.

Логика работы

Поместим логику работы в отдельный класс SemanticEventSearch. Этот класс не претендует на звание лучшего кода на планете, и написан только в в демонстрационных целях - поэтому опустим замечания по его качеству.

Класс SemanticEventSearch

final class SemanticEventSearch { private string $model = 'Xenova/paraphrase-multilingual-MiniLM-L12-v2'; private string $cachePath; private string $defaultQuery = 'санкции против IT-компаний'; private int $topN; private ?string $query = null; /** @var list<array{id:int,title:string,description:string}> */ private array $events; /** @var array<int, list<float|int>> */ private array $eventEmbeddingsById = []; private $embedder; /** * Create a new semantic search instance. * * @param int $topN Number of results to return. */ public function __construct(int $topN = 3) { $this->cachePath = __DIR__ . '/../embeddings.events.json'; $this->events = []; $this->embedder = null; $this->topN = $topN; } /** * Inject events that will be indexed/searched. * * @param list<array{id:int,title:string,description:string}> $events * @return $this */ public function setEvents(array $events): self { $this->events = $events; $this->eventEmbeddingsById = []; return $this; } /** * Set the embeddings model identifier. * * Switching model invalidates in-memory embeddings. * * @param string $model * @return $this */ public function setModel(string $model): self { $this->model = $model; $this->embedder = null; $this->eventEmbeddingsById = []; return $this; } /** * Set the query to be searched. * * @param string $query * @return $this */ public function setQuery(string $query): self { $q = trim($query); $this->query = $q === '' ? null : $q; return $this; } /** * Run the end-to-end semantic search pipeline (cache -> embed query -> score -> top-N). * * @return array{query:string,results:list<array{score:float,event:array{id:int,title:string,description:string}}>} * @throws RuntimeException If events are not set or embeddings output is unexpected. */ public function run(): array { if (count($this->events) === 0) { throw new RuntimeException('Events list is empty. Call setEvents() before run().'); } if ($this->embedder === null) { $this->embedder = pipeline('embeddings', $this->model); } $this->loadEmbeddingsFromCacheIfCompatible(); $this->ensureAllEventEmbeddings(); $query = $this->query ?? $this->defaultQuery; $queryVec = $this->embedText($query); $results = $this->search($queryVec); return [ 'query' => $query, 'results' => $results, ]; } /** * Compute an embedding vector for a single text. * * @param string $text * @return list<float|int> * @throws RuntimeException */ private function embedText(string $text): array { $emb = ($this->embedder)($text, normalize: true, pooling: 'mean'); if (!is_array($emb) || !isset($emb[0]) || !is_array($emb[0])) { throw new RuntimeException('Unexpected embeddings output format'); } return $emb[0]; } /** * Cosine similarity between two vectors. * * @param list<float|int> $a * @param list<float|int> $b * @return float */ private function cosineSimilarity(array $a, array $b): float { $n = min(count($a), count($b)); $dot = 0.0; $normA = 0.0; $normB = 0.0; for ($i = 0; $i < $n; $i++) { $x = (float) $a[$i]; $y = (float) $b[$i]; $dot += $x * $y; $normA += $x * $x; $normB += $y * $y; } if ($normA <= 0.0 || $normB <= 0.0) { return 0.0; } return $dot / (sqrt($normA) * sqrt($normB)); } /** * Load a JSON file and decode to array. * * @param string $path * @return array|null */ private function loadJsonFile(string $path): ?array { if (!is_file($path)) { return null; } $raw = file_get_contents($path); if ($raw === false) { return null; } $data = json_decode($raw, true); return is_array($data) ? $data : null; } /** * Encode and save data to JSON file. * * @param string $path * @param array $data * @throws RuntimeException */ private function saveJsonFile(string $path, array $data): void { $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); if ($json === false) { throw new RuntimeException('Failed to encode JSON'); } $ok = file_put_contents($path, $json); if ($ok === false) { throw new RuntimeException('Failed to write cache file: ' . $path); } } /** * Load cached event embeddings only if they were produced by the current model. * * @return void */ private function loadEmbeddingsFromCacheIfCompatible(): void { $cached = $this->loadJsonFile($this->cachePath); if (!is_array($cached) || !isset($cached['model'], $cached['events']) || !is_array($cached['events'])) { return; } if ($cached['model'] !== $this->model) { return; } foreach ($cached['events'] as $row) { if (isset($row['id'], $row['embedding']) && is_array($row['embedding'])) { $this->eventEmbeddingsById[(int) $row['id']] = $row['embedding']; } } } /** * Ensure embeddings exist for all events and persist them to cache. * * @return void * @throws RuntimeException */ private function ensureAllEventEmbeddings(): void { $missing = []; foreach ($this->events as $event) { $id = (int) $event['id']; if (!isset($this->eventEmbeddingsById[$id])) { $missing[] = $event; } } if (count($missing) === 0) { return; } foreach ($missing as $event) { $id = (int) $event['id']; $text = (string) $event['description']; $this->eventEmbeddingsById[$id] = $this->embedText($text); } $toCache = [ 'model' => $this->model, 'events' => array_values(array_map( fn(array $event): array => [ 'id' => (int) $event['id'], 'embedding' => $this->eventEmbeddingsById[(int) $event['id']], ], $this->events )), ]; $this->saveJsonFile($this->cachePath, $toCache); } /** * Score all events against the query embedding and return the top-N results. * * @param list<float|int> $queryVec * @return list<array{score:float,event:array{id:int,title:string,description:string}}> */ private function search(array $queryVec): array { $scored = []; foreach ($this->events as $event) { $id = (int) $event['id']; $score = $this->cosineSimilarity($queryVec, $this->eventEmbeddingsById[$id]); $scored[] = [ 'score' => $score, 'event' => $event, ]; } usort($scored, static fn(array $a, array $b): int => $b['score'] <=> $a['score']); return array_slice($scored, 0, $this->topN); } /** * Render results as plain text. * * @param string $query * @param list<array{score:float,event:array{id:int,title:string,description:string}}> $results * @return void */ public function render(string $query, array $results): void { echo "Query: {$query}\n\n"; foreach ($results as $row) { $event = $row['event']; $score = (float) $row['score']; echo "[" . number_format($score, 4) . "] #{$event['id']} {$event['title']}\n"; echo " {$event['description']}\n\n"; } } }

Подготовим данные

Предположим, что это наши данные, собранные из разных источников. Для простоты поместим их в массив.

Массив $events

$events = [ [ 'id' => 1, 'title' => 'Ограничения против технологических корпораций', 'description' => 'Введены новые экономические меры в отношении крупных технологических компаний.', ], [ 'id' => 2, 'title' => 'Развитие космических программ', 'description' => 'Несколько стран региона увеличили финансирование национальных спутниковых проектов.', ], [ 'id' => 3, 'title' => 'Эскалация политического конфликта', 'description' => 'Обострение конфликта на политической почве в нескольких провинциях.', ], [ 'id' => 4, 'title' => 'Ограничения против ИТ-сектора', 'description' => 'Правительство объявило о новых ограничениях для компаний, работающих в сфере информационных технологий.', ], [ 'id' => 5, 'title' => 'Рост инфляции и пересмотр ключевой ставки', 'description' => 'Центральный банк повысил ключевую ставку на фоне ускорения инфляции и роста цен на импортные товары.', ], [ 'id' => 6, 'title' => 'Запуск программы поддержки малого бизнеса', 'description' => 'Власти объявили о льготных кредитах и налоговых послаблениях для малого и среднего бизнеса в регионах.', ], [ 'id' => 8, 'title' => 'Утечка данных в сфере онлайн-ритейла', 'description' => 'Интернет-магазин расследует утечку персональных данных клиентов после компрометации учётных записей сотрудников.', ], [ 'id' => 9, 'title' => 'Прорыв в медицине: новый метод диагностики', 'description' => 'Исследователи представили метод ранней диагностики заболеваний по биомаркерам, сокращающий время анализа.', ], [ 'id' => 10, 'title' => 'Сезонный рост заболеваемости', 'description' => 'В нескольких городах отмечен рост заболеваемости респираторными инфекциями, клиники усилили приём пациентов.', ], [ 'id' => 12, 'title' => 'Засуха и риски для сельского хозяйства', 'description' => 'Из-за продолжительной засухи фермеры прогнозируют снижение урожайности, обсуждаются меры поддержки аграриев.', ], [ 'id' => 13, 'title' => 'Финал крупного спортивного турнира', 'description' => 'В решающем матче сезона команда одержала победу в дополнительное время, установив новый рекорд по посещаемости.', ], [ 'id' => 14, 'title' => 'Трансфер игрока и усиление состава', 'description' => 'Клуб подписал контракт с новым нападающим, рассчитывая усилить атакующую линию перед серией дерби.', ], [ 'id' => 15, 'title' => 'Новые правила для маркетплейсов', 'description' => 'Регулятор предложил требования к маркировке товаров и прозрачности комиссий на торговых онлайн-платформах.', ], [ 'id' => 17, 'title' => 'Сбои в поставках полупроводников', 'description' => 'Производители электроники предупредили о задержках поставок чипов из-за ограничений экспорта и перегрузки заводов.', ], [ 'id' => 18, 'title' => 'Открытие фестиваля современного искусства', 'description' => 'В столице стартовал фестиваль современного искусства с выставками, перформансами и лекциями художников.', ], [ 'id' => 19, 'title' => 'Крупная сделка на рынке недвижимости', 'description' => 'Инвестфонд приобрёл портфель коммерческой недвижимости, планируя реконструкцию и повышение энергоэффективности.', ], [ 'id' => 20, 'title' => 'Исследование океана и новые данные', 'description' => 'Научная экспедиция собрала данные о течениях и температуре воды, уточнив прогнозы по изменению климата.', ], ];

Использование примера

Здесь всё просто - запускаем наш код и ждём результата.

require_once __DIR__ . '/../vendor/autoload.php'; use function Codewithkyrian\Transformers\Pipelines\pipeline; final class SemanticEventSearch {...} $events = [...]; $query = 'санкции против IT-компаний'; $search = new SemanticEventSearch(topN: 3); $search->setModel('Xenova/paraphrase-multilingual-MiniLM-L12-v2'); $search->setEvents($events); $search->setQuery($query); $out = $search->run(); $search->render($out['query'], $out['results']);

Логика работы (по шагам)

Если вам лень разбираться в логике работы класса SemanticEventSearch, то вы можете заглянуть в логику его работы ниже

Логика работы кода
  • Снаружи задаём:

    • список событий (setEvents)

    • модель (setModel)

    • запрос (setQuery)

    • topN через конструктор

  • Дальше — обычная логика в run():

    • Поднимаем embedder (pipeline('embeddings', model)) если ещё не поднят.

    • .transformers-cache:

      • при первом использовании модель и файлы токенизации/весов скачиваются и кладутся в .transformers-cache

      • дальше они берутся оттуда, чтобы не качать заново и работать быстрее

    • embeddings.events.json:

      • это наш локальный кэш эмбеддингов событий

      • пытаемся его прочитать и использовать только если model в кэше совпадает с текущей моделью

    • Если для каких-то событий эмбеддингов нет:

      • считаем эмбеддинги для description

      • сохраняем обратно в embeddings.events.json

    • Эмбеддим запрос (эмбеддинги нормализуются, чтобы косинусная близость была стабильной и сравнимой)

    • Считаем близость запроса к каждому событию (cosine similarity)

    • Сортируем, берём top‑N, возвращаем результаты

    • Рендер (вывод) делается снаружи через render(query, results)

Результат

На выходе мы получаем не совпадение слов, а совпадение по смыслу. Первый результат - наиболее подходящий по схожести с нашим запросом про "санкции против IT-компаний". И всё это - без сложной математики и без танцев с бубнами.

Query: санкции против IT-компаний [0.4288] #4 Ограничения против ИТ-сектора Правительство объявило о новых ограничениях для компаний, работающих в сфере информационных технологий. [0.3356] #15 Новые правила для маркетплейсов Регулятор предложил требования к маркировке товаров и прозрачности комиссий на торговых онлайн-платформах. [0.2598] #8 Утечка данных в сфере онлайн-ритейла Интернет-магазин расследует утечку персональных данных клиентов после компрометации учётных записей сотрудников.

Где это можно применять в проде

В целом это уже похоже на продуктовый подход, а не на эксперимент. К тому же вы можете легко заметить, что такой подход:

  • слабо привязан к конкретной формулировке запроса

  • хорошо работает на коротких описаниях

  • легко комбинируется с обычными фильтрами (дата, регион, тип события)

То есть это не "AI ради AI", а вполне конкретная прикладная логика. Та самая, которую можно объяснить, отладить и поддерживать.

Ограничения и подводные камни

Важно не обманываться: это не серебряная пуля.

Модели весят немало. Производительность нужно учитывать. Некоторые задачи проще и надёжнее решаются без AI обычным SQL.

Но это уже нормальный инженерный разговор – про trade-off’ы, а не про магию или чёрный ящик.

Куда двигаться дальше

Как для меня, то TransformersPHP - это хороший пример того, что AI можно использовать напрямую в PHP-проектах без смены стека и без Python.

В своей книге "AI для PHP-разработчиков" (открытой и бесплатной) я как раз и разбираю подобные кейсы: где это имеет смысл, как выбирать модели и как не превратить проект в набор экспериментальных фич.

Ссылка на книгу "AI для PHP-разработчиков".

Кстати, все примеры можно скачать и запустить через готовую среду Docker.

Или же вы также можете запускать все примеры из книги напрямую.

Если тема откликается – буду рад обсуждению и фидбэку.
Особенно интересно, какие задачи вы уже решаете или хотели бы решать с помощью AI в PHP-проектах.

Источник

Возможности рынка
Логотип Ucan fix life in1day
Ucan fix life in1day Курс (1)
$0.0004974
$0.0004974$0.0004974
+3.51%
USD
График цены Ucan fix life in1day (1) в реальном времени
Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу service@support.mexc.com для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.

Вам также может быть интересно

Galaxy Digital разрешает обратный выкуп акций на $200 млн на фоне восстановления котировок

Galaxy Digital разрешает обратный выкуп акций на $200 млн на фоне восстановления котировок

Galaxy Digital Holdings Ltd. объявила на этой неделе, что ее совет директоров одобрил программу обратного выкупа акций на сумму $200 миллионов для обыкновенных акций класса A компании. Galaxy
Поделиться
Coinstats2026/02/08 07:30
Сигнал следующей мем-монеты 1000x: APEMARS Stage 7 возглавляет лучшие криптовалюты для покупки сегодня с потенциалом роста 9763%, в то время как SHIB, FARTCOIN отстают

Сигнал следующей мем-монеты 1000x: APEMARS Stage 7 возглавляет лучшие криптовалюты для покупки сегодня с потенциалом роста 9763%, в то время как SHIB, FARTCOIN отстают

Мемы по-прежнему двигают рынки, но время решает, кто победит. Когда графики выравниваются, а нарративы повторяются, усталость наступает быстро. Последние новости о Bitcoin подчеркивают осторожность
Поделиться
Timestabloid2026/02/08 08:15
Саванна Гатри общается с похитителями матери в новом видео: «Мы получили ваше сообщение»

Саванна Гатри общается с похитителями матери в новом видео: «Мы получили ваше сообщение»

Произошло новое развитие событий в деле о похищении Нэнси Гатри, 84-летней матери со-ведущей шоу «Today» Саванны Гатри, поскольку Саванна опубликовала новое
Поделиться
Rawstory2026/02/08 08:13