开发基础

第一个 Harness 插件

从零搭建你的第一个 DeepSeek Harness 插件,掌握目录规范、微内核挂载与本地热重载调试。

在深度求索(DeepSeek)的工程体系中,智能体并非一个不可拆分的单体黑盒,而是遵循 “大脑(Model)+ 神经中枢与外周骨架(Harness)” 的协同演进哲学。

作为 Harness 运行时的中枢神经,Cordis 微内核 赋予了系统无限扩展的能力——从文件检索、代码沙箱执行,到外部 API 网关甚至自定义安全审计,所有能力皆以插件(Plugin)的形式声明式插拔。

本文将带领你从零设计、编码并调试一个生产级别的 “智能体行为审计与安全探针插件(Agent Security & Action Auditor)”,深入理解 Cordis 上下文注入机制与热重载流转。


架构透视:为什么插件化是智能体工程的基石?

很多开发者在构建 AI Agent 时习惯将 Prompt、API 请求与系统命令混杂在同一个脚本中。当面对多智能体协同、异构环境迁移或安全合规审计时,这种紧耦合代码往往难以为继。

DeepSeek Harness 采用了控制反转(IoC)与微内核架构

┌─────────────────────────────────────────────────────────────┐
│                 Cordis 微内核控制中枢 (Microkernel)          │
├─────────────────────────────────────────────────────────────┤
│  • 统一服务总线 (Service Registry)                          │
│  • 异步事件调度器 (Event Bus: emit / bail / waterfall)      │
│  • 生命周期沙箱追踪 (Context Effect Tracking)                │
└───────────────┬─────────────────────────────┬───────────────┘
                │ 上下文注入 (Context Fork)    │
        ┌───────▼────────┐           ┌────────▼───────┐
        │  官方核心能力包 │           │  自研业务插件   │
        │  (Bash / PTC)  │           │  (Audit Guard) │
        └────────────────┘           └────────────────┘
  • 微内核零业务偏见:内核本身不包含具体大模型 Prompt 或终端执行逻辑,仅负责插件的注册、依赖解析与内存清理;
  • 独立的 Context 作用域:每个被加载的插件都会获得专属的 Context 实例。插件所创建的事件监听器、定时轮询与服务注册,均被自动绑定到当前 Fiber 作用域中,彻底消除内存泄漏隐患。

步骤一:创建独立的插件开发工作区

为保障主项目代码的整洁,建议在工作区内建立专门的插件实验目录:

bash
# 1. 创建插件根目录与源码子目录
mkdir -p custom-plugins/agent-guard/src
cd custom-plugins/agent-guard

# 2. 初始化 npm package.json
npm init -y

# 3. 安装 Cordis 核心微内核类型依赖
npm install @deepseek-ai/cordis --save-dev

custom-plugins/agent-guard/tsconfig.json 中配置针对现代 Node.js 的 TypeScript 编译规则:

json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "declaration": true,
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

步骤二:编写安全审计插件核心逻辑

custom-plugins/agent-guard/src/index.ts 中实现我们的插件代码。该插件将监听智能体在每一轮会话(Turn)中的交互时序,并在后台执行资源健康检查:

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

// 1. 声明插件全局唯一标识符
export const name = 'agent-security-guard';

// 2. 核心挂载入口:由 Cordis 运行时在依赖就绪后自动调用
export function apply(ctx: Context) {
    let turnSequence = 0;

    // 监听 Harness 初始化就绪事件
    ctx.on('ready', () => {
        console.log('[Guard]  智能体安全审计插件已成功挂载至 Harness 运行时!');
    });

    // 监听模型推理前的会话触发事件
    ctx.on('session/before-turn', (session) => {
        turnSequence++;
        console.log(`[Guard] >>> 第 #${turnSequence} 轮推理开始,会话 ID: ${session?.id ?? 'main-session'}`);
    });

    // 监听模型完成思考与工具调用后的会话结束事件
    ctx.on('session/after-turn', (session) => {
        console.log(`[Guard] <<< 第 #${turnSequence} 轮推理完成,已记录 Trajectory 审计快照。`);
    });

    // 注册定时内存健康探测(由 ctx 托管,在插件卸载或热重载时由微内核自动清理,无需手动 clearInterval)
    ctx.setInterval(() => {
        const memMB = (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1);
        console.log(`[Guard] 运行时内存健康度探测: 堆内存占用 ${memMB} MB`);
    }, 45000);
}

步骤三:在 cordis.yml 中挂载并启动调试

在你的 Harness 项目根目录下的 cordis.yml 中注册刚才编写的本地插件路径:

yaml
mode: standard

# 模型配置区域
model:
  provider: deepseek
  name: deepseek-coder
  apiKey: env(DEEPSEEK_API_KEY)

# 插件挂载列表
plugins:
  # 挂载我们刚刚编写的本地自研插件
  - name: "./custom-plugins/agent-guard"

  # 挂载 Harness 官方基础执行器
  - name: "@deepseek-ai/dsh-plugin-bash"
    config:
      timeoutMs: 30000
  - name: "@deepseek-ai/dsh-plugin-str-replace"

使用热补丁模式启动 Web 控制台:

bash
pnpm dsh web --patch ./cordis.yml

启动后控制台会即时打印 [Guard] 智能体安全审计插件已成功挂载 的日志。尝试修改 src/index.ts 中的输出内容并保存,Cordis 的 HMR 引擎将在零停机状态下瞬时完成热更新。


企业级插件开发避坑指南

  1. 杜绝使用原生 Node.js 全局定时器:切勿直接调用 global.setInterval,必须使用 ctx.setInterval,否则在多次热重载后会留下大量无法回收的僵尸定时器;
  2. 状态隔离原则:避免将业务缓存直接保存在插件模块的顶层全局变量中,推荐通过微内核的 Service 单例进行状态托管;
  3. 异常防线隔离:在事件监听回调中若涉及高风险的 I/O 操作,应做好局部 try-catch 降级,避免单个监听器异常中断整条流水线。

常见问题解答 (FAQ)

Q1: 在 cordis.yml 中配置相对路径时,为什么有时会提示模块加载失败?

解答:请确保相对路径以 ./ 开头(例如 ./custom-plugins/agent-guard)。此外,如果使用的是纯 TypeScript 源码,需确保启动命令携带了 --import tsx 或预先执行了 npm run build 输出编译后的 JavaScript 文件。

Q2: 插件内部的 apply 函数是在什么时机被调用的?

解答:当 Harness 解析完插件依赖树,且该插件在 export const inject 中声明的所有前置服务(如 toolsllm)均已处于 ACTIVE 状态后,微内核才会正式调用 apply(ctx, config)

Q3: 为什么 Cordis 能够保证插件卸载时不会发生内存泄漏?

解答:Cordis 通过 Context 对象拦截并记录了所有注册行为(包括事件总线监听器、定时器以及挂载的子插件)。当插件执行 dispose 卸载时,微内核会自底向上遍历释放树,执行所有逆向清理操作。

Q4: 如何在插件中读取企业级敏感配置(如数据库密码或自定义认证 Token)?

解答:推荐在插件中导出基于 @deepseek-ai/schemastery 的强类型 Config Schema,并在 cordis.yml 中通过 apiKey: env(MY_SECRET) 注入,结合 .role('secret') 属性实现全流程安全脱敏。