Wszystko to plugin: projekt Cordis stojący za systemem DeepSeek Harness
Jak DeepSeek Harness tworzy środowisko działania agenta na podstawie pluginów, wykorzystując koncepcje Cordis takie jak odwracalne efekty, reaktywne zależności oraz zastępowalny cykl agenta.
Źródła: DeepSeek Quietly Built a Different Kind of AI Agent Framework
Zwykły schemat i jego słabe punkty
Większość frameworki agentowych opiera się na tych samych składnikach: modelu językowym, kilku narzędziach, przechowywanej rozmowie oraz pętli obejmującej je wszystkie. Poniższy szkic pokazuje jego strukturę.
User asks something
↓
LLM thinks
↓
LLM calls a tool
↓
Tool returns data
↓
LLM continues
↓
Final answer
Z definicji to już stanowi agenta. Problemy pojawiają się, gdy system rośnie. Gromadzą się wymagania: liczni dostawcy modeli, narzędzia przeglądarkowe, wykonywanie w środowisku shell, serwery MCP, procesy zatwierdzania, pamięć, telemetryka, autoryzacja, różne interfejsy użytkownika, agenci w tle, spersonalizowane polecenia, uprawnienia oraz szybkie ładowanie. Wkrótce każda część zależy od wszystkich innych. Zmiana dostawcy modeli wpływa na pętlę agentów, usunięcie wtyczki pozostawia słuchacze zdarzeń, a ponowne załadowanie narzędzia może uszkodzić agenty działające w tym momencie. Wtyczki polegają również na usługach, które mogą pojawić się lub zniknąć w trakcie działania.
DeepSeek Harness podchodzi do tego zupełnie inaczej. Jego centralna idea brzmi niemal zbyt prosto: wszystko jest wtyczką, łącznie z elementami, które większość frameworków określa jako kluczowe.
Pod tym wszystkim znajduje się framework o nazwie Cordis, który powstał na podstawie artykułu badawczego zatytułowanego „A Programming Paradigm for Spatiotemporal Composability”. Ten artykuł jest interesujący, ponieważ projekt nie jest zwykłym sposobem organizacji modułów JavaScript. On adresuje głębsze pytanie inżynieryjne: jak komponenty mogą się łączyć, odchodzić, zastępować siebie oraz zmieniać zależności w działającym programie, bez tego, by środowisko wykonawcze popadło w chaos?
Czym jest harness
DeepSeek Harness, skracane jako dsh, to open-source harness dla agentów wydany przez DeepSeek. Harness stanowi infrastrukturę otaczającą model. Niezależnie od tego, czy chodzi o model DeepSeek, Claude, GPT, Gemini, czy lokalny, harness zajmuje się wszystkim, co jest z nim związane.
┌───────────────┐
│ LLM │
└───────┬───────┘
│
┌──────────▼──────────┐
│ Agent Harness │
│ │
│ Prompt construction │
│ Tool execution │
│ Session history │
│ Permissions │
│ Streaming │
│ Agent lifecycle │
│ Model adapters │
└─────────────────────┘
Różnica ta ma ogromne znaczenie. Zazwyczaj zwraca się uwagę na to, jak inteligentny jest model, jednak stworzenie czegoś takiego jak Claude Code, Codex, Cursor czy długotrwały autonomiczny agent pokazuje, że czas działania aplikacji ma równie dużą wagę. DeepSeek Harness obsługuje ten czas działania poprzez zestaw dynamicznie komponowanych pluginów, a nie jako jedną monolityczną aplikację – decyzja ta pochodzi bezpośrednio z Cordis.
Zagadka zależności
Wyobraź sobie stworzenie agenta do programowania z trzech pluginów.
GitPlugin
TerminalPlugin
CodingAgent
Agent do kodowania wymaga Git i Terminala. Podczas uruchamiania najprostszym rozwiązaniem jest załadowanie ich po kolei.
load Git
load Terminal
load CodingAgent
Latwo, a większość frameworki do iniekcji zależności radzi sobie z tym dobrze. Trudniejszym pytaniem jest to, co się stanie, jeśli podczas działania programu zniknie plugin do Git. Może zostać zaktualizowany, jego konfiguracja mogła ulec zmianie, użytkownik mógł go wyłączyć lub może zostać zastąpiona inna implementacja. W takim przypadku środowisko wykonawcze musi zatrzymać wszystko, co polega na tym usłudze.
CodingAgent
↓ -------->depends on
GitPlugin
Jeśli Git zniknie, agent kodujący nie powinien dalej udawać, że istnieje. A jeśli później pojawi się inny dostawca Git, być może agent powinien znów zacząć pracować. Kryją się tu dwa oddzielne problemy, które w artykule Cordis nazywane są kompozowalnością temporalną i kompozowalnością przestrzenną.
Kompozowalność temporalna: czyste wycofanie
Załóżmy, że plugin się ładuje i rejestruje słuchacz zdarzeń.
events.on("message", handler)
Później plugin jest wyłączany, a pytanie brzmi, co się dzieje z obsługą zdarzeń. Jeśli nic jej nie usunie, plugin przestaje istnieć pod względem nazwy, ale jego część nadal działa. Należy dodać, że plugin mógł uruchomić timery, nawiązać połączenia z bazą danych, zarejestrować polecenia, zmienić wspólny stan, dodać narzędzia, dołączyć obsługę zdarzeń oraz uruchomić procesy w tle. Wyłączenie go oznacza odwrócenie każdego z tych kroków.
To jest temporalna kompozycyjność: komponent, który dołącza do systemu, powinien pozwolić systemowi w pełni cofnąć to, co ten komponent zrobił. Cordis traktuje takie zmiany jako efekty możliwe do odwrócenia.
Efekty bez żargonu
Efekt to po prostu coś, co robi twój kod i co zmienia świat zewnętrzny lub stan w czasie wykonywania. Weźmy na przykład rejestrację narzędzia.
toolRegistry.add("search", searchTool)
Przed połączeniem rejestr wygląda w ten sposób.
tools = {}
Następnie przechowuje nowy narzędzie.
tools = {
search: searchTool
}
Operacja przesunięcia to dodawanie.
add(searchTool)
Cordis pragnie jednak czegoś więcej. Chce, aby operacja miała swoją własną funkcję odwrotną. Koncepcyjnie efekt łączy działanie z jego odwołaniem.
Effect:
do: add search tool
undo: remove search tool
Bez notacji matematycznej pomysł brzmi w następujący sposób.
state --effect--> new state
new state --inverse--> original state
To odwrotne działanie jest kluczowym elementem, a Cordis nazywa cały ten zestaw efektem odwracalnym.
Każda zmiana wymaga jej usunięcia
Cordis ujawnia to za pomocą czegoś w rodzaju poniższego wzorca, w którym funkcja wprowadza zmianę i zwraca funkcję, która ją cofa.
ctx.effect(() => {
registerSomething()
return () => {
unregisterSomething()
}
})
Czas wykonywania aplikuje efekt, przechowuje funkcję czyszczenia i wywołuje ją później, gdy wtyczka przestaje działać.
apply effect
↓
store cleanup function
↓
plugin runs
↓
plugin unloads
↓
run cleanup
Gdy wtyczka zawiera kilka efektów, są one stosowane w jednej kolejności.
effect A
effect B
effect C
Czyszczenie odbywa się następnie w odwrotnej kolejności, tak jak rozpakowuje się stos.
undo C
undo B
undo A
Dlaczego odwrotnie? Rozważmy plugin, który uruchamia się w ten sposób.
1. open database
2. start service using database
3. start agent using service
Zamykanie w tym samym porządku byłoby niebezpieczne, ponieważ baza danych zostaje zamknięta, gdy inne programy nadal ją używają.
close database
stop service
stop agent
Bezpieczny zamówienie jest jego przeciwieństwem.
stop agent
stop service
close database
To zasada „ostatni w, pierwszy wy”.
Last In
First Out
Cordis wbudowuje to zachowanie w zarządzanie cyklem życia komponentów.
Samodzielne usuwanie w odwrotnej kolejności jest niewystarczające
Tutaj artykuł staje się ciekawszy. Załóżmy, że Plugin A zmienia wspólną wartość, następnie Plugin B zmienia tę samą wartość, a potem A próbuje przywrócić stan początkowy. To może usunąć modyfikację dokonaną przez B.
Initial: x = 10
Plugin A: x = 20
Plugin B: x = 30
Plugin A unloads: restore x = 10
B jest nadal aktywne, ale jego stan zniknął. Dlatego efekty odwracalne są bezpieczne jedynie przy określonych założeniach niezależności, które w artykule sformalizowano jako niezależność efektów. Mówiąc potocznie, dwa efekty są niezależne, gdy ich zastosowanie lub odwrócenie nie unieważnia znaczenia drugiego. Rejestracja różnych narzędzi zazwyczaj spełnia ten warunek, ponieważ oddziałują one na różne miejsca.
Plugin A registers tool "search"
Plugin B registers tool "calculator"
Zastąpienie tej samej konfiguracji prawdopodobnie nie pomoże.
Plugin A replaces configuration X
Plugin B also replaces configuration X
To rozróżnienie pojawia się ponownie, gdy mowa jest o systemie kontekstowym Cordis.
Kompozycyjność przestrzenna: wiedza o tym, czego potrzebuje plugin
Kompozycyjność czasowa dotyczy pytania, czy komponent można bezpiecznie usunąć. Kompozycyjność przestrzenna dotyczy pytania, czy system rozumie, od czego zależy dany komponent. Wyobraźmy sobie ten plugin.
CodingAgent
Potrzebuje trzech rzeczy.
LLM
ToolRegistry
SessionStore
Klasyczny kod może korzystać z zmiennych globalnych.
global.llm
global.tools
global.sessions
W takim przypadku architektura nie posiada rzeczywistej wiedzy na temat zależności. Cordis czyni je jawnymi. Koncepcyjnie plugin deklaruje to, co wstrzykuje.
inject: [
"llm",
"tools",
"sessions"
]
Teraz środowisko wykonywania może widzieć wymagania wtyczki jako drzewo.
CodingAgent
├── requires LLM
├── requires ToolRegistry
└── requires SessionStore
To umożliwia reakcję w momencie zmian tych usług.
Zależności reaktywne
To może być najlepszy pomysł w Cordis. Załóżmy, że komponent potrzebuje LLM.
LLM
Początkowo nic nie istnieje.
LLM unavailable
Dlatego komponent nie może zostać aktywny. Później ładowany jest plugin dostawcy, który dostarcza jeden taki komponent.
OpenAI Plugin loads
↓
provides LLM
↓
dependency becomes satisfied
↓
component activates
Jeśli ten dostawca zostanie usunięty, jego funkcjonalność również zniknie.
OpenAI Plugin unloads
↓
LLM disappears
↓
dependency becomes unsatisfied
↓
dependent component unloads
Gdy załaduje się inny dostawca, funkcjonalność zostaje przywrócona.
DeepSeek Plugin
↓
provides LLM
↓
component becomes active again
Zależności nie są sprawdzane tylko raz podczas uruchamiania. Są reaktywne, dlatego w artykule mowa jest o reaktywnych koeffectach.
Efekty versus koeffecty
Terminologia może wydawać się skomplikowana, ale rozróżnienie jest proste. Efekt opisuje to, co komponent zmienia, przechodząc z wtyczki do środowiska.
Plugin → Environment
Przykładami są te działania.
register tool
open database
subscribe to event
start timer
Koeffect opisuje coś, czego komponent wymaga od swojego otoczenia, przepływając w drugą stronę.
Environment → Plugin
Typowymi przykładami są te potrzeby.
need an LLM
need a logger
need session storage
need a tool registry
Po połączeniu tych dwóch elementów wymagania płyną w jednym kierunku.
requirements
Environment ───────────────────> Plugin
podczas gdy efekty wracają.
Environment <─────────────────── Plugin
effects
To jest podstawowa idea stojąca za Cordis. Komponent określa, czego potrzebuje, co dostarcza oraz jakie odwracalne efekty wywołuje.
I NEED these services.
I PROVIDE these services.
I PERFORM these reversible effects.
Wtedy silnik wykonawczy koordynuje cykl życia aplikacji.
Pochodzenie terminu „spatiotemporalny”
Teraz tytuł artykułu nabiera sensu. Kompozycyjność przestrzenna dotyczy relacji pomiędzy komponentami.
A depends on B
B provides X
C depends on X
Kompozowalność temporalna dotyczy sposobu, w jaki komponenty zmieniają się w czasie.
load
activate
replace
deactivate
unload
reload
Razem tworzą całą filozofię.
Spatiotemporal composability = dependency-aware composition + reversible lifecycle changes
Obiekt kontekstu
DeepSeek Harness nie przekazuje ręcznie dziesiątek usług za pośrednictwem konstruktorów. Wtyczki wchodzą ze sobą w interakcję poprzez wspólny kontekst, zwykle nazywany w ten sposób.
ctx
Koncepcyjnie przedstawia główne usługi.
ctx
├── llm
├── tools
├── sessions
├── agents
├── agentLoop
├── logger
└── ...
Jednak ctx to coś więcej niż tylko obiekt JavaScript wypełniony zmiennymi globalnymi. Dzięki niemu pluginy mogą znajdować swoje zależności, oferować usługi, rejestrować efekty, przysłuchiwać się wydarzeniom, izolować zasoby, przechwytywać dostęp do usług oraz koordynować cykle życia, dlatego Cordis przedstawia ten kontekst jako jednolitą abstrakcję.
Od teorii do rzeczywistego projektu
DeepSeek Harness udostępnia swoje główne podsystemy poprzez ten wspólny kontekst.
ctx.sessions
ctx.systemPrompt
ctx.tools
ctx.agents
ctx.agentLoop
ctx.llm
Zauważ coś niezwykłego: nawet pętla agentów to po prostu usługa.
ctx.agentLoop
Zatem ten pętla może zostać zastąpiona, a nie jest to święty kod frameworka. To prowadzi do kluczowej zasady projektu: nie istnieje uprzywilejowana część, którą każda funkcja musiałaby naprawiać.
Dlaczego pętla jako plugin ma znaczenie
Większość frameworków agentów zawiera centralną pętlę przypominającą tę tutaj.
while not finished:
response = model(history)
if response.tool_call:
result = execute_tool(response.tool_call)
history.append(result)
else:
return response
Następnie każda zaawansowana funkcja zaczyna ją modyfikować. Zatwierdzenie przed uruchomieniem poleceń? Popraw loopa. Śledzenie? Popraw loopa. Próby ponowne, delegacja między wieloma agentami, inny backend do inferencji? Popraw loopa. W końcu staje się hubem, od którego zależą narzędzia, modele i pamięć.
Agent Loop
/ | \
/ | \
tools models memory
/ | \
approvals retries tracing
Pętla staje się „bóstwem” aplikacji. DeepSeek Harness unika tego, pozwalając pętli uczestniczyć w tej samej architekturze pluginów co wszystkie inne elementy.
Kształt jednej rundy agenta
W rzeczywistości interakcja przechodzi przez etapy mniej więcej w takiej kolejności.
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
Ważne jest to, że każdy etap ujawnia wydarzenia oraz punkty łączeniowe usług, co daje pluginom możliwość uczestnictwa bez konieczności przepisywania całego środowiska działania.
Wydarzenia typu waterfall jako middleware
Cordis obsługuje kilka trybów uruchamiania wydarzeń. Jeden z przydatnych trybów, waterfall, funkcjonuje jak middleware. Wyobraź sobie żądanie przechodzące przez łańcuch operacji.
request
↓
Plugin A
↓
Plugin B
↓
Plugin C
↓
LLM
Wtyczka B może otoczyć wykonywanie zadania obsługą, która przyjmuje następny krok.
async function handler(next) {
console.log("before")
const result = await next()
console.log("after")
return result
}
Wykonywanie jest następnie nawarstwiane, przy czym każdy plugin działa przed i po wewnętrznym wywołaniu.
Plugin A before
Plugin B before
Plugin C before
LLM
Plugin C after
Plugin B after
Plugin A after
To obejmuje uprawnienia, przepisywanie żądań, telemetrię, buforowanie, routowanie modeli, sprawdzanie bezpieczeństwa oraz iniekcję kontekstu – żadna z tych funkcji nie musi być bezpośrednio włączona do adaptera modelu.
Model znajduje się za granicą możliwości
Pakiet ten abstrahuje również LLM, dzięki czemu agent nie musi znać każdego SDK dostawcy. Komunikuje się z jedną usługą.
Agent
↓
ctx.llm
↓
Model Adapter
↓
Provider API
Jeden dostawca może to zaimplementować dla API DeepSeek.
DeepSeek API
Kolejny dotyczy OpenAI.
OpenAI
Trzeci dotyczy Anthropic.
Anthropic
Pozostała część architektury agenta obejmuje tę samą funkcjonalność LLM. Dokumentacja Harness czasami określa to jako „szew funkcjonalności”, składający się z definicji usługi i dostawcy usług.
Service Definition
↓
Service Provider
↓
Consumer
Dla modelu językowego ta łańcuchowa struktura biegnie od interfejsu do konkretnego dostawcy.
LLM interface
↓
OpenAI provider
↓
Agent loop
Dlatego dodanie dostawcy modeli nie oznacza koniecznie przepisywania silnika wykonawczego. Wystarczy dostarczyć inną implementację tej samej funkcjonalności.
Sesje oparte na zdarzeniach
Rozmowy nie są przechowywane przez system jako jeden duży, modyfikowalny tabliczka, który stanowi źródło prawdy. Zamiast tego sesje są przechowywane jako strumień zdarzeń, do którego można jedynie dodawać nowe elementy.
turn/start
user/message
assistant/chunk
assistant/chunk
assistant/message
tool/call
tool/result
assistant/message
turn/end
Historię modelu można odbudować na podstawie tych zdarzeń, dzięki czemu journal sesji stanowi źródło prawdy.
Session log = source of truth
To zastępuje powszechną alternatywę.
Mutable chat history = source of truth
Dlaczego agenci czerpią korzyści z event sourcing
Agenci stały się na tyle złożone, że przechowywanie zwykłej listy wiadomości często nie jest wystarczające.
[
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}
]
Debugowanie może wymagać znajomości tego, który narzędzie zostało użyte i które wywołano, który model napisał odpowiedź, jaki prompt systemu został zastosowany, gdzie rozpoczęła się prośba, gdzie zakończył się krok rozumowania, jaka konfiguracja była aktywna, czy próba połączenia została powtórzona oraz jaki wynik w formie strumienia wrócił. Narzędzie to rejestruje ustrukturyzowane zdarzenia sesji, dzięki czemu można odtworzyć wszystko, co model mógł zobaczyć. Jedna z zasad jego projektu stanowi, że wszystko, co jest widoczne dla modelu, powinno dać się odzyskać z dziennika. Zamiast spekulować na temat tego, dlaczego agent postąpił w określony sposób, można odtworzyć prośbę, która za tym stała.
Fibry i stan cyklu życia
Cordis reprezentuje działające komponenty za pomocą struktur typu Fiber. Nie ma to nic wspólnego z fibrami systemu operacyjnego. Fiber w Cordis to instancja komponentu w czasie wykonywania, która przechodzi przez różne stany.
PENDING
↓
LOADING
↓
ACTIVE
↓
UNLOADING
↓
DISPOSED
Istnieją również stany awarii. Co ważniejsze, włókno rejestruje kilka informacji na temat samego siebie.
what dependencies I need
what providers currently satisfy them
what effects I have created
what cleanup functions belong to me
Gdy zmieniają się zależności, fiber może odświeżyć swój cykl życia.
Dlaczego ważna jest tożsamość dostawcy
Załóżmy, że komponent zależy od usługi bazy danych.
database
W tej chwili jest dostarczany przez jednego dostawcę.
DatabaseProvider A
Podczas gorącego ładowania jest zastępowane innym.
DatabaseProvider B
Nawet gdy oba zwracają równoważne obiekty JavaScript, Cordis potrafi odróżnić tożsamości dostawców. Powodem jest to, że znaczenie zależności nie polega wyłącznie na tym, iż dana wartość istnieje.
"value exists"
Może to również oznaczać, że wartość pochodzi z innej instancji komponentu.
"this value came from a different component instance"
Zmiana dostawcy może więc sprawić, że elementy zależne zostaną prawidłowo ponownie załadowane, co zapobiega wielu błędom związanym ze starymi referencjami.
Wyłączanie w kolejności zależności
Rozważmy łańcuch, w którym jeden plugin dostarcza bazę danych.
Plugin A provides database
Drugi plugin zależy od bazy danych, a trzeci od drugiego.
Plugin B depends on databasePlugin C depends on Plugin B
Łańcuch zależności wygląda w ten sposób.
A
↓
B
↓
C
Teraz usunięto pierwszy. Prosty silnik wykonywania może natychmiast zamknąć bazę danych, mimo że pozostałe dwa mogą nadal być w trakcie wykonywania. Zamiast tego Cordis koordynuje graf zależności. Rozpoczyna się rozładowywanie.
A begins unloading
Następnie zależne elementy są usuwane po kolei, zanim oryginał zakończy swoją czynność oczyszczania.
↓ mark A unavailableB notices dependency disappeared↓ B unloadsC notices B disappeared↓ C unloadsthenA completes cleanup
Kolejność ma ogromne znaczenie, ponieważ zapobiega to osobom zależnym od używania czegoś, co jest już na wpół zniszczone.
Izolacja: jedna nazwa, oddzielne światy
Jedno globalne nazewnictwo usług może okazać się ograniczające. Wyobraźmy sobie dwa agenty, które potrzebują różnych baz danych.
Agent A → Database A
Agent B → Database B
Oboje nadal proszą o to samo.
database
Cordis obsługuje izolację, więc rozwiązywanie problemów z usługami działa w parach.
service name + isolation realm
i nie tylko na podstawie imienia.
service name
Koncepcyjnie każda domena przypisuje to samo nazwę do swojej własnej instancji.
Realm A: database → DB_A
Realm B: database → DB_B
Ta sama zależność logiczna może więc być rozwiązywana inaczej w zależności od kontekstu, co jest przydatne w systemach wielu użytkowników, wielu agentach, środowiskach testowych, pluginach w izolacji oraz oddzielnych przestrzeniach roboczych.
Przechwytywanie
Cordis może również kształtować dostęp do usług za pomocą metadanych. Rozważmy rejestr narzędzi, w którym agent administracyjny może je widzieć.
shell
browser
filesystem
deploy
podczas gdy agent o ograniczonych uprawnieniach widzi mniejszy zbiór.
browser
filesystem-readonly
Zamiast tworzyć całkowicie oddzielny rejestr, dostęp można przechwytywać w zależności od kontekstu.
ctx + metadata
↓
service resolution
↓
modified view of capability
To silny wzorzec dla systemów polityk.
Bezpieczniejsza zamiana modułów w czasie rzeczywistym
Hot reloading wydaje się proste.
file changed
↓
reload module
W czasie działania wtyczki sytuacja staje się skomplikowana. Co jeśli nowy moduł zawiera błąd, a co się stanie z dawną, sprawną wersją? Ładowarka Cordis obsługuje zachowanie ponownego załadunku, które funkcjonuje jak transakcja.
working plugin
↓
code changes
↓
attempt new import
↓
success?
┌───────┴───────┐
yes no
↓ ↓
replace rollback
plugin old module
To naturalnie pasuje do efektów odwracalnych. Wtyczka o jasnym cyklu życia, określonym zestawie zależności oraz procedurze czyszczenia jest znacznie mniej ryzykowna pod względem wymiany.
Przypadek z praktyki
Cordis nie został stworzony wyłącznie dla DeepSeek Harness. Artykuł omawia Koishi – frameworka do chatbotów otwartego oprogramowania zbudowanego na jego bazie, który posiada ekosystem społeczności składający się z tysięcy wtyczek. Prawdziwy ekosystem sprawia, że dynamiczna kompozycja jest o wiele bardziej skuteczna niż dziesięć ręcznie budowanych modułów. Artykuł zachowuje jednak ostrożność i nie twierdzi, że kontrolowane testy wykazały duże wzrosty wydajności lub produktywności. Potrzebne są dalsze oceny.
Nie wszystko da się cofnąć
Tutaj teoria spotyka się z rzeczywistością. Niektóre efekty w czasie wykonywania są rzeczywiście odwracalne, np. otwieranie i zamykanie pliku.
open file
→ close file
Inne pary łączą się równie dobrze.
allocate memory
→ free memorystart worker
→ terminate workerregister event listener
→ unregister listener
Następnie jest wysyłanie e-maila.
send email
Jakie byłoby jego odwrotność?
unsend email?
Zazwyczaj jest to niemożliwe, i to samo dotyczy grupy innych działań.
send HTTP request
transfer money
publish message
delete external data
Gdy coś wpływa na świat zewnętrzny, prawdziwe odwrócenie sytuacji może nie istnieć. Najlepszą dostępną opcją jest odszkodowanie.
payment → refund
message → follow-up correction
database mutation → compensating transaction
Kompensacja nie jest tym samym co matematyczna korekta. Artykuł wyraźnie wskazuje na tę różnicę, a Cordis najlepiej sprawdza się w przypadku efektów, które czas wykonywania programu może faktycznie kontrolować i naprawiać.
Cordis polega na twojej obsłudze końcowa
Załóżmy, że plugin uruchamia serwer wewnątrz jakiegoś efektu i zwraca funkcję do jego usunięcia.
ctx.effect(() => {
startServer()
return () => {
// supposedly cleanup
}
}))
Cordis nie ma sposobu, aby udowodnić, że operacja czyszczenia rzeczywiście odwraca proces uruchamiania. Formalny model stanowi, że operacja odwrotna musi przywrócić środowisko do jego wcześniejszego stanu, ale implementacja zależy od programisty, który musi napisać poprawny mechanizm usuwania. Dlatego łączenie efektu z operacją czyszczenia wymaga określonej dyscypliny.
effect + cleanup
To nie jest automatyczna weryfikacja formalna, a to istotna różnica.
Dlaczego to pasuje do agentów AI
Klasyczne aplikacje pozostają dość stabilne po uruchomieniu. Agenci rozwijają się w przeciwnym kierunku. Przyszłe systemy agentów mogą nieprzerwanie wykonywać takie operacje.
install tool
remove tool
change model
spawn agent
kill agent
load skill
reload code
switch permissions
connect service
disconnect service
modify environment
Załóżmy agenta, który decyduje, że potrzebuje wsparcia GitHuba, i ładuje odpowiednią funkcjonalność, a następnie odrzuca ją później, gdy przestaje być przydatna, lub zastępuje nieudaną implementację. Taki system wymaga znacznie silniejszych semantyk kompozycji w czasie wykonywania niż te, które oferuje tradycyjny injektor zależności. Bez nich agent działający przez długi czas mógłby gromadzić niepotrzebne elementy.
orphan listeners
stale tools
dangling processes
broken dependencies
zombie services
duplicated handlers
Cordis stara się uczynić kompozycję w czasie wykonywania koncepcją o pierwszorzędnym znaczeniu w oprogramowaniu.
Dlaczego projekt wydaje się elegancki
Gdy Cordis zajmuje się kompozycją, DeepSeek Harness może modelować niemal wszystko w ten sam sposób.
Model provider → plugin
Tool → plugin
Agent loop → plugin
Session store → plugin
UI → plugin
Telemetry → plugin
Approval system → plugin
Sandbox → plugin
Credential store → plugin
Nowe funkcje nie stają się specjalnymi przypadkami w ramach frameworka. Stają się jedynie dodatkowymi elementami w obrębie jednego systemu kompozycji.
Profilowie i pakiety
Jeśli wszystko jest plikiem wtyczkowym, nikt nie chce za każdym razem ręcznie składać pięćdziesięciu z nich. Dlatego framework oferuje bardziej zaawansowane struktury kompozycji. Profil opisuje nazwaną konfigurację systemu, a pakiet rozprowadza grupy wtyczek wraz z ich konfiguracjami. Podstawowy pakiet może wyglądać w ten sposób.
Base Bundle
├── model adapters
├── tools
├── sessions
├── sandbox
├── approvals
├── credentials
├── settings
└── telemetry
Kolejny pakiet mógłby zawierać aplikację internetową.
Web Application
Ktoś inny mógłby stworzyć bezgłowego agenta.
Headless Agent
Architektura pozostaje modułowa, nie utrudniając codziennego użytkowania.
Pięć pomysłów wartych zapamiętania
Gdy wszystko inne zaniknie, to te pięć punktów należy pamiętać.
Najważniejsze są efekty odwracalne: każda zmiana wprowadzona przez komponent powinna mieć możliwość odwrócenia.
change + inverse
Drugi typ to reaktywne współdziałania. Komponenty deklarują, czego potrzebują, a system w czasie wykonywania reaguje, gdy zależności pojawiają się, znikają lub zmieniają.
dependency available → activate
dependency gone → deactivate
Trzecim elementem jest kontekst. Efekty, zależności oraz dostęp do usług istnieją w ramach jednego, zintegrowanego kontekstu działania.
ctx
Czwartym elementem jest dynamiczny rachunek komponentów, który opisuje komponent poprzez to, czego potrzebuje, co dostarcza oraz co zmienia, zapewniając w czasie wykonywania wystarczającą strukturę do rozumienia procesów ładowania i rozładowywania.
Dependencies + Provisions + Effects
Piątą jest kompozycyjność przestrzenno-czasowa, która łączy aspekty przestrzenne i czasowe.
spatial:
who depends on whom?
+
temporal:
how can components safely enter and leave?
W skrócie chodzi o zmianę odwracalną w połączeniu z zależnościami reaktywnymi.
Dlaczego ma to znaczenie poza pojedynczym projektem
Większość obecnych frameworki agentów nadal zakłada dość statyczną aplikację: uruchom program, załaduj narzędzia, załaduj model, uruchom agenta, zatrzymaj. Autonomiczne agenty dążą do środowisk uruchomieniowych, które mogą się modyfikować podczas działania. Wyobraź sobie agenta, który może postępować zgodnie z taką sekwencją.
discover a capability
install it
use it
upgrade it
replace it
remove it
continue running
W tym momencie zarządzanie cyklem życia pluginu przestaje być przyjemną cechą architektury i staje się podstawową infrastrukturą. To właśnie sprawia, że projekt jest interesujący. Model nie jest najbardziej innowacyjną częścią – to środowisko działania wokół niego jest takie.
Głębsza lekcja
Architektura oprogramowania zazwyczaj dotyczy kompozycji w określonym momencie czasu – łączy się elementy i uruchamia program.
A + B + C
Cordis zadaje trudniejsze pytania.
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?
To całkowicie zmienia charakter problemu. Komponenty przestają być statycznymi blokami i zachowują się jak żywe uczestnicy: przybywają, nawiązują relacje, zmieniają swoje otoczenie, są zależne od innych i w końcu odchodzą.
Główny wniosek
DeepSeek Harness może wyglądać jak kolejna ramowa struktura dla agentów, a jej zaletą nie jest to, że potrafi wywoływać modele językowe lub uruchamiać narzędzia, co potrafią wiele innych ram. Kluczową koncepcją jest to, że sam system kompozycji stanowi architekturę. Każda funkcjonalność to komponent, każda zależność jest jawna, każdy kontrolowalny efekt wie, jak się sam usunąć, komponenty reagują na zmiany w zależnościach, a nawet pętla agenta może zostać zastąpiona – nie jest to niezmienialny kod ramy. Podstawowym założeniem jest uczynienie z każdej funkcjonalności komponentu, każdej zależności czymś jawnym oraz zapewnienie, by każdy efekt miał własną funkcję odwracającą.
W zwykłych aplikacjach jest to uporządkowana architektura wtyczek. W długotrwałych, samomodificujących się agentach sztucznej inteligencji może to mieć znacznie większe znaczenie. Jeśli agenci w końcu zmienią swój własny środowisko działania, oprogramowanie będzie potrzebowało zasadniczej metody zmian bez stopniowego niszczenia środowiska, w którym działa. To właśnie problem, który ma na celu rozwiązanie DeepSeek Harness oraz badania Cordis stojące za nim.