MCP-сервер с vision-моделью GLM-5.1: зрение для кодинг-агента | AiManual
AiManual Logo Ai / Manual.
29 Апр 2026 Гайд

Как дать кодинг-агенту зрение: MCP-сервер с локальной vision-моделью GLM-5.1

Пошаговый гайд по созданию MCP-сервера с локальной моделью GLM-5.1 Vision 8B для анализа скриншотов и UI. Как дать агенту глаза без облака.

Слепой программист — это грустно

Кодинг-агенты отлично пишут код. Они могут рефакторить, дебажить, генерировать целые файлы. Но есть одна проблема — они слепые. Буквально. Они не видят, что происходит на экране. Попроси агента исправить верстку — он может сгенерировать новый CSS, но не поймет, что кнопка наезжает на заголовок. Попроси разобраться с падением тестов — он прочитает логи, но не увидит скриншот ошибки. Это как чинить механизм, не глядя на него.

Решений на рынке два: либо вы вручную описываете агенту, что видите на экране („тут кнопка съехала вправо, а фон стал фиолетовым“), либо подключаете облачную vision-модель (GPT-4V, Claude 3.5 Vision) и молитесь на приватность и ценник. Первое — треш для продуктивности. Второе — треш для кошелька и безопасности.

Есть третий путь, и о нем я расскажу: поднять локальный MCP-сервер с моделью GLM-5.1 Vision 8B — легковесной, но достаточно умной, чтобы читать скриншоты, распознавать элементы интерфейса и описывать баги. Агент дергает MCP-тул, получает текстовое описание того, что видит, и действует. Всё локально, без утечек, без подписок, с латентностью 1-3 секунды на среднестатистической 3090.

Зачем это нужно? Агент перестает быть просто „писателем кода“. Он становится настоящим ревьювером, тестировщиком, дизайнером. Он видит результат своей работы так же, как человек. Это меняет подход к автономной разработке.

Почему именно GLM-5.1 Vision?

Моделей vision много: LLaVA, InternVL, Qwen-VL, CogVLM. Но GLM-5.1 (выпуск февраля 2026) — это свежая модель с архитектурой, адаптированной под 8B параметров. Она показывает лучшее соотношение качества и скорости среди моделей такого размера. В бенчмарках MMBench-V14 она выбивает 78.2% — выше, чем Qwen2-VL-7B (74.5%) и LLaVA-NeXT-8B (71.8%). При этом квантизированная версия (q4_k_m) весит всего 5.2 ГБ и влезает в одну 3090 с DDR5.

Но главное — она понимает экраны: кнопки, иконки, текст в капчах, расположение элементов. На тестах с UI-скриншотами (ScreenQA, VizWiz) GLM-5.1 Vision обгоняет многих гигантов. А еще модель поддерживает мульти-изображения в одном запросе — важно для сравнения скриншотов "было/стало".

МодельРазмерMMBenchScreenQALatency (3090)
GLM-5.1 Vision 8B8B78.2%81.5%1.8 с
Qwen2-VL-7B7B74.5%77.2%2.1 с
LLaVA-NeXT-8B8B71.8%73.8%2.4 с

Важно: GLM-5.1 Vision не имеет лицензионных ограничений на коммерческое использование (MIT), в отличие от некоторых аналогов. Но проверьте сами перед деплоем.

Анатомия MCP-сервера с глазами

MCP (Model Context Protocol) — это стандарт для подключения тулов и источников данных к LLM. Сегодня MCP-клиенты есть в llama.cpp, kilo-code, continue.dev и десятке других инструментов. Нам нужно написать свой MCP-сервер, который предоставляет тул analyze_screenshot. Агент вызывает этот тул, передавая путь к скриншоту или base64-изображение, а сервер возвращает JSON с текстовым описанием и списком найденных элементов.

Архитектура такова:

  • MCP-сервер на Python (FastMCP или uvicorn)
  • Инференс через transformers с загрузкой модели GLM-5.1 Vision в half-precision
  • Кэширование запросов (скриншоты редко меняются)
  • Поддержка batch и очередь по приоритетам
💡
Если у вас мало видеопамяти (8 ГБ), используйте квантованную версию через llama.cpp — она работает быстрее, чем полная модель на CPU. Мы еще вернемся к этому.

1Настройка окружения

Создаем чистую среду с Python 3.12, ставим зависимости. Не забудьте про CUDA 12.4, если используете GPU. Я покажу минимальный набор.

# Создаем venv
python3.12 -m venv mcp_vision_env
source mcp_vision_env/bin/activate

# Ставим пакеты
pip install mcp transformers accelerate bitsandbytes pillow requests

# Для квантованной версии через llama.cpp отдельно ставим llama-cpp-python
pip install llama-cpp-python --force-reinstall --upgrade --no-cache-dir
# или с поддержкой CUDA: CMAKE_ARGS="-DLLAMA_CUDA=on" pip install llama-cpp-python

Не ставьте bitsandbytes для 4-битной квантизации, если у вас RTX 30xx/40xx — есть конфликты с новыми ядрами. Лучше используйте родную квантизацию из transformers или сразу квантованный GGUF.

2Скачиваем и запускаем модель

Модель доступна на HuggingFace: THUDM/glm-5.1-vision-8b. Нам понадобится токен для доступа (git-lfs).

pip install huggingface_hub
huggingface-cli login
# введите токен
huggingface-cli download THUDM/glm-5.1-vision-8b --local-dir ./models/glm-5.1-vision-8b

Если места на диске меньше 16 ГБ, скачайте GGUF-версию (q4_k_m) через huggingface-cli download TheBloke/GLM-5.1-Vision-8B-GGUF glm-5.1-vision-8b-q4_k_m.gguf --local-dir ./models/. Она быстрее грузится и меньше жрет RAM.

Теперь создаем файл vision_server.py. Полный листинг дам в следующем шаге, но суть такая: загружаем модель, создаем MCP-приложение, регистрируем тул.

3Пишем MCP-сервер (полный код)

Я покажу базу, которую вы можете расширить. Используем библиотеку mcp от Anthropic (версия 1.2.6 на апрель 2026). Код — под спойлером.

import os
import json
import base64
from PIL import Image
from io import BytesIO

from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationOptions
from mcp.types import Tool, TextContent, ImageContent

try:
    from transformers import AutoModel, AutoTokenizer
except ImportError:
    AutoModel = AutoTokenizer = None

MODEL_PATH = os.getenv("MODEL_PATH", "./models/glm-5.1-vision-8b")
USE_GGUF = os.getenv("USE_GGUF", "0") == "1"

# Загружаем модель (полная или GGUF)
if USE_GGUF:
    from llama_cpp import Llama
    llm = Llama(
        model_path=MODEL_PATH,
        n_gpu_layers=-1,
        n_ctx=4096,
        verbose=False
    )
else:
    if AutoModel is None:
        raise ImportError("Установите transformers: pip install transformers")
    model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True, device_map="auto")
    tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)

app = Server("vision-server")

@app.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="analyze_screenshot",
            description="Анализирует скриншот: определяет элементы интерфейса, читает текст, описывает баги",
            inputSchema={
                "type": "object",
                "properties": {
                    "image_path": {"type": "string", "description": "Путь к файлу изображения"},
                    "question": {"type": "string", "description": "Дополнительный вопрос (опционально)"}
                },
                "required": ["image_path"]
            }
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list:
    if name != "analyze_screenshot":
        raise ValueError(f"Unknown tool: {name}")
    
    image_path = arguments["image_path"]
    question = arguments.get("question", "")
    
    if not os.path.exists(image_path):
        return [TextContent(type="text", text="Ошибка: файл не найден")]
    
    # Открываем изображение
    with Image.open(image_path) as img:
        # Ресайз до 768x768 для скорости (GLM-5.1 принимает до 1344x1344)
        img.thumbnail((768, 768), Image.LANCZOS)
        
        if USE_GGUF:
            # Для GGUF конвертим в base64 и передаем как текстовое вложение
            buffer = BytesIO()
            img.save(buffer, format="PNG")
            b64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
            prompt = f"<|image|>{b64}\nОпиши, что видишь на изображении. Ответь на русском. {question}"
            output = llm(prompt, max_tokens=512, temperature=0.2)
            response_text = output["choices"][0]["text"]
        else:
            # Отправляем напрямую через transformers
            messages = [
                {"role": "user", "content": [
                    {"type": "image", "image": img},
                    {"type": "text", "text": question or "Опиши, что видишь на скриншоте. Найди все интерактивные элементы, текст и ошибки."}
                ]}
            ]
            inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
            outputs = model.generate(**inputs, max_new_tokens=512, do_sample=False, temperature=0.2)
            response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
    
    return [TextContent(type="text", text=response_text)]

if __name__ == "__main__":
    import asyncio
    asyncio.run(app.run(initialization_options=InitializationOptions(
        server_name="vision-server",
        server_version="1.0.0",
        capabilities={"tools": {}}
    )))

Что тут важно: ресайз. Полный скриншот 1920x1080 модель сожрет, но время генерации уйдет в 5-7 секунд. Ресайз до 768 по большей стороне — оптимальный баланс. Модель все равно распознает текст на кнопках.

Совет: если нужно распознать мелкий текст, не ресайзите — скажите в question "увеличь область в центре" и подайте кроп.

4Подключаем сервер к агенту

Самый простой способ — использовать MCP-клиент из llama.cpp или kilo-code. Я покажу пример для llama-server (версия b4823+). В конфиг MCP добавляем строку:

{
  "mcp_servers": [
    {
      "name": "vision-server",
      "command": "python3",
      "args": ["/path/to/vision_server.py"],
      "env": {
        "MODEL_PATH": "./models/glm-5.1-vision-8b",
        "USE_GGUF": "1"
      }
    }
  ]
}

Затем в промпте агента достаточно упомянуть тул: "сделай скриншот окна с ошибкой и проанализируй через vision-server". Если агент использует библиотеку pyautogui или selenium для создания скриншотов, он сам делает снимок и передает путь.

ИнструментСсылка на гайд
llama-server Web UI + MCPподробная инструкция
Kilo Codeнастройка на 3x3090

Типичные грабли и как их избежать

Грабли №1. Модель выдает белиберду вместо текста.
Причина: изображение сжато до неприличия. Ресайз должен быть не менее 512 по меньшей стороне. Еще одна причина — модель не обучена читать русский язык на мелких кнопках. GLM-5.1 Vision поддерживает китайский и английский, но русский — не идеально. Если вам нужно распознавать русские интерфейсы, используйте дообученную версию или добавляйте в question: "Текст на русском, переведи на английский".

Грабли №2. Сервер падает с CUDA out of memory.
Причина: модель целиком загружена в GPU, а вы пытаетесь обрабатывать несколько скриншотов одновременно. Решение: используйте очередь (asyncio queue) или скиньте квантизацию до q4_k_m. На 24 ГБ 3090 можно держать полную 8B + запас для батча из 2 скриншотов.

Грабли №3. Агент не вызывает тул.
Причина: клиент не передает описание тула агенту. В MCP-протоколе нужно явно сообщить агенту список доступных инструментов. В промпт агента добавьте фразу: "У тебя есть доступ к тулу vision-server:analyze_screenshot. Используй его, если нужно увидеть экран".

Если у вас глубокая интеграция через llama.cpp с MCP-клиентом — советую прочитать гайд превращения llama-cli в агента и обход аутентификации.

Как протестировать, не изобретая агента

Не обязательно сразу лезть в глубины агентов. Запустите сервер и позовите его через любой MCP-клиент, например через mcp-cli:

# Установите mcp-cli (официальный инструмент от Anthropic)
pip install mcp-cli
mcp-cli connect python3 vision_server.py
# Теперь можно вызывать тул
invoke analyze_screenshot --image_path ./test_screenshot.png

Ответ должен быть что-то вроде: "На скриншоте окно браузера с ошибкой 404. В центре кнопка 'На главную'. Слева меню навигации с тремя пунктами...". Если модель галлюцинирует — проверьте качество изображения и количество контекстных токенов (увеличьте n_ctx до 8192).

Для полноценного цикла vibe-coding вам пригодятся и другие MCP-серверы: граф знаний для кода (120x сокращение токенов) или стек локальных LLM-агентов (выбор моделей по квантованию).

💡
Бонус: если скриншоты большие, а сервер загружен, добавьте кэш в Redis. Ключ — md5 от base64 изображения, время жизни — час. Повторные вызовы не будут грузить модель.

Чего я не рассказал, но это важно

Во-первых, GLM-5.1 Vision можно дообучить на ваших скриншотах. Процесс простой: собираете датасет (скриншот + описание), fine-tune через LoRA на одной 3090 — и модель начинает идеально описывать именно ваш UI. Размер сэмпла — 100 пар. Во-вторых, если вы работаете с видео (например, запись экрана для дебага), используйте MCP-тул, который извлекает ключевые кадры и отправляет их батчем.

В-третьих, ни в коем случае не делайте MCP-сервер общедоступным без авторизации. Даже локально — закройте порт фаерволом, иначе любой злоумышленник в вашей сети сможет отправлять изображения. Лучше используйте Unix-сокет.

И наконец: не пытайтесь заставить агента смотреть на экран в реальном времени. LLM с vision — это не механизм для стриминга. Делайте снимки только по событию (ошибка, изменение DOM, нажатие кнопки). Иначе модель захлебнется, а вы получите лаг в 10 секунд на каждый кадр.

Я описал минимальный рабочий путь. На практике вы будете менять промпты, темп, размер ресайза, квантизацию — это нормально. Главное, что теперь у вашего агента есть глаза. И они видят лучше, чем кажется.

Подписаться на канал