DeepSeek Harness:用 Creator 模式开发自定义插件,从 0 到可调用

用 Creator 模式开发 DeepSeek Harness 自定义插件:从 0 到可调用

💡 摘要:很多同学以为"Creator 模式 = 一键开发插件",其实不准确。Creator 模式是 DeepSeek Harness 的高信任运行模式 ,它允许 Agent 操作运行时、自省 Cordis 插件系统、生成新的 Preset;而真正写一个插件 ,走的还是 Cordis 插件机制:写一个 TypeScript 模块 → 用 cordis.ymlinsert 把它挂进运行时 → 通过 --patch 启动加载。本文以"做一个翻译工具插件"为完整示例,带你跑通"Creator 模式 + 自定义插件"的全链路,并附上避坑清单与质量自检。

文章目录


一、先把概念勘误:Creator 模式 ≠ 插件开发按钮

DeepSeek Harness 目前内置四种模式:Standard / PTC(Code) / Minimal / Creator。它们的关系是同一套 Cordis 内核 + 不同的会话级插件组合(Preset) ,切换模式切换的是"这台 Agent 被允许拥有什么手脚",模型还是你配置的那个模型

Creator 模式的特殊之处 :它在 Standard 全套能力的基础上,额外开放了一组用于操作 Cordis 插件系统的专属工具,让 Agent 可以:

  • 检查当前运行时有哪些插件在跑
  • 在内存里试验新的插件组合
  • 直接创作出一个全新的模式预设(生成 agent.cordis.yml

所以 Creator 模式在插件开发中扮演的角色是:提供一个高信任的本地试验场 ------你可以在 Creator 模式下让 Agent 帮你生成插件骨架、调试 Preset,但插件本身的写法遵循的是 Cordis 的统一规范,跟用哪种模式启动 Web UI 加载它关系不大。

⚠️ 安全红线 :Creator 模式允许 Agent 操作运行时,只在可信本地环境使用,禁止公网部署开启。下面的所有操作都默认你在本地可信环境进行。


二、环境准备:从源码开始

开发插件建议直接克隆源码,而不是用 npx @deepseek-ai/dsh web 体验版------因为官方插件教程本身就是建立在源码仓库之上的。

bash 复制代码
# 1. 克隆仓库
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness

# 2. 安装依赖(需要 Node.js 22.19+ 或 24+,Corepack 提供的 pnpm)
corepack enable
pnpm install
pnpm run build

# 3. 第一次 typecheck,确认依赖与 TypeScript 项目引用处于可用状态
pnpm run typecheck

📌 项目目前仍处于 Developer Preview(开发者预览) 阶段,官方明确提醒可能存在兼容性破坏性变化。今天能跑的插件,未来版本可能需要微调。


三、最小插件:先证明"它能加载"

在仓库根目录建一个临时插件目录:

bash 复制代码
mkdir -p scratch-plugin/src

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

typescript 复制代码
import type { Context } from '@deepseek-ai/cordis'

export const name = 'hello-plugin'

export function apply(ctx: Context) {
  console.log('[hello-plugin] plugin loaded!')
}

这就是 DeepSeek Harness 插件的最小形态------导出 name + 导出 apply 函数 。Cordis 加载插件时会调用 apply,并把运行时上下文 ctx 传入。

用 Patch 把它挂进去

scratch-plugin/ 下创建 cordis.yml

yaml 复制代码
- insert:
  - id: hello
    name: '/ABSOLUTE_PATH_TO/deepseek-harness/scratch-plugin/src/my-plugin.ts'

⚠️ 最容易踩的坑 :插件路径必须是绝对路径 。官方文档特别强调了这一点。不要写 ./scratch-plugin/...,而要写 /Users/你的用户名/deepseek-harness/scratch-plugin/...(Windows 下类似 C:/workspace/deepseek-harness/scratch-plugin/...,建议用正斜杠减少转义问题)。

启动并验证

bash 复制代码
pnpm dsh web --patch ./scratch-plugin/cordis.yml

打开 http://127.0.0.1:3080,如果终端打印出 [hello-plugin] plugin loaded!,恭喜------你的第一个插件已经进入了 Harness 运行时。

💡 --patch 是命令行传入的覆盖层,优先级高于 Profile 里的 Bundle 和 $DSH_HOME/cordis.patch.yml。本地试验插件时用它最方便;长期使用的插件再合并进自己的 Profile 层。


四、升级为工具插件:让 Agent 真正能调用

光打印日志不够,我们要让 Agent 能主动调用我们的能力。这就要用到 defineTool + ctx.tools.register

scratch-plugin/src/my-plugin.ts 替换为下面这个完整的翻译工具插件

typescript 复制代码
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'tool-translate'

// 声明依赖:需要 tools 服务(注册工具)和 llm 服务(翻译时调用模型)
export const inject = ['tools', 'llm'] as const

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'translate',
    description: 'Translate text to a specified language. Use this when the user asks you to translate content.',

    // 1. 参数 schema(JSON Schema 格式):Agent 靠它知道怎么调用
    parameters: {
      type: 'object',
      properties: {
        text: {
          type: 'string',
          description: 'The text to translate',
        },
        targetLanguage: {
          type: 'string',
          description: 'Target language (e.g., "English", "Chinese", "Japanese")',
        },
        sourceLanguage: {
          type: 'string',
          description: 'Source language. If omitted, auto-detected.',
        },
      },
      required: ['text', 'targetLanguage'],
      additionalProperties: false,
    },

    // 2. 输出 schema + 渲染函数
    output: {
      schema: {
        type: 'object',
        properties: {
          translatedText: { type: 'string' },
          detectedLanguage: { type: 'string' },
        },
        required: ['translatedText', 'detectedLanguage'],
        additionalProperties: false,
      },
      render(value) {
        return [{
          type: 'text',
          text: `[${value.detectedLanguage} → ${value.targetLanguage}]\n${value.translatedText}`,
        }]
      },
    },

    // 3. 执行函数:通过能力接缝调用 LLM,而非硬编码 API
    async execute(args, exec) {
      const systemPrompt = `You are a professional translator. Translate the following text to ${args.targetLanguage}. Output only the translation, nothing else.`

      const messages = [{
        role: 'user' as const,
        content: [{ type: 'text' as const, text: args.text }],
        source: 'plugin' as const,
      }]

      // 通过 resolveCallConfig 获取当前配置的默认模型,而不是写死
      const callConfig = await ctx.llm.resolveCallConfig({ purpose: 'primary' })

      const stream = ctx.llm.stream({
        provider: callConfig.provider,
        model: callConfig.model,
        messages,
        system: systemPrompt,
        temperature: 0.3,
        signal: exec.signal,   // 支持取消
        purpose: 'primary',
      })

      let translatedText = ''
      for await (const chunk of stream) {
        if (chunk.type === 'text-delta') {
          translatedText += chunk.text
        }
      }

      return {
        translatedText,
        detectedLanguage: args.sourceLanguage ?? 'auto-detected',
        targetLanguage: args.targetLanguage,
      }
    },

    // 4. 展示函数:纯函数,不做 I/O
    presentCall(args) {
      return {
        kind: 'generic',
        data: { text: args.text?.slice(0, 100), target: args.targetLanguage },
      }
    },
    presentResult(_args, content) {
      return {
        kind: 'generic',
        data: { preview: content.map(c => c.text || '').join('').slice(0, 200) },
      }
    },
  }))
}

这段代码的关键点

  1. inject = ['tools', 'llm'] :声明依赖。只有 toolsllm 服务就绪,Harness 才会调用 apply不要假设 ctx 上所有服务都已存在 ,依赖关系必须通过 inject 声明,让运行时负责排序和生命周期。
  2. defineTool :参数 parameters 使用标准 JSON Schema 格式,包含 type: 'object'propertiesrequiredadditionalProperties: false。这是注册工具时的强制要求。
  3. 通过 ctx.llm 调用模型 :不直接调用 API,走"能力接缝";使用 resolveCallConfig 获取配置的模型而非硬编码。
  4. exec.signal 传递:支持 AbortSignal,让工具调用可以被取消。
  5. 具名导出 :必须用 export const name + export function apply千万别写 export default ------否则 inject 元数据会被 Loader 悄悄丢掉,表现为"装上了但不依赖、行为诡异"。

重新启动并验证

bash 复制代码
pnpm dsh web --patch ./scratch-plugin/cordis.yml

在 Web UI 里输入:"请把'你好世界'翻译成英文"。如果配置正常,Agent 会调用 translate 工具,返回:

复制代码
[auto-detected → English]
Hello world

插件闭环跑通!


五、插件生命周期:为什么不需要手动清理?

DeepSeek Harness 的一个核心设计:通过 ctx 注册的事件、工具、定时器,会在插件卸载时自动清理

如果你的插件创建了额外资源(如网络连接、轮询定时器),通过 ctx.effect 提供释放函数:

typescript 复制代码
import type { Context } from '@deepseek-ai/cordis'

export const name = 'heartbeat-plugin'

export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(() => {
      console.log('[heartbeat] alive')
    }, 5000)
    // 返回清理函数,插件卸载时执行
    return () => clearInterval(timer)
  })
}

这比手动维护全局数组更可靠------每项副作用都跟着插件实例绑定,配置变化或热更新时不会把旧监听器留在运行时里。


六、在 Creator 模式下开发:让 Agent 帮你造插件

前面我们手动写了插件。现在来到 Creator 模式的真正价值------让 Agent 辅助生成插件骨架

步骤 1:用 Creator 模式启动 Web UI

bash 复制代码
# 在 Web UI 设置里选择 Creator 模式,或在新建会话时选 Creator
pnpm dsh web

步骤 2:用自然语言描述你想要的插件

在 Creator 模式的会话里输入:

复制代码
请在 scratch-plugin/src/ 下为我生成一个自定义插件,要求:
1、插件名叫做 jira-ticket-fetcher
2、inject 依赖 tools 服务
3、注册一个工具叫 fetch_jira_ticket,参数 ticket_id(必填字符串)
4、工具内部调用 https://your-jira-instance/rest/api/2/issue/{ticket_id}
5、返回 issue 的 key、summary、status、assignee
6、API Token 从环境变量 JIRA_TOKEN 读取,不要硬编码
7、同时生成 scratch-plugin/cordis.yml,用绝对路径 insert 这个插件

Agent 会直接生成:

  • scratch-plugin/src/jira-ticket-fetcher.ts
  • scratch-plugin/cordis.yml

步骤 3:加载并验证

bash 复制代码
pnpm dsh web --patch ./scratch-plugin/cordis.yml

新开会话(任意模式),让 Agent "用 fetch_jira_ticket 工具查一下 PROJ-123",插件即生效。

💡 Creator 模式的优势:把"插件开发"这件需要熟悉 Cordis API 的事,降级为"用自然语言描述需求"。Agent 本身运行在 Standard 全套能力之上,能读文档、能写代码、能生成符合规范的 cordis.yml但生成的代码你仍要 review------尤其是权限、凭据、网络访问这些安全敏感点。


七、把插件固化进自定义 Preset(进阶)

当你发现自己反复做某一类任务时,就该考虑自定义 Preset 了。Creator 模式可以帮你生成 agent.cordis.yml

复制代码
请为我生成一个自定义 Agent 预设,名字叫 translator-expert。需求:
1、继承 standard 基础能力;
2、默认加载我刚刚写的 tool-translate 插件;
3、角色 persona 设定:你是一名资深翻译专家,优先考虑语境与习语;
4、输出完整 agent.cordis.yml,保存到 ~/.dsh/.agent-presets/translator-expert/ 下面。

Agent 会直接生成完整的 agent.cordis.yml 配置,保存到 .agent-presets 目录,立刻可以选用。

核心价值:把"翻译专家"的工作 SOP 固化成专属 Agent,新会话里不用每次从头解释一遍。Creator 模式真正省掉的,是重复任务里的沟通成本。


八、插件开发避坑清单

⚠️ 这些都是社区踩过的真实坑,开发前看一遍能省 2 小时:

  1. 路径必须绝对cordis.ymlname 字段必须是绝对路径,相对路径会找不到模块。
  2. 具名导出,禁用 defaultexport const name + export function apply,写 export default 会导致 inject 丢失。
  3. inject 声明要全 :插件用到的服务(toolsllmlogger 等)都要在 inject 数组里声明,否则 apply 执行时 ctx.xxx 可能是 undefined。
  4. 对象类型输出必须 additionalProperties: falseoutput.schema 如果用 type: 'object',必须显式声明 additionalProperties: false,否则注册直接失败。
  5. 异步执行用 exec.signalexecute(args, exec) 第二个参数里的 signal 要传给下游调用,支持取消。
  6. 凭据走环境变量:API Key、Token 通过环境变量注入,别写死在代码或截图里。
  7. 本地试验用 --patch,长期使用合并进 Profile--patch 是覆盖层,适合快速验证;稳定后的插件应该合并进自己的 Profile 或 Bundle。
  8. 预设切换必须新开会话:已经在对话框里打开了会话,再去设置里改 Preset,当前会话不会变。必须新开会话才能应用新的 Preset。

九、一个完整的"生产级插件"目录结构

如果你想把插件做得规范(参考官方 packages/ 下的插件包结构):

复制代码
scratch-plugin/
├── src/
│   ├── index.ts          # 插件入口,export name / inject / apply
│   └── invariant.ts      # 运行时不变式声明
├── tests/
│   └── translate.spec.ts # 单元测试(vitest)
├── cordis.yml            # 本地 patch 配置
├── package.json          # name: "@deepseek-ai/dsh-tool-translate"
├── tsconfig.json         # extends 仓库 tsconfig.base.json
└── README.md             # 含 Model Experience 部分

invariant.ts 的最小写法(每个包必须有):

typescript 复制代码
import { registerPackageInvariant } from '@deepseek-ai/dsh-invariants'

registerPackageInvariant(
  '@deepseek-ai/dsh-tool-translate',
  'No runtime invariant: leaf tool plugin with no owned state.',
)

测试约定:单元测试放 tests/ 目录(不放 src/__tests__/),产品可见的插件还需要 REAL-composition 测试(通过 Loader 加载,而不是手动 ctx.plugin)。

如果要让插件出现在 dsh 的 Bundle 里(而不只是 --patch 临时加载),需要在 Bundle 的 cordis.patch.yml 添加:

yaml 复制代码
- id: tool-translate
  name: '@deepseek-ai/dsh-tool-translate'

同时在 Bundle 的 package.json 声明依赖:"@deepseek-ai/dsh-tool-translate": "workspace:*".


结语

回到"如何用 Creator 模式开发自定义插件"这个问题,答案其实分成两层:

表层:Creator 模式是 DeepSeek Harness 的高信任运行模式,开放了运行时自省与 Preset 创作能力。你可以在 Creator 模式下让 Agent 帮你生成插件骨架、调试配置、固化工作流------把"插件开发"降级为"自然语言描述"。

底层 :插件本身的写法是统一的 Cordis 规范------一个导出 nameapply 的 TypeScript 模块,通过 ctx.tools.register(defineTool(...)) 注册工具,用 cordis.ymlinsert 挂载,通过 pnpm dsh web --patch 加载进运行时。这套机制跟你用 Standard / PTC / Minimal / Creator 哪种模式启动 Web UI 加载插件无关。

所以正确的工作流是

  1. Creator 模式 在可信本地环境试验、让 Agent 辅助生成插件代码
  2. Cordis 插件规范 写好 apply + defineTool,处理依赖注入与副作用清理
  3. --patch 快速加载验证,稳定后合并进自定义 Preset 或 Bundle
  4. 遵守 安全红线:Creator 模式禁公网部署,API Key 走环境变量,工作区要窄

DeepSeek Harness 的关键不是"它已经内置了多少功能",而是:你能不能把自己的工作方式写成一个可加载、可替换、可清理的插件。当一个重复任务需要固定步骤时,写 Plugin;当一个能力要被其他模块复用时,拆成 Service;当一个版本需要组合多个能力时,使用 Profile 和 Patch。

📌 由于项目迭代极快,Star 数、版本号、插件 API 细节请以 GitHub 仓库实页与官方文档(deepseek-harness.github.io)为准。本文基于 2026 年 8 月的公开资料整理,部分细节可能已在最新版本中变化。

参考资料


相关推荐
u13013017 分钟前
AI 日报(2026年8月28日)
人工智能
老郑聊AI业财智造17 分钟前
从“思考-行动”到“知行合一”:ReActAgent的架构原理与工程实践全景剖析
人工智能·架构·系统架构·软件工程·软件构建
l12586519 分钟前
# LangGraph Tool Calling Agent 深度实战:从零构建 ReAct 循环与工具调用链
人工智能·python·自然语言处理·langchain·agent
VIP_CQCRE20 分钟前
用 Ace Data Cloud 快速接入 MiniMax H3:从提示词到 2K 商业级视频生成
人工智能·api·ai视频·minimax·ace data cloud
迁移科技26 分钟前
3D视觉引导销轴上下料:单相机双工位高效方案
人工智能·自动化·视觉检测
数据皮皮侠29 分钟前
企业知识重组能力数据(1988-2025)
大数据·人工智能·搜索引擎·智慧城市·制造
戴西软件30 分钟前
远程协同仿真是什么体验?
运维·jvm·人工智能·自动化·rpa
m4Rk_37 分钟前
【论文阅读】Agent 记忆机制(53):Experience-Following——为什么错误经验会在记忆中不断传播
论文阅读·人工智能·学习·开源·github
艾莉丝努力练剑3 小时前
【AI大模型接入SDK】Provider分析与实现
c++·人工智能·学习·面试·大模型·llm