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

от автора

Технологии ушли вперёд, и теперь мы живём в эру больших языковых моделей и автономных 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-ответа)

Сервер поддерживает сессии клиентов с помощью 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.pasTool := 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 (например, ...\data\media\frame.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).

Внутри 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 секунд. Не нужно писать сценарии интеграции – ИИ-агент сам решает, в какой последовательности вызывать предоставленные сервером инструменты.

Есть другой пример – случай (на этапе тестирования) аудита безопасности в университетской сети, где 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.

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

ссылка на оригинал статьи https://habr.com/ru/articles/1051210/