- BrainTools - https://www.braintools.ru -
Давайте будем честны: текущее состояние вызова функций (Function Calling) у LLM и экосистемы MCP (Model Context Protocol) вызывает изжогу в лучшем случае. Чтобы дать агенту возможность сделать curl или дернуть локальный скрипт, современному разработчику предлагают:
Описать килобайты монструозных JSON-схем прямо в системном промпте (и спалить 15-30k токенов контекста еще до первого вопроса пользователя).
Написать полноценный сетевой MCP-сервер со своим бойлерплейтом, SDK и зависимостями.
Завернуть всё это в жирный Docker-контейнер, выделив 200 Мб ОЗУ ради выполнения элементарного скрипта на три строки.
И один bash-tool вместо десяти нормальных инструментов задачу не решает, сессии превращаются в нечто вроде (псевдокод):
bash("systemctl is-active my-app.service")
bash("journalctl -u my-app.service -n 50 --no-pager")
bash("ss -tunlp | grep 3000")
bash("kill -9 $(lsof -t -i:3000)")
bash("systemctl restart my-app.service")
Что повторяется три раза только за эту сессию, и это – совсем не DRY (Don’t Repeat Yourself). Интуитивно у инженера в этом моменте слегка подскакивает пульс и давление, и он хочет абстрагировать это в call_tool("check_journals_and_stats", {...})
А когда в проекте накапливается больше 30 таких абстракций – модель неизбежно ловит Lost in the Middle и начинает галлюцинировать схемами, а разработчик оплачивает тонны входящих токенов за документацию API, которую агент даже не собирался вызывать в текущей сессии.
Отдельное спасибо здесь стоит сказать маркетингу Anthropic – работает он отлично. Термины вроде MCP, agent, harness, tooling теперь на слуху у людей, которые до этого к тулингу LLM никакого отношения не имели, причём многие оперируют ими скорее на уровне потребления хайпа, чем реального понимания. Модели у Anthropic отличные, тут вопросов нет. Но стоит помнить, что всё вокруг их флагманского продукта (включая сам MCP как протокол) в первую очередь продвигает этот продукт, а развитие области вокруг него – уже вторичная задача.
Короче, я устал от натягивания совы на глобус именованием MCP как стандартизированного протокола для всего тулинга самой популярной области последних лет и сделал 🛠️ toolhub [1] – self-hosted движок, который превращает любой консольный скрипт (Bun, Python, Bash, Go, PHP, etc) в инструмент для LLM на лету, с иерархической навигацией по дереву папок и околонулевыми требованиями к зависимостям.
Почему контекст модели забивается? Потому что типичные MCP-прокси дают модели плоский массив из 100 тулов.
У меня подход иной – древовидная навигация:
/
├── system/
│ ├── fetch_logs
│ └── restart_service
├── database/
│ └── query_pg
└── sublime/
└── replace-literal
Скажите мне, серьёзные разработчики, ML-щики, архитекторы, системные аналитики – не это ли было самым адекватным путём с самого начала? Хотя бы с момента, когда стало ясно, что без кеширования контекста направление продолжит быть убыточным.
Модель не знает схемы сотен инструментов заранее. На старте через hub.getSmartPrompt() в системный промпт инжектится только корневая карта дерева с краткими аннотациями назначений категорий (1–2 строки на папку). Модель стартует с базовым контрактом:
listTools("/system") – посмотреть, что валяется в конкретной ветке, и подтянуть точные описания только нужных инструментов.
callTool("/system/fetch_logs", { "service": "nginx" }) – вызвать конкретный инструмент с конкретными параметрами.
Результат: окно контекста чистое, модель лезет в папку только тогда, когда ей действительно нужен инструмент из этой категории. Когда она понимает, что к текущему запросу папка с ЭТИМ названием может иметь непосредственное отношение.
Важное уточнение – toolhub сознательно не работает ни с одним из стандартных интерфейсов tool calling каких-то конкретных провайдеров. Весь Re-Act loop построен на классических XML-like командах:
<hub>listTools("/")</hub>
<hub>callTool("/sublime/list-project-files", {})</hub>
Так с минимальным оверхедом на вызов мы даём возможность пользоваться тулами абсолютно любым моделям, даже если у них не встроена поддержка тулинга, или она сделана как-то странно/криво. По сути XML-обвязка (<hub>...</hub>) сопоставима или легче, чем JSON-разметка нативного tool_call у большинства провайдеров. Единственный таким образом порог входа для модели – понимание, что такое xml, json, js и древовидная навигация.
Поднимать Docker-контейнер на каждый чих – это дорого по ресурсам (особенно если вы крутите агентов на скромной VPS или Raspberry Pi). toolhub использует только Bun и его возможности. Глобальная зависимость всего одна, так что я даже не стал собирать для вас бойлерплейтные Dockerfile || docker-compose.yml – нужны они будут не всем, собрать самому – 3 минуты.
Механика исполнения под капотом простая как топор:
Workspace (/tmp) ➔ Инъекция (код + input.json) ➔ Установка deps ➔ runCmd с таймаутом ➔ rm -rf
Создание изолята: На лету генерируется временная папка /tmp/hub_run_<timestamp>_<hash>.
Инъекция кода и аргументов: В папку кладется код и input.json, а все параметры вызова автоматически пробрасываются в ENV-переменные вида $INPUT_<KEY_NAME>. Хочешь – парси JSON в коде, хочешь – пиши bash-однострочник, который сразу читает $INPUT_HOST. Никаких обвязок.
Зависимости: Если прописан installCmd, он отрабатывает в контексте папки. Bun и современные пакетные менеджеры за счет глобального кэша хоста подтягивают модули за миллисекунды; для Python-скриптов аналогично используются локальные wheel-кэши либо глобальное окружение хоста.
Запуск процесса: Выполняется runCmd с жестким ограничением timeoutMs. Результат считывается из output.json или stdout.
Очистка: Папка воркспейса сразу же аннигилируется через rm -rf.
Если скрипт выполняется в консоли – он автоматически становится навыком ИИ. Без написания оберток на протоколе MCP.
Я не против MCP как идеи, я против его кривых реализаций. В toolhub нативная поддержка MCP реализована в двух режимах:
Stateless (Stdio): Классика. Спавнится процесс -> отправляется запрос -> профит -> процесс убивается. Подходит для разовых утилит, т.е. для 90% того что есть на рынке. Так же для удалённых MCP в том числе.
Stateful Pool (Persistent PID): Самое вкусное. Если ваш MCP-сервер держит тяжелый контекст (Puppeteer с авторизованной сессией браузера, SSH-соединение, пул подключений к БД) – toolhub держит процесс в памяти [2]. Повторные вызовы отрабатывают за 10–30 мс без затрат на холодный старт. Простой регулируется через TTL (по дефолту 5 минут), после чего память освобождается. Повторное использование агентом до истечения TTL – продлевает его жизнедеятельность.
Если MCP-инструмент из внешнего сервера вам понравился, но вам хочется затюнить его описание под свою модель, навесить строгую output-schema или прописать few-shots примеры (что классический MCP напрямую не поддерживает) – кнопка MCP Promote в админке в 1 клик переносит его метаданные и схемы вызова напрямую в базу toolhub, позволяя докручивать промпт и валидацию инструмента прямо из UI.
Сам я не фанат завязки на конкретные закрытые проприетарные CLI, но если вы плотно сидите на Claude Code, Cursor или Cline – концептуально вам ничего не мешает использовать toolhub как единый бэкенд навыков.
Для этого достаточно написать микроскопический Stdio-мост на 40 строк (toolhub-mcp-bridge), который будет транслировать агентские вызовы Клода в REST API вашего локального toolhub. Прописали один сервер в конфиг – и Клодоподобные на лету видят всю вашу древовидную экосистему скриптов.
Перчатка брошена: кто первый напишет и оформит красивый toolhub-mcp-bridge в виде отдельного пакета/PR – с удовольствием добавлю в официальный README и закреплю авторство!
Узлы toolhub умеют каскадироваться.
Вы можете поднять локальный toolhub на рабочем ноутбуке, подключить к нему Remote-узлом toolhub вашего домашнего сервера, к которому подключен toolhub инфраструктуры дева.
Для агента это выглядит как единый путь: <hub>callTool("/office/home/server/reboot", {})</hub>
HubSDK берет на себя рекурсивную нормализацию путей, проксирование авторизации и прозрачное туннелирование запросов. А для защиты от дурака и циклических ссылок (когда Сервер А случайно ссылается на Сервер Б, ссылающийся на А) каждый запрос маркируется цепочкой заголовков x-hub-nodes с отслеживанием node_id каждого узла и жестким лимитом глубины (maxHops, по умолчанию задан разумный предел).
Любой инструмент или целую ветку дерева со всеми подпапками, раннерами и схемами можно экспортировать в один переносимый JSON-манифест .toolpack. Написал крутую связку для деплоя или работы с базой – выгрузил в 1 клик, закинул коллеге, он нажал «Import», и навыки сразу появились в его агенте. Так же с отдельными тулами.
А чтобы не бояться экспериментировать с промптами и кодом инструментов прямо в админке, toolhub ведет автоматический журнал ревизий (ToolVersion). Каждое сохранение создает неизменяемый снимок кода, схем и описания с возможностью отката к любой предыдущей версии в один клик.
Главная претензия теоретиков к древовидной навигации: «Это же лишний RTT! Зачем делать listTools, если можно отправить всё сразу?»
Воспроизводимый бенчмарк-сьют – код лежит прямо в репозитории в папке basic-toolpacks/benchmark/. Замеры проводились на локальном тестовом стенде (VM в Proxmox, 12 vCPU Intel i5-14500 cpu_type: host, 32 GB RAM, Debian 12, NVMe SSD).
Методология: 100 последовательных итераций на каждый сценарий с расчетом p50, p90, p99 перцентилей + стресс-тест на параллельный спавн воркспейсов:
|
Сценарий / Нагрузка |
Среда / Раннер |
Cold Start |
Avg Latency |
p50 |
p90 |
p99 |
Throughput |
|---|---|---|---|---|---|---|---|
|
|
Navigation Engine |
5.5 ms |
1.0 ms |
0.8 ms |
1.7 ms |
2.6 ms |
~990 req/s |
|
|
Navigation Engine |
1.7 ms |
1.5 ms |
1.4 ms |
2.1 ms |
3.6 ms |
~680 req/s |
|
Bash System Telemetry |
OS Spawn ( |
33.7 ms |
13.4 ms |
12.7 ms |
20.3 ms |
27.1 ms |
~75 req/s |
|
Bun CPU Cruncher (50k iters) |
Bun Runtime |
30.9 ms |
25.8 ms |
25.2 ms |
30.1 ms |
38.0 ms |
~39 req/s |
|
Bun Heavy Disk I/O (25 files batch) |
FS Workspace I/O |
31.7 ms |
34.3 ms |
26.2 ms |
66.7 ms |
129.6 ms |
~29 req/s |
|
Python 3 SHA256 (500 hashes) |
Python 3 Process |
69.5 ms |
64.8 ms |
41.6 ms |
129.1 ms |
177.0 ms |
~15 req/s |
Важно про железо: замеры выше сделаны на мощной VM (12 vCPU/32GB) – это потолок производительности runtime’а, а не то, что вы получите на Raspberry Pi или бюджетной VPS. На слабом железе (1 vCPU/512MB) абсолютные цифры будут выше в разы, но относительная разница между навигацией по дереву (~1 мс) и полноценным спавном процесса (10-60+ мс) сохранится – узкое место всегда сам раннер, а не Navigation Engine.
Параллельный стресс-тест на изоляцию воркспейсов:
15 одновременных вызовов тулов отработали параллельно суммарно за 138 мс (Ошибок: 0, 100% изоляция ФС во временных директориях).
Задержка навигации по дереву каталогов составляет 1.0–1.5 мс. Замеры отражают чистый overhead самого toolhub Runtime. На фоне сетевого latency и генерации токенов LLM (500–2000 мс) оверхед toolhub – это абсолютная погрешность.
Полный жизненный цикл одноразового bash-инструмента (генерация воркспейса /tmp/hub_run_*, запись input.json, спавн процесса, чтение output.json и атомарный rm -rf) занимает всего 13 миллисекунд.
Про базу данных: По умолчанию из коробки используется легковесная встроенная SQLite – чтобы проект поднимался без поднятия отдельных сервисов СУБД. Но благодаря тому, что весь бэкенд построен на Prisma ORM, при желании вынести ToolHub в высоконагруженный корпоративный кластер переезд на PostgreSQL, MySQL или CockroachDB сводится грубо говоря к изменению одной строчки провайдера в
schema.prisma.
Когда дерево НЕ даёт выигрыша (или даже мешает) – честный список:
|
Ситуация |
Почему |
|---|---|
|
Мало тулов (до ~10) |
Схема и так маленькая, а лишний |
|
Агент трогает почти все категории за сессию |
Если он всё равно облазит 50 из 60 тулов – дерево платит почти как плоский список, но ещё и с лишним RTT сверху |
|
Задача всегда одна и та же, тул известен заранее |
Навигация не нужна, если и так понятно, что вызывать |
|
Слабая модель с плохим tool-use |
Двухшаговая схема (сначала посмотреть, потом вызвать) требует дисциплины; модель послабее может путать пути или забывать [3] зайти в папку – тут надёжность плоского списка важнее экономии токенов |
|
Сервис с очень высокой параллельностью на одном тулсете |
Нишевый кейс, не основной сценарий toolhub – подробно можно обсудить отдельно, если кому-то актуально |
Итого: чем шире набор тулов и чем уже реальное использование за сессию – тем сильнее дерево выигрывает. toolhub целится ровно в этот кейс.
# 1. Клонируем
git clone https://github.com/Talos-Popcorn/toolhub.git
cd toolhub
# 2. Установка зависимостей
bun install
# 3. Синхронизация схемы БД Prisma
bun run db:push
# 4. Интерактивный seed+install
# (Выбор языка системного промпта EN/RU/ZH, настройка паролей админки и агента)
bun run db:seed
# Или в тихом режиме для CI/Docker:
# bun run prisma/seed.ts --lang=ru --admin-pass=admin --agent-pass=123
# 5. Запуск в дев режиме
bun run dev
Панель управления доступна по адресу http://localhost:5173/admin/ (или порт 3000 в прод-сборке). Swagger/OpenAPI документация: http://localhost:3000/docs (в дев-режиме). Дальше – импортируем какие-нибудь тулпаки из папки basic-toolpacks и пробуем запускать их в Playground.
Интеграция в любой JS/TS код занимает пару строк:
import { HubSDK } from './SDK/JS/sdk';
const hub = new HubSDK('http://localhost:3000', '123');
async function runAgentLoop(userQuery: string) {
const systemPrompt = await hub.getSmartPrompt();
const messages = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userQuery }
];
while (true) {
const aiResponse = await llm.generate(messages);
const action = await hub.processAgentResponse(aiResponse);
if (!action.called) {
console.log('Ответ агента:', aiResponse);
break;
}
messages.push({ role: 'assistant', content: aiResponse });
messages.push({
role: 'user',
content: `HUB_RESULT: ${JSON.stringify(action.result)}`
});
}
}
Кстати, для тех, кто пробовал мой прошлый проект 🧪lab [4] (статья [5], репо [6]) (serverless-клиент для LLM без бэкенда) – поддержка 🛠️ toolhub уже встроена и прекрасно работает. Теперь можно цеплять локальные тулы прямо из браузерного интерфейса. Смотрим, как работает:
Проект полностью открыт, распространяется под AGPL-3.0. Первичная задача – выкинуть из стека лишний оверхед и дать нормальный инструмент для быстрой автоматизации без переплат за токены.
GitHub Repo: github.com/Talos-popcorn/toolhub [1]
Буду рад конструктивной критике, issue и пулл-реквестам.
Запускать такое на рабочей тачке – опасно! Да, исполнение скриптов прямо в ОС хоста без изоляции несет риски. То, что по дефолту toolhub работает без Docker – это фича для экономии ресурсов на слабых VPS и Raspberry Pi. Заворачивать сам toolhub в Docker, LXC-контейнер или отдельную VM – это задача оператора под конкретный контур безопасности. Тебе никто этого не запрещает!
Зачем мне toolhub, если есть LangChain / LlamaIndex / AutoGen? Затем, что это тяжелые фреймворки с горой абстракций, жестко завязанные на код вашего приложения. 🛠️ toolhub – это самостоятельный standalone-сервис. Вы можете менять языки программирования, переключать LLM-провайдеров или переписывать агентов с нуля – ваши инструменты остаются централизованными, версионируемыми и доступными по единому REST/SDK API.
«Древовидная навигация через listTools – это же лишние RTT (Round Trip Time)! Модель тратит лишние итерации вместо того, чтобы сразу вызвать тул!» Арифметика простая: listTools("/") уходит в системный промпт через getSmartPrompt() и кэшируется точно так же, как и плоский список схем. А вот когда агент дальше идёт в конкретную папку – listTools("/database") – это уже вызов внутри диалога, и вот тут дерево начинает реально экономить, потому что в контекст попадают схемы только нужной ветки, а не всех 60 тулов разом. Проверялось на синтетических тулпаках для типичного использования toolhub – один агент, личное пользование, разработка – дерево дешевле плоского списка на 60-85% уже начиная с 30+ тулов, и разрыв растёт вместе с их числом. Подробнее – выше!
«Зачем писать бэкенд на Bun/TS? Почему не на Rust/Go, если мы боремся за производительность?» Потому что Bun дает идеальный баланс: стартует за миллисекунды, мгновенно исполняет JS/TS без транспиляции и не требует компиляции под каждую платформу. Если тебе нужен Rust – пиши тул на Rust, компилируй в бинарник и вызывай его через ToolHub как runCmd: "./my_rust_binary". ToolHub – ЯП-агностичен, а оверхед на запуск тулов благодаря Fastify минимален.
«Что произойдет, если 50 агентов одновременно запустят скрипты с pip install? Все упадет от гонки зависимостей?» Каждый вызов исполняется в абсолютном изоляте /tmp/hub_run_<timestamp>_<hash>. Папки и файлы не пересекаются в файловой системе. Если тебе нужен глобальный кэш тяжелых пакетов – используй глобальный pip-кэш на хосте или нативные бинарные раннеры. Безусловно, важно понимать, что всё зависит от раннера, тула, и прямоты твоих рук.
«А если скрипт зависнет в бесконечном цикле или пожрет всю память?» Для этого у каждого тула есть жестко заданный timeoutMs. По истечении таймаута процесс жестко гасится через SIGKILL, воркспейс подчищается через rm -rf, а агент получает нормальную ошибку [7] с таймаутом, а не висит намертво.
«Интеграция с Sublime Text в 2026 году?! Кто вообще пользуется Sublime, когда есть Cursor / VS Code?» Всё просто – я это сделал для себя. Мне важна скорость в 2 миллисекунды на diff, а не поедание 4 Гб ОЗУ Электроном ради подсветки пары строк. Поддержка Sublime сделана для тех, кто ценит реактивность. Хочешь под VS Code/Cursor/Windsurf/etc – SDK открыт, пиши .toolpack+bridge, PRы приветствуются. Лучшие тулпаки и тулы готов курировать, тестировать и добавлять в репо.
Почему XML-теги через обычный текст? Потому что нативный Function Calling жестко привязывает к конкретным вендорам, часто ломает стриминг и плохо работает на открытых/локальных моделях. XML-формат универсален для любых сеток. В SDK toolhub встроена базовая санитаризация (игнорирование тегов внутри markdown-блоков с кодом, исправление базовых огрехов JSON вроде одинарных кавычек), но общая культура промптинга и экранирование в UI – это, как и всегда, ответственность архитектуры вашего приложения.
Как это – нет докера? Что значит это фича? Это недоработка! toolhub рассчитан на Local-First / Trusted Node сценарии (когда агент крутится на твоем компе и выполняет ТВОИ скрипты). А если нужен Untrusted режим – докерфайл пишется за 3 секунды любой LLM. Сильно нужен готовый + композ = жду в issues и PR.
А без LLM этим вообще можно пользоваться? Да! 🛠️ toolhub – это, по сути, сверхлегкий self-hosted FaaS (Function-as-a-Service) с древовидным REST API. Если тебе нужно из Node.js бэкенда прозрачно вызывать Python-скрипты, дергать bash-автоматизацию по вебхукам или централизованно хранить утилиты с логами и версионированием – ты просто используешь REST/SDK напрямую из обычного классического кода без всяких нейросетей.
Три коммита в гитхабе? Четыре дня от роду? И ты предлагаешь мне этим пользоваться? Да так-то нет. Разработку своих проектов я веду локально в Gitea, и делюсь только тем, что действительно заслуживает шейринга. Самому проекту около 5 месяцев, а локальный репо насчитывает на данную секунду 57 коммитов. То же касается и 🧪lab [4] и всего что я ещё захочу выложить.
Автор: Talos13
Источник [8]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/35340
URLs in this post:
[1] 🛠️ toolhub: https://github.com/Talos-popcorn/toolhub
[2] памяти: http://www.braintools.ru/article/4140
[3] забывать: http://www.braintools.ru/article/333
[4] 🧪lab: https://labstudio.tech
[5] статья: https://habr.com/ru/articles/1047026/
[6] репо: https://github.com/Talos-popcorn/lab
[7] ошибку: http://www.braintools.ru/article/4192
[8] Источник: https://habr.com/ru/articles/1080890/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1080890
Нажмите здесь для печати.