4
0
0

插件开发与扩展

2026-08-14
2026-09-07
插件开发与扩展
文章摘要
|

入门10:插件开发与扩展

这是入门系列的最后一篇。我们将从零开发一个完整的"天气查询"插件。
如果你写过 Vue 插件或 Express 中间件,那这篇对你来说会很轻松。


1. 插件开发模式

Harness 所有插件遵循"能力缝设计模式"——其实就是接口 + 实现 + 消费三层:

接口约定
提供服务
Service Definition
定义接口
Service Provider
实现功能
Consumer
消费使用
角色类比前端干啥的
Service Definition定义一个接口/抽象类说清楚"这个能力长啥样"
Service Provider实现这个接口真正干活
Consumer调用方使用这个能力

2. 实战:开发一个"天气查询"插件

我们要做三个包:

packages/weather/
├── weather-service/          # 1. 定义"查天气"这个接口
├── weather-service-local/    # 2. 真正调用天气 API
└── tool-weather/             # 3. 注册成 AI 能用的工具

2.1 第一步:定义接口

就像写一个 TypeScript 的 interface,或者 Vue 的 composable 类型定义。

packages/weather/weather-service/src/index.ts

import { Context, Service } from 'cordis'

// 1. 定义一个抽象类(就像 Vue 的 composable 的接口定义)
export abstract class WeatherService extends Service {
  constructor(ctx: Context) {
    // 注册到 ctx.weather——就像 provide('weather', this)
    super(ctx, 'weather')
  }

  // 查当前天气
  abstract getCurrentWeather(city: string): Promise<WeatherResult>

  // 查天气预报
  abstract getForecast(city: string): Promise<ForecastResult>
}

// 返回的数据类型
export interface WeatherResult {
  temperature: number
  humidity: number
  description: string
  windSpeed: number
}

export interface ForecastResult {
  city: string
  daily: Array<{
    date: string
    high: number
    low: number
    description: string
  }>
}

// 2. 声明类型——让 ctx.weather 有类型提示
declare module 'cordis' {
  interface Context {
    weather: WeatherService
  }
}

package.json

{
  "name": "@deepseek-ai/dsh-weather-service",
  "version": "0.1.0",
  "type": "module",
  "main": "src/index.ts",
  "peerDependencies": {
    "cordis": "workspace:^"
  }
}

2.2 第二步:实现服务

就像写一个 class implements 接口,或者写一个具体的 composable。

packages/weather/weather-service-local/src/index.ts

import { Context } from 'cordis'
import {
  WeatherService,
  WeatherResult,
  ForecastResult,
} from '@deepseek-ai/dsh-weather-service'

// 实现天气服务
export class WeatherServiceLocal extends WeatherService {
  constructor(ctx: Context) {
    super(ctx)
    // 声明我需要 ctx.web 这个依赖
    ctx.inject(['web'], this)
  }

  async getCurrentWeather(city: string): Promise<WeatherResult> {
    // 调用天气 API(就像用 axios 调接口)
    const response = await ctx.web.fetch(
      `https://api.weather.com/current?city=${encodeURIComponent(city)}`
    )
    const data = JSON.parse(response)

    return {
      temperature: data.temp,
      humidity: data.humidity,
      description: data.condition,
      windSpeed: data.wind,
    }
  }

  async getForecast(city: string): Promise<ForecastResult> {
    const response = await ctx.web.fetch(
      `https://api.weather.com/forecast?city=${encodeURIComponent(city)}`
    )
    const data = JSON.parse(response)

    return {
      city: data.city,
      daily: data.daily.map((d) => ({
        date: d.date,
        high: d.temp_max,
        low: d.temp_min,
        description: d.condition,
      })),
    }
  }
}

// 导出插件
export default WeatherServiceLocal

2.3 第三步:注册成 AI 工具

就像用 app.component() 注册一个全局组件,AI 就能用了。

packages/weather/tool-weather/src/index.ts

import { Context } from 'cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export function apply(ctx: Context) {
  // 声明需要 ctx.weather
  ctx.inject(['weather'], ctx)

  // 注册工具——就像 Vue 的 app.component('weather_query', ...)
  ctx.tools.define('weather_query', defineTool({
    name: 'weather_query',
    description: '查询指定城市的当前天气',
    parameters: {
      type: 'object',
      properties: {
        city: {
          type: 'string',
          description: '城市名称,如 "北京"、"上海"',
        },
      },
      required: ['city'],
    },
    async execute(args: { city: string }) {
      try {
        const result = await ctx.weather.getCurrentWeather(args.city)
        return {
          success: true,
          data: `${args.city}: ${result.description},${result.temperature}°C`,
        }
      } catch (error) {
        return {
          success: false,
          error: `查天气失败了:${error.message}`,
        }
      }
    },
  }))
}

3. 注册插件

cordis.yml 里注册:

plugins:
  - name: weather-service
    path: @deepseek-ai/dsh-weather-service-local
    config:
      apiKey: your-api-key

  - name: tool-weather
    path: @deepseek-ai/dsh-tool-weather

4. 配置验证

Schemastery 做配置验证——就像 Vue 的 props 校验:

import { Schema } from 'schemastery'

// 定义配置格式
export const Config = Schema.object({
  apiKey: Schema.string().description('API 密钥').required(),
  units: Schema.union(['metric', 'imperial'])
    .description('温度单位')
    .default('metric'),
  cacheDuration: Schema.number()
    .description('缓存时间(秒)')
    .default(300),
})

// 在插件里用
export function apply(ctx: Context, config: Config) {
  // config 现在有正确的类型和验证过的值
  console.log('API Key:', config.apiKey)
}

5. 用 Waterfall 扩展核心流程

就像 Express 中间件——在核心流程里"插一脚"。

// 在 AI 调用前加个自定义指令
ctx.waterfall('llm/stream', (options) => {
  options.messages.unshift({
    role: 'system',
    content: '今天是 ' + new Date().toLocaleDateString(),
  })
  return options
})

// 在工具执行后记日志
ctx.waterfall('tools/post-execute', (execution) => {
  console.log(`工具 ${execution.name} 执行耗时 ${execution.duration}ms`)
  return execution
})

// 在拼提示词时加自定义节
ctx.waterfall('system-prompt/assemble', (assembly) => {
  assembly.sections.push({
    priority: 50,
    content: '注意:当前为严格模式。',
  })
  return assembly
})

6. 生命周期管理

就像 Vue 的 onMounted / onUnmounted,插件也有完整的生命周期。

import { Context } from 'cordis'

export function apply(ctx: Context) {
  // 启动时
  console.log('插件启动')

  // 注册副作用——就像 React 的 useEffect,自动清理
  ctx.effect(() => {
    const timer = setInterval(() => {
      console.log('心跳')
    }, 30000)

    // 插件销毁时自动执行
    return () => {
      clearInterval(timer)
      console.log('插件已清理')
    }
  })

  // 监听事件——就像 Vue 的 $on
  ctx.on('session/created', (session) => {
    console.log('新会话:', session.header.sessionId)
  })

  // 支持 HMR 热更新
  if (ctx.hmr) {
    ctx.hmr.accept()
  }
}

7. 发布与安装

# 发布到 npm
cd packages/weather/weather-service
npm publish

# 或者从 git 安装
# cordis.yml 支持 git 安装
plugins:
  - name: weather
    path: https://github.com/user/dsh-weather.git
    config:
      apiKey: '${WEATHER_API_KEY}'

8. 参考示例

项目提供了 6 个完整示例,适合作为参考:

示例说明
examples/headless-agent/非交互式 Agent,跑一次就退出
examples/jsonrpc-agent/通过 Python SDK 驱动的 Agent
examples/mcp-memory/连接 MCP 记忆服务
examples/web-cordis/自引用 Agent,可以修改自己的插件
examples/web-schedule/定时提醒插件
examples/acp-agent/ACP 协议的自动化服务器

恭喜!你已完成入门系列的学习

现在你应该对 DeepSeek Harness 有了全面的理解。接下来可以:

  1. 读官方文档docs/ 目录有完整的架构、教程、指南
  2. 跑示例项目examples/ 目录有 6 个可运行示例
  3. 读源码packages/ 下每个包都可以深入阅读
  4. 写自己的插件:按本篇教程,写一个属于你的插件
  5. 参与社区:GitHub Discussions 和 Discord

进阶阅读


要点总结

  • 插件开发三步骤:定义接口 → 实现功能 → 注册工具
  • 能力缝模式 = 接口 + 实现 + 消费,三层分离
  • ctx.effect() 管理生命周期,自动清理
  • 用 Waterfall 事件在核心流程里"插一脚"
  • 用 Schemastery 做配置验证
  • cordis.yml 注册插件,支持 npm 和 git 安装
  • 参考 examples/ 目录获取完整示例

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!