DeepSeek Harness插件和工具的区别介绍及开发入门指南

猫哥写了三个 dsh 插件(会话导出、通知、报表)之后,发现也挺简单哈,看来想做贡献也没那么难。有个问题一直绕不开:插件(plugin)和工具(tool)到底啥关系? 官方文档里一会儿说"一切皆插件",一会儿又冒出个"工具编写参考",初看挺懵的。

这篇文章猫哥用大白话 + 最小 demo 把这件事讲清楚。看完你会发现:它俩根本不是并列概念------工具只是插件能注册的"内容"之一。
------ 猫哥,一个热爱分享AI技术和爱折腾的人。更多折腾记录见我的博客:blog.csdn.net/qq8864

一句话结论

  • 插件 = 一个 apply(ctx) 模块,是框架的安装/生命周期单元。它决定"什么时候加载、依赖什么、卸载时清理什么"。
  • 工具 = 插件通过 ctx.tools.register(...) 注册的能力 ,是模型可见、可调用的函数。它决定"agent 能做什么"。

工具是插件注册的内容之一,不是和插件并列的东西。

打个比方:插件是安装的应用程序 ,工具是应用暴露的函数/API 端点。一个应用可以暴露 0 个、1 个或多个 API------对应一个插件可以注册 0 个、1 个或多个工具。

插件是什么?最小 demo 长这样

dsh 的"一切皆插件"指的是:模型适配器、工具、文件访问、agent 循环本身,全都是挂到共享上下文(ctx)上的插件。一个插件就是一个导出 apply 函数的模块:

ts 复制代码
// hello-plugin.ts ------ 插件的最小形态
import type { Context } from '@deepseek-ai/cordis'

export const name = 'hello-plugin'   // 显示名(可选)
export const inject = ['tools']      // 依赖就绪后才执行 apply(可选)

export function apply(ctx: Context) {
  // 在这里注册这个插件贡献的一切:
  // ctx.tools.register(...)    注册工具
  // ctx.on('session/event', ...) 订阅事件
  // ctx.effect(() => cleanup)  声明卸载清理
  console.log('[hello-plugin] loaded!')
}

插件有完整的生命周期 (Fiber 状态机:PENDING → LOADING → ACTIVE/FAILED → UNLOADING → DISPOSED),声明了 inject 会等依赖服务就绪;通过 ctx 注册的任何东西在插件卸载时自动撤销(不用手动 removeListener)。

工具是什么?最小 demo 长这样

工具是"给模型用的函数"。核心约定:description 和 parameters 是给模型看的 (决定模型什么时候调、传什么参),execute 是真正干活的

ts 复制代码
// my-tool.ts ------ 工具的最小形态(工具型插件)
import { readFile } from 'node:fs/promises'
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'my-tool'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'read_file',
    description: 'Read a file from disk.',          // ← 模型看到什么
    parameters: {
      path: { type: 'string', required: true, description: 'Absolute path' },
      limit: { type: 'number' },                    // 默认可选
    },
    output: {
      schema: { type: 'string' },                    // 规范返回值的 schema
      render: (_args, value) => [{ type: 'text', text: value }],  // ← 模型看到的结果
    },
    async execute(args, exec) {
      // args 已被 schema 校验且只读:{ path: string; limit?: number }
      return readFile(args.path, { encoding: 'utf8', signal: exec.signal })
    },
  }))
}

注意几个点:

  • schema 是你的桥梁parameters 校验模型生成的参数(类型、必填、联合分支),校验通过的 args 才进 execute;schema 还会自动流入系统提示词的组装,模型"知道"有这个工具、怎么用。
  • execute 的约定args 只读;exec.signal 是取消信号(信号触发要取消进行中的工作);抛异常或返回无效值 = isError;成功要返回规范 JSON 值(别把 UI 格式塞进返回值)。
  • 展示两分output.render 给模型生成自然语言结果;presentCall/presentResult 给 UI 画卡片(terminal/diff/search/web/generic)。展示方法是纯函数------会话日志回放时也会运行,不能做 I/O、不能读状态。

一次工具调用,背后发生了什么

复制代码
模型(根据 description/parameters 生成调用)
  → tools 注册表(schema 校验 → args 冻结为只读 + exec.token → 权限策略 pre-execute/guard)
  → execute(args, exec)(真实干活,遵守 signal 取消)
  → 规范 JSON 值(output.schema 校验、冻结、presentationMeta 投影)
  ├── output.render(args, value)   → 模型可见的自然语言
  └── presentResult(args, result)  → UI 卡片(terminal/diff/search/web)

附带两个很爽的能力:

  • Code Mode 自动触达 :每个已注册工具在 Code Mode 里都能直接 await tools.read_file({ path, limit }) 调用,参数/返回类型从同一套 schema 自动派生------写完工具,程序化 API 也自动有了。
  • 长时间任务 :声明 run_in_background 后用 ctx.jobs.start(...) 注册后台任务,返回类型化的 { kind: 'background', jobId } 句柄。

那"观察型插件"呢?------我们做的三个就是

工具型插件给 agent 加能力 ;还有一类插件不给模型加任何能力,只在后台被动观察会话。我们之前写的三个插件全是这种:

ts 复制代码
// dsh-session-export / dsh-notify / dsh-session-report 的骨架
export const inject = ['sessions']

export function apply(ctx: Context) {
  ctx.on('session/event', (session, event) => {
    if (event.type !== 'turn/end') return
    // 防抖 → 导出 Markdown / 推送 webhook / 生成报表
  })
}

它们不注册工具,但同样是"插件"------通过事件订阅在 turn 结束后自动干活。所以:

工具型插件 观察型插件
给模型加能力? ✅ 是 ❌ 否
核心注册 ctx.tools.register(defineTool(...)) ctx.on('session/event', ...)
例子 tool-bash、文件读写、web 搜索 我们的导出/通知/报表

两种形态也可以混在一个插件里:既注册工具,又订阅事件。

什么时候写哪种?

  • 想让 agent 会做新的事 (查数据库、操作浏览器、调用内部 API)→ 写工具型 插件:defineTool({ name, description, parameters, execute }) 四件套。
  • 想让 dsh 在后台自动做事 (复盘、通知、记账、备份、监控)→ 写观察型 插件:订阅 session/event + 防抖 + dispose flush 三件套(我们三篇文章就是这个骨架)。
  • 不确定?先想清楚:这个能力是"模型主动调"还是"系统自动做"。模型调 → 工具;系统做 → 观察型插件。

关于作者

本文作者 猫哥,DeepSeek Harness 插件开发者,日常折腾 AI Agent、开源与效率工具。写过并开源了三个 dsh 插件:

更多 AI/开发折腾记录,欢迎来我的博客坐坐:https://blog.csdn.net/qq8864。觉得有用的话点个关注、留个言,后续还会持续输出 dsh 插件生态的实战内容。

参考

相关推荐
小新讲网安1 小时前
HTTP请求走私攻击实战:CL.TE与TE.CL绕过前端服务器全解析
服务器·前端·网络·web安全·http·架构·漏洞
安逸sgr1 小时前
优化器是什么?SGD、Momentum、Adam 有什么区别?
人工智能·ai·大模型·agent·智能体
风骏时光牛马2 小时前
AI办公系统异常事件复盘分析
前端
王莹月2 小时前
全店商品图风格怎么统一?生图API 用 nano banana pro 批量出同一套视觉
gpt·ai·chatgpt·ai作画·aigc·agi
console.log('npc')2 小时前
React 19 + Vite 企业级前端项目:从零搭建到规范交付
前端·react.js·前端框架
程序员黑豆2 小时前
Java字符串常量池完全指南:原理、intern()方法与性能优化最佳实践
java·前端·ai编程
九里九里2 小时前
Deepseek Harness 接glm/minimax等其他模型
deepseek·deepseekharness
光影少年2 小时前
react离线缓存、图片缓存方案
开发语言·前端·javascript·react native·react.js·缓存·前端框架
Canace2 小时前
笔记本都合上了,Claude 为什么还能在手机上执行电脑上装的技能?
前端·人工智能·ai编程