Обзор DeepSeek Harness для разработки плагинов
Разбираем архитектуру DeepSeek Harness: как работает модульная система плагинов и как использовать этот фреймворк для создания AI-агентов.
Архитектура DeepSeek Harness
Архитектура DeepSeek Harness
С практической точки зрения DeepSeek Harness (DSH) — это не просто обёртка над API, а полноценный каркас для управления жизненным циклом AI-агента. Если вы когда-либо настраивали сложные цепочки в LangChain, то знаете, что проблема часто заключается в отсутствии предсказуемости: сложно понять, в какой именно момент агент решил вызвать инструмент и почему передал в него некорректные данные. В DSH эта проблема решается за счёт жёсткой модульности.
Принцип Everything is a Plugin
В основе DSH лежит концепция, которую можно сравнить с архитектурой микросервисов, но внутри одного процесса. Здесь всё — от способа подключения к LLM до конкретного навыка поиска в базе данных — является плагином.
Такой подход обеспечивает изоляцию. Если вам нужно заменить модель DeepSeek на локальную Llama через Ollama или изменить логику логирования, вы не переписываете ядро системы, а просто подменяете модуль в конфигурации. Это напоминает работу с Docker-контейнерами: каждый плагин имеет свои границы и интерфейс взаимодействия.
Типичная структура плагина в DSH выглядит так:
- Manifest: описание возможностей и зависимостей.
- Core Logic: реализация функционала.
- Interface: определение входных и выходных типов данных.
Для регистрации нового плагина в системе используется конфигурационный файл. Например, чтобы добавить кастомный инструмент для мониторинга ресурсов вашего сервера (что будет полезно, если вы строите систему по принципу автоматизации и ИИ-агентов на сервере), запись в dsh-config.yaml будет выглядеть примерно так:
yamlplugins:
- name: "system_monitor"
path: "./plugins/system_monitor"
enabled: true
config:
metrics: ["cpu", "ram", "disk"]
interval: 60s
Взаимодействие компонентов системы
Взаимодействие внутри Harness строится на событийной модели и строгом соблюдении контрактов. Компоненты не обращаются друг к другу напрямую, а обмениваются сообщениями через внутреннюю шину.
Процесс обработки запроса выглядит следующим образом:
- Input Handler принимает запрос и передаёт его в Orchestrator.
- Orchestrator анализирует доступные плагины и определяет, какой из них может решить задачу.
- Если требуется внешний инструмент, вызывается соответствующий DSH-plugin, который возвращает результат в строго определённом формате.
- Context Manager обновляет историю диалога, чтобы агент не терял нить рассуждений.
Если мы посмотрим на взаимодействие с точки зрения отладки, то увидим, что каждый шаг фиксируется в логах. Чтобы проверить, как агент вызывает плагины в реальном времени, можно запустить Harness с флагом расширенного логирования:
bashdsh run --verbose --plugin-trace=all
С практической точки зрения такая архитектура позволяет создавать сложные системы, подобные агенту-управляемому экспонату музея на Rust, где один модуль отвечает за сенсоры, другой за синтез речи, а DSH выступает в роли «мозга», координирующего их работу.
Важно понимать, что предсказуемость системы здесь зависит от качества описания интерфейсов плагинов. Если вы используете навыки Matt Pocock для LLM при составлении промптов для плагинов, вероятность того, что агент вызовет функцию с неверными аргументами, значительно снижается.
В итоге мы получаем систему, где ядро отвечает только за маршрутизацию и управление состоянием, а вся бизнес-логика вынесена в сменные модули. Это избавляет от необходимости поддерживать огромный монолитный код и позволяет обновлять отдельные части агента без риска обрушить всю инфраструктуру.
Разработка и подключение плагинов
Структура типичного dsh-плагина
Каждый плагин в DeepSeek Harness — это Go-пакет, реализующий интерфейс Plugin. Минимальная структура выглядит так:
myplugin/
├── go.mod
├── main.go
└── plugin.go
В plugin.go определяется точка входа и регистрация хендлеров:
gopackage main
import (
"context"
"github.com/data-star-org/dsh/pkg/plugin"
)
type MyPlugin struct{}
func (p *MyPlugin) Name() string {
return "myplugin"
}
func (p *MyPlugin) Register(ctx context.Context, reg *plugin.Registry) error {
reg.RegisterHandler("myplugin.action", p.handleAction)
return nil
}
func (p *MyPlugin) handleAction(ctx context.Context, input map[string]any) (map[string]any, error) {
return map[string]any{"result": "done"}, nil
}
func main() {
plugin.Serve(&MyPlugin{})
}
Собираем и кладём в директорию плагинов:
bashgo build -o /opt/dsh/plugins/myplugin.so -buildmode=plugin .
Плагин становится доступным в системе после перезапуска dshd.
Регистрация и конфигурация модулей
Регистрация плагина происходит через конфигурационный файл dsh.yaml. Пример:
yamlplugins:
myplugin:
enabled: true
path: /opt/dsh/plugins/myplugin.so
config:
timeout: 30
retries: 3
Параметр config передаётся в плагин через контекст. Чтобы прочитать его:
gofunc (p *MyPlugin) handleAction(ctx context.Context, input map[string]any) (map[string]any, error) {
cfg := plugin.ConfigFromContext(ctx)
timeout := cfg.GetInt("timeout", 10)
// ...
return map[string]any{"timeout": timeout}, nil
}
Перезагрузить конфиг без перезапуска демона:
bashdshctl reload
Если плагин не появляется в списке:
bashdshctl plugins list
Убедитесь, что .so-файл собран под ту же версию Go, что и dshd. Несовпадение версий — частая причина падений при загрузке плагина.
Ранее мы разбирали подход к запуску агентов на собственном сервере — смотрите Автоматизация и ИИ-агенты на сервере. Также интересен материал о Ponytail: ленивом подходе к AI-агентам, где обсуждается похожая модульность.
Практическое применение в AI-агентах
Интеграция с внешними инструментами
DeepSeek Harness строится вокруг идеи, что агент без инструментов — это как сервер без сети: теоретически работает, но бесполезен. Архитектура плагинов здесь выдержана похоже на systemd-юниты: каждый плагин — это самостоятельный модуль с чётко описанным интерфейсом, который можно включать и выключать без перезагрузки ядра.
Вот как выглядит структура плагина:
python# plugins/weather.py
from dsh.plugin import DSHPlugin
import requests
class WeatherPlugin(DSHPlugin):
name = "weather"
description = "Получение погоды по геокоординатам"
def execute(self, lat: float, lon: float) -> dict:
url = f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}¤t_weather=true"
resp = requests.get(url, timeout=10)
return resp.json()
Регистрация плагина в конфиге dsh.yaml:
yamlplugins:
- name: weather
path: ./plugins/weather.py
enabled: true
permissions:
network: true
timeout: 10s
С практической точки зрения, это удобно, потому что вы изолируете внешние вызовы. В продакшене я всегда заворачиваю такие плагины в circuit breaker и ставлю timeout на уровне конфига — иначе один повисший API может положить всего агента.
Если мы посмотрим на автоматизацию ИИ-агентов на сервере, то увидим, что Harness позволяет не писать boilerplate для каждого нового инструмента. Достаточно унаследоваться от DSHPlugin, реализовать execute, и плагин сам подхватится через рефлексию.
Обеспечение предсказуемости ответов
Здесь у Harness есть свои нюансы. В отличие от Ponytail, где агент «действует», а потом объясняет, что делал, DeepSeek Harness требует явного контракта между плагином и LLM-ядром. Каждый плагин описывает не только входные параметры, но и формат выхода:
python# plugins/database.py
class DatabaseQueryPlugin(DSHPlugin):
name = "db_query"
schema = {
"type": "function",
"function": {
"name": "db_query",
"parameters": {
"type": "object",
"properties": {
"sql": {"type": "string", "description": "SQL-запрос"},
"timeout": {"type": "integer", "default": 30}
},
"required": ["sql"]
}
}
}
def execute(self, sql: str, timeout: int = 30) -> dict:
# Выполнение запроса с ограничением по времени
...
Этот подход даёт предсказуемость, но требует дисциплины. В реальной лаборатории мне приходилось дорабатывать плагины, чтобы они не возвращали сырые JSON-ответы, а структурировали данные заранее. Иначе LLM начинает «домыслить» поля, которых нет в ответе, и агент уходит в бесконечный цикл.
Для отладки полезен режим трассировки:
bashdsh run --trace --plugin-trace ./agents/museum_bot.py
Он выводит каждый вызов плагина, входные параметры и сырой ответ. Это как strace для агента — через пару сессий вы чётко понимаешь, где «промахивается» модель.
Связанный материал: Реверс-инжиниринг с агентами показывает, как предсказуемость помогает не просто дёргать API, а анализировать поведение внешних систем через агентов.
Сравнение с альтернативными фреймворками
Гибкость против сложности настройки
DeepSeek Harness ставит своей задачей дать разработчику максимум свободы в описании поведения агента, но эта свобода имеет цену. Где конвенциональные фреймворки вроде LangChain или AutoGen предлагают готовые цепочки обработки (chains, agents, tools), DSH требует описывать каждый шаг вручную через DSL на базе Python-классов.
Вот типичный плагин в DSH:
pythonfrom dsh.plugin import Plugin
from dsh.context import Context
class MyPlugin(Plugin):
name = "my_plugin"
async def handle(self, ctx: Context):
# Полная свобода действий
if ctx.intent == "search":
result = await self.call_api("search", query=ctx.query)
ctx.response = f"Результат: {result}"
elif ctx.intent == "summarize":
summary = await self.summarize(ctx.input_text)
ctx.response = summary
Сравните с LangChain, где тот же функционал занимает три строки:
pythonfrom langchain.agents import load_tools, initialize_agent
from langchain.llms import OpenAI
llm = OpenAI(temperature=0)
tools = load_tools(["serpapi", "wikipedia"], llm=llm)
agent = initialize_agent(tools, llm, agent="zero-shot-react-description")
agent.run("Найди и кратко перескажи статью о ZFS")
Разница в философии: LangChain решает задачу за вас, DSH даёт вам набор кирпичиков и говорит «стройте сами, но помните, что за каждым кирпичиком — последствия». Это как сравнить сборку мебели из IKEA с покупкой готовой в магазине: в первом случае вы знаете каждую деталь, во втором — просто пользуетесь.
С практической точки зрения, если вы хотите быстро прототипировать AI-агента для домашнего сервера — например, чтобы он отвечал на вопросы по логам journald — DSH потребует больше времени на изучение API. Но если ваш агент должен работать в сложном цикле обратной связи с внешними системами (как в этой статье про экспонаты музея), гибкость DSH становится не просто преимуществом, а необходимостью.
Производительность при масштабировании
Архитектура DSH построена вокруг асинхронного цикла событий (asyncio), что даёт ей преимущество над синхронными фреймворками при работе с I/O-операциями. Но при масштабировании до сотен одновременных запросов начинают проявляться ограничения.
Вот как выглядит запуск агента в продакшене с использованием systemd:
ini# /etc/systemd/system/dsh-agent.service
[Unit]
Description=DeepSeek Harness Agent
After=network.target
[Service]
Type=simple
User=dsh
WorkingDirectory=/opt/dsh
ExecStart=/usr/bin/python3 -m dsh.server --config /etc/dsh/config.yaml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Конфигурация для вертикального масштабирования:
yaml# /etc/dsh/config.yaml
server:
host: 0.0.0.0
port: 8080
workers: 4
plugins:
- name: llm_backend
type: deepseek
model: deepseek-chat
api_key: ${DEEPSEEK_API_KEY}
timeout: 30
- name: cache
type: redis
url: redis://localhost:6379/0
logging:
level: INFO
format: json
Сравните с Ponytail, который использует более лёгкий подход с lazy-вычислениями и может выдерживать большую нагрузку за счёт упрощённой архитектуры. Или с Matt Pocock skills, где каждый навык — это отдельный HTTP-сервис, масштабируемый независимо.
Горизонтальное масштабирование в DSH работает через запуск нескольких воркеров, но синхронизация состояния между ними — это уже ваша забота. В отличие от Kubernetes-ориентированных решений, DSH не предоставляет встроенного механизма распределения сессий или shared state.
Если мы посмотрим на реверс-инжиниринг с агентами, где требуется сохранять контекст диалога на протяжении длительных сессий, DSH показывает себя хорошо благодаря поддержке Redis и PostgreSQL в качестве бекендов для хранения состояния. Но это требует ручной настройки и мониторинга — типичная для DSH позиция: вы получаете мощь, но за ней следует поддерживать инфраструктуру.
Часто задаваемые вопросы
Что такое DeepSeek Harness и зачем он нужен для разработки плагинов
DeepSeek Harness (DSH) — это модульный каркас для управления жизненным циклом AI-агента, который решает проблему непредсказуемости цепочек вызовов, характерную для LangChain. Если вы когда-либо сталкивались с тем, что агент внезапно передаёт в инструмент некорректные данные или вызывает его в неподходящий момент, вы поймёте, в чём преимущество DSH. Здесь всё — от подключения к LLM до конкретного навыка — реализовано как плагин с чётко определённым интерфейсом, что обеспечивает предсказуемость и изоляцию компонентов.
Как устроена типичная структура плагина в DSH
Каждый плагин в DSH состоит из трёх компонентов: манифеста (manifest), описывающего возможности и зависимости; основной логики (core logic), реализующей функционал; и интерфейса (interface), определяющего входные и выходные типы данных. Эта структура напоминает архитектуру микросервисов, но внутри одного процесса: каждый плагин имеет свои границы и чётко определённый контракт взаимодействия. Благодаря этому вы можете заменить, например, модель DeepSeek на локальную Llama через Ollama, не переписывая ядро системы — достаточно подменить соответствующий модуль в конфигурации.
Как зарегистрировать и включить кастомный плагин в конфигурации DSH
Для регистрации нового плагина используется конфигурационный файл dsh-config.yaml, где в секции plugins указывается имя, путь к директории с плагином, флаг включения и параметры конфигурации. Например, чтобы добавить кастомный инструмент для мониторинга ресурсов сервера, достаточно создать директорию ./plugins/system_monitor с реализацией плагина и добавить запись в конфиг с нужными параметрами. После этого DSH автоматически обнаружит плагин при запуске и сделает его доступным для использования в цепочках агента.
Можно ли заменить встроенную модель DeepSeek на локальную Llama через Ollama
Да, это одно из ключевых преимуществ модульной архитектуры DSH. Поскольку подключение к LLM реализовано как отдельный плагин, вы можете создать или использовать существующий плагин для Ollama и подменить им стандартный плагин DeepSeek в конфигурации. Это особенно полезно, если вы строите систему на собственном оборудовании и хотите сохранить конфиденциальность данных, не прибегая к внешним API. При этом остальная логика агента остаётся неизменной — только источник ответов меняется.
Что делать, если плагин не регистрируется или вызывает ошибки при запуске
Во-первых, убедитесь, что путь к плагину в конфигурации указан корректно и директория существует. Во-вторых, проверьте, что манифест плагина содержит все необходимые поля и соответствует формату, ожидаемому DSH. В-третьих, включите отладочное логирование для плагина — это поможет выявить, на каком этапе происходит сбой. Если проблема в логике вызова, изучите журналы выполнения цепочки: DSH предоставляет детальную трассировку того, какие плагины вызываются и с какими параметрами, что значительно упрощает отладку по сравнению с более «чёрными» фреймворками.