$ cat toc.txt

Языковая модель по своей природе умеет делать только одну вещь — предсказывать следующий токен. Вы даёте ей текст, она продолжает текст. Всё. Никаких баз данных, никаких API-вызовов, никакой возможности посмотреть, который час.

Но вы читаете этот текст через LLM, которая прямо сейчас обращается к файлам на сервере, читает логи и запускает bash-команды. Как модель, внутри которой — только слои Self-Attention и матрицы весов, вдруг научилась «брать в руки» инструменты?

Ответ состоит из двух частей: инженерный трюк под названием function calling и стандартизирующий протокол под названием Model Context Protocol (MCP). Разберём их по очереди, с механикой под капотом.

Архитектура Model Context Protocol: клиенты подключаются к серверам (как USB-кабели), а серверы — к данным и внешним сервисам

1. Проблема: LLM как prisoner в собственной context window

Представьте блестящего специалиста, запертого в комнате без окон. Он знает всё, что знал на момент ареста, может рассуждать о чём угодно — но не может ни позвонить, ни заглянуть в интернет, ни даже посмотреть на часы. Вы передаёте ему записки под дверью, он пишет ответы.

Именно так живёт «голая» языковая модель:

  • Данные заморожены во времени. Модель знает только то, что было в обучающей выборке. Вчерашний курс акций? Новости за последний час? Невозможно.
  • Нет состояния между запросами. Каждый запрос (инференс) независим. Модель не помнит, о чём вы говорили минуту назад, — если только вы не прокидываете всю историю в контекст заново.
  • Нет вычислительной точности. LLM плохо считают «в уме»: попросите умножить 3 487 на 8 921 — и вы получите уверенно выглядящий, но часто ошибочный ответ.

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

2. Что такое инструмент для LLM

Инструмент (tool, иногда function) — это обычная функция в обычном коде, которую может вызвать внешняя программа. Модель ничего не исполняет сама — она только сообщает, что хочет вызвать такую-то функцию с такими-то аргументами. Исполнение делает окружение (рантайм-обёртка вокруг модели).

Пример. У вас есть функция на Python:

1
2
3
def get_disk_usage(host: str) -> dict:
    """Возвращает занятое место на сервере."""
    ...

Чтобы модель могла ей воспользоваться, её нужно описать на языке, понятном модели — в виде JSON-схемы:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "name": "get_disk_usage",
  "description": "Возвращает занятое место на сервере по его имени или IP.",
  "parameters": {
    "type": "object",
    "properties": {
      "host": {
        "type": "string",
        "description": "Имя или IP-адрес сервера, например 'db-01.local'"
      }
    },
    "required": ["host"]
  }
}

Ключевой момент: для модели инструмент — это просто текст. Никакого бинарного интерфейса, никакого системного вызова. Описание функции подаётся в контекст как часть системного промпта, и модель «видит» его тем же механизмом Self-Attention, которым видит любой другой текст.

3. Как LLM «понимает», что инструменты существуют

Здесь кроется главный философский подвох. Модель не «понимает» наличие инструментов в человеческом смысле. В ней нет отдельного модуля «осознание инструментов».

Что происходит на самом деле:

Во время обучения (function-calling fine-tuning). Современные модели проходят специальный этап дообучения, где им показывают тысячи примеров вида: «вот промпт пользователя, вот список доступных функций, вот корректный ответ в виде вызова функции». Модель учится статистическому паттерну: если вопрос требует внешних данных, а в контексте есть описание подходящей функции — нужно сгенерировать специальный токен вызова.

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

У тебя есть доступ к следующим инструментам:
1. get_disk_usage(host) — место на диске...
2. ssh_command(host, command) — выполнить команду...
3. read_file(path) — прочитать файл...

Чтобы вызвать инструмент, выведи JSON: {"name": "...", "arguments": {...}}

Модель читает это тем же вниманием, каким читает запрос пользователя. Когда вы спрашиваете «сколько свободного места на db-server?», Self-Attention связывает слово «место» в запросе со словом «место» в описании get_disk_usage — и вероятность того, что модель сгенерирует именно вызов этой функции, резко возрастает.

Это та самая механика, которую мы разбирали в статье про самовнимание, только теперь контекстом выступает не предложение, а вся диалоговая сессия с описаниями инструментов.

4. Как LLM выбирает нужный инструмент

Механически выбор инструмента — это обычное предсказание следующего токена, но с одним трюком: структурированный вывод (structured output / constrained decoding).

Вот что происходит пошагово, когда вы спрашиваете: «Проверь диск на db-01»:

Шаг 1. Контекстуализация. Рантайм собирает полный контекст: системный промпт + описания инструментов + история диалога + ваш новый запрос. Всё это подаётся в модель.

Шаг 2. Внимание к описаниям. Self-Attention распределяет «веса» между токенами запроса и токенами описаний. Слово «проверь» и «диск» из запроса получают высокие веса внимания к описанию get_disk_usage (где есть слова «место на диске»). Остальные инструменты получают веса близкие к нулю.

Шаг 3. Генерация имени функции. Модель начинает генерировать ответ. Если она «решила» вызвать инструмент (а это тоже вероятностное решение), рантайм ограничивает (constrains) её вывод: следующие токены обязаны быть валидным именем из списка. Декодер буквально не может выдать get_ram_usage, если такого инструмента нет — вероятности этих токенов принудительно зануляются.

Шаг 4. Генерация аргументов. По тому же принципу генерируются аргументы, но с дополнительной грамматической схемой: если параметр host типа string, модель не сможет выдать число. Декодинг загоняется в рамки JSON-схемы конкретной функции.

Шаг 5. Передача управления. Как только JSON вызова сгенерирован, рантайм прерывает генерацию модели, исполняет реальную функцию в коде и вставляет результат обратно в контекст как новое сообщение (tool_result). Модель продолжает генерацию уже «видя» результат — и формулирует ответ пользователю.

Почему это надёжно работает? Потому что выбор инструмента сводится к задаче, которую трансформер решает лучше всего: семантическому сопоставлению текста запроса с текстом описания. Это то же самое, что распознать смысл слова «замок» по контексту — только контекстом теперь служит каталог функций.

5. Проблема хаоса: почему появилась стандартизация

Описанный выше механизм (function calling) работает, но у него есть серьёзная инженерная болезнь — отсутствие стандартов.

У OpenAI формат описания инструментов — один. У Anthropic — другой. У Google Gemini — третий. У локальных моделей через Ollama — четвёртый (или никакой, в зависимости от модели). Параметры называются по-разному, схемы вложены по-разному, форматы ответа не совпадают.

Если вы пишете DevOps-агента, который должен работать с тремя разными провайдерами LLM, вам приходится писать три разных адаптера. А если вы меняете модель с GPT-4 на локальную Llama через vLLM — переписывать интеграции заново.

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

Вы написали классный MCP-инструмент для чтения логов Loki? Отлично. Теперь он работает в Claude Desktop. А чтобы он заработал в Cursor, в Windsurf или в вашем самописном агенте на Python — нужно писать интеграцию заново, потому что у каждого приложения свой способ подключения инструментов. Один и тот же «мостик к Slack» переписывается десятки раз.

Именно эту проблему решает Model Context Protocol.

6. Model Context Protocol: USB-C для LLM

В конце 2024 года Anthropic открыл спецификацию Model Context Protocol (MCP) — клиент-серверный протокол поверх JSON-RPC, который стандартизирует, как приложения (клиенты) подключаются к источникам данных и инструментам (серверам).

Архитектуру протокола удобно представить как USB-экосистему (это метафора из официальной документации, она же на диаграмме выше):

USBMCP
Ноутбук с USB-C портомMCP-клиент (приложение: Claude Desktop, Cursor, ваш самописный агент)
USB-кабель / адаптерMCP-сервер (программа-посредник)
Периферийное устройство (мышь, флешка)Источник данных (БД, API, файлы, Slack, Jira)

Три ключевых участника:

MCP Host (хост) — приложение, в котором работает LLM. Claude Desktop, IDE вроде Cursor, ваш самописный агент. Хост хочет дать «своей» модели доступ к данным, но не хочет писать интеграцию под каждый источник.

MCP Client (клиент) — компонент внутри хоста, который устанавливает соединение с серверами по протоколу MCP. Хост может держать несколько клиентов одновременно — по одному на каждый подключённый сервер.

MCP Server (сервер) — отдельная программа, которая реализует протокол MCP и оборачивает доступ к конкретному источнику данных. Сервер Slack знает, как ходить в Slack API. Сервер PostgreSQL знает, как выполнять SQL. Сервер файловой системы читает файлы. Каждый сервер — это тонкий, специализированный адаптер.

Связь на диаграмме читается так: клиент (слева на ноутбуке) подключается к серверам (пунктирные «кабели» в центре), а серверы тянут данные — из удалённых сервисов (Slack, Google, Calendar слева внизу) и из локальных источников (справа внизу). Хосты (справа вверху: Claude и другие приложения) получают единый, стандартизированный доступ ко всему этому зоопарку через один протокол.

Главная ценность MCP — разделение ответственности. Автор сервера Slack пишет интеграцию один раз. Любой MCP-совместимый клиент после этого может ей воспользоваться. Автор клиента не думает о специфике Slack API — он просто разговаривает с сервером по стандартному протоколу.

7. Что MCP-сервер предоставляет модели

Протокол определяет три типа ресурсов, которые сервер может экспортировать:

Tools (инструменты) — функции, которые модель может вызвать. Те самые, что мы разбирали в разделах 2–4, только теперь их описание приходит не из системного промпта, а динамически от MCP-сервера по протоколу. Пример: run_query(sql) на сервере БД или send_message(channel, text) на сервере Slack.

Resources (ресурсы) — данные, которые можно прочитать как файл. Сервер файловой системы отдаёт файлы; сервер Git — исходный код; сервер БД — результаты запросов. Ресурсы адресуются URI (file:///etc/nginx/nginx.conf, postgres://users/schema). В отличие от инструментов, ресурсы пассивны — модель не «вызывает» их, а просит клиента прочитать.

Prompts (шаблоны промптов) — готовые шаблоны диалогов, которые сервер предлагает хосту. Удобно для типовых задач: сервер Jira может предлагать шаблон «проанализируй backlog спринта», который сразу подключает нужные ресурсы и инструменты.

8. Почему это критично для self-hosting и DevOps

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

MCP меняет картину: вместо того чтобы писать отдельную интеграцию под каждый сервис и каждую LLM, вы оборачиваете источник данных в MCP-сервер один раз — и получаете к нему единый доступ из любого MCP-совместимого приложения.

Единый интерфейс к инфраструктуре. Раньше для управления серверами инженер собирал свою связку: SSH-скрипты для удалённых команд, ручные дашборды в Grafana, отдельные CLI-утилиты под каждый гипервизор или контейнерный рантайм. Теперь каждый источник данных (PostgreSQL, API гипервизора, логи, Docker) можно обернуть в отдельный MCP-сервер — и LLM-агент получает к ним доступ как к обычным инструментам. Один промпт — и модель сама решает, позвать ли статус виртуальной машины или docker ps.

Observability на естественном языке. Классический observability-стек (Prometheus + Grafana + Loki, о котором мы говорили в статье про Шеннона) требует знания запросных языков: PromQL, LogQL. С MCP-серверами поверх этих систем вы просто спрашиваете агента: «покажи аномалии нагрузки за последний час» — и модель сама формирует запросы к нужным источникам, интерпретирует результат и объясняет его. Высокая энтропия в логах (неожиданные события) теперь находится не глазами оператора в Grafana, а самим LLM.

Локальные модели, локальные инструменты. MCP-сервер — это обычный процесс, он не требует облака. Вы можете запускать его рядом с локальной LLM через Ollama или vLLM, и вся связка работает внутри вашей сети, без единого пакета наружу. Для чувствительной инфраструктуры (банковские выписки, приватные репозитории, внутренние API) это решает и практическую, и этическую задачу.

9. Ограничения и что дальше

MCP — молодой протокол (конец 2024 года), и у него есть шероховатости:

  • Зрелость экосистемы. Стандарт есть, но реализации разной степени готовности. Многие серверы сырые, документы часто расходятся с кодом.
  • Безопасность. Дать LLM возможность вызывать rm -rf — это серьёзное решение. Хорошие MCP-клиенты реализуют подтверждение действий (human-in-the-loop), но это зона ответственности хоста, а не протокола.
  • Производительность. Каждый вызов инструмента — это полноценный раунд: генерация модели → исполнение функции → возврат результата → новая генерация. Сложные задачи с десятками вызовов могут быть медленными и дорогими.
  • Надёжность выбора. Модель всё ещё ошибается: выбирает не тот инструмент, путает аргументы, галлюцинирует имена функций. Это особенно заметно, когда в каталоге 50+ инструментов — Self-Attention начинает «размазиваться».

Последняя проблема — самая глубокая. Она ведёт нас к архитектуре агентов: систем, которые не просто вызывают инструменты по одному, а планируют многошаговые последовательности действий, исправляют собственные ошибки и сами решают, когда остановиться. Но это тема для отдельного разбора.

Заключение

Инструменты в мире LLM — это элегантный инженерный трюк. Мы не научили модель «действовать» в мире — мы научили её генерировать структурированный текст, который внешняя программа интерпретирует как команду. Вся магия function calling сводится к двум вещам: правильному описанию инструментов в контексте (чтобы Self-Attention их «заметил») и ограничению декодинга (чтобы модель не могла выдать невалидный вызов).

Model Context Protocol добавляет к этому второй слой — стандартизацию интерфейса между источниками данных и приложениями. Тот же принцип, что сделал USB-C универсальным разъёмом: одно соединение, бесконечное разнообразие периферии.

Если вернуться к философии из прошлой статьи, то инструменты — это то, что превращает изолированную модель в расширение собственного опыта. LLM без инструментов — это разум, запертый в моменте обучения. LLM с инструментами — это разум, способный действовать в мире.

← все посты [поделиться] [rss]