DeepSeek Harness 为什么敢说"一切皆插件"?拆透 Cordis 引擎的五大核心机制
DeepSeek Harness 为ä»ä¹æ¢è¯´"ä¸åçæä»¶"ï¼æé Cordis 弿çäºå¤§æ ¸å¿æºå¶
䏿³ä¸æ¯è¶ 人 2026-08-15 1 é 读13åé大夿° Agent æ¡æ¶çæ ¸å¿å¾ªç¯éæ»å¨ä»£ç éï¼æ³æ¹è°åº¦é»è¾åªè½ forkãDeepSeek Harnessï¼DSHï¼éäºä¸æ¡æ´æ¿è¿çè·¯ï¼è¿ agent loop æ¬èº«é½æ¯æä»¶ãæ¯æè¿å¥è®¾è®¡çåºå±å¼æå« Cordisââæ¬ææéå®çäºä¸ªæ ¸å¿æºå¶ï¼æä»¶ãçå½å¨æä¸å¯ä½ç¨ãæå¡ãäºä»¶ãå¯é ç½®æä»¶ã
1. é®é¢ï¼Agent æ¡æ¶ç"é»çå°å¢"
大夿° Agent æ¡æ¶ââLangChainãAutoGenãCrewAIââé½éç¨åä¸å¥æ¨¡å¼ï¼ä¸ä¸ªåºå®çæ ¸å¿å¼æï¼å¤æä¸äºå¯æ©å±çå·¥å ·åæ¨¡åéé å¨ãä½ è½å¨è¾¹ç¼å ä¸è¥¿ï¼ä½æ ¸å¿å¾ªç¯ãä¸ä¸æç®¡çãè°åº¦çç¥é½éæ»å¨æ¡æ¶å é¨ã
æ³æ¢ä¸ä¸ª agent loop çè°åº¦é»è¾ï¼è¦ä¹ fork æ´ä¸ªæ¡æ¶ï¼è¦ä¹æäº¤ä¸ä¸ª PR ç人 reviewãæ³æ¿æ¢ session å卿¹å¼ï¼å¯¹ä¸èµ·ï¼æ ¸å¿ä»£ç éåæ»äºã
DeepSeek Harnessï¼ä¸ç§° DSHï¼éäºä¸æ¡ä¸åçè·¯ï¼ä¸åçæä»¶ã模åéé 卿¯æä»¶ãå·¥å ·æ³¨åè¡¨æ¯æä»¶ãä¼è¯æ¥å¿æ¯æä»¶ãagent loop æ¬èº«ä¹æ¯æä»¶ã没æç¹ææ ¸å¿ï¼æ²¡æä¸è½æ¿æ¢çç»ä»¶ã
æ¯æè¿å¥è®¾è®¡çåºå±å¼æå« Cordisââä¸ä¸ªç± Koishi æ¡æ¶ä½è å¼åãç»è¿ 4000+ ç¤¾åºæä»¶éªè¯ç"å æ¡æ¶"ãå®åªåä¸ä»¶äºï¼ç®¡æä»¶å è½½å¸è½½ã管æå¡ä¾èµã管äºä»¶ååãææ Agent ä¸å¡é»è¾é½å¨å®ä¹ä¸ä»¥æä»¶å½¢å¼åå¨ã
Cordis å屿¶æââåºå±æ¯å æ¡æ¶ï¼ä¸å±æ¯ DSH æ ¸å¿æä»¶ï¼é¡¶å±æ¯ç¨æ·æ©å±ãä¸å±ä¹é´æ²¡æç¡¬ç¼ç ä¾èµï¼å ¨é¨éè¿æå¡é®åäºä»¶åä½ã
æ³¨ææ¶æå¾éä¸ä¸ªå
³é®ç¹å¾ï¼ä¸å±ä¹é´æ²¡æç®å¤´æå"æ ¸å¿"ââå 为ä¸åå¨ç¹ææ ¸å¿ãagent-loopãsessionãtools è¿äºçèµ·æ¥å"æ¡æ¶éª¨æ¶"çç»ä»¶ï¼åé¡¶å±ç"èªå®ä¹å·¥å
·"æ¯åä¸ç§ä¸è¥¿ï¼æ®éæä»¶ãä½ å¯ä»¥å¨é¡¶å±åä¸ä¸ªæä»¶æ¿æ¢æä¸å±çä»»ä½ä¸ä¸ªç»ä»¶ï¼ä¸éè¦ forkï¼ä¸éè¦ PRã
æ¥ä¸æ¥äºä¸ªç« èéä¸æè§£è¿å¥æ¶æçäºå¤§æ ¸å¿æºå¶ã
2. æä»¶ï¼ä¸åèªå¸¦è¯´æä¹¦ç积æ¨
å¨ Cordis éï¼æä»¶æ¯ä»ä¹ï¼ä¸æ¯ä¸æ®µè¢«æ³¨å
¥çèæ¬ï¼ä¸æ¯ä¸ä¸ªæ¥å£çå®ç°ç±»ââå®å°±æ¯ä¸ä¸ªå½æ°ï¼æ¥æ¶ä¸ä¸ª ctxï¼ä¸ä¸æï¼ï¼å¨éé¢å¹²èªå·±çæ´»ãå°±è¿ä¹ç®åã
ä¸ç§åæ³ï¼åä¸ä¸ªä¸è¥¿
Cordis æ¯æä¸ç§çä»·çæä»¶å®ä¹æ¹å¼ï¼
// åæ³ä¸ï¼çº¯å½æ°ï¼æå¸¸è§ï¼
export function apply(ctx) {
ctx.on('tool/call', (event) => {
console.log('å·¥å
·è¢«è°ç¨äº', event.name)
})
}
// åæ³äºï¼å¸¦ä¾èµå£°æåååç对象
export const name = 'my-plugin'
export const inject = ['tools', 'session']
export function apply(ctx) {
// æ¤æ¶ ctx.tools å ctx.session ä¸å®å·²å°±ç»ª
}
// åæ³ä¸ï¼ç±»
class MyPlugin {
static inject = ['tools']
constructor(ctx) {
// çä»·äº apply(ctx)
}
}
ä¸ç§åæ³å¨è¿è¡æ¶å®å
¨çä»·ãæ ¸å¿çº¦å®åªæä¸ä¸ªï¼æä»¶éè¦ä¸ä¸ª apply(ctx) å
¥å£ï¼å½æ°æ¬èº«å°±æ¯ applyï¼ç±»ç constructor çä»·ï¼å¯¹è±¡éè¦ apply æ¹æ³ï¼ã
å¨ Harness ä¸ï¼"ä¸åçæä»¶"é¿ä»ä¹æ ·
DSH é»è®¤é¨ç½²æ 159 个æä»¶ãè¿ä¸æ¯å¤¸å¼ ââè¿ agent loopï¼è´è´£é©±å¨æ¯ä¸è½®å¯¹è¯çæ ¸å¿è°åº¦å¨ï¼é½æ¯ä¸ä¸ªæ®éæä»¶ï¼æå¨ ctx.agentLoop è¿ä¸ªæå¡é®ä¸ãä½ å¯ä»¥ç´æ¥ç¦ç¨å®ï¼æ¢ä¸èªå·±çè°åº¦é»è¾ã
æ¥çä¸ä¸ªçå®çä¾åãåè®¾ä½ æ³ç» agent å ä¸ä¸ª"æ¯æ¬¡è°ç¨å·¥å ·åè®°å½å®¡è®¡æ¥å¿"çåè½ï¼
export const name = 'audit-log'
export const inject = ['tools'] // ä¾èµå·¥å
·æå¡
export function apply(ctx) {
// çå¬ tools/pre-execute äºä»¶ï¼waterfall ç±»åï¼
ctx.on('tools/pre-execute', (event, next) => {
console.log(
`[审计] å·¥å
·=${event.name} åæ°=${JSON.stringify(event.args)}`
)
next() // ç»§ç»æ§è¡é¾ï¼ä¸é»å¡
})
}
è¿å°±å®äºãä¸éè¦ç»§æ¿æä¸ªåºç±»ï¼ä¸éè¦å®ç°æä¸ªæ¥å£ï¼ä¸éè¦æ³¨åå°æä¸ªå
¨å±æ³¨å表ãapply(ctx) éä½ æ¿å°äºä¸ä¸æï¼ä½ å°±å¯ä»¥çå¬äºä»¶ã注åå·¥å
·ãæä¾æå¡ãCordis è´è´£æä½ çæä»¶æå°æä»¶æ ä¸ï¼å¨åéçæ¶æºè°ç¨ applyã
å ³é®ï¼æä»¶çæ¬è´¨ä¸æ¯"å®ç°æä¸ªæ¥å£"ï¼èæ¯"æ¿å° ctx åå¨é颿³¨åå¯ä½ç¨"ãctx æ¯ä¸åçä¸ä»ââä½ ä¸éè¦ import ä»»ä½å ·ä½å®ç°ï¼ææåä½é½éè¿ ctx ä¸çæå¡åäºä»¶å®æã
3. çå½å¨æä¸å¯ä½ç¨ï¼æ¥å¾å¹²åï¼èµ°å¾ä¹å¹²å
æä»¶ç³»ç»ç头å·é¾é¢ä¸æ¯"æä¹å è½½"ï¼èæ¯"æä¹å¸è½½"ã
ä¸ä¸ªæä»¶å¯å¨æ¶å¯è½æ³¨åäº 5 个äºä»¶çå¬å¨ãå¼äº 2 ä¸ªå®æ¶å¨ãè¿äºä¸ä¸ªæ°æ®åºã注åäº 3 个工å
·ãå¸è½½å®çæ¶åï¼ä½ æä¹ä¿è¯è¿äºå
¨é½è¢«æ£ç¡®æ¸
çï¼ä¼ ç»åæ³æ¯è®©æä»¶ä½è
èªå·±å cleanup() æ¹æ³ââä½äººæ»ä¼å¿ï¼ä¸æ¦å¿äºå°±æ¯å
åæ³æ¼ã
Cordis ççæ¡æ¯ï¼æææå¯ä½ç¨æ¶å£å°ä¸ä¸ªåè¯ ctx.effect()ï¼è®©æ¡æ¶èªå¨è¿½è¸ªååæ»ã
Fiber ç¶ææºï¼æä»¶çä¸ç
æ¯ä¸ªæä»¶å¨ Cordis ä¸è¢«å è£ æä¸ä¸ª Fiber å®ä¾ï¼æèªå·±çç¶ææºï¼
Fiber ç¶ææºââä»çå¾ ä¾èµå°å®æå¸è½½ç宿´çå½å¨æãDISPOSED å妿ä¾èµéæ°åºç°ï¼ä¼èªå¨åå° PENDING éæ°å è½½ã
è¿éè¦å æ¾æ¸ ä¸ä¸ªå®¹ææ··æ·çç¹ï¼Fiber æ¯æä»¶ççå½å¨æå®¹å¨ï¼ä¸æ¯å¯ä½ç¨ççå½å¨æå®¹å¨ã å¯ä½ç¨æ¯æå¨ Fiber ä¸ç"å项"ï¼ç± Fiber ç»ä¸ç®¡çåæ¶ï¼ä½ä¸¤è çå°ä½ä¸åï¼
Fiberï¼æä»¶ççå½å¨æå®¹å¨ï¼
âââ ç¶ææºï¼PENDING â LOADING â ACTIVE â DISPOSING â DISPOSED
âââ ä¾èµå£°æï¼inject = ['tools', 'session'] â Fiber è´è´£è§£æ
â
âââ å¯ä½ç¨ 1: ctx.on('agent/pre-step', handler)
âââ å¯ä½ç¨ 2: ctx.provide('notify', {...})
âââ å¯ä½ç¨ 3: ctx.effect(() => clearInterval(timer))
âââ å Fiberï¼å¦æ ctx.plugin(child) 被è°ç¨ï¼
âââ åæèªå·±çç¶ææºåå¯ä½ç¨...
ç®å说ï¼Fiber æ¯å£³ï¼å¯ä½ç¨æ¯å£³éè£ çä¸è¥¿ã ä½ dispose çæ¯ Fiberï¼å£³ï¼ï¼Fiber è´è´£æéé¢çå¯ä½ç¨éä¸ªæ¸ æãå¯ä½ç¨æ¬èº«æ²¡æç¶ææºââåªæ"已注å"å"å·²æ¸ ç"ä¸¤ä¸ªç¶æã
å ä¸ªå ³é®ç»èï¼
- PENDING â LOADINGï¼æä»¶å£°æäº
inject: ['tools', 'session']ï¼Cordis ä¼çè¿ä¸¤ä¸ªæå¡é½å°±ç»ªåææ§è¡apply()ãä¸éè¦æå轮询é»è¾ã - ACTIVE â DISPOSINGï¼å½æä»¶è¢«å¸è½½ï¼æè å®ä¾èµçæå¡è¢«å¸è½½æ¶ï¼Fiber è¿å ¥ DISPOSING ç¶æï¼å¼å§éåºæ§è¡ææ disposerã
- DISPOSED â PENDINGï¼å¦æä¾èµçæå¡éæ°åºç°ï¼æ¯å¦çéè½½ï¼ï¼æä»¶ä¼èªå¨éæ°å è½½ãè¿å°±æ¯çææçåºç¡ã
ctx.effect()ï¼å¯ä½ç¨ç"å¯é注å"
æ ¸å¿æºå¶æ¯ ctx.effect()ã宿¥æ¶ä¸ä¸ªå½æ°ï¼å½æ°éåå¯ä½ç¨æä½ï¼å¹¶è¿åä¸ä¸ªæ¤é彿°ï¼
export function apply(ctx) {
ctx.effect(() => {
// === åå¯ä½ç¨ ===
const timer = setInterval(() => {
console.log('å¿è·³')
}, 5000)
const off = ctx.on('tool/call', handler)
// === è¿åæ¤é彿° ===
return () => {
clearInterval(timer)
off()
}
})
}
å½è¿ä¸ªæä»¶è¢«å¸è½½æ¶ï¼Cordis èªå¨è°ç¨é£ä¸ªè¿åçæ¤é彿°ã宿¶å¨è¢«æ¸ é¤ï¼äºä»¶çå¬è¢«ç§»é¤ââå ¨èªå¨åã
妿ä¸ä¸ªæä»¶æ³¨åäºå¤ä¸ª effectï¼å®ä»¬æ LIFOï¼åè¿å åºï¼ é¡ºåºæ§è¡æ¤éï¼ç±»ä¼¼éæ ï¼
å¯éå¯ä½ç¨æ¯ LIFO åæ»ââåæ³¨åçå æ¤éï¼ä¿è¯ä¾èµå ³ç³»ä¸è¢«ç ´åã
å¨ Harness ä¸ççå®ä½ç¨
DSH ççéè½½ï¼HMRï¼æä»¶å°±æ¯è¿ä¸ªæºå¶çåçè ãå½ä½ ä¿®æ¹äºä¸ä¸ªå·¥å ·æä»¶ç代ç ï¼Cordis ä¼ï¼
- å¸è½½æ§æä»¶ â èªå¨åæ»å®æ³¨åçææå·¥å ·ãçå¬å¨ã宿¶å¨
- å è½½æ°æä»¶ â éæ°æ§è¡
apply(ctx)ï¼æ³¨åæ°çå¯ä½ç¨ - æ´ä¸ªè¿ç¨å¯¹å ¶ä»æä»¶éæââå®ä»¬åªçå°"å·¥å ·å表åäº"ï¼ä¸éè¦ç¥éæ¯è°å¨çéè½½
è¿å°±æ¯Cordis ç 'æ¶é´å¯ç»åæ§'ï¼ä¸ä¸ªç»ä»¶çå¯ä½ç¨å¨ç§»é¤æ¶å¯ä»¥å®å ¨åéã䏿¯é 人å cleanup 代ç ï¼èæ¯é æ¡æ¶èªå¨è¿½è¸ªã
éè¦æ³¨æçæ¯ï¼
ctx.effect()åªè½åæ»éè¿ Context åçä¿®æ¹ââæ³¨åäºä»¶ãæä¾æå¡ãæåæä»¶ã对äºå¤é¨ä¸ççä¸å¯éæä½ï¼å·²åéç HTTP 请æ±ãå·²åå ¥æ°æ®åºçæ°æ®ãå·²ååºçé®ä»¶ï¼ï¼æ¡æ¶æ æ³èªå¨åæ»ãæä»¶ä½è éè¦èªå·±å¤çè¿ç±»è¾¹çã
ä»ä¹æ¶åéè¦æå¨è°ç¨ ctx.effect()ï¼
䏿¡è§åè®°ä½ï¼Cordis å
ç½® API 注åçä¸è¥¿èªå¨åæ¶ï¼å¤é¨èµæºçæå¨æ¸
çæéè¦ ctx.effect()ã
export function apply(ctx) {
// â
è¿äºä¸ç¨å
effectï¼Fiber å¸è½½æ¶èªå¨æ¤é
ctx.on('agent/pre-step', handler) // listener èªå¨ç§»é¤
ctx.provide('notify', { ... }) // service èªå¨æ³¨é
ctx.middleware((next, send) => { ... }) // ä¸é´ä»¶èªå¨æé¤
ctx.plugin(childPlugin) // å Fiber èªå¨ dispose
// â è¿äºæ¯å¤é¨èµæºï¼Cordis 管ä¸å°ï¼å¿
é¡»æå¨æ³¨å effect
const timer = setInterval(() => heartbeat(), 5000)
ctx.effect(() => clearInterval(timer)) // å¦å宿¶å¨æ³æ¼
const db = await connectDatabase(url)
ctx.effect(() => db.close()) // å¦åè¿æ¥æ³æ¼
const watcher = fs.watch('./config.json', reload)
ctx.effect(() => watcher.close()) // å¦å watcher æ³æ¼
const server = app.listen(3000)
ctx.effect(() => server.close()) // å¦åç«¯å£æ³æ¼
}
夿å£è¯ï¼è¿ä¸ªèµæºæ¯ Cordis ç API å建çåï¼æ¯ â ä¸ç¨ effectï¼å¦ â ç¨ effectã 常è§éè¦ effect çï¼setIntervalãsetTimeoutãEventEmitter.onãfs.watchãæ°æ®åºè¿æ¥ãHTTP serverãWebSocketãchild_processãç¬¬ä¸æ¹åºç订é
ã
4. æå¡ï¼æä»¶ä¹é´ç"æ¥å¤´æå·"
æä»¶ä¹é´æä¹åä½ï¼å¦ææä»¶ A éè¦è°ç¨æä»¶ B çåè½ï¼ç´æ¥ import B ç代ç åï¼ä¸è¡ââ飿 ·å°±ç¡¬è¦åäºï¼B è¢«æ¿æ¢æ A å°±åäºã
Seam 䏿¯å¯éç设计模å¼ï¼æ¯æä»¶ä½ç³»çæ ¹æ¬å使¹å¼
å åçä¸ä¸ªå ³é®é®é¢ï¼Seam å°åºæ¯ä»ä¹ï¼æ¯èªå®ä¹æå¡æ¶ç¨çä¸ç§è®¾è®¡æ¨¡å¼ï¼è¿æ¯æä»¶ä½ç³»æ¬èº«çä¸è¥¿ï¼
çæ¡æ¯åè
ãSeam æ¯ Cordis æä»¶ä½ç³»å¯ä¸çè·¨æä»¶å使¨¡åã ä¸åå¨"ç¨ Seam"å"ä¸ç¨ Seam"两ç§éæ©ââåªè¦ä½ éè¿ ctx.xxx 访é®å¦ä¸ä¸ªæä»¶çè½åï¼ä½ 就卿¶è´¹ä¸ä¸ª Seamï¼åªè¦ä½ éè¿ ctx.provide('xxx', ...) 注åè½åï¼ä½ 就卿ä¾ä¸ä¸ª Seamã
DSH éæææ ¸å¿æå¡ââctx.toolsãctx.llmãctx.sessionsãctx.agentLoopââå
¨é¨æ¯ Seamãå®ä»¬ä¸æ¯"碰巧ç¨äºè¿ä¸ªæ¨¡å¼"ï¼èæ¯ Cordis æ¡æ¶å
ç½®çæå¡æ³¨å表æºå¶æ¬èº«ãæ¡æ¶åªè®¤æå¡é®ï¼ä¸è®¤å
·ä½å®ç°ãè¿æå³çï¼
- æ ¸å¿æå¡ä¹æ¯ Seamï¼
core/toolsæä»¶éè¿ctx.provide('tools', ...)注åå·¥å ·æå¡ï¼åä½ çèªå®ä¹æä»¶æ³¨åctx.provide('myService', ...)èµ°çæ¯å䏿¡è·¯å¾ï¼æ²¡æç¹æã - æ¿æ¢æ ¸å¿æå¡ = æä¾æ° providerï¼æ³æ¢æé»è®¤çå·¥å
·æ§è¡ç®¡éï¼åä¸ä¸ªæä»¶ï¼
ctx.provide('tools', yourImpl)ï¼åæ¶è´¹è èªå¨åå°æ°å®ç°ã - 没æ"æè·¯"ï¼ä½ ä¸è½ç»è¿ Seam ç´æ¥ import å¦ä¸ä¸ªæä»¶ç代ç ãCordis çæ¨¡åé离æºå¶ä¿è¯äºæä»¶ä¹é´åªè½éè¿ ctx ä¸çæå¡é®éä¿¡ã
ä¸ä¸ª Seam ç宿´çå½å¨æï¼å®ä¹ â æä¾ â æ¶è´¹
æ¥çä¸ä¸ªå®æ´çä¾åãå设 DSH éæ²¡æ"éç¥æå¡"ï¼ä½ æ³èªå·±å»ºä¸ä¸ªââè®©å ¶ä»æä»¶å¯ä»¥åéæ¡é¢éç¥ã
ç¬¬ä¸æ¥ï¼å®ä¹æå¡æ¥å£ï¼å¥çº¦ï¼
// éç¥æå¡çæ¥å£å¥çº¦ââ约å®äºæ¶è´¹è
è½è°ç¨ä»ä¹æ¹æ³
interface NotificationService {
notify(title: string, body: string): void
setEnabled(enabled: boolean): void
}
å¨ DSH ä¸ï¼æå¡æ¥å£é常以 TypeScript ç±»å声æåå¨ï¼ä½ä¸ºæä»¶ä¹é´ç"åå"ãæ¶è´¹è çæ¥å£å°±ç¥éè½è°ä»ä¹æ¹æ³ï¼ä¸éè¦çæä¾è çå®ç°ä»£ç ã
ç¬¬äºæ¥ï¼æä¾è ââæ³¨åæå¡å®ç°
// desktop-notify.js â éç¥æå¡çæä¾è
export const name = 'desktop-notify'
export function apply(ctx) {
let enabled = true
// 注åæå¡ï¼æå®ç°æå° 'notify' è¿ä¸ªé®ä¸
ctx.provide('notify', {
notify(title, body) {
if (!enabled) return
// è°ç¨ç³»ç»éç¥ API
process.stdout.write(`\x1b]9;${title}^${body}\x07`)
},
setEnabled(val) {
enabled = val
}
})
// æä¾è
å¸è½½æ¶ï¼æ¡æ¶èªå¨æ³¨é 'notify' æå¡é®
// æ¶è´¹è
伿ç¥å°æå¡æ¶å¤±ï¼èªå¨è¿å
¥ PENDING çå¾
}
ç¬¬ä¸æ¥ï¼æ¶è´¹è ââéè¿ ctx ä½¿ç¨æå¡
// task-reminder.js â éç¥æå¡çæ¶è´¹è
export const name = 'task-reminder'
export const inject = ['notify'] // 声æä¾èµ
export function apply(ctx) {
// å°è¿éï¼ctx.notify ä¸å®å·²å°±ç»ª
// å 为 inject 声æäºä¾èµï¼æ¡æ¶ä¿è¯äºå 载顺åº
ctx.on('task/completed', (event) => {
ctx.notify.notify('ä»»å¡å®æ', `ã${event.taskName}ã已宿`)
})
}
ä¸ä¸ªè§è²åå¸å ¶èï¼å®ä¹è 管"è½è°ä»ä¹"ï¼æä¾è 管"æä¹å®ç°"ï¼æ¶è´¹è 管"ä»ä¹æ¶åè°"ãæä¾è å¯ä»¥è¢«éæ¶æ¿æ¢ï¼æ¶è´¹è 代ç ä¸è¡ä¸ç¨æ¹ã
Seam 模åââè¿ä¸æ¯æä¸ªèªå®ä¹æå¡"碰巧ç¨äº"çæ¨¡å¼ï¼èæ¯ Cordis æä»¶ä½ç³»çæ ¹æ¬å使¹å¼ãææ ctx.xxx 访é®é½æ¯ Seamã
DSH ä¸çæ ¸å¿æå¡å ¨é¨éµå¾ªè¿ä¸ªæ¨¡åï¼
| æå¡é® | æä¾è | è½å |
|---|---|---|
ctx.tools | core/tools æä»¶ | å·¥å ·æ³¨å表ååä¿æ¤çæ§è¡ç®¡é |
ctx.llm | llm/llm æä»¶ | æ¶æ¯è¯æ±è¡¨å模åéé 卿¥ç¼ |
ctx.sessions | core/session æä»¶ | 追å å¼äºä»¶æ¥å¿åå ååå¨ |
ctx.agentLoop | core/agent-loop æä»¶ | é»è®¤ç Turn/Step 驱å¨å®ç° |
ctx.systemPrompt | core/system-prompt æä»¶ | Prompt 段è½åå·¥å · schema ç»è£ |
注æï¼è¿äº"æ ¸å¿"æå¡åä¸é¢ä¾åéç ctx.notify èµ°çæ¯å®å
¨ç¸åçæ³¨åè·¯å¾ãcore/tools æä»¶éåç乿¯ ctx.provide('tools', {...})ï¼æ²¡æç¹æ APIã
æ¿æ¢ä¸ä¸ª provider = æ¢äºå个产å
Seam æå¼ºå¤§çå°æ¹å¨äºï¼æ¢ä¸ä¸ª providerï¼æ¶è´¹æ¹ä»£ç ä¸è¡é½ä¸ç¨æ¹ã
æ¥ç DSH éçä¸ä¸ªçå®åºæ¯ãé»è®¤æ åµä¸ï¼æä»¶ç³»ç» provider æåæ¬å°ç£çââBash å·¥å ·å¨æ¬å°æ§è¡ï¼æä»¶ç¼è¾å¨æ¹æ¬å°æä»¶ãç°å¨ä½ æ³ææææ§è¡é½æ¬å°è¿ç¨æ²ç®±ï¼
// remote-sandbox.js â æ¿æ¢ fs æå¡çæä¾è
export const name = 'remote-sandbox'
export function apply(ctx) {
// æä¾æ°ç fs æå¡å®ç°ï¼è¦çé»è®¤çæ¬å°æä»¶ç³»ç»
ctx.provide('fs', {
readFile: (path) => rpc.call('remote_read', path),
writeFile: (path, data) => rpc.call('remote_write', path, data),
exec: (cmd) => rpc.call('remote_exec', cmd),
})
}
æä¸è¿ä¸ªæä»¶åï¼BashãPTYãLSP ä¸ä¸ªå·¥å
·èªå¨è¿ç§»å°è¿ç¨æ²ç®±ââå 为å®ä»¬æ¶è´¹çæ¯ ctx.fs è¿ä¸ªæå¡é®ï¼è䏿¯ import æä¸ªå
·ä½çæ¬å°æä»¶ç³»ç»æ¨¡åãprovider æ¢äºï¼æ¶è´¹æ¹æ æç¥ã
æ¿æ¢ provider çææââ仿¬å°æä»¶ç³»ç»å°è¿ç¨æ²ç®±ï¼é¶ä»£ç ä¿®æ¹ãè¿å°±æ¯"æ ¸å¿æå¡ä¹æ¯ Seam"çç´æ¥å¥½å¤ï¼è¿æä»¶ç³»ç»è¿ç§åºç¡è®¾æ½é½è½è¢«ä¸ä¸ªæ®éæä»¶æ¿æ¢ã
injectï¼å£°æçä¾èµï¼èªå¨çå 载顺åº
æä»¶éè¿ inject 声æå®éè¦åªäºæå¡ãCordis æ ¹æ®è¿ä¸ªå£°æèªå¨æ¨å¯¼å 载顺åºï¼
// è¿ä¸ªæä»¶éè¦ tools å session 两个æå¡
export const inject = ['tools', 'session']
export function apply(ctx) {
// å°è¿éï¼ctx.tools å ctx.session ä¸å®å·²å°±ç»ª
// ä¸éè¦ if (ctx.tools) ä¹ç±»ç夿
ctx.tools.register({
name: 'search',
execute: (args) => { ... }
})
}
妿 tools æå¡è¿æ²¡å°±ç»ªï¼æä¾è
è¿æ²¡å è½½ï¼ï¼è¿ä¸ªæä»¶ç Fiber ä¼åå¨ PENDING ç¶æï¼ç´å° tools å¯ç¨æè¿å
¥ LOADINGãåè¿æ¥ï¼å¦æ tools æå¡çæä¾è
被å¸è½½äºï¼è¿ä¸ªæä»¶ä¼å
被èªå¨å¸è½½ï¼å 为ä¾èµæ²¡äºï¼ï¼ç tools éæ°åºç°æ¶åèªå¨å è½½ã
è¿å°±æ¯ Cordis ç '空é´å¯ç»åæ§'ï¼ç»ä»¶ä¹é´éè¿æå¡å£°æä¾èµï¼æ¡æ¶èªå¨ç®¡çå è½½åå¸è½½çå æå ³ç³»ãä½ ä¸éè¦åä¸è¡"ç对æ¹åå¤å¥½"ç代ç ã
5. äºä»¶ï¼æä»¶çç¥ç»ç³»ç»
æå¡è§£å³äº"æä»¶æä¹è°ç¨å½¼æ¤çè½å"ï¼ä½è¿æä¸ç±»é®é¢æå¡è§£å³ä¸äºï¼æä»¶æä¹å¨å ³é®èç¹æä¸èï¼
æ¯å¦ï¼æ¯æ¬¡æ¨¡å请æ±åï¼æ£æ¥ä¸ä¸æ¶æ¯æ¯å¦å 嫿æä¿¡æ¯ãæ¯æ¬¡å·¥å ·æ§è¡åï¼è®°å½ä¸ä¸èæ¶ãæ¯æ¬¡ turn ç»æåï¼å³å®æ¯å¦è¦è¿½å ä¸ä¸ª stepãè¿äºä¸æ¯"è°ç¨æä¸ªæå¡"ââå®ä»¬æ¯"å¨æä¸ªæ¶æºæ¦æªæè§å¯"ã
Cordis ç¨ç±»ååäºä»¶è§£å³è¿ä¸ªé®é¢ï¼æåç§æ´¾å模å¼ï¼
åç§äºä»¶æ¨¡å¼
| æ¨¡å¼ | è¡ä¸º | ç±»æ¯ |
|---|---|---|
emit | åå°å³å¿ï¼ææçå¬å¨åæ¥æ§è¡ï¼å¿½ç¥è¿åå¼ | 广æéç¥ââ"æåçäºä¸ä»¶äºï¼å¬å°çèªå·±å¤ç" |
waterfall | é¾å¼ä¼ éï¼æ¯ä¸ªçå¬å¨æ¶å°ä¸ä¸ä¸ªçç»æï¼å¿
é¡»è° next() æç»§ç» | ä¸é´ä»¶ç®¡éââ"æ°æ®ç»è¿ææï¼æå¯ä»¥æ¹å®ï¼ä¹å¯ä»¥ç´æ¥æ¦ä¸æ¥" |
serial | ä¸²è¡æ§è¡ï¼æ next()ï¼ä¸è½å§æ | éä¸è¯¢é®ââ"æ¯ä¸ªäººè¯´ä¸å¥ï¼æ²¡æå驳æ" |
parallel | å¹¶è¡æåºï¼ææçå¬å¨åæ¶æ§è¡ | 群åä»»å¡ââ"大家ä¸èµ·å¹²ï¼çææ ¢çé£ä¸ª" |
å¨ DSH 䏿ä¹éï¼
- éè¦æ¦æª/æ¹åæ°æ® â waterfallï¼å¦ agent/pre-step å¯æç»ææ¹åæ¶æ¯ï¼
- éè¦è§å¯/è®°å½ â emitï¼å¦ session/created ä¸å½±åæµç¨ï¼
- éè¦éä¸å³ç â serialï¼å¦ agent/turn-stopping æ¯ä¸ªçå¬å¨æç¥¨ï¼
宿ï¼Agent Loop éçäºä»¶æµ
DSH ç agent loop æ¯äºä»¶ç³»ç»æå¥½çæå¦æ¡ä¾ãä¸è½®å¯¹è¯ï¼Turnï¼è¢«åæå¤ä¸ªæ¥éª¤ï¼Stepï¼ï¼æ¯ä¸ªå ³é®èç¹é½æå¯¹åºçäºä»¶ï¼
ä¸å¾æ¯Agent Loop ç宿´äºä»¶æµââæ è®°äº"æ©å±ç¹"çæ¯å¯æ¦æªäºä»¶ï¼waterfall/serialï¼ï¼å ¶ä½æ¯ durable æä¹ åäºä»¶ã
注æå¾ä¸ä¸¤ç§èç¹çåºå«ï¼
- durable èç¹ï¼æä¹ åäºä»¶ï¼åå ¥ session logãç¨äºè®°å½"åçäºä»ä¹"ââforkãresumeãreplay é½ä»è¿æ¡äºä»¶æµæ´¾çã
- æ©å±ç¹èç¹ï¼waterfall æ serial äºä»¶ï¼æ¯æä»¶å¯ä»¥æ¦æªç"æ¥ç¼"ã
举个å®é çæ¦æªä¾åãåè®¾ä½ æ³åä¸ä¸ª"ææè¯è¿æ»¤"æä»¶ââæ¯æ¬¡æ¨¡å请æ±åæ£æ¥æ¶æ¯ï¼åç°ææè¯å°±æ¦æªï¼
export const name = 'sensitive-filter'
export const inject = ['agent']
export function apply(ctx) {
// agent/pre-step æ¯ waterfall äºä»¶
ctx.on('agent/pre-step', (event, next) => {
const messages = event.messages
const hasSensitive = messages.some(m =>
m.content.includes('å¯ç ') || m.content.includes('token')
)
if (hasSensitive) {
// ä¸è° next()ï¼ç´æ¥ rejectââçè·¯æ´æ¡é¾
return { kind: 'reject', reason: 'æ£æµå°ææä¿¡æ¯' }
}
// 没é®é¢ï¼æ¾è¡
next()
})
}
å
³é®å¨äº next()ãwaterfall äºä»¶ä¸ï¼æ¯ä¸ªçå¬å¨æ¶å° (event, next) ä¸¤ä¸ªåæ°ãè°ç¨ next() å°±ææ§å¶æäº¤ç»ä¸ä¸ä¸ªçå¬å¨ï¼ä¸è°ç¨å°±ç´æ¥çè·¯ââåé¢ççå¬å¨åé»è®¤è¡ä¸ºé½ä¸ä¼æ§è¡ãè¿å Koa çä¸é´ä»¶ãExpress ç middleware æ¯åä¸ä¸ªæè·¯ã
äºä»¶ vs æå¡ï¼ä»ä¹æ¶åç¨åªä¸ªï¼ æä¸æ¡ç®åç夿ååï¼æ¦æªåçç¥ç¨äºä»¶ï¼ç´æ¥è°ç¨ç¨³å®è½åç¨æå¡æ¹æ³ãæ¯å¦"æ¯æ¬¡å·¥å ·è°ç¨åæ£æ¥æé"æ¯çç¥ï¼ç¨
tools/pre-executeäºä»¶ï¼"注åä¸ä¸ªæ°å·¥å ·"æ¯ç´æ¥è½åï¼ç¨ctx.tools.register()æå¡æ¹æ³ã
6. å¯é ç½®æä»¶ï¼ç¨é ç½®æä»¶æ¼ä¹é«
å°ç®å为æ¢ï¼æä»¬è¯´ç齿¯"ç¨ä»£ç åæä»¶"ãä½ DSH è¿æä¸å±æ´é«çº§çè½åï¼ç¨é ç½®æä»¶ç»åæä»¶ï¼ä¸éè¦åä¸è¡ä»£ç å°±è½å®å¶ä½ ç Agentã
è¿å¥ç³»ç»ç±ä¸ä¸ªæ¦å¿µç»æï¼BundleãProfileãPatchã
åå±é ç½®ï¼ä»ç²å°ç»
åå±é ç½®çå±å 模åââä» Bundle å° CLI overlayï¼éå±è¦çã
æ¯ä¸å±çä½ç¨ï¼
- Bundleï¼ä¸ç» Cordis é
ç½®è¡ + 对åºä»£ç çååæ ¼å¼ã
dsh-baseæ¯ææ Profile ç第ä¸å±ï¼æä¾æ ¸å¿è½åãä¸é¢åådsh-web-appï¼å æµè§å¨ UIï¼ædsh-headlessï¼å æ 头è¿è¡å¨ï¼ã - Profile Patchï¼é对ç¹å® Profile çè¦çæä»¶ãæ¯å¦ä½ ç web Profile æ³æ¢ä¸ä¸ªä¸åçæ¨¡åéé å¨ï¼å°±å¨è¿é patchã
- Home Patchï¼å ¨å±è¦çï¼å¯¹ææ Profile çæãæ¯å¦ä½ æ³å ¨å±ç¦ç¨æä¸ªå·¥å ·ã
- CLI Overlayï¼å½ä»¤è¡
--patchåæ°ï¼ä¸´æ¶æé«ä¼å 级è¦çãéåè°è¯å䏿¬¡æ§å®éªã
Patch é¿ä»ä¹æ ·
Patch æä»¶å°±æ¯ä¸ä¸ª YAMLï¼éè¿è¡ ID å®ä½è¦æ¿æ¢ææ°å¢çé ç½®ï¼
# cordis.patch.yml
# æ¿æ¢é»è®¤ç LLM éé
å¨ï¼æ¹ç¨èªå®ä¹ provider
- id: llm-deepseek
replace:
plugin: my-custom-llm
config:
apiKey: ${env.MY_API_KEY}
model: deepseek-v4-pro
# æ°å¢ä¸ä¸ªå®¡è®¡æ¥å¿æä»¶
- id: audit-log
insert:
plugin: @my-org/dsh-audit
config:
logPath: /var/log/dsh-audit.jsonl
æ³çä½ çæºå¨å®é å¯å¨äºä»ä¹ï¼ä¸è¡å½ä»¤ï¼
dsh --profile web --dump-config
è¿ä¼æå°åºåå¹¶åç宿´æä»¶æ ââæ¯ä¸è¡é½è½è¢«ä½ èªå·±ç patch è¦çã
å¨ Harness ä¸çä½ç¨ï¼åç§æ¨¡å¼
DSH å ç½®äºåç§ Profile 模å¼ï¼æ¯ç§å è½½ä¸åçæä»¶éåï¼
| æ¨¡å¼ | å è½½çæä»¶ | éç¨åºæ¯ |
|---|---|---|
| æ 忍¡å¼ | 宿´å·¥å ·ç»å + Web UI | æ¥å¸¸å¼åä½¿ç¨ |
| PTC æ¨¡å¼ | ç¨åºåå·¥å ·è°ç¨ââæ¨¡åçæä»£ç æ¥ç»åå¤è½®å·¥å · | 夿工使µèªå¨å |
| æç®æ¨¡å¼ | ä» Shell + æä»¶ç¼è¾å·¥å · | æå°ç¯å¢ä¸ç模ååºåæµè¯ |
| åé æ¨¡å¼ | 坿£æ¥è¿è¡æ¶ãå¨å åä¸è¯éª Cordis æä»¶ | ç»ååå使°çæ¨¡å¼ |
è¿åç§æ¨¡å¼çåºå«ä» ä» æ¯å è½½çæä»¶éåä¸åââæ²¡æä»»ä½ if-else 忝åå¨ä»£ç éãåæ¢æ¨¡å¼å°±æ¯åæ¢ Profileï¼å°±æ¯æ¢ä¸æ£µæä»¶æ ãè¿å°±æ¯"ä¸åçæä»¶"å¨å®è·µä¸æå³çä»ä¹ï¼è¿"产åå½¢æ"æ¬èº«é½æ¯é ç½®ã
7. è¿å¥æ¶æçä¼å¿å¨åªé
äºä¸ªç« èæå®ï¼åå°æå¼å§çé®é¢ï¼DSH 为ä»ä¹è¦ç¨ Cordisï¼è¿å¥æ¶æå°åºå¥½å¨åªï¼
ä¼å¿ä¸ï¼é¶ fork æ©å±
ä¼ ç»æ¡æ¶æ³æ¹æ ¸å¿è¡ä¸ºï¼è·¯å¾æ¯ fork â æ¹æºç â ç»´æ¤å·®å¼ãCordis çè·¯å¾æ¯åä¸ä¸ªæä»¶ â ctx.provide('xxx', newImpl) â å®äºã
åé¢çå°çè¿ç¨æ²ç®±æ¿æ¢å°±æ¯å
¸åæ¡ä¾ï¼ææ¬å°æä»¶ç³»ç»æ¢æè¿ç¨ RPCï¼Bash/PTY/LSP ä¸ä¸ªå·¥å
·é¶ä»£ç ä¿®æ¹èªå¨è¿ç§»ãå¨ä¼ ç»æ¡æ¶éè¿æ¯å¤§å·¥ç¨ââä½ éè¦æ¹æ¡æ¶æºç éææ fs.readFile çè°ç¨ç¹ãå¨ Cordis éï¼ä½ åªæ¯æä¾äºä¸åæ°ç Seam providerã
ä¼å¿äºï¼å®å ¨ççææ
ctx.effect() + LIFO åæ»ä¿è¯äºæä»¶"æ¥å¾å¹²åï¼èµ°å¾ä¹å¹²å"ãè¿æå³çä½ å¯ä»¥ï¼
- çéè½½ï¼ä¿®æ¹æä»¶ä»£ç åèªå¨å¸è½½æ§çãå è½½æ°çï¼å ¶ä»æä»¶æ æç¥
- 卿å¯åï¼è¿è¡æ¶æéå è½½/å¸è½½æä»¶ï¼ä¸éè¦éå¯è¿ç¨
- A/B å®éªï¼åæ¶å 载两个å®ç°ä¸åçç¥çæä»¶ï¼éè¿é ç½®åæ¢åªä¸ªçæ
è¿äºè½åçæ ¹åºæ¯æ¡æ¶èªå¨è¿½è¸ªå¯ä½ç¨ââ䏿¯é æä»¶ä½è
èªè§å cleanupï¼èæ¯é ctx.effect() çå¯éæ³¨åæºå¶ã
ä¼å¿ä¸ï¼ä¾èµèªç»ç»
inject 声æ + Fiber ç¶ææº = ä¾èµå
³ç³»èªå¨æ¨å¯¼ãä½ ä¸éè¦ï¼
- æå¨ææä»¶å 载顺åº
- å
if (ctx.tools)夿æå¡æ¯å¦å°±ç»ª - æ å¿å¾ªç¯ä¾èµââæ¡æ¶å¨å è½½é¶æ®µå°±è½æ£æµå°
æä»¶ä¹é´éè¿æå¡é®å£°æä¾èµï¼æ¡æ¶è´è´£æææåºåçå½å¨æèå¨ãä¾èµæ¶å¤±æ¶èªå¨å¸è½½æ¶è´¹è ï¼ä¾èµæ¢å¤æ¶èªå¨éæ°å è½½ãè¿ä¸å齿¯å£°æå¼çã
ä¼å¿åï¼é ç½®å³äº§åå½¢æ
åç§ Profile 模å¼ï¼æ å/PTC/æç®/åé ï¼çå·®å«ä»
ä»
æ¯å è½½äºä¸åçæä»¶éåãæ²¡æä»»ä½ if (mode === 'ptc') åå¨ä»£ç éãåæ¢äº§åå½¢æ = 忢é
ç½®æä»¶ = æ¢ä¸æ£µæä»¶æ ã
è¿æå³çä½ å¯ä»¥ç¨åä¸å¥ä»£ç åºï¼éè¿ä¸åç Bundle + Patch ç»åï¼æ´¾çåºå®å ¨ä¸åç产åå½¢æââå¼åå·¥å ·ãCI æºå¨äººãåºåæµè¯å¹³å°ââèä¸éè¦ç»´æ¤å¤ä¸ª forkã
ä¸å¥è¯æ»ç»
å½ Agent é¢åè¿å¨å¿«éæ¼åââæ°çæ¨¡åè½åãæ°çå·¥å ·ç±»åãæ°çè°åº¦çç¥å±åºä¸ç©·ââä½ éè¦ç䏿¯ä¸ä¸ªåºå®çæ¡æ¶ï¼èæ¯ä¸ä¸ªè½è®©ææé¨ä»¶èªç±æ¿æ¢ãèªç±ç»åãèªç±çææçåºåº§ãCordis å°±æ¯è¿ä¸ªåºåº§ï¼æ²¡æç¹ææ ¸å¿ï¼ä¸åçæä»¶ï¼æ³¨åå³å¯éï¼ä¾èµèªç»ç»ã
åèï¼
- deepseek-ai/deepseek-harness â GitHub ä»åº
- cordiverse/cordis â Cordis å æ¡æ¶
- A Programming Paradigm for Spatiotemporal Composability â DeepSeek AI & å京大å¦, 2026-08-13
- cordis.moe â Cordis 宿¹ææ¡£
- Koishi â åå¹´å¼åï¼4000+ ç¤¾åºæä»¶ï¼Cordis çé¦ä¸ªå¤§è§æ¨¡éªè¯æ¡ä¾
Aitishiku.com