dsh Plugin Development
為 DeepSeek Harness
(dsh) 開發插件,這是一個基於 Cordis 的代理框架。插件是 TypeScript 模組,匯出
apply(ctx: Context),並透過 cordis.yml 進行組合。
快速參考
Plugin 結構
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin'
export function apply(ctx: Context) { /* 註冊能力 */ }
三種形式:函式(預設)、物件({ name, inject, apply })、類別
(extends Service — 僅在為其他 plugin 提供服務時使用)。
依賴:export const inject = ['tools', 'llm'] — 框架會保持 plugin 狀態為
PENDING,直到所有注入的服務都已就緒。
自動清理:所有透過 ctx 註冊的內容都是 effect — 卸載時會自動清理。對於自訂資源(計時器、連線),請包裝在
ctx.effect(() => { ... return () => cleanup }) 中。
子 Plugin:const fiber = ctx.plugin(childPlugin) — 子 plugin 繼承父 context;
await fiber.dispose() 會遞迴卸載。
Fiber 狀態
PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
↘ FAILED
PENDING → 缺少服務依賴。FAILED → apply 拋出錯誤或設定驗證失敗。服務在執行期間消失會自動卸載依賴的 plugin。
工具 (Tools)
import { defineTool } from '@deepseek-ai/dsh-tools'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet', description: '...',
parameters: { name: { type: 'string', required: true } },
output: { schema: { type: 'string' }, render: (_args, value) => [{ type: 'text', text: value }] },
async execute(args) { return `Hello, ${args.name}!` },
}))
}
defineTool 會將參數轉換為 JSON Schema,驗證參數,並在卸載時自動取消註冊。
詳見 references/basic/tool.md。
設定 (Configuration)
import Schema from '@deepseek-ai/schemastery'
export interface Config { greeting: string; maxRetries: number }
export const Config: Schema<Config> = Schema.object({
greeting: Schema.string().default('Hello'),
maxRetries: Schema.number().default(3),
})
export function apply(ctx: Context, config: Config) { /* 已驗證的設定 */ }
必須同時匯出 interface Config 和 const Config: Schema<Config>。
無效的設定會導致 FAILED 狀態並顯示清楚的錯誤訊息。在 YAML 中使用 !!js 來表示動態
值:config: { apiKey: !!js process.env.API_KEY }。
詳見 references/basic/config.md。
服務 (Services)
提供:class extends Service { constructor(ctx) { super(ctx, 'name') } }
declare module '@deepseek-ai/cordis' { interface Context { name: NameService } }
消費:export const inject = ['name'] → ctx.name.method()。
可選:ctx.get('name') → 當不存在時回傳 undefined。
服務隔離:group: true + isolate: { shell: true } 會為每個群組提供自己的 provider 實例。詳見 references/framework/service.md。
事件 (Events)
| 模式 | 呼叫方式 | 用途 |
|---|---|---|
emit | ctx.emit(n, ...args) | 同步廣播,忽略回傳值 |
parallel | await ctx.parallel(n, ...args) | 並行監聽器 |
serial | await ctx.serial(n, ...args) | 順序執行,第一個非 null 的值獲勝 |
bail | ctx.bail(n, ...args) | 同步順序 |
waterfall | ctx.waterfall(n, ...args, next) | 中介軟體鏈 |
Waterfall 監聽器必須呼叫 next(),除非有意提前短路。
關鍵 harness 事件:agent/step、tools/result、session/event。
詳見 references/framework/events.md。
LLM 轉接器 (Adapters)
class MyAdapter extends LlmAdapter {
async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
// 轉換 → provider API → yield StreamChunk
}
}
ctx.llm.registerAdapter(['my-provider'], adapter)
每個區塊的 StreamChunk 順序:block-start → text-delta/tool-call-delta →
block-end → usage → finish。拋出帶有穩定錯誤碼的 LlmError。
詳見 references/practice/llm-adapter.md。
三層能力設計
定義(Service 抽象類別)→ 提供者(實作)→ 消費者(工具)
三者都透過 inject 共用相同的服務名稱。僅在角色需要獨立演化時才拆分為獨立套件。
詳見 references/practice/index.md。
cordis.yml / 套件 (Bundles)
本地開發:--patch ./cordis.patch.yml
- insert:
- id: my-plugin
name: '/absolute/path/to/src/my-plugin.ts'
config: { greeting: 'Hi' }
發布:包含 dsh.bundle 清單 + cordis.patch.yml 的 npm 套件。
安裝:dsh plugin --profile demo add ./hello-plugin。
層級順序:profile bundles → profile patch → home patch → --patch 覆蓋層。
後面的層級按行覆蓋(沒有深度合併)。
詳見 references/basic/publish.md。
HMR
載入 @deepseek-ai/cordis-plugin-hmr + @deepseek-ai/cordis-plugin-logger-console
@deepseek-ai/cordis-plugin-timer以在檔案儲存時自動重新載入。
診斷 PENDING
import { FiberState, type Context } from '@deepseek-ai/cordis'
for (const fiber of ctx.registry.values().flatMap(r => r.fibers))
if (fiber.state === FiberState.PENDING)
console.log(`${fiber.name} is PENDING`)
參考文件
references/ 目錄中的詳細指南:
| 路徑 | 涵蓋內容 |
|---|---|
references/basic/ | 第一個 plugin、工具、設定、發布 |
references/framework/ | 生命週期、服務、事件 |
references/practice/ | 三層設計、LLM 轉接器 |
references/cordis-tutorial/ | Cordis 框架逐步教學 |