跳到主要内容

插件的开发步骤

本篇提供一套从样例模板开发到发布插件的详细实践教程,样例项目是基于 nx monorepo 的模板(见下方目录结构)。内容涵盖初始化、代码实现、配置 schema、测试、构建、发布以及常见问题排查。

1. 先决条件

在开始之前,请确保环境满足:

  • Node.js(推荐 LTS >= 18)
  • npm 或 pnpm
  • nx 工具(无需全局安装:通过 npx nx 使用)
  • TypeScript
  • 已配置 monorepo(项目模板已具备)

开发前建议安装依赖:

npm install
# 或
pnpm install

2. 模板项目结构说明

你提供的模板目录如下(摘录):

.
├── AGENTS.md
├── CHANGELOG.md
├── CLAUDE.md
├── README.md
├── TREE.txt
├── jest.config.ts
├── jest.preset.js
├── nx.json
├── package-lock.json
├── package.json
├── packages
│   └── my-plugin
│   ├── README.md
│   ├── jest.config.js
│   ├── package.json
│   ├── src
│   │   ├── index.ts
│   │   └── lib
│   │   ├── my-plugin.spec.ts
│   │   ├── my-plugin.ts
│   │   ├── tool.ts
│   │   ├── toolset.strategy.ts
│   │   └── types.ts
│   ├── tsconfig.json
│   └── tsconfig.lib.json
├── tsconfig.base.json
├── tsconfig.json
└── tsconfig.spec.json

说明:模板把每个插件放在 packages/<name> 下,使用 @nx/js:lib 生成的 --publishable 库可以单独构建并发布到 npm。


3. 快速起步(用 Nx 新建插件)

在项目根目录下运行:

npx nx g @nx/js:lib packages/my-plugin --publishable --importPath=@my-org/my-plugin

常用构建与发布命令:

# build library
npx nx build my-plugin

# 使用 monorepo 的 release 流程(由模板提供)
npx nx release

# 手动发布 npm(可传 otp)
npx nx run @my-org/my-plugin:nx-release-publish --access public --otp=<one-time-password-if-needed>

注意:--importPathpackage.jsonname 字段应一致,便于外部依赖导入。


4. 编写插件入口(index.ts

插件入口是宿主装载插件的第一站。通常导出一个 defaultXpertPlugin 对象。

要点:

  • 使用 zod 定义插件的 config schema(用于校验与 UI 自动生成表单)
  • meta 包含插件元数据(name/version/category/icon/displayName/description/keywords)
  • register(ctx) 返回插件的模块类(用于注入到 NestJS 应用)
  • 可选实现 onStart/onStop 生命周期钩子

在模板中 index.ts 示例已经给出:它定义了 ConfigSchemametaregisteronStartonStop

建议

  • meta.name 使用 scoped package 名称(例如 @xpert-ai/my-plugin),与 package.json 保持一致。
  • icon 建议使用 svg 字符串或数据 URL,便于前端渲染。

5. 实现插件模块(MyPlugin)与生命周期钩子

插件的模块类使用 @XpertServerPlugin 装饰器声明:它会在宿主应用中被导入并注册。

关键字段:

  • imports:其他模块(例如 RouterModule.register([{ path: '/xxx', module: MyPlugin }])
  • entities:TypeORM/ORM 实体类数组(如果插件需要数据库持久化)
  • providers:注入到容器的服务/策略
  • controllers:暴露路由的 controllers

生命周期接口:

  • IOnPluginBootstraponPluginBootstrap() 在插件被初始化时调用(适合做一次性注册/检查)
  • IOnPluginDestroyonPluginDestroy() 在插件被卸载/销毁时调用

实现建议

  • 使用 ctx.logger(由宿主提供)记录启动/停止日志,不直接使用 console.log
  • 避免在模块加载阶段做长时间阻塞操作;在 onPluginBootstrap 中以异步方式执行需要的初始化工作。

6. 实现 Strategy(Toolset / Integration / DocumentSource)

Strategy 是插件扩展系统能力的核心。常见的类型有:

  • ToolsetStrategy(工具集合,例如计算器、转换工具)
  • IntegrationStrategy(外部系统集成,例如 Firecrawl、第三方 API)
  • DocumentSourceStrategy(文档数据源,例如 Web 爬取、S3、数据库)

下面以 Calculator(toolset)和 Firecrawl(document source / integration)为示例说明。

6.1 ToolsetStrategy(以 Calculator 为例)

@ToolsetStrategy(MyToolName) 装饰器将该类标记为 toolset 提供者。主要职责:提供元信息、校验配置、创建工具集合。

  • meta:包含 name、label、description、icon、configSchema 等,用于 UI 列表和配置表单
  • validateConfig(config):校验传入配置
  • create(config):返回具体的 BuiltinToolset 实例(可复用的工具集合)
  • createTools():若不使用 BuiltinToolset,也可以直接返回工具数组

示例:CalculatorStrategy 中创建 CalculatorToolset,并依赖 buildCalculatorTool() 构建单个工具。

6.2 IntegrationStrategy

用于将外部服务建模为宿主可以配置的集成项。主要职责:

  • 提供 meta(包括 schema 以在 UI 中展示集成配置字段)
  • 实现 execute(integration, payload) 来执行一次集成操作或触发任务

安全注意meta.schema 中用于显示 API Key 的字段,应使用 ISchemaSecretField 提示前端加密/掩码并允许持久化存储(persist: true)。

6.3 DocumentSourceStrategy

用于把外部文档/数据源转换为宿主可索引的 Document[](例如 LangChain 的 Document)。核心方法:

  • validateConfig(config):配置校验
  • test(config):测试连接/连通性
  • loadDocuments(config, context?):返回 Document[],每个文档包含 pageContentmetadata(例如 source、title、description)

示例中 FirecrawlSourceStrategy.loadDocuments 调用 firecrawlService.crawlUrl(context.integration, config) 并把结果映射为 Document


7. 服务(Service)与控制器(Controller)

插件通常需要在 NestJS 容器中注册服务与控制器:

  • Service 负责与外部 API 的通信(带重试/超时逻辑、限流)
  • Controller 暴露用于测试/调试的 HTTP 接口(例如 POST /test

示例:FirecrawlController.connect 接收 IIntegration payload 并调用 firecrawlService.test

实现注意事项:

  • 对外请求应设置合理超时并处理异常。
  • 对于长任务(爬取、批量导入),建议使用作业队列(Bull、Queue)处理并返回任务 id,而不是阻塞 HTTP 请求。
  • 使用 @I18nLang() 或其他国际化工具在 Service/Controller 中处理多语言提示。

8. 配置 Schema 与 UI 元数据(zod + JSON Schema)

建议:插件入口使用 zod 定义配置(强类型且方便)。当需要在 UI 中渲染配置表单时,需把 zod schema 转换为 JSON Schema 或直接在 meta.schema 中维护一套 JSON Schema。

示例:

const ConfigSchema = z.object({
apiUrl: z.string().url().optional(),
apiKey: z.string().optional()
})

9. 单元测试与集成测试

模板中包含 my-plugin.spec.ts,推荐写法:

  • 使用 Jest 和 Nest 测试工具(@nestjs/testing)创建模块上下文
  • 对 Strategy 的 validateConfigcreatecreateTools 做单元测试
  • Mock 外部 HTTP 调用(nock / msw)来测试 Service
  • 对 Controller 编写 e2e 风格的请求测试(使用 supertest

运行测试:

npx nx test my-plugin

10. 构建、发布与 npm 发布流程

构建

npx nx build c

构建后,packages/packages/dist(或模板配置的输出目录)会生成可发布文件。

版本管理与发布

模板中提供 nx release 流程(请参考 repo 中 release 脚本)。一般步骤:

  1. 更新 package.jsonversion(或使用 release 脚本自动 bump)
  2. npx nx build my-plugin
  3. npx nx release(如果项目配置了 release 插件,会基于 CHANGELOG/commit 自动创建发布)

手动 publish:

npx nx run @my-org/my-plugin:nx-release-publish --access public --otp=<otp-if-needed>

提示:确保 package.json 中的 files, main, types 等字段正确指向构建输出;publishConfig 可设置访问权限。


11. 运行时行为与调试技巧

插件加载与运行时要点:

  • 宿主会调用插件的 register,并把返回的 module 注入到 Nest 应用。确保 register 返回 { module: YourPluginModule, global: true|false }
  • onStart/onStop 可以放置插件运行时初始化与销毁操作。
  • IOnPluginBootstrap / IOnPluginDestroy 为插件内部的生命周期钩子(与宿主框架生命周期互补)。

调试技巧:

  • 启用详细日志:在 registeronStart 中使用 ctx.logger.debug/info/error 能把日志写入宿主日志系统。
  • 检查 DI 错误:典型的 Nest can't resolve dependencies 通常是 provider 未在 providers 中声明。
  • 检查实体注册:如果 entities 未正确导出或重复注册,会导致 ORM 报错。

12. 安全性和最佳实践

  • 不要在源代码中硬编码密钥或凭证。
  • 使用 ISchemaSecretField 标记密钥字段,并让宿主对其加密/受控存储。
  • 插件内部对外调用应有重试、超时和熔断策略。
  • 将长任务放入队列,避免在请求生命周期内阻塞。
  • 使用 Typescript 严格模式,明确类型(尤其是 IIntegration<T> 等泛型)。
  • 单一职责:每个插件尽量只实现一类功能(例如只做集成或只做数据源),更利于维护与权限控制。

13. 常见问题与排查指南

  • 问题:插件无法加载,抛出 Cannot find module

    • 检查 package.jsonname/main 字段是否正确。
    • 构建输出路径是否包含 index.js
  • 问题:Nest DI 注入失败

    • 检查是否在 @XpertServerPlugin.providers 中注册了对应的 provider。
    • 检查构造函数参数是否正确使用 @Inject() 或类型注解。
  • 问题:实体重复注册或数据库迁移异常

    • 确认 entities 中没有重复类,且命名空间/路径一致。
  • 问题:API Key 在 UI 中无法明文显示或保存

    • 检查 meta.schema 中是否使用了 ISchemaSecretField 并设置 persist: true

14. 附录:示例文件与常用命令

14.1 关键示例命令

# 初始化插件库
npx nx g @nx/js:lib packages/my-plugin --publishable --importPath=@xpert-ai/my-plugin

# 构建
npx nx build my-plugin

# 测试
npx nx test my-plugin

# 发布(release 脚本)
npx nx release

# 手动 publish
npx nx run @xpert-ai/my-plugin:nx-release-publish --access public --otp=<otp>

14.2 常用代码片段(概览)

  • index.ts:导出 XpertPlugin 对象
  • my-plugin.ts:使用 @XpertServerPlugin 的模块类
  • toolset.strategy.ts:实现 ToolsetStrategy 的类
  • tool.ts:实际构建工具(buildCalculatorTool()
  • types.ts:插件相关常量和类型

(模板中的具体实现已包含在你的示例代码里,可按需修改。)


小结

本章节提供了从创建插件骨架到发布的全流程指南,并覆盖了实现 Strategy、Service、Controller、配置 schema、安全性、测试、构建与发布等环节。你可以基于仓库里的模板快速开始,并根据上文的 "最佳实践" 与 "排查指南" 对插件做健壮化改进。

如果你愿意,我可以:

  • Calculator 工具的 buildCalculatorTool() 的完整实现补全为可运行代码;
  • 或者把 Firecrawl 插件的 Service(HTTP 调用、错误处理、重试)实现为一个完整的样例;
  • 也可逐段把上面的章节展开为更长的教程或示例文件(例如直接生成 src/lib/tool.tsservice.ts 等文件)。

请告诉我你希望我接着做哪项(例如:生成可运行的 tool.ts、生成 FirecrawlService 实现、或把文档转为中文/英文 README)。