tools
— в модели данных этого модуля; связь выбора агента
(agent_tools)
— в Agent Engine. Ниже — концепция, не контракт API.
Инструмент — вызываемая функция, которую платформа даёт модели как tool-schema. Каталог инструментов держит Admin.
Разные сущности, их не путать.
Схема инструмента приходит из структурированного источника, не из прозы админа — контракт вызова надёжен и не ломается правкой текста.
Каталог открытый; механизм — реестр типов, зеркало реестра коннекторов Harvester: тип объявляет себя классом (имя · tool-schema · вид credential · тело) и саморегистрируется.
тип — класс в коде, саморегистрируется ──▶
экземпляр — строка в
tools
(флаги + ключ)
preset v1web_search,
fetch_url.
custom v1custom/ (свой инструмент).
mcp · openapi v2
Провайдер за интерфейсом. web_search —
один инструмент для модели (web_search(query)), а провайдер
(Tavily · Brave · Serper · Google CSE) выбирает админ на
экране, не конфиг-файлом: выбор ложится в несекретную колонку
config, ключ — в credential_enc. Модель видит
одну стабильную схему; чем её исполнить — дело адаптера. Тот же приём,
что у провайдеров ai_models и встроенного эмбеддера.
Секрет инструмента (API-ключ) — не в config (JSONB), а в
отдельной колонке credential_enc: шифротекст AES-256-GCM
крипто-ядром Auth,
наружу — только флаг «задан». Как ключи провайдеров и SMTP.
Человекочитаемое имя идёт через t(), в БД — только ключ.
Организация добавляет свой инструмент одним классом по контракту:
тело — метод call, tool-schema — в манифесте,
регистрация декоратором.
✦ канонический pip-пакет → entry point achilles.tools ядро не трогаешь · апгрейд платформы чист ○ быстрый dev в дереве → achilles/ai_foundation/tools/custom/ своя папка · не мешает встроенным
call# 3 · discovery подхватит сам @register_tool class WebSearchTool(BaseTool): # контракт: схема + call manifest = ToolManifest( # определяет, что видит модель name = "web_search", # ключ + имя в tool-schema params = {"query": str}, # аргументы для модели access = ReadOnly, # read_only · write config = [Provider], # Tavily · Brave · Serper · CSE credential = [ApiKey], # → credential_enc ) def call(self, args): # тело инструмента return provider.search(args.query) def probe(self): # кнопка «Протестировать» → status return provider.ping() # active · error # любым способом ниже — в дереве или pip-пакетом — класс виден # только после пересборки образа и передеплоя контейнера: # discovery читает классы на старте процесса, не из Admin
├── base.py BaseTool — контракт: манифест + call ├── registry.py discovery: пакет + entry points ├── web_search.py ┐ встроенные ├── fetch_url.py ┘ пресеты (read-only) └── custom/ ← платформа папку не трогает └── jira.py ← свой класс сюда
# ядро не трогаем — discovery найдёт через entry point ├── tool.py ← тот же класс по контракту └── pyproject.toml [project.entry-points."achilles.tools"]
Контракт нейтрален: read-only — политика пресетов
платформы, а свой класс может писать — под ответственность
организации, как custom-коннектор (общий процесс, без песочницы;
оператор сам решает, что ставить). Контракт < 1.0 не
заморожен — пакет ребилдится под версию платформы.
Список на экране — живой реестр типов, не таблица:
таблица tools
хранит только состояние (флаги + ключ) и связана с типом по
name. Поэтому новый класс встаёт в каталог сам — без
миграции и правок UI.
tools = обе
отметки false, допуск).
tools заводится лениво — при
первом включении или вводе ключа.
name просто не сматчится и не мешает.
Один каталог питает обе поверхности, но контур у них разный.
chat_enabled)search · graph · sql, всегда, locked)KS-ядро у агента не снимается. Снятие добавленного инструмента — мягкая деградация: агент продолжает на ядре и в гейт готовности не входит (в отличие от снятия модели, которое агента останавливает).
«Locked» — про конфигурацию, не про безусловность.
«Всегда» и «locked» у ядра значат «нельзя выключить вручную ни админу,
ни владельцу», а не «присутствует при любом состоянии базы». Есть
вторая, производная ось: при пустой базе
(is_empty) KS-инструменты не подаются вовсе — искать
нечем. Это единый принцип на обе поверхности: и
search_knowledge у чата, и ядро у агента отпадают, пока в
базе нет данных; решает это общий
харнесс, а не отметка админа. Не «снятие» и не деградация
конфигом — просто отсутствие данных; наполнится база — ядро вернётся
само.
Каталог — лишь реестр типов; допуск инструмента к работе задают две независимые отметки, по одной на поверхность. Обе по умолчанию выключены — инструмент входит в работу только осознанным действием админа; активен он, пока подан хоть одной.
chat_enabled — инструмент виден чату.agents_allowed — разрешён к выбору владельцем в редакторе агента.
Включение инструмента и смена его настройки (провайдер, ключ) — это
админ-действие: оно ложится в аудит-журнал
наравне с прочими изменениями доступа (сами вызовы модели туда не
льются — журнал держит значимые события, не каждый запрос). Дефолт OFF
и аудит включения — периметр осторожности: внешний выход открывается
явно и под наблюдением. Лимит / бюджет вызовов web_search —
v2.
Ключ задан, провайдер выбран — но рабочий ли он? Админ не должен
узнавать о протухшем ключе из жалоб пользователей. У каждого инструмента
с внешним выходом — кнопка «Протестировать»: платформа
зовёт probe() типа (лёгкий пинг провайдера, не настоящий
запрос модели) и сохраняет итог. Зеркало проверки соединения у
провайдеров AI-моделей.
tools.status
и last_check_at — состояние держится между заходами, на
экране это пилюля «проверено · N назад» либо «ошибка».
web_search
(провайдер + ключ), fetch_url (доступность egress). Ядро
KS не тестируется — оно внутреннее и всегда доступно.
probe() — часть контракта типа, рядом с call;
свой инструмент организации объявляет её сам и встаёт в проверку
наравне с пресетами.
Подключение внешних MCP-серверов и инструментов записи — следующая итерация. Зафиксированный дизайн: per-user OAuth (каждый пользователь авторизуется сам), ACL держит сам сервер, доступ read-only через курирование админом. В макетах — серые неактивные заглушки.