Архив рубрики ~Лента новостей~

Протокол, который оптимизировал архитектуру наших агентов.

Протокол, который оптимизировал архитектуру наших агентов.
Протокол, который оптимизировал архитектуру наших агентов.

Подробный анализ MCP, который превратил мои разрозненные определения инструментов в стабильный, легкодоступный сервер.

Делиться

Изображение создано с помощью подробного запроса и Google Gemini.

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

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

Мы это исправили, но это вызвало один серьезный вопрос: зачем мы построили это именно так?

Честный ответ: у нас не было альтернативы. В LangGraph вызов инструментов по своей сути является локальной задачей. Вы определяете инструменты там, где они вам нужны, вызываете их там, где вам нужно, и контролируете всю инфраструктуру. Это управляемо, когда у вас всего два агента, но становится проблемой, когда семь агентов используют пересекающиеся инструменты вместе с человеком-контроллером.

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

В этой статье

  1. Что такое MCP?
  2. Создание сервера MCP
  3. Stdio против HTTP
  4. Подключение к LangGraph
  5. Участие человека на границе протокола
  6. Что может сломаться в процессе производства и почему?
  7. Влияние MCP на нашу агентную систему
  8. Заключение

Что такое MCP?

Протокол контекста модели (Model Context Protocol) — это открытый стандарт, опубликованный компанией Anthropic в конце 2024 года. Он стандартизирует способ обнаружения и вызова инструментов агентом ИИ. Вместо определения инструментов внутри оркестратора, их запускают на отдельном сервере. Агент подключается к этому серверу во время выполнения, запрашивает информацию о доступных инструментах и получает список в ответ.

Опытный инженер, читающий эту статью, сразу же спросит: а нельзя ли просто создать централизованный реестр инструментов и внедрить его в каждый агент при запуске? Я задал себе этот вопрос и использовал реестр инструментов вместо MCP в другой системе.

Да, это возможно, и если у вас уже есть что-то подобное работающее, MCP не является чрезвычайной ситуацией. Чего вам не даст специализированный реестр, так это границы совместимости . MCP — это протокол, а не библиотека. Любой MCP-совместимый клиент может подключиться к вашему серверу, сегодня — к LangGraph, а в следующем году — к другому фреймворку. Клиент TypeScript может вызвать ваш сервер Python без какой-либо дополнительной работы по интеграции. Реестр инструментов не предоставляет этой функциональности.

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

Создание сервера MCP

Сервер MCP может предоставлять три компонента: инструменты (вызываемые действия), ресурсы (данные только для чтения) и подсказки (многократно используемые шаблоны). Для агентной системы, которой необходимо выполнять определенные действия, инструменты являются первостепенной задачей.

В состав Python SDK входит FastMCP , который обрабатывает генерацию схемы на основе подсказок типов и управляет жизненным циклом протокола. Вам нужно написать функцию и пометить её декоратором инструмента, а сервер позаботится обо всём остальном.

Одна из ошибок, которую часто допускают при использовании stdio-транспорта: никогда не записывайте данные в stdout . Протокол MCP использует stdout в качестве канала связи. Любой случайный вызов print() исказит поток сообщений таким образом, что отладка станет очень сложной.

 import sys import logging from mcp.server.fastmcp import FastMCP logging.basicConfig(level=logging.INFO, stream=sys.stderr) logger = logging.getLogger("analyst-tools") mcp = FastMCP("analyst-tools") @mcp.tool() async def run_analysis(code: str, dataset: str) -> dict: """ Executes a Python snippet against live data and returns the result. Use when the user wants to compute aggregates, filter records, or derive insights. The code must assign its final output to a variable named 'output'. Args: code: Python code to execute. dataset: One of 'sales', 'inventory', 'pipeline'. """ logger.info(f"run_analysis | dataset={dataset}") return await execute_in_sandbox(code, dataset) @mcp.tool() async def write_to_db(table: str, payload: dict) -> dict: """ Persists a result record to the analyst results table. Only call this after run_analysis has returned a verified output. Args: table: Target table name. payload: Key-value pairs to write as a new record. """ logger.info(f"write_to_db | table={table}") return await persist_result(table, payload) if __name__ == "__main__": mcp.run(transport="stdio")

Документационные строки используются LLM, чтобы помочь агенту решить, какой инструмент следует использовать. Поэтому написание хорошей документационной строки очень важно.

Stdio против HTTP

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

Stdio запускает сервер как дочерний процесс клиента. Связь осуществляется через стандартный ввод и вывод. Задержка составляет несколько миллисекунд, сеть не задействована, а настройка минимальна. Идеальный выбор для локальной разработки, развертывания на одной машине или в любом месте, где сервер и клиент находятся в одном дереве процессов .

Streamable HTTP запускает сервер как независимую службу. Используйте это, когда сервер необходимо совместно использовать на нескольких клиентах или машинах, когда вы хотите развернуть его в виде контейнера или когда вам требуется горизонтальное масштабирование. В этом случае хорошо подходят бессерверные развертывания, такие как Cloud Run. Stdio совершенно не подходит для бессерверной модели, поскольку предполагает наличие долгоживущего родительского процесса.

Переключение между ними в FastMCP осуществляется всего одной строкой:

 mcp.run(transport="streamable-http", host="0.0.0.0", port=8080)

Нам нужно лишь изменить транспорт в mcp.run() , а все остальное останется без изменений.

Что касается требований к размещению данных, сервер MCP, работающий локально и использующий инструменты, которые никогда не обращаются к внешнему API, обеспечит вашей команде по соблюдению нормативных требований четкую картину. Протоколу все равно, где работает сервер.

Подключение к LangGraph

Библиотека langchain-mcp-adapters управляет жизненным циклом подпроцесса, выполняет процедуру установления соединения при обнаружении инструментов и преобразует схемы инструментов MCP в объекты инструментов, совместимые с LangChain.

 from langchain_mcp_adapters.client import MultiServerMCPClient from langgraph.graph import StateGraph, MessagesState, START from langgraph.prebuilt import ToolNode, tools_condition from langchain_google_vertexai import ChatVertexAI llm = ChatVertexAI( model="gemini-2.5-flash", temperature=0, max_tokens=None ) async def run(query: str): async with MultiServerMCPClient({ "analyst-tools": { "command": "python", "args": ["./mcp_server.py"], "transport": "stdio", } }) as client: tools = await client.get_tools() llm_with_tools = llm.bind_tools(tools) def agent_node(state: MessagesState): return {"messages": [llm_with_tools.invoke(state["messages"])]} graph = StateGraph(MessagesState) graph.add_node("agent", agent_node) graph.add_node("tools", ToolNode(tools)) graph.add_edge(START, "agent") graph.add_conditional_edges("agent", tools_condition) graph.add_edge("tools", "agent") app = graph.compile() result = await app.ainvoke({ "messages": [{"role": "user", "content": query}] }) print(result["messages"][-1].content)

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

Одна из особенностей, о которой стоит знать: MultiServerMCPClient по умолчанию создает новую сессию MCP для каждого вызова инструмента. Для одного запроса, который выполняет пять последовательных вызовов инструментов, это пять рукопожатий. Это хорошо для стандартного ввода-вывода на одной машине, но заметно на HTTP-транспорте с удаленным сервером. Для производственных нагрузок с цепочками вызовов инструментов используйте async with client.session("analyst-tools") чтобы привязать несколько вызовов к одной сессии.

Человек-участник на границе протокола

До внедрения MCP наш механизм подтверждения находился в графе. Мы использовали interrupt_before для определенных узлов, встраивали собственную логику подтверждения в ребра графа и обновляли пользовательский интерфейс каждый раз, когда добавлялся новый важный инструмент. Это работало, но также означало, что добавление инструмента, требующего подтверждения, требовало координации действий трех команд.

После MCP шлюз перемещается в отдельный слой между исполнителем LangGraph и клиентом MCP. Любой инструмент, соответствующий политике конфиденциальности, проходит через шлюз до того, как достигнет сервера. Граф ничего об этом не знает.

 SENSITIVE_TOOLS = frozenset({"write_to_db", "send_notification", "trigger_webhook"}) async def gated_call(tool_name: str, arguments: dict, execute) -> dict: if tool_name in SENSITIVE_TOOLS: # In production: push to Slack / internal UI / audit queue print(f"nAPPROVAL REQUIRED {tool_name}") print(f"Arguments: {arguments}") decision = input("Approve? (y/n): ").strip().lower() if decision != "y": return { "status": "rejected", "reason": f"Operator declined '{tool_name}'." } return await execute(tool_name, arguments)

SENSITIVE_TOOLS — это единый набор, который используется при каждом вызове инструмента независимо от того, какой агент его инициировал. Добавлен новый инструмент для работы с конфиденциальной информацией на сервер? Добавьте его имя в этот набор. График не меняется. Интерфейс подтверждения не меняется. В нашей внутренней системе мы загружали это из конфигурационного файла при запуске. Команда разработчиков и команда по соблюдению нормативных требований могли обновлять его без развертывания кода.

Что может сломаться в процессе производства и почему?

Сервер аварийно завершает работу в середине выполнения. Клиент получит ошибку при следующем вызове инструмента. ToolNode LangGraph выводит это сообщение об ошибке обратно в LLM. Восстановится ли модель или зациклится в неразберихе, зависит от командной строки вашей системы. Как минимум, запишите в лог stderr дочернего процесса отдельно, чтобы увидеть, что именно привело к сбою сервера; без этого отладка будет лишь гаданием.

LLM вызывает не тот инструмент. MCP не защищает от этого. Если описания ваших инструментов расплывчаты или имеют пересекающиеся значения, модель примет неверное решение о маршрутизации. Мы потратили значительное время на настройку строк документации на нашем сервере именно потому, что плохо сформулированное описание приводило к вызову write_to_db до завершения run_analysis . Рассматривайте описания инструментов как оперативную инженерную проблему.

Контрольный пункт для длительных рабочих процессов. Если оператору необходимо утвердить вызов инструмента, и это занимает пять минут, граф агентов приостанавливается в ожидании. LangGraph поддерживает сохранение состояния графа с помощью контрольных точек, поэтому вы можете позволить процессу завершиться и возобновиться после принятия решения. Это более сложная архитектура, чем показано здесь, но она подходит для рабочих процессов, которые не могут блокировать поток на неопределенное время.

Влияние MCP на нашу агентную систему

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

Мы полностью исключили дублирование инструментов. Теперь run_analysis определен точно в одном месте и одновременно обслуживает семь рабочих процессов. Для обновления схемы выходных данных нам достаточно внести изменения на сервере, после чего все потребители получат эти изменения.

Добавление новых возможностей происходило быстро. Например, на следующей неделе мы добавили инструмент generate_visualisation , и агент начал использовать его уже на следующий день. Никаких изменений в оркестраторе не производилось.

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

Хочу поделиться одной вещью, которую MCP не исправляет : он не сделает ненадежные инструменты надежными. Он не поможет LLM принимать более взвешенные решения по маршрутизации, если ваши описания неверны. И он не заменяет наблюдаемость, вам все равно нужно регистрировать вызовы инструментов и отслеживать пути выполнения. Структура упрощает инструментирование этих процессов, но работа все равно остается за вами.

Заключение

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

Благодаря этому переходу наша команда машинного обучения теперь может развертывать и версионировать инструменты независимо, не затрагивая граф приложений.

Если вам понравился этот подробный анализ MCP, я бы посоветовал вам ознакомиться с моей текущей серией: «RAG для корпоративной базы знаний в гибридном поиске» и «RAG по реранжированию в производственной среде».

Приянш Бхардвадж Посмотреть все о Приянш Бхардвадж

Источник: towardsdatascience.com

Оцените материал:

Читайте также
Архив рубрики ~Обо всем~ По сообщениям, Apple планирует выпустить еще три функции для iOS 27 осенью. Архив рубрики ~Коротко из Telegram~ ИИ-агент прошёл путь от взлома сервера до базы данных за… Архив рубрики ~Коротко из Telegram~ Xiaomi открыла MiMo Code — AI-агента, который не забывает проект… Архив рубрики ~Коротко из Telegram~ «Второй мозг» теперь реально становится рабочим инструментом Notion, Obsidian и… Архив рубрики ~Обо всем~ 7 способов, которыми ИИ может помочь в управлении вашей системой Linux Архив рубрики ~Коротко из Telegram~ В США научились переписывать ДНК эмбриона точечно и без поломок… Архив рубрики ~Коротко из Telegram~ 🚨 Исследователи представили работу Code as Agent Harness, в которой… Архив рубрики ~Коротко из Telegram~ ✨ Microsoft объявила, что популярный open-source агентный фреймворк OpenClaw теперь… Архив рубрики ~Обо всем~ Срок действия спорного закона о слежке FISA истекает сегодня вечером. Слежка продолжится. Новости робототехники Инженерные решения играют решающую роль в повышении продовольственной безопасности. Архив рубрики ~Обо всем~ Ваш Kindle может открыть доступ к тысячам бесплатных книг: вот лучшие места, где их можно найти. Новости робототехники Вот что будет делать новый стартап Джеффа Безоса, Prometheus. Архив рубрики ~Обо всем~ Этот университетский эксперимент 1976 года дал толчок развитию ветроэнергетической отрасли США. Архив рубрики ~Обо всем~ На вашем iPhone 11 будет установлена iOS 27, но не будет работать Siri AI. Архив рубрики ~Обо всем~ По сообщениям, Apple планирует выпустить еще три функции для iOS 27 осенью. Архив рубрики ~Коротко из Telegram~ ИИ-агент прошёл путь от взлома сервера до базы данных за… Архив рубрики ~Коротко из Telegram~ Xiaomi открыла MiMo Code — AI-агента, который не забывает проект… Архив рубрики ~Коротко из Telegram~ «Второй мозг» теперь реально становится рабочим инструментом Notion, Obsidian и… Архив рубрики ~Обо всем~ 7 способов, которыми ИИ может помочь в управлении вашей системой Linux Архив рубрики ~Коротко из Telegram~ В США научились переписывать ДНК эмбриона точечно и без поломок… Архив рубрики ~Коротко из Telegram~ 🚨 Исследователи представили работу Code as Agent Harness, в которой… Архив рубрики ~Коротко из Telegram~ ✨ Microsoft объявила, что популярный open-source агентный фреймворк OpenClaw теперь… Архив рубрики ~Обо всем~ Срок действия спорного закона о слежке FISA истекает сегодня вечером. Слежка продолжится. Новости робототехники Инженерные решения играют решающую роль в повышении продовольственной безопасности. Архив рубрики ~Обо всем~ Ваш Kindle может открыть доступ к тысячам бесплатных книг: вот лучшие места, где их можно найти. Новости робототехники Вот что будет делать новый стартап Джеффа Безоса, Prometheus. Архив рубрики ~Обо всем~ Этот университетский эксперимент 1976 года дал толчок развитию ветроэнергетической отрасли США. Архив рубрики ~Обо всем~ На вашем iPhone 11 будет установлена iOS 27, но не будет работать Siri AI.

Оставить комментарий