首页 › LLM大语言模型 › Deepseek › DeepSeek Harnrss 实战手册 › 第 6 章 插件开发实战:从 Hello 到第一个工具

第 6 章 插件开发实战:从 Hello 到第一个工具

这一章全程动手。目标:创建一个插件 → 加载进 Web UI → 再给它加一个能被模型调用的工具。整个流程在 15 分钟内可以走完。​

6.1 准备:一个源码 checkout​

插件开发建议从官方仓库的源码 checkout 开始(这样能用 pnpm dsh 和内置的 build 环境):​

​

代码块​

Plain Text

git clone https://github.com/deepseek-ai/deepseek-harness.git​

cd deepseek-harness​

pnpm install​

pnpm run build​

​

然后在仓库根目录建一个实验项目:​

​

代码块​

Plain Text

mkdir -p scratch-plugin/src​

​

6.2 第一个插件:Hello​

创建 scratch-plugin/src/my-plugin.ts:​

​

代码块​

Plain Text

import type { Context } from '@deepseek-ai/cordis'​

​

export const name = 'hello-plugin'​

​

export function apply(ctx: Context) {​

console.log('[hello-plugin] plugin loaded!')​

}​

​

这就是一个完整插件:导出 name 和 apply(ctx)。框架加载时调用 apply,把上下文 ctx 交给你。​

6.3 加载它:--patch 覆盖层​

创建 scratch-plugin/cordis.yml。注意插件路径必须是绝对路径,把 /absolute/path/to/deepseek-harness 换成你自己的:​

​

代码块​

Plain Text

- insert:​

- id: hello​

name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'​

​

用覆盖层启动 Web UI:​

​

代码块​

Plain Text

pnpm dsh web --patch ./scratch-plugin/cordis.yml​

​

打开 http://127.0.0.1:3080,你应该在启动终端的日志里看到:​

​

代码块​

Plain Text

[hello-plugin] plugin loaded!​

​

恭喜,你的第一个插件已经跑起来了。​

6.4 插件的自动清理​

通过 ctx 注册的任何东西——事件监听、工具、定时器——在插件卸载时都会被自动清理,你不用手动 removeListener 或 clearInterval。​

如果需要手动清理的资源(比如一个网络连接),用 ctx.effect() 声明:​

​

代码块​

Plain Text

import type { Context } from '@deepseek-ai/cordis'​

​

export function apply(ctx: Context) {​

ctx.effect(() => {​

​

6.5 声明依赖:inject​

如果插件要用其他服务(如工具注册表 tools、模型层 llm),声明 inject,框架会等服务就绪再加载:​

​

代码块​

Plain Text

import type { Context } from '@deepseek-ai/cordis'​

​

export const name = 'my-tool-plugin'​

export const inject = ['tools']​

​

6.6 开发一个工具:greet​

把 scratch-plugin/src/my-plugin.ts 替换成下面这个"问候工具":​

​

代码块​

Plain Text

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',​

​

拆解一下这个 DSL:​

•

parameters:声明工具入参,defineTool 会根据它推导并校验 args 的类型;​

•

output.schema:声明工具返回值的规范形式;​

•

output.render:把规范值转换成面向模型的内容;​

•

execute:真正的执行逻辑。​

重启开发命令(如果还在运行):​

​

代码块​

Plain Text

pnpm dsh web --patch ./scratch-plugin/cordis.yml​

​

在 Web UI 里输入:​

​

代码块​

Plain Text

Use the greet tool to greet Ada.​

​

模型会调用 greet,并收到 Hello, Ada! 这个工具结果——一个能被模型自主调用的工具就诞生了。​

6.7 插件的三种形态​

函数形式之外,还有对象形式和类形式:​

​

代码块​

Plain Text

// 对象形式​

export default {​

name: 'my-plugin',​

inject: ['tools'],​

apply(ctx: Context) {​

// ...​

},​

}​

​

​

代码块​

Plain Text

// 类形式:需要向其他插件提供服务时使用​

import { Service, type Context } from '@deepseek-ai/cordis'​

​

export default class MyService extends Service {​

static inject = ['tools']​

​

constructor(ctx: Context) {​

super(ctx, 'myService')​

​

大多数场景函数形式就够了;类形式意味着你的插件本身是一个服务,其他插件可以依赖它。​

6.8 本章小结​

•

插件 = 导出 apply(ctx) 的 TS 模块,通过 ctx 注册能力;​

•

--patch 覆盖层是开发期加载插件的最快方式(路径要绝对路径);​

•

ctx 的注册自动清理,ctx.effect() 处理手动资源;​

•

inject 声明服务依赖,框架保证顺序;​

•

defineTool 是写面向模型工具的标准姿势:入参 schema + 执行 + 渲染。​

下一章,让插件学会"接受配置"。​