Архитектура DeepSeek Harness: всё — это плагин
Разбираем архитектуру DeepSeek Harness для AI-агентов: философия «всё — это плагин», роль Cordis, YAML-конфиги и деплой на Proxmox или Kubernetes.
Что такое DeepSeek Harness
Что такое DeepSeek Harness
DeepSeek Harness — это фреймворк для построения AI-агентов, который сам по себе не делает почти ничего. И в этом его сила.
Звучит парадоксально, но на практике именно такой подход даёт максимальную гибкость. Вместо того чтобы тащить в ядро фреймворка поддержку всех возможных провайдеров, инструментов и протоколов, авторы Harness оставили в ядре только самый минимум: запуск модели, обработку промптов, цикл диалога. Всё остальное — LLM-клиенты, интеграции с MCP-серверами, плагины для внешних API — вынесено в отдельные модули, которые подключаются по необходимости.
Для домашней лаборатории это особенно удобно: можно собрать минимальный агент под конкретную задачу, не раздувая стек на десятки зависимостей, которые потом ещё и обновлять придётся.
Философия «всё — это плагин»
Если посмотреть на репозиторий deepseek-ai/DeepSeek-Harness, архитектурная идея считывается с первого взгляда. Ядро (пакет dsh) предоставляет базовый класс агента и контракт для плагинов, а дальше начинается собственно сборка.
С практической точки зрения это означает три вещи.
Во-первых, предсказуемость поведения. Ядро небольшое, его можно прочитать целиком и понять, что именно происходит между запросом пользователя и вызовом модели. Никакой магии под капотом, никаких неявных сайд-эффектов — что в коде написано, то и работает.
Во-вторых, расширяемость через плагины (dsh-plugin-*). Нужен доступ к GitHub — подключаете соответствующий плагин. Нужна работа с файловой системой — другой плагин. Нужно интегрировать собственный инструмент — пишете свой плагин по документации, и он встраивается точно так же, как и штатные.
В-третьих, честное разделение ответственности. Плагины не лезут в ядро, ядро не лезет в плагины. Когда что-то ломается, сразу понятно, где искать причину: либо в вашем конфиге, либо в конкретном плагине. Это сильно упрощает отладку по сравнению с монолитными фреймворками, где одно тянет за собой другое.
Пример минимального агента с подключением плагина через конфиг выглядит примерно так:
yaml# harness.config.yaml
agent:
name: home-assistant
model: deepseek-chat
system_prompt: |
Ты ассистент для управления домашней лабораторией.
Отвечай кратко, используй только те инструменты, которые доступны.
plugins:
- dsh-plugin-shell
- dsh-plugin-http
Сам по себе этот конфиг ещё ничего не делает — он лишь описывает, какие модули подключить. Дальше Harness стартует агент, регистрирует плагины в контейнере зависимостей и передаёт управление циклу диалога.
Роль Cordis в AI-оркестрации
Cordis — это сервисный контейнер, на котором построен Harness. Если проводить аналогию из мира, который мне ближе, Cordis для Harness — это как systemd для Linux: не самая очевидная для новичка абстракция, но именно она держит на себе всю архитектуру.
Технически Cordis — это IoC-контейнер с поддержкой жизненного цикла сервисов. Плагины в Harness регистрируются как сервисы, объявляют зависимости друг от друга через декораторы, а Cordis сам разруливает порядок инициализации и завершения. Когда агент стартует, Cordis последовательно поднимает плагины в правильном порядке, инжектит зависимости и следит за тем, чтобы при остановке всё корректно свернулось в обратном порядке.
Для оркестрации AI-агента это критично. У плагина, который ходит в LLM, есть зависимость на конфиг. У плагина, который ходит во внешний API, есть зависимость на HTTP-клиент и, возможно, на тот же конфиг. Если попытаться разруливать это руками, быстро получится клубок из импортов и побочных эффектов. Cordis берёт эту работу на себя.
С практической точки зрения Cordis даёт ещё один полезный эффект: ленивая загрузка. Плагин не поднимается до тех пор, пока его не попросят. Можно держать в конфиге десяток модулей, но пока агент реально не вызовет соответствующий инструмент — этот модуль не съест ни мегабайта памяти.
Возвращаясь к теме расширяемости: если вы пишете собственный плагин, вы просто наследуетесь от базового класса и регистрируете сервис через декоратор. Никакой магии:
typescriptimport { Service } from '@cordis/core';
import { definePlugin } from 'dsh';
class GitHubPlugin extends Service {
async fetchIssue(repo: string, num: number) {
const resp = await this.ctx.http.get(
`https://api.github.com/repos/${repo}/issues/${num}`
);
return resp.data;
}
}
export default definePlugin({
name: 'github',
services: [GitHubPlugin],
});
Дальше этот плагин подключается через тот же harness.config.yaml — и агент получает новый инструмент. Ядро Harness про этот плагин ничего не знает и знать не должно.
Именно эта комбинация — минимальное ядро, плагинная архитектура поверх сервисного контейнера — делает Harness удобной базой для экспериментов с AI-агентами в домашней инфраструктуре. Можно начать с одного плагина, постепенно добавлять интеграции и не бояться, что очередное обновление сломает половину сценариев использования.
Если тема AI-агентов и построения графов знаний по кодовой базе вам близка, имеет смысл заглянуть в материал по созданию графа знаний кодовой базы с помощью Graphify — там хорошо раскрыта смежная идея: маленькое ядро и подключаемые модули под конкретную задачу.
Установка и первый запуск
Развёртывание на Proxmox или Kubernetes
Прежде чем запускать Harness, определитесь с площадкой. У меня дома три ноды Proxmox (Винни-Пух, Пятачок и Сова — про имена серверов в честь советских мультфильмов я уже писал, и традиция жива), плюс k3s-кластер для экспериментов. Для Harness подходят оба варианта, но подход разный.
Если вы уже работаете с Proxmox — проще всего использовать LXC-контейнер. Не путайте с полноценной виртуалкой: для Harness не нужна изоляция на уровне гипервизора, достаточно контейнера с доступом к сети. На ноде Proxmox:
bashpveam available | grep debian
pveam download local debian-12-standard_12.2-1_amd64.tar.zst
Создайте контейнер. Идём в веб-интерфейс Proxmox либо через CLI:
bashpct create 240 local:vztmpl/debian-12-standard_12.2-1_amd64.tar.zst \
--hostname harness-01 \
--cores 4 \
--memory 8192 \
--rootfs local-lvm:32 \
--net0 name=eth0,ip=192.168.1.240/24,gw=192.168.1.1,bridge=vmbr0 \
--features nesting=1
pct start 240
pct enter 240
Внутри контейнера ставим Node.js 20 и pnpm. Без nvm — это лишний слой, который потом мешает systemd-юнитам:
bashapt update && apt install -y curl ca-certificates gnupg
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
corepack enable && corepack prepare pnpm@latest --activate
Дальше — клонируем репозиторий и ставим зависимости:
bashgit clone https://github.com/deepseek-ai/DeepSeek-Harness.git /opt/harness
cd /opt/harness
pnpm install
pnpm build
Теперь самое интересное — как это вообще запускается. Harness — это Node.js-процесс, который держит Cordis-контейнер с активными плагинами. Под неё нужен обычный systemd-юнит, без всяких менеджеров процессов уровня pm2. Они только мешают, когда нужно нормально логировать через journald.
bashuseradd -r -s /bin/false harness
mkdir -p /etc/harness /var/log/harness
chown -R harness:harness /opt/harness /var/log/harness
Конфиг кладём в /etc/harness/harness.yaml. Подробнее о его структуре поговорим в следующем разделе, а пока — минимальный рабочий вариант:
yaml# /etc/harness/harness.yaml
plugins:
- dsh-plugin-config
- dsh-plugin-http
- dsh-plugin-agent
log:
level: info
destination: /var/log/harness/harness.log
Сам юнит:
ini# /etc/systemd/system/harness.service
[Unit]
Description=DeepSeek Harness AI Agent Runtime
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=harness
Group=harness
WorkingDirectory=/opt/harness
EnvironmentFile=-/etc/harness/harness.env
ExecStart=/usr/bin/node /opt/harness/dist/index.js
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Не забудьте systemctl daemon-reload и systemctl enable --now harness. Проверяем:
bashsystemctl status harness journalctl -u harness -f
Если видите в логах что-то вроде plugin dsh-plugin-agent loaded in 142ms — всё, базовый рантайм живой.
С Kubernetes подход другой, но не принципиально. Для экспериментов у меня крутится k3s на двух Raspberry Pi 5 (8 ГБ) — туда я и закидываю Harness, когда нужно проверить, как оно работает под оркестратором. Базовый манифест:
yaml# harness-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: harness
labels:
app: harness
spec:
replicas: 1
selector:
matchLabels:
app: harness
template:
metadata:
labels:
app: harness
spec:
containers:
- name: harness
image: ghcr.io/deepseek-ai/harness:latest
args: ["--config", "/etc/harness/harness.yaml"]
ports:
- containerPort: 8080
name: http
volumeMounts:
- name: config
mountPath: /etc/harness
readOnly: true
- name: plugins
mountPath: /opt/harness/plugins
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2"
memory: "4Gi"
volumes:
- name: config
configMap:
name: harness-config
- name: plugins
emptyDir: {}
ConfigMap с конфигом — отдельный манифест:
yamlapiVersion: v1
kind: ConfigMap
metadata:
name: harness-config
data:
harness.yaml: |
plugins:
- dsh-plugin-config
- dsh-plugin-http
- dsh-plugin-agent
log:
level: info
С практической точки зрения, для домашней лаборатории LXC на Proxmox проще и предсказуемее: виден весь стек, легко снять дамп памяти, если что-то пошло не так, а ресурсы расходуются экономнее, чем под отдельной виртуалкой. Kubernetes оправдан, только если вы уже подняли там Prometheus, Grafana и хотите единый мониторинг — тогда добавьте scrape-конфиг и снимайте метрики с плагина dsh-plugin-metrics. Без этого смысла городить k3s ради одного пода нет.
Для продакшн-окружения однозначно выбирайте Kubernetes — но это уже за рамками домашней лаборатории.
Создание первого плагина через dsh create-plugin
Вот тут начинается самое интересное — философия «всё — это плагин» в действии. Если посмотреть на исходники репозитория, то сам по себе Harness не делает почти ничего: HTTP-сервер, логгер и runtime для плагинов. Всё остальное — агенты, коннекторы к LLM, интеграции с внешними системами — это плагины, которые подключаются через Cordis.
Чтобы создать заготовку плагина, в репозитории есть CLI-команда dsh create-plugin. Она генерирует шаблон с правильной структурой, манифестом и точкой входа:
bashcd /opt/harness
pnpm run dsh create-plugin @myorg/dsh-plugin-greeter
После выполнения в директории plugins/@myorg/dsh-plugin-greeter появится типичный для Cordis-плагина набор файлов. Структура примерно такая:
plugins/@myorg/dsh-plugin-greeter/
├── package.json
├── src/
│ ├── index.ts
│ └── config.ts
└── README.md
Главное в этом наборе — package.json, потому что именно оттуда Harness узнаёт, что вообще нужно подгрузить. Посмотрите на сгенерированный манифест:
json{
"name": "@myorg/dsh-plugin-greeter",
"version": "0.1.0",
"description": "Example greeting plugin for Harness",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"harness": {
"name": "greeter",
"config": {
"schema": "./dist/config.js"
}
},
"cordis": {
"service": "greeter"
}
}
Поле harness — это контракт между плагином и рантаймом. Здесь указывается имя сервиса (внутренний идентификатор в Cordis-контейнере) и путь к схеме конфигурации. Поле cordis.service дублирует это для самой Cordis — так работает её механизм внедрения зависимостей.
Точка входа src/index.ts содержит базовый класс сервиса. Cordis использует декораторы для регистрации обработчиков событий, поэтому сгенерированный код выглядит примерно так:
typescriptimport { Service, Context } from 'cordis';
import { Schema } from 'dsh-plugin-config';
interface GreeterConfig {
greeting: string;
language: 'ru' | 'en';
}
export class GreeterService extends Service {
static inject = ['config'];
constructor(ctx: Context, private options: GreeterConfig) {
super(ctx, 'greeter');
ctx.on('ready', () => {
this.ctx.logger('greeter').info(
`${this.options.greeting}, language=${this.options.language}`
);
});
}
}
export default GreeterService;
Статическое поле inject = ['config'] — это запрос зависимости. Cordis-контейнер при инициализации плагина найдёт сервис с тегом config (его предоставляет dsh-plugin-config) и передаст его экземпляр в конструктор. Если зависимость не зарегистрирована — получите внятную ошибку на старте, а не падение через час в продакшне. Это одна из тех мелочей, которые делают Cordis предсказуемой штукой.
Файл src/config.ts отвечает за валидацию YAML-конфигурации. Схема — обычная Zod-схема, обёрнутая в декоратор:
typescriptimport { Schema } from 'dsh-plugin-config';
@Schema({ expose: true })
export class GreeterSchema {
greeting: string = 'Привет';
language: 'ru' | 'en' = 'ru';
}
Что здесь происходит: декоратор @Schema регистрирует схему в плагине конфигурации, а expose: true означает, что поля можно переопределить через harness.yaml. Если поле не указано — используется значение по умолчанию из класса. Это даёт предсказуемость: разработчик видит дефолты прямо в коде, а не ищет их в документации.
После того как код готов, соберите плагин:
bashcd plugins/@myorg/dsh-plugin-greeter
pnpm install
pnpm build
В dist/ появится скомпилированный JS с типизацией. Чтобы Harness подхватил плагин, есть два пути.
Первый — локальный, через директорию plugins/ в репозитории. Добавьте плагин в pnpm-workspace.yaml, если используете монорепо (а Harness его использует):
yaml# pnpm-workspace.yaml
packages:
- 'plugins/*'
- 'plugins/@*/*'
Затем пересоберите корень и перезапустите сервис:
bashcd /opt/harness
pnpm install
pnpm build
systemctl restart harness
Второй путь — npm-реестр. Если плагин опубликован, в harness.yaml достаточно указать имя пакета в секции plugins:
yamlplugins:
- dsh-plugin-config
- dsh-plugin-http
- dsh-plugin-agent
- '@myorg/dsh-plugin-greeter'
Harness на старте пройдёт по списку, загрузит каждый пакет, зарегистрирует его в Cordis-контейнере и свяжет через inject-зависимости. Если на каком-то этапе что-то отвалится — увидите это в journalctl с указанием конкретного плагина.
Несколько практических замечаний, прежде чем двигаться дальше. Во-первых, не пихайте всю логику в один плагин. Cordis располагает к композиции: один плагин отвечает за транспорт (HTTP), другой за аутентификацию, третий за интеграцию с LLM. Если плагин разросся — это сигнал, что его стоит разбить. Во-вторых, всегда объявляйте зависимости через static inject. Иначе получите неявный порядок инициализации, и через полгода сами не вспомните, почему всё работает только в определённой последовательности. В-третьих, версии плагинов фиксируйте. Cordis не придирчив к semver, но вы при регрессии — придирчивы.
Похожий подход к плагинам используется и в других AI-инструментах — например, в Ponytail для Claude Code тоже сделана ставка на расширяемость через дополнительные модули. Но там это скиллы для конкретного клиента, а здесь — полноценный runtime с собственной системой сервисов.
Если вам интересно копнуть глубже в смежные темы — например, как строить граф знаний поверх всего этого хозяйства — посмотрите настройку Graphify и руководство по оптимизации AI-агентов. Там архитектурные идеи пересекаются.
Анатомия плагина: YAML, TypeScript и IDashPlugin
Структура YAML-конфигурации
Прежде чем лезть в TypeScript, посмотрим на внешний слой — YAML-конфиг. DeepSeek Harness читает его через Cordis и превращает в дерево зависимостей. Файл делится на секции, каждая из которых описывает либо метаданные плагина, либо его конфигурацию, либо регистрацию middleware.
Минимальный рабочий конфиг выглядит так:
yaml# config.yaml
plugins:
- name: my-plugin
enabled: true
config:
apiKey: ${API_KEY}
timeout: 30
middleware:
- request
- response
Здесь name — это идентификатор плагина, по которому Cordis будет искать соответствующий TypeScript-модуль. Директива enabled позволяет выключить плагин без удаления его кода, что удобно для staging-среды. Блок config пробрасывается в конструктор плагина, а middleware объявляет точки расширения конвейера.
Если плагин требует внешних сервисов, их описывают в секции dependencies:
yamlplugins:
- name: postgres-plugin
enabled: true
dependencies:
- name: postgresql
host: ${DB_HOST}
port: 5432
database: agents
config:
poolSize: 10
Cordis разрешает зависимости автоматически — если postgres-plugin зависит от postgresql, фреймворк сначала зарегистрирует драйвер, затем передаст его инстанс в конструктор плагина. С практической точки зрения это убирает ручную инициализацию: нет нужды вручную вызывать injector.get(...) внутри плагина, фреймворк сделает это за вас через декораторы.
Переменные окружения подставляются через синтаксис ${VAR}. На стадии парсинга YAML Cordis выполняет интерполяцию, поэтому отсутствующая переменная уронит загрузку с понятной ошибкой — это лучше, чем молчаливый undefined в рантайме.
Интерфейс IDashPlugin и его методы
Теперь переходим к TypeScript-стороне. Каждый плагин реализует интерфейс IDashPlugin, который задаёт контракт между фреймворком и вашим кодом. Упрощённо он выглядит так:
typescriptinterface IDashPlugin {
readonly name: string;
readonly version: string;
apply(ctx: PluginContext, config: Record<string, unknown>): void | Promise<void>;
onStart?(ctx: PluginContext): void | Promise<void>;
onStop?(ctx: PluginContext): void | Promise<void>;
// Хуки конвейера (опциональны)
beforeRequest?(req: Request): void | Promise<void>;
afterResponse?(res: Response): void | Promise<void>;
}
Метод apply — точка входа. Cordis вызывает его первым, передавая PluginContext (по сути обёртку над DI-контейнером) и распарсенный блок config. Здесь плагин регистрирует свои сервисы, обработчики, middleware. Например:
typescriptimport { IDashPlugin, PluginContext } from 'dsh';
export default class LoggingPlugin implements IDashPlugin {
readonly name = 'logging-plugin';
readonly version = '1.2.0';
apply(ctx: PluginContext, config: Record<string, unknown>): void {
const level = config.level ?? 'info';
ctx.register('logger', () => new Logger(level));
ctx.on('request', this.logRequest);
}
private logRequest = (req: any) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
};
async onStart(ctx: PluginContext): Promise<void> {
const logger = ctx.get('logger');
logger.info('LoggingPlugin started');
}
async onStop(ctx: PluginContext): Promise<void> {
const logger = ctx.get('logger');
logger.info('LoggingPlugin stopped');
}
}
Жизненный цикл плагина состоит из трёх фаз: apply (регистрация), onStart (инициализация ресурсов после старта всех плагинов) и onStop (корректное завершение). Такой порядок гарантирует, что к моменту запуска все зависимости уже загружены — плагин может смело запрашивать их через ctx.get(...).
Хуки beforeRequest и afterResponse подключают плагин к конвейеру обработки запросов. Они вызываются синхронно или асинхронно — фреймворк дожидается Promise, если хук его вернул. Это позволяет строить цепочки middleware, где каждый участник модифицирует запрос или ответ. В реальной практике я использую эту механику для трассировки, rate limiting и кэширования — каждый из этих плагинов весит 30–50 строк и не знает о существовании соседей.
Отдельно стоит упомянуть регистрацию плагина в фреймворке. Поскольку DeepSeek Harness — это «плагин для плагинов», сам фреймворк тоже реализует IDashPlugin. Если вы пишете собственный оркестратор поверх Harness, ваш код станет плагином в иерархии фреймворка — такая рекурсия выглядит странно, но на практике даёт предсказуемость композиции: поведение системы полностью описывается списком загруженных плагинов.
Если хотите копнуть глубже в смежную тему кодовой базы, посмотрите создание графа знаний с помощью Graphify — там разобран похожий подход к декларативной конфигурации инструментов, а оптимизационные приёмы для AI-агентов собраны в руководстве по affaan-m/ECC.
Сравнение с альтернативами
DeepSeek Harness против LangChain и AutoGen
Прежде чем влезать в технические детали, давайте определимся с терминами. DeepSeek Harness — это не фреймворк для построения нейросетей и не SDK для вызова LLM. Это runtime для AI-агентов, построенный вокруг идеи «всё — это плагин». Если LangChain — это швейцарский нож с кучей насадок, а AutoGen — это конструктор диалогов между агентами, то Harness — это сборочный цех, где каждая деталь лежит на своём месте и подключается через единую шину.
Самое важное различие — в точке расширения. В LangChain вы наследуете класс BaseTool или BaseChain, импортируете нужные модули из langchain-community и надеетесь, что версии зависимостей не поедут. В AutoGen вы описываете агентов как объекты с system_message и регистрируете функции через декораторы. В обоих случаях расширение — это код на Python, и ничего больше.
В Harness всё иначе. Плагин — это директория с тремя обязательными файлами:
/etc/dsh/plugins/weather-plugin/
├── plugin.yaml
├── index.ts
└── README.md
plugin.yaml — это манифест:
yamlname: weather
version: 1.2.0
runtime: nodejs20
entry: index.ts
permissions:
- network:outbound
- storage:read
hooks:
- on_agent_init
- before_tool_call
Никаких setup.py, никаких pyproject.toml, никаких регистраций в глобальном реестре через PR в основной репозиторий. Cordis (движок плагинов внутри Harness) находит плагины по манифесту, загружает их в изолированный контекст и прокидывает зависимости через систему инъекций. Это очень похоже на то, как Cordis работает в экосистеме Koishi, и это не случайно — проект явно вдохновлён этой философией.
С практической точки зрения, это даёт три вещи, которых нет у конкурентов.
Во-первых, предсказуемость жизненного цикла. Плагин можно установить, обновить, отключить или удалить без перезапуска агента. В LangChain для замены тула часто нужно пересобирать цепочку целиком, потому что тул прибит гвоздями к графу выполнения.
Во-вторых, разрешения как first-class citizen. Секция permissions в манифесте — это не декоративная строчка. Cordis парсит её и применяет через capabilities на уровне V8-isolates. Плагин, заявивший storage:read, физически не сможет записать файл за пределы выделенной директории. В LangChain и AutoGen вопросы песочницы решаются на уровне ОС или вообще остаются на совести разработчика.
В-третьих, YAML вместо кода для конфигурации. Вот типичный конфиг агента в Harness:
yamlagent:
id: research-assistant
model: deepseek-chat
system_prompt: |
Ты исследователь. Ищи информацию, проверяй источники,
формируй структурированный отчёт.
plugins:
- web-search
- pdf-parser
- citation-formatter
memory:
backend: postgres
retention: 30d
rate_limits:
requests_per_minute: 30
tokens_per_hour: 100000
Такой конфиг можно хранить в Git, можно рендерить через Ansible, можно передавать через ConfigMap в Kubernetes. В LangChain аналогичная логика размазана по Python-коду и часто требует отдельного слоя абстракции, чтобы не хардкодить параметры модели.
Не стоит воспринимать это как агитацию против LangChain. У LangChain огромная экосистема интеграций, и если ваша задача — быстро склеить пайплайн из готовых компонентов, он справится лучше. Harness целесообразен, когда вы строите долгоживущую систему, в которой плагины будут появляться от разных команд и обновляться независимо.
Когда стоит выбирать плагинную архитектуру
Плагинная архитектура — это не серебряная пуля. Она оправдана в конкретных сценариях, и попытка применить её там, где она не нужна, закончится overengineering.
Когда выбирать Harness:
- У вас несколько команд разработчиков, которые пишут инструменты для агента независимо друг от друга. Плагины решают проблему координации версий и зависимостей без центрального релиз-менеджера.
- Агент работает в продакшене и должен переживать обновления без даунтайма. Hot-reload плагинов в Cordis — это не маркетинговая фича, а реальный механизм, который можно наблюдать через
dsh plugin reload weather. - Вам важна аудитируемость. Манифест плагина с явными
permissionsиhooks— это уже половина документации для compliance. Остальное собирается из логов Cordis. - Инфраструктура у вас на Kubernetes или Proxmox, и вы привыкли к тому, что всё декларативно. Harness ложится в этот стек естественно: плагины упаковываются в OCI-образы, конфиги агентов едут через Helm-чарты.
Когда НЕ стоит выбирать Harness:
- Вы пишете прототип на выходные. Оверхед на описание манифеста, настройку Cordis и деплой плагина не окупится.
- У вас один агент с тремя статичными инструментами. Плагинная архитектура добавит сложности без выгоды.
- Стек жёстко завязан на Python, а команда не готова поддерживать Node.js-рантайм. Да, у Harness есть экспериментальная поддержка Python через cordis-python, но она пока сырая.
С практической точки зрения, граница проходит по количеству плагинов и частоте их обновлений. Если у вас меньше пяти инструментов и они меняются раз в квартал — берите LangChain. Если десять и больше, и они обновляются каждую неделю от разных авторов — Harness окупится.
Если вы уже работаете с AI-агентами и оптимизируете их производительность, посмотрите на руководство по оптимизации AI-агентов с affaan-m/ECC. Там разбираются подходы, которые хорошо ложатся на плагинную модель — например, вынос тяжёлой обработки в отдельные плагины с собственными лимитами.
Ещё один аргумент в пользу Harness — совместимость с инструментами вроде Graphify. Когда граф знаний кодовой базы строится отдельным плагином с собственными permissions: storage:write, основной агент остаётся чистым и не разрастается в монолит. Подробнее о связке таких подходов — в материале про создание графа знаний с помощью Graphify.
Итог: DeepSeek Harness — это не замена LangChain или AutoGen, а инструмент для другого класса задач. Если ваша система похожа на операционную систему с пакетами, а не на скрипт из двадцати строк — плагинная архитектура даст ту самую предсказуемость, которая отличает домашнюю лабораторию от продакшена.
Часто задаваемые вопросы
Чем DeepSeek Harness отличается от LangChain или LlamaIndex на практике?
Если коротко — шириной ядра и предсказуемостью. Ядро Harness (dsh) делает буквально одну вещь: запускает цикл «промпт → модель → ответ», всё остальное подключается плагинами. В LangChain за годы развития накопилось столько абстракций, что отладка типичного агента превращается в археологическую экспедицию по слоям колбэков. Для домашней лаборатории, где агент обычно один и задачи у него конкретные, Harness удобнее именно за счёт читаемости — открываете код ядра и за десять минут понимаете, что и в каком порядке выполняется.
Зачем нужен Cordis, если есть обычные плагины?
Cordis — это не альтернатива плагинам, а инфраструктура для их жизненного цикла: регистрация, разрешение зависимостей между плагинами, корректный запуск и остановка. С практической точки зрения Cordis решает типичную боль плагинных систем: когда плагин A зависит от плагина B, а тот — от C, руками разруливать порядок загрузки и обработку ошибок инициалисти неудобно. Cordis делает это по контракту, поэтому предсказуемость поведения сохраняется даже при десятке подключённых модулей.
Можно ли запустить DeepSeek Harness без YAML-конфига?
Технически — да, минимальный агент стартует и с дефолтами, потому что ядро само по себе почти ничего не требует. Но смысл YAML в том, чтобы явно описать, какие плагины подключены и с какими параметрами. Для домашнего использования я рекомендую всё-таки заводить конфиг: во-первых, его удобно коммитить в Git и отслеживать изменения, во-вторых, при обновлении плагинов сразу видно, что именно у вас включено. Файлы конфигурации у Harness человекочитаемые, так что это не та история, где «yaml в yaml поверх yaml».
Как лучше деплоить Harness — на Proxmox в контейнере или в Kubernetes?
Если у вас уже есть кластер на Proxmox и вы запускаете агента штучно — LXC-контейнер или VM с Docker Compose будет проще и экономичнее. Никакого смысла тащить k3s ради одного пода нет. Kubernetes оправдан, когда агентов несколько, они используют разные модели, и хочется единообразно управлять секретами (API-ключи провайдеров), лимитами и мониторингом через Prometheus. В моём случае на домашней лаборатории живёт один агент в LXC на ноде «Сова», и для этой нагрузки k8s был бы оверкилом.
Что делать, если плагин после обновления перестал работать?
Во-первых, не паниковать и не катить обновление сразу в работающий агент — это общее правило для любых плагинных систем. Во-вторых, посмотреть journalctl -u dsh-agent или логи контейнера: Harness пишет, какой именно плагин упал и на каком этапе жизненного цикла. В-третьих, проверить changelog конкретного плагина — обычно ломается совместимость контракта, и откат на предыдущую версию (dsh-plugin-foo==1.2.3 в Compose-файле) решает вопрос быстрее, чем разбор причин. Долгосрочное решение — фиксировать версии плагинов в манифесте зависимостей и поднимать их осознанно.