編輯|Panda
昨天,DeepSeek 接連發布了 DeepSeek V4 Pro 正式版模型和 DeepSeek Harness 開發者預覽版,AI 社羣宛如過年,褒獎、評測、爭吵接連不斷論文。截至本文發稿,開源僅一天的 DeepSeek Harness,star 數已接近 9.5 萬,fork 數也達到了 8.8k,並仍在持續上漲。
是的,DeepSeek Harness 非常受歡迎論文。而它受歡迎的一大原因,藏在一個很多人還沒注意到的名字裡:Cordis。
先說 Harness 本身論文。它是一個跑在本地的程式設計智慧體,可簡稱 dsh,讀檔案、跑命令、改程式碼、查資料,差不多就類似於 Claude Code。
真正讓它區別於同類的是設計方式:一切皆外掛論文。
模型接入、工具登錄檔、會話日誌、審批策略,連驅動智慧體運轉的主迴圈本身,全都是外掛論文。
想換搜尋引擎、接自己公司的模型服務,改配置就行,不用動框架程式碼;給系統加功能就是掛一個新外掛,解除安裝時註冊的一切自動撤銷,不留殘餘論文。
支撐這件事的就是 Cordis論文。它是一套以依賴注入和可逆副作用為核心的外掛與上下文核心,來自 Koishi 生態,只負責外掛的載入、解除安裝和依賴關係。dsh 的所有具體元件都是不同外掛,靠服務與事件協作,在配置層自由組合。
這套底座的一大用法,從介面的模式選擇器裡就能看到論文。
四檔模式裡,標準模式功能最全,PTC 模式讓模型生成一段程式碼來組合多輪工具呼叫,極簡模式只留一個終端和一個文字編輯器、系統提示詞只有一句話,適合跑基準測試論文。它們都是在既有的外掛樹上做加減法。
而第四檔「創造模式」不一樣,它是一組自指的 Cordis 工具,是高階入口:選擇這一預設後,Agent 可以檢查當前執行時的外掛樹,並動態掛載或解除安裝臨時外掛論文。模型可以臨時寫一個事件監聽器、註冊一個新工具、提供一個服務,任務完成後再把它卸掉。
展開全文
DeepSeek Harness 預設的四個模式以及使用創造模式構建的 Obsidian 筆記模式
這聽上去有點像讓汽車在高速公路上給自己換髮動機,所以專案沒有預設開啟它,信任等級被標註為等同於 shell 訪問許可權論文。臨時外掛只存在於程序記憶體裡,不寫檔案、不裝包、不改配置,重啟即消失。
自修改式 Agent 的難點之一是寫完之後怎麼把元件乾淨地拿下來:一次自我修改留下的事件監聽器、開啟的連線、註冊進去的服務,如果沒有一條明確的回收路徑,系統執行幾個小時就會變成一堆無人認領的殘留論文。dsh 的做法是把動態外掛仍然放進已有的外掛生命週期裡,跑在 Cordis 的 Context 和 Effect 機制下,註冊項有確定的清理路徑。
這套機制的設計依據,已經寫在 DeepSeek 同步釋出的一篇論文裡:《A Programming Paradigm for Spatiotemporal Composability(時空可組合性的一種程式設計正規化)》論文。作者包括北京大學的 Yifan Shi(同時屬 DeepSeek)、張偉(Wei Zhang),以及 DeepSeek 的崔添翼(Tianyi Cui)。這不是一篇 Agent 論文,甚至幾乎不談模型,它是一篇程式語言理論的論文,八十頁,正文裡有二十多個定理和證明。
論文地址:
軟體工程的基石是組合
論文開篇提出的問題已經頗有歷史:軟體工程的基石是組合,把複雜系統拆成簡單部件再拼起來論文。但傳統的組合是靜態的:函式呼叫、模組匯入、類繼承,編譯期就確定,執行期不再變化,這套東西有極其豐富的形式化基礎。
而現代軟體越來越需要動態組合:元件在執行時被載入、解除安裝、重新配置論文。
論文的判斷是,相比靜態組合,動態組合方向的理論基礎仍然欠缺,工業界的通行做法是繞開它論文。
作者拿 VSCode 做了實證論文。VSCode 的所有擴充套件跑在一個共享程序裡,雖然擴充套件可以動態安裝,但這個 Host 沒有提供任何在執行時解除安裝單個擴充套件程式碼的機制。一旦某個擴充套件的 activate 執行過,停用或解除安裝它就要重啟整個 Host,影響所有已載入的擴充套件。論文統計了安裝量前 100 的擴充套件,其中 87 個包含可執行程式碼,也就是說移除它們都需要一次重啟。
VSCode 確實提供了 deactivate 鉤子,但它只是 Host 程序終止時的優雅關閉回撥,無法用於線上移除;而且這個鉤子把副作用的銷燬和副作用的產生(在 activate 裡)分開了,破壞了關注點的區域性性,完整清理很難被驗證論文。
依賴那一側也很稀薄論文。VSCode 提供了 extensionDependencies 用於宣告擴充套件間依賴,但前 100 個擴充套件裡只有 7 個對非內建擴充套件宣告瞭它。更關鍵的是,擴充套件間互動沒有結構化契約:透過 getExtension(...).exports 拿到的返回值預設是 any,消費方無法依賴一個受檢查的介面。
那為什麼還沒有改?因為作業系統和容器編排提供了一個粗粒度的替代品論文。作業系統在程序粒度上提供時間維度的可組合性,容器編排在服務粒度上提供空間維度的可組合性。模組出問題就重啟程序,服務依賴交給編排器。
論文對這個替代方案提出了具體的批評:每次重啟都會丟棄全部程序本地的累積狀態(快取、連線、部分計算結果),重建它們要花數秒到數分鐘;要在這期間維持可用性就得跑冗餘副本,為了無法恢復單個元件而付出整機的資源開銷論文。空間上,容器級編排無法表達共享同一地址空間的元件之間的依賴,還會給本可以是本地函式呼叫的互動引入網路開銷。兩種機制都工作在程序和容器的邊界上,而現代系統的組合粒度正在不斷變細。
這個粒度錯配,正是自演化 Agent harness 會撞上的牆論文。
論文在動機部分寫道:一個未來的 harness 可能在持續服務請求的同時,生成並部署對自身元件的修改,每一次這樣的修改都是一次動態組合論文。沒有時間可組合性,每次自我修改都要一次全量重啟,在高頻率下累積的不可用時間相當可觀,進行中的任務被反覆打斷;更糟的是,一次有缺陷的自我修改可能讓恢復所需的那個程序本身失效。沒有空間可組合性,每個模組都要自己去檢測所依賴的模組何時出現、消失或換了身份,而且只能用臨時手段;一個天真的程式碼替換策略可能悄悄弄壞依賴方,或者引入只在過載時才暴露的迴圈依賴。
把編譯期的概念搬到執行時
論文的技術路線可以一句話概括:把效應(effect)和餘效應(coeffect)這兩個型別系統裡的經典概念,從編譯期的靜態標註提升為執行時的機制論文。
效應系統描述一個計算對環境做了什麼,餘效應系統描述一個計算需要環境提供什麼論文。
前者的譜系從 Moggi 的單子、Plotkin 與 Power 的代數效應一路到 Koka、Eff 和 OCaml 5;後者從 Uustalu 與 Vene 的餘單子到 Petricek 等人的餘效應統一靜態分析論文。
論文指出,這兩套系統恰好對應動態可組合性的兩個維度,但它們都是靜態工具:效應在詞法固定的作用域內被追蹤、由編譯期的 handler 處理,餘效應標註是對執行前就確定的上下文做驗證論文。而部署之後才載入的外掛,沒有任何固定的詞法作用域能圈住它。
於是論文把這兩個概念具體化(reify)成執行時可以直接操作的物件論文。
可逆副作用處理時間維度論文。一個副作用被建模為 Γ → Γ × (Γ → Γ) 型別的函式:作用於當前上下文,返回修改後的上下文,同時返回一個顯式的逆函式。執行時把這些逆函式不斷累積到一個稱為累加器的複合函式上,解除安裝元件時把累加器整個作用一次,上下文就回到組合之前的狀態。因為逆函式按施加順序前置累積,累加器天然以後進先出的順序回收,與資源獲取的巢狀結構一致。
這裡有兩個設計選擇值得一提論文。
第一,逆函式由呼叫方在施加副作用的那一刻提供,而不是事後回憶,也不是由系統推導論文。這條看似樸素的約束,把它和可逆計算傳統區分開了。Janus 那樣的可逆語言、以及 Heunen 等人用 dagger arrow 建模可逆效應的工作,要求整個計算全域性可逆,可逆性由構造保證;論文只要求每個原子副作用有一個單側逆,且這個逆是被寫出來交給執行時的。執行時不驗證它——論文表示,逆函式確實撤銷了它所伴隨的副作用,這是元件作者的義務,不是執行時檢查的性質。
第二,複合的逆由組合自動得出論文。開發者只為每個原子操作寫逆,任何由這些操作拼起來的複合操作,它的逆自動跟著出來。用論文的說法,一個元件的拆除過程是從它的載入過程派生出來的,而不是與之並列手寫的。這正是 VSCode 的 deactivate 做不到的事:那個鉤子把銷燬和產生分開寫,誰忘了一行,就漏一份資源,而且這種漏沒有任何機制能發現。
論文點名了結構上最接近的對照物:React 的 useEffect論文。它同樣讓副作用返回一個 cleanup,由執行時在重新執行前和解除安裝時呼叫,是少有的把副作用和它的逆結構性配對的設計。它的短板在可組合性:hook 只能寫在元件或另一個 hook 的頂層,永遠不能放進條件、迴圈或巢狀函式,effect body 既不接受 async 函式也不接受迭代器。副作用因此無法由其他副作用裝配而成,也無法與控制流交錯,也就沒有任何東西可以讓複合的逆從中派生。Cordis 的副作用沒有這些限制:它們是可以自由組合、可以非同步執行的普通操作。
反應式餘效應處理空間維度論文。一個元件把自己需要的依賴宣告為一份規格 d,上下文每發生一次變化,系統就對照這份規格把這次變化分類為啟用、去啟用或中性三種之一。依賴沒有就緒時元件不啟用,而不是樂觀地去訪問然後在缺失時報錯;依賴被撤走時,對應的元件被反應式地去啟用。
在這個基礎上論文加了兩個機制論文。
餘效應隔離讓同一個鍵在不同上下文裡解析到不同的值,用於多租戶、測試環境和元件沙箱,它的實現是在鍵和值之間插一層「隔離域」對映,本質上是一套執行時的特設多型論文。
餘效應攔截則給依賴訪問附加橫切後設資料,不改變一個鍵解析到什麼,只改變解析到的東西怎麼被使用論文。論文舉的例子是檔案系統依賴攜帶路徑白名單,或者給社羣元件只讀的資料庫許可權而核心元件保留完整許可權;因為攔截後設資料掛在上下文上而不在雙方任何一方的程式碼裡,編排器可以在不修改提供方也不修改消費方的前提下調整它,而且由於它隻影響呼叫方式、不影響依賴是否被滿足,安裝、重配或移除它都不會觸發任何過載。
最難的半個定理
空間可組合性有兩半,一半容易,一半很難論文。
容易的一半是啟用順序:消費方要在提供方之後啟用論文。這個直接由滿足性前提保證;宣告瞭某個鍵的元件,在沒有元件提供該鍵之前根本無法啟用。
難的一半是撤銷順序:提供方要在它的消費方全部去啟用之後,才能撤回自己提供的東西論文。
論文強調,這一半要交付的東西比「狀態變化的先後順序」更多論文。一個因為提供方要走而正在被拆除的元件,它自己的拆除程式碼可能正需要那個即將消失的依賴:關閉一個連線池通常意味著把連線交還給提供它們的東西。所以需要保證:消費方在自己整個去啟用過程中都還能讀到那個鍵,而提供方對該鍵的撤回只在這之後生效。
樸素的形式化做不到這一點,因為它把「移除提供」和「執行逆函式」放在同一步裡,中間沒有留出任何區間給消費方的拆除程式碼論文。
論文的解法是把這一步拆成兩條規則,中間插入一個 UNLOADING 生命週期狀態:第一條規則只記錄「決定去啟用」這個事實,讓該元件立即停止對外提供餘效應,但保留自己和所有人已經提交的依賴檢視;第二條規則才真正執行累加器,並且帶一個守衛條件:只要還有任何元件把某個鍵解析到它,這一步就不許執行論文。
這類守衛通常會死鎖論文。論文的論證是它不會:一旦第一條規則標記了某個元件,它的表就退出了共享的餘效應上下文,任何目標檢視都不可能再指向它,所以每一個曾經依賴它的元件自身也都在離開的路上。這個論證後來被寫成進展性定理,證明守衛總會釋放。
元理論:動態歷史不留痕跡
有了元件和 fiber(元件的一次例項化)的概念,論文給出一個動態組合演算,十條規則,四個生命週期狀態,還專門處理了三件真實執行時無法迴避的事:一次轉換不是原子的(可以在迭代邊界被打斷並部分回滾)、不是即時的(非同步操作一旦發起就必須落地,論文稱之為慣性)、不是必然成功的(失敗被路由成一次去啟用,元件帶著錯誤結果回到未啟用狀態,不影響它的兄弟元件)論文。
在此之上是五個定理:保持性、全域性時間可組合性、全域性空間可組合性、進展性,以及匯流性論文。
分量最重的是最後一個論文。匯流性說的是,無論一個執行中的系統經歷了怎樣一串啟用與去啟用,它最終靜止時所處的狀態,等同於把同樣的插入與退役指令一次性執行、每個最終處於啟用狀態的元件按依賴順序載入一次且從未解除安裝所得到的狀態。一個編排器增添一個元件、移除它、替換某個提供方、再撤銷這次替換,最終到達的狀態與一開始就寫下最終組合完全一致。
論文把這稱作增量計算裡「與從頭計算保持一致」這條性質在動態組合上的對應物論文。
它的實際含義是:推理一個 Cordis 應用可以當它是靜態裝配的來推理論文。元件作者想知道自己能拿到哪些餘效應,只需要看靜止狀態,不必在腦子裡回放整部載入歷史。dsh 的外掛樹能被一層層疊加的配置(bundle、profile、patch)任意覆蓋而不產生順序依賴的怪象,靠的就是這條。
結語
論文的結論部分,作者自己點了未來驗證方向的名:超越人工策劃的外掛生態論文。其中一個有說服力的方向是自演化的 Agent harness,即 AI Agent 持續地生成並替換自己 harness 的元件,人類監督很少。
把 Cordis 用在這樣的場景裡,可以驗證快速元件替換下完整恢復的時間性保證,以及拓撲頻繁變化下依賴協調的空間性保證論文。
而 dsh 的創造模式就是第一個產品形態論文。它還遠談不上安全無憂。論文中就說了,語言級的訪問控制在惡意元件面前不成立,真正的沙箱需要語言之外的執行邊界;dsh 也把它的信任等級標在 shell 訪問許可權上,並且預設關閉。
但它展示了這套架構真正想抵達的地方:智慧體不只是使用能力,也能在受控邊界內重新組合自己的執行時論文。
更多詳情請參閱原論文論文。