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