Files
deepseek-harness/docs/user/develop/basic/index.zh.md
T
Yichen Jiang 0a2ac90617 docs: fix reference sidebar ordering and group the subsystem pages
The VitePress config declared no position for the subsystem or other-interface
sections, so `indexOf` returned -1 and sorted them ahead of every declared
group: the reference landing page's own sidebar entry sat 1549px below the
fold. Four subsystem pages also shared `order` values with pages in the same
section, resolved only by sort stability and array concatenation order.

Section placement and collapse move into the manifest as a per-locale
declaration, and `sectionSpec` throws for an undeclared section instead of
sorting it silently to the top. Subsystem pages are grouped by concern, the
six topical groups collapse until one holds the page being read, and page
order derives from array position.

The projector drops the language-switcher line and repository badge the
canonical pages carry for their GitHub readers. The navigation bar gains the
DeepSeek wordmark, a release-stage tag, and a favicon; the sidebar scrollbar
rests invisible and appears while scrolling. Subsystem pages carry a two-level
outline, and the two plugin-development tracks now cross-link.
2026-08-12 13:52:27 +08:00

3.4 KiB
Raw Blame History

第一个插件

English | 中文

本教程会创建一个最小的 Harness 插件,并将其加载到 Web UI 中。请从已完成从源码运行路径的仓库检出开始。

创建本地项目

在仓库根目录创建本教程使用的临时项目:

mkdir -p scratch-plugin/src

插件是什么

在 Harness 中,插件是一个导出 apply 函数的 TypeScript 模块。框架在加载时调用 apply,传入一个 ctx(上下文对象),你通过 ctx 注册能力:

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

export const name = 'my-plugin'

export function apply(ctx: Context) {
  // Register capabilities here.
}

这就是完整配置。

创建插件文件

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

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

export const name = 'hello-plugin'

export function apply(ctx: Context) {
  // Required dependencies are ready before apply runs.
  console.log('[hello-plugin] plugin loaded!')
}

注册到 cordis.yml

创建 scratch-plugin/cordis.yml,作为插入本地插件的 Web 覆盖层:

- insert:
    - id: hello
      name: './src/my-plugin.ts'

使用该覆盖层启动 Web UI

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

打开 http://127.0.0.1:3080。启动期间,终端会打印 [hello-plugin] plugin loaded!

自动清理

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

如果你有需要手动清理的资源(比如一个网络连接),用 ctx.effect() 告诉框架怎么清理:

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

export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(() => {
      console.log('heartbeat')
    }, 5000)

    // The returned function runs when the plugin unloads.
    return () => clearInterval(timer)
  })
}

声明依赖

如果你的插件需要使用其他服务(如 toolsllm),需要声明 inject

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

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

export function apply(ctx: Context) {
  // ctx.tools is ready here.
  ctx.tools.register(/* ... */)
}

框架会确保依赖的服务就绪后才加载你的插件。

插件的三种形态

除了函数形式,插件还支持对象形式和类形式:

对象形式

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

export default {
  name: 'my-plugin',
  inject: ['tools'],
  apply(ctx: Context) {
    // ...
  },
}

类形式

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

export default class MyService extends Service {
  static inject = ['tools']

  constructor(ctx: Context) {
    super(ctx, 'myService')
    // Perform synchronous initialization in the constructor.
  }
}

大多数情况下,函数形式足够了。当插件需要向其他插件提供服务时,可使用类形式(见 服务与依赖)。

下一步