marchliu/dsh-cybernetics:把 agent 編排做成控制迴路的 DSH 插件

前言

用 DSH 驅動多步任務時常見的問題是:目標只活在當前 prompt 裏。一次性把需求寫好發下去,agent 做一步算一步,做完之後驗收責任完全落在人身上;結果偏離目標時,只能靠再補一條 prompt 修正,修正完再等一輪——整個系統是開環的,測量和校準都在人這邊。

下面介紹的插件把這套迴路搬進系統本身。marchliu/dsh-cybernetics 由 MarchLiu 維護,分類屬「工作流」,GitHub 上有 11 個 stars、2 個 forks,許可證 MIT,協議名爲 Cybernetics 工作協議。官方定位寫得很直接:這是一個把多 agent 協作顯式設計成控制迴路的 DSH 插件,讓用戶從「逐條 prompt 的作者」變成「目標的設定者與外環的校準者」。協議全文見倉庫內的 docs/protocol.md,演進歷史見 docs/CHANGELOG.md,兩份文檔均爲中文。

它解決什麼問題

插件的命名來自維納的控制論:設定目標(setpoint)、行動、測量輸出與目標的偏差、用偏差修正下一次行動。README 把這套設計映射爲四件事:

  • session goal 作爲 agent 思考的產物持久存在,而不是隻存在於當前 prompt;
  • task agent 行動,verify agent 測量,triage agent 分診;
  • controller 在輪間推進;
  • 用戶通過對話完成外環校準。

目標、行動、測量、校準構成負反饋閉環。當測量結果指出目標本身有問題時,迴路不再自動迭代,而是回到對話。這正是它與傳統一次性 prompt 的分野:傳統做法是開環系統,測量的責任全在人。

核心概念與角色分工

插件內部定義了五類角色。先理解分工,後面講的功能纔有依託:

角色 職責
session agent 控制器:識別用戶輸入、維護 goal 板、啓動/收取/取消輪、向用戶轉述;從不親自執行任務
controller 輪間狀態機,位於 src/controller.ts:startRound/foldRound/runRound,按輪推進 subgoal(task → verify → triage),維護審計日誌
task agent 執行器:無對話歷史的獨立 sub agent,面向工作委託書(brief)實際執行,產出結構化交付報告
verify agent 測量儀器:獨立於執行者驗收,產出結構化 verdict {met, criteria, gaps, goalItselfQuestionable}
triage agent 分診:對 verdict 暴露的後續線索四分揀 auto / defer / reject / human
用戶 外環校準者:對話校準(目標修訂經 session agent 的 revise 執行)、裁定 human 類目、確認完成

兩條硬約束值得先記住。其一,controller 永不自宣完成,完成與否最終由用戶確認。其二,上下文是被配給的、不是被繼承的——task agent 只拿委託書,verify agent 只拿 goal、任務報告與證據,絕不拿 task agent 的敘述,否則驗證就被污染。插件明令永遠不使用 context: 'fork'。

核心功能

goal 是持久狀態,內環自動、外環兜底

goal 是一等公民的持久狀態(objective + 當前 subgoal + 完整工作委託書),構建在 dsh-goal 服務之上,自 v0.2 起不再依賴 goal.md 文件。

內環(task → verify → triage → subgoal 修正)在目標不變時自動迭代,每輪有連續迭代上限作爲兜底。外環(用戶與 session agent)只在目標本身有問題時介入——verdict 一旦給出 goalItselfQuestionable=true,插件禁止自動迭代,強制回到對話。這條邊界是整套協議的安全閥。

append-only goals 與 revise 修訂決策樹

goal 一經存在不再原地修改,revise 是唯一的改目標動詞,且 reason 必填。修訂會創建帶 parentGoalId 的新 goal 並立即啓動其第 1 輪,舊 goal 置 superseded、記錄 supersededBy,每次修訂追加審計行到 .cybernetics/revisions-<新goalId>.jsonl。

v0.7 起,abortPrevious 缺省時按自動決策樹裁定(純函數 decideAbort,依據 goal 板的 running 狀態):舊輪在跑且修訂改寫目標本身(objective 或 depth 變化)→ 自動中止舊輪;舊輪在跑但目標不變(僅 brief/title/role 修正)→ 等待舊輪自然結束;舊 task agent 已結束 → 直接啓動新 goal。顯式傳 abortPrevious: true/false 則保持人工控制,每次修訂的審計行與 revise 返回值都攜帶 abort: {abort, mode: auto|explicit, basis}。

被取代的 goal 再次 revise 會被拒絕(already superseded),以免譜系分叉。

quick / full 雙檔輪次

  • quick:單 task agent 一輪出結果直接反饋,跳過 verify/triage,成本約爲 full 的三分之一,verdict 由任務報告派生。
  • full(默認):完整走「工作 → 驗證 → 分診」循環,驗收通過才交付。協議明令禁止用 quick 抄近路做變更。

allGoalsMode:任何請求先建 goal 再執行(默認 false)

開啓後注入「ALL-GOALS MODE ACTIVE」路由協議,四條規則:

  1. 任何用戶請求(問題、檢索、分析、改動)一律先建 goal(start + title/brief)再執行;session agent 禁止用讀工具就地完成任務,哪怕單文件查詢或一次計算也走迴路。
  2. 簡單隻讀請求 → depth quick:有界查詢、單文件讀取、小型統計,單 task agent 一輪。
  3. 複雜分析/寫任務 → depth full 多輪漸進:start 異步啓動,逐輪 collect 收斂,controller 按輪推進 subgoal;寫任務額外依賴硬門——session agent 永不親自改動,goal 迴路是唯一路徑,改動再小也一樣。

關閉該模式時,提示段與 revise 行爲與 v0.6 完全一致。配置入口在 v0.7.1 之後:插件導出 schemastery Config schema,allGoalsMode 爲 volatile 字段,在 DSH Web 編輯器 Settings → Plugins 的本插件條目面板上直接切換,保存後 loader 提交新值進運行實例(不 remount),提示段即時重掛,無需再手改 profile JSON。

硬門 hardGate:默認 allowlist 的結構性門禁

hardGate 默認 true,安裝即激活,v0.8.10 起在插件頁設置卡片上可熱切換。hardGate 是 volatile 字段:保存開關即把新值提交進運行實例,插件隨即卸載或重裝門禁監聽(裝配摘工具 + 分發拒絕 + 審批透傳),即時生效,無需重啓宿主、無需插件重載。

生效語義(v0.8.6,默認安全):hardGate 開啓且未配置 gateMode 時,門按 allowlist 運行。頂層 session agent 只保留讀 + 對話 + 展示 + goal/控制動詞(內置 DEFAULT_GATE_ALLOWLIST,可經 gateAllowlist、gateAllowlistExtra 擴展),所有變更類工具在裝配期被移除,並在調用分發層被拒絕。

之所以改成這個默認,是因爲舊的 denylist 默認是個陷阱:它只隱藏 gatedTools 的三個名字(bash/edit/write),bsk_*、taskboard_*、schedule_*、farm_*、uno_*、ppt_*、config_restore 以及整個變更工具長尾都仍可調用——名爲硬門、實爲半門。schema 不再給 gateMode 設默認值,因此「未設置」與「顯式 denylist」可以被區分。hardGate: false 則與以前完全一致,不裝門。

顯式配置 gateMode: 'denylist' 屬殘留風險、需主動選擇:只移除 gatedTools(默認 bash/edit/write)中的工具名,頂層 agent 至少仍可使用 bsk_*(含 bsk_session 啓停)、taskboard_* 寫操作(create/update/move/comment/checklist/delete)、schedule_* 寫操作、farm_* 服務生命週期(start/restart/stop/register/unregister)、uno_* 與 ppt_* 生成器、config_restore、subagent/team 派生、knowledge_save、job_kill。這個集合隨每個新裝插件增長,denylist 永遠不可能完備,只有在清醒接受該風險時才顯式選擇,並把需要的變更工具逐項補進 gatedTools。該模式下注入的 HARD GATE 提示會如實說明「只摘 gatedTools」的半門立場,不再假稱所有改世界的工具都結構性不可用。

task/verify/triage 子 agent 從不在門禁範圍內,保留全部工具。部署注意:本構建首次上線時運行中宿主仍持有 pre-0.8.10 代碼,需重啓宿主一次加載新代碼;此後開關即熱生效(lib/client.js 只需刷新瀏覽器)。

子代理審批透傳 gateApprovalRelay(默認 true)

宿主在委派邊界把每個子代理(workflow 子代理、teammate)的審批策略釘死爲 never——每次審批請求在任何 answerer 看到之前就被確定性拒絕。結果是 task agent 需要沙箱越權(典型:寫會話工作區之外的路徑,如 ~/.dsh/profiles/web/cordis.patch.yml)時,用戶根本沒機會被詢問。

gateApprovalRelay 開啓時(默認,hardGate 伴隨項),插件做兩件事:

  1. 創建時解除子代理策略釘死:監聽 agent/created,對委派子代理在其 session 上追加一條持久 approval/policy {policy:'ask'} 事件。宿主的摺疊規則保證後寫事件覆蓋委派時的釘死值;沒有這一步,審批服務會在任何 answerer 運行之前把請求確定性拒絕成 rejected。
  2. 在頂層 session agent 上重新發起請求:插件把 approval/request answerer 註冊爲最外層(prepend: true)。宿主在啓動時(早於所有用戶插件)註冊的 approval/request 轉發器會被瀏覽器審批 answerer 搶先 claim,子會話面板用戶看不見,轉發器永遠輪不到執行;插件搶在最外層運行後,沿持久 parentSession 鏈找到子代理的頂層活祖先,直接調用宿主的 ApprovalService.request,讓原生允許一次/拒絕面板出現在用戶正在看的那個會話裏,並在該 session 的 approval/asked + approval/decided 審計對中留痕。子代理的 tool call id 不向上轉發,面板標題(本地化 displayReason)會寫明是哪個子代理發的請求。

回退與終態語義(v0.8.8):若頂層回合已關閉(continuable 子代理比父回合活得久),或原生面板應答 unavailable(沒有應答端 claim 該請求,例如 frame 根本沒被瀏覽器認領),relay 繼續回退到同一頂層 agent 上可見的 ask_user_question 卡片(「子代理審批透傳」)——原生 unavailable 不是終態。僅當問題卡片通道也不可用時才以終態 unavailable 結束;完全沒有活的頂層 agent 則回落原生應答鏈。拒絕語義始終顯式:轉發失敗答 rejected、子代理已中止答 cancelled,子代理模型總能經自己的審批審計對拿到真實結果,不會靜默成功。

可觀測性與排障(v0.8.9):relay 每次決策記錄兩處——宿主 logger 一行 cybernetics approval-relay: path=… outcome=… root=… elapsed=…ms(無 logger 時 console.debug 兜底),以及 <workspace>/.cybernetics/relay-log.jsonl 追加一條 JSON({ts, root, path, outcome, elapsedMs})。該文件是驗收時的落盤事實,無需宿主 stdout 或瀏覽器 console 即可直接讀取。path 取值:native-panel、question-card、next-passthrough(無活頂層 agent → 原生鏈)。典型排障:子代理快速失敗且審計爲 decided unavailable,但 relay-log.jsonl 一條記錄都沒有,說明請求根本沒到 relay,先確認宿主確實重載了插件構建;若出現 path=native-panel outcome=unavailable 後跟 path=question-card 兩條,說明 frame 離開了宿主但瀏覽器沒有認領——此時用戶仍能看到問題卡片。

設 gateApprovalRelay: false 可回到嚴格確定性拒絕的立場(CI/無人值守部署)。這個字段是 hardGate 伴隨項,屬非 volatile / 配置文件維護字段,請在 profile 配置文檔中維護,保存觸發插件重載。宿主側證據(文件:行號)、被否決的方案與復現方式記錄在 docs/approval-relay-investigation.md。

持久執行者 executor

executor: 'ephemeral' | 'persistent',與 depth 正交。persistent 用 Agent Teams 的持久 teammate 執行 task 工作:首輪 spawnTeammate(context:'fresh') 投生,後續輪只投遞委託書修訂(新增約束/驗收標準變化/範圍收窄,禁止開放式對話)。full 輪中 teammate 報告直接作爲 task 報告,task agent 被跳過,verify/triage 仍以臨時 agent 獨立運行——驗證不繼承任務敘述。quick 的 verdict 由 teammate 輸出派發(輸出契約要求最後一行 {"done": bool, "summary": "...", "evidence": "..."},缺失標記即視爲未完成)。

這是軟集成:運行時優先用 Cordis 的無注入反射讀 ctx.reflect.get('agentTeams')(不需要 inject,返回服務值或 undefined),再回退 try/catch 安全訪問器讀 ctx.agentTeams(Cordis 的 context 代理在讀取未注入的服務屬性時會直接拋錯,不能當成「服務不存在」)。兩路都取不到就回退 workflow,不硬綁實驗包,報錯是結構化的 needs Agent Teams … fall back to executor "ephemeral",不是 Cordis 代理異常。goal 完成/cancel 時會 interrupt 並清理常駐隊員,每 goal 至多一個執行者,goal 板保持唯一真相、不雙寫 team 的 task 板。

多 goal 板與客戶端頁籤

同一 session 支持多個併發 goal(插件自有 goal 板;dsh-goal 的單槽降級爲焦點 goal)。goal 板是進程內存態,每個寫點(goal 創建、輪次啓動、結算決策、cancel)同時把整板快照到 <workspace>/.cybernetics/board-<sessionId>.json;首次觸碰空板時從快照 rehydrate,恢復後立即 publishBoard() 一次,頁籤不用等下一輪就顯示歷史 goal。sidecar 文件寫盤失敗只吞掉(try/catch),只讀工作區照樣能跑輪次;快照是當前狀態、不是事件日誌,事件日誌仍在 session 裏。中途被殺死的輪會以 status: 'running' 恢復但無運行句柄,下一次 round/start 會正常重新啓動。

客戶端 lib/client.js 提供兩個會話頁籤,經插件自有 session 投影 cyberneticsGoals 實時推送、就地更新:

  • 「目標 / Goal」:本會話全部 goal(標題、狀態徽標、第 N 輪、當前 subgoal、depth、executor 小標籤、被取代與修訂來源標註),按「未完成在前、已完成在後」兩段排序,空板回退 dsh-goal 單 goal 渲染。
  • 「控制 / Control」:遞歸展示本會話全部 agent(主控 + subagent catalog)的運行狀態與各自 goal 視圖,頂部彙總運行中數量,並列出每個 goal 的執行者行(狀態 + 標題 + executor 臨時/持久 + 持久 teammate 名稱 + 輪次),與「目標」頁籤同一數據源。

兩個頁籤都沒有第二套狀態,數據源就是同一份 cyberneticsGoals 整值投影。v0.10.0 起,每個會話頭部常駐一枚徽標:綠點加「開」表示當前控制模式生效中(本會話 /cybernetics on 顯式 override,或繼承全局 allGoalsMode),灰點加「關」表示生效關閉。

安裝與啓用

插件發佈在社區目錄 SkillHub 與 GitHub 上。安裝命令請從社區目錄頁獲取,不要憑倉庫名拼接:

  • 社區目錄頁:https://www.skillhub.cn/plugins/MarchLiu/dsh-cybernetics
  • GitHub 倉庫:https://github.com/MarchLiu/dsh-cybernetics

需要先說明的是,社區目錄是獨立站點,與 DeepSeek / 幻方無官方從屬關係,不要把安裝來源當成官方應用商店。包信息(來自倉庫 package.json):包名 @mars.liu/dsh-cybernetics,版本 0.15.0,許可證 MIT,type: module,兩份 README 的徽章版本爲 0.10.0。peer dependency 爲 @deepseek-ai/dsh-session,運行時依賴 @deepseek-ai/schemastery ^3.18.5;engines.node 要求 >=23.6。

啓用後的行爲:

  • hardGate 默認開啓,無需額外配置即拿到門禁;gateMode/gatedTools/gateAllowlist/gateAllowlistExtra/gateApprovalRelay/executor/depth/axiom* 等字段屬非 volatile,在 profile 配置文檔中維護,保存觸發插件重載。
  • allGoalsMode 默認關閉,需開啓全量目標路由時在 Settings → Plugins 面板切換。
  • 首次安裝本構建時,宿主若仍持有舊代碼,需重啓宿主一次加載新代碼,之後開關即熱生效。
  • 倉庫根目錄提供 cordis.patch.yml 作爲 bundle patch,lib/client.js 側依賴若干 @deepseek-ai/dsh-client-* 注入包,加載前確認宿主的插件機制已支持 bundle patch 與 client inject。

典型用法

插件的工具面集中在 goal 生命週期,cybernetics 是核心動詞。會話內走一輪的路徑如下:

  1. 發起目標。由 session agent 創建 goal(給出 title/brief/objective),或在 allGoalsMode 開啓後由任意用戶請求自動觸發。goal 板隨即出現一行 running 記錄。
  2. 觀察輪次推進。插件以每輪一次 workflow 運行的方式驅動內環:task agent 讀委託書執行併產出結構化交付報告,verify agent 產出 verdict,triage agent 把線索四分揀爲 auto / defer / reject / human;auto 類目自行迭代,human 類目回到對話等待用戶裁定。
  3. 用 revise 修訂目標。調用形如 cybernetics { action:'revise', goalId, reason, objective?, title?, depth?, executor?, abortPrevious? }——新 goal 繼承舊 goal 的 depth/executor/role/objective(可被參數覆蓋),寫入 parentGoalId,立即啓動第 1 輪;舊 goal 置 superseded。
  4. 在 full 寫任務下驗證門禁與審批透傳。session agent 會委派 task agent;若子代理請求越權寫入會話工作區之外,gateApprovalRelay 會把允許一次/拒絕面板帶到當前會話,決策後可在 <workspace>/.cybernetics/relay-log.jsonl 覈對實際路徑。若子代理請求被靜默拒絕,檢查本會話是否確實啓用了 gateApprovalRelay,並確認宿主已重載插件構建。
  5. 在客戶端確認完成。在「目標 / Goal」或「控制 / Control」頁籤查看本輪 verdict 與執行者行。controller 不會自宣完成,需由用戶確認。

適用場景與注意事項

適合這幾類實踐者:

  • 需要在多步任務裏把「測量」這一步交給系統的人,而不是每次手工驗收。迭代型分析、多輪寫任務尤其受益。
  • 希望有明確安全邊界的團隊。默認 allowlist 的結構性門禁,比只隱藏幾個工具名的 denylist 更接近其聲稱的強度。
  • 需要同時跟蹤多個 goal、並分別修訂的場景。多 goal 板與兩個頁籤正是爲此設計。

三條注意事項:

  1. 權限範圍。插件以當前 dsh 進程權限運行,安裝前應閱讀源碼與 LICENSE(倉庫內 LICENSE 爲 MIT),並覈對 package.json 聲明的依賴與 peer dependencies;社區目錄頁只作發現入口,不等於官方背書。
  2. 門禁語義不要混淆。「顯式配置 gateMode: 'denylist'」是殘留風險、需主動選擇,只摘除 gatedTools 名單內的工具;需要結構性隔離時保持 gateMode 未設置,走默認 allowlist。
  3. 狀態與審計的邊界。goal 板是進程內存態,靠 sidecar 快照跨進程恢復,快照存的是當前狀態;審計與事件日誌在 session 內,relay-log.jsonl、revisions-<goalId>.jsonl 是落盤證據,用途不同,不要混用。

小結

一句話回顧:marchliu/dsh-cybernetics 用控制論的語言重構了 DSH 裏的 agent 編排——目標持久化、行動與測量分離、分診決定下一步、用戶只校準外環,配合默認開啓的結構門禁和審批透傳,把一個容易跑偏且無人驗收的開環流程,變成有測量、有兜底、有人工確認的負反饋迴路。

更多細節可查閱協議全文 docs/protocol.md 與演進歷史 docs/CHANGELOG.md:

  • 社區目錄頁:https://www.skillhub.cn/plugins/MarchLiu/dsh-cybernetics
  • GitHub:https://github.com/MarchLiu/dsh-cybernetics
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜