Alles ist ein Plugin: Das Cordis-Design hinter DeepSeek Harness
Wie DeepSeek Harness einen Agenten-Runtime aus Plugins aufbaut, unter Verwendung von Cordis-Ideen wie umkehrbaren Effekten, reaktiven Abhängigkeiten und einem austauschbaren Agenten-Loop.
Quellen: DeepSeek Quietly Built a Different Kind of AI Agent Framework
Das übliche Rezept und seine Schwachstellen
Die meisten Agenten-Frameworks beginnen mit denselben Bestandteilen: einem Sprachmodell, einigen Werkzeugen, einer gespeicherten Konversation sowie einer Schleife, die alles umschließt. Der untenstehende Überblick zeigt diese Struktur.
User asks something
↓
LLM thinks
↓
LLM calls a tool
↓
Tool returns data
↓
LLM continues
↓
Final answer
Per Definition zählt das bereits als Agent. Das Problem beginnt, wenn das System wächst. Die Anforderungen häufen sich: mehrere Modellanbieter, Browser-Tools, Shell-Ausführung, MCP-Server, Genehmigungsprozesse, Speicher, Telemetrie, Authentifizierung, verschiedene Benutzeroberflächen, Hintergrundagenten, benutzerdefinierte Anfragen, Berechtigungen und Hot-Reloading. Bald hängt jeder Teil vom anderen ab. Der Wechsel des Modellanbieters hat Auswirkungen auf den Agent-Zyklus, das Entfernen eines Plugins hinterlässt Event-Listener, und das Neu laden einer Tool kann Agenten stören, die zu diesem Zeitpunkt laufen. Auch Plugins sind von Diensten abhängig, die während des Laufs auftauchen oder verschwinden können.
DeepSeek Harness geht dabei völlig anders vor. Die zentrale Idee klingt fast zu einfach: Alles ist ein Plugin – einschließlich der Teile, die die meisten Frameworks als Kern bezeichnen.
Hinter all dem steht ein Framework namens Cordis, das aus einer Forschungsarbeit mit dem Titel „A Programming Paradigm for Spatiotemporal Composability“ hervorgegangen ist. Diese Arbeit ist von Interesse, da es sich bei dem Projekt nicht um einen weiteren Ansatz zur Organisation von JavaScript-Modulen handelt. Es befasst sich vielmehr mit einer tieferliegenden ingenieurtechnischen Frage: Wie können Komponenten in einem laufenden Programm miteinander verbunden, ersetzt oder getrennt werden sowie Abhängigkeiten geändert werden, ohne dass der Laufzeitumfeld in Chaos verfällt?
Was ein Harness ist
DeepSeek Harness, abgekürzt dsh, ist ein open-source Agent-Harness, der von DeepSeek veröffentlicht wurde. Ein Harness stellt die Infrastruktur dar, die das Modell umgibt. Egal ob es sich um ein DeepSeek-, Claude-, GPT-, Gemini-Modell oder ein lokales Modell handelt – der Harness kümmert sich um alles, was damit zusammenhängt.
┌───────────────┐
│ LLM │
└───────┬───────┘
│
┌──────────▼──────────┐
│ Agent Harness │
│ │
│ Prompt construction │
│ Tool execution │
│ Session history │
│ Permissions │
│ Streaming │
│ Agent lifecycle │
│ Model adapters │
└─────────────────────┘
Dieser Unterschied ist von großer Bedeutung. Die Aufmerksamkeit richtet sich in der Regel darauf, wie intelligent das Modell ist, doch die Entwicklung von Tools wie Claude Code, Codex, Cursor oder einem langlaufenden autonomen Agenten zeigt, dass die damit verbundene Laufzeit genauso wichtig ist. DeepSeek Harness handhabt diese Laufzeit als eine Reihe dynamisch zusammengesetzter Plugins und nicht als eine monolithische Anwendung – diese Entscheidung leitet sich direkt von Cordis ab.
Ein Abhängigkeitsrätsel
Stellen Sie sich vor, man entwickelt einen Programmieragenten aus drei Plugins.
GitPlugin
TerminalPlugin
CodingAgent
Der Kodierungsagent benötigt Git und Terminal. Beim Start ist der einfachste Ansatz, sie nacheinander zu laden.
load Git
load Terminal
load CodingAgent
Einfach – die meisten Dependency-Injection-Frameworks handhaben das problemlos. Die schwierigere Frage ist, was passiert, wenn das Git-Plugin während des Laufs des Programms verschwindet. Es könnte aktualisiert worden sein, seine Konfiguration könnte sich geändert haben, ein Benutzer könnte es deaktiviert haben oder eine andere Implementierung könnte übernommen haben. In diesem Fall muss der Laufzeitumgebung alles stoppen, was von diesem Service abhängt.
CodingAgent
↓ -------->depends on
GitPlugin
Falls Git verschwindet, sollte der Programmieragent nicht weiter so tun, als würde es existieren. Und wenn später ein anderer Git-Anbieter auftaucht, sollte der Agent vielleicht wieder mit der Arbeit beginnen. Hier verbergen sich zwei separate Probleme, die im Cordis-Artikel als temporale Kompositionsfähigkeit und räumliche Kompositionsfähigkeit bezeichnet werden.
Temporale Kompositionsfähigkeit: sauberes Beenden
Nehmen wir an, ein Plugin lädt sich und registriert einen Ereignislistener.
events.on("message", handler)
Später wird das Plugin geladen, und die Frage ist, was aus dem Zuhörer wird. Wenn nichts ihn entfernt, ist das Plugin zwar im Namen verschwunden, doch ein Teil davon läuft weiter. Hinzu kommt, dass das Plugin Timer gestartet haben könnte, Datenbankverbindungen hergestellt, Befehle registriert, den gemeinsamen Zustand verändert, Tools hinzugefügt, Ereignishandler angehängt und Worker gestartet haben könnte. Das Entladen bedeutet, alle diese Schritte rückgängig zu machen.
Das ist temporäre Kompositionsfähigkeit: Ein Komponente, die sich dem System anschließt, sollte das System in der Lage lassen, alles, was sie getan hat, vollständig rückgängig zu machen. Cordis betrachtet solche Änderungen als rückgängig machbare Effekte.
Effekte ohne Jargon
Ein Effekt ist einfach etwas, was Ihr Code tut und das die Außenwelt oder den Laufzeitzustand verändert. Nehmen wir die Registrierung eines Tools.
toolRegistry.add("search", searchTool)
Vor dem Anruf sieht das Register so aus.
tools = {}
Danach enthält es das neue Werkzeug.
tools = {
search: searchTool
}
Die Vorwärtsoperation ist eine Addition.
add(searchTool)
Cordis möchte jedoch mehr als das. Es wünscht sich, dass die Operation ihre eigene Umkehrung besitzt. Konzeptionell verbindet eine Wirkung eine Aktion mit ihrer Rückgängigmachung.
Effect:
do: add search tool
undo: remove search tool
Ohne mathematische Notation lautet die Idee wie folgt.
state --effect--> new state
new state --inverse--> original state
Diese Umkehrfunktion ist der entscheidende Bestandteil, und Cordis bezeichnet das gesamte Konstrukt als rückgängig machbaren Effekt.
Jeder Wandel erfordert eine Nachbereitung
Cordis macht dies durch ein Muster wie das folgende sichtbar, bei dem eine Funktion eine Änderung vornimmt und die Funktion zurückgibt, die diese wieder rückgängig macht.
ctx.effect(() => {
registerSomething()
return () => {
unregisterSomething()
}
})
Die Laufzeitumgebung wendet den Effekt an, speichert die Aufräumfunktion und ruft sie später auf, wenn das Plugin deaktiviert wird.
apply effect
↓
store cleanup function
↓
plugin runs
↓
plugin unloads
↓
run cleanup
Wenn ein Plugin mehrere Effekte enthält, werden diese in einer bestimmten Reihenfolge angewendet.
effect A
effect B
effect C
Die Aufräumarbeiten laufen dann rückwärts ab, so wie ein Stapel entfaltet wird.
undo C
undo B
undo A
Warum umkehren? Betrachten Sie ein Plugin, das so startet.
1. open database
2. start service using database
3. start agent using service
Das Herunterfahren in derselben Reihenfolge wäre gefährlich, denn die Datenbank wird geschlossen, während andere Prozesse sie noch verwenden.
close database
stop service
stop agent
Die sichere Anordnung ist das Gegenteil.
stop agent
stop service
close database
Das ist das Letzte, das hineinkommt – erstes heraus.
Last In
First Out
Cordis integriert dieses Verhalten in die Komponenten-Lebenszyklusverwaltung.
Reine umgekehrte Bereinigung reicht nicht aus
Hier wird der Artikel interessanter. Angenommen, Plugin A ändert einen gemeinsam genutzten Wert, anschließend ändert Plugin B denselben Wert, und danach versucht A, den ursprünglichen Zustand wiederherzustellen. Dadurch kann die Änderung von B überschrieben werden.
Initial: x = 10
Plugin A: x = 20
Plugin B: x = 30
Plugin A unloads: restore x = 10
B ist weiterhin aktiv, doch sein Zustand existiert nicht mehr. Daher sind umkehrbare Effekte nur unter bestimmten Unabhängigkeitsannahmen sicher, die in dem Paper als Effektunabhängigkeit formalisiert werden. Im Alltagssprachlichen sind zwei Effekte unabhängig, wenn die Anwendung oder Umkehrung des einen die Bedeutung des anderen nicht ungültig macht. Die Verwendung unterschiedlicher Tools erfüllt in der Regel diese Voraussetzung, da sie verschiedene Bereiche betreffen.
Plugin A registers tool "search"
Plugin B registers tool "calculator"
Das Ersetzen derselben Konfiguration bringt vermutlich nichts.
Plugin A replaces configuration X
Plugin B also replaces configuration X
Dieser Unterschied taucht später wieder auf, wenn das Kontextsystem von Cordis zur Sprache kommt.
Räumliche Kompositionsfähigkeit: Was ein Plugin benötigt
Die zeitliche Kompositionsfähigkeit befragt, ob ein Komponente sicher entfernt werden kann. Die räumliche Kompositionsfähigkeit fragt, ob das System versteht, wovon die Komponente abhängt. Stellen Sie sich dieses Plugin vor.
CodingAgent
Dafür sind drei Dinge notwendig.
LLM
ToolRegistry
SessionStore
Traditioneller Code greift möglicherweise auf Globale zu.
global.llm
global.tools
global.sessions
In diesem Fall verfügt die Architektur nicht über tatsächliches Wissen bezüglich der Abhängigkeiten. Cordis macht diese explizit. Konzeptionell deklariert ein Plugin, was es injiziert.
inject: [
"llm",
"tools",
"sessions"
]
Nun kann die Laufzeitumgebung die Anforderungen des Plugins als Baum darstellen.
CodingAgent
├── requires LLM
├── requires ToolRegistry
└── requires SessionStore
Dadurch kann es reagieren, wenn sich diese Dienste ändern.
Reaktive Abhängigkeiten
Das könnte die beste Idee in Cordis sein. Angenommen, ein Komponent benötigt einen LLM.
LLM
Zu Beginn existiert keines.
LLM unavailable
Deshalb kann der Komponente nicht aktiv werden. Später lädt ein Provider-Plugin und stellt eine zur Verfügung.
OpenAI Plugin loads
↓
provides LLM
↓
dependency becomes satisfied
↓
component activates
Falls dieser Anbieter entfernt wird, geht auch die entsprechende Funktion verloren.
OpenAI Plugin unloads
↓
LLM disappears
↓
dependency becomes unsatisfied
↓
dependent component unloads
Wenn ein anderer Anbieter geladen wird, kehrt die Funktionalität zurück.
DeepSeek Plugin
↓
provides LLM
↓
component becomes active again
Abhängigkeiten werden daher nicht einmal bei der Startzeit überprüft. Sie sind reaktiv, weshalb in dem Paper von reaktiven Coeffects die Rede ist.
Effekte versus Coeffects
Die Terminologie klingt kompliziert, doch der Unterschied ist einfach. Ein Effekt beschreibt etwas, das eine Komponente ändert und vom Plugin in die Umgebung übertragen wird.
Plugin → Environment
Beispiele umfassen diese Aktionen.
register tool
open database
subscribe to event
start timer
Ein Koeffekt beschreibt etwas, das die Komponente von ihrer Umgebung benötigt, und fließt in die entgegengesetzte Richtung.
Environment → Plugin
Typische Beispiele sind diese Anforderungen.
need an LLM
need a logger
need session storage
need a tool registry
Vereinigen Sie die beiden, dann fließen die Anforderungen in eine Richtung.
requirements
Environment ───────────────────> Plugin
während die Effekte zurückfließen.
Environment <─────────────────── Plugin
effects
Das ist die grundlegende Idee hinter Cordis. Ein Komponente gibt an, was sie benötigt, was sie bereitstellt und welche umkehrbaren Effekte sie hervorruft.
I NEED these services.
I PROVIDE these services.
I PERFORM these reversible effects.
Der Laufzeitumgebung koordiniert anschließend den Lebenszyklus.
Ursprung des Begriffs „räumlich-zeitlich“
Nun ergibt der Titel des Artikels Sinn. Die räumliche Kompositionstauglichkeit bezieht sich auf Beziehungen zwischen Komponenten.
A depends on B
B provides X
C depends on X
Temporale Komposition betrifft die Art und Weise, wie Komponenten im Laufe der Zeit voranschreiten.
load
activate
replace
deactivate
unload
reload
Zusammen bilden sie die gesamte Philosophie.
Spatiotemporal composability = dependency-aware composition + reversible lifecycle changes
Das Kontextobjekt
DeepSeek Harness leitet keine Dutzende von Diensten manuell über Konstruktoren weiter. Plugins kommunizieren über einen gemeinsamen Kontext, der in der Regel so benannt ist.
ctx
Konzeptionell werden die wichtigsten Dienste dargestellt.
ctx
├── llm
├── tools
├── sessions
├── agents
├── agentLoop
├── logger
└── ...
Doch ctx ist mehr als nur ein JavaScript-Objekt, das mit Globalen gefüllt ist. Über dieses Objekt finden Plugins ihre Abhängigkeiten, bieten Dienste an, registrieren Effekte, lauschen auf Ereignisse, isolieren Ressourcen, unterbinden den Zugriff auf Dienste und koordinieren Lebenszyklen – weshalb Cordis den Kontext als eine vereinigende Abstraktion darstellt.
Von der Theorie zum eigentlichen Projekt
DeepSeek Harness stellt seine wichtigsten UnterSubsysteme über diesen gemeinsamen Kontext zur Verfügung.
ctx.sessions
ctx.systemPrompt
ctx.tools
ctx.agents
ctx.agentLoop
ctx.llm
Bemerken Sie etwas Ungewöhnliches: Selbst der Agent-Loop ist lediglich ein Service.
ctx.agentLoop
Daher kann der Loop ersetzt werden, und es handelt sich dabei nicht um heiligen Framework-Code. Das führt zum definierenden Prinzip des Designs: Es gibt keinen privilegierten Kern, den jede Funktion reparieren muss.
Warum der Loop als Plugin wichtig ist
Die meisten Agent-Frameworks enthalten einen zentralen Loop, der diesem ähnelt.
while not finished:
response = model(history)
if response.tool_call:
result = execute_tool(response.tool_call)
history.append(result)
else:
return response
Dann beginnt jede fortgeschrittene Funktion, es zu verändern. Genehmigung vor Ausführung von Befehlen? Patchen Sie den Loop. Protokollierung? Patchen Sie den Loop. Wiederholversuche, Delegation an mehrere Agenten, ein anderer Inferenz-Backend? Patchen Sie den Loop. Schließlich wird daraus ein Hub, an dem Tools, Modelle und Speicher angebunden sind.
Agent Loop
/ | \
/ | \
tools models memory
/ | \
approvals retries tracing
Der Loop wird zum „God Object“ der Anwendung. DeepSeek Harness umgeht das, indem er es dem Loop ermöglicht, an derselben Plugin-Architektur wie alle anderen Komponenten teilzunehmen.
Die Struktur eines Agenten-Zyklus
Im Hintergrund durchläuft eine Interaktion mehr oder weniger diese Phasen.
turn/start
↓
claim incoming user input
↓
assemble prompt + available tools
↓
agent/pre-step
↓
step/start
↓
record user message
↓
reconstruct conversation history
↓
send model request
↓
stream assistant response
↓
tool call?
│
├── yes → execute tool
│ record result
│ continue another step
│
└── no → finish
↓
step/end
↓
turn/end
Wichtig ist, dass jede Phase Ereignisse und Service-Schnittstellen offenlegt, wodurch Plugins die Möglichkeit haben, mitzuwirken, ohne den gesamten Laufzeitumfeld neu schreiben zu müssen.
Wasserfall-Ereignisse als Agent-Middleware
Cordis unterstützt mehrere Modi zum Ausführen von Ereignissen. Ein praktischer Modus, der Wasserfall-Modus, verhält sich wie Middleware. Stellen Sie sich eine Anfrage vor, die durch ein Pipeline-System geleitet wird.
request
↓
Plugin A
↓
Plugin B
↓
Plugin C
↓
LLM
Das Plugin B kann die Ausführung mit einem Handler umhüllen, der den nächsten Schritt entgegennimmt.
async function handler(next) {
console.log("before")
const result = await next()
console.log("after")
return result
}
Die Ausführung ist dabei verschachtelt, wobei jedes Plugin vor und nach dem inneren Aufruf wirkt.
Plugin A before
Plugin B before
Plugin C before
LLM
Plugin C after
Plugin B after
Plugin A after
Dazu gehören Berechtigungen, Anfrageumformung, Telemetrie, Caching, Modellrouting, Sicherheitsprüfungen und Kontexteinjection – wobei keines davon fest im Modelladapter implementiert werden muss.
Das Modell befindet sich hinter einer Funktionsgrenze
Der Framework abstrahiert außerdem das LLM, sodass ein Agent nicht jedes Provider-SDK verstehen muss. Er kommuniziert mit einem einzigen Service.
Agent
↓
ctx.llm
↓
Model Adapter
↓
Provider API
Ein Anbieter könnte es für die DeepSeek-API implementieren.
DeepSeek API
Ein weiterer befasst sich mit OpenAI.
OpenAI
Ein dritter bezieht sich auf Anthropic.
Anthropic
Der Rest der Agent-Architektur befasst sich mit derselben LLM-Fähigkeit. In der Harness-Dokumentation wird dies manchmal als Fähigkeitsnaht bezeichnet, die aus einer Servicedefinition und einem Serviceanbieter besteht.
Service Definition
↓
Service Provider
↓
Consumer
Für den LLM verläuft diese Kette von der Schnittstelle zu einem bestimmten Anbieter.
LLM interface
↓
OpenAI provider
↓
Agent loop
Daher bedeutet das Hinzufügen eines Modellanbieters nicht unbedingt eine Neuimplementierung des Laufzeitsystems. Man stellt einfach eine weitere Implementierung derselben Funktionalität bereit.
Ereignisbasierte Sessions
Die Gespräche werden vom System nicht als ein großer, veränderbarer Array aufbewahrt, das als einzige Quelle der Wahrheit dient. Stattdessen werden die Sessions als ein nur zum Hinzufügen geeigneter Stream von Ereignissen gespeichert.
turn/start
user/message
assistant/chunk
assistant/chunk
assistant/message
tool/call
tool/result
assistant/message
turn/end
Die Modellgeschichte kann aus diesen Ereignissen wieder aufgebaut werden, wodurch das Sitzungslog die Quelle der Wahrheit darstellt.
Session log = source of truth
Das ersetzt die gängige Alternative.
Mutable chat history = source of truth
Warum Agenten von Event-Sourcing profitieren
Agenten sind inzwischen so komplex geworden, dass das Speichern einer einfachen Nachrichtenliste oft nicht ausreicht.
[
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}
]
Beim Debuggen ist es notwendig zu wissen, welches Tool zur Verfügung stand und welches aufgerufen wurde, welches Modell eine Antwort erstellte, welche Systemanweisung angewendet wurde, wo eine Anfrage begann, wo ein Überlegungsschritt endete, welche Konfiguration aktiv war, ob ein Aufruf erneut versucht wurde und welche gestreamten Ausgaben zurückkamen. Das Framework protokolliert strukturierte Sitzungsevents, sodass alles, was das Modell sehen konnte, rekonstruiert werden kann. Ein Grundsatz in seiner Konzeption besagt, dass alles, was für das Modell sichtbar ist, aus den Protokollen wiederherstellbar sein sollte. Anstatt darüber zu spekulieren, warum der Agent so handelte, kann man die dahinterliegende Anfrage rekonstruieren.
Fibers und Lebenszykluszustände
Cordis repräsentiert laufende Komponenten mit einer Fiber. Damit hat es nichts zu tun mit den Fibers des Betriebssystems. Eine Cordis Fiber ist eine Instanz einer Laufzeitkomponente, die durch solche Zustände wechselt.
PENDING
↓
LOADING
↓
ACTIVE
↓
UNLOADING
↓
DISPOSED
Auch Fehlerzustände existieren. Noch wichtiger ist, dass eine Faser mehrere Informationen über sich selbst speichert.
what dependencies I need
what providers currently satisfy them
what effects I have created
what cleanup functions belong to me
Wenn sich Abhängigkeiten ändern, kann die Fiber ihren Lebenszyklus erneuern.
Warum die Identität des Anbieters wichtig ist
Nehmen wir an, eine Komponente hängt von einem Datenbankdienst ab.
database
Derzeit wird es von einem Anbieter geliefert.
DatabaseProvider A
Während eines Hot Reloads wird es durch ein anderes ersetzt.
DatabaseProvider B
Auch wenn beide äquivalente JavaScript-Objekte zurückgeben, kann Cordis die Identitäten der Anbieter voneinander unterscheiden. Der Grund dafür ist, dass die Abhängigkeitsbeziehung nicht nur darin besteht, dass ein Wert existiert.
"value exists"
Das kann auch bedeuten, dass der Wert von einer anderen Komponenteninstanz stammt.
"this value came from a different component instance"
Durch den Wechsel des Anbieters kann dazu geführt werden, dass die Abhängigkeiten ordnungsgemäß neu geladen werden, wodurch eine große Anzahl von Fehlern durch veraltete Referenzen vermieden wird.
Löschung in Abhängigkeitsreihenfolge
Betrachten Sie eine Kette, in der ein Plugin die Datenbank bereitstellt.
Plugin A provides database
Ein weiteres Plugin hängt von der Datenbank ab, und ein drittes hängt vom zweiten ab.
Plugin B depends on databasePlugin C depends on Plugin B
Die Abhängigkeitskette sieht so aus.
A
↓
B
↓
C
Nun wird der Erste entfernt. Ein naiver Laufzeitumgebungen könnte die Datenbank sofort schließen, obwohl die anderen beiden möglicherweise noch ausgeführt werden. Cordis koordiniert stattdessen den Abhängigkeitsgraphen. Das Entladen beginnt.
A begins unloading
Dann werden die Abhängigkeiten nacheinander freigegeben, bevor der ursprüngliche Prozess seine Aufräumarbeiten abschließt.
↓ mark A unavailableB notices dependency disappeared↓ B unloadsC notices B disappeared↓ C unloadsthenA completes cleanup
Die Reihenfolge ist von enormer Bedeutung, denn sie verhindert, dass Abhängige etwas verwenden, das bereits zur Hälfte zerstört ist.
Isolement: ein Name, getrennte Welten
Ein einziger globaler Service-Namespace kann begrenzend wirken. Stellen Sie sich zwei Agenten vor, die unterschiedliche Datenbanken benötigen.
Agent A → Database A
Agent B → Database B
Sie beides bitten immer noch um dasselbe.
database
Cordis unterstützt die Isolation, sodass die Fehlerbehebung von einem Paar aus funktioniert.
service name + isolation realm
und nicht nur aus einem Namen.
service name
Konzeptionell führt jede Domäne denselben Namen auf ihre eigene Instanz zurück.
Realm A: database → DB_A
Realm B: database → DB_B
Die gleiche logische Abhängigkeit kann daher je nach Kontext unterschiedlich gelöst werden, was bei Mehrbenutzer-Systemen, mehreren Agenten, Testumgebungen, sandboxierten Plugins und separaten Arbeitsräumen hilft.
Abschirmung
Cordis kann den Zugriff auf Dienste auch mithilfe von Metadaten steuern. Betrachten Sie ein Tool-Register, in dem ein Admin-Agent diese Tools sehen kann.
shell
browser
filesystem
deploy
während ein eingeschränkter Agent eine kleinere Menge sieht.
browser
filesystem-readonly
Anstatt ein vollständig separates Register zu erstellen, kann der Zugriff je nach Kontext abgefangen werden.
ctx + metadata
↓
service resolution
↓
modified view of capability
Das ist ein starkes Muster für Politiksysteme.
Sicheres Hot-Module-Austauschverfahren
Hot Reloading klingt einfach.
file changed
↓
reload module
In der Laufzeit eines Plugins wird es schwierig. Was passiert, wenn das neue Modul einen Fehler enthält – was geschieht dann mit der alten, funktionierenden Version? Der Cordis-Loader unterstützt ein Neu laden-Verhalten, das wie eine Transaktion funktioniert.
working plugin
↓
code changes
↓
attempt new import
↓
success?
┌───────┴───────┐
yes no
↓ ↓
replace rollback
plugin old module
Dies passt natürlich zu umkehrbaren Effekten. Ein Plugin mit klar definiertem Lebenszyklus, Abhängigkeitsmenge und Bereinigungsmethode ist weitaus weniger problematisch bei der Ersetzung.
Ein Fallbeispiel aus der Praxis
Cordis wurde nicht ausschließlich für DeepSeek Harness entwickelt. Die Veröffentlichung befasst sich mit Koishi, einem darauf basierenden Open-Source-Chatbot-Framework mit einem Community-Ekosystem, das laut den Autoren aus Tausenden von Plugins besteht. Ein echtes Ekosystem macht eine dynamische Komposition weitaus überzeugender als zehn manuell erstellte Module. Die Veröffentlichung bleibt jedoch vorsichtig und behauptet nicht, dass kontrollierte Benchmarks große Leistungs- oder Produktivitätssteigerungen gezeigt hätten. Es sind weitere Bewertungen erforderlich.
Nicht alles kann rückgängig gemacht werden
Hier trifft Theorie auf Realität. Einige Laufzeiteffekte sind tatsächlich umkehrbar, wie zum Beispiel das Öffnen und Schließen einer Datei.
open file
→ close file
Anderes Paarungsmaterial eignet sich genauso gut.
allocate memory
→ free memorystart worker
→ terminate workerregister event listener
→ unregister listener
Dann gibt es noch das Versenden einer E-Mail.
send email
Was wäre sein Umkehrwert?
unsend email?
In der Regel ist das unmöglich, und das gilt auch für eine Gruppe anderer Aktionen.
send HTTP request
transfer money
publish message
delete external data
Wenn etwas die Außenwelt beeinflusst, kann es keine echte Umkehrung geben. Die beste verfügbare Option ist eine Entschädigung.
payment → refund
message → follow-up correction
database mutation → compensating transaction
Kompensation ist nicht dasselbe wie mathematische Umkehrung. Die Veröffentlichung erkennt diese Grenze ausdrücklich an, und Cordis funktioniert am besten für Effekte, die die Laufzeit tatsächlich steuern und wiederherstellen kann.
Cordis verlässt sich auf Ihre Aufräumarbeit
Nehmen wir an, ein Plugin startet einen Server innerhalb eines Effekts und gibt eine Aufräumfunktion zurück.
ctx.effect(() => {
startServer()
return () => {
// supposedly cleanup
}
}))
Cordis hat keine Möglichkeit, nachzuweisen, dass die Aufräumarbeit tatsächlich den Startzustand rückgängig macht. Das formale Modell besagt, dass der Umkehrvorgang die Umgebung in ihren ursprünglichen Zustand versetzen muss, doch die Implementierung hängt davon ab, dass der Programmierer einen gültigen Disposer schreibt. Die Kombination eines Effekts mit einer Aufräumarbeit ist daher eine Disziplin.
effect + cleanup
Es handelt sich nicht um eine automatische formale Überprüfung, und das ist ein wichtiger Unterschied.
Warum dies für KI-Agenten geeignet ist
Traditionelle Anwendungen bleiben nach dem Start recht stabil. Agenten entwickeln sich in die entgegengesetzte Richtung. Zukünftige Agentensysteme könnten kontinuierlich solche Operationen ausführen.
install tool
remove tool
change model
spawn agent
kill agent
load skill
reload code
switch permissions
connect service
disconnect service
modify environment
Stellen Sie sich einen Agenten vor, der entscheidet, dass er GitHub-Unterstützung benötigt, und daher eine GitHub-Funktion lädt – diese später wieder deaktiviert, wenn sie nicht mehr nützlich ist, oder eine fehlerhafte Implementierung ersetzt. Ein solches System erfordert weitaus stärkere Semantiken zur Laufzeit-Komposition als ein herkömmlicher Abhängigkeitsinjektor bietet. Ohne diese könnte ein langlaufender Agent Überreste sammeln.
orphan listeners
stale tools
dangling processes
broken dependencies
zombie services
duplicated handlers
Cordis versucht, die Laufzeit-Komposition zu einem erstklassigen Softwarekonzept zu machen.
Warum das Design elegant wirkt
Durch Cordis, das die Komposition übernimmt, kann DeepSeek Harness nahezu alles auf dieselbe Weise modellieren.
Model provider → plugin
Tool → plugin
Agent loop → plugin
Session store → plugin
UI → plugin
Telemetry → plugin
Approval system → plugin
Sandbox → plugin
Credential store → plugin
Neue Funktionen werden im Framework nicht zu Sonderfällen. Sie werden zu weiteren Teilnehmern innerhalb eines Kompositionsystems.
Profile und Bündel
Falls alles ein Plugin ist, möchte niemand jedes Mal fünfzig davon manuell zusammenstellen. Deshalb bietet das Framework höherwertige Kompositionen an. Ein Profil beschreibt eine benannte Systemkonfiguration, während ein Bündel Gruppen von Plugins zusammen mit ihrer Konfiguration bereitstellt. Ein Basisbündel könnte so aussehen.
Base Bundle
├── model adapters
├── tools
├── sessions
├── sandbox
├── approvals
├── credentials
├── settings
└── telemetry
Ein weiteres Paket könnte eine Webanwendung hinzufügen.
Web Application
Ein anderes könnte einen kopflosen Agenten erzeugen.
Headless Agent
Die Architektur bleibt modular, ohne den täglichen Gebrauch erschwert zu machen.
Fünf Ideen, die man beibehalten sollte
Auch wenn alles andere in Vergessenheit gerät, sollten man sich diese fünf Punkte merken.
Rückgängig machbare Effekte haben Vorrang: Jede Änderung, die eine Komponente vornimmt, sollte mit einer Möglichkeit zur Rückgängigmachung einhergehen.
change + inverse
Der zweite Fall sind reaktive Koeffekte. Komponenten deklarieren, was sie benötigen, und die Laufzeit reagiert, wenn Abhängigkeiten auftauchen, verschwinden oder sich ändern.
dependency available → activate
dependency gone → deactivate
Drittens ist da der Kontext. Effekte, Abhängigkeiten und Zugriff auf Dienste befinden sich innerhalb eines einheitlichen Laufzeitkontexts.
ctx
Viertens gibt es einen dynamischen Komponenten-Kalkül, der eine Komponente durch das beschreibt, was sie benötigt, was sie bereitstellt und was sich bei ihr ändert, wodurch dem Laufzeitumfeld genügend Struktur zur Verarbeitung von Lade- und Entladevorgängen gegeben wird.
Dependencies + Provisions + Effects
Die fünfte ist die räumlich-zeitliche Kompositionsfähigkeit, die die räumlichen und zeitlichen Aspekte miteinander verbindet.
spatial:
who depends on whom?
+
temporal:
how can components safely enter and leave?
Kurz ausgedrückt handelt es sich um einen rückgängig machbaren Wandel in Kombination mit einer reaktiven Abhängigkeit.
Warum es über ein einzelnes Projekt hinaus wichtig ist
Die meisten aktuellen Agentenframeworks gehen weiterhin von einer recht statischen Anwendung aus: Programm starten, Tools laden, Modell laden, Agenten ausführen, beenden. Autonome Agenten streben nach Laufzeiten, die sich selbst anpassen können, während sie weiterhin aktiv sind. Stellen Sie sich einen Agenten vor, der einer solchen Abfolge folgen kann.
discover a capability
install it
use it
upgrade it
replace it
remove it
continue running
Zu diesem Zeitpunkt ist die Verwaltung des Plugin-Lebenszyklus nicht mehr nur ein nützliches architektonisches Merkmal, sondern wird zu einer grundlegenden Infrastruktur. Das ist es, was das Projekt interessant macht. Das Modell selbst ist nicht der innovativste Teil – vielmehr ist es die darum herumliegende Laufzeitumgebung.
Die tiefere Lektion
Softwarearchitektur befasst sich in der Regel zu einem bestimmten Zeitpunkt mit der Komposition von Komponenten. Man setzt diese Teile zusammen und führt das Programm aus.
A + B + C
Cordis stellt härtere Fragen.
What if A disappears?
What if B is replaced?
What if C suddenly gains a dependency?
What if another implementation of A appears?
What if all of this happens while the system is running?
Das verändert das Problem völlig. Komponenten sind nicht mehr statische Blöcke, sondern verhalten sich wie lebende Teilnehmer: Sie kommen, bauen Beziehungen auf, verändern ihre Umgebung, sind von anderen abhängig und gehen schließlich wieder.
Die Schlussfolgerung
DeepSeek Harness mag wie ein weiteres Agent-Framework erscheinen, doch sein Reiz liegt nicht darin, dass es einen LLM aufrufen oder Tools ausführen kann – was viele Frameworks bereits können. Die wichtigere Idee ist, dass das Kompositionsverfahren selbst die Architektur darstellt. Jede Fähigkeit ist eine Komponente, jede Abhängigkeit ist explizit definiert, jeder steuerbare Effekt weiß, wie er sich selbst aufräumen kann, Komponenten reagieren auf Veränderungen der Abhängigkeiten, und sogar der Agent-Zyklus kann ersetzt werden – er ist kein unveränderlicher Framework-Code. Das Leitprinzip besteht darin, jede Fähigkeit zu einer Komponente zu machen, jede Abhängigkeit explizit darzustellen und jeden Effekt so zu gestalten, dass er sich selbst rückgängig machen kann.
In gewöhnlichen Anwendungen handelt es sich dabei um eine übersichtliche Plugin-Architektur. Bei langlaufenden, sich selbst modifizierenden KI-Agenten kann dies jedoch weitaus wichtiger sein. Wenn Agenten letztendlich ihre eigene Laufzeitumgebung ändern, benötigt die Software eine prinzipiengerechte Methode zur Anpassung, ohne allmählich die Umgebung zu schädigen, in der sie läuft. Genau dieses Problem soll DeepSeek Harness sowie die dahinterstehende Cordis-Forschung lösen.