needhelp
← Retour au blog

DeepSeek Harness : un framework d'agent open source où littéralement tout est un plugin

par needhelp
DeepSeek
AI Agents
Open Source
Cordis
Plugin Architecture

Une pile agent complète, déposée sur GitHub

DeepSeek a lancé une preview développeur de DeepSeek Harness (dsh) le 13 août 2026. Le dépôt se trouve sur deepseek-ai/deepseek-harness. En fin de journée, il comptait 1,6k étoiles, 12 293 commits et 19 contributeurs — des chiffres qui indiquent qu’il ne s’agit pas d’un projet de week-end. La base de code est à 97,1 % TypeScript, avec 1,6 % de CSS et 0,7 % de Python. Version du paquet à la publication : 0.1.0-rc.5.

La page de présentation est sur deepseek.com/harness et la documentation développeur sur deepseek-harness.github.io/deepseek-harness.

Licence MIT.

Le pitch en une ligne

D’après le README : « Agent = Model + Harness. »

Le modèle, c’est l’âme. Un harness permet à un agent de comprendre son environnement, d’utiliser des outils et de continuer à fonctionner dans des contextes réels. La position de DeepSeek : chaque capacité — modèles, outils, compétences, sessions, bacs à sable, stockage, boucles, ordonnancement et l’interface — est un plugin échangeable.

Ce n’est pas un slogan. C’est imposé par l’architecture.

Cordis : le noyau sous tout ça

DeepSeek Harness est construit au-dessus de Cordis, un framework de plugins vendu. La conception de Cordis est décrite dans un article intitulé A Programming Paradigm for Spatiotemporal Composability.

Tout le framework se réduit à cinq idées :

  1. Un plugin est un objet qui implémente Service. Ça peut être une fonction avec inject et apply(ctx), ou une sous-classe de Service.
  2. Un contexte est un dépôt de services. Un plugin réclame une clé stable comme ctx.tools, ctx.llm ou ctx.sessions. Les autres plugins trouvent les services par clé au lieu d’importer des implémentations concrètes.
  3. Déclarez la dépendance de service via inject. Un plugin qui nomme les services requis attend que ceux-ci existent. Pas de séquençage de démarrage manuel.
  4. Événements typés pour la communication. Quatre modes de diffusion : emit (on tire et on oublie), waterfall (style middleware avec next()), parallel (tous les auditeurs s’exécutent en même temps) et serial (les auditeurs s’exécutent dans l’ordre avec des valeurs de retour).
  5. Les enregistrements sont des effets réversibles. Sections de prompt, schémas d’outils, adaptateurs, auditeurs — tout s’installe via ctx.effect() pour que le rechargement et l’arrêt les déroulent de façon prévisible.

Le modèle waterfall de Cordis est un middleware enveloppant. Un auditeur reçoit (...args, next). Appelez next() pour déléguer au service suivant ; retournez sans next() pour court-circuiter. Pour les événements à décision unique, le court-circuit fait partie du design — un auditeur de politique peut s’approprier une décision purement et simplement.

Tout est un plugin (sérieusement)

La liste des plugins sur deepseek.com/harness détaille toute la surface de capacités :

  • Models — les backends LLM (DeepSeek, OpenAI, Anthropic, Bedrock, Vertex, Azure, Codex, plus n’importe quel point de terminaison compatible OpenAI)
  • Tools — ce que le modèle peut appeler
  • Skills — ensembles réutilisables de prompts et d’outils
  • Sessions — gestion de l’état de conversation
  • Sandboxes — où le code s’exécute (bash PTY, backend local danger-full-access, bac à sable natif basé sur landlock)
  • Storage — persistance des sessions
  • Loops — la logique de tour de l’agent
  • Scheduling — répartition des sous-agents et des tâches
  • UI — l’interface Web servie sur le port 3080 par défaut

Un développeur peut sélectionner, échanger ou étendre tout ça via la configuration — sans modifier le code source de DeepSeek Harness lui-même. Le Catalogue de config des plugins est généré automatiquement à partir de la source (scripts/gen-config-catalog.ts) et vérifié à jour par la CI, donc chaque champ d’un bloc config: de cordis.yml correspond exactement au type de config déclaré par un plugin.

Quatre modes d’exécution

La documentation livre quatre préréglages. Chacun cible un cas d’usage différent.

Mode Standard — Agent de codage complet : édition de fichiers, shell, recherche de fichiers et sur le Web, compétences, planification, objectifs, sous-agents et flux de travail. C’est l’expérience par défaut de l’interface Web.

Mode Code — Tout ce qui est dans Standard, mais les outils sont exposés via un SDK Code Mode pour que le modèle puisse combiner des opérations multi-étapes dans un seul programme TypeScript. Un programme généré par le modèle remplace plusieurs tours d’appels d’outils.

Mode Minimal — Juste deux outils : un shell bash persistant et str_replace_editor. C’est pour étalonner des modèles dans un environnement épuré. Pas de compétences, pas de planification, pas de sous-agents — la capacité brute du modèle face à une vraie base de code.

Mode Creator — Conçu pour écrire des préréglages d’agent personnalisés. Inclut toutes les capacités du mode Standard plus l’inspection à l’exécution, des expérimentations de plugins Cordis en mémoire et des conseils de création de préréglages. C’est comme ça qu’on construit le cinquième, sixième, septième mode.

Chaque exécution est traçable

C’est ici que Harness se distingue de la plupart des outils pour agents. Tout ce que le modèle voit est enregistré dans un journal de session en écriture seule :

  • Prompts système
  • Sortie de raisonnement
  • Appels d’outils et leurs résultats
  • Décisions d’ordonnancement des sous-agents
  • Chaque injection de contexte

L’interface Web a une vue Trajectory où on inspecte ces enregistrements filtrés par source. Reprendre, bifurquer, chercher et rejouer — ces quatre opérations fonctionnent toutes sur le même flux d’événements. Pas de base de données de traces séparée, pas d’étape d’export. Le JSONL de session est la source de vérité.

Pour le SDK Python, le répertoire de session stocke du JSONL non compressé contenant les requêtes modèle assemblées et les appels d’outils. L’exemple dans examples/jsonrpc-agent/minimal.py montre ça de bout en bout.

Configuration des modèles : DeepSeek + n’importe quoi

La page de configuration des modèles (Configurer les modèles) comporte trois couches.

Officiel DeepSeek. Ouvrez Settings → Models, collez une clé API DeepSeek, enregistrez. La clé est en écriture seule. Après enregistrement, l’interface reçoit un descripteur masqué, jamais le texte brut. Les clés résident dans \$DSH_HOME/.credentials.yaml ; la page des paramètres ne garde qu’une référence à la crédentielle.

Fournisseurs du catalogue. Cliquez sur « Add provider », choisissez Anthropic ou OpenAI, collez la clé. Les fournisseurs qui utilisent une authentification native (Bedrock via creds AWS + région, Vertex via projet ADC, Azure via api-version, Codex via OAuth) ne fonctionnent pas avec juste un champ de clé API — chacun a besoin de son propre chemin d’authentification.

Fournisseurs personnalisés. Pour une passerelle d’entreprise, un serveur auto-hébergé ou n’importe quel fournisseur hors catalogue. Définissez un ID de fournisseur en minuscules (permanent — les requêtes, sessions sauvegardées, valeurs par défaut de modèle et références de crédentielles l’utilisent tous), l’URL de base, le protocole API, les crédentielles et au moins un modèle.

Les modèles visuels nécessitent une étape supplémentaire. Comme un point de terminaison personnalisé n’a aucun moyen d’annoncer ses modalités prises en charge, le formulaire ne peut pas détecter automatiquement la prise en charge de la vision. Vous ajoutez input: [text, image] au modèle dans \$DSH_HOME/settings.yaml. Si tous vos modèles personnalisés acceptent des images, définissez defaultInput: [text, image] une fois au niveau du fournisseur au lieu de par modèle. Le champ input est une assertion, pas une vérification — si vous prétendez qu’un modèle fait de la vision mais que le point de terminaison ne le prend pas en charge, c’est le fournisseur qui rejette la requête, pas Harness.

Le dépannage est directement détaillé dans la doc :

  • MISSING_CREDENTIAL → stockez la clé du fournisseur via la page Models ou exportez la variable d’environnement référencée
  • UNKNOWN_MODEL → choisissez un modèle configuré, ou ajoutez le modèle manquant au fournisseur personnalisé
  • « Get available models returns 401 » → vérifiez la clé. La découverte de modèles appelle le point de terminaison GET /models compatible OpenAI. Si votre service ne l’expose pas, saisissez les modèles manuellement.
  • « Image rejected before send » → le modèle n’a pas déclaré la modalité image. Ajoutez input: [text, image].
  • « Provider rejects a request with an image » → le modèle a prétendu à une capacité de vision que son point de terminaison n’a pas réellement. Retirez image de la liste et démarrez une session fraîche (l’ancienne image reste dans le journal de session et continue de répéter la même requête).

Premiers pas : trois chemins

Chemin 1 : npx @deepseek-ai/dsh web

Installez Node.js, exécutez une commande. L’interface Web démarre sur http://127.0.0.1:3080. C’est tout. Pas de clone, pas de build, pas de pnpm.

Terminal window
npx @deepseek-ai/dsh web

Ensuite : Settings → Models → collez la clé API DeepSeek. Choisissez un espace de travail (le répertoire où dsh a été invoqué fait l’affaire). Démarrez une session :

Résumez ce dépôt et identifiez ses principaux paquets.

L’agent lit et modifie les fichiers de l’espace de travail, exécute des commandes, délègue du travail et maintient un plan. Toute opération nécessitant une approbation sous la politique de permissions active déclenche une boîte de dialogue dans l’interface Web avant exécution.

Chemin 2 : Cloner et compiler depuis la source

Terminal window
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web

Chemin 3 : SDK Python

Prérequis : Python 3.10+, Linux x64 / Linux arm64 / macOS 14+ sur arm64, un point de terminaison compatible DeepSeek.

Terminal window
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
python -m venv .venv
. .venv/bin/activate
python -m pip install deepseek-harness-sdk

Définissez les crédentielles :

8000/v1
export DEEPSEEK_API_KEY=sk-your-key-here
# Optionnel :
# export DSH_MODEL=deepseek-v4-flash
# export DSH_SYSTEM_PROMPT='You are a helpful software engineer assistant.'

Exécutez une tâche sur un espace de travail isolé :

Terminal window
python examples/jsonrpc-agent/minimal.py \
--workspace /absolute/path/to/workspace \
--session-root /absolute/path/to/sessions \
--session-id example-001 \
"Inspect the repository and fix the failing tests."

Pour votre propre code, le point d’entrée du SDK est DeepSeekHarness en tant que gestionnaire de contexte :

from pathlib import Path
from deepseek_harness import DeepSeekHarness
config = Path("examples/jsonrpc-agent/minimal.cordis.yml").resolve()
workspace = Path("/absolute/path/to/workspace").resolve()
sessions = Path("/absolute/path/to/sessions").resolve()
with DeepSeekHarness(
provider="deepseek-official",
model="deepseek-v4-flash",
max_tokens=49_152,
cwd=str(workspace),
session_root=str(sessions),
cordis=str(config),
) as harness:
result = harness.run(
"Inspect the repository and fix the failing tests.",
session_id="example-001",
)
print(result.final_response)

DeepSeekHarness démarre paresseusement le runtime fourni et le réutilise jusqu’à la sortie du bloc with. Réutilisez le même ID de session pour préserver le processus Bash (répertoire de travail, variables d’environnement, fonctions shell). Utilisez un ID de session frais pour une tâche indépendante.

La composition minimale jsonrpc-agent est volontairement clairsemée : seulement bash persistant et str_replace_editor comme outils exposés au modèle. Délai d’expiration Bash : 300 secondes. Limite de sortie de l’éditeur : 16 000 caractères. Compaction de contexte désactivée. Le système de fichiers utilise le backend local brut — les chemins de l’éditeur peuvent adresser tout ce que le processus runtime peut voir. La doc prévient explicitement : « Ne l’exécutez que dans un checkout jetable ou un conteneur. » Le backend PTY persistant nécessite aussi un substrat de terminal POSIX — pas de prise en charge Windows pour cette composition.

Écrivez votre premier plugin

Tutoriel : Votre premier plugin. Un plugin est un module TypeScript qui exporte une fonction apply :

import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
export function apply(ctx: Context) {
console.log('[hello-plugin] plugin loaded!')
}

Enregistrez-le dans un correctif cordis.yml :

- insert:
- id: hello
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'

Démarrez avec la superposition :

Terminal window
pnpm dsh web --patch ./scratch-plugin/cordis.yml

Le nettoyage automatique est la fonctionnalité tueuse. Tout ce qui est enregistré via ctx — auditeurs d’événements, outils, temporisateurs — est nettoyé quand le plugin se décharge. Pas de removeListener ni de clearInterval manuels. Pour un nettoyage explicite (connexions réseau), retournez un disposer depuis ctx.effect().

Les dépendances sont déclarées avec inject :

export const name = 'my-tool-plugin'
export const inject = ['tools']
export function apply(ctx: Context) {
// ctx.tools est prêt ici
}

Cordis attend que chaque service requis soit disponible avant de charger le plugin.

Trois formes de plugin existent : fonction (ci-dessus), objet avec apply, et classe étendant Service. Utilisez la forme classe quand le plugin lui-même fournit un service que d’autres plugins consomment.

Écrivez votre premier outil

Tutoriel : Construire un outil. Utilisez defineTool depuis @deepseek-ai/dsh-tools :

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet someone by name.',
parameters: {
name: {
type: 'string',
required: true,
description: 'The name to greet',
},
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return `Hello, ${args.name}!`
},
}))
}

defineTool déduit et valide args depuis parameters. execute retourne la valeur canonique déclarée par output.schema. output.render convertit cette valeur canonique en contenu exposé au modèle. Après un redémarrage avec le correctif, demandez à l’interface Web : « Use the greet tool to greet Ada. » Le modèle appelle greet et reçoit Hello, Ada!.

Les étapes suivantes du tutoriel sont la configuration de plugin, la référence de création d’outils (schémas imbriqués, valeurs canoniques, travail en arrière-plan, hooks de politique, Code Mode, cartes UI) et le découpage en couches de capacités (Définition de service → Fournisseur de service → Paquet consommateur).

Modes d’entrée CLI

La commande @deepseek-ai/dsh est le lanceur du produit. Quatre points d’entrée :

Commande But
dsh --profile <name> Démarrer le profil nommé sous \$DSH_HOME/profiles/<name>
dsh --profile headless "job" Exécuter une session persistante fraîche, afficher la réponse finale, quitter
dsh web Alias de --profile web
dsh plugin --profile <name> <pnpm args> Gérer les plugins d’un profil en transmettant à pnpm

Le répertoire d’invocation est la racine par défaut de l’espace de travail. Les profils web et headless s’initialisent automatiquement depuis les modèles livrés au premier usage. Tout autre profil doit être créé via dsh plugin.

Les flags du lanceur viennent en premier. Le premier token que le lanceur ne reconnaît pas devient les arguments de l’app. Exemple : dsh --profile web --port 8080 transmet --port 8080 à l’appli Web, pas au lanceur.

Un répertoire de profil contient un package.json (dépendances de plugins hors arborescence, plus le manifeste de profil dsh.profile avec sa liste ordonnée bundles) et un cordis.patch.yml (la couche de correctif propre à l’utilisateur). L’ordre de composition sur une racine vide est : le correctif de chaque bundle dans l’ordre dsh.profile.bundles → le cordis.patch.yml du profil → le \$DSH_HOME/cordis.patch.yml au niveau home → les superpositions --patch. Utilisez --dump-default-config et --dump-config pour inspecter l’arbre composé sans le démarrer.

Plugins communautaires et écosystème

Taggez votre dépôt de plugin avec dsh-plugin sur GitHub pour qu’il soit repérable. Le site officiel pointe directement vers cette page de sujet. Il y a aussi une communauté Discord DeepSeek Harness pour les discussions.

Le projet utilise GitHub Discussions pour les retours et les signalements de bugs. La doc pointe vers CONTRIBUTING.md pour le workflow de développement, architecture.md pour la conception du système et AGENTS.md pour les conventions de codage spécifiques aux agents.

Preview développeur — oui, ça va casser

Le README le met en MAJUSCULES : « IL Y AURA DES CHANGEMENTS QUI CASSENT LA COMPATIBILITÉ. »

Les plugins et APIs de base évoluent encore. La page de présentation le dit directement : « DeepSeek Harness reste en preview développeur et est toujours testé par des développeurs qui construisent des harnesses d’agents. »

Si vous construisez dessus, figez un hash de commit, gardez vos plugins fins par rapport au catalogue de config, et attendez-vous à retester à chaque bump de rc. Le nettoyage automatique des plugins et l’injection de dépendances rendent le retest moins pénible qu’avec des frameworks monolithiques, mais « preview développeur » veut dire exactement ce que ça dit.

Qu’est-ce qui le rend différent

La plupart des frameworks d’agent aujourd’hui commencent par une boucle de tour et bricolent l’extensibilité par-dessus. Harness inverse la donne : l’extensibilité est le framework, et la boucle de tour n’est juste qu’un autre plugin. Le résultat observable, ce sont trois choses qu’on n’obtient généralement pas dans un seul paquet :

  • Échangez n’importe quoi sans fork. Le système d’outils intégré ne vous plaît pas ? Remplacez-le. Vous voulez une couche de routage LLM différente ? Échangez le fournisseur. Tout se résout via des clés de service Cordis.
  • Traçabilité par défaut. Le journal en écriture seule n’est pas un module complémentaire d’observabilité. C’est comme fonctionnent les sessions. Reprendre, bifurquer, chercher et rejouer utilisent tous le même flux.
  • Préréglages composables, pas des flags de fonctionnalité. Les quatre modes (Standard / Code / Minimal / Creator) ne sont que des couches de correctifs ordonnées de bundles de plugins. Vous pouvez superposer vos propres préréglages par-dessus avec du YAML au lieu de code.

Savoir si cette approche l’emportera sur les SDK monolithiques dépend de la capacité de l’écosystème de plugins à produire assez d’outils et de fournisseurs tiers pour rendre l’histoire d’échange/recomposition réelle. Avec 1,6k étoiles et 12k commits au premier jour, la dynamique est clairement là.

Références

Partager cette page