核心摘要

DeepSeek Harness 插件是通过 apply(ctx)、依赖声明、服务、事件和可逆副作用扩展 Cordis 的模块。一个正确插件不止要能编译:它应选择最窄的文档化接缝,在卸载时释放资源,保持授权边界,并针对锁定的 DSH 版本进行测试。本文讨论这些契约,而不是提供一份很快失效的插件清单。

目录

最小可用插件

DeepSeek Harness 插件是由 Cordis 挂载的 TypeScript 模块。官方“第一个插件”教程使用导出的 nameapply 函数;apply 接收运行时 Context,插件通过文档化服务或事件注册行为。

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

export const name = 'workspace-notice'

export function apply(ctx: Context) {
  console.info('[workspace-notice] mounted')
}

这个例子故意很小。它只能证明加载器可导入并挂载模块,不代表提供了工具、服务、授权、持久化或清理策略。每次只添加一种能力,并为该能力增加可观察的测试。

官方教程通过绝对路径的 cordis.yml Overlay 加载本地插件:

yaml
- insert:
    - id: workspace-notice
      name: "/absolute/path/to/deepseek-harness/scratch-plugin/src/workspace-notice.ts"

使用 Overlay 启动 Web Profile:

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

先只在一次性工作区使用这套流程。Patch 能改变激活的插件树,因此生产 Patch 应具备与可执行部署配置相同的审查和回滚要求。

服务、依赖与生命周期

Cordis 服务在 Context 上提供具名能力。需要某项服务的插件应在 inject 中声明,框架会等待依赖就绪,而不是依赖隐式的注册顺序。

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

export const name = 'guarded-tool-extension'
export const inject = ['tools']

export function apply(ctx: Context) {
  // 因为已声明依赖,ctx.tools 在此处已经可用。
  // 在这里注册一个边界明确的能力。
}

该模式有三个关键边界:

关注点 正确契约 不安全的捷径
依赖 为消费的服务声明 inject 访问可选服务并假定它必定存在
生命周期 通过 ctx.on()ctx.effect() 注册 保留未受管理的定时器、Socket 或监听器
能力 依赖 ctx.tools 等接口 导入并修改某个具体内部实现

Cordis 注册是可逆副作用。受支持的事件监听器和注册项会在插件卸载时移除。定时器、网络客户端等需要自定义回收的资源,应从 ctx.effect() 返回 disposer:

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

export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(() => {
      console.info('plugin heartbeat')
    }, 30_000)

    return () => clearInterval(timer)
  })
}

不要把定时器当作持久化调度或恢复机制。进程重启后它会消失;需要定时执行的任务必须拥有持久化 Job 契约和明确恢复模型。

服务、事件和副作用如何选择

DSH 架构区分直接能力和生命周期接缝。插件需要直接调用能力时使用服务;需要观察、转换或治理运行时中的工作流动时使用事件。

flowchart TD A["需要新行为"] --> B{"是否直接调用能力?"} B -->|是| C["消费文档化服务"] B -->|否| D{"是否属于生命周期策略或观测?"} D -->|是| E["使用文档化事件接缝"] D -->|否| F["定义边界明确的服务接口"] C --> G["注册可逆副作用"] E --> G F --> G

官方 Extension Cookbook 给出如下分工:

  • tools/pre-execute:在派发前决定允许、拒绝或要求审批。
  • tools/execute:包裹派发生命周期,实现超时、重试或计量。
  • tools/post-execute:只有确实需要时才转换结果。
  • tools/result:观察不可变最终结果,用于审计或指标。
  • session/event:将日志事件投影到界面、遥测或外部协议。

这些接缝不能互换。事后记录结果无法阻止被禁止的操作;权限决定也不应藏在指标监听器中。

工具策略插件示例

下方草图遵循官方 tools/pre-execute 模式,只展示一个边界明确的策略决定,并不是完整授权系统。

ts
import type { Context } from '@deepseek-ai/cordis'
import type { PreToolDecision, ToolExecution } from '@deepseek-ai/dsh-tools'

export const name = 'repository-write-policy'
export const inject = ['tools']

async function canWriteWorkspace(exec: ToolExecution): Promise<boolean> {
  // 从应用拥有的策略服务解析用户身份、租户、工作区、分支与审批状态。
  // 不能信任模型生成的参数。
  return exec.tool.name !== 'write_file'
}

export function apply(ctx: Context) {
  ctx.on(
    'tools/pre-execute',
    async (exec, next): Promise<PreToolDecision> => {
      if (!(await canWriteWorkspace(exec))) {
        return { kind: 'deny', reason: 'Workspace write is not approved.' }
      }
      return next()
    },
  )
}

核心细节是 next()。仅追加检查的 waterfall 监听器必须把控制权交给后续链路;真正拥有最终决定的监听器可以返回类型化拒绝来停止链路。事件的派发模式属于公共契约,不能把它假设为普通中间件。

该插件仍不能证明写入安全。生产策略必须将已认证的用户和租户绑定到精确资源、参数、时间窗口、策略版本和审批制品。更广泛的设计可参阅 人机协同审批门禁

配置覆盖与发布

Profile、Bundle 和 Patch 使 DSH 部署可组合,同时也使配置成为会改变行为的制品。建议采用如下发布契约:

  1. 锁定 DSH 版本及所有外部插件依赖。
  2. 将经过审查的 Patch 文件纳入版本控制,避免手工修改生产状态。
  3. 在预发布环境导出有效插件树,并与批准配置进行比较。
  4. 在隔离工作区执行能力、拒绝、取消和卸载测试。
  5. 以最小工具白名单和作用域凭证逐步发布。
  6. 启用写操作之前,保留回滚 Patch 或上一版 Profile。

不要根据插件名称承诺兼容性。DeepSeek Harness 仍处于开发者预览阶段,配置形态和扩展契约均可能改变;可重复的测试集比教程快照更可靠。

插件契约测试

应测试插件的可观察契约,而不是只测试成功路径。下表是实用的最小集:

测试 断言
挂载 声明的服务依赖在 apply() 运行前已经可用
卸载 监听器、定时器和客户端被释放
允许操作 被允许的工具进入执行并产生结果
拒绝操作 被禁止的工具不会进入执行
审批过期 过期审批不能授权已变化的参数
取消 取消不会留下未受管理的后台工作
升级 相同测试集可通过目标锁定的 DSH 版本

对于工具插件,应使用伪造或沙箱化的下游系统做集成测试。不能把共享仓库或真实客户数据当作写入策略的测试环境。Agent Harness 评测指南 中的故障注入和发布门禁可以补充框架层测试。

常见问题 (FAQ)

每个 DeepSeek Harness 功能都应该拆成插件吗?

不应该。只有具备独立生命周期、配置面、依赖边界或替换需求的能力才适合成为插件。把每个小辅助函数都拆成插件会掩盖控制流并增加兼容测试成本。

可以直接使用 DSH 仓库的私有 API 吗?

从源码检出中导入内部实现技术上可行,但不应把它视为稳定扩展契约。优先使用文档化的服务和事件接缝;若确实无法避免,必须锁定源码版本并增加升级测试。

插件状态应如何保存?

按状态语义选择。仅运行时缓存可以保存在内存;模型可见或与回放相关的事实需要持久化会话事件;外部副作用则需要应用拥有的操作记录与对账。

插件可以添加 MCP Server 吗?

DSH 的扩展映射将 MCP 描述为每个 Server 一个插件:发现工具后注册到 ctx.tools。发现不等于授权,仍要验证 Server、工具元数据、租户 Scope、参数、结果上限和下游副作用。

插件需要锁定哪些版本?

至少锁定插件包或提交、DSH 版本、配置 Patch、服务假设、模型路由、策略版本、测试集版本,以及所有会改变行为的外部协议或 Tool Schema。

相关资源