前言¶
寫 DSH profile 的人遲早會遇到一個具體問題:父智能體要把一件活交給子智能體,而子智能體跑在一個獨立的 CLI 進程裏,不是進程內的隊友,也不是可以繼續對話的本地孩子。這時用通用委派工具得到的只有一句 started subagent <id>,模型不知道它已經開跑、也等不到任何進度,只能反覆去問 wait_agent / list_agents,而這兩個工具只跟蹤進程內隊友和可繼續的本地孩子,對進程外一次性子智能體永遠回答 “no subagent running”。
下面介紹的 lloydzhou/dsh-subagent-codebuddy 就是針對這個場景的 profile bundle:它把每次委派落在本地 CodeBuddy CLI(codebuddy --acp)裏,模型通過配置釘住,並在工具描述與結果文案上明確告訴模型「子智能體已經在跑、你不要等它」。
這是什麼¶
一句話定位:一個獨立的 DeepSeek Harness profile bundle,提供一個一次性(one-shot)子智能體 provider,每次子任務在本地 CodeBuddy CLI 中以 ACP 會話運行,子模型由配置釘住(默認 deepseek-v4.1-flash,以 --model <id> 傳入)。
- 維護者:lloydzhou
- 包名:
dsh-subagent-codebuddy,npm 上同名發佈 - 版本:0.2.1-rc.5(見 package.json)
- 許可證:MIT
- 語言:TypeScript
- 分類:工作流
- 來源歸屬:社區目錄頁在 skillhub.cn,屬獨立社區站點;插件本身是開源倉庫。目錄站與 DeepSeek / 幻方沒有官方從屬關係。
- 星標:0(截至寫作時,來自 GitHub 倉庫頁)
它解決的是三個連在一起的問題:委派要能真正發起、結果要能在子智能體 settle 時回到父會話、模型不要因爲一句含糊的結果去輪詢一個永遠查不到的運行。
運行時行爲:一次性委派與結果回傳¶
provider 寫在 harness 暴露的公開 subagent 接口上,具體是 @deepseek-ai/dsh-subagent 裏的 NO_START_CAPABILITIES、settleRunResult、subprocessRunHandle 三個 helper。它不帶 prepareContinuable,所以兩側都沒有 inbox:CodeBuddy 子智能體是獨立 CLI 進程,沒有 dsh 工具,無法回調父會話。
結果回傳方式因此很有限:子智能體只在 settle 時報告一次,harness 注入一條 Background subagent <id> finished,帶着子智能體的收尾消息,這條 notice 會啓動父智能體的下一輪。運行中途沒有進度通道——README 強調這是後端特性,不是插件缺陷。
作爲對照,README 解釋了 Team message from <child>: … 這類 notice 屬於可繼續的本地子智能體,它們帶 dsh 的 send_message 工具和父會話收件箱。CodeBuddy 子智能體不屬於這一類,因此不會出現這種中途消息。
於是工具要做的事變成:阻止父智能體在原地空等。它的結果文案說明輪詢無效、父智能體應繼續做別的事或結束本輪,讓 settle notice 能在最早的 step 邊界到達。
模型側的兩個工具¶
bundle 掛載的兩個工具都來自這個包自己的 ./tool 入口,而不是通用的 @deepseek-ai/dsh-tool-subagent:
subagent_codebuddy:啓動一次運行codebuddy_steer({ childId, message }):給仍在運行的一次運行追加消息
兩個工具走的是同一條運行時路徑 ctx.subagents.startActivation({ delivery: 'parent' });差別在文案。通用工具的 started subagent <id> 沒有措辭旋鈕,模型沒有理由停止尋找這次運行;這個包的描述和結果明確說子智能體已經在跑、沒有任何工具能觀測它、結果會自動到達。
codebuddy_steer 包的是 CLI 的 ACP steer 擴展,這是運行中唯一能進消息的通道。README 給了一個對照表:
second session/prompt → CLI 當作競爭輪次,約 33ms 結束第一輪,輸出爲空
--input-format stream-json → 啓動的是另一個 session,正在運行的那個看不到
session/steer → {steered: true},消息在子智能體下一個 step 邊界投遞,
正在進行的工具調用不被中斷
childId 是啓動 notice 裏那個 id。provider 在 session/new 返回時於 src/steer.ts 發佈 live handle,運行 settle 時撤銷,所以已經結束的 id 會被拒絕,工具改爲列出仍然 live 的 id,而不是猜。子智能體返回 {steered:false} 時按拒絕上報並附原因,不重試,常見原因是子智能體正處在兩輪之間。
聊天卡片展示什麼¶
每次委派在 transcript 中生成一張卡片(conversation.chat.node,kind 爲 codebuddy-run),錨定在自己的 tool/call 事件上。卡片摺疊三樣東西:交接結果、settle notice、以及中途發進去的消息。外部子智能體只產生這三個時刻,所以卡片能誠實表達的範圍也就是這些:
| 狀態 | 觸發來源 | 卡片展示 |
|---|---|---|
| 運行中 | tool/call |
計時從調用開始 |
| 後臺運行中 | tool/result(activation notice) |
計時繼續;正文點名運行時與日誌,隨子智能體動作填充 |
| 已完成 | settle finished |
收尾語句,附在任務與時間線之下,CLI 的 cost 行提到頭部 |
| 已停止 | settle was stopped |
完整保留該 notice |
| 被拒絕 | settle declined the task |
同上 |
| 失敗 | settle failed before it finished / ended abnormally,或運行拋錯 |
診斷信息,外加所有 - [done] … 步驟 |
原則是「一次運行一張卡片」。codebuddy_steer 調用在 transcript 裏也是普通工具調用,同樣匹配 /codebuddy/,若只看名字會另開一張永遠沒人 settle 的卡片,停在「後臺運行中」挨着它只是發消息的那次運行。所以定義按調用做了什麼來判:arguments 裏帶 childId 的是「對某次運行說話」而不是「啓動一次運行」,會折進啓動那個孩子的卡片(正文裏顯示爲 ↪ 插話 …),若當前頁面從未見過那張卡片則折入空。它自己的結果在任何情況下都被忽略——運行狀態只在子智能體 settle 時改變。普通工具行照常上報這次調用。
卡片正文是記錄而非替換:按運行發生的順序讀——孩子拿到的任務(完整 prompt,頭部只放調用方的 3–5 詞標籤,正文摺疊四行、本地可展開)、中途消息、孩子做過的事,最後纔是它怎麼結束。settle 是追加收尾塊,不覆蓋已有內容。爲了讓重掛載(重連、從 session log 重新渲染 transcript)後仍成立,src/client/live.ts 裏有一個按運行的小緩存:輪詢在 settle 後停止,沒有它,已完成的卡片重新渲染時歷史會丟失。
兩條需要直說的後果:
- 計時衡量的是等待,不是生命。兩個事件之間卡片從 transcript 什麼也聽不到,瀏覽器讀不到運行的 beacon;所以頭部只計數並明說這一點,hover 文本指向
codebuddy-runs,那裏確實能回答這個問題。卡片真正能看到的子智能體動作,取決於宿主是否應答(見下一節)。 - 隨宿主一起死掉的子智能體會永遠停在「後臺運行中」,因爲沒有事件可摺疊,
codebuddy-runs稱之爲失聯。宿主活着而孩子死掉是另一種情況:ACP 管道關閉,運行以errorsettle,卡片轉「失敗」,該場景由tests/mock-acp-child.mjs的crash固定。
實時進度:transcript 之外唯一的通道¶
只摺疊 transcript 事件的卡片,在一次十分鐘的運行裏只能展示兩個時刻。子智能體的動作根本不在那份 transcript 裏——它們是 ACP session/update 通知,到達宿主進程,provider 本來已把它們鏡像進運行日誌。src/progress.ts 每個運行在內存裏保留最近 60 條,鍵是運行時採用的 run id(即 child id),src/progress-route.ts 在宿主自己的監聽上提供服務:
GET /subagent-codebuddy/progress?ids=<childId>[,<childId>…]
→ { runs: { <childId>: { steps, updatedAt, endedAt? } } }
卡片在運行未結束時輪詢這個路徑(src/client/live.ts,每 1.5s),在 settle 或連續三次失敗後停止。README 列了四個設計決定,其中一條是使用相對 URL 而非端口,因爲 web UI 由同一個 listener 提供服務。卡片能看到的動作行隨宿主應答而出現,最後動作 12s 前 會隨時間變舊,摺疊頭部的 N 步 計數會變化;沒有這個 feed 時退回純計時。
本包還帶一個 codebuddy-runs 命令行入口(package.json 的 bin),供人工查看運行狀態。
安裝與啓用¶
先說清楚一件事:我手上的 README 沒有給出一條可以直接複製的一鍵安裝命令,因此這裏不替它拼一條 dsh plugin add ...。從 package.json 看,這個包聲明瞭 bundle 結構:
{
"dsh": {
"bundle": { "patch": "./cordis.patch.yml" },
"client": {
"inject": ["@deepseek-ai/dsh-client-ui-conversation"],
"platform": "web"
}
},
"engines": { "node": ">=20", "dsh": ">=0.2.1-rc.3" },
"bin": { "codebuddy-runs": "./bin/codebuddy-runs.mjs" },
"license": "MIT"
}
也就是:安裝這個包之後,harness 讀取 dsh.bundle.patch 指向的 cordis.patch.yml,在其中插入兩行——provider 行與掛載本包 ./tool 入口的 tool 行。cordis.patch.yml 的原文如下:
- insert:
- id: subagent-codebuddy
name: 'dsh-subagent-codebuddy'
config:
permission: allow
startupTimeoutMs: 60000
envFile: ~/.dsh/secrets/codebuddy-api-key
env:
CODEBUDDY_INTERNET_ENVIRONMENT: internal
- id: tool-subagent-codebuddy
name: 'dsh-subagent-codebuddy/tool'
config:
provider: codebuddy
toolName: subagent_codebuddy
maxDepth: provider-managed
幾處配置的意思需要講明,它們都來自補丁原文的註釋:
1、permission: allow 讓子智能體的工具提示自動批准,真實任務才能動手;需要嚴格只讀的孩子時改成 reject。
2、envFile: ~/.dsh/secrets/codebuddy-api-key 指定承載 API key 的文件;運行消耗的是登錄賬號的訂閱額度,或這個 envFile 提供的 key。
3、CODEBUDDY_INTERNET_ENVIRONMENT: internal 顯式指定驗證 key 時使用的 CodeBuddy 站點。國內站(copilot.tencent.com)與國際站(codebuddy.ai)不互相接受對方的 key,而 CLI 按機器上緩存的產品配置選站;從未裝過桌面端的機器會退回國際站,導致一個完全有效的 key 報 401。這裏顯式寫名字就繞開了這個依賴。
4、maxDepth: provider-managed 是必填項,且必須顯式寫這個值。省略 key 時 harness 會退回自己的數字 depth 默認值,而 CodeBuddy provider 沒有 advertise 出 depthLimit capability,無法強制,該行會直接激活失敗並報 provider "codebuddy" cannot enforce maxDepth ...。對進程外子智能體來說 provider-managed 是唯一合法值。
啓用前需要滿足的對端依賴(來自 peerDependencies):@deepseek-ai/cordis ~4.0.5,以及 @deepseek-ai/dsh-session、@deepseek-ai/dsh-subagent、@deepseek-ai/dsh-subprocess、@deepseek-ai/dsh-timeout 均爲 ^0.2.1-rc.3;Node 要求 >=20。
還有一個位置約束寫在 cordis.patch.yml 註釋裏:兩行都必須放在 bundle 頂層。如果把 tool 掛進某個 agent preset(例如 preset-cordis),它就被綁死在那個 preset 上,任何不帶這個確切 preset 行的 profile 都無法掛載該工具,provider 也就沒有讓模型觸達它的路徑。
適用場景與注意點¶
適合誰:已經在 DSH 上寫 profile、希望把子任務放進本地 CodeBuddy CLI 的開發者;需要進程外一次性委派,且能接受「沒有中途進度、結果只在 settle 時回來一次」這一後端特性的人。如果需要子智能體中途雙向收發消息,這個插件不滿足,它只提供從父到子的單向 steer。
需要注意的幾點:
- 插件以當前 DSH 進程的權限運行,會讀取
envFile指向的密鑰文件、會拉起本地codebuddy --acp子進程。安裝前先讀源碼與 LICENSE(MIT),確認這個權限面你能接受。 - 開支走 CodeBuddy 賬號的訂閱額度或
envFile提供的 key,不是 dsh 側的額度。 - 沒有中途進度通道,參數上不要指望模型在等待期間看到什麼。工具已經通過文案盡力阻止父智能體空等,但 settle 之前父會話拿不到子智能體的動作,除非宿主提供上一節的 progress feed。
- 子智能體隨宿主一起掛掉時會永久停在「後臺運行中」,因爲沒有事件可摺疊;這是設計邊界,不是可修的 bug。
- 星標爲 0,倉庫近幾天內創建,生態層面屬於很新的小項目。採用前值得自行檢查一次倉庫活躍度與依賴版本。
- 社區目錄站是獨立站點,插件不是 DeepSeek / 幻方的官方發佈。
結尾¶
這個包的價值不在於做了一件別人做不到的事,而在於把一個邊界情況說清楚:進程外的一次性子智能體沒有 mid-run 進度,工具文案、steer 通道、聊天卡片、progress feed 這四層都圍繞「讓父智能體不空等、讓結果誠實地到達」這個目標設計。如果你的 profile 正需要把委派發給本地 CodeBuddy CLI,它是一個可以直接放進 bundle 的實現。
- 目錄頁:https://www.skillhub.cn/plugins/lloydzhou/dsh-subagent-codebuddy
- GitHub:https://github.com/lloydzhou/dsh-subagent-codebuddy