DeepSeek Harness · 編排與子 Agent

Skill、Preset 與自我修改

cordis_define 讓 Agent 在運行時改寫自己的運行時。核心目錄:packages/extensions/tool-cordis/apps/cli/config/agent-presets/

課程目標讀完你能説清三件事:發佈文裏的「標準 / 程式碼 / 極簡 / 創造」四種模式為什麼只是四份 YAML;創造模式裏 Agent 怎麼用 cordis_definecordis_run 在運行時給自己長出新工具,以及這套自我修改的信任邊界畫在哪;skill 分層註冊表裏 preset 層怎麼遮蔽全局層。
互動演示 · 一次自我修改的全程

場景:創造模式(cordis preset)的會話裏,用戶説「給我做一個統計程式碼行數的工具」。左邊是運行時的即時狀態,右邊是 Agent 的動作,跟着字幕走一遍。

0 / 8

插件登記簿(Plugin / Package)

loc · Package v1未定義
loc · Package v2未定義

工具目錄

bashstr_replacesubagentcordis_inspect_querycordis_definecordis_runloc_count

Agent 的動作

cordis_inspect_query → 讀 Service 與 Builtin 的精確簽名
cordis_define kind:"new" → 返回 pluginId=loc / packageId=v1
cordis_run mode:"run" → v1 激活,loc_count 進目錄
工具集變了 → 日誌記錄變更後的完整請求頭
loc_count → 結果把空行也算進去了,要改
cordis_define kind:"existing" → 追加 v2,v1 原樣保留
cordis_run mode:"update" → 切到 v2,失敗可回滾 v1
把插件行寫進 ~/.dsh/.agent-presets/my-mode/ → 第五種模式
點「播放」,看 Agent 怎麼在運行時給自己長出一個新工具。
演示為教學化模擬,流程依據 packages/extensions/tool-cordis/src/index.ts 中 cordis_define 與 cordis_run 的工具描述原文(第 148 至 259 行)整理,核對日期 2026-08-13。
邏輯拆解 · 模式只是一份 YAML

先解發布文那個懸念:標準、程式碼、極簡、創造四種模式,在源碼裏找不到一行模式分支。apps/cli/config/agent-presets/ 下就是四個目錄,每個目錄一份 agent.cordis.yml,一份文件描述一種插件組合,給一個會話掛載。極簡模式全文 62 行:persona 一句「You are a helpful software engineer assistant.」加 complete: true(拒絕任何後續拼裝往提示詞裏加料),工具只有持久 bash 和編輯器,連壓縮都沒有,這是拿來跑基準測試的配置。創造模式則是標準模式原封不動,多掛三樣:自指工具集 tool-cordis、一個教寫組合的 skill、一段教模型分清兩個平面的 persona。

兩個平面是這套體系的座標系。HOST 組合放跨會話共享的東西:持久化、沙箱與審批、模型路由、subagent 註冊表。AGENT PRESET 放一個會話貢獻給這些註冊表的東西:它的工具、persona、提示詞段落。有個細節能看出邊界畫得多細:preset 裏發佈服務的行,要麼歸 host,要麼包進 isolate realm。極簡模式想用不帶沙箱的本地文件系統,就把 fs-local 包在自己的 realm 裏,只遮蔽自己這個會話的 fs,別的會話照舊走沙箱(minimal/agent.cordis.yml 第 46 至 57 行)。

skill 也是分層的。全局層放部署級註冊的(倉庫插件),preset 層放隨 preset 走的,讀取時近層同名直接贏,排序權重只在同一層內起作用。創造模式那個 editing-cordis-compositions skill 就住在 preset 目錄裏,跟着 preset 被複制和編輯,理由寫在 yml 註釋裏:它描述的是這個部署的兩個平面,preset 才是被複制的單位(cordis/agent.cordis.yml 第 248 至 254 行)。設計記錄裏還專門否決過跨層合併排序的方案,遮蔽必須乾脆,不然模型看到兩個同名 skill 會無所適從(.agents/notes/implemented/architecture/2026-08-09-layered-skill-registry.zh.md)。

HOST 組合 · 跨會話共享 持久化 · 沙箱與審批 · 模型路由 · subagent 註冊表 skill 註冊表 · 全局層 部署級 skill,被近層同名條目遮蔽 四份 preset YAML standard · code · minimal · cordis 每份 = 一個會話的插件組合,含自己的 skill 層與 isolate realm 創造模式的自我修改閉環 cordis_inspect_query 讀運行時 cordis_define 只記錄源碼,不執行 cordis_run 激活不可變 Package 失敗:currentPackageId 不動,可回滾 滿意:落盤成新 preset 目錄 發行版 preset 目錄一個字不許改 模式沒有專門程式碼,模式 = 插件組合;自我修改改的也是同一種東西:往組合裏加行、減行
教學化結構圖:節點與連綫用於解釋源碼關係,內容經過課程化整理。
define 不執行

cordis_define 只校驗參數和語法、把源碼記成一個不可變 Package,不申請審批、不執行、不動 currentPackageId。要讓它跑起來,得再調 cordis_run。定義與激活分開,改壞了才有得回滾。

失敗不動指針

cordis_run 只在完全成功後才切 currentPackageId;啓動失敗時舊 current 原地不動。改版本用 kind:"existing" 追加新 Package,舊版本永遠保留,回滾就是 run 一個舊 ID。

邊界是信任

創造模式文件頭原話:「Treat a session on this preset as shell access」。模型寫的 JS 貼着活運行時跑,沒有沙箱兜底,防綫畫在誰能用這個 preset 上,程式碼本身不設圍欄。

關鍵證據 · 文件頭的警告與近層遮蔽

第一段證據是創造模式 YAML 的文件頭。前八行講它是什麼:標準模式加自指工具集。後四行是信任聲明,把「當 shell 權限對待」寫進了配置文件的註釋裏:

apps/cli/config/agent-presets/cordis/agent.cordis.yml第 1 至 12 行
# The `cordis` agent preset: the standard coding agent, plus the ability to
# read and write the runtime it is running in.
#
# It exists so a person can ask an agent to author another agent. Everything in
# `standard` is here unchanged; what is added is the self-referential Cordis
# toolset, a skill that teaches composition authoring, and a persona that says
# which of the two planes an edit belongs to.
#
# TRUST: `cordis_mount` evaluates model-written JavaScript against the live
# runtime, and a composition this agent writes becomes a preset other sessions
# mount. Treat a session on this preset as shell access — the toolset's own
# documentation makes the same statement.
源碼快照説明:依據本地倉庫 deepseek-harness-master,核對文件 apps/cli/config/agent-presets/cordis/agent.cordis.yml,核對日期 2026-08-13。程式碼塊保留源碼原文。

第二條證據是 skill 註冊表的合併讀取,邏輯不貼程式碼也説得清。collectFresh 把所有層排成一列:全局層排最前,preset 作用域鏈按遠祖先在前、本層最後依次跟上。然後按順序把每一層的條目灌進同一個 Map,鍵是 skill 名字。Map 的天性就是後寫的覆蓋先寫的,所以近層同名條目自動替換遠層,遮蔽就是一次 Map.set,沒有任何跨層的權重比較。函式頭上的註釋把這條規則和工具註冊表的遮蔽規則對齊了,還補了一句:排序權重只在同一層內對重名起作用。

出處:packages/skill/skill/src/index.ts 第 552 至 566 行的 collectFresh,核對日期 2026-08-13。

還有兩條機制值得記住,出處寫在這裏。其一,動態插件讓工具集中途變了形狀時,會話日誌會記錄變更後的完整請求頭,維持「模型看到的 ⟺ 日誌裏的」不變數(.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.zh.md)。其二,persona 明令 Agent 絕不許編輯發行版 preset 目錄:升級會整個覆蓋它,而且改壞 cordis preset 等於親手關掉自己所在的模式,要改就複製出去改副本(cordis/agent.cordis.yml 第 27 行)。

橫向對比 · 一份配置描述角色 vs 一整包插件組合角色

Claude Code 定義一個角色,用的是一個帶 frontmatter 的 markdown 文件:寫清 description、可用工具、model,AgentTool 的調用參數還能臨時覆蓋 frontmatter 裏的 model 字段(書稿 study/chapters/05-multi-agent.md 第 32 至 38 行引 AgentTool.tsx 的 inputSchema)。Skills 同理,一個目錄一份 SKILL.md。這條路綫是「配置描述一個角色」:角色能差異化的維度,取決於框架在 frontmatter 裏開放了哪些字段。

DSH 的 preset 是「整包插件組合一個角色」:persona 只是組合裏普通的一行插件,和工具、壓縮策略、subagent 後端平起平坐。極簡模式甚至能換掉整個文件系統實現、關掉壓縮,這種深度的差異化在 frontmatter 字段裏表達不出來。代價也直白:寫一份 preset 要懂兩個平面和 isolate realm,門檻比寫一份 markdown 高得多,所以創造模式才要隨身帶一個教學 skill,再配一個能讀運行時的 cordis_inspect_query。一邊是低門檻的角色卡,一邊是全功率的組合語言,兩家對寫角色的人是誰,想得很不一樣。

課堂練習
01

推演兩個邊界場景

其一:創造模式的 Agent 一時糊塗,直接編輯了發行版的 cordis/agent.cordis.yml 並且改壞了,下一個想用創造模式嘅會話會發生咩事?persona 第 27 行點解將複製出去改副本寫成鐵律?其二:你自己組裝第五種模式時,把兩份 preset 都想發佈的同名服務行都留在了各自文件裏且都沒包 isolate realm,兩個會話同時掛載會點?提示:回看極簡模式是怎麼用 realm 遮蔽 fs 的。

Takeaway:模式 = 一份 YAML 描述的插件組合,自我修改 = 在運行時往這個組合裏加行減行。define 只記錄、run 才激活、失敗不動指針,三條紀律讓「Agent 寫 Agent」有得後悔。安全不靠沙箱靠信任邊界:給誰開創造模式,等於給誰開 shell。