Исполняющие среды
Плагин объявляет в манифесте одну или несколько исполняющих сред — способов, которыми ядро может с ним взаимодействовать:
[plugin]
runtime = "in_process" # одна среда
# или
runtime = ["in_process", "mcp_stdio"] # плагин работает в нескольких средах
Ядро выбирает подходящий адаптер при загрузке и общается с плагином через него. Для потребителя (кода, который получает плагин из реестра и вызывает его методы) разница между средами невидима — все адаптеры возвращают единый прокси-объект.
Три среды
in_process — нативный вызов в том же процессе
Плагин живёт в том же процессе, что и ядро. Вызов хука — прямой вызов метода объекта плагина без сериализации.
Когда использовать:
- Плагин написан на том же языке, что и ядро.
- Плагин не требует изоляции (доверенный код).
- Производительность критична (вызовы миллионы раз в секунду).
Ограничения:
- Плагин должен быть в пакете, импортируемом ядром.
- Падение плагина (segfault в C-extension, утечка памяти, deadlock) = падение ядра.
mcp_stdio — подпроцесс через stdio
Плагин запускается как отдельный процесс; общение — через stdin/stdout по MCP-протоколу (JSON-RPC 2.0 поверх line-delimited JSON).
Когда использовать:
- Плагин написан на другом языке (Python-ядро, Go-плагин).
- Нужна изоляция (сторонний плагин из untrusted-источника).
- Плагин имеет тяжёлые зависимости (C-extension, ML-модель), которые не хочется тянуть в основной процесс.
Ограничения:
- Все входы/выходы обязаны быть JSON-сериализуемыми (инвариант 2 из ADR-0003).
- Overhead на каждый вызов — spawn процесса (один раз на lifetime плагина) и JSON-сериализация (на каждый вызов). Не подходит для hot-path с жёсткими требованиями к latency.
mcp_http — удалённый HTTP-сервис
Плагин развёрнут как отдельный сервис, доступный по HTTP. Ядро делает MCP-запросы через HTTP.
Когда использовать:
- Плагин — внешний сервис (SaaS, корпоративный микросервис).
- Плагин требует специальной инфраструктуры (GPU-инстанс, scaled-horizontally worker-пул).
- Вы хотите обновлять плагин без перезапуска ядра.
Ограничения:
- Сеть — дополнительная точка отказа (таймауты, transient errors).
- Все ограничения
mcp_stdioплюс свои — auth-токены, TLS, CA-bundles. - Overhead на каждый вызов = network roundtrip + JSON-сериализация.
Таблица сравнения
| Свойство | in_process | mcp_stdio | mcp_http |
|---|---|---|---|
| Разные языки ядра и плагина | ✗ | ✓ | ✓ |
| Изоляция от основного процесса | ✗ | ✓ (subprocess) | ✓ (отдельный хост) |
| Latency вызова | ~μs | ~10-100μs | ~ms |
| Serialization required | ✗ (нативные объекты) | ✓ (JSON) | ✓ (JSON) |
| Сетевые ошибки | невозможны | невозможны | возможны |
| Deploy независим от ядра | ✗ | ✓ | ✓ |
| Подходит для live-stream большого трафика | ✓ | ✗ | ✗ |
Множественный runtime в одном плагине
Плагин может декларировать несколько runtime-адаптеров:
runtime = ["in_process", "mcp_stdio"]
Это значит: ядро может запустить плагин в любом из указанных режимов, выбор — на стороне хоста. Типичный сценарий:
- in_process — когда ядро на Python и плагин тоже на Python: прямой импорт.
- mcp_stdio — когда ядро на TypeScript, плагин только на Python: запуск subprocess.
Каждый binding ответственен за реализацию адаптера для поддерживаемых runtime-адаптеров.
Декларация runtime для своего плагина
Минимальный случай — один runtime:
[plugin]
name = "openai_compatible"
kind = "llm"
runtime = "in_process"
core_version = ">=0.1.0,<1.0.0"
Множественный — массив:
[plugin]
name = "semantic_search"
kind = "tool"
runtime = ["in_process", "mcp_stdio", "mcp_http"]
core_version = ">=0.1.0,<1.0.0"
Runtime-specific поля манифеста
Для каждого runtime-адаптера могут быть специфичные поля. Отдельный groupId от таб-блоков с языковыми примерами, чтобы выбор «мой язык» не перепутался с выбором «какой runtime обсуждаем».
- in_process
- mcp_stdio
- mcp_http
[plugin]
runtime = "in_process"
entry_point = "plugin:OpenAIPlugin" # REQUIRED для in_process
[plugin]
runtime = "mcp_stdio"
[plugin.mcp_stdio]
command = ["python", "-m", "openai_plugin"]
startup_timeout_sec = 30
[plugin]
runtime = "mcp_http"
[plugin.mcp_http]
url = "https://plugins.example.com/openai"
auth_header = "X-API-Token"
timeout_sec = 60
Полная схема полей каждого runtime-адаптера — в spec-репозитории.
Когда какой runtime выбирать — flowchart
┌─ Плагин на том же языке, что и ядро?
│
├─ Да → нужна ли изоляция?
│ │
│ ├─ Нет, и производительность важна
│ │ → in_process
│ │
│ └─ Да, плагин из untrusted-источника
│ → mcp_stdio
│
└─ Нет (разные языки):
│
├─ Плагин — внешний сервис (SaaS, корп. микросервис)
│ → mcp_http
│
└─ Плагин — локальный, нужна локальная установка
→ mcp_stdio
Последствия выбора runtime для кода плагина
| Инвариант | in_process | mcp_stdio | mcp_http |
|---|---|---|---|
| Serializable boundaries (ADR-0003 §2) | рекомендуется | обязательно | обязательно |
| Resources через DI | обязательно | обязательно | обязательно |
| Чистота от внешнего состояния среды | обязательно | обязательно | обязательно |
execution_model имеет значение | да | нет (process boundary) | нет (network boundary) |
Ключевое наблюдение: все три runtime-адаптера требуют одинаковых runtime-инвариантов. Это делает миграцию плагина с in_process на mcp_stdio (например, когда нужно выделить его в подпроцесс для изоляции) механической — код плагина не меняется, меняется только манифест.
См. также
- Манифест плагина — полное поле
runtimeи runtime-specific секции. - Обнаружение — как ядро выбирает адаптер при загрузке.
- Инварианты runtime — 8 инвариантов, обязательных во всех runtime-адаптерах.
- ADR-0003: Orchestration-neutral runtime — нормативный контракт.