编写第一个 Cordis 插件
Cordis 是什么
Cordis 是 DeepSeek Harness 底层以 vendor 方式引入的插件框架。它的五个核心概念:
- 插件是实现能力的对象:可以是带可选
inject和apply(ctx)字段的函数,也可以是Service子类; - 上下文是服务的容器:服务占据稳定的
ctx.<key>(如ctx.tools、ctx.llm),插件按 key 查找而非导入实现; - 用
inject声明依赖:插件等待所需服务就绪后才启动,加载顺序由依赖决定而非手动编排; - 类型化事件用于通信:以
emit、waterfall、parallel、serial等模式分发; - 注册是可逆的副作用:通过
ctx.effect()或ctx.on()安装的东西,卸载时会自动撤销。
插件的最小结构
插件是一个导出 apply 函数的模块,框架加载时调用它并传入 ctx:
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin'
export function apply(ctx: Context) {
// 在这里注册你的能力。
}
name 是可选的显示元数据,用于诊断信息。函数形态最常见,但也支持对象形态({ name, inject, apply })和类形态(继承 Service)。大多数情况下函数形态就够了。
注册一个工具
下面这个完整示例向 harness 的 tools 服务注册一个可由模型调用的 greet 工具:
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet someone by name.',
parameters: {
name: { type: 'string', required: true, description: 'The name to greet' },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return `Hello, ${args.name}!`
},
}))
}
拆解一下:
inject: ['tools']让 Cordis 等待工具注册表就绪,apply内可放心使用ctx.tools;defineTool把parameters规约转换为展示给模型的 JSON Schema,推导并校验args的类型;execute返回output.schema声明的规范值,output.render再把结果转换成面向模型的内容;ctx.tools.register(...)的注册属于 effect,插件卸载时工具会自动注销。
在 DSH 里加载
开发期用 --patch 覆盖层把插件挂进 Web UI。先创建 scratch-plugin/cordis.yml:
- insert:
- id: greet
name: '/absolute/path/to/your-plugin/src/greet-tool.ts'
插件路径必须是绝对路径。然后带覆盖层启动(源码检出用 pnpm dsh web,已安装用 dsh web):
pnpm dsh web --patch ./scratch-plugin/cordis.yml
打开 http://127.0.0.1:3080,在会话里输入 Use the greet tool to greet Ada.,模型就能调用 greet 并收到 Hello, Ada!。终端日志会显示工具调用与结果事件。
想要发布给别人安装,就把插件打包成声明 dsh.bundle 的组合包,参考《安装 DSH 插件》中的 dsh plugin --profile web add 流程。