Перейти к основному содержимому

Виды плагинов

Вид плагина (kind) — это контракт, которому обязаны соответствовать все плагины данного вида. Вид задаёт:

  • набор хуков, которые плагин обязан реализовать;
  • типы входов и выходов каждого хука;
  • семантику диспетчеризации (один из пяти классов — см. ADR-0002);
  • требования к ресурсам (что приложение обязано инъектировать).

Когда вы пишете плагин и объявляете kind = "chunker" в манифесте, ядро проверяет, что ваш плагин реализует контракт chunker-вида, и регистрирует его. Другие компоненты приложения запрашивают плагины по виду, не зная конкретных имён реализаций.

Встроенные виды

В текущей v1.0-реализации нормативно зафиксированы два вида в spec-репозитории; остальные виды объявляются приложением-потребителем:

ВидHookspecНазначение
toolkinds/tool/v1.yamlФункция-как-плагин: принимает структурированный аргумент, возвращает структурированный результат.
orchestratorkinds/orchestrator/v1.yamlУправление жизненным циклом долгоживущих операций — enqueue, status, backfill.

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

Data-обработка:

  • source — источник данных (S3-bucket, Postgres-таблица, Kafka-топик).
  • processor — трансформация/обогащение записей.
  • sink — целевое хранилище (Elasticsearch, warehouse, файл).

Уведомления и интеграции:

  • notifier — отправка уведомлений (email / SMS / webhook / push).
  • auth_provider — OAuth / SAML / LDAP интеграция.
  • webhook_handler — обработчик входящих webhooks.

Бизнес-логика:

  • payment_provider — Stripe / PayPal / внутренний платёжный модуль.
  • tax_calculator — расчёт налогов per юрисдикция.
  • pricing_strategy — ценовая стратегия.

AI / RAG (один из возможных сценариев):

  • llm — языковая модель.
  • embedder — текст → вектор.
  • vector_store — векторное хранилище.
  • chunker — разбиение текста на фрагменты.
  • reranker — повторное ранжирование.

Observability / platform:

  • metric_exporter — выгрузка метрик в Prometheus / OTLP.
  • audit_logger — запись событий в audit-trail.
  • rate_limiter — лимитирование запросов.

Эти виды не встроены в plugin-system ядро — приложение регистрирует их hookspecs при старте. Один и тот же dagstack-инсталл применим ко всем этим классам: plugin-system не знает про домен, только про контракты.

Kind — opaque string для ядра

Plugin-system не валидирует значение поля kind в манифесте. Для ядра это непрозрачный идентификатор. Ответственность за список разрешённых видов — на приложении-потребителе:

  1. Приложение регистрирует hookspecs видов, которые ожидает, на старте.
  2. При discovery манифест плагина с неизвестным kind отклоняется с ошибкой KindUnknown.
  3. Приложение читает плагины определённого вида через registry.get_plugin("kind", name="...") или через диспетчер.

Это делает kind расширяемой концепцией — приложение определяет свой набор видов без изменений в ядре plugin-system.

Объявление собственного вида

Приложению часто нужны собственные виды плагинов — например, notifier с конкретной сигнатурой send(recipient, message, metadata), payment_provider с charge(amount, customer_id, idempotency_key), или llm с complete(prompt, temperature, max_tokens). Объявление собственного вида выполняется в два шага:

1. Написать hookspec в YAML

Hookspec описывает все хуки вида, их сигнатуры (через JSON Schema) и классы диспетчеризации.

kinds/llm/v1.yaml
kind: llm
kind_api_version: 1.0.0
description: |
Языковая модель — завершает текст по промпту; опционально поддерживает
чат-интерфейс и streaming.

hooks:
- name: complete
dispatch: singleton
description: Сгенерировать завершение для промпта.
input_schema: schemas/complete.input.json
output_schema: schemas/complete.output.json
mcp_exposed: true

- name: chat
dispatch: singleton
description: Чат-запрос с историей сообщений.
input_schema: schemas/chat.input.json
output_schema: schemas/chat.output.json
mcp_exposed: true

Структура hookspec детально описана в ADR-0004.

2. Сгенерировать типы для своего языка

Из hookspec эмитируются типы для каждой реализации (pydantic для Python, zod для TypeScript, struct+interface для Go). Приложение коммитит сгенерированный код в свой репозиторий и использует как обычные типы.

# Generated from kinds/llm/v1.yaml
from typing import Protocol
from pydantic import BaseModel


class CompleteInput(BaseModel):
prompt: str
temperature: float = 0.7
max_tokens: int | None = None


class CompleteOutput(BaseModel):
text: str
tokens_used: int


class LLMPlugin(Protocol):
def complete(self, input: CompleteInput) -> CompleteOutput: ...
def chat(self, input: ChatInput) -> ChatOutput: ...

3. Зарегистрировать hookspec в приложении

При старте приложение регистрирует hookspec, чтобы ядро могло валидировать манифесты плагинов этого вида:

import asyncio
import logging
from dagstack.plugin_system import PluginContext, PluginRegistry
from my_app.kinds import LlmHookSpec, EmbedderHookSpec, VectorStoreHookSpec


async def main() -> None:
registry = PluginRegistry()
# Register the hookspecs of our kinds first.
registry.add_hookspecs(LlmHookSpec)
registry.add_hookspecs(EmbedderHookSpec)
registry.add_hookspecs(VectorStoreHookSpec)

registry.discover("plugins/")

ctx = PluginContext(
config={},
logger=logging.getLogger("app"),
registry=registry,
)
await registry.setup_all(ctx)


asyncio.run(main())

Версионирование видов

Hookspec вида версионируется через поле kind_api_version (semver). Плагин объявляет в манифесте, какую версию вида он реализует; при несовместимости — VersionIncompatible.

  • Minor bump (1.0.01.1.0) — добавлен новый опциональный хук, старые плагины продолжают работать.
  • Major bump (1.0.02.0.0) — breaking change в сигнатуре существующего хука. Плагины обязаны обновиться.

Антипаттерны

  • «Мега-вид» на все случаи. Вид с хуками на 10 разных операций (chunk, embed, search, rerank) стирает границы между ответственностями. Разбивайте на специализированные виды.
  • Вид ради одного плагина. Если вид имеет одну реализацию без планов на альтернативы — возможно, это прямой вызов функции, а не плагин. Плагин — это когда есть хотя бы две реализации (реальные или потенциальные).
  • Произвольные kind-имена без конвенций. В экосистеме приложения — договоритесь о naming convention: всё строчными, snake_case, существительные, без версий в имени. llm, не LLM / LanguageModel / llm_v2.

См. также