Обычно AI-агент взаимодействует с приложением через tools — заранее описанные функции с понятными входными параметрами.
Например:
get_orders
get_product
update_stock
create_reportМодель выбирает нужный инструмент, формирует аргументы, получает результат и решает, что делать дальше.
Такой подход хорошо работает, пока инструментов немного, а задача укладывается в один или два вызова. Но при сложных сценариях агенту приходится выполнять длинную последовательность действий, а приложению — каждый раз передавать модели схемы tools и результаты промежуточных запросов.
Есть другой подход: вместо множества отдельных вызовов агент пишет небольшую программу на Python и выполняет всю последовательность внутри изолированной среды. Этот паттерн называют CodeAct, Code Mode или programmatic tool calling.
В чём проблема обычного tool calling
Представим, что агенту нужно найти новые заказы, получить товары для каждого заказа, отобрать отсутствующие на складе позиции и подготовить результат.
При обычном tool calling процесс может выглядеть так:
LLM
↓
get_orders
↓
LLM
↓
get_order_products
↓
LLM
↓
get_stock
↓
LLM
↓
следующий товарКаждый переход к модели занимает время и расходует токены. Промежуточные ответы tools попадают в контекст, хотя значительная часть этих данных нужна только для фильтрации или следующего вызова.
Дополнительная проблема возникает при большом количестве инструментов. Каждый tool имеет название, описание и JSON Schema входных параметров. Если подключить несколько крупных MCP-серверов, их схемы начинают занимать заметную часть контекста ещё до выполнения задачи.
Что предлагает Code Mode
В Code Mode модель получает один основной инструмент:
run_python(code)Вместо серии отдельных решений модель сразу пишет программу:
orders = await tools["get-orders"](status="new")
products = []
for order in orders:
items = await tools["get-order-products"](
orderId=order["id"]
)
products.extend(items)
missing = [
product
for product in products
if product["stock"] == 0
]
print(missing)Этот код запускается в sandbox. Внутри него можно использовать циклы, условия, обработку ошибок, параллельные вызовы и обычные средства Python для преобразования данных.
Модель получает только результат, который программа явно вывела через print(). Большие промежуточные ответы можно обработать внутри sandbox и не добавлять целиком в контекст LLM.
Схема меняется:
Пользователь
↓
LLM
↓
Python-программа
↓
Sandbox
↓
MCP tools / API
↓
Итоговый результат
↓
LLMЭто действительно работа без tools?
Не совсем.
run_python сам является tool, а доступ к приложению всё равно должен быть ограничен. Агенту не обязательно выдавать сетевой доступ, пароли от базы данных или возможность выполнять любые системные команды.
В безопасной реализации Python используется как язык оркестрации, а реальные действия проходят через разрешённые функции:
await tools["find-articles"](...)
await tools["publish-article"](...)
await tools["create-support-request"](...)Получается два уровня:
Python-код определяет последовательность действий
↓
Tools определяют доступные возможности и границы доступаМодель может самостоятельно написать цикл или объединить результаты нескольких запросов, но не получает неограниченный доступ к инфраструктуре.
codemode-lite
Практическую реализацию такого подхода представила команда Red Hat Emerging Technologies в проекте codemode-lite.
Это MCP-сервер, который подключается к другим MCP-серверам, но показывает агенту только один инструмент — run_python. Доступные MCP tools вызываются из Python через RPC.
Упрощённо архитектура выглядит так:
AI-агент
↓
run_python
↓
Podman или Pyodide
↓ RPC
MCP-серверыПеред вызовом агент может найти подходящий инструмент и запросить его схему:
tools_list = await _discover()
schema = await _schema("create-article")После этого инструмент вызывается как функция:
article = await tools["create-article"](
title="Code Mode для AI-агентов",
status="draft"
)
print(article)Для параллельного выполнения можно использовать asyncio:
import asyncio
articles, categories = await asyncio.gather(
tools["find-articles"](status="draft"),
tools["get-categories"]()
)
print({
"articles": articles,
"categories": categories,
})Авторы проверяли codemode-lite с 38 инструментами из четырёх MCP-серверов. В их тестах перенос схем и промежуточной обработки за run_python сократил расход токенов на 53%. Это результат конкретного эксперимента проекта, а не универсальная гарантия для любого агента.
Откуда появилась идея CodeAct
Подход основан на более общей идее CodeAct — представлять действия агента исполняемым кодом вместо JSON или другого заранее заданного формата.
В работе Executable Code Actions Elicit Better LLM Agents, опубликованной на ICML 2024, агент использовал Python как единое пространство действий. Он мог вызывать доступные функции, объединять их, работать с результатами и исправлять собственный код после получения ошибки.
В экспериментах авторов CodeAct превосходил сравниваемые варианты на величину до 20% по успешности выполнения задач. Главное преимущество кода состояло в выразительности: условия, циклы и преобразования данных не приходилось раскладывать на отдельные решения модели.
Похожая идея используется в библиотеке Hugging Face smolagents. В ней есть два отдельных типа агентов:
CodeAgent — формирует действия как Python-код
ToolCallingAgent — использует обычные структурированные tool callsTools в CodeAgent доступны как Python-функции, поэтому модель может объединять их внутри одного фрагмента кода.
Где это полезно
Code Mode хорошо подходит для задач, в которых агенту нужно:
- вызвать один инструмент много раз;
- обработать большой набор результатов;
- отфильтровать данные до передачи в контекст;
- выполнить несколько запросов параллельно;
- связать несколько MCP-серверов;
- использовать циклы, условия и промежуточные вычисления;
- сократить количество обращений к LLM.
Например, агент может получить список документов, проверить каждый из них, сгруппировать найденные ошибки и вернуть только итоговую сводку. При обычном tool calling результаты каждой проверки могут последовательно проходить через контекст модели. В Code Mode большую часть работы выполнит программа.
Когда обычные tools лучше
Для простого действия Code Mode добавляет лишний слой.
Если нужно получить один заказ по идентификатору или создать одну запись, прямой tool call проще, прозрачнее и обычно надёжнее:
get_order({ id: 42 })Обычные tools также удобнее там, где каждое действие требует отдельной оценки модели или подтверждения пользователя. Например, чтение данных можно объединить в программу, а операции изменения лучше оставить отдельными и хорошо наблюдаемыми.
Code Mode не отменяет проектирование API. Если приложение предоставляет слишком широкие или небезопасные операции, Python-код лишь упростит их комбинирование.
Безопасность
Исполнение сгенерированного кода нельзя считать безопасным само по себе.
codemode-lite предлагает два варианта sandbox:
- rootless-контейнер Podman;
- Python в WebAssembly через Pyodide.
Для Podman используются ограничения сети, файловой системы, прав процесса, памяти и количества процессов. Учётные данные остаются на стороне хоста, а реальные MCP-вызовы проходят через RPC.
Hugging Face также предупреждает, что локальный Python executor не является полноценной границей безопасности. Для недоверенного кода проект рекомендует изолированные среды, включая Docker и специализированные sandbox-сервисы.
Минимальный набор ограничений должен включать:
- отсутствие прямого доступа к секретам;
- запрет или строгий контроль сети;
- изолированную файловую систему;
- ограничения CPU, памяти и времени выполнения;
- белый список доступных функций;
- журналирование внешних действий;
- отдельное подтверждение чувствительных операций.
Как это связано с MCP
MCP-сервер с большим количеством tools обычно передаёт клиенту описание каждой функции. Code Mode может поставить перед такими серверами дополнительный слой:
Agent
↓
run_python
↓
Code Mode MCP
↓
несколько обычных MCP-серверовАгент сначала ищет инструменты по необходимости, затем получает схемы только выбранных функций. Это уменьшает объём постоянно передаваемого описания и позволяет объединять разные MCP-возможности обычным Python-кодом.
Такой слой особенно интересен для систем, где число интеграций постепенно растёт. Вместо десятков или сотен схем в каждом запросе модель получает компактный интерфейс выполнения кода и динамическое обнаружение доступных операций.
Итог
Code Mode не заменяет tools полностью. Он меняет способ, которым агент ими управляет.
Обычный агент вызывает функции по одной и после каждого результата снова обращается к модели. Code Mode позволяет модели один раз написать программу, выполнить внутри неё несколько операций и вернуть только обработанный результат.
Это даёт агенту привычные возможности языка программирования: циклы, условия, параллельность, обработку ошибок и преобразование данных. Одновременно sandbox и ограниченный набор функций сохраняют контролируемый доступ к приложению.
Для одного простого действия прямой tool call останется более понятным решением. Но когда агент работает с десятками инструментов и выполняет многошаговые операции, Python может оказаться более удобным интерфейсом, чем длинная последовательность JSON-вызовов.


Добавить комментарий