DeepSeek Harness (dsh): Ich habe DeepSeeks Open-Source-Coding-Agent installiert, in meine Chat-App eingebunden und einem Benchmark unterzogen
- Kategorie
- KI & lokale LLMs
- Veröffentlicht
- 18 August 2026
- Von
- Jacob Lloyd — mit KI-Unterstützung geschrieben, im Nachhinein
- Lesezeit
- 14 Min. Lesezeit
Kurz gesagt: DeepSeek hat einen kostenlosen Open-Source-„Coding-Agenten“ veröffentlicht – ein Programm, das dein Projekt liest, Dateien bearbeitet und Befehle für dich ausführt, ähnlich wie Claude Code. Dieser Artikel zeigt, wie ich es auf meinem Linux-PC installiert, automatisch starten lassen, mit meiner Heim-Chat-App verbunden und an kleinen echten Programmieraufgaben getestet habe – sowohl mit DeepSeeks Cloud-Modellen als auch mit einem Modell auf meiner eigenen Hardware. Es funktionierte, war günstig, und es gibt ein paar scharfe Kanten.
Am 13. August 2026 veröffentlichte DeepSeek DeepSeek Harness — dsh — einen unter MIT-Lizenz stehenden Coding-Agenten, der auf der Idee basiert, dass alles ein Plugin ist: der Modell-Adapter, die Tools, die Sandbox, sogar die Agent-Schleife. In zwei Tagen erreichte er 95.000 GitHub-Sterne. Ich installierte ihn noch in derselben Woche auf meinem Heimrechner, gab ihm einen Platz neben Reasonix, meinem bestehenden Coding-Agenten, in DisPatch, richtete ihn sowohl auf DeepSeeks Cloud-Modelle als auch auf ein Modell auf meinem eigenen GPU-Rig aus und ließ ihn eine kleine Testaufgaben-Suite durchlaufen. Genau das wird hier beschrieben: was es brauchte, was es kostete und wo es zwickte.
tl;dr
- Was es ist: ein Agent der Claude-Code-Klasse (liest/ändert Dateien, führt Shell-Befehle aus, behält einen Plan, erzeugt Unteragenten), ausgeliefert als npm-Paket. Zwei Modi: eine Web-UI auf
127.0.0.1:3080und ein headless Einmal-Modus, der eine Antwort ausgibt und sich beendet — gedacht für Skripte und andere Agenten. - Was es kostet: Die Software ist kostenlos. Mein kompletter Benchmark mit 5 Aufgaben kostete etwa 3 Cent auf V4-Flash und etwa 7 Cent auf V4-Pro bei Spitzentarifen; für das lokale Modell nichts.
- Was du brauchst: Node.js 22.19+/24, einen DeepSeek-API-Schlüssel oder einen beliebigen OpenAI-kompatiblen Server (ich habe beides genutzt).
- Was am Ende dabei herauskommt:
dsh --profile headless "fix the failing test"aus jedem Projektordner, eine Browser-UI als Hintergrunddienst, Modellwechsel durch Bearbeiten einer YAML-Datei und — in meinem Fall — ein „DeepSeek Harness“-Bot in meiner Chat-App mit einem Run-Button. - Das Ergebnis: 15/15 bestanden bei V4-Flash, V4-Pro und einem lokalen Gemma-4-26B, 2–18 s pro Aufgabe. Entwicklervorschau — die README warnt in Großbuchstaben davor, dass zwischen Versionen Dinge kaputtgehen, und ich bin auf ein paar der unten aufgeführten Stolpersteine gestoßen.
Der Weg, von einfach bis fortgeschritten
Mit den Schritten 1–3 haben Sie in zehn Minuten einen funktionierenden Agenten. Die Schritte 4–5 sind das, was ich obendrauf gemacht habe, damit andere Software – und andere Agenten – ihn aufrufen können.
Was am Ende dabei herauskommt
Was ich tatsächlich verwende, ist der Headless-Modus. Aus einem Projektverzeichnis heraus:
$ cd ~/Projects/dsh-playground
$ dsh --profile headless "Create fizz.py that prints FizzBuzz for 1..15 and run it; reply with the program output only."
1
2
Fizz
4
Buzz
…
FizzBuzz
$ echo $?
0
Sechs Sekunden, Datei erstellt, Programm ausgeführt, Antwort ausgegeben, Exit-Code 0. Es schreibt nichts außerhalb des Ordners, in dem du es gestartet hast (Standard-Berechtigungsmodus workspace-write), gibt nur die endgültige Nachricht auf stdout aus und speichert jeden Lauf, sodass du ihn später in der Web-UI öffnen und genau nachlesen kannst, was passiert ist – jeden Tool-Aufruf, jede Token-Anzahl.
Die Browser-Oberfläche ist das vertraute 2026-Layout – Sitzungen links, Chat in der Mitte, eine Einstellungsseite für Modelle – mit einer guten Angewohnheit: Der API-Schlüssel wird in die Einstellungsseite eingefügt, nie in eine Konfigurationsdatei. Bei mir läuft das Ganze als systemd-Benutzerdienst und ist in meine Chat-App neben Reasonix eingebettet:
Schritt 1: Installation (zwei Minuten)
Ein npm-Paket. Ich behalte ein privates Node-Präfix für Agent-Tools, damit nichts im Systembaum landet – auf einer unveränderlichen Distribution wie Bluefin ist das sowieso der einzig sinnvolle Ort – aber eine normale globale Installation ist derselbe Befehl:
npm install -g @deepseek-ai/dsh
dsh --version # 0.1.0-rc.7 at the time of writing
dsh web # starts the UI, prints http://127.0.0.1:3080
Öffnen Sie die URL, gehen Sie zu Einstellungen → Modelle, fügen Sie Ihren DeepSeek-Schlüssel ein und speichern Sie. Dadurch wird ~/.dsh/.credentials.yaml (Modus 0600) geschrieben und die Modellroute funktioniert sofort, ohne Neustart. Für die Skriptsteuerung ist die Datei ein einfaches YAML-Mapping – DEEPSEEK_API_KEY: sk-… – und ich habe meine aus der Env-Datei geschrieben, die meine anderen Dienste bereits lesen, sodass das Geheimnis an einem weiteren Ort existiert, aber nie in einer Shell-History oder einer Unit-Datei.
Zwei Dinge, die Sie wissen sollten, bevor Sie weitermachen:
- Telemetrie ist standardmäßig deaktiviert (
DSH_TELEMETRY_MODEnicht gesetzt = deaktiviert). Ich habe die ausgelieferte Konfiguration geprüft, nicht den Marketing-Text; der OTLP-Exporter existiert, er ist nur nicht aktiviert. - Die Sandbox ist echt, aber eng.
workspace-writebeschränkt Schreibvorgänge auf das Verzeichnis, aus dem Sie gestartet haben. Lesezugriffe sind nicht beschränkt – die Dokumentation sagt das deutlich – starten Sie es also für eine echte Aufgabe nicht aus Ihrem Home-Verzeichnis und richten Sie es nicht auf Ordner mit Geheimnissen.
Schritt 2: als Dienst ausführen
Die Weboberfläche ist ein langlebiger Node-Prozess; ich wollte, dass er beim Anmelden läuft, ohne Terminal, nur Loopback. Eine systemd --user-Unit erledigt das:
# ~/.config/systemd/user/dsh-web.service
[Unit]
Description=DeepSeek Harness web UI (dsh web) on 127.0.0.1:3080
After=network.target
[Service]
WorkingDirectory=%h
Environment=DSH_HOME=%h/.dsh
Environment=DSH_PERMISSION_MODE=workspace-write
ExecStart=/usr/local/bin/dsh web --host 127.0.0.1 --port 3080 # `which dsh`
Restart=on-failure
RestartSec=3
[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable --now dsh-web.service
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:3080/ # 200
WorkingDirectory ist das Standard-Workspace-Verzeichnis der Weboberfläche, aber die UI zwingt einen trotzdem, zuerst explizit einen Workspace auszuwählen, bevor man tippen kann. Gute Standardeinstellung. Die CLI lehnt außerdem --host 0.0.0.0 ab – die Autoren bezeichnen das Binden an alle Schnittstellen als „absichtlich noch nicht unterstützt“, und da die UI keinerlei Authentifizierung besitzt, stimme ich zu. Von einem anderen Rechner aus zugreifen? Dann stellt man einen authentifizierenden Reverse-Proxy davor oder erreicht den Browser des Hosts über den Remote-Desktop-Pfad (so mache ich es).
Schritt 3: Modelle – eine YAML-Datei, heiß nachgeladen
~/.dsh/settings.yaml enthält das Standardmodell und alle zusätzlichen Provider, und dsh liest die Datei für die nächste Anfrage neu ein – kein Neustart, keine erneute Anmeldung. Meine:
agent-default-model:
provider: deepseek-official
model: deepseek-v4-flash # or deepseek-v4-pro
llm-deepseek:
reasoningEffort: high # off | low | high | max
# A local OpenAI-compatible server (mine is llama.cpp-based on a GPU rig).
llm-pi-ai:
providers:
buildpc:
displayName: StudioForge (GPU rig)
apiKeyEnv: STUDIOFORGE_PLACEHOLDER_KEY # a reference, not a value
api: openai-completions
baseURL: http://my-gpu-rig:1234/v1 # your server; mine sits on the tailnet
defaultContextWindow: 65536
models:
- id: unsloth/gemma-4-26B-A4B-it-qat-GGUF/gemma-4-26B-A4B-it-qat-UD-Q4_K_XL
name: Gemma 4 26B-A4B (rig)
Zwei Details, die mich jeweils zehn Minuten gekostet haben: apiKeyEnv ist eine Referenz, die aus .credentials.yaml oder der Umgebung aufgelöst wird, niemals ein Literal – und ein lokaler Server ohne Schlüssel braucht trotzdem die Referenz auf irgendwelche Zugangsdaten, weil der OpenAI-kompatible Client auf einem Bearer-Token besteht. Ein Platzhalterwert in .credentials.yaml genügt. Und die Provider-ID (buildpc) ist dauerhaft, sobald Sitzungen darauf verweisen; zum Umbenennen fügt man eine neue hinzu.
Da die Datei die gesamte Schnittstelle ist, ist „dsh auf das lokale Modell umstellen“ eine Änderung in zwei Zeilen, die jedes Skript – oder jeder andere Agent – vornehmen kann. Mein Hauptagent stellt das Harness für Routineaufgaben auf ein kostenloses lokales Modell um und für schwierige Probleme zurück auf V4-Pro, ohne irgendetwas anderes anzufassen.
Schritt 4: neben Reasonix in DisPatch
Ich hatte Reasonix bereits als Pseudo-Bot in DisPatch: Ein Klick darauf verwandelt das Chat-Paneel in ein Terminal, das seine TUI über eine PTY ausführt. Daneben wollte ich dsh. Der Haken: Das offizielle Paket hat keine TUI. Plugins von Drittanbietern im „Claude-Code-TUI“-Stil gibt es zwar auf npm, aber sie waren vier Tage alt mit kaputten workspace:*-Abhängigkeiten, und ein ungeprüftes Paket mit Shell-Zugriff kommt nicht auf einen Familienserver. Das dsh-Paneel ist also aus dem gebaut, was das offizielle Paket tatsächlich bietet:
- die in einen iframe eingebettete Web-UI (sie sendet keine Frame-blockierenden Header), die nur angezeigt wird, wenn der Browser den Loopback des Hosts erreichen kann – ein Telefon im Tailnet bekommt stattdessen eine einfache Erklärung;
- Start / Neustart / Stopp für die systemd-Unit, plus Health;
- ein Model-Dropdown, das
agent-default-modelin der YAML-Datei überschreibt; - einen Tab Headless-Jobs: Aufgabe eintippen, einen Ordner im Home-Verzeichnis wählen, Run drücken. Der Server startet
dsh --profile headless "…"als feste Argumentliste (keine Shell – die Aufgabe ist ein argv-Element, eine Aufgabe mit; rm -rf /ist also nur Text), immer nur einen Job gleichzeitig, und hält eine kurze Historie mit jeder endgültigen Antwort vor.
dsh --profile headless-Lauf; die Antwort ist das Letzte, was dsh ausgegeben hat. Drei echte Läufe: 5 s, 5 s, 19 s.Alles in diesem Paneel liegt hinter der Admin-Freischaltung der App – ein Headless-Job ist beliebige Codeausführung, und die gesperrten Familiengeräte erfahren nie, dass das Paneel existiert. Wenn du dsh in etwas Eigenes einbindest, übernimm das: Behandle „Aufgabe ausführen“ genauso, wie du eine Shell behandelst.
dsh vs. Reasonix im direkten Vergleich
Beide sind Claude-Code-artige Agenten, die pro Token auf DeepSeek abgerechnet werden. Sie unterscheiden sich darin, wer sie gebaut hat und welche Oberfläche sie dir bieten:
| DeepSeek Harness (dsh) | Reasonix | |
|---|---|---|
| Wer | DeepSeek, offiziell, MIT | Drittanbieter, Claude-Code-Stil |
| Oberfläche | Web-UI + Headless-One-Shot; keine TUI | Terminal-TUI (interaktive Sitzung in einer PTY) |
| Per Skript aufrufbar | dsh --profile headless "…" – eine Antwort, Exit-Code | Für einen Menschen im Loop gedacht; Skripting ist umständlich |
| Modellwechsel | settings.yaml bearbeiten, Hot-Reload; kein --model-Flag | config.toml-Stufen (flash/pro) + Routing pro Skill |
| Lokale Modelle | Beliebiger OpenAI-kompatibler Server über einen Provider-Block | Gleiches Trio: Base-URL, Key-Env, Modell-IDs |
| Erweiterbarkeit | Alles ist ein Plugin (Modell, Tools, Sandbox, Loop) | Subagents, Skills, projektbezogener Speicher |
| In meinem DisPatch | iframe + Unit-Steuerung + Headless-Jobs-Tab | xterm.js-Terminal über eine PTY |
| Reifegrad | Developer Preview (rc.7), bricht zwischen Versionen | v1.18, Self-Updater, stabile Konfiguration |
In der Praxis: Reasonix öffne ich, wenn ich mich mit dem Agenten hinsetzen will; dsh ist das, was meine andere Software aufruft. Diese Aufteilung ist der Grund, warum beide bleiben.
Der Benchmark: 5 Aufgaben, 3 Modelle, 15/15
Nichts Wissenschaftliches – fünf kleine Aufgaben, die ich tatsächlich einem Coding-Agenten geben würde, jede in einem frischen Scratch-Ordner, jede automatisch geprüft (existiert die Datei und läuft? Bestehen die Tests, ohne die Testdatei anzufassen? Hat die Umbenennung null alte Referenzen hinterlassen?). Die gemessene Gesamtzeit umfasst den gesamten Prozess inklusive des ~7.500-Token-Systemprompts; die Tokens stammen aus dem Sitzungsprotokoll von dsh.
| Aufgabe | V4-Flash | V4-Pro | Gemma-4-26B (lokal, Rig) |
|---|---|---|---|
| „PONG“ antworten (Booten + ein Aufruf) | ✅ 2.1 s | ✅ 2.8 s | ✅ 13.2 s* |
| FizzBuzz schreiben + ausführen | ✅ 5.3 s · 2 Tools | ✅ 8.4 s · 2 Tools | ✅ 4.9 s · 2 Tools |
| 2 Bugs beheben, damit Unit-Tests bestehen (Tests unverändert) | ✅ 11.7 s · 8 Tools | ✅ 15.8 s · 7 Tools | ✅ 10.4 s · 8 Tools |
| 6-Modul-Codebasis zusammenfassen (<150 Wörter) | ✅ 8.9 s · 9 Tools | ✅ 10.9 s · 7 Tools | ✅ 12.1 s · 7 Tools |
| Funktion über 3 Dateien + Tests umbenennen, Grün nachweisen | ✅ 15.1 s · 14 Tools | ✅ 18.2 s · 12 Tools | ✅ 12.2 s · 11 Tools |
| Gesamtzeit | 43 s | 56 s | 53 s |
| Tokens (Input-Miss / Cache-Read / Output) | 42.6k / 136k / 4.5k | 41.4k / 107k / 3.2k | 40.5k / 237k / 5.6k |
| Kosten bei Spitzenpreisen (außerhalb der Spitzenzeiten halb so viel) | ≈ $0.027 | ≈ $0.072 | $0 (Strom) |
*Erster Aufruf, nachdem das Modell auf dem Rig kalt geladen wurde; spätere Aufgaben zeigen die Geschwindigkeit im warmen Zustand. Preise von der DeepSeek-Preisseite vom 18.08.2026: Flash $0.014 / $0.44 / $1.32 pro Million (Cache-Hit / Miss / Output), Pro $0.044 / $1.32 / $3.96.
Was die Tabelle zeigt:
- Prompt-Caching trägt die Rechnung. Jede Aufgabe zahlt ~7,5k Token Systemprompt, aber nach dem ersten Schritt sind es fast nur noch Cache-Reads zu 3 % des Miss-Preises. Mehrstufige Aufgaben sind günstig, weil der Harness das Präfix stabil hält.
- Pro hat weniger Schritte und weniger Tools benötigt für dasselbe Ergebnis (12 statt 14 Tool-Aufrufen bei der Umbenennung; 3 statt 4 Schritten bei der Zusammenfassung). In dieser Suite war Flash schneller und kostete nur ein Drittel, also bleibt es meine Standardwahl.
- Das lokale Modell hat sich gut behauptet. Gemma-4-26B (ein MoE mit 4B aktiven Parametern, Q4-Quantisierung, bereitgestellt von llama.cpp auf zwei RTX 5090s) hat alles bestanden, mit mehr Schritten und mehr Tokens, aber wettbewerbsfähiger Gesamtzeit. Zum ersten Mal war ein lokales Modell hier eine echte Option für lästige Aufgaben statt einer Neuheit – auch wenn fünf kleine Aufgaben nichts über ein Refactoring mit 40 Dateien aussagen.
Dann der Test mit den „normalen Dingen“: Ich habe es auf das Repository dieser Website angesetzt und es gebeten, das Runbook des Projekts zu lesen, den Build- und Link-Check auszuführen und Bericht zu erstatten – kein Veröffentlichen, keine Änderungen. Es hat das Runbook gelesen, den richtigen Befehl ausgeführt, die genauen Prüfzeilen berichtet (290 Seiten, 1.752 Bilder, keine toten Links), die Buildzeit, und – unaufgefordert – bemerkt, dass das Runbook noch „288 Seiten“ sagte, und die Abweichung gemeldet. 53 Sekunden. Das ist die einfache Aufgabe, die ich den ganzen Tag abgebe.
Der Spaßtest: Bau mir ein Spielzeug
Benchmarks sind das eine; ich wollte auch sehen, was es mit einem offenen, kreativen Briefing macht. Also: „Erstelle eine interaktive Pixel-Art-Version des Logos dieser Website – vanilla JS, überall einbettbar, Hover bewirkt etwas Physisches, Klick etwas Cooles, keine Abhängigkeiten, testen Sie es selbst.“ Hier ist, was es gebaut hat – es ist live, also legen Sie los:
prefers-reduced-motion.
Wie es lief, ehrlich gesagt:
- Versuch 1 (V4-Flash): eine 10-minütige Schleife, keine Dateien. Das Briefing erlaubte entweder eine handgezeichnete Bitmap oder eine prozedurale. Es entschied, in seinem Reasoning eine 40×40-Bitmap von Hand zu zeichnen, und verfiel in eine entartete Schleife – das Sitzungslog ist voll mit Hunderten von Zeilen
################/....– bis mein Timeout es abbrach. Ungefähr 2 Cent verschwendet. Lehre: Lassen Sie ein Modell niemals Pixel in seinem Kopf von Hand zeichnen. - Versuch 2 (V4-Flash, Briefing geändert auf „aus Geometrie rasterisieren, keine Bitmaps“): 25 Minuten, alles geliefert. 100 Modellschritte, 201 Tool-Aufrufe, 172k Ausgabetoken (davon 123k Reasoning), 16,3 Mio. Cache-Read-Tokens — ≈ 49 Cent bei Spitzentarifen. Es schrieb eine 399-zeilige
ll-pixel-logo.jsmit einer einzigen globalen API unddata--Reglern, eine Demoseite, eine README, einen Node-Unit-Test für den Rasterizer und — unaufgefordert — ein Playwright-Skript, das von der Demo in drei Größen Screenshots erstellt. Es stieß beim Feinschliff der README an mein 25-Minuten-Limit, der Exit-Code war also ein Timeout, aber die Arbeit war erledigt. Exit-Codes lügen in beide Richtungen. - Die Kunst: Ring und zwei schräge Ls wirken auf einen Blick wie das Zeichen; sie sind klobiger und Z-lastiger als das echte Logo, und ich würde zehn Minuten damit verbringen, seine Scherungskonstanten zu justieren, bevor ich es im Ernst einsetze. Habe ich nicht getan – was Sie sehen, ist unverändert.
- Es hier einzubetten brauchte nur eine Zeile, weil die CSP der Website
script-src 'self'lautet und das Widget keine Netzwerkanfragen stellt. Das stand im Auftrag; es hat sich daran gehalten.
Stolperfallen
- Developer Preview, und das steht auch in Großbuchstaben da.
0.1.0-rc.7bei der Installation; rc.6 war drei Tage früher. Profile, Konfigurationsschlüssel und das Plugin-Layout können sich ändern. Pinne die Version in allem, was du automatisierst, und führe nach jedem Upgrade erneut einen Smoke-Test aus. - Der Headless-Modus ist still, bis er abgeschlossen ist. Es wird nichts nach stdout gestreamt – eine lange Aufgabe wirkt wie hängengeblieben. Lies das Session-Log (
~/.dsh/sessions/…/session.jsonl.zstd, zstd-komprimiertes JSONL) oder verfolge es in der Web-UI. Und Exit-Code 0 bedeutet „der Turn ist abgeschlossen“, nicht „die Aufgabe war erfolgreich“. Überprüfe das Ergebnis. - Kein
--model-Flag im Headless-Modus. Das Standardmodell kommt aussettings.yaml; ändere es dort (Hot-Reload) oder in der UI. - Lokale Server ohne Schlüssel benötigen Platzhalter-Zugangsdaten, auf die
apiKeyEnvverweist, und Provider-IDs sind dauerhaft. Weiter oben behandelt; das wird dich beißen. - Die Assets der Web-UI haben absolute Pfade (
/assets/…,/api), daher kannst du sie nicht ohne Umschreiben unter einem Unterpfad hinter deinem eigenen Reverse-Proxy bereitstellen; bette sie ein oder gib ihr einen eigenen Hostnamen. - Das UI-Locale folgt deinem Browser – die ausgelieferte index.html enthält
lang="zh-CN", und als Erstes siehst du einen „Internal Testing Notice“-Dialog. Klicke auf Weiter; danach war bei mir alles Englisch. - Die Installation führt Postinstall-Skripte aus (node-pty, koffi, protobufjs). npm warnt davor. Ich konnte nichts Bösartiges sehen, aber es ist nativer Code, der in deinem Prefix kompiliert wird – ein weiterer Grund, es in einem privaten Prefix aufzubewahren und nicht im System-Prefix.
- Nur Node 22.19+ oder 24. Ältere LTS-Versionen weigern sich, es auszuführen.
Wo mich das hinführt
Es hat sich seinen Platz an einem Nachmittag verdient. Der Headless-Modus ist die richtige Form für einen Coding-Agenten, den andere Software aufruft – ein Befehl, eine Antwort, ein Exit-Code, ein nachvollziehbares Log – und Modell-per-YAML bedeutet, dass mein Agenten-Stack ihn auf jedes Gehirn ausrichten kann, das zur Aufgabe passt. Wenn du bereits einen lokalen Modellserver betreibst, probiere dieselben fünf Aufgaben aus; zehn Cent API-Guthaben und ein Scratch-Ordner sind der gesamte Aufwand.
Verwandt: Reasonix (der andere Coding-Agent in diesem Artikel), DeepSeek Everywhere (DeepSeek in Claude Code und einen Agenten-Stack einbinden), DisPatch (die Chat-App, in der das Panel lebt) und bench-llm (lokale Modelle ernsthafter benchmarken, als ich es hier getan habe).