第一个 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 作用域中,彻底消除内存泄漏隐患。
步骤一:创建独立的插件开发工作区
为保障主项目代码的整洁,建议在工作区内建立专门的插件实验目录:
# 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 编译规则:
{
"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)中的交互时序,并在后台执行资源健康检查:
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 中注册刚才编写的本地插件路径:
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 控制台:
pnpm dsh web --patch ./cordis.yml
启动后控制台会即时打印 [Guard] 智能体安全审计插件已成功挂载 的日志。尝试修改 src/index.ts 中的输出内容并保存,Cordis 的 HMR 引擎将在零停机状态下瞬时完成热更新。
企业级插件开发避坑指南
- 杜绝使用原生 Node.js 全局定时器:切勿直接调用
global.setInterval,必须使用ctx.setInterval,否则在多次热重载后会留下大量无法回收的僵尸定时器; - 状态隔离原则:避免将业务缓存直接保存在插件模块的顶层全局变量中,推荐通过微内核的
Service单例进行状态托管; - 异常防线隔离:在事件监听回调中若涉及高风险的 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 中声明的所有前置服务(如 tools、llm)均已处于 ACTIVE 状态后,微内核才会正式调用 apply(ctx, config)。
Q3: 为什么 Cordis 能够保证插件卸载时不会发生内存泄漏?
解答:Cordis 通过 Context 对象拦截并记录了所有注册行为(包括事件总线监听器、定时器以及挂载的子插件)。当插件执行 dispose 卸载时,微内核会自底向上遍历释放树,执行所有逆向清理操作。
Q4: 如何在插件中读取企业级敏感配置(如数据库密码或自定义认证 Token)?
解答:推荐在插件中导出基于 @deepseek-ai/schemastery 的强类型 Config Schema,并在 cordis.yml 中通过 apiKey: env(MY_SECRET) 注入,结合 .role('secret') 属性实现全流程安全脱敏。