Языковая модель по своей природе умеет делать только одну вещь — предсказывать следующий токен. Вы даёте ей текст, она продолжает текст. Всё. Никаких баз данных, никаких API-вызовов, никакой возможности посмотреть, который час.
Но вы читаете этот текст через LLM, которая прямо сейчас обращается к файлам на сервере, читает логи и запускает bash-команды. Как модель, внутри которой — только слои Self-Attention и матрицы весов, вдруг научилась «брать в руки» инструменты?
Ответ состоит из двух частей: инженерный трюк под названием function calling и стандартизирующий протокол под названием Model Context Protocol (MCP). Разберём их по очереди, с механикой под капотом.

1. Проблема: LLM как prisoner в собственной context window
Представьте блестящего специалиста, запертого в комнате без окон. Он знает всё, что знал на момент ареста, может рассуждать о чём угодно — но не может ни позвонить, ни заглянуть в интернет, ни даже посмотреть на часы. Вы передаёте ему записки под дверью, он пишет ответы.
Именно так живёт «голая» языковая модель:
- Данные заморожены во времени. Модель знает только то, что было в обучающей выборке. Вчерашний курс акций? Новости за последний час? Невозможно.
- Нет состояния между запросами. Каждый запрос (инференс) независим. Модель не помнит, о чём вы говорили минуту назад, — если только вы не прокидываете всю историю в контекст заново.
- Нет вычислительной точности. LLM плохо считают «в уме»: попросите умножить 3 487 на 8 921 — и вы получите уверенно выглядящий, но часто ошибочный ответ.
Это фундаментальное ограничение, и решается оно единственным способом: дать модели руки.
2. Что такое инструмент для LLM
Инструмент (tool, иногда function) — это обычная функция в обычном коде, которую может вызвать внешняя программа. Модель ничего не исполняет сама — она только сообщает, что хочет вызвать такую-то функцию с такими-то аргументами. Исполнение делает окружение (рантайм-обёртка вокруг модели).
Пример. У вас есть функция на Python:
| |
Чтобы модель могла ей воспользоваться, её нужно описать на языке, понятном модели — в виде JSON-схемы:
| |
Ключевой момент: для модели инструмент — это просто текст. Никакого бинарного интерфейса, никакого системного вызова. Описание функции подаётся в контекст как часть системного промпта, и модель «видит» его тем же механизмом 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-экосистему (это метафора из официальной документации, она же на диаграмме выше):
| USB | MCP |
|---|---|
| Ноутбук с 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 с инструментами — это разум, способный действовать в мире.