Радикальный Дельфизм в эпоху AI: подключаем ИИ-ассистентов к OpenCV и FFmpeg через MCP. ai.. ai. delphi.. ai. delphi. ffmpeg.. ai. delphi. ffmpeg. onvif.. ai. delphi. ffmpeg. onvif. onvifdm.. ai. delphi. ffmpeg. onvif. onvifdm. opencv.. ai. delphi. ffmpeg. onvif. onvifdm. opencv. агенты.. ai. delphi. ffmpeg. onvif. onvifdm. opencv. агенты. искусственный интеллект.
Радикальный Дельфизм в эпоху AI: подключаем ИИ-ассистентов к OpenCV и FFmpeg через MCP - 1

Технологии ушли вперёд, и теперь мы живём в эру больших языковых моделей и автономных AI-агентов. В настоящее время существует несколько агентных систем, работающие с компьютерным зрением и камерами. Интеллектуальные видеоагенты обрабатывают видеопотоки в реальном времени, распознают объекты, анализируют поведение людей, фиксируют нарушения и действуют автономно. В основном – это готовые коммерческие ИИ-платформы для видеонаблюдения (например, Lumana, VisionPlatform.ai, Spot AI).
Для создания собственных решений можно настроить захват кадров (через Frame Forwarder) и передать их в визуальные модели обработки. Можно создавать логику на базе Amazon Bedrock Agents или фреймворков для ИИ-агентов (LangChain, CrewAI, AutoGen), где камера выступает как “инструмент” (take_snapshot()) восприятия.
Есть еще более специализированные решения – VisionAgent (от Landing AI), Microsoft AutoGen, LlamaIndex (Multimodal Agents).

А можно как-то по проще? Да еще из подручных средств? Да еще в «бытовые» агентные системы?
А давайте попробуем…

Дисклеймер:
Платформа: Windows, WSL
Язык: Object Pascal (Delphi)


Еще один дисклеймер:
Стиль, грамматика и пунктуация этой статьи причёсаны искусственным интеллектом. Автор честно пытался писать сам, но засомневался в своих деепричастных оборотах. Архитектурные UML-диаграммы рождены из множества попыток сформулировать для ИИ разумное техническое описание процессов. Ни один разработчик и ИИ не пострадали.
Если вы видите следы генерации – я их тоже вижу.

«Бытовые» мультиагентные системы в целом остаются «слепыми» – они заперты в текстовом контексте и локальных песочницах. Они не умеют напрямую работать с веб-камерами, сканировать локальную сеть на наличие IP-камер, быстро нарезать видео или распознавать лица в кадре без Node.js- или Python-окружения.
Ставить Python-окружение весом в гигабайт с кучей зависимостей ради того, чтобы ИИ-агент просто сделал скриншот с веб-камеры – это грех против нативной разработки.
В статье Delphi+OpenCV (сейчас Delphi-OpenCV-Class перенесен в private) был описан проект, который связал Object Pascal с миром компьютерного зрения. Импорт C++ классов напрямую из OpenCV 4.xx DLL по декорированным именам, использование соглашений о вызовах Windows x64 и Custom Managed Records в Delphi – позволили автоматически управлять жизненным циклом структур TMat (обёток для cv::Mat) и избавиться от утечек памяти. Всё это не пропало даром и сейчас перенесено в Delphi-OpenCV5 для версии 5.0. Кроме этого под рукой оказались – Delphi-FFmpeg и Delphi-ONVIF
Попробуем объединить это все и превратить в Delphi-движок media-mcp-server – быстрый и “родной” сервер протокола MCP (Model Context Protocol), в единую экосистему инструментов для AI-ассистента.

На всякий случай.
Model Context Protocol (MCP) – открытый стандарт взаимодействия между ИИ-клиентами (например, локальными ИИ-ассистентами или средами разработки) и внешними локальными инструментами (серверами), разработанный компанией Anthropic. Вся коммуникация идет по стандарту JSON-RPC. Клиент запрашивает список доступных функций (tools/list), а затем вызывает нужную команду с параметрами в формате JSON (tools/call), ожидая ответ от сервера.

Большинство готовых MCP-серверов написано на TypeScript (Node.js) или Python. Но если цель – быстрый медиа-процессинг на Windows-машине с поддержкой веб-камер и RTSP-потоков, нативный код даёт максимум:

  • Получаем один скомпилированный MediaMCPServer.exe и несколько DLL рядом, то есть решение практически с нулевыми зависимостями. Никаких гигабайтных node_modules, вызовов pip install и виртуальных окружений.

  • Сервер запускается мгновенно, потребляет минимальный объём RAM и выдаёт чистую нативную скорость при работе с видеопотоками. Потребление RAM в режиме ожидания составляет всего 12–15 МБ. При активной работе с DNN-моделями память расходуется только под структуры кадров в C++ DLL.

  • Работаем с кадрами OpenCV и потоками FFmpeg прямо в общей памяти процесса, не тратя ресурсы на межпроцессную сериализацию мегабайтных массивов пикселей.

Транспортных протоколов два:

  1. Классика stdio (стандартные потоки ввода-вывода) – клиент запускает исполняемый файл сервера как дочерний процесс и общается с ним через перенаправление потоков stdin и stdout. На первый взгляд, Pascal отлично подходит для работы с консолью, однако на практике выяснилось следующее:

    • Сторонние динамические библиотеки (в частности, библиотеки FFmpeg и OpenCV) при возникновении внутренних предупреждений пишут отладочные сообщения напрямую в стандартный вывод stdout. Этот мусор ломал парсер JSON-RPC на стороне ИИ-клиента, из-за чего сессия аварийно завершалась.

    • Использование Readln в Delphi по умолчанию буферизует ввод, что приводило к зависанию на некоторых типах сообщений, когда разделитель строк не совпадал с ожидаемым средой выполнения. Другие решения не искал, но наверное они есть.

    • stdio-транспорт не может работать через границу виртуализации (например, когда ИИ-клиент запущен в WSL Linux, а сам медиа-сервер с физическими USB-камерами и видеокартами с поддержкой CUDA должен работать на хост-системе Windows). По этой причине поддержка транспорта stdio в проекте была признана устаревшей и оставлена только для совместимости с простейшими локальными сценариями.

  2. Streamable HTTP – сейчас основной транспорт. Сервер запускается как самостоятельный процесс и слушает порт, а клиент подключается к нему по HTTP, получая ответы на запросы в реальном времени. Это гарантирует изоляцию и стабильность сессий.

Архитектура проекта построена на легковесном HTTP-сервере, написанном на WinSock API (uMCPHttpServer.pas). Он слушает входящие POST-запросы и распределяет их на уровне HTTP-сессий.

Взаимодействие компонентов сервера, ИИ-клиента и нативных библиотек обработки медиа

Взаимодействие компонентов сервера, ИИ-клиента и нативных библиотек обработки медиа

Также далее для информации – показан сценарий обработки сообщений внутри самого Windows-хоста Media-MCP-Server (от приёма HTTP-запроса до вызова OpenCV/FFmpeg и отправки HTTP-ответа)

Радикальный Дельфизм в эпоху AI: подключаем ИИ-ассистентов к OpenCV и FFmpeg через MCP - 3

Сервер поддерживает сессии клиентов с помощью HTTP-заголовка Mcp-Session-Id. Жизненный цикл сессии и обработка запросов реализованы в методе ProcessHttpRequest (uMCPHttpServer.pas):

procedure TMCPHttpServer.ProcessHttpRequest(ASocket: NativeUInt; const Method, Path, Body: string;
  const Headers: TDictionary<string, string>);
var
  SessionId, ResponseJson, MethodName: string;
  StatusCode: Integer;
  StatusText, ContentType, ExtraHeaders, ResponseBody: string;
  JsonVal: TJSONValue;
  JsonReq: TJSONObject;
begin
  StatusCode := 200; StatusText := 'OK';
  ContentType := 'application/json'; ExtraHeaders := ''; ResponseBody := '';

  if not SameText(NormalizePath(Path), FConfig.HttpPath) then
  begin
    StatusCode := 404; StatusText := 'Not Found';
    Exit;
  end;

  SessionId := HeaderValue(Headers, 'Mcp-Session-Id');

  if SameText(Method, 'DELETE') then
  begin
    RemoveSession(SessionId);
    StatusCode := 200; ResponseBody := '{"status":"session_closed"}';
  end
  else if SameText(Method, 'POST') then
  begin
    JsonVal := TJSONObject.ParseJSONValue(Body);
    if JsonVal is TJSONObject then
    try
      JsonReq := JsonVal as TJSONObject;
      JsonReq.TryGetValue<string>('method', MethodName);

      // При initialize создаем новую сессию
      if SameText(MethodName, 'initialize') then
      begin
        SessionId := CreateSessionId;
        AddSession(SessionId);
        ResponseJson := FHandler.ProcessMessage(Body);
        ResponseBody := ResponseJson;
        ExtraHeaders := ExtraHeaders + 'Mcp-Session-Id: ' + SessionId + #13#10;
      end
      else
      begin
        // Для обычных вызовов проверяем существование сессии
        if not SessionExists(SessionId) then
        begin
          StatusCode := 401; ResponseBody := '{"error":"Unauthorized session"}';
        end
        else
        begin
          ResponseJson := FHandler.ProcessMessage(Body);
          ResponseBody := ResponseJson;
          ExtraHeaders := ExtraHeaders + 'Mcp-Session-Id: ' + SessionId + #13#10;
        end;
      end;
    finally
      JsonVal.Free;
    end;
  end;

  SendHttpResponse(ASocket, StatusCode, StatusText, ContentType, ExtraHeaders, ResponseBody);
end;

Внутри uMCPHandler.pas JSON-запрос от ИИ-агента разбирается с помощью базовой диспетчеризации методов:

function TMCPHandler.ProcessMessage(const JsonText: string): string;
...
begin
  // Парсим JSON и извлекаем jsonrpc, method, id и params
  ...
  if Method = 'initialize' then
    Exit(BuildInitializeResponse(Id, Params))
  else if Method = 'tools/list' then
    Exit(BuildToolsListResponse(Id))
  else if Method = 'tools/call' then
    Exit(BuildToolsCallResponse(Id, Params))
  else if Id <> nil then
    Exit(BuildErrorResponse(Id, -32601, 'Method not found: ' + Method));
end;

Когда ИИ-агент запрашивает tools/list, возвращаем ему массив JSON-объектов, описывающих наши возможности. Например, инструмент для захвата кадра с веб-камеры регистрируется так:

// Описание свойства в uMediaEngine.pas
Tool := TJSONObject.Create;
Tool.AddPair('name', 'webcam_grab_frame');
Tool.AddPair('description', 'Grab a frame from a local webcam by its index and save it as a JPEG image.');
Schema := TJSONObject.Create;
Schema.AddPair('type', 'object');
// Описание свойств: cameraIndex (integer), outputPath (string, required), etc.
...
Tool.AddPair('inputSchema', Schema);
Result.Add(Tool);

Вся функциональность сервера разделена на логические модули на Object Pascal. Каждый модуль отвечает за определенный круг задач и регистрирует соответствующие JSON-RPC инструменты, с которыми может взаимодействовать ИИ-ассистент.

Для реализации функциональности потребовалось не так уж и много модулей. Модули обеспечивают инфраструктуру сервера, парсинг JSON-RPC и коммуникацию с клиентом:

  • MediaMCPServer.dpr – точка входа консольного приложения. Обрабатывает аргументы командной строки и инициализирует stdio или HTTP транспорт.

  • uMCPServer.pas – стандартный stdio-транспорт. Читает stdin, передает запросы обработчику и отправляет ответы в stdout.

  • uMCPHttpServer.pas – встроенный HTTP-сервер для кросс-окружения (WSL/Windows). Поддерживает REST API и SSE (Server-Sent Events) для уведомлений.

  • uMCPHandler.pas – логический диспетчер JSON-RPC. Проверяет структуру пакетов, обрабатывает запросы инициализации и перенаправляет вызовы инструментов.

  • uMCPConfig.pas – управление конфигурациями, путями к DLL и рабочими каталогами.

  • uMediaEngine.pas – оркестратор, содержит метаданные и схемы параметров для всех 47 инструментов (их список с пояснениями – ниже), а также распределяет вызовы методов к соответствующим модулям.

Модуль uONVIFTools.pas, реализованный на базе Delphi-ONVIF, отвечает за интеграцию с IP-камерами по протоколу ONVIF (и содержит 6 инструментов):

  • camera_discover – автоматический поиск ONVIF IP-камер в локальной подсети по WS-Discovery (UDP Multicast).

  • camera_get_stream_uri – запрос RTSP-ссылки на медиапоток с камеры (с возможностью выбора профиля качества).

  • camera_ptz_move – управление движением поворотной камеры (Pan, Tilt, Zoom) с заданной скоростью и длительностью.

  • camera_ptz_stop – мгновенная остановка любого PTZ-движения камеры.

  • camera_get_imaging_settings – получение параметров сенсора камеры (яркость, контрастность, резкость, насыщенность).

  • camera_set_imaging_settings – изменение настроек изображения на физической камере.

Модули uOpenCVTools.pas и uOpenCVHelpers.pas (на базе Delphi-OpenCV5) выполняют базовый захват медиаданных и простейший анализ (содержат 4 инструмента):

  • webcam_list – сканирование DirectShow/MSMF системных устройств для вывода списка подключенных USB-камер.

  • webcam_grab_frame – захват кадра с локальной USB-камеры (или RTSP-потока) с сохранением на диск в JPEG.

  • image_detect_objects – базовая детекция объектов на изображении с возвратом координат.

  • image_detect_faces – быстрая детекция человеческих лиц с помощью детектора YuNet.

Блок FFmpeg Tools (использует Delphi-FFmpeg) – инструменты этого модуля вызывают функции FFmpeg DLL напрямую для работы с медиаконтейнерами без перекодирования (или с минимальным декодированием). Он объединяет файлы uFFmpegTools.pas, uFFmpegProbe.pas и uFFmpegHelpers.pas и содержит 15 инструментов:

  • video_probe – технический анализ видеофайла (длительность, FPS, кодеки, параметры аудио).

  • stream_test – проверка доступности сетевого потока или файла с замером пинга и чтением метаданных.

  • video_grab_frame – вырезание одиночного кадра в JPEG на определенной секунде видеофайла.

  • video_grab_frames – серийная нарезка кадров из видеофайла через заданные интервалы времени.

  • video_thumbnail – быстрое создание миниатюры (превью) заданного размера из видео.

  • video_remux – смена контейнера видеофайла (например, .mkv в .mp4) без перекодирования потоков.

  • video_trim – быстрая обрезка видео по таймкодам начала и конца (без транскодирования).

  • video_concat – склейка нескольких видеофайлов одного формата в один файл.

  • audio_extract – извлечение аудиодорожки из видео в формате сырого PCM S16LE.

  • video_record_segment – запись отрезка живого RTSP-вещания заданной длительности в файл.

  • video_scale – изменение разрешения видео с перекодированием.

  • video_filter – применение цепочки фильтров FFmpeg (например, кадрирование, водяные знаки, цветокоррекция).

  • video_detect_silence – анализ аудиодорожки на предмет обнаружения периодов тишины ниже заданного уровня дБ.

  • video_scene_detect – обнаружение монтажных склеек (смен сцен) на основе разности кадров.

  • video_metadata_read – чтение тегов и метаданных из медиафайла.

Раздел OpenCV DNN Tools (на базе Delphi-OpenCV5) представлен модулем uOpenCVDnnTools.pas. Он использует модуль cv::dnn из OpenCV для работы с ONNX- и TensorFlow-моделями и предоставляет 8 инструментов:

  • image_classify – классификация изображения по 1000 классам с помощью свёрточной сети MobileNetV2.

  • image_segment_person – сегментация силуэта человека в кадре (PP-HumanSeg) с сохранением маски в PNG.

  • image_detect_text – обнаружение текстовых блоков и полигонов их границ с помощью OCR-модели PP-OCR.

  • image_detect_text_east – детектор текста EAST для быстрого поиска повёрнутых областей текста.

  • face_compare – сравнение двух лиц по геометрии (SFace) с вычислением косинусного сходства.

  • face_enroll – регистрация нового лица в локальную базу данных (сохранение вектора признаков).

  • face_identify – распознавание лица путём поиска по зарегистрированной локальной базе векторов лиц.

  • face_list – вывод списка имён всех зарегистрированных лиц.

В OpenCV 5.0 модуль cv::dnn позволяет запускать глубокие нейросети «из коробки» без разворачивания окружения PyTorch или TensorFlow. В проекте media-mcp-server используются преднастроенные и оптимизированные ONNX- и Protobuf-модели.

  1. YuNet (face_detection_yunet) – сверхбыстрое и легковесное обнаружение лиц. Позволяет находить в кадре лица людей (даже если они частично перекрыты, обращены в профиль или выражают удивление) и возвращает координаты описывающего прямоугольника, а также точные координаты 5 ключевых ориентиров лица (глаза, нос, уголки губ) и оценку уверенности (confidence).

  2. SFace (face_recognition_sface) – распознавание и сравнение лиц. Принимает два вырезанных и выровненных по ключевым точкам лица, извлекает из них вектор признаков (эмбеддинг из 128 чисел типа float) и вычисляет косинусное сходство. Это позволяет ИИ-агенту определять степень сходства лиц.

  3. YOLOX (object_detection_yolox) – универсальный детектор объектов. Находит в кадре до 80 типов распространенных объектов (люди, машины, сумки, котики) и возвращает их координаты. Подходит для сценариев вроде «проверь, есть ли машины перед воротами».

  4. MobileNetV2 (image_classify) – классификация изображения. Определяет, какой объект изображён на картинке, возвращая идентификатор класса из базы ImageNet (1000 классов).

  5. PP-HumanSeg (human_segmentation_pphumanseg) – сегментация силуэта человека. Выделяет людей на изображении попиксельно. Полезно для автоматической изоляции людей в кадре с целью сохранения их признаков в базу данных.

  6. PP-OCR / EAST (text_detection_ppocr / frozen_east_text_detection) – обнаружение текста (OCR). Локализует области, содержащие печатный текст под любым углом. PP-OCR строит полигоны вокруг слов, а EAST строит повёрнутые ограничивающие рамки. Это первый шаг для последующего распознавания текста.

  7. TrackerNano (video_track_object) – локальный трекинг объектов на видео. Трекер на базе ONNX, сопровождающий произвольный объект, однажды выделенный рамкой, на протяжении всего видеоряда. Это позволяет не запускать ресурсоёмкую детекцию на каждом кадре, а следовать за целью.

OpenCV Image Tools (Delphi-OpenCV5) – модуль uOpenCVImgTools.pas геометрические трансформации, фильтрация и классическое компьютерное зрение (11 инструментов):

  • image_read_qrcode – распознавание и декодирование QR-кодов на изображении.

  • image_encode_qrcode – генерация QR-кода по текстовой строке с сохранением в JPEG.

  • image_read_barcode – сканирование и декодирование штрихкодов.

  • image_detect_aruco – детекция маркеров позиционирования ArUco (для робототехники).

  • image_template_match – поиск эталонного фрагмента (шаблона) на большом изображении.

  • image_find_contours – выделение контуров объектов и определение самого крупного из них.

  • image_detect_edges – выделение границ на изображении методом Canny.

  • image_detect_lines – поиск прямых линий с помощью преобразования Хафа.

  • image_detect_circles – поиск окружностей с помощью преобразования Хафа.

  • image_transform – трансформация кадра (поворот на произвольный угол, обрезка, изменение разрешения).

  • image_annotate – графическая разметка изображения (нанесение рамок, подписей и масок детекции YOLO/YuNet).

OpenCV Video Tools (Delphi-OpenCV5) – модуль uOpenCVVideoTools.pas алгоритмы, требующие анализа последовательности кадров (3 инструмента):

  • webcam_record_video – фоновая запись видео с USB-камеры заданной длительности без создания графических окон Windows.

  • video_track_object – отслеживание перемещения объекта в видеопотоке с помощью трекера TrackerNano.

  • image_optical_flow – вычисление плотного оптического потока Фарнебека между парой кадров для анализа движения.

Чтобы ИИ-агент понимал, как правильно вызывать эти инструменты и связывать их с окружением используется базовый системный промпт (простой):

Вы имеете доступ к локальному медиа-серверу MediaMCPServer через протокол MCP. Правила работы с инструментами:

  1. Для обнаружения камер в сети используйте camera_discover.

  2. Если ИИ-клиент запущен на той же машине, всегда указывайте локальные пути (например, C:/temp/frame.jpg) в параметрах сохранения изображений. Не запрашивайте Base64 без необходимости.

  3. При поиске людей используйте связку image_detect_faces -> face_compare для сравнения лиц с базой данных.

  4. При отслеживании движения в реальном времени используйте video_track_object вместо серийного вызова детектора для экономии CPU/GPU.

Основная проблема возникла при попытке передать обработанное изображение обратно в контекст языковой модели. Первоначальный «артефакт» проектирования, первая попытка в лоб – передача сырого изображения высокого разрешения (например, кадра 4K с IP-камеры) в Base64 прямо внутри JSON-RPC ответа. Это приводило к раздуванию JSON до десятков мегабайт, из-за чего парсеры ИИ-клиентов аварийно завершали сессию по тайм-ауту. К тому же отправка больших Base64-строк быстро исчерпывала контекстное окно ИИ-моделей. Попытки сжимать изображения на лету до превью размером 120×120 пикселей решали проблему объёма данных, но делали картинку бесполезной – ИИ-модель видела пиксельную кашу и не могла разобрать текст на автомобильных номерах или мелкие объекты.

Сейчас в силу отсутствия лучшего решения – используется гибридный конвейер. Передача изображений происходит по схеме:

  • Если ИИ-клиент запущен локально на той же машине, сервер сохраняет кадр в оригинальном качестве в каталог data/media/ и возвращает только текстовый путь к файлу. Клиент сам считывает его с диска. Когда агент вызывает webcam_grab_frame, он передает параметр outputPath (например, ...datamediaframe.jpg). ИИ-модель оперирует строкой в несколько десятков символов.

  • Если требуется прямая передача данных (или клиент работает удаленно), сервер выполняет адаптивное масштабирование кадра (downscaling с сохранением соотношения сторон) и сжатие в JPEG с качеством 80%, после чего кодирует полученный буфер в Base64. Это дает баланс между детализацией картинки и размером JSON.

Когда агент вызывает инструмент image_detect_faces, управление переходит к Delphi-коду, который загружает изображение в TMat и запускает легковесную сеть YuNet:

var
  Img, Faces: TMat;
  Detector: TFaceDetectorYN;
begin
  Img := TMat.imread(ImagePath);
  if Img.empty then
    raise Exception.Create('Could not read image');

  // YuNet требует инициализации под размер входного изображения
  Detector := TFaceDetectorYN.create(ModelPath, '', Img.size);
  Faces := TMat.Create;
  try
    Detector.detect(Img, Faces);
    // Извлекаем bounding box'ы лиц, координаты 5 ключевых ориентиров и confidence
    ResultJSON := ParseFacesToJSON(Faces);
  finally
    Faces.Free;
  end;
end;

Отдельно – интеграция системы с мультиагентными средами. В рамках тестирования подключали media-mcp-server к мультиагентной системе, работающей под WSL. Это потребовало создания сетевого и межпроцессного моста между Windows и виртуальным окружением Linux (WSL), поскольку агенты запускались в контексте Linux и ожидали, что MCP-сервер будет работать там же.

Работа в виртуализированном окружении Linux сопряжена со сложностями. WSL не имеет прямого доступа к физическим USB-устройствам Windows-хоста. Захват картинки с локальной веб-камеры ноутбука из Linux-контейнера требует проброса USB-портов через утилиты вроде usbipd-win. На практике это решение оказалось нестабильным, требовало прав администратора и периодически отваливается. Кроме того, для быстрой работы нейросетевых моделей в OpenCV (например, детекции объектов или лиц) важен прямой доступ к GPU. Настройка проброса CUDA внутри WSL технически возможна, но сложна и снижает производительность по сравнению с работой на физическом хосте, где уже установлены родные драйверы видеокарты и библиотеки OpenCV 5.0 с поддержкой CUDA.

Рассматривались и альтернативные варианты. Например, запуск сервера внутри WSL через Wine не увенчался успехом – Wine не смог стабильно работать с драйверами захвата видео DirectShow/MSMF и падал при инициализации камеры. Идея написать отдельный Python-демон для трансляции потоков из Windows в WSL через IP-сеть также не выстрелила, поскольку это усложняло архитектуру. Использование stdio-транспорта – не подошло из-за невозможности работы через границу WSL/Windows без перенаправления пайпов через SSH.

В результате окончательно – Streamable HTTP-сервер, и это решение полностью себя оправдало.

ИИ-клиент внутри WSL Linux общается с сервером по протоколу HTTP (через сетевой мост WSL). В репозитории проекта есть готовые шаблоны конфигурационных файлов (например, config/mcp.json.template для stdio (это осталось от старых попыток, не используйте это) и config/mcp.http.json.template для HTTP).

Радикальный Дельфизм в эпоху AI: подключаем ИИ-ассистентов к OpenCV и FFmpeg через MCP - 4

Внутри WSL запускается скрипт, вычисляющий IP-адрес Windows-хоста (через /etc/resolv.conf) и прописывающий в конфигурационный файл ИИ-клиента ссылку на этот адрес (по аналогии с шаблоном config/mcp.wsl.http.json.template):

"media-mcp-server": {
  "url": "http://<windows_host_ip>:8765/mcp"
}

Получилось довольно-таки неплохое разделение обязанностей – тяжёлая математика нейросетей выполняется Delphi-сервером на GPU хоста, хранение векторов берёт на себя специализированная СУБД, а LLM работает только с JSON-вызовами. Лёгкий ИИ-агент функционирует в WSL Linux, а медиа-сервер MediaMCPServer.exe выполняется на Windows-хосте, получая прямой доступ к аппаратному ускорению видеокарты и физическим веб-камерам без задержек виртуализации.

В мультиагентных ИИ-системах один специализированный агент редко решает задачу в одиночку. В рамках тестирования под media-mcp-server были созданы:

  • Агент-Наблюдатель (Сенсорный уровень) – использует media-mcp-server для физического взаимодействия с миром (захват кадров, детекция лиц, объектов, считывание QR-кодов).

  • Агент-Хранитель (условно – Гиппокамп) – использует векторные и семантические базы данных через специализированные MCP (например, vector-memory-mcp или knowledge-graph-mcp).

  • Агент-Аналитик – оркестратор, сопоставляет информацию и принимает решения.

Типичный сценарий долговременного отслеживания и ведения логов событий:

  1. Когда Агент-Наблюдатель вызывает face_identify или image_detect_faces, сервер (mcp) находит лицо и вычисляет его математический вектор признаков (эмбеддинг размерностью 128 (float) с помощью модели SFace). Этап сбора эмбеддингов.

  2. Вместо сохранения видео, Агент-Аналитик преобразует это событие в текстовое описание и вектор:

    • Метаданные: {"event": "face_detected", "name": "Иванов И.И.", "confidence": 0.92, "timestamp": "2026-06-23T12:00:00Z", "location": "Вход в корпус А"}

    • Вектор: Вектор признаков лица SFace, полученный от mcp.

  3. Агент обращается к vector-memory-mcp и вызывает инструмент store_memory (или create_entities в Graph-базе данных), сохраняя этот вектор и метаданные.

  4. Позже, когда пользователю нужно найти информацию о перемещениях человека, Агент-Аналитик выполняет семантический поиск: “Когда в последний раз видели Иванова?”. Векторная база выдает сохраненные записи с разных камер. Агент сопоставляет их по времени (timestamp) и строит хронологическую цепочку: “Сначала Иванов зашел в корпус А в 12:00 (Камера 1), затем прошел по коридору второго этажа в 12:02 (Камера 5) и зашел в аудиторию 204 в 12:05 (Камера 12)”.

Один из вопросов – «Можно ли перенести этот сервер на Linux/macOS, чтобы запускать его в Docker-контейнерах или на MacBook?»
Object Pascal позволяет сделать это – да, но есть нюанс. Здесь есть два пути развития проекта.
Данные предположения носят теоретический характер, так как практические тесты на этих платформах пока не проводились.

Если необходимо иметь полностью бесплатный и свободный кроссплатформенный стек, надо переходить на Free Pascal. В FPC есть поддержка расширенных записей (Advanced Records) и перегрузка операторов.
Но общий план адаптации выглядит следующим образом:

  1. Нужна stdio-обёртка. Надо переписать чтение/запись под UNIX-потоки ввода-вывода (с использованием файловых дескрипторов StdInput и StdOutput в FPC) и учесть специфику неблокирующего чтения в POSIX-системах.

  2. Заменить модули System.JSON (специфичные для Delphi) на fpjson и jsonparser (Lazarus).

  3. Использовать пакет fphttpapp (встроенный в FPC HTTP-сервер) или библиотеку Synapse для поднятия REST API на Linux/macOS.

  4. Поскольку FPC не поддерживает автоматическое декорирование С++ имён классов при линковке на не-Windows системах, собрать разделяемую библиотеку (.so под Linux, .dylib под macOS), которая скроет вызовы C++ классов OpenCV 5.0 за плоским C-интерфейсом. После выполнения этих шагов проект будет готов к компиляции под целевую платформу, например: fpc -Mdelphi -Tlinux -Pxd86_64 MediaMCPServer.lpr. Ха!

Ещё один путь – использование Delphi Enterprise (DCCLinux / DCCMac). Delphi компилируется под Linux x64 и macOS, поэтому вся системная логика (System.JSON, System.Net.HttpClient) перенесётся без изменений.
Но там есть свои сложности:

  1. Под Windows x64 действует соглашение Microsoft x64 calling convention, под Linux и macOS x64 – System V AMD64 ABI. Из-за разницы в передаче параметров (в System V ABI структуры до 16 байт передаются непосредственно через регистры RDI, RSI, а не по ссылке) заголовки импорта OpenCV могут приводить к Access Violation. Delphi все непростые типы (например, записи) даже размером 1 байт передаёт как адрес. Чтобы избежать проблем с ABI, придётся переписать сигнатуры импортируемых методов, принимающих TPoint, TRect или TSize, объявив их как указатели (pTRect, const или const [ref]). Фактически это потребует полного переписывания биндинга OpenCV.

  2. Можно разработать Flat C Wrapper. Собрать C++ библиотеку-посредник libopencv_wrapper.so (или .dylib под macOS), которая экспортирует C-совместимые функции (где все структуры передаются только по ссылке или указателю) с соглашением cdecl. Это избавит от привязки к ABI конкретной ОС на стороне Delphi. После выполнения этих шагов останется настроить PAServer, собрать проект под Linux или macOS, положить рядом динамические библиотеки OpenCV и FFmpeg и указать к ним пути через переменные окружения LD_LIBRARY_PATH (или DYLD_LIBRARY_PATH в macOS) при запуске. Ха!

В заключение – несколько примеров. Сессия взаимодействия с мультиагентной системой, когда ИИ получает в своё распоряжение media-mcp-server.

Запрос пользователя в чате к ИИ-ассистенту:

Найди IP-камеры в сети. Если найдешь камеру на парковке, забери с нее один кадр и скажи, есть ли свободные места.

Цепочка действий агента:

  1. Агент видит в списке доступных инструментов camera_discover и вызывает его.

  2. Delphi-сервер сканирует сеть по ONVIF и возвращает:

    [{"ip": "192.168.1.50", "name": "Parking-Gate-Camera"}]
  3. Агент вызывает инструмент camera_get_stream_uri с параметром cameraIp: "192.168.1.50". Delphi-сервер отправляет камере ONVIF-запрос и возвращает RTSP-адрес потока:

    {"streamUri": "rtsp://admin:pass@192.168.1.50:554/live/main"}
  4. Агент вызывает webcam_grab_frame, передавая полученную RTSP-ссылку в cameraUrl и путь сохранения на диске.

  5. Cервер инициализирует захват кадра через cv::VideoCapture, декодирует RTSP-поток с помощью FFmpeg, забирает кадр, сохраняет его на диск как JPEG и сообщает об успехе.

  6. Агент считывает полученную картинку с диска, отправляет её в визуальную модель и отвечает пользователю:

    На парковке обнаружено 2 свободных места в первом ряду. Одно место около въезда свободно, второе занято синей машиной, но рядом с ней есть свободный карман.

Вся цепочка выполняется автономно за 8–10 секунд. Не нужно писать сценарии интеграции – ИИ-агент сам решает, в какой последовательности вызывать предоставленные сервером инструменты.

Радикальный Дельфизм в эпоху AI: подключаем ИИ-ассистентов к OpenCV и FFmpeg через MCP - 5

Есть другой пример – случай (на этапе тестирования) аудита безопасности в университетской сети, где media-mcp-server выступил в роли «глаз» автономного ИИ-агента.

Задача для ИИ-агента:
Пользователь загружает фотографию студента и отправляет запрос: «Найди, в какую аудиторию зашёл этот студент за последние 15 минут. Доступ к локальной сети корпуса у тебя есть».

Ход процесса:

  1. Агент вызывает инструмент camera_discover. Delphi-сервер отправляет UDP Multicast по протоколу WS-Discovery. Так как сегмент видеонаблюдения не был изолирован от университетской беспроводной сети на уровне VLAN, агент обнаруживает в подсети корпуса 34 ONVIF-совместимые камеры.

  2. Агент пытается запросить RTSP-адреса потоков через camera_get_stream_uri. Поскольку инструмент требует авторизации, агент методично пробует дефолтные пары (admin:admin, admin:12345, admin:password – это мы его научили). На 3 коридорных камерах авторизация проходит успешно, и агент получает их RTSP-ссылки.

  3. Чтобы не перегружать сетевой канал и CPU/GPU сервера передачей Base64, агент настраивает дискретный опрос – каждые 3 секунды он делает снимок через webcam_grab_frame на скомпрометированных камерах, запускает быструю детекцию лиц image_detect_faces (YuNet) и сравнивает результаты с целевым фото через face_compare.

  4. На камере возле лифтового холла модель распознавания лиц SFace возвращает косинусное сходство 0.47 (при установленном пороге совпадения 0.36). Студент обнаружен в кадре.

  5. Агент считывает метаданные ONVIF-камер и анализирует их имена: ONVIF_Cam_Fl4_Lobby (где был замечен студент) и ONVIF_Cam_Fl4_West (коридор). Он переключает фокус опроса кадров на камеру коридора.

  6. Из-за задержек дискретного опроса (интервал в 3 секунды) студент успевает скрыться в одной из дверей. Агент фиксирует, что объект прошёл мимо первой камеры коридора, но не появился на камере в конце коридора.

  7. Агент выдаёт пользователю отчёт – «Целевой субъект зафиксирован в лифтовом холле в 12:47:15. Проследовал по коридору. В 12:47:30 скрылся из поля зрения. С высокой вероятностью вошёл в одну из аудиторий в начале коридора, так как на конечной камере коридора обнаружен не был»*.

После демонстрации логов расследования руководству департамента ИТ, уязвимости были оперативно устранены. На всех камерах принудительно обновили пароли, отключили протокол WS-Discovery для гостевых подсетей и настроили списки контроля доступа (ACL) на коммутаторах, изолировав трафик видеонаблюдения от общей сети.

Ну и в заключение.

Delphi продолжает замечательно чувствовать себя в современную эру искусственного интеллекта. Скорость компиляции, низкое потребление ресурсов и возможность интеграции с C/C++ кодом делают Object Pascal хорошим выбором для написания легковесных системных утилит. В этом случае – связывающих физический мир с возможностями больших языковых моделей.

  • Полный исходный код проекта, скрипты автоматической настройки MCP-клиентов и шаблоны конфигураций доступны на GitHub: MediaMCPServer.

  • Дополнительные примеры использования с описанием различных сценариев вызова инструментов ИИ-агентом собраны в файле EXAMPLES.md.

  • Буду рад вашим комментариям, замечаниям, идеям новых инструментов. Ну и ругайте, конечно…

Автор: Laax

Источник